Codebase list nzbget / ee923c2
New upstream version 21.0~r2296+dfsg Andreas Moog 5 years ago
123 changed file(s) with 8428 addition(s) and 841 deletion(s). Raw diff Collapse all Expand all
0 # Configuration file for integration with http://lgtm.com
1
2 path_classifiers:
3 library:
4 # exclude these directories from default alerts report:
5 - lib
6 - webui/lib
7
8 extraction:
9 cpp:
10 configure:
11 command:
12 # compile with tests to activate scanning of C++ sources for tests
13 - ./configure --enable-tests
14
15 queries:
16 - exclude: js/incomplete-sanitization # this one gives false positives only and nothing useful
00 #
11 # This file is part of nzbget. See <http://nzbget.net>.
22 #
3 # Copyright (C) 2008-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 # Copyright (C) 2008-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 #
55 # This program is free software; you can redistribute it and/or modify
66 # it under the terms of the GNU General Public License as published by
6767 daemon/main/nzbget.h \
6868 daemon/main/Options.cpp \
6969 daemon/main/Options.h \
70 daemon/main/WorkState.cpp \
71 daemon/main/WorkState.h \
7072 daemon/main/Scheduler.cpp \
7173 daemon/main/Scheduler.h \
7274 daemon/main/StackTrace.cpp \
1616 #
1717 # This file is part of nzbget. See <http://nzbget.net>.
1818 #
19 # Copyright (C) 2008-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
19 # Copyright (C) 2008-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
2020 #
2121 # This program is free software; you can redistribute it and/or modify
2222 # it under the terms of the GNU General Public License as published by
231231 daemon/main/DiskService.h daemon/main/Maintenance.cpp \
232232 daemon/main/Maintenance.h daemon/main/nzbget.cpp \
233233 daemon/main/nzbget.h daemon/main/Options.cpp \
234 daemon/main/Options.h daemon/main/Scheduler.cpp \
234 daemon/main/Options.h daemon/main/WorkState.cpp \
235 daemon/main/WorkState.h daemon/main/Scheduler.cpp \
235236 daemon/main/Scheduler.h daemon/main/StackTrace.cpp \
236237 daemon/main/StackTrace.h daemon/nntp/ArticleDownloader.cpp \
237238 daemon/nntp/ArticleDownloader.h daemon/nntp/ArticleWriter.cpp \
386387 daemon/main/CommandLineParser.$(OBJEXT) \
387388 daemon/main/DiskService.$(OBJEXT) \
388389 daemon/main/Maintenance.$(OBJEXT) daemon/main/nzbget.$(OBJEXT) \
389 daemon/main/Options.$(OBJEXT) daemon/main/Scheduler.$(OBJEXT) \
390 daemon/main/Options.$(OBJEXT) daemon/main/WorkState.$(OBJEXT) \
391 daemon/main/Scheduler.$(OBJEXT) \
390392 daemon/main/StackTrace.$(OBJEXT) \
391393 daemon/nntp/ArticleDownloader.$(OBJEXT) \
392394 daemon/nntp/ArticleWriter.$(OBJEXT) \
719721 daemon/main/DiskService.h daemon/main/Maintenance.cpp \
720722 daemon/main/Maintenance.h daemon/main/nzbget.cpp \
721723 daemon/main/nzbget.h daemon/main/Options.cpp \
722 daemon/main/Options.h daemon/main/Scheduler.cpp \
724 daemon/main/Options.h daemon/main/WorkState.cpp \
725 daemon/main/WorkState.h daemon/main/Scheduler.cpp \
723726 daemon/main/Scheduler.h daemon/main/StackTrace.cpp \
724727 daemon/main/StackTrace.h daemon/nntp/ArticleDownloader.cpp \
725728 daemon/nntp/ArticleDownloader.h daemon/nntp/ArticleWriter.cpp \
11561159 daemon/main/nzbget.$(OBJEXT): daemon/main/$(am__dirstamp) \
11571160 daemon/main/$(DEPDIR)/$(am__dirstamp)
11581161 daemon/main/Options.$(OBJEXT): daemon/main/$(am__dirstamp) \
1162 daemon/main/$(DEPDIR)/$(am__dirstamp)
1163 daemon/main/WorkState.$(OBJEXT): daemon/main/$(am__dirstamp) \
11591164 daemon/main/$(DEPDIR)/$(am__dirstamp)
11601165 daemon/main/Scheduler.$(OBJEXT): daemon/main/$(am__dirstamp) \
11611166 daemon/main/$(DEPDIR)/$(am__dirstamp)
15521557 @AMDEP_TRUE@@am__include@ @am__quote@daemon/main/$(DEPDIR)/Options.Po@am__quote@
15531558 @AMDEP_TRUE@@am__include@ @am__quote@daemon/main/$(DEPDIR)/Scheduler.Po@am__quote@
15541559 @AMDEP_TRUE@@am__include@ @am__quote@daemon/main/$(DEPDIR)/StackTrace.Po@am__quote@
1560 @AMDEP_TRUE@@am__include@ @am__quote@daemon/main/$(DEPDIR)/WorkState.Po@am__quote@
15551561 @AMDEP_TRUE@@am__include@ @am__quote@daemon/main/$(DEPDIR)/nzbget.Po@am__quote@
15561562 @AMDEP_TRUE@@am__include@ @am__quote@daemon/nntp/$(DEPDIR)/ArticleDownloader.Po@am__quote@
15571563 @AMDEP_TRUE@@am__include@ @am__quote@daemon/nntp/$(DEPDIR)/ArticleWriter.Po@am__quote@
00 # NZBGet #
11 [![License](https://img.shields.io/badge/license-GPL-blue.svg)](http://www.gnu.org/licenses/)
22 [![Build Status](https://img.shields.io/travis/nzbget/nzbget/develop.svg)](https://travis-ci.org/nzbget/nzbget)
3 [![Code Quality: Cpp](https://img.shields.io/lgtm/grade/cpp/g/nzbget/nzbget.svg?label=code%20quality:%20c%2b%2b)](https://lgtm.com/projects/g/nzbget/nzbget/context:cpp)
4 [![Code Quality: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/nzbget/nzbget.svg?label=code%20quality:%20js)](https://lgtm.com/projects/g/nzbget/nzbget/context:javascript)
5 [![Total Alerts](https://img.shields.io/lgtm/alerts/g/nzbget/nzbget.svg)](https://lgtm.com/projects/g/nzbget/nzbget/alerts)
6
7 [![Total downloads](https://img.shields.io/github/downloads/nzbget/nzbget/total.svg)](https://github.com/nzbget/nzbget/releases)
8 [![Downloads (latest release)](https://img.shields.io/github/downloads/nzbget/nzbget/latest/total.svg?label=latest%20release)](https://github.com/nzbget/nzbget/releases/latest)
39
410 NZBGet is a binary downloader, which downloads files from Usenet
511 based on information given in nzb-files.
612
713 NZBGet is written in C++ and is known for its extraordinary performance and efficiency.
814
9 NZBGet can be run at almost every platform - classic PCs, NAS, media players, SAT-receivers, WLAN-routers, etc.
15 NZBGet can be run on almost every device - classic PCs, NAS, media players, SAT-receivers, WLAN-routers, etc.
1016 The download area provides precompiled binaries
11 for Windows, Mac OS X and Linux (compatible with many CPUs and platform variants). For other platforms
17 for Windows, macOS, Linux (compatible with many CPUs and platform variants), FreeBSD and Android. For other platforms
1218 the program can be compiled from sources.
1319
14 - [Home page (nzbget.net)](http://nzbget.net) - for first time visitors, learn more about NZBGet;
15 - [Downloads](http://nzbget.net/download) - get the binaries and sources;
20 - [Home page (nzbget.net)](http://nzbget.net) - learn more about NZBGet;
21 - [Downloads](http://nzbget.net/download) - get compiled binaries and sources;
1622 - [Documentation](http://nzbget.net/documentation) - installation manuals, HOW-TOs, API;
1723 - [Forum](http://forum.nzbget.net) - get support, share your ideas, scripts, add-ons.
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
33 * Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
4 * Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
144144 {
145145 debug("Destroying Connection");
146146
147 Disconnect();
147 Connection::Disconnect();
148148 }
149149
150150 void Connection::SetSuppressErrors(bool suppressErrors)
194194 CRYPTO_set_locking_callback(nullptr);
195195 CRYPTO_set_id_callback(nullptr);
196196 #endif
197 #if OPENSSL_VERSION_NUMBER < 0x10100000L
197198 ERR_remove_state(0);
199 #endif
198200 #if OPENSSL_VERSION_NUMBER >= 0x10002000L && ! defined (LIBRESSL_VERSION_NUMBER)
199201 SSL_COMP_free_compression_methods();
200202 #endif
201203 //ENGINE_cleanup();
202204 CONF_modules_free();
203205 CONF_modules_unload(1);
206 #ifndef OPENSSL_NO_COMP
204207 COMP_zlib_cleanup();
208 #endif
205209 ERR_free_strings();
206210 EVP_cleanup();
207211 CRYPTO_cleanup_all_ex_data();
213217 Close();
214218
215219 #ifdef HAVE_OPENSSL
220 #if OPENSSL_VERSION_NUMBER < 0x10100000L
216221 ERR_remove_state(0);
222 #endif
217223 #endif
218224 }
219225
558564 // hostname verification
559565 if (!m_host.Empty() && X509_check_host(cert, m_host, m_host.Length(), 0, nullptr) != 1)
560566 {
561 char* certHost = nullptr;
567 const unsigned char* certHost = nullptr;
562568 // Find the position of the CN field in the Subject field of the certificate
563569 int common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name(cert), NID_commonName, -1);
564570 if (common_name_loc >= 0)
571577 ASN1_STRING* common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
572578 if (common_name_asn1 != nullptr)
573579 {
574 certHost = (char*)ASN1_STRING_data(common_name_asn1);
580 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
581 certHost = ASN1_STRING_get0_data(common_name_asn1);
582 #else
583 certHost = ASN1_STRING_data(common_name_asn1);
584 #endif
575585 }
576586 }
577587 }
4444 virtual void PrintError(const char* errMsg);
4545
4646 private:
47 SOCKET m_socket;
4748 bool m_isClient;
4849 CString m_host;
4950 CString m_certFile;
5051 CString m_keyFile;
5152 CString m_cipher;
52 SOCKET m_socket;
5353 bool m_suppressErrors = false;
5454 bool m_initialized = false;
5555 bool m_connected = false;
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2012-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2012-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2121 #include "WebDownloader.h"
2222 #include "Log.h"
2323 #include "Options.h"
24 #include "WorkState.h"
2425 #include "Util.h"
2526 #include "FileSystem.h"
2627
7172
7273 if ((((Status == adFailed) && (remainedDownloadRetries > 1)) ||
7374 ((Status == adConnectError) && (remainedConnectRetries > 1)))
74 && !IsStopped() && !(!m_force && g_Options->GetPauseDownload()))
75 && !IsStopped() && !(!m_force && g_WorkState->GetPauseDownload()))
7576 {
7677 detail("Waiting %i sec to retry", g_Options->GetUrlInterval());
7778 int msec = 0;
7879 while (!IsStopped() && (msec < g_Options->GetUrlInterval() * 1000) &&
79 !(!m_force && g_Options->GetPauseDownload()))
80 {
81 usleep(100 * 1000);
80 !(!m_force && g_WorkState->GetPauseDownload()))
81 {
82 Util::Sleep(100);
8283 msec += 100;
8384 }
8485 }
8586
86 if (IsStopped() || (!m_force && g_Options->GetPauseDownload()))
87 if (IsStopped() || (!m_force && g_WorkState->GetPauseDownload()))
8788 {
8889 Status = adRetry;
8990 break;
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2222 #include "Log.h"
2323 #include "Util.h"
2424 #include "Options.h"
25 #include "WorkState.h"
2526
2627 static const int POSTPROCESS_PARCHECK = 92;
2728 static const int POSTPROCESS_SUCCESS = 93;
271272 m_postInfo->SetProgressLabel(text);
272273 }
273274
274 if (g_Options->GetPausePostProcess() && !m_postInfo->GetNzbInfo()->GetForcePriority())
275 if (g_WorkState->GetPausePostProcess() && !m_postInfo->GetNzbInfo()->GetForcePriority())
275276 {
276277 time_t stageTime = m_postInfo->GetStageTime();
277278 time_t startTime = m_postInfo->GetStartTime();
278279 time_t waitTime = Util::CurrentTime();
279280
280281 // wait until Post-processor is unpaused
281 while (g_Options->GetPausePostProcess() && !m_postInfo->GetNzbInfo()->GetForcePriority() && !IsStopped())
282 {
283 usleep(100 * 1000);
282 while (g_WorkState->GetPausePostProcess() && !m_postInfo->GetNzbInfo()->GetForcePriority() && !IsStopped())
283 {
284 Util::Sleep(100);
284285
285286 // update time stamps
286287
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2013-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2013-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2020 #include "nzbget.h"
2121 #include "FeedCoordinator.h"
2222 #include "Options.h"
23 #include "WorkState.h"
2324 #include "WebDownloader.h"
2425 #include "Util.h"
2526 #include "FileSystem.h"
6566
6667 m_downloadQueueObserver.m_owner = this;
6768 DownloadQueue::Guard()->Attach(&m_downloadQueueObserver);
69
70 m_workStateObserver.m_owner = this;
71 g_WorkState->Attach(&m_workStateObserver);
6872 }
6973
7074 FeedCoordinator::~FeedCoordinator()
8488
8589 while (!DownloadQueue::IsLoaded())
8690 {
87 usleep(20 * 1000);
91 Util::Sleep(20);
8892 }
8993
9094 if (g_Options->GetServerMode())
9397 g_DiskState->LoadFeeds(&m_feeds, &m_feedHistory);
9498 }
9599
96 int sleepInterval = 100;
97 int updateCounter = 0;
98 int cleanupCounter = 60000;
99
100 time_t lastCleanup = 0;
100101 while (!IsStopped())
101102 {
102 usleep(sleepInterval * 1000);
103
104 updateCounter += sleepInterval;
105 if (updateCounter >= 1000)
106 {
107 // this code should not be called too often, once per second is OK
108
109 if (!g_Options->GetPauseDownload() || m_force || g_Options->GetUrlForce())
110 {
111 Guard guard(m_downloadsMutex);
112
113 time_t current = Util::CurrentTime();
114 if ((int)m_activeDownloads.size() < g_Options->GetUrlConnections())
103 // this code should not be called too often, once per second is OK
104 if (!g_WorkState->GetPauseDownload() || m_force || g_Options->GetUrlForce())
105 {
106 Guard guard(m_downloadsMutex);
107
108 time_t current = Util::CurrentTime();
109 if ((int)m_activeDownloads.size() < g_Options->GetUrlConnections())
110 {
111 m_force = false;
112 // check feed list and update feeds
113 for (FeedInfo* feedInfo : &m_feeds)
115114 {
116 m_force = false;
117 // check feed list and update feeds
118 for (FeedInfo* feedInfo : &m_feeds)
115 if (((feedInfo->GetInterval() > 0 &&
116 (feedInfo->GetNextUpdate() == 0 ||
117 current >= feedInfo->GetNextUpdate() ||
118 current < feedInfo->GetNextUpdate() - feedInfo->GetInterval() * 60)) ||
119 feedInfo->GetFetch()) &&
120 feedInfo->GetStatus() != FeedInfo::fsRunning)
119121 {
120 if (((feedInfo->GetInterval() > 0 &&
121 (feedInfo->GetNextUpdate() == 0 ||
122 current >= feedInfo->GetNextUpdate() ||
123 current < feedInfo->GetNextUpdate() - feedInfo->GetInterval() * 60)) ||
124 feedInfo->GetFetch()) &&
125 feedInfo->GetStatus() != FeedInfo::fsRunning)
126 {
127 StartFeedDownload(feedInfo, feedInfo->GetFetch());
128 }
129 else if (feedInfo->GetFetch())
130 {
131 m_force = true;
132 }
122 StartFeedDownload(feedInfo, feedInfo->GetFetch());
123 }
124 else if (feedInfo->GetFetch())
125 {
126 m_force = true;
133127 }
134128 }
135129 }
136
137 CheckSaveFeeds();
138 ResetHangingDownloads();
139 updateCounter = 0;
140 }
141
142 cleanupCounter += sleepInterval;
143 if (cleanupCounter >= 60000)
130 }
131
132 CheckSaveFeeds();
133 ResetHangingDownloads();
134
135 if (std::abs(Util::CurrentTime() - lastCleanup) >= 60)
144136 {
145137 // clean up feed history once a minute
146138 CleanupHistory();
147139 CleanupCache();
148140 CheckSaveFeeds();
149 cleanupCounter = 0;
141 lastCleanup = Util::CurrentTime();
142 }
143
144 Guard guard(m_downloadsMutex);
145 if (m_force)
146 {
147 // don't sleep too long if there active feeds scheduled for redownload
148 m_waitCond.WaitFor(m_downloadsMutex, 1000, [&]{ return IsStopped(); });
149 }
150 else
151 {
152 // no active jobs, we can sleep longer:
153 // - if option "UrlForce" is active or if the feed list is empty we need to wake up
154 // only when a new feed preview is requested. We could wait indefinitely for that
155 // but we need to do some job every now and then and therefore we sleep only 60 seconds.
156 // - if option "UrlForce" is disabled we need also to wake up when state "DownloadPaused"
157 // is changed. We detect this via notification from 'WorkState'. However such
158 // notifications are not 100% reliable due to possible race conditions. Therefore
159 // we sleep for max. 5 seconds.
160 int waitInterval = g_Options->GetUrlForce() || m_feeds.empty() ? 60000 : 5000;
161 m_waitCond.WaitFor(m_downloadsMutex, waitInterval, [&]{ return m_force || IsStopped(); });
150162 }
151163 }
152164
160172 completed = m_activeDownloads.size() == 0;
161173 }
162174 CheckSaveFeeds();
163 usleep(100 * 1000);
175 Util::Sleep(100);
164176 ResetHangingDownloads();
165177 }
166178 debug("FeedCoordinator: Downloads are completed");
179191 feedDownloader->Stop();
180192 }
181193 debug("UrlDownloads are notified");
194
195 // Resume Run() to exit it
196 m_waitCond.NotifyAll();
197 }
198
199 void FeedCoordinator::WorkStateUpdate(Subject* caller, void* aspect)
200 {
201 m_force = true;
202 m_waitCond.NotifyAll();
182203 }
183204
184205 void FeedCoordinator::ResetHangingDownloads()
521542 }
522543
523544 StartFeedDownload(feedInfo.get(), true);
545
546 m_force = true;
547 m_waitCond.NotifyAll();
524548 }
525549
526550 // wait until the download in a separate thread completes
527551 while (feedInfo->GetStatus() == FeedInfo::fsRunning)
528552 {
529 usleep(100 * 1000);
553 Util::Sleep(100);
530554 }
531555
532556 // now can process the feed
585609 m_force = true;
586610 }
587611 }
612
613 m_waitCond.NotifyAll();
588614 }
589615
590616 std::unique_ptr<FeedFile> FeedCoordinator::parseFeed(FeedInfo* feedInfo)
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2013-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2013-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
6666 virtual void Update(Subject* caller, void* aspect) { m_owner->DownloadQueueUpdate(caller, aspect); }
6767 };
6868
69 class WorkStateObserver: public Observer
70 {
71 public:
72 FeedCoordinator* m_owner;
73 virtual void Update(Subject* caller, void* aspect) { m_owner->WorkStateUpdate(caller, aspect); }
74 };
75
6976 class FeedCacheItem
7077 {
7178 public:
105112 FeedHistory m_feedHistory;
106113 Mutex m_downloadsMutex;
107114 DownloadQueueObserver m_downloadQueueObserver;
115 WorkStateObserver m_workStateObserver;
108116 bool m_force = false;
109117 bool m_save = false;
110118 FeedCache m_feedCache;
119 ConditionVar m_waitCond;
120 bool m_wokenUp = false;
111121
112122 void StartFeedDownload(FeedInfo* feedInfo, bool force);
113123 void FeedCompleted(FeedDownloader* feedDownloader);
121131 void CheckSaveFeeds();
122132 std::unique_ptr<FeedFile> parseFeed(FeedInfo* feedInfo);
123133 void SchedulerNextUpdate(FeedInfo* feedInfo, bool success);
134 void WorkStateUpdate(Subject* caller, void* aspect);
124135 };
125136
126137 extern FeedCoordinator* g_FeedCoordinator;
5050
5151 if (m_command < fcEqual && !strValue)
5252 {
53 intBuf.Format("%lld", intValue);
53 intBuf.Format("%" PRId64, intValue);
5454 strValue = intBuf;
5555 }
5656
7171 int m_id;
7272 CString m_name;
7373 CString m_url;
74 bool m_backlog;
7475 int m_interval;
7576 CString m_filter;
7677 uint32 m_filterHash;
8687 CString m_outputFilename;
8788 bool m_fetch = false;
8889 bool m_force = false;
89 bool m_backlog;
9090 };
9191
9292 typedef std::deque<std::unique_ptr<FeedInfo>> Feeds;
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
33 * Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
4 * Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
2020
2121 #include "nzbget.h"
2222 #include "Options.h"
23 #include "WorkState.h"
2324 #include "Frontend.h"
2425 #include "Log.h"
2526 #include "Connection.h"
3233 {
3334 debug("Creating Frontend");
3435
36 m_workStateObserver.m_owner = this;
37 g_WorkState->Attach(&m_workStateObserver);
38
3539 m_updateInterval = g_Options->GetUpdateInterval();
40 }
41
42 void Frontend::Stop()
43 {
44 Thread::Stop();
45
46 m_waitCond.NotifyAll();
47 }
48
49 void Frontend::WorkStateUpdate(Subject* caller, void* aspect)
50 {
51 m_waitCond.NotifyAll();
3652 }
3753
3854 bool Frontend::PrepareData()
5672 if (m_summary)
5773 {
5874 m_currentDownloadSpeed = g_StatMeter->CalcCurrentDownloadSpeed();
59 m_pauseDownload = g_Options->GetPauseDownload();
60 m_downloadLimit = g_Options->GetDownloadRate();
75 m_pauseDownload = g_WorkState->GetPauseDownload();
76 m_downloadLimit = g_WorkState->GetSpeedLimit();
6177 m_threadCount = Thread::GetThreadCount();
6278 g_StatMeter->CalcTotalStat(&m_upTimeSec, &m_dnTimeSec, &m_allBytes, &m_standBy);
6379
107123 }
108124 else
109125 {
110 g_Options->SetResumeTime(0);
111 g_Options->SetPauseDownload(pause);
126 g_WorkState->SetResumeTime(0);
127 g_WorkState->SetPauseDownload(pause);
112128 }
113129 }
114130
120136 }
121137 else
122138 {
123 g_Options->SetDownloadRate(rate);
139 g_WorkState->SetSpeedLimit(rate);
124140 }
125141 }
126142
306322 IdList ids = { id };
307323 return client.RequestServerEditQueue(action, offset, nullptr, &ids, nullptr, rmId);
308324 }
325
326 void Frontend::Wait(int milliseconds)
327 {
328 if (g_WorkState->GetPauseFrontend())
329 {
330 Guard guard(m_waitMutex);
331 m_waitCond.WaitFor(m_waitMutex, 2000);
332 }
333 else
334 {
335 Util::Sleep(milliseconds);
336 }
337 }
2626 #include "DownloadInfo.h"
2727 #include "MessageBase.h"
2828 #include "QueueEditor.h"
29 #include "Observer.h"
2930
3031 class Frontend : public Thread
3132 {
5051 int m_dnTimeSec = 0;
5152 int64 m_allBytes = 0;
5253 bool m_standBy = false;
54 Mutex m_waitMutex;
55 ConditionVar m_waitCond;
5356
57 virtual void Stop();
5458 bool PrepareData();
5559 void FreeData();
5660 GuardedMessageList GuardMessages();
6266 bool RequestSetDownloadRate(int rate);
6367 bool ServerEditQueue(DownloadQueue::EEditAction action, int offset, int entry);
6468 bool RequestEditQueue(DownloadQueue::EEditAction action, int offset, int id);
69 void Wait(int milliseconds);
6570
6671 private:
72 class WorkStateObserver : public Observer
73 {
74 public:
75 Frontend* m_owner;
76 virtual void Update(Subject* caller, void* aspect) { m_owner->WorkStateUpdate(caller, aspect); }
77 };
78
6779 MessageList m_remoteMessages;
80 WorkStateObserver m_workStateObserver;
6881
6982 bool RequestMessages();
7083 bool RequestFileList();
84 void WorkStateUpdate(Subject* caller, void* aspect);
7185 };
7286
7387 #endif
1919
2020
2121 #include "nzbget.h"
22 #include "Util.h"
2223 #include "LoggableFrontend.h"
2324 #include "Log.h"
2425
2930 while (!IsStopped())
3031 {
3132 Update();
32 usleep(m_updateInterval * 1000);
33 Wait(m_updateInterval);
3334 }
3435 // Printing the last messages
3536 Update();
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
33 * Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
4 * Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
212212 m_dataUpdatePos = m_updateInterval;
213213 }
214214
215 usleep(10 * 1000);
216 m_dataUpdatePos -= 10;
215 // update more often (sleep shorter) if need faster reaction on user input
216 int sleepInterval = m_inputMode == normal ? 100 : 10;
217 Wait(sleepInterval);
218 m_dataUpdatePos -= sleepInterval;
217219 }
218220
219221 FreeData();
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
914914 }
915915
916916 int editQueueIdCount = 0;
917 if (editQueueIdTo != 0)
917 if (editQueueIdFrom < editQueueIdTo)
918918 {
919 if (editQueueIdFrom < editQueueIdTo)
920 {
921 editQueueIdCount = editQueueIdTo - editQueueIdFrom + 1;
922 }
923 else
924 {
925 editQueueIdCount = editQueueIdFrom - editQueueIdTo + 1;
926 }
919 editQueueIdCount = editQueueIdTo - editQueueIdFrom + 1;
927920 }
928921 else
929922 {
930 editQueueIdCount = 1;
923 editQueueIdCount = editQueueIdFrom - editQueueIdTo + 1;
931924 }
932925
933926 for (int i = 0; i < editQueueIdCount; i++)
934927 {
935 if (editQueueIdFrom < editQueueIdTo || editQueueIdTo == 0)
928 if (editQueueIdFrom < editQueueIdTo)
936929 {
937930 m_editQueueIdList.push_back(editQueueIdFrom + i);
938931 }
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2015-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2015-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2020 #include "nzbget.h"
2121 #include "DiskService.h"
2222 #include "Options.h"
23 #include "WorkState.h"
2324 #include "StatMeter.h"
2425 #include "Log.h"
2526 #include "Util.h"
2627 #include "FileSystem.h"
2728
29 DiskService::DiskService()
30 {
31 g_WorkState->Attach(this);
32 }
33
34 void DiskService::Update(Subject* caller, void* aspect)
35 {
36 WakeUp();
37 }
38
39 int DiskService::ServiceInterval()
40 {
41 return m_waitingRequiredDir ? 1 :
42 g_Options->GetDiskSpace() <= 0 ? Service::Sleep :
43 // notifications from 'WorkState' are not 100% reliable due to race conditions
44 !g_WorkState->GetDownloading() ? 10 :
45 1;
46 }
47
2848 void DiskService::ServiceWork()
2949 {
30 m_interval++;
31 if (m_interval == 5)
50 debug("Disk service work");
51
52 if (g_Options->GetDiskSpace() > 0 && g_WorkState->GetDownloading())
3253 {
33 if (!g_Options->GetPauseDownload() &&
34 g_Options->GetDiskSpace() > 0 && !g_StatMeter->GetStandBy())
35 {
36 // check free disk space every 1 second
37 CheckDiskSpace();
38 }
39 m_interval = 0;
54 // check free disk space every 1 second
55 CheckDiskSpace();
4056 }
4157
4258 if (m_waitingRequiredDir)
4763
4864 void DiskService::CheckDiskSpace()
4965 {
66 debug("Disk service work: check disk space");
67
5068 int64 freeSpace = FileSystem::FreeDiskSize(g_Options->GetDestDir());
5169 if (freeSpace > -1 && freeSpace / 1024 / 1024 < g_Options->GetDiskSpace())
5270 {
5371 warn("Low disk space on %s. Pausing download", g_Options->GetDestDir());
54 g_Options->SetPauseDownload(true);
72 g_WorkState->SetPauseDownload(true);
5573 }
5674
5775 if (!Util::EmptyStr(g_Options->GetInterDir()))
6078 if (freeSpace > -1 && freeSpace / 1024 / 1024 < g_Options->GetDiskSpace())
6179 {
6280 warn("Low disk space on %s. Pausing download", g_Options->GetInterDir());
63 g_Options->SetPauseDownload(true);
81 g_WorkState->SetPauseDownload(true);
6482 }
6583 }
6684 }
6785
6886 void DiskService::CheckRequiredDir()
6987 {
88 debug("Disk service work: check required dir");
89
7090 if (!Util::EmptyStr(g_Options->GetRequiredDir()))
7191 {
7292 bool allExist = true;
96116 info("All required directories available");
97117 }
98118
99 g_Options->SetTempPauseDownload(false);
100 g_Options->SetTempPausePostprocess(false);
119 g_WorkState->SetTempPauseDownload(false);
120 g_WorkState->SetTempPausePostprocess(false);
101121 m_waitingRequiredDir = false;
102122 }
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2015-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2015-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2121 #define DISKSERVICE_H
2222
2323 #include "Service.h"
24 #include "Observer.h"
2425
25 class DiskService : public Service
26 class DiskService : public Service, public Observer
2627 {
28 public:
29 DiskService();
30
2731 protected:
28 virtual int ServiceInterval() { return 200; }
32 virtual int ServiceInterval();
2933 virtual void ServiceWork();
34 virtual void Update(Subject* caller, void* aspect);
3035
3136 private:
32 int m_interval = 0;
3337 bool m_waitingRequiredDir = true;
3438 bool m_waitingReported = false;
3539
6767 {
6868 while (m_updateScriptController)
6969 {
70 usleep(20*1000);
70 Util::Sleep(20);
7171 }
7272 }
7373 }
178178 return false;
179179 }
180180
181 int len = (int)(pend - p);
181 size_t len = pend - p;
182182 if (len >= sizeof(fileName))
183183 {
184184 error("Could not parse file %s", *fileName);
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
33 * Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
4 * Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
19051905 {
19061906 for (OptEntry& opt : optEntries)
19071907 {
1908 const char* optname = opt.GetName();
1909 if (!strncasecmp(optname, "category", 8))
1908 const char* catoptname = opt.GetName();
1909 if (!strncasecmp(catoptname, "category", 8))
19101910 {
1911 char* p = (char*)optname + 8;
1911 char* p = (char*)catoptname + 8;
19121912 while (*p >= '0' && *p <= '9') p++;
19131913 if (p && (!strcasecmp(p, ".extensions")))
19141914 {
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
33 * Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
4 * Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
299299 bool GetDirectRename() { return m_directRename; }
300300 bool GetReorderFiles() { return m_reorderFiles; }
301301 EFileNaming GetFileNaming() { return m_fileNaming; }
302 int GetDownloadRate() const { return m_downloadRate; }
302303
303304 Categories* GetCategories() { return &m_categories; }
304305 Category* FindCategory(const char* name, bool searchAliases) { return m_categories.FindCategory(name, searchAliases); }
310311 bool GetDaemonMode() { return m_daemonMode; }
311312 void SetRemoteClientMode(bool remoteClientMode) { m_remoteClientMode = remoteClientMode; }
312313 bool GetRemoteClientMode() { return m_remoteClientMode; }
313 void SetPauseDownload(bool pauseDownload) { m_pauseDownload = pauseDownload; }
314 bool GetPauseDownload() const { return m_pauseDownload; }
315 void SetPausePostProcess(bool pausePostProcess) { m_pausePostProcess = pausePostProcess; }
316 bool GetPausePostProcess() const { return m_pausePostProcess; }
317 void SetPauseScan(bool pauseScan) { m_pauseScan = pauseScan; }
318 bool GetPauseScan() const { return m_pauseScan; }
319 void SetTempPauseDownload(bool tempPauseDownload) { m_tempPauseDownload = tempPauseDownload; }
320 bool GetTempPauseDownload() const { return m_tempPauseDownload; }
321 bool GetTempPausePostprocess() const { return m_tempPausePostprocess; }
322 void SetTempPausePostprocess(bool tempPausePostprocess) { m_tempPausePostprocess = tempPausePostprocess; }
323 void SetDownloadRate(int rate) { m_downloadRate = rate; }
324 int GetDownloadRate() const { return m_downloadRate; }
325 void SetResumeTime(time_t resumeTime) { m_resumeTime = resumeTime; }
326 time_t GetResumeTime() const { return m_resumeTime; }
327 void SetLocalTimeOffset(int localTimeOffset) { m_localTimeOffset = localTimeOffset; }
328 int GetLocalTimeOffset() { return m_localTimeOffset; }
329 void SetQuotaReached(bool quotaReached) { m_quotaReached = quotaReached; }
330 bool GetQuotaReached() { return m_quotaReached; }
331314
332315 private:
333316 OptEntries m_optEntries;
449432 int m_dailyQuota = 0;
450433 bool m_reorderFiles = false;
451434 EFileNaming m_fileNaming = nfArticle;
452
453 // Current state
435 int m_downloadRate = 0;
436
437 // Application mode
454438 bool m_serverMode = false;
455439 bool m_daemonMode = false;
456440 bool m_remoteClientMode = false;
457 bool m_pauseDownload = false;
458 bool m_pausePostProcess = false;
459 bool m_pauseScan = false;
460 bool m_tempPauseDownload = true;
461 bool m_tempPausePostprocess = true;
462 int m_downloadRate = 0;
463 time_t m_resumeTime = 0;
464 int m_localTimeOffset = 0;
465 bool m_quotaReached = false;
466441
467442 void Init(const char* exeName, const char* configFilename, bool noConfig,
468443 CmdOptList* commandLineOptions, bool noDiskAccess, Extender* extender);
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2008-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2008-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2020 #include "nzbget.h"
2121 #include "Scheduler.h"
2222 #include "Options.h"
23 #include "WorkState.h"
2324 #include "Log.h"
2425 #include "NewsServer.h"
2526 #include "ServerPool.h"
5051 CheckTasks();
5152 }
5253
54 void Scheduler::ScheduleNextWork()
55 {
56 // Ideally we should calculate wait time until next scheduler task or until resume time.
57 // The first isn't trivial and the second requires watching/reaction on changed scheduled resume time.
58 // We do it simpler instead: check once per minute, when seconds are changing from 59 to 00.
59
60 time_t curTime = Util::CurrentTime();
61 tm sched;
62 gmtime_r(&curTime, &sched);
63 sched.tm_min++;
64 sched.tm_sec = 0;
65 time_t nextMinute = Util::Timegm(&sched);
66
67 m_serviceInterval = nextMinute - curTime;
68 }
69
5370 void Scheduler::ServiceWork()
5471 {
72 debug("Scheduler service work");
73
5574 if (!DownloadQueue::IsLoaded())
5675 {
5776 return;
5877 }
78
79 debug("Scheduler service work: doing work");
5980
6081 if (!m_firstChecked)
6182 {
6788 m_executeProcess = true;
6889 CheckTasks();
6990 CheckScheduledResume();
91 ScheduleNextWork();
7092 }
7193
7294 void Scheduler::CheckTasks()
99121 }
100122 }
101123
102 time_t localCurrent = current + g_Options->GetLocalTimeOffset();
103 time_t localLastCheck = m_lastCheck + g_Options->GetLocalTimeOffset();
124 time_t localCurrent = current + g_WorkState->GetLocalTimeOffset();
125 time_t localLastCheck = m_lastCheck + g_WorkState->GetLocalTimeOffset();
104126
105127 tm tmCurrent;
106128 gmtime_r(&localCurrent, &tmCurrent);
158180
159181 void Scheduler::ExecuteTask(Task* task)
160182 {
183 #ifdef DEBUG
161184 const char* commandName[] = { "Pause", "Unpause", "Pause Post-processing", "Unpause Post-processing",
162185 "Set download rate", "Execute process", "Execute script",
163186 "Pause Scan", "Unpause Scan", "Enable Server", "Disable Server", "Fetch Feed" };
164187 debug("Executing scheduled command: %s", commandName[task->m_command]);
188 #endif
165189
166190 bool executeProcess = m_executeProcess || task->m_hours == Task::STARTUP_TASK;
167191
170194 case scDownloadRate:
171195 if (!task->m_param.Empty())
172196 {
173 g_Options->SetDownloadRate(atoi(task->m_param) * 1024);
197 g_WorkState->SetSpeedLimit(atoi(task->m_param) * 1024);
174198 m_downloadRateChanged = true;
175199 }
176200 break;
177201
178202 case scPauseDownload:
179203 case scUnpauseDownload:
180 g_Options->SetPauseDownload(task->m_command == scPauseDownload);
204 g_WorkState->SetPauseDownload(task->m_command == scPauseDownload);
181205 m_pauseDownloadChanged = true;
182206 break;
183207
184208 case scPausePostProcess:
185209 case scUnpausePostProcess:
186 g_Options->SetPausePostProcess(task->m_command == scPausePostProcess);
210 g_WorkState->SetPausePostProcess(task->m_command == scPausePostProcess);
187211 m_pausePostProcessChanged = true;
188212 break;
189213
190214 case scPauseScan:
191215 case scUnpauseScan:
192 g_Options->SetPauseScan(task->m_command == scPauseScan);
216 g_WorkState->SetPauseScan(task->m_command == scPauseScan);
193217 m_pauseScanChanged = true;
194218 break;
195219
228252 {
229253 if (m_downloadRateChanged)
230254 {
231 info("Scheduler: setting download rate to %i KB/s", g_Options->GetDownloadRate() / 1024);
255 info("Scheduler: setting download rate to %i KB/s", g_WorkState->GetSpeedLimit() / 1024);
232256 }
233257 if (m_pauseDownloadChanged)
234258 {
235 info("Scheduler: %s download", g_Options->GetPauseDownload() ? "pausing" : "unpausing");
259 info("Scheduler: %s download", g_WorkState->GetPauseDownload() ? "pausing" : "unpausing");
236260 }
237261 if (m_pausePostProcessChanged)
238262 {
239 info("Scheduler: %s post-processing", g_Options->GetPausePostProcess() ? "pausing" : "unpausing");
263 info("Scheduler: %s post-processing", g_WorkState->GetPausePostProcess() ? "pausing" : "unpausing");
240264 }
241265 if (m_pauseScanChanged)
242266 {
243 info("Scheduler: %s scan", g_Options->GetPauseScan() ? "pausing" : "unpausing");
267 info("Scheduler: %s scan", g_WorkState->GetPauseScan() ? "pausing" : "unpausing");
244268 }
245269 if (m_serverChanged)
246270 {
307331
308332 void Scheduler::CheckScheduledResume()
309333 {
310 time_t resumeTime = g_Options->GetResumeTime();
334 time_t resumeTime = g_WorkState->GetResumeTime();
311335 time_t currentTime = Util::CurrentTime();
312336 if (resumeTime > 0 && currentTime >= resumeTime)
313337 {
314338 info("Autoresume");
315 g_Options->SetResumeTime(0);
316 g_Options->SetPauseDownload(false);
317 g_Options->SetPausePostProcess(false);
318 g_Options->SetPauseScan(false);
319 }
320 }
339 g_WorkState->SetResumeTime(0);
340 g_WorkState->SetPauseDownload(false);
341 g_WorkState->SetPausePostProcess(false);
342 g_WorkState->SetPauseScan(false);
343 }
344 }
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2008-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2008-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
6565 void AddTask(std::unique_ptr<Task> task);
6666
6767 protected:
68 virtual int ServiceInterval() { return 1000; }
68 virtual int ServiceInterval() { return m_serviceInterval; }
6969 virtual void ServiceWork();
7070
7171 private:
8383 bool m_serverChanged;
8484 ServerStatusList m_serverStatusList;
8585 bool m_firstChecked = false;
86 int m_serviceInterval = 1;
8687
8788 void ExecuteTask(Task* task);
8889 void CheckTasks();
9293 void FetchFeed(const char* feedList);
9394 void CheckScheduledResume();
9495 void FirstCheck();
96 void ScheduleNextWork();
9597 };
9698
9799 #endif
281281 void DoSegFault()
282282 {
283283 char* N = nullptr;
284 strcpy(N, "");
284 *N = '\0';
285285 }
286286 };
287287
0 /*
1 * This file is part of nzbget. See <http://nzbget.net>.
2 *
3 * Copyright (C) 2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20 #include "nzbget.h"
21 #include "WorkState.h"
22
23 void WorkState::Changed()
24 {
25 Notify(nullptr);
26 }
0 /*
1 * This file is part of nzbget. See <http://nzbget.net>.
2 *
3 * Copyright (C) 2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20 #ifndef WORKSTATE_H
21 #define WORKSTATE_H
22
23 #include "Observer.h"
24
25 // WorkState is observable but notifications are not 100% reliable.
26 // The changes via Set-methods and readings via Get-methods are not synchronized throughout the program.
27 // As result race conditions may occur and some changes may go unnoticed.
28 // When waiting for changes don't wait too long to avoid lock ups.
29
30 class WorkState : public Subject
31 {
32 public:
33 void SetPauseDownload(bool pauseDownload) { m_pauseDownload = pauseDownload; Changed(); }
34 bool GetPauseDownload() const { return m_pauseDownload; }
35 void SetPausePostProcess(bool pausePostProcess) { m_pausePostProcess = pausePostProcess; Changed(); }
36 bool GetPausePostProcess() const { return m_pausePostProcess; }
37 void SetPauseScan(bool pauseScan) { m_pauseScan = pauseScan; Changed(); }
38 bool GetPauseScan() const { return m_pauseScan; }
39 void SetTempPauseDownload(bool tempPauseDownload) { m_tempPauseDownload = tempPauseDownload; Changed(); }
40 bool GetTempPauseDownload() const { return m_tempPauseDownload; }
41 void SetTempPausePostprocess(bool tempPausePostprocess) { m_tempPausePostprocess = tempPausePostprocess; Changed(); }
42 bool GetTempPausePostprocess() const { return m_tempPausePostprocess; }
43 void SetPauseFrontend(bool pauseFrontend) { m_pauseFrontend = pauseFrontend; Changed(); }
44 bool GetPauseFrontend() const { return m_pauseFrontend; }
45 void SetSpeedLimit(int speedLimit) { m_speedLimit = speedLimit; Changed(); }
46 int GetSpeedLimit() const { return m_speedLimit; }
47 void SetResumeTime(time_t resumeTime) { m_resumeTime = resumeTime; Changed(); }
48 time_t GetResumeTime() const { return m_resumeTime; }
49 void SetLocalTimeOffset(int localTimeOffset) { m_localTimeOffset = localTimeOffset; Changed(); }
50 int GetLocalTimeOffset() { return m_localTimeOffset; }
51 void SetQuotaReached(bool quotaReached) { m_quotaReached = quotaReached; Changed(); }
52 bool GetQuotaReached() { return m_quotaReached; }
53 void SetDownloading(bool downloading) { m_downloading = downloading; Changed(); }
54 bool GetDownloading() { return m_downloading; }
55
56 private:
57 bool m_pauseDownload = false;
58 bool m_pausePostProcess = false;
59 bool m_pauseScan = false;
60 bool m_tempPauseDownload = true;
61 bool m_tempPausePostprocess = true;
62 bool m_pauseFrontend = false;
63 int m_downloadRate = 0;
64 time_t m_resumeTime = 0;
65 int m_localTimeOffset = 0;
66 bool m_quotaReached = false;
67 int m_speedLimit = 0;
68 bool m_downloading = false;
69
70 void Changed();
71 };
72
73 extern WorkState* g_WorkState;
74
75 #endif
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
33 * Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
4 * Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
2323 #include "Log.h"
2424 #include "NzbFile.h"
2525 #include "Options.h"
26 #include "WorkState.h"
2627 #include "CommandLineParser.h"
2728 #include "ScriptConfig.h"
2829 #include "Thread.h"
7071 // Globals
7172 Log* g_Log;
7273 Options* g_Options;
74 WorkState* g_WorkState;
7375 ServerPool* g_ServerPool;
7476 QueueCoordinator* g_QueueCoordinator;
7577 UrlCoordinator* g_UrlCoordinator;
192194 // globals
193195 std::unique_ptr<Log> m_log;
194196 std::unique_ptr<Options> m_options;
197 std::unique_ptr<WorkState> m_workState;
195198 std::unique_ptr<ServerPool> m_serverPool;
196199 std::unique_ptr<QueueCoordinator> m_queueCoordinator;
197200 std::unique_ptr<UrlCoordinator> m_urlCoordinator;
222225
223226 bool m_reloading = false;
224227 bool m_daemonized = false;
228 bool m_stopped = false;
229 Mutex m_waitMutex;
230 ConditionVar m_waitCond;
225231
226232 void Init();
227233 void Final();
338344 g_WinConsole = m_winConsole.get();
339345 #endif
340346
347 m_workState = std::make_unique<WorkState>();
348 g_WorkState = m_workState.get();
349
341350 m_serviceCoordinator = std::make_unique<ServiceCoordinator>();
342351 g_ServiceCoordinator = m_serviceCoordinator.get();
343352
411420 m_commandLineParser->GetNoConfig(), (Options::CmdOptList*)m_commandLineParser->GetOptionList(), this);
412421 m_options->SetRemoteClientMode(m_commandLineParser->GetRemoteClientMode());
413422 m_options->SetServerMode(m_commandLineParser->GetServerMode());
414 m_options->SetPauseDownload(m_commandLineParser->GetPauseDownload());
423 m_workState->SetPauseDownload(m_commandLineParser->GetPauseDownload());
424 m_workState->SetSpeedLimit(g_Options->GetDownloadRate());
415425
416426 m_log->InitOptions();
417427
423433 m_commandLineParser->GetClientOperation() == CommandLineParser::opClientNoOperation)
424434 {
425435 info("Pausing all activities due to errors in configuration");
426 m_options->SetPauseDownload(true);
427 m_options->SetPausePostProcess(true);
428 m_options->SetPauseScan(true);
436 m_workState->SetPauseDownload(true);
437 m_workState->SetPausePostProcess(true);
438 m_workState->SetPauseScan(true);
429439 }
430440
431441 m_serverPool->SetTimeout(m_options->GetArticleTimeout());
536546 (m_remoteSecureServer && m_remoteSecureServer->IsRunning())) &&
537547 maxWaitMSec > 0)
538548 {
539 usleep(100 * 1000);
549 Util::Sleep(100);
540550 maxWaitMSec -= 100;
541551 }
542552
555565 (m_remoteSecureServer && m_remoteSecureServer->IsRunning())) &&
556566 maxWaitMSec > 0)
557567 {
558 usleep(100 * 1000);
568 Util::Sleep(100);
559569 maxWaitMSec -= 100;
560570 }
561571
613623 }
614624 while (m_frontend->IsRunning())
615625 {
616 usleep(50 * 1000);
626 Util::Sleep(50);
617627 }
618628 debug("Frontend stopped");
619629 }
693703 m_serviceCoordinator->Stop();
694704 }
695705 }
696 usleep(100 * 1000);
706 Util::Sleep(100);
707
708 if (m_options->GetServerMode() && !m_stopped)
709 {
710 // wait for stop signal
711 Guard guard(m_waitMutex);
712 m_waitCond.Wait(m_waitMutex, [&]{ return m_stopped; });
713 }
697714 }
698715
699716 debug("Main program loop terminated");
896913 #endif
897914 }
898915 }
916
917 // trigger stop/reload signal
918 Guard guard(m_waitMutex);
919 m_stopped = true;
920 m_waitCond.NotifyAll();
899921 }
900922
901923 void NZBGet::PrintOptions()
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
114114
115115 // WINDOWS INCLUDES
116116
117 // Using "WIN32_LEAN_AND_MEAN" to disable including on many unneeded headers
117 // Using "WIN32_LEAN_AND_MEAN" to disable including of many unneeded headers
118118 #define WIN32_LEAN_AND_MEAN
119119 #define NOMINMAX
120120
208208 #include <stdarg.h>
209209 #include <time.h>
210210 #include <ctype.h>
211 #include <inttypes.h>
211212
212213 #include <string>
213214 #include <vector>
221222 #include <fstream>
222223 #include <memory>
223224 #include <functional>
225 #include <thread>
226 #include <mutex>
227 #include <condition_variable>
228 #include <chrono>
224229
225230 // NOTE: do not include <iostream> in "nzbget.h". <iostream> contains objects requiring
226231 // intialization, causing every unit in nzbget to have initialization routine. This in particular
271276 #include <cassert>
272277 #ifdef HAVE_MEMORY_H
273278 # include <memory.h>
274 #endif
275 #ifdef HAVE_INTTYPES_H
276 # include <inttypes.h>
277279 #endif
278280 #endif /* NOT DISABLE_PARCHECK */
279281
301303 #define S_ISDIR(mode) __S_ISTYPE((mode), _S_IFDIR)
302304 #define S_ISREG(mode) __S_ISTYPE((mode), _S_IFREG)
303305 #define S_DIRMODE nullptr
304 #define usleep(usec) Sleep((usec) / 1000)
305306 #define socklen_t int
306307 #define SHUT_WR 0x01
307308 #define SHUT_RDWR 0x02
382383 typedef unsigned long long uint64;
383384 #endif
384385
386 #ifndef PRId64
387 #define PRId64 "lld"
388 #endif
389 #ifndef PRIi64
390 #define PRIi64 "lli"
391 #endif
392 #ifndef PRIu64
393 #define PRIu64 "llu"
394 #endif
395
385396 typedef unsigned char uchar;
386397
387398 // Assume little endian if byte order is not defined
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
33 * Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
4 * Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
2424 #include "Decoder.h"
2525 #include "Log.h"
2626 #include "Options.h"
27 #include "WorkState.h"
2728 #include "ServerPool.h"
2829 #include "StatMeter.h"
2930 #include "Util.h"
9697 while (!m_connection && !(IsStopped() || serverConfigGeneration != g_ServerPool->GetGeneration()))
9798 {
9899 m_connection = g_ServerPool->GetConnection(level, wantServer, &failedServers);
99 usleep(5 * 1000);
100 Util::Sleep(5);
100101 }
101102 SetLastUpdateTimeNow();
102103 SetStatus(adRunning);
103104
104 if (IsStopped() || ((g_Options->GetPauseDownload() || g_Options->GetQuotaReached()) && !force) ||
105 (g_Options->GetTempPauseDownload() && !m_fileInfo->GetExtraPriority()) ||
105 if (IsStopped() || ((g_WorkState->GetPauseDownload() || g_WorkState->GetQuotaReached()) && !force) ||
106 (g_WorkState->GetTempPauseDownload() && !m_fileInfo->GetExtraPriority()) ||
106107 serverConfigGeneration != g_ServerPool->GetGeneration())
107108 {
108109 status = adRetry;
193194 break;
194195 }
195196
196 if (IsStopped() || ((g_Options->GetPauseDownload() || g_Options->GetQuotaReached()) && !force) ||
197 (g_Options->GetTempPauseDownload() && !m_fileInfo->GetExtraPriority()) ||
197 if (IsStopped() || ((g_WorkState->GetPauseDownload() || g_WorkState->GetQuotaReached()) && !force) ||
198 (g_WorkState->GetTempPauseDownload() && !m_fileInfo->GetExtraPriority()) ||
198199 serverConfigGeneration != g_ServerPool->GetGeneration())
199200 {
200201 status = adRetry;
338339 while (!IsStopped() && !m_decoder.GetEof())
339340 {
340341 // throttle the bandwidth
341 while (!IsStopped() && (g_Options->GetDownloadRate() > 0.0f) &&
342 (g_StatMeter->CalcCurrentDownloadSpeed() > g_Options->GetDownloadRate() ||
343 g_StatMeter->CalcMomentaryDownloadSpeed() > g_Options->GetDownloadRate()))
342 while (!IsStopped() && (g_WorkState->GetSpeedLimit() > 0.0f) &&
343 (g_StatMeter->CalcCurrentDownloadSpeed() > g_WorkState->GetSpeedLimit() ||
344 g_StatMeter->CalcMomentaryDownloadSpeed() > g_WorkState->GetSpeedLimit()))
344345 {
345346 SetLastUpdateTimeNow();
346 usleep(10 * 1000);
347 Util::Sleep(10);
347348 }
348349
349350 char* buffer;
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2014-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2014-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
112112
113113 while (!m_articleData.GetData() && g_ArticleCache->GetFlushing())
114114 {
115 usleep(5 * 1000);
115 Util::Sleep(5);
116116 m_articleData = g_ArticleCache->Alloc(m_articleSize);
117117 }
118118
125125 if (!m_articleData.GetData())
126126 {
127127 bool directWrite = (g_Options->GetDirectWrite() || m_fileInfo->GetForceDirectWrite()) && m_format == Decoder::efYenc;
128 const char* filename = directWrite ? m_outputFilename : m_tempFilename;
129 if (!m_outFile.Open(filename, directWrite ? DiskFile::omReadWrite : DiskFile::omWrite))
128 const char* outFilename = directWrite ? m_outputFilename : m_tempFilename;
129 if (!m_outFile.Open(outFilename, directWrite ? DiskFile::omReadWrite : DiskFile::omWrite))
130130 {
131131 m_fileInfo->GetNzbInfo()->PrintMessage(Message::mkError,
132 "Could not %s file %s: %s", directWrite ? "open" : "create", filename,
132 "Could not %s file %s: %s", directWrite ? "open" : "create", outFilename,
133133 *FileSystem::GetLastErrorMessage());
134134 return false;
135135 }
774774 {
775775 g_DiskState->WriteCacheFlag();
776776 }
777 if (!m_allocated)
778 {
779 // Resume Run(), the notification arrives later, after releasing m_allocMutex
780 m_allocCond.NotifyAll();
781 }
777782 m_allocated += size;
778783 }
779784 }
820825 bool justFlushed = false;
821826 while (!IsStopped() || m_allocated > 0)
822827 {
823 if ((justFlushed || resetCounter >= 1000 || IsStopped() ||
828 if ((justFlushed || resetCounter >= 1000 || IsStopped() ||
824829 (g_Options->GetDirectWrite() && m_allocated >= fillThreshold)) &&
825830 m_allocated > 0)
826831 {
827832 justFlushed = CheckFlush(m_allocated >= fillThreshold);
828833 resetCounter = 0;
829834 }
835 else if (!m_allocated)
836 {
837 Guard guard(m_allocMutex);
838 m_allocCond.Wait(m_allocMutex, [&]{ return IsStopped() || m_allocated > 0; });
839 resetCounter = 0;
840 }
830841 else
831842 {
832 usleep(5 * 1000);
843 Util::Sleep(5);
833844 resetCounter += 5;
834845 }
835846 }
847 }
848
849 void ArticleCache::Stop()
850 {
851 Thread::Stop();
852
853 // Resume Run() to exit it
854 Guard guard(m_allocMutex);
855 m_allocCond.NotifyAll();
836856 }
837857
838858 bool ArticleCache::CheckFlush(bool flushEverything)
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2014-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2014-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
9494 };
9595
9696 virtual void Run();
97 virtual void Stop();
9798 CachedSegmentData Alloc(int size);
9899 bool Realloc(CachedSegmentData* segment, int newSize);
99100 void Free(CachedSegmentData* segment);
110111 Mutex m_flushMutex;
111112 Mutex m_contentMutex;
112113 FileInfo* m_fileInfo = nullptr;
114 ConditionVar m_allocCond;
113115
114116 bool CheckFlush(bool flushEverything);
115117 };
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
373373 }
374374 else
375375 {
376 if (effLen >= 1)
377 {
378 *optr++ = UU_DECODE_CHAR (iptr[0]) << 2 | UU_DECODE_CHAR (iptr[1]) >> 4;
379 }
376 *optr++ = UU_DECODE_CHAR (iptr[0]) << 2 | UU_DECODE_CHAR (iptr[1]) >> 4;
380377 if (effLen >= 2)
381378 {
382379 *optr++ = UU_DECODE_CHAR (iptr[1]) << 4 | UU_DECODE_CHAR (iptr[2]) >> 2;
2424 NewsServer::NewsServer(int id, bool active, const char* name, const char* host, int port, int ipVersion,
2525 const char* user, const char* pass, bool joinGroup, bool tls, const char* cipher,
2626 int maxConnections, int retention, int level, int group, bool optional) :
27 m_id(id), m_active(active), m_port(port), m_ipVersion(ipVersion), m_level(level), m_normLevel(level),
28 m_group(group), m_maxConnections(maxConnections), m_joinGroup(joinGroup), m_tls(tls),
29 m_name(name), m_host(host ? host : ""), m_user(user ? user : ""), m_password(pass ? pass : ""),
30 m_cipher(cipher ? cipher : ""), m_retention(retention), m_optional(optional)
27 m_id(id), m_active(active), m_name(name), m_host(host ? host : ""), m_port(port), m_ipVersion(ipVersion),
28 m_user(user ? user : ""), m_password(pass ? pass : ""), m_joinGroup(joinGroup), m_tls(tls),
29 m_cipher(cipher ? cipher : ""), m_maxConnections(maxConnections), m_retention(retention),
30 m_level(level), m_normLevel(level), m_group(group), m_optional(optional)
3131 {
3232 if (m_name.Empty())
3333 {
6464 int m_stateId = 0;
6565 bool m_active;
6666 CString m_name;
67 int m_group;
6867 CString m_host;
6968 int m_port;
7069 int m_ipVersion;
7170 CString m_user;
7271 CString m_password;
73 int m_maxConnections;
74 int m_level;
75 int m_normLevel;
7672 bool m_joinGroup;
7773 bool m_tls;
7874 CString m_cipher;
75 int m_maxConnections;
7976 int m_retention;
77 int m_level;
78 int m_normLevel;
79 int m_group;
8080 bool m_optional = false;
8181 time_t m_blockTime = 0;
8282 };
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2014-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2014-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2020 #include "nzbget.h"
2121 #include "StatMeter.h"
2222 #include "Options.h"
23 #include "WorkState.h"
2324 #include "ServerPool.h"
2425 #include "DiskState.h"
2526 #include "Util.h"
5657 void ServerVolume::AddData(int bytes)
5758 {
5859 time_t curTime = Util::CurrentTime();
59 time_t locCurTime = curTime + g_Options->GetLocalTimeOffset();
60 time_t locDataTime = m_dataTime + g_Options->GetLocalTimeOffset();
60 time_t locCurTime = curTime + g_WorkState->GetLocalTimeOffset();
61 time_t locDataTime = m_dataTime + g_WorkState->GetLocalTimeOffset();
6162
6263 int lastMinSlot = m_minSlot;
6364 int lastHourSlot = m_hourSlot;
137138
138139 for (int i = 0; i < 60; i++)
139140 {
140 msg.AppendFmt("[%i]=%lli ", i, m_bytesPerSeconds[i]);
141 msg.AppendFmt("[%i]=%" PRIi64 " ", i, m_bytesPerSeconds[i]);
141142 }
142143 info("Secs: %s", *msg);
143144
144145 msg.Clear();
145146 for (int i = 0; i < 60; i++)
146147 {
147 msg.AppendFmt("[%i]=%lli ", i, m_bytesPerMinutes[i]);
148 msg.AppendFmt("[%i]=%" PRIi64 " ", i, m_bytesPerMinutes[i]);
148149 }
149150 info("Mins: %s", *msg);
150151
151152 msg.Clear();
152153 for (int i = 0; i < 24; i++)
153154 {
154 msg.AppendFmt("[%i]=%lli ", i, m_bytesPerHours[i]);
155 msg.AppendFmt("[%i]=%" PRIi64 " ", i, m_bytesPerHours[i]);
155156 }
156157 info("Hours: %s", *msg);
157158
158159 msg.Clear();
159160 for (int i = 0; i < (int)m_bytesPerDays.size(); i++)
160161 {
161 msg.AppendFmt("[%i]=%lli ", m_firstDay + i, m_bytesPerDays[i]);
162 msg.AppendFmt("[%i]=%" PRIi64 " ", m_firstDay + i, m_bytesPerDays[i]);
162163 }
163164 info("Days: %s", *msg);
164165 }
187188 tmSplittedTime.tm_isdst = -1;
188189 time_t locTime = mktime(&tmSplittedTime);
189190 time_t localTimeDelta = utcTime - locTime;
190 g_Options->SetLocalTimeOffset((int)localTimeDelta + g_Options->GetTimeCorrection());
191 g_WorkState->SetLocalTimeOffset((int)localTimeDelta + g_Options->GetTimeCorrection());
191192 m_lastTimeOffset = utcTime;
192193
193 debug("UTC delta: %i (%i+%i)", g_Options->GetLocalTimeOffset(), (int)localTimeDelta, g_Options->GetTimeCorrection());
194 debug("UTC delta: %i (%i+%i)", g_WorkState->GetLocalTimeOffset(), (int)localTimeDelta, g_Options->GetTimeCorrection());
194195 }
195196
196197 /*
379380 int timeDiff = (int)Util::CurrentTime() - m_speedStartTime * SPEEDMETER_SLOTSIZE;
380381 info(" Speed: %i", speed);
381382 info(" SpeedStartTime: %i", m_speedStartTime);
382 info(" SpeedTotalBytes: %lli", m_speedTotalBytes);
383 info(" SpeedTotalBytes: %" PRIi64, m_speedTotalBytes);
383384 info(" SpeedBytesIndex: %i", m_speedBytesIndex);
384 info(" AllBytes: %lli", m_allBytes);
385 info(" AllBytes: %" PRIi64, m_allBytes);
385386 info(" Time: %i", (int)Util::CurrentTime());
386387 info(" TimeDiff: %i", timeDiff);
387388 for (int i=0; i < SPEEDMETER_SLOTS; i++)
445446
446447 for (ServerVolume& serverVolume : m_serverVolumes)
447448 {
448 serverVolume.CalcSlots(serverVolume.GetDataTime() + g_Options->GetLocalTimeOffset());
449 serverVolume.CalcSlots(serverVolume.GetDataTime() + g_WorkState->GetLocalTimeOffset());
449450 }
450451
451452 return ok;
464465 bool monthlyQuotaReached = g_Options->GetMonthlyQuota() > 0 && monthBytes >= (int64)g_Options->GetMonthlyQuota() * 1024 * 1024;
465466 bool dailyQuotaReached = g_Options->GetDailyQuota() > 0 && dayBytes >= (int64)g_Options->GetDailyQuota() * 1024 * 1024;
466467
467 if (monthlyQuotaReached && !g_Options->GetQuotaReached())
468 if (monthlyQuotaReached && !g_WorkState->GetQuotaReached())
468469 {
469470 warn("Monthly quota reached at %s", *Util::FormatSize(monthBytes));
470471 }
471 else if (dailyQuotaReached && !g_Options->GetQuotaReached())
472 else if (dailyQuotaReached && !g_WorkState->GetQuotaReached())
472473 {
473474 warn("Daily quota reached at %s", *Util::FormatSize(dayBytes));
474475 }
475 else if (!monthlyQuotaReached && !dailyQuotaReached && g_Options->GetQuotaReached())
476 else if (!monthlyQuotaReached && !dailyQuotaReached && g_WorkState->GetQuotaReached())
476477 {
477478 info("Quota lifted");
478479 }
479480
480 g_Options->SetQuotaReached(monthlyQuotaReached || dailyQuotaReached);
481 g_WorkState->SetQuotaReached(monthlyQuotaReached || dailyQuotaReached);
481482 }
482483
483484 void StatMeter::CalcQuotaUsage(int64& monthBytes, int64& dayBytes)
486487
487488 ServerVolume totalVolume = m_serverVolumes[0];
488489
489 time_t locTime = Util::CurrentTime() + g_Options->GetLocalTimeOffset();
490 time_t locTime = Util::CurrentTime() + g_WorkState->GetLocalTimeOffset();
490491 int daySlot = (int)(locTime / 86400) - totalVolume.GetFirstDay();
491492
492493 dayBytes = 0;
514515 {
515516 int elapsedDays;
516517
517 time_t locCurTime = Util::CurrentTime() + g_Options->GetLocalTimeOffset();
518 time_t locCurTime = Util::CurrentTime() + g_WorkState->GetLocalTimeOffset();
518519 tm dayparts;
519520 gmtime_r(&locCurTime, &dayparts);
520521
8383 void AddServerData(int bytes, int serverId);
8484 void CalcTotalStat(int* upTimeSec, int* dnTimeSec, int64* allBytes, bool* standBy);
8585 void CalcQuotaUsage(int64& monthBytes, int64& dayBytes);
86 bool GetStandBy() { return m_standBy; }
8786 void IntervalCheck();
8887 void EnterLeaveStandBy(bool enter);
8988 GuardedServerVolumes GuardServerVolumes();
3333 while (!IsStopped())
3434 {
3535 Update();
36 usleep(100 * 1000);
36 Util::Sleep(100);
3737 }
3838 // Printing the last messages
3939 Update();
123123 }
124124
125125 info("Press Ctrl+C to quit");
126 while (getchar()) usleep(1000*200);
126 while (getchar()) Util::Sleep(200);
127127
128128 for (std::unique_ptr<NntpServer>& serv: instances)
129129 {
139139 {
140140 hasRunning |= serv->IsRunning();
141141 }
142 usleep(50 * 1000);
142 Util::Sleep(50);
143143 } while (hasRunning);
144144
145145 return 0;
3939 int m_id;
4040 int m_serverId;
4141 std::unique_ptr<Connection> m_connection;
42 int m_latency;
43 int m_speed;
4442 const char* m_dataDir;
4543 const char* m_cacheDir;
4644 const char* m_secureCert;
4745 const char* m_secureKey;
46 int m_latency;
47 int m_speed;
4848 const char* m_messageid;
4949 CString m_filename;
5050 int m_part;
102102 break;
103103 }
104104 m_connection.reset();
105 usleep(500 * 1000);
105 Util::Sleep(500);
106106 continue;
107107 }
108108
215215
216216 if (m_latency)
217217 {
218 usleep(1000 * m_latency);
218 Util::Sleep(m_latency);
219219 }
220220
221221 bool ok = false;
267267
268268 void NntpProcessor::SendSegment()
269269 {
270 detail("[%i] Sending segment %s (%i=%lli:%i)", m_id, *m_filename, m_part, (long long)m_offset, m_size);
270 detail("[%i] Sending segment %s (%i=%" PRIi64 ":%i)", m_id, *m_filename, m_part, m_offset, m_size);
271271
272272 if (m_speed > 0)
273273 {
274 m_start = Util::GetCurrentTicks();
274 m_start = Util::CurrentTicks();
275275 }
276276
277277 BString<1024> fullFilename("%s/%s", m_dataDir, *m_filename);
278278 BString<1024> cacheFileDir("%s/%s", m_cacheDir, *m_filename);
279 BString<1024> cacheFileName("%i=%lli-%i", m_part, (long long)m_offset, m_size);
279 BString<1024> cacheFileName("%i=%" PRIi64 "-%i", m_part, m_offset, m_size);
280280 BString<1024> cacheFullFilename("%s/%s", *cacheFileDir, *cacheFileName);
281281 BString<1024> cacheKey("%s/%s", *m_filename, *cacheFileName);
282282
382382 return;
383383 }
384384
385 int64 expectedTime = (int64)1000 * size / (m_speed * 1024) - (Util::GetCurrentTicks() - m_start) / 1000;
385 int64 expectedTime = (int64)1000 * size / (m_speed * 1024) - (Util::CurrentTicks() - m_start) / 1000;
386386
387387 int chunkNum = 21;
388388 int chunkSize = size;
406406 }
407407
408408 m_connection->Send(buffer + sent, len);
409 int64 now = Util::GetCurrentTicks();
409 int64 now = Util::CurrentTicks();
410410 if (now + pause * 1000 < m_start + expectedTime * 1000)
411411 {
412 usleep(pause * 1000);
412 Util::Sleep(pause);
413413 }
414414 sent += len;
415415 }
6464 int m_id;
6565 CString m_host;
6666 int m_port;
67 int m_latency;
68 int m_speed;
69 CString m_dataDir;
70 CString m_cacheDir;
7167 CString m_secureCert;
7268 CString m_secureKey;
7369 std::unique_ptr<Connection> m_connection;
70 CString m_dataDir;
71 CString m_cacheDir;
72 int m_latency;
73 int m_speed;
7474 NntpCache* m_cache;
7575 };
7676
117117 for (int segno = 1; segno <= segmentCount; segno++)
118118 {
119119 int segSize = (int)(segOffset + m_segmentSize < fileSize ? m_segmentSize : fileSize - segOffset);
120 outfile.Print("<segment bytes=\"%i\" number=\"%i\">%s%s%s?%i=%lli:%i</segment>\n",
121 m_segmentSize, segno,
120 outfile.Print("<segment bytes=\"%i\" number=\"%i\">%s%s%s?%i=%" PRIi64 ":%i</segment>\n",
121 m_segmentSize, segno,
122122 relativePath ? relativePath : "",
123123 relativePath ? "/" : "",
124 FileSystem::BaseFileName(filename), segno, (long long)segOffset, (int)segSize);
124 FileSystem::BaseFileName(filename), segno, segOffset, segSize);
125125 segOffset += segSize;
126126 }
127127
6363 StringBuilder outbuf;
6464 outbuf.Reserve(std::max(2048, std::min((int)(m_size * 1.1), 16 * 1024 * 1024)));
6565
66 outbuf.Append(CString::FormatStr("=ybegin part=%i line=128 size=%lli name=%s\r\n", m_part, (long long)m_fileSize, FileSystem::BaseFileName(m_filename)));
67 outbuf.Append(CString::FormatStr("=ypart begin=%lli end=%lli\r\n", (long long)(m_offset + 1), (long long)(m_offset + m_size)));
66 outbuf.Append(CString::FormatStr("=ybegin part=%i line=128 size=%" PRIi64 " name=%s\r\n", m_part, m_fileSize, FileSystem::BaseFileName(m_filename)));
67 outbuf.Append(CString::FormatStr("=ypart begin=%" PRIi64 " end=%" PRIi64 "\r\n", m_offset + 1, m_offset + m_size));
6868
6969 Crc32 crc;
7070 CharBuffer inbuf(std::min(m_size, 16 * 1024 * 1024));
104104 {
105105 break;
106106 }
107 usleep(100 * 1000);
107 Util::Sleep(100);
108108 }
109109 }
110110
7979 curTime + timeoutSec > Util::CurrentTime() &&
8080 curTime >= Util::CurrentTime()) // in a case clock was changed
8181 {
82 usleep(200 * 1000);
82 Util::Sleep(200);
8383 }
8484
8585 if (unrar.IsRunning())
9090 // wait until terminated or killed
9191 while (unrar.IsRunning())
9292 {
93 usleep(200 * 1000);
93 Util::Sleep(200);
9494 }
9595
9696 *maxSize = unrar.m_maxSize;
160160 char filename[1024];
161161 FindLargestFile(m_destDir, filename, sizeof(filename), &m_maxSize, &m_compressed);
162162 bool sizeOK = SizeDiffOK(m_maxSize, m_expectedSize, 20);
163 PrintMessage(Message::mkDetail, "Found main file %s with size %lli bytes%s",
163 PrintMessage(Message::mkDetail, "Found main file %s with size %" PRIi64 " bytes%s",
164164 filename, m_maxSize, sizeOK ? "" : ", size mismatch");
165165 return sizeOK;
166166 }
172172 char filename[1024];
173173 FindLargestFile(dupeDir, filename, sizeof(filename), &dupeMaxSize, &dupeCompressed);
174174 bool ok = dupeMaxSize == m_maxSize && dupeCompressed == m_compressed;
175 PrintMessage(Message::mkDetail, "Found main file %s with size %lli bytes%s",
175 PrintMessage(Message::mkDetail, "Found main file %s with size %" PRIi64 " bytes%s",
176176 filename, m_maxSize, ok ? "" : ", size mismatch");
177177 return ok;
178178 }
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
4141 "internal error occurred",
4242 "out of memory" };
4343
44 // Sleep interval for synchronisation (microseconds)
45 #ifdef WIN32
46 // Windows doesn't allow sleep intervals less than one millisecond
47 #define SYNC_SLEEP_INTERVAL 1000
48 #else
49 #define SYNC_SLEEP_INTERVAL 100
50 #endif
51
5244 class RepairThread;
5345
5446 class Repairer : public Par2::Par2Repairer, public ParChecker::AbstractRepairer
7365 private:
7466 typedef vector<Thread*> Threads;
7567
68 ParChecker* m_owner;
7669 Par2::CommandLine commandLine;
77 ParChecker* m_owner;
7870 Threads m_threads;
7971 bool m_parallel;
8072 Mutex progresslock;
8274 virtual void BeginRepair();
8375 virtual void EndRepair();
8476 void RepairBlock(Par2::u32 inputindex, Par2::u32 outputindex, size_t blocklength);
77 static void SyncSleep();
8578
8679 friend class ParChecker;
8780 friend class RepairThread;
252245
253246 if (!jobAdded)
254247 {
255 usleep(SYNC_SLEEP_INTERVAL);
248 SyncSleep();
256249 }
257250 }
258251
267260 if (repairThread->IsWorking())
268261 {
269262 working = true;
270 usleep(SYNC_SLEEP_INTERVAL);
263 SyncSleep();
271264 break;
272265 }
273266 }
304297 }
305298 }
306299
300 // Sleep for synchronisation
301 void Repairer::SyncSleep()
302 {
303 #ifdef WIN32
304 // Windows doesn't allow sleep intervals less than one millisecond
305 Sleep(1);
306 #else
307 usleep(100);
308 #endif
309 }
310
307311 void RepairThread::Run()
308312 {
309313 while (!IsStopped())
315319 }
316320 else
317321 {
318 usleep(SYNC_SLEEP_INTERVAL);
322 Repairer::SyncSleep();
319323 }
320324 }
321325 }
689693 Guard guard(m_queuedParFilesMutex);
690694 queuedParFilesChanged = m_queuedParFilesChanged;
691695 }
692 usleep(100 * 1000);
696 Util::Sleep(100);
693697 }
694698 }
695699 }
755759 Guard guard(m_queuedParFilesMutex);
756760 queuedParFilesChanged = m_queuedParFilesChanged;
757761 }
758 usleep(100 * 1000);
762 Util::Sleep(100);
759763 }
760764 }
761765 }
821825 DirBrowser dir(m_destDir);
822826 while (const char* filename = dir.Next())
823827 {
824 if (IsParredFile(filename) && !IsProcessedFile(filename))
828 if (!IsParredFile(filename) && !IsProcessedFile(filename))
825829 {
826830 for (Par2::Par2RepairerSourceFile *sourcefile : GetRepairer()->sourcefiles)
827831 {
828832 std::string target = sourcefile->TargetFileName();
829 const char* filename2 = target.c_str();
830 const char* basename2 = FileSystem::BaseFileName(filename2);
831 int baseLen = strlen(basename2);
832
833 if (!strncasecmp(filename, basename2, baseLen))
833 const char* current = FileSystem::BaseFileName(target.c_str());
834
835 // if file was renamed by par-renamer we also check the original filename
836 const char* original = FindFileOrigname(current);
837
838 if (MaybeSplittedFragement(filename, current) ||
839 (!Util::EmptyStr(original) && strcasecmp(original, current) &&
840 MaybeSplittedFragement(filename, original)))
834841 {
835 const char* p = filename + baseLen;
836 if (*p == '.')
837 {
838 for (p++; *p && strchr("0123456789", *p); p++) ;
839 if (!*p)
840 {
841 debug("Found splitted fragment %s", filename);
842 BString<1024> fullfilename("%s%c%s", *m_destDir, PATH_SEPARATOR, filename);
843 Par2::CommandLine::ExtraFile extrafile(*fullfilename, FileSystem::FileSize(fullfilename));
844 extrafiles.push_back(extrafile);
845 }
846 }
842 detail("Found splitted fragment %s", filename);
843 BString<1024> fullfilename("%s%c%s", *m_destDir, PATH_SEPARATOR, filename);
844 Par2::CommandLine::ExtraFile extrafile(*fullfilename, FileSystem::FileSize(fullfilename));
845 extrafiles.push_back(extrafile);
846 break;
847847 }
848848 }
849849 }
862862 }
863863
864864 return fragmentsAdded;
865 }
866
867 bool ParChecker::MaybeSplittedFragement(const char* filename1, const char* filename2)
868 {
869 // check if name is same but the first name has additional numerical extension
870 int len = strlen(filename2);
871 if (!strncasecmp(filename1, filename2, len))
872 {
873 const char* p = filename1 + len;
874 if (*p == '.')
875 {
876 for (p++; *p && strchr("0123456789", *p); p++) ;
877 if (!*p)
878 {
879 return true;
880 }
881 }
882 }
883
884 // check if same name (without extension) and extensions are numerical and exactly 3 characters long
885 const char* ext1 = strrchr(filename1, '.');
886 const char* ext2 = strrchr(filename2, '.');
887 if (ext1 && ext2 && (strlen(ext1) == 4) && (strlen(ext2) == 4) &&
888 !strncasecmp(filename1, filename2, ext1 - filename1))
889 {
890 for (ext1++; *ext1 && strchr("0123456789", *ext1); ext1++) ;
891 for (ext2++; *ext2 && strchr("0123456789", *ext2); ext2++) ;
892 if (!*ext1 && !*ext2)
893 {
894 return true;
895 }
896 }
897
898 return false;
865899 }
866900
867901 bool ParChecker::AddMissingFiles()
13941428 int64 blockEnd = blockStart + blocksize < fileSize - 1 ? blockStart + blocksize : fileSize - 1;
13951429 bool blockOK = false;
13961430 bool blockEndFound = false;
1397 Par2::u64 curOffset = 0;
1431 int64 curOffset = 0;
13981432 for (Segment& segment : segments)
13991433 {
14001434 if (!blockOK && segment.GetSuccess() && segment.GetOffset() <= blockStart &&
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
131131 virtual void RegisterParredFile(const char* filename) {}
132132 virtual bool IsParredFile(const char* filename) { return false; }
133133 virtual EFileStatus FindFileCrc(const char* filename, uint32* crc, SegmentList* segments) { return fsUnknown; }
134 virtual const char* FindFileOrigname(const char* filename) { return nullptr; }
134135 virtual void RequestDupeSources(DupeSourceList* dupeSourceList) {}
135136 virtual void StatDupeSources(DupeSourceList* dupeSourceList) {}
136137 EStage GetStage() { return m_stage; }
220221 bool DumbCalcFileRangeCrc(DiskFile& file, int64 start, int64 end, uint32* downloadCrc);
221222 void CheckEmptyFiles();
222223 CString GetPacketCreator();
224 bool MaybeSplittedFragement(const char* filename1, const char* filename2);
223225
224226 friend class Repairer;
225227 };
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2018 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2020 #include "nzbget.h"
2121 #include "PrePostProcessor.h"
2222 #include "Options.h"
23 #include "WorkState.h"
2324 #include "Log.h"
2425 #include "HistoryCoordinator.h"
2526 #include "DupeCoordinator.h"
4849
4950 while (!DownloadQueue::IsLoaded())
5051 {
51 usleep(20 * 1000);
52 Util::Sleep(20);
5253 }
5354
5455 if (g_Options->GetServerMode())
5859
5960 while (!IsStopped())
6061 {
61 if (!g_Options->GetTempPausePostprocess() && m_queuedJobs)
62 if (g_WorkState->GetTempPausePostprocess())
63 {
64 // Postprocess is paused: just wait and loop
65 Util::Sleep(200);
66 continue;
67 }
68
69 if (m_queuedJobs)
6270 {
6371 // check post-queue every 200 msec
6472 CheckPostQueue();
65 }
66
67 usleep(200 * 1000);
73 Util::Sleep(200);
74 }
75 else
76 {
77 // Wait until we get the stop signal or more jobs in the queue
78 Guard guard(m_waitMutex);
79 m_waitCond.Wait(m_waitMutex, [&]{ return m_queuedJobs || IsStopped(); });
80 }
6881 }
6982
7083 WaitJobs();
88101 }
89102 }
90103 CheckPostQueue();
91 usleep(200 * 1000);
104 Util::Sleep(200);
92105 }
93106
94107 // kill remaining post-processing jobs; not safe but we can't wait any longer
123136 break;
124137 }
125138 }
126 usleep(200 * 1000);
139 Util::Sleep(200);
127140 }
128141
129142 // disconnect remaining direct unpack jobs
141154 void PrePostProcessor::Stop()
142155 {
143156 Thread::Stop();
144 GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
145
146 for (NzbInfo* postJob : m_activeJobs)
147 {
148 if (postJob->GetPostInfo() && postJob->GetPostInfo()->GetPostThread())
149 {
150 postJob->GetPostInfo()->GetPostThread()->Stop();
151 }
152 }
153
154 for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
155 {
156 if (nzbInfo->GetUnpackThread())
157 {
158 ((DirectUnpack*)nzbInfo->GetUnpackThread())->Stop(downloadQueue, nzbInfo);
159 }
160 }
157
158 {
159 GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
160
161 for (NzbInfo* postJob : m_activeJobs)
162 {
163 if (postJob->GetPostInfo() && postJob->GetPostInfo()->GetPostThread())
164 {
165 postJob->GetPostInfo()->GetPostThread()->Stop();
166 }
167 }
168
169 for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
170 {
171 if (nzbInfo->GetUnpackThread())
172 {
173 ((DirectUnpack*)nzbInfo->GetUnpackThread())->Stop(downloadQueue, nzbInfo);
174 }
175 }
176 }
177
178 // Resume Run() to exit it
179 Guard guard(m_waitMutex);
180 m_waitCond.NotifyAll();
161181 }
162182
163183 /**
323343 nzbInfo->PrintMessage(Message::mkInfo, "Queueing %s for post-processing", nzbInfo->GetName());
324344
325345 nzbInfo->EnterPostProcess();
326 m_queuedJobs++;
327346
328347 if (nzbInfo->GetParStatus() == NzbInfo::psNone &&
329348 g_Options->GetParCheck() != Options::pcAlways &&
339358 ((DirectUnpack*)nzbInfo->GetUnpackThread())->NzbDownloaded(downloadQueue, nzbInfo);
340359 }
341360
342 downloadQueue->Save();
361 nzbInfo->SetChanged(true);
362 downloadQueue->SaveChanged();
363
364 // We have more jobs in the queue, notify Run()
365 Guard guard(m_waitMutex);
366 m_queuedJobs++;
367 m_waitCond.NotifyAll();
343368 }
344369 else
345370 {
585610 !g_QueueScriptCoordinator->HasJob(nzbInfo1->GetId(), nullptr) &&
586611 nzbInfo1->GetDirectUnpackStatus() != NzbInfo::nsRunning &&
587612 (!nzbInfo || nzbInfo1->GetPriority() > nzbInfo->GetPriority()) &&
588 (!g_Options->GetPausePostProcess() || nzbInfo1->GetForcePriority()) &&
613 (!g_WorkState->GetPausePostProcess() || nzbInfo1->GetForcePriority()) &&
589614 (allowPar || !nzbInfo1->GetPostInfo()->GetNeedParCheck()) &&
590615 (std::find(m_activeJobs.begin(), m_activeJobs.end(), nzbInfo1) == m_activeJobs.end()) &&
591616 nzbInfo1->IsDownloadCompleted(true))
619644
620645 PostInfo* postInfo = postJob->GetPostInfo();
621646 if (postInfo->GetStage() == PostInfo::ptQueued &&
622 (!g_Options->GetPausePostProcess() || postInfo->GetNzbInfo()->GetForcePriority()))
647 (!g_WorkState->GetPausePostProcess() || postInfo->GetNzbInfo()->GetForcePriority()))
623648 {
624649 StartJob(downloadQueue, postInfo, allowPar);
625650 CheckRequestPar(downloadQueue);
831856 NzbCompleted(downloadQueue, nzbInfo, false);
832857 }
833858
859 Guard guard(m_waitMutex);
834860 m_queuedJobs--;
835861 }
836862
866892 }
867893 }
868894
869 if (needPause && !g_Options->GetTempPauseDownload())
895 if (needPause && !g_WorkState->GetTempPauseDownload())
870896 {
871897 info("Pausing download before post-processing");
872898 }
873 else if (!needPause && g_Options->GetTempPauseDownload())
899 else if (!needPause && g_WorkState->GetTempPauseDownload())
874900 {
875901 info("Unpausing download after post-processing");
876902 }
877903
878 g_Options->SetTempPauseDownload(needPause);
904 g_WorkState->SetTempPauseDownload(needPause);
879905 }
880906
881907 bool PrePostProcessor::EditList(DownloadQueue* downloadQueue, IdList* idList,
4343 private:
4444 int m_queuedJobs = 0;
4545 RawNzbList m_activeJobs;
46 Mutex m_waitMutex;
47 ConditionVar m_waitCond;
4648
4749 void CheckPostQueue();
4850 void CheckRequestPar(DownloadQueue* downloadQueue);
172172 uint8 buf[256];
173173 while (size > 0)
174174 {
175 int64 len = size <= sizeof(buf) ? size : sizeof(buf);
175 int64 len = size <= (int64)sizeof(buf) ? size : (int64)sizeof(buf);
176176 if (!Read(file, block, buf, len)) return false;
177177 size -= len;
178178 }
288288 block.trailsize = blocksize - sizeof(buf) - 4;
289289 }
290290
291 #ifdef DEBUG
291292 static int num = 0;
292 debug("%i) %llu, %i, %i, %i, %u, %llu", ++num, (long long)block.crc, (int)block.type, (int)block.flags, (int)size, (int)block.addsize, (long long)block.trailsize);
293 debug("%i) %u, %i, %i, %i, %" PRIu64 ", %" PRIu64, ++num, block.crc, block.type, block.flags, size, block.addsize, block.trailsize);
294 #endif
293295
294296 return block;
295297 }
449451 if ((block.flags & RAR5_BLOCK_DATAAREA) && !ReadV(file, &block, &datasize)) return {0};
450452 block.trailsize += datasize;
451453
454 #ifdef DEBUG
452455 static int num = 0;
453 debug("%i) %llu, %i, %i, %i, %u, %llu", ++num, (long long)block.crc, (int)block.type, (int)block.flags, (int)size, (int)block.addsize, (long long)block.trailsize);
456 debug("%i) %u, %i, %i, %i, %" PRIu64 ", %" PRIu64, ++num, block.crc, block.type, block.flags, size, block.addsize, block.trailsize);
457 #endif
454458
455459 return block;
456460 }
526530 }
527531 }
528532
529 debug("%llu, %i, %s", (long long)block.trailsize, (int)namelen, (const char*)name);
533 debug("%" PRIu64 ", %" PRIu64 ", %s", block.trailsize, namelen, (const char*)name);
530534
531535 return true;
532536 }
533537
534538 void RarVolume::LogDebugInfo()
535539 {
540 #ifdef DEBUG
536541 debug("Volume: version:%i, multi:%i, vol-no:%i, new-naming:%i, has-next:%i, encrypted:%i, file-count:%i, [%s]",
537542 (int)m_version, (int)m_multiVolume, m_volumeNo, (int)m_newNaming, (int)m_hasNextVolume,
538543 (int)m_encrypted, (int)m_files.size(), FileSystem::BaseFileName(m_filename));
539544
540545 for (RarFile& file : m_files)
541546 {
542 debug(" time:%i, size:%lli, attr:%i, split-before:%i, split-after:%i, [%s]",
543 (int)file.m_time, (long long)file.m_size, (int)file.m_attr,
544 (int)file.m_splitBefore, (int)file.m_splitAfter, *file.m_filename);
545 }
547 debug(" time:%i, size:%" PRIi64 ", attr:%i, split-before:%i, split-after:%i, [%s]",
548 file.m_time, file.m_size, file.m_attr,
549 file.m_splitBefore, file.m_splitAfter, *file.m_filename);
550 }
551 #endif
546552 }
547553
548554 bool RarVolume::DecryptRar3Prepare(const uint8 salt[8])
272272 }),
273273 m_sets.end());
274274
275 #ifdef DEBUG
275276 // debug log
276277 for (RarVolumeSet& set : m_sets)
277278 {
281282 debug(" %s", FileSystem::BaseFileName(volume->GetFilename()));
282283 }
283284 }
285 #endif
284286 }
285287
286288 bool RarRenamer::SameArchiveName(const char* filename1, const char* filename2, bool newNaming)
345347 {
346348 setPartLen = partNo.Length();
347349 }
348 bool ok = atoi(partNo) == volume->GetVolumeNo() + 1 &&
350 bool ok = (uint32)atoi(partNo) == volume->GetVolumeNo() + 1 &&
349351 partNo.Length() == setPartLen &&
350352 !strncmp(setBasename, filename, regExPart.GetMatchStart(1));
351353 if (!ok)
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2016-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
212212 {
213213 if (!strcasecmp(completedFile.GetFilename(), oldFilename))
214214 {
215 if (Util::EmptyStr(completedFile.GetOrigname()))
216 {
217 completedFile.SetOrigname(completedFile.GetFilename());
218 }
215219 completedFile.SetFilename(newFilename);
216220 break;
217221 }
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2222 #include "DupeCoordinator.h"
2323 #include "ParParser.h"
2424 #include "Options.h"
25 #include "WorkState.h"
2526 #include "DiskState.h"
2627 #include "Log.h"
2728 #include "FileSystem.h"
111112 completedFile->GetStatus() == CompletedFile::cfPartial && segments->size() > 0 &&
112113 !m_postInfo->GetNzbInfo()->GetReprocess()? ParChecker::fsPartial :
113114 ParChecker::fsUnknown;
115 }
116
117 const char* RepairController::PostParChecker::FindFileOrigname(const char* filename)
118 {
119 for (CompletedFile& completedFile : m_postInfo->GetNzbInfo()->GetCompletedFiles())
120 {
121 if (!strcasecmp(completedFile.GetFilename(), filename))
122 {
123 return completedFile.GetOrigname();
124 }
125 }
126
127 return nullptr;
114128 }
115129
116130 void RepairController::PostParChecker::RequestDupeSources(DupeSourceList* dupeSourceList)
518532
519533 void RepairController::CheckPauseState(PostInfo* postInfo)
520534 {
521 if (g_Options->GetPausePostProcess() && !postInfo->GetNzbInfo()->GetForcePriority())
535 if (g_WorkState->GetPausePostProcess() && !postInfo->GetNzbInfo()->GetForcePriority())
522536 {
523537 time_t stageTime = postInfo->GetStageTime();
524538 time_t startTime = postInfo->GetStartTime();
527541 time_t waitTime = Util::CurrentTime();
528542
529543 // wait until Post-processor is unpaused
530 while (g_Options->GetPausePostProcess() && !postInfo->GetNzbInfo()->GetForcePriority() && !IsStopped())
531 {
532 usleep(50 * 1000);
544 while (g_WorkState->GetPausePostProcess() && !postInfo->GetNzbInfo()->GetForcePriority() && !IsStopped())
545 {
546 Util::Sleep(50);
533547
534548 // update time stamps
535549
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
6767 virtual void RegisterParredFile(const char* filename);
6868 virtual bool IsParredFile(const char* filename);
6969 virtual EFileStatus FindFileCrc(const char* filename, uint32* crc, SegmentList* segments);
70 virtual const char* FindFileOrigname(const char* filename);
7071 virtual void RequestDupeSources(DupeSourceList* dupeSourceList);
7172 virtual void StatDupeSources(DupeSourceList* dupeSourceList);
7273 private:
873873
874874 if (m_unpacker == upUnrar && !strncmp(text, "Unrar: Extracting from ", 23))
875875 {
876 #ifdef DEBUG
876877 const char *filename = text + 23;
877878 debug("Filename: %s", filename);
879 #endif
878880 SetProgressLabel(text + 7);
879881 }
880882
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2017-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
207207 fileInfo->SetPaused(false);
208208 }
209209
210 downloadQueue->Save();
210 nzbInfo->SetChanged(true);
211 downloadQueue->SaveChanged();
211212 }
212213
213214 if (fileInfo->GetParFile())
257258 // all first articles downloaded
258259 UnpausePars(nzbInfo);
259260 nzbInfo->SetWaitingPar(true);
260 downloadQueue->Save();
261 nzbInfo->SetChanged(true);
262 downloadQueue->SaveChanged();
261263 }
262264
263265 if (nzbInfo->GetWaitingPar() && !nzbInfo->GetLoadingPar())
375377 {
376378 nzbInfo->PrintMessage(Message::mkInfo, "Renaming in-progress file %s to %s",
377379 fileInfo->GetFilename(), *newName);
380 if (Util::EmptyStr(fileInfo->GetOrigname()))
381 {
382 fileInfo->SetOrigname(fileInfo->GetFilename());
383 }
378384 fileInfo->SetFilename(newName);
379385 fileInfo->SetFilenameConfirmed(true);
380386 renamedCount++;
381387 }
382388 else if (RenameCompletedFile(nzbInfo, fileInfo->GetFilename(), newName))
383389 {
390 if (Util::EmptyStr(fileInfo->GetOrigname()))
391 {
392 fileInfo->SetOrigname(fileInfo->GetFilename());
393 }
384394 fileInfo->SetFilename(newName);
385395 fileInfo->SetFilenameConfirmed(true);
386396 renamedCount++;
403413
404414 if (newName && RenameCompletedFile(nzbInfo, completedFile.GetFilename(), newName))
405415 {
416 if (Util::EmptyStr(completedFile.GetOrigname()))
417 {
418 completedFile.SetOrigname(completedFile.GetFilename());
419 }
406420 completedFile.SetFilename(newName);
407421 renamedCount++;
408422 }
522536 void RenameContentAnalyzer::Append(const void* buffer, int len)
523537 {
524538 #ifndef DISABLE_PARCHECK
525 if (m_dataSize < sizeof(m_signature))
539 if ((size_t)m_dataSize < sizeof(m_signature))
526540 {
527541 memcpy(m_signature + m_dataSize, buffer, std::min((size_t)len, sizeof(m_signature) - m_dataSize));
528542 }
529543
530 if (m_dataSize >= sizeof(m_signature) && (*(Par2::MAGIC*)m_signature) == Par2::packet_magic)
544 if ((size_t)m_dataSize >= sizeof(m_signature) && (*(Par2::MAGIC*)m_signature) == Par2::packet_magic)
531545 {
532546 m_parFile = true;
533547 m_parSetId = ((Par2::PACKET_HEADER*)m_signature)->setid.print().c_str();
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2018 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2626 #include "FileSystem.h"
2727
2828 static const char* FORMATVERSION_SIGNATURE = "nzbget diskstate file version ";
29 const int DISKSTATE_QUEUE_VERSION = 61;
30 const int DISKSTATE_FILE_VERSION = 5;
29 const int DISKSTATE_QUEUE_VERSION = 62;
30 const int DISKSTATE_FILE_VERSION = 6;
3131 const int DISKSTATE_STATS_VERSION = 3;
3232 const int DISKSTATE_FEEDS_VERSION = 3;
3333
4444 {
4545 va_list ap;
4646 va_start(ap, format);
47 CString str;
48 str.FormatV(format, ap);
47 BString<1024> str;
48 int len = str.FormatV(format, ap);
4949 va_end(ap);
50
51 int len = str.Length();
5250
5351 // replacing terminating <NULL> with <LF>
5452 str[len++] = '\n';
116114 private:
117115 BString<1024> m_destFilename;
118116 BString<1024> m_tempFilename;
117 int m_formatVersion;
119118 bool m_transactional;
120 int m_formatVersion;
121119 int m_fileVersion;
122120 StateDiskFile m_file;
123121
312310 }
313311 }
314312
313 // progress-file isn't needed after saving of full queue data
314 StateFile progressStateFile("progress", DISKSTATE_QUEUE_VERSION, true);
315 progressStateFile.Discard();
316
315317 return ok;
316318 }
317319
334336
335337 formatVersion = stateFile.GetFileVersion();
336338
337 if (formatVersion < 47)
339 if (formatVersion <= 0)
340 {
341 error("Failed to read queue: diskstate file is corrupted");
342 goto error;
343 }
344 else if (formatVersion < 47)
338345 {
339346 error("Failed to read queue and history data. Only queue and history from NZBGet v13 or newer can be converted by this NZBGet version. "
340347 "Old queue and history data still can be converted using NZBGet v16 as an intermediate version.");
350357 }
351358 }
352359
360 {
361 StateFile stateFile("progress", DISKSTATE_QUEUE_VERSION, true);
362 if (stateFile.FileExists())
363 {
364 StateDiskFile* infile = stateFile.BeginRead();
365 if (!infile)
366 {
367 return false;
368 }
369
370 if (stateFile.GetFileVersion() <= 0)
371 {
372 error("Failed to read queue: diskstate file is corrupted");
373 goto error;
374 }
375
376 if (!LoadProgress(downloadQueue->GetQueue(), servers, *infile, stateFile.GetFileVersion())) goto error;
377 }
378 }
379
353380 if (formatVersion == 0 || formatVersion >= 57)
354381 {
355382 StateFile stateFile("history", DISKSTATE_QUEUE_VERSION, true);
360387 {
361388 return false;
362389 }
390
391 if (stateFile.GetFileVersion() <= 0)
392 {
393 error("Failed to read queue: diskstate file is corrupted");
394 goto error;
395 }
396
363397 if (!LoadHistory(downloadQueue->GetHistory(), servers, *infile, stateFile.GetFileVersion())) goto error;
364398 }
365399 }
383417 FileInfo::ResetGenId(true);
384418
385419 CalcFileStats(downloadQueue, formatVersion);
420
421 return ok;
422 }
423
424 bool DiskState::SaveDownloadProgress(DownloadQueue* downloadQueue)
425 {
426 int count = 0;
427 for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
428 {
429 count += nzbInfo->GetChanged() ? 1 : 0;
430 }
431
432 debug("Saving queue progress to disk");
433
434 bool ok = true;
435
436 {
437 StateFile stateFile("progress", DISKSTATE_QUEUE_VERSION, true);
438 if (count > 0)
439 {
440 StateDiskFile* outfile = stateFile.BeginWrite();
441 if (!outfile)
442 {
443 return false;
444 }
445
446 SaveProgress(downloadQueue->GetQueue(), *outfile, count);
447
448 // now rename to dest file name
449 ok = stateFile.FinishWrite();
450 }
451 else
452 {
453 stateFile.Discard();
454 }
455 }
386456
387457 return ok;
388458 }
416486
417487 error:
418488 error("Error reading nzb list from disk");
489 return false;
490 }
491
492 void DiskState::SaveProgress(NzbList* queue, StateDiskFile& outfile, int changedCount)
493 {
494 debug("Saving nzb progress to disk");
495
496 outfile.PrintLine("%i", changedCount);
497 for (NzbInfo* nzbInfo : queue)
498 {
499 if (nzbInfo->GetChanged())
500 {
501 outfile.PrintLine("%i", nzbInfo->GetId());
502 SaveNzbInfo(nzbInfo, outfile);
503 }
504 }
505 }
506
507 bool DiskState::LoadProgress(NzbList* queue, Servers* servers, StateDiskFile& infile, int formatVersion)
508 {
509 debug("Loading nzb progress from disk");
510
511 // load nzb-infos
512 int size;
513 if (infile.ScanLine("%i", &size) != 1) goto error;
514 for (int i = 0; i < size; i++)
515 {
516 int id;
517 if (infile.ScanLine("%i", &id) != 1) goto error;
518
519 NzbInfo* nzbInfo = queue->Find(id);
520 if (!nzbInfo)
521 {
522 error("NZB with id %i could not be found", id);
523 goto error;
524 }
525
526 if (!LoadNzbInfo(nzbInfo, servers, infile, formatVersion)) goto error;
527 }
528
529 return true;
530
531 error:
532 error("Error reading nzb progress from disk");
419533 return false;
420534 }
421535
472586 outfile.PrintLine("%i", (int)nzbInfo->GetCompletedFiles()->size());
473587 for (CompletedFile& completedFile : nzbInfo->GetCompletedFiles())
474588 {
475 outfile.PrintLine("%i,%i,%u,%i,%s,%s,%s", completedFile.GetId(), (int)completedFile.GetStatus(),
589 outfile.PrintLine("%i,%i,%u,%i,%s,%s", completedFile.GetId(), (int)completedFile.GetStatus(),
476590 completedFile.GetCrc(), (int)completedFile.GetParFile(),
477591 completedFile.GetHash16k() ? completedFile.GetHash16k() : "",
478 completedFile.GetParSetId() ? completedFile.GetParSetId() : "",
479 completedFile.GetFilename());
592 completedFile.GetParSetId() ? completedFile.GetParSetId() : "");
593 outfile.PrintLine("%s", completedFile.GetFilename());
594 outfile.PrintLine("%s", completedFile.GetOrigname() ? completedFile.GetOrigname() : "");
480595 }
481596
482597 outfile.PrintLine("%i", (int)nzbInfo->GetParameters()->size());
715830 nzbInfo->SetUnpackSec(unpackSec);
716831 }
717832
833 nzbInfo->GetCompletedFiles()->clear();
718834 if (infile.ScanLine("%i", &fileCount) != 1) goto error;
719835 for (int i = 0; i < fileCount; i++)
720836 {
727843 int parFile = 0;
728844 char* hash16k = nullptr;
729845 char* parSetId = nullptr;
846 char filenameBuf[1024];
847 char origName[1024];
730848
731849 if (formatVersion >= 49)
732850 {
764882 {
765883 fileName++;
766884 }
885 if (formatVersion >= 62)
886 {
887 if (!infile.ReadLine(filenameBuf, sizeof(filenameBuf))) goto error;
888 fileName = filenameBuf;
889 if (!infile.ReadLine(origName, sizeof(origName))) goto error;
890 }
767891 }
768892
769893 nzbInfo->GetCompletedFiles()->emplace_back(id, fileName,
894 Util::EmptyStr(origName) ? nullptr : origName,
770895 (CompletedFile::EStatus)status, crc, (bool)parFile,
771896 Util::EmptyStr(hash16k) ? nullptr : hash16k,
772897 Util::EmptyStr(parSetId) ? nullptr : parSetId);
773898 }
774899
900 nzbInfo->GetParameters()->clear();
775901 int parameterCount;
776902 if (infile.ScanLine("%i", &parameterCount) != 1) goto error;
777903 for (int i = 0; i < parameterCount; i++)
787913 }
788914 }
789915
916 nzbInfo->GetScriptStatuses()->clear();
790917 int scriptCount;
791918 if (infile.ScanLine("%i", &scriptCount) != 1) goto error;
792919 for (int i = 0; i < scriptCount; i++)
816943 }
817944 }
818945
946 nzbInfo->GetFileList()->clear();
819947 if (infile.ScanLine("%i", &fileCount) != 1) goto error;
820948 for (int i = 0; i < fileCount; i++)
821949 {
9081036 {
9091037 outfile.PrintLine("%s", fileInfo->GetSubject());
9101038 outfile.PrintLine("%s", fileInfo->GetFilename());
1039 outfile.PrintLine("%s", fileInfo->GetOrigname() ? fileInfo->GetOrigname() : "");
9111040
9121041 outfile.PrintLine("%i,%i", (int)fileInfo->GetFilenameConfirmed(), (int)fileInfo->GetTime());
9131042
9701099
9711100 if (!infile.ReadLine(buf, sizeof(buf))) goto error;
9721101 if (fileSummary) fileInfo->SetFilename(buf);
1102
1103 if (formatVersion >= 6)
1104 {
1105 if (!infile.ReadLine(buf, sizeof(buf))) goto error;
1106 if (fileSummary) fileInfo->SetOrigname(Util::EmptyStr(buf) ? nullptr : buf);
1107 }
9731108
9741109 if (formatVersion >= 5)
9751110 {
10631198 outfile.PrintLine("%u,%u,%u,%u,%u,%u", High1, Low1, High2, Low2, High3, Low3);
10641199
10651200 outfile.PrintLine("%s", fileInfo->GetFilename());
1066 outfile.PrintLine("%s", fileInfo->GetHash16k());
1201 outfile.PrintLine("%s", fileInfo->GetHash16k() ? fileInfo->GetHash16k() : "");
1202 outfile.PrintLine("%s", fileInfo->GetParSetId() ? fileInfo->GetParSetId() : "");
10671203 outfile.PrintLine("%i", (int)fileInfo->GetParFile());
10681204
10691205 SaveServerStats(fileInfo->GetServerStats(), outfile);
11221258 {
11231259 if (!infile.ReadLine(buf, sizeof(buf))) goto error;
11241260 fileInfo->SetHash16k(*buf ? buf : nullptr);
1261 if (formatVersion >= 6)
1262 {
1263 if (!infile.ReadLine(buf, sizeof(buf))) goto error;
1264 fileInfo->SetParSetId(*buf ? buf : nullptr);
1265 }
11251266 int parFile = 0;
11261267 if (infile.ScanLine("%i", &parFile) != 1) goto error;
11271268 fileInfo->SetParFile((bool)parFile);
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
3535 bool DownloadQueueExists();
3636 bool SaveDownloadQueue(DownloadQueue* downloadQueue, bool saveHistory);
3737 bool LoadDownloadQueue(DownloadQueue* downloadQueue, Servers* servers);
38 bool SaveDownloadProgress(DownloadQueue* downloadQueue);
3839 bool SaveFile(FileInfo* fileInfo);
3940 bool LoadFile(FileInfo* fileInfo, bool fileSummary, bool articles);
4041 bool SaveAllFileInfos(DownloadQueue* downloadQueue);
6263 bool LoadFileState(FileInfo* fileInfo, Servers* servers, StateDiskFile& infile, int formatVersion, bool completed);
6364 void SaveQueue(NzbList* queue, StateDiskFile& outfile);
6465 bool LoadQueue(NzbList* queue, Servers* servers, StateDiskFile& infile, int formatVersion);
66 void SaveProgress(NzbList* queue, StateDiskFile& outfile, int changedCount);
67 bool LoadProgress(NzbList* queue, Servers* servers, StateDiskFile& infile, int formatVersion);
6568 void SaveNzbInfo(NzbInfo* nzbInfo, StateDiskFile& outfile);
6669 bool LoadNzbInfo(NzbInfo* nzbInfo, Servers* servers, StateDiskFile& infile, int formatVersion);
6770 void SaveDupInfo(DupInfo* dupInfo, StateDiskFile& outfile);
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
33 * Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
4 * Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
837837 }
838838
839839
840 CompletedFile::CompletedFile(int id, const char* filename, EStatus status, uint32 crc,
841 bool parFile, const char* hash16k, const char* parSetId) :
842 m_id(id), m_filename(filename), m_status(status), m_crc(crc), m_parFile(parFile),
843 m_hash16k(hash16k), m_parSetId(parSetId)
840 CompletedFile::CompletedFile(int id, const char* filename, const char* origname, EStatus status,
841 uint32 crc, bool parFile, const char* hash16k, const char* parSetId) :
842 m_id(id), m_filename(filename), m_origname(origname), m_status(status),
843 m_crc(crc), m_parFile(parFile), m_hash16k(hash16k), m_parSetId(parSetId)
844844 {
845845 if (FileInfo::m_idMax < m_id)
846846 {
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
33 * Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
4 * Copyright (C) 2007-2018 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
139139 void SetSubject(const char* subject) { m_subject = subject; }
140140 const char* GetFilename() { return m_filename; }
141141 void SetFilename(const char* filename) { m_filename = filename; }
142 void SetOrigname(const char* origname) { m_origname = origname; }
143 const char* GetOrigname() { return m_origname; }
142144 void MakeValidFilename();
143145 bool GetFilenameConfirmed() { return m_filenameConfirmed; }
144146 void SetFilenameConfirmed(bool filenameConfirmed) { m_filenameConfirmed = filenameConfirmed; }
208210 ServerStatList m_serverStats;
209211 CString m_subject;
210212 CString m_filename;
213 CString m_origname;
211214 int64 m_size = 0;
212215 int64 m_remainingSize = 0;
213216 int64 m_successSize = 0;
258261 cfFailure
259262 };
260263
261 CompletedFile(int id, const char* filename, EStatus status, uint32 crc,
262 bool parFile, const char* hash16k, const char* parSetId);
264 CompletedFile(int id, const char* filename, const char* oldname, EStatus status,
265 uint32 crc, bool parFile, const char* hash16k, const char* parSetId);
263266 int GetId() { return m_id; }
264267 void SetFilename(const char* filename) { m_filename = filename; }
265268 const char* GetFilename() { return m_filename; }
269 void SetOrigname(const char* origname) { m_origname = origname; }
270 const char* GetOrigname() { return m_origname; }
266271 bool GetParFile() { return m_parFile; }
267272 EStatus GetStatus() { return m_status; }
268273 uint32 GetCrc() { return m_crc; }
274279 private:
275280 int m_id;
276281 CString m_filename;
282 CString m_origname;
277283 EStatus m_status;
278284 uint32 m_crc;
279285 bool m_parFile;
894900 };
895901
896902 HistoryInfo(std::unique_ptr<NzbInfo> nzbInfo) : m_info(nzbInfo.release()),
897 m_kind(nzbInfo->GetKind() == NzbInfo::nkNzb ? hkNzb : hkUrl) {}
903 m_kind(GetNzbInfo()->GetKind() == NzbInfo::nkNzb ? hkNzb : hkUrl) {}
898904 HistoryInfo(std::unique_ptr<DupInfo> dupInfo) : m_info(dupInfo.release()), m_kind(hkDup) {}
899905 ~HistoryInfo();
900906 EKind GetKind() { return m_kind; }
907913 const char* GetName();
908914
909915 private:
916 void* m_info;
910917 EKind m_kind;
911 void* m_info;
912918 time_t m_time = 0;
913919 };
914920
925931 eaNzbAdded,
926932 eaNzbDeleted,
927933 eaNzbNamed,
934 eaNzbReturned,
928935 eaFileCompleted,
929936 eaFileDeleted,
930937 eaUrlFound,
931938 eaUrlAdded,
932939 eaUrlDeleted,
933940 eaUrlCompleted,
934 eaUrlFailed
941 eaUrlFailed,
942 eaUrlReturned
935943 };
936944
937945 struct Aspect
10121020 virtual bool EditList(IdList* idList, NameList* nameList, EMatchMode matchMode, EEditAction action, const char* args) = 0;
10131021 virtual void HistoryChanged() = 0;
10141022 virtual void Save() = 0;
1023 virtual void SaveChanged() = 0;
10151024 void CalcRemainingSize(int64* remaining, int64* remainingForced);
10161025
10171026 protected:
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2018 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
130130 {
131131 nzbInfo->UpdateCompletedStats(fileInfo);
132132 nzbInfo->GetCompletedFiles()->emplace_back(fileInfo->GetId(), fileInfo->GetFilename(),
133 CompletedFile::cfNone, 0, fileInfo->GetParFile(), fileInfo->GetHash16k(), fileInfo->GetParSetId());
133 fileInfo->GetOrigname(), CompletedFile::cfNone, 0, fileInfo->GetParFile(),
134 fileInfo->GetHash16k(), fileInfo->GetParSetId());
134135 }
135136
136137 // Cleaning up parked files if par-check was successful or unpack was successful or
422423 // start postprocessing
423424 debug("Restarting postprocessing for %s", *nicename);
424425 g_PrePostProcessor->NzbDownloaded(downloadQueue, nzbInfo);
426 DownloadQueue::Aspect aspect = {DownloadQueue::eaNzbReturned, downloadQueue, nzbInfo, nullptr};
427 downloadQueue->Notify(&aspect);
425428 }
426429 }
427430
437440 nzbInfo->SetDupeHint(nzbInfo->GetDupeHint() == NzbInfo::dhNone ? NzbInfo::dhRedownloadManual : nzbInfo->GetDupeHint());
438441 downloadQueue->GetQueue()->Add(std::unique_ptr<NzbInfo>(nzbInfo), true);
439442 downloadQueue->GetHistory()->erase(itHistory);
443
444 DownloadQueue::Aspect aspect = {DownloadQueue::eaUrlReturned, downloadQueue, nzbInfo, nullptr};
445 downloadQueue->Notify(&aspect);
446
440447 return;
441448 }
442449
524531 MoveToQueue(downloadQueue, itHistory, historyInfo, false);
525532
526533 g_PrePostProcessor->NzbAdded(downloadQueue, nzbInfo);
534
535 DownloadQueue::Aspect aspect = {DownloadQueue::eaNzbReturned, downloadQueue, nzbInfo, nullptr};
536 downloadQueue->Notify(&aspect);
527537 }
528538
529539 void HistoryCoordinator::HistoryReturn(DownloadQueue* downloadQueue, HistoryList::iterator itHistory,
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
3333 void Redownload(DownloadQueue* downloadQueue, HistoryInfo* historyInfo);
3434
3535 protected:
36 virtual int ServiceInterval() { return 600000; }
36 virtual int ServiceInterval() { return 60 * 60; }
3737 virtual void ServiceWork();
3838
3939 private:
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
33 * Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
4 * Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
2121 #include "nzbget.h"
2222 #include "QueueCoordinator.h"
2323 #include "Options.h"
24 #include "WorkState.h"
2425 #include "ServerPool.h"
2526 #include "ArticleDownloader.h"
2627 #include "ArticleWriter.h"
7071
7172 m_wantSave = false;
7273 m_historyChanged = false;
74
75 // queue has changed, time to wake up if in standby
76 m_owner->WakeUp();
77 }
78
79 void QueueCoordinator::CoordinatorDownloadQueue::SaveChanged()
80 {
81 if (g_Options->GetServerMode())
82 {
83 g_DiskState->SaveDownloadProgress(this);
84 m_stateChanged = true;
85 }
7386 }
7487
7588 QueueCoordinator::QueueCoordinator()
7790 debug("Creating QueueCoordinator");
7891
7992 CoordinatorDownloadQueue::Init(&m_downloadQueue);
93 g_WorkState->Attach(this);
8094 }
8195
8296 QueueCoordinator::~QueueCoordinator()
178192 bool articeDownloadsRunning = false;
179193 time_t lastReset = 0;
180194 g_StatMeter->IntervalCheck();
195 int waitInterval = 100;
181196
182197 while (!IsStopped())
183198 {
198213 downloadsChecked = true;
199214 m_hasMoreJobs = hasMoreArticles || articeDownloadsRunning;
200215 if (hasMoreArticles && !IsStopped() && (int)m_activeDownloads.size() < m_downloadsLimit &&
201 (!g_Options->GetTempPauseDownload() || fileInfo->GetExtraPriority()))
216 (!g_WorkState->GetTempPauseDownload() || fileInfo->GetExtraPriority()))
202217 {
203218 StartArticleDownload(fileInfo, articleInfo, connection);
204219 articeDownloadsRunning = true;
226241 if (standBy != wasStandBy)
227242 {
228243 g_StatMeter->EnterLeaveStandBy(standBy);
244 g_WorkState->SetDownloading(!standBy);
229245 wasStandBy = standBy;
230246 if (standBy)
231247 {
234250 }
235251
236252 // sleep longer in StandBy
237 int sleepInterval = downloadStarted ? 0 : standBy ? 100 : 5;
238 usleep(sleepInterval * 1000);
239
240 if (!standBy)
241 {
253 if (standBy)
254 {
255 Util::SetStandByMode(true);
256 Guard guard(m_waitMutex);
257 // sleeping max. 2 seconds; can't sleep much longer because we can't rely on
258 // notifications from 'WorkState' and we also have periodical work to do here
259 waitInterval = std::min(waitInterval * 2, 2000);
260 m_waitCond.WaitFor(m_waitMutex, waitInterval, [&]{ return m_hasMoreJobs || IsStopped(); });
261 }
262 else
263 {
264 int sleepInterval = downloadStarted ? 0 : 5;
265 Util::Sleep(sleepInterval);
242266 g_StatMeter->AddSpeedReading(0);
243 }
244
245 Util::SetStandByMode(standBy);
246
247 time_t currentTime = Util::CurrentTime();
248 if (lastReset != currentTime)
267 waitInterval = 100;
268 }
269
270 if (lastReset != Util::CurrentTime())
249271 {
250272 // this code should not be called too often, once per second is OK
251273 g_ServerPool->CloseUnusedConnections();
257279 g_StatMeter->IntervalCheck();
258280 g_Log->IntervalCheck();
259281 AdjustDownloadsLimit();
260 lastReset = currentTime;
282 Util::SetStandByMode(false);
283 lastReset = Util::CurrentTime();
261284 }
262285 }
263286
264287 WaitJobs();
265288 SaveAllPartialState();
289 SaveQueueIfChanged();
266290 SaveAllFileState();
267291
268292 debug("Exiting QueueCoordinator-loop");
293 }
294
295 void QueueCoordinator::WakeUp()
296 {
297 debug("Waking up QueueCoordinator");
298 // Resume Run()
299 Guard guard(m_waitMutex);
300 m_hasMoreJobs = true;
301 m_waitCond.NotifyAll();
269302 }
270303
271304 void QueueCoordinator::WaitJobs()
282315 break;
283316 }
284317 }
285 usleep(100 * 1000);
318 Util::Sleep(100);
286319 ResetHangingDownloads();
287320 }
288321
418451 {
419452 // If more than two files have same filename we don't filter them out since that
420453 // naming might be intentional and correct filenames must be read from article bodies.
421 int dupeCount = std::count_if(nzbInfo->GetFileList()->begin(), nzbInfo->GetFileList()->end(),
454 int dupeCount = (int)std::count_if(nzbInfo->GetFileList()->begin(), nzbInfo->GetFileList()->end(),
422455 [fileInfo2](std::unique_ptr<FileInfo>& fileInfo3)
423456 {
424457 return !strcmp(fileInfo3->GetFilename(), fileInfo2->GetFilename());
454487 Thread::Stop();
455488
456489 debug("Stopping ArticleDownloads");
457 GuardedDownloadQueue guard = DownloadQueue::Guard();
458 for (ArticleDownloader* articleDownloader : m_activeDownloads)
459 {
460 articleDownloader->Stop();
490 {
491 GuardedDownloadQueue guard = DownloadQueue::Guard();
492 for (ArticleDownloader* articleDownloader : m_activeDownloads)
493 {
494 articleDownloader->Stop();
495 }
461496 }
462497 debug("ArticleDownloads are notified");
498
499 // Resume Run() to exit it
500 Guard guard(m_waitMutex);
501 m_waitCond.NotifyAll();
463502 }
464503
465504 /*
494533 bool nzbPaused = nzbInfo->GetFileList()->size() - nzbInfo->GetPausedFileCount() <= 0;
495534
496535 if ((!fileInfo || nzbHigherPriority) && !nzbPaused &&
497 (!(g_Options->GetPauseDownload() || g_Options->GetQuotaReached()) || nzbInfo->GetForcePriority()))
536 (!(g_WorkState->GetPauseDownload() || g_WorkState->GetQuotaReached()) || nzbInfo->GetForcePriority()))
498537 {
499538 for (FileInfo* fileInfo1 : nzbInfo->GetFileList())
500539 {
618657 articleDownloader->Start();
619658 }
620659
621 void QueueCoordinator::Update(Subject* Caller, void* Aspect)
622 {
660 void QueueCoordinator::Update(Subject* caller, void* aspect)
661 {
662 if (caller == g_WorkState)
663 {
664 debug("Notification from WorkState received");
665 WakeUp();
666 return;
667 }
668
623669 debug("Notification from ArticleDownloader received");
624670
625 ArticleDownloader* articleDownloader = (ArticleDownloader*)Caller;
671 ArticleDownloader* articleDownloader = (ArticleDownloader*)caller;
626672 if ((articleDownloader->GetStatus() == ArticleDownloader::adFinished) ||
627673 (articleDownloader->GetStatus() == ArticleDownloader::adFailed) ||
628674 (articleDownloader->GetStatus() == ArticleDownloader::adRetry))
767813 if (deleteFileObj)
768814 {
769815 DeleteFileInfo(downloadQueue, fileInfo, fileCompleted);
770 downloadQueue->Save();
816 nzbInfo->SetChanged(true);
817 downloadQueue->SaveChanged();
771818 }
772819 }
773820
775822 {
776823 while (g_ArticleCache->FileBusy(fileInfo))
777824 {
778 usleep(5*1000);
825 Util::Sleep(5);
779826 }
780827
781828 NzbInfo* nzbInfo = fileInfo->GetNzbInfo();
819866 fileInfo->GetId(),
820867 completed && fileInfo->GetOutputFilename() ?
821868 FileSystem::BaseFileName(fileInfo->GetOutputFilename()) : fileInfo->GetFilename(),
822 fileStatus,
869 fileInfo->GetOrigname(), fileStatus,
823870 fileStatus == CompletedFile::cfSuccess ? fileInfo->GetCrc() : 0,
824871 fileInfo->GetParFile(), fileInfo->GetHash16k(), fileInfo->GetParSetId());
825872 }
865912 }
866913 }
867914
868 void QueueCoordinator::SaveAllPartialState()
915 void QueueCoordinator::SaveQueueIfChanged()
869916 {
870917 if (!g_Options->GetServerMode())
871918 {
872919 return;
873920 }
874921
875 bool hasUnsavedData = false;
922 bool hasChanges = false;
876923 GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
877924 for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
878925 {
879 if (g_Options->GetContinuePartial())
880 {
881 for (FileInfo* fileInfo : nzbInfo->GetFileList())
882 {
883 SavePartialState(fileInfo);
884 }
885 }
886 hasUnsavedData |= nzbInfo->GetChanged();
887 }
888
889 if (hasUnsavedData)
926 hasChanges |= nzbInfo->GetChanged();
927 }
928
929 if (hasChanges)
890930 {
891931 downloadQueue->Save();
892932 }
933 }
934
935 void QueueCoordinator::SaveAllPartialState()
936 {
937 if (!g_Options->GetServerMode() || !g_Options->GetContinuePartial())
938 {
939 return;
940 }
941
942 GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
943 for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
944 {
945 for (FileInfo* fileInfo : nzbInfo->GetFileList())
946 {
947 SavePartialState(fileInfo);
948 }
949 }
950
951 downloadQueue->SaveChanged();
893952 }
894953
895954 void QueueCoordinator::SavePartialState(FileInfo* fileInfo)
9771036 downloadQueue->CalcRemainingSize(&remaining, &remainingForced);
9781037 info(" Remaining: %.1f MB, Forced: %.1f MB", remaining / 1024.0 / 1024.0, remainingForced / 1024.0 / 1024.0);
9791038 info(" Download: %s, Post-process: %s, Scan: %s",
980 (g_Options->GetPauseDownload() ? "paused" : g_Options->GetTempPauseDownload() ? "temp-paused" : "active"),
981 (g_Options->GetPausePostProcess() ? "paused" : "active"),
982 (g_Options->GetPauseScan() ? "paused" : "active"));
1039 (g_WorkState->GetPauseDownload() ? "paused" : g_WorkState->GetTempPauseDownload() ? "temp-paused" : "active"),
1040 (g_WorkState->GetPausePostProcess() ? "paused" : "active"),
1041 (g_WorkState->GetPauseScan() ? "paused" : "active"));
9831042
9841043 info(" ---------- QueueCoordinator");
9851044 info(" Active Downloads: %i, Limit: %i", (int)m_activeDownloads.size(), m_downloadsLimit);
13141373 downloadQueue->EditEntry(nzbInfo->GetId(), DownloadQueue::eaGroupSortFiles, nullptr);
13151374 }
13161375
1317 downloadQueue->Save();
1376 nzbInfo->SetChanged(true);
1377 downloadQueue->SaveChanged();
13181378
13191379 DownloadQueue::Aspect namedAspect = { DownloadQueue::eaNzbNamed, downloadQueue, nzbInfo, nullptr };
13201380 downloadQueue->Notify(&namedAspect);
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
33 * Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
4 * Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
4040 virtual ~QueueCoordinator();
4141 virtual void Run();
4242 virtual void Stop();
43 void Update(Subject* Caller, void* Aspect);
43 void Update(Subject* caller, void* aspect);
4444
4545 // editing queue
4646 NzbInfo* AddNzbFileToQueue(std::unique_ptr<NzbInfo> nzbInfo, NzbInfo* urlInfo, bool addFirst);
6666 EEditAction action, const char* args);
6767 virtual void HistoryChanged() { m_historyChanged = true; }
6868 virtual void Save();
69 virtual void SaveChanged();
6970 private:
7071 QueueCoordinator* m_owner;
7172 bool m_massEdit = false;
9394 bool m_hasMoreJobs = true;
9495 int m_downloadsLimit;
9596 int m_serverConfigGeneration = 0;
97 Mutex m_waitMutex;
98 ConditionVar m_waitCond;
9699
97100 bool GetNextArticle(DownloadQueue* downloadQueue, FileInfo* &fileInfo, ArticleInfo* &articleInfo);
98101 bool GetNextFirstArticle(NzbInfo* nzbInfo, FileInfo* &fileInfo, ArticleInfo* &articleInfo);
107110 void ResetHangingDownloads();
108111 void AdjustDownloadsLimit();
109112 void Load();
113 void SaveQueueIfChanged();
110114 void SaveAllPartialState();
111115 void SavePartialState(FileInfo* fileInfo);
112116 void LoadPartialState(FileInfo* fileInfo);
113117 void SaveAllFileState();
114118 void WaitJobs();
119 void WakeUp();
115120 };
116121
117122 extern QueueCoordinator* g_QueueCoordinator;
3232 class EditItem
3333 {
3434 public:
35 int m_offset;
3635 FileInfo* m_fileInfo;
3736 NzbInfo* m_nzbInfo;
37 int m_offset;
3838
3939 EditItem(FileInfo* fileInfo, NzbInfo* nzbInfo, int offset) :
4040 m_fileInfo(fileInfo), m_nzbInfo(nzbInfo), m_offset(offset) {}
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2018 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2020 #include "nzbget.h"
2121 #include "Scanner.h"
2222 #include "Options.h"
23 #include "WorkState.h"
2324 #include "Log.h"
2425 #include "QueueCoordinator.h"
2526 #include "HistoryCoordinator.h"
7273
7374 void Scanner::InitOptions()
7475 {
75 m_nzbDirInterval = g_Options->GetNzbDirInterval() * 1000;
76 m_nzbDirInterval = 1;
7677 m_scanScript = ScanScriptController::HasScripts();
7778 }
7879
80 int Scanner::ServiceInterval()
81 {
82 return m_requestedNzbDirScan ? Service::Now :
83 g_Options->GetNzbDirInterval() <= 0 ? Service::Sleep :
84 // g_Options->GetPauseScan() ? Service::Sleep : // for that to work we need to react on changing of pause-state
85 m_nzbDirInterval;
86 }
87
7988 void Scanner::ServiceWork()
8089 {
90 debug("Scanner service work");
91
8192 if (!DownloadQueue::IsLoaded())
8293 {
8394 return;
8495 }
8596
97 m_nzbDirInterval = g_Options->GetNzbDirInterval();
98
99 if (g_WorkState->GetPauseScan() && !m_requestedNzbDirScan)
100 {
101 return;
102 }
103
104 debug("Scanner service work: doing work");
105
86106 Guard guard(m_scanMutex);
87107
88 if (m_requestedNzbDirScan ||
89 (!g_Options->GetPauseScan() && g_Options->GetNzbDirInterval() > 0 &&
90 m_nzbDirInterval >= g_Options->GetNzbDirInterval() * 1000))
91 {
92 // check nzbdir every g_pOptions->GetNzbDirInterval() seconds or if requested
93 bool checkStat = !m_requestedNzbDirScan;
94 m_requestedNzbDirScan = false;
95 m_scanning = true;
108 // check nzbdir every g_pOptions->GetNzbDirInterval() seconds or if requested
109 bool checkStat = !m_requestedNzbDirScan;
110 m_requestedNzbDirScan = false;
111 m_scanning = true;
112 CheckIncomingNzbs(g_Options->GetNzbDir(), "", checkStat);
113 if (!checkStat && m_scanScript)
114 {
115 // if immediate scan requested, we need second scan to process files extracted by scan-scripts
96116 CheckIncomingNzbs(g_Options->GetNzbDir(), "", checkStat);
97 if (!checkStat && m_scanScript)
98 {
99 // if immediate scan requested, we need second scan to process files extracted by scan-scripts
100 CheckIncomingNzbs(g_Options->GetNzbDir(), "", checkStat);
101 }
102 m_scanning = false;
103 m_nzbDirInterval = 0;
104
105 // if NzbDirFileAge is less than NzbDirInterval (that can happen if NzbDirInterval
106 // is set for rare scans like once per hour) we make 4 scans:
107 // - one additional scan is neccessary to check sizes of detected files;
108 // - another scan is required to check files which were extracted by scan-scripts;
109 // - third scan is needed to check sizes of extracted files.
110 if (g_Options->GetNzbDirInterval() > 0 && g_Options->GetNzbDirFileAge() < g_Options->GetNzbDirInterval())
111 {
112 int maxPass = m_scanScript ? 3 : 1;
113 if (m_pass < maxPass)
114 {
115 // scheduling another scan of incoming directory in NzbDirFileAge seconds.
116 m_nzbDirInterval = (g_Options->GetNzbDirInterval() - g_Options->GetNzbDirFileAge()) * 1000;
117 m_pass++;
118 }
119 else
120 {
121 m_pass = 0;
122 }
123 }
124
125 DropOldFiles();
126 m_queueList.clear();
127 }
128 m_nzbDirInterval += 200;
117 }
118 m_scanning = false;
119
120 // if NzbDirFileAge is less than NzbDirInterval (that can happen if NzbDirInterval
121 // is set for rare scans like once per hour) we make 4 scans:
122 // - one additional scan is neccessary to check sizes of detected files;
123 // - another scan is required to check files which were extracted by scan-scripts;
124 // - third scan is needed to check sizes of extracted files.
125 if (g_Options->GetNzbDirInterval() > 0 && g_Options->GetNzbDirFileAge() < g_Options->GetNzbDirInterval())
126 {
127 int maxPass = m_scanScript ? 3 : 1;
128 if (m_pass < maxPass)
129 {
130 // scheduling another scan of incoming directory in NzbDirFileAge seconds.
131 m_nzbDirInterval = g_Options->GetNzbDirFileAge();
132 m_pass++;
133 }
134 else
135 {
136 m_pass = 0;
137 }
138 }
139
140 DropOldFiles();
141 m_queueList.clear();
129142 }
130143
131144 /**
485498 Guard guard(m_scanMutex);
486499 m_scanning = true;
487500 m_requestedNzbDirScan = true;
501 WakeUp();
488502 }
489503
490504 while (syncMode && (m_scanning || m_requestedNzbDirScan))
491505 {
492 usleep(100 * 1000);
506 Util::Sleep(100);
493507 }
494508 }
495509
496510 Scanner::EAddStatus Scanner::AddExternalFile(const char* nzbName, const char* category,
497 int priority, const char* dupeKey, int dupeScore, EDupeMode dupeMode,
511 int priority, const char* dupeKey, int dupeScore, EDupeMode dupeMode,
498512 NzbParameterList* parameters, bool addTop, bool addPaused, NzbInfo* urlInfo,
499513 const char* fileName, const char* buffer, int bufSize, int* nzbId)
500514 {
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
4545 void InitPPParameters(const char* category, NzbParameterList* parameters, bool reset);
4646
4747 protected:
48 virtual int ServiceInterval() { return 200; }
48 virtual int ServiceInterval();
4949 virtual void ServiceWork();
5050
5151 private:
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2012-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2012-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2020 #include "nzbget.h"
2121 #include "UrlCoordinator.h"
2222 #include "Options.h"
23 #include "WorkState.h"
2324 #include "WebDownloader.h"
2425 #include "Util.h"
2526 #include "FileSystem.h"
5960 }
6061 }
6162
63 UrlCoordinator::UrlCoordinator()
64 {
65 m_downloadQueueObserver.m_owner = this;
66 DownloadQueue::Guard()->Attach(&m_downloadQueueObserver);
67 }
68
6269 UrlCoordinator::~UrlCoordinator()
6370 {
6471 debug("Destroying UrlCoordinator");
7885
7986 while (!DownloadQueue::IsLoaded())
8087 {
81 usleep(20 * 1000);
82 }
83
84 int resetCounter = 0;
88 Util::Sleep(20);
89 }
8590
8691 while (!IsStopped())
8792 {
93 time_t lastReset = 0;
8894 bool downloadStarted = false;
89 if (!g_Options->GetPauseDownload() || g_Options->GetUrlForce())
90 {
91 // start download for next URL
95
96 {
97 NzbInfo* nzbInfo = nullptr;
9298 GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
9399 if ((int)m_activeDownloads.size() < g_Options->GetUrlConnections())
94100 {
95 NzbInfo* nzbInfo = GetNextUrl(downloadQueue);
96 bool hasMoreUrls = nzbInfo != nullptr;
97 bool urlDownloadsRunning = !m_activeDownloads.empty();
98 m_hasMoreJobs = hasMoreUrls || urlDownloadsRunning;
99 if (hasMoreUrls && !IsStopped())
101 nzbInfo = GetNextUrl(downloadQueue);
102 if (nzbInfo && (!g_WorkState->GetPauseDownload() || g_Options->GetUrlForce()))
100103 {
101104 StartUrlDownload(nzbInfo);
102105 downloadStarted = true;
103106 }
104107 }
105 }
106
107 int sleepInterval = downloadStarted ? 0 : 100;
108 usleep(sleepInterval * 1000);
109
110 resetCounter += sleepInterval;
111 if (resetCounter >= 1000)
108 m_hasMoreJobs = !m_activeDownloads.empty() || nzbInfo;
109 }
110
111 if (lastReset != Util::CurrentTime())
112112 {
113113 // this code should not be called too often, once per second is OK
114114 ResetHangingDownloads();
115 resetCounter = 0;
115 lastReset = Util::CurrentTime();
116 }
117
118 if (!m_hasMoreJobs && !IsStopped())
119 {
120 Guard guard(m_waitMutex);
121 m_waitCond.Wait(m_waitMutex, [&] { return m_hasMoreJobs || IsStopped(); });
122 }
123 else
124 {
125 int sleepInterval = downloadStarted ? 0 : 100;
126 Util::Sleep(sleepInterval);
116127 }
117128 }
118129
135146 break;
136147 }
137148 }
138 usleep(100 * 1000);
149 Util::Sleep(100);
139150 ResetHangingDownloads();
140151 }
141152
147158 Thread::Stop();
148159
149160 debug("Stopping UrlDownloads");
150 GuardedDownloadQueue guard = DownloadQueue::Guard();
151 for (UrlDownloader* urlDownloader : m_activeDownloads)
152 {
153 urlDownloader->Stop();
161 {
162 GuardedDownloadQueue guard = DownloadQueue::Guard();
163 for (UrlDownloader* urlDownloader : m_activeDownloads)
164 {
165 urlDownloader->Stop();
166 }
154167 }
155168 debug("UrlDownloads are notified");
169
170 // Resume Run() to exit it
171 Guard guard(m_waitMutex);
172 m_waitCond.NotifyAll();
173 }
174
175 void UrlCoordinator::DownloadQueueUpdate(Subject* caller, void* aspect)
176 {
177 debug("Notification from download queue received");
178
179 DownloadQueue::Aspect* queueAspect = (DownloadQueue::Aspect*)aspect;
180 if (queueAspect->action == DownloadQueue::eaUrlAdded ||
181 queueAspect->action == DownloadQueue::eaUrlReturned)
182 {
183 // Resume Run()
184 Guard guard(m_waitMutex);
185 m_hasMoreJobs = true;
186 m_waitCond.NotifyAll();
187 }
156188 }
157189
158190 void UrlCoordinator::ResetHangingDownloads()
193225 */
194226 NzbInfo* UrlCoordinator::GetNextUrl(DownloadQueue* downloadQueue)
195227 {
196 bool pauseDownload = g_Options->GetPauseDownload();
197
198228 NzbInfo* nzbInfo = nullptr;
199229
200230 for (NzbInfo* nzbInfo1 : downloadQueue->GetQueue())
202232 if (nzbInfo1->GetKind() == NzbInfo::nkUrl &&
203233 nzbInfo1->GetUrlStatus() == NzbInfo::lsNone &&
204234 nzbInfo1->GetDeleteStatus() == NzbInfo::dsNone &&
205 (!pauseDownload || g_Options->GetUrlForce()) &&
206235 (!nzbInfo || nzbInfo1->GetPriority() > nzbInfo->GetPriority()))
207236 {
208237 nzbInfo = nzbInfo1;
396425
397426 downloadQueue->Save();
398427 }
428
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2012-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2012-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
3232 class UrlCoordinator : public Thread, public Observer, public Debuggable
3333 {
3434 public:
35 UrlCoordinator();
3536 virtual ~UrlCoordinator();
3637 virtual void Run();
3738 virtual void Stop();
4849 private:
4950 typedef std::list<UrlDownloader*> ActiveDownloads;
5051
52 class DownloadQueueObserver: public Observer
53 {
54 public:
55 UrlCoordinator* m_owner;
56 virtual void Update(Subject* caller, void* aspect) { m_owner->DownloadQueueUpdate(caller, aspect); }
57 };
58
5159 ActiveDownloads m_activeDownloads;
5260 bool m_hasMoreJobs = true;
5361 bool m_force;
62 Mutex m_waitMutex;
63 ConditionVar m_waitCond;
64 DownloadQueueObserver m_downloadQueueObserver;
5465
5566 NzbInfo* GetNextUrl(DownloadQueue* downloadQueue);
5667 void StartUrlDownload(NzbInfo* nzbInfo);
5768 void UrlCompleted(UrlDownloader* urlDownloader);
5869 void ResetHangingDownloads();
5970 void WaitJobs();
71 void DownloadQueueUpdate(Subject* caller, void* aspect);
6072 };
6173
6274 extern UrlCoordinator* g_UrlCoordinator;
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
33 * Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
4 * Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
2222 #include "BinRpc.h"
2323 #include "Log.h"
2424 #include "Options.h"
25 #include "WorkState.h"
2526 #include "QueueEditor.h"
2627 #include "Util.h"
2728 #include "FileSystem.h"
312313 return;
313314 }
314315
315 g_Options->SetResumeTime(0);
316 g_WorkState->SetResumeTime(0);
316317
317318 switch (ntohl(PauseUnpauseRequest.m_action))
318319 {
319320 case rpDownload:
320 g_Options->SetPauseDownload(ntohl(PauseUnpauseRequest.m_pause));
321 g_WorkState->SetPauseDownload(ntohl(PauseUnpauseRequest.m_pause));
321322 break;
322323
323324 case rpPostProcess:
324 g_Options->SetPausePostProcess(ntohl(PauseUnpauseRequest.m_pause));
325 g_WorkState->SetPausePostProcess(ntohl(PauseUnpauseRequest.m_pause));
325326 break;
326327
327328 case rpScan:
328 g_Options->SetPauseScan(ntohl(PauseUnpauseRequest.m_pause));
329 g_WorkState->SetPauseScan(ntohl(PauseUnpauseRequest.m_pause));
329330 break;
330331 }
331332
340341 return;
341342 }
342343
343 g_Options->SetDownloadRate(ntohl(SetDownloadRequest.m_downloadRate));
344 g_WorkState->SetSpeedLimit(ntohl(SetDownloadRequest.m_downloadRate));
344345 SendBoolResponse(true, "Rate-Command completed successfully");
345346 }
346347
673674 Util::SplitInt64(remainingSize, &sizeHi, &sizeLo);
674675 ListResponse.m_remainingSizeHi = htonl(sizeHi);
675676 ListResponse.m_remainingSizeLo = htonl(sizeLo);
676 ListResponse.m_downloadLimit = htonl(g_Options->GetDownloadRate());
677 ListResponse.m_downloadPaused = htonl(g_Options->GetPauseDownload());
678 ListResponse.m_postPaused = htonl(g_Options->GetPausePostProcess());
679 ListResponse.m_scanPaused = htonl(g_Options->GetPauseScan());
677 ListResponse.m_downloadRate = htonl(g_WorkState->GetSpeedLimit());
678 ListResponse.m_downloadPaused = htonl(g_WorkState->GetPauseDownload());
679 ListResponse.m_postPaused = htonl(g_WorkState->GetPausePostProcess());
680 ListResponse.m_scanPaused = htonl(g_WorkState->GetPauseScan());
680681 ListResponse.m_threadCount = htonl(Thread::GetThreadCount() - 1); // not counting itself
681682 ListResponse.m_postJobCount = htonl(postJobCount);
682683
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
33 * Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
4 * Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
496496 }
497497
498498 printf("-----------------------------------\n");
499 printf("Groups: %i\n", downloadQueue->GetQueue()->size());
499 printf("Groups: %i\n", (int)downloadQueue->GetQueue()->size());
500500 if (pattern)
501501 {
502502 printf("Matches: %i\n", matches);
540540 int averageSpeed = (int)(ntohl(ListResponse.m_downloadTimeSec) > 0 ? allBytes / ntohl(ListResponse.m_downloadTimeSec) : 0);
541541 printf("Session download rate: %s\n", *Util::FormatSpeed(averageSpeed));
542542
543 if (ntohl(ListResponse.m_downloadLimit) > 0)
544 {
545 printf("Speed limit: %s\n", *Util::FormatSpeed(ntohl(ListResponse.m_downloadLimit)));
543 if (ntohl(ListResponse.m_downloadRate) > 0)
544 {
545 printf("Speed limit: %s\n", *Util::FormatSpeed(ntohl(ListResponse.m_downloadRate)));
546546 }
547547
548548 int sec = ntohl(ListResponse.m_upTimeSec);
796796
797797 if (textLen > 0)
798798 {
799 strcpy(trailingData, text);
799 strncpy(trailingData, text, textLen);
800800 }
801801
802802 int32* ids = (int32*)(trailingData + textLen);
811811 char *names = trailingData + textLen + idLength;
812812 for (CString& name : nameList)
813813 {
814 int len = strlen(name);
814 // "len" must be less or equal than: "buffer size" - "already used buffer" - "ending \0"
815 size_t len = strnlen(name, length - (names - trailingData) - 1);
815816 strncpy(names, name, len + 1);
816817 names += len + 1;
817818 }
7878 break;
7979 }
8080 m_connection.reset();
81 usleep(500 * 1000);
81 Util::Sleep(500);
8282 continue;
8383 }
8484
111111 Guard guard(m_processorsMutex);
112112 completed = m_activeProcessors.size() == 0;
113113 }
114 usleep(100 * 1000);
114 Util::Sleep(100);
115115 }
116116 debug("RemoteServer: request processor are completed");
117117
5252
5353 for (int j = uaControl; j <= uaAdd; j++)
5454 {
55 for (int i = 0; i < sizeof(m_serverAuthToken[j]) - 1; i++)
55 for (size_t i = 0; i < sizeof(m_serverAuthToken[j]) - 1; i++)
5656 {
5757 int ch = rand() % (10 + 26 + 26);
5858 if (0 <= ch && ch < 10)
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2121 #include "XmlRpc.h"
2222 #include "Log.h"
2323 #include "Options.h"
24 #include "WorkState.h"
2425 #include "Scanner.h"
2526 #include "FeedCoordinator.h"
2627 #include "ServerPool.h"
403404 int valueLen = 0;
404405 if (const char* methodPtr = WebUtil::JsonFindField(m_request, "method", &valueLen))
405406 {
406 valueLen = valueLen >= sizeof(methodName) ? sizeof(methodName) - 1 : valueLen;
407 valueLen = valueLen >= (int)sizeof(methodName) ? (int)sizeof(methodName) - 1 : valueLen;
407408 methodName.Set(methodPtr + 1, valueLen - 2);
408409 }
409410 if (const char* requestIdPtr = WebUtil::JsonFindField(m_request, "id", &valueLen))
410411 {
411 valueLen = valueLen >= sizeof(requestId) ? sizeof(requestId) - 1 : valueLen;
412 valueLen = valueLen >= (int)sizeof(requestId) ? (int)sizeof(requestId) - 1 : valueLen;
412413 requestId.Set(requestIdPtr, valueLen);
413414 }
414415 }
11011102 {
11021103 bool ok = true;
11031104
1104 g_Options->SetResumeTime(0);
1105 g_WorkState->SetResumeTime(0);
11051106
11061107 switch (m_pauseAction)
11071108 {
11081109 case paDownload:
1109 g_Options->SetPauseDownload(m_pause);
1110 g_WorkState->SetPauseDownload(m_pause);
11101111 break;
11111112
11121113 case paPostProcess:
1113 g_Options->SetPausePostProcess(m_pause);
1114 g_WorkState->SetPausePostProcess(m_pause);
11141115 break;
11151116
11161117 case paScan:
1117 g_Options->SetPauseScan(m_pause);
1118 g_WorkState->SetPauseScan(m_pause);
11181119 break;
11191120
11201121 default:
11361137
11371138 time_t curTime = Util::CurrentTime();
11381139
1139 g_Options->SetResumeTime(curTime + seconds);
1140 g_WorkState->SetResumeTime(curTime + seconds);
11401141
11411142 BuildBoolResponse(true);
11421143 }
11781179 return;
11791180 }
11801181
1181 g_Options->SetDownloadRate(rate * 1024);
1182 g_WorkState->SetSpeedLimit(rate * 1024);
11821183 BuildBoolResponse(true);
11831184 }
11841185
13211322 int articleCacheMBytes = (int)(articleCache / 1024 / 1024);
13221323
13231324 int downloadRate = (int)(g_StatMeter->CalcCurrentDownloadSpeed());
1324 int downloadLimit = (int)(g_Options->GetDownloadRate());
1325 bool downloadPaused = g_Options->GetPauseDownload();
1326 bool postPaused = g_Options->GetPausePostProcess();
1327 bool scanPaused = g_Options->GetPauseScan();
1328 bool quotaReached = g_Options->GetQuotaReached();
1325 int downloadLimit = (int)(g_WorkState->GetSpeedLimit());
1326 bool downloadPaused = g_WorkState->GetPauseDownload();
1327 bool postPaused = g_WorkState->GetPausePostProcess();
1328 bool scanPaused = g_WorkState->GetPauseScan();
1329 bool quotaReached = g_WorkState->GetQuotaReached();
13291330 int threadCount = Thread::GetThreadCount() - 1; // not counting itself
13301331
13311332 uint32 downloadedSizeHi, downloadedSizeLo;
13531354 int freeDiskSpaceMB = (int)(freeDiskSpace / 1024 / 1024);
13541355
13551356 int serverTime = (int)Util::CurrentTime();
1356 int resumeTime = (int)g_Options->GetResumeTime();
1357 int resumeTime = (int)g_WorkState->GetResumeTime();
13571358 bool feedActive = g_FeedCoordinator->HasActiveDownloads();
13581359 int queuedScripts = g_QueueScriptCoordinator->GetQueueSize();
13591360
9696
9797 m_logFile->Seek(0, DiskFile::soEnd);
9898
99 #ifdef DEBUG
99100 #ifdef WIN32
100101 uint64 processId = GetCurrentProcessId();
101102 uint64 threadId = GetCurrentThreadId();
103104 uint64 processId = (uint64)getpid();
104105 uint64 threadId = (uint64)pthread_self();
105106 #endif
106 #ifdef DEBUG
107 m_logFile->Print("%s\t%llu\t%llu\t%s%s", time, processId, threadId, tmp2, LINE_ENDING);
107 m_logFile->Print("%s\t%" PRIu64 "\t%" PRIu64 "\t%s%s", time, processId, threadId, tmp2, LINE_ENDING);
108108 #else
109109 m_logFile->Print("%s\t%s%s", time, tmp2, LINE_ENDING);
110110 #endif
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2015-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2015-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7575 }
7676
7777 template <int size>
78 void BString<size>::Format(const char* format, ...)
79 {
80 va_list ap;
81 va_start(ap, format);
82 FormatV(format, ap);
83 va_end(ap);
84 }
85
86 template <int size>
87 void BString<size>::FormatV(const char* format, va_list ap)
88 {
89 vsnprintf(m_data, size, format, ap);
78 int BString<size>::Format(const char* format, ...)
79 {
80 va_list ap;
81 va_start(ap, format);
82 int len = FormatV(format, ap);
83 va_end(ap);
84 return len;
85 }
86
87 template <int size>
88 int BString<size>::FormatV(const char* format, va_list ap)
89 {
90 // ensure result isn't negative (in case of bad argument)
91 int len = std::max(vsnprintf(m_data, size, format, ap), 0);
92 return len;
9093 }
9194
9295 bool CString::operator==(const CString& other)
158161 va_end(ap2);
159162 }
160163
161 void CString::Format(const char* format, ...)
162 {
163 va_list ap;
164 va_start(ap, format);
165 FormatV(format, ap);
166 va_end(ap);
167 }
168
169 void CString::FormatV(const char* format, va_list ap)
164 int CString::Format(const char* format, ...)
165 {
166 va_list ap;
167 va_start(ap, format);
168 int len = FormatV(format, ap);
169 va_end(ap);
170 return len;
171 }
172
173 int CString::FormatV(const char* format, va_list ap)
170174 {
171175 va_list ap2;
172176 va_copy(ap2, ap);
173177
174 int newLen = vsnprintf(nullptr, 0, format, ap);
175 if (newLen < 0) return; // bad argument
178 // "std::max" to ensure result isn't negative (in case of bad argument)
179 int newLen = std::max(vsnprintf(nullptr, 0, format, ap), 0);
176180
177181 m_data = (char*)realloc(m_data, newLen + 1);
178 vsnprintf(m_data, newLen + 1, format, ap2);
182 newLen = vsnprintf(m_data, newLen + 1, format, ap2);
179183
180184 va_end(ap2);
185 return newLen;
181186 }
182187
183188 int CString::Find(const char* str, int pos)
280285 m_data = (wchar_t*)malloc((strlen(utfstr) * 2 + 1) * sizeof(wchar_t));
281286
282287 wchar_t* out = m_data;
283 unsigned int codepoint;
288 unsigned int codepoint = 0;
284289 while (*utfstr != 0)
285290 {
286291 unsigned char ch = (unsigned char)*utfstr;
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2015-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2015-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
4545 void Append(const char* str, int len = 0);
4646 void AppendFmt(const char* format, ...) PRINTF_SYNTAX(2);
4747 void AppendFmtV(const char* format, va_list ap);
48 void Format(const char* format, ...) PRINTF_SYNTAX(2);
49 void FormatV(const char* format, va_list ap);
48 int Format(const char* format, ...) PRINTF_SYNTAX(2);
49 int FormatV(const char* format, va_list ap);
5050
5151 protected:
5252 char m_data[size];
8282 void Append(const char* str, int len = 0);
8383 void AppendFmt(const char* format, ...) PRINTF_SYNTAX(2);
8484 void AppendFmtV(const char* format, va_list ap);
85 void Format(const char* format, ...) PRINTF_SYNTAX(2);
86 void FormatV(const char* format, va_list ap);
85 int Format(const char* format, ...) PRINTF_SYNTAX(2);
86 int FormatV(const char* format, va_list ap);
8787 int Find(const char* str, int pos = 0);
8888 void Replace(int pos, int len, const char* str, int strLen = 0);
8989 void Replace(const char* from, const char* to);
149149 CharBuffer(int size) : m_size(size) { m_data = (char*)malloc(size); }
150150 CharBuffer(CharBuffer& other) : m_data(other.m_data), m_size(other.m_size) { other.m_data = nullptr; other.m_size = 0; }
151151 ~CharBuffer() { free(m_data); }
152 CharBuffer& operator=(CharBuffer&& other) = delete;
152153 int Size() { return m_size; }
153154 void Reserve(int size) { m_data = (char*)realloc(m_data, size); m_size = size; }
154155 void Clear() { free(m_data); m_data = nullptr; m_size = 0; }
6565 time_t start = Util::CurrentTime();
6666 while (!IsStopped() && (Util::CurrentTime() - start) < WAIT_SECONDS)
6767 {
68 usleep(10 * 1000);
68 Util::Sleep(10);
6969 }
7070
7171 if (!IsStopped())
393393 }
394394 while (watchDog.IsRunning())
395395 {
396 usleep(5 * 1000);
396 Util::Sleep(5);
397397 }
398398 #endif
399399
583583 args.emplace_back(nullptr);
584584 char* const* argdata = (char* const*)args.data();
585585
586 #ifdef DEBUG
586587 debug("Starting process: %s", script);
587588 for (const char* arg : m_args)
588589 {
589590 debug("arg: %s", arg);
590591 }
592 #endif
591593
592594 debug("forking");
593595 pid_t pid = fork();
738740 time_t curtime = Util::CurrentTime();
739741 while (!script->m_completed && std::abs(curtime - Util::CurrentTime()) <= 10)
740742 {
741 usleep(100 * 1000);
743 Util::Sleep(100);
742744 }
743745 }
744746 script->Terminate();
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2015-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2015-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2929 g_ServiceCoordinator->RegisterService(this);
3030 }
3131
32 void Service::WakeUp()
33 {
34 g_ServiceCoordinator->WakeUp();
35 };
36
3237
3338 ServiceCoordinator::ServiceCoordinator()
3439 {
4550 {
4651 debug("Entering ServiceCoordinator-loop");
4752
48 const int stepMSec = 100;
49 int curTick = 0;
50
5153 while (!IsStopped())
5254 {
55 // perform service work
5356 for (Service* service : m_services)
5457 {
55 if (curTick >= service->m_lastTick + service->ServiceInterval() || // interval expired
56 curTick == 0 || // first start
57 curTick + 10000 < service->m_lastTick) // int overflow
58 int serviceInterval = service->ServiceInterval();
59 if (serviceInterval >= Service::Now &&
60 abs(Util::CurrentTime() - service->m_lastWork) >= serviceInterval)
5861 {
5962 service->ServiceWork();
60 service->m_lastTick = curTick;
63 service->m_lastWork = Util::CurrentTime();
6164 }
6265 }
6366
64 curTick += stepMSec;
65 usleep(stepMSec * 1000);
67 // calculate wait interval
68 int waitInterval = 60 * 60 * 24; // something very large
69 time_t curTime = Util::CurrentTime();
70 for (Service* service : m_services)
71 {
72 int serviceInterval = service->ServiceInterval();
73 int remaining = Service::Sleep;
74 if (serviceInterval >= Service::Now)
75 {
76 remaining = serviceInterval - (curTime - service->m_lastWork);
77 waitInterval = std::min(waitInterval, remaining);
78 }
79 debug("serviceInterval: %i, remaining: %i", serviceInterval, remaining);
80 }
81
82 debug("Waiting in ServiceCoordinator: %i", waitInterval);
83 if (waitInterval > 0)
84 {
85 Guard guard(m_waitMutex);
86 m_waitCond.WaitFor(m_waitMutex, waitInterval * 1000, [&] { return m_workenUp || IsStopped(); });
87 m_workenUp = false;
88 }
6689 }
6790
6891 debug("Exiting ServiceCoordinator-loop");
92 }
93
94 void ServiceCoordinator::Stop()
95 {
96 Thread::Stop();
97
98 // Resume Run() to exit it
99 Guard guard(m_waitMutex);
100 m_waitCond.NotifyAll();
101 }
102
103 void ServiceCoordinator::WakeUp()
104 {
105 debug("Waking up ServiceCoordinator");
106 // Resume Run()
107 Guard guard(m_waitMutex);
108 m_workenUp = true;
109 m_waitCond.NotifyAll();
69110 }
70111
71112 void ServiceCoordinator::RegisterService(Service* service)
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2015-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2015-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2727 public:
2828 Service();
2929
30 static const int Now = 0;
31 static const int Sleep = -1;
32
3033 protected:
3134 virtual int ServiceInterval() = 0;
3235 virtual void ServiceWork() = 0;
36 void WakeUp();
3337
3438 private:
35 int m_lastTick = 0;
39 time_t m_lastWork = 0;
3640
3741 friend class ServiceCoordinator;
3842 };
4549 ServiceCoordinator();
4650 virtual ~ServiceCoordinator();
4751 virtual void Run();
52 virtual void Stop();
4853
4954 private:
5055 ServiceList m_services;
56 Mutex m_waitMutex;
57 ConditionVar m_waitCond;
58 bool m_workenUp = false;
5159
5260 void RegisterService(Service* service);
61 void WakeUp();
5362
5463 friend class Service;
5564 };
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
33 * Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
4 * Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
2323
2424 int Thread::m_threadCount = 1; // take the main program thread into account
2525 std::unique_ptr<Mutex> Thread::m_threadMutex;
26
27
28 Mutex::Mutex()
29 {
30 #ifdef WIN32
31 InitializeCriticalSection(&m_mutexObj);
32 #else
33 pthread_mutex_init(&m_mutexObj, nullptr);
34 #endif
35 }
36
37 Mutex::~Mutex()
38 {
39 #ifdef WIN32
40 DeleteCriticalSection(&m_mutexObj);
41 #else
42 pthread_mutex_destroy(&m_mutexObj);
43 #endif
44 }
45
46 void Mutex::Lock()
47 {
48 #ifdef WIN32
49 EnterCriticalSection(&m_mutexObj);
50 #ifdef DEBUG
51 // CriticalSections on Windows can be locked many times from the same thread,
52 // but we do not want this and must treat such situations as errors and detect them.
53 if (m_mutexObj.RecursionCount > 1)
54 {
55 error("Internal program error: inconsistent thread-lock detected");
56 }
57 #endif
58 #else
59 pthread_mutex_lock(&m_mutexObj);
60 #endif
61 }
62
63 void Mutex::Unlock()
64 {
65 #ifdef WIN32
66 LeaveCriticalSection(&m_mutexObj);
67 #else
68 pthread_mutex_unlock(&m_mutexObj);
69 #endif
70 }
7126
7227
7328 void Thread::Init()
9348
9449 m_running = true;
9550
96 // NOTE: we must guarantee, that in a time we set m_running
97 // to value returned from pthread_create, the thread-object still exists.
98 // This is not obvious!
99 // pthread_create could wait long enough before returning result
100 // back to allow the started thread to complete its job and terminate.
101 // We lock mutex m_threadMutex on calling pthread_create; the started thread
102 // then also try to lock the mutex (see thread_handler) and therefore
103 // must wait until we unlock it
51 // NOTE: "m_threadMutex" ensures that "t" lives until the very end of the function
10452 Guard guard(m_threadMutex);
10553
106 #ifdef WIN32
107 m_threadObj = (HANDLE)_beginthread(Thread::thread_handler, 0, (void*)this);
108 m_running = m_threadObj != 0;
109 #else
110 pthread_attr_t m_attr;
111 pthread_attr_init(&m_attr);
112 pthread_attr_setdetachstate(&m_attr, PTHREAD_CREATE_DETACHED);
113 pthread_attr_setinheritsched(&m_attr, PTHREAD_INHERIT_SCHED);
114 m_running = !pthread_create(&m_threadObj, &m_attr, Thread::thread_handler, (void *) this);
115 pthread_attr_destroy(&m_attr);
116 #endif
54 // start the new thread
55 std::thread t([&]{
56 {
57 // trying to lock "m_threadMutex", this will wait until function "Start()" is completed
58 // and "t" is detached.
59 Guard guard(m_threadMutex);
60 }
61
62 thread_handler();
63 });
64
65 // save the native handle to be able to cancel (Kill) the thread and then detach
66 m_threadObj = t.native_handle();
67 t.detach();
11768 }
11869
11970 void Thread::Stop()
154105 return terminated;
155106 }
156107
157 #ifdef WIN32
158 void __cdecl Thread::thread_handler(void* object)
159 #else
160 void* Thread::thread_handler(void* object)
161 #endif
108 void Thread::thread_handler()
162109 {
163 {
164 Guard guard(m_threadMutex);
165 m_threadCount++;
166 }
110 m_threadCount++;
167111
168112 debug("Entering Thread-func");
169113
170 Thread* thread = (Thread*)object;
171
172 thread->Run();
114 Run();
173115
174116 debug("Thread-func exited");
175117
176 if (thread->m_autoDestroy)
118 m_running = false;
119
120 m_threadCount--;
121
122 if (m_autoDestroy)
177123 {
178124 debug("Autodestroying Thread-object");
179 delete thread;
125 delete this;
180126 }
181 else
182 {
183 thread->m_running = false;
184 }
185
186 {
187 Guard guard(m_threadMutex);
188 m_threadCount--;
189 }
190
191 #ifndef WIN32
192 return nullptr;
193 #endif
194127 }
195128
196129 int Thread::GetThreadCount()
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
33 * Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
4 * Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
2424 class Mutex
2525 {
2626 public:
27 Mutex();
27 Mutex() {};
2828 Mutex(const Mutex&) = delete;
29 ~Mutex();
30 void Lock();
31 void Unlock();
29 void Lock() { m_mutexObj.lock(); }
30 void Unlock() { m_mutexObj.unlock(); }
3231
3332 private:
34 #ifdef WIN32
35 CRITICAL_SECTION m_mutexObj;
36 #else
37 pthread_mutex_t m_mutexObj;
38 #endif
33 std::mutex m_mutexObj;
34
35 friend class ConditionVar;
3936 };
4037
4138 class Guard
7976 void Unlock() { if (m_mutex) { m_mutex->Unlock(); m_mutex = nullptr; } }
8077 };
8178
79 class ConditionVar
80 {
81 public:
82 ConditionVar() {}
83 ConditionVar(const ConditionVar& other) = delete;
84 void Wait(Mutex& mutex) { Lock l(mutex); m_condObj.wait(l); }
85 template <typename Pred> void Wait(Mutex& mutex, Pred pred) { Lock l(mutex); m_condObj.wait(l, pred); }
86 void WaitFor(Mutex& mutex, int msec) { Lock l(mutex); m_condObj.wait_for(l, std::chrono::milliseconds(msec)); }
87 template <typename Pred> void WaitFor(Mutex& mutex, int msec, Pred pred)
88 { Lock l(mutex); m_condObj.wait_for(l, std::chrono::milliseconds(msec), pred); }
89 void NotifyOne() { m_condObj.notify_one(); }
90 void NotifyAll() { m_condObj.notify_all(); }
91
92 private:
93 std::condition_variable m_condObj;
94
95 struct Lock : public std::unique_lock<std::mutex>
96 {
97 Lock(Mutex& mutex) : std::unique_lock<std::mutex>(mutex.m_mutexObj, std::adopt_lock) {}
98 ~Lock() { release(); }
99 };
100 };
101
82102 class Thread
83103 {
84104 public:
104124 private:
105125 static std::unique_ptr<Mutex> m_threadMutex;
106126 static int m_threadCount;
107 #ifdef WIN32
108 HANDLE m_threadObj = 0;
109 #else
110 pthread_t m_threadObj = 0;
111 #endif
127 std::thread::native_handle_type m_threadObj = 0;
112128 bool m_running = false;
113129 bool m_stopped = false;
114130 bool m_autoDestroy = false;
115131
116 #ifdef WIN32
117 static void __cdecl thread_handler(void* object);
118 #else
119 static void *thread_handler(void* object);
120 #endif
132 void thread_handler();
121133 };
122134
123135 #endif
121121 }
122122
123123 // init static vars there
124 GetCurrentTicks();
124 CurrentTicks();
125125 }
126126
127127 int64 Util::JoinInt64(uint32 Hi, uint32 Lo)
492492 c -= a; c -= b; c ^= (b>>15); \
493493 }
494494
495 uint32 hash(register uint8 *k, register uint32 length, register uint32 initval)
495 uint32 hash(uint8 *k, uint32 length, uint32 initval)
496496 // register uint8 *k; /* the key */
497497 // register uint32 length; /* the length of the key */
498498 // register uint32 initval; /* the previous hash, or an arbitrary value */
662662 return -1;
663663 }
664664
665 int64 Util::GetCurrentTicks()
665 int64 Util::CurrentTicks()
666666 {
667667 #ifdef WIN32
668668 static int64 hz=0, hzo=0;
678678 timeval t;
679679 gettimeofday(&t, nullptr);
680680 return (int64)(t.tv_sec) * 1000000ll + (int64)(t.tv_usec);
681 #endif
682 }
683
684 void Util::Sleep(int milliseconds)
685 {
686 #ifdef WIN32
687 ::Sleep(milliseconds);
688 #else
689 usleep(milliseconds * 1000);
681690 #endif
682691 }
683692
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
3232 {
3333 public:
3434 static bool MatchFileExt(const char* filename, const char* extensionList, const char* listSeparator);
35 static int64 GetCurrentTicks();
3635
3736 /*
3837 * Split command line into arguments.
6463 static void SetStandByMode(bool standBy);
6564
6665 static time_t CurrentTime();
66 static int64 CurrentTicks();
67 static void Sleep(int milliseconds);
6768
6869 /* cross platform version of GNU timegm, which is similar to mktime but takes an UTC time as parameter */
6970 static time_t Timegm(tm const *t);
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2014-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2014-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2020 #include "nzbget.h"
2121 #include "Log.h"
2222 #include "Options.h"
23 #include "WorkState.h"
2324 #include "FeedCoordinator.h"
2425 #include "StatMeter.h"
2526 #include "WinConsole.h"
2829 #include "Util.h"
2930 #include "resource.h"
3031
31 extern Options* g_Options;
3232 extern char* (*g_Arguments)[];
3333 extern int g_ArgumentCount;
3434 extern void ExitProc();
3535 extern void Reload();
3636 extern WinConsole* g_WinConsole;
37 extern FeedCoordinator* g_FeedCoordinator;
38 extern StatMeter* g_StatMeter;
3937
4038 #define UM_TRAYICON (WM_USER + 1)
4139 #define UM_QUIT (WM_USER + 2)
6159 ExitProc();
6260 while (g_WinConsole)
6361 {
64 usleep(20 * 1000);
62 Util::Sleep(20);
6563 }
6664 return TRUE;
6765
197195 }
198196 else
199197 {
200 usleep(20 * 1000);
198 Util::Sleep(20);
201199 counter += 20;
202200 if (counter >= 200)
203201 {
289287 case UM_TRAYICON:
290288 if (lParam == WM_LBUTTONUP && !m_doubleClick)
291289 {
292 g_Options->SetPauseDownload(!g_Options->GetPauseDownload());
293 g_Options->SetPausePostProcess(g_Options->GetPauseDownload());
294 g_Options->SetPauseScan(g_Options->GetPauseDownload());
295 g_Options->SetResumeTime(0);
290 g_WorkState->SetPauseDownload(!g_WorkState->GetPauseDownload());
291 g_WorkState->SetPausePostProcess(g_WorkState->GetPauseDownload());
292 g_WorkState->SetPauseScan(g_WorkState->GetPauseDownload());
293 g_WorkState->SetResumeTime(0);
296294 UpdateTrayIcon();
297295 }
298296 else if (lParam == WM_LBUTTONDBLCLK && m_doubleClick)
661659 void WinConsole::ApplyPrefs()
662660 {
663661 ShowWindow(GetConsoleWindow(), m_showConsole ? SW_SHOW : SW_HIDE);
662 g_WorkState->SetPauseFrontend(!m_showConsole);
664663 if (m_showTrayIcon)
665664 {
666665 UpdateTrayIcon();
760759 strncpy(oldTip, m_iconData->szTip, sizeof(m_iconData->szTip));
761760 oldTip[200-1] = '\0';
762761
763 if (g_Options->GetPauseDownload())
762 if (g_WorkState->GetPauseDownload())
764763 {
765764 m_iconData->hIcon = m_pausedIcon;
766765 strncpy(m_iconData->szTip, "NZBGet - paused", sizeof(m_iconData->szTip));
767766 }
768 else if (!g_StatMeter->GetStandBy())
767 else if (g_WorkState->GetDownloading())
769768 {
770769 m_iconData->hIcon = m_workingIcon;
771770 BString<100> tip("NZBGet - downloading at %s", *Util::FormatSpeed(g_StatMeter->CalcCurrentDownloadSpeed()));
955954 BString<1024> path;
956955 CString errmsg;
957956
958 g_Options->SetPauseDownload(true);
959 g_Options->SetPausePostProcess(true);
957 g_WorkState->SetPauseDownload(true);
958 g_WorkState->SetPausePostProcess(true);
960959
961960 char commonAppDataPath[MAX_PATH];
962961 SHGetFolderPath(nullptr, CSIDL_COMMON_APPDATA, nullptr, 0, commonAppDataPath);
972971 while (retry > 0 && FileSystem::DirectoryExists(path) &&
973972 !FileSystem::DeleteDirectoryWithContent(path, errmsg))
974973 {
975 usleep(200 * 1000);
974 Util::Sleep(200);
976975 retry--;
977976 }
978977
29122912 for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it )
29132913 if( !(*it)->matches( testCase ) )
29142914 return false;
2915 return true;
2915 return true;
29162916 }
29172917 };
29182918
140140 virtual bool RepairData(u32 inputindex, size_t blocklength) { return false; }
141141
142142 protected:
143 std::ostream& cout;
144 std::ostream& cerr;
145
143146 ParHeaders* headers; // Headers
144147 bool alreadyloaded; // Already loaded ?
145148 CommandLine::NoiseLevel noiselevel; // OnScreen display
195198 u64 totalsize; // Total data size
196199
197200 bool cancelled; // repair cancelled
198
199 std::ostream& cout;
200 std::ostream& cerr;
201201 };
202202
203203 } // end namespace Par2
229229
230230 // Allocate the left hand matrix
231231
232 leftmatrix = new G[outcount * incount];
233 memset(leftmatrix, 0, outcount * incount * sizeof(G));
232 leftmatrix = new G[outcount * incount]();
234233
235234 // Allocate the right hand matrix only if we are recovering
236235
237236 G *rightmatrix = 0;
238237 if (datamissing > 0)
239238 {
240 rightmatrix = new G[outcount * outcount];
241 memset(rightmatrix, 0, outcount *outcount * sizeof(G));
239 rightmatrix = new G[outcount * outcount]();
242240 }
243241
244242 // Fill in the two matrices:
303303 buildroot-debug|buildroot-debug-strip)
304304 LDFLAGS="-static $STRIP" \
305305 CXXFLAGS="-std=c++14 -g -fasynchronous-unwind-tables" \
306 LIBS="-lcrypto -ldl -lz $LUBACKTRACE" \
306 LIBS="-lcrypto -ldl -lz -Wl,--whole-archive -lpthread -Wl,--no-whole-archive $LUBACKTRACE" \
307307 ./configure --disable-dependency-tracking --host=$ARCH --enable-debug
308308 ;;
309309 buildroot-release|buildroot-release-nostrip)
310310 LDFLAGS="-static $STRIP" \
311311 CXXFLAGS="-std=c++14 -O2 $DEBUG" \
312 LIBS="-lcrypto -ldl -lz" \
312 LIBS="-lcrypto -ldl -lz -Wl,--whole-archive -lpthread -Wl,--no-whole-archive" \
313313 ./configure --disable-dependency-tracking --host=$ARCH
314314 ;;
315315 ndk-debug|ndk-debug-strip)
202202 <ClCompile Include="daemon\main\Maintenance.cpp" />
203203 <ClCompile Include="daemon\main\nzbget.cpp" />
204204 <ClCompile Include="daemon\main\Options.cpp" />
205 <ClCompile Include="daemon\main\WorkState.cpp" />
205206 <ClCompile Include="daemon\main\Scheduler.cpp" />
206207 <ClCompile Include="daemon\main\StackTrace.cpp" />
207208 <ClCompile Include="daemon\nntp\ArticleDownloader.cpp" />
307308 <ClInclude Include="daemon\main\Maintenance.h" />
308309 <ClInclude Include="daemon\main\nzbget.h" />
309310 <ClInclude Include="daemon\main\Options.h" />
311 <ClInclude Include="daemon\main\WorkState.h" />
310312 <ClInclude Include="daemon\main\Scheduler.h" />
311313 <ClInclude Include="daemon\main\StackTrace.h" />
312314 <ClInclude Include="daemon\nntp\ArticleDownloader.h" />
9090 import sys
9191 import datetime
9292 import smtplib
93 from urllib2 import quote
9493
9594 from email.mime.text import MIMEText
96 try:
97 from xmlrpclib import ServerProxy # python 2
98 except ImportError:
99 from xmlrpc.client import ServerProxy # python 3
95 from email.header import Header
96 try: # python 2
97 from urllib2 import quote
98 from xmlrpclib import ServerProxy
99 except ImportError: # python 3
100 from urllib.parse import quote
101 from xmlrpc.client import ServerProxy
100102
101103 # Exit codes used by NZBGet
102104 POSTPROCESS_SUCCESS=93
253255 text += '\n%s\t%s\t%s' % (entry['Kind'], datetime.datetime.fromtimestamp(int(entry['Time'])), entry['Text'])
254256
255257 # Create message
256 msg = MIMEText(text)
257 msg['Subject'] = subject
258 print('[DETAIL] Creating Email')
259 msg = MIMEText(text.encode('utf-8'), 'plain', 'utf-8')
260 msg['Subject'] = Header(subject, 'utf-8')
258261 msg['From'] = os.environ['NZBPO_FROM']
259262 msg['To'] = os.environ['NZBPO_TO']
260263 msg['Date'] = datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S +0000")
3535 import os
3636 import sys
3737 import datetime
38 from urllib2 import quote # python 2/3
3938
4039 try:
4140 from xmlrpclib import ServerProxy # python 2
41 from urllib2 import quote
4242 except ImportError:
4343 from xmlrpc.client import ServerProxy # python 3
44 from urllib.parse import quote
4445
4546 # Exit codes used by NZBGet
4647 POSTPROCESS_SUCCESS=93
00 import pytest
11 import subprocess
22 import os
3 import sys
43 import time
54 import shutil
65 import base64
2120 nserv_datadir = nzbget_srcdir + '/tests/testdata/nserv.temp'
2221
2322 sevenzip_bin = distutils.spawn.find_executable('7z')
24 if sevenzip_bin == None:
23 if sevenzip_bin is None:
2524 sevenzip_bin = nzbget_srcdir + '/7z' + exe_ext
2625
2726 par2_bin = distutils.spawn.find_executable('par2')
28 if par2_bin == None:
27 if par2_bin is None:
2928 par2_bin = nzbget_srcdir + '/par2' + exe_ext
3029
3130 has_failures = False
109108 os.rename(nzbget_maindir, nzbget_maindir + '.old')
110109 shutil.rmtree(nzbget_maindir + '.old')
111110 completed = True
112 except:
111 except Exception:
113112 if attempt > 20:
114113 raise
115114 attempt += 1
164163 for x in range(0, 3):
165164 try:
166165 stat = self.api.status()
167 except:
166 except Exception:
168167 time.sleep(0.5)
169168
170 if stat == None:
169 if stat is None:
171170 raise Exception('Could not start nzbget')
172171 print('Started')
173172
174173 def append_nzb(self, nzb_name, nzb_content, unpack = None, dupekey = '', dupescore = 0, dupemode = 'FORCE', params = None):
175174 nzbcontent64 = base64.standard_b64encode(nzb_content)
176 if params == None:
175 if params is None:
177176 params = []
178177 if unpack == True:
179178 params.append(('*unpack:', 'yes'))
00 import os
11 import shutil
22 import subprocess
3 import random
4 import array
53 import pytest
64
75
5755 os.makedirs(bigdir)
5856
5957 f = open(bigdir + '/' + str(sizemb) + 'mb.dat', 'wb')
60 for n in xrange(sizemb / partmb):
61 print('Writing block %i from %i' % (n + 1, sizemb / partmb))
58 for n in xrange(sizemb // partmb):
59 print('Writing block %i from %i' % (n + 1, sizemb // partmb))
6260 f.write(os.urandom(partmb * 1024 * 1024))
6361 f.close()
6462
00 import os
1 import shutil
21 import subprocess
32 import pytest
43
00 nzbget_options = ['SaveQueue=no']
11
22 import os
3 import time
43
54 nzbget = None
65
1817 nzbget.api.pausedownload()
1918
2019 nzbget_srcdir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
21 testdata_dir = nzbget_srcdir + '/tests/testdata'
2220 fullfilename = nzbget_srcdir + '/tests/testdata/nzbfile/plain.nzb'
2321 in_file = open(fullfilename, 'r')
2422 nzb_content = in_file.read()
99
1010 nserv_datadir = pytest.config.getini('nserv_datadir')
1111 nzbget_bin = pytest.config.getini('nzbget_bin')
12 sevenzip_bin = pytest.config.getini('sevenzip_bin')
1312
1413 if not os.path.exists(nserv_datadir):
1514 print('Creating nserv datadir')
8181 shutil.copyfile(testdata_dir + '/rarrenamer/testfile3.part02.rar', nserv_datadir + '/rar3ignoreext/testfile3-2.cbr')
8282 shutil.copyfile(testdata_dir + '/rarrenamer/testfile3.part03.rar', nserv_datadir + '/rar3ignoreext/testfile3-3.cbr')
8383
84 if not os.path.exists(nserv_datadir + '/parjoin1.nzb'):
85 os.makedirs(nserv_datadir + '/parjoin1')
86 shutil.copyfile(testdata_dir + '/parchecker/testfile.dat', nserv_datadir + '/parjoin1/testfile.dat')
87 shutil.copyfile(testdata_dir + '/parchecker/testfile.nfo', nserv_datadir + '/parjoin1/testfile.nfo')
88 shutil.copyfile(testdata_dir + '/parchecker/testfile.par2', nserv_datadir + '/parjoin1/testfile.par2')
89 shutil.copyfile(testdata_dir + '/parchecker/testfile.vol00+1.PAR2', nserv_datadir + '/parjoin1/testfile.vol00+1.PAR2')
90 split_test_file(nserv_datadir + '/parjoin1', 'testfile.dat', 50244);
91
92 if not os.path.exists(nserv_datadir + '/parjoin2.nzb'):
93 os.makedirs(nserv_datadir + '/parjoin2')
94 shutil.copyfile(testdata_dir + '/parchecker/testfile.dat', nserv_datadir + '/parjoin2/testfile.dat')
95 shutil.copyfile(testdata_dir + '/parchecker/testfile.nfo', nserv_datadir + '/parjoin2/testfile.nfo')
96 shutil.copyfile(testdata_dir + '/parchecker/testfile.par2', nserv_datadir + '/parjoin2/testfile.par2')
97 shutil.copyfile(testdata_dir + '/parchecker/testfile.vol00+1.PAR2', nserv_datadir + '/parjoin2/testfile.vol00+1.PAR2')
98 split_test_file(nserv_datadir + '/parjoin2', 'testfile.dat', 50244);
99 os.rename(nserv_datadir + '/parjoin2/testfile.dat.001', nserv_datadir + '/parjoin2/renamed.001')
100 os.rename(nserv_datadir + '/parjoin2/testfile.dat.002', nserv_datadir + '/parjoin2/renamed.002')
101 os.rename(nserv_datadir + '/parjoin2/testfile.dat.003', nserv_datadir + '/parjoin2/renamed.003')
102
84103 if 0 != subprocess.call([nzbget_bin, '--nserv', '-d', nserv_datadir, '-v', '2', '-z', '3000', '-q']):
85104 pytest.exit('Test file generation failed')
105
106 if not os.path.exists(nserv_datadir + '/parjoin3.nzb'):
107 os.makedirs(nserv_datadir + '/parjoin3')
108 create_test_file(nserv_datadir + '/parjoin3', None, 20, 1)
109 os.chdir(nserv_datadir + '/parjoin3')
110 if 0 != subprocess.call([par2_bin, 'c', '-b100', 'parrename.par2', '*']):
111 pytest.exit('Test file generation failed')
112 split_test_file(nserv_datadir + '/parjoin3', '20mb.dat', 7*1024*1024);
113 os.rename(nserv_datadir + '/parjoin3/20mb.dat.001', nserv_datadir + '/parjoin3/renamed.001')
114 os.rename(nserv_datadir + '/parjoin3/20mb.dat.002', nserv_datadir + '/parjoin3/renamed.002')
115 os.rename(nserv_datadir + '/parjoin3/20mb.dat.003', nserv_datadir + '/parjoin3/renamed.003')
116 if 0 != subprocess.call([nzbget_bin, '--nserv', '-d', nserv_datadir, '-v', '2', '-z', '100000', '-q']):
117 pytest.exit('Test file generation failed')
86118
87119 if not os.path.exists(nserv_datadir + '/rarrename3sm'):
88120 os.makedirs(nserv_datadir + '/rarrename3sm')
92124 os.chdir(nserv_datadir + '/rarrename3sm')
93125 if 0 != subprocess.call([par2_bin, 'c', '-b100', 'parrename.par2', '*']):
94126 pytest.exit('Test file generation failed')
95 if 0 != subprocess.call([nzbget_bin, '--nserv', '-d', nserv_datadir, '-v', '2', '-z', '500', '-q']):
96 pytest.exit('Test file generation failed')
127 if 0 != subprocess.call([nzbget_bin, '--nserv', '-d', nserv_datadir, '-v', '2', '-z', '500', '-q']):
128 pytest.exit('Test file generation failed')
97129
98130 if not os.path.exists(nserv_datadir + '/obfuscated1.nzb'):
99131 create_test_file(nserv_datadir + '/obfuscated1', sevenzip_bin, 5, 1)
162194 f.write(os.urandom(partmb * 1024 * 1024))
163195 f.close()
164196
165 if 0 != subprocess.call([sevenzip_bin, 'a', bigdir + '/' + str(sizemb) + 'mb.7z', '-mx=0', '-v' + str(partmb) + 'm', bigdir + '/' + str(sizemb) + 'mb.dat']):
166 pytest.exit('Test file generation failed')
167
168 os.remove(bigdir + '/' + str(sizemb) + 'mb.dat')
197 if sevenzip_bin != None:
198 if 0 != subprocess.call([sevenzip_bin, 'a', bigdir + '/' + str(sizemb) + 'mb.7z', '-mx=0', '-v' + str(partmb) + 'm', bigdir + '/' + str(sizemb) + 'mb.dat']):
199 pytest.exit('Test file generation failed')
200
201 os.remove(bigdir + '/' + str(sizemb) + 'mb.dat')
202
203 def split_test_file(filedir, filename, partsize):
204 print('Splitting test file ' + filename)
205
206 inp = open(filedir + '/' + filename, 'rb')
207 inp.seek(0,2) # move the cursor to the end of the file
208 size = inp.tell()
209 inp.seek(0,0) # move the cursor to the start of the file
210
211 written = 0
212 part = 1
213 while (written < size):
214 block = inp.read(partsize)
215 written += partsize
216 f = open(filedir + '/' + filename + '.' + str(part).zfill(3), 'wb')
217 f.write(block)
218 f.close()
219 part += 1
220 inp.close()
221
222 os.remove(filedir + '/' + filename)
8282
8383 def test_rename_obf2(nserv, nzbget):
8484 hist = nzbget.download_nzb('obfuscated2.nzb', unpack=True)
85 assert hist['Status'] == 'SUCCESS/UNPACK'
85 assert hist['Status'] == 'SUCCESS/UNPACK'
86
87 def test_parjoin_1(nserv, nzbget):
88 hist = nzbget.download_nzb('parjoin1.nzb', unpack=True)
89 assert hist['Status'] == 'SUCCESS/PAR'
90
91 def test_parjoin_2(nserv, nzbget):
92 hist = nzbget.download_nzb('parjoin2.nzb', unpack=True)
93 assert hist['Status'] == 'SUCCESS/PAR'
94
95 def test_parjoin_3(nserv, nzbget):
96 hist = nzbget.download_nzb('parjoin3.nzb', unpack=True)
97 assert hist['Status'] == 'SUCCESS/PAR'
1414 def test_rename_obf2(nserv, nzbget):
1515 hist = nzbget.download_nzb('obfuscated2.nzb', unpack=True)
1616 assert hist['Status'] == 'SUCCESS/HEALTH'
17
18 def test_parjoin_1(nserv, nzbget):
19 hist = nzbget.download_nzb('parjoin1.nzb', unpack=True)
20 assert hist['Status'] == 'SUCCESS/UNPACK'
21
22 def test_parjoin_2(nserv, nzbget):
23 hist = nzbget.download_nzb('parjoin2.nzb', unpack=True)
24 assert hist['Status'] == 'WARNING/DAMAGED'
25
26 def test_parjoin_3(nserv, nzbget):
27 hist = nzbget.download_nzb('parjoin3.nzb', unpack=True)
28 assert hist['Status'] == 'WARNING/DAMAGED'
8282 assert hist['Status'] == 'SUCCESS/HEALTH'
8383 for entry in nzbget.api.loadlog(hist['ID'], 0, 10000):
8484 assert entry['Kind'] != 'ERROR', entry['Text']
85
86 def test_parjoin_1(nserv, nzbget):
87 hist = nzbget.download_nzb('parjoin1.nzb', unpack=True)
88 assert hist['Status'] == 'SUCCESS/PAR'
89
90 def test_parjoin_2(nserv, nzbget):
91 hist = nzbget.download_nzb('parjoin2.nzb', unpack=True)
92 assert hist['Status'] == 'SUCCESS/PAR'
93
94 def test_parjoin_3(nserv, nzbget):
95 hist = nzbget.download_nzb('parjoin3.nzb', unpack=True)
96 assert hist['Status'] == 'SUCCESS/PAR'
8282 assert hist['Status'] == 'SUCCESS/HEALTH'
8383 for entry in nzbget.api.loadlog(hist['ID'], 0, 10000):
8484 assert entry['Kind'] != 'ERROR', entry['Text']
85
86 def test_parjoin_1(nserv, nzbget):
87 hist = nzbget.download_nzb('parjoin1.nzb', unpack=True)
88 assert hist['Status'] == 'SUCCESS/PAR'
89
90 def test_parjoin_2(nserv, nzbget):
91 hist = nzbget.download_nzb('parjoin2.nzb', unpack=True)
92 assert hist['Status'] == 'SUCCESS/PAR'
93
94 def test_parjoin_3(nserv, nzbget):
95 hist = nzbget.download_nzb('parjoin3.nzb', unpack=True)
96 assert hist['Status'] == 'SUCCESS/PAR'
0 nzbget_options = ['ParRename=yes', 'RarRename=yes', 'ParCheck=auto', 'DirectRename=yes', 'ParScan=full']
1
2 def test_parjoin_1(nserv, nzbget):
3 hist = nzbget.download_nzb('parjoin1.nzb', unpack=True)
4 assert hist['Status'] == 'SUCCESS/PAR'
5
6 def test_parjoin_2(nserv, nzbget):
7 hist = nzbget.download_nzb('parjoin2.nzb', unpack=True)
8 assert hist['Status'] == 'SUCCESS/PAR'
9
10 def test_parjoin_3(nserv, nzbget):
11 hist = nzbget.download_nzb('parjoin3.nzb', unpack=True)
12 assert hist['Status'] == 'SUCCESS/PAR'
0 nzbget_options = ['ParRename=yes', 'RarRename=yes', 'ParCheck=auto', 'DirectRename=no', 'ParScan=full']
1
2 def test_parjoin_1(nserv, nzbget):
3 hist = nzbget.download_nzb('parjoin1.nzb', unpack=True)
4 assert hist['Status'] == 'SUCCESS/PAR'
5
6 def test_parjoin_2(nserv, nzbget):
7 hist = nzbget.download_nzb('parjoin2.nzb', unpack=True)
8 assert hist['Status'] == 'SUCCESS/PAR'
9
10 def test_parjoin_3(nserv, nzbget):
11 hist = nzbget.download_nzb('parjoin3.nzb', unpack=True)
12 assert hist['Status'] == 'SUCCESS/PAR'
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2017-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
3535 EEditAction action, const char* args) { return false; }
3636 virtual void HistoryChanged() {}
3737 virtual void Save() {};
38 virtual void SaveChanged() {}
3839 };
3940
4041 TEST_CASE("Direct-unpack simple", "[Rar][DirectUnpack][Unrar][Slow][TestData]")
7374 ((DirectUnpack*)nzbPtr->GetUnpackThread())->NzbDownloaded(downloadQueue, nzbPtr);
7475 break;
7576 }
76 usleep(50*1000);
77 Util::Sleep(50);
7778 }
7879
7980 while (nzbPtr->GetDirectUnpackStatus() == NzbInfo::nsRunning)
8081 {
81 usleep(20 * 1000);
82 Util::Sleep(20);
8283 }
8384
8485 REQUIRE(nzbPtr->GetDirectUnpackStatus() == NzbInfo::nsSuccess);
128129 ((DirectUnpack*)nzbPtr->GetUnpackThread())->NzbDownloaded(downloadQueue, nzbPtr);
129130 break;
130131 }
131 usleep(50 * 1000);
132 Util::Sleep(50);
132133 }
133134
134135 while (nzbPtr->GetDirectUnpackStatus() == NzbInfo::nsRunning)
135136 {
136 usleep(20 * 1000);
137 Util::Sleep(20);
137138 }
138139
139140 REQUIRE(nzbPtr->GetDirectUnpackStatus() == NzbInfo::nsSuccess);
6464 void TestCleanup()
6565 {
6666 // If tests were run (via "TestMain") the Catch-framework does clean up automatically.
67 // Hoeweve if no tests were run the global objects remain alive and causing memory leak
67 // However, if no tests were run, the global objects remain alive and causing memory leak
6868 // detection reports. Therefore we clean up the Catch-framework when we don't run any tests.
6969 Catch::cleanUp();
7070 }
2121 #include <iostream>
2222 #include "catch.h"
2323
24 #include "Util.h"
2425 #include "FileSystem.h"
2526 #include "TestUtil.h"
2627
9293 FileSystem::DeleteDirectoryWithContent(workDir.c_str(), errmsg);
9394 while (FileSystem::DirectoryExists(workDir.c_str()) && retries > 0)
9495 {
95 usleep(1000 * 100);
96 Util::Sleep(100);
9697 retries--;
9798 FileSystem::DeleteDirectoryWithContent(workDir.c_str(), errmsg);
9899 }
4646 REQUIRE(!strcmp(str2, "0123456789 Hi, Worl"));
4747
4848 BString<20> str3;
49 strncpy(str3, "Hello, 0123456789 world", str3.Capacity());
49 memcpy(str3, "Hello, 0123456789 world", str3.Capacity());
5050 str3[str3.Capacity()] = '\0';
5151 REQUIRE(!strcmp(str3, "Hello, 0123456789 w"));
5252
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2012-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2012-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
3131
3232 // Properties (public)
3333 this.options;
34 this.postParamConfig;
34 this.postParamConfig = [];
3535 this.categories = [];
3636 this.restricted = false;
3737
137137
138138 readWebSettings(config);
139139
140 var serverConfig = readConfigTemplate(serverTemplateData[0].Template, undefined, HIDDEN_SECTIONS, '', '');
140 var serverConfig = readConfigTemplate(serverTemplateData[0].Template, undefined, HIDDEN_SECTIONS, '');
141141 mergeValues(serverConfig.sections, serverValues);
142142 config.push(serverConfig);
143143
147147 var scriptName = serverTemplateData[i].Name;
148148 var scriptConfig = readConfigTemplate(serverTemplateData[i].Template, undefined, HIDDEN_SECTIONS, scriptName + ':');
149149 scriptConfig.scriptName = scriptName;
150 scriptConfig.id = scriptName.replace(/ |\/|\\|[\.|$|\:|\*]/g, '_');
150 scriptConfig.id = Util.makeId(scriptName);
151151 scriptConfig.name = scriptName.substr(0, scriptName.lastIndexOf('.')) || scriptName; // remove file extension
152152 scriptConfig.name = scriptConfig.name.replace(/\\/, ' \\ ').replace(/\//, ' / ');
153153 scriptConfig.shortName = shortScriptName(scriptName);
185185 webValues.push({Name: optname, Value: value.toString()});
186186 }
187187
188 var webConfig = readConfigTemplate(webTemplate, undefined, '', '', '');
188 var webConfig = readConfigTemplate(webTemplate, undefined, '', '');
189189 mergeValues(webConfig.sections, webValues);
190190 config.push(webConfig);
191191 }
215215 {
216216 var section = {};
217217 section.name = line.substr(4, line.length - 8).trim();
218 section.id = (nameprefix + section.name).replace(/ |\/|\\|[\.|$|\:|\*]/g, '_');
218 section.id = Util.makeId(nameprefix + section.name);
219219 section.options = [];
220220 description = '';
221221 section.hidden = !(hiddensections === undefined || (hiddensections.indexOf(section.name) == -1)) ||
252252 // bad template file; create default section.
253253 section = {};
254254 section.name = 'OPTIONS';
255 section.id = (nameprefix + section.name).replace(/ |\/|[\.|$|\:|\*]/g, '_');
255 section.id = Util.makeId(nameprefix + section.name);
256256 section.options = [];
257257 description = '';
258258 config.sections.push(section);
456456 if (data[i].PostScript || data[i].QueueScript)
457457 {
458458 var scriptName = data[i].Name;
459 var sectionId = (scriptName + ':').replace(/ |\/|[\.|$|\:|\*]/g, '_');
459 var sectionId = Util.makeId(scriptName + ':');
460460 var option = {};
461461 option.name = scriptName + ':';
462462 option.caption = shortScriptName(scriptName);
512512
513513 // State
514514 var config = null;
515 var values;
516515 var filterText = '';
517516 var lastSection;
518517 var reloadTime;
769768 value = option.defvalue;
770769 }
771770
772 option.formId = (option.name.indexOf(':') == -1 ? 'S_' : '') + option.name.replace(/ |\/|\\|[\.|$|\:|\*]/g, '_');
771 option.formId = (option.name.indexOf(':') == -1 ? 'S_' : '') + Util.makeId(option.name);
773772
774773 var caption = option.caption;
775774 if (section.multi)
959958
960959 if (!hasmore)
961960 {
962 var nextid = hasoptions ? multiid + 1 : 1;
963961 html += '<div class="' + section.id + '">';
964962 html += '<button type="button" class="btn config-add ' + section.id + ' multiset" onclick="Config.addSet(\'' + setname + '\',\'' + section.id +
965963 '\')">Add ' + (hasoptions ? 'another ' : '') + setname + '</button>';
11601158
11611159 this.switchClick = function(control)
11621160 {
1163 var state = $(control).val().toLowerCase();
11641161 $('.btn', $(control).parent()).removeClass('btn-primary');
11651162 $(control).addClass('btn-primary');
11661163
26672664 {
26682665 e.preventDefault();
26692666
2670 var selectedSections = [];
26712667 var checkedRows = $SectionTable.fasttable('checkedRows');
26722668 var checkedCount = $SectionTable.fasttable('checkedCount');
26732669 if (checkedCount === 0)
30012997 e.preventDefault();
30022998 var kind = $(this).attr('data-kind');
30032999 var script = PackageInfo['install-script'];
3004 var info = PackageInfo['install-' + kind + '-info'];
30053000
30063001 if (!script)
30073002 {
31373132 var $ExecScriptDialog_Status;
31383133
31393134 // State
3140 var logReceived = false;
31413135 var visible = false;
31423136
31433137 this.init = function()
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2012-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2012-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
4343 var notification = null;
4444 var updateTabInfo;
4545 var groups;
46 var urls;
4746 var nameColumnWidth = null;
4847 var cached = false;
4948 var lastDownloadRate;
396395 {
397396 var checkedRows = $DownloadsTable.fasttable('checkedRows');
398397
399 var hasIDs = false;
400398 var checkedEditIDs = [];
401399 for (var i = 0; i < groups.length; i++)
402400 {
895893
896894 this.buildPriority = function(group)
897895 {
898 var priority = group.MaxPriority
896 var priority = group.MaxPriority;
899897 var text;
900898
901899 if (priority >= 900) text = ' <div class="icon-circle-red" title="Force priority"></div>';
11211119 var deletePark = $('#DownloadsDeleteConfirmDialog_DeletePark', dialog).is(':checked');
11221120 var deleteDirect = $('#DownloadsDeleteConfirmDialog_DeleteDirect', dialog).is(':checked');
11231121 var deleteDupe = $('#DownloadsDeleteConfirmDialog_DeleteDupe', dialog).is(':checked');
1124 var deleteFinal = $('#DownloadsDeleteConfirmDialog_DeleteFinal', dialog).is(':checked');
1122 //var deleteFinal = $('#DownloadsDeleteConfirmDialog_DeleteFinal', dialog).is(':checked');
11251123 var command = deletePark ? "GroupParkDelete" : (deleteDirect ? 'GroupDelete' : (deleteDupe ? 'GroupDupeDelete' : 'GroupFinalDelete'));
11261124 actionCallback(command);
11271125 }
11391137
11401138 /*** DOWNLOADS ACTION MENU *************************************************************************/
11411139
1142 var DownloadsActionsMenu = (new function()
1140 var DownloadsActionsMenu = (new function($)
11431141 {
11441142 'use strict'
11451143
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2012-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2012-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
130130
131131 curGroup = group;
132132
133 var status = DownloadsUI.buildStatus(group);
134 var age = Util.formatAge(group.MinPostTime + UISettings.timeZoneCorrection*60*60);
135133 var size = Util.formatSizeMB(group.FileSizeMB, group.FileSizeLo);
136134 var remaining = Util.formatSizeMB(group.RemainingSizeMB-group.PausedSizeMB, group.RemainingSizeLo-group.PausedSizeLo);
137135 var pausedSize = Util.formatSizeMB(group.PausedSizeMB, group.PausedSizeLo);
138 var estimated = group.Status === 'PAUSED' ? '' : (Status.status.DownloadRate > 0 ? Util.formatTimeHMS((group.RemainingSizeMB-group.PausedSizeMB)*1024/(Status.status.DownloadRate/1024)) : '');
139136 var completion = group.SuccessArticles + group.FailedArticles > 0 ? Util.round0(group.SuccessArticles * 100.0 / (group.SuccessArticles + group.FailedArticles)) + '%' : '--';
140137 if (group.FailedArticles > 0 && completion === '100%')
141138 {
14671464
14681465 /*** EDIT HISTORY DIALOG *************************************************************************/
14691466
1470 var HistoryEditDialog = (new function()
1467 var HistoryEditDialog = (new function($)
14711468 {
14721469 'use strict'
14731470
16191616 {
16201617 completion = '99.9%';
16211618 }
1622 var time = Util.formatTimeHMS(hist.DownloadTimeSec + hist.PostTotalTimeSec);
16231619
16241620 var table = '';
16251621 table += '<tr><td><a href="#" id="HistoryEdit_TimeStats" data-tab="HistoryEdit_TimeStatsTab" title="Size and time statistics">Total '+
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2012-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2012-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
199199 return this.each(function()
200200 {
201201 var $this = $(this);
202 var data = $this.data('fasttable');
203202
204203 // Namespacing FTW
205204 $(window).unbind('.fasttable');
737736 function toggleCheck(data, id)
738737 {
739738 var checkedRows = data.checkedRows;
740 var index = checkedRows[id];
741739 if (checkedRows[id])
742740 {
743741 checkedRows[id] = undefined;
819817
820818 function validateChecks(data)
821819 {
822 var filteredContent = data.filteredContent;
823820 var checkedRows = data.checkedRows;
824821 data.checkedRows = {}
825822 data.checkedCount = 0;
12611258 'use strict';
12621259
12631260 this.source;
1264 this.len;
1265 this.p;
1261 this.len = 0;
1262 this.p = 0;
12661263
12671264 this.initLexer = function(source)
12681265 {
15101507 return value;
15111508 }
15121509
1513 this.nameMap;
1510 this.nameMap = undefined;
15141511 this.buildNameMap = function(data)
15151512 {
15161513 this.nameMap = {};
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2013-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2013-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
409409 var $Splitter;
410410 var $FilterInput;
411411 var $FilterBlock;
412 var $FilterLines;
413412 var $FilterNumbers;
414413 var $PreviewBlock;
415414 var $ModalBody;
453452 $('#FeedFilterDialog_Save').click(save);
454453 $FilterInput = $('#FeedFilterDialog_FilterInput');
455454 $FilterBlock = $('#FeedFilterDialog_FilterBlock');
456 $FilterLines = $('#FeedFilterDialog_FilterLines');
457455 $FilterNumbers = $('#FeedFilterDialog_FilterNumbers');
458456 $PreviewBlock = $('#FeedFilterDialog_PreviewBlock');
459457 $ModalBody = $('.modal-body', $FeedFilterDialog);
895893 var lines = tarea.value.split("\n");
896894
897895 // calculate start/end
898 var startPos = 0, endPos = tarea.value.length;
896 var startPos = 0;
899897 for (var x = 0; x < lines.length; x++)
900898 {
901899 if (x == lineNum)
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2012-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2012-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
653653
654654 /*** HISTORY ACTION MENU *************************************************************************/
655655
656 var HistoryActionsMenu = (new function()
656 var HistoryActionsMenu = (new function($)
657657 {
658658 'use strict'
659659
11 <!--
22 * This file is part of nzbget. See <http://nzbget.net>.
33 *
4 * Copyright (C) 2012-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
4 * Copyright (C) 2012-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
7777 // prevent Android browsers from using of icon designed for iOS
7878 if (window.navigator.userAgent.match(/iPhone/i) || window.navigator.userAgent.match(/iPad/i))
7979 {
80 document.write('<link rel="apple-touch-icon" href="img/favicon-256x256-opaque.png">');
80 document.write('<link rel="apple-touch-icon" href="img/favicon-256x256-opaque.png">'); // lgtm [js/eval-like-call]
8181 }
8282 </script>
8383 <!-- android -->
319319 <th width="102px">Status</th>
320320 <th class="dropafter-cell" id="DownloadsTable_Name"><a href="#" onclick="Downloads.sort('name', event)">Name</a></th>
321321 <th id="DownloadsTable_Category"><a href="#" onclick="Downloads.sort('category', event)">Category</a></th>
322 <th class="dropafter-cell" width="30px" class="text-right"><a href="#" onclick="Downloads.sort('age', event)">Age</a></th>
322 <th class="dropafter-cell text-right" width="30px"><a href="#" onclick="Downloads.sort('age', event)">Age</a></th>
323323 <th width="120px"><div style="float:left; position:relative; width:50%; text-align:left;"><a href="#"onclick="Downloads.sort('size', event)">Size</a></div><div style="float:right; position:relative; width:50%; text-align:right;"><a href="#" onclick="Downloads.sort('left', event)">Left</a></div></th>
324324 <th width="60px" class="text-right">Est. Time</th>
325325 </tr>
29352935 <strong>Please select records first</strong>
29362936 </div>
29372937
2938 <div id="Notif_History_Deleted" data-duration="1000" class="alert alert-inverse alert-center alert-center-small hide">
2939 <strong>Deleted</strong>
2940 </div>
2941
29422938 <div id="Notif_History_Cleared" data-duration="1000" class="alert alert-inverse alert-center alert-center-small hide">
29432939 <strong>Cleared</strong>
29442940 </div>
0 /*!
1 * Bootstrap v2.0.3
2 *
3 * Copyright 2012 Twitter, Inc
4 * Licensed under the Apache License v2.0
5 * http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Designed and built with all the love in the world @twitter by @mdo and @fat.
8 */
9
10 article,
11 aside,
12 details,
13 figcaption,
14 figure,
15 footer,
16 header,
17 hgroup,
18 nav,
19 section {
20 display: block;
21 }
22
23 audio,
24 canvas,
25 video {
26 display: inline-block;
27 *display: inline;
28 *zoom: 1;
29 }
30
31 audio:not([controls]) {
32 display: none;
33 }
34
35 html {
36 font-size: 100%;
37 -webkit-text-size-adjust: 100%;
38 -ms-text-size-adjust: 100%;
39 }
40
41 a:focus {
42 outline: thin dotted #333;
43 outline: 5px auto -webkit-focus-ring-color;
44 outline-offset: -2px;
45 }
46
47 a:hover,
48 a:active {
49 outline: 0;
50 }
51
52 sub,
53 sup {
54 position: relative;
55 font-size: 75%;
56 line-height: 0;
57 vertical-align: baseline;
58 }
59
60 sup {
61 top: -0.5em;
62 }
63
64 sub {
65 bottom: -0.25em;
66 }
67
68 img {
69 max-width: 100%;
70 vertical-align: middle;
71 border: 0;
72 -ms-interpolation-mode: bicubic;
73 }
74
75 button,
76 input,
77 select,
78 textarea {
79 margin: 0;
80 font-size: 100%;
81 vertical-align: middle;
82 }
83
84 button,
85 input {
86 *overflow: visible;
87 line-height: normal;
88 }
89
90 button::-moz-focus-inner,
91 input::-moz-focus-inner {
92 padding: 0;
93 border: 0;
94 }
95
96 button,
97 input[type="button"],
98 input[type="reset"],
99 input[type="submit"] {
100 cursor: pointer;
101 -webkit-appearance: button;
102 }
103
104 input[type="search"] {
105 -webkit-box-sizing: content-box;
106 -moz-box-sizing: content-box;
107 box-sizing: content-box;
108 -webkit-appearance: textfield;
109 }
110
111 input[type="search"]::-webkit-search-decoration,
112 input[type="search"]::-webkit-search-cancel-button {
113 -webkit-appearance: none;
114 }
115
116 textarea {
117 overflow: auto;
118 vertical-align: top;
119 }
120
121 .clearfix {
122 *zoom: 1;
123 }
124
125 .clearfix:before,
126 .clearfix:after {
127 display: table;
128 content: "";
129 }
130
131 .clearfix:after {
132 clear: both;
133 }
134
135 .hide-text {
136 font: 0/0 a;
137 color: transparent;
138 text-shadow: none;
139 background-color: transparent;
140 border: 0;
141 }
142
143 .input-block-level {
144 display: block;
145 width: 100%;
146 min-height: 28px;
147 -webkit-box-sizing: border-box;
148 -moz-box-sizing: border-box;
149 -ms-box-sizing: border-box;
150 box-sizing: border-box;
151 }
152
153 body {
154 margin: 0;
155 font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
156 font-size: 13px;
157 line-height: 18px;
158 color: #333333;
159 background-color: #ffffff;
160 }
161
162 a {
163 color: #0088cc;
164 text-decoration: none;
165 }
166
167 a:hover {
168 color: #005580;
169 text-decoration: underline;
170 }
171
172 .row {
173 margin-left: -20px;
174 *zoom: 1;
175 }
176
177 .row:before,
178 .row:after {
179 display: table;
180 content: "";
181 }
182
183 .row:after {
184 clear: both;
185 }
186
187 [class*="span"] {
188 float: left;
189 margin-left: 20px;
190 }
191
192 .container,
193 .navbar-fixed-top .container,
194 .navbar-fixed-bottom .container {
195 width: 940px;
196 }
197
198 .span12 {
199 width: 940px;
200 }
201
202 .span11 {
203 width: 860px;
204 }
205
206 .span10 {
207 width: 780px;
208 }
209
210 .span9 {
211 width: 700px;
212 }
213
214 .span8 {
215 width: 620px;
216 }
217
218 .span7 {
219 width: 540px;
220 }
221
222 .span6 {
223 width: 460px;
224 }
225
226 .span5 {
227 width: 380px;
228 }
229
230 .span4 {
231 width: 300px;
232 }
233
234 .span3 {
235 width: 220px;
236 }
237
238 .span2 {
239 width: 140px;
240 }
241
242 .span1 {
243 width: 60px;
244 }
245
246 .offset12 {
247 margin-left: 980px;
248 }
249
250 .offset11 {
251 margin-left: 900px;
252 }
253
254 .offset10 {
255 margin-left: 820px;
256 }
257
258 .offset9 {
259 margin-left: 740px;
260 }
261
262 .offset8 {
263 margin-left: 660px;
264 }
265
266 .offset7 {
267 margin-left: 580px;
268 }
269
270 .offset6 {
271 margin-left: 500px;
272 }
273
274 .offset5 {
275 margin-left: 420px;
276 }
277
278 .offset4 {
279 margin-left: 340px;
280 }
281
282 .offset3 {
283 margin-left: 260px;
284 }
285
286 .offset2 {
287 margin-left: 180px;
288 }
289
290 .offset1 {
291 margin-left: 100px;
292 }
293
294 .row-fluid {
295 width: 100%;
296 *zoom: 1;
297 }
298
299 .row-fluid:before,
300 .row-fluid:after {
301 display: table;
302 content: "";
303 }
304
305 .row-fluid:after {
306 clear: both;
307 }
308
309 .row-fluid [class*="span"] {
310 display: block;
311 float: left;
312 width: 100%;
313 min-height: 28px;
314 margin-left: 2.127659574%;
315 *margin-left: 2.0744680846382977%;
316 -webkit-box-sizing: border-box;
317 -moz-box-sizing: border-box;
318 -ms-box-sizing: border-box;
319 box-sizing: border-box;
320 }
321
322 .row-fluid [class*="span"]:first-child {
323 margin-left: 0;
324 }
325
326 .row-fluid .span12 {
327 width: 99.99999998999999%;
328 *width: 99.94680850063828%;
329 }
330
331 .row-fluid .span11 {
332 width: 91.489361693%;
333 *width: 91.4361702036383%;
334 }
335
336 .row-fluid .span10 {
337 width: 82.97872339599999%;
338 *width: 82.92553190663828%;
339 }
340
341 .row-fluid .span9 {
342 width: 74.468085099%;
343 *width: 74.4148936096383%;
344 }
345
346 .row-fluid .span8 {
347 width: 65.95744680199999%;
348 *width: 65.90425531263828%;
349 }
350
351 .row-fluid .span7 {
352 width: 57.446808505%;
353 *width: 57.3936170156383%;
354 }
355
356 .row-fluid .span6 {
357 width: 48.93617020799999%;
358 *width: 48.88297871863829%;
359 }
360
361 .row-fluid .span5 {
362 width: 40.425531911%;
363 *width: 40.3723404216383%;
364 }
365
366 .row-fluid .span4 {
367 width: 31.914893614%;
368 *width: 31.8617021246383%;
369 }
370
371 .row-fluid .span3 {
372 width: 23.404255317%;
373 *width: 23.3510638276383%;
374 }
375
376 .row-fluid .span2 {
377 width: 14.89361702%;
378 *width: 14.8404255306383%;
379 }
380
381 .row-fluid .span1 {
382 width: 6.382978723%;
383 *width: 6.329787233638298%;
384 }
385
386 .container {
387 margin-right: auto;
388 margin-left: auto;
389 *zoom: 1;
390 }
391
392 .container:before,
393 .container:after {
394 display: table;
395 content: "";
396 }
397
398 .container:after {
399 clear: both;
400 }
401
402 .container-fluid {
403 padding-right: 20px;
404 padding-left: 20px;
405 *zoom: 1;
406 }
407
408 .container-fluid:before,
409 .container-fluid:after {
410 display: table;
411 content: "";
412 }
413
414 .container-fluid:after {
415 clear: both;
416 }
417
418 p {
419 margin: 0 0 9px;
420 font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
421 font-size: 13px;
422 line-height: 18px;
423 }
424
425 p small {
426 font-size: 11px;
427 color: #999999;
428 }
429
430 .lead {
431 margin-bottom: 18px;
432 font-size: 20px;
433 font-weight: 200;
434 line-height: 27px;
435 }
436
437 h1,
438 h2,
439 h3,
440 h4,
441 h5,
442 h6 {
443 margin: 0;
444 font-family: inherit;
445 font-weight: bold;
446 color: inherit;
447 text-rendering: optimizelegibility;
448 }
449
450 h1 small,
451 h2 small,
452 h3 small,
453 h4 small,
454 h5 small,
455 h6 small {
456 font-weight: normal;
457 color: #999999;
458 }
459
460 h1 {
461 font-size: 30px;
462 line-height: 36px;
463 }
464
465 h1 small {
466 font-size: 18px;
467 }
468
469 h2 {
470 font-size: 24px;
471 line-height: 36px;
472 }
473
474 h2 small {
475 font-size: 18px;
476 }
477
478 h3 {
479 font-size: 18px;
480 line-height: 27px;
481 }
482
483 h3 small {
484 font-size: 14px;
485 }
486
487 h4,
488 h5,
489 h6 {
490 line-height: 18px;
491 }
492
493 h4 {
494 font-size: 14px;
495 }
496
497 h4 small {
498 font-size: 12px;
499 }
500
501 h5 {
502 font-size: 12px;
503 }
504
505 h6 {
506 font-size: 11px;
507 color: #999999;
508 text-transform: uppercase;
509 }
510
511 .page-header {
512 padding-bottom: 17px;
513 margin: 18px 0;
514 border-bottom: 1px solid #eeeeee;
515 }
516
517 .page-header h1 {
518 line-height: 1;
519 }
520
521 ul,
522 ol {
523 padding: 0;
524 margin: 0 0 9px 25px;
525 }
526
527 ul ul,
528 ul ol,
529 ol ol,
530 ol ul {
531 margin-bottom: 0;
532 }
533
534 ul {
535 list-style: disc;
536 }
537
538 ol {
539 list-style: decimal;
540 }
541
542 li {
543 line-height: 18px;
544 }
545
546 ul.unstyled,
547 ol.unstyled {
548 margin-left: 0;
549 list-style: none;
550 }
551
552 dl {
553 margin-bottom: 18px;
554 }
555
556 dt,
557 dd {
558 line-height: 18px;
559 }
560
561 dt {
562 font-weight: bold;
563 line-height: 17px;
564 }
565
566 dd {
567 margin-left: 9px;
568 }
569
570 .dl-horizontal dt {
571 float: left;
572 width: 120px;
573 overflow: hidden;
574 clear: left;
575 text-align: right;
576 text-overflow: ellipsis;
577 white-space: nowrap;
578 }
579
580 .dl-horizontal dd {
581 margin-left: 130px;
582 }
583
584 hr {
585 margin: 18px 0;
586 border: 0;
587 border-top: 1px solid #eeeeee;
588 border-bottom: 1px solid #ffffff;
589 }
590
591 strong {
592 font-weight: bold;
593 }
594
595 em {
596 font-style: italic;
597 }
598
599 .muted {
600 color: #999999;
601 }
602
603 abbr[title] {
604 cursor: help;
605 border-bottom: 1px dotted #ddd;
606 }
607
608 abbr.initialism {
609 font-size: 90%;
610 text-transform: uppercase;
611 }
612
613 blockquote {
614 padding: 0 0 0 15px;
615 margin: 0 0 18px;
616 border-left: 5px solid #eeeeee;
617 }
618
619 blockquote p {
620 margin-bottom: 0;
621 font-size: 16px;
622 font-weight: 300;
623 line-height: 22.5px;
624 }
625
626 blockquote small {
627 display: block;
628 line-height: 18px;
629 color: #999999;
630 }
631
632 blockquote small:before {
633 content: '\2014 \00A0';
634 }
635
636 blockquote.pull-right {
637 float: right;
638 padding-right: 15px;
639 padding-left: 0;
640 border-right: 5px solid #eeeeee;
641 border-left: 0;
642 }
643
644 blockquote.pull-right p,
645 blockquote.pull-right small {
646 text-align: right;
647 }
648
649 q:before,
650 q:after,
651 blockquote:before,
652 blockquote:after {
653 content: "";
654 }
655
656 address {
657 display: block;
658 margin-bottom: 18px;
659 font-style: normal;
660 line-height: 18px;
661 }
662
663 small {
664 font-size: 100%;
665 }
666
667 cite {
668 font-style: normal;
669 }
670
671 code,
672 pre {
673 padding: 0 3px 2px;
674 font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
675 font-size: 12px;
676 color: #333333;
677 -webkit-border-radius: 3px;
678 -moz-border-radius: 3px;
679 border-radius: 3px;
680 }
681
682 code {
683 padding: 2px 4px;
684 color: #d14;
685 background-color: #f7f7f9;
686 border: 1px solid #e1e1e8;
687 }
688
689 pre {
690 display: block;
691 padding: 8.5px;
692 margin: 0 0 9px;
693 font-size: 12.025px;
694 line-height: 18px;
695 word-break: break-all;
696 word-wrap: break-word;
697 white-space: pre;
698 white-space: pre-wrap;
699 background-color: #f5f5f5;
700 border: 1px solid #ccc;
701 border: 1px solid rgba(0, 0, 0, 0.15);
702 -webkit-border-radius: 4px;
703 -moz-border-radius: 4px;
704 border-radius: 4px;
705 }
706
707 pre.prettyprint {
708 margin-bottom: 18px;
709 }
710
711 pre code {
712 padding: 0;
713 color: inherit;
714 background-color: transparent;
715 border: 0;
716 }
717
718 .pre-scrollable {
719 max-height: 340px;
720 overflow-y: scroll;
721 }
722
723 form {
724 margin: 0 0 18px;
725 }
726
727 fieldset {
728 padding: 0;
729 margin: 0;
730 border: 0;
731 }
732
733 legend {
734 display: block;
735 width: 100%;
736 padding: 0;
737 margin-bottom: 27px;
738 font-size: 19.5px;
739 line-height: 36px;
740 color: #333333;
741 border: 0;
742 border-bottom: 1px solid #eee;
743 }
744
745 legend small {
746 font-size: 13.5px;
747 color: #999999;
748 }
749
750 label,
751 input,
752 button,
753 select,
754 textarea {
755 font-size: 13px;
756 font-weight: normal;
757 line-height: 18px;
758 }
759
760 input,
761 button,
762 select,
763 textarea {
764 font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
765 }
766
767 label {
768 display: block;
769 margin-bottom: 5px;
770 color: #333333;
771 }
772
773 input,
774 textarea,
775 select,
776 .uneditable-input {
777 display: inline-block;
778 width: 210px;
779 height: 18px;
780 padding: 4px;
781 margin-bottom: 9px;
782 font-size: 13px;
783 line-height: 18px;
784 color: #555555;
785 background-color: #ffffff;
786 border: 1px solid #cccccc;
787 -webkit-border-radius: 3px;
788 -moz-border-radius: 3px;
789 border-radius: 3px;
790 }
791
792 .uneditable-textarea {
793 width: auto;
794 height: auto;
795 }
796
797 label input,
798 label textarea,
799 label select {
800 display: block;
801 }
802
803 input[type="image"],
804 input[type="checkbox"],
805 input[type="radio"] {
806 width: auto;
807 height: auto;
808 padding: 0;
809 margin: 3px 0;
810 *margin-top: 0;
811 /* IE7 */
812
813 line-height: normal;
814 cursor: pointer;
815 background-color: transparent;
816 border: 0 \9;
817 /* IE9 and down */
818
819 -webkit-border-radius: 0;
820 -moz-border-radius: 0;
821 border-radius: 0;
822 }
823
824 input[type="image"] {
825 border: 0;
826 }
827
828 input[type="file"] {
829 width: auto;
830 padding: initial;
831 line-height: initial;
832 background-color: #ffffff;
833 background-color: initial;
834 border: initial;
835 -webkit-box-shadow: none;
836 -moz-box-shadow: none;
837 box-shadow: none;
838 }
839
840 input[type="button"],
841 input[type="reset"],
842 input[type="submit"] {
843 width: auto;
844 height: auto;
845 }
846
847 select,
848 input[type="file"] {
849 height: 28px;
850 /* In IE7, the height of the select element cannot be changed by height, only font-size */
851
852 *margin-top: 4px;
853 /* For IE7, add top margin to align select with labels */
854
855 line-height: 28px;
856 }
857
858 input[type="file"] {
859 line-height: 18px \9;
860 }
861
862 select {
863 width: 220px;
864 background-color: #ffffff;
865 }
866
867 select[multiple],
868 select[size] {
869 height: auto;
870 }
871
872 input[type="image"] {
873 -webkit-box-shadow: none;
874 -moz-box-shadow: none;
875 box-shadow: none;
876 }
877
878 textarea {
879 height: auto;
880 }
881
882 input[type="hidden"] {
883 display: none;
884 }
885
886 .radio,
887 .checkbox {
888 min-height: 18px;
889 padding-left: 18px;
890 }
891
892 .radio input[type="radio"],
893 .checkbox input[type="checkbox"] {
894 float: left;
895 margin-left: -18px;
896 }
897
898 .controls > .radio:first-child,
899 .controls > .checkbox:first-child {
900 padding-top: 5px;
901 }
902
903 .radio.inline,
904 .checkbox.inline {
905 display: inline-block;
906 padding-top: 5px;
907 margin-bottom: 0;
908 vertical-align: middle;
909 }
910
911 .radio.inline + .radio.inline,
912 .checkbox.inline + .checkbox.inline {
913 margin-left: 10px;
914 }
915
916 input,
917 textarea {
918 -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
919 -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
920 box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
921 -webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
922 -moz-transition: border linear 0.2s, box-shadow linear 0.2s;
923 -ms-transition: border linear 0.2s, box-shadow linear 0.2s;
924 -o-transition: border linear 0.2s, box-shadow linear 0.2s;
925 transition: border linear 0.2s, box-shadow linear 0.2s;
926 }
927
928 input:focus,
929 textarea:focus {
930 border-color: rgba(82, 168, 236, 0.8);
931 outline: 0;
932 outline: thin dotted \9;
933 /* IE6-9 */
934
935 -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
936 -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
937 box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
938 }
939
940 input[type="file"]:focus,
941 input[type="radio"]:focus,
942 input[type="checkbox"]:focus,
943 select:focus {
944 outline: thin dotted #333;
945 outline: 5px auto -webkit-focus-ring-color;
946 outline-offset: -2px;
947 -webkit-box-shadow: none;
948 -moz-box-shadow: none;
949 box-shadow: none;
950 }
951
952 .input-mini {
953 width: 60px;
954 }
955
956 .input-small {
957 width: 90px;
958 }
959
960 .input-medium {
961 width: 150px;
962 }
963
964 .input-large {
965 width: 210px;
966 }
967
968 .input-xlarge {
969 width: 270px;
970 }
971
972 .input-xxlarge {
973 width: 530px;
974 }
975
976 input[class*="span"],
977 select[class*="span"],
978 textarea[class*="span"],
979 .uneditable-input[class*="span"],
980 .row-fluid input[class*="span"],
981 .row-fluid select[class*="span"],
982 .row-fluid textarea[class*="span"],
983 .row-fluid .uneditable-input[class*="span"] {
984 float: none;
985 margin-left: 0;
986 }
987
988 input,
989 textarea,
990 .uneditable-input {
991 margin-left: 0;
992 }
993
994 input.span12,
995 textarea.span12,
996 .uneditable-input.span12 {
997 width: 930px;
998 }
999
1000 input.span11,
1001 textarea.span11,
1002 .uneditable-input.span11 {
1003 width: 850px;
1004 }
1005
1006 input.span10,
1007 textarea.span10,
1008 .uneditable-input.span10 {
1009 width: 770px;
1010 }
1011
1012 input.span9,
1013 textarea.span9,
1014 .uneditable-input.span9 {
1015 width: 690px;
1016 }
1017
1018 input.span8,
1019 textarea.span8,
1020 .uneditable-input.span8 {
1021 width: 610px;
1022 }
1023
1024 input.span7,
1025 textarea.span7,
1026 .uneditable-input.span7 {
1027 width: 530px;
1028 }
1029
1030 input.span6,
1031 textarea.span6,
1032 .uneditable-input.span6 {
1033 width: 450px;
1034 }
1035
1036 input.span5,
1037 textarea.span5,
1038 .uneditable-input.span5 {
1039 width: 370px;
1040 }
1041
1042 input.span4,
1043 textarea.span4,
1044 .uneditable-input.span4 {
1045 width: 290px;
1046 }
1047
1048 input.span3,
1049 textarea.span3,
1050 .uneditable-input.span3 {
1051 width: 210px;
1052 }
1053
1054 input.span2,
1055 textarea.span2,
1056 .uneditable-input.span2 {
1057 width: 130px;
1058 }
1059
1060 input.span1,
1061 textarea.span1,
1062 .uneditable-input.span1 {
1063 width: 50px;
1064 }
1065
1066 input[disabled],
1067 select[disabled],
1068 textarea[disabled],
1069 input[readonly],
1070 select[readonly],
1071 textarea[readonly] {
1072 cursor: not-allowed;
1073 background-color: #eeeeee;
1074 border-color: #ddd;
1075 }
1076
1077 input[type="radio"][disabled],
1078 input[type="checkbox"][disabled],
1079 input[type="radio"][readonly],
1080 input[type="checkbox"][readonly] {
1081 background-color: transparent;
1082 }
1083
1084 .control-group.warning > label,
1085 .control-group.warning .help-block,
1086 .control-group.warning .help-inline {
1087 color: #c09853;
1088 }
1089
1090 .control-group.warning input,
1091 .control-group.warning select,
1092 .control-group.warning textarea {
1093 color: #c09853;
1094 border-color: #c09853;
1095 }
1096
1097 .control-group.warning input:focus,
1098 .control-group.warning select:focus,
1099 .control-group.warning textarea:focus {
1100 border-color: #a47e3c;
1101 -webkit-box-shadow: 0 0 6px #dbc59e;
1102 -moz-box-shadow: 0 0 6px #dbc59e;
1103 box-shadow: 0 0 6px #dbc59e;
1104 }
1105
1106 .control-group.warning .input-prepend .add-on,
1107 .control-group.warning .input-append .add-on {
1108 color: #c09853;
1109 background-color: #fcf8e3;
1110 border-color: #c09853;
1111 }
1112
1113 .control-group.error > label,
1114 .control-group.error .help-block,
1115 .control-group.error .help-inline {
1116 color: #b94a48;
1117 }
1118
1119 .control-group.error input,
1120 .control-group.error select,
1121 .control-group.error textarea {
1122 color: #b94a48;
1123 border-color: #b94a48;
1124 }
1125
1126 .control-group.error input:focus,
1127 .control-group.error select:focus,
1128 .control-group.error textarea:focus {
1129 border-color: #953b39;
1130 -webkit-box-shadow: 0 0 6px #d59392;
1131 -moz-box-shadow: 0 0 6px #d59392;
1132 box-shadow: 0 0 6px #d59392;
1133 }
1134
1135 .control-group.error .input-prepend .add-on,
1136 .control-group.error .input-append .add-on {
1137 color: #b94a48;
1138 background-color: #f2dede;
1139 border-color: #b94a48;
1140 }
1141
1142 .control-group.success > label,
1143 .control-group.success .help-block,
1144 .control-group.success .help-inline {
1145 color: #468847;
1146 }
1147
1148 .control-group.success input,
1149 .control-group.success select,
1150 .control-group.success textarea {
1151 color: #468847;
1152 border-color: #468847;
1153 }
1154
1155 .control-group.success input:focus,
1156 .control-group.success select:focus,
1157 .control-group.success textarea:focus {
1158 border-color: #356635;
1159 -webkit-box-shadow: 0 0 6px #7aba7b;
1160 -moz-box-shadow: 0 0 6px #7aba7b;
1161 box-shadow: 0 0 6px #7aba7b;
1162 }
1163
1164 .control-group.success .input-prepend .add-on,
1165 .control-group.success .input-append .add-on {
1166 color: #468847;
1167 background-color: #dff0d8;
1168 border-color: #468847;
1169 }
1170
1171 input:focus:required:invalid,
1172 textarea:focus:required:invalid,
1173 select:focus:required:invalid {
1174 color: #b94a48;
1175 border-color: #ee5f5b;
1176 }
1177
1178 input:focus:required:invalid:focus,
1179 textarea:focus:required:invalid:focus,
1180 select:focus:required:invalid:focus {
1181 border-color: #e9322d;
1182 -webkit-box-shadow: 0 0 6px #f8b9b7;
1183 -moz-box-shadow: 0 0 6px #f8b9b7;
1184 box-shadow: 0 0 6px #f8b9b7;
1185 }
1186
1187 .form-actions {
1188 padding: 17px 20px 18px;
1189 margin-top: 18px;
1190 margin-bottom: 18px;
1191 background-color: #f5f5f5;
1192 border-top: 1px solid #ddd;
1193 *zoom: 1;
1194 }
1195
1196 .form-actions:before,
1197 .form-actions:after {
1198 display: table;
1199 content: "";
1200 }
1201
1202 .form-actions:after {
1203 clear: both;
1204 }
1205
1206 .uneditable-input {
1207 overflow: hidden;
1208 white-space: nowrap;
1209 cursor: not-allowed;
1210 background-color: #ffffff;
1211 border-color: #eee;
1212 -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
1213 -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
1214 box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
1215 }
1216
1217 :-moz-placeholder {
1218 color: #999999;
1219 }
1220
1221 ::-webkit-input-placeholder {
1222 color: #999999;
1223 }
1224
1225 .help-block,
1226 .help-inline {
1227 color: #555555;
1228 }
1229
1230 .help-block {
1231 display: block;
1232 margin-bottom: 9px;
1233 }
1234
1235 .help-inline {
1236 display: inline-block;
1237 *display: inline;
1238 padding-left: 5px;
1239 vertical-align: middle;
1240 *zoom: 1;
1241 }
1242
1243 .input-prepend,
1244 .input-append {
1245 margin-bottom: 5px;
1246 }
1247
1248 .input-prepend input,
1249 .input-append input,
1250 .input-prepend select,
1251 .input-append select,
1252 .input-prepend .uneditable-input,
1253 .input-append .uneditable-input {
1254 position: relative;
1255 margin-bottom: 0;
1256 *margin-left: 0;
1257 vertical-align: middle;
1258 -webkit-border-radius: 0 3px 3px 0;
1259 -moz-border-radius: 0 3px 3px 0;
1260 border-radius: 0 3px 3px 0;
1261 }
1262
1263 .input-prepend input:focus,
1264 .input-append input:focus,
1265 .input-prepend select:focus,
1266 .input-append select:focus,
1267 .input-prepend .uneditable-input:focus,
1268 .input-append .uneditable-input:focus {
1269 z-index: 2;
1270 }
1271
1272 .input-prepend .uneditable-input,
1273 .input-append .uneditable-input {
1274 border-left-color: #ccc;
1275 }
1276
1277 .input-prepend .add-on,
1278 .input-append .add-on {
1279 display: inline-block;
1280 width: auto;
1281 height: 18px;
1282 min-width: 16px;
1283 padding: 4px 5px;
1284 font-weight: normal;
1285 line-height: 18px;
1286 text-align: center;
1287 text-shadow: 0 1px 0 #ffffff;
1288 vertical-align: middle;
1289 background-color: #eeeeee;
1290 border: 1px solid #ccc;
1291 }
1292
1293 .input-prepend .add-on,
1294 .input-append .add-on,
1295 .input-prepend .btn,
1296 .input-append .btn {
1297 margin-left: -1px;
1298 -webkit-border-radius: 0;
1299 -moz-border-radius: 0;
1300 border-radius: 0;
1301 }
1302
1303 .input-prepend .active,
1304 .input-append .active {
1305 background-color: #a9dba9;
1306 border-color: #46a546;
1307 }
1308
1309 .input-prepend .add-on,
1310 .input-prepend .btn {
1311 margin-right: -1px;
1312 }
1313
1314 .input-prepend .add-on:first-child,
1315 .input-prepend .btn:first-child {
1316 -webkit-border-radius: 3px 0 0 3px;
1317 -moz-border-radius: 3px 0 0 3px;
1318 border-radius: 3px 0 0 3px;
1319 }
1320
1321 .input-append input,
1322 .input-append select,
1323 .input-append .uneditable-input {
1324 -webkit-border-radius: 3px 0 0 3px;
1325 -moz-border-radius: 3px 0 0 3px;
1326 border-radius: 3px 0 0 3px;
1327 }
1328
1329 .input-append .uneditable-input {
1330 border-right-color: #ccc;
1331 border-left-color: #eee;
1332 }
1333
1334 .input-append .add-on:last-child,
1335 .input-append .btn:last-child {
1336 -webkit-border-radius: 0 3px 3px 0;
1337 -moz-border-radius: 0 3px 3px 0;
1338 border-radius: 0 3px 3px 0;
1339 }
1340
1341 .input-prepend.input-append input,
1342 .input-prepend.input-append select,
1343 .input-prepend.input-append .uneditable-input {
1344 -webkit-border-radius: 0;
1345 -moz-border-radius: 0;
1346 border-radius: 0;
1347 }
1348
1349 .input-prepend.input-append .add-on:first-child,
1350 .input-prepend.input-append .btn:first-child {
1351 margin-right: -1px;
1352 -webkit-border-radius: 3px 0 0 3px;
1353 -moz-border-radius: 3px 0 0 3px;
1354 border-radius: 3px 0 0 3px;
1355 }
1356
1357 .input-prepend.input-append .add-on:last-child,
1358 .input-prepend.input-append .btn:last-child {
1359 margin-left: -1px;
1360 -webkit-border-radius: 0 3px 3px 0;
1361 -moz-border-radius: 0 3px 3px 0;
1362 border-radius: 0 3px 3px 0;
1363 }
1364
1365 .search-query {
1366 padding-right: 14px;
1367 padding-right: 4px \9;
1368 padding-left: 14px;
1369 padding-left: 4px \9;
1370 /* IE7-8 doesn't have border-radius, so don't indent the padding */
1371
1372 margin-bottom: 0;
1373 -webkit-border-radius: 14px;
1374 -moz-border-radius: 14px;
1375 border-radius: 14px;
1376 }
1377
1378 .form-search input,
1379 .form-inline input,
1380 .form-horizontal input,
1381 .form-search textarea,
1382 .form-inline textarea,
1383 .form-horizontal textarea,
1384 .form-search select,
1385 .form-inline select,
1386 .form-horizontal select,
1387 .form-search .help-inline,
1388 .form-inline .help-inline,
1389 .form-horizontal .help-inline,
1390 .form-search .uneditable-input,
1391 .form-inline .uneditable-input,
1392 .form-horizontal .uneditable-input,
1393 .form-search .input-prepend,
1394 .form-inline .input-prepend,
1395 .form-horizontal .input-prepend,
1396 .form-search .input-append,
1397 .form-inline .input-append,
1398 .form-horizontal .input-append {
1399 display: inline-block;
1400 *display: inline;
1401 margin-bottom: 0;
1402 *zoom: 1;
1403 }
1404
1405 .form-search .hide,
1406 .form-inline .hide,
1407 .form-horizontal .hide {
1408 display: none;
1409 }
1410
1411 .form-search label,
1412 .form-inline label {
1413 display: inline-block;
1414 }
1415
1416 .form-search .input-append,
1417 .form-inline .input-append,
1418 .form-search .input-prepend,
1419 .form-inline .input-prepend {
1420 margin-bottom: 0;
1421 }
1422
1423 .form-search .radio,
1424 .form-search .checkbox,
1425 .form-inline .radio,
1426 .form-inline .checkbox {
1427 padding-left: 0;
1428 margin-bottom: 0;
1429 vertical-align: middle;
1430 }
1431
1432 .form-search .radio input[type="radio"],
1433 .form-search .checkbox input[type="checkbox"],
1434 .form-inline .radio input[type="radio"],
1435 .form-inline .checkbox input[type="checkbox"] {
1436 float: left;
1437 margin-right: 3px;
1438 margin-left: 0;
1439 }
1440
1441 .control-group {
1442 margin-bottom: 9px;
1443 }
1444
1445 legend + .control-group {
1446 margin-top: 18px;
1447 -webkit-margin-top-collapse: separate;
1448 }
1449
1450 .form-horizontal .control-group {
1451 margin-bottom: 18px;
1452 *zoom: 1;
1453 }
1454
1455 .form-horizontal .control-group:before,
1456 .form-horizontal .control-group:after {
1457 display: table;
1458 content: "";
1459 }
1460
1461 .form-horizontal .control-group:after {
1462 clear: both;
1463 }
1464
1465 .form-horizontal .control-label {
1466 float: left;
1467 width: 140px;
1468 padding-top: 5px;
1469 text-align: right;
1470 }
1471
1472 .form-horizontal .controls {
1473 *display: inline-block;
1474 *padding-left: 20px;
1475 margin-left: 160px;
1476 *margin-left: 0;
1477 }
1478
1479 .form-horizontal .controls:first-child {
1480 *padding-left: 160px;
1481 }
1482
1483 .form-horizontal .help-block {
1484 margin-top: 9px;
1485 margin-bottom: 0;
1486 }
1487
1488 .form-horizontal .form-actions {
1489 padding-left: 160px;
1490 }
1491
1492 table {
1493 max-width: 100%;
1494 background-color: transparent;
1495 border-collapse: collapse;
1496 border-spacing: 0;
1497 }
1498
1499 .table {
1500 width: 100%;
1501 margin-bottom: 18px;
1502 }
1503
1504 .table th,
1505 .table td {
1506 padding: 8px;
1507 line-height: 18px;
1508 text-align: left;
1509 vertical-align: top;
1510 border-top: 1px solid #dddddd;
1511 }
1512
1513 .table th {
1514 font-weight: bold;
1515 }
1516
1517 .table thead th {
1518 vertical-align: bottom;
1519 }
1520
1521 .table caption + thead tr:first-child th,
1522 .table caption + thead tr:first-child td,
1523 .table colgroup + thead tr:first-child th,
1524 .table colgroup + thead tr:first-child td,
1525 .table thead:first-child tr:first-child th,
1526 .table thead:first-child tr:first-child td {
1527 border-top: 0;
1528 }
1529
1530 .table tbody + tbody {
1531 border-top: 2px solid #dddddd;
1532 }
1533
1534 .table-condensed th,
1535 .table-condensed td {
1536 padding: 4px 5px;
1537 }
1538
1539 .table-bordered {
1540 border: 1px solid #dddddd;
1541 border-collapse: separate;
1542 *border-collapse: collapsed;
1543 border-left: 0;
1544 -webkit-border-radius: 4px;
1545 -moz-border-radius: 4px;
1546 border-radius: 4px;
1547 }
1548
1549 .table-bordered th,
1550 .table-bordered td {
1551 border-left: 1px solid #dddddd;
1552 }
1553
1554 .table-bordered caption + thead tr:first-child th,
1555 .table-bordered caption + tbody tr:first-child th,
1556 .table-bordered caption + tbody tr:first-child td,
1557 .table-bordered colgroup + thead tr:first-child th,
1558 .table-bordered colgroup + tbody tr:first-child th,
1559 .table-bordered colgroup + tbody tr:first-child td,
1560 .table-bordered thead:first-child tr:first-child th,
1561 .table-bordered tbody:first-child tr:first-child th,
1562 .table-bordered tbody:first-child tr:first-child td {
1563 border-top: 0;
1564 }
1565
1566 .table-bordered thead:first-child tr:first-child th:first-child,
1567 .table-bordered tbody:first-child tr:first-child td:first-child {
1568 -webkit-border-top-left-radius: 4px;
1569 border-top-left-radius: 4px;
1570 -moz-border-radius-topleft: 4px;
1571 }
1572
1573 .table-bordered thead:first-child tr:first-child th:last-child,
1574 .table-bordered tbody:first-child tr:first-child td:last-child {
1575 -webkit-border-top-right-radius: 4px;
1576 border-top-right-radius: 4px;
1577 -moz-border-radius-topright: 4px;
1578 }
1579
1580 .table-bordered thead:last-child tr:last-child th:first-child,
1581 .table-bordered tbody:last-child tr:last-child td:first-child {
1582 -webkit-border-radius: 0 0 0 4px;
1583 -moz-border-radius: 0 0 0 4px;
1584 border-radius: 0 0 0 4px;
1585 -webkit-border-bottom-left-radius: 4px;
1586 border-bottom-left-radius: 4px;
1587 -moz-border-radius-bottomleft: 4px;
1588 }
1589
1590 .table-bordered thead:last-child tr:last-child th:last-child,
1591 .table-bordered tbody:last-child tr:last-child td:last-child {
1592 -webkit-border-bottom-right-radius: 4px;
1593 border-bottom-right-radius: 4px;
1594 -moz-border-radius-bottomright: 4px;
1595 }
1596
1597 .table-striped tbody tr:nth-child(odd) td,
1598 .table-striped tbody tr:nth-child(odd) th {
1599 background-color: #f9f9f9;
1600 }
1601
1602 .table tbody tr:hover td,
1603 .table tbody tr:hover th {
1604 background-color: #f5f5f5;
1605 }
1606
1607 table .span1 {
1608 float: none;
1609 width: 44px;
1610 margin-left: 0;
1611 }
1612
1613 table .span2 {
1614 float: none;
1615 width: 124px;
1616 margin-left: 0;
1617 }
1618
1619 table .span3 {
1620 float: none;
1621 width: 204px;
1622 margin-left: 0;
1623 }
1624
1625 table .span4 {
1626 float: none;
1627 width: 284px;
1628 margin-left: 0;
1629 }
1630
1631 table .span5 {
1632 float: none;
1633 width: 364px;
1634 margin-left: 0;
1635 }
1636
1637 table .span6 {
1638 float: none;
1639 width: 444px;
1640 margin-left: 0;
1641 }
1642
1643 table .span7 {
1644 float: none;
1645 width: 524px;
1646 margin-left: 0;
1647 }
1648
1649 table .span8 {
1650 float: none;
1651 width: 604px;
1652 margin-left: 0;
1653 }
1654
1655 table .span9 {
1656 float: none;
1657 width: 684px;
1658 margin-left: 0;
1659 }
1660
1661 table .span10 {
1662 float: none;
1663 width: 764px;
1664 margin-left: 0;
1665 }
1666
1667 table .span11 {
1668 float: none;
1669 width: 844px;
1670 margin-left: 0;
1671 }
1672
1673 table .span12 {
1674 float: none;
1675 width: 924px;
1676 margin-left: 0;
1677 }
1678
1679 table .span13 {
1680 float: none;
1681 width: 1004px;
1682 margin-left: 0;
1683 }
1684
1685 table .span14 {
1686 float: none;
1687 width: 1084px;
1688 margin-left: 0;
1689 }
1690
1691 table .span15 {
1692 float: none;
1693 width: 1164px;
1694 margin-left: 0;
1695 }
1696
1697 table .span16 {
1698 float: none;
1699 width: 1244px;
1700 margin-left: 0;
1701 }
1702
1703 table .span17 {
1704 float: none;
1705 width: 1324px;
1706 margin-left: 0;
1707 }
1708
1709 table .span18 {
1710 float: none;
1711 width: 1404px;
1712 margin-left: 0;
1713 }
1714
1715 table .span19 {
1716 float: none;
1717 width: 1484px;
1718 margin-left: 0;
1719 }
1720
1721 table .span20 {
1722 float: none;
1723 width: 1564px;
1724 margin-left: 0;
1725 }
1726
1727 table .span21 {
1728 float: none;
1729 width: 1644px;
1730 margin-left: 0;
1731 }
1732
1733 table .span22 {
1734 float: none;
1735 width: 1724px;
1736 margin-left: 0;
1737 }
1738
1739 table .span23 {
1740 float: none;
1741 width: 1804px;
1742 margin-left: 0;
1743 }
1744
1745 table .span24 {
1746 float: none;
1747 width: 1884px;
1748 margin-left: 0;
1749 }
1750
1751 [class^="icon-"],
1752 [class*=" icon-"] {
1753 display: inline-block;
1754 width: 14px;
1755 height: 14px;
1756 *margin-right: .3em;
1757 line-height: 14px;
1758 vertical-align: text-top;
1759 /* background-image: url("../img/glyphicons-halflings.png"); */
1760 background-position: 14px 14px;
1761 background-repeat: no-repeat;
1762 }
1763
1764 [class^="icon-"]:last-child,
1765 [class*=" icon-"]:last-child {
1766 *margin-left: 0;
1767 }
1768
1769 .icon-white {
1770 /* background-image: url("../img/glyphicons-halflings-white.png"); */
1771 }
1772
1773 .icon-glass {
1774 background-position: 0 0;
1775 }
1776
1777 .icon-music {
1778 background-position: -24px 0;
1779 }
1780
1781 .icon-search {
1782 background-position: -48px 0;
1783 }
1784
1785 .icon-envelope {
1786 background-position: -72px 0;
1787 }
1788
1789 .icon-heart {
1790 background-position: -96px 0;
1791 }
1792
1793 .icon-star {
1794 background-position: -120px 0;
1795 }
1796
1797 .icon-star-empty {
1798 background-position: -144px 0;
1799 }
1800
1801 .icon-user {
1802 background-position: -168px 0;
1803 }
1804
1805 .icon-film {
1806 background-position: -192px 0;
1807 }
1808
1809 .icon-th-large {
1810 background-position: -216px 0;
1811 }
1812
1813 .icon-th {
1814 background-position: -240px 0;
1815 }
1816
1817 .icon-th-list {
1818 background-position: -264px 0;
1819 }
1820
1821 .icon-ok {
1822 background-position: -288px 0;
1823 }
1824
1825 .icon-remove {
1826 background-position: -312px 0;
1827 }
1828
1829 .icon-zoom-in {
1830 background-position: -336px 0;
1831 }
1832
1833 .icon-zoom-out {
1834 background-position: -360px 0;
1835 }
1836
1837 .icon-off {
1838 background-position: -384px 0;
1839 }
1840
1841 .icon-signal {
1842 background-position: -408px 0;
1843 }
1844
1845 .icon-cog {
1846 background-position: -432px 0;
1847 }
1848
1849 .icon-trash {
1850 background-position: -456px 0;
1851 }
1852
1853 .icon-home {
1854 background-position: 0 -24px;
1855 }
1856
1857 .icon-file {
1858 background-position: -24px -24px;
1859 }
1860
1861 .icon-time {
1862 background-position: -48px -24px;
1863 }
1864
1865 .icon-road {
1866 background-position: -72px -24px;
1867 }
1868
1869 .icon-download-alt {
1870 background-position: -96px -24px;
1871 }
1872
1873 .icon-download {
1874 background-position: -120px -24px;
1875 }
1876
1877 .icon-upload {
1878 background-position: -144px -24px;
1879 }
1880
1881 .icon-inbox {
1882 background-position: -168px -24px;
1883 }
1884
1885 .icon-play-circle {
1886 background-position: -192px -24px;
1887 }
1888
1889 .icon-repeat {
1890 background-position: -216px -24px;
1891 }
1892
1893 .icon-refresh {
1894 background-position: -240px -24px;
1895 }
1896
1897 .icon-list-alt {
1898 background-position: -264px -24px;
1899 }
1900
1901 .icon-lock {
1902 background-position: -287px -24px;
1903 }
1904
1905 .icon-flag {
1906 background-position: -312px -24px;
1907 }
1908
1909 .icon-headphones {
1910 background-position: -336px -24px;
1911 }
1912
1913 .icon-volume-off {
1914 background-position: -360px -24px;
1915 }
1916
1917 .icon-volume-down {
1918 background-position: -384px -24px;
1919 }
1920
1921 .icon-volume-up {
1922 background-position: -408px -24px;
1923 }
1924
1925 .icon-qrcode {
1926 background-position: -432px -24px;
1927 }
1928
1929 .icon-barcode {
1930 background-position: -456px -24px;
1931 }
1932
1933 .icon-tag {
1934 background-position: 0 -48px;
1935 }
1936
1937 .icon-tags {
1938 background-position: -25px -48px;
1939 }
1940
1941 .icon-book {
1942 background-position: -48px -48px;
1943 }
1944
1945 .icon-bookmark {
1946 background-position: -72px -48px;
1947 }
1948
1949 .icon-print {
1950 background-position: -96px -48px;
1951 }
1952
1953 .icon-camera {
1954 background-position: -120px -48px;
1955 }
1956
1957 .icon-font {
1958 background-position: -144px -48px;
1959 }
1960
1961 .icon-bold {
1962 background-position: -167px -48px;
1963 }
1964
1965 .icon-italic {
1966 background-position: -192px -48px;
1967 }
1968
1969 .icon-text-height {
1970 background-position: -216px -48px;
1971 }
1972
1973 .icon-text-width {
1974 background-position: -240px -48px;
1975 }
1976
1977 .icon-align-left {
1978 background-position: -264px -48px;
1979 }
1980
1981 .icon-align-center {
1982 background-position: -288px -48px;
1983 }
1984
1985 .icon-align-right {
1986 background-position: -312px -48px;
1987 }
1988
1989 .icon-align-justify {
1990 background-position: -336px -48px;
1991 }
1992
1993 .icon-list {
1994 background-position: -360px -48px;
1995 }
1996
1997 .icon-indent-left {
1998 background-position: -384px -48px;
1999 }
2000
2001 .icon-indent-right {
2002 background-position: -408px -48px;
2003 }
2004
2005 .icon-facetime-video {
2006 background-position: -432px -48px;
2007 }
2008
2009 .icon-picture {
2010 background-position: -456px -48px;
2011 }
2012
2013 .icon-pencil {
2014 background-position: 0 -72px;
2015 }
2016
2017 .icon-map-marker {
2018 background-position: -24px -72px;
2019 }
2020
2021 .icon-adjust {
2022 background-position: -48px -72px;
2023 }
2024
2025 .icon-tint {
2026 background-position: -72px -72px;
2027 }
2028
2029 .icon-edit {
2030 background-position: -96px -72px;
2031 }
2032
2033 .icon-share {
2034 background-position: -120px -72px;
2035 }
2036
2037 .icon-check {
2038 background-position: -144px -72px;
2039 }
2040
2041 .icon-move {
2042 background-position: -168px -72px;
2043 }
2044
2045 .icon-step-backward {
2046 background-position: -192px -72px;
2047 }
2048
2049 .icon-fast-backward {
2050 background-position: -216px -72px;
2051 }
2052
2053 .icon-backward {
2054 background-position: -240px -72px;
2055 }
2056
2057 .icon-play {
2058 background-position: -264px -72px;
2059 }
2060
2061 .icon-pause {
2062 background-position: -288px -72px;
2063 }
2064
2065 .icon-stop {
2066 background-position: -312px -72px;
2067 }
2068
2069 .icon-forward {
2070 background-position: -336px -72px;
2071 }
2072
2073 .icon-fast-forward {
2074 background-position: -360px -72px;
2075 }
2076
2077 .icon-step-forward {
2078 background-position: -384px -72px;
2079 }
2080
2081 .icon-eject {
2082 background-position: -408px -72px;
2083 }
2084
2085 .icon-chevron-left {
2086 background-position: -432px -72px;
2087 }
2088
2089 .icon-chevron-right {
2090 background-position: -456px -72px;
2091 }
2092
2093 .icon-plus-sign {
2094 background-position: 0 -96px;
2095 }
2096
2097 .icon-minus-sign {
2098 background-position: -24px -96px;
2099 }
2100
2101 .icon-remove-sign {
2102 background-position: -48px -96px;
2103 }
2104
2105 .icon-ok-sign {
2106 background-position: -72px -96px;
2107 }
2108
2109 .icon-question-sign {
2110 background-position: -96px -96px;
2111 }
2112
2113 .icon-info-sign {
2114 background-position: -120px -96px;
2115 }
2116
2117 .icon-screenshot {
2118 background-position: -144px -96px;
2119 }
2120
2121 .icon-remove-circle {
2122 background-position: -168px -96px;
2123 }
2124
2125 .icon-ok-circle {
2126 background-position: -192px -96px;
2127 }
2128
2129 .icon-ban-circle {
2130 background-position: -216px -96px;
2131 }
2132
2133 .icon-arrow-left {
2134 background-position: -240px -96px;
2135 }
2136
2137 .icon-arrow-right {
2138 background-position: -264px -96px;
2139 }
2140
2141 .icon-arrow-up {
2142 background-position: -289px -96px;
2143 }
2144
2145 .icon-arrow-down {
2146 background-position: -312px -96px;
2147 }
2148
2149 .icon-share-alt {
2150 background-position: -336px -96px;
2151 }
2152
2153 .icon-resize-full {
2154 background-position: -360px -96px;
2155 }
2156
2157 .icon-resize-small {
2158 background-position: -384px -96px;
2159 }
2160
2161 .icon-plus {
2162 background-position: -408px -96px;
2163 }
2164
2165 .icon-minus {
2166 background-position: -433px -96px;
2167 }
2168
2169 .icon-asterisk {
2170 background-position: -456px -96px;
2171 }
2172
2173 .icon-exclamation-sign {
2174 background-position: 0 -120px;
2175 }
2176
2177 .icon-gift {
2178 background-position: -24px -120px;
2179 }
2180
2181 .icon-leaf {
2182 background-position: -48px -120px;
2183 }
2184
2185 .icon-fire {
2186 background-position: -72px -120px;
2187 }
2188
2189 .icon-eye-open {
2190 background-position: -96px -120px;
2191 }
2192
2193 .icon-eye-close {
2194 background-position: -120px -120px;
2195 }
2196
2197 .icon-warning-sign {
2198 background-position: -144px -120px;
2199 }
2200
2201 .icon-plane {
2202 background-position: -168px -120px;
2203 }
2204
2205 .icon-calendar {
2206 background-position: -192px -120px;
2207 }
2208
2209 .icon-random {
2210 background-position: -216px -120px;
2211 }
2212
2213 .icon-comment {
2214 background-position: -240px -120px;
2215 }
2216
2217 .icon-magnet {
2218 background-position: -264px -120px;
2219 }
2220
2221 .icon-chevron-up {
2222 background-position: -288px -120px;
2223 }
2224
2225 .icon-chevron-down {
2226 background-position: -313px -119px;
2227 }
2228
2229 .icon-retweet {
2230 background-position: -336px -120px;
2231 }
2232
2233 .icon-shopping-cart {
2234 background-position: -360px -120px;
2235 }
2236
2237 .icon-folder-close {
2238 background-position: -384px -120px;
2239 }
2240
2241 .icon-folder-open {
2242 background-position: -408px -120px;
2243 }
2244
2245 .icon-resize-vertical {
2246 background-position: -432px -119px;
2247 }
2248
2249 .icon-resize-horizontal {
2250 background-position: -456px -118px;
2251 }
2252
2253 .icon-hdd {
2254 background-position: 0 -144px;
2255 }
2256
2257 .icon-bullhorn {
2258 background-position: -24px -144px;
2259 }
2260
2261 .icon-bell {
2262 background-position: -48px -144px;
2263 }
2264
2265 .icon-certificate {
2266 background-position: -72px -144px;
2267 }
2268
2269 .icon-thumbs-up {
2270 background-position: -96px -144px;
2271 }
2272
2273 .icon-thumbs-down {
2274 background-position: -120px -144px;
2275 }
2276
2277 .icon-hand-right {
2278 background-position: -144px -144px;
2279 }
2280
2281 .icon-hand-left {
2282 background-position: -168px -144px;
2283 }
2284
2285 .icon-hand-up {
2286 background-position: -192px -144px;
2287 }
2288
2289 .icon-hand-down {
2290 background-position: -216px -144px;
2291 }
2292
2293 .icon-circle-arrow-right {
2294 background-position: -240px -144px;
2295 }
2296
2297 .icon-circle-arrow-left {
2298 background-position: -264px -144px;
2299 }
2300
2301 .icon-circle-arrow-up {
2302 background-position: -288px -144px;
2303 }
2304
2305 .icon-circle-arrow-down {
2306 background-position: -312px -144px;
2307 }
2308
2309 .icon-globe {
2310 background-position: -336px -144px;
2311 }
2312
2313 .icon-wrench {
2314 background-position: -360px -144px;
2315 }
2316
2317 .icon-tasks {
2318 background-position: -384px -144px;
2319 }
2320
2321 .icon-filter {
2322 background-position: -408px -144px;
2323 }
2324
2325 .icon-briefcase {
2326 background-position: -432px -144px;
2327 }
2328
2329 .icon-fullscreen {
2330 background-position: -456px -144px;
2331 }
2332
2333 .dropup,
2334 .dropdown {
2335 position: relative;
2336 }
2337
2338 .dropdown-toggle {
2339 *margin-bottom: -3px;
2340 }
2341
2342 .dropdown-toggle:active,
2343 .open .dropdown-toggle {
2344 outline: 0;
2345 }
2346
2347 .caret {
2348 display: inline-block;
2349 width: 0;
2350 height: 0;
2351 vertical-align: top;
2352 border-top: 4px solid #000000;
2353 border-right: 4px solid transparent;
2354 border-left: 4px solid transparent;
2355 content: "";
2356 opacity: 0.3;
2357 filter: alpha(opacity=30);
2358 }
2359
2360 .dropdown .caret {
2361 margin-top: 8px;
2362 margin-left: 2px;
2363 }
2364
2365 .dropdown:hover .caret,
2366 .open .caret {
2367 opacity: 1;
2368 filter: alpha(opacity=100);
2369 }
2370
2371 .dropdown-menu {
2372 position: absolute;
2373 top: 100%;
2374 left: 0;
2375 z-index: 1000;
2376 display: none;
2377 float: left;
2378 min-width: 160px;
2379 padding: 4px 0;
2380 margin: 1px 0 0;
2381 list-style: none;
2382 background-color: #ffffff;
2383 border: 1px solid #ccc;
2384 border: 1px solid rgba(0, 0, 0, 0.2);
2385 *border-right-width: 2px;
2386 *border-bottom-width: 2px;
2387 -webkit-border-radius: 5px;
2388 -moz-border-radius: 5px;
2389 border-radius: 5px;
2390 -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
2391 -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
2392 box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
2393 -webkit-background-clip: padding-box;
2394 -moz-background-clip: padding;
2395 background-clip: padding-box;
2396 }
2397
2398 .dropdown-menu.pull-right {
2399 right: 0;
2400 left: auto;
2401 }
2402
2403 .dropdown-menu .divider {
2404 *width: 100%;
2405 height: 1px;
2406 margin: 8px 1px;
2407 *margin: -5px 0 5px;
2408 overflow: hidden;
2409 background-color: #e5e5e5;
2410 border-bottom: 1px solid #ffffff;
2411 }
2412
2413 .dropdown-menu a {
2414 display: block;
2415 padding: 3px 15px;
2416 clear: both;
2417 font-weight: normal;
2418 line-height: 18px;
2419 color: #333333;
2420 white-space: nowrap;
2421 }
2422
2423 .dropdown-menu li > a:hover,
2424 .dropdown-menu .active > a,
2425 .dropdown-menu .active > a:hover {
2426 color: #ffffff;
2427 text-decoration: none;
2428 background-color: #0088cc;
2429 }
2430
2431 .open {
2432 *z-index: 1000;
2433 }
2434
2435 .open .dropdown-menu {
2436 display: block;
2437 }
2438
2439 .pull-right .dropdown-menu {
2440 right: 0;
2441 left: auto;
2442 }
2443
2444 .dropup .caret,
2445 .navbar-fixed-bottom .dropdown .caret {
2446 border-top: 0;
2447 border-bottom: 4px solid #000000;
2448 content: "\2191";
2449 }
2450
2451 .dropup .dropdown-menu,
2452 .navbar-fixed-bottom .dropdown .dropdown-menu {
2453 top: auto;
2454 bottom: 100%;
2455 margin-bottom: 1px;
2456 }
2457
2458 .typeahead {
2459 margin-top: 2px;
2460 -webkit-border-radius: 4px;
2461 -moz-border-radius: 4px;
2462 border-radius: 4px;
2463 }
2464
2465 .well {
2466 min-height: 20px;
2467 padding: 19px;
2468 margin-bottom: 20px;
2469 background-color: #f5f5f5;
2470 border: 1px solid #eee;
2471 border: 1px solid rgba(0, 0, 0, 0.05);
2472 -webkit-border-radius: 4px;
2473 -moz-border-radius: 4px;
2474 border-radius: 4px;
2475 -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
2476 -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
2477 box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
2478 }
2479
2480 .well blockquote {
2481 border-color: #ddd;
2482 border-color: rgba(0, 0, 0, 0.15);
2483 }
2484
2485 .well-large {
2486 padding: 24px;
2487 -webkit-border-radius: 6px;
2488 -moz-border-radius: 6px;
2489 border-radius: 6px;
2490 }
2491
2492 .well-small {
2493 padding: 9px;
2494 -webkit-border-radius: 3px;
2495 -moz-border-radius: 3px;
2496 border-radius: 3px;
2497 }
2498
2499 .fade {
2500 opacity: 0;
2501 filter: alpha(opacity=0);
2502 -webkit-transition: opacity 0.15s linear;
2503 -moz-transition: opacity 0.15s linear;
2504 -ms-transition: opacity 0.15s linear;
2505 -o-transition: opacity 0.15s linear;
2506 transition: opacity 0.15s linear;
2507 }
2508
2509 .fade.in {
2510 opacity: 1;
2511 filter: alpha(opacity=100);
2512 }
2513
2514 .collapse {
2515 position: relative;
2516 height: 0;
2517 overflow: hidden;
2518 -webkit-transition: height 0.35s ease;
2519 -moz-transition: height 0.35s ease;
2520 -ms-transition: height 0.35s ease;
2521 -o-transition: height 0.35s ease;
2522 transition: height 0.35s ease;
2523 }
2524
2525 .collapse.in {
2526 height: auto;
2527 }
2528
2529 .close {
2530 float: right;
2531 font-size: 20px;
2532 font-weight: bold;
2533 line-height: 18px;
2534 color: #000000;
2535 text-shadow: 0 1px 0 #ffffff;
2536 opacity: 0.2;
2537 filter: alpha(opacity=20);
2538 }
2539
2540 .close:hover {
2541 color: #000000;
2542 text-decoration: none;
2543 cursor: pointer;
2544 opacity: 0.4;
2545 filter: alpha(opacity=40);
2546 }
2547
2548 button.close {
2549 padding: 0;
2550 cursor: pointer;
2551 background: transparent;
2552 border: 0;
2553 -webkit-appearance: none;
2554 }
2555
2556 .btn {
2557 display: inline-block;
2558 *display: inline;
2559 padding: 4px 10px 4px;
2560 margin-bottom: 0;
2561 *margin-left: .3em;
2562 font-size: 13px;
2563 line-height: 18px;
2564 *line-height: 20px;
2565 color: #333333;
2566 text-align: center;
2567 text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
2568 vertical-align: middle;
2569 cursor: pointer;
2570 background-color: #f5f5f5;
2571 *background-color: #e6e6e6;
2572 background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6);
2573 background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
2574 background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
2575 background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
2576 background-image: linear-gradient(top, #ffffff, #e6e6e6);
2577 background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
2578 background-repeat: repeat-x;
2579 border: 1px solid #cccccc;
2580 *border: 0;
2581 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
2582 border-color: #e6e6e6 #e6e6e6 #bfbfbf;
2583 border-bottom-color: #b3b3b3;
2584 -webkit-border-radius: 4px;
2585 -moz-border-radius: 4px;
2586 border-radius: 4px;
2587 filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);
2588 filter: progid:dximagetransform.microsoft.gradient(enabled=false);
2589 *zoom: 1;
2590 -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
2591 -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
2592 box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
2593 }
2594
2595 .btn:hover,
2596 .btn:active,
2597 .btn.active,
2598 .btn.disabled,
2599 .btn[disabled] {
2600 background-color: #e6e6e6;
2601 *background-color: #d9d9d9;
2602 }
2603
2604 .btn:active,
2605 .btn.active {
2606 background-color: #cccccc \9;
2607 }
2608
2609 .btn:first-child {
2610 *margin-left: 0;
2611 }
2612
2613 .btn:hover {
2614 color: #333333;
2615 text-decoration: none;
2616 background-color: #e6e6e6;
2617 *background-color: #d9d9d9;
2618 /* Buttons in IE7 don't get borders, so darken on hover */
2619
2620 background-position: 0 -15px;
2621 -webkit-transition: background-position 0.1s linear;
2622 -moz-transition: background-position 0.1s linear;
2623 -ms-transition: background-position 0.1s linear;
2624 -o-transition: background-position 0.1s linear;
2625 transition: background-position 0.1s linear;
2626 }
2627
2628 .btn:focus {
2629 outline: thin dotted #333;
2630 outline: 5px auto -webkit-focus-ring-color;
2631 outline-offset: -2px;
2632 }
2633
2634 .btn.active,
2635 .btn:active {
2636 background-color: #e6e6e6;
2637 background-color: #d9d9d9 \9;
2638 background-image: none;
2639 outline: 0;
2640 -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
2641 -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
2642 box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
2643 }
2644
2645 .btn.disabled,
2646 .btn[disabled] {
2647 cursor: default;
2648 background-color: #e6e6e6;
2649 background-image: none;
2650 opacity: 0.65;
2651 filter: alpha(opacity=65);
2652 -webkit-box-shadow: none;
2653 -moz-box-shadow: none;
2654 box-shadow: none;
2655 }
2656
2657 .btn-large {
2658 padding: 9px 14px;
2659 font-size: 15px;
2660 line-height: normal;
2661 -webkit-border-radius: 5px;
2662 -moz-border-radius: 5px;
2663 border-radius: 5px;
2664 }
2665
2666 .btn-large [class^="icon-"] {
2667 margin-top: 1px;
2668 }
2669
2670 .btn-small {
2671 padding: 5px 9px;
2672 font-size: 11px;
2673 line-height: 16px;
2674 }
2675
2676 .btn-small [class^="icon-"] {
2677 margin-top: -1px;
2678 }
2679
2680 .btn-mini {
2681 padding: 2px 6px;
2682 font-size: 11px;
2683 line-height: 14px;
2684 }
2685
2686 .btn-primary,
2687 .btn-primary:hover,
2688 .btn-warning,
2689 .btn-warning:hover,
2690 .btn-danger,
2691 .btn-danger:hover,
2692 .btn-success,
2693 .btn-success:hover,
2694 .btn-info,
2695 .btn-info:hover,
2696 .btn-inverse,
2697 .btn-inverse:hover {
2698 color: #ffffff;
2699 text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
2700 }
2701
2702 .btn-primary.active,
2703 .btn-warning.active,
2704 .btn-danger.active,
2705 .btn-success.active,
2706 .btn-info.active,
2707 .btn-inverse.active {
2708 color: rgba(255, 255, 255, 0.75);
2709 }
2710
2711 .btn {
2712 border-color: #ccc;
2713 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
2714 }
2715
2716 .btn-primary {
2717 background-color: #0074cc;
2718 *background-color: #0055cc;
2719 background-image: -ms-linear-gradient(top, #0088cc, #0055cc);
2720 background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0055cc));
2721 background-image: -webkit-linear-gradient(top, #0088cc, #0055cc);
2722 background-image: -o-linear-gradient(top, #0088cc, #0055cc);
2723 background-image: -moz-linear-gradient(top, #0088cc, #0055cc);
2724 background-image: linear-gradient(top, #0088cc, #0055cc);
2725 background-repeat: repeat-x;
2726 border-color: #0055cc #0055cc #003580;
2727 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
2728 filter: progid:dximagetransform.microsoft.gradient(startColorstr='#0088cc', endColorstr='#0055cc', GradientType=0);
2729 filter: progid:dximagetransform.microsoft.gradient(enabled=false);
2730 }
2731
2732 .btn-primary:hover,
2733 .btn-primary:active,
2734 .btn-primary.active,
2735 .btn-primary.disabled,
2736 .btn-primary[disabled] {
2737 background-color: #0055cc;
2738 *background-color: #004ab3;
2739 }
2740
2741 .btn-primary:active,
2742 .btn-primary.active {
2743 background-color: #004099 \9;
2744 }
2745
2746 .btn-warning {
2747 background-color: #faa732;
2748 *background-color: #f89406;
2749 background-image: -ms-linear-gradient(top, #fbb450, #f89406);
2750 background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
2751 background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
2752 background-image: -o-linear-gradient(top, #fbb450, #f89406);
2753 background-image: -moz-linear-gradient(top, #fbb450, #f89406);
2754 background-image: linear-gradient(top, #fbb450, #f89406);
2755 background-repeat: repeat-x;
2756 border-color: #f89406 #f89406 #ad6704;
2757 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
2758 filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);
2759 filter: progid:dximagetransform.microsoft.gradient(enabled=false);
2760 }
2761
2762 .btn-warning:hover,
2763 .btn-warning:active,
2764 .btn-warning.active,
2765 .btn-warning.disabled,
2766 .btn-warning[disabled] {
2767 background-color: #f89406;
2768 *background-color: #df8505;
2769 }
2770
2771 .btn-warning:active,
2772 .btn-warning.active {
2773 background-color: #c67605 \9;
2774 }
2775
2776 .btn-danger {
2777 background-color: #da4f49;
2778 *background-color: #bd362f;
2779 background-image: -ms-linear-gradient(top, #ee5f5b, #bd362f);
2780 background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));
2781 background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f);
2782 background-image: -o-linear-gradient(top, #ee5f5b, #bd362f);
2783 background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f);
2784 background-image: linear-gradient(top, #ee5f5b, #bd362f);
2785 background-repeat: repeat-x;
2786 border-color: #bd362f #bd362f #802420;
2787 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
2788 filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);
2789 filter: progid:dximagetransform.microsoft.gradient(enabled=false);
2790 }
2791
2792 .btn-danger:hover,
2793 .btn-danger:active,
2794 .btn-danger.active,
2795 .btn-danger.disabled,
2796 .btn-danger[disabled] {
2797 background-color: #bd362f;
2798 *background-color: #a9302a;
2799 }
2800
2801 .btn-danger:active,
2802 .btn-danger.active {
2803 background-color: #942a25 \9;
2804 }
2805
2806 .btn-success {
2807 background-color: #5bb75b;
2808 *background-color: #51a351;
2809 background-image: -ms-linear-gradient(top, #62c462, #51a351);
2810 background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));
2811 background-image: -webkit-linear-gradient(top, #62c462, #51a351);
2812 background-image: -o-linear-gradient(top, #62c462, #51a351);
2813 background-image: -moz-linear-gradient(top, #62c462, #51a351);
2814 background-image: linear-gradient(top, #62c462, #51a351);
2815 background-repeat: repeat-x;
2816 border-color: #51a351 #51a351 #387038;
2817 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
2818 filter: progid:dximagetransform.microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);
2819 filter: progid:dximagetransform.microsoft.gradient(enabled=false);
2820 }
2821
2822 .btn-success:hover,
2823 .btn-success:active,
2824 .btn-success.active,
2825 .btn-success.disabled,
2826 .btn-success[disabled] {
2827 background-color: #51a351;
2828 *background-color: #499249;
2829 }
2830
2831 .btn-success:active,
2832 .btn-success.active {
2833 background-color: #408140 \9;
2834 }
2835
2836 .btn-info {
2837 background-color: #49afcd;
2838 *background-color: #2f96b4;
2839 background-image: -ms-linear-gradient(top, #5bc0de, #2f96b4);
2840 background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));
2841 background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4);
2842 background-image: -o-linear-gradient(top, #5bc0de, #2f96b4);
2843 background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4);
2844 background-image: linear-gradient(top, #5bc0de, #2f96b4);
2845 background-repeat: repeat-x;
2846 border-color: #2f96b4 #2f96b4 #1f6377;
2847 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
2848 filter: progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);
2849 filter: progid:dximagetransform.microsoft.gradient(enabled=false);
2850 }
2851
2852 .btn-info:hover,
2853 .btn-info:active,
2854 .btn-info.active,
2855 .btn-info.disabled,
2856 .btn-info[disabled] {
2857 background-color: #2f96b4;
2858 *background-color: #2a85a0;
2859 }
2860
2861 .btn-info:active,
2862 .btn-info.active {
2863 background-color: #24748c \9;
2864 }
2865
2866 .btn-inverse {
2867 background-color: #414141;
2868 *background-color: #222222;
2869 background-image: -ms-linear-gradient(top, #555555, #222222);
2870 background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#222222));
2871 background-image: -webkit-linear-gradient(top, #555555, #222222);
2872 background-image: -o-linear-gradient(top, #555555, #222222);
2873 background-image: -moz-linear-gradient(top, #555555, #222222);
2874 background-image: linear-gradient(top, #555555, #222222);
2875 background-repeat: repeat-x;
2876 border-color: #222222 #222222 #000000;
2877 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
2878 filter: progid:dximagetransform.microsoft.gradient(startColorstr='#555555', endColorstr='#222222', GradientType=0);
2879 filter: progid:dximagetransform.microsoft.gradient(enabled=false);
2880 }
2881
2882 .btn-inverse:hover,
2883 .btn-inverse:active,
2884 .btn-inverse.active,
2885 .btn-inverse.disabled,
2886 .btn-inverse[disabled] {
2887 background-color: #222222;
2888 *background-color: #151515;
2889 }
2890
2891 .btn-inverse:active,
2892 .btn-inverse.active {
2893 background-color: #080808 \9;
2894 }
2895
2896 button.btn,
2897 input[type="submit"].btn {
2898 *padding-top: 2px;
2899 *padding-bottom: 2px;
2900 }
2901
2902 button.btn::-moz-focus-inner,
2903 input[type="submit"].btn::-moz-focus-inner {
2904 padding: 0;
2905 border: 0;
2906 }
2907
2908 button.btn.btn-large,
2909 input[type="submit"].btn.btn-large {
2910 *padding-top: 7px;
2911 *padding-bottom: 7px;
2912 }
2913
2914 button.btn.btn-small,
2915 input[type="submit"].btn.btn-small {
2916 *padding-top: 3px;
2917 *padding-bottom: 3px;
2918 }
2919
2920 button.btn.btn-mini,
2921 input[type="submit"].btn.btn-mini {
2922 *padding-top: 1px;
2923 *padding-bottom: 1px;
2924 }
2925
2926 .btn-group {
2927 position: relative;
2928 *margin-left: .3em;
2929 *zoom: 1;
2930 }
2931
2932 .btn-group:before,
2933 .btn-group:after {
2934 display: table;
2935 content: "";
2936 }
2937
2938 .btn-group:after {
2939 clear: both;
2940 }
2941
2942 .btn-group:first-child {
2943 *margin-left: 0;
2944 }
2945
2946 .btn-group + .btn-group {
2947 margin-left: 5px;
2948 }
2949
2950 .btn-toolbar {
2951 margin-top: 9px;
2952 margin-bottom: 9px;
2953 }
2954
2955 .btn-toolbar .btn-group {
2956 display: inline-block;
2957 *display: inline;
2958 /* IE7 inline-block hack */
2959
2960 *zoom: 1;
2961 }
2962
2963 .btn-group > .btn {
2964 position: relative;
2965 float: left;
2966 margin-left: -1px;
2967 -webkit-border-radius: 0;
2968 -moz-border-radius: 0;
2969 border-radius: 0;
2970 }
2971
2972 .btn-group > .btn:first-child {
2973 margin-left: 0;
2974 -webkit-border-bottom-left-radius: 4px;
2975 border-bottom-left-radius: 4px;
2976 -webkit-border-top-left-radius: 4px;
2977 border-top-left-radius: 4px;
2978 -moz-border-radius-bottomleft: 4px;
2979 -moz-border-radius-topleft: 4px;
2980 }
2981
2982 .btn-group > .btn:last-child,
2983 .btn-group > .dropdown-toggle {
2984 -webkit-border-top-right-radius: 4px;
2985 border-top-right-radius: 4px;
2986 -webkit-border-bottom-right-radius: 4px;
2987 border-bottom-right-radius: 4px;
2988 -moz-border-radius-topright: 4px;
2989 -moz-border-radius-bottomright: 4px;
2990 }
2991
2992 .btn-group > .btn.large:first-child {
2993 margin-left: 0;
2994 -webkit-border-bottom-left-radius: 6px;
2995 border-bottom-left-radius: 6px;
2996 -webkit-border-top-left-radius: 6px;
2997 border-top-left-radius: 6px;
2998 -moz-border-radius-bottomleft: 6px;
2999 -moz-border-radius-topleft: 6px;
3000 }
3001
3002 .btn-group > .btn.large:last-child,
3003 .btn-group > .large.dropdown-toggle {
3004 -webkit-border-top-right-radius: 6px;
3005 border-top-right-radius: 6px;
3006 -webkit-border-bottom-right-radius: 6px;
3007 border-bottom-right-radius: 6px;
3008 -moz-border-radius-topright: 6px;
3009 -moz-border-radius-bottomright: 6px;
3010 }
3011
3012 .btn-group > .btn:hover,
3013 .btn-group > .btn:focus,
3014 .btn-group > .btn:active,
3015 .btn-group > .btn.active {
3016 z-index: 2;
3017 }
3018
3019 .btn-group .dropdown-toggle:active,
3020 .btn-group.open .dropdown-toggle {
3021 outline: 0;
3022 }
3023
3024 .btn-group > .dropdown-toggle {
3025 *padding-top: 4px;
3026 padding-right: 8px;
3027 *padding-bottom: 4px;
3028 padding-left: 8px;
3029 -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
3030 -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
3031 box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
3032 }
3033
3034 .btn-group > .btn-mini.dropdown-toggle {
3035 padding-right: 5px;
3036 padding-left: 5px;
3037 }
3038
3039 .btn-group > .btn-small.dropdown-toggle {
3040 *padding-top: 4px;
3041 *padding-bottom: 4px;
3042 }
3043
3044 .btn-group > .btn-large.dropdown-toggle {
3045 padding-right: 12px;
3046 padding-left: 12px;
3047 }
3048
3049 .btn-group.open .dropdown-toggle {
3050 background-image: none;
3051 -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
3052 -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
3053 box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
3054 }
3055
3056 .btn-group.open .btn.dropdown-toggle {
3057 background-color: #e6e6e6;
3058 }
3059
3060 .btn-group.open .btn-primary.dropdown-toggle {
3061 background-color: #0055cc;
3062 }
3063
3064 .btn-group.open .btn-warning.dropdown-toggle {
3065 background-color: #f89406;
3066 }
3067
3068 .btn-group.open .btn-danger.dropdown-toggle {
3069 background-color: #bd362f;
3070 }
3071
3072 .btn-group.open .btn-success.dropdown-toggle {
3073 background-color: #51a351;
3074 }
3075
3076 .btn-group.open .btn-info.dropdown-toggle {
3077 background-color: #2f96b4;
3078 }
3079
3080 .btn-group.open .btn-inverse.dropdown-toggle {
3081 background-color: #222222;
3082 }
3083
3084 .btn .caret {
3085 margin-top: 7px;
3086 margin-left: 0;
3087 }
3088
3089 .btn:hover .caret,
3090 .open.btn-group .caret {
3091 opacity: 1;
3092 filter: alpha(opacity=100);
3093 }
3094
3095 .btn-mini .caret {
3096 margin-top: 5px;
3097 }
3098
3099 .btn-small .caret {
3100 margin-top: 6px;
3101 }
3102
3103 .btn-large .caret {
3104 margin-top: 6px;
3105 border-top-width: 5px;
3106 border-right-width: 5px;
3107 border-left-width: 5px;
3108 }
3109
3110 .dropup .btn-large .caret {
3111 border-top: 0;
3112 border-bottom: 5px solid #000000;
3113 }
3114
3115 .btn-primary .caret,
3116 .btn-warning .caret,
3117 .btn-danger .caret,
3118 .btn-info .caret,
3119 .btn-success .caret,
3120 .btn-inverse .caret {
3121 border-top-color: #ffffff;
3122 border-bottom-color: #ffffff;
3123 opacity: 0.75;
3124 filter: alpha(opacity=75);
3125 }
3126
3127 .alert {
3128 padding: 8px 35px 8px 14px;
3129 margin-bottom: 18px;
3130 color: #c09853;
3131 text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
3132 background-color: #fcf8e3;
3133 border: 1px solid #fbeed5;
3134 -webkit-border-radius: 4px;
3135 -moz-border-radius: 4px;
3136 border-radius: 4px;
3137 }
3138
3139 .alert-heading {
3140 color: inherit;
3141 }
3142
3143 .alert .close {
3144 position: relative;
3145 top: -2px;
3146 right: -21px;
3147 line-height: 18px;
3148 }
3149
3150 .alert-success {
3151 color: #468847;
3152 background-color: #dff0d8;
3153 border-color: #d6e9c6;
3154 }
3155
3156 .alert-danger,
3157 .alert-error {
3158 color: #b94a48;
3159 background-color: #f2dede;
3160 border-color: #eed3d7;
3161 }
3162
3163 .alert-info {
3164 color: #3a87ad;
3165 background-color: #d9edf7;
3166 border-color: #bce8f1;
3167 }
3168
3169 .alert-block {
3170 padding-top: 14px;
3171 padding-bottom: 14px;
3172 }
3173
3174 .alert-block > p,
3175 .alert-block > ul {
3176 margin-bottom: 0;
3177 }
3178
3179 .alert-block p + p {
3180 margin-top: 5px;
3181 }
3182
3183 .nav {
3184 margin-bottom: 18px;
3185 margin-left: 0;
3186 list-style: none;
3187 }
3188
3189 .nav > li > a {
3190 display: block;
3191 }
3192
3193 .nav > li > a:hover {
3194 text-decoration: none;
3195 background-color: #eeeeee;
3196 }
3197
3198 .nav > .pull-right {
3199 float: right;
3200 }
3201
3202 .nav .nav-header {
3203 display: block;
3204 padding: 3px 15px;
3205 font-size: 11px;
3206 font-weight: bold;
3207 line-height: 18px;
3208 color: #999999;
3209 text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
3210 text-transform: uppercase;
3211 }
3212
3213 .nav li + .nav-header {
3214 margin-top: 9px;
3215 }
3216
3217 .nav-list {
3218 padding-right: 15px;
3219 padding-left: 15px;
3220 margin-bottom: 0;
3221 }
3222
3223 .nav-list > li > a,
3224 .nav-list .nav-header {
3225 margin-right: -15px;
3226 margin-left: -15px;
3227 text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
3228 }
3229
3230 .nav-list > li > a {
3231 padding: 3px 15px;
3232 }
3233
3234 .nav-list > .active > a,
3235 .nav-list > .active > a:hover {
3236 color: #ffffff;
3237 text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
3238 background-color: #0088cc;
3239 }
3240
3241 .nav-list [class^="icon-"] {
3242 margin-right: 2px;
3243 }
3244
3245 .nav-list .divider {
3246 *width: 100%;
3247 height: 1px;
3248 margin: 8px 1px;
3249 *margin: -5px 0 5px;
3250 overflow: hidden;
3251 background-color: #e5e5e5;
3252 border-bottom: 1px solid #ffffff;
3253 }
3254
3255 .nav-tabs,
3256 .nav-pills {
3257 *zoom: 1;
3258 }
3259
3260 .nav-tabs:before,
3261 .nav-pills:before,
3262 .nav-tabs:after,
3263 .nav-pills:after {
3264 display: table;
3265 content: "";
3266 }
3267
3268 .nav-tabs:after,
3269 .nav-pills:after {
3270 clear: both;
3271 }
3272
3273 .nav-tabs > li,
3274 .nav-pills > li {
3275 float: left;
3276 }
3277
3278 .nav-tabs > li > a,
3279 .nav-pills > li > a {
3280 padding-right: 12px;
3281 padding-left: 12px;
3282 margin-right: 2px;
3283 line-height: 14px;
3284 }
3285
3286 .nav-tabs {
3287 border-bottom: 1px solid #ddd;
3288 }
3289
3290 .nav-tabs > li {
3291 margin-bottom: -1px;
3292 }
3293
3294 .nav-tabs > li > a {
3295 padding-top: 8px;
3296 padding-bottom: 8px;
3297 line-height: 18px;
3298 border: 1px solid transparent;
3299 -webkit-border-radius: 4px 4px 0 0;
3300 -moz-border-radius: 4px 4px 0 0;
3301 border-radius: 4px 4px 0 0;
3302 }
3303
3304 .nav-tabs > li > a:hover {
3305 border-color: #eeeeee #eeeeee #dddddd;
3306 }
3307
3308 .nav-tabs > .active > a,
3309 .nav-tabs > .active > a:hover {
3310 color: #555555;
3311 cursor: default;
3312 background-color: #ffffff;
3313 border: 1px solid #ddd;
3314 border-bottom-color: transparent;
3315 }
3316
3317 .nav-pills > li > a {
3318 padding-top: 8px;
3319 padding-bottom: 8px;
3320 margin-top: 2px;
3321 margin-bottom: 2px;
3322 -webkit-border-radius: 5px;
3323 -moz-border-radius: 5px;
3324 border-radius: 5px;
3325 }
3326
3327 .nav-pills > .active > a,
3328 .nav-pills > .active > a:hover {
3329 color: #ffffff;
3330 background-color: #0088cc;
3331 }
3332
3333 .nav-stacked > li {
3334 float: none;
3335 }
3336
3337 .nav-stacked > li > a {
3338 margin-right: 0;
3339 }
3340
3341 .nav-tabs.nav-stacked {
3342 border-bottom: 0;
3343 }
3344
3345 .nav-tabs.nav-stacked > li > a {
3346 border: 1px solid #ddd;
3347 -webkit-border-radius: 0;
3348 -moz-border-radius: 0;
3349 border-radius: 0;
3350 }
3351
3352 .nav-tabs.nav-stacked > li:first-child > a {
3353 -webkit-border-radius: 4px 4px 0 0;
3354 -moz-border-radius: 4px 4px 0 0;
3355 border-radius: 4px 4px 0 0;
3356 }
3357
3358 .nav-tabs.nav-stacked > li:last-child > a {
3359 -webkit-border-radius: 0 0 4px 4px;
3360 -moz-border-radius: 0 0 4px 4px;
3361 border-radius: 0 0 4px 4px;
3362 }
3363
3364 .nav-tabs.nav-stacked > li > a:hover {
3365 z-index: 2;
3366 border-color: #ddd;
3367 }
3368
3369 .nav-pills.nav-stacked > li > a {
3370 margin-bottom: 3px;
3371 }
3372
3373 .nav-pills.nav-stacked > li:last-child > a {
3374 margin-bottom: 1px;
3375 }
3376
3377 .nav-tabs .dropdown-menu {
3378 -webkit-border-radius: 0 0 5px 5px;
3379 -moz-border-radius: 0 0 5px 5px;
3380 border-radius: 0 0 5px 5px;
3381 }
3382
3383 .nav-pills .dropdown-menu {
3384 -webkit-border-radius: 4px;
3385 -moz-border-radius: 4px;
3386 border-radius: 4px;
3387 }
3388
3389 .nav-tabs .dropdown-toggle .caret,
3390 .nav-pills .dropdown-toggle .caret {
3391 margin-top: 6px;
3392 border-top-color: #0088cc;
3393 border-bottom-color: #0088cc;
3394 }
3395
3396 .nav-tabs .dropdown-toggle:hover .caret,
3397 .nav-pills .dropdown-toggle:hover .caret {
3398 border-top-color: #005580;
3399 border-bottom-color: #005580;
3400 }
3401
3402 .nav-tabs .active .dropdown-toggle .caret,
3403 .nav-pills .active .dropdown-toggle .caret {
3404 border-top-color: #333333;
3405 border-bottom-color: #333333;
3406 }
3407
3408 .nav > .dropdown.active > a:hover {
3409 color: #000000;
3410 cursor: pointer;
3411 }
3412
3413 .nav-tabs .open .dropdown-toggle,
3414 .nav-pills .open .dropdown-toggle,
3415 .nav > li.dropdown.open.active > a:hover {
3416 color: #ffffff;
3417 background-color: #999999;
3418 border-color: #999999;
3419 }
3420
3421 .nav li.dropdown.open .caret,
3422 .nav li.dropdown.open.active .caret,
3423 .nav li.dropdown.open a:hover .caret {
3424 border-top-color: #ffffff;
3425 border-bottom-color: #ffffff;
3426 opacity: 1;
3427 filter: alpha(opacity=100);
3428 }
3429
3430 .tabs-stacked .open > a:hover {
3431 border-color: #999999;
3432 }
3433
3434 .tabbable {
3435 *zoom: 1;
3436 }
3437
3438 .tabbable:before,
3439 .tabbable:after {
3440 display: table;
3441 content: "";
3442 }
3443
3444 .tabbable:after {
3445 clear: both;
3446 }
3447
3448 .tab-content {
3449 overflow: auto;
3450 }
3451
3452 .tabs-below > .nav-tabs,
3453 .tabs-right > .nav-tabs,
3454 .tabs-left > .nav-tabs {
3455 border-bottom: 0;
3456 }
3457
3458 .tab-content > .tab-pane,
3459 .pill-content > .pill-pane {
3460 display: none;
3461 }
3462
3463 .tab-content > .active,
3464 .pill-content > .active {
3465 display: block;
3466 }
3467
3468 .tabs-below > .nav-tabs {
3469 border-top: 1px solid #ddd;
3470 }
3471
3472 .tabs-below > .nav-tabs > li {
3473 margin-top: -1px;
3474 margin-bottom: 0;
3475 }
3476
3477 .tabs-below > .nav-tabs > li > a {
3478 -webkit-border-radius: 0 0 4px 4px;
3479 -moz-border-radius: 0 0 4px 4px;
3480 border-radius: 0 0 4px 4px;
3481 }
3482
3483 .tabs-below > .nav-tabs > li > a:hover {
3484 border-top-color: #ddd;
3485 border-bottom-color: transparent;
3486 }
3487
3488 .tabs-below > .nav-tabs > .active > a,
3489 .tabs-below > .nav-tabs > .active > a:hover {
3490 border-color: transparent #ddd #ddd #ddd;
3491 }
3492
3493 .tabs-left > .nav-tabs > li,
3494 .tabs-right > .nav-tabs > li {
3495 float: none;
3496 }
3497
3498 .tabs-left > .nav-tabs > li > a,
3499 .tabs-right > .nav-tabs > li > a {
3500 min-width: 74px;
3501 margin-right: 0;
3502 margin-bottom: 3px;
3503 }
3504
3505 .tabs-left > .nav-tabs {
3506 float: left;
3507 margin-right: 19px;
3508 border-right: 1px solid #ddd;
3509 }
3510
3511 .tabs-left > .nav-tabs > li > a {
3512 margin-right: -1px;
3513 -webkit-border-radius: 4px 0 0 4px;
3514 -moz-border-radius: 4px 0 0 4px;
3515 border-radius: 4px 0 0 4px;
3516 }
3517
3518 .tabs-left > .nav-tabs > li > a:hover {
3519 border-color: #eeeeee #dddddd #eeeeee #eeeeee;
3520 }
3521
3522 .tabs-left > .nav-tabs .active > a,
3523 .tabs-left > .nav-tabs .active > a:hover {
3524 border-color: #ddd transparent #ddd #ddd;
3525 *border-right-color: #ffffff;
3526 }
3527
3528 .tabs-right > .nav-tabs {
3529 float: right;
3530 margin-left: 19px;
3531 border-left: 1px solid #ddd;
3532 }
3533
3534 .tabs-right > .nav-tabs > li > a {
3535 margin-left: -1px;
3536 -webkit-border-radius: 0 4px 4px 0;
3537 -moz-border-radius: 0 4px 4px 0;
3538 border-radius: 0 4px 4px 0;
3539 }
3540
3541 .tabs-right > .nav-tabs > li > a:hover {
3542 border-color: #eeeeee #eeeeee #eeeeee #dddddd;
3543 }
3544
3545 .tabs-right > .nav-tabs .active > a,
3546 .tabs-right > .nav-tabs .active > a:hover {
3547 border-color: #ddd #ddd #ddd transparent;
3548 *border-left-color: #ffffff;
3549 }
3550
3551 .navbar {
3552 *position: relative;
3553 *z-index: 2;
3554 margin-bottom: 18px;
3555 overflow: visible;
3556 }
3557
3558 .navbar-inner {
3559 min-height: 40px;
3560 padding-right: 20px;
3561 padding-left: 20px;
3562 background-color: #2c2c2c;
3563 background-image: -moz-linear-gradient(top, #333333, #222222);
3564 background-image: -ms-linear-gradient(top, #333333, #222222);
3565 background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));
3566 background-image: -webkit-linear-gradient(top, #333333, #222222);
3567 background-image: -o-linear-gradient(top, #333333, #222222);
3568 background-image: linear-gradient(top, #333333, #222222);
3569 background-repeat: repeat-x;
3570 -webkit-border-radius: 4px;
3571 -moz-border-radius: 4px;
3572 border-radius: 4px;
3573 filter: progid:dximagetransform.microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);
3574 -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1);
3575 -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1);
3576 box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1);
3577 }
3578
3579 .navbar .container {
3580 width: auto;
3581 }
3582
3583 .nav-collapse.collapse {
3584 height: auto;
3585 }
3586
3587 .navbar {
3588 color: #999999;
3589 }
3590
3591 .navbar .brand:hover {
3592 text-decoration: none;
3593 }
3594
3595 .navbar .brand {
3596 display: block;
3597 float: left;
3598 padding: 8px 20px 12px;
3599 margin-left: -20px;
3600 font-size: 20px;
3601 font-weight: 200;
3602 line-height: 1;
3603 color: #999999;
3604 }
3605
3606 .navbar .navbar-text {
3607 margin-bottom: 0;
3608 line-height: 40px;
3609 }
3610
3611 .navbar .navbar-link {
3612 color: #999999;
3613 }
3614
3615 .navbar .navbar-link:hover {
3616 color: #ffffff;
3617 }
3618
3619 .navbar .btn,
3620 .navbar .btn-group {
3621 margin-top: 5px;
3622 }
3623
3624 .navbar .btn-group .btn {
3625 margin: 0;
3626 }
3627
3628 .navbar-form {
3629 margin-bottom: 0;
3630 *zoom: 1;
3631 }
3632
3633 .navbar-form:before,
3634 .navbar-form:after {
3635 display: table;
3636 content: "";
3637 }
3638
3639 .navbar-form:after {
3640 clear: both;
3641 }
3642
3643 .navbar-form input,
3644 .navbar-form select,
3645 .navbar-form .radio,
3646 .navbar-form .checkbox {
3647 margin-top: 5px;
3648 }
3649
3650 .navbar-form input,
3651 .navbar-form select {
3652 display: inline-block;
3653 margin-bottom: 0;
3654 }
3655
3656 .navbar-form input[type="image"],
3657 .navbar-form input[type="checkbox"],
3658 .navbar-form input[type="radio"] {
3659 margin-top: 3px;
3660 }
3661
3662 .navbar-form .input-append,
3663 .navbar-form .input-prepend {
3664 margin-top: 6px;
3665 white-space: normal;
3666 }
3667
3668 .navbar-form .input-append input,
3669 .navbar-form .input-prepend input {
3670 margin-top: 0;
3671 }
3672
3673 .navbar-search {
3674 position: relative;
3675 float: left;
3676 margin-top: 6px;
3677 margin-bottom: 0;
3678 }
3679
3680 .navbar-search .search-query {
3681 padding: 4px 9px;
3682 font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
3683 font-size: 13px;
3684 font-weight: normal;
3685 line-height: 1;
3686 color: #ffffff;
3687 background-color: #626262;
3688 border: 1px solid #151515;
3689 -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
3690 -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
3691 box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
3692 -webkit-transition: none;
3693 -moz-transition: none;
3694 -ms-transition: none;
3695 -o-transition: none;
3696 transition: none;
3697 }
3698
3699 .navbar-search .search-query:-moz-placeholder {
3700 color: #cccccc;
3701 }
3702
3703 .navbar-search .search-query::-webkit-input-placeholder {
3704 color: #cccccc;
3705 }
3706
3707 .navbar-search .search-query:focus,
3708 .navbar-search .search-query.focused {
3709 padding: 5px 10px;
3710 color: #333333;
3711 text-shadow: 0 1px 0 #ffffff;
3712 background-color: #ffffff;
3713 border: 0;
3714 outline: 0;
3715 -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
3716 -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
3717 box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
3718 }
3719
3720 .navbar-fixed-top,
3721 .navbar-fixed-bottom {
3722 position: fixed;
3723 right: 0;
3724 left: 0;
3725 z-index: 1030;
3726 margin-bottom: 0;
3727 }
3728
3729 .navbar-fixed-top .navbar-inner,
3730 .navbar-fixed-bottom .navbar-inner {
3731 padding-right: 0;
3732 padding-left: 0;
3733 -webkit-border-radius: 0;
3734 -moz-border-radius: 0;
3735 border-radius: 0;
3736 }
3737
3738 .navbar-fixed-top .container,
3739 .navbar-fixed-bottom .container {
3740 width: 940px;
3741 }
3742
3743 .navbar-fixed-top {
3744 top: 0;
3745 }
3746
3747 .navbar-fixed-bottom {
3748 bottom: 0;
3749 }
3750
3751 .navbar .nav {
3752 position: relative;
3753 left: 0;
3754 display: block;
3755 float: left;
3756 margin: 0 10px 0 0;
3757 }
3758
3759 .navbar .nav.pull-right {
3760 float: right;
3761 }
3762
3763 .navbar .nav > li {
3764 display: block;
3765 float: left;
3766 }
3767
3768 .navbar .nav > li > a {
3769 float: none;
3770 padding: 9px 10px 11px;
3771 line-height: 19px;
3772 color: #999999;
3773 text-decoration: none;
3774 text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
3775 }
3776
3777 .navbar .btn {
3778 display: inline-block;
3779 padding: 4px 10px 4px;
3780 margin: 5px 5px 6px;
3781 line-height: 18px;
3782 }
3783
3784 .navbar .btn-group {
3785 padding: 5px 5px 6px;
3786 margin: 0;
3787 }
3788
3789 .navbar .nav > li > a:hover {
3790 color: #ffffff;
3791 text-decoration: none;
3792 background-color: transparent;
3793 }
3794
3795 .navbar .nav .active > a,
3796 .navbar .nav .active > a:hover {
3797 color: #ffffff;
3798 text-decoration: none;
3799 background-color: #222222;
3800 }
3801
3802 .navbar .divider-vertical {
3803 width: 1px;
3804 height: 40px;
3805 margin: 0 9px;
3806 overflow: hidden;
3807 background-color: #222222;
3808 border-right: 1px solid #333333;
3809 }
3810
3811 .navbar .nav.pull-right {
3812 margin-right: 0;
3813 margin-left: 10px;
3814 }
3815
3816 .navbar .btn-navbar {
3817 display: none;
3818 float: right;
3819 padding: 7px 10px;
3820 margin-right: 5px;
3821 margin-left: 5px;
3822 background-color: #2c2c2c;
3823 *background-color: #222222;
3824 background-image: -ms-linear-gradient(top, #333333, #222222);
3825 background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));
3826 background-image: -webkit-linear-gradient(top, #333333, #222222);
3827 background-image: -o-linear-gradient(top, #333333, #222222);
3828 background-image: linear-gradient(top, #333333, #222222);
3829 background-image: -moz-linear-gradient(top, #333333, #222222);
3830 background-repeat: repeat-x;
3831 border-color: #222222 #222222 #000000;
3832 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3833 filter: progid:dximagetransform.microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);
3834 filter: progid:dximagetransform.microsoft.gradient(enabled=false);
3835 -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
3836 -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
3837 box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
3838 }
3839
3840 .navbar .btn-navbar:hover,
3841 .navbar .btn-navbar:active,
3842 .navbar .btn-navbar.active,
3843 .navbar .btn-navbar.disabled,
3844 .navbar .btn-navbar[disabled] {
3845 background-color: #222222;
3846 *background-color: #151515;
3847 }
3848
3849 .navbar .btn-navbar:active,
3850 .navbar .btn-navbar.active {
3851 background-color: #080808 \9;
3852 }
3853
3854 .navbar .btn-navbar .icon-bar {
3855 display: block;
3856 width: 18px;
3857 height: 2px;
3858 background-color: #f5f5f5;
3859 -webkit-border-radius: 1px;
3860 -moz-border-radius: 1px;
3861 border-radius: 1px;
3862 -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
3863 -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
3864 box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
3865 }
3866
3867 .btn-navbar .icon-bar + .icon-bar {
3868 margin-top: 3px;
3869 }
3870
3871 .navbar .dropdown-menu:before {
3872 position: absolute;
3873 top: -7px;
3874 left: 9px;
3875 display: inline-block;
3876 border-right: 7px solid transparent;
3877 border-bottom: 7px solid #ccc;
3878 border-left: 7px solid transparent;
3879 border-bottom-color: rgba(0, 0, 0, 0.2);
3880 content: '';
3881 }
3882
3883 .navbar .dropdown-menu:after {
3884 position: absolute;
3885 top: -6px;
3886 left: 10px;
3887 display: inline-block;
3888 border-right: 6px solid transparent;
3889 border-bottom: 6px solid #ffffff;
3890 border-left: 6px solid transparent;
3891 content: '';
3892 }
3893
3894 .navbar-fixed-bottom .dropdown-menu:before {
3895 top: auto;
3896 bottom: -7px;
3897 border-top: 7px solid #ccc;
3898 border-bottom: 0;
3899 border-top-color: rgba(0, 0, 0, 0.2);
3900 }
3901
3902 .navbar-fixed-bottom .dropdown-menu:after {
3903 top: auto;
3904 bottom: -6px;
3905 border-top: 6px solid #ffffff;
3906 border-bottom: 0;
3907 }
3908
3909 .navbar .nav li.dropdown .dropdown-toggle .caret,
3910 .navbar .nav li.dropdown.open .caret {
3911 border-top-color: #ffffff;
3912 border-bottom-color: #ffffff;
3913 }
3914
3915 .navbar .nav li.dropdown.active .caret {
3916 opacity: 1;
3917 filter: alpha(opacity=100);
3918 }
3919
3920 .navbar .nav li.dropdown.open > .dropdown-toggle,
3921 .navbar .nav li.dropdown.active > .dropdown-toggle,
3922 .navbar .nav li.dropdown.open.active > .dropdown-toggle {
3923 background-color: transparent;
3924 }
3925
3926 .navbar .nav li.dropdown.active > .dropdown-toggle:hover {
3927 color: #ffffff;
3928 }
3929
3930 .navbar .pull-right .dropdown-menu,
3931 .navbar .dropdown-menu.pull-right {
3932 right: 0;
3933 left: auto;
3934 }
3935
3936 .navbar .pull-right .dropdown-menu:before,
3937 .navbar .dropdown-menu.pull-right:before {
3938 right: 12px;
3939 left: auto;
3940 }
3941
3942 .navbar .pull-right .dropdown-menu:after,
3943 .navbar .dropdown-menu.pull-right:after {
3944 right: 13px;
3945 left: auto;
3946 }
3947
3948 .breadcrumb {
3949 padding: 7px 14px;
3950 margin: 0 0 18px;
3951 list-style: none;
3952 background-color: #fbfbfb;
3953 background-image: -moz-linear-gradient(top, #ffffff, #f5f5f5);
3954 background-image: -ms-linear-gradient(top, #ffffff, #f5f5f5);
3955 background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5));
3956 background-image: -webkit-linear-gradient(top, #ffffff, #f5f5f5);
3957 background-image: -o-linear-gradient(top, #ffffff, #f5f5f5);
3958 background-image: linear-gradient(top, #ffffff, #f5f5f5);
3959 background-repeat: repeat-x;
3960 border: 1px solid #ddd;
3961 -webkit-border-radius: 3px;
3962 -moz-border-radius: 3px;
3963 border-radius: 3px;
3964 filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);
3965 -webkit-box-shadow: inset 0 1px 0 #ffffff;
3966 -moz-box-shadow: inset 0 1px 0 #ffffff;
3967 box-shadow: inset 0 1px 0 #ffffff;
3968 }
3969
3970 .breadcrumb li {
3971 display: inline-block;
3972 *display: inline;
3973 text-shadow: 0 1px 0 #ffffff;
3974 *zoom: 1;
3975 }
3976
3977 .breadcrumb .divider {
3978 padding: 0 5px;
3979 color: #999999;
3980 }
3981
3982 .breadcrumb .active a {
3983 color: #333333;
3984 }
3985
3986 .pagination {
3987 height: 36px;
3988 margin: 18px 0;
3989 }
3990
3991 .pagination ul {
3992 display: inline-block;
3993 *display: inline;
3994 margin-bottom: 0;
3995 margin-left: 0;
3996 -webkit-border-radius: 3px;
3997 -moz-border-radius: 3px;
3998 border-radius: 3px;
3999 *zoom: 1;
4000 -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
4001 -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
4002 box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
4003 }
4004
4005 .pagination li {
4006 display: inline;
4007 }
4008
4009 .pagination a {
4010 float: left;
4011 padding: 0 14px;
4012 line-height: 34px;
4013 text-decoration: none;
4014 border: 1px solid #ddd;
4015 border-left-width: 0;
4016 }
4017
4018 .pagination a:hover,
4019 .pagination .active a {
4020 background-color: #f5f5f5;
4021 }
4022
4023 .pagination .active a {
4024 color: #999999;
4025 cursor: default;
4026 }
4027
4028 .pagination .disabled span,
4029 .pagination .disabled a,
4030 .pagination .disabled a:hover {
4031 color: #999999;
4032 cursor: default;
4033 background-color: transparent;
4034 }
4035
4036 .pagination li:first-child a {
4037 border-left-width: 1px;
4038 -webkit-border-radius: 3px 0 0 3px;
4039 -moz-border-radius: 3px 0 0 3px;
4040 border-radius: 3px 0 0 3px;
4041 }
4042
4043 .pagination li:last-child a {
4044 -webkit-border-radius: 0 3px 3px 0;
4045 -moz-border-radius: 0 3px 3px 0;
4046 border-radius: 0 3px 3px 0;
4047 }
4048
4049 .pagination-centered {
4050 text-align: center;
4051 }
4052
4053 .pagination-right {
4054 text-align: right;
4055 }
4056
4057 .pager {
4058 margin-bottom: 18px;
4059 margin-left: 0;
4060 text-align: center;
4061 list-style: none;
4062 *zoom: 1;
4063 }
4064
4065 .pager:before,
4066 .pager:after {
4067 display: table;
4068 content: "";
4069 }
4070
4071 .pager:after {
4072 clear: both;
4073 }
4074
4075 .pager li {
4076 display: inline;
4077 }
4078
4079 .pager a {
4080 display: inline-block;
4081 padding: 5px 14px;
4082 background-color: #fff;
4083 border: 1px solid #ddd;
4084 -webkit-border-radius: 15px;
4085 -moz-border-radius: 15px;
4086 border-radius: 15px;
4087 }
4088
4089 .pager a:hover {
4090 text-decoration: none;
4091 background-color: #f5f5f5;
4092 }
4093
4094 .pager .next a {
4095 float: right;
4096 }
4097
4098 .pager .previous a {
4099 float: left;
4100 }
4101
4102 .pager .disabled a,
4103 .pager .disabled a:hover {
4104 color: #999999;
4105 cursor: default;
4106 background-color: #fff;
4107 }
4108
4109 .modal-open .dropdown-menu {
4110 z-index: 2050;
4111 }
4112
4113 .modal-open .dropdown.open {
4114 *z-index: 2050;
4115 }
4116
4117 .modal-open .popover {
4118 z-index: 2060;
4119 }
4120
4121 .modal-open .tooltip {
4122 z-index: 2070;
4123 }
4124
4125 .modal-backdrop {
4126 position: fixed;
4127 top: 0;
4128 right: 0;
4129 bottom: 0;
4130 left: 0;
4131 z-index: 1040;
4132 background-color: #000000;
4133 }
4134
4135 .modal-backdrop.fade {
4136 opacity: 0;
4137 }
4138
4139 .modal-backdrop,
4140 .modal-backdrop.fade.in {
4141 opacity: 0.8;
4142 filter: alpha(opacity=80);
4143 }
4144
4145 .modal {
4146 position: fixed;
4147 top: 50%;
4148 left: 50%;
4149 z-index: 1050;
4150 width: 560px;
4151 margin: -250px 0 0 -280px;
4152 overflow: auto;
4153 background-color: #ffffff;
4154 border: 1px solid #999;
4155 border: 1px solid rgba(0, 0, 0, 0.3);
4156 *border: 1px solid #999;
4157 -webkit-border-radius: 6px;
4158 -moz-border-radius: 6px;
4159 border-radius: 6px;
4160 -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
4161 -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
4162 box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
4163 -webkit-background-clip: padding-box;
4164 -moz-background-clip: padding-box;
4165 background-clip: padding-box;
4166 }
4167
4168 .modal.fade {
4169 top: -25%;
4170 -webkit-transition: opacity 0.3s linear, top 0.3s ease-out;
4171 -moz-transition: opacity 0.3s linear, top 0.3s ease-out;
4172 -ms-transition: opacity 0.3s linear, top 0.3s ease-out;
4173 -o-transition: opacity 0.3s linear, top 0.3s ease-out;
4174 transition: opacity 0.3s linear, top 0.3s ease-out;
4175 }
4176
4177 .modal.fade.in {
4178 top: 50%;
4179 }
4180
4181 .modal-header {
4182 padding: 9px 15px;
4183 border-bottom: 1px solid #eee;
4184 }
4185
4186 .modal-header .close {
4187 margin-top: 2px;
4188 }
4189
4190 .modal-body {
4191 max-height: 400px;
4192 padding: 15px;
4193 overflow-y: auto;
4194 }
4195
4196 .modal-form {
4197 margin-bottom: 0;
4198 }
4199
4200 .modal-footer {
4201 padding: 14px 15px 15px;
4202 margin-bottom: 0;
4203 text-align: right;
4204 background-color: #f5f5f5;
4205 border-top: 1px solid #ddd;
4206 -webkit-border-radius: 0 0 6px 6px;
4207 -moz-border-radius: 0 0 6px 6px;
4208 border-radius: 0 0 6px 6px;
4209 *zoom: 1;
4210 -webkit-box-shadow: inset 0 1px 0 #ffffff;
4211 -moz-box-shadow: inset 0 1px 0 #ffffff;
4212 box-shadow: inset 0 1px 0 #ffffff;
4213 }
4214
4215 .modal-footer:before,
4216 .modal-footer:after {
4217 display: table;
4218 content: "";
4219 }
4220
4221 .modal-footer:after {
4222 clear: both;
4223 }
4224
4225 .modal-footer .btn + .btn {
4226 margin-bottom: 0;
4227 margin-left: 5px;
4228 }
4229
4230 .modal-footer .btn-group .btn + .btn {
4231 margin-left: -1px;
4232 }
4233
4234 .tooltip {
4235 position: absolute;
4236 z-index: 1020;
4237 display: block;
4238 padding: 5px;
4239 font-size: 11px;
4240 opacity: 0;
4241 filter: alpha(opacity=0);
4242 visibility: visible;
4243 }
4244
4245 .tooltip.in {
4246 opacity: 0.8;
4247 filter: alpha(opacity=80);
4248 }
4249
4250 .tooltip.top {
4251 margin-top: -2px;
4252 }
4253
4254 .tooltip.right {
4255 margin-left: 2px;
4256 }
4257
4258 .tooltip.bottom {
4259 margin-top: 2px;
4260 }
4261
4262 .tooltip.left {
4263 margin-left: -2px;
4264 }
4265
4266 .tooltip.top .tooltip-arrow {
4267 bottom: 0;
4268 left: 50%;
4269 margin-left: -5px;
4270 border-top: 5px solid #000000;
4271 border-right: 5px solid transparent;
4272 border-left: 5px solid transparent;
4273 }
4274
4275 .tooltip.left .tooltip-arrow {
4276 top: 50%;
4277 right: 0;
4278 margin-top: -5px;
4279 border-top: 5px solid transparent;
4280 border-bottom: 5px solid transparent;
4281 border-left: 5px solid #000000;
4282 }
4283
4284 .tooltip.bottom .tooltip-arrow {
4285 top: 0;
4286 left: 50%;
4287 margin-left: -5px;
4288 border-right: 5px solid transparent;
4289 border-bottom: 5px solid #000000;
4290 border-left: 5px solid transparent;
4291 }
4292
4293 .tooltip.right .tooltip-arrow {
4294 top: 50%;
4295 left: 0;
4296 margin-top: -5px;
4297 border-top: 5px solid transparent;
4298 border-right: 5px solid #000000;
4299 border-bottom: 5px solid transparent;
4300 }
4301
4302 .tooltip-inner {
4303 max-width: 200px;
4304 padding: 3px 8px;
4305 color: #ffffff;
4306 text-align: center;
4307 text-decoration: none;
4308 background-color: #000000;
4309 -webkit-border-radius: 4px;
4310 -moz-border-radius: 4px;
4311 border-radius: 4px;
4312 }
4313
4314 .tooltip-arrow {
4315 position: absolute;
4316 width: 0;
4317 height: 0;
4318 }
4319
4320 .popover {
4321 position: absolute;
4322 top: 0;
4323 left: 0;
4324 z-index: 1010;
4325 display: none;
4326 padding: 5px;
4327 }
4328
4329 .popover.top {
4330 margin-top: -5px;
4331 }
4332
4333 .popover.right {
4334 margin-left: 5px;
4335 }
4336
4337 .popover.bottom {
4338 margin-top: 5px;
4339 }
4340
4341 .popover.left {
4342 margin-left: -5px;
4343 }
4344
4345 .popover.top .arrow {
4346 bottom: 0;
4347 left: 50%;
4348 margin-left: -5px;
4349 border-top: 5px solid #000000;
4350 border-right: 5px solid transparent;
4351 border-left: 5px solid transparent;
4352 }
4353
4354 .popover.right .arrow {
4355 top: 50%;
4356 left: 0;
4357 margin-top: -5px;
4358 border-top: 5px solid transparent;
4359 border-right: 5px solid #000000;
4360 border-bottom: 5px solid transparent;
4361 }
4362
4363 .popover.bottom .arrow {
4364 top: 0;
4365 left: 50%;
4366 margin-left: -5px;
4367 border-right: 5px solid transparent;
4368 border-bottom: 5px solid #000000;
4369 border-left: 5px solid transparent;
4370 }
4371
4372 .popover.left .arrow {
4373 top: 50%;
4374 right: 0;
4375 margin-top: -5px;
4376 border-top: 5px solid transparent;
4377 border-bottom: 5px solid transparent;
4378 border-left: 5px solid #000000;
4379 }
4380
4381 .popover .arrow {
4382 position: absolute;
4383 width: 0;
4384 height: 0;
4385 }
4386
4387 .popover-inner {
4388 width: 280px;
4389 padding: 3px;
4390 overflow: hidden;
4391 background: #000000;
4392 background: rgba(0, 0, 0, 0.8);
4393 -webkit-border-radius: 6px;
4394 -moz-border-radius: 6px;
4395 border-radius: 6px;
4396 -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
4397 -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
4398 box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
4399 }
4400
4401 .popover-title {
4402 padding: 9px 15px;
4403 line-height: 1;
4404 background-color: #f5f5f5;
4405 border-bottom: 1px solid #eee;
4406 -webkit-border-radius: 3px 3px 0 0;
4407 -moz-border-radius: 3px 3px 0 0;
4408 border-radius: 3px 3px 0 0;
4409 }
4410
4411 .popover-content {
4412 padding: 14px;
4413 background-color: #ffffff;
4414 -webkit-border-radius: 0 0 3px 3px;
4415 -moz-border-radius: 0 0 3px 3px;
4416 border-radius: 0 0 3px 3px;
4417 -webkit-background-clip: padding-box;
4418 -moz-background-clip: padding-box;
4419 background-clip: padding-box;
4420 }
4421
4422 .popover-content p,
4423 .popover-content ul,
4424 .popover-content ol {
4425 margin-bottom: 0;
4426 }
4427
4428 .thumbnails {
4429 margin-left: -20px;
4430 list-style: none;
4431 *zoom: 1;
4432 }
4433
4434 .thumbnails:before,
4435 .thumbnails:after {
4436 display: table;
4437 content: "";
4438 }
4439
4440 .thumbnails:after {
4441 clear: both;
4442 }
4443
4444 .row-fluid .thumbnails {
4445 margin-left: 0;
4446 }
4447
4448 .thumbnails > li {
4449 float: left;
4450 margin-bottom: 18px;
4451 margin-left: 20px;
4452 }
4453
4454 .thumbnail {
4455 display: block;
4456 padding: 4px;
4457 line-height: 1;
4458 border: 1px solid #ddd;
4459 -webkit-border-radius: 4px;
4460 -moz-border-radius: 4px;
4461 border-radius: 4px;
4462 -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075);
4463 -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075);
4464 box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075);
4465 }
4466
4467 a.thumbnail:hover {
4468 border-color: #0088cc;
4469 -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
4470 -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
4471 box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
4472 }
4473
4474 .thumbnail > img {
4475 display: block;
4476 max-width: 100%;
4477 margin-right: auto;
4478 margin-left: auto;
4479 }
4480
4481 .thumbnail .caption {
4482 padding: 9px;
4483 }
4484
4485 .label,
4486 .badge {
4487 font-size: 10.998px;
4488 font-weight: bold;
4489 line-height: 14px;
4490 color: #ffffff;
4491 text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
4492 white-space: nowrap;
4493 vertical-align: baseline;
4494 background-color: #999999;
4495 }
4496
4497 .label {
4498 padding: 1px 4px 2px;
4499 -webkit-border-radius: 3px;
4500 -moz-border-radius: 3px;
4501 border-radius: 3px;
4502 }
4503
4504 .badge {
4505 padding: 1px 9px 2px;
4506 -webkit-border-radius: 9px;
4507 -moz-border-radius: 9px;
4508 border-radius: 9px;
4509 }
4510
4511 a.label:hover,
4512 a.badge:hover {
4513 color: #ffffff;
4514 text-decoration: none;
4515 cursor: pointer;
4516 }
4517
4518 .label-important,
4519 .badge-important {
4520 background-color: #b94a48;
4521 }
4522
4523 .label-important[href],
4524 .badge-important[href] {
4525 background-color: #953b39;
4526 }
4527
4528 .label-warning,
4529 .badge-warning {
4530 background-color: #f89406;
4531 }
4532
4533 .label-warning[href],
4534 .badge-warning[href] {
4535 background-color: #c67605;
4536 }
4537
4538 .label-success,
4539 .badge-success {
4540 background-color: #468847;
4541 }
4542
4543 .label-success[href],
4544 .badge-success[href] {
4545 background-color: #356635;
4546 }
4547
4548 .label-info,
4549 .badge-info {
4550 background-color: #3a87ad;
4551 }
4552
4553 .label-info[href],
4554 .badge-info[href] {
4555 background-color: #2d6987;
4556 }
4557
4558 .label-inverse,
4559 .badge-inverse {
4560 background-color: #333333;
4561 }
4562
4563 .label-inverse[href],
4564 .badge-inverse[href] {
4565 background-color: #1a1a1a;
4566 }
4567
4568 @-webkit-keyframes progress-bar-stripes {
4569 from {
4570 background-position: 40px 0;
4571 }
4572 to {
4573 background-position: 0 0;
4574 }
4575 }
4576
4577 @-moz-keyframes progress-bar-stripes {
4578 from {
4579 background-position: 40px 0;
4580 }
4581 to {
4582 background-position: 0 0;
4583 }
4584 }
4585
4586 @-ms-keyframes progress-bar-stripes {
4587 from {
4588 background-position: 40px 0;
4589 }
4590 to {
4591 background-position: 0 0;
4592 }
4593 }
4594
4595 @-o-keyframes progress-bar-stripes {
4596 from {
4597 background-position: 0 0;
4598 }
4599 to {
4600 background-position: 40px 0;
4601 }
4602 }
4603
4604 @keyframes progress-bar-stripes {
4605 from {
4606 background-position: 40px 0;
4607 }
4608 to {
4609 background-position: 0 0;
4610 }
4611 }
4612
4613 .progress {
4614 height: 18px;
4615 margin-bottom: 18px;
4616 overflow: hidden;
4617 background-color: #f7f7f7;
4618 background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9);
4619 background-image: -ms-linear-gradient(top, #f5f5f5, #f9f9f9);
4620 background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));
4621 background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);
4622 background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);
4623 background-image: linear-gradient(top, #f5f5f5, #f9f9f9);
4624 background-repeat: repeat-x;
4625 -webkit-border-radius: 4px;
4626 -moz-border-radius: 4px;
4627 border-radius: 4px;
4628 filter: progid:dximagetransform.microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0);
4629 -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
4630 -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
4631 box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
4632 }
4633
4634 .progress .bar {
4635 width: 0;
4636 height: 18px;
4637 font-size: 12px;
4638 color: #ffffff;
4639 text-align: center;
4640 text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
4641 background-color: #0e90d2;
4642 background-image: -moz-linear-gradient(top, #149bdf, #0480be);
4643 background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));
4644 background-image: -webkit-linear-gradient(top, #149bdf, #0480be);
4645 background-image: -o-linear-gradient(top, #149bdf, #0480be);
4646 background-image: linear-gradient(top, #149bdf, #0480be);
4647 background-image: -ms-linear-gradient(top, #149bdf, #0480be);
4648 background-repeat: repeat-x;
4649 filter: progid:dximagetransform.microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0);
4650 -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
4651 -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
4652 box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
4653 -webkit-box-sizing: border-box;
4654 -moz-box-sizing: border-box;
4655 -ms-box-sizing: border-box;
4656 box-sizing: border-box;
4657 -webkit-transition: width 0.6s ease;
4658 -moz-transition: width 0.6s ease;
4659 -ms-transition: width 0.6s ease;
4660 -o-transition: width 0.6s ease;
4661 transition: width 0.6s ease;
4662 }
4663
4664 .progress-striped .bar {
4665 background-color: #149bdf;
4666 background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4667 background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4668 background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4669 background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4670 background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
4671 background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4672 -webkit-background-size: 40px 40px;
4673 -moz-background-size: 40px 40px;
4674 -o-background-size: 40px 40px;
4675 background-size: 40px 40px;
4676 }
4677
4678 .progress.active .bar {
4679 -webkit-animation: progress-bar-stripes 2s linear infinite;
4680 -moz-animation: progress-bar-stripes 2s linear infinite;
4681 -ms-animation: progress-bar-stripes 2s linear infinite;
4682 -o-animation: progress-bar-stripes 2s linear infinite;
4683 animation: progress-bar-stripes 2s linear infinite;
4684 }
4685
4686 .progress-danger .bar {
4687 background-color: #dd514c;
4688 background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
4689 background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
4690 background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));
4691 background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
4692 background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
4693 background-image: linear-gradient(top, #ee5f5b, #c43c35);
4694 background-repeat: repeat-x;
4695 filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);
4696 }
4697
4698 .progress-danger.progress-striped .bar {
4699 background-color: #ee5f5b;
4700 background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
4701 background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4702 background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4703 background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4704 background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4705 background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4706 }
4707
4708 .progress-success .bar {
4709 background-color: #5eb95e;
4710 background-image: -moz-linear-gradient(top, #62c462, #57a957);
4711 background-image: -ms-linear-gradient(top, #62c462, #57a957);
4712 background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));
4713 background-image: -webkit-linear-gradient(top, #62c462, #57a957);
4714 background-image: -o-linear-gradient(top, #62c462, #57a957);
4715 background-image: linear-gradient(top, #62c462, #57a957);
4716 background-repeat: repeat-x;
4717 filter: progid:dximagetransform.microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);
4718 }
4719
4720 .progress-success.progress-striped .bar {
4721 background-color: #62c462;
4722 background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
4723 background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4724 background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4725 background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4726 background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4727 background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4728 }
4729
4730 .progress-info .bar {
4731 background-color: #4bb1cf;
4732 background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
4733 background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
4734 background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));
4735 background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
4736 background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
4737 background-image: linear-gradient(top, #5bc0de, #339bb9);
4738 background-repeat: repeat-x;
4739 filter: progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);
4740 }
4741
4742 .progress-info.progress-striped .bar {
4743 background-color: #5bc0de;
4744 background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
4745 background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4746 background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4747 background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4748 background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4749 background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4750 }
4751
4752 .progress-warning .bar {
4753 background-color: #faa732;
4754 background-image: -moz-linear-gradient(top, #fbb450, #f89406);
4755 background-image: -ms-linear-gradient(top, #fbb450, #f89406);
4756 background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
4757 background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
4758 background-image: -o-linear-gradient(top, #fbb450, #f89406);
4759 background-image: linear-gradient(top, #fbb450, #f89406);
4760 background-repeat: repeat-x;
4761 filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);
4762 }
4763
4764 .progress-warning.progress-striped .bar {
4765 background-color: #fbb450;
4766 background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
4767 background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4768 background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4769 background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4770 background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4771 background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
4772 }
4773
4774 .accordion {
4775 margin-bottom: 18px;
4776 }
4777
4778 .accordion-group {
4779 margin-bottom: 2px;
4780 border: 1px solid #e5e5e5;
4781 -webkit-border-radius: 4px;
4782 -moz-border-radius: 4px;
4783 border-radius: 4px;
4784 }
4785
4786 .accordion-heading {
4787 border-bottom: 0;
4788 }
4789
4790 .accordion-heading .accordion-toggle {
4791 display: block;
4792 padding: 8px 15px;
4793 }
4794
4795 .accordion-toggle {
4796 cursor: pointer;
4797 }
4798
4799 .accordion-inner {
4800 padding: 9px 15px;
4801 border-top: 1px solid #e5e5e5;
4802 }
4803
4804 .carousel {
4805 position: relative;
4806 margin-bottom: 18px;
4807 line-height: 1;
4808 }
4809
4810 .carousel-inner {
4811 position: relative;
4812 width: 100%;
4813 overflow: hidden;
4814 }
4815
4816 .carousel .item {
4817 position: relative;
4818 display: none;
4819 -webkit-transition: 0.6s ease-in-out left;
4820 -moz-transition: 0.6s ease-in-out left;
4821 -ms-transition: 0.6s ease-in-out left;
4822 -o-transition: 0.6s ease-in-out left;
4823 transition: 0.6s ease-in-out left;
4824 }
4825
4826 .carousel .item > img {
4827 display: block;
4828 line-height: 1;
4829 }
4830
4831 .carousel .active,
4832 .carousel .next,
4833 .carousel .prev {
4834 display: block;
4835 }
4836
4837 .carousel .active {
4838 left: 0;
4839 }
4840
4841 .carousel .next,
4842 .carousel .prev {
4843 position: absolute;
4844 top: 0;
4845 width: 100%;
4846 }
4847
4848 .carousel .next {
4849 left: 100%;
4850 }
4851
4852 .carousel .prev {
4853 left: -100%;
4854 }
4855
4856 .carousel .next.left,
4857 .carousel .prev.right {
4858 left: 0;
4859 }
4860
4861 .carousel .active.left {
4862 left: -100%;
4863 }
4864
4865 .carousel .active.right {
4866 left: 100%;
4867 }
4868
4869 .carousel-control {
4870 position: absolute;
4871 top: 40%;
4872 left: 15px;
4873 width: 40px;
4874 height: 40px;
4875 margin-top: -20px;
4876 font-size: 60px;
4877 font-weight: 100;
4878 line-height: 30px;
4879 color: #ffffff;
4880 text-align: center;
4881 background: #222222;
4882 border: 3px solid #ffffff;
4883 -webkit-border-radius: 23px;
4884 -moz-border-radius: 23px;
4885 border-radius: 23px;
4886 opacity: 0.5;
4887 filter: alpha(opacity=50);
4888 }
4889
4890 .carousel-control.right {
4891 right: 15px;
4892 left: auto;
4893 }
4894
4895 .carousel-control:hover {
4896 color: #ffffff;
4897 text-decoration: none;
4898 opacity: 0.9;
4899 filter: alpha(opacity=90);
4900 }
4901
4902 .carousel-caption {
4903 position: absolute;
4904 right: 0;
4905 bottom: 0;
4906 left: 0;
4907 padding: 10px 15px 5px;
4908 background: #333333;
4909 background: rgba(0, 0, 0, 0.75);
4910 }
4911
4912 .carousel-caption h4,
4913 .carousel-caption p {
4914 color: #ffffff;
4915 }
4916
4917 .hero-unit {
4918 padding: 60px;
4919 margin-bottom: 30px;
4920 background-color: #eeeeee;
4921 -webkit-border-radius: 6px;
4922 -moz-border-radius: 6px;
4923 border-radius: 6px;
4924 }
4925
4926 .hero-unit h1 {
4927 margin-bottom: 0;
4928 font-size: 60px;
4929 line-height: 1;
4930 letter-spacing: -1px;
4931 color: inherit;
4932 }
4933
4934 .hero-unit p {
4935 font-size: 18px;
4936 font-weight: 200;
4937 line-height: 27px;
4938 color: inherit;
4939 }
4940
4941 .pull-right {
4942 float: right;
4943 }
4944
4945 .pull-left {
4946 float: left;
4947 }
4948
4949 .hide {
4950 display: none;
4951 }
4952
4953 .show {
4954 display: block;
4955 }
4956
4957 .invisible {
4958 visibility: hidden;
4959 }
0 /* ===================================================
1 * bootstrap-transition.js v2.0.3
2 * http://twitter.github.com/bootstrap/javascript.html#transitions
3 * ===================================================
4 * Copyright 2012 Twitter, Inc.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 * ========================================================== */
18
19
20 !function ($) {
21
22 $(function () {
23
24 "use strict"; // jshint ;_;
25
26
27 /* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
28 * ======================================================= */
29
30 $.support.transition = (function () {
31
32 var transitionEnd = (function () {
33
34 var el = document.createElement('bootstrap')
35 , transEndEventNames = {
36 'WebkitTransition' : 'webkitTransitionEnd'
37 , 'MozTransition' : 'transitionend'
38 , 'OTransition' : 'oTransitionEnd'
39 //, 'msTransition' : 'MSTransitionEnd'
40 , 'transition' : 'transitionend'
41 }
42 , name
43
44 for (name in transEndEventNames){
45 if (el.style[name] !== undefined) {
46 return transEndEventNames[name]
47 }
48 }
49
50 }())
51
52 return transitionEnd && {
53 end: transitionEnd
54 }
55
56 })()
57
58 })
59
60 }(window.jQuery);/* ==========================================================
61 * bootstrap-alert.js v2.0.3
62 * http://twitter.github.com/bootstrap/javascript.html#alerts
63 * ==========================================================
64 * Copyright 2012 Twitter, Inc.
65 *
66 * Licensed under the Apache License, Version 2.0 (the "License");
67 * you may not use this file except in compliance with the License.
68 * You may obtain a copy of the License at
69 *
70 * http://www.apache.org/licenses/LICENSE-2.0
71 *
72 * Unless required by applicable law or agreed to in writing, software
73 * distributed under the License is distributed on an "AS IS" BASIS,
74 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
75 * See the License for the specific language governing permissions and
76 * limitations under the License.
77 * ========================================================== */
78
79
80 !function ($) {
81
82 "use strict"; // jshint ;_;
83
84
85 /* ALERT CLASS DEFINITION
86 * ====================== */
87
88 var dismiss = '[data-dismiss="alert"]'
89 , Alert = function (el) {
90 $(el).on('click', dismiss, this.close)
91 }
92
93 Alert.prototype.close = function (e) {
94 var $this = $(this)
95 , selector = $this.attr('data-target')
96 , $parent
97
98 if (!selector) {
99 selector = $this.attr('href')
100 selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
101 }
102
103 $parent = $(selector)
104
105 e && e.preventDefault()
106
107 $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
108
109 $parent.trigger(e = $.Event('close'))
110
111 if (e.isDefaultPrevented()) return
112
113 $parent.removeClass('in')
114
115 function removeElement() {
116 $parent
117 .trigger('closed')
118 .remove()
119 }
120
121 $.support.transition && $parent.hasClass('fade') ?
122 $parent.on($.support.transition.end, removeElement) :
123 removeElement()
124 }
125
126
127 /* ALERT PLUGIN DEFINITION
128 * ======================= */
129
130 $.fn.alert = function (option) {
131 return this.each(function () {
132 var $this = $(this)
133 , data = $this.data('alert')
134 if (!data) $this.data('alert', (data = new Alert(this)))
135 if (typeof option == 'string') data[option].call($this)
136 })
137 }
138
139 $.fn.alert.Constructor = Alert
140
141
142 /* ALERT DATA-API
143 * ============== */
144
145 $(function () {
146 $('body').on('click.alert.data-api', dismiss, Alert.prototype.close)
147 })
148
149 }(window.jQuery);/* ============================================================
150 * bootstrap-button.js v2.0.3
151 * http://twitter.github.com/bootstrap/javascript.html#buttons
152 * ============================================================
153 * Copyright 2012 Twitter, Inc.
154 *
155 * Licensed under the Apache License, Version 2.0 (the "License");
156 * you may not use this file except in compliance with the License.
157 * You may obtain a copy of the License at
158 *
159 * http://www.apache.org/licenses/LICENSE-2.0
160 *
161 * Unless required by applicable law or agreed to in writing, software
162 * distributed under the License is distributed on an "AS IS" BASIS,
163 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
164 * See the License for the specific language governing permissions and
165 * limitations under the License.
166 * ============================================================ */
167
168
169 !function ($) {
170
171 "use strict"; // jshint ;_;
172
173
174 /* BUTTON PUBLIC CLASS DEFINITION
175 * ============================== */
176
177 var Button = function (element, options) {
178 this.$element = $(element)
179 this.options = $.extend({}, $.fn.button.defaults, options)
180 }
181
182 Button.prototype.setState = function (state) {
183 var d = 'disabled'
184 , $el = this.$element
185 , data = $el.data()
186 , val = $el.is('input') ? 'val' : 'html'
187
188 state = state + 'Text'
189 data.resetText || $el.data('resetText', $el[val]())
190
191 $el[val](data[state] || this.options[state])
192
193 // push to event loop to allow forms to submit
194 setTimeout(function () {
195 state == 'loadingText' ?
196 $el.addClass(d).attr(d, d) :
197 $el.removeClass(d).removeAttr(d)
198 }, 0)
199 }
200
201 Button.prototype.toggle = function () {
202 var $parent = this.$element.parent('[data-toggle="buttons-radio"]')
203
204 $parent && $parent
205 .find('.active')
206 .removeClass('active')
207
208 this.$element.toggleClass('active')
209 }
210
211
212 /* BUTTON PLUGIN DEFINITION
213 * ======================== */
214
215 $.fn.button = function (option) {
216 return this.each(function () {
217 var $this = $(this)
218 , data = $this.data('button')
219 , options = typeof option == 'object' && option
220 if (!data) $this.data('button', (data = new Button(this, options)))
221 if (option == 'toggle') data.toggle()
222 else if (option) data.setState(option)
223 })
224 }
225
226 $.fn.button.defaults = {
227 loadingText: 'loading...'
228 }
229
230 $.fn.button.Constructor = Button
231
232
233 /* BUTTON DATA-API
234 * =============== */
235
236 $(function () {
237 $('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) {
238 var $btn = $(e.target)
239 if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
240 $btn.button('toggle')
241 })
242 })
243
244 }(window.jQuery);/* ==========================================================
245 * bootstrap-carousel.js v2.0.3
246 * http://twitter.github.com/bootstrap/javascript.html#carousel
247 * ==========================================================
248 * Copyright 2012 Twitter, Inc.
249 *
250 * Licensed under the Apache License, Version 2.0 (the "License");
251 * you may not use this file except in compliance with the License.
252 * You may obtain a copy of the License at
253 *
254 * http://www.apache.org/licenses/LICENSE-2.0
255 *
256 * Unless required by applicable law or agreed to in writing, software
257 * distributed under the License is distributed on an "AS IS" BASIS,
258 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
259 * See the License for the specific language governing permissions and
260 * limitations under the License.
261 * ========================================================== */
262
263
264 !function ($) {
265
266 "use strict"; // jshint ;_;
267
268
269 /* CAROUSEL CLASS DEFINITION
270 * ========================= */
271
272 var Carousel = function (element, options) {
273 this.$element = $(element)
274 this.options = options
275 this.options.slide && this.slide(this.options.slide)
276 this.options.pause == 'hover' && this.$element
277 .on('mouseenter', $.proxy(this.pause, this))
278 .on('mouseleave', $.proxy(this.cycle, this))
279 }
280
281 Carousel.prototype = {
282
283 cycle: function (e) {
284 if (!e) this.paused = false
285 this.options.interval
286 && !this.paused
287 && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
288 return this
289 }
290
291 , to: function (pos) {
292 var $active = this.$element.find('.active')
293 , children = $active.parent().children()
294 , activePos = children.index($active)
295 , that = this
296
297 if (pos > (children.length - 1) || pos < 0) return
298
299 if (this.sliding) {
300 return this.$element.one('slid', function () {
301 that.to(pos)
302 })
303 }
304
305 if (activePos == pos) {
306 return this.pause().cycle()
307 }
308
309 return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos]))
310 }
311
312 , pause: function (e) {
313 if (!e) this.paused = true
314 clearInterval(this.interval)
315 this.interval = null
316 return this
317 }
318
319 , next: function () {
320 if (this.sliding) return
321 return this.slide('next')
322 }
323
324 , prev: function () {
325 if (this.sliding) return
326 return this.slide('prev')
327 }
328
329 , slide: function (type, next) {
330 var $active = this.$element.find('.active')
331 , $next = next || $active[type]()
332 , isCycling = this.interval
333 , direction = type == 'next' ? 'left' : 'right'
334 , fallback = type == 'next' ? 'first' : 'last'
335 , that = this
336 , e = $.Event('slide')
337
338 this.sliding = true
339
340 isCycling && this.pause()
341
342 $next = $next.length ? $next : this.$element.find('.item')[fallback]()
343
344 if ($next.hasClass('active')) return
345
346 if ($.support.transition && this.$element.hasClass('slide')) {
347 this.$element.trigger(e)
348 if (e.isDefaultPrevented()) return
349 $next.addClass(type)
350 $next[0].offsetWidth // force reflow
351 $active.addClass(direction)
352 $next.addClass(direction)
353 this.$element.one($.support.transition.end, function () {
354 $next.removeClass([type, direction].join(' ')).addClass('active')
355 $active.removeClass(['active', direction].join(' '))
356 that.sliding = false
357 setTimeout(function () { that.$element.trigger('slid') }, 0)
358 })
359 } else {
360 this.$element.trigger(e)
361 if (e.isDefaultPrevented()) return
362 $active.removeClass('active')
363 $next.addClass('active')
364 this.sliding = false
365 this.$element.trigger('slid')
366 }
367
368 isCycling && this.cycle()
369
370 return this
371 }
372
373 }
374
375
376 /* CAROUSEL PLUGIN DEFINITION
377 * ========================== */
378
379 $.fn.carousel = function (option) {
380 return this.each(function () {
381 var $this = $(this)
382 , data = $this.data('carousel')
383 , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option)
384 if (!data) $this.data('carousel', (data = new Carousel(this, options)))
385 if (typeof option == 'number') data.to(option)
386 else if (typeof option == 'string' || (option = options.slide)) data[option]()
387 else if (options.interval) data.cycle()
388 })
389 }
390
391 $.fn.carousel.defaults = {
392 interval: 5000
393 , pause: 'hover'
394 }
395
396 $.fn.carousel.Constructor = Carousel
397
398
399 /* CAROUSEL DATA-API
400 * ================= */
401
402 $(function () {
403 $('body').on('click.carousel.data-api', '[data-slide]', function ( e ) {
404 var $this = $(this), href
405 , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
406 , options = !$target.data('modal') && $.extend({}, $target.data(), $this.data())
407 $target.carousel(options)
408 e.preventDefault()
409 })
410 })
411
412 }(window.jQuery);/* =============================================================
413 * bootstrap-collapse.js v2.0.3
414 * http://twitter.github.com/bootstrap/javascript.html#collapse
415 * =============================================================
416 * Copyright 2012 Twitter, Inc.
417 *
418 * Licensed under the Apache License, Version 2.0 (the "License");
419 * you may not use this file except in compliance with the License.
420 * You may obtain a copy of the License at
421 *
422 * http://www.apache.org/licenses/LICENSE-2.0
423 *
424 * Unless required by applicable law or agreed to in writing, software
425 * distributed under the License is distributed on an "AS IS" BASIS,
426 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
427 * See the License for the specific language governing permissions and
428 * limitations under the License.
429 * ============================================================ */
430
431
432 !function ($) {
433
434 "use strict"; // jshint ;_;
435
436
437 /* COLLAPSE PUBLIC CLASS DEFINITION
438 * ================================ */
439
440 var Collapse = function (element, options) {
441 this.$element = $(element)
442 this.options = $.extend({}, $.fn.collapse.defaults, options)
443
444 if (this.options.parent) {
445 this.$parent = $(this.options.parent)
446 }
447
448 this.options.toggle && this.toggle()
449 }
450
451 Collapse.prototype = {
452
453 constructor: Collapse
454
455 , dimension: function () {
456 var hasWidth = this.$element.hasClass('width')
457 return hasWidth ? 'width' : 'height'
458 }
459
460 , show: function () {
461 var dimension
462 , scroll
463 , actives
464 , hasData
465
466 if (this.transitioning) return
467
468 dimension = this.dimension()
469 scroll = $.camelCase(['scroll', dimension].join('-'))
470 actives = this.$parent && this.$parent.find('> .accordion-group > .in')
471
472 if (actives && actives.length) {
473 hasData = actives.data('collapse')
474 if (hasData && hasData.transitioning) return
475 actives.collapse('hide')
476 hasData || actives.data('collapse', null)
477 }
478
479 this.$element[dimension](0)
480 this.transition('addClass', $.Event('show'), 'shown')
481 this.$element[dimension](this.$element[0][scroll])
482 }
483
484 , hide: function () {
485 var dimension
486 if (this.transitioning) return
487 dimension = this.dimension()
488 this.reset(this.$element[dimension]())
489 this.transition('removeClass', $.Event('hide'), 'hidden')
490 this.$element[dimension](0)
491 }
492
493 , reset: function (size) {
494 var dimension = this.dimension()
495
496 this.$element
497 .removeClass('collapse')
498 [dimension](size || 'auto')
499 [0].offsetWidth
500
501 this.$element[size !== null ? 'addClass' : 'removeClass']('collapse')
502
503 return this
504 }
505
506 , transition: function (method, startEvent, completeEvent) {
507 var that = this
508 , complete = function () {
509 if (startEvent.type == 'show') that.reset()
510 that.transitioning = 0
511 that.$element.trigger(completeEvent)
512 }
513
514 this.$element.trigger(startEvent)
515
516 if (startEvent.isDefaultPrevented()) return
517
518 this.transitioning = 1
519
520 this.$element[method]('in')
521
522 $.support.transition && this.$element.hasClass('collapse') ?
523 this.$element.one($.support.transition.end, complete) :
524 complete()
525 }
526
527 , toggle: function () {
528 this[this.$element.hasClass('in') ? 'hide' : 'show']()
529 }
530
531 }
532
533
534 /* COLLAPSIBLE PLUGIN DEFINITION
535 * ============================== */
536
537 $.fn.collapse = function (option) {
538 return this.each(function () {
539 var $this = $(this)
540 , data = $this.data('collapse')
541 , options = typeof option == 'object' && option
542 if (!data) $this.data('collapse', (data = new Collapse(this, options)))
543 if (typeof option == 'string') data[option]()
544 })
545 }
546
547 $.fn.collapse.defaults = {
548 toggle: true
549 }
550
551 $.fn.collapse.Constructor = Collapse
552
553
554 /* COLLAPSIBLE DATA-API
555 * ==================== */
556
557 $(function () {
558 $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) {
559 var $this = $(this), href
560 , target = $this.attr('data-target')
561 || e.preventDefault()
562 || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
563 , option = $(target).data('collapse') ? 'toggle' : $this.data()
564 $(target).collapse(option)
565 })
566 })
567
568 }(window.jQuery);/* ============================================================
569 * bootstrap-dropdown.js v2.0.3
570 * http://twitter.github.com/bootstrap/javascript.html#dropdowns
571 * ============================================================
572 * Copyright 2012 Twitter, Inc.
573 *
574 * Licensed under the Apache License, Version 2.0 (the "License");
575 * you may not use this file except in compliance with the License.
576 * You may obtain a copy of the License at
577 *
578 * http://www.apache.org/licenses/LICENSE-2.0
579 *
580 * Unless required by applicable law or agreed to in writing, software
581 * distributed under the License is distributed on an "AS IS" BASIS,
582 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
583 * See the License for the specific language governing permissions and
584 * limitations under the License.
585 * ============================================================ */
586
587
588 !function ($) {
589
590 "use strict"; // jshint ;_;
591
592
593 /* DROPDOWN CLASS DEFINITION
594 * ========================= */
595
596 var toggle = '[data-toggle="dropdown"]'
597 , Dropdown = function (element) {
598 var $el = $(element).on('click.dropdown.data-api', this.toggle)
599 $('html').on('click.dropdown.data-api', function () {
600 $el.parent().removeClass('open')
601 })
602 }
603
604 Dropdown.prototype = {
605
606 constructor: Dropdown
607
608 , toggle: function (e) {
609 var $this = $(this)
610 , $parent
611 , selector
612 , isActive
613
614 if ($this.is('.disabled, :disabled')) return
615
616 selector = $this.attr('data-target')
617
618 if (!selector) {
619 selector = $this.attr('href')
620 selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
621 }
622
623 $parent = $(selector)
624 $parent.length || ($parent = $this.parent())
625
626 isActive = $parent.hasClass('open')
627
628 clearMenus()
629
630 if (!isActive) $parent.toggleClass('open')
631
632 return false
633 }
634
635 }
636
637 function clearMenus() {
638 $(toggle).parent().removeClass('open')
639 }
640
641
642 /* DROPDOWN PLUGIN DEFINITION
643 * ========================== */
644
645 $.fn.dropdown = function (option) {
646 return this.each(function () {
647 var $this = $(this)
648 , data = $this.data('dropdown')
649 if (!data) $this.data('dropdown', (data = new Dropdown(this)))
650 if (typeof option == 'string') data[option].call($this)
651 })
652 }
653
654 $.fn.dropdown.Constructor = Dropdown
655
656
657 /* APPLY TO STANDARD DROPDOWN ELEMENTS
658 * =================================== */
659
660 $(function () {
661 $('html').on('click.dropdown.data-api', clearMenus)
662 $('body')
663 .on('click.dropdown', '.dropdown form', function (e) { e.stopPropagation() })
664 .on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
665 })
666
667 }(window.jQuery);/* =========================================================
668 * bootstrap-modal.js v2.0.3
669 * http://twitter.github.com/bootstrap/javascript.html#modals
670 * =========================================================
671 * Copyright 2012 Twitter, Inc.
672 *
673 * Licensed under the Apache License, Version 2.0 (the "License");
674 * you may not use this file except in compliance with the License.
675 * You may obtain a copy of the License at
676 *
677 * http://www.apache.org/licenses/LICENSE-2.0
678 *
679 * Unless required by applicable law or agreed to in writing, software
680 * distributed under the License is distributed on an "AS IS" BASIS,
681 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
682 * See the License for the specific language governing permissions and
683 * limitations under the License.
684 * ========================================================= */
685
686
687 !function ($) {
688
689 "use strict"; // jshint ;_;
690
691
692 /* MODAL CLASS DEFINITION
693 * ====================== */
694
695 var Modal = function (content, options) {
696 this.options = options
697 this.$element = $(content)
698 .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
699 }
700
701 Modal.prototype = {
702
703 constructor: Modal
704
705 , toggle: function () {
706 return this[!this.isShown ? 'show' : 'hide']()
707 }
708
709 , show: function () {
710 var that = this
711 , e = $.Event('show')
712
713 this.$element.trigger(e)
714
715 if (this.isShown || e.isDefaultPrevented()) return
716
717 $('body').addClass('modal-open')
718
719 this.isShown = true
720
721 escape.call(this)
722 backdrop.call(this, function () {
723 var transition = $.support.transition && that.$element.hasClass('fade')
724
725 if (!that.$element.parent().length) {
726 that.$element.appendTo(document.body) //don't move modals dom position
727 }
728
729 that.$element
730 .show()
731
732 if (transition) {
733 that.$element[0].offsetWidth // force reflow
734 }
735
736 that.$element.addClass('in')
737
738 transition ?
739 that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) :
740 that.$element.trigger('shown')
741
742 })
743 }
744
745 , hide: function (e) {
746 e && e.preventDefault()
747
748 var that = this
749
750 e = $.Event('hide')
751
752 this.$element.trigger(e)
753
754 if (!this.isShown || e.isDefaultPrevented()) return
755
756 this.isShown = false
757
758 $('body').removeClass('modal-open')
759
760 escape.call(this)
761
762 this.$element.removeClass('in')
763
764 $.support.transition && this.$element.hasClass('fade') ?
765 hideWithTransition.call(this) :
766 hideModal.call(this)
767 }
768
769 }
770
771
772 /* MODAL PRIVATE METHODS
773 * ===================== */
774
775 function hideWithTransition() {
776 var that = this
777 , timeout = setTimeout(function () {
778 that.$element.off($.support.transition.end)
779 hideModal.call(that)
780 }, 500)
781
782 this.$element.one($.support.transition.end, function () {
783 clearTimeout(timeout)
784 hideModal.call(that)
785 })
786 }
787
788 function hideModal(that) {
789 this.$element
790 .hide()
791 .trigger('hidden')
792
793 backdrop.call(this)
794 }
795
796 function backdrop(callback) {
797 var that = this
798 , animate = this.$element.hasClass('fade') ? 'fade' : ''
799
800 if (this.isShown && this.options.backdrop) {
801 var doAnimate = $.support.transition && animate
802
803 this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
804 .appendTo(document.body)
805
806 if (this.options.backdrop != 'static') {
807 this.$backdrop.click($.proxy(this.hide, this))
808 }
809
810 if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
811
812 this.$backdrop.addClass('in')
813
814 doAnimate ?
815 this.$backdrop.one($.support.transition.end, callback) :
816 callback()
817
818 } else if (!this.isShown && this.$backdrop) {
819 this.$backdrop.removeClass('in')
820
821 $.support.transition && this.$element.hasClass('fade')?
822 this.$backdrop.one($.support.transition.end, $.proxy(removeBackdrop, this)) :
823 removeBackdrop.call(this)
824
825 } else if (callback) {
826 callback()
827 }
828 }
829
830 function removeBackdrop() {
831 this.$backdrop.remove()
832 this.$backdrop = null
833 }
834
835 function escape() {
836 var that = this
837 if (this.isShown && this.options.keyboard) {
838 $(document).on('keyup.dismiss.modal', function ( e ) {
839 e.which == 27 && that.hide()
840 })
841 } else if (!this.isShown) {
842 $(document).off('keyup.dismiss.modal')
843 }
844 }
845
846
847 /* MODAL PLUGIN DEFINITION
848 * ======================= */
849
850 $.fn.modal = function (option) {
851 return this.each(function () {
852 var $this = $(this)
853 , data = $this.data('modal')
854 , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
855 if (!data) $this.data('modal', (data = new Modal(this, options)))
856 if (typeof option == 'string') data[option]()
857 else if (options.show) data.show()
858 })
859 }
860
861 $.fn.modal.defaults = {
862 backdrop: true
863 , keyboard: true
864 , show: true
865 }
866
867 $.fn.modal.Constructor = Modal
868
869
870 /* MODAL DATA-API
871 * ============== */
872
873 $(function () {
874 $('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
875 var $this = $(this), href
876 , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
877 , option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data())
878
879 e.preventDefault()
880 $target.modal(option)
881 })
882 })
883
884 }(window.jQuery);/* ===========================================================
885 * bootstrap-tooltip.js v2.0.3
886 * http://twitter.github.com/bootstrap/javascript.html#tooltips
887 * Inspired by the original jQuery.tipsy by Jason Frame
888 * ===========================================================
889 * Copyright 2012 Twitter, Inc.
890 *
891 * Licensed under the Apache License, Version 2.0 (the "License");
892 * you may not use this file except in compliance with the License.
893 * You may obtain a copy of the License at
894 *
895 * http://www.apache.org/licenses/LICENSE-2.0
896 *
897 * Unless required by applicable law or agreed to in writing, software
898 * distributed under the License is distributed on an "AS IS" BASIS,
899 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
900 * See the License for the specific language governing permissions and
901 * limitations under the License.
902 * ========================================================== */
903
904
905 !function ($) {
906
907 "use strict"; // jshint ;_;
908
909
910 /* TOOLTIP PUBLIC CLASS DEFINITION
911 * =============================== */
912
913 var Tooltip = function (element, options) {
914 this.init('tooltip', element, options)
915 }
916
917 Tooltip.prototype = {
918
919 constructor: Tooltip
920
921 , init: function (type, element, options) {
922 var eventIn
923 , eventOut
924
925 this.type = type
926 this.$element = $(element)
927 this.options = this.getOptions(options)
928 this.enabled = true
929
930 if (this.options.trigger != 'manual') {
931 eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
932 eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
933 this.$element.on(eventIn, this.options.selector, $.proxy(this.enter, this))
934 this.$element.on(eventOut, this.options.selector, $.proxy(this.leave, this))
935 }
936
937 this.options.selector ?
938 (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
939 this.fixTitle()
940 }
941
942 , getOptions: function (options) {
943 options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
944
945 if (options.delay && typeof options.delay == 'number') {
946 options.delay = {
947 show: options.delay
948 , hide: options.delay
949 }
950 }
951
952 return options
953 }
954
955 , enter: function (e) {
956 var self = $(e.currentTarget)[this.type](this._options).data(this.type)
957
958 if (!self.options.delay || !self.options.delay.show) return self.show()
959
960 clearTimeout(this.timeout)
961 self.hoverState = 'in'
962 this.timeout = setTimeout(function() {
963 if (self.hoverState == 'in') self.show()
964 }, self.options.delay.show)
965 }
966
967 , leave: function (e) {
968 var self = $(e.currentTarget)[this.type](this._options).data(this.type)
969
970 if (!self.options.delay || !self.options.delay.hide) return self.hide()
971
972 clearTimeout(this.timeout)
973 self.hoverState = 'out'
974 this.timeout = setTimeout(function() {
975 if (self.hoverState == 'out') self.hide()
976 }, self.options.delay.hide)
977 }
978
979 , show: function () {
980 var $tip
981 , inside
982 , pos
983 , actualWidth
984 , actualHeight
985 , placement
986 , tp
987
988 if (this.hasContent() && this.enabled) {
989 $tip = this.tip()
990 this.setContent()
991
992 if (this.options.animation) {
993 $tip.addClass('fade')
994 }
995
996 placement = typeof this.options.placement == 'function' ?
997 this.options.placement.call(this, $tip[0], this.$element[0]) :
998 this.options.placement
999
1000 inside = /in/.test(placement)
1001
1002 $tip
1003 .remove()
1004 .css({ top: 0, left: 0, display: 'block' })
1005 .appendTo(inside ? this.$element : document.body)
1006
1007 pos = this.getPosition(inside)
1008
1009 actualWidth = $tip[0].offsetWidth
1010 actualHeight = $tip[0].offsetHeight
1011
1012 switch (inside ? placement.split(' ')[1] : placement) {
1013 case 'bottom':
1014 tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
1015 break
1016 case 'top':
1017 tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
1018 break
1019 case 'left':
1020 tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
1021 break
1022 case 'right':
1023 tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
1024 break
1025 }
1026
1027 $tip
1028 .css(tp)
1029 .addClass(placement)
1030 .addClass('in')
1031 }
1032 }
1033
1034 , isHTML: function(text) {
1035 // html string detection logic adapted from jQuery
1036 return typeof text != 'string'
1037 || ( text.charAt(0) === "<"
1038 && text.charAt( text.length - 1 ) === ">"
1039 && text.length >= 3
1040 ) || /^(?:[^<]*<[\w\W]+>[^>]*$)/.exec(text)
1041 }
1042
1043 , setContent: function () {
1044 var $tip = this.tip()
1045 , title = this.getTitle()
1046
1047 $tip.find('.tooltip-inner')[this.isHTML(title) ? 'html' : 'text'](title)
1048 $tip.removeClass('fade in top bottom left right')
1049 }
1050
1051 , hide: function () {
1052 var that = this
1053 , $tip = this.tip()
1054
1055 $tip.removeClass('in')
1056
1057 function removeWithAnimation() {
1058 var timeout = setTimeout(function () {
1059 $tip.off($.support.transition.end).remove()
1060 }, 500)
1061
1062 $tip.one($.support.transition.end, function () {
1063 clearTimeout(timeout)
1064 $tip.remove()
1065 })
1066 }
1067
1068 $.support.transition && this.$tip.hasClass('fade') ?
1069 removeWithAnimation() :
1070 $tip.remove()
1071 }
1072
1073 , fixTitle: function () {
1074 var $e = this.$element
1075 if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
1076 $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
1077 }
1078 }
1079
1080 , hasContent: function () {
1081 return this.getTitle()
1082 }
1083
1084 , getPosition: function (inside) {
1085 return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
1086 width: this.$element[0].offsetWidth
1087 , height: this.$element[0].offsetHeight
1088 })
1089 }
1090
1091 , getTitle: function () {
1092 var title
1093 , $e = this.$element
1094 , o = this.options
1095
1096 title = $e.attr('data-original-title')
1097 || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
1098
1099 return title
1100 }
1101
1102 , tip: function () {
1103 return this.$tip = this.$tip || $(this.options.template)
1104 }
1105
1106 , validate: function () {
1107 if (!this.$element[0].parentNode) {
1108 this.hide()
1109 this.$element = null
1110 this.options = null
1111 }
1112 }
1113
1114 , enable: function () {
1115 this.enabled = true
1116 }
1117
1118 , disable: function () {
1119 this.enabled = false
1120 }
1121
1122 , toggleEnabled: function () {
1123 this.enabled = !this.enabled
1124 }
1125
1126 , toggle: function () {
1127 this[this.tip().hasClass('in') ? 'hide' : 'show']()
1128 }
1129
1130 }
1131
1132
1133 /* TOOLTIP PLUGIN DEFINITION
1134 * ========================= */
1135
1136 $.fn.tooltip = function ( option ) {
1137 return this.each(function () {
1138 var $this = $(this)
1139 , data = $this.data('tooltip')
1140 , options = typeof option == 'object' && option
1141 if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
1142 if (typeof option == 'string') data[option]()
1143 })
1144 }
1145
1146 $.fn.tooltip.Constructor = Tooltip
1147
1148 $.fn.tooltip.defaults = {
1149 animation: true
1150 , placement: 'top'
1151 , selector: false
1152 , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
1153 , trigger: 'hover'
1154 , title: ''
1155 , delay: 0
1156 }
1157
1158 }(window.jQuery);/* ===========================================================
1159 * bootstrap-popover.js v2.0.3
1160 * http://twitter.github.com/bootstrap/javascript.html#popovers
1161 * ===========================================================
1162 * Copyright 2012 Twitter, Inc.
1163 *
1164 * Licensed under the Apache License, Version 2.0 (the "License");
1165 * you may not use this file except in compliance with the License.
1166 * You may obtain a copy of the License at
1167 *
1168 * http://www.apache.org/licenses/LICENSE-2.0
1169 *
1170 * Unless required by applicable law or agreed to in writing, software
1171 * distributed under the License is distributed on an "AS IS" BASIS,
1172 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1173 * See the License for the specific language governing permissions and
1174 * limitations under the License.
1175 * =========================================================== */
1176
1177
1178 !function ($) {
1179
1180 "use strict"; // jshint ;_;
1181
1182
1183 /* POPOVER PUBLIC CLASS DEFINITION
1184 * =============================== */
1185
1186 var Popover = function ( element, options ) {
1187 this.init('popover', element, options)
1188 }
1189
1190
1191 /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
1192 ========================================== */
1193
1194 Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
1195
1196 constructor: Popover
1197
1198 , setContent: function () {
1199 var $tip = this.tip()
1200 , title = this.getTitle()
1201 , content = this.getContent()
1202
1203 $tip.find('.popover-title')[this.isHTML(title) ? 'html' : 'text'](title)
1204 $tip.find('.popover-content > *')[this.isHTML(content) ? 'html' : 'text'](content)
1205
1206 $tip.removeClass('fade top bottom left right in')
1207 }
1208
1209 , hasContent: function () {
1210 return this.getTitle() || this.getContent()
1211 }
1212
1213 , getContent: function () {
1214 var content
1215 , $e = this.$element
1216 , o = this.options
1217
1218 content = $e.attr('data-content')
1219 || (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
1220
1221 return content
1222 }
1223
1224 , tip: function () {
1225 if (!this.$tip) {
1226 this.$tip = $(this.options.template)
1227 }
1228 return this.$tip
1229 }
1230
1231 })
1232
1233
1234 /* POPOVER PLUGIN DEFINITION
1235 * ======================= */
1236
1237 $.fn.popover = function (option) {
1238 return this.each(function () {
1239 var $this = $(this)
1240 , data = $this.data('popover')
1241 , options = typeof option == 'object' && option
1242 if (!data) $this.data('popover', (data = new Popover(this, options)))
1243 if (typeof option == 'string') data[option]()
1244 })
1245 }
1246
1247 $.fn.popover.Constructor = Popover
1248
1249 $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
1250 placement: 'right'
1251 , content: ''
1252 , template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'
1253 })
1254
1255 }(window.jQuery);/* =============================================================
1256 * bootstrap-scrollspy.js v2.0.3
1257 * http://twitter.github.com/bootstrap/javascript.html#scrollspy
1258 * =============================================================
1259 * Copyright 2012 Twitter, Inc.
1260 *
1261 * Licensed under the Apache License, Version 2.0 (the "License");
1262 * you may not use this file except in compliance with the License.
1263 * You may obtain a copy of the License at
1264 *
1265 * http://www.apache.org/licenses/LICENSE-2.0
1266 *
1267 * Unless required by applicable law or agreed to in writing, software
1268 * distributed under the License is distributed on an "AS IS" BASIS,
1269 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1270 * See the License for the specific language governing permissions and
1271 * limitations under the License.
1272 * ============================================================== */
1273
1274
1275 !function ($) {
1276
1277 "use strict"; // jshint ;_;
1278
1279
1280 /* SCROLLSPY CLASS DEFINITION
1281 * ========================== */
1282
1283 function ScrollSpy( element, options) {
1284 var process = $.proxy(this.process, this)
1285 , $element = $(element).is('body') ? $(window) : $(element)
1286 , href
1287 this.options = $.extend({}, $.fn.scrollspy.defaults, options)
1288 this.$scrollElement = $element.on('scroll.scroll.data-api', process)
1289 this.selector = (this.options.target
1290 || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
1291 || '') + ' .nav li > a'
1292 this.$body = $('body')
1293 this.refresh()
1294 this.process()
1295 }
1296
1297 ScrollSpy.prototype = {
1298
1299 constructor: ScrollSpy
1300
1301 , refresh: function () {
1302 var self = this
1303 , $targets
1304
1305 this.offsets = $([])
1306 this.targets = $([])
1307
1308 $targets = this.$body
1309 .find(this.selector)
1310 .map(function () {
1311 var $el = $(this)
1312 , href = $el.data('target') || $el.attr('href')
1313 , $href = /^#\w/.test(href) && $(href)
1314 return ( $href
1315 && href.length
1316 && [[ $href.position().top, href ]] ) || null
1317 })
1318 .sort(function (a, b) { return a[0] - b[0] })
1319 .each(function () {
1320 self.offsets.push(this[0])
1321 self.targets.push(this[1])
1322 })
1323 }
1324
1325 , process: function () {
1326 var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
1327 , scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
1328 , maxScroll = scrollHeight - this.$scrollElement.height()
1329 , offsets = this.offsets
1330 , targets = this.targets
1331 , activeTarget = this.activeTarget
1332 , i
1333
1334 if (scrollTop >= maxScroll) {
1335 return activeTarget != (i = targets.last()[0])
1336 && this.activate ( i )
1337 }
1338
1339 for (i = offsets.length; i--;) {
1340 activeTarget != targets[i]
1341 && scrollTop >= offsets[i]
1342 && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
1343 && this.activate( targets[i] )
1344 }
1345 }
1346
1347 , activate: function (target) {
1348 var active
1349 , selector
1350
1351 this.activeTarget = target
1352
1353 $(this.selector)
1354 .parent('.active')
1355 .removeClass('active')
1356
1357 selector = this.selector
1358 + '[data-target="' + target + '"],'
1359 + this.selector + '[href="' + target + '"]'
1360
1361 active = $(selector)
1362 .parent('li')
1363 .addClass('active')
1364
1365 if (active.parent('.dropdown-menu')) {
1366 active = active.closest('li.dropdown').addClass('active')
1367 }
1368
1369 active.trigger('activate')
1370 }
1371
1372 }
1373
1374
1375 /* SCROLLSPY PLUGIN DEFINITION
1376 * =========================== */
1377
1378 $.fn.scrollspy = function ( option ) {
1379 return this.each(function () {
1380 var $this = $(this)
1381 , data = $this.data('scrollspy')
1382 , options = typeof option == 'object' && option
1383 if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
1384 if (typeof option == 'string') data[option]()
1385 })
1386 }
1387
1388 $.fn.scrollspy.Constructor = ScrollSpy
1389
1390 $.fn.scrollspy.defaults = {
1391 offset: 10
1392 }
1393
1394
1395 /* SCROLLSPY DATA-API
1396 * ================== */
1397
1398 $(function () {
1399 $('[data-spy="scroll"]').each(function () {
1400 var $spy = $(this)
1401 $spy.scrollspy($spy.data())
1402 })
1403 })
1404
1405 }(window.jQuery);/* ========================================================
1406 * bootstrap-tab.js v2.0.3
1407 * http://twitter.github.com/bootstrap/javascript.html#tabs
1408 * ========================================================
1409 * Copyright 2012 Twitter, Inc.
1410 *
1411 * Licensed under the Apache License, Version 2.0 (the "License");
1412 * you may not use this file except in compliance with the License.
1413 * You may obtain a copy of the License at
1414 *
1415 * http://www.apache.org/licenses/LICENSE-2.0
1416 *
1417 * Unless required by applicable law or agreed to in writing, software
1418 * distributed under the License is distributed on an "AS IS" BASIS,
1419 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1420 * See the License for the specific language governing permissions and
1421 * limitations under the License.
1422 * ======================================================== */
1423
1424
1425 !function ($) {
1426
1427 "use strict"; // jshint ;_;
1428
1429
1430 /* TAB CLASS DEFINITION
1431 * ==================== */
1432
1433 var Tab = function ( element ) {
1434 this.element = $(element)
1435 }
1436
1437 Tab.prototype = {
1438
1439 constructor: Tab
1440
1441 , show: function () {
1442 var $this = this.element
1443 , $ul = $this.closest('ul:not(.dropdown-menu)')
1444 , selector = $this.attr('data-target')
1445 , previous
1446 , $target
1447 , e
1448
1449 if (!selector) {
1450 selector = $this.attr('href')
1451 selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
1452 }
1453
1454 if ( $this.parent('li').hasClass('active') ) return
1455
1456 previous = $ul.find('.active a').last()[0]
1457
1458 e = $.Event('show', {
1459 relatedTarget: previous
1460 })
1461
1462 $this.trigger(e)
1463
1464 if (e.isDefaultPrevented()) return
1465
1466 $target = $(selector)
1467
1468 this.activate($this.parent('li'), $ul)
1469 this.activate($target, $target.parent(), function () {
1470 $this.trigger({
1471 type: 'shown'
1472 , relatedTarget: previous
1473 })
1474 })
1475 }
1476
1477 , activate: function ( element, container, callback) {
1478 var $active = container.find('> .active')
1479 , transition = callback
1480 && $.support.transition
1481 && $active.hasClass('fade')
1482
1483 function next() {
1484 $active
1485 .removeClass('active')
1486 .find('> .dropdown-menu > .active')
1487 .removeClass('active')
1488
1489 element.addClass('active')
1490
1491 if (transition) {
1492 element[0].offsetWidth // reflow for transition
1493 element.addClass('in')
1494 } else {
1495 element.removeClass('fade')
1496 }
1497
1498 if ( element.parent('.dropdown-menu') ) {
1499 element.closest('li.dropdown').addClass('active')
1500 }
1501
1502 callback && callback()
1503 }
1504
1505 transition ?
1506 $active.one($.support.transition.end, next) :
1507 next()
1508
1509 $active.removeClass('in')
1510 }
1511 }
1512
1513
1514 /* TAB PLUGIN DEFINITION
1515 * ===================== */
1516
1517 $.fn.tab = function ( option ) {
1518 return this.each(function () {
1519 var $this = $(this)
1520 , data = $this.data('tab')
1521 if (!data) $this.data('tab', (data = new Tab(this)))
1522 if (typeof option == 'string') data[option]()
1523 })
1524 }
1525
1526 $.fn.tab.Constructor = Tab
1527
1528
1529 /* TAB DATA-API
1530 * ============ */
1531
1532 $(function () {
1533 $('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
1534 e.preventDefault()
1535 $(this).tab('show')
1536 })
1537 })
1538
1539 }(window.jQuery);/* =============================================================
1540 * bootstrap-typeahead.js v2.0.3
1541 * http://twitter.github.com/bootstrap/javascript.html#typeahead
1542 * =============================================================
1543 * Copyright 2012 Twitter, Inc.
1544 *
1545 * Licensed under the Apache License, Version 2.0 (the "License");
1546 * you may not use this file except in compliance with the License.
1547 * You may obtain a copy of the License at
1548 *
1549 * http://www.apache.org/licenses/LICENSE-2.0
1550 *
1551 * Unless required by applicable law or agreed to in writing, software
1552 * distributed under the License is distributed on an "AS IS" BASIS,
1553 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1554 * See the License for the specific language governing permissions and
1555 * limitations under the License.
1556 * ============================================================ */
1557
1558
1559 !function($){
1560
1561 "use strict"; // jshint ;_;
1562
1563
1564 /* TYPEAHEAD PUBLIC CLASS DEFINITION
1565 * ================================= */
1566
1567 var Typeahead = function (element, options) {
1568 this.$element = $(element)
1569 this.options = $.extend({}, $.fn.typeahead.defaults, options)
1570 this.matcher = this.options.matcher || this.matcher
1571 this.sorter = this.options.sorter || this.sorter
1572 this.highlighter = this.options.highlighter || this.highlighter
1573 this.updater = this.options.updater || this.updater
1574 this.$menu = $(this.options.menu).appendTo('body')
1575 this.source = this.options.source
1576 this.shown = false
1577 this.listen()
1578 }
1579
1580 Typeahead.prototype = {
1581
1582 constructor: Typeahead
1583
1584 , select: function () {
1585 var val = this.$menu.find('.active').attr('data-value')
1586 this.$element
1587 .val(this.updater(val))
1588 .change()
1589 return this.hide()
1590 }
1591
1592 , updater: function (item) {
1593 return item
1594 }
1595
1596 , show: function () {
1597 var pos = $.extend({}, this.$element.offset(), {
1598 height: this.$element[0].offsetHeight
1599 })
1600
1601 this.$menu.css({
1602 top: pos.top + pos.height
1603 , left: pos.left
1604 })
1605
1606 this.$menu.show()
1607 this.shown = true
1608 return this
1609 }
1610
1611 , hide: function () {
1612 this.$menu.hide()
1613 this.shown = false
1614 return this
1615 }
1616
1617 , lookup: function (event) {
1618 var that = this
1619 , items
1620 , q
1621
1622 this.query = this.$element.val()
1623
1624 if (!this.query) {
1625 return this.shown ? this.hide() : this
1626 }
1627
1628 items = $.grep(this.source, function (item) {
1629 return that.matcher(item)
1630 })
1631
1632 items = this.sorter(items)
1633
1634 if (!items.length) {
1635 return this.shown ? this.hide() : this
1636 }
1637
1638 return this.render(items.slice(0, this.options.items)).show()
1639 }
1640
1641 , matcher: function (item) {
1642 return ~item.toLowerCase().indexOf(this.query.toLowerCase())
1643 }
1644
1645 , sorter: function (items) {
1646 var beginswith = []
1647 , caseSensitive = []
1648 , caseInsensitive = []
1649 , item
1650
1651 while (item = items.shift()) {
1652 if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
1653 else if (~item.indexOf(this.query)) caseSensitive.push(item)
1654 else caseInsensitive.push(item)
1655 }
1656
1657 return beginswith.concat(caseSensitive, caseInsensitive)
1658 }
1659
1660 , highlighter: function (item) {
1661 var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
1662 return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
1663 return '<strong>' + match + '</strong>'
1664 })
1665 }
1666
1667 , render: function (items) {
1668 var that = this
1669
1670 items = $(items).map(function (i, item) {
1671 i = $(that.options.item).attr('data-value', item)
1672 i.find('a').html(that.highlighter(item))
1673 return i[0]
1674 })
1675
1676 items.first().addClass('active')
1677 this.$menu.html(items)
1678 return this
1679 }
1680
1681 , next: function (event) {
1682 var active = this.$menu.find('.active').removeClass('active')
1683 , next = active.next()
1684
1685 if (!next.length) {
1686 next = $(this.$menu.find('li')[0])
1687 }
1688
1689 next.addClass('active')
1690 }
1691
1692 , prev: function (event) {
1693 var active = this.$menu.find('.active').removeClass('active')
1694 , prev = active.prev()
1695
1696 if (!prev.length) {
1697 prev = this.$menu.find('li').last()
1698 }
1699
1700 prev.addClass('active')
1701 }
1702
1703 , listen: function () {
1704 this.$element
1705 .on('blur', $.proxy(this.blur, this))
1706 .on('keypress', $.proxy(this.keypress, this))
1707 .on('keyup', $.proxy(this.keyup, this))
1708
1709 if ($.browser.webkit || $.browser.msie) {
1710 this.$element.on('keydown', $.proxy(this.keypress, this))
1711 }
1712
1713 this.$menu
1714 .on('click', $.proxy(this.click, this))
1715 .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
1716 }
1717
1718 , keyup: function (e) {
1719 switch(e.keyCode) {
1720 case 40: // down arrow
1721 case 38: // up arrow
1722 break
1723
1724 case 9: // tab
1725 case 13: // enter
1726 if (!this.shown) return
1727 this.select()
1728 break
1729
1730 case 27: // escape
1731 if (!this.shown) return
1732 this.hide()
1733 break
1734
1735 default:
1736 this.lookup()
1737 }
1738
1739 e.stopPropagation()
1740 e.preventDefault()
1741 }
1742
1743 , keypress: function (e) {
1744 if (!this.shown) return
1745
1746 switch(e.keyCode) {
1747 case 9: // tab
1748 case 13: // enter
1749 case 27: // escape
1750 e.preventDefault()
1751 break
1752
1753 case 38: // up arrow
1754 if (e.type != 'keydown') break
1755 e.preventDefault()
1756 this.prev()
1757 break
1758
1759 case 40: // down arrow
1760 if (e.type != 'keydown') break
1761 e.preventDefault()
1762 this.next()
1763 break
1764 }
1765
1766 e.stopPropagation()
1767 }
1768
1769 , blur: function (e) {
1770 var that = this
1771 setTimeout(function () { that.hide() }, 150)
1772 }
1773
1774 , click: function (e) {
1775 e.stopPropagation()
1776 e.preventDefault()
1777 this.select()
1778 }
1779
1780 , mouseenter: function (e) {
1781 this.$menu.find('.active').removeClass('active')
1782 $(e.currentTarget).addClass('active')
1783 }
1784
1785 }
1786
1787
1788 /* TYPEAHEAD PLUGIN DEFINITION
1789 * =========================== */
1790
1791 $.fn.typeahead = function (option) {
1792 return this.each(function () {
1793 var $this = $(this)
1794 , data = $this.data('typeahead')
1795 , options = typeof option == 'object' && option
1796 if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
1797 if (typeof option == 'string') data[option]()
1798 })
1799 }
1800
1801 $.fn.typeahead.defaults = {
1802 source: []
1803 , items: 8
1804 , menu: '<ul class="typeahead dropdown-menu"></ul>'
1805 , item: '<li><a href="#"></a></li>'
1806 }
1807
1808 $.fn.typeahead.Constructor = Typeahead
1809
1810
1811 /* TYPEAHEAD DATA-API
1812 * ================== */
1813
1814 $(function () {
1815 $('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
1816 var $this = $(this)
1817 if ($this.data('typeahead')) return
1818 e.preventDefault()
1819 $this.typeahead($this.data())
1820 })
1821 })
1822
1823 }(window.jQuery);
0 /*!
1 * Bootstrap.js by @fat & @mdo
2 * Copyright 2012 Twitter, Inc.
3 * http://www.apache.org/licenses/LICENSE-2.0.txt
4 */
5 !function(a){a(function(){"use strict",a.support.transition=function(){var a=function(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",transition:"transitionend"},c;for(c in b)if(a.style[c]!==undefined)return b[c]}();return a&&{end:a}}()})}(window.jQuery),!function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function f(){e.trigger("closed").remove()}var c=a(this),d=c.attr("data-target"),e;d||(d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),e=a(d),b&&b.preventDefault(),e.length||(e=c.hasClass("alert")?c:c.parent()),e.trigger(b=a.Event("close"));if(b.isDefaultPrevented())return;e.removeClass("in"),a.support.transition&&e.hasClass("fade")?e.on(a.support.transition.end,f):f()},a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("alert");e||d.data("alert",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.alert.Constructor=c,a(function(){a("body").on("click.alert.data-api",b,c.prototype.close)})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.button.defaults,c)};b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.data(),e=c.is("input")?"val":"html";a+="Text",d.resetText||c.data("resetText",c[e]()),c[e](d[a]||this.options[a]),setTimeout(function(){a=="loadingText"?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.parent('[data-toggle="buttons-radio"]');a&&a.find(".active").removeClass("active"),this.$element.toggleClass("active")},a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("button"),f=typeof c=="object"&&c;e||d.data("button",e=new b(this,f)),c=="toggle"?e.toggle():c&&e.setState(c)})},a.fn.button.defaults={loadingText:"loading..."},a.fn.button.Constructor=b,a(function(){a("body").on("click.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle")})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=c,this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.prototype={cycle:function(b){return b||(this.paused=!1),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},to:function(b){var c=this.$element.find(".active"),d=c.parent().children(),e=d.index(c),f=this;if(b>d.length-1||b<0)return;return this.sliding?this.$element.one("slid",function(){f.to(b)}):e==b?this.pause().cycle():this.slide(b>e?"next":"prev",a(d[b]))},pause:function(a){return a||(this.paused=!0),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(b,c){var d=this.$element.find(".active"),e=c||d[b](),f=this.interval,g=b=="next"?"left":"right",h=b=="next"?"first":"last",i=this,j=a.Event("slide");this.sliding=!0,f&&this.pause(),e=e.length?e:this.$element.find(".item")[h]();if(e.hasClass("active"))return;if(a.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(j);if(j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),this.$element.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)})}else{this.$element.trigger(j);if(j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}},a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("carousel"),f=a.extend({},a.fn.carousel.defaults,typeof c=="object"&&c);e||d.data("carousel",e=new b(this,f)),typeof c=="number"?e.to(c):typeof c=="string"||(c=f.slide)?e[c]():f.interval&&e.cycle()})},a.fn.carousel.defaults={interval:5e3,pause:"hover"},a.fn.carousel.Constructor=b,a(function(){a("body").on("click.carousel.data-api","[data-slide]",function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=!e.data("modal")&&a.extend({},e.data(),c.data());e.carousel(f),b.preventDefault()})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.collapse.defaults,c),this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.prototype={constructor:b,dimension:function(){var a=this.$element.hasClass("width");return a?"width":"height"},show:function(){var b,c,d,e;if(this.transitioning)return;b=this.dimension(),c=a.camelCase(["scroll",b].join("-")),d=this.$parent&&this.$parent.find("> .accordion-group > .in");if(d&&d.length){e=d.data("collapse");if(e&&e.transitioning)return;d.collapse("hide"),e||d.data("collapse",null)}this.$element[b](0),this.transition("addClass",a.Event("show"),"shown"),this.$element[b](this.$element[0][c])},hide:function(){var b;if(this.transitioning)return;b=this.dimension(),this.reset(this.$element[b]()),this.transition("removeClass",a.Event("hide"),"hidden"),this.$element[b](0)},reset:function(a){var b=this.dimension();return this.$element.removeClass("collapse")[b](a||"auto")[0].offsetWidth,this.$element[a!==null?"addClass":"removeClass"]("collapse"),this},transition:function(b,c,d){var e=this,f=function(){c.type=="show"&&e.reset(),e.transitioning=0,e.$element.trigger(d)};this.$element.trigger(c);if(c.isDefaultPrevented())return;this.transitioning=1,this.$element[b]("in"),a.support.transition&&this.$element.hasClass("collapse")?this.$element.one(a.support.transition.end,f):f()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("collapse"),f=typeof c=="object"&&c;e||d.data("collapse",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.collapse.defaults={toggle:!0},a.fn.collapse.Constructor=b,a(function(){a("body").on("click.collapse.data-api","[data-toggle=collapse]",function(b){var c=a(this),d,e=c.attr("data-target")||b.preventDefault()||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),f=a(e).data("collapse")?"toggle":c.data();a(e).collapse(f)})})}(window.jQuery),!function(a){function d(){a(b).parent().removeClass("open")}"use strict";var b='[data-toggle="dropdown"]',c=function(b){var c=a(b).on("click.dropdown.data-api",this.toggle);a("html").on("click.dropdown.data-api",function(){c.parent().removeClass("open")})};c.prototype={constructor:c,toggle:function(b){var c=a(this),e,f,g;if(c.is(".disabled, :disabled"))return;return f=c.attr("data-target"),f||(f=c.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,"")),e=a(f),e.length||(e=c.parent()),g=e.hasClass("open"),d(),g||e.toggleClass("open"),!1}},a.fn.dropdown=function(b){return this.each(function(){var d=a(this),e=d.data("dropdown");e||d.data("dropdown",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.dropdown.Constructor=c,a(function(){a("html").on("click.dropdown.data-api",d),a("body").on("click.dropdown",".dropdown form",function(a){a.stopPropagation()}).on("click.dropdown.data-api",b,c.prototype.toggle)})}(window.jQuery),!function(a){function c(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),d.call(b)},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),d.call(b)})}function d(a){this.$element.hide().trigger("hidden"),e.call(this)}function e(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('<div class="modal-backdrop '+d+'" />').appendTo(document.body),this.options.backdrop!="static"&&this.$backdrop.click(a.proxy(this.hide,this)),e&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),e?this.$backdrop.one(a.support.transition.end,b):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,a.proxy(f,this)):f.call(this)):b&&b()}function f(){this.$backdrop.remove(),this.$backdrop=null}function g(){var b=this;this.isShown&&this.options.keyboard?a(document).on("keyup.dismiss.modal",function(a){a.which==27&&b.hide()}):this.isShown||a(document).off("keyup.dismiss.modal")}"use strict";var b=function(b,c){this.options=c,this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this))};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this,c=a.Event("show");this.$element.trigger(c);if(this.isShown||c.isDefaultPrevented())return;a("body").addClass("modal-open"),this.isShown=!0,g.call(this),e.call(this,function(){var c=a.support.transition&&b.$element.hasClass("fade");b.$element.parent().length||b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in"),c?b.$element.one(a.support.transition.end,function(){b.$element.trigger("shown")}):b.$element.trigger("shown")})},hide:function(b){b&&b.preventDefault();var e=this;b=a.Event("hide"),this.$element.trigger(b);if(!this.isShown||b.isDefaultPrevented())return;this.isShown=!1,a("body").removeClass("modal-open"),g.call(this),this.$element.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?c.call(this):d.call(this)}},a.fn.modal=function(c){return this.each(function(){var d=a(this),e=d.data("modal"),f=a.extend({},a.fn.modal.defaults,d.data(),typeof c=="object"&&c);e||d.data("modal",e=new b(this,f)),typeof c=="string"?e[c]():f.show&&e.show()})},a.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},a.fn.modal.Constructor=b,a(function(){a("body").on("click.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("modal")?"toggle":a.extend({},e.data(),c.data());b.preventDefault(),e.modal(f)})})}(window.jQuery),!function(a){"use strict";var b=function(a,b){this.init("tooltip",a,b)};b.prototype={constructor:b,init:function(b,c,d){var e,f;this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.enabled=!0,this.options.trigger!="manual"&&(e=this.options.trigger=="hover"?"mouseenter":"focus",f=this.options.trigger=="hover"?"mouseleave":"blur",this.$element.on(e,this.options.selector,a.proxy(this.enter,this)),this.$element.on(f,this.options.selector,a.proxy(this.leave,this))),this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(b){return b=a.extend({},a.fn[this.type].defaults,b,this.$element.data()),b.delay&&typeof b.delay=="number"&&(b.delay={show:b.delay,hide:b.delay}),b},enter:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);if(!c.options.delay||!c.options.delay.show)return c.show();clearTimeout(this.timeout),c.hoverState="in",this.timeout=setTimeout(function(){c.hoverState=="in"&&c.show()},c.options.delay.show)},leave:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);if(!c.options.delay||!c.options.delay.hide)return c.hide();clearTimeout(this.timeout),c.hoverState="out",this.timeout=setTimeout(function(){c.hoverState=="out"&&c.hide()},c.options.delay.hide)},show:function(){var a,b,c,d,e,f,g;if(this.hasContent()&&this.enabled){a=this.tip(),this.setContent(),this.options.animation&&a.addClass("fade"),f=typeof this.options.placement=="function"?this.options.placement.call(this,a[0],this.$element[0]):this.options.placement,b=/in/.test(f),a.remove().css({top:0,left:0,display:"block"}).appendTo(b?this.$element:document.body),c=this.getPosition(b),d=a[0].offsetWidth,e=a[0].offsetHeight;switch(b?f.split(" ")[1]:f){case"bottom":g={top:c.top+c.height,left:c.left+c.width/2-d/2};break;case"top":g={top:c.top-e,left:c.left+c.width/2-d/2};break;case"left":g={top:c.top+c.height/2-e/2,left:c.left-d};break;case"right":g={top:c.top+c.height/2-e/2,left:c.left+c.width}}a.css(g).addClass(f).addClass("in")}},isHTML:function(a){return typeof a!="string"||a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3||/^(?:[^<]*<[\w\W]+>[^>]*$)/.exec(a)},setContent:function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.isHTML(b)?"html":"text"](b),a.removeClass("fade in top bottom left right")},hide:function(){function d(){var b=setTimeout(function(){c.off(a.support.transition.end).remove()},500);c.one(a.support.transition.end,function(){clearTimeout(b),c.remove()})}var b=this,c=this.tip();c.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?d():c.remove()},fixTitle:function(){var a=this.$element;(a.attr("title")||typeof a.attr("data-original-title")!="string")&&a.attr("data-original-title",a.attr("title")||"").removeAttr("title")},hasContent:function(){return this.getTitle()},getPosition:function(b){return a.extend({},b?{top:0,left:0}:this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight})},getTitle:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||(typeof c.title=="function"?c.title.call(b[0]):c.title),a},tip:function(){return this.$tip=this.$tip||a(this.options.template)},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(){this[this.tip().hasClass("in")?"hide":"show"]()}},a.fn.tooltip=function(c){return this.each(function(){var d=a(this),e=d.data("tooltip"),f=typeof c=="object"&&c;e||d.data("tooltip",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.tooltip.Constructor=b,a.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover",title:"",delay:0}}(window.jQuery),!function(a){"use strict";var b=function(a,b){this.init("popover",a,b)};b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype,{constructor:b,setContent:function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.isHTML(b)?"html":"text"](b),a.find(".popover-content > *")[this.isHTML(c)?"html":"text"](c),a.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-content")||(typeof c.content=="function"?c.content.call(b[0]):c.content),a},tip:function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip}}),a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("popover"),f=typeof c=="object"&&c;e||d.data("popover",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.defaults=a.extend({},a.fn.tooltip.defaults,{placement:"right",content:"",template:'<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'})}(window.jQuery),!function(a){function b(b,c){var d=a.proxy(this.process,this),e=a(b).is("body")?a(window):a(b),f;this.options=a.extend({},a.fn.scrollspy.defaults,c),this.$scrollElement=e.on("scroll.scroll.data-api",d),this.selector=(this.options.target||(f=a(b).attr("href"))&&f.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=a("body"),this.refresh(),this.process()}"use strict",b.prototype={constructor:b,refresh:function(){var b=this,c;this.offsets=a([]),this.targets=a([]),c=this.$body.find(this.selector).map(function(){var b=a(this),c=b.data("target")||b.attr("href"),d=/^#\w/.test(c)&&a(c);return d&&c.length&&[[d.position().top,c]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},process:function(){var a=this.$scrollElement.scrollTop()+this.options.offset,b=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,c=b-this.$scrollElement.height(),d=this.offsets,e=this.targets,f=this.activeTarget,g;if(a>=c)return f!=(g=e.last()[0])&&this.activate(g);for(g=d.length;g--;)f!=e[g]&&a>=d[g]&&(!d[g+1]||a<=d[g+1])&&this.activate(e[g])},activate:function(b){var c,d;this.activeTarget=b,a(this.selector).parent(".active").removeClass("active"),d=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',c=a(d).parent("li").addClass("active"),c.parent(".dropdown-menu")&&(c=c.closest("li.dropdown").addClass("active")),c.trigger("activate")}},a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("scrollspy"),f=typeof c=="object"&&c;e||d.data("scrollspy",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.defaults={offset:10},a(function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),!function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype={constructor:b,show:function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target"),e,f,g;d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,""));if(b.parent("li").hasClass("active"))return;e=c.find(".active a").last()[0],g=a.Event("show",{relatedTarget:e}),b.trigger(g);if(g.isDefaultPrevented())return;f=a(d),this.activate(b.parent("li"),c),this.activate(f,f.parent(),function(){b.trigger({type:"shown",relatedTarget:e})})},activate:function(b,c,d){function g(){e.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),f?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var e=c.find("> .active"),f=d&&a.support.transition&&e.hasClass("fade");f?e.one(a.support.transition.end,g):g(),e.removeClass("in")}},a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("tab");e||d.data("tab",e=new b(this)),typeof c=="string"&&e[c]()})},a.fn.tab.Constructor=b,a(function(){a("body").on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.typeahead.defaults,c),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.$menu=a(this.options.menu).appendTo("body"),this.source=this.options.source,this.shown=!1,this.listen()};b.prototype={constructor:b,select:function(){var a=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(a)).change(),this.hide()},updater:function(a){return a},show:function(){var b=a.extend({},this.$element.offset(),{height:this.$element[0].offsetHeight});return this.$menu.css({top:b.top+b.height,left:b.left}),this.$menu.show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(b){var c=this,d,e;return this.query=this.$element.val(),this.query?(d=a.grep(this.source,function(a){return c.matcher(a)}),d=this.sorter(d),d.length?this.render(d.slice(0,this.options.items)).show():this.shown?this.hide():this):this.shown?this.hide():this},matcher:function(a){return~a.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(a){var b=[],c=[],d=[],e;while(e=a.shift())e.toLowerCase().indexOf(this.query.toLowerCase())?~e.indexOf(this.query)?c.push(e):d.push(e):b.push(e);return b.concat(c,d)},highlighter:function(a){var b=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");return a.replace(new RegExp("("+b+")","ig"),function(a,b){return"<strong>"+b+"</strong>"})},render:function(b){var c=this;return b=a(b).map(function(b,d){return b=a(c.options.item).attr("data-value",d),b.find("a").html(c.highlighter(d)),b[0]}),b.first().addClass("active"),this.$menu.html(b),this},next:function(b){var c=this.$menu.find(".active").removeClass("active"),d=c.next();d.length||(d=a(this.$menu.find("li")[0])),d.addClass("active")},prev:function(a){var b=this.$menu.find(".active").removeClass("active"),c=b.prev();c.length||(c=this.$menu.find("li").last()),c.addClass("active")},listen:function(){this.$element.on("blur",a.proxy(this.blur,this)).on("keypress",a.proxy(this.keypress,this)).on("keyup",a.proxy(this.keyup,this)),(a.browser.webkit||a.browser.msie)&&this.$element.on("keydown",a.proxy(this.keypress,this)),this.$menu.on("click",a.proxy(this.click,this)).on("mouseenter","li",a.proxy(this.mouseenter,this))},keyup:function(a){switch(a.keyCode){case 40:case 38:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}a.stopPropagation(),a.preventDefault()},keypress:function(a){if(!this.shown)return;switch(a.keyCode){case 9:case 13:case 27:a.preventDefault();break;case 38:if(a.type!="keydown")break;a.preventDefault(),this.prev();break;case 40:if(a.type!="keydown")break;a.preventDefault(),this.next()}a.stopPropagation()},blur:function(a){var b=this;setTimeout(function(){b.hide()},150)},click:function(a){a.stopPropagation(),a.preventDefault(),this.select()},mouseenter:function(b){this.$menu.find(".active").removeClass("active"),a(b.currentTarget).addClass("active")}},a.fn.typeahead=function(c){return this.each(function(){var d=a(this),e=d.data("typeahead"),f=typeof c=="object"&&c;e||d.data("typeahead",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>'},a.fn.typeahead.Constructor=b,a(function(){a("body").on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(b){var c=a(this);if(c.data("typeahead"))return;b.preventDefault(),c.typeahead(c.data())})})}(window.jQuery);
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2012-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2012-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
3737 var $CHPauseDownload;
3838 var $CHPausePostProcess;
3939 var $CHPauseScan;
40 var $StatusPausing;
41 var $StatusPaused;
42 var $StatusLeft;
4340 var $StatusSpeed;
4441 var $StatusSpeedIcon;
4542 var $StatusTimeIcon;
4643 var $StatusTime;
47 var $StatusURLs;
4844 var $PlayBlock;
4945 var $PlayButton;
5046 var $PauseButton;
5147 var $PlayAnimation;
52 var $StatDialog;
5348 var $ScheduledPauseDialog;
5449 var $PauseForInput;
5550 var $PauseForPreview;
7873 $PlayButton = $('#PlayButton');
7974 $PauseButton = $('#PauseButton');
8075 $PlayAnimation = $('#PlayAnimation');
81 $StatusPausing = $('#StatusPausing');
82 $StatusPaused = $('#StatusPaused');
83 $StatusLeft = $('#StatusLeft');
8476 $StatusSpeed = $('#StatusSpeed');
8577 $StatusSpeedIcon = $('#StatusSpeedIcon');
8678 $StatusTimeIcon = $('#StatusTimeIcon');
8779 $StatusTime = $('#StatusTime');
88 $StatusURLs = $('#StatusURLs');
89 $ScheduledPauseDialog = $('#ScheduledPauseDialog')
80 $ScheduledPauseDialog = $('#ScheduledPauseDialog');
9081 $PauseForInput = $('#PauseForInput');
9182 $PauseForPreview = $('#PauseForPreview');
9283
366357 var parsedTime = parsable.match(/(\d+):?(\d+)?/) || [];
367358 var primaryValue = parsedTime[1];
368359 var secondaryValue = parsedTime[2];
369 var is12H = (indicator === 'am' || indicator === 'pm')
360 var is12H = (indicator === 'am' || indicator === 'pm');
370361
371362 if (indicator === undefined && secondaryValue === undefined)
372363 {
531522 var $StatDialog_ArticleCache;
532523 var $StatDialog_QueueScripts;
533524 var $StatDialog_ChartBlock;
534 var $StatDialog_Server;
535525 var $StatRangeDialog;
536526 var $StatRangeDialog_PeriodInput;
537527 var $StatDialog_Tooltip;
580570 $StatDialog_ArticleCache = $('#StatDialog_ArticleCache');
581571 $StatDialog_QueueScripts = $('#StatDialog_QueueScripts');
582572 $StatDialog_ChartBlock = $('#StatDialog_ChartBlock');
583 $StatDialog_Server = $('#StatDialog_Server');
584573 $StatRangeDialog = $('#StatRangeDialog');
585574 $StatRangeDialog_PeriodInput = $('#StatRangeDialog_PeriodInput');
586575 $StatDialog_Tooltip = $('#StatDialog_Tooltip');
941930 var monDataLo = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
942931
943932 // aggregate daily volumes into months
944 var len = servervolumes[serverNo].BytesPerDays.length;
945933 var daySlot = servervolumes[serverNo].DaySlot;
946934 var slotDelta = servervolumes[0].FirstDay - servervolumes[serverNo].FirstDay;
947 var startDate = new Date(monStartDate.getTime());
948935 var day = 0;
949936 for (var i = monStartIndex; i <= monEndIndex; i++, day++)
950937 {
12081195 monthListInitialized = true;
12091196
12101197 var firstDay = servervolumes[0].FirstDay;
1211 var lastDay = firstDay + servervolumes[0].BytesPerDays.length - 1;
12121198 var curDay = firstDay + servervolumes[0].DaySlot;
12131199 var firstDt = dayToDate(firstDay);
1214 var lastDt = dayToDate(lastDay);
12151200 var curDt = dayToDate(curDay);
12161201
12171202 var menu = $('#StatDialog_MonthMenu');
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2012-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2012-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
193193
194194 function addFiles(selectedFiles)
195195 {
196 var list = '';
197196 for (var i = 0; i<selectedFiles.length; i++)
198197 {
199198 var file = selectedFiles[i];
222221
223222 this.renameClick = function(no)
224223 {
225 var file = files[no];
226224 var info = infos[no];
227225 AddParamDialog.showModal(info, function() {
228226 $('#AddDialog_File' + no).html(Util.formatNZBName(info.name + info.ext));
00 /*
11 * This file is part of nzbget. See <http://nzbget.net>.
22 *
3 * Copyright (C) 2012-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
3 * Copyright (C) 2012-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
5656
5757 this.formatTimeLeft = function(sec)
5858 {
59 var hms = '';
6059 var days = Math.floor(sec / 86400);
6160 var hours = Math.floor((sec % 86400) / 3600);
6261 var minutes = Math.floor((sec / 60) % 60);
341340 return text.substring(text.length - substr.length, text.length) === substr;
342341 }
343342
343 this.makeId = function(text)
344 {
345 return text.replace(/ |\/|\\|\.|\$|\:|\*/g, '_');
346 }
347
344348 }(jQuery));
345349
346350
378382 var dialog = this;
379383 var body = $('.modal-body', dialog);
380384 var footer = $('.modal-footer', dialog);
381 var header = $('.modal-header', dialog);
382385 dialog.css({margin: '', left: '', top: '', bottom: '', right: '', width: '', height: ''});
383386 body.css({position: '', height: '', left: '', right: '', top: '', bottom: '', 'max-height': ''});
384387 footer.css({position: '', left: '', right: '', bottom: ''});
399402 var header = $('.modal-header', dialog);
400403
401404 var oldBodyHeight = body.height();
402 var oldWinHeight = dialog.height();
403405 var windowWidth = $(window).width();
404406 var windowHeight = $(window).height();
405407 var oldTabWidth = fromTab.width();
529531 'use strict';
530532
531533 // Properties
532 this.rpcUrl;
533 this.defaultFailureCallback;
534 this.rpcUrl = '';
535 this.defaultFailureCallback = undefined;
534536 this.connectErrorMessage = 'Cannot establish connection';
535537 this.safeMethods = [];
536538 this.etags = {};