Compare commits

...

3 Commits

Author SHA1 Message Date
b6124f5ee8 Version 8.0.0. 2021-08-27 18:14:41 +02:00
cd303869c8 Status mit ZeitConsens synchronisieren 2021-08-27 18:14:09 +02:00
4283ee6b5c Userscript template aktualisiert 2021-08-27 18:12:26 +02:00
24 changed files with 18544 additions and 11216 deletions

9
.babelrc.js Normal file
View File

@ -0,0 +1,9 @@
module.exports = function (api) {
api.cache(true)
const presets = ['@babel/preset-env']
return {
presets,
}
}

3
.browserslistrc Normal file
View File

@ -0,0 +1,3 @@
> 1%
not IE 11
not dead

File diff suppressed because one or more lines are too long

View File

@ -5,9 +5,9 @@ module.exports = {
namespace: 'http://cp-solutions.at', namespace: 'http://cp-solutions.at',
version: pkg.version, version: pkg.version,
author: pkg.author, author: pkg.author,
copyright: 'Copyright 2020 CP Solutions GmbH', copyright: 'Copyright 2021 CP Solutions GmbH',
source: pkg.repository.url, source: pkg.repository.url,
downloadURL: 'http://scootaloo.cp-austria.at/gitlist/3cx_tapi.git/raw/master/3CX_TAPI.user.js', downloadURL: 'https://source.cp-austria.at/git/CPATRD/3cx_tapi/raw/branch/master/3CX_TAPI.user.js',
match: [ match: [
'https://192.168.0.154:5001/webclient*', 'https://192.168.0.154:5001/webclient*',
'https://cpsolution.my3cx.at:5001/webclient*' 'https://cpsolution.my3cx.at:5001/webclient*'
@ -18,8 +18,10 @@ module.exports = {
`https://cdn.jsdelivr.net/npm/axios-userscript-adapter@${pkg.dependencies['axios-userscript-adapter']}/dist/axiosGmxhrAdapter.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',
'GM.notification' 'GM.notification',
'GM.getValue',
'GM.setValue'
], ],
connect: [ connect: [
'cpatapi.cpsrvweb2016.cp-austria.at' 'cpatapi.cpsrvweb2016.cp-austria.at'

View File

@ -1,42 +1,38 @@
const path = require('path') const path = require('path')
const webpack = require('webpack') const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
const webpackConfig = { const webpackConfig = {
node: {
Buffer: false
},
resolve: { resolve: {
extensions: ['.js', '.ts'] extensions: ['.js', '.ts']
}, },
// performance: {
// hints: false
// },
optimization: { optimization: {
minimize: false minimize: false,
moduleIds: 'named',
}, },
entry: './src/js/index.js', entry: './src/index.js',
output: { output: {
path: path.resolve(__dirname, '../dist') path: path.resolve(__dirname, '../dist')
}, },
externals: { externals: {
jquery: '$',
axios: 'axios', axios: 'axios',
'axios-userscript-adapter': 'axiosGmxhrAdapter' 'axios-userscript-adapter': 'axiosGmxhrAdapter'
}, },
module: { module: {
rules: [ rules: [
{ {
use: {
loader: 'babel-loader',
},
test: /\.js$/, test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader'
}, },
{ {
test: /\.ts$/, test: /\.ts$/,
exclude: /node_modules/,
loader: 'ts-loader' loader: 'ts-loader'
}, },
{ {
test: /\.less$/, test: /\.less$/,
loader: [ use: [
'style-loader', 'style-loader',
'css-loader', 'css-loader',
'less-loader', // 将 Less 编译为 CSS 'less-loader', // 将 Less 编译为 CSS
@ -44,16 +40,14 @@ const webpackConfig = {
}, },
{ {
test: /\.css$/, test: /\.css$/,
loader: [ use: [
'style-loader', 'style-loader',
'css-loader', 'css-loader',
] ]
} }
] ]
}, },
plugins: [ plugins: process.env.npm_config_report ? [new BundleAnalyzerPlugin()] : [],
new webpack.HashedModuleIdsPlugin()
]
} }
module.exports = webpackConfig module.exports = webpackConfig

View File

@ -0,0 +1,37 @@
const path = require('path')
const { merge } = require('webpack-merge')
const LiveReloadPlugin = require('webpack-livereload-plugin')
const UserScriptMetaDataPlugin = require('userscript-metadata-webpack-plugin')
const metadata = require('./metadata.cjs')
const webpackConfig = require('./webpack.config.base.cjs')
metadata.require.push(
'file://' + path.resolve(__dirname, '../dist/index.prod.user.js')
)
const cfg = merge(webpackConfig, {
entry: {
prod: webpackConfig.entry,
dev: path.resolve(__dirname, './empty.cjs'),
},
output: {
filename: 'index.[name].user.js',
path: path.resolve(__dirname, '../dist'),
},
devtool: 'inline-source-map',
watch: true,
watchOptions: {
ignored: /node_modules/,
},
plugins: [
new LiveReloadPlugin({
delay: 500,
}),
new UserScriptMetaDataPlugin({
metadata,
}),
],
})
module.exports = cfg

View File

@ -1,43 +0,0 @@
const path = require("path");
const { merge } = require("webpack-merge");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
const LiveReloadPlugin = require("webpack-livereload-plugin");
const UserScriptMetaDataPlugin = require("userscript-metadata-webpack-plugin");
const metadata = require("./metadata");
const webpackConfig = require("./webpack.config.base");
metadata.require.push(
"file://" + path.resolve(__dirname, "../dist/index.prod.user.js")
);
const cfg = merge(webpackConfig, {
entry: {
prod: webpackConfig.entry,
dev: path.resolve(__dirname, "./empty.js"),
},
output: {
filename: "index.[name].user.js",
path: path.resolve(__dirname, "../dist"),
},
devtool: "inline-source-map",
watch: true,
watchOptions: {
ignored: /node_modules/,
},
plugins: [
new LiveReloadPlugin({
delay: 500,
}),
new UserScriptMetaDataPlugin({
metadata,
}),
],
});
if (process.env.npm_config_report) {
cfg.plugins.push(new BundleAnalyzerPlugin());
}
module.exports = cfg;

View File

@ -0,0 +1,19 @@
const { merge } = require('webpack-merge')
const UserScriptMetaDataPlugin = require('userscript-metadata-webpack-plugin')
const metadata = require('./metadata.cjs')
const webpackConfig = require('./webpack.config.base.cjs')
const cfg = merge(webpackConfig, {
mode: 'production',
output: {
filename: metadata.name + '.prod.user.js',
},
plugins: [
new UserScriptMetaDataPlugin({
metadata,
}),
],
})
module.exports = cfg

View File

@ -1,23 +0,0 @@
const { merge } = require("webpack-merge");
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const UserScriptMetaDataPlugin = require('userscript-metadata-webpack-plugin')
const metadata = require('./metadata')
const webpackConfig = require('./webpack.config.base')
const cfg = merge({}, webpackConfig, {
output: {
filename: 'index.prod.user.js'
},
plugins: [
new UserScriptMetaDataPlugin({
metadata
})
]
})
if (process.env.npm_config_report) {
cfg.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = cfg

15469
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,22 @@
{ {
"name": "3cp-tapi", "name": "3cp-tapi",
"description": "Build your UserScript with webpack", "description": "Build your UserScript with webpack",
"version": "7.0.3", "version": "8.0.0",
"author": { "author": {
"name": "Daniel Triendl", "name": "Daniel Triendl",
"email": "d.triendl@cp-solutions.at" "email": "d.triendl@cp-solutions.at"
}, },
"browserslist": [
"last 2 version",
"> 1%"
],
"eslintIgnore": [ "eslintIgnore": [
"dist/*.js" "dist/*.js",
"node_modules"
], ],
"scripts": { "scripts": {
"lint": "eslint src", "lint": "eslint --ext .ts,.js src",
"preversion": "npm run lint", "preversion": "npm run lint",
"postversion": "git push --follow-tags", "postversion": "git push --follow-tags",
"anylize": "npm_config_report=true npm run build", "anylize": "npm_config_report=true npm run build",
"build": "webpack --mode production --config config/webpack.config.production.js", "build": "webpack --mode production --config config/webpack.config.production.cjs",
"dev": "webpack --mode development --config config/webpack.config.dev.js" "dev": "webpack --mode development --config config/webpack.config.dev.cjs"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -27,32 +24,34 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"axios": "0.20.0", "axios": "0.21.1",
"axios-userscript-adapter": "0.0.7", "axios-userscript-adapter": "0.1.4",
"chrono-node": "2.1.9" "chrono-node": "^2.3.0"
}, },
"devDependencies": { "devDependencies": {
"@typescript-eslint/eslint-plugin": "4.3.0", "@babel/core": "7.14.6",
"@typescript-eslint/parser": "4.3.0", "@babel/preset-env": "7.14.5",
"css-loader": "4.3.0", "@typescript-eslint/eslint-plugin": "4.27.0",
"eslint": "7.10.0", "@typescript-eslint/parser": "4.27.0",
"eslint-config-standard": "14.1.1", "babel-loader": "8.2.2",
"eslint-loader": "4.0.2", "browserslist": "4.16.6",
"eslint-plugin-import": "2.22.1", "css-loader": "5.2.6",
"eslint": "7.29.0",
"eslint-config-standard": "16.0.3",
"eslint-plugin-import": "2.23.4",
"eslint-plugin-node": "11.1.0", "eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "4.2.1", "eslint-plugin-promise": "5.1.0",
"eslint-plugin-standard": "4.0.1", "eslint-plugin-standard": "4.1.0",
"less": "3.12.2", "less": "4.1.1",
"less-loader": "7.0.1", "less-loader": "10.0.0",
"style-loader": "1.2.1", "style-loader": "2.0.0",
"ts-loader": "8.0.4", "ts-loader": "9.2.3",
"typescript": "4.0.3", "typescript": "4.3.4",
"userscript-metadata-webpack-plugin": "0.0.6", "userscript-metadata-webpack-plugin": "0.1.0",
"webpack": "4.44.2", "webpack": "5.39.1",
"webpack-bundle-analyzer": "3.9.0", "webpack-bundle-analyzer": "4.4.2",
"webpack-cli": "3.3.12", "webpack-cli": "4.7.2",
"webpack-dev-server": "^3.11.0", "webpack-livereload-plugin": "3.0.1",
"webpack-livereload-plugin": "2.3.0", "webpack-merge": "5.8.0"
"webpack-merge": "5.1.4"
} }
} }

View File

View File

@ -3,6 +3,7 @@ import * as chrono from 'chrono-node'
import { CallHistory } from './call-history' import { CallHistory } from './call-history'
import { CallNotification } from './call-notification' import { CallNotification } from './call-notification'
import { Search } from './search' import { Search } from './search'
import { Status } from './status'
console.log('script start') console.log('script start')
@ -17,3 +18,7 @@ waitForKeyElements('call-view', (element) => { callNotification.showCallNotifica
const callHistory = new CallHistory() const callHistory = new CallHistory()
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
waitForKeyElements('.call-history-list call', (element) => { callHistory.showCallHistory(element) }, false) waitForKeyElements('.call-history-list call', (element) => { callHistory.showCallHistory(element) }, false)
const status = new Status()
// eslint-disable-next-line no-undef
waitForKeyElements('#status-change', (element) => { status.showStatus(element) }, false)

View File

@ -1,33 +0,0 @@
/**
* @typedef {Object} AxiosResponse
* @property {Object} data
* @property {Object} headers
* @property {Object} config
* @property {Object} request
* @property {number} code
* @property {string} statusText
*/
/**
* @typedef {Object} AxiosError
* @property {AxiosResponse} response
*/
import axios from 'axios'
import adapter from 'axios-userscript-adapter'
axios.defaults.adapter = adapter
export { axios }
export function extractNumber (s: string) {
var match = /(\+?[0-9]{4,})/.exec(s)
if (!match) {
return undefined
}
var number = match[1]
if (number.startsWith('+')) {
number = number.replace('+', '00')
}
return number
}

View File

@ -1,6 +1,6 @@
import { TapiContact } from './tapi-contact' import { TapiContact } from './tapi-contact'
import { debounce } from './debounce' import { debounce } from './debounce'
import { axios } from './utils' import { axios, fireChangeEvents } from './utils'
export class Search { export class Search {
private currentSearchText = '' private currentSearchText = ''
@ -175,23 +175,7 @@ export class Search {
(<HTMLInputElement>searchInput[0]).value = number (<HTMLInputElement>searchInput[0]).value = number
searchInput[0].focus() searchInput[0].focus()
this.fireChangeEvents(searchInput[0]) fireChangeEvents(searchInput[0])
} }
} }
private fireChangeEvents (element: Element) {
var changeEvent = null
changeEvent = document.createEvent('HTMLEvents')
changeEvent.initEvent('input', true, true)
element.dispatchEvent(changeEvent)
console.debug('input event dispatched for element: ' + element.id)
changeEvent = document.createEvent('HTMLEvents')
changeEvent.initEvent('keyup', true, true)
element.dispatchEvent(changeEvent)
console.debug('keyup event dispatched for element: ' + element.id)
changeEvent = document.createEvent('HTMLEvents')
changeEvent.initEvent('change', true, true)
element.dispatchEvent(changeEvent)
console.debug('change event dispatched for element: ' + element.id)
}
} }

18
src/status.css Normal file
View File

@ -0,0 +1,18 @@
.tapi-dropdown {
position: relative;
display: inline-block;
}
.tapi-dropdown-content {
display: none;
position: absolute;
min-width: 200px;
overflow: auto;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
color: #000;
}
.show {
display: block;
}

129
src/status.ts Normal file
View File

@ -0,0 +1,129 @@
import './status.css';
import { axios } from './utils';
import { ZcStatus } from './zc-status';
declare function waitForKeyElements(selectorOrFunction: any, callback: any, waitOnce: boolean): any;
export class Status {
private _user: string;
private _enabled = false;
private _statusOn = 'menuAvailable';
private _statusOff = 'menuAway';
private _currentStatus: boolean = undefined;
public async showStatus(element: HTMLElement) {
this._user = await GM.getValue('tapi-zc-user', '');
this._enabled = await GM.getValue('tapi-zc-enabled', false);
this._statusOn = await GM.getValue('tapi-zc-on', 'menuAvailable');
this._statusOff = await GM.getValue('tapi-zc-off', 'menuAvailable');
console.log('tapi-zc-user', this._user, 'tapi-zc-enabled', this._enabled, 'tapi-zc-on', this._statusOn, 'tapi-zc-off', this._statusOff);
var div = document.createElement('div');
div.classList.add('tapi-dropdown');
var button = document.createElement('button');
button.id = 'tapi-zc-button';
button.classList.add('btn');
button.classList.add('btn-default');
button.innerText = 'ZeitConsens';
button.onclick = () => {
document.getElementById('tapi-zc-dropdown').classList.toggle('show');
}
div.appendChild(button);
var html =
'<div class="form-group">' +
' <label for="tapi-zc-user">Username</label>' +
' <input type="text" class="form-control" name="tapi-zc-user" id="tapi-zc-user">' +
'</div>' +
'<div class="form-group">' +
' <label for="tapi-zc-on">Signed in</label>' +
' <select id="tapi-zc-on" class="form-control">' +
' <option value="menuAvailable">Available</option>' +
' <option value="menuOutofoffice">Do Not Disturb</option>' +
' <option value="menuCustom1">Verfügbar DW</option>' +
' </select>' +
'</div>' +
'<div class="form-group">' +
' <label for="tapi-zc-off">Signed in</label>' +
' <select id="tapi-zc-off" class="form-control">' +
' <option value="menuAway">Away</option>' +
' <option value="menuOutofoffice">Do Not Disturb</option>' +
' </select>' +
'</div>' +
'<div class="checkbox">' +
' <label class="i-checks" for="tapi-zc-enabled">' +
' <input type="checkbox" id="tapi-zc-enabled">' +
' <i></i><span>Enabled</span>' +
'</label>'
'</div>';
var dropdown = document.createElement('div');
dropdown.classList.add('tapi-dropdown-content');
dropdown.classList.add('panel-body');
dropdown.id = 'tapi-zc-dropdown';
dropdown.innerHTML = html;
div.appendChild(dropdown);
element.insertBefore(div, element.firstChild);
var zcUser = document.getElementById('tapi-zc-user') as HTMLInputElement;
zcUser.value = this._user;
zcUser.onchange = () => {
this._user = zcUser.value;
GM.setValue('tapi-zc-user', this._user);
console.log('tapi-zc-user', this._user);
this._currentStatus = undefined;
}
var zcEnabled = document.getElementById('tapi-zc-enabled') as HTMLInputElement;
zcEnabled.checked = this._enabled;
zcEnabled.onchange = () => {
this._enabled = zcEnabled.checked;
GM.setValue('tapi-zc-enabled', this._enabled);
console.log('tapi-zc-enabled', this._enabled);
this._currentStatus = undefined;
this.checkStatus();
}
var zcOn = document.getElementById('tapi-zc-on') as HTMLSelectElement;
zcOn.value = this._statusOn;
zcOn.onchange = () => {
this._statusOn = zcOn.value;
GM.setValue('tapi-zc-on', this._statusOn);
console.log('tapi-zc-on', this._statusOn);
this._currentStatus = undefined;
}
var zcOff = document.getElementById('tapi-zc-off') as HTMLSelectElement;
zcOff.value = this._statusOff;
zcOff.onchange = () => {
this._statusOff = zcOff.value;
GM.setValue('tapi-zc-off', this._statusOff);
console.log('tapi-zc-off', this._statusOff);
this._currentStatus = undefined;
}
this.checkStatus();
}
private async checkStatus() {
if (this._enabled) {
var response = await axios.get<ZcStatus>('http://cpatapi.cpsrvweb2016.cp-austria.at/availability/' + encodeURIComponent(this._user));
if (response.status == 200) {
var status = response.data;
if (this._currentStatus !== status.loggedIn) {
this._currentStatus = status.loggedIn;
console.log('New status, loggedIn', this._currentStatus);
(document.getElementsByClassName("current-status")[0] as HTMLAnchorElement).click();
setTimeout(() => {
var statusId = this._currentStatus ? this._statusOn : this._statusOff;
(document.getElementById(statusId) as HTMLAnchorElement).click();
}, 1000);
}
}
setTimeout(() => this.checkStatus(), 10000);
}
}
}

49
src/utils.ts Normal file
View File

@ -0,0 +1,49 @@
/**
* @typedef {Object} AxiosResponse
* @property {Object} data
* @property {Object} headers
* @property {Object} config
* @property {Object} request
* @property {number} code
* @property {string} statusText
*/
/**
* @typedef {Object} AxiosError
* @property {AxiosResponse} response
*/
import axios from 'axios'
import adapter from 'axios-userscript-adapter'
axios.defaults.adapter = adapter
export { axios }
export function extractNumber (s: string) {
var match = /(\+?[0-9]{4,})/.exec(s)
if (!match) {
return undefined
}
var number = match[1]
if (number.startsWith('+')) {
number = number.replace('+', '00')
}
return number
}
export function fireChangeEvents (element: Element) {
var changeEvent = null
changeEvent = document.createEvent('HTMLEvents')
changeEvent.initEvent('input', true, true)
element.dispatchEvent(changeEvent)
console.debug('input event dispatched for element: ' + element.id)
changeEvent = document.createEvent('HTMLEvents')
changeEvent.initEvent('keyup', true, true)
element.dispatchEvent(changeEvent)
console.debug('keyup event dispatched for element: ' + element.id)
changeEvent = document.createEvent('HTMLEvents')
changeEvent.initEvent('change', true, true)
element.dispatchEvent(changeEvent)
console.debug('change event dispatched for element: ' + element.id)
}

4
src/zc-status.ts Normal file
View File

@ -0,0 +1,4 @@
export class ZcStatus {
public user: string;
public loggedIn: boolean;
}