// ==UserScript== // @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'); const debounce = (func, wait) => { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }; 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); }, removeSearchResults: () => { var resultList = document.getElementById('tapiResults'); if (resultList) { resultList.parentNode.removeChild(resultList); } tapi.currentSearchText = ''; }, 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(); tapi.fireChangeEvents(searchInput[0]); } }, 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'); } resultLi.classList.add('bg-light'); resultLi.classList.add('tapi-search-result-selected'); }, 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);