├── .gitmodules ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── changelog.txt ├── css ├── common.css ├── nabnotify.css ├── newznab.css ├── nzbmatrix.css ├── popup.css └── settings.css ├── images ├── addon_icon.svg ├── content_icon.png ├── content_icon_error.png ├── content_icon_fetching.png ├── content_icon_success.png ├── control_cancel.png ├── control_pause.png ├── control_play.png ├── sab_128.png └── sab_48.png ├── manifest.json ├── popup.html ├── project └── sabconnect++.pnproj ├── scripts ├── content │ ├── animenzb.js │ ├── animezb.js │ ├── binsearch.js │ ├── bintube.js │ ├── common.js │ ├── dognzb.js │ ├── fanzub.js │ ├── newznab.js │ ├── nzbclub.js │ ├── nzbindex.js │ ├── nzbrss.js │ ├── omgwtfnzbs.js │ ├── usenet4ever.js │ └── yubse.js ├── convert.js ├── pages │ ├── background.js │ ├── common.js │ ├── context_menu.js │ ├── localization.js │ ├── manifest.js │ ├── newznab-autoadd.js │ ├── newznab-check.js │ ├── popup.js │ └── settings.js ├── profile.js └── utility.js ├── settings.html └── third_party ├── jqplot ├── jqplot.barRenderer.js ├── jqplot.pointLabels.js ├── jquery.jqplot.css └── jquery.jqplot.js ├── jquery ├── jquery-1.12.4.min.js ├── jquery-ui-1.8.20.sortable.min.js ├── jquery.notify.js └── jquery.urldecoder.min.js └── webtoolkit └── webtoolkit.base64.js /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/fancy-settings"] 2 | path = third_party/fancy-settings 3 | url = git://github.com/jsterken/fancy-settings.git 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Sadly, there is no more 'main developer' working on this Chrome extension. 2 | Past developers who worked on most of the code have now stopped working on it. 3 | 4 | If you are interested in taking charge of this extension, let us know by opening a new issue. 5 | 6 | Otherwise, keep the Pull requests coming; I (Guillaume Boudreau) will try to merge them and release new versions that include the changes on the Chrome Web Store ASAP. 7 | 8 | Cheers. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | **Important note:** 3 | Nobody is maintaining this codebase right now. Anyone who needs a fix will need to submit a Pull Request with the required changes. 4 | 5 | --- 6 | 7 | SABconnect++ adds one-click 'Send to SABnzbd' buttons to many popular NZB index sites. 8 | 9 | You also get a taskbar button that allows you to keep an eye on your SABnzbd: current downloads, pause (individual downloads, or pause all), or remove individual queued downloads. 10 | 11 | Install SABconnect++ at our [Chrome Web Store page](https://chrome.google.com/webstore/detail/okphadhbbjadcifjplhifajfacbkkbod). 12 | 13 | Features: 14 | 15 | * One-click NZB downloads for the following sites: 16 | * binsearch.info (binsearch.net) 17 | * bintube.com 18 | * dognzb.com 19 | * fanzub.com 20 | * nzbclub.com 21 | * nzbindex.com (nzbindex.nl) 22 | * omgwtfnzbs.me 23 | * yubse.com 24 | * animezb.com 25 | * animenzb.com 26 | * Any Newznab-based indexer 27 | * Context menu option for sending links to SABnzbd 28 | * Options page that looks consistent with Chrome's own options layout 29 | * Download speed graph 30 | * Pause individual downloads 31 | * Pause all downloads 32 | * Remove individual downloads 33 | * Desktop notifications (Download Complete/Failed) 34 | * Storage sync for settings 35 | 36 | SABconnect++ is a fork of the now unmaintained Chrome extension [SABconnect](http://code.google.com/p/sabconnect/). 37 | -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | Version 0.6.37 2 | -------------- 3 | Fix for latest Chrome version that removed chrome.extension (thanks jeremybergen) 4 | 5 | Version 0.6.36 6 | -------------- 7 | omgwtfnzbs TLD has changed from omgwtfnzbs.me back to omgwtfnzbs.org 8 | 9 | Version 0.6.35 10 | -------------- 11 | Fixed downloads on Dognzb (thanks jeremybergen) 12 | 13 | Version 0.6.34 14 | -------------- 15 | Fixed Generic newznab (drunkenslug) category detection (thanks jdart) 16 | 17 | Version 0.6.33 18 | -------------- 19 | Enable context menu on https pages (thanks sakana280) 20 | 21 | Version 0.6.32 22 | -------------- 23 | Fix style for omgwtfnzbs.me #190 (thanks omgstaff) 24 | 25 | Version 0.6.31 26 | -------------- 27 | Fix nzbindex (thanks gissehel) 28 | 29 | Version 0.6.30 30 | -------------- 31 | Fix jqPlot (thanks flo333) 32 | Replace 'KBps' with '%' for max speed 33 | 34 | Version 0.6.29 35 | -------------- 36 | Fix for autoscrolling webpages (thanks kbuffington) 37 | 38 | Version 0.6.28 39 | -------------- 40 | DrunkenSlug.com: fixed how the query string is found in the page; fixed how the href is found in list view (thanks jdart) 41 | 42 | Version 0.6.27 43 | -------------- 44 | omgwtfnzbs.me: Cleaned up Category Discovery (thanks omgstaff) 45 | 46 | Version 0.6.26 47 | -------------- 48 | omgwtfnzbs.me: Updated getUserName and getApiKey (thanks omgstaff) 49 | 50 | Version 0.6.25 51 | -------------- 52 | Fixed omgwtfnzbs.me: username changed in HTML (thanks omgstaff) 53 | 54 | Version 0.6.24 55 | -------------- 56 | Fixed omgwtfnzbs.me: nzbId length has increased on site from 5 to 6 (thanks omgstaff) 57 | 58 | Version 0.6.23 59 | -------------- 60 | Fixed omgwtfnzbs.me: send Category now on browse.php/details.php/trends.php pages; support for Cart; fixed README/label (thanks omgstaff) 61 | 62 | Version 0.6.22 63 | -------------- 64 | Added category discovery for details.php and trends.php on their website so that it sends to sabnzb with the proper category. Also the TLD has changed from omgwtfnzbs.org to omgwtfnzbs.me (thanks gewfie) 65 | 66 | Version 0.6.20 67 | -------------- 68 | (again) Fixed nzbclub integration (thanks Mathias Rohnstock) 69 | 70 | Version 0.6.19 71 | -------------- 72 | Fixed nzbclub integration (thanks Mathias Rohnstock) 73 | 74 | Version 0.6.18 75 | -------------- 76 | Corrected support for animenzb.com - Incorrectly named provider 77 | Removed exec bit from usenet4ever provider 78 | 79 | Version 0.6.17 80 | -------------- 81 | Added support for animenzb.com (credit PPSlim) 82 | 83 | Version 0.6.16 84 | -------------- 85 | Fixed omgwtfnzbs.org support 86 | 87 | Version 0.6.15 88 | -------------- 89 | Added support for usenet4ever.info (thanks Marat Bedoev) 90 | Replaced (deprecated) webkit notifications with Chrome notifications (thanks Marat Bedoev) 91 | Option to disable Newznab auto detection (thanks Omri Iluz) 92 | 93 | Version 0.6.14 94 | -------------- 95 | Added support for animezb.com (thanks Benni-chan) 96 | 97 | Version 0.6.13 98 | -------------- 99 | Fix icon size on dognzbd. 100 | 101 | Version 0.6.11 102 | -------------- 103 | More bugfixes for dognzbd. Fixes #96, #102. 104 | 105 | Version 0.6.10 106 | -------------- 107 | Bugfix for dognzbd. Fixes #106. 108 | 109 | Version 0.6.9 110 | ------------- 111 | Change notification mechanism to use a single timeout handler, rather than one per notification. Fixes #99. 112 | Bugfix for some Newznab themes: show the notification over other UI elements 113 | Added setting to not send any categories to SAB, and let it try to guess it from the .nzb content instead. Default to off for now (i.e. same behaviour as before) 114 | 115 | Version 0.6.8 116 | ------------- 117 | Improved nzbs.org support (and all Newznab at the same time) 118 | Removed support for nzbx.co (read here to know why it shut down: http://www.dslreports.com/forum/r28443268-nzbx.co-shuts-down) 119 | Removed support for nzbsrus.com (read here to know why it shut down: http://sickbeard.com/forums/viewtopic.php?f=3&t=7596) 120 | Added support for nzb-rss.com (thanks brandon-barker) 121 | 122 | Version 0.6.7 123 | ------------- 124 | Improved Newznab support (tested on nzb.su): one-click links in details page, fixed cart, fixed double links 125 | Added support for omgwtfnzbs.org: added categories (thanks brandon-barker) 126 | 127 | Version 0.6.6 128 | ------------- 129 | Added support for omgwtfnzbs.org (thanks brandon-barker) 130 | 131 | Version 0.6.5 132 | ------------- 133 | Added support for nzbsrus.com categories 134 | Removed nzb.su from the list of providers; it's now a Newznab indexer 135 | 136 | Version 0.6.4 137 | ------------- 138 | ... 139 | 140 | Version 0.6.3 141 | ------------- 142 | Added support for mega.nzbx.co 143 | Improvement: Added multi-select to nzbx.co 144 | Bugfix: nzbx.co no category failure fixed 145 | 146 | Version 0.6.2 147 | ------------- 148 | Re-added: dognzb.cr 149 | 150 | Version 0.6.1 151 | ------------- 152 | Re-added: nzb.su 153 | 154 | Version 0.6.0 155 | ------------- 156 | New feature: Settings are now synced to and from your Google account, if you enabled sync in Chrome. 157 | Improvement: One-click support for binsearch.info 158 | Improvement: Added category for nzbx.co one-click 159 | Removed: nzb.su, mysterbin.com support (sites are gone) 160 | 161 | Version 0.5.22 162 | -------------- 163 | Improvement: Better looking icons, in particular in the action bar 164 | Improvement: Ability to pause for X minutes (thanks jsterken) 165 | Improvement: Code improvement on how domain and protocol are handled, when sending a link to SAB. Solves part of the NZBClub problem. (thanks jsterken) 166 | Bugfix: SAB icon would not change to the red error icons, when an error occurred when sending a link to SAB. (thanks jsterken) 167 | Bugfix: Newznab providers: when a cover has multiple releases, send the correct URL to SAB (was always sending the 1st release URL) (thanks jsterken) 168 | 169 | Version 0.5.21 170 | -------------- 171 | Bugfix: Newznab sites: select all checkbox would break the 'Send to SAB' top button 172 | Improved fanzub support (thanks Benni-chan) 173 | Added support for nzbx.co (thanks sharkuw) 174 | 175 | Version 0.5.20 176 | -------------- 177 | Shorter notifications timeout available (1, 2, 3, 5 seconds) 178 | Bugfix: right-click "Send link to SAB" never worked. Now it should work, at least on sites that don't require a login to download NZBs. 179 | Bugfix: support for arbitrary Newznab providers setting wasn't saved correctly. 180 | 181 | Version 0.5.19 182 | -------------- 183 | Added support for arbitrary Newznab providers. 184 | Newznab (nzbs.org, etc.): Added 1-click button in Cover view 185 | Removed NZBMatrix & Newzbin settings (sites are gone). 186 | 187 | Version 0.5.18 188 | -------------- 189 | Better nzb.su implementation (thanks Benni-chan) 190 | Removed NZBMatrix & Newzbin support (sites are gone) 191 | 192 | Version 0.5.17 193 | -------------- 194 | Bugfix: popup was broken when an item was paused. (thanks eegeeZA) 195 | Bugfix: Fixed download button on dognzb.cr (thanks VTWoods) 196 | 197 | Version 0.5.16 198 | -------------- 199 | Bugfix: Remove unneeded labels on graph 200 | 201 | Version 0.5.15 202 | -------------- 203 | More NZBRus fixes 204 | Fix for missing notification icon 205 | 206 | Version 0.5.14 207 | -------------- 208 | Added ability to override categories from popup menu (thanks mattb3) 209 | Fix for non-working NZBRus (thanks rolfwessels) 210 | 211 | Version 0.5.13 212 | -------------- 213 | Added support for mysterbin.com (thanks mattb3) 214 | Improvement: Somehow improved the graph. Doesn't show when empty, etc. 215 | Task: updated Chrome extension manifest to latest version (2). Previous version (1) will be deprecated in Chrome soon, so this was needed to keep the extension working. 216 | 217 | Version 0.5.12 218 | -------------- 219 | Added support for yubse.com (thanks tofito) 220 | Added support for Newzxxx2.ch 221 | Bugfix: Don't send default category for NZBMatrix downloads 222 | 223 | Version 0.5.11 224 | -------------- 225 | Bugfix: missing queued items and graph in popup 226 | 227 | Version 0.5.10 228 | -------------- 229 | Bugfix: Missing profiles names from select box in popup 230 | 231 | Version 0.5.9 232 | ------------- 233 | Added support for dognzb.cr (thanks to commits from wolrah & gboudreau) 234 | Added support for www.nzbhq.com 235 | Bugfix: Now working with the new nzbs.org (thanks to commits from wolrah, jdart & gboudreau) 236 | Bugfix: Always use https for binsearch.info URLs. 237 | 238 | Version 0.5.8 239 | ------------- 240 | Change [Issue 124]: Added newzbin2.es and removed old newzbin.com 241 | 242 | Version 0.5.7 243 | ------------- 244 | Change [Issue 37]: 1-Click on individual posts on newzbin, not just search list (Thanks fabian) 245 | Bugfix [Issue 107]: nzbxxx.com 1-click was not working due to incorrect API URL usage. 246 | 247 | Version 0.5.6 248 | ------------- 249 | Feature: [Issue 85]: Save multiple SABnzbd connections with quick change option from popup. 250 | Bugfix: [Issue 87]: nzbsrus.com changed their HTML in the browse & search pages. 251 | 252 | Version 0.5.5 253 | ------------- 254 | Feature: [Issue 51]: Max Speed for SABnzbd can now be set in the popup. You can press the ENTER key in the input field to accept the changes or click the "Set" button. 255 | Bugfix: [Issue 81]: nzb.su anime series page doesn't have 1-click. 256 | 257 | Version 0.5.4 258 | ------------- 259 | Change: The "nice names" for binsearch.info have been simplified. Thanks to "thibaut....@gmail.com" for this. 260 | Change: The Categories section of the Configuration tab in Options now has a summary paragraph with a link to the Categories wiki page on our Google Code project page. 261 | Bugfix: (Regression) [Issue 77] "Open SABnzbd" link in the popup was not working for https connections. 262 | 263 | Version 0.5.3 264 | ------------- 265 | Feature: New option to enable automatic authentication. This feature automatically provides your SABnzbd username & password to SABnzbd through URL parameters so you don't have to type in your username & password manually when you click the "Open SABnzbd" button in the popup. Enabled by default. 266 | Change: If options are changed in the extension, tabs currently open for indexer sites automatically pick up those changes. Previously the website had to be refreshed by the user to pick up changes in options (such as nzbxxx username & api key). 267 | 268 | Version 0.5.2 269 | ------------- 270 | Bugfix: X-DNZB-Category was not working on compatible sites. 271 | Bugfix: NZBmatrix was not working in iframes (Issue 60) 272 | 273 | Version 0.5.1 274 | ------------- 275 | Change: Fresh new icon (the smaller 16x16 icon remains the same) 276 | Change: 'Notification Timeout' setting now defaults to 10 seconds (previously defaulted to Disabled). 277 | Bugfix: (Regression) 'Enable Context Menu' option was not working. 278 | Bugfix: (Regression) binsearch.info wasn't working. 279 | Bugfix: Renamed the display options check boxes in settings to mean the reverse of what they meant before (i.e. Keeping them checked actually enables nice display names). 280 | Bugfix: (Regression) 'Pause Queue' popup option was missing. 281 | 282 | Version 0.5.0 283 | ------------- 284 | Feature: "Send link to SABnzbd" link added to chrome context menu for http links. Note that this feature is not intended to be used on links that will require authentication. 285 | Feature: New setting in options page to enable/disable the new context menu. 286 | Feature: Added support for fanzub 287 | Feature: Completely redesigned Options page 288 | Change: Removed newzleech support since that website is down. 289 | Change: Extreme architecture change that fixes many bugs and improves stability. 290 | Bugfix: Fixed continuous memory leaks happening during refreshes. 291 | Bugfix: SabConnect++ no longer crashes after a period of time. 292 | Bugfix: Several script errors eliminated and stability improved. 293 | 294 | Version 0.4.12 295 | -------------- 296 | New feature: Added 1-click NZB download for fanzub.com 297 | Improvement: Allow 1, 2 or 4 hours refresh rates, or disabling background refresh altogether 298 | Bugfix: fixing 1-click for nzbclub.com 299 | Bugfix: Missing 1-click downloads on binsearch.info Watchlist 300 | Bugfix: AJAX search results on nzbsrus.com didn't show the 1-click SAB icons 301 | 302 | Version 0.4.11 303 | -------------- 304 | Bugfix: Now working in Chrome 12 (dev) 305 | 306 | Version 0.4.10 307 | -------------- 308 | New feature: Added 1-click NZB download for nzb.su (Newznab). 309 | Note: SABconnect++ won't send a category when using cover view. This is because those views don't display the categories. 310 | 311 | Version 0.4.9 312 | ------------- 313 | Improvement: Timeout for notifications (to disappear) is now configurable. Default is to keep them open indefinitely. 314 | Improvement: Allow you to use the X-DNZB-Category HTTP header on sites that supports it (nzbs.org, Newzbin), instead of using the default auto-categorization. The X-DNZB-Category header will only contain the main category (eg. 'TV'), versus auto-categorization that will contain the full category name (eg. TV.x264) http://code.google.com/p/sabconnectplusplus/issues/detail?id=18 315 | Improvement: Added individual 1-click NZB download buttons to nzbindex.com http://code.google.com/p/sabconnectplusplus/issues/detail?id=20 316 | Improvement: Added option to use the NZB file name, instead of the full name displayed on the website, for nzbindex.com and binsearch.info http://code.google.com/p/sabconnectplusplus/issues/detail?id=20 317 | Bugfix: Show 'Download Failed' notifications instead of 'Download Complete' when appropriate. 318 | 319 | Version 0.4.8 320 | ------------- 321 | New feature: "Download Complete" desktop notifications 322 | New feature: "Hard-coded category" & "Default category" options, to override NZB index site categories, or send a default when no category is found on the NZB index site. 323 | Bugfix: nzbs.org auto-categorization didn't work on homepage (Browse All NZBs) 324 | Bugfix: NZBXXX.com 1-click NZB download was not working; added Username & API Key options in the extension options to fix. http://code.google.com/p/sabconnectplusplus/issues/detail?id=12 325 | Bugfix: Some search results & subsequent results pages wouldn't show the 1-click NZB download buttons. http://code.google.com/p/sabconnectplusplus/issues/detail?id=11 326 | 327 | Version 0.4.7 328 | ------------- 329 | Improvement: Auto-categorization for nzbs.org. Categories are named "Console.XBox360", "TV.x264", etc. and need to match a category NAME you have defined in SABnzbd. See http://code.google.com/p/sabconnectplusplus/wiki/Categories#nzbs.org for details. 330 | 331 | Version 0.4.6 332 | ------------- 333 | Bugfix: 0.4.5 broke 1-click NZB download buttons on nzbs.org, in list view 334 | Bugfix: 1-click NZB download button on nzbs.org details page looked weird after click 335 | 336 | Version 0.4.5 337 | ------------- 338 | Bugfix: bandwidth graph wasn't working; thanks to Sajid Anwar for the fix. 339 | New feature: Added 1-click NZB download for nzbindex.nl 340 | Improvement: Added 'Send selected to SABnzbd' buttons on nzbs.org, to send multiple NZBs to SAB in one click. 341 | Improvement: Preload graph image in the background to remove flickering. 342 | Improvement: Added manual refresh menu item. 343 | Improvement: Allow user to change refresh rate. 344 | Improvement: Number of developers working on SABconnect++ increased! Welcome Sajid Anwar! 345 | 346 | Version 0.4.4 347 | ------------- 348 | New feature: Added 1-click NZB download for NZBsRus.com 349 | 350 | Version 0.4.3 351 | ------------- 352 | Bugfix: newzbin.com "Send to SABnzbd" button was broken. 353 | New feature: Added 1-click NZB download for newzleech.com; thanks to johannespfau for the patch. 354 | New feature: Added 1-click NZB download to nzbs.org details page. 355 | Improvement: nzbs.org: Made it clearer that the SAB 1-click icon and the download link are distinct. 356 | Improvement: Removed badge text (# queued items) instead of displaying '0'. 357 | 358 | Version 0.4.2 359 | ------------- 360 | New feature: double-click the toolbar icon to open SABnzbd. 361 | Note: Chrome doesn't support this natively. 362 | The extension tries to detect if you tried to open the popup twice fast enough, and will open SABnzbd if it detects you did. 363 | You might want to continue clicking on the toolbar icon until a new tab opens, if just double-clicking doesn't work for you. 364 | Bugfix (Issue #1): one-click NZB button was missing from binsearch.net (same as binsearch.info); thanks evertbeurskens for the patch. 365 | Bugfix: "Enable Graph" option couldn't be disabled. 366 | 367 | Version 0.4.1 368 | ------------- 369 | New feature: Added nzbindex.com 1-click NZB downloads 370 | Auto-categorization: 1st group that appears in "Group" column on nzbindex.com will be sent as the Category. Make sure to create categories that have names equal to the groups you usually download from (eg. a.b.sounds.mp3.french). Sorry, but there's no way I can use the SABnzbd 'group' field using the API... :\ 371 | Bugfix: SABnzbd icons (used to send a NZB to SABnzbd) changed to 'fetching' (green arrows), but never changed to 'OK' (green logo). Cosmetic bugfix; the actual send operation worked fine. 372 | 373 | Version 0.4 374 | ----------- 375 | New feature: Added nzbs.org 1-click NZB downloads (code comes from SABConnect, slightly modified) http://code.google.com/p/sabconnect/issues/detail?id=4 376 | New feature: Added binsearch.info 1-click NZB downloads. http://code.google.com/p/sabconnect/issues/detail?id=8 377 | Auto-categorization: 1st group that appears in "Group" column on binsearch.info will be sent as the Category. Make sure to create categories that have names equal to the groups you usually download from (eg. a.b.sounds.mp3.french). Sorry, but there's no way I can use the SABnzbd 'group' field using the API... :\ 378 | New feature: Thanks to SABnzbd 0.5.3, auto-categorization for NZBMatrix.com! :) 379 | Documentation here: http://wiki.sabnzbd.org/configure-categories 380 | Example SABnzbd categories you need to create: http://cl.ly/eec6e7308ee01f800b3d 381 | Improvement: Re-enabled the option to show a graph in popup. 382 | Bugfix: Fixed 1-click for all services in latest (dev) Chrome; it wasn't sending to SABnzbd anymore; was just downloading the NZB locally instead. 383 | Bugfix: Options page that wasn't working at all in latest (dev) Chrome. 384 | Bugfix: Options page checkboxes: trying to uncheck any service would 'appear' to not work: checkbox would still be checked when reloading the options page. 385 | Bugfix: Disabling a service in the options page didn't disable 1-click NZB downloads! 386 | Bugfix: Connection to SABnzbd now works even if the user chose to "Disable API-key" in SABnzbd settings. 387 | Bugfix: Popup always showed 'Resume queue' even if the queue wasn't paused. 388 | Bugfix: Status (at the top of popup) would stay red (paused color) if you paused then resumed. 389 | Bugfix: NZBMatrix.com download URLs had the hostname twice in them! http://code.google.com/p/sabconnect/issues/detail?id=9 390 | -------------------------------------------------------------------------------- /css/common.css: -------------------------------------------------------------------------------- 1 | .serversPriorities ul, 2 | .serversPriorities ul li 3 | { 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | .serversPriorities ul { 9 | padding: 0 5px; 10 | } 11 | 12 | .serversPriorities ul li { 13 | list-style-type: none; 14 | } -------------------------------------------------------------------------------- /css/nabnotify.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | .autonabSticky p { float:left; padding:0px; margin:0px; margin-left:10px; line-height:45px; color:#fff; font-size:12px;} 4 | .autonabSticky p a { color:#fbffa2; } 5 | .autonabSticky a.close { float:right; margin:13px 10px 0px 0px; color:#fff; font-size: 14px;} 6 | .autonabSticky { 7 | position: fixed; 8 | top: 0; 9 | left: 0; 10 | z-index: 999999999; 11 | width: 100%; 12 | border-bottom: 3px solid #fff !important; 13 | height: 45px; 14 | background-color: #91BD09; 15 | box-shadow: 1px 1px 7px #676767; 16 | } 17 | -------------------------------------------------------------------------------- /css/newznab.css: -------------------------------------------------------------------------------- 1 | 2 | #browsetable a.addSABnzbd { 3 | margin-right: 3px; position: relative; top: 3px; 4 | } 5 | 6 | #infohead div.icon { 7 | float: none; 8 | display: inline; 9 | top: 4px; 10 | } -------------------------------------------------------------------------------- /css/nzbmatrix.css: -------------------------------------------------------------------------------- 1 | span.downloadButton { 2 | display: inline-block; 3 | width: 171px; 4 | height: 26px; 5 | text-decoration: none; 6 | color: black; 7 | position: relative; 8 | top: -15px; 9 | } 10 | 11 | span.downloadButton .text { 12 | position: relative; 13 | top: 5px; 14 | } 15 | 16 | span.downloadButton img { 17 | position: relative; 18 | top: 5px; 19 | } 20 | 21 | a:.downloadButton:hover { 22 | text-decoration: none; 23 | } 24 | -------------------------------------------------------------------------------- /css/popup.css: -------------------------------------------------------------------------------- 1 | body { 2 | overflow: hidden; 3 | margin: 0; 4 | padding: 4px; 5 | font-family: tahoma, sans-serif; 6 | width: 230px; 7 | } 8 | 9 | .menu div { 10 | background-color: white; 11 | color: black; 12 | font-size: 11px; 13 | padding: 4px 14px; 14 | white-space: nowrap; 15 | } 16 | 17 | .menu div:hover { 18 | background-color: #316AC5; 19 | color: white; 20 | cursor: pointer; 21 | } 22 | 23 | .menu div span { 24 | float: right; 25 | } 26 | 27 | hr { 28 | margin: 2px 0; 29 | } 30 | 31 | #sabInfo { 32 | color: black; 33 | font-size: 11px; 34 | padding: 10px; 35 | } 36 | 37 | #sabInfo .label { 38 | font-weight: bold; 39 | width:140px; 40 | display:inline-block; 41 | } 42 | 43 | .status { 44 | padding: 4px; 45 | text-transform: uppercase; 46 | } 47 | 48 | .Idle { 49 | background-color: yellow; 50 | } 51 | 52 | .Paused { 53 | background-color: red; 54 | } 55 | .Paused ~ #sab-timeleft { 56 | font-weight: bold; 57 | } 58 | 59 | .Downloading { 60 | background-color: lime; 61 | } 62 | 63 | #sab-status { 64 | padding: 1px 3px; 65 | } 66 | 67 | #sab-speed { 68 | font-weight: bold; 69 | } 70 | 71 | .speed { 72 | float: right; 73 | } 74 | 75 | 76 | .file-Downloading { 77 | color: green; 78 | } 79 | 80 | .file-Paused { 81 | color: #ccc; 82 | } 83 | 84 | 85 | .float-fix { 86 | clear:both; 87 | } 88 | 89 | 90 | .progressBarContainer { 91 | width: 198px; 92 | height: 2px; 93 | background-color: #333; 94 | border: 2px solid #333; 95 | } 96 | 97 | .progressBarInner { 98 | height: 100%; 99 | background-color: lime; 100 | } 101 | 102 | 103 | 104 | ul#sab-queue { 105 | margin: 0; 106 | padding: 0; 107 | list-style: none; 108 | } 109 | 110 | ul#sab-queue li.item { 111 | margin: 5px 0; 112 | padding: 3px; 113 | border: 1px solid #fff; 114 | } 115 | 116 | .controls { 117 | float: right; 118 | } 119 | 120 | div.filename { 121 | float: left; 122 | width:160px; 123 | overflow: hidden; 124 | } 125 | 126 | .lowOpacity { 127 | opacity: 0.2; 128 | } 129 | 130 | .filename { 131 | cursor: move; 132 | } 133 | 134 | 135 | 136 | .col-left, .col-right { 137 | width:49%; 138 | float: left; 139 | line-height: 20px; 140 | } 141 | 142 | .col-right { 143 | text-align: right; 144 | } 145 | 146 | .col-container { 147 | overflow: auto; 148 | } 149 | 150 | li.highlight { 151 | background-color: #eee; 152 | border: 1px solid #ccc !important; 153 | } 154 | 155 | #graph { 156 | height: 75px; 157 | width: 220px; 158 | } 159 | 160 | #errors-container { 161 | background-color: #ffd3d3; 162 | padding: 2px 4px; 163 | border: 1px solid #ff8383; 164 | margin: 4px 0 20px 0; 165 | } 166 | 167 | #speed-input 168 | { 169 | width: 50px; 170 | } -------------------------------------------------------------------------------- /css/settings.css: -------------------------------------------------------------------------------- 1 | #connection-status 2 | { 3 | display: inline; 4 | margin-left: 30px; 5 | padding: 5px 25px; 6 | } 7 | 8 | .connection-status-success 9 | { 10 | background-color: #41AD37; 11 | } 12 | 13 | .connection-status-failure 14 | { 15 | background-color: #FF0000; 16 | } 17 | 18 | .setting.element.button 19 | { 20 | display: inline; 21 | } -------------------------------------------------------------------------------- /images/addon_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /images/content_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboudreau/sabconnectplusplus/04b2e0c3b68de74d780ac2569066eedee36d74ad/images/content_icon.png -------------------------------------------------------------------------------- /images/content_icon_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboudreau/sabconnectplusplus/04b2e0c3b68de74d780ac2569066eedee36d74ad/images/content_icon_error.png -------------------------------------------------------------------------------- /images/content_icon_fetching.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboudreau/sabconnectplusplus/04b2e0c3b68de74d780ac2569066eedee36d74ad/images/content_icon_fetching.png -------------------------------------------------------------------------------- /images/content_icon_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboudreau/sabconnectplusplus/04b2e0c3b68de74d780ac2569066eedee36d74ad/images/content_icon_success.png -------------------------------------------------------------------------------- /images/control_cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboudreau/sabconnectplusplus/04b2e0c3b68de74d780ac2569066eedee36d74ad/images/control_cancel.png -------------------------------------------------------------------------------- /images/control_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboudreau/sabconnectplusplus/04b2e0c3b68de74d780ac2569066eedee36d74ad/images/control_pause.png -------------------------------------------------------------------------------- /images/control_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboudreau/sabconnectplusplus/04b2e0c3b68de74d780ac2569066eedee36d74ad/images/control_play.png -------------------------------------------------------------------------------- /images/sab_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboudreau/sabconnectplusplus/04b2e0c3b68de74d780ac2569066eedee36d74ad/images/sab_128.png -------------------------------------------------------------------------------- /images/sab_48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboudreau/sabconnectplusplus/04b2e0c3b68de74d780ac2569066eedee36d74ad/images/sab_48.png -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "SABconnect++", 4 | "version": "0.6.38", 5 | "description": "SABnzbd extension for Google Chrome.", 6 | "minimum_chrome_version": "88.0", 7 | "background": { 8 | "scripts": [ 9 | "third_party/jquery/jquery-1.12.4.min.js", 10 | "third_party/jquery/jquery.urldecoder.min.js", 11 | "third_party/fancy-settings/lib/store.js", 12 | "scripts/convert.js", 13 | "scripts/utility.js", 14 | "scripts/profile.js", 15 | "scripts/pages/common.js", 16 | "scripts/pages/background.js", 17 | "scripts/pages/context_menu.js", 18 | "scripts/pages/newznab-check.js" 19 | ] 20 | }, 21 | "options_page": "settings.html", 22 | "browser_action": { 23 | "default_icon": "images/content_icon.png", 24 | "default_title": "SABconnect++", 25 | "default_popup": "popup.html" 26 | }, 27 | "icons": { 28 | "16": "images/content_icon.png", 29 | "48": "images/sab_48.png", 30 | "128": "images/sab_128.png" 31 | }, 32 | "web_accessible_resources": [ 33 | "images/content_icon.png", 34 | "images/content_icon_error.png", 35 | "images/content_icon_fetching.png", 36 | "images/content_icon_success.png", 37 | "css/common.css" 38 | ], 39 | "permissions": 40 | [ 41 | "*://*/*", 42 | "tabs", 43 | "notifications", 44 | "contextMenus", 45 | "storage" 46 | ], 47 | "content_scripts": [ 48 | { 49 | "matches": [ 50 | "*://*.nzbclub.com/*", 51 | "*://*.bintube.com/*", 52 | "*://*.binsearch.info/*", 53 | "*://*.binsearch.net/*", 54 | "*://*.binsearch.co.uk/*", 55 | "*://*.binsear.ch/*", 56 | "*://*.nzbindex.com/*", 57 | "*://*.nzbindex.nl/*", 58 | "*://*.fanzub.com/*", 59 | "*://*.animezb.com/*", 60 | "*://animenzb.com/*", 61 | "*://*.animenzb.com/*", 62 | "*://*.dognzb.cr/*", 63 | "*://*.yubse.com/*", 64 | "*://*.omgwtfnzbs.org/*", 65 | "*://*.nzb-rss.com/*", 66 | "*://*.usenet4ever.info/*" 67 | ], 68 | "js": [ 69 | "third_party/jquery/jquery-1.12.4.min.js", 70 | "scripts/content/common.js", 71 | "third_party/webtoolkit/webtoolkit.base64.js" 72 | ], 73 | "all_frames": true 74 | }, 75 | 76 | { 77 | "matches": [ "*://*.nzbclub.com/*" ], 78 | "js": [ "scripts/content/nzbclub.js" ], 79 | "all_frames": true 80 | }, 81 | { 82 | "matches": [ "*://*.bintube.com/*" ], 83 | "js": [ "scripts/content/bintube.js" ], 84 | "all_frames": true 85 | }, 86 | { 87 | "matches": [ 88 | "*://*.binsearch.info/*", 89 | "*://*.binsearch.net/*", 90 | "*://*.binsearch.co.uk/*", 91 | "*://*.binsear.ch/*" 92 | ], 93 | "js": [ "scripts/content/binsearch.js" ], 94 | "all_frames": true 95 | }, 96 | { 97 | "matches" : [ 98 | "*://*.nzbindex.com/*", 99 | "*://*.nzbindex.nl/*" 100 | ], 101 | "js": [ "scripts/content/nzbindex.js" ], 102 | "all_frames": true 103 | }, 104 | { 105 | "matches": [ "*://*.fanzub.com/*" ], 106 | "js": [ "scripts/content/fanzub.js" ], 107 | "all_frames": true 108 | }, 109 | { 110 | "matches": [ "*://*.animezb.com/*" ], 111 | "js": [ "scripts/content/animezb.js" ], 112 | "all_frames": true 113 | }, 114 | { 115 | "matches": [ 116 | "*://animenzb.com/*", 117 | "*://*.animenzb.com/*" 118 | ], 119 | "js": [ "scripts/content/animenzb.js" ], 120 | "all_frames": true 121 | }, 122 | { 123 | "matches": [ "*://*.dognzb.cr/*" ], 124 | "js": [ "scripts/content/dognzb.js" ], 125 | "all_frames": true 126 | }, 127 | { 128 | "matches": [ "*://*.yubse.com/*" ], 129 | "js": [ "scripts/content/yubse.js" ], 130 | "all_frames": true 131 | }, 132 | { 133 | "matches": [ "*://omgwtfnzbs.org/*" ], 134 | "js": [ "scripts/content/omgwtfnzbs.js" ], 135 | "all_frames": true 136 | }, 137 | { 138 | "matches": [ "*://*.nzb-rss.com/*" ], 139 | "js": [ "scripts/content/nzbrss.js" ], 140 | "all_frames": true 141 | }, 142 | { 143 | "all_frames": true, 144 | "js": [ "scripts/content/usenet4ever.js" ], 145 | "matches": [ "*://*.usenet4ever.info/*" ] 146 | } 147 | 148 | ] 149 | } 150 | -------------------------------------------------------------------------------- /popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 26 | 27 | 28 | 29 | 30 |
31 |
32 | 33 |
34 | Active Profile: 35 |
36 | 37 |
38 |
39 |
40 |
41 | 42 |
43 |
44 | 45 |
46 |
47 | 48 |
49 | 50 | 51 |
52 | 53 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /project/sabconnect++.pnproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /scripts/content/animenzb.js: -------------------------------------------------------------------------------- 1 | function addToSABnzbdFromAnimenzb() { 2 | var addLink = this; 3 | 4 | // Set the image to an in-progress image 5 | var img = chrome.runtime.getURL('images/content_icon_fetching.png'); 6 | var nzburl = this.href; 7 | 8 | var category = null; 9 | try { 10 | category = this.parent().parent().find('td > a > img').first().attr('alt').toLowerCase(); 11 | } catch (ex) { } 12 | $(this).find('img').first().attr('src', img); 13 | addToSABnzbd(addLink, nzburl, "addurl", null, category); 14 | 15 | return false; 16 | } 17 | 18 | 19 | function handleAllDownloadLinks() { 20 | // Find all Table Rows - $('td.file > a[type="application/x-nzb"]').parent().parent() 21 | // Find all Download links - $('td.file > a[type="application/x-nzb"]') 22 | $('td.file > a[type="application/x-nzb"]').each(function() { 23 | var img = chrome.runtime.getURL('/images/content_icon.png'); 24 | var href = $(this).attr('href'); 25 | var link = ' '; 26 | $(this).before(link); 27 | $(this).parent().find('a[class="addSABnzbd"]').first().click(addToSABnzbdFromAnimenzb); 28 | }); 29 | } 30 | 31 | Initialize( 'animenzb', null, function() { 32 | handleAllDownloadLinks(); 33 | }); 34 | -------------------------------------------------------------------------------- /scripts/content/animezb.js: -------------------------------------------------------------------------------- 1 | function addToSABnzbdFromAnimezb() { 2 | var addLink = this; 3 | 4 | // Set the image to an in-progress image 5 | var img = chrome.runtime.getURL('images/content_icon_fetching.png'); 6 | var nzburl = this.href; 7 | 8 | var category = null; 9 | try { 10 | category = $(this).parent().parent().find('span[class*="label"]')[0].innerHTML.toLowerCase(); 11 | } catch (ex) { } 12 | addToSABnzbd(addLink, nzburl, "addurl", null, category); 13 | 14 | return false; 15 | } 16 | 17 | function addMultiToSABnzbdFromAnimezb() { 18 | //if ($('form input[type=checkbox][checked]').size() == 0) 19 | if ($('table[id="search-results"] tr[class*="highlighted"][class*="results-top-tr"]').size() == 0) 20 | { 21 | alert('Please select at least one file'); 22 | return false; 23 | } else { 24 | $('table[id="search-results"] tr[class*="highlighted"][class*="results-top-tr"]').each(function() { 25 | $(this).find('a[class="addSABnzbd"]').first().click(); 26 | $(this).click(); 27 | }); 28 | } 29 | } 30 | 31 | function handleAllDownloadLinks() { 32 | $('table[id="search-results"] td:nth-child(2) a').each(function() { 33 | var img = chrome.runtime.getURL('/images/content_icon.png'); 34 | var href = $(this).attr('href'); 35 | var link = '  '; 36 | $(this).before(link); 37 | $(this).parent().find('a[class="addSABnzbd"]').first().click(addToSABnzbdFromAnimezb); 38 | }); 39 | } 40 | 41 | function addDownloadAllButton() { 42 | $('div[class=row] form button[class*="btn-sm"]').each(function() { 43 | var img = chrome.runtime.getURL('/images/content_icon.png'); 44 | var href = $(this).attr('href'); 45 | var link = ''; 46 | $(this).after(link); 47 | $(this).parent().find('input[id="addMultiSABnzbd"]').first().click(addMultiToSABnzbdFromAnimezb); 48 | }); 49 | } 50 | 51 | Initialize( 'animezb', null, function() { 52 | handleAllDownloadLinks(); 53 | addDownloadAllButton(); 54 | }); 55 | -------------------------------------------------------------------------------- /scripts/content/binsearch.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | var useNiceName; 3 | 4 | function addToSABnzbdFromBinsearch() { 5 | //grab all checked boxes on page 6 | $(".xMenuT input:checked[type=checkbox]").each(function() { 7 | addOne($(this).siblings("a.addSABnzbd")); 8 | }); 9 | 10 | return false; 11 | } 12 | 13 | function oneClick() { 14 | addOne(this); 15 | return false; 16 | } 17 | 18 | function addOne(addLink) { 19 | var $addLink = $(addLink); 20 | // Set the image to an in-progress image 21 | var img = chrome.runtime.getURL('images/content_icon_fetching.png'); 22 | if ($addLink.find('img').length > 0) { 23 | $addLink.find('img').attr("src", img); 24 | } else { 25 | $addLink.css('background-image', 'url('+img+')'); 26 | } 27 | 28 | var tr = $addLink.parents("tr").first(); 29 | $tds = $(tr).children("td"); 30 | var categories = $tds[4].innerText; 31 | if (categories.indexOf('\n') != -1) { 32 | var category = categories.substr(0, categories.indexOf('\n')); 33 | } else { 34 | var category = categories; 35 | } 36 | if( useNiceName ) { 37 | var nice_name = document.getElementsByName("q")[0].value; 38 | if (!nice_name.length) { 39 | nice_name = $tds[2].getElementsByTagName('span')[0].innerText; 40 | } 41 | } 42 | addToSABnzbd($addLink, '/?action=nzb&' + $("input", $tds[1]).attr("name") + '=1', "addurl", nice_name, category); 43 | } 44 | 45 | function handleAllDownloadLinks() { 46 | var img = chrome.runtime.getURL('/images/content_icon.png'); 47 | 48 | $(".xMenuT input[type=checkbox]").each(function() { 49 | var href = '/?action=nzb&' + $(this).attr("name") + '=1'; 50 | var link = $(''); 51 | $(this).before(link); 52 | link.click(oneClick); 53 | }); 54 | 55 | $('input[name$="watchlist"]').each(function() { 56 | // add button to h3 to move checked in to SABConnect 57 | var link = $(''); 58 | link.css({ 59 | "background-image": "url("+img+")", 60 | "background-repeat": "no-repeat", 61 | "background-position": "3px 1px", 62 | "padding-left": 25, 63 | "cursor": "pointer" 64 | }); 65 | $(this).after(link); 66 | $(this).siblings('input[class="b addSABnzbd"]').click(addToSABnzbdFromBinsearch); 67 | }); 68 | } 69 | 70 | function RefreshSettings() 71 | { 72 | GetSetting( 'use_name_binsearch', function( state ) { 73 | useNiceName = state; 74 | }); 75 | } 76 | 77 | Initialize( 'binsearch', RefreshSettings, function() { 78 | handleAllDownloadLinks(); 79 | }); 80 | })(jQuery); -------------------------------------------------------------------------------- /scripts/content/bintube.js: -------------------------------------------------------------------------------- 1 | function addToSABnzbdFromBintube() { 2 | // Set the image to an in-progress image 3 | var img = chrome.runtime.getURL('images/content_icon_fetching.png'); 4 | $(this).find('img').attr("src", img); 5 | 6 | var nzburl = $(this).attr('href'); 7 | var addLink = this; 8 | 9 | addToSABnzbd(addLink, nzburl, "addurl"); 10 | 11 | return false; 12 | } 13 | 14 | function handleAllDownloadLinks() { 15 | $('a.dlbtn').each(function() { 16 | var href = $(this).attr('href'); 17 | var img = chrome.runtime.getURL('/images/content_icon.png'); 18 | var link = ' '; 19 | $(this).before(link); 20 | $(this).remove(); 21 | }); 22 | 23 | // Change the on click handler to send to sabnzbd 24 | // moved because the way it was the click was firing multiple times 25 | $('.addSABnzbd').each(function() { 26 | $(this).click(addToSABnzbdFromBintube); 27 | }); 28 | 29 | return; 30 | } 31 | 32 | Initialize( 'bintube', null, function() { 33 | handleAllDownloadLinks(); 34 | }); -------------------------------------------------------------------------------- /scripts/content/common.js: -------------------------------------------------------------------------------- 1 | var ignoreCats; 2 | 3 | function onResponseAdd( response, addLink ) 4 | { 5 | switch( response.ret ) { 6 | case 'error' : 7 | alert("Could not contact SABnzbd \n Check it is running and your settings are correct"); 8 | var img = chrome.runtime.getURL('images/content_icon_error.png'); 9 | if ($(addLink).find('img').length > 0) { 10 | $(addLink).find('img').attr("src", img); 11 | } else { 12 | // Prevent updating the background image of Bootstrap buttons 13 | if ($(addLink).hasClass('btn') == false) { 14 | $(addLink).css('background-image', 'url('+img+')'); 15 | } 16 | } 17 | break; 18 | case 'success': 19 | // If there was an error of some type, report it to the user and abort! 20 | if (response.data.error) { 21 | alert(response.data.error); 22 | var img = chrome.runtime.getURL('images/content_icon_error.png'); 23 | if ($(addLink).find('img').length > 0) { 24 | $(addLink).find('img').attr("src", img); 25 | } else { 26 | // Prevent updating the background image of Bootstrap buttons 27 | if ($(addLink).hasClass('btn') == false) { 28 | $(addLink).css('background-image', 'url('+img+')'); 29 | } 30 | } 31 | return; 32 | } 33 | var img = chrome.runtime.getURL('images/content_icon_success.png'); 34 | if ($(addLink).find('img').length > 0) { 35 | $(addLink).find('img').attr("src", img); 36 | } else if (addLink.nodeName && addLink.nodeName.toUpperCase() == 'INPUT' && addLink.value == 'Sent to SABnzbd!') { 37 | // Nothing; handled in nzbsorg.js 38 | } else { 39 | // Prevent updating the background image of Bootstrap buttons 40 | if ($(addLink).hasClass('btn') == false) { 41 | $(addLink).css('background-image', 'url('+img+')'); 42 | } 43 | } 44 | break; 45 | default: 46 | alert("SABconnect: Oops! Something went wrong. Try again."); 47 | } 48 | } 49 | 50 | function addToSABnzbd(addLink, nzburl, mode, nice_name, category) { 51 | 52 | if(nzburl.substring(0, 1) == "/") { 53 | var locHost = window.location.host; 54 | if (locHost == "dognzb.cr") { 55 | locHost = "dl.dognzb.cr"; 56 | } 57 | nzburl = window.location.protocol + "//" + locHost + nzburl; 58 | } 59 | 60 | var request = { 61 | action: 'addToSABnzbd', 62 | nzburl: nzburl, 63 | mode: mode 64 | }; 65 | 66 | if (typeof nice_name != 'undefined' && nice_name != null) { 67 | request.nzbname = nice_name; 68 | } 69 | 70 | GetSetting('config_ignore_categories', function( value ) { 71 | ignoreCats = value; 72 | }); 73 | if (!ignoreCats && typeof category != 'undefined' && category != null) { 74 | request.category = category; 75 | } 76 | 77 | console.log("Sending to SABnzbd:"); 78 | console.log(request); 79 | 80 | chrome.runtime.sendMessage( 81 | request, 82 | function(response) { onResponseAdd( response, addLink ) } 83 | ); 84 | } 85 | 86 | function GetSetting( setting, callback ) 87 | { 88 | var request = { 89 | action: 'get_setting', 90 | setting: setting 91 | } 92 | 93 | chrome.runtime.sendMessage( request, function( response ) { 94 | var value = response.value; 95 | 96 | if( typeof value == 'undefined' || value == null ) { 97 | throw 'GetSetting(): ' + setting + ' could not be found.'; 98 | } 99 | else { 100 | callback( value ); 101 | } 102 | }); 103 | } 104 | 105 | var refresh_func = null; 106 | 107 | function CallRefreshFunction() 108 | { 109 | if( refresh_func ) { 110 | refresh_func(); 111 | } 112 | } 113 | 114 | function Initialize( provider, refresh_function, callback ) 115 | { 116 | var request = { 117 | action: 'initialize', 118 | provider: provider 119 | } 120 | 121 | chrome.runtime.sendMessage( request, function( response ) { 122 | if( response.enabled ) { 123 | callback(); 124 | } 125 | else { 126 | console.info( 'SABconnect: 1-click functionality for this site is disabled' ); 127 | } 128 | }); 129 | 130 | refresh_func = refresh_function 131 | CallRefreshFunction(); 132 | } 133 | 134 | function OnRequest( request, sender, onResponse ) 135 | { 136 | switch( request.action ) { 137 | case 'refresh_settings': 138 | CallRefreshFunction(); 139 | break; 140 | } 141 | }; 142 | 143 | chrome.runtime.onMessage.addListener( OnRequest ); 144 | -------------------------------------------------------------------------------- /scripts/content/dognzb.js: -------------------------------------------------------------------------------- 1 | var nzburl; 2 | var addLink; 3 | var category = null; 4 | 5 | function findNZBId(elem) { 6 | nzbid = $(elem).attr('id'); 7 | url = '/fetch/' + nzbid; 8 | return url; 9 | } 10 | 11 | function addToSABnzbdFromDognzb() { 12 | var rss_hash = $('input[name="rsstoken"]').val(); 13 | if (this.nodeName.toUpperCase() == 'INPUT') { 14 | this.value = "Sending..."; 15 | $(this).css('color', 'green'); 16 | 17 | $('table.data input:checked').each(function() { 18 | var tr = $(this).parent().parent(); 19 | var a = tr.find('a[title="Send to SABnzbd"]'); 20 | 21 | // Find the nzb id from the href 22 | nzburl = findNZBId(a); 23 | if (nzburl) { 24 | category = tr.find('span[class~="labelstyle-444444"]').text(); 25 | 26 | addLink = a; 27 | 28 | // Add the authentication to the link about to be fetched 29 | nzburl += '/' + rss_hash; 30 | 31 | addToSABnzbd(addLink, nzburl, "addurl", null, category); 32 | } 33 | }); 34 | this.value = 'Sent to SAB!'; 35 | $(this).css('color', 'red'); 36 | sendToSabButton = this; 37 | 38 | setTimeout(function(){ sendToSabButton.value = 'Send to SAB'; $(sendToSabButton).css('color', '#888'); }, 4000); 39 | 40 | return false; 41 | } else { 42 | // Find the newzbin id from the href 43 | nzburl = findNZBId(this); 44 | if (nzburl) { 45 | // Set the image to an in-progress image 46 | var img = chrome.runtime.getURL('images/content_icon_fetching.png'); 47 | $(this).css('background-image', 'url('+img+')'); 48 | 49 | var tr = $(this).parent().parent(); 50 | category = tr.find('span[class~="labelstyle-444444"]').text(); 51 | 52 | addLink = this; 53 | 54 | 55 | // Add the authentication to the link about to be fetched 56 | nzburl += '/' + rss_hash; 57 | 58 | addToSABnzbd(addLink, nzburl, "addurl", null, category); 59 | 60 | return false; 61 | } 62 | } 63 | } 64 | 65 | function handleAllDownloadLinks() { 66 | //XXX: Needs to be fixed with new site layout 67 | // List view: add a button above the list to send selected NZBs to SAB 68 | //$('input[class="nzb_multi_operations_sab"]').each(function() { 69 | // $(this).css('display', 'inline-block'); 70 | // $(this).click(addToSABnzbdFromDognzb); 71 | //}); 72 | 73 | $('div[class="dog-icon-download"]').not('.sabcpp-fake-godnzb-marker').each(function() { 74 | // Change the title to "Send to SABnzbd" 75 | newlink = $('
').attr("title", "Send to SABnzbd"); 76 | newlink.addClass('dog-icon-download').addClass('sabcpp-fake-dognzb-marker'); 77 | 78 | // Change the nzb download image 79 | var img = chrome.runtime.getURL('images/content_icon.png'); 80 | newlink.css('background-image', 'url('+img+')'); 81 | newlink.css('background-position', '0 0'); 82 | newlink.css('background-size', 'inherit'); 83 | 84 | // Extract NZB id from onClick and set to ID attribute 85 | 86 | var nzbid = $(this).attr('onClick'); 87 | var nzbid = nzbid.split('\'')[1]; 88 | newlink.attr("id", nzbid); 89 | 90 | // Change the on click handler to send to sabnzbd 91 | // this is the 92 | //$(this).removeAttr("onClick"); 93 | newlink.click(addToSABnzbdFromDognzb); 94 | $(this).replaceWith(newlink); 95 | }); 96 | } 97 | 98 | Initialize( 'dognzb', null, function() { 99 | handleAllDownloadLinks(); 100 | sabcppDogCheck = function(){ 101 | if($('div[class="dog-icon-download"]').not('.sabcpp-fake-godnzb-marker').length >= 1) { 102 | handleAllDownloadLinks(); 103 | } 104 | setTimeout(sabcppDogCheck, 1000); 105 | }; 106 | setTimeout(sabcppDogCheck, 1000); 107 | }); 108 | -------------------------------------------------------------------------------- /scripts/content/fanzub.js: -------------------------------------------------------------------------------- 1 | function addToSABnzbdFromFanzub() { 2 | var addLink = this; 3 | 4 | // Set the image to an in-progress image 5 | var img = chrome.runtime.getURL('images/content_icon_fetching.png'); 6 | var nzburl = this.href; 7 | 8 | var category = null; 9 | try { 10 | category = $(this).parent().parent().find('img[src*="/images/cat/"]')[0].alt.toLowerCase(); 11 | } catch (ex) { } 12 | addToSABnzbd(addLink, nzburl, "addurl", null, category); 13 | 14 | return false; 15 | } 16 | 17 | function addMultiToSABnzbdFromFanzub() { 18 | if ($('table.fanzub input[type=checkbox][checked]').size() == 0) 19 | { 20 | alert('Please select at least one file'); 21 | return false; 22 | } else { 23 | $('table.fanzub input[type=checkbox][checked]').each(function() { 24 | $(this).parent().parent().find('a[class="addSABnzbd"]').first().click(); 25 | $(this).parent().parent().click(); 26 | }); 27 | } 28 | } 29 | 30 | function handleAllDownloadLinks() { 31 | $('table[class="fanzub"] td[class="file"] a').each(function() { 32 | var img = chrome.runtime.getURL('/images/content_icon.png'); 33 | var href = $(this).attr('href'); 34 | var link = '  '; 35 | $(this).before(link); 36 | $(this).parent().find('a[class="addSABnzbd"]').first().click(addToSABnzbdFromFanzub); 37 | }); 38 | } 39 | 40 | function addDownloadAllButton() { 41 | $('table[class="nav"] td button').each(function() { 42 | var img = chrome.runtime.getURL('/images/content_icon.png'); 43 | var href = $(this).attr('href'); 44 | var link = ''; 45 | $(this).after(link); 46 | $(this).parent().find('input[id="addMultiSABnzbd"]').first().click(addMultiToSABnzbdFromFanzub); 47 | }); 48 | } 49 | 50 | Initialize( 'fanzub', null, function() { 51 | handleAllDownloadLinks(); 52 | addDownloadAllButton(); 53 | }); -------------------------------------------------------------------------------- /scripts/content/newznab.js: -------------------------------------------------------------------------------- 1 | 2 | (function() { // Encapsulate 3 | 4 | var queryString = '?i=' + $('[name=UID]').val() + '&r=' + $('[name=RSSTOKEN]').val() + '&del=1', 5 | oneClickImgTag = '', 6 | ignoreCats, 7 | linkRelAlternate = $('link[rel=alternate]').attr('href'); 8 | 9 | if (linkRelAlternate) { 10 | var found = linkRelAlternate.match(/([\?&]i=.+$)/); 11 | if (found) { 12 | queryString = '?' + found[0]; 13 | } 14 | } 15 | 16 | GetSetting('config_ignore_categories', function( value ) { 17 | ignoreCats = value; 18 | }); 19 | 20 | function addMany(e) { 21 | 22 | var $button = $(this); 23 | 24 | $button.val("Sending..."); 25 | 26 | $('#browsetable ' + e.data.selector).each(function() { 27 | addOne($(this).closest('tr')); 28 | }); 29 | 30 | $button.val('Sent to SABnzbd!'); 31 | 32 | setTimeout(function() { 33 | $button.val('Send to SABnzbd'); 34 | }, 4000); 35 | 36 | return false; 37 | } 38 | 39 | // $tr is a table row from #browsetable (one nzb) 40 | // http://nzbs.org/getnzb/abef39dde2baaad865adecb95e5eb26d 41 | function addOne($tr) { 42 | 43 | var $anchor = $tr.find('a.addSABnzbd'); 44 | 45 | // Set the image to an in-progress image 46 | var img = chrome.runtime.getURL('images/content_icon_fetching.png'); 47 | 48 | if ($($anchor.get(0)).find('img').length > 0) { 49 | $($anchor.get(0)).find('img').attr("src", img); 50 | } 51 | 52 | var category = null; 53 | if (!ignoreCats) { 54 | $tr.parent().find('tr:nth-child(1)').find('th').each(function(i) { 55 | if (!category && $.trim($(this).text().toLowerCase()) === 'category') { 56 | category = $.trim($tr.find(`td:nth-child(${i + 1}) a`).text().match(/^\s*([^> -]+)/)[1]); 57 | } 58 | }); 59 | } 60 | 61 | addToSABnzbd( 62 | $anchor.get(0), 63 | $anchor.attr('href') + queryString, 64 | 'addurl', 65 | null, 66 | category 67 | ); 68 | } 69 | 70 | Initialize('newznab', null, function() { 71 | 72 | if ($('a.addSABnzbd').length == 0) { 73 | // Cover view: Loop through each #coverstable and #browselongtable row and add a one click link next to the download link 74 | $.merge( 75 | $('#coverstable > tbody > tr:gt(0)'), 76 | $('#browselongtable > tbody > tr:gt(0)') 77 | ).each(function() { 78 | var $tr = $(this); 79 | 80 | $("div.icon_nzb", $tr).each(function() { 81 | var href = $("a", this).attr("href"); 82 | $(this).before('
' + oneClickImgTag + '
') 83 | }); 84 | 85 | $tr.find('a.addSABnzbd') 86 | .on('click', function() { 87 | addOne($(this).closest('tr')); 88 | return false; 89 | }) 90 | ; 91 | }); 92 | } 93 | 94 | if ($('a.addSABnzbd').length == 0) { 95 | // List view: Loop through all the td.check items and add a one-click link next the nearby title 96 | $('td.check').each(function() { 97 | var $tr = $(this).parent(), 98 | href = $tr.find('.icon_nzb a').attr('href') || $tr.find('a.icon_nzb').attr('href'); 99 | 100 | $tr.find('a.title').parent() 101 | .prepend('' + oneClickImgTag + '') 102 | ; 103 | 104 | $tr.find('a.addSABnzbd') 105 | .on('click', function() { 106 | addOne($(this).closest('tr')); 107 | return false; 108 | }) 109 | ; 110 | }); 111 | } 112 | 113 | if ($('a.addSABnzbd').length == 0) { 114 | // Details view (etc.) 115 | $('div.icon_nzb').each(function() { 116 | var $tr = $(this), 117 | href = $(this).children("a").attr('href'); 118 | 119 | $tr 120 | .before('
' + oneClickImgTag + '
') 121 | ; 122 | 123 | $tr.parent().find('a.addSABnzbd') 124 | .on('click', function() { 125 | addOne($(this).closest('tr')); 126 | return false; 127 | }) 128 | ; 129 | }); 130 | } 131 | 132 | if ($('a.addSABnzbdDetails').length == 0) { 133 | // Details view: Find the download buttons, and prepend a sabnzbd button 134 | $('table#detailstable .icon_nzb').parents('td').each(function() { 135 | var $tdWithButtons = $(this), 136 | href = $tdWithButtons.find('.icon_nzb a').attr('href'), 137 | oneClickButton = '
' + oneClickImgTag + '
'; 138 | 139 | $('#infohead').append(oneClickButton); 140 | 141 | $tdWithButtons.prepend(oneClickButton) 142 | .find('a.addSABnzbdDetails') 143 | .add('#infohead .addSABnzbdDetails') 144 | .on('click', function() { 145 | var category = null; 146 | if ($('table#detailstable a[href^="/browse?t="]')) { 147 | category = $.trim($('table#detailstable a[href^="/browse?t="]').text().match(/^\s*([^< -]+)/)[1]); 148 | } 149 | addToSABnzbd( 150 | this, 151 | $(this).attr('href') + queryString, 152 | 'addurl', 153 | null, 154 | category 155 | ); 156 | return false; 157 | }) 158 | ; 159 | }); 160 | } 161 | 162 | if ($('[value="Send to SABnzbd"]').length == 0) { 163 | // List view: add a button above the list to send selected NZBs to SAB 164 | $('input.nzb_multi_operations_cart') 165 | .after(' ') 166 | .siblings('input.multiDownload') 167 | .on('click', {selector: 'td input:checked'}, addMany) 168 | ; 169 | } 170 | 171 | // Cart page: add a button above the list to send all NZBs to SAB 172 | if ($('#main h1').text() === 'My Cart' || $('.container h1').text() === 'My Cart') { 173 | $('.nzb_multi_operations') 174 | .append('') 175 | .find('input.cartDownload') 176 | .on('click', {selector: 'tr:gt(0)'}, addMany) 177 | ; 178 | } 179 | }); 180 | 181 | })(); 182 | -------------------------------------------------------------------------------- /scripts/content/nzbclub.js: -------------------------------------------------------------------------------- 1 | function addToSABnzbdFromNZBCLUB() { 2 | // Set the image to an in-progress image 3 | var img = chrome.runtime.getURL('images/content_icon_fetching.png'); 4 | $(this).find('img').first().attr("src", img); 5 | 6 | var nzburl = $(this).attr('href'); 7 | var addLink = $(this).parent(); 8 | 9 | addToSABnzbd(addLink, nzburl, "addurl"); 10 | 11 | return false; 12 | } 13 | 14 | function handleAllDownloadLinks() { 15 | $('div.project-action > div > button[id="get"]').each(function() { 16 | var img = chrome.runtime.getURL('/images/content_icon.png'); 17 | var href = "/nzb_get/" + $(this).parent().parent().attr('collectionid'); 18 | var link = ''; 19 | $(this).after(link); 20 | $(this).parent().find('a[class="addSABnzbd"]').first().click(addToSABnzbdFromNZBCLUB); 21 | }); 22 | return; 23 | } 24 | 25 | Initialize( 'nzbclub', null, function() { 26 | handleAllDownloadLinks(); 27 | }); -------------------------------------------------------------------------------- /scripts/content/nzbindex.js: -------------------------------------------------------------------------------- 1 | var use_nice_name_nzbindex; 2 | 3 | function addToSABnzbdFromNzbindex() { 4 | var addLink = this; 5 | 6 | // Set the image to an in-progress image 7 | var img = chrome.runtime.getURL('images/content_icon_fetching.png'); 8 | if ($(this).find('img').length > 0) { 9 | $(this).find('img').attr("src", img); 10 | var nzburl = $(this).attr('href'); 11 | var categories = $(this).parent().parent().parent().parent().find('td')[3].innerText; 12 | if (categories.indexOf('\n') != -1) { 13 | var category = categories.substr(0, categories.indexOf('\n')); 14 | } else { 15 | var category = categories; 16 | } 17 | if (use_nice_name_nzbindex == '1') { 18 | var nice_name = $($(this).parent().parent().parent().parent().find('td')[1]).find('label')[0].innerText; 19 | } 20 | addToSABnzbd(addLink, nzburl, "addurl", nice_name, category); 21 | } else { 22 | $(this).css('background-image', 'url(' + img + ')'); 23 | 24 | //grab all checked boxes on page 25 | var a = document.getElementsByTagName('input'); 26 | for (var i = 0, len = a.length; i < len; ++i) { 27 | if (a[i].type == 'checkbox' && a[i].checked) { 28 | if ($(a[i]).parent().parent().find('td').length < 4) { 29 | continue; 30 | } 31 | var categories = $(a[i]).parent().parent().find('td')[3].innerText; 32 | if (categories.indexOf('\n') != -1) { 33 | var category = categories.substr(0, categories.indexOf('\n')); 34 | } else { 35 | var category = categories; 36 | } 37 | if (use_nice_name_nzbindex == '1') { 38 | var nice_name = $($(a[i]).parent().parent().find('td')[1]).find('label')[0].innerText; 39 | } 40 | addToSABnzbd(addLink, 'https://nzbindex.com/download/' + a[i].value + '/' + escape(nice_name), "addurl", nice_name, category); 41 | } 42 | } 43 | } 44 | 45 | return false; 46 | } 47 | 48 | function handleAllDownloadLinks() { 49 | $('input[value="Create NZB"]').each(function () { 50 | if ($(this).attr('x-nzbpatched') !== 'true') { 51 | $(this).attr('x-nzbpatched', 'true'); 52 | // add button to send checked items to SABConnect 53 | var img = chrome.runtime.getURL('/images/content_icon.png'); 54 | var link = ''; 55 | $(this).after(link); 56 | $(this).parent().find('input[class="addSABnzbd"]').first().click(addToSABnzbdFromNzbindex); 57 | } 58 | }); 59 | 60 | $('table a[href*="nzbindex.nl\\/download\\/"]').each(function () { 61 | if ($(this).attr('x-nzbpatched') !== 'true') { 62 | $(this).attr('x-nzbpatched', 'true'); 63 | var img = chrome.runtime.getURL('/images/content_icon.png'); 64 | var href = $(this).attr('href'); 65 | var link = $(''); 66 | $(this).before(link); 67 | $(link).click(addToSABnzbdFromNzbindex); 68 | } 69 | }); 70 | 71 | $('table a[href*="nzbindex.com\\/download\\/"]').each(function () { 72 | if ($(this).attr('x-nzbpatched') !== 'true') { 73 | $(this).attr('x-nzbpatched', 'true'); 74 | var img = chrome.runtime.getURL('/images/content_icon.png'); 75 | var href = $(this).attr('href'); 76 | var link = $(' '); 77 | $(this).before(link); 78 | $(link).click(addToSABnzbdFromNzbindex); 79 | } 80 | }); 81 | } 82 | 83 | function RefreshSettings() { 84 | GetSetting('use_name_nzbindex', function (value) { 85 | use_nice_name_nzbindex = value; 86 | }); 87 | } 88 | 89 | Initialize('nzbindex', RefreshSettings, function () { 90 | let insertionInProgress = false; 91 | 92 | function DOMNodeInserted() { 93 | if (!insertionInProgress) { 94 | insertionInProgress = true; 95 | handleAllDownloadLinks(); 96 | insertionInProgress = false; 97 | } 98 | } 99 | document.documentElement.addEventListener('DOMNodeInserted', DOMNodeInserted, false); 100 | DOMNodeInserted(); 101 | }); -------------------------------------------------------------------------------- /scripts/content/nzbrss.js: -------------------------------------------------------------------------------- 1 | function addToSABnzbdFromNzbrss() { 2 | // Set the image to an in-progress image 3 | var img = chrome.runtime.getURL('images/content_icon_fetching.png'); 4 | 5 | // Only replace the image if it isn't a Bootstrap button style 6 | if ($(this).hasClass('btn') == false) { 7 | $(this).find('img').attr("src", img); 8 | } 9 | 10 | var nzburl = $(this).attr('href'); 11 | var addLink = this; 12 | 13 | // Send the NZB to SABnzbd 14 | addToSABnzbd( 15 | addLink, 16 | nzburl, 17 | "addurl", 18 | null, 19 | null); 20 | 21 | return false; 22 | } 23 | 24 | function handleAllDownloadLinks() { 25 | // Process each NZB link on the front page and show page 26 | $.each($('a[href^="/nzb/"]'), function(index, val) { 27 | // Prevent it from detecting the NZB link in the Breadcrumbs on the NZB page 28 | if ($(this).parents('ul').hasClass('breadcrumb') == false) { 29 | var href = $(this).attr('href').replace("/nzb/", "/get/"); 30 | var img = chrome.runtime.getURL('/images/content_icon.png'); 31 | var link = ' '; 32 | $(this).before(link); 33 | } 34 | }); 35 | 36 | // Process the "Download Button" on the NZB page 37 | var nzburl = $(".downloadBtn").children("a:first").attr("href"); 38 | var link = 'Send to SABnzbd'; 39 | $(".downloadBtn").append(link); 40 | 41 | // Change the on click handler to send to sabnzbd 42 | // moved because the way it was the click was firing multiple times 43 | $('.addSABnzbd').each(function() { 44 | $(this).click(addToSABnzbdFromNzbrss); 45 | }); 46 | 47 | return; 48 | } 49 | 50 | Initialize( 'nzbrss', null, function() { 51 | handleAllDownloadLinks(); 52 | }); -------------------------------------------------------------------------------- /scripts/content/omgwtfnzbs.js: -------------------------------------------------------------------------------- 1 | function getNzbId(elem) { 2 | var match = /\?id=([0-9a-zA-Z]{5,6})/i.exec(elem); 3 | 4 | if (typeof match != 'undefined' && match != null) { 5 | var nzbId = match[1]; 6 | return nzbId; 7 | } else { 8 | return null; 9 | } 10 | } 11 | 12 | function getUserName() { 13 | var protocol = 'http'; 14 | 15 | if (window.location.href.indexOf('https') == 0) { 16 | protocol = 'https'; 17 | } 18 | 19 | var apiHtml = $.ajax({url: protocol + "://omgwtfnzbs.org/account.php?action=api", async: false}).responseText; 20 | var username = apiHtml.match(/(.*)<\/sabuser>/)[1]; 21 | return username; 22 | } 23 | 24 | function getApiKey() { 25 | var protocol = 'http'; 26 | 27 | if (window.location.href.indexOf('https') == 0) { 28 | protocol = 'https'; 29 | } 30 | 31 | var apiHtml = $.ajax({url: protocol + "://omgwtfnzbs.org/account.php?action=api", async: false}).responseText; 32 | var apiKey = apiHtml.match(/(.*)<\/sabapikey>/)[1]; 33 | 34 | if (apiKey != null) { 35 | return apiKey; 36 | } else { 37 | return null; 38 | } 39 | } 40 | 41 | function addMany(e) { 42 | var category = null; 43 | 44 | 45 | $("#sendToSabButton").prop('value', 'Sending...'); 46 | 47 | $("input:checkbox").each(function() { 48 | if ( $(this).attr('name') == "cartcbitems[]" && this.checked) { 49 | category = $.trim($(this).attr('category').match(/^\s*([^:]+)/)[1]); 50 | addOne($(this).val(),category); 51 | } 52 | }); 53 | 54 | $("#sendToSabButton").prop('value', 'Sent to SABnzbd'); 55 | 56 | setTimeout(function() { 57 | $("#sendToSabButton").prop('value', 'Send to SABnzbd'); 58 | }, 4000); 59 | 60 | return false; 61 | } 62 | 63 | function addOne(nzbid,cat) { 64 | var addLink = this; 65 | var url = "https://api.omgwtfnzbs.org/nzb/?"; 66 | 67 | // Build up the URL to the API for direct downloading by getting the NZB Id, Username and API Key 68 | url = url + 'id=' + nzbid + '&user=' + getUserName() + '&api=' + getApiKey(); 69 | 70 | // Get the category 71 | var category = cat; 72 | 73 | if (category === null) { 74 | category = "default"; 75 | } 76 | 77 | addToSABnzbd( 78 | addLink, 79 | url, 80 | "addurl", 81 | null, 82 | category); 83 | 84 | return false; 85 | 86 | } 87 | 88 | function addToSABnzbdFromOmgwtfnzbs() { 89 | // Set the image to an in-progress image 90 | var img = chrome.runtime.getURL('images/content_icon_fetching.png'); 91 | $(this).find('img').attr("src", img); 92 | 93 | var nzburl = $(this).attr('href'); 94 | var addLink = this; 95 | var url = "https://api.omgwtfnzbs.org/nzb/?"; 96 | 97 | if (nzburl.indexOf('http://') == 0) { 98 | url = "http://api.omgwtfnzbs.org/nzb/?"; 99 | } 100 | 101 | // Build up the URL to the API for direct downloading by getting the NZB Id, Username and API Key 102 | url = url + 'id=' + getNzbId(nzburl) + '&user=' + getUserName() + '&api=' + getApiKey(); 103 | 104 | // Get the category 105 | var category = null; 106 | // find the category for the browse.php page 107 | if ($.trim($(this).parents('.nzbt_row').html())) { 108 | category = $.trim($(this).parents('.nzbt_row').html()); 109 | category = category.match(/(.*)<\/sabcategory>/)[1]; 110 | } 111 | // find the category for the details.php page 112 | else if ($( "#category" ).length != 0) 113 | { 114 | category = $.trim($('#category').html()); 115 | category = category.match(/(.*)<\/sabcategory>/)[1]; 116 | } 117 | // find the category for the trends.php page 118 | else if ($(this).parents('.flag_float:first').children('.small_middle').children('.bmtip.cat_class').html()) { 119 | category = $.trim($(this).parents('.flag_float:first').html()); 120 | category = category.match(/(.*)<\/sabcategory>/)[1]; 121 | } 122 | 123 | if (category === null) { 124 | category = "default"; 125 | } 126 | 127 | // Send the NZB to SABnzbd 128 | addToSABnzbd( 129 | addLink, 130 | url, 131 | "addurl", 132 | null, 133 | category); 134 | 135 | return false; 136 | } 137 | 138 | function handleAllDownloadLinks() { 139 | $('img[src="pics/dload.gif"]').each(function() { 140 | var href = $(this).parent().attr('href'); 141 | var img = chrome.runtime.getURL('/images/content_icon.png'); 142 | var link_mini = ' '; 143 | var link_full = ' Send to SABnzbd '; 144 | 145 | if ($(this).parent().hasClass('linky') === false) { 146 | $(this).parent().before(link_mini); 147 | } else { 148 | $(this).parent().before(link_full); 149 | } 150 | }); 151 | 152 | $("#dlButton:submit").each(function() { 153 | $(this) 154 | .after(' ') 155 | ; 156 | $(document).on("click", "#sendToSabButton", function(){ 157 | addMany(); 158 | }); 159 | }); 160 | 161 | // Change the on click handler to send to sabnzbd 162 | // moved because the way it was the click was firing multiple times 163 | $('.addSABnzbd').each(function() { 164 | $(this).click(addToSABnzbdFromOmgwtfnzbs); 165 | }); 166 | 167 | return; 168 | } 169 | 170 | Initialize( 'omgwtfnzbs', null, function() { 171 | handleAllDownloadLinks(); 172 | }); 173 | 174 | -------------------------------------------------------------------------------- /scripts/content/usenet4ever.js: -------------------------------------------------------------------------------- 1 | function getHeaderFromURL(url) 2 | { 3 | console.log("extracting header fom "+url); 4 | var regex = new RegExp("[?&]q=([^&#]*)"), 5 | results = regex.exec(url); 6 | return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); 7 | } 8 | 9 | function handleAllDownloadLinks() 10 | { 11 | $('body').on('click', 'img[src="http://usenet4ever.info/vb4/posten/images/nzb_link1.png"]', function(e) { 12 | e.preventDefault(); 13 | $("#sabnzbdoverlay").remove(); 14 | 15 | var url = $(this).parent('a').attr('href'); 16 | if(url.substr(url.length - 4) == ".nzb") 17 | { 18 | addToSABnzbd($(this), url, "addurl", "", "usenet4ever"); 19 | } 20 | else 21 | { 22 | var div = $(this).parent().parent(); 23 | div.css('position','relative'); 24 | div.append('
'); 25 | 26 | var title = $(this).parents('div.content').find('h1').text(); 27 | //console.log('title: '+title); 28 | 29 | //if(div.text().match(/.*(passwor).*/)) alert("pass!!"); 30 | if(div.text().match(/.*(asswor).*/g)) 31 | { 32 | var pass = ""; 33 | if (window.getSelection) 34 | { 35 | pass = window.getSelection().toString(); 36 | } else if (document.selection && document.selection.type != "Control") 37 | { 38 | pass = document.selection.createRange().text; 39 | } 40 | title = title+"{{"+pass+"}}"; 41 | //console.log("add passwort to the title: " + title); 42 | } 43 | 44 | 45 | var header = url.match("[\\?&]q=([^&#]*)"); 46 | header = header[1]; 47 | 48 | var binsearch = "http://binsearch.info/?q="+header+"&max=10&adv_age=&server #r2"; 49 | var nzbindex = "http://nzbindex.nl/search/?q="+header+"&sort=sizedesc"; 50 | console.log("url: "+nzbindex); 51 | 52 | $.get(nzbindex, function(data) 53 | { 54 | var results = $(data).find('#results').find('input[type="checkbox"]'); 55 | 56 | if(results.length > 0) 57 | { 58 | results.each(function() { 59 | var desc = $(this).parent().next().find('label').text()+$(this).parent().next().next().text(); 60 | $('#sabnzbdoverlay').append('
'); 61 | }); 62 | } else { 63 | $('#sabnzbdoverlay').append('

