From d49b934c9ae44ddf8dde1fe9c2bcc31695848746 Mon Sep 17 00:00:00 2001 From: Daniel Triendl Date: Tue, 20 Oct 2020 17:47:54 +0200 Subject: [PATCH] Restructure, improive keyboard input handling, debouncing --- 3CX_TAPI.user.js | 349 +++++++++++++++++++++++++++++------------------ 1 file changed, 218 insertions(+), 131 deletions(-) diff --git a/3CX_TAPI.user.js b/3CX_TAPI.user.js index fb05846..570e243 100644 --- a/3CX_TAPI.user.js +++ b/3CX_TAPI.user.js @@ -1,146 +1,233 @@ // ==UserScript== -// @name 3CX TAPI -// @version 1 -// @grant GM.xmlHttpRequest -// @include https://192.168.0.154:5001/webclient* -// @include https://cpsolution.my3cx.at:5001/webclient* -// @downloadURL http://scootaloo.cp-austria.at/gitlist/3cx_tapi.git/raw/master/3CX_TAPI.user.js -// @require https://cdn.jsdelivr.net/gh/CoeJoder/waitForKeyElements.js@v1.2/waitForKeyElements.js +// @name 3CX TAPI +// @author Daniel Triendl +// @namespace http://cp-solutions.at +// @copyright Copyright 2020 CP Solutions GmbH +// @version 2 +// @grant GM.xmlHttpRequest +// @include https://192.168.0.154:5001/webclient* +// @include https://cpsolution.my3cx.at:5001/webclient* +// @downloadURL http://scootaloo.cp-austria.at/gitlist/3cx_tapi.git/raw/master/3CX_TAPI.user.js +// @require https://cdn.jsdelivr.net/gh/CoeJoder/waitForKeyElements.js@v1.2/waitForKeyElements.js // ==/UserScript== console.log('TAPI init'); -function fireChangeEvents(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); -} +const debounce = (func, wait) => { + let timeout; -function removeSearchResults() { - var resultList = document.getElementById('tapiResults'); - if (resultList) { - resultList.parentNode.removeChild(resultList); - } -} + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout); + func(...args); + }; -var doSearch = function () { - var search = document.getElementById('tapiSearchInput'); - var searchText = search.value.trim(); - if (searchText == '') { - removeSearchResults(); - return; - } - console.log('Searching TAPI'); - GM.xmlHttpRequest({ - method: 'GET', - url: 'http://cpatapi.cpsrvweb2016.cp-austria.at/search?query=' + encodeURIComponent(searchText), - onload: function (response) { - console.log('TAPI Search response', response); - var contacts = JSON.parse(response.responseText); - console.log('TAPI Contacts', contacts); - removeSearchResults(); - - 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 = ''; - for (var i = 0; i < contacts.length; i++) { - var li = document.createElement('li'); - li.classList.add('search-result'); - li.classList.add('pointer'); - li.onmouseover = function () { - this.classList.add('bg-light'); - }; - li.onmouseout = function () { - this.classList.remove('bg-light'); - }; - li.contact = contacts[i]; - li.onclick = function () { - var contact = this.contact; - var searchInput = document.getElementsByName('searchByNumberInput'); - if (searchInput.length > 0) { - searchInput[0].value = contact.tD_NUMBER_TAPI; - searchInput[0].focus(); - - fireChangeEvents(searchInput[0]); - } - }; - 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(contacts[i].tD_NAME)); - resultText.appendChild(line1); - - var line2 = document.createElement('div') - line2.appendChild(document.createTextNode(contacts[i].tD_NUMBER_TAPI)); - resultText.appendChild(line2); - - resultList.appendChild(li); - } - } - }); + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; }; -waitForKeyElements('div.nav-search', (element) => { - console.log('Create TAPI Search'); +const tapi = { + fireChangeEvents: (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); + }, - var form = document.createElement('form'); - form.style.width = '200px'; - form.style.float = 'right'; - form.style.marginRight = '20px'; + removeSearchResults: () => { + var resultList = document.getElementById('tapiResults'); + if (resultList) { + resultList.parentNode.removeChild(resultList); + } + tapi.currentSearchText = ''; + }, - var searchBox = document.createElement('div'); - searchBox.classList.add('contact-search-box'); - searchBox.id = 'tapiSearchBox'; - form.appendChild(searchBox); + dial: (item) => { + console.log(item); + var contact = item.contact; + var searchInput = document.getElementsByName('searchByNumberInput'); + if (searchInput.length > 0) { + searchInput[0].value = contact.tD_NUMBER_TAPI; + searchInput[0].focus(); - var searchWrapper = document.createElement('div'); - searchWrapper.classList.add('search-input-wrapper'); - searchWrapper.style.position = 'relative'; - searchBox.appendChild(searchWrapper); + tapi.fireChangeEvents(searchInput[0]); + } + }, - var search = document.createElement('input'); - search.id = 'tapiSearchInput'; - 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 = doSearch; - search.onkeyup = doSearch; - search.onblur = function () { - console.log('TAPI Search exit'); - setTimeout(function () { - console.log('TAPI clear search results'); - removeSearchResults(); - }, 500); - }; - searchWrapper.appendChild(search); + selectResult: (resultLi) => { + var items = document.getElementsByClassName('tapi-search-result'); + for (var item of items) { + item.classList.remove('bg-light'); + item.classList.remove('tapi-search-result-selected'); + } - 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); + resultLi.classList.add('bg-light'); + resultLi.classList.add('tapi-search-result-selected'); + }, - element.appendChild(form); -}, false); \ No newline at end of file + currentSearchText: '', + + doSearch: debounce(() => { + var search = document.getElementById('tapiSearchInput'); + var searchText = search.value.trim(); + if (searchText == '') { + tapi.removeSearchResults(); + return; + } else if (searchText == tapi.currentSearchText) { + return; + } + console.log('Searching TAPI'); + GM.xmlHttpRequest({ + method: 'GET', + url: 'http://cpatapi.cpsrvweb2016.cp-austria.at/search?query=' + encodeURIComponent(searchText), + onload: function (response) { + console.log('TAPI Search response', response); + var contacts = JSON.parse(response.responseText); + console.log('TAPI Contacts', contacts); + tapi.removeSearchResults(); + tapi.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 = ''; + for (var i = 0; i < contacts.length; i++) { + var li = document.createElement('li'); + li.classList.add('tapi-search-result'); + li.classList.add('search-result'); + li.classList.add('pointer'); + li.onmouseover = function () { + tapi.selectResult(this); + }; + li.contact = contacts[i]; + li.onclick = function () { + tapi.dial(this); + } + 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(contacts[i].tD_NAME)); + resultText.appendChild(line1); + + var line2 = document.createElement('div') + line2.appendChild(document.createTextNode(contacts[i].tD_NUMBER_TAPI)); + resultText.appendChild(line2); + + resultList.appendChild(li); + } + } + }); + }, 200), + + doSearchKeyDown: (ev) => { + var items; + if (ev.key == 'ArrowUp') { + items = document.getElementsByClassName('tapi-search-result-selected'); + if (items.length > 0) { + var prev = items[0].previousSibling; + } + if (!prev) { + items = document.getElementsByClassName('tapi-search-result'); + if (items.length > 0) { + prev = items[items.length - 1]; + } + } + if (prev) { + tapi.selectResult(prev); + prev.scrollIntoView(true); + } + } else if (ev.key == 'ArrowDown') { + items = document.getElementsByClassName('tapi-search-result-selected'); + if (items.length > 0) { + var next = items[0].nextSibling; + } + if (!next) { + items = document.getElementsByClassName('tapi-search-result'); + if (items.length > 0) { + next = items[0]; + } + } + if (next) { + tapi.selectResult(next); + next.scrollIntoView(false); + } + } else { + tapi.doSearch(); + } + }, + + createSearchBox: (element) => { + console.log('Create TAPI Search'); + + var form = document.createElement('form'); + form.style.width = '200px'; + form.style.float = 'right'; + form.style.marginRight = '20px'; + form.onsubmit = function () { + var items = document.getElementsByClassName('tapi-search-result-selected'); + if (items.length > 0) { + tapi.dial(items[0]); + } + + 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 = tapi.doSearch; + search.onkeydown = tapi.doSearchKeyDown; + search.onblur = function () { + console.log('TAPI Search exit'); + setTimeout(function () { + console.log('TAPI clear search results'); + tapi.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.appendChild(form); + } +}; + +waitForKeyElements('div.nav-search', tapi.createSearchBox, false);