Compare commits
	
		
			22 Commits
		
	
	
		
			v5.0.0
			...
			6186b14b16
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6186b14b16 | |||
| f578bd2fe1 | |||
| 933b445ed6 | |||
| bbe20d6351 | |||
| 9e5d93bad2 | |||
| b83cef625a | |||
| ad5c8ece12 | |||
| 0455cb1926 | |||
| 3e33155276 | |||
| de34a6c66e | |||
| a4a346b48d | |||
| cd303869c8 | |||
| 4283ee6b5c | |||
|   | fec9885a64 | ||
|   | 29fc426161 | ||
|   | 231d24b26a | ||
|   | f3693162ab | ||
|   | c09bdd856b | ||
|   | ecb9097f5f | ||
|   | 7db79afca2 | ||
|   | e40a0810ff | ||
|   | 10c1a9185b | 
							
								
								
									
										9
									
								
								.babelrc.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.babelrc.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | module.exports = function (api) { | ||||||
|  |   api.cache(true) | ||||||
|  |  | ||||||
|  |   const presets = ['@babel/preset-env'] | ||||||
|  |  | ||||||
|  |   return { | ||||||
|  |     presets, | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								.browserslistrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.browserslistrc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | > 1% | ||||||
|  | not IE 11 | ||||||
|  | not dead | ||||||
							
								
								
									
										15
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | [*] | ||||||
|  | charset=utf-8 | ||||||
|  | end_of_line=lf | ||||||
|  | trim_trailing_whitespace=true | ||||||
|  | insert_final_newline=true | ||||||
|  | indent_style=space | ||||||
|  | indent_size=4 | ||||||
|  |  | ||||||
|  | [{.babelrc,.stylelintrc,.eslintrc,jest.config,*.bowerrc,*.jsb3,*.jsb2,*.json,*.yaml,*.yml}] | ||||||
|  | indent_style=space | ||||||
|  | indent_size=2 | ||||||
|  |  | ||||||
|  | [{*.js,*.vue,*.ts}] | ||||||
|  | indent_style=space | ||||||
|  | indent_size=2 | ||||||
							
								
								
									
										28
									
								
								.eslintrc.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								.eslintrc.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | --- | ||||||
|  | extends: | ||||||
|  |   - standard | ||||||
|  |   - plugin:import/recommended | ||||||
|  |   - plugin:import/typescript # this line does the trick | ||||||
|  |  | ||||||
|  | # or configure manually: | ||||||
|  | plugins: | ||||||
|  |   - import | ||||||
|  |   - promise | ||||||
|  |   - standard | ||||||
|  |   - '@typescript-eslint' | ||||||
|  |  | ||||||
|  | parser: '@typescript-eslint/parser' | ||||||
|  |  | ||||||
|  | parserOptions: | ||||||
|  |   sourceType: module | ||||||
|  |   ecmaVersion: 6 | ||||||
|  |  | ||||||
|  | env: | ||||||
|  |     browser: true | ||||||
|  |     node: true | ||||||
|  |  | ||||||
|  | rules: | ||||||
|  |   comma-dangle: off | ||||||
|  |   "import/order": "error" | ||||||
|  |   "no-unused-vars": "off" | ||||||
|  |   "@typescript-eslint/no-unused-vars": ["error"] | ||||||
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | node_modules | ||||||
|  | .idea | ||||||
|  | .vscode | ||||||
|  | dist | ||||||
							
								
								
									
										8803
									
								
								3CX_TAPI.user.js
									
									
									
									
									
								
							
							
						
						
									
										8803
									
								
								3CX_TAPI.user.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										0
									
								
								config/empty.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								config/empty.cjs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										30
									
								
								config/metadata.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								config/metadata.cjs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | const pkg = require('../package.json') | ||||||
|  |  | ||||||
|  | module.exports = { | ||||||
|  |   name: '3CX TAPI', | ||||||
|  |   namespace: 'http://cp-solutions.at', | ||||||
|  |   version: pkg.version, | ||||||
|  |   author: pkg.author, | ||||||
|  |   copyright: 'Copyright 2021 CP Solutions GmbH', | ||||||
|  |   source: pkg.repository.url, | ||||||
|  |   downloadURL: 'https://source.cp-austria.at/git/CPATRD/3cx_tapi/raw/branch/master/3CX_TAPI.user.js', | ||||||
|  |   match: [ | ||||||
|  |     'https://192.168.0.154:5001/webclient*', | ||||||
|  |     'https://cpsolution.my3cx.at:5001/webclient*' | ||||||
|  |   ], | ||||||
|  |   require: [ | ||||||
|  |     '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: [ | ||||||
|  |     'GM.xmlHttpRequest', | ||||||
|  |     'GM.notification', | ||||||
|  |     'GM.getValue', | ||||||
|  |     'GM.setValue' | ||||||
|  |   ], | ||||||
|  |   connect: [ | ||||||
|  |     'cpatapi.cpsrvweb2016.cp-austria.at' | ||||||
|  |   ], | ||||||
|  |   'run-at': 'document-end' | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								config/webpack.config.base.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								config/webpack.config.base.cjs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | const path = require('path') | ||||||
|  | const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') | ||||||
|  |  | ||||||
|  | const webpackConfig = { | ||||||
|  |   resolve: { | ||||||
|  |     extensions: ['.js', '.ts'] | ||||||
|  |   }, | ||||||
|  |   optimization: { | ||||||
|  |     minimize: false, | ||||||
|  |     moduleIds: 'named', | ||||||
|  |   }, | ||||||
|  |   entry: './src/index.js', | ||||||
|  |   output: { | ||||||
|  |     path: path.resolve(__dirname, '../dist') | ||||||
|  |   }, | ||||||
|  |   externals: { | ||||||
|  |     jquery: '$', | ||||||
|  |     axios: 'axios', | ||||||
|  |     'axios-userscript-adapter': 'axiosGmxhrAdapter' | ||||||
|  |   }, | ||||||
|  |   module: { | ||||||
|  |     rules: [ | ||||||
|  |       { | ||||||
|  |         use: { | ||||||
|  |           loader: 'babel-loader', | ||||||
|  |         }, | ||||||
|  |         test: /\.js$/, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         test: /\.ts$/, | ||||||
|  |         loader: 'ts-loader' | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         test: /\.less$/, | ||||||
|  |         use: [ | ||||||
|  |           'style-loader', | ||||||
|  |           'css-loader', | ||||||
|  |           'less-loader', // 将 Less 编译为 CSS | ||||||
|  |         ] | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         test: /\.css$/, | ||||||
|  |         use: [ | ||||||
|  |           'style-loader', | ||||||
|  |           'css-loader', | ||||||
|  |         ] | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   plugins: process.env.npm_config_report ? [new BundleAnalyzerPlugin()] : [], | ||||||
|  | } | ||||||
|  |  | ||||||
|  | module.exports = webpackConfig | ||||||
							
								
								
									
										37
									
								
								config/webpack.config.dev.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								config/webpack.config.dev.cjs
									
									
									
									
									
										Normal 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 | ||||||
							
								
								
									
										19
									
								
								config/webpack.config.production.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								config/webpack.config.production.cjs
									
									
									
									
									
										Normal 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 | ||||||
							
								
								
									
										12144
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										12144
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										57
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | |||||||
|  | { | ||||||
|  |   "name": "3cx-tapi", | ||||||
|  |   "description": "3CX CP Tapi and Projectmanager integration", | ||||||
|  |   "version": "9.0.1", | ||||||
|  |   "author": { | ||||||
|  |     "name": "Daniel Triendl", | ||||||
|  |     "email": "d.triendl@cp-solutions.at" | ||||||
|  |   }, | ||||||
|  |   "eslintIgnore": [ | ||||||
|  |     "dist/*.js", | ||||||
|  |     "node_modules" | ||||||
|  |   ], | ||||||
|  |   "scripts": { | ||||||
|  |     "lint": "eslint --ext .ts,.js src", | ||||||
|  |     "preversion": "npm run lint", | ||||||
|  |     "postversion": "git push --follow-tags", | ||||||
|  |     "anylize": "npm_config_report=true npm run build", | ||||||
|  |     "build": "webpack --mode production --config config/webpack.config.production.cjs", | ||||||
|  |     "dev": "webpack --mode development --config config/webpack.config.dev.cjs" | ||||||
|  |   }, | ||||||
|  |   "repository": { | ||||||
|  |     "type": "git", | ||||||
|  |     "url": "https://source.cp-austria.at/git/CPATRD/3cx_tapi.git" | ||||||
|  |   }, | ||||||
|  |   "private": true, | ||||||
|  |   "dependencies": { | ||||||
|  |     "axios": "0.21.1", | ||||||
|  |     "axios-userscript-adapter": "0.1.4", | ||||||
|  |     "chrono-node": "^2.3.0" | ||||||
|  |   }, | ||||||
|  |   "devDependencies": { | ||||||
|  |     "@babel/core": "7.14.6", | ||||||
|  |     "@babel/preset-env": "7.14.5", | ||||||
|  |     "@typescript-eslint/eslint-plugin": "4.27.0", | ||||||
|  |     "@typescript-eslint/parser": "4.27.0", | ||||||
|  |     "babel-loader": "8.2.2", | ||||||
|  |     "browserslist": "4.16.6", | ||||||
|  |     "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-promise": "5.1.0", | ||||||
|  |     "eslint-plugin-standard": "4.1.0", | ||||||
|  |     "less": "4.1.1", | ||||||
|  |     "less-loader": "10.0.0", | ||||||
|  |     "style-loader": "2.0.0", | ||||||
|  |     "ts-loader": "9.2.3", | ||||||
|  |     "typescript": "4.3.4", | ||||||
|  |     "userscript-metadata-webpack-plugin": "0.1.0", | ||||||
|  |     "webpack": "5.39.1", | ||||||
|  |     "webpack-bundle-analyzer": "4.4.2", | ||||||
|  |     "webpack-cli": "4.7.2", | ||||||
|  |     "webpack-livereload-plugin": "3.0.1", | ||||||
|  |     "webpack-merge": "5.8.0" | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								readme.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								readme.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | # 3CX TAPI | ||||||
|  |  | ||||||
|  | Inject CPA TAPI functions into the 3CX Webclient | ||||||
|  |  | ||||||
|  | ## dev | ||||||
|  |  | ||||||
|  | 1. Allow Tampermonkey's access to local file URIs [tampermonkey/faq](https://tampermonkey.net/faq.php?ext=dhdg#Q204) | ||||||
|  | 2. install deps with `npm i` or `npm ci`. | ||||||
|  | 3. `npm run dev` to start your development. | ||||||
|  | 4. open `webpack-userscript-template/dist/index.dev.user.js` in your Chrome and install it with your userscript manager. | ||||||
|  |  | ||||||
|  | this userscript's meta contains `// @require file://path/to/dist/index.prod.user.js`, | ||||||
|  | it will run the code in `index.prod.user.js`, | ||||||
|  | which take [src/js/index.js](./src/js/index.js) as entry point. | ||||||
|  |  | ||||||
|  | every times you edit your metadata, you'll have to install it again, | ||||||
|  | because Tampermonkey don't read it from dist every times. | ||||||
|  |  | ||||||
|  | 5. edit [src/js/index.js](./src/js/index.js) with es6, you can even import css or less files. You can use scss if you like. | ||||||
|  |  | ||||||
|  | livereload is default enabled, use [this chrome extension](https://chrome.google.com/webstore/detail/jnihajbhpnppcggbcgedagnkighmdlei) | ||||||
|  |  | ||||||
|  | ## dependencies | ||||||
|  |  | ||||||
|  | There are two ways to using a package on npm. | ||||||
|  |  | ||||||
|  | ### UserScript way | ||||||
|  |  | ||||||
|  | Like original UserScript way, you will need to add them to your [user script metadata's require section](./config/metadata.js#L13-L17) , and exclude them in [config/webpack.config.base.js](./config/webpack.config.base.js#L21-L25) | ||||||
|  |  | ||||||
|  | ### Webpack way | ||||||
|  |  | ||||||
|  | just install a package and import it in your js file. webpack will pack them with in your final production js file. | ||||||
|  |  | ||||||
|  | ## build | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | npm run build | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | `dist/index.prod.user.js` is the final script. | ||||||
|  |  | ||||||
|  | ## distribution | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | cp "dist/3CX TAPI.prod.user.js" 3CX_TAPI.user.js | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | And commit 3CX_TAPI.user.js | ||||||
|  |  | ||||||
|  | ## see also | ||||||
|  |  | ||||||
|  | Based on [webpack-userscript-template](https://github.com/Trim21/webpack-userscript-template/) | ||||||
							
								
								
									
										97
									
								
								src/call-history.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/call-history.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | |||||||
|  | import * as chrono from 'chrono-node' | ||||||
|  | import { TapiContact } from './tapi-contact' | ||||||
|  | import { axios, extractNumber } from './utils' | ||||||
|  |  | ||||||
|  | export class CallHistory { | ||||||
|  |   private callerIds: { [number: string]: TapiContact } = {} | ||||||
|  |  | ||||||
|  |   private updateCallHistoryEntry (call: HTMLElement, callerId: TapiContact) { | ||||||
|  |     var span = call.querySelector(':scope > span') | ||||||
|  |     this.showTimeManager(call, call.querySelector('.date').textContent, callerId) | ||||||
|  |  | ||||||
|  |     if (callerId && callerId.tD_NAME !== '') { | ||||||
|  |       var text = span.textContent | ||||||
|  |       span.textContent = callerId.tD_NAME + ' ' + callerId.tD_NUMBER | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private showTimeManager (call: HTMLElement, date: string, callerId: TapiContact) { | ||||||
|  |     var dateParts = date.match(/^(?<date>.*), (?<duration>[0-9]{2}:[0-9]{2}:[0-9]{2})$/) | ||||||
|  |     var duration = '00:00:00' | ||||||
|  |     if (dateParts) { | ||||||
|  |       date = dateParts.groups.date | ||||||
|  |       duration = dateParts.groups.duration | ||||||
|  |     } | ||||||
|  |     var parsedDate = chrono.de.parseDate(date) | ||||||
|  |     if (!parsedDate) { | ||||||
|  |       parsedDate = chrono.parseDate(date) | ||||||
|  |     } | ||||||
|  |     if (!parsedDate) { | ||||||
|  |       return | ||||||
|  |     } | ||||||
|  |     // Date parsing is awful, just assume the first number is the day of month | ||||||
|  |     var day = date.match(/[0-9]+/)[0] | ||||||
|  |  | ||||||
|  |     var parsedDuration = chrono.parseDate(duration) | ||||||
|  |     console.log('TAPI call history time:', date, 'parsedDate:', parsedDate, 'duration:', duration, 'parsedDuration:', parsedDuration) | ||||||
|  |  | ||||||
|  |     var connect = parsedDate.getFullYear().toString() + | ||||||
|  |       (parsedDate.getMonth() + 1).toString().padStart(2, '0') + // (January gives 0) | ||||||
|  |       day.toString().padStart(2, '0') + | ||||||
|  |       parsedDate.getHours().toString().padStart(2, '0') + | ||||||
|  |       parsedDate.getMinutes().toString().padStart(2, '0') | ||||||
|  |  | ||||||
|  |     var length = (parsedDuration.getHours() * 60 + parsedDuration.getMinutes()).toString() | ||||||
|  |  | ||||||
|  |     var toolbar = call.querySelector('call-history-options') | ||||||
|  |     var href = 'domizil://PM/Zeitbuchung?' | ||||||
|  |     if (callerId && callerId.tD_ID) { | ||||||
|  |       href += 'KontaktId=' + callerId.tD_ID + '&' | ||||||
|  |     } | ||||||
|  |     href += 'connect=' + connect + '&length=' + length | ||||||
|  |     var a = document.createElement('a') | ||||||
|  |     a.title = 'PM Zeitbuchung' | ||||||
|  |     a.dataset.domizilLink = href | ||||||
|  |     a.onclick = () => { | ||||||
|  |       window.open(href) | ||||||
|  |     } | ||||||
|  |     a.innerHTML = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 559.98 559.98">' + | ||||||
|  |       '<g>' + | ||||||
|  |       '  <g>' + | ||||||
|  |       '    <path d="M279.99,0C125.601,0,0,125.601,0,279.99c0,154.39,125.601,279.99,279.99,279.99c154.39,0,279.99-125.601,279.99-279.99' + | ||||||
|  |       '      C559.98,125.601,434.38,0,279.99,0z M279.99,498.78c-120.644,0-218.79-98.146-218.79-218.79' + | ||||||
|  |       '      c0-120.638,98.146-218.79,218.79-218.79s218.79,98.152,218.79,218.79C498.78,400.634,400.634,498.78,279.99,498.78z"/>' + | ||||||
|  |       '    <path d="M304.226,280.326V162.976c0-13.103-10.618-23.721-23.716-23.721c-13.102,0-23.721,10.618-23.721,23.721v124.928' + | ||||||
|  |       '      c0,0.373,0.092,0.723,0.11,1.096c-0.312,6.45,1.91,12.999,6.836,17.926l88.343,88.336c9.266,9.266,24.284,9.266,33.543,0' + | ||||||
|  |       '      c9.26-9.266,9.266-24.284,0-33.544L304.226,280.326z"/>' + | ||||||
|  |       '  </g>' + | ||||||
|  |       '</g>' + | ||||||
|  |       '</svg>' | ||||||
|  |  | ||||||
|  |     a.classList.add('btn'); | ||||||
|  |     a.classList.add('btn-plain'); | ||||||
|  |     toolbar.insertBefore(a, toolbar.firstChild) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public async showCallHistory (element: HTMLElement) { | ||||||
|  |     var span = element.querySelector(':scope > span') | ||||||
|  |     var number = extractNumber(span.textContent) | ||||||
|  |     if (!number) { | ||||||
|  |       this.updateCallHistoryEntry(element, undefined) | ||||||
|  |       return | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (this.callerIds[number] !== undefined) { | ||||||
|  |       this.updateCallHistoryEntry(element, this.callerIds[number]) | ||||||
|  |     } else { | ||||||
|  |       var response = await axios.get<TapiContact>('http://cpatapi.cpsrvweb2016.cp-austria.at/callerid/' + encodeURIComponent(number)) | ||||||
|  |       var callerId: TapiContact = { tD_NAME: '' } | ||||||
|  |       if (response.status === 200) { | ||||||
|  |         callerId = response.data | ||||||
|  |       } | ||||||
|  |       console.log('TAPI call histroy callerid response', number, response, callerId) | ||||||
|  |       this.callerIds[number] = callerId | ||||||
|  |       this.updateCallHistoryEntry(element, callerId) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										31
									
								
								src/call-notification.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/call-notification.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | import { TapiContact } from './tapi-contact' | ||||||
|  | import { axios, extractNumber } from './utils' | ||||||
|  |  | ||||||
|  | export class CallNotification { | ||||||
|  |   public async showCallNotification (element: HTMLElement) { | ||||||
|  |     var number = element.querySelector('.callNumber').textContent | ||||||
|  |     console.log('TAPI call notification', number) | ||||||
|  |  | ||||||
|  |     number = extractNumber(number) | ||||||
|  |     if (!number) { | ||||||
|  |       console.log('TAPI callerid no number found') | ||||||
|  |       return | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     console.log('TAPI searching callerid for', number) | ||||||
|  |     var response = await axios.get<TapiContact>('http://cpatapi.cpsrvweb2016.cp-austria.at/callerid/' + encodeURIComponent(number)) | ||||||
|  |     console.log('TAPI callerid response', response) | ||||||
|  |     var notification = { | ||||||
|  |       text: number | ||||||
|  |     } | ||||||
|  |     if (response.status === 200) { | ||||||
|  |       var callerId = response.data | ||||||
|  |       if (callerId) { | ||||||
|  |         notification.text = callerId.tD_NAME + '\r\n' + number + ' (' + callerId.tD_MEDIUM + ')' | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // eslint-disable-next-line no-undef | ||||||
|  |     GM.notification(notification) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								src/debounce.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/debounce.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | export function debounce (func, wait) { | ||||||
|  |   let timeout | ||||||
|  |  | ||||||
|  |   return function executedFunction (...args) { | ||||||
|  |     const later = () => { | ||||||
|  |       clearTimeout(timeout) | ||||||
|  |       func(...args) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     clearTimeout(timeout) | ||||||
|  |     timeout = setTimeout(later, wait) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								src/decs.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/decs.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | declare module 'axios-userscript-adapter' | ||||||
|  |  | ||||||
|  | declare const GM: any | ||||||
							
								
								
									
										24
									
								
								src/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||||||
|  | import * as chrono from 'chrono-node' | ||||||
|  | import { CallHistory } from './call-history' | ||||||
|  | import { CallNotification } from './call-notification' | ||||||
|  | import { Search } from './search' | ||||||
|  | import { Status } from './status' | ||||||
|  |  | ||||||
|  | console.log('script start') | ||||||
|  |  | ||||||
|  | const search = new Search() | ||||||
|  | // eslint-disable-next-line no-undef | ||||||
|  | waitForKeyElements('ongoing-call-button', (element) => { search.createSearchWindow(element) }, true) | ||||||
|  |  | ||||||
|  | const callNotification = new CallNotification() | ||||||
|  | // eslint-disable-next-line no-undef | ||||||
|  | waitForKeyElements('call-view', (element) => { callNotification.showCallNotification(element) }, false) | ||||||
|  |  | ||||||
|  | const callHistory = new CallHistory() | ||||||
|  | // eslint-disable-next-line no-undef | ||||||
|  | waitForKeyElements('.call-history-list call', (element) => { callHistory.showCallHistory(element) }, false) | ||||||
|  |  | ||||||
|  | const status = new Status() | ||||||
|  | // eslint-disable-next-line no-undef | ||||||
|  | waitForKeyElements('wc-account-menu', (element) => { status.showStatus(element) }, false) | ||||||
							
								
								
									
										186
									
								
								src/search.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								src/search.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,186 @@ | |||||||
|  | import { TapiContact } from './tapi-contact' | ||||||
|  | import { debounce } from './debounce' | ||||||
|  | import { axios, fireChangeEvents } from './utils' | ||||||
|  |  | ||||||
|  | export class Search { | ||||||
|  |   private currentSearchText = '' | ||||||
|  |  | ||||||
|  |   public createSearchWindow (element: HTMLElement) { | ||||||
|  |     console.log('Create TAPI Search') | ||||||
|  |  | ||||||
|  |     var form = document.createElement('form') | ||||||
|  |     form.style.width = '200px' | ||||||
|  |     form.style.float = 'right' | ||||||
|  |     form.style.marginRight = '20px' | ||||||
|  |     form.onsubmit = () => { | ||||||
|  |       var items = document.getElementsByClassName('tapi-search-result-selected') | ||||||
|  |       if (items.length === 0) { | ||||||
|  |         items = document.getElementsByClassName('tapi-search-result') | ||||||
|  |       } | ||||||
|  |       if (items.length > 0) { | ||||||
|  |         this.dial((<HTMLElement>items[0]).dataset.tapiNumber) | ||||||
|  |       } else { | ||||||
|  |         this.dial((<HTMLInputElement>document.getElementById('tapiSearchInput')).value) | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       return false | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     var searchBox = document.createElement('div') | ||||||
|  |     searchBox.classList.add('contact-search-box') | ||||||
|  |     searchBox.id = 'tapiSearchBox' | ||||||
|  |     form.appendChild(searchBox) | ||||||
|  |  | ||||||
|  |     var searchWrapper = document.createElement('div') | ||||||
|  |     searchWrapper.classList.add('search-input-wrapper') | ||||||
|  |     searchWrapper.style.position = 'relative' | ||||||
|  |     searchBox.appendChild(searchWrapper) | ||||||
|  |  | ||||||
|  |     var search = document.createElement('input') | ||||||
|  |     search.id = 'tapiSearchInput' | ||||||
|  |     search.autocomplete = 'off' | ||||||
|  |     search.classList.add('padder') | ||||||
|  |     search.classList.add('rounded') | ||||||
|  |     search.classList.add('bg-light') | ||||||
|  |     search.classList.add('no-border') | ||||||
|  |     search.classList.add('contact-search-box') | ||||||
|  |     search.placeholder = 'TAPI Suche' | ||||||
|  |     search.onfocus = () => { this.doSearch() } | ||||||
|  |     search.onkeydown = (ev) => { this.doSearchKeyDown(ev) } | ||||||
|  |     search.onblur = () => { | ||||||
|  |       console.log('TAPI Search exit', this) | ||||||
|  |       setTimeout(() => { | ||||||
|  |         console.log('TAPI clear search results') | ||||||
|  |         this.removeSearchResults() | ||||||
|  |       }, 500) | ||||||
|  |     } | ||||||
|  |     searchWrapper.appendChild(search) | ||||||
|  |  | ||||||
|  |     var icon = document.createElement('span') | ||||||
|  |     icon.classList.add('fa') | ||||||
|  |     icon.classList.add('fa-search') | ||||||
|  |     icon.classList.add('form-control-feedback') | ||||||
|  |     icon.style.color = 'grey' | ||||||
|  |     searchWrapper.appendChild(icon) | ||||||
|  |  | ||||||
|  |     element.parentElement.insertBefore(form, element) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private removeSearchResults () { | ||||||
|  |     var resultList = document.getElementById('tapiResults') | ||||||
|  |     if (resultList) { | ||||||
|  |       resultList.parentNode.removeChild(resultList) | ||||||
|  |     } | ||||||
|  |     this.currentSearchText = '' | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private doSearchKeyDown (ev: KeyboardEvent) { | ||||||
|  |     if (ev.key === 'ArrowUp') { | ||||||
|  |       let items = document.getElementsByClassName('tapi-search-result-selected') | ||||||
|  |       if (items.length > 0) { | ||||||
|  |         var prev = <Element>items[0].previousSibling | ||||||
|  |       } | ||||||
|  |       if (!prev) { | ||||||
|  |         items = document.getElementsByClassName('tapi-search-result') | ||||||
|  |         if (items.length > 0) { | ||||||
|  |           prev = items[items.length - 1] | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       if (prev) { | ||||||
|  |         this.selectResult(prev) | ||||||
|  |         prev.scrollIntoView(true) | ||||||
|  |       } | ||||||
|  |     } else if (ev.key === 'ArrowDown') { | ||||||
|  |       let items = document.getElementsByClassName('tapi-search-result-selected') | ||||||
|  |       if (items.length > 0) { | ||||||
|  |         var next = <Element>items[0].nextSibling | ||||||
|  |       } | ||||||
|  |       if (!next) { | ||||||
|  |         items = document.getElementsByClassName('tapi-search-result') | ||||||
|  |         if (items.length > 0) { | ||||||
|  |           next = items[0] | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       if (next) { | ||||||
|  |         this.selectResult(next) | ||||||
|  |         next.scrollIntoView(false) | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       this.doSearch() | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private doSearch = debounce(async () => { | ||||||
|  |     var search = <HTMLInputElement>document.getElementById('tapiSearchInput') | ||||||
|  |     var searchText = search.value.trim() | ||||||
|  |     if (searchText === '') { | ||||||
|  |       this.removeSearchResults() | ||||||
|  |       return | ||||||
|  |     } else if (searchText === this.currentSearchText) { | ||||||
|  |       return | ||||||
|  |     } | ||||||
|  |     console.log('Searching TAPI') | ||||||
|  |     var response = await axios.get<TapiContact[]>('http://cpatapi.cpsrvweb2016.cp-austria.at/search?query=' + encodeURIComponent(searchText)) | ||||||
|  |     console.log('TAPI Search response', response) | ||||||
|  |     var contacts = response.data | ||||||
|  |     console.log('TAPI Contacts', contacts) | ||||||
|  |     this.removeSearchResults() | ||||||
|  |     this.currentSearchText = searchText | ||||||
|  |  | ||||||
|  |     var resultList = document.createElement('ul') | ||||||
|  |     resultList.id = 'tapiResults' | ||||||
|  |     resultList.classList.add('search-nav-absolute') | ||||||
|  |     resultList.classList.add('search-nav-ul') | ||||||
|  |     document.getElementById('tapiSearchBox').appendChild(resultList) | ||||||
|  |  | ||||||
|  |     resultList.innerHTML = '' | ||||||
|  |     contacts.forEach(contact => { | ||||||
|  |       var li = document.createElement('li') | ||||||
|  |       li.classList.add('tapi-search-result') | ||||||
|  |       li.classList.add('search-result') | ||||||
|  |       li.classList.add('pointer') | ||||||
|  |       li.onmouseover = () => { this.selectResult(li) } | ||||||
|  |       li.dataset.tapiNumber = contact.tD_NUMBER_TAPI | ||||||
|  |       li.onclick = () => { this.dial(contact.tD_NUMBER_TAPI) } | ||||||
|  |       li.style.listStyle = 'outside none none' // display: flex; align-items: center; | ||||||
|  |  | ||||||
|  |       var resultText = document.createElement('div') | ||||||
|  |       resultText.classList.add('search-result-txt') | ||||||
|  |       li.appendChild(resultText) | ||||||
|  |  | ||||||
|  |       var line1 = document.createElement('div') | ||||||
|  |       line1.appendChild(document.createTextNode(contact.tD_NAME)) | ||||||
|  |       resultText.appendChild(line1) | ||||||
|  |  | ||||||
|  |       var line2 = document.createElement('div') | ||||||
|  |       line2.appendChild(document.createTextNode(contact.tD_MEDIUM + ': ' + contact.tD_NUMBER_TAPI)) | ||||||
|  |       resultText.appendChild(line2) | ||||||
|  |  | ||||||
|  |       resultList.appendChild(li) | ||||||
|  |     }) | ||||||
|  |   }, 200) | ||||||
|  |  | ||||||
|  |   private selectResult (resultLi: Element) { | ||||||
|  |     var items = document.getElementsByClassName('tapi-search-result') | ||||||
|  |     for (var item of items) { | ||||||
|  |       item.classList.remove('bg-light') | ||||||
|  |       item.classList.remove('tapi-search-result-selected') | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     resultLi.classList.add('bg-light') | ||||||
|  |     resultLi.classList.add('tapi-search-result-selected') | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private dial (number: string) { | ||||||
|  |     console.log('TAPI Search dialing', number); | ||||||
|  |     var searchInput = document.getElementById('dialpad-input'); | ||||||
|  |     (<HTMLInputElement>searchInput).value = number; | ||||||
|  |     (<HTMLInputElement>searchInput).focus; | ||||||
|  |     fireChangeEvents(searchInput); | ||||||
|  |  | ||||||
|  |     var toaster = document.querySelector('toaster-container'); | ||||||
|  |     if (window.getComputedStyle(toaster, null).display == 'none') { | ||||||
|  |       document.getElementById('menuDialer').click(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										19
									
								
								src/status.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/status.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | .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; | ||||||
|  |     background-color: #fff; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .show { | ||||||
|  |     display: block; | ||||||
|  | } | ||||||
							
								
								
									
										157
									
								
								src/status.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								src/status.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | |||||||
|  | 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); | ||||||
|  |  | ||||||
|  |     this.checkStatus(); | ||||||
|  |  | ||||||
|  |     waitForKeyElements("wc-account-menu > div > ul", (element: HTMLElement) => { this.addZcStatusPopup(element) }, true); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private async checkStatus() { | ||||||
|  |     if (this._enabled) { | ||||||
|  |       try { | ||||||
|  |         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.getElementsByTagName("wcavatar")[0] as HTMLAnchorElement).click(); | ||||||
|  |             setTimeout(() => { | ||||||
|  |               var statusId = this._currentStatus ? this._statusOn : this._statusOff; | ||||||
|  |               (document.getElementById(statusId) as HTMLSpanElement).click(); | ||||||
|  |             }, 1000); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } catch (error) { | ||||||
|  |         console.log(error); | ||||||
|  |       } | ||||||
|  |       setTimeout(() => this.checkStatus(), 30000); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private addZcStatusPopup(element: HTMLElement) { | ||||||
|  |     var divider = document.createElement('li'); | ||||||
|  |     divider.classList.add('divider'); | ||||||
|  |     divider.classList.add('dropdown-divider'); | ||||||
|  |     element.appendChild(divider); | ||||||
|  |  | ||||||
|  |     var menu = document.createElement('li'); | ||||||
|  |     element.appendChild(menu); | ||||||
|  |  | ||||||
|  |     var link = document.createElement('a'); | ||||||
|  |     link.id = 'tapi-zc-button'; | ||||||
|  |     link.innerText = 'ZeitConsens'; | ||||||
|  |     link.classList.add('dropdown-item'); | ||||||
|  |     link.classList.add('d-flex'); | ||||||
|  |     link.onclick = () => { | ||||||
|  |       document.getElementById('zc-modal').classList.toggle('show'); | ||||||
|  |     } | ||||||
|  |     menu.appendChild(link); | ||||||
|  |  | ||||||
|  |     var html = | ||||||
|  |     '<div role="document" class="modal-dialog">' + | ||||||
|  |     '  <div class="modal-content">' + | ||||||
|  |     '    <div class="modal-header">' + | ||||||
|  |     '      <h4 class="modal-title float-left">ZeitConsens Status</h4><button id="zc-btnClose" type="button" aria-label="Close" class="close float-right"><span aria-hidden="true">×</span></button>' + | ||||||
|  |     '    </div>' + | ||||||
|  |     '    <div class="modal-body">' + | ||||||
|  |     '      <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 out</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>'; | ||||||
|  |     '    </div>' + | ||||||
|  |     '    <div class="modal-footer">' + | ||||||
|  |     '      <button id="zc-btnOk" type="button" class="btn btn-primary">OK </button>' + | ||||||
|  |     '      <button id="zc-btnCancel" type="button" class="btn btn-light">Cancel </button>' + | ||||||
|  |     '    </div>' + | ||||||
|  |     '  </div>' + | ||||||
|  |     '</div>'; | ||||||
|  |     var modal = document.createElement('modal-container'); | ||||||
|  |     modal.id = 'zc-modal'; | ||||||
|  |     modal.classList.add('modal'); | ||||||
|  |     modal.classList.add('fade'); | ||||||
|  |     modal.innerHTML = html; | ||||||
|  |     var body = document.getElementsByTagName('body')[0].appendChild(modal); | ||||||
|  |  | ||||||
|  |     var btnClose = document.getElementById('zc-btnClose'); | ||||||
|  |     btnClose.onclick = () => { | ||||||
|  |       document.getElementById('zc-modal').classList.toggle('show'); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								src/tapi-contact.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/tapi-contact.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | /* eslint-disable camelcase */ | ||||||
|  | export interface TapiContact { | ||||||
|  |   tD_ID?: string; | ||||||
|  |   tD_NAME: string; | ||||||
|  |   tD_NUMBER?: string; | ||||||
|  |   tD_NUMBER_TAPI?: string; | ||||||
|  |   tD_MEDIUM?: string; | ||||||
|  | } | ||||||
							
								
								
									
										49
									
								
								src/utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/utils.ts
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										4
									
								
								src/zc-status.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | export class ZcStatus { | ||||||
|  |   public user: string; | ||||||
|  |   public loggedIn: boolean; | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | { | ||||||
|  |   "compilerOptions": { | ||||||
|  |     "outDir": "./dist/", | ||||||
|  |     "noImplicitAny": true, | ||||||
|  |     "module": "es6", | ||||||
|  |     "target": "es6", | ||||||
|  |     "allowJs": true, | ||||||
|  |     "moduleResolution": "node" | ||||||
|  |   } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user