first commit
This commit is contained in:
94
src/OC/msg.js
Normal file
94
src/OC/msg.js
Normal file
@@ -0,0 +1,94 @@
|
||||
import $ from 'jquery'
|
||||
|
||||
/**
|
||||
* A little class to manage a status field for a "saving" process.
|
||||
* It can be used to display a starting message (e.g. "Saving...") and then
|
||||
* replace it with a green success message or a red error message.
|
||||
*
|
||||
* @namespace OC.msg
|
||||
*/
|
||||
export default {
|
||||
/**
|
||||
* Displayes a "Saving..." message in the given message placeholder
|
||||
*
|
||||
* @param {Object} selector Placeholder to display the message in
|
||||
*/
|
||||
startSaving(selector) {
|
||||
this.startAction(selector, t('core', 'Saving …'))
|
||||
},
|
||||
|
||||
/**
|
||||
* Displayes a custom message in the given message placeholder
|
||||
*
|
||||
* @param {Object} selector Placeholder to display the message in
|
||||
* @param {string} message Plain text message to display (no HTML allowed)
|
||||
*/
|
||||
startAction(selector, message) {
|
||||
$(selector).text(message)
|
||||
.removeClass('success')
|
||||
.removeClass('error')
|
||||
.stop(true, true)
|
||||
.show()
|
||||
},
|
||||
|
||||
/**
|
||||
* Displayes an success/error message in the given selector
|
||||
*
|
||||
* @param {Object} selector Placeholder to display the message in
|
||||
* @param {Object} response Response of the server
|
||||
* @param {Object} response.data Data of the servers response
|
||||
* @param {string} response.data.message Plain text message to display (no HTML allowed)
|
||||
* @param {string} response.status is being used to decide whether the message
|
||||
* is displayed as an error/success
|
||||
*/
|
||||
finishedSaving(selector, response) {
|
||||
this.finishedAction(selector, response)
|
||||
},
|
||||
|
||||
/**
|
||||
* Displayes an success/error message in the given selector
|
||||
*
|
||||
* @param {Object} selector Placeholder to display the message in
|
||||
* @param {Object} response Response of the server
|
||||
* @param {Object} response.data Data of the servers response
|
||||
* @param {string} response.data.message Plain text message to display (no HTML allowed)
|
||||
* @param {string} response.status is being used to decide whether the message
|
||||
* is displayed as an error/success
|
||||
*/
|
||||
finishedAction(selector, response) {
|
||||
if (response.status === 'success') {
|
||||
this.finishedSuccess(selector, response.data.message)
|
||||
} else {
|
||||
this.finishedError(selector, response.data.message)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Displayes an success message in the given selector
|
||||
*
|
||||
* @param {Object} selector Placeholder to display the message in
|
||||
* @param {string} message Plain text success message to display (no HTML allowed)
|
||||
*/
|
||||
finishedSuccess(selector, message) {
|
||||
$(selector).text(message)
|
||||
.addClass('success')
|
||||
.removeClass('error')
|
||||
.stop(true, true)
|
||||
.delay(3000)
|
||||
.fadeOut(900)
|
||||
.show()
|
||||
},
|
||||
|
||||
/**
|
||||
* Displayes an error message in the given selector
|
||||
*
|
||||
* @param {Object} selector Placeholder to display the message in
|
||||
* @param {string} message Plain text error message to display (no HTML allowed)
|
||||
*/
|
||||
finishedError(selector, message) {
|
||||
$(selector).text(message)
|
||||
.addClass('error')
|
||||
.removeClass('success')
|
||||
.show()
|
||||
},
|
||||
}
|
||||
3
src/aria2Options.js
Normal file
3
src/aria2Options.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const aria2Options = ["ca-certificate", "certificate", "dht-file-path", "dht-file-path6", "dir", "input-file", "load-cookies", "log", "metalink-file", "netrc-path", "on-bt-download-complete", "on-download-complete", "on-download-error", "on-download-start", "on-download-stop", "on-download-pause", "out", "private-key", "rpc-certificate", "rpc-private-key", "save-cookies", "save-session", "server-stat-if", "server-stat-of", "torrent-file", "all-proxy", "all-proxy-passwd", "all-proxy-user", "allow-overwrite", "allow-piece-length-change", "always-resume", "async-dns", "auto-file-renaming", "bt-enable-hook-after-hash-check", "bt-enable-lpd", "bt-exclude-tracker", "bt-external-ip", "bt-force-encryption", "bt-hash-check-seed", "bt-load-saved-metadata", "bt-max-peers", "bt-metadata-only", "bt-min-crypto-level", "bt-prioritize-piece", "bt-remove-unselected-file", "bt-request-peer-speed-limit", "bt-require-crypto", "bt-save-metadata", "bt-seed-unverified", "bt-stop-timeout", "bt-tracker", "bt-tracker-connect-timeout", "bt-tracker-interval", "bt-tracker-timeout", "check-integrity", "checksum", "conditional-get", "connect-timeout", "content-disposition-default-utf8", "continue", "dir", "dry-run", "enable-http-keep-alive", "enable-http-pipelining", "enable-mmap", "enable-peer-exchange", "file-allocation", "follow-metalink", "follow-torrent", "force-save", "ftp-passwd", "ftp-pasv", "ftp-proxy", "ftp-proxy-passwd", "ftp-proxy-user", "ftp-reuse-connection", "ftp-type", "ftp-user", "gid", "hash-check-only", "header", "http-accept-gzip", "http-auth-challenge", "http-no-cache", "http-passwd", "http-proxy", "http-proxy-passwd", "http-proxy-user", "http-user", "https-proxy", "https-proxy-passwd", "https-proxy-user", "index-out", "lowest-speed-limit", "max-connection-per-server", "max-download-limit", "max-file-not-found", "max-mmap-limit", "max-resume-failure-tries", "max-tries", "max-upload-limit", "metalink-base-uri", "metalink-enable-unique-protocol", "metalink-language", "metalink-location", "metalink-os", "metalink-preferred-protocol", "metalink-version", "min-split-size", "no-file-allocation-limit", "no-netrc", "no-proxy", "out", "parameterized-uri", "pause", "pause-metadata", "piece-length", "proxy-method", "realtime-chunk-checksum", "referer", "remote-time", "remove-control-file", "retry-wait", "reuse-uri", "rpc-save-upload-metadata", "seed-ratio", "seed-time", "select-file", "split", "ssh-host-key-md", "stream-piece-selector", "timeout", "uri-selector", "use-head", "user-agent", "dry-run", "metalink-base-uri", "parameterized-uri", "pause", "piece-length", "rpc-save-upload-metadata", "bt-max-peers", "bt-request-peer-speed-limit", "bt-remove-unselected-file", "force-save", "max-download-limit", "max-upload-limit", "bt-max-open-files", "download-result", "keep-unfinished-download-result", "log", "log-level", "max-concurrent-downloads", "max-download-result", "max-overall-download-limit", "max-overall-upload-limit", "optimize-concurrent-downloads", "save-cookies", "save-session", "server-stat-of"];
|
||||
|
||||
export default aria2Options
|
||||
306
src/autoComplete.js
Normal file
306
src/autoComplete.js
Normal file
@@ -0,0 +1,306 @@
|
||||
class autoComplete {
|
||||
options;
|
||||
static UP = 38;
|
||||
static DOWN = 40;
|
||||
static ENTER = 13;
|
||||
static ESC = 27;
|
||||
static entryClass = ".suggestion-item";
|
||||
static entryClassContainer = ".suggestion-container";
|
||||
constructor(options) {
|
||||
this.options = {
|
||||
selector: 0,
|
||||
source: 0,
|
||||
minChars: 3,
|
||||
delay: 150,
|
||||
offsetLeft: 0,
|
||||
offsetTop: 1,
|
||||
cache: 1,
|
||||
menuClass: '',
|
||||
renderItem: function (item, search) {
|
||||
search = search.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
var re = new RegExp(`(${search.split(' ').join('|')})`, "gi");
|
||||
return `<div class="suggestion-item" data-val="${item}">${item.replace(re, "<b>$1</b>")}</div>`;
|
||||
},
|
||||
onSelect: function (e, term, item) { }
|
||||
};
|
||||
for (let k in this.options) {
|
||||
if (options.hasOwnProperty(k)) this.options[k] = options[k];
|
||||
}
|
||||
if (typeof this.options.selector !== 'string' && !(this.options.selector instanceof NodeList))
|
||||
throw ("invalid selecor!");
|
||||
this.elements = typeof this.options.selector == 'object' ? this.options.selector : document.querySelectorAll(this.options.selector);
|
||||
}
|
||||
static getInstance(options) {
|
||||
return new autoComplete(options);
|
||||
}
|
||||
attachData(element) {
|
||||
element.rect = element.getBoundingClientRect();
|
||||
element.sgBox = this.createSuggestionBox();
|
||||
element.options = this.options;
|
||||
}
|
||||
run() {
|
||||
for (const element of this.elements) {
|
||||
this.init(element);
|
||||
}
|
||||
}
|
||||
|
||||
init(element) {
|
||||
element.autocompleteAttr = element.getAttribute('autocomplete');
|
||||
element.setAttribute('autocomplete', 'off');
|
||||
element.cache = {};
|
||||
element.lastValue = '';
|
||||
this.attachData(element);
|
||||
this.attach('resize', window, function (e) {
|
||||
autoComplete.updateSuggestionBox(element);
|
||||
});
|
||||
document.body.appendChild(element.sgBox);
|
||||
this.live('suggestion-item', 'mouseleave', function (e) {
|
||||
var sel = element.sgBox.querySelector('.suggestion-item.selected');
|
||||
if (sel)
|
||||
setTimeout(function () { sel.className = sel.className.replace('selected', ''); }, 20);
|
||||
}, element.sgBox);
|
||||
|
||||
this.live('suggestion-item', 'mouseover', function (e) {
|
||||
var sel = element.sgBox.querySelector('.suggestion-item.selected');
|
||||
if (sel) {
|
||||
sel.classList.remove("selected");
|
||||
}
|
||||
this.className += ' selected';
|
||||
}, element.sgBox);
|
||||
const selectHandler = function (selected, element, e) {
|
||||
if (autoComplete.hasClass(selected, 'suggestion-item')) {
|
||||
let v = selected.getAttribute('data-val');
|
||||
element.value = v;
|
||||
element.options.onSelect(e, v, selected);
|
||||
element.sgBox.style.display = 'none';
|
||||
}
|
||||
}
|
||||
this.live('suggestion-item', 'mousedown,pointerdown', function (e) {
|
||||
e.stopPropagation();
|
||||
//this refers to the found element within;
|
||||
let selected = this;
|
||||
selectHandler(selected, element, e);
|
||||
}, element.sgBox);
|
||||
|
||||
this.attach('blur', element, autoComplete.blurCallback);
|
||||
this.attach('keydown', element, autoComplete.keyDownCallback);
|
||||
this.attach('keyup', element, autoComplete.keyUpCallback);
|
||||
if (!this.options.minChars)
|
||||
this.attach('focus', element, autoComplete.focusCallback);
|
||||
}
|
||||
static suggest(element, data) {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
let sgBox = element.sgBox, options = element.options;
|
||||
var val = element.value;
|
||||
element.cache[val] = data;
|
||||
if (data.length && val.length >= options.minChars) {
|
||||
var s = '';
|
||||
for (var i = 0; i < data.length; i++) s += options.renderItem(data[i], val);
|
||||
sgBox.innerHTML = s;
|
||||
autoComplete.updateSuggestionBox(element, 0);
|
||||
}
|
||||
else {
|
||||
sgBox.style.display = 'none';
|
||||
}
|
||||
}
|
||||
static hasClass(el, className) {
|
||||
return el.classList ? el.classList.contains(className) : new RegExp('\\b' + className + '\\b').test(el.className);
|
||||
}
|
||||
attach(eventType, target, selector, callback) {
|
||||
if (typeof selector === 'function' && !callback) {
|
||||
callback = selector;
|
||||
selector = target;
|
||||
}
|
||||
if (typeof target === 'object') {
|
||||
if (target.attachEvent) {
|
||||
target.attachEvent('on' + eventType, function (e) {
|
||||
callback.call(target, e);
|
||||
});
|
||||
}
|
||||
else {
|
||||
target.addEventListener(eventType, function (e) {
|
||||
callback.call(target, e);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
let el = document.querySelector(target);
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
el.addEventListener(eventType, function (e) {
|
||||
let element = e.target;
|
||||
if (element === this) {
|
||||
callback.call(element, e);
|
||||
return;
|
||||
}
|
||||
for (; element && element != this; element = element.parentNode) {
|
||||
if (element.matches(selector)) {
|
||||
callback.call(element, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
live(elClass, event, cb, context) {
|
||||
if (typeof event === 'string' && event.indexOf(',')) {
|
||||
var events = event.split(',');
|
||||
}
|
||||
for (const event of events) {
|
||||
this.attach(event, context || document, function (e) {
|
||||
var found, el = e.target || e.srcElement;
|
||||
while (el && !(found = autoComplete.hasClass(el, elClass)))
|
||||
el = el.parentElement;
|
||||
if (found) cb.call(el, e);
|
||||
});
|
||||
}
|
||||
}
|
||||
static blurCallback(e) {
|
||||
let element = this
|
||||
let sgBox = element.sgBox;
|
||||
let hoverActive;
|
||||
try {
|
||||
hoverActive = document.querySelector('.suggestion-container:hover');
|
||||
} catch (e) {
|
||||
hoverActive = 0;
|
||||
}
|
||||
if (!hoverActive) {
|
||||
element.lastValue = element.value;
|
||||
sgBox.style.display = 'none';
|
||||
setTimeout(function () { sgBox.style.display = 'none'; }, 350);
|
||||
} else if (element !== document.activeElement) {
|
||||
setTimeout(function () {
|
||||
element.focus();
|
||||
}, 20);
|
||||
}
|
||||
}
|
||||
static keyUpCallback(e) {
|
||||
let element = this;
|
||||
let sgBox = element.sgBox, options = element.options;
|
||||
var key = window.event ? e.keyCode : e.which;
|
||||
if (!key || (key < 35 || key > 40) && ![autoComplete.ENTER, autoComplete.ESC].includes(key)) {
|
||||
var val = element.value;
|
||||
if (val.length >= options.minChars) {
|
||||
if (val != element.lastValue) {
|
||||
element.lastValue = val;
|
||||
clearTimeout(element.timer);
|
||||
if (options.cache) {
|
||||
if (val in element.cache) {
|
||||
autoComplete.suggest(element, element.cache[val]); return;
|
||||
}
|
||||
for (var i = 1; i < val.length - options.minChars; i++) {
|
||||
var part = val.slice(0, val.length - i);
|
||||
if (part in element.cache && !element.cache[part].length) {
|
||||
autoComplete.suggest([]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
const msuggest = function (data) {
|
||||
autoComplete.suggest(element, data);
|
||||
}
|
||||
element.timer = setTimeout(function () { options.source(val, msuggest) }, options.delay);
|
||||
}
|
||||
} else {
|
||||
element.lastValue = val;
|
||||
sgBox.style.display = 'none';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static keyDownCallback(e) {
|
||||
let element = this;
|
||||
let sgBox = element.sgBox, options = element.options;
|
||||
var key = window.event ? e.keyCode : e.which;
|
||||
// down =40, up =38
|
||||
if ((key == 40 || key == 38) && sgBox.innerHTML) {
|
||||
var next, sel = sgBox.querySelector('.suggestion-item.selected');
|
||||
if (!sel) {
|
||||
next = (key == 40) ? sgBox.querySelector('.suggestion-item') : sgBox.childNodes[scBox.childNodes.length - 1]; // first : last
|
||||
next.className += ' selected';
|
||||
element.value = next.getAttribute('data-val');
|
||||
} else {
|
||||
next = (key == 40) ? sel.nextSibling : sel.previousSibling;
|
||||
if (next) {
|
||||
sel.className = sel.className.replace('selected', '');
|
||||
next.className += ' selected';
|
||||
element.value = next.getAttribute('data-val');
|
||||
}
|
||||
else {
|
||||
sel.className = sel.className.replace('selected', '');
|
||||
element.value = element.lastValue; next = 0;
|
||||
}
|
||||
}
|
||||
autoComplete.updateSuggestionBox(element, 0, next);
|
||||
return false;
|
||||
//ESC = 27
|
||||
} else if (key == 27) {
|
||||
element.value = element.lastValue;
|
||||
sgBox.style.display = 'none';
|
||||
//enter = 13,tab = 9
|
||||
} else if (key == 13 || key == 9) {
|
||||
var sel = sgBox.querySelector('.suggestion-item.selected');
|
||||
if (sel && sgBox.style.display != 'none') {
|
||||
options.onSelect(e, sel.getAttribute('data-val'), sel);
|
||||
setTimeout(function () { sgBox.style.display = 'none'; }, 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static focusCallback(e) {
|
||||
let element = this;
|
||||
element.lastValue = '\n';
|
||||
autoComplete.keyUpCallback(e)
|
||||
}
|
||||
|
||||
static getMaxHeight(element) {
|
||||
let style = window.getComputedStyle ? getComputedStyle(element, null) : element.currentStyle;
|
||||
return parseInt(style.maxHeight);
|
||||
}
|
||||
|
||||
static updatePosition(element, rect, options) {
|
||||
element.style.left = Math.round(rect.left + (window.pageXOffset || document.documentElement.scrollLeft) + options.offsetLeft) + 'px';
|
||||
element.style.top = Math.round(rect.bottom + (window.pageYOffset || document.documentElement.scrollTop) + options.offsetTop) + 'px';
|
||||
element.style.width = Math.round(rect.right - rect.left) + 'px';
|
||||
}
|
||||
|
||||
static updateSuggestionBox(element, resize, next) {
|
||||
let sgBox = element.sgBox, rect = element.getBoundingClientRect(), options = element.options;
|
||||
autoComplete.updatePosition(sgBox, rect, options);
|
||||
if (resize && !next) {
|
||||
return;
|
||||
}
|
||||
sgBox.style.display = 'block';
|
||||
if (!sgBox.maxHeight) {
|
||||
sgBox.maxHeight = autoComplete.getMaxHeight(sgBox);
|
||||
}
|
||||
if (!sgBox.suggestionHeight) {
|
||||
sgBox.suggestionHeight = sgBox.querySelector('.suggestion-item').offsetHeight;
|
||||
}
|
||||
|
||||
if (!sgBox.suggestionHeight) {
|
||||
return;
|
||||
}
|
||||
if (!next) {
|
||||
sgBox.scrollTop = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
let scrTop = sgBox.scrollTop
|
||||
let selTop = next.getBoundingClientRect().top - sgBox.getBoundingClientRect().top;
|
||||
if (selTop + sgBox.suggestionHeight - sgBox.maxHeight > 0) {
|
||||
sgBox.scrollTop = selTop + sgBox.suggestionHeight + scrTop - sgBox.maxHeight;
|
||||
} else if (selTop < 0) {
|
||||
sgBox.scrollTop = selTop + scrTop;
|
||||
}
|
||||
}
|
||||
createSuggestionBox() {
|
||||
let sgBox = document.createElement('div');
|
||||
sgBox.classList.add('suggestion-container');
|
||||
//suggestionBox.classList.add(options.menuClass);
|
||||
return sgBox;
|
||||
}
|
||||
}
|
||||
export default autoComplete;
|
||||
39
src/buttonActions.js
Normal file
39
src/buttonActions.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import $ from 'jquery'
|
||||
import Http from './http'
|
||||
import helper from './helper'
|
||||
const buttonHandler = (event, type) => {
|
||||
let element = event.target;
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
let url = element.getAttribute("path");
|
||||
let row, data = {};
|
||||
let removeRow = true;
|
||||
if (row = element.closest('.table-row-search')) {
|
||||
data['form_input_text'] = row.dataset.link;
|
||||
} else {
|
||||
row = element.closest('.table-row')
|
||||
data = row.dataset;
|
||||
if (!data.gid) {
|
||||
console.log("gid is not set!");
|
||||
}
|
||||
}
|
||||
Http.getInstance(url).setErrorHandler(function (xhr, textStatus, error) {
|
||||
console.log(error);
|
||||
}).setHandler(function (data) {
|
||||
if (data.hasOwnProperty('error')) {
|
||||
helper.message(data['error']);
|
||||
return;
|
||||
}
|
||||
if (data.hasOwnProperty('result')) {
|
||||
helper.message("Success for " + data['result']);
|
||||
}
|
||||
if (row && removeRow)
|
||||
row.remove();
|
||||
}).setData(data).send();
|
||||
|
||||
}
|
||||
export default {
|
||||
run: function () {
|
||||
$("#ncdownloader-table-wrapper").on("click", ".table-cell-action-item .button-container button", e => buttonHandler(e, ''));
|
||||
}
|
||||
}
|
||||
42
src/eventHandler.js
Normal file
42
src/eventHandler.js
Normal file
@@ -0,0 +1,42 @@
|
||||
const eventHandler = {
|
||||
add: function (eventType, target, selector, callback) {
|
||||
if (typeof selector === 'function' && !callback) {
|
||||
callback = selector;
|
||||
selector = target;
|
||||
}
|
||||
if (typeof target === 'object') {
|
||||
if (target.attachEvent) {
|
||||
target.attachEvent('on' + eventType, function (e) {
|
||||
callback.call(target, e);
|
||||
});
|
||||
}
|
||||
else {
|
||||
target.addEventListener(eventType, function (e) {
|
||||
callback.call(target, e);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
let el = document.querySelector(target);
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
el.addEventListener(eventType, function (e) {
|
||||
let element = e.target;
|
||||
if (element === this && selector === target) {
|
||||
callback.call(element, e);
|
||||
return;
|
||||
}
|
||||
for (; element && element != this; element = element.parentNode) {
|
||||
if (element.matches(selector)) {
|
||||
callback.call(element, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
off: function (element, eventType, callback) {
|
||||
element.removeEventListener(eventType, callback);
|
||||
}
|
||||
}
|
||||
export default eventHandler;
|
||||
149
src/helper.js
Normal file
149
src/helper.js
Normal file
@@ -0,0 +1,149 @@
|
||||
import $ from 'jquery'
|
||||
import {
|
||||
generateUrl
|
||||
} from '@nextcloud/router'
|
||||
import Toastify from 'toastify-js'
|
||||
import "toastify-js/src/toastify.css"
|
||||
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
|
||||
import nctable from './ncTable';
|
||||
import Http from './http'
|
||||
|
||||
const helper = {
|
||||
generateUrl: generateUrl,
|
||||
loop(callback, delay, ...args) {
|
||||
callback(...args);
|
||||
clearTimeout(helper.timeoutID);
|
||||
this.polling(callback, delay, ...args);
|
||||
},
|
||||
enabledPolling: 1,
|
||||
trim(string, char) {
|
||||
return string.split(char).filter(Boolean).join(char)
|
||||
},
|
||||
isHtml(string) {
|
||||
const htmlRegex = new RegExp('^<([a-z]+)[^>]+>(.*?)</\\1>', 'i');
|
||||
return htmlRegex.test(string);
|
||||
},
|
||||
ucfirst(string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1)
|
||||
},
|
||||
polling(callback, delay, ...args) {
|
||||
self = this;
|
||||
helper.timeoutID = setTimeout(function () {
|
||||
if (self.enabledPolling) {
|
||||
callback(...args);
|
||||
self.polling(callback, delay, ...args);
|
||||
}
|
||||
}, delay);
|
||||
},
|
||||
isURL(url) {
|
||||
const pattern = new RegExp('^(https?:\\/\\/)?' + // protocol
|
||||
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
|
||||
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
|
||||
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
|
||||
'(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
|
||||
'(\\#[-a-z\\d_]*)?$', 'i'); // fragment locator
|
||||
return pattern.test(url);
|
||||
},
|
||||
isMagnetURI(url) {
|
||||
const magnetURI = /^magnet:\?xt=urn:[a-z0-9]+:[a-z0-9]{32,40}&dn=.+&tr=.+$/i;
|
||||
|
||||
return magnetURI.test(url.trim());
|
||||
},
|
||||
message: function (message) {
|
||||
Toastify({
|
||||
text: message,
|
||||
duration: 3000,
|
||||
newWindow: true,
|
||||
close: true,
|
||||
gravity: "top", // `top` or `bottom`
|
||||
position: "center", // `left`, `center` or `right`
|
||||
backgroundColor: "linear-gradient(to right, rgb(26, 28, 27), rgb(126, 138, 105))",
|
||||
stopOnFocus: true, // Prevents dismissing of toast on hover
|
||||
onClick: function () { } // Callback after click
|
||||
}).showToast();
|
||||
},
|
||||
aria2Toggle: function (data) {
|
||||
if (!data.status) {
|
||||
return;
|
||||
}
|
||||
if (!data.status && data.error) {
|
||||
return;
|
||||
}
|
||||
let $element = $("#start-aria2 button");
|
||||
let aria2 = $element.attr("data-aria2");
|
||||
if (aria2 === 'on') {
|
||||
$element.attr("data-aria2", "off").html(t("ncdownloader", "Start Aria2"));
|
||||
} else {
|
||||
$element.attr("data-aria2", "on").html(t("ncdownloader", "Stop Aria2"));
|
||||
}
|
||||
},
|
||||
getPathLast: function (path) {
|
||||
return path.substring(path.lastIndexOf('/') + 1)
|
||||
},
|
||||
updateCounter(data) {
|
||||
for (let key in data) {
|
||||
const counter = document.getElementById(key + "-downloads-counter")
|
||||
counter.innerHTML = '<div class="number">' + data[key] + '</div>';
|
||||
}
|
||||
},
|
||||
refresh(path) {
|
||||
path = path || "/apps/ncdownloader/status/active";
|
||||
let url = helper.generateUrl(path);
|
||||
Http.getInstance(url).setHandler(function (data) {
|
||||
if (data && data.row) {
|
||||
nctable.getInstance(data.title, data.row).create();
|
||||
} else {
|
||||
nctable.getInstance().noData();
|
||||
}
|
||||
if (data.counter)
|
||||
helper.updateCounter(data.counter);
|
||||
}).send();
|
||||
},
|
||||
html2DOM: function (htmlString) {
|
||||
const parser = new window.DOMParser();
|
||||
let doc = parser.parseFromString(htmlString, "text/html")
|
||||
return doc.querySelector("div");
|
||||
},
|
||||
makePair: function (data, prefix = "aria2-settings") {
|
||||
for (let key in data) {
|
||||
let index;
|
||||
if ((index = key.indexOf(prefix + "-key-")) !== -1) {
|
||||
let valueKey = prefix + "-value-" + key.substring(key.lastIndexOf('-') + 1);
|
||||
if (!data[valueKey]) continue;
|
||||
let newkey = data[key];
|
||||
data[newkey] = data[valueKey];
|
||||
delete data[key];
|
||||
delete data[valueKey];
|
||||
}
|
||||
}
|
||||
},
|
||||
getData(selector) {
|
||||
const element = typeof selector === "object" ? selector : document.getElementById(selector)
|
||||
const data = {}
|
||||
data['path'] = element.getAttribute('path') || '';
|
||||
//if the targeted element is not of input or select type, search for such elements below it
|
||||
if (!['SELECT', 'INPUT'].includes(element.nodeName.toUpperCase())) {
|
||||
const nodeList = element.querySelectorAll('input,select')
|
||||
|
||||
for (let i = 0; i < nodeList.length; i++) {
|
||||
const element = nodeList[i]
|
||||
if (element.hasAttribute('type') && element.getAttribute('type') === 'button') {
|
||||
continue
|
||||
}
|
||||
const key = element.getAttribute('id')
|
||||
data[key] = element.value
|
||||
for (let prop in element.dataset) {
|
||||
data[prop] = element.dataset[prop];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let prop in element.dataset) {
|
||||
data[prop] = element.dataset[prop];
|
||||
}
|
||||
const key = element.getAttribute('id')
|
||||
data[key] = element.value
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
export default helper
|
||||
52
src/http.js
Normal file
52
src/http.js
Normal file
@@ -0,0 +1,52 @@
|
||||
const Http = class {
|
||||
data;
|
||||
constructor(url) {
|
||||
this.url = url;
|
||||
this.method = 'POST';
|
||||
this.data = null;
|
||||
this.dataType = 'application/json';
|
||||
this.xhr = new XMLHttpRequest();
|
||||
}
|
||||
static getInstance(url) {
|
||||
return new Http(url);
|
||||
}
|
||||
setData(data) {
|
||||
this.data = data
|
||||
return this
|
||||
}
|
||||
send() {
|
||||
let token = this.getToken();
|
||||
this.xhr.open(this.method, this.url);
|
||||
this.xhr.setRequestHeader('requesttoken', token)
|
||||
this.xhr.setRequestHeader('OCS-APIREQUEST', 'true')
|
||||
this.xhr.setRequestHeader('Content-Type', this.dataType);
|
||||
let callback = this.handler;
|
||||
this.xhr.onload = () => {
|
||||
if (typeof callback === 'function')
|
||||
callback(JSON.parse(this.xhr.response));
|
||||
}
|
||||
this.xhr.onerror = this.errorHandler;
|
||||
this.xhr.send(JSON.stringify(this.data));
|
||||
}
|
||||
getToken() {
|
||||
return document.getElementsByTagName('head')[0].getAttribute('data-requesttoken')
|
||||
}
|
||||
setUrl(url) {
|
||||
this.url = url
|
||||
return this
|
||||
}
|
||||
setMethod(method) {
|
||||
this.method = method
|
||||
return this
|
||||
}
|
||||
setHandler(handler) {
|
||||
this.handler = handler || function (data) { };
|
||||
return this;
|
||||
}
|
||||
setErrorHandler(handler) {
|
||||
this.errorHandler = handler
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export default Http
|
||||
48
src/index.js
Normal file
48
src/index.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import helper from './helper'
|
||||
import $ from 'jquery'
|
||||
import Http from './http'
|
||||
//import actionLinks from './actionLinks'
|
||||
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
|
||||
import inputAction from './inputAction'
|
||||
import updatePage from './updatePage'
|
||||
import buttonActions from './buttonActions'
|
||||
import inputBox from './inputBox'
|
||||
'use strict'
|
||||
const basePath = "/apps/ncdownloader";
|
||||
$(document).on('ajaxSend', function (elm, xhr, settings) {
|
||||
let token = document.getElementsByTagName('head')[0].getAttribute('data-requesttoken')
|
||||
if (settings.crossDomain === false) {
|
||||
xhr.setRequestHeader('requesttoken', token)
|
||||
xhr.setRequestHeader('OCS-APIREQUEST', 'true')
|
||||
}
|
||||
})
|
||||
window.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
document.addEventListener("keydown", function (event) {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
);
|
||||
inputAction.run();
|
||||
updatePage.run();
|
||||
buttonActions.run();
|
||||
|
||||
$("#ncdownloader-form-wrapper").append(inputBox.getInstance(t("ncdownloader", 'New Download')).create());
|
||||
$("#start-aria2").on("click", function (e) {
|
||||
const path = basePath + "/aria2/start";
|
||||
let url = helper.generateUrl(path);
|
||||
Http.getInstance(url).setHandler(function (data) {
|
||||
helper.aria2Toggle(data);
|
||||
}).send();
|
||||
})
|
||||
|
||||
$('#ncdownloader-user-settings button').on("click", function (e) {
|
||||
let link = helper.generateUrl(e.target.getAttribute('path'));
|
||||
window.location.href = link;
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
|
||||
104
src/inputAction.js
Normal file
104
src/inputAction.js
Normal file
@@ -0,0 +1,104 @@
|
||||
import helper from './helper'
|
||||
import $ from 'jquery'
|
||||
import Http from './http'
|
||||
import 'tippy.js/dist/tippy.css';
|
||||
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
|
||||
import inputBox from './inputBox'
|
||||
import nctable from './ncTable'
|
||||
|
||||
const basePath = "/apps/ncdownloader";
|
||||
|
||||
const createInputBox = (event, type) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
//let id = event.target.closest("div").getAttribute('id');
|
||||
let inputID = event.target.closest("div").dataset.inputbox;
|
||||
let inputElement = inputID ? document.getElementById(inputID) : null;
|
||||
if (inputElement) {
|
||||
inputElement.remove();
|
||||
}
|
||||
let height = $(window).scrollTop();
|
||||
if (height > 50)
|
||||
$("html, body").animate({ scrollTop: 0 }, "fast");
|
||||
let name;
|
||||
switch (type) {
|
||||
case "ytdl":
|
||||
name = t("ncdownloader", 'YTDL Download');
|
||||
break;
|
||||
case "search":
|
||||
name = t("ncdownloader", 'Search');
|
||||
break;
|
||||
default:
|
||||
name = t("ncdownloader", 'New Download');
|
||||
}
|
||||
let container;
|
||||
if (type === 'search') {
|
||||
container = inputBox.getInstance(name, type).addSpinner().create();
|
||||
//container.appendChild(inputBox.createLoading());
|
||||
} else {
|
||||
container = inputBox.getInstance(name, type).create();
|
||||
}
|
||||
$("#ncdownloader-form-wrapper").append(container);
|
||||
}
|
||||
|
||||
const toggleButton = element => {
|
||||
if (!element.previousSibling) {
|
||||
return;
|
||||
}
|
||||
if (element.style.display === 'none') {
|
||||
element.style.display = 'block'
|
||||
element.previousSibling.style.display = 'none';
|
||||
} else {
|
||||
element.style.display = 'none'
|
||||
element.previousSibling.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
const inputHandler = (event) => {
|
||||
event.preventDefault();
|
||||
let element = event.target;
|
||||
// element.textContent = '';
|
||||
//$(element).append(inputBox.createLoading());
|
||||
toggleButton(element);
|
||||
let inputData = helper.getData('form-input-wrapper');
|
||||
let inputValue = inputData.form_input_text;
|
||||
if (inputData.type !== 'search' && !helper.isURL(inputValue) && !helper.isMagnetURI(inputValue)) {
|
||||
helper.message(t("ncdownloader", "Invalid url"));
|
||||
return;
|
||||
}
|
||||
if (inputData.type === 'ytdl') {
|
||||
helper.message(t("ncdownloader", "YTDL Download initiated"));
|
||||
}
|
||||
if (inputData.type === 'search') {
|
||||
nctable.getInstance().loading();
|
||||
}
|
||||
const successCallback = (data, element) => {
|
||||
//data = JSON.parse(data.target.response)
|
||||
if (data !== null && data.hasOwnProperty("file")) {
|
||||
helper.message(t("ncdownloader", "Downloading" + " " + data.file));
|
||||
}
|
||||
toggleButton(element);
|
||||
if (data && data.title) {
|
||||
helper.enabledPolling = 0;
|
||||
const tableInst = nctable.getInstance(data.title, data.row);
|
||||
tableInst.actionLink = false;
|
||||
tableInst.rowClass = "table-row-search";
|
||||
tableInst.create();
|
||||
}
|
||||
}
|
||||
const path = inputData.path || basePath + "/new";
|
||||
let url = helper.generateUrl(path);
|
||||
Http.getInstance(url).setData(inputData).setHandler(function (data) {
|
||||
successCallback(data, element);
|
||||
}).send();
|
||||
}
|
||||
|
||||
export default {
|
||||
run: function () {
|
||||
$("#app-navigation").on("click", "#new-download-ytdl", (event) => createInputBox(event, 'ytdl'));
|
||||
$("#app-navigation").on("click", "#new-download", (event) => createInputBox(event, ''));
|
||||
$("#app-navigation").on("click", "#torrent-search-button", (event) => createInputBox(event, 'search'));
|
||||
|
||||
$("#ncdownloader-form-wrapper").on("click", "#form-input-button", (event) => inputHandler(event))
|
||||
}
|
||||
}
|
||||
62
src/inputBox.js
Normal file
62
src/inputBox.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
|
||||
import helper from './helper'
|
||||
|
||||
|
||||
class inputBox {
|
||||
constructor(name, id) {
|
||||
this.name = name;
|
||||
this.container = this._createForm();
|
||||
this.textInput = this._createTextInput(id);
|
||||
this.controlsContainer = this._createControlsContainer();
|
||||
}
|
||||
static getInstance(name, id) {
|
||||
return new inputBox(name, id);
|
||||
}
|
||||
create() {
|
||||
this.container.appendChild(this.textInput);
|
||||
this.controlsContainer.appendChild(this._createControls());
|
||||
this.container.appendChild(this.controlsContainer);
|
||||
return this.container;
|
||||
}
|
||||
_createControlsContainer() {
|
||||
let div = document.createElement("div");
|
||||
div.classList.add("controls-container");
|
||||
return div;
|
||||
}
|
||||
_createForm() {
|
||||
let container = document.createElement("form");
|
||||
container.classList.add("form-input-wrapper");
|
||||
container.setAttribute('id', 'form-input-wrapper');
|
||||
return container;
|
||||
}
|
||||
_createTextInput(id) {
|
||||
id = id || 'general';
|
||||
let textInput = document.createElement('input');
|
||||
textInput.setAttribute('type', 'text');
|
||||
textInput.setAttribute('id', "form_input_text");
|
||||
textInput.setAttribute('data-type', id);
|
||||
textInput.setAttribute('value', '');
|
||||
textInput.classList.add('form-input-text');
|
||||
return textInput;
|
||||
}
|
||||
_createControls() {
|
||||
let button = document.createElement('button');
|
||||
button.setAttribute('type', this.name);
|
||||
button.setAttribute('id', 'form-input-button');
|
||||
//buttonInput.setAttribute('value', t('ncdownloader', helper.ucfirst(name)));
|
||||
let text = document.createTextNode(t('ncdownloader', helper.ucfirst(this.name)));
|
||||
button.appendChild(text);
|
||||
return button;
|
||||
}
|
||||
addSpinner() {
|
||||
const parser = new window.DOMParser();
|
||||
let htmlString = '<button class="bs-spinner"><span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" disabled></span><span class="visually-hidden">Loading...</span></button>'
|
||||
let doc = parser.parseFromString(htmlString, "text/html")
|
||||
let element = doc.querySelector(".bs-spinner");
|
||||
element.style.display = 'none';
|
||||
this.controlsContainer.appendChild(element);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
export default inputBox;
|
||||
145
src/ncTable.js
Normal file
145
src/ncTable.js
Normal file
@@ -0,0 +1,145 @@
|
||||
import helper from './helper'
|
||||
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
|
||||
|
||||
class ncTable {
|
||||
actionLink = true;
|
||||
bodyClass = "ncdownloader-table-data";
|
||||
rowClass = "table-row";
|
||||
headingClass = "table-heading";
|
||||
cellClass = "table-cell";
|
||||
//this is the parent element the table is going to append to
|
||||
tableContainer = 'ncdownloader-table-wrapper';
|
||||
numRow;
|
||||
table;
|
||||
|
||||
constructor(heading, rows) {
|
||||
this.table = document.getElementById(this.tableContainer);
|
||||
if (heading && rows) {
|
||||
this.table.innerHTML = '';
|
||||
this.rows = rows;
|
||||
this.heading = heading;
|
||||
this.actionButtons = [];
|
||||
}
|
||||
}
|
||||
static getInstance(heading, row) {
|
||||
return new ncTable(heading, row);
|
||||
}
|
||||
create() {
|
||||
let thead = this.createHeading()
|
||||
let tbody = this.createRow();
|
||||
this.table.appendChild(thead);
|
||||
this.table.appendChild(tbody);
|
||||
return this;
|
||||
}
|
||||
clear() {
|
||||
this.table.innerHTML = '';
|
||||
}
|
||||
loading() {
|
||||
let htmlStr = '<div class="text-center"><div class="spinner-border" role="status"> <span class="visually-hidden">Loading...</span></div></div>'
|
||||
this.table.innerHTML = htmlStr;
|
||||
return this;
|
||||
}
|
||||
noData() {
|
||||
this.clear();
|
||||
let div = document.createElement('div');
|
||||
div.classList.add("no-items");
|
||||
div.appendChild(document.createTextNode(t("ncdownloader", 'No items')));
|
||||
this.table.appendChild(div);
|
||||
}
|
||||
createHeading(prefix = "table-heading") {
|
||||
let thead = document.createElement("section");
|
||||
thead.classList.add(this.headingClass);
|
||||
let headRow = document.createElement("header");
|
||||
headRow.classList.add(this.rowClass);
|
||||
thead.classList.add(this.headingClass);
|
||||
this.heading.forEach(name => {
|
||||
let rowItem = document.createElement("div");
|
||||
rowItem.classList.add(prefix + "-" + name.toLowerCase());
|
||||
rowItem.classList.add(this.cellClass);
|
||||
let text = document.createTextNode(t("ncdownloader", helper.ucfirst(name)));
|
||||
rowItem.appendChild(text);
|
||||
headRow.appendChild(rowItem);
|
||||
})
|
||||
thead.appendChild(headRow);
|
||||
return thead;
|
||||
}
|
||||
createRow() {
|
||||
let tbody = document.createElement("section");
|
||||
tbody.classList.add(this.bodyClass);
|
||||
tbody.classList.add("table-body");
|
||||
let row;
|
||||
for (const element of this.rows) {
|
||||
if (element === null) {
|
||||
continue;
|
||||
}
|
||||
row = document.createElement("div");
|
||||
row.classList.add(this.rowClass);
|
||||
let text;
|
||||
for (let key in element) {
|
||||
if (key.substring(0, 4) == 'data') {
|
||||
let name = key.replace("_", "-");
|
||||
row.setAttribute(name, element[key]);
|
||||
row.setAttribute("id", element[key]);
|
||||
continue;
|
||||
}
|
||||
let rowItem = document.createElement("div");
|
||||
rowItem.classList.add(this.cellClass);
|
||||
if (key === 'actions') {
|
||||
rowItem.classList.add([this.cellClass, "action-item"].join("-"));
|
||||
let container = document.createElement("div");
|
||||
container.classList.add("button-container");
|
||||
element[key].forEach(value => {
|
||||
container.appendChild(this.createActionButton(value.name, value.path));
|
||||
})
|
||||
rowItem.appendChild(container);
|
||||
row.appendChild(rowItem);
|
||||
continue;
|
||||
}
|
||||
if (typeof element[key] === 'object') {
|
||||
let child = element[key];
|
||||
let div;
|
||||
child.forEach(ele => {
|
||||
div = document.createElement('div');
|
||||
if (helper.isHtml(ele)) {
|
||||
div.innerHTML = ele;
|
||||
} else {
|
||||
text = document.createTextNode(ele);
|
||||
div.appendChild(text);
|
||||
}
|
||||
rowItem.appendChild(div);
|
||||
})
|
||||
rowItem.setAttribute("id", [this.cellClass, key].join("-"));
|
||||
row.appendChild(rowItem);
|
||||
continue;
|
||||
}
|
||||
text = document.createTextNode(element[key]);
|
||||
rowItem.appendChild(text);
|
||||
rowItem.setAttribute("id", [this.cellClass, key].join("-"));
|
||||
row.appendChild(rowItem);
|
||||
}
|
||||
tbody.appendChild(row);
|
||||
}
|
||||
return tbody;
|
||||
|
||||
}
|
||||
|
||||
createActionButton(name, path) {
|
||||
let button = document.createElement("button");
|
||||
button.classList.add("icon-" + name);
|
||||
button.setAttribute("path", path);
|
||||
return button;
|
||||
}
|
||||
|
||||
createActionCell(cell) {
|
||||
let div = document.createElement("div");
|
||||
let button = document.createElement("button");
|
||||
button.classList.add("icon-more", "action-button");
|
||||
button.setAttribute("id", "action-links-button");
|
||||
div.classList.add("action-item");
|
||||
div.appendChild(button);
|
||||
//div.appendChild(actionLinks);
|
||||
cell.appendChild(div);
|
||||
}
|
||||
}
|
||||
|
||||
export default ncTable;
|
||||
105
src/settings.js
Normal file
105
src/settings.js
Normal file
@@ -0,0 +1,105 @@
|
||||
import Http from './http'
|
||||
import OC_msg from './OC/msg'
|
||||
import {
|
||||
generateUrl
|
||||
} from '@nextcloud/router'
|
||||
import settingsForm from './settingsForm'
|
||||
import autoComplete from './autoComplete';
|
||||
import eventHandler from './eventHandler';
|
||||
import aria2Options from './aria2Options';
|
||||
import helper from './helper';
|
||||
'use strict';
|
||||
window.addEventListener('DOMContentLoaded', function () {
|
||||
eventHandler.add('click', '.ncdownloader-admin-settings', 'input[type="button"]', function (event) {
|
||||
e.stopPropagation();
|
||||
OC_msg.startSaving('#ncdownloader-message-banner');
|
||||
const target = this.getAttribute("data-rel");
|
||||
const path = inputData.url || "/apps/ncdownloader/admin/save";
|
||||
let url = generateUrl(path);
|
||||
Http.getInstance(url).setData(helper.getData(target)).setHandler(function () {
|
||||
OC_msg.finishedSuccess('#ncdownloader-message-banner', "OK");
|
||||
}).send();
|
||||
});
|
||||
eventHandler.add('click', '.ncdownloader-personal-settings', 'input[type="button"]', function (event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (event.target.matches('.custom-aria2-settings-container')) {
|
||||
return;
|
||||
}
|
||||
OC_msg.startSaving('#ncdownloader-message-banner');
|
||||
const target = this.getAttribute("data-rel");
|
||||
let inputData = helper.getData(target);
|
||||
const path = inputData.url || "/apps/ncdownloader/personal/save";
|
||||
let url = generateUrl(path);
|
||||
Http.getInstance(url).setData(inputData).setHandler(function (data) {
|
||||
OC_msg.finishedSuccess('#ncdownloader-message-banner', "OK");
|
||||
}).send();
|
||||
});
|
||||
eventHandler.add('click', '#custom-aria2-settings-container', "button.add-custom-aria2-settings", function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
let element = e.target;
|
||||
let selector = "#aria2-settings-key-1";
|
||||
let form = settingsForm.getInstance();
|
||||
let nodeList, key, value;
|
||||
nodeList = document.querySelectorAll("[id^='aria2-settings-key']")
|
||||
if (nodeList.length === 0) {
|
||||
key = "aria2-settings-key-1";
|
||||
value = "aria2-settings-value-1";
|
||||
} else {
|
||||
let index = nodeList.length + 1;
|
||||
key = "aria2-settings-key-" + index;
|
||||
value = "aria2-settings-value-" + index;
|
||||
selector = "[id^='aria2-settings-key']";
|
||||
}
|
||||
element.before(form.createCustomInput(key, value));
|
||||
//appended the latest one
|
||||
nodeList = document.querySelectorAll("[id^='aria2-settings-key']")
|
||||
try {
|
||||
autoComplete.getInstance({
|
||||
selector: (nodeList.length !== 0) ? nodeList : selector,
|
||||
minChars: 1,
|
||||
source: function (term, suggest) {
|
||||
term = term.toLowerCase();
|
||||
let suggestions = [], data = aria2Options;
|
||||
for (const item of data) {
|
||||
if (item.toLowerCase().indexOf(term, 0) !== -1) {
|
||||
suggestions.push(item);
|
||||
}
|
||||
}
|
||||
suggest(suggestions);
|
||||
}
|
||||
}).run();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
eventHandler.add("click", "#custom-aria2-settings-container", "button.save-custom-aria2-settings", function (e) {
|
||||
e.stopImmediatePropagation();
|
||||
let data = helper.getData(this.getAttribute("data-rel"));
|
||||
let url = generateUrl(data.path);
|
||||
delete data.path;
|
||||
OC_msg.startSaving('.message-banner');
|
||||
helper.makePair(data);
|
||||
Http.getInstance(url).setData(data).setHandler(function (data) {
|
||||
OC_msg.finishedSuccess('.message-banner', "OK");
|
||||
}).send();
|
||||
})
|
||||
eventHandler.add('click', '.ncdownloader-personal-settings', 'button.icon-close', function (e) {
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
this.parentNode.remove();
|
||||
})
|
||||
Http.getInstance(generateUrl("/apps/ncdownloader/personal/aria2/get")).setHandler(function (data) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
let input = [];
|
||||
for (let key in data) {
|
||||
input.push({ name: key, value: data[key], id: key });
|
||||
}
|
||||
settingsForm.getInstance().render(input);
|
||||
}).send();
|
||||
});
|
||||
110
src/settingsForm.js
Normal file
110
src/settingsForm.js
Normal file
@@ -0,0 +1,110 @@
|
||||
class settingsForm {
|
||||
parent = "custom-aria2-settings-container";
|
||||
constructor() {
|
||||
|
||||
}
|
||||
static getInstance() {
|
||||
return new this();
|
||||
}
|
||||
create(parent, element) {
|
||||
let label = this._createLabel(element.name, element.id)
|
||||
let input = this._createInput(element);
|
||||
//let saveBtn = this._createSaveBtn(element.id);
|
||||
let cancelBtn = this._createCancelBtn("has-content");
|
||||
let container = this._createContainer(element.id);
|
||||
[label, input, cancelBtn].forEach(ele => {
|
||||
container.appendChild(ele);
|
||||
})
|
||||
let button;
|
||||
if (button = parent.querySelector('button.add-custom-aria2-settings')) {
|
||||
return parent.insertBefore(container, button);
|
||||
}
|
||||
return parent.appendChild(container);
|
||||
}
|
||||
|
||||
createCustomInput(keyId, valueId) {
|
||||
let div = this._createContainer(keyId + "-container")
|
||||
div.appendChild(this._createInput({ id: keyId }));
|
||||
div.appendChild(this._createInput({ id: valueId }));
|
||||
div.appendChild(this._createCancelBtn());
|
||||
return div;
|
||||
}
|
||||
|
||||
createInput(element) {
|
||||
let div = document.createElement("div");
|
||||
div.classList.add(this.parent);
|
||||
/* element.forEach(element => {
|
||||
let label = document.createElement('label');
|
||||
label.setAttribute("for", element.id);
|
||||
let text = document.createTextNode(element.name);
|
||||
label.appendChild(text);
|
||||
div.appendChild(label);
|
||||
// div.appendChild(this._createInput(element));
|
||||
});*/
|
||||
div.appendChild(this._createInput(element));
|
||||
let button = document.createElement("button");
|
||||
//button.setAttribute("type",'button')
|
||||
button.classList.add("icon-close");
|
||||
div.appendChild(button);
|
||||
button = document.createElement("input");
|
||||
button.setAttribute('type', 'button');
|
||||
button.setAttribute('value', 'save');
|
||||
button.setAttribute("data-rel", this.parent);
|
||||
div.appendChild(button);
|
||||
return div;
|
||||
|
||||
}
|
||||
_createContainer(id) {
|
||||
let div = document.createElement("div");
|
||||
div.classList.add(id);
|
||||
return div;
|
||||
}
|
||||
_createCancelBtn(className = '') {
|
||||
let button = document.createElement("button");
|
||||
if (className)
|
||||
button.classList.add(className);
|
||||
//button.setAttribute("type",'button')
|
||||
button.classList.add("icon-close");
|
||||
return button;
|
||||
}
|
||||
_createSaveBtn(id) {
|
||||
let button = document.createElement("input");
|
||||
button.setAttribute('type', 'button');
|
||||
button.setAttribute('value', 'save');
|
||||
button.setAttribute("data-rel", id + "-container");
|
||||
return button;
|
||||
}
|
||||
_createLabel(name, id) {
|
||||
name = name.replace('_', '-');
|
||||
let label = document.createElement("lable");
|
||||
label.setAttribute("for", id);
|
||||
let text = document.createTextNode(name);
|
||||
label.appendChild(text);
|
||||
return label;
|
||||
}
|
||||
_createInput(data) {
|
||||
let input = document.createElement('input');
|
||||
let type = data.type || "text";
|
||||
let placeholder = data.placeholder || '';
|
||||
let value = data.value || placeholder;
|
||||
input.setAttribute('type', type);
|
||||
input.setAttribute('id', data.id);
|
||||
input.setAttribute("name", data.name || data.id);
|
||||
if (type === 'text') {
|
||||
input.setAttribute('value', value);
|
||||
input.setAttribute('placeholder', value);
|
||||
}
|
||||
input.classList.add('form-input-' + type);
|
||||
return input;
|
||||
}
|
||||
|
||||
render(data) {
|
||||
let parent = document.getElementById(this.parent)
|
||||
for (const element of data) {
|
||||
this.create(parent, element)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default settingsForm
|
||||
42
src/updatePage.js
Normal file
42
src/updatePage.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import helper from './helper'
|
||||
import $ from 'jquery'
|
||||
import Http from './http'
|
||||
const basePath = "/apps/ncdownloader/status/";
|
||||
const tableContainer = ".table";
|
||||
export default {
|
||||
run: function () {
|
||||
|
||||
const eventHandler = (event, type) => {
|
||||
event.preventDefault();
|
||||
const path = basePath + type;
|
||||
let name = type + "-downloads";
|
||||
//avoid repeated click
|
||||
if ($(tableContainer).attr("type") === name && helper.enabledPolling) {
|
||||
return;
|
||||
}
|
||||
helper.enabledPolling = 1;
|
||||
$(tableContainer).removeClass().addClass("table " + name);
|
||||
$(tableContainer).attr("type", name);
|
||||
let delay = 15000;
|
||||
if (name === "active-downloads") {
|
||||
delay = 1500;
|
||||
}
|
||||
helper.loop(helper.refresh, delay, ...[path])
|
||||
};
|
||||
$(".waiting-downloads").on("click", event => eventHandler(event, 'waiting'));
|
||||
$(".complete-downloads").on("click", event => eventHandler(event, 'complete'));
|
||||
$(".active-downloads").on("click", event => eventHandler(event, 'active'));
|
||||
$(".fail-downloads").on("click", event => eventHandler(event, 'fail'));
|
||||
|
||||
helper.refresh(basePath + "waiting")
|
||||
helper.refresh(basePath + "complete")
|
||||
helper.refresh(basePath + "fail")
|
||||
|
||||
helper.loop(helper.refresh, 1000, basePath + "active");
|
||||
|
||||
helper.polling(function (url) {
|
||||
url = helper.generateUrl(url);
|
||||
Http.getInstance(url).setMethod('GET').send();
|
||||
}, 60000, "/apps/ncdownloader/update");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user