added typescript files converted from js files

This commit is contained in:
huangjx
2022-02-26 11:47:23 +08:00
parent b689900012
commit 677c88e88c
13 changed files with 607 additions and 8 deletions

View File

@@ -32,6 +32,7 @@
"stylelint:fix": "stylelint src --fix" "stylelint:fix": "stylelint src --fix"
}, },
"dependencies": { "dependencies": {
"chalk": "^5.0.0",
"toastr": "^2.1.4", "toastr": "^2.1.4",
"v-tooltip": "^4.0.0-alpha.1" "v-tooltip": "^4.0.0-alpha.1"
}, },
@@ -59,6 +60,8 @@
"svgo-loader": "^3.0.0", "svgo-loader": "^3.0.0",
"tippy.js": "^6.3.2", "tippy.js": "^6.3.2",
"toastify-js": "^1.11.1", "toastify-js": "^1.11.1",
"ts-loader": "^9.2.6",
"typescript": "^4.5.5",
"url-loader": "^4.1.1", "url-loader": "^4.1.1",
"validator": "^13.6.0", "validator": "^13.6.0",
"vue": "^3.2.20", "vue": "^3.2.20",

View File

@@ -15,7 +15,7 @@ import toggleButton from "./components/toggleButton";
import helper from "./utils/helper"; import helper from "./utils/helper";
import { translate as t, translatePlural as n } from "@nextcloud/l10n"; import { translate as t, translatePlural as n } from "@nextcloud/l10n";
import Http from "./lib/http"; import Http from "./lib/http";
import nctable from "./lib/ncTable"; import contentTable from "./lib/contentTable";
const successCallback = (data, element) => { const successCallback = (data, element) => {
if (!data) { if (!data) {
@@ -83,7 +83,7 @@ export default {
return; return;
} }
helper.enabledPolling = 0; helper.enabledPolling = 0;
nctable.getInstance().loading(); contentTable.getInstance().loading();
let url = formWrapper.getAttribute("action"); let url = formWrapper.getAttribute("action");
Http.getInstance(url) Http.getInstance(url)
@@ -91,7 +91,7 @@ export default {
.setHandler(function (data) { .setHandler(function (data) {
if (data && data.title) { if (data && data.title) {
vm.$data.loading = 0; vm.$data.loading = 0;
const tableInst = nctable.getInstance(data.title, data.row); const tableInst = contentTable.getInstance(data.title, data.row);
tableInst.actionLink = false; tableInst.actionLink = false;
tableInst.rowClass = "table-row-search"; tableInst.rowClass = "table-row-search";
tableInst.create(); tableInst.create();

View File

@@ -1,7 +1,7 @@
import Http from '../lib/http' import Http from '../lib/http'
import helper from '../utils/helper' import helper from '../utils/helper'
import eventHandler from '../lib/eventHandler' import eventHandler from '../lib/eventHandler'
import Clipboard from '../utils/clipboard' import Clipboard from '../lib/clipboard'
import '../css/clipboard.scss'; import '../css/clipboard.scss';
const buttonHandler = (event, type) => { const buttonHandler = (event, type) => {

64
src/lib/clipboard.ts Normal file
View File

@@ -0,0 +1,64 @@
import Tooltip from "./tooltip";
class Clipboard {
text: string;
element: string | Element | HTMLElement;
constructor(element:string | Element | HTMLElement, text:string) {
this.element = typeof element == 'object' ? element : document.querySelector(element);
this.text = text || this.element.getAttribute("data-text");
}
_copy(text:string) {
let textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.top = "0";
textArea.style.left = "0";
textArea.style.position = "fixed";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
let result;
try {
result = document.execCommand('copy');
//console.log('copied using exceCommand');
} catch (err) {
console.error('failed to copy', err);
result = false;
} finally {
document.body.removeChild(textArea);
}
if (result) {
this.ShowMsg("Copied!");
}
}
ShowMsg(msg:string) {
let tip = new Tooltip(this.element, msg);
let html = tip.create('copy-alert').html();
document.body.appendChild(html);
const callback = (element:Element) => {
element.remove()
}
setTimeout(() => {
callback(html)
}, 1000);
}
Copy() {
if (!navigator.clipboard) {
return this._copy(this.text);
}
return navigator.clipboard.writeText(this.text).then(() => {
this.ShowMsg("Copied!");
}, function (err) {
console.error('failed to copy text: ', err);
});
}
}
export default Clipboard;

162
src/lib/contentTable.ts Normal file
View File

@@ -0,0 +1,162 @@
import helper from '../utils/helper'
interface Map {
[key: string]: string | {} | Array<any>
}
type rowData = Array<Map>
class contentTable {
actionLink: boolean = true;
bodyClass: string = "ncdownloader-table-data";
rowClass: string = "table-row";
headingClass: string = "table-heading";
cellClass: string = "table-cell";
//this is the parent element the table is going to append to
tableContainer: string = 'ncdownloader-table-wrapper';
numRow: number;
table: HTMLElement;
rows: rowData
heading: Array<string>
actionButtons: Array<{}>
constructor(heading: Array<string>, rows: rowData) {
this.table = document.getElementById(this.tableContainer) as HTMLElement;
if (heading && rows) {
this.table.innerHTML = '';
this.rows = rows;
this.heading = heading;
}
}
static getInstance(heading: Array<string>, rows: rowData) {
return new contentTable(heading, rows);
}
create(): contentTable {
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(helper.t('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(helper.t(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("_", "-");
if (typeof element[key] == "string") {
row.setAttribute(name, (<string>element[key]));
row.setAttribute("id", (<string>element[key]));
}
continue;
}
let rowItem = document.createElement("div");
rowItem.classList.add(this.cellClass);
if (key === 'actions' && Array.isArray(element[key])) {
let tmp = element[key] as Array<any>;
rowItem.classList.add([this.cellClass, "action-item"].join("-"));
let container = document.createElement("div");
container.classList.add("button-container");
tmp.forEach(value => {
if (!value.name) {
return;
}
let data = value.data || '';
container.appendChild(this.createActionButton(value.name, value.path, data));
})
rowItem.appendChild(container);
row.appendChild(rowItem);
} else if (Array.isArray(element[key])) {
let child = element[key] as any[];
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;
} else if (typeof element[key] === "string") {
text = document.createTextNode(element[key] as string);
rowItem.appendChild(text);
rowItem.setAttribute("id", [this.cellClass, key].join("-"));
row.appendChild(rowItem);
}
}
tbody.appendChild(row);
}
return tbody;
}
createActionButton(name: string, path: string, data: string) {
let button = document.createElement("button");
button.classList.add("icon-" + name);
button.setAttribute("path", path);
button.setAttribute("data", data);
if (name == 'refresh') {
name = helper.t('Redownload');
}
button.setAttribute("data-tippy-content", helper.ucfirst(name));
button.setAttribute("title", helper.ucfirst(name));
return button;
}
createActionCell(cell: HTMLElement) {
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 contentTable;

40
src/lib/eventHandler.ts Normal file
View File

@@ -0,0 +1,40 @@
type callback = (event: any) => void;
type target = string | Element | HTMLElement
const eventHandler = {
add: function (eventType: string, target: target, selector: string | callback | Element, callback?: callback) {
if (typeof selector === 'function' && !callback) {
callback = selector;
selector = target;
}
if (typeof target === 'object') {
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 as HTMLElement;
if (element === this && selector === target) {
callback.call(element, e);
return;
}
for (; element && element != this; element = element.parentElement) {
if (typeof selector === "string" && element.matches(selector)) {
callback.call(element, e);
break;
}
}
});
},
remove: function (element: target, eventType: string, callback: callback) {
(<Element>element).removeEventListener(eventType, callback);
}
}
export default eventHandler;

79
src/lib/http.ts Normal file
View File

@@ -0,0 +1,79 @@
type httpData = {
[key: string]: any
}
type httpMethod = "POST" | "HEAD" | "GET";
type handler = (data: any) => void;
const Http = class {
data: httpData;
url: string;
method: httpMethod;
dataType: string;
xhr: XMLHttpRequest;
handler: handler;
errorHandler: handler;
constructor(url: string) {
this.url = url;
this.method = 'POST';
this.data = null;
this.dataType = 'application/json';
this.xhr = new XMLHttpRequest();
}
static getInstance(url: string) {
return new Http(url);
}
setData(data: httpData) {
this.data = data
return this
}
setDataType(value: string) {
this.dataType = value;
}
send() {
let token = this.getToken();
this.xhr.open(this.method, this.url);
this.xhr.setRequestHeader('requesttoken', token)
this.xhr.setRequestHeader('OCS-APIREQUEST', 'true')
if (this.dataType)
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: string) {
this.url = url
return this
}
setMethod(method: httpMethod) {
this.method = method
return this
}
setHandler(handler: handler) {
this.handler = handler || function (data) { };
return this;
}
setErrorHandler(handler: handler) {
this.errorHandler = handler
return this;
}
upload(file: File) {
const fd = new FormData();
this.xhr.open(this.method, this.url, true);
let callback = this.handler;
this.xhr.onload = () => {
if (typeof callback === 'function')
callback(JSON.parse(this.xhr.response));
}
fd.append('torrentfile', file);
return this.xhr.send(fd);
}
}
export default Http

77
src/lib/msg.ts Normal file
View File

@@ -0,0 +1,77 @@
import { translate as t } from '@nextcloud/l10n'
type Response = {
data: { message: string };
status: string;
}
export default {
startSaving(selector: string) {
this.startAction(selector, t('core', 'Saving …'))
},
startAction(selector: string, message: string) {
let el = document.querySelector(selector) as HTMLElement;
el.style.removeProperty("display")
el.textContent = message;
},
finishedSaving(selector: string, response: Response) {
this.finishedAction(selector, response)
},
finishedAction(selector: string, response: Response) {
if (response.status === 'success') {
this.finishedSuccess(selector, response.data.message)
} else {
this.finishedError(selector, response.data.message)
}
},
finishedSuccess(selector: string, message: string) {
let el = document.querySelector(selector);
el.textContent = message;
if (el.classList.contains("error")) el.classList.remove("error");
el.classList.add("success");
this.fadeOut(el);
},
finishedError(selector: string, message: string) {
let el = document.querySelector(selector);
el.textContent = message;
if (el.classList.contains("success")) el.classList.remove("success");
el.classList.add("error");
},
fadeIn(element: HTMLElement, duration = 1000) {
(function increment() {
element.style.opacity = String(0);
element.style.removeProperty("display")
let opacity = parseFloat(element.style.opacity);
if (opacity !== 1) {
setTimeout(() => {
opacity += 0.1
increment();
}, duration / 10);
}
})();
},
fadeOut(element: HTMLElement, duration = 1000) {
let opacity = parseFloat(element.style.opacity) || 1;
(function decrement() {
if ((opacity -= 0.1) < 0) {
element.style.display = 'none'
element.style.removeProperty('opacity');
} else {
setTimeout(() => {
decrement();
}, duration / 10);
}
})();
},
show(el: HTMLElement) {
el.style.display = '';
},
hide(el: HTMLElement) {
el.style.display = 'none';
}
}

101
src/lib/settingsForm.ts Normal file
View File

@@ -0,0 +1,101 @@
type dataItems = {
name: string;
value: any;
id: string;
type?: "text" | "button" | "radio" | "checkbox";
placeholder?: string;
}
type data = Array<dataItems>
class settingsForm {
parent = "custom-aria2-settings-container";
constructor() {
}
static getInstance() {
return new this();
}
setParent(selector: string) {
this.parent = selector;
return this;
}
create(parent: HTMLElement, element: dataItems) {
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);
})
return parent.prepend(container);
}
createCustomInput(keyId:string, valueId:string) {
let div = this._createContainer(keyId + "-container")
let items:dataItems = {
id:keyId,
name:'',
value:''
}
div.appendChild(this._createInput(items));
items.id = valueId
div.appendChild(this._createInput(items));
div.appendChild(this._createCancelBtn());
return div;
}
_createContainer(id: string) {
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: string) {
let button = document.createElement("input");
button.setAttribute('type', 'button');
button.setAttribute('value', 'save');
button.setAttribute("data-rel", id + "-container");
return button;
}
_createLabel(name: string, id: string) {
name = name.replace('_', '-');
let label = document.createElement("lable");
label.setAttribute("for", id);
let text = document.createTextNode(name);
label.appendChild(text);
return label;
}
_createInput(data: dataItems) {
let input = document.createElement('input');
let type = data.type || "text";
let placeholder = data.placeholder || 'Leave empty if no value needed';
let value = data.value || '';
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', placeholder);
}
input.classList.add('form-input-' + type);
return input;
}
render(data: data) {
let parent = document.getElementById(this.parent)
for (const element of data) {
this.create(parent, element)
}
}
}
export default settingsForm

50
src/lib/tooltip.ts Normal file
View File

@@ -0,0 +1,50 @@
class Tooltip {
id = "ncdownloader-tooltip";
messageNode: HTMLDivElement;
style = {
display: '',
position: ''
};
text: string;
element: string | Element | HTMLElement;
constructor(element: string | HTMLElement | Element, text: string) {
this.element = typeof element == 'object' ? element : document.querySelector(element);
this.style = {
position: 'fixed',
display: 'block',
}
this.text = text || this.element.getAttribute("data-text");
}
create(id: string) {
this.messageNode = document.createElement("div");
this.messageNode.classList.add(this.id);
this.messageNode.setAttribute("id", this.id);
this.messageNode.style.display = this.style.display;
this.messageNode.style.position = this.style.position;
this.messageNode.style.zIndex = "10000";
let div = document.createElement('div');
div.setAttribute("id", id);
let text = document.createTextNode(this.text);
div.appendChild(text);
this.messageNode.appendChild(div);
this.setPosition();
return this;
}
render() {
document.body.appendChild(this.messageNode);
}
html() {
return this.messageNode;
}
setPosition(bottomMargin: number = 20, leftMargin: number = 0) {
let element = this.element as Element;
let rect = element.getBoundingClientRect();
let top = (rect['top'] + bottomMargin) + "px";
let left = (rect['left'] - leftMargin) + "px";
this.messageNode.style.top = top;
this.messageNode.style.left = left
}
}
export default Tooltip;

View File

@@ -4,7 +4,7 @@ import {
import Toastify from 'toastify-js' import Toastify from 'toastify-js'
import "toastify-js/src/toastify.css" import "toastify-js/src/toastify.css"
import { translate as t, translatePlural as n } from '@nextcloud/l10n' import { translate as t, translatePlural as n } from '@nextcloud/l10n'
import nctable from '../lib/ncTable'; import contentTable from '../lib/contentTable';
import Http from '../lib/http' import Http from '../lib/http'
const helper = { const helper = {
@@ -120,9 +120,9 @@ const helper = {
let url = helper.generateUrl(path); let url = helper.generateUrl(path);
Http.getInstance(url).setHandler(function (data) { Http.getInstance(url).setHandler(function (data) {
if (data && data.row) { if (data && data.row) {
nctable.getInstance(data.title, data.row).create(); contentTable.getInstance(data.title, data.row).create();
} else { } else {
nctable.getInstance().noData(); contentTable.getInstance().noData();
} }
if (data.counter) if (data.counter)
helper.updateCounter(data.counter); helper.updateCounter(data.counter);
@@ -190,7 +190,7 @@ const helper = {
}, },
showDownload() { showDownload() {
helper.showElement('download'); helper.showElement('download');
nctable.getInstance().clear(); contentTable.getInstance().clear();
helper.enabledPolling = 0; helper.enabledPolling = 0;
}, },
hideDownload() { hideDownload() {

18
tsconfig.json Normal file
View File

@@ -0,0 +1,18 @@
{
"compilerOptions": {
"outDir": "./dist/",
"noImplicitAny": true,
"module": "es6",
"target": "es5",
"jsx": "react",
"allowJs": true,
"moduleResolution": "node"
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}

View File

@@ -49,6 +49,11 @@ module.exports = {
use: 'vue-loader' use: 'vue-loader'
}, },
/*{ test: /\.css$/, use: ['vue-style-loader', 'css-loader'] },*/ /*{ test: /\.css$/, use: ['vue-style-loader', 'css-loader'] },*/
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
] ]
}, },
resolve: { resolve: {