diff --git a/package.json b/package.json index 440d86e..ddc4c74 100755 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "private": true, "scripts": { "build": "NODE_ENV=production webpack --progress --config webpack.app.js", - "app": "NODE_ENV=development webpack --progress --config webpack.app.js", + "test": "NODE_ENV=development webpack --progress --config webpack.app.js", "watch": "NODE_ENV=development webpack --progress --watch --config webpack.app.js", "lint": "eslint --ext .js,.vue src", "lint:fix": "eslint --ext .js,.vue src --fix", diff --git a/src/App.vue b/src/App.vue index 3b202c4..aa142d4 100644 --- a/src/App.vue +++ b/src/App.vue @@ -79,7 +79,7 @@ export default { helper.info(message); } let url = formWrapper.getAttribute("action"); - Http.getInstance(url) + helper.httpClient(url) .setData(formData) .setHandler(function (data) { successCallback(data, element); @@ -100,7 +100,7 @@ export default { contentTable.getInstance().loading(); let url = formWrapper.getAttribute("action"); - Http.getInstance(url) + helper.httpClient(url) .setData(formData) .setHandler(function (data) { if (data && data.title) { @@ -123,7 +123,7 @@ export default { if (files) { let formWrapper = element.closest("form"); let url = formWrapper.getAttribute("action"); - return Http.getInstance(url) + return helper.httpClient(url) .setHandler(function (data) { successCallback(data, element); }) diff --git a/src/actions/buttonActions.js b/src/actions/buttonActions.js index 3b521b2..7d588cd 100644 --- a/src/actions/buttonActions.js +++ b/src/actions/buttonActions.js @@ -29,7 +29,7 @@ const buttonHandler = (event, type) => { console.log("gid is not set!"); } } - Http.getInstance(url).setErrorHandler(function (xhr, textStatus, error) { + helper.httpClient(url).setErrorHandler(function (xhr, textStatus, error) { console.log(error); }).setHandler(function (data) { if (data.hasOwnProperty('error')) { diff --git a/src/components/folderSettings.vue b/src/components/folderSettings.vue index 770ce14..c8ab43d 100644 --- a/src/components/folderSettings.vue +++ b/src/components/folderSettings.vue @@ -23,7 +23,7 @@ export default { } let data = { ncd_downloader_dir: path }; let url = helper.generateUrl("/apps/ncdownloader/personal/save"); - Http.getInstance(url) + helper.httpClient(url) .setData(data) .setHandler((data) => { if (data.status) { diff --git a/src/index.js b/src/index.js index 94bf2bd..5931d97 100644 --- a/src/index.js +++ b/src/index.js @@ -73,7 +73,7 @@ window.addEventListener('DOMContentLoaded', function () { element.textContent = t("ncdownloader", "Stop Aria2"); } } - Http.getInstance(url).setHandler(function (data) { + helper.httpClient(url).setHandler(function (data) { callback(parent, oldHtml, data); }).send(); }) diff --git a/src/lib/http.ts b/src/lib/http.ts index c1e2e7a..71d688e 100644 --- a/src/lib/http.ts +++ b/src/lib/http.ts @@ -3,49 +3,131 @@ type httpData = { } type httpMethod = "POST" | "HEAD" | "GET"; type handler = (data: any) => void; -const Http = class { +type httpClient = XMLHttpRequest; +type requestOptions = { + [key: string]: any; + headers: Headers; +} +export const Http = class { data: httpData; url: string; method: httpMethod; - dataType: string; - xhr: XMLHttpRequest; + contentType: string; + client: httpClient; handler: handler; errorHandler: handler; + legacyHttp: boolean; + headers: Headers - constructor(url: string) { + constructor(url: string, legacyHttp: boolean = false) { this.url = url; this.method = 'POST'; this.data = null; - this.dataType = 'application/json'; - this.xhr = new XMLHttpRequest(); + this.contentType = 'application/json'; + this.legacyHttp = legacyHttp + if (!legacyHttp) { + this.headers = new Headers(); + } } - static getInstance(url: string) { - return new Http(url); + isFetchAPISupported() { + return (typeof fetch == 'function') + } + static create(url: string, legacyHttp: boolean = false) { + return new Http(url, legacyHttp); } setData(data: httpData) { this.data = data return this } - setDataType(value: string) { - this.dataType = value; + setContentType(value: string) { + this.contentType = value; + 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') - 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)); + if (!this.isFetchAPISupported() || this.legacyHttp) { + this.client = new XMLHttpRequest(); + this.client.open(this.method, this.url); + if (this.contentType) + this.setHeader('Content-Type', this.contentType); + if (token) { + this.setHeader('requesttoken', token) + this.setHeader('OCS-APIREQUEST', 'true') + } + let callback = this.handler; + this.client.onreadystatechange = () => { + if (this.client.readyState === XMLHttpRequest.DONE) { + let status = this.client.status; + const contentType = this.client.getResponseHeader("Content-Type") + if (status === 0 || (status >= 200 && status < 400)) { + if (typeof callback === 'function' && contentType.indexOf("application/json") !== -1) { + callback(JSON.parse(this.client.response)); + } + else { + callback(this.client.response); + } + } + } + } + this.client.onerror = this.errorHandler; + let params = this.data ? JSON.stringify(this.data) : null + this.client.send(params); + } else { + let options = this.getRequestOpts(); + fetch(options).then(response => { + if (response.status !== 200) { + console.log('Network failures. Status Code: ' + response.status); + return; + } + const contentType = response.headers.get('content-type'); + if (contentType && contentType.indexOf("application/json") !== -1) { + response.json().then(data => { + this.handler(data) + }) + } else { + response.text().then(data => { + this.handler(data) + }) + } + + }).catch(this.errorHandler) } - this.xhr.onerror = this.errorHandler; - this.xhr.send(JSON.stringify(this.data)); + + } + setHeader(key: string, val: string) { + if (this.legacyHttp) { + this.client.setRequestHeader(key, val) + } else { + this.headers.set(key, val); + } + } + appendHeader(key: string, val: string) { + this.headers.append(key, val); + } + getRequestOpts() { + this.setHeader('content-type', this.contentType); + let token; + if (token = this.getToken()) { + this.setHeader('requesttoken', token) + this.setHeader('OCS-APIREQUEST', 'true') + } + if (this.method == 'POST' && this.data) { + var body = JSON.stringify(this.data); + } + let options: requestOptions = { + headers: this.headers, + method: this.method, + body: body, + mode: 'cors', + cache: 'default' + } + return new Request(this.url, options); } getToken() { - return document.getElementsByTagName('head')[0].getAttribute('data-requesttoken') + if (typeof document == "undefined") { + return null + } + return window.document.getElementsByTagName('head')[0].getAttribute('data-requesttoken') } setUrl(url: string) { this.url = url @@ -60,19 +142,19 @@ const Http = class { return this; } setErrorHandler(handler: handler) { - this.errorHandler = handler + this.errorHandler = handler || function (error) { console.log(error) }; return this; } upload(file: File) { const fd = new FormData(); - this.xhr.open(this.method, this.url, true); + this.client.open(this.method, this.url, true); let callback = this.handler; - this.xhr.onload = () => { + this.client.onload = () => { if (typeof callback === 'function') - callback(JSON.parse(this.xhr.response)); + callback(JSON.parse(this.client.response)); } fd.append('torrentfile', file); - return this.xhr.send(fd); + return this.client.send(fd); } } diff --git a/src/settings.js b/src/settings.js index 71e00dd..f980174 100644 --- a/src/settings.js +++ b/src/settings.js @@ -44,7 +44,7 @@ window.addEventListener('DOMContentLoaded', function () { OC_msg.finishedError('#ncdownloader-message-banner', 'invalid options: ' + badOptions.join(',')); return; } - Http.getInstance(url).setData(data).setHandler(function (data) { + helper.httpClient(url).setData(data).setHandler(function (data) { if (data.hasOwnProperty("error")) { OC_msg.finishedError('#ncdownloader-message-banner', data.error); } else if (data.hasOwnProperty("message")) { @@ -115,7 +115,7 @@ window.addEventListener('DOMContentLoaded', function () { e.preventDefault(); this.parentNode.remove(); }) - Http.getInstance(generateUrl("/apps/ncdownloader/personal/aria2/get")).setHandler(function (data) { + helper.httpClient(generateUrl("/apps/ncdownloader/personal/aria2/get")).setHandler(function (data) { if (!data) { return; } @@ -127,7 +127,7 @@ window.addEventListener('DOMContentLoaded', function () { settingsForm.getInstance().render(input); }).send(); - Http.getInstance(generateUrl("/apps/ncdownloader/personal/youtube-dl/get")).setHandler(function (data) { + helper.httpClient(generateUrl("/apps/ncdownloader/personal/youtube-dl/get")).setHandler(function (data) { if (!data) { return; } diff --git a/src/settingsBar.vue b/src/settingsBar.vue index 9dde049..c650ae0 100644 --- a/src/settingsBar.vue +++ b/src/settingsBar.vue @@ -70,7 +70,7 @@ export default { data[name] = value ? 1 : 0; let path = (name == "ncd_disable_bt") ? "/admin/save" : "/personal/save"; const url = helper.generateUrl(basePath + path); - Http.getInstance(url) + helper.httpClient(url) .setData(data) .setHandler((resp) => { if (resp["message"]) { diff --git a/src/utils/helper.js b/src/utils/helper.js index 178313f..a7b615a 100644 --- a/src/utils/helper.js +++ b/src/utils/helper.js @@ -34,7 +34,7 @@ const helper = { scanFolder(forceScan = false, path = "/apps/ncdownloader/scanfolder") { let url = helper.generateUrl(path); return new Promise((resolve) => { - Http.getInstance(url).setData({ "force": forceScan }).setHandler(function (data) { + helper.httpClient(url).setData({ "force": forceScan }).setHandler(function (data) { resolve(data.status); }).send(); }); @@ -48,7 +48,7 @@ const helper = { refresh(path) { path = path || "/apps/ncdownloader/status/active"; let url = helper.generateUrl(path); - Http.getInstance(url).setHandler(function (data) { + helper.httpClient(url).setHandler(function (data) { if (data && data.row) { contentTable.getInstance(data.title, data.row).create(); } else { @@ -144,7 +144,7 @@ const helper = { }, getCounters() { let url = helper.generateUrl("apps/ncdownloader/counters"); - Http.getInstance(url).setMethod("GET").setHandler(function (data) { + helper.httpClient(url).setMethod("GET").setHandler(function (data) { if (data["counter"]) helper.updateCounter(data["counter"]); }).send(); @@ -299,10 +299,13 @@ const helper = { getSettings(key, defaultValue = null, type = 2) { let url = helper.generateUrl("/apps/ncdownloader/getsettings"); return new Promise(resolve => { - Http.getInstance(url).setData({ name: key, type: type, default: defaultValue }).setHandler(data => { + helper.httpClient(url).setData({ name: key, type: type, default: defaultValue }).setHandler(data => { resolve(data) }).send() }) + }, + httpClient(url) { + return new Http.create(url, true) } }