├── icons.woff
├── img
├── error80.png
├── icon128.png
├── icon16.png
├── icon19.png
├── icon38.png
├── icon48.png
├── refresh24.png
├── square80.png
├── nzbget-arrow.svg
└── nzbget-icon.svg
├── screens
├── screen1.png
└── screen2.png
├── sites
├── newzleech.css
├── spotweb.css
├── newzleech.js
├── common.css
├── nzbindex.js
├── nzbclub.js
├── spotweb.js
├── feedly.js
├── binsearch.js
├── newsnab.js
├── freshrss.js
├── ttrss.js
├── common.js
├── dognzb.js
└── tests
│ └── binsearch.html
├── elements
├── ng-element.js
├── ng-checkbox.css
├── ng-checkbox.js
├── ng-download-item.css
└── ng-download-item.js
├── js
├── framework.js
├── options.js
├── utilities.js
├── popup.js
└── nzbget.js
├── manifest.json
├── popup.html
├── options.html
├── README.md
├── options.css
└── popup.css
/icons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frame/nzbget-chrome/master/icons.woff
--------------------------------------------------------------------------------
/img/error80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frame/nzbget-chrome/master/img/error80.png
--------------------------------------------------------------------------------
/img/icon128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frame/nzbget-chrome/master/img/icon128.png
--------------------------------------------------------------------------------
/img/icon16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frame/nzbget-chrome/master/img/icon16.png
--------------------------------------------------------------------------------
/img/icon19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frame/nzbget-chrome/master/img/icon19.png
--------------------------------------------------------------------------------
/img/icon38.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frame/nzbget-chrome/master/img/icon38.png
--------------------------------------------------------------------------------
/img/icon48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frame/nzbget-chrome/master/img/icon48.png
--------------------------------------------------------------------------------
/img/refresh24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frame/nzbget-chrome/master/img/refresh24.png
--------------------------------------------------------------------------------
/img/square80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frame/nzbget-chrome/master/img/square80.png
--------------------------------------------------------------------------------
/screens/screen1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frame/nzbget-chrome/master/screens/screen1.png
--------------------------------------------------------------------------------
/screens/screen2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frame/nzbget-chrome/master/screens/screen2.png
--------------------------------------------------------------------------------
/sites/newzleech.css:
--------------------------------------------------------------------------------
1 | table.contentt td.get {
2 | width: 36px;
3 | }
4 | .get img.nzbgc_download {
5 | padding-right: 6px;
6 | }
--------------------------------------------------------------------------------
/sites/spotweb.css:
--------------------------------------------------------------------------------
1 | table.spots a.nzb {
2 | display: inline;
3 | }
4 |
5 | div.spots table.spots td.nzb,
6 | div.spots table.spots th.nzb {
7 | width: 45px;
8 | }
9 |
10 | .nzbgc_download {
11 | vertical-align: middle;
12 | }
13 |
--------------------------------------------------------------------------------
/img/nzbget-arrow.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/sites/newzleech.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Inject nzbgc-markup to newzleech.com
3 | */
4 | (function() {
5 | 'use strict';
6 | var dllinks = document.querySelectorAll('a[href*="dl=1"]');
7 |
8 | for(var i = 0; i < dllinks.length; i++) {
9 | var dlitem = dllinks.item(i),
10 | lid = '';
11 |
12 | // Skip if already processed
13 | if(dlitem.nzbGetProcessed) {
14 | continue;
15 | }
16 | dlitem.nzbGetProcessed = true;
17 |
18 | lid = 'ngi' + dlitem.href.match(/post=([0-9]+)/)[1];
19 |
20 | var newitem = createNgIcon(
21 | lid + '_nzbgc',
22 | dlitem.href
23 | );
24 |
25 | dlitem.parentElement.insertBefore(newitem, dlitem);
26 | }
27 | })();
28 |
--------------------------------------------------------------------------------
/img/nzbget-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
--------------------------------------------------------------------------------
/elements/ng-element.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | class NgElement {
4 | constructor(ob) {
5 | this._elements = {
6 | root: ob
7 | };
8 | }
9 |
10 | $element(tag, id, children, params) {
11 | if(!id) {
12 | id = tag;
13 | }
14 |
15 | if(!Array.isArray(children)) {
16 | params = children;
17 | children = [];
18 | }
19 | this._elements[id] = document.createElement(tag);
20 | this._elements[id].className = params && params.class || id;
21 | for(var child in children) {
22 | this._elements[id].appendChild(children[child]);
23 | }
24 | if(params && params.text) {
25 | this._elements[id].appendChild(
26 | document.createTextNode(params.text));
27 | }
28 | return this._elements[id];
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/sites/common.css:
--------------------------------------------------------------------------------
1 | @keyframes spin {
2 | from {
3 | transform: rotate(0deg);
4 | }
5 | to {
6 | transform: rotate(360deg);
7 | }
8 | }
9 |
10 | .nzbgc_download {
11 | filter: grayscale(0.8);
12 | transition: all 250ms ease-in-out;
13 | cursor: pointer;
14 | display: inline-block;
15 | }
16 |
17 | .nzbgc_download img {
18 | width: 16px;
19 | }
20 |
21 | .nzbgc_download.nzbgc_added_ok{
22 | filter: grayscale(0);
23 | }
24 |
25 | .nzbgc_download.nzbgc_added_failed{
26 | opacity:0.5;
27 | }
28 |
29 | .nzbgc_download.nzbgc_adding{
30 | animation-name: spin;
31 | animation-duration: 500ms;
32 | animation-iteration-count: infinite;
33 | animation-timing-function: linear;
34 | }
35 |
36 | .nzbgc_download:hover {
37 | filter: grayscale(0.5);
38 | }
39 |
40 | a.headerInfo-expanded-img .nzbgc_download {
41 | padding: 3px;
42 | }
43 |
--------------------------------------------------------------------------------
/sites/nzbindex.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Inject nzbgc-markup to nzbindex.nl
3 | */
4 | (function() {
5 | 'use strict';
6 | var dllinks = document.querySelectorAll('a[href*="/download/"]');
7 | for(var i = 0; i < dllinks.length; i++) {
8 | var dlitem = dllinks.item(i);
9 | var category = '', lid = '';
10 |
11 | var trParent = findParentOfType(dlitem, 'TR');
12 | if(trParent) {
13 | var chb = trParent.querySelector('TD INPUT[type=checkbox]');
14 | if(chb && chb.value) {
15 | lid = chb.value;
16 | }
17 | }
18 |
19 | var newitem = createNgIcon(
20 | lid + '_nzbgc',
21 | dlitem.href,
22 | category
23 | );
24 |
25 | newitem.style.verticalAlign = 'middle';
26 | newitem.style.paddingRight = '4px';
27 |
28 | var dlparent = dlitem.parentElement;
29 | dlparent.insertBefore(newitem, dlitem);
30 | }
31 | })();
32 |
--------------------------------------------------------------------------------
/sites/nzbclub.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Inject nzbgc-markup to nzbclub.com
3 | */
4 | (function() {
5 | 'use strict';
6 | var containers = document.querySelectorAll(
7 | '.row .project-action[collectionid]:first-child');
8 | for(var i = 0; i < containers.length; i++) {
9 | var container = containers.item(i);
10 | var lid = container.getAttribute('collectionid');
11 | var resultaction = container.querySelector('div');
12 | var newdiv = document.createElement('button');
13 |
14 | newdiv.className = 'btn btn-xs icon_nzbgc';
15 | //newdiv.style.padding = '0 5px';
16 |
17 | var newitem = createNgIcon(
18 | lid + '_nzbgc',
19 | window.location.protocol +
20 | '//' + window.location.host + '/nzb_get/' + lid
21 | );
22 | newitem.style.marginTop = '0';
23 | newitem.style.marginBottom = '0';
24 | newitem.style.width = 'auto';
25 | newitem.style.height = '11px';
26 | newitem.style.borderRadius = '0';
27 |
28 | newdiv.appendChild(newitem);
29 |
30 | resultaction.appendChild(newdiv);
31 | }
32 | })();
33 |
--------------------------------------------------------------------------------
/elements/ng-checkbox.css:
--------------------------------------------------------------------------------
1 | ng-checkbox {
2 | display: block;
3 | font-size: 1.1rem;
4 | margin-bottom: 5px;
5 | padding: 0.6rem 0 0.3rem 0;
6 | }
7 |
8 | ng-checkbox:focus {
9 | border-color: #e91e63;
10 | border-style: solid;
11 | border-width: 0 0 2px 0;
12 | margin-bottom: 3px;
13 | outline: none;
14 | }
15 |
16 | ng-checkbox .checkboxContainer {
17 | cursor: pointer;
18 | display: inline-block;
19 | height: 18px;
20 | position: relative;
21 | width: 18px;
22 | }
23 |
24 | ng-checkbox .checkbox {
25 | border: solid 2px #5a5a5a;
26 | box-sizing: border-box;
27 | height: 18px;
28 | left: 0;
29 | pointer-events: none;
30 | position: absolute;
31 | top: 0;
32 | transition: all 100ms linear;
33 | width: 18px;
34 | }
35 |
36 | ng-checkbox[checked] .checkbox {
37 | border-color: #0f9d58;
38 | border-width: 0 2px 2px 0;
39 | height: 21px;
40 | left: 6px;
41 | top: -4px;
42 | transform: rotate(45deg);
43 | width: 10px;
44 | }
45 |
46 | ng-checkbox label {
47 | display: flex;
48 | font-weight: 400;
49 | padding: 0;
50 | }
51 |
52 | ng-checkbox .textContainer {
53 | display: flex;
54 | flex-direction: column;
55 | margin-top: -5px;
56 | padding-left: 14px;
57 | }
58 |
59 | ng-checkbox description {
60 | font-size: 0.6rem;
61 | }
62 |
--------------------------------------------------------------------------------
/sites/spotweb.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Run when a site matching spotweb-markup is found
3 | */
4 | (function(){
5 | 'use strict';
6 | var timeout = null;
7 | var leap = 0;
8 | /**
9 | * DOMModificationCallback
10 | * Called after the last DOMSubtreeModified event in a chain.
11 | * @return {void}
12 | */
13 | function domModificationCallback() {
14 | var dllinks = document.querySelectorAll('a[title*="Download NZB"');
15 | for(var i = 0; i < dllinks.length; i++) {
16 | var dlitem = dllinks.item(i);
17 |
18 | // Skip if already processed
19 | if(dlitem.nzbGetProcessed) {
20 | continue;
21 | }
22 | dlitem.nzbGetProcessed = true;
23 |
24 | var eParent = dlitem.parentElement;
25 |
26 | var newitem = createNgIcon(
27 | leap++ + '_nzbgc',
28 | dlitem.href,
29 | ''
30 | );
31 |
32 | eParent.insertBefore(newitem, dlitem);
33 | }
34 | }
35 |
36 | function domModificationHandler(){
37 | if(timeout) {
38 | clearTimeout(timeout);
39 | }
40 | timeout = setTimeout(domModificationCallback, 200);
41 | }
42 |
43 | window.addEventListener(
44 | 'DOMSubtreeModified',
45 | domModificationHandler,
46 | false);
47 | domModificationCallback();
48 | })();
49 |
--------------------------------------------------------------------------------
/js/framework.js:
--------------------------------------------------------------------------------
1 | function $E(params) {
2 | 'use strict';
3 | var tmp = document.createElement(params.tag);
4 | if(params.className) {
5 | tmp.className = params.className;
6 | }
7 | if(params.text) {
8 | tmp.appendChild(document.createTextNode(params.text));
9 | }
10 | if(params.styles) {
11 | for(var k in params.styles) {
12 | tmp.style[k] = params.styles[k];
13 | }
14 | }
15 | if(params.rel) {
16 | tmp.setAttribute('rel', params.rel);
17 | }
18 | return tmp;
19 | }
20 |
21 | function modalDialog(header, body, buttons) {
22 | 'use strict';
23 | const
24 | shroud = document.querySelector('.shroud'),
25 | btnbar = shroud.querySelector('.btnbar');
26 |
27 | shroud.querySelector('h2').innerHTML = header;
28 | shroud.querySelector('p').innerHTML = body;
29 | btnbar.innerHTML = '';
30 |
31 | const clickFunc = function() {
32 | if(this.clickfunc) {
33 | this.clickfunc();
34 | }
35 | shroud.classList.remove('active');
36 | };
37 |
38 | for(let button of buttons) {
39 | let btnElm = $E({
40 | tag: 'a',
41 | text: button.title});
42 | if(button.href) {
43 | btnElm.href = button.href;
44 | btnElm.target = '_blank';
45 | } else {
46 | btnElm.href = '#';
47 | }
48 | btnElm.clickfunc = button.onClick;
49 |
50 | btnElm.addEventListener('click', clickFunc);
51 | btnbar.appendChild(btnElm);
52 | }
53 |
54 | shroud.classList.add('active');
55 | }
56 |
--------------------------------------------------------------------------------
/sites/feedly.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | 'use strict';
3 | var timeout = null;
4 |
5 | /**
6 | * DOMModificationCallback
7 | * Called after the last DOMSubtreeModified event in a chain.
8 | *
9 | * @return {void}
10 | */
11 | function DOMModificationCallback() {
12 | var dllinks = document.querySelectorAll('#timeline A.entryTitle');
13 | for(var i = 0; i < dllinks.length; i++) {
14 | var dlitem = dllinks.item(i);
15 |
16 | // Skip if already processed or if href="" doesn't contain '.nzb'
17 | if(dlitem.nzbGetProcessed || !dlitem.href.match(/\.nzb/)) {
18 | continue;
19 | }
20 | dlitem.nzbGetProcessed = true;
21 |
22 | var eRoot = dlitem.parentElement.parentElement.parentElement,
23 | eParent = eRoot.querySelector('.shareHolder .left'),
24 | eBody = eRoot.querySelector('.entryBody');
25 |
26 | var newitem = createNgIcon(
27 | eRoot.id.replace('_entryHolder', ' _nzbgc'),
28 | dlitem.href,
29 | eBody.innerText.match(/Category[\s-:]*(.+)/)[1]
30 | );
31 | newitem.classList.add('headerInfo-expanded-img');
32 |
33 | eParent.insertBefore(newitem, eParent.firstChild);
34 | }
35 | }
36 |
37 | function DOMModificationHandler(){
38 | if(timeout) {
39 | clearTimeout(timeout);
40 | }
41 | timeout = setTimeout(DOMModificationCallback, 200);
42 | }
43 |
44 | window.addEventListener(
45 | 'DOMSubtreeModified',
46 | DOMModificationHandler,
47 | false);
48 | })();
49 |
--------------------------------------------------------------------------------
/sites/binsearch.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Inject nzbgc-markup to binsearch.info
3 | */
4 | (function() {
5 | 'use strict';
6 | var dllinks = document.querySelectorAll('input[type=checkbox]'),
7 | linkref = window.location.href.replace(/^.+\?/, '').split('&'),
8 | baselink = 'http://www.binsearch.info/?action=nzb',
9 | get = {};
10 |
11 | for(var i in linkref) {
12 | var part = linkref[i].split('=');
13 | get[part[0]] = part.length > 1 ? part[1] : '';
14 | }
15 | if(get.b && get.g) {
16 | baselink += '&b=' + get.b + '&g=' + get.g;
17 | }
18 |
19 | for(var j = 0; j < dllinks.length; j++) {
20 | var dlitem = dllinks.item(j),
21 | lid = dlitem.name,
22 | newSpan = document.createElement('span'),
23 | trParent = findParentOfType(dlitem, 'TR'),
24 | nameTag = trParent.querySelector('span.s'),
25 | name = '';
26 |
27 | if(!nameTag) {
28 | nameTag = trParent.querySelectorAll('td')[1];
29 | }
30 |
31 | if(nameTag) {
32 | name = nameTag.childNodes[0].textContent
33 | .replace(/\[[0-9]+\/[0-9]+\]\s*-\s*/g, '')
34 | .replace(/\s*\([0-9]+\/[0-9]+\)/g, '')
35 | .replace(/"/g, '')
36 | .replace(/\s*yEnc\s*/, '')
37 | .replace(/\//g, '');
38 | }
39 |
40 | newSpan.className = 'icon_nzbgc';
41 | newSpan.style.padding = '0 5px';
42 |
43 | var newitem = createNgIcon(
44 | lid + '_nzbgc',
45 | baselink + '&' + dlitem.name + '=1',
46 | '',
47 | name
48 | );
49 |
50 | newSpan.appendChild(newitem);
51 | dlitem.parentElement.insertBefore(newSpan, dlitem);
52 | }
53 | })();
54 |
--------------------------------------------------------------------------------
/sites/newsnab.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Run when a site matching newznab-markup is found
3 | */
4 |
5 | (function(){
6 | 'use strict';
7 | var dllinks = document.querySelectorAll('div.icon_nzb a[href*="/getnzb"]');
8 | for(var i = 0; i < dllinks.length; i++) {
9 | var dlitem = dllinks.item(i),
10 | category = '',
11 | lid = '',
12 | newdiv = document.createElement('div'),
13 | tabParent = document.getElementById('details'),
14 | trParent = findParentOfType(dlitem, 'TR'),
15 | dlparent = dlitem.parentElement,
16 | tdCat = '';
17 |
18 | // Skip if already processed
19 | if(dlitem.nzbGetProcessed) {
20 | continue;
21 | }
22 | dlitem.nzbGetProcessed = true;
23 |
24 | newdiv.className = 'icon icon_nzbgc';
25 |
26 | // Try to find category and an unique id
27 | if(!tabParent) {
28 | tabParent = document.getElementById('detailstable');
29 | }
30 |
31 | if(trParent.id) {
32 | lid = trParent.id;
33 | }
34 |
35 | if(tabParent) { // Details page
36 | tdCat = tabParent.querySelector('td a[href*="/browse?t"]');
37 | if(tdCat) {
38 | category = tdCat.innerText;
39 | }
40 | }
41 | else { // Assume listing page
42 | tdCat = trParent.querySelector('td:nth-child(3)');
43 | if(tdCat) {
44 | category = tdCat.innerText;
45 | }
46 | }
47 |
48 | var newitem = createNgIcon(
49 | lid + '_nzbgc',
50 | dlitem.href,
51 | category
52 | );
53 |
54 | newdiv.appendChild(newitem);
55 | dlparent.parentElement.insertBefore(newdiv, dlparent);
56 | }
57 | })();
58 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "background": {
3 | "scripts": [ "js/nzbget.js", "js/utilities.js" ]
4 | },
5 | "description": "Interact with and send NZB-files to NZBGet",
6 | "icons": {
7 | "16": "img/icon16.png"
8 | ,"19": "img/icon19.png"
9 | ,"38": "img/icon38.png"
10 | ,"48": "img/icon48.png"
11 | },
12 | "manifest_version": 2,
13 | "web_accessible_resources": [
14 | "img/icon16.png"
15 | ,"img/nzbget-arrow.svg"
16 | ],
17 | "name": "nzbget-chrome-phalanx",
18 | "short_name": "nzbget-chrome-phalanx",
19 | "options_ui": {
20 | "page": "options.html",
21 | "chrome_style": false
22 | },
23 | "permissions": [ "*://*/", "contextMenus", "tabs", "notifications", "storage" ],
24 | "version": "1.6",
25 | "browser_action": {
26 | "default_icon": "img/icon38.png",
27 | "default_popup": "popup.html"
28 | },
29 | "content_scripts": [
30 | {
31 | "matches": [ "*://*.feedly.com/*" ]
32 | ,"js": [ "sites/common.js", "sites/feedly.js" ]
33 | ,"css": [ "sites/common.css" ]
34 | },{
35 | "matches": [ "*://*.nzbclub.com/*" ]
36 | ,"js": [ "sites/common.js", "sites/nzbclub.js" ]
37 | ,"css": [ "sites/common.css" ]
38 | },{
39 | "matches": [ "*://*.nzbindex.nl/*", "*://*.nzbindex.com/*" ]
40 | ,"js": [ "sites/common.js", "sites/nzbindex.js" ]
41 | ,"css": [ "sites/common.css" ]
42 | },{
43 | "matches": [ "*://*.binsearch.info/*", "*://*.binsearch.net/*" ]
44 | ,"js": [ "sites/common.js", "sites/binsearch.js" ]
45 | ,"css": [ "sites/common.css" ]
46 | },{
47 | "matches": [ "*://*.dognzb.cr/*" ]
48 | ,"js": [ "sites/common.js", "sites/dognzb.js" ]
49 | ,"css": [ "sites/common.css" ]
50 | },{
51 | "matches": [ "*://*.newzleech.com/*" ]
52 | ,"js": [ "sites/common.js", "sites/newzleech.js" ]
53 | ,"css": [ "sites/common.css", "sites/newzleech.css" ]
54 | }
55 | ]
56 | }
57 |
--------------------------------------------------------------------------------
/elements/ng-checkbox.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | class NgCheckBox extends NgElement {
4 |
5 | constructor(ob) {
6 | super(ob);
7 | this._setup();
8 | }
9 |
10 | get checked() {
11 | return this._elements.root.getAttribute('checked') !== null;
12 | }
13 |
14 | set checked(value) {
15 | if(value) {
16 | this._elements.root.setAttribute('checked', '');
17 | }
18 | else {
19 | this._elements.root.removeAttribute('checked');
20 | }
21 | }
22 |
23 | _sync() {
24 | this._elements.heading.innerText =
25 | this._elements.root.getAttribute('label');
26 | this._elements.root.label =
27 | this._elements.root.getAttribute('label');
28 | this._elements.description.innerText =
29 | this._elements.root.getAttribute('description');
30 | this._elements.root.checked = this.checked;
31 | }
32 |
33 | _setup() {
34 | Object.defineProperty(this._elements.root, 'value', {
35 | get: () => {
36 | return this.checked;
37 | },
38 | set: (value) => {
39 | this.checked = value;
40 | }
41 | });
42 | var observer = new MutationObserver(() => {
43 | this._sync();
44 | });
45 | observer.observe(this._elements.root, {attributes: true});
46 | this._elements.root.addEventListener('click', () => {
47 | this.checked = !this.checked;
48 | });
49 |
50 | this._elements.root.appendChild(
51 | this.$element('label', 'label', [
52 | this.$element('div', 'checkboxContainer', [
53 | this.$element('div', 'checkbox')
54 | ]),
55 | this.$element('div', 'textContainer', [
56 | this.$element('header', 'heading'),
57 | this.$element('description')
58 | ])
59 | ])
60 | );
61 | this._sync();
62 | }
63 | }
64 |
65 | var list = document.querySelectorAll('ng-checkbox');
66 | list.forEach(function(ob) {
67 | new NgCheckBox(ob);
68 | });
69 |
--------------------------------------------------------------------------------
/sites/freshrss.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | 'use strict';
3 | var timeout = null;
4 |
5 | /**
6 | * DOMModificationCallback
7 | * Called after the last DOMSubtreeModified event in a chain.
8 | *
9 | * @return {void}
10 | */
11 | function DOMModificationCallback() {
12 | var dllinks = document.querySelectorAll('div.flux li.item.title a');
13 | for(var i = 0; i < dllinks.length; i++) {
14 | var dlitem = dllinks.item(i);
15 |
16 | // Skip if already processed or if href="" doesn't contain '.nzb'
17 | if(dlitem.nzbGetProcessed || !dlitem.href.match(/\.nzb/)) {
18 | continue;
19 | }
20 | dlitem.nzbGetProcessed = true;
21 |
22 | var eParent = dlitem.parentElement;
23 | var eRoot = findParentOfType(eParent, 'DIV');
24 | if(eRoot.querySelector('.nzbgc_download')) {
25 | return;
26 | }
27 | var eContent = eRoot.querySelector('DIV.flux_content');
28 | var eTarget = eContent.querySelector('H1');
29 |
30 | var eBody = eTarget.nextElementSibling;
31 | var catMatch = eBody.innerHTML.replace(/<(?:.|\n)*?>/gm, '\n')
32 | .match(/Category[\s-:]*([^\n]+)/);
33 | var category = catMatch && catMatch.length ? catMatch[1] : '';
34 |
35 | var newItem = createNgIcon(
36 | eRoot.id + ' _nzbgc',
37 | dlitem.href,
38 | category
39 | );
40 | newItem.style.marginRight = '10px';
41 | newItem.style.marginTop = '10px';
42 | newItem.firstChild.style.width = '22px';
43 | newItem.style.float = 'left';
44 | newItem.style.lineHeight = '1';
45 | newItem.style.display = 'inline-block';
46 | eTarget.insertBefore(newItem, eTarget.firstChild);
47 | }
48 | }
49 |
50 | function DOMModificationHandler(){
51 | if(timeout) {
52 | clearTimeout(timeout);
53 | }
54 | timeout = setTimeout(DOMModificationCallback, 200);
55 | }
56 |
57 | window.addEventListener(
58 | 'DOMSubtreeModified',
59 | DOMModificationHandler,
60 | false);
61 | })();
62 |
--------------------------------------------------------------------------------
/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
36 |
37 |
38 |
No active downloads.
39 |
40 |
41 |
42 |
43 |
44 | search
45 |
49 |
50 |
51 |
52 |
53 |
54 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Options - nzbget-chrome
5 |
6 |
7 |
8 |
9 |
10 |
11 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/sites/ttrss.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | 'use strict';
3 | var timeout = null;
4 |
5 | /**
6 | * DOMModificationCallback
7 | * Called after the last DOMSubtreeModified event in a chain.
8 | *
9 | * @return {void}
10 | */
11 | function DOMModificationCallback() {
12 | var noCdm = document.querySelector('.postReply .postTitle A');
13 | if(noCdm) {
14 | // Skip if already processed or if href="" doesn't contain '.nzb'
15 | if(noCdm.nzbGetProcessed || !noCdm.href.match(/\.nzb/)) {
16 | return;
17 | }
18 | noCdm.nzbGetProcessed = true;
19 | var sRoot = noCdm.parentElement.parentElement.parentElement,
20 | sBody = sRoot.querySelector('.postContent'),
21 | sParent = sRoot.querySelector('.postTags'),
22 | sMatch = sBody.innerText.match(/Category[\s-:]*(.+)/),
23 | scat = sMatch && sMatch.length ? sMatch[1] : '';
24 | var sItem = createNgIcon(
25 | sRoot.id + ' _nzbgc',
26 | noCdm.href,
27 | scat
28 | );
29 | sItem.style.display = 'inline-block';
30 | sParent.insertBefore(sItem, sParent.firstChild);
31 | return;
32 | }
33 | var dllinks = document.querySelectorAll('.cdmHeader A.title');
34 | for(var i = 0; i < dllinks.length; i++) {
35 | var dlitem = dllinks.item(i);
36 |
37 | // Skip if already processed or if href="" doesn't contain '.nzb'
38 | if(dlitem.nzbGetProcessed || !dlitem.href.match(/\.nzb/)) {
39 | continue;
40 | }
41 | dlitem.nzbGetProcessed = true;
42 |
43 | var eRoot = dlitem.parentElement.parentElement.parentElement,
44 | eSibling = eRoot.querySelector('img.tagsPic'),
45 | eParent = eSibling.parentElement,
46 | eBody = eRoot.querySelector('.cdmContent .cdmContentInner'),
47 | catMatch = eBody.innerText.match(/Category[\s-:]*(.+)/),
48 | category = catMatch && catMatch.length ? catMatch[1] : '';
49 | var newitem = createNgIcon(
50 | eRoot.id + ' _nzbgc',
51 | dlitem.href,
52 | category
53 | );
54 | newitem.classList.add('headerInfo-expanded-img');
55 |
56 | eParent.insertBefore(newitem, eParent.firstChild);
57 | }
58 | }
59 |
60 | function DOMModificationHandler(){
61 | if(timeout) {
62 | clearTimeout(timeout);
63 | }
64 | timeout = setTimeout(DOMModificationCallback, 200);
65 | }
66 |
67 | window.addEventListener(
68 | 'DOMSubtreeModified',
69 | DOMModificationHandler,
70 | false);
71 | })();
72 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
nzbget-chrome
2 | =============
3 |
4 | Google chrome extension to interact with nzbget
5 |
6 | ### Version history
7 | #### 1.6 - (current dev)
8 |
9 | ---
10 |
11 | #### 1.5 - 2017-09-12
12 | * Category support
13 | * New site scripts for ttRSS, FreshRSS
14 | * Bug fixes and polish
15 | ---
16 |
17 | #### 1.4 - 2016-01-19
18 | * New tabbed popup appearance
19 | * History search
20 | * Chromium 49 compatibility fixes
21 |
22 | ---
23 |
24 |
25 | #### 1.3 - 2015-06-05
26 | * Make popup notifications optional and use new rich notification API.
27 | * Try to lookup status in notified downloads
28 | * Use category Detected from site markup when no category is present in header.
29 | * Support one-click integration for spotweb, binsearch, nzbindex.com
30 | * Fixes and polish
31 |
32 | ---
33 |
34 | #### 1.2 - 2014-10-13
35 | * Better handling of connection failures.
36 | * support one-click integration on nzbindex.nl
37 | * support one-click integration on fanzub.com
38 | * support one-click integration on DOGnzb (thanks @GrimSerious)
39 | * Minor fixes to feedly support due to layout changes
40 | * Updated appearance
41 | * New feature: [optional] Persistent download status on download icons.
42 | * Progress bars now show estimated remaining download time
43 |
44 | ---
45 |
46 | #### 1.1 - 2014-05-31
47 | * Protocol selector (Thanks dakky)
48 | * Less strict nzb header check (Thanks dakky)
49 | * One-click site integration for newznab, feedly and nzbclub
50 | * Provides category to NZBGet if available
51 | * Show paused state on badge label color (Thanks pdf)
52 | * Show nzb health in popup on low quality nzbs
53 | * Use webp-icon in notifications, since rich notifications lost support for SVG
54 | * Additional polish and bugfixes
55 |
56 | ---
57 |
58 | #### 1.0 - 2013-12-31
59 | * Initial release
60 |
61 | ### Current main features
62 | * Browser action popup that displays active downloads, history and stats
63 | * Browser action icons badge indicating number of current active downloads
64 | * Adds a context menu item to links to download with NZBGet
65 | * Notification when downloads complete
66 | * Uses no toolkits or frameworks, just Javascript, CSS3 and chrome.*-apis
67 | * Drag-n-drop sorting of download queue
68 | * Flow control (pause, resume, delete) on individual items or whole queue.
69 | * One click site intergarion on newznab sites and feedly.com
70 |
71 | ### Installation from chrome webstore
72 | https://chrome.google.com/webstore/detail/nzbget-chrome/pbhceneiekgjjeblaghpkdkaomlloghm
73 |
74 | ### Installation instructions (as unpacked extension)
75 | *Note: Newer versions of chrome for windows will show a yellow icon backgorund for unpacked extensions*
76 |
77 | 1. Download a release or checkout repo with git
78 | 2. Open chrome://extensions/ in Chrome / Chromium
79 | 3. Make sure the "Developer mode" checkbox is checked.
80 | 4. Click "Load unpacked extension..." and choose the path you used in step 1.
81 |
--------------------------------------------------------------------------------
/sites/common.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Loop through an elements parents until we find one that matches
3 | * or return null
4 | *
5 | * @param {element} el Element
6 | * @param {string} type Type expected of parent element
7 | * @return {element} Found parent or null
8 | */
9 | function findParentOfType(el, type){
10 | 'use strict';
11 | var par = el;
12 | while(par !== null) {
13 | if(par.tagName === type) {
14 | return par;
15 | }
16 | par = par.parentElement;
17 | }
18 | return null;
19 | }
20 |
21 | /**
22 | * Create an IMG-element with properties and events
23 | * ready to inject into a sites markup.
24 | *
25 | * @param {string} id Unique identifier
26 | * @param {string} href URL
27 | * @param {string} cat Category
28 | * @param {string} nameOverride Override NZB-name provided in header
29 | * @return {element} Ready constructed element
30 | */
31 | function createNgIcon(id, href, cat, nameOverride){
32 | 'use strict';
33 | var eNgIcon = document.createElement('img'),
34 | eNgContainer = document.createElement('a');
35 | eNgIcon.src = chrome.extension.getURL('img/nzbget-arrow.svg');
36 | eNgContainer.title = 'Click to download with NZBGet.';
37 | eNgContainer.className = 'nzbgc_download';
38 |
39 | eNgContainer.href = href;
40 | eNgContainer.id = id;
41 | eNgContainer.nameOverride = nameOverride;
42 | eNgContainer.category = cat;
43 |
44 | eNgContainer.addEventListener('click', function(e) {
45 | e.preventDefault();
46 |
47 | chrome.runtime.sendMessage({
48 | message: 'addURL',
49 | href: this.href,
50 | id: this.id,
51 | category: this.category,
52 | nameOverride: this.nameOverride
53 | });
54 |
55 | this.classList.add('nzbgc_adding');
56 |
57 | return false;
58 | });
59 |
60 | // Check for match in stored URLs
61 | chrome.runtime.sendMessage(
62 | {message: 'checkCachedURL', url: href},
63 | function(response) {
64 | if(response) {
65 | document.getElementById(id).classList.add('nzbgc_added_ok');
66 | }
67 | }
68 | );
69 | eNgContainer.appendChild(eNgIcon);
70 | return eNgContainer;
71 | }
72 |
73 | /**
74 | * Listen to events sent to the tab. Update icon class to reflect add status
75 | *
76 | * @TODO: Implement fail status
77 | * @param {object} m Message object
78 | * @return {bool} success
79 | */
80 | chrome.runtime.onMessage.addListener(function(m) {
81 | 'use strict';
82 | switch(m.message) {
83 | case 'addedurl':
84 | var eInject = document.getElementById(m.id);
85 | if(!eInject) {
86 | return false;
87 | }
88 | eInject.classList.remove('nzbgc_adding');
89 | if(m.status) {
90 | eInject.classList.add('nzbgc_added_ok');
91 | } else {
92 | eInject.classList.add('nzbgc_added_failed');
93 | }
94 | break;
95 | }
96 | return true;
97 | });
98 |
--------------------------------------------------------------------------------
/sites/dognzb.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Inject nzbgc-markup to dognzb.cr
3 | */
4 | (function() {
5 | 'use strict';
6 | function injectBrowsingMode() {
7 | var dllinks = document.querySelectorAll('div.dog-icon-download');
8 | for(var i = 0; i < dllinks.length; i++) {
9 | var dlitem = dllinks.item(i);
10 | var category = '', lid = '';
11 |
12 | // create the element we want to insert into the html
13 | var newtd = document.createElement('td');
14 | newtd.className = 'icon_nzbgc';
15 | newtd.style.padding = '0 2px';
16 | var trParent = findParentOfType(dlitem, 'TR');
17 | if(trParent && trParent.id) {
18 | lid = trParent.id;
19 | }
20 |
21 | // read the nzb id from the onclick attribute
22 | var nzbid = dlitem.getAttribute('onclick');
23 | nzbid = nzbid.split('\'')[1];
24 |
25 | // we need the personal token to assemble the download link below
26 | var rssToken = document.getElementsByName('rsstoken')[0].value;
27 |
28 | // create the nzbget icon and assemble the download link
29 | var newitem = createNgIcon(
30 | lid + '_nzbgc',
31 | 'https://dognzb.cr' + '/fetch/' + nzbid + '/' + rssToken,
32 | category
33 | );
34 |
35 | newtd.appendChild(newitem);
36 |
37 | var dlparent = dlitem.parentElement;
38 | dlparent.parentElement.insertBefore(newtd, dlparent);
39 | }
40 |
41 | var warnings = document.querySelectorAll('div.dog-icon-warning');
42 | for (var j = 0; j < warnings.length; j++) {
43 | var warningitem = warnings.item(j);
44 |
45 | // we add an empty td to preserve the layout
46 | var tdParent = document.createElement('td');
47 | var warningparent = warningitem.parentElement;
48 | warningparent.parentElement.insertBefore(tdParent, warningparent);
49 | }
50 | }
51 | function injectDetailsMode() {
52 | var dlitem = document.querySelector('i.icon-download');
53 | var category = '', lid = 'details';
54 | // read the nzb id from the onclick attribute
55 | var nzbid = dlitem.parentNode.getAttribute('onclick').split("'")[1];
56 |
57 | // we need the personal token to assemble the download link below
58 | var rssToken = document.getElementsByName('rsstoken')[0].value;
59 |
60 | // create the nzbget icon and assemble the download link
61 | var newitem = createNgIcon(
62 | lid + '_nzbgc',
63 | 'https://dognzb.cr' + '/fetch/' + nzbid + '/' + rssToken,
64 | category
65 | );
66 | newitem.querySelector('img').style.paddingRight='5px';
67 | var container = document.querySelector('#details strong');
68 | container.insertBefore(newitem, container.childNodes[0]);
69 | }
70 |
71 | if(document.querySelector('title').text.match(/Browse/)) {
72 | injectBrowsingMode();
73 | } else {
74 | injectDetailsMode();
75 | }
76 | })();
77 |
--------------------------------------------------------------------------------
/js/options.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | 'use strict';
3 | function parseComponentsFromURL(value) {
4 | var x = new URL(value);
5 |
6 | document.querySelector('#opt_protocol').value = x.protocol.replace(
7 | ':', '');
8 | document.querySelector('#opt_host').value = x.hostname;
9 | document.querySelector('#opt_port').value = x.port ? x.port : 80;
10 |
11 | var keys = ['username', 'password', 'pathname'];
12 | for(var i in keys) {
13 | var key = 'opt_' + keys[i],
14 | el = document.getElementById(key);
15 | el.value = x[keys[i]];
16 | }
17 | return x;
18 | }
19 |
20 | document.addEventListener('DOMContentLoaded', function() {
21 | var elConnectionTest = document.querySelector('#connection_test');
22 | window.ngAPI = chrome.extension.getBackgroundPage().ngAPI;
23 | var opts = window.ngAPI.Options;
24 |
25 | document.querySelector('#opt_protocol')
26 | .addEventListener('change', function(evt) {
27 | var port = document.querySelector('#opt_port');
28 | if(port.value === '6789' || port.value === '6791') {
29 | port.value = evt.target.value === 'http' ? '6789' : '6791';
30 | }
31 | });
32 |
33 | var inputs = document.querySelectorAll(
34 | 'input[type=text],input[type=password],ng-checkbox,select'
35 | );
36 |
37 | for(var i = 0; i < inputs.length; i++) {
38 | inputs[i].value = opts.get(inputs[i].id);
39 | }
40 |
41 | document.querySelector('#btn_save')
42 | .addEventListener('click', function(){
43 | for(var input in inputs) {
44 | opts.set(inputs[input].id, inputs[input].value);
45 | }
46 | chrome.runtime.sendMessage({message: 'optionsUpdated'});
47 |
48 | elConnectionTest.innerText = 'Settings saved!';
49 | elConnectionTest.className = 'success';
50 | window.close();
51 | });
52 |
53 |
54 | document.querySelector('#btn_test')
55 | .addEventListener('click', function(){
56 | elConnectionTest.className = 'working';
57 | elConnectionTest.innerText = 'Trying to connect...';
58 | elConnectionTest.style.animationName = 'flip';
59 |
60 | var opOb = {get: function(v) {return this[v]; }};
61 | for(var input in inputs) {
62 | opOb[inputs[input].id] = (inputs[input].id,
63 | inputs[input].value);
64 | }
65 |
66 | window.ngAPI.version(function(r){
67 | elConnectionTest.innerText = 'Successfully connected ' +
68 | 'to NZBGet v' + r.result;
69 | elConnectionTest.style.animationName = 'pulse';
70 | elConnectionTest.className = 'success';
71 | }, function(reason){
72 | elConnectionTest.className = 'error';
73 | elConnectionTest.style.animationName = 'shake';
74 | elConnectionTest.innerHTML = '' +
75 | 'Connection failed!' +
76 | ' ' + reason;
77 | }, opOb);
78 | });
79 | elConnectionTest.addEventListener('webkitAnimationEnd', function(){
80 | this.style.webkitAnimationName = '';
81 | }, false);
82 | elConnectionTest.addEventListener('click', function(){
83 | this.innerHTML = '';
84 | this.className = '';
85 | });
86 | /* Parse text in host field and try to place URI-parts
87 | in their right form fields. */
88 | document.querySelector('#opt_host')
89 | .addEventListener('blur', function() {
90 | var prot = this.value.match(/^([a-z]+):\/\//);
91 |
92 | if(prot) {
93 | parseComponentsFromURL(this.value);
94 | }
95 | });
96 | });
97 | })();
98 |
--------------------------------------------------------------------------------
/js/utilities.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | 'use strict';
3 | var MAX32 = 4294967296;
4 | window.ngAPI.parse = {
5 | /**
6 | * Returns a string a history entrys status
7 | * @param {object} hist history item object
8 | * @return {void}
9 | */
10 | historyStatus: function(hist) {
11 | if(hist.Status) {
12 | return hist.Status
13 | .toLowerCase()
14 | .split('/');
15 | }
16 |
17 | // < v13 compat
18 | if (hist.Kind === 'NZB') {
19 | switch(true) {
20 | case hist.ParStatus === 'FAILURE':
21 | case hist.UnpackStatus === 'FAILURE':
22 | case hist.MoveStatus === 'FAILURE':
23 | case hist.ScriptStatus === 'FAILURE':
24 | return ['failure'];
25 | case hist.ParStatus === 'MANUAL':
26 | return ['damaged'];
27 | case hist.ScriptStatus === 'UNKNOWN':
28 | return ['unknown'];
29 | case hist.ScriptStatus === 'SUCCESS':
30 | case hist.UnpackStatus === 'SUCCESS':
31 | case hist.ParStatus === 'SUCCESS':
32 | return ['success'];
33 | case hist.ParStatus === 'REPAIR_POSSIBLE':
34 | return ['repairable'];
35 | case hist.ParStatus === 'NONE':
36 | return ['unknown'];
37 | }
38 | } else if (hist.Kind === 'URL') {
39 | switch (hist.UrlStatus) {
40 | case 'SUCCESS': return ['success'];
41 | case 'FAILURE': return ['failure'];
42 | case 'UNKNOWN': return ['unknown'];
43 | }
44 | }
45 | },
46 |
47 | /**
48 | * function detectGroupStatus()
49 | * Returns a string representing current download status
50 | *
51 | * @param {object} group Group object
52 | * @return {string} Group status
53 | */
54 | groupStatus: function(group) {
55 | if(group.Status !== 'undefined') {
56 | return group.Status.toLowerCase();
57 | }
58 |
59 | // < v13 compat
60 | switch(true) {
61 | case typeof group.post !== 'undefined':
62 | return 'postprocess';
63 | case group.ActiveDownloads > 0:
64 | return 'downloading';
65 | case group.PausedSizeLo !== 0 &&
66 | group.RemainingSizeLo === group.PausedSizeLo:
67 | return 'paused';
68 | }
69 | return 'queued';
70 | },
71 |
72 | /**
73 | * bigNumber
74 | * Combines two 32-bit integers to a 64-bit Double
75 | * (may lose data with extreme sizes)
76 | *
77 | * @param {integer} hi high-end int
78 | * @param {integer} lo low-end int
79 | * @return {number} Larger number
80 | */
81 | bigNumber: function(hi, lo) {
82 | return Number(hi * MAX32 + lo);
83 | },
84 |
85 | /**
86 | * function formatHRSize()
87 | * Formats an integer of seconds to a human readable string
88 | *
89 | * @param {integer} bytes size in bytes
90 | * @return {string} Human readable representation of bytes
91 | */
92 | toHRDataSize: function(bytes) {
93 | var sizes = {
94 | 1: ['KiB', 0],
95 | 2: ['MiB', 1],
96 | 3: ['GiB', 2],
97 | 4: ['TiB', 2]
98 | },
99 | output = null;
100 | Object.keys(sizes).reverse().forEach( function(i) {
101 | if(!output && this >= Math.pow(1024, i)) {
102 | var nmr = this / Math.pow(1024, i);
103 | output = nmr.toFixed(nmr < 100 ? sizes[i][1] : 0) +
104 | ' ' + sizes[i][0];
105 | }
106 | }.bind(bytes));
107 | return output !== null ? output : bytes + 'B';
108 | }
109 | };
110 | })();
111 |
--------------------------------------------------------------------------------
/options.css:
--------------------------------------------------------------------------------
1 | /*
2 | Animatioms are copied and modified from animate.css
3 | http://daneden.me/animate
4 | */
5 | @keyframes flip {
6 | 0% {
7 | transform: rotateY(-360deg);
8 | animation-timing-function: ease-out;
9 | }
10 |
11 | 50% {
12 | transform: rotateY(0deg);
13 | animation-timing-function: ease-in;
14 | }
15 | }
16 |
17 | @keyframes shake {
18 | 0%, 100% {
19 | transform: translateX(0);
20 | }
21 |
22 | 10%, 30%, 50%, 70%, 90% {
23 | transform: translateX(-10px);
24 | }
25 |
26 | 20%, 40%, 60%, 80% {
27 | transform: translateX(10px);
28 | }
29 | }
30 |
31 | @keyframes pulse {
32 | 0% {
33 | transform: scale(1);
34 | }
35 |
36 | 50% {
37 | transform: scale(1.1);
38 | }
39 |
40 | 100% {
41 | transform: scale(1);
42 | }
43 | }
44 |
45 | * {
46 | font-family:
47 | 'Roboto Condensed',
48 | Helvetica,
49 | Arial,
50 | sans-serif;
51 | }
52 |
53 | body {
54 | text-align: center;
55 | }
56 |
57 | .header {
58 | background-color: #e91e63;
59 | margin: -44px;
60 | margin-bottom: 16px;
61 | height: 96px;
62 | box-shadow:
63 | 0 2px 6px rgba(0,0,0,.16),
64 | 0 2px 6px rgba(0,0,0,.23);
65 | color: #fff;
66 | border-radius: 2px 2px 0 0;
67 | }
68 |
69 | .header h1 {
70 | font-size: 36px;
71 | font-weight: 500;
72 | line-height: 1.1;
73 | padding: 0;
74 | margin: 0;
75 | padding-top: 24px;
76 | }
77 |
78 | .header span {
79 | font-size: 16px;
80 | }
81 |
82 | .header img#logo {
83 | float: left;
84 | margin: 16px 8px 16px 16px;
85 | width: 64px;
86 | height: 64px;
87 | filter: grayscale(100%) invert(100%) brightness(150%);
88 | }
89 |
90 | b, strong {
91 | font-weight: 700;
92 | }
93 |
94 | div.card {
95 | text-align: left;
96 | display: inline-block;
97 | background: #fff;
98 | border-radius: 2px;
99 | padding: 36px;
100 | margin: 0;
101 | width: 350px;
102 | color: #333;
103 | font-size: 14px;
104 | line-height: 1.42857143;
105 | }
106 |
107 | fieldset {
108 | border: none;
109 | padding: 0;
110 | margin: 0;
111 | }
112 |
113 | label {
114 | display: block;
115 | padding: 0.6rem 0 0.3rem 0;
116 | font-weight: 700;
117 | }
118 |
119 | input[type=text], input[type=password], select {
120 | display: block;
121 | padding: 0.3rem 0.6rem;
122 | width: 100%;
123 | font-size: 18px;
124 | line-height: 1.33;
125 | color: #555;
126 | outline: 0;
127 | border: 0;
128 | border-bottom: 1px solid #7e7e7e;
129 | outline-width: 2px;
130 | padding-left: 0;
131 | margin-bottom: 1px;
132 | background: transparent;
133 | }
134 |
135 | input[type=text]:focus, input[type=password]:focus, select:focus {
136 | border-color: #e91e63;
137 | border-width: 2px;
138 | margin-bottom: 0;
139 | }
140 |
141 | input[type=button] {
142 | background-color: #4285f4;
143 | border-color: #285e8e;
144 | padding: 8px 30px;
145 | border: 0;
146 | margin: 10px 1px 0 1px;
147 | cursor: pointer;
148 | border-radius: 4px;
149 | text-transform: uppercase;
150 | transition: box-shadow .28s cubic-bezier(0.4,0,.2,1);
151 | outline: 0!important;
152 | font-size: 14px;
153 | font-weight: 400;
154 | line-height: 1.42857143;
155 | white-space: nowrap;
156 | vertical-align: middle;
157 | color: rgba(255,255,255,.84);
158 | }
159 |
160 | input[type=button]:hover {
161 | box-shadow:
162 | 0 3px 6px rgba(0,0,0,.2),
163 | 0 3px 6px rgba(0,0,0,.28);
164 | }
165 |
166 | input[type=button]:active {
167 | box-shadow:
168 | 0 10px 20px rgba(0,0,0,.19),
169 | 0 6px 6px rgba(0,0,0,.23);
170 | }
171 |
172 | input[type=button]#btn_test {
173 | color: rgba(0,0,0,.84);
174 | background-color: transparent;
175 | }
176 |
177 | input.small[type=text], input.small[type=password] {
178 | width: 3rem;
179 | }
180 |
181 | div.small, span.small {
182 | font-size: 0.6rem;
183 | font-weight: 400;
184 | }
185 |
186 | #connection_test {
187 | display: block;
188 | animation-timing-function: linear;
189 | }
190 |
191 | #connection_test.working {
192 | animation-duration: 2s;
193 | animation-iteration-count: infinite;
194 | background-color: #03a9f4;
195 | color: #fff;
196 | padding: 10px;
197 | margin: 10px 0;
198 | }
199 |
200 | #connection_test.error {
201 | color: #fff;
202 | background-color: #f44336;
203 | animation-duration: 500ms;
204 | animation-iteration-count: 1;
205 | padding: 10px;
206 | margin: 10px 0;
207 | }
208 |
209 | #connection_test.success {
210 | color: #fff;
211 | animation-duration: 250ms;
212 | animation-iteration-count: 1;
213 | background-color: #0f9d58;
214 | padding: 10px;
215 | margin: 10px 0;
216 | }
217 |
218 | #btn_grp {
219 | clear: both;
220 | text-align: right;
221 | }
222 |
--------------------------------------------------------------------------------
/elements/ng-download-item.css:
--------------------------------------------------------------------------------
1 | ng-download-item {
2 | background: #fff;
3 | border-bottom: 1px solid #dadada;
4 | display: flex;
5 | padding: 15px;
6 | position: relative;
7 | width: 430px;
8 | box-sizing: border-box;
9 | }
10 |
11 | ng-download-item .info {
12 | flex: 1;
13 | padding: 0 10px;
14 | position: relative;
15 | }
16 |
17 | ng-download-item .title {
18 | font-size: 16px;
19 | font-weight: 400;
20 | padding-bottom: 5px;
21 | word-wrap: break-word;
22 | }
23 |
24 | ng-download-item .postinfo {
25 | color: #999;
26 | font-size: 12px;
27 | word-wrap: break-word;
28 | }
29 |
30 | ng-download-item .info .progress_piece {
31 | background-color: #c0c0c0;
32 | height: 8px;
33 | transition: width 0.6s ease;
34 | width: 0;
35 | }
36 |
37 | ng-download-item .progress_bar {
38 | background-color: #e0e0e0;
39 | display: flex;
40 | }
41 |
42 | ng-download-item .progress_piece.paused {
43 | opacity: 0.2;
44 | }
45 |
46 | ng-download-item .progress_bar.paused {
47 | background-color: #fff59d;
48 | }
49 |
50 | ng-download-item .progress_bar.paused .progress_piece {
51 | background-color: #fdd835;
52 | }
53 |
54 | ng-download-item .progress_bar.postprocess {
55 | background-color: #d7ccc8;
56 | }
57 |
58 | ng-download-item .progress_bar.postprocess .progress_piece {
59 | background-color: #795548;
60 | }
61 |
62 | ng-download-item .progress_bar.downloading {
63 | background-color: rgba(76, 175, 80, 0.2);
64 | }
65 |
66 | ng-download-item .progress_bar.downloading .progress_piece {
67 | background-color: #59b75c;
68 | }
69 |
70 | ng-download-item .text {
71 | flex: 1;
72 | padding: 5px 0;
73 | }
74 |
75 | ng-download-item .healthWarning {
76 | background-color: #ff5722;
77 | border-radius: 2px;
78 | box-shadow:
79 | 0 3px 1px -2px rgba(0, 0, 0, 0.2),
80 | 0 2px 2px 0 rgba(0, 0, 0, 0.14),
81 | 0 1px 5px 0 rgba(0, 0, 0, 0.12);
82 | color: #fff;
83 | font-size: 80%;
84 | margin: 3px;
85 | max-height: 16px;
86 | opacity: 0;
87 | padding: 4px;
88 | transform: scale(0);
89 | transition:
90 | opacity 0.2s cubic-bezier(0.4, 0.0, 1, 1),
91 | transform 0.2s cubic-bezier(0.4, 0.0, 1, 1);
92 | }
93 |
94 | ng-download-item .categoryBadge {
95 | align-self: center;
96 | border-radius: 2px;
97 | cursor: pointer;
98 | font-size: 80%;
99 | margin: 0 0 0 0;
100 | padding: 4px;
101 | }
102 |
103 | ng-download-item .categoryBadge.add {
104 | font-size: 80%;
105 | font-style: italic;
106 | opacity: 0.8;
107 | padding: 4px;
108 | }
109 |
110 | ng-download-item .healthWarning.active {
111 | opacity: 1;
112 | transform: scale(1);
113 | }
114 |
115 | ng-download-item .menu {
116 | border-radius: 50%;
117 | cursor: pointer;
118 | position: relative;
119 | }
120 |
121 | ng-download-item .contextmenu {
122 | background: #fafafa;
123 | box-shadow:
124 | 0 2px 2px 0 rgba(0, 0, 0, 0.14),
125 | 0 1px 5px 0 rgba(0, 0, 0, 0.12),
126 | 0 3px 1px -2px rgba(0, 0, 0, 0.2);
127 | display: block;
128 | min-width: 150px;
129 | opacity: 0;
130 | padding: 0;
131 | position: absolute;
132 | right: 15px;
133 | transform: scale(0, 0);
134 | transform-origin: top right;
135 | transition:
136 | z-index 0s linear 0.2s,
137 | transform 0.1s linear,
138 | opacity 0.2s linear;
139 | z-index: -1;
140 | }
141 |
142 | ng-download-item .contextmenu.show {
143 | opacity: 1;
144 | transform: scale(1, 1);
145 | transition-delay: 0s;
146 | z-index: 2;
147 | }
148 |
149 | ng-download-item .contextmenu ul {
150 | list-style-type: none;
151 | margin: 0;
152 | padding: 0;
153 | }
154 |
155 | ng-download-item .contextmenu ul li {
156 | cursor: pointer;
157 | font-size: 16px;
158 | font-weight: 400;
159 | line-height: 24px;
160 | padding: 7px 15px;
161 | }
162 |
163 | ng-download-item .contextmenu ul li:hover {
164 | background: #e5e5e5;
165 | }
166 |
167 | ng-download-item .contextmenu i {
168 | color: rgba(0, 0, 0, 0.54);
169 | padding-right: 10px;
170 | vertical-align: top;
171 | z-index: 1;
172 | }
173 |
174 | ng-download-item .icons {
175 | display: inline-block;
176 | font-size: 24px;
177 | font-style: normal;
178 | font-weight: 400;
179 | height: 1em;
180 | letter-spacing: normal;
181 | line-height: 1;
182 | text-rendering: optimizeLegibility;
183 | text-transform: none;
184 | width: 1em;
185 | word-wrap: normal;
186 | }
187 |
188 | ng-download-item .bottom_box {
189 | display: flex;
190 | }
191 |
192 | /* Highlight on click */
193 |
194 | ng-download-item .ripple {
195 | --ripple-left: 0;
196 | --ripple-top: 0;
197 | overflow: hidden;
198 | position: relative;
199 | transform: translate3d(0, 0, 0);
200 | transition:
201 | box-shadow .3s ease-in,
202 | background-color .3s ease-in;
203 | }
204 |
205 | .ripple:hover {
206 | background-color: rgba(0,0,0,0.05);
207 | box-shadow:
208 | 0 3px 1px -2px rgba(0, 0, 0, 0.2),
209 | 0 2px 2px 0 rgba(0, 0, 0, 0.14),
210 | 0 1px 5px 0 rgba(0, 0, 0, 0.12);
211 | }
212 |
213 | .ripple:after {
214 | background: rgba(0, 0, 0, 1.2);
215 | border-radius: 50%;
216 | content: '';
217 | display: block;
218 | height: 10%;
219 | left: var(--ripple-left);
220 | opacity: 0;
221 | pointer-events: none;
222 | position: absolute;
223 | top: var(--ripple-top);
224 | transform: scale(0);
225 | width: 10%;
226 | }
227 |
228 | .ripple.animate:after {
229 | animation: ripple-in 325ms ease-in-out;
230 | animation-fill-mode: forwards;
231 | }
232 |
233 | @keyframes ripple-in {
234 | 100% {
235 | opacity: .2;
236 | transform: scale(27);
237 | }
238 | }
239 |
--------------------------------------------------------------------------------
/sites/tests/binsearch.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | |
7 | |
8 |
15 | |
16 | |
17 | |
| |
18 |
19 | Search!
20 |
21 |
41 |
42 |
43 |
44 | Results
45 |
97 |
98 |
99 | Generated in 0.03 seconds. |
100 | |
101 |
102 |
103 | | |
104 |
105 | Copyright © 2006-2011 binsearch - disclaimer
107 | |
108 | |
109 | |
110 |
111 |
112 |
113 |