Compare commits
2 Commits
bbb4a910a0
...
248fbd5f0f
Author | SHA1 | Date | |
---|---|---|---|
248fbd5f0f | |||
20e011bb55 |
@ -1,7 +1,7 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name 3CX TAPI
|
// @name 3CX TAPI
|
||||||
// @namespace http://cp-solutions.at
|
// @namespace http://cp-solutions.at
|
||||||
// @version 9.2.0
|
// @version 9.2.1
|
||||||
// @author Daniel Triendl <d.triendl@cp-solutions.at>
|
// @author Daniel Triendl <d.triendl@cp-solutions.at>
|
||||||
// @copyright Copyright 2021 CP Solutions GmbH
|
// @copyright Copyright 2021 CP Solutions GmbH
|
||||||
// @source https://source.cp-austria.at/git/CPATRD/3cx_tapi.git
|
// @source https://source.cp-austria.at/git/CPATRD/3cx_tapi.git
|
||||||
@ -9,8 +9,6 @@
|
|||||||
// @match https://192.168.0.154:5001/*
|
// @match https://192.168.0.154:5001/*
|
||||||
// @match https://cpsolution.my3cx.at:5001/*
|
// @match https://cpsolution.my3cx.at:5001/*
|
||||||
// @require https://cdn.jsdelivr.net/gh/CoeJoder/waitForKeyElements.js@v1.2/waitForKeyElements.js
|
// @require https://cdn.jsdelivr.net/gh/CoeJoder/waitForKeyElements.js@v1.2/waitForKeyElements.js
|
||||||
// @require https://cdn.jsdelivr.net/npm/axios@undefined/dist/axios.min.js
|
|
||||||
// @require https://cdn.jsdelivr.net/npm/axios-userscript-adapter@undefined/dist/axiosGmxhrAdapter.min.js
|
|
||||||
// @grant GM.xmlHttpRequest
|
// @grant GM.xmlHttpRequest
|
||||||
// @grant GM.notification
|
// @grant GM.notification
|
||||||
// @grant GM.getValue
|
// @grant GM.getValue
|
||||||
@ -4251,7 +4249,7 @@ function fireChangeEvents(element) {
|
|||||||
console.debug('change event dispatched for element: ' + element.id);
|
console.debug('change event dispatched for element: ' + element.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
;// ./node_modules/@trim21/gm-fetch/dist/index.mjs
|
;// ./src/gm-fetch/utils.ts
|
||||||
function parseRawHeaders(h) {
|
function parseRawHeaders(h) {
|
||||||
const s = h.trim();
|
const s = h.trim();
|
||||||
if (!s) {
|
if (!s) {
|
||||||
@ -4273,10 +4271,20 @@ function parseGMResponse(req, res) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
class ResImpl {
|
class ResImpl {
|
||||||
|
_bodyUsed;
|
||||||
|
rawBody;
|
||||||
|
init;
|
||||||
|
body;
|
||||||
|
headers;
|
||||||
|
redirected;
|
||||||
|
status;
|
||||||
|
statusText;
|
||||||
|
type;
|
||||||
|
url;
|
||||||
constructor(body, init) {
|
constructor(body, init) {
|
||||||
this.rawBody = body;
|
this.rawBody = body;
|
||||||
this.init = init;
|
this.init = init;
|
||||||
this.body = toReadableStream(body);
|
//this.body = toReadableStream(body);
|
||||||
const { headers, statusCode, statusText, finalUrl, redirected } = init;
|
const { headers, statusCode, statusText, finalUrl, redirected } = init;
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
this.status = statusCode;
|
this.status = statusCode;
|
||||||
@ -4350,14 +4358,18 @@ function decode(body) {
|
|||||||
});
|
});
|
||||||
return form;
|
return form;
|
||||||
}
|
}
|
||||||
function toReadableStream(value) {
|
/*
|
||||||
return new ReadableStream({
|
function toReadableStream(value: Blob) {
|
||||||
start(controller) {
|
return new ReadableStream({
|
||||||
controller.enqueue(value);
|
start(controller) {
|
||||||
controller.close();
|
controller.enqueue(value);
|
||||||
},
|
controller.close();
|
||||||
});
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
;// ./src/gm-fetch/index.ts
|
||||||
|
|
||||||
async function GM_fetch(input, init) {
|
async function GM_fetch(input, init) {
|
||||||
const request = new Request(input, init);
|
const request = new Request(input, init);
|
||||||
@ -4405,9 +4417,6 @@ function gmXHRMethod(method) {
|
|||||||
throw new Error(`unsupported http method ${method}`);
|
throw new Error(`unsupported http method ${method}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//# sourceMappingURL=index.mjs.map
|
|
||||||
|
|
||||||
;// ./src/call-history.ts
|
;// ./src/call-history.ts
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,8 +14,6 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
require: [
|
require: [
|
||||||
'https://cdn.jsdelivr.net/gh/CoeJoder/waitForKeyElements.js@v1.2/waitForKeyElements.js',
|
'https://cdn.jsdelivr.net/gh/CoeJoder/waitForKeyElements.js@v1.2/waitForKeyElements.js',
|
||||||
`https://cdn.jsdelivr.net/npm/axios@${pkg.dependencies.axios}/dist/axios.min.js`,
|
|
||||||
`https://cdn.jsdelivr.net/npm/axios-userscript-adapter@${pkg.dependencies['axios-userscript-adapter']}/dist/axiosGmxhrAdapter.min.js`
|
|
||||||
],
|
],
|
||||||
grant: [
|
grant: [
|
||||||
'GM.xmlHttpRequest',
|
'GM.xmlHttpRequest',
|
||||||
|
11
package-lock.json
generated
11
package-lock.json
generated
@ -1,14 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "3cx-tapi",
|
"name": "3cx-tapi",
|
||||||
"version": "9.1.1",
|
"version": "9.2.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "3cx-tapi",
|
"name": "3cx-tapi",
|
||||||
"version": "9.1.1",
|
"version": "9.2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@trim21/gm-fetch": "^0.1.15",
|
|
||||||
"chrono-node": "^2.7.7"
|
"chrono-node": "^2.7.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -1864,12 +1863,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@trim21/gm-fetch": {
|
|
||||||
"version": "0.1.15",
|
|
||||||
"resolved": "https://registry.npmjs.org/@trim21/gm-fetch/-/gm-fetch-0.1.15.tgz",
|
|
||||||
"integrity": "sha512-197WkYDd1XY8eDNWMBWSrAV77kCJzvh8EOnKSgIoHDXTb1AFLDN1iFnK/Y2x4XTOUDdOfUQd25rKUlVGWmQYhQ==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@types/estree": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "3cx-tapi",
|
"name": "3cx-tapi",
|
||||||
"description": "3CX CP Tapi and Projectmanager integration",
|
"description": "3CX CP Tapi and Projectmanager integration",
|
||||||
"version": "9.2.0",
|
"version": "9.2.1",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Daniel Triendl",
|
"name": "Daniel Triendl",
|
||||||
"email": "d.triendl@cp-solutions.at"
|
"email": "d.triendl@cp-solutions.at"
|
||||||
@ -24,8 +24,7 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chrono-node": "^2.7.7",
|
"chrono-node": "^2.7.7"
|
||||||
"@trim21/gm-fetch": "^0.1.15"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/greasemonkey": "^4.0.7",
|
"@types/greasemonkey": "^4.0.7",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import * as chrono from 'chrono-node'
|
import * as chrono from 'chrono-node'
|
||||||
import { TapiContact } from './tapi-contact'
|
import { TapiContact } from './tapi-contact'
|
||||||
import { extractNumber } from './utils'
|
import { extractNumber } from './utils'
|
||||||
import GM_fetch from '@trim21/gm-fetch'
|
import GM_fetch from './gm-fetch'
|
||||||
|
|
||||||
export class CallHistory {
|
export class CallHistory {
|
||||||
private callerIds: { [number: string]: TapiContact } = {}
|
private callerIds: { [number: string]: TapiContact } = {}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import GM_fetch from '@trim21/gm-fetch'
|
import GM_fetch from './gm-fetch'
|
||||||
import { TapiContact } from './tapi-contact'
|
import { TapiContact } from './tapi-contact'
|
||||||
import { extractNumber } from './utils'
|
import { extractNumber } from './utils'
|
||||||
|
|
||||||
|
55
src/gm-fetch/index.ts
Normal file
55
src/gm-fetch/index.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { parseGMResponse } from "./utils";
|
||||||
|
|
||||||
|
export default async function GM_fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
|
||||||
|
const request = new Request(input, init);
|
||||||
|
|
||||||
|
let data: string | undefined;
|
||||||
|
if (init?.body) {
|
||||||
|
data = await request.text();
|
||||||
|
}
|
||||||
|
|
||||||
|
return await XHR(request, init, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function XHR(request: Request, init: RequestInit | undefined, data: string | undefined): Promise<Response> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (request.signal && request.signal.aborted) {
|
||||||
|
return reject(new DOMException("Aborted", "AbortError"));
|
||||||
|
}
|
||||||
|
|
||||||
|
GM.xmlHttpRequest({
|
||||||
|
url: request.url,
|
||||||
|
method: gmXHRMethod(request.method.toUpperCase()),
|
||||||
|
headers: Object.fromEntries(new Headers(init?.headers).entries()),
|
||||||
|
data: data,
|
||||||
|
responseType: "blob",
|
||||||
|
onload(res) {
|
||||||
|
resolve(parseGMResponse(request, res));
|
||||||
|
},
|
||||||
|
onabort() {
|
||||||
|
reject(new DOMException("Aborted", "AbortError"));
|
||||||
|
},
|
||||||
|
ontimeout() {
|
||||||
|
reject(new TypeError("Network request failed, timeout"));
|
||||||
|
},
|
||||||
|
onerror(err) {
|
||||||
|
reject(new TypeError("Failed to fetch: " + err.finalUrl));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const httpMethods = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "TRACE", "OPTIONS", "CONNECT"] as const;
|
||||||
|
|
||||||
|
// a ts type helper to narrow type
|
||||||
|
function includes<T extends U, U>(array: ReadonlyArray<T>, element: U): element is T {
|
||||||
|
return array.includes(element as T);
|
||||||
|
}
|
||||||
|
|
||||||
|
function gmXHRMethod(method: string): (typeof httpMethods)[number] {
|
||||||
|
if (includes(httpMethods, method)) {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`unsupported http method ${method}`);
|
||||||
|
}
|
145
src/gm-fetch/utils.ts
Normal file
145
src/gm-fetch/utils.ts
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
export function parseRawHeaders(h: string): Headers {
|
||||||
|
const s = h.trim();
|
||||||
|
if (!s) {
|
||||||
|
return new Headers();
|
||||||
|
}
|
||||||
|
|
||||||
|
const array: [string, string][] = s.split("\r\n").map((value) => {
|
||||||
|
let s = value.split(":");
|
||||||
|
return [s[0].trim(), s[1].trim()];
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Headers(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseGMResponse(req: Request, res: GM.Response<any>): Response {
|
||||||
|
return new ResImpl(res.response as Blob, {
|
||||||
|
statusCode: res.status,
|
||||||
|
statusText: res.statusText,
|
||||||
|
headers: parseRawHeaders(res.responseHeaders),
|
||||||
|
finalUrl: res.finalUrl,
|
||||||
|
redirected: res.finalUrl === req.url,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResInit {
|
||||||
|
redirected: boolean;
|
||||||
|
headers: Headers;
|
||||||
|
statusCode: number;
|
||||||
|
statusText: string;
|
||||||
|
finalUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ResImpl implements Response {
|
||||||
|
private _bodyUsed: boolean;
|
||||||
|
private readonly rawBody: Blob;
|
||||||
|
private readonly init: ResInit;
|
||||||
|
|
||||||
|
readonly body: ReadableStream<Uint8Array> | null;
|
||||||
|
readonly headers: Headers;
|
||||||
|
readonly redirected: boolean;
|
||||||
|
readonly status: number;
|
||||||
|
readonly statusText: string;
|
||||||
|
readonly type: ResponseType;
|
||||||
|
readonly url: string;
|
||||||
|
|
||||||
|
constructor(body: Blob, init: ResInit) {
|
||||||
|
this.rawBody = body;
|
||||||
|
this.init = init;
|
||||||
|
|
||||||
|
//this.body = toReadableStream(body);
|
||||||
|
const { headers, statusCode, statusText, finalUrl, redirected } = init;
|
||||||
|
this.headers = headers;
|
||||||
|
this.status = statusCode;
|
||||||
|
this.statusText = statusText;
|
||||||
|
this.url = finalUrl;
|
||||||
|
this.type = "basic";
|
||||||
|
this.redirected = redirected;
|
||||||
|
this._bodyUsed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get bodyUsed(): boolean {
|
||||||
|
return this._bodyUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
get ok(): boolean {
|
||||||
|
return this.status < 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
arrayBuffer(): Promise<ArrayBuffer> {
|
||||||
|
if (this.bodyUsed) {
|
||||||
|
throw new TypeError("Failed to execute 'arrayBuffer' on 'Response': body stream already read");
|
||||||
|
}
|
||||||
|
this._bodyUsed = true;
|
||||||
|
return this.rawBody.arrayBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
blob(): Promise<Blob> {
|
||||||
|
if (this.bodyUsed) {
|
||||||
|
throw new TypeError("Failed to execute 'blob' on 'Response': body stream already read");
|
||||||
|
}
|
||||||
|
this._bodyUsed = true;
|
||||||
|
// `slice` will use empty string as default value, so need to pass all arguments.
|
||||||
|
return Promise.resolve(this.rawBody.slice(0, this.rawBody.size, this.rawBody.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
clone(): Response {
|
||||||
|
if (this.bodyUsed) {
|
||||||
|
throw new TypeError("Failed to execute 'clone' on 'Response': body stream already read");
|
||||||
|
}
|
||||||
|
return new ResImpl(this.rawBody, this.init);
|
||||||
|
}
|
||||||
|
|
||||||
|
formData(): Promise<FormData> {
|
||||||
|
if (this.bodyUsed) {
|
||||||
|
throw new TypeError("Failed to execute 'formData' on 'Response': body stream already read");
|
||||||
|
}
|
||||||
|
this._bodyUsed = true;
|
||||||
|
return this.rawBody.text().then(decode);
|
||||||
|
}
|
||||||
|
|
||||||
|
async json(): Promise<any> {
|
||||||
|
if (this.bodyUsed) {
|
||||||
|
throw new TypeError("Failed to execute 'json' on 'Response': body stream already read");
|
||||||
|
}
|
||||||
|
this._bodyUsed = true;
|
||||||
|
return JSON.parse(await this.rawBody.text());
|
||||||
|
}
|
||||||
|
|
||||||
|
text(): Promise<string> {
|
||||||
|
if (this.bodyUsed) {
|
||||||
|
throw new TypeError("Failed to execute 'text' on 'Response': body stream already read");
|
||||||
|
}
|
||||||
|
this._bodyUsed = true;
|
||||||
|
return this.rawBody.text();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function decode(body: string) {
|
||||||
|
const form = new FormData();
|
||||||
|
|
||||||
|
body
|
||||||
|
.trim()
|
||||||
|
.split("&")
|
||||||
|
.forEach(function (bytes) {
|
||||||
|
if (bytes) {
|
||||||
|
const split = bytes.split("=");
|
||||||
|
const name = split.shift()?.replace(/\+/g, " ");
|
||||||
|
const value = split.join("=").replace(/\+/g, " ");
|
||||||
|
form.append(decodeURIComponent(name!), decodeURIComponent(value));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
function toReadableStream(value: Blob) {
|
||||||
|
return new ReadableStream({
|
||||||
|
start(controller) {
|
||||||
|
controller.enqueue(value);
|
||||||
|
controller.close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
*/
|
@ -2,7 +2,7 @@ import './search.css'
|
|||||||
import { TapiContact } from './tapi-contact'
|
import { TapiContact } from './tapi-contact'
|
||||||
import { debounce } from './debounce'
|
import { debounce } from './debounce'
|
||||||
import { fireChangeEvents } from './utils'
|
import { fireChangeEvents } from './utils'
|
||||||
import GM_fetch from '@trim21/gm-fetch'
|
import GM_fetch from './gm-fetch'
|
||||||
|
|
||||||
export class Search {
|
export class Search {
|
||||||
private currentSearchText = ''
|
private currentSearchText = ''
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
import GM_fetch from './gm-fetch';
|
||||||
import './status.css';
|
import './status.css';
|
||||||
import { ZcStatus } from './zc-status';
|
import { ZcStatus } from './zc-status';
|
||||||
import GM_fetch from "@trim21/gm-fetch";
|
|
||||||
|
|
||||||
declare function waitForKeyElements(selectorOrFunction: any, callback: any, waitOnce: boolean): any;
|
declare function waitForKeyElements(selectorOrFunction: any, callback: any, waitOnce: boolean): any;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user