sorry bro, nix gefunden :( schau selbst

'); 64 | } 65 | 66 | /* binsearch 67 | $(data).find('input[type="checkbox"]').each(function() { 68 | var desc = $(this).parent().parent().find('span.d').html(); 69 | $('#sabnzbdoverlay').append(''); 70 | }); 71 | */ 72 | }); 73 | } 74 | 75 | }); 76 | /* 77 | $('img[src="http://usenet4ever.info/vb4/posten/images/nzb_link1.png"]').each(function() { 78 | // add button to send item to SABConnect 79 | var img = chrome.runtime.getURL('/images/content_icon.png'); 80 | var link = '
'; 81 | $(this).parent('a').before(link); 82 | //$(this).parent('a').next('a.addSABnzbd').click(addToSABnzbdFromUsenet4ever); 83 | }); 84 | */ 85 | 86 | // Change the onclick handler to send to sabnzbd 87 | $('body').on('click','.addToSABnzbd', function() { 88 | var title = $(this).attr("title"); 89 | var url = $(this).attr("rel"); 90 | addToSABnzbd($(this), url, "addurl", title, "usenet4ever"); 91 | }); 92 | } 93 | 94 | Initialize( 'usenet4ever', null, function() { 95 | console.log("initializing usenet4ever"); 96 | handleAllDownloadLinks(); 97 | }); -------------------------------------------------------------------------------- /scripts/content/yubse.js: -------------------------------------------------------------------------------- 1 | var useNiceName; 2 | 3 | function addAllToSABnzbdFromYubse() { 4 | // Set the image to an in-progress image 5 | var img = chrome.runtime.getURL('images/content_icon_fetching.png'); 6 | if ($(this).find('img').length > 0) { 7 | $(this).find('img').attr("src", img); 8 | } else { 9 | $(this).css('background-image', 'url('+img+')'); 10 | } 11 | var addLink = this; 12 | 13 | //grab all checked boxes on page 14 | $("#tableResult input:checked").each(function(){ 15 | var nice_name = ""; 16 | if( useNiceName ) { 17 | nice_name_element_parent = $(this).parents()[5]; 18 | nice_name = $(nice_name_element_parent).find('#file_detail').text(); 19 | var regnN = new RegExp('"[^""\r\n]*"'); 20 | var nN = regnN.exec(nice_name); 21 | if (nN !=null) 22 | { 23 | nice_name = "Yubse_" + nN[0].replace("\"","").replace("\"",""); 24 | } 25 | } 26 | addToSABnzbd(addLink, 'http://www.yubse.com/serv/YubseSearch.svc/Download/' + this.name, "addurl",nice_name, null); 27 | }); 28 | return false; 29 | } 30 | 31 | function addToSABnzbdFromYubse(node) { 32 | // Set the image to an in-progress image 33 | var img = chrome.runtime.getURL('images/content_icon_fetching.png'); 34 | if ($(node).find('a[class="addSABnzbd"]').find('img').length > 0) { 35 | $(node).find('a[class="addSABnzbd"]').find('img').attr("src", img); 36 | } else { 37 | $(node).find('a[class="addSABnzbd"]').css('background-image', 'url('+img+')'); 38 | } 39 | var addLink = this; 40 | 41 | var nice_name = ""; 42 | if( useNiceName ) { 43 | nice_name = $(node).find('#file_detail').text(); 44 | var regnN = new RegExp('"[^""\r\n]*"'); 45 | var nN = regnN.exec(nice_name); 46 | if (nN !=null) 47 | { 48 | nice_name = "Yubse_" + nN[0].replace("\"","").replace("\"",""); 49 | } 50 | } 51 | addToSABnzbd(addLink, 'http://www.yubse.com/serv/YubseSearch.svc/Download/' +$(node).find('input').first().attr('name'), "addurl",nice_name, null); 52 | return false; 53 | } 54 | 55 | function handleAllDownloadLinks() { 56 | $('.header_btn a:last-child').after(function() { 57 | var img = chrome.runtime.getURL('/images/content_icon.png'); 58 | var href = $(this).attr('href'); 59 | var link = ' '; 60 | $(this).after(link); 61 | $(this).parent().find('a[class="addSABnzbd"]').first().click(addAllToSABnzbdFromYubse); 62 | }); 63 | 64 | } 65 | 66 | function handleSingleDownloadLink(node) { 67 | if ($(node).find('a[class="addSABnzbd"]').length==0){ 68 | $(node).find('input').each(function() { 69 | var img = chrome.runtime.getURL('/images/content_icon.png'); 70 | var href = $(this).attr('href'); 71 | var link = ' '; 72 | $(this).parent().parent().find('td:last').after(link); 73 | $(this).parent().parent().find('a[class="addSABnzbd"]').first().click(function(){ addToSABnzbdFromYubse($(node))}); 74 | }); 75 | } 76 | } 77 | 78 | function RefreshSettings() 79 | { 80 | GetSetting( 'use_name_yubse', function( state ) { 81 | useNiceName = state; 82 | }); 83 | } 84 | 85 | $(document).ready(function() { 86 | Initialize( 'yubse', RefreshSettings(), function() { 87 | handleAllDownloadLinks(); 88 | $("#tableResult tr").each(function(){ 89 | handleSingleDownloadLink(this); 90 | }); 91 | }); 92 | 93 | $("#tableResult").bind('DOMNodeInserted', function(event) { 94 | if (event.target.nodeName=='THEAD')//detect the THEAD construction in the table for create button one time 95 | { 96 | handleAllDownloadLinks(); 97 | } 98 | else if (event.target.nodeName=='TR')//detect all the tr in the table and create button for each 99 | { 100 | handleSingleDownloadLink(event.target); 101 | } 102 | }); 103 | }); 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /scripts/convert.js: -------------------------------------------------------------------------------- 1 | // 'value' must be a string. 2 | function toBoolean( value ) 3 | { 4 | if( typeof value != 'string' ) { 5 | throw 'Parameter "value" must be of type string'; 6 | } 7 | 8 | value = value.toLowerCase(); 9 | 10 | if( value == 'yes' || value == 'true' || value == '1' ) { 11 | return true; 12 | } 13 | else if( value == 'no' || value == 'false' || value == '0' ) { 14 | return false; 15 | } 16 | 17 | return null; 18 | } -------------------------------------------------------------------------------- /scripts/pages/background.js: -------------------------------------------------------------------------------- 1 | // List of sites that send the X-DNZB-Category HTTP header 2 | //var category_header_sites = ['nzbs.org', 'newzbin2.es', 'newzxxx.com']; 3 | var category_header_sites = []; 4 | 5 | var defaultSettings = { 6 | sabnzbd_url: 'http://localhost:8080/', 7 | sabnzbd_api_key: '', 8 | sabnzbd_username: '', 9 | provider_binsearch: true, 10 | provider_bintube: true, 11 | provider_dognzb: true, 12 | provider_fanzub: true, 13 | provider_animezb: true, 14 | provider_animenzb: true, 15 | provider_nzbclub: true, 16 | provider_nzbindex: true, 17 | provider_yubse: true, 18 | provider_omgwtfnzbs: true, 19 | provider_nzbrss: true, 20 | provider_newznab: 'your_newznab.com, some_other_newznab.com', 21 | provider_usenet4ever: true, 22 | use_name_binsearch: true, 23 | use_name_nzbindex: true, 24 | use_name_yubse: true, 25 | config_refresh_rate: 15, 26 | config_enable_graph: true, 27 | config_enable_context_menu: true, 28 | config_enable_notifications: true, 29 | config_notification_timeout: 10, 30 | config_ignore_categories: false, 31 | config_use_user_categories: false, 32 | config_use_category_header: false, 33 | config_hard_coded_category: '', 34 | config_default_category: '', 35 | config_enable_automatic_authentication: true, 36 | config_enable_automatic_detection: true, 37 | profiles: {}, 38 | first_profile_initialized: false, 39 | active_category: '*', 40 | settings_synced: false 41 | }; 42 | 43 | var notification_container = []; 44 | 45 | var store = new StoreClass( 'settings', defaultSettings, undefined, storeReady_background ); 46 | 47 | function storeReady_background() { 48 | startTimer(); 49 | 50 | initializeBackgroundPage(); 51 | 52 | //context_menu.js 53 | SetupContextMenu(); 54 | } 55 | 56 | function resetSettings() 57 | { 58 | store.fromObject( defaultSettings ); 59 | } 60 | 61 | //file size formatter - takes an input in bytes 62 | function fileSizes(value, decimals) 63 | { 64 | if(decimals == null) decimals = 2; 65 | kb = value / 1024 66 | mb = value / 1048576 67 | gb = value / 1073741824 68 | if (gb >= 1){ 69 | return gb.toFixed(decimals)+"GB" 70 | } else if (mb >= 1) { 71 | return mb.toFixed(decimals)+"MB" 72 | } else { 73 | return kb.toFixed(decimals)+"KB" 74 | } 75 | } 76 | 77 | function updateBadge( data ) 78 | { 79 | if( data ) { 80 | var slots = data.queue.noofslots; 81 | var badge = {}; 82 | if( !slots ) { 83 | badge.text = ''; 84 | } else { 85 | badge.text = slots.toString(); 86 | } 87 | chrome.browserAction.setBadgeText(badge); 88 | } 89 | } 90 | 91 | function isDownloading( kbpersec ) 92 | { 93 | return kbpersec && parseFloat( kbpersec ) > 1; 94 | } 95 | 96 | function updateBackground( data ) 97 | { 98 | if( data ) { 99 | var badgeColor = {} 100 | if( isDownloading( data.queue.kbpersec ) ) { 101 | badgeColor.color = new Array(0, 213, 7, 100); 102 | } else { 103 | badgeColor.color = new Array(255, 0, 0, 100); 104 | } 105 | 106 | chrome.browserAction.setBadgeBackgroundColor(badgeColor) 107 | } 108 | } 109 | 110 | function updateSpeedLog( data ) 111 | { 112 | var speedlog = []; 113 | 114 | var speedlogData = getPref( 'speedlog' ); 115 | if( speedlogData ) 116 | { 117 | speedlog = JSON.parse(getPref('speedlog')); 118 | 119 | // Only allow 10 values, if at our limit, remove the first value (oldest) 120 | while( speedlog.length >= 10 ) { 121 | speedlog.shift(); 122 | } 123 | } 124 | 125 | speedlog.push( data ? parseFloat( data.queue.kbpersec ) : 0 ); 126 | setPref( 'speedlog', JSON.stringify( speedlog ) ); 127 | } 128 | 129 | function displayNotificationCallback( data ) 130 | { 131 | // Return early if data is null, which can happen if we 132 | // have invalid connection information in settings and 133 | // we actually can't establish a connection with sabnzbd. 134 | if( !data || data.error ) { 135 | return; 136 | } 137 | 138 | data.history.slots.forEach(function(entry) 139 | { 140 | // console.log(entry); 141 | var key = 'past_dl-' + entry.name + '-' + entry.bytes; 142 | if (typeof localStorage[key] == 'undefined') 143 | { 144 | console.log("Possible History notification:"); 145 | console.log(entry.name); 146 | 147 | // Only notify when post-processing is complete 148 | if (entry.action_line == '') 149 | { 150 | chrome.notifications.onButtonClicked.addListener(function(notId, buttonIndex) { 151 | chrome.tabs.create({url: 'file:///'+entry.storage}, function(tab) { console.log("opening tab"); }); 152 | }); 153 | if (entry.fail_message != '') 154 | { 155 | var fail_msg = entry.fail_message.split('<')[0]; 156 | var notification = chrome.notifications.create( 157 | entry.name, 158 | { 159 | type: 'basic', 160 | iconUrl: 'images/addon_icon.svg', 161 | title: 'Download Failed', 162 | message: entry.name + ': ' + fail_msg, 163 | buttons: [{ title: entry.storage }] 164 | }, 165 | function(notId) { console.log("notification for "+notId); } 166 | ); 167 | } else { 168 | var notification = chrome.notifications.create( 169 | entry.name, 170 | { 171 | type: 'basic', 172 | iconUrl: 'images/addon_icon.svg', 173 | title: 'Download Complete', 174 | message: entry.name, 175 | buttons: [{ title: entry.storage }] 176 | }, 177 | function(notId) { console.log("notification for "+notId); } 178 | ); 179 | } 180 | 181 | localStorage[key] = true; 182 | } 183 | } 184 | }); 185 | } 186 | 187 | function displayNotificationCycle ( ) 188 | { 189 | var al = notification_container.length, notification_temp = []; 190 | for ( var i = 0; i < al; i++ ) 191 | { 192 | var notif = notification_container[i]; 193 | var notification = notif.shift(); 194 | var timeout = notif.shift(); 195 | if ( timeout <= 1 ) 196 | { 197 | console.log( "Notification timeout reached, killing popup" ); 198 | notification.cancel(); 199 | } 200 | else 201 | { 202 | var notif_temp = [ notification, timeout - 1 ]; 203 | notification_temp.push( notif_temp ); 204 | } 205 | } 206 | notification_container = notification_temp; 207 | setTimeout( displayNotificationCycle, 1000 ); 208 | } 209 | setTimeout( displayNotificationCycle, 1000 ); 210 | 211 | 212 | function fetchInfoSuccess( data, quickUpdate, callback ) 213 | { 214 | if( !data || data.error ) { 215 | setPref( 'error', data ? data.error : 'Success with no data?' ); 216 | 217 | if( callback ) { 218 | callback(); 219 | } 220 | 221 | return; 222 | } 223 | 224 | // This will remove the error 225 | // Will cause problems if the error pref is used elsewhere to report other errors 226 | setPref('error', ''); 227 | setPref('timeleft', data ? data.queue.timeleft : '0' ); 228 | if(data) { 229 | // Convert to bytes 230 | var bytesPerSec = parseFloat(data.queue.kbpersec)*1024; 231 | var speed = data.queue.speed + 'B/s'; 232 | } else { 233 | var speed = '-'; 234 | } 235 | setPref('speed', speed); 236 | 237 | // Do not run this on a quickUpdate (unscheduled refresh) 238 | if( !quickUpdate ) { 239 | updateSpeedLog( data ); 240 | } 241 | 242 | var queueSize = ''; 243 | if( data && data.queue.mbleft > 0 ) { 244 | // Convert to bytes 245 | var bytesInMegabyte = 1048576; 246 | var bytesLeft = data.queue.mbleft * bytesInMegabyte; 247 | var queueSize = fileSizes(bytesLeft); 248 | } 249 | setPref('sizeleft', queueSize); 250 | 251 | setPref('queue', data ? JSON.stringify(data.queue.slots) : '' ); 252 | 253 | setPref( 'status', data ? data.queue.status : '' ); 254 | setPref( 'paused', data ? data.queue.paused : '' ); 255 | if(data.queue.paused) { 256 | setPref("pause_int", data.queue.pause_int); 257 | } 258 | 259 | updateBadge( data ); 260 | updateBackground( data ); 261 | 262 | if( callback ) { 263 | callback(); 264 | } 265 | } 266 | 267 | function fetchInfoError( XMLHttpRequest, textStatus, errorThrown, callback ) { 268 | setPref('error', 'Could not connect to SABnzbd - Check it is running, the details in this plugin\'s settings are correct and that you are running at least SABnzbd version 0.5!'); 269 | 270 | if( callback ) { 271 | callback(); 272 | } 273 | } 274 | 275 | function testConnection( profileValues, callback ) 276 | { 277 | fetchInfo( true, callback, profileValues ); 278 | } 279 | 280 | /** 281 | * quickUpdate 282 | * If set to true, will not update the graph ect, currently used when a queue item has been moved/deleted in order to refresh the queue list 283 | */ 284 | function fetchInfo( quickUpdate, callback, profileValues ) 285 | { 286 | var params = { 287 | mode: 'queue', 288 | limit: '5' 289 | }; 290 | 291 | sendSabRequest( 292 | params, 293 | function(data) { fetchInfoSuccess( data, quickUpdate, callback ) }, 294 | function(_1, _2, _3) { fetchInfoError( _1, _2, _3, callback ) }, 295 | profileValues 296 | ); 297 | } 298 | 299 | function displayNotifications() 300 | { 301 | if( store.get('config_enable_notifications') === true ) { 302 | var params = { 303 | mode: 'history', 304 | limit: '10' 305 | }; 306 | 307 | sendSabRequest( params, displayNotificationCallback ); 308 | } 309 | } 310 | 311 | function setMaxSpeed( speed, success_callback, error_callback ) 312 | { 313 | var params = { 314 | mode: 'config', 315 | name: 'speedlimit', 316 | value: speed 317 | }; 318 | 319 | sendSabRequest( params, success_callback, error_callback ); 320 | } 321 | 322 | function getMaxSpeed( success_callback ) 323 | { 324 | var params = { 325 | mode: 'config', 326 | name: 'get_speedlimit' 327 | }; 328 | 329 | sendSabRequest( params, success_callback ); 330 | } 331 | 332 | function sendSabRequest( params, success_callback, error_callback, profileValues ) 333 | { 334 | var profile = profileValues || activeProfile(); 335 | 336 | var sabApiUrl = constructApiUrl( profile ); 337 | var data = constructApiPost( profile ); 338 | data.output = 'json'; 339 | 340 | $.ajax({ 341 | type: "GET", 342 | url: sabApiUrl, 343 | data: combine( data, params ), 344 | username: profile.username, 345 | password: profile.password, 346 | dataType: 'json', 347 | success: success_callback, 348 | error: error_callback 349 | }); 350 | } 351 | 352 | function updatePopup() 353 | { 354 | var views = chrome.runtime.getViews({ type: "popup" }); 355 | if( views.length == 1 ) 356 | { 357 | var popup = views[0]; 358 | popup.reDrawPopup(); 359 | } 360 | } 361 | 362 | function refresh( quick, callback ) 363 | { 364 | if( !callback ) { 365 | callback = updatePopup; 366 | } 367 | 368 | fetchInfo( quick, callback ); 369 | 370 | if( !quick ) { 371 | displayNotifications(); 372 | } 373 | } 374 | 375 | var gTimer; 376 | 377 | function restartTimer() 378 | { 379 | if( gTimer ) { 380 | clearInterval( gTimer ); 381 | } 382 | 383 | startTimer(); 384 | } 385 | 386 | function startTimer() 387 | { 388 | var refreshRate = getRefreshRate(); 389 | if( refreshRate > 0 ) { 390 | console.log("Will refresh from SABnzbd every " + refreshRate + " ms."); 391 | gTimer = setInterval( refresh, refreshRate ); 392 | } 393 | else { 394 | console.log("Will NOT refresh from SABnzbd automatically (refresh disabled in options)."); 395 | } 396 | } 397 | 398 | function DoesSiteSupportCatHeader( nzburl ) 399 | { 400 | var supported = false; 401 | for (var i=0; iwiki page.' 343 | }, 344 | { 345 | 'tab': 'Configuration', 346 | 'group': 'Categories', 347 | 'name': 'config_ignore_categories', 348 | 'type': 'checkbox', 349 | 'label': 'Do not attempt to pass category names, forcing SABnzbd to use group names in the NZB instead.\ 350 | This will ignore all of the following category options. Note that some indexers (newznab, etc.)\ 351 | embed category names in the nzb itself.' 352 | }, 353 | { 354 | 'tab': 'Configuration', 355 | 'group': 'Categories', 356 | 'name': 'config_use_user_categories', 357 | 'type': 'checkbox', 358 | 'label': 'Override the category with the one chosen from the menu. The list of categories is loaded by SABnzbd.' 359 | }, 360 | { 361 | 'tab': 'Configuration', 362 | 'group': 'Categories', 363 | 'name': 'config_use_category_header', 364 | 'type': 'checkbox', 365 | 'label': 'Use X-DNZB-Category HTTP header instead of auto-categorization\ 366 | on supported sites (NZBs.org, Newzbin).' 367 | }, 368 | { 369 | 'tab': 'Configuration', 370 | 'group': 'Categories', 371 | 'name': 'config_hard_coded_category', 372 | 'type': 'text', 373 | 'label': 'Hard-coded Category:' 374 | }, 375 | { 376 | 'tab': 'Configuration', 377 | 'group': 'Categories', 378 | 'name': 'config_category_desc1', 379 | 'type': 'description', 380 | 'text': 381 | 'Will use this category for all downloads sent to SABnzbd.' 382 | }, 383 | { 384 | 'tab': 'Configuration', 385 | 'group': 'Categories', 386 | 'name': 'config_default_category', 387 | 'type': 'text', 388 | 'label': 'Default Category:' 389 | }, 390 | { 391 | 'tab': 'Configuration', 392 | 'group': 'Categories', 393 | 'name': 'config_category_desc2', 394 | 'type': 'description', 395 | 'text': 396 | 'Will use this category if no category can be obtained from the NZB index site.' 397 | }, 398 | ] 399 | }; 400 | -------------------------------------------------------------------------------- /scripts/pages/newznab-autoadd.js: -------------------------------------------------------------------------------- 1 | (function () { // Encapsulate 2 | 3 | /* 4 | New indexes are frequently requested in sabconnectplusplus 5 | of which the majority are supported as they are Newznab. 6 | Lets add support to automatically notify the user their 7 | popular indexer is supported, as well as add it to the 8 | config automatically if requested. 9 | */ 10 | 11 | var newznabDetection = function(){ 12 | // Lets try and wrap all detection functions to a simplistic block 13 | var runFound = false; 14 | 15 | // most sites detect here 16 | if ( ($('[name=RSSTOKEN]').filter(':first').length) && 17 | ($('input.nzb_multi_operations_cart').filter(':first').length) ) 18 | { return true; } 19 | 20 | // some additionals 21 | $('#browsetable tr:gt(0)').filter(':first').each(function() { 22 | if ( $(this).find('td.item label').filter(':first').length ) 23 | runFound = true; 24 | }); 25 | if ( runFound ) 26 | return true; 27 | 28 | // If all else fails, we are not sab ;) 29 | return false; 30 | } 31 | 32 | // Lets restrict our movements to pages that are newznab, logged in and displaying triggerable data 33 | if ( newznabDetection() ) { 34 | var thishost = (window.location.hostname.match(/([^.]+)\.\w{2,3}(?:\.\w{2})?$/) || [])[0]; 35 | $('body').prepend( 36 | $('
').addClass('notification autonabSticky hide').prepend( 37 | $('

SABconnect++ says: Would you like to enable one-click "Send to SAB" buttons on this site?

').append( 38 | ' Enable | Ignore' 39 | ), 40 | $('×') 41 | ) 42 | ); 43 | $('.notification.autonabSticky').notify(); 44 | $('#autonabIgnore').click(function(){ 45 | var request = { 46 | action: 'set_setting', 47 | setting: 'nabignore.' + thishost, 48 | value: true 49 | }; 50 | chrome.runtime.sendMessage( request ); 51 | $('a.close').click(); 52 | }); 53 | $('#autonabEnable').click(function(){ 54 | var request = { 55 | action: 'get_setting', 56 | setting: 'provider_newznab' 57 | }; 58 | chrome.runtime.sendMessage( request, function( response ) { 59 | var request = { 60 | action: 'set_setting', 61 | setting: 'provider_newznab', 62 | value: response.value + ', ' + thishost 63 | }; 64 | chrome.runtime.sendMessage( request, function() { 65 | location.reload(); 66 | }); 67 | }); 68 | }); 69 | } 70 | })(); 71 | -------------------------------------------------------------------------------- /scripts/pages/newznab-check.js: -------------------------------------------------------------------------------- 1 | String.prototype.trim = function(){return this.replace(/^\s+|\s+$/g, '');}; 2 | 3 | chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { 4 | if (changeInfo.status == 'complete') { 5 | var found_nab = false; 6 | var newznab_urls_pre = store.get('provider_newznab'); 7 | if (typeof newznab_urls_pre == 'undefined') 8 | return; 9 | var newznab_urls = newznab_urls_pre.split(','); 10 | var parsedurl = $.url.parse( tab.url ); 11 | var host = (parsedurl.host.match(/([^.]+)\.\w{2,3}(?:\.\w{2})?$/) || [])[0] 12 | for (var i = 0; i < newznab_urls.length; i++) { 13 | var newznab_url = newznab_urls[i].trim(); 14 | if (newznab_url.length > 0 && tab.url.match('https?://.*' + newznab_url + '.*')) { 15 | chrome.tabs.executeScript(tabId, {file: "third_party/jquery/jquery-1.12.4.min.js"}); 16 | chrome.tabs.executeScript(tabId, {file: "scripts/content/common.js"}); 17 | chrome.tabs.executeScript(tabId, {file: "third_party/webtoolkit/webtoolkit.base64.js"}); 18 | chrome.tabs.executeScript(tabId, {file: "scripts/content/newznab.js"}); 19 | chrome.tabs.insertCSS(tabId, {file: "css/newznab.css"}); 20 | if ( store.get( 'nabignore.' + host ) === false ) 21 | store.set( 'nabignore.' + host ); 22 | found_nab = true; 23 | break; 24 | } 25 | } 26 | if ( (!found_nab) && (tab.url.indexOf('http') == 0) ) { 27 | var nabenabled = store.get( 'nabignore.' + host ); 28 | var nabdetection = store.get('config_enable_automatic_detection'); 29 | if ( nabdetection && !nabenabled ) { 30 | chrome.tabs.executeScript(tabId, {file: "third_party/jquery/jquery-1.12.4.min.js"}); 31 | chrome.tabs.executeScript(tabId, {file: "third_party/jquery/jquery.notify.js"}); 32 | chrome.tabs.executeScript(tabId, {file: "scripts/content/common.js"}); 33 | chrome.tabs.executeScript(tabId, {file: "scripts/pages/newznab-autoadd.js"}); 34 | chrome.tabs.insertCSS(tabId, {file: "css/nabnotify.css"}); 35 | } 36 | if ( nabenabled === false ) 37 | store.set( 'nabignore.' + host ); 38 | } 39 | } 40 | }); 41 | -------------------------------------------------------------------------------- /scripts/pages/popup.js: -------------------------------------------------------------------------------- 1 | var store = new StoreClass('settings', {}, undefined, storeReady_popup); 2 | 3 | function storeReady_popup() { 4 | var nowtime = new Date(); 5 | var lastOpened = parseInt(getPref("lastOpened")); 6 | var closeWindow = false; 7 | if (lastOpened > 0) { 8 | if (nowtime.getTime() - lastOpened < 700) { 9 | chrome.tabs.create({url: activeProfile().url}); 10 | closeWindow = true; 11 | window.close(); 12 | } 13 | } 14 | if (!closeWindow) { 15 | setPref("lastOpened", nowtime.getTime()); 16 | SetupTogglePause(); 17 | reDrawPopup(); 18 | } 19 | 20 | $('#open_sabnzbd').click( function() { 21 | var profile = activeProfile(); 22 | var url = $.url.parse( profile.url ); 23 | 24 | var build = { 25 | protocol: url.protocol, 26 | host: url.host, 27 | port: url.port, 28 | path: url.path, 29 | } 30 | 31 | if( store.get( 'config_enable_automatic_authentication' ) ) { 32 | build.user = $.url.encode(profile.username); 33 | build.password = $.url.encode(profile.password); 34 | } 35 | 36 | chrome.tabs.create( { url: $.url.build( build ) } ); 37 | }); 38 | 39 | $('#extension_settings').click( function() { 40 | chrome.tabs.create({url: 'settings.html'}); 41 | }); 42 | 43 | $('#refresh').click( function() { 44 | refresh(); 45 | }); 46 | 47 | $('#set-speed').click( function() { 48 | setMaxSpeed( $('#speed-input').val() ); 49 | }); 50 | 51 | $('#speed-input').keydown( function( event ) { 52 | var code = event.keyCode || event.which; 53 | if( code == 13 ) { // Enter pressed 54 | setMaxSpeed( $('#speed-input').val() ); 55 | } 56 | }); 57 | 58 | populateProfileList(); 59 | 60 | $('#profiles').val( profiles.getActiveProfile().name ); 61 | $('#profiles').change( OnProfileChanged ); 62 | 63 | if (store.get('config_use_user_categories')) { 64 | $('#user_category').css("display", "block"); 65 | populateAndSetCategoryList(); 66 | } 67 | 68 | setMaxSpeedText(); 69 | } 70 | 71 | function refresh() 72 | { 73 | var background = chrome.extension.getBackgroundPage(); 74 | background.refresh(); 75 | } 76 | 77 | function setMaxSpeedText() 78 | { 79 | getMaxSpeed( function( data ) { 80 | $('#speed-input').val( data ? data.speedlimit : '' ); 81 | }); 82 | } 83 | 84 | /// @param speed Maximum speed in KBps 85 | function setMaxSpeed( speed ) 86 | { 87 | var background = chrome.extension.getBackgroundPage(); 88 | background.setMaxSpeed( speed, 89 | // Success 90 | function() { 91 | setMaxSpeedText(); 92 | }, 93 | // Failure 94 | function( XMLHttpRequest, textStatus, errorThrown ) { 95 | alert( 'Failed to set max speed.' ); 96 | } 97 | ); 98 | } 99 | 100 | function getMaxSpeed( success_callback ) 101 | { 102 | var background = chrome.extension.getBackgroundPage(); 103 | background.getMaxSpeed( success_callback ); 104 | } 105 | 106 | function moveQueueItem(nzoid, pos) 107 | { 108 | var sabApiUrl = constructApiUrl(); 109 | var data = constructApiPost(); 110 | data.mode = 'switch'; 111 | data.value = nzoid; 112 | data.value2 = pos; 113 | 114 | $.ajax({ 115 | type: "POST", 116 | url: sabApiUrl, 117 | data: data, 118 | username: activeProfile().username, 119 | password: activeProfile().password, 120 | success: function(data) { refresh() }, 121 | error: function() { 122 | $('#error').html('Failed to move item, please check your connection to SABnzbd'); 123 | } 124 | }); 125 | } 126 | 127 | function queueItemAction(action, nzoid, callback) 128 | { 129 | var sabApiUrl = constructApiUrl(); 130 | var data = constructApiPost(); 131 | data.mode = 'queue'; 132 | data.name = action; 133 | data.value = nzoid; 134 | 135 | $.ajax({ 136 | type: "POST", 137 | url: sabApiUrl, 138 | data: data, 139 | username: activeProfile().username, 140 | password: activeProfile().password, 141 | success: function(data) { refresh() }, 142 | error: function() { 143 | $('#error').html('Failed to move item, please check your connection to SABnzbd'); 144 | } 145 | }); 146 | } 147 | 148 | var paused = false; 149 | var oldPos = -1; 150 | 151 | function durationPause(e) { 152 | var val = parseInt($(this).val()); 153 | if(isNaN(val)) { 154 | val = parseInt(window.prompt("Duration (minutes)")); 155 | } 156 | if(val > 0) { 157 | togglePause(val); 158 | } else { 159 | $(this).val(0); 160 | } 161 | } 162 | 163 | function togglePause(duration) { 164 | if (paused) { 165 | var mode = 'resume'; 166 | var wasPaused = true; 167 | } else { 168 | var mode = 'pause'; 169 | var wasPaused = false; 170 | } 171 | 172 | var sabApiUrl = constructApiUrl(); 173 | var data = constructApiPost(); 174 | 175 | data.mode = mode; 176 | if(mode == "pause" && typeof duration == "number") { 177 | data.mode = "config"; 178 | data.name = "set_pause"; 179 | data.value = duration; 180 | } 181 | 182 | $.ajax({ 183 | type: "GET", 184 | url: sabApiUrl, 185 | data: data, 186 | success: function(data) { 187 | if (wasPaused) { 188 | var msg = 'Pause Queue'; 189 | } else { 190 | var msg = 'Resume Queue'; 191 | } 192 | $('#togglePause').replaceWith(buildPauseDiv(msg, !wasPaused)); 193 | 194 | refresh(); 195 | }, 196 | error: function() { 197 | $('#togglePause').html('failed - try again'); 198 | } 199 | }); 200 | } 201 | 202 | function SetupTogglePause() { 203 | paused = getPref('paused'); 204 | 205 | if (paused) { 206 | var playImg = chrome.runtime.getURL('images/control_play.png'); 207 | var img = ''; 208 | var msg = 'Resume Queue'; 209 | } else { 210 | var pauseImg = chrome.runtime.getURL('images/control_pause.png'); 211 | var img = ''; 212 | var msg = 'Pause Queue'; 213 | } 214 | 215 | $(".menu").prepend("
", buildPauseDiv(msg)); 216 | 217 | $(".menu").on("click", "select", function(e) { e.stopPropagation(); }); 218 | $(".menu").on("change", "#pause-duration", durationPause); 219 | $(".menu").on("click", "#togglePause", togglePause); 220 | } 221 | 222 | function buildPauseDiv(msg, overridePaused) { 223 | var pauseState = overridePaused; 224 | if(typeof pauseState == "undefined") { 225 | pauseState = getPref('paused'); 226 | } 227 | var $div = $("
"+ msg +" "); 231 | var durations = { 232 | 0: "∞", 233 | 5: "5 minutes", 234 | 15: "15 minutes", 235 | 30: "30 minutes", 236 | 60: "1 hour", 237 | 180: "3 hours", 238 | 360: "6 hours", 239 | NaN: "Other..." 240 | } 241 | for(var minutes in durations) { 242 | var intMinutes = parseInt(minutes); 243 | selectDuration.append($("")); 244 | } 245 | 246 | $div.append(selectDuration); 247 | } 248 | 249 | return $div; 250 | } 251 | 252 | function getSortItemPos(id) { 253 | var list = $('ul#sab-queue').sortable('toArray'); 254 | var pos = -1; 255 | 256 | $.each(list, function(i, item) { 257 | if(item == id) { 258 | pos = i; 259 | } 260 | }); 261 | 262 | return pos; 263 | } 264 | 265 | function reDrawPopup() { 266 | // If we do not want to redraw (such as in the middle of a drag and drop event, then skip 267 | if(getPref('skip_redraw') == '1') return; 268 | 269 | var error = getPref('error'); 270 | if(error) { 271 | $('#sab-errors').html('
' + error + '
'); 272 | } else { 273 | // No errors, remove anything that could have been here 274 | // If the refresh rate is too low, will cause no errors to be ever seen 275 | $('#sab-errors').html(''); 276 | } 277 | 278 | // Make sure the current queue is clear 279 | $('ul#sab-queue').html(''); 280 | 281 | paused = getPref('paused'); 282 | 283 | var fields = ['status', 'paused', 'timeleft', 'speed', 'sizeleft', 'paused_jobs']; 284 | 285 | $.each(fields, function(i, field) { 286 | var value = getPref(field); 287 | $('#sab-' + field).html(value); 288 | }); 289 | 290 | var status = getPref('status'); 291 | $('#sab-status').removeClass().addClass(status); 292 | 293 | if(paused) { 294 | var remaining = getPref("pause_int"); 295 | if(remaining == 0) { //"0" 296 | $("#sab-timeleft").html("∞"); 297 | } else { 298 | $("#sab-timeleft").html(remaining); 299 | } 300 | } 301 | 302 | var data = { 303 | 'playImg':chrome.runtime.getURL('images/control_play.png'), 304 | 'pauseImg':chrome.runtime.getURL('images/control_pause.png'), 305 | 'deleteImg':chrome.runtime.getURL('images/control_cancel.png') 306 | }; 307 | 308 | // Grab a list of jobs (array of slot objects from the json API) 309 | var queue = getPref("queue"); 310 | var jobs = []; 311 | if(typeof queue != "undefined") 312 | jobs = JSON.parse(getPref('queue')); 313 | $.each(jobs, function(i, slot) { 314 | // Replaced jqote, which doesn't work in Chrome extensions, when using manifest v2. 315 | var el = '
  • ' 316 | + '
    ' + slot.filename + '
    ' 317 | + '
    '; 318 | if ( slot.status == "Paused" ) { 319 | el += ''; 320 | } else { 321 | el += ''; 322 | } 323 | el += '' 324 | + '
    ' 325 | + '
    '; 326 | if (slot.percentage != "0") { 327 | el += '
    '; 328 | } 329 | el += '
  • '; 330 | 331 | $(el).appendTo($('#sab-queue')); 332 | }); 333 | 334 | // The controls are low transparency until the user hovers over the parent item 335 | $(".item").hover( 336 | function () { 337 | // Restore opacity to full 338 | $(this).find('.lowOpacity').addClass('fullOpacity').removeClass('lowOpacity'); 339 | }, 340 | function () { 341 | // Set opacity to 20% 342 | $(this).find('.fullOpacity').addClass('lowOpacity').removeClass('fullOpacity'); 343 | } 344 | ); 345 | /* 346 | // The controls are low transparency until the user hovers over the parent item 347 | $(".filename").hover( 348 | function () { 349 | // Restore opacity to full 350 | $(this).closest('li').addClass('highlight') 351 | }, 352 | function () { 353 | // Set opacity to 20% 354 | $(this).closest('li').removeClass('highlight') 355 | } 356 | ); */ 357 | // The controls are low transparency until the user hovers over the parent item 358 | $(".item").hover( 359 | function () { 360 | // Restore opacity to full 361 | $(this).addClass('highlight'); 362 | }, 363 | function () { 364 | // Set opacity to 20% 365 | $(this).removeClass('highlight'); 366 | } 367 | ); 368 | 369 | // Make the ul sortable (only in the y axis) 370 | $("ul#sab-queue").sortable({ axis: 'y' }); 371 | $("ul#sab-queue").disableSelection(); 372 | 373 | // Cache the position when we start sorting 374 | $('ul#sab-queue').bind('sortstart', function(event, ui) { 375 | // Skip queue redrawing for the duration of the sort 376 | setPref('skip_redraw', 1); 377 | var id = $(ui.item).attr('id'); 378 | oldPos = getSortItemPos(id); 379 | }); 380 | 381 | // When the sorting has finished, do a SABnzbd api call 382 | $('ul#sab-queue').bind('sortstop', function(event, ui) { 383 | setPref('skip_redraw', 0); 384 | var id = $(ui.item).attr('id'); 385 | var pos = getSortItemPos(id); 386 | // Make sure it has actually moved position 387 | if(pos == oldPos) return; 388 | // Position has moved, send off a SABnzbd api call 389 | moveQueueItem(id, pos); 390 | }); 391 | 392 | // Do these need to be .live()? 393 | $('.resumeItem').click(function() { 394 | var id = $(this).closest('li.item').attr('id'); 395 | queueItemAction('resume', id, reDrawPopup); 396 | 397 | return false; 398 | }); 399 | 400 | $('.pauseItem').click(function() { 401 | var id = $(this).closest('li.item').attr('id'); 402 | queueItemAction('pause', id, reDrawPopup); 403 | 404 | return false; 405 | }); 406 | 407 | $('.deleteItem').click(function() { 408 | var li = $(this).closest('li.item'); 409 | var id = li.attr('id'); 410 | // Delete the li element (mainly for user feedback). If they choose to open the popup again 411 | // before the delete and redraw have taken place the item will show. Really needs removing from 'queue' object (preference) 412 | li.remove(); 413 | queueItemAction('delete', id, reDrawPopup); 414 | 415 | return false; 416 | }); 417 | 418 | if( store.get( 'config_enable_graph' ) == '1' ) { 419 | var speedlog = getPref('speedlog'); 420 | var line1 = [0]; 421 | if(typeof speedlog != "undefined") { 422 | line1 = JSON.parse(speedlog); 423 | } 424 | if (line1.sum() == 0 || status == 'Idle') { 425 | $('#graph').hide(); 426 | } else { 427 | $('#graph').html(''); 428 | $('#graph').show(); 429 | $('body').css({height: 'none'}); 430 | $('html').css({height: 'none'}); 431 | var plot1 = $.jqplot('graph', [line1], { 432 | seriesColors: ['#696'], 433 | axes: {yaxis: {min: 0}, xaxis: {min: 1, max: 10, numberTicks: 5, tickOptions: {showLabel: false}}}, 434 | seriesDefaults: { 435 | fill: true, 436 | fillAndStroke: true, 437 | fillColor: '#ADA', 438 | showMarker: false, 439 | pointLabels: { show: false } 440 | }, 441 | grid: { 442 | drawGridLines: false, 443 | gridLineColor: '#FFF', 444 | shadow: false, 445 | background: '#FFF', 446 | borderWidth: 0.2 447 | } 448 | }); 449 | } 450 | } else { 451 | // Graph is disabled 452 | $('#graph').hide(); 453 | } 454 | var newHeight = $('#sabInfo').height() + $('.menu').height() + 28; 455 | $('body').css({height: newHeight+'px'}); 456 | $('html').css({height: newHeight+'px'}); 457 | } 458 | 459 | Array.prototype.sum = function() { 460 | return this.reduce(function(a,b){return a+b;}); 461 | } 462 | 463 | function OnProfileChanged( event ) 464 | { 465 | var profileName = event.target.value; 466 | profiles.setActiveProfile( profileName ); 467 | 468 | var tabs = chrome.runtime.getViews( {type: 'tab'} ); 469 | for( var t in tabs ) { 470 | var tab = tabs[t]; 471 | if( tab.is_sabconnect_settings ) { 472 | tab.changeActiveProfile( profileName ); 473 | } 474 | } 475 | 476 | setMaxSpeedText(); 477 | refresh(); 478 | } 479 | 480 | function populateProfileList() 481 | { 482 | var profiles = store.get( 'profiles' ); 483 | for( var p in profiles ) { 484 | $('#profiles').append( 485 | $(''; 510 | $('#userCategory').append(cat); 511 | } 512 | $('#userCategory').val(store.get('active_category')); 513 | $('#userCategory').change(OnCategoryChanged); 514 | }); 515 | } 516 | -------------------------------------------------------------------------------- /scripts/pages/settings.js: -------------------------------------------------------------------------------- 1 | var store = new StoreClass('settings', undefined, undefined, storeReady_settings); 2 | 3 | function storeReady_settings() { 4 | new FancySettings.initWithManifest( InitializeSettings ); 5 | } 6 | 7 | var popup = null; 8 | var settings = null; 9 | 10 | // This variable serves as a way for the popup (and other pages in the future) 11 | // to determine if this Document object is the settings page. 12 | this.is_sabconnect_settings = true; 13 | 14 | var profileMissingErrorMsg = 15 | 'A connection profile exists in the popup but does not exist in localStorage for some reason. '+ 16 | 'Please file a bug at the SABconnect++ Google Code page if you see this message and explain '+ 17 | 'what you did to reproduce this error.' 18 | 19 | var ProfilePopup = new Class({ 20 | 'profiles': {}, 21 | 22 | 'initialize': function ( settings ) 23 | { 24 | this.settings = settings; 25 | }, 26 | 27 | 'add': function ( name ) 28 | { 29 | var opt = new Element('option', { 30 | 'id': name, 31 | 'text': name 32 | }); 33 | 34 | opt.inject(this.settings.manifest.profile_popup.element); 35 | this.profiles[name] = opt; 36 | }, 37 | 38 | 'remove': function ( name ) 39 | { 40 | this.profiles[name].dispose(); 41 | delete this.profiles[name]; 42 | }, 43 | 44 | 'rename': function( currentName, newName ) 45 | { 46 | var p = this.profiles[currentName]; 47 | p.set( 'id', newName ); 48 | p.set( 'text', newName ); 49 | 50 | delete this.profiles[currentName]; 51 | this.profiles[newName] = p; 52 | }, 53 | 54 | 'setSelection': function( name ) 55 | { 56 | this.settings.manifest.profile_popup.element.value = name; 57 | }, 58 | 59 | 'getSelection': function() 60 | { 61 | return this.settings.manifest.profile_popup.element.value; 62 | } 63 | }); 64 | 65 | function checkForErrors() 66 | { 67 | // Called after a request to SABnzbd's api has been tried, 'error' should be set if there was an error 68 | var error = getPref('error'); 69 | if(error) { 70 | $('connection-status') 71 | .set( 'class', 'connection-status-failure' ) 72 | .set( 'html', 'Failed' ) 73 | ; 74 | } else { 75 | $('connection-status') 76 | .set( 'class', 'connection-status-success' ) 77 | .set( 'html', 'Succeeded' ) 78 | ; 79 | } 80 | 81 | // Unsetting error here as otherwise would appear in popup - could confuse the user 82 | // Could move this just so it is removed when connected successfully 83 | setPref('error', ''); 84 | } 85 | 86 | function OnTestConnectionClicked() 87 | { 88 | $('connection-status') 89 | .set( 'class', '' ) 90 | .set( 'html', 'Running...' ) 91 | ; 92 | 93 | background().testConnection( getConnectionValues(), checkForErrors ); 94 | } 95 | 96 | function RefreshControlStates( settings ) 97 | { 98 | for( var name in settings.manifest ) { 99 | var setting = settings.manifest[name]; 100 | if( typeOf( setting.set ) === "function" ) { 101 | setting.set( store.get( setting.params.name ) ); 102 | } 103 | } 104 | } 105 | 106 | function OnResetConfigClicked( settings ) 107 | { 108 | background().resetSettings(); 109 | RefreshControlStates( settings ); 110 | } 111 | 112 | function OnRefreshRateChanged() 113 | { 114 | background().restartTimer(); 115 | } 116 | 117 | function CreateTestConnectionStatusElement( settings ) 118 | { 119 | var resultDiv = new Element( 'div', { 120 | id: 'connection-status' 121 | }); 122 | 123 | resultDiv.inject( settings.manifest.test_connection.container, 'bottom' ); 124 | } 125 | 126 | function OnToggleContextMenu() 127 | { 128 | background().SetupContextMenu() 129 | } 130 | 131 | function NotifyTabRefresh() 132 | { 133 | chrome.windows.getAll( {populate: true}, function( windows ) { 134 | Array.each( windows, function( window ) { 135 | Array.each( window.tabs, function( tab ) { 136 | chrome.tabs.sendMessage( tab.id, { action: 'refresh_settings' } ); 137 | }); 138 | }); 139 | }); 140 | } 141 | 142 | function RegisterContentScriptNotifyHandlers( settings ) 143 | { 144 | Object.each( settings.manifest, function( setting ) { 145 | if( setting.params.type !== 'button' ) { 146 | setting.addEvent( 'action', NotifyTabRefresh ); 147 | } 148 | }); 149 | } 150 | 151 | function SetupConnectionProfiles( settings ) 152 | { 153 | popup = new ProfilePopup( settings ); 154 | 155 | var profileNames = store.get( 'profiles' ); 156 | for( var p in profileNames ) { 157 | popup.add( p ); 158 | } 159 | 160 | changeActiveProfile( profiles.getActiveProfile().name ); 161 | } 162 | 163 | function getConnectionValues() 164 | { 165 | return { 166 | 'url': settings.manifest.sabnzbd_url.get(), 167 | 'api_key': settings.manifest.sabnzbd_api_key.get(), 168 | 'username': settings.manifest.sabnzbd_username.get(), 169 | 'password': settings.manifest.sabnzbd_password.get() 170 | }; 171 | } 172 | 173 | function setConnectionValues( profileName, url, api_key, username, password ) 174 | { 175 | settings.manifest.profile_name.set( profileName ); 176 | settings.manifest.sabnzbd_url.set( url, true ); 177 | settings.manifest.sabnzbd_api_key.set( api_key, true ); 178 | settings.manifest.sabnzbd_username.set( username, true ); 179 | settings.manifest.sabnzbd_password.set( password, true ); 180 | } 181 | 182 | function generateUniqueName( name ) 183 | { 184 | var newName = name; 185 | var counter = 1; 186 | 187 | while( profiles.contains( newName ) ) { 188 | newName = name + counter++; 189 | } 190 | 191 | return newName; 192 | } 193 | 194 | function OnCreateProfileClicked() 195 | { 196 | try { 197 | var name = generateUniqueName( 'New Profile' ); 198 | 199 | setConnectionValues( name, '', '', '', '' ); 200 | profiles.add( name, getConnectionValues() ); 201 | profiles.setActiveProfile( name ); 202 | 203 | popup.add( name ); 204 | popup.setSelection( name ); 205 | } 206 | catch( e ) { 207 | throw e; 208 | } 209 | } 210 | 211 | function OnDuplicateProfileClicked() 212 | { 213 | try { 214 | var activeProfile = profiles.getActiveProfile(); 215 | var name = generateUniqueName( activeProfile.name ); 216 | 217 | var values = activeProfile.values; 218 | setConnectionValues( name, values.url, values.api_key, values.username, values.password ); 219 | profiles.add( name, activeProfile.values ); 220 | profiles.setActiveProfile( name ); 221 | 222 | popup.add( name ); 223 | popup.setSelection( name ); 224 | } 225 | catch( e ) { 226 | throw e; 227 | } 228 | } 229 | 230 | function OnDeleteProfileClicked() 231 | { 232 | try { 233 | var selectedProfile = popup.getSelection(); 234 | popup.remove( selectedProfile ); 235 | 236 | var newActive = profiles.remove( selectedProfile ); 237 | if( newActive ) { 238 | changeActiveProfile( newActive ); 239 | } 240 | } 241 | catch( e ) { 242 | if( e == 'profile_missing' ) { 243 | alert( profileMissingErrorMsg ); 244 | } 245 | else { 246 | throw e; 247 | } 248 | } 249 | } 250 | 251 | function changeActiveProfile( profileName ) 252 | { 253 | profiles.setActiveProfile( profileName ); 254 | popup.setSelection( profileName ); 255 | 256 | var profile = profiles.getActiveProfile().values; 257 | if( profile) { 258 | setConnectionValues( profileName, profile.url, profile.api_key, profile.username, profile.password ); 259 | } 260 | } 261 | 262 | function OnProfileChanged( profileName ) 263 | { 264 | changeActiveProfile( profileName ); 265 | } 266 | 267 | function OnConnectionFieldEdited( fieldName, value ) 268 | { 269 | var profile = profiles.getActiveProfile(); 270 | profile.values[fieldName] = value; 271 | profiles.setProfile( profile ); 272 | } 273 | 274 | function OnProfileNameChanged( value ) 275 | { 276 | var profileName = profiles.getActiveProfile().name; 277 | var newProfileName = settings.manifest.profile_name.get(); 278 | if( profileName != newProfileName ) { 279 | popup.rename( profileName, newProfileName ); 280 | profiles.edit( profileName, getConnectionValues(), newProfileName ); 281 | profiles.setActiveProfile( newProfileName ); 282 | } 283 | } 284 | 285 | function AddProfileButtons( settings ) 286 | { 287 | var m = settings.manifest; 288 | m.profile_create.bundle.inject( m.profile_popup.bundle ); 289 | m.profile_duplicate.bundle.inject( m.profile_popup.bundle ); 290 | m.profile_delete.bundle.inject( m.profile_popup.bundle ); 291 | 292 | m.profile_popup.container.setStyle( 'display', 'inline-block' ); 293 | m.profile_popup.container.setStyle( 'margin-right', '10'); 294 | m.profile_popup.element.setStyle( 'width', '150'); 295 | m.profile_create.bundle.setStyle( 'display', 'inline-block'); 296 | m.profile_duplicate.bundle.setStyle( 'display', 'inline-block'); 297 | m.profile_delete.bundle.setStyle( 'display', 'inline-block'); 298 | 299 | m.profile_create.addEvent( 'action', OnCreateProfileClicked ); 300 | m.profile_duplicate.addEvent( 'action', OnDuplicateProfileClicked ); 301 | m.profile_delete.addEvent( 'action', OnDeleteProfileClicked ); 302 | 303 | m.sabnzbd_url.addEvent( 'action', function(v) { OnConnectionFieldEdited( 'url', v ) } ); 304 | m.sabnzbd_api_key.addEvent( 'action', function(v) { OnConnectionFieldEdited( 'api_key', v ) } ); 305 | m.sabnzbd_username.addEvent( 'action', function(v) { OnConnectionFieldEdited( 'username', v ) } ); 306 | m.sabnzbd_password.addEvent( 'action', function(v) { OnConnectionFieldEdited( 'password', v ) } ); 307 | 308 | m.profile_name.element.addEvent( 'blur', OnProfileNameChanged ); 309 | } 310 | 311 | function InitializeSettings( settings ) 312 | { 313 | this.settings = settings; 314 | 315 | settings.manifest.config_reset.addEvent( 'action', OnResetConfigClicked ); 316 | settings.manifest.test_connection.addEvent( 'action', OnTestConnectionClicked ); 317 | settings.manifest.config_refresh_rate.addEvent( 'action', OnRefreshRateChanged ); 318 | settings.manifest.config_enable_context_menu.addEvent( 'action', OnToggleContextMenu ); 319 | settings.manifest.profile_popup.addEvent( 'action', OnProfileChanged ); 320 | 321 | CreateTestConnectionStatusElement( settings ); 322 | SetupConnectionProfiles( settings ); 323 | AddProfileButtons( settings ); 324 | RegisterContentScriptNotifyHandlers( settings ); 325 | } 326 | 327 | window.onbeforeunload = function() { 328 | store.queueCommit(); 329 | var profile_name = settings.manifest.profile_name.get(); 330 | if( profiles.getActiveProfile().name !== profile_name ) { 331 | var msg = 332 | 'You have made changes to the active profile\'s name, ' + 333 | 'but it has not been saved. Click out of the "Profile Name" ' + 334 | 'text field to save the changes. You may also leave the ' + 335 | 'options page now to discard those changes.' 336 | ; 337 | 338 | return msg; 339 | } 340 | }; -------------------------------------------------------------------------------- /scripts/profile.js: -------------------------------------------------------------------------------- 1 | function ProfileManager() 2 | { 3 | } 4 | 5 | ProfileManager.prototype.count = function() 6 | { 7 | return store.get( 'profiles' ).length; 8 | } 9 | 10 | ProfileManager.prototype.add = function( profileName, values ) 11 | { 12 | var profiles = store.get( 'profiles' ); 13 | if(typeof profiles == "object" && profiles.hasOwnProperty( profileName ) ) { 14 | throw 'already_exists'; 15 | } 16 | 17 | profiles[profileName] = values; 18 | this.saveProfiles(profiles); 19 | } 20 | 21 | ProfileManager.prototype.edit = function( profileName, values, newProfileName ) 22 | { 23 | var profiles = store.get( 'profiles' ); 24 | 25 | if( !profiles[profileName] ) { 26 | throw 'profile_missing'; 27 | } 28 | 29 | if( profileName != newProfileName ) { 30 | if( profiles.hasOwnProperty( newProfileName ) ) { 31 | throw 'renamed_exists'; 32 | } 33 | 34 | delete profiles[profileName]; 35 | profileName = newProfileName; 36 | } 37 | 38 | profiles[profileName] = values; 39 | this.saveProfiles(profiles); 40 | } 41 | 42 | ProfileManager.prototype.remove = function( profileName ) 43 | { 44 | var profiles = store.get( 'profiles' ); 45 | if( !profiles.hasOwnProperty( profileName ) ) { 46 | throw 'profile_missing'; 47 | } 48 | 49 | delete profiles[profileName]; 50 | this.saveProfiles(profiles); 51 | 52 | var newActive = first( profiles ); 53 | this.setActiveProfile( newActive ); 54 | return newActive; 55 | } 56 | 57 | ProfileManager.prototype.setProfile = function( profileData ) 58 | { 59 | var profiles = store.get( 'profiles' ); 60 | profiles[profileData.name] = profileData.values; 61 | this.saveProfiles(profiles); 62 | } 63 | 64 | ProfileManager.prototype.getProfile = function( profileName ) 65 | { 66 | if( !profileName ) { 67 | return null; 68 | } 69 | 70 | var profiles = store.get( 'profiles' ); 71 | var profile = profiles[profileName]; 72 | 73 | if( !profile ) { 74 | return null; 75 | } 76 | 77 | var password = getPref("profile_pass" + profileName); 78 | if(password === null || password === "null" || typeof password == "undefined") { 79 | password = ""; 80 | } 81 | profiles[profileName]["password"] = password; 82 | 83 | return { 84 | 'name': profileName, 85 | 'values': profiles[profileName] 86 | }; 87 | } 88 | 89 | ProfileManager.prototype.getActiveProfile = function() 90 | { 91 | var profileName = getPref( 'active_profile' ); 92 | return this.getProfile( profileName ); 93 | } 94 | 95 | ProfileManager.prototype.getFirstProfile = function() 96 | { 97 | var profileName = first( store.get( 'profiles' ) ); 98 | return this.getProfile( profileName ); 99 | } 100 | 101 | ProfileManager.prototype.setActiveProfile = function( profileName ) 102 | { 103 | setPref( 'active_profile', profileName ); 104 | } 105 | 106 | ProfileManager.prototype.contains = function( profileName ) 107 | { 108 | var profiles = store.get( 'profiles' ); 109 | return profiles.hasOwnProperty( profileName ); 110 | } 111 | 112 | ProfileManager.prototype.saveProfiles = function(profiles) { 113 | //discard passwords 114 | for(var profileName in profiles) { 115 | var profile = profiles[profileName]; 116 | if(profile.hasOwnProperty("password")) { 117 | setPref("profile_pass" + profileName, profile.password); 118 | delete profile.password; 119 | } 120 | } 121 | store.set( 'profiles', profiles ); 122 | } -------------------------------------------------------------------------------- /scripts/utility.js: -------------------------------------------------------------------------------- 1 | /// Gets the first item in the object. This is 2 | /// particularly useful for associative arrays. 3 | /// @return Return value will be 'undefined' if 4 | /// the object is empty. 5 | function first( object ) 6 | { 7 | for( var obj in object ) { 8 | return obj; 9 | } 10 | 11 | return undefined; 12 | } -------------------------------------------------------------------------------- /settings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 36 |
    37 |
    38 |

    39 |
    40 |
    41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /third_party/jqplot/jqplot.pointLabels.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jqPlot 3 | * Pure JavaScript plotting plugin using jQuery 4 | * 5 | * Version: 1.0.9 6 | * Revision: d96a669 7 | * 8 | * Copyright (c) 2009-2016 Chris Leonello 9 | * jqPlot is currently available for use in all personal or commercial projects 10 | * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 11 | * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 12 | * choose the license that best suits your project and use it accordingly. 13 | * 14 | * Although not required, the author would appreciate an email letting him 15 | * know of any substantial use of jqPlot. You can reach the author at: 16 | * chris at jqplot dot com or see http://www.jqplot.com/info.php . 17 | * 18 | * If you are feeling kind and generous, consider supporting the project by 19 | * making a donation at: http://www.jqplot.com/donate.php . 20 | * 21 | * sprintf functions contained in jqplot.sprintf.js by Ash Searle: 22 | * 23 | * version 2007.04.27 24 | * author Ash Searle 25 | * http://hexmen.com/blog/2007/03/printf-sprintf/ 26 | * http://hexmen.com/js/sprintf.js 27 | * The author (Ash Searle) has placed this code in the public domain: 28 | * "This code is unrestricted: you are free to use it however you like." 29 | * 30 | */ 31 | (function($) { 32 | 33 | /** 34 | * Class: $.jqplot.PointLabels 35 | * Plugin for putting labels at the data points. 36 | * 37 | * To use this plugin, include the js 38 | * file in your source: 39 | * 40 | * > 41 | * 42 | * By default, the last value in the data ponit array in the data series is used 43 | * for the label. For most series renderers, extra data can be added to the 44 | * data point arrays and the last value will be used as the label. 45 | * 46 | * For instance, 47 | * this series: 48 | * 49 | * > [[1,4], [3,5], [7,2]] 50 | * 51 | * Would, by default, use the y values in the labels. 52 | * Extra data can be added to the series like so: 53 | * 54 | * > [[1,4,'mid'], [3 5,'hi'], [7,2,'low']] 55 | * 56 | * And now the point labels would be 'mid', 'low', and 'hi'. 57 | * 58 | * Options to the point labels and a custom labels array can be passed into the 59 | * "pointLabels" option on the series option like so: 60 | * 61 | * > series:[{pointLabels:{ 62 | * > labels:['mid', 'hi', 'low'], 63 | * > location:'se', 64 | * > ypadding: 12 65 | * > } 66 | * > }] 67 | * 68 | * A custom labels array in the options takes precendence over any labels 69 | * in the series data. If you have a custom labels array in the options, 70 | * but still want to use values from the series array as labels, set the 71 | * "labelsFromSeries" option to true. 72 | * 73 | * By default, html entities (<, >, etc.) are escaped in point labels. 74 | * If you want to include actual html markup in the labels, 75 | * set the "escapeHTML" option to false. 76 | * 77 | */ 78 | $.jqplot.PointLabels = function(options) { 79 | // Group: Properties 80 | // 81 | // prop: show 82 | // show the labels or not. 83 | this.show = $.jqplot.config.enablePlugins; 84 | // prop: location 85 | // compass location where to position the label around the point. 86 | // 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw' 87 | this.location = 'n'; 88 | // prop: labelsFromSeries 89 | // true to use labels within data point arrays. 90 | this.labelsFromSeries = false; 91 | // prop: seriesLabelIndex 92 | // array index for location of labels within data point arrays. 93 | // if null, will use the last element of the data point array. 94 | this.seriesLabelIndex = null; 95 | // prop: labels 96 | // array of arrays of labels, one array for each series. 97 | this.labels = []; 98 | // actual labels that will get displayed. 99 | // needed to preserve user specified labels in labels array. 100 | this._labels = []; 101 | // prop: stackedValue 102 | // true to display value as stacked in a stacked plot. 103 | // no effect if labels is specified. 104 | this.stackedValue = false; 105 | // prop: ypadding 106 | // vertical padding in pixels between point and label 107 | this.ypadding = 6; 108 | // prop: xpadding 109 | // horizontal padding in pixels between point and label 110 | this.xpadding = 6; 111 | // prop: escapeHTML 112 | // true to escape html entities in the labels. 113 | // If you want to include markup in the labels, set to false. 114 | this.escapeHTML = true; 115 | // prop: edgeTolerance 116 | // Number of pixels that the label must be away from an axis 117 | // boundary in order to be drawn. Negative values will allow overlap 118 | // with the grid boundaries. 119 | this.edgeTolerance = -5; 120 | // prop: formatter 121 | // A class of a formatter for the tick text. sprintf by default. 122 | this.formatter = $.jqplot.DefaultTickFormatter; 123 | // prop: formatString 124 | // string passed to the formatter. 125 | this.formatString = ''; 126 | // prop: hideZeros 127 | // true to not show a label for a value which is 0. 128 | this.hideZeros = false; 129 | this._elems = []; 130 | 131 | $.extend(true, this, options); 132 | }; 133 | 134 | var locations = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w']; 135 | var locationIndicies = {'nw':0, 'n':1, 'ne':2, 'e':3, 'se':4, 's':5, 'sw':6, 'w':7}; 136 | var oppositeLocations = ['se', 's', 'sw', 'w', 'nw', 'n', 'ne', 'e']; 137 | 138 | // called with scope of a series 139 | $.jqplot.PointLabels.init = function (target, data, seriesDefaults, opts, plot){ 140 | var options = $.extend(true, {}, seriesDefaults, opts); 141 | options.pointLabels = options.pointLabels || {}; 142 | if (this.renderer.constructor === $.jqplot.BarRenderer && this.barDirection === 'horizontal' && !options.pointLabels.location) { 143 | options.pointLabels.location = 'e'; 144 | } 145 | // add a pointLabels attribute to the series plugins 146 | this.plugins.pointLabels = new $.jqplot.PointLabels(options.pointLabels); 147 | this.plugins.pointLabels.setLabels.call(this); 148 | }; 149 | 150 | // called with scope of series 151 | $.jqplot.PointLabels.prototype.setLabels = function() { 152 | var p = this.plugins.pointLabels; 153 | var labelIdx; 154 | if (p.seriesLabelIndex != null) { 155 | labelIdx = p.seriesLabelIndex; 156 | } 157 | else if (this.renderer.constructor === $.jqplot.BarRenderer && this.barDirection === 'horizontal') { 158 | labelIdx = (this._plotData[0].length < 3) ? 0 : this._plotData[0].length -1; 159 | } 160 | else { 161 | labelIdx = (this._plotData.length === 0) ? 0 : this._plotData[0].length -1; 162 | } 163 | p._labels = []; 164 | if (p.labels.length === 0 || p.labelsFromSeries) { 165 | if (p.stackedValue) { 166 | if (this._plotData.length && this._plotData[0].length){ 167 | // var idx = p.seriesLabelIndex || this._plotData[0].length -1; 168 | for (var i=0; i scr || elb + et > scb) { 360 | elem.remove(); 361 | } 362 | 363 | elem = null; 364 | helem = null; 365 | } 366 | 367 | // finally, animate them if the series is animated 368 | // if (this.renderer.animation && this.renderer.animation._supported && this.renderer.animation.show && plot._drawCount < 2) { 369 | // var sel = '.jqplot-point-label.jqplot-series-'+this.index; 370 | // $(sel).hide(); 371 | // $(sel).fadeIn(1000); 372 | // } 373 | 374 | } 375 | }; 376 | 377 | $.jqplot.postSeriesInitHooks.push($.jqplot.PointLabels.init); 378 | $.jqplot.postDrawSeriesHooks.push($.jqplot.PointLabels.draw); 379 | })(jQuery); 380 | -------------------------------------------------------------------------------- /third_party/jqplot/jquery.jqplot.css: -------------------------------------------------------------------------------- 1 | /*rules for the plot target div. These will be cascaded down to all plot elements according to css rules*/ 2 | .jqplot-target { 3 | position: relative; 4 | color: #666666; 5 | font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; 6 | font-size: 1em; 7 | /* height: 300px; 8 | width: 400px;*/ 9 | } 10 | 11 | /*rules applied to all axes*/ 12 | .jqplot-axis { 13 | font-size: 0.75em; 14 | } 15 | 16 | .jqplot-xaxis { 17 | margin-top: 10px; 18 | } 19 | 20 | .jqplot-x2axis { 21 | margin-bottom: 10px; 22 | } 23 | 24 | .jqplot-yaxis { 25 | margin-right: 10px; 26 | } 27 | 28 | .jqplot-y2axis, .jqplot-y3axis, .jqplot-y4axis, .jqplot-y5axis, .jqplot-y6axis, .jqplot-y7axis, .jqplot-y8axis, .jqplot-y9axis, .jqplot-yMidAxis { 29 | margin-left: 10px; 30 | margin-right: 10px; 31 | } 32 | 33 | /*rules applied to all axis tick divs*/ 34 | .jqplot-axis-tick, .jqplot-xaxis-tick, .jqplot-yaxis-tick, .jqplot-x2axis-tick, .jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick, .jqplot-yMidAxis-tick { 35 | position: absolute; 36 | white-space: pre; 37 | } 38 | 39 | 40 | .jqplot-xaxis-tick { 41 | top: 0px; 42 | /* initial position untill tick is drawn in proper place */ 43 | left: 15px; 44 | /* padding-top: 10px;*/ 45 | vertical-align: top; 46 | } 47 | 48 | .jqplot-x2axis-tick { 49 | bottom: 0px; 50 | /* initial position untill tick is drawn in proper place */ 51 | left: 15px; 52 | /* padding-bottom: 10px;*/ 53 | vertical-align: bottom; 54 | } 55 | 56 | .jqplot-yaxis-tick { 57 | right: 0px; 58 | /* initial position untill tick is drawn in proper place */ 59 | top: 15px; 60 | /* padding-right: 10px;*/ 61 | text-align: right; 62 | } 63 | 64 | .jqplot-yaxis-tick.jqplot-breakTick { 65 | right: -20px; 66 | margin-right: 0px; 67 | padding:1px 5px 1px 5px; 68 | /*background-color: white;*/ 69 | z-index: 2; 70 | font-size: 1.5em; 71 | } 72 | 73 | .jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick { 74 | left: 0px; 75 | /* initial position untill tick is drawn in proper place */ 76 | top: 15px; 77 | /* padding-left: 10px;*/ 78 | /* padding-right: 15px;*/ 79 | text-align: left; 80 | } 81 | 82 | .jqplot-yMidAxis-tick { 83 | text-align: center; 84 | white-space: nowrap; 85 | } 86 | 87 | .jqplot-xaxis-label { 88 | margin-top: 10px; 89 | font-size: 11pt; 90 | position: absolute; 91 | } 92 | 93 | .jqplot-x2axis-label { 94 | margin-bottom: 10px; 95 | font-size: 11pt; 96 | position: absolute; 97 | } 98 | 99 | .jqplot-yaxis-label { 100 | margin-right: 10px; 101 | /* text-align: center;*/ 102 | font-size: 11pt; 103 | position: absolute; 104 | } 105 | 106 | .jqplot-yMidAxis-label { 107 | font-size: 11pt; 108 | position: absolute; 109 | } 110 | 111 | .jqplot-y2axis-label, .jqplot-y3axis-label, .jqplot-y4axis-label, .jqplot-y5axis-label, .jqplot-y6axis-label, .jqplot-y7axis-label, .jqplot-y8axis-label, .jqplot-y9axis-label { 112 | /* text-align: center;*/ 113 | font-size: 11pt; 114 | margin-left: 10px; 115 | position: absolute; 116 | } 117 | 118 | .jqplot-meterGauge-tick { 119 | font-size: 0.75em; 120 | color: #999999; 121 | } 122 | 123 | .jqplot-meterGauge-label { 124 | font-size: 1em; 125 | color: #999999; 126 | } 127 | 128 | table.jqplot-table-legend { 129 | margin-top: 12px; 130 | margin-bottom: 12px; 131 | margin-left: 12px; 132 | margin-right: 12px; 133 | } 134 | 135 | table.jqplot-table-legend, table.jqplot-cursor-legend { 136 | background-color: rgba(255,255,255,0.6); 137 | border: 1px solid #cccccc; 138 | position: absolute; 139 | font-size: 0.75em; 140 | } 141 | 142 | td.jqplot-table-legend { 143 | vertical-align:middle; 144 | } 145 | 146 | /* 147 | These rules could be used instead of assigning 148 | element styles and relying on js object properties. 149 | */ 150 | 151 | /* 152 | td.jqplot-table-legend-swatch { 153 | padding-top: 0.5em; 154 | text-align: center; 155 | } 156 | 157 | tr.jqplot-table-legend:first td.jqplot-table-legend-swatch { 158 | padding-top: 0px; 159 | } 160 | */ 161 | 162 | td.jqplot-seriesToggle:hover, td.jqplot-seriesToggle:active { 163 | cursor: pointer; 164 | } 165 | 166 | .jqplot-table-legend .jqplot-series-hidden { 167 | text-decoration: line-through; 168 | } 169 | 170 | div.jqplot-table-legend-swatch-outline { 171 | border: 1px solid #cccccc; 172 | padding:1px; 173 | } 174 | 175 | div.jqplot-table-legend-swatch { 176 | width:0px; 177 | height:0px; 178 | border-top-width: 5px; 179 | border-bottom-width: 5px; 180 | border-left-width: 6px; 181 | border-right-width: 6px; 182 | border-top-style: solid; 183 | border-bottom-style: solid; 184 | border-left-style: solid; 185 | border-right-style: solid; 186 | } 187 | 188 | .jqplot-title { 189 | top: 0px; 190 | left: 0px; 191 | padding-bottom: 0.5em; 192 | font-size: 1.2em; 193 | } 194 | 195 | table.jqplot-cursor-tooltip { 196 | border: 1px solid #cccccc; 197 | font-size: 0.75em; 198 | } 199 | 200 | 201 | .jqplot-cursor-tooltip { 202 | border: 1px solid #cccccc; 203 | font-size: 0.75em; 204 | white-space: nowrap; 205 | background: rgba(208,208,208,0.5); 206 | padding: 1px; 207 | } 208 | 209 | .jqplot-highlighter-tooltip, .jqplot-canvasOverlay-tooltip { 210 | border: 1px solid #cccccc; 211 | font-size: 0.75em; 212 | white-space: nowrap; 213 | background: rgba(208,208,208,0.5); 214 | padding: 1px; 215 | } 216 | 217 | .jqplot-point-label { 218 | font-size: 0.75em; 219 | z-index: 2; 220 | } 221 | 222 | td.jqplot-cursor-legend-swatch { 223 | vertical-align: middle; 224 | text-align: center; 225 | } 226 | 227 | div.jqplot-cursor-legend-swatch { 228 | width: 1.2em; 229 | height: 0.7em; 230 | } 231 | 232 | .jqplot-error { 233 | /* Styles added to the plot target container when there is an error go here.*/ 234 | text-align: center; 235 | } 236 | 237 | .jqplot-error-message { 238 | /* Styling of the custom error message div goes here.*/ 239 | position: relative; 240 | top: 46%; 241 | display: inline-block; 242 | } 243 | 244 | div.jqplot-bubble-label { 245 | font-size: 0.8em; 246 | /* background: rgba(90%, 90%, 90%, 0.15);*/ 247 | padding-left: 2px; 248 | padding-right: 2px; 249 | color: rgb(20%, 20%, 20%); 250 | } 251 | 252 | div.jqplot-bubble-label.jqplot-bubble-label-highlight { 253 | background: rgba(90%, 90%, 90%, 0.7); 254 | } 255 | 256 | div.jqplot-noData-container { 257 | text-align: center; 258 | background-color: rgba(96%, 96%, 96%, 0.3); 259 | } 260 | -------------------------------------------------------------------------------- /third_party/jquery/jquery-ui-1.8.20.sortable.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.8.20 - 2012-04-30 2 | * https://github.com/jquery/jquery-ui 3 | * Includes: jquery.ui.core.js 4 | * Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */ 5 | (function(a,b){function c(b,c){var e=b.nodeName.toLowerCase();if("area"===e){var f=b.parentNode,g=f.name,h;return!b.href||!g||f.nodeName.toLowerCase()!=="map"?!1:(h=a("img[usemap=#"+g+"]")[0],!!h&&d(h))}return(/input|select|textarea|button|object/.test(e)?!b.disabled:"a"==e?b.href||c:c)&&d(b)}function d(b){return!a(b).parents().andSelf().filter(function(){return a.curCSS(this,"visibility")==="hidden"||a.expr.filters.hidden(this)}).length}a.ui=a.ui||{};if(a.ui.version)return;a.extend(a.ui,{version:"1.8.20",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}}),a.fn.extend({propAttr:a.fn.prop||a.fn.attr,_focus:a.fn.focus,focus:function(b,c){return typeof b=="number"?this.each(function(){var d=this;setTimeout(function(){a(d).focus(),c&&c.call(d)},b)}):this._focus.apply(this,arguments)},scrollParent:function(){var b;return a.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?b=this.parents().filter(function(){return/(relative|absolute|fixed)/.test(a.curCSS(this,"position",1))&&/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0):b=this.parents().filter(function(){return/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0),/fixed/.test(this.css("position"))||!b.length?a(document):b},zIndex:function(c){if(c!==b)return this.css("zIndex",c);if(this.length){var d=a(this[0]),e,f;while(d.length&&d[0]!==document){e=d.css("position");if(e==="absolute"||e==="relative"||e==="fixed"){f=parseInt(d.css("zIndex"),10);if(!isNaN(f)&&f!==0)return f}d=d.parent()}}return 0},disableSelection:function(){return this.bind((a.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),a.each(["Width","Height"],function(c,d){function h(b,c,d,f){return a.each(e,function(){c-=parseFloat(a.curCSS(b,"padding"+this,!0))||0,d&&(c-=parseFloat(a.curCSS(b,"border"+this+"Width",!0))||0),f&&(c-=parseFloat(a.curCSS(b,"margin"+this,!0))||0)}),c}var e=d==="Width"?["Left","Right"]:["Top","Bottom"],f=d.toLowerCase(),g={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};a.fn["inner"+d]=function(c){return c===b?g["inner"+d].call(this):this.each(function(){a(this).css(f,h(this,c)+"px")})},a.fn["outer"+d]=function(b,c){return typeof b!="number"?g["outer"+d].call(this,b):this.each(function(){a(this).css(f,h(this,b,!0,c)+"px")})}}),a.extend(a.expr[":"],{data:function(b,c,d){return!!a.data(b,d[3])},focusable:function(b){return c(b,!isNaN(a.attr(b,"tabindex")))},tabbable:function(b){var d=a.attr(b,"tabindex"),e=isNaN(d);return(e||d>=0)&&c(b,!e)}}),a(function(){var b=document.body,c=b.appendChild(c=document.createElement("div"));c.offsetHeight,a.extend(c.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0}),a.support.minHeight=c.offsetHeight===100,a.support.selectstart="onselectstart"in c,b.removeChild(c).style.display="none"}),a.extend(a.ui,{plugin:{add:function(b,c,d){var e=a.ui[b].prototype;for(var f in d)e.plugins[f]=e.plugins[f]||[],e.plugins[f].push([c,d[f]])},call:function(a,b,c){var d=a.plugins[b];if(!d||!a.element[0].parentNode)return;for(var e=0;e0?!0:(b[d]=1,e=b[d]>0,b[d]=0,e)},isOverAxis:function(a,b,c){return a>b&&a=9||!!b.button?this._mouseStarted?(this._mouseDrag(b),b.preventDefault()):(this._mouseDistanceMet(b)&&this._mouseDelayMet(b)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,b)!==!1,this._mouseStarted?this._mouseDrag(b):this._mouseUp(b)),!this._mouseStarted):this._mouseUp(b)},_mouseUp:function(b){return a(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,b.target==this._mouseDownEvent.target&&a.data(b.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(b)),!1},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(a){return this.mouseDelayMet},_mouseStart:function(a){},_mouseDrag:function(a){},_mouseStop:function(a){},_mouseCapture:function(a){return!0}})})(jQuery);;/*! jQuery UI - v1.8.20 - 2012-04-30 14 | * https://github.com/jquery/jquery-ui 15 | * Includes: jquery.ui.sortable.js 16 | * Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */ 17 | (function(a,b){a.widget("ui.sortable",a.ui.mouse,{widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3},_create:function(){var a=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?a.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):!1,this.offset=this.element.offset(),this._mouseInit(),this.ready=!0},destroy:function(){a.Widget.prototype.destroy.call(this),this.element.removeClass("ui-sortable ui-sortable-disabled"),this._mouseDestroy();for(var b=this.items.length-1;b>=0;b--)this.items[b].item.removeData(this.widgetName+"-item");return this},_setOption:function(b,c){b==="disabled"?(this.options[b]=c,this.widget()[c?"addClass":"removeClass"]("ui-sortable-disabled")):a.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(b,c){var d=this;if(this.reverting)return!1;if(this.options.disabled||this.options.type=="static")return!1;this._refreshItems(b);var e=null,f=this,g=a(b.target).parents().each(function(){if(a.data(this,d.widgetName+"-item")==f)return e=a(this),!1});a.data(b.target,d.widgetName+"-item")==f&&(e=a(b.target));if(!e)return!1;if(this.options.handle&&!c){var h=!1;a(this.options.handle,e).find("*").andSelf().each(function(){this==b.target&&(h=!0)});if(!h)return!1}return this.currentItem=e,this._removeCurrentsFromItems(),!0},_mouseStart:function(b,c,d){var e=this.options,f=this;this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(b),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),a.extend(this.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this._generatePosition(b),this.originalPageX=b.pageX,this.originalPageY=b.pageY,e.cursorAt&&this._adjustOffsetFromHelper(e.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!=this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),e.containment&&this._setContainment(),e.cursor&&(a("body").css("cursor")&&(this._storedCursor=a("body").css("cursor")),a("body").css("cursor",e.cursor)),e.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",e.opacity)),e.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",e.zIndex)),this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",b,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions();if(!d)for(var g=this.containers.length-1;g>=0;g--)this.containers[g]._trigger("activate",b,f._uiHash(this));return a.ui.ddmanager&&(a.ui.ddmanager.current=this),a.ui.ddmanager&&!e.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(b),!0},_mouseDrag:function(b){this.position=this._generatePosition(b),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs);if(this.options.scroll){var c=this.options,d=!1;this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-b.pageY=0;e--){var f=this.items[e],g=f.item[0],h=this._intersectsWithPointer(f);if(!h)continue;if(g!=this.currentItem[0]&&this.placeholder[h==1?"next":"prev"]()[0]!=g&&!a.ui.contains(this.placeholder[0],g)&&(this.options.type=="semi-dynamic"?!a.ui.contains(this.element[0],g):!0)){this.direction=h==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(f))this._rearrange(b,f);else break;this._trigger("change",b,this._uiHash());break}}return this._contactContainers(b),a.ui.ddmanager&&a.ui.ddmanager.drag(this,b),this._trigger("sort",b,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(b,c){if(!b)return;a.ui.ddmanager&&!this.options.dropBehaviour&&a.ui.ddmanager.drop(this,b);if(this.options.revert){var d=this,e=d.placeholder.offset();d.reverting=!0,a(this.helper).animate({left:e.left-this.offset.parent.left-d.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:e.top-this.offset.parent.top-d.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){d._clear(b)})}else this._clear(b,c);return!1},cancel:function(){var b=this;if(this.dragging){this._mouseUp({target:null}),this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("deactivate",null,b._uiHash(this)),this.containers[c].containerCache.over&&(this.containers[c]._trigger("out",null,b._uiHash(this)),this.containers[c].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),a.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?a(this.domPosition.prev).after(this.currentItem):a(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(b){var c=this._getItemsAsjQuery(b&&b.connected),d=[];return b=b||{},a(c).each(function(){var c=(a(b.item||this).attr(b.attribute||"id")||"").match(b.expression||/(.+)[-=_](.+)/);c&&d.push((b.key||c[1]+"[]")+"="+(b.key&&b.expression?c[1]:c[2]))}),!d.length&&b.key&&d.push(b.key+"="),d.join("&")},toArray:function(b){var c=this._getItemsAsjQuery(b&&b.connected),d=[];return b=b||{},c.each(function(){d.push(a(b.item||this).attr(b.attribute||"id")||"")}),d},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,d=this.positionAbs.top,e=d+this.helperProportions.height,f=a.left,g=f+a.width,h=a.top,i=h+a.height,j=this.offset.click.top,k=this.offset.click.left,l=d+j>h&&d+jf&&b+ka[this.floating?"width":"height"]?l:f0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){return this._refreshItems(a),this.refreshPositions(),this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(b){var c=this,d=[],e=[],f=this._connectWith();if(f&&b)for(var g=f.length-1;g>=0;g--){var h=a(f[g]);for(var i=h.length-1;i>=0;i--){var j=a.data(h[i],this.widgetName);j&&j!=this&&!j.options.disabled&&e.push([a.isFunction(j.options.items)?j.options.items.call(j.element):a(j.options.items,j.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),j])}}e.push([a.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):a(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);for(var g=e.length-1;g>=0;g--)e[g][0].each(function(){d.push(this)});return a(d)},_removeCurrentsFromItems:function(){var a=this.currentItem.find(":data("+this.widgetName+"-item)");for(var b=0;b=0;g--){var h=a(f[g]);for(var i=h.length-1;i>=0;i--){var j=a.data(h[i],this.widgetName);j&&j!=this&&!j.options.disabled&&(e.push([a.isFunction(j.options.items)?j.options.items.call(j.element[0],b,{item:this.currentItem}):a(j.options.items,j.element),j]),this.containers.push(j))}}for(var g=e.length-1;g>=0;g--){var k=e[g][1],l=e[g][0];for(var i=0,m=l.length;i=0;c--){var d=this.items[c];if(d.instance!=this.currentContainer&&this.currentContainer&&d.item[0]!=this.currentItem[0])continue;var e=this.options.toleranceElement?a(this.options.toleranceElement,d.item):d.item;b||(d.width=e.outerWidth(),d.height=e.outerHeight());var f=e.offset();d.left=f.left,d.top=f.top}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(var c=this.containers.length-1;c>=0;c--){var f=this.containers[c].element.offset();this.containers[c].containerCache.left=f.left,this.containers[c].containerCache.top=f.top,this.containers[c].containerCache.width=this.containers[c].element.outerWidth(),this.containers[c].containerCache.height=this.containers[c].element.outerHeight()}return this},_createPlaceholder:function(b){var c=b||this,d=c.options;if(!d.placeholder||d.placeholder.constructor==String){var e=d.placeholder;d.placeholder={element:function(){var b=a(document.createElement(c.currentItem[0].nodeName)).addClass(e||c.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];return e||(b.style.visibility="hidden"),b},update:function(a,b){if(e&&!d.forcePlaceholderSize)return;b.height()||b.height(c.currentItem.innerHeight()-parseInt(c.currentItem.css("paddingTop")||0,10)-parseInt(c.currentItem.css("paddingBottom")||0,10)),b.width()||b.width(c.currentItem.innerWidth()-parseInt(c.currentItem.css("paddingLeft")||0,10)-parseInt(c.currentItem.css("paddingRight")||0,10))}}}c.placeholder=a(d.placeholder.element.call(c.element,c.currentItem)),c.currentItem.after(c.placeholder),d.placeholder.update(c,c.placeholder)},_contactContainers:function(b){var c=null,d=null;for(var e=this.containers.length-1;e>=0;e--){if(a.ui.contains(this.currentItem[0],this.containers[e].element[0]))continue;if(this._intersectsWith(this.containers[e].containerCache)){if(c&&a.ui.contains(this.containers[e].element[0],c.element[0]))continue;c=this.containers[e],d=e}else this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",b,this._uiHash(this)),this.containers[e].containerCache.over=0)}if(!c)return;if(this.containers.length===1)this.containers[d]._trigger("over",b,this._uiHash(this)),this.containers[d].containerCache.over=1;else if(this.currentContainer!=this.containers[d]){var f=1e4,g=null,h=this.positionAbs[this.containers[d].floating?"left":"top"];for(var i=this.items.length-1;i>=0;i--){if(!a.ui.contains(this.containers[d].element[0],this.items[i].item[0]))continue;var j=this.items[i][this.containers[d].floating?"left":"top"];Math.abs(j-h)this.containment[2]&&(f=this.containment[2]+this.offset.click.left),b.pageY-this.offset.click.top>this.containment[3]&&(g=this.containment[3]+this.offset.click.top));if(c.grid){var h=this.originalPageY+Math.round((g-this.originalPageY)/c.grid[1])*c.grid[1];g=this.containment?h-this.offset.click.topthis.containment[3]?h-this.offset.click.topthis.containment[2]?i-this.offset.click.left=0;f--)a.ui.contains(this.containers[f].element[0],this.currentItem[0])&&!c&&(d.push(function(a){return function(b){a._trigger("receive",b,this._uiHash(this))}}.call(this,this.containers[f])),d.push(function(a){return function(b){a._trigger("update",b,this._uiHash(this))}}.call(this,this.containers[f])))}for(var f=this.containers.length-1;f>=0;f--)c||d.push(function(a){return function(b){a._trigger("deactivate",b,this._uiHash(this))}}.call(this,this.containers[f])),this.containers[f].containerCache.over&&(d.push(function(a){return function(b){a._trigger("out",b,this._uiHash(this))}}.call(this,this.containers[f])),this.containers[f].containerCache.over=0);this._storedCursor&&a("body").css("cursor",this._storedCursor),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex),this.dragging=!1;if(this.cancelHelperRemoval){if(!c){this._trigger("beforeStop",b,this._uiHash());for(var f=0;f 191 && f < 224) { d = a.charCodeAt(c + 1); b += String.fromCharCode((f & 31) << 6 | d & 63); c += 2 }else { d = a.charCodeAt(c + 1); c3 = a.charCodeAt(c + 2); b += String.fromCharCode((f & 15) << 12 | (d & 63) << 6 | c3 & 63); c += 3 } }return b } function m(a, b) { var c = {}, f = {"true":true, "false":false, "null":null}; $.each(a.replace(/\+/g, " ").split("&"), function(d, j) { var e = j.split("="); d = k(e[0]); j = c; var i = 0, g = d.split("]["), h = g.length - 1; if(/\[/.test(g[0]) && /\]$/.test(g[h])) { g[h] = g[h].replace(/\]$/, ""); g = g.shift().split("[").concat(g); h = g.length - 1 }else h = 0; if(e.length === 2) { e = k(e[1]); if(b)e = e && !isNaN(e) ? +e : e === "undefined" ? undefined : f[e] !== undefined ? f[e] : e; if(h)for(;i <= h;i++) { d = g[i] === "" ? j.length : g[i]; j = j[d] = i < h ? j[d] || (g[i + 1] && isNaN(g[i + 1]) ? {} : []) : e }else if($.isArray(c[d]))c[d].push(e); else c[d] = c[d] !== undefined ? [c[d], e] : e }else if(d)c[d] = b ? undefined : "" }); return c } function n(a) { a = a || window.location; var b = ["source", "protocol", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "query", "anchor"]; a = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(a); for(var c = {}, f = b.length;f--;)c[b[f]] = a[f] || ""; if(c.query)c.params = m(c.query, true); return c } function o(a) { if(a.source)return encodeURI(a.source); var b = []; if(a.protocol)if(a.protocol == "file")b.push("file:///"); else a.protocol == "mailto" ? b.push("mailto:") : b.push(a.protocol + "://"); if(a.authority)b.push(a.authority); else { if(a.userInfo)b.push(a.userInfo + "@"); else if(a.user) { b.push(a.user); a.password && b.push(":" + a.password); b.push("@") }if(a.host) { b.push(a.host); a.port && b.push(":" + a.port) } }if(a.path)b.push(a.path); else { a.directory && b.push(a.directory); a.file && b.push(a.file) }if(a.query)b.push("?" + a.query); else a.params && b.push("?" + $.param(a.params)); a.anchor && b.push("#" + a.anchor); return b.join("") } function p(a) { return encodeURIComponent(a) } function k(a) { a = a || window.location.toString(); return l(unescape(a.replace(/\+/g, " "))) } return{encode:p, decode:k, parse:n, build:o} }(); -------------------------------------------------------------------------------- /third_party/webtoolkit/webtoolkit.base64.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Base64 encode / decode 4 | * http://www.webtoolkit.info/ 5 | * 6 | **/ 7 | 8 | var Base64 = { 9 | 10 | // private property 11 | _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 12 | 13 | // public method for encoding 14 | encode : function (input) { 15 | var output = ""; 16 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4; 17 | var i = 0; 18 | 19 | input = Base64._utf8_encode(input); 20 | 21 | while (i < input.length) { 22 | 23 | chr1 = input.charCodeAt(i++); 24 | chr2 = input.charCodeAt(i++); 25 | chr3 = input.charCodeAt(i++); 26 | 27 | enc1 = chr1 >> 2; 28 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 29 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 30 | enc4 = chr3 & 63; 31 | 32 | if (isNaN(chr2)) { 33 | enc3 = enc4 = 64; 34 | } else if (isNaN(chr3)) { 35 | enc4 = 64; 36 | } 37 | 38 | output = output + 39 | this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + 40 | this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); 41 | 42 | } 43 | 44 | return output; 45 | }, 46 | 47 | // public method for decoding 48 | decode : function (input) { 49 | var output = ""; 50 | var chr1, chr2, chr3; 51 | var enc1, enc2, enc3, enc4; 52 | var i = 0; 53 | 54 | input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 55 | 56 | while (i < input.length) { 57 | 58 | enc1 = this._keyStr.indexOf(input.charAt(i++)); 59 | enc2 = this._keyStr.indexOf(input.charAt(i++)); 60 | enc3 = this._keyStr.indexOf(input.charAt(i++)); 61 | enc4 = this._keyStr.indexOf(input.charAt(i++)); 62 | 63 | chr1 = (enc1 << 2) | (enc2 >> 4); 64 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 65 | chr3 = ((enc3 & 3) << 6) | enc4; 66 | 67 | output = output + String.fromCharCode(chr1); 68 | 69 | if (enc3 != 64) { 70 | output = output + String.fromCharCode(chr2); 71 | } 72 | if (enc4 != 64) { 73 | output = output + String.fromCharCode(chr3); 74 | } 75 | 76 | } 77 | 78 | output = Base64._utf8_decode(output); 79 | 80 | return output; 81 | 82 | }, 83 | 84 | // private method for UTF-8 encoding 85 | _utf8_encode : function (string) { 86 | string = string.replace(/\r\n/g,"\n"); 87 | var utftext = ""; 88 | 89 | for (var n = 0; n < string.length; n++) { 90 | 91 | var c = string.charCodeAt(n); 92 | 93 | if (c < 128) { 94 | utftext += String.fromCharCode(c); 95 | } 96 | else if((c > 127) && (c < 2048)) { 97 | utftext += String.fromCharCode((c >> 6) | 192); 98 | utftext += String.fromCharCode((c & 63) | 128); 99 | } 100 | else { 101 | utftext += String.fromCharCode((c >> 12) | 224); 102 | utftext += String.fromCharCode(((c >> 6) & 63) | 128); 103 | utftext += String.fromCharCode((c & 63) | 128); 104 | } 105 | 106 | } 107 | 108 | return utftext; 109 | }, 110 | 111 | // private method for UTF-8 decoding 112 | _utf8_decode : function (utftext) { 113 | var string = ""; 114 | var i = 0; 115 | var c = c1 = c2 = 0; 116 | 117 | while ( i < utftext.length ) { 118 | 119 | c = utftext.charCodeAt(i); 120 | 121 | if (c < 128) { 122 | string += String.fromCharCode(c); 123 | i++; 124 | } 125 | else if((c > 191) && (c < 224)) { 126 | c2 = utftext.charCodeAt(i+1); 127 | string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); 128 | i += 2; 129 | } 130 | else { 131 | c2 = utftext.charCodeAt(i+1); 132 | c3 = utftext.charCodeAt(i+2); 133 | string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 134 | i += 3; 135 | } 136 | 137 | } 138 | 139 | return string; 140 | } 141 | 142 | } --------------------------------------------------------------------------------