-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #393 from FriendsOfREDAXO/tyrant88-patch-161
integrated autocomplete without jQuery into AddOn
- Loading branch information
Showing
18 changed files
with
772 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
document.addEventListener('DOMContentLoaded', function () { | ||
function suggest(input, options) { | ||
var cache = [], | ||
cacheSize = 0, | ||
results = document.createElement('ul'), | ||
timeout = null, | ||
prevLength = 0; | ||
|
||
results.className = options.resultsClass; | ||
document.body.appendChild(results); | ||
|
||
function getPosition(el) { | ||
var rect = el.getBoundingClientRect(); | ||
return { | ||
top: rect.top + window.scrollY, | ||
left: rect.left + window.scrollX | ||
}; | ||
} | ||
|
||
function updatePosition() { | ||
var pos = getPosition(input); | ||
results.style.top = pos.top + input.offsetHeight + 'px'; | ||
results.style.left = pos.left + 'px'; | ||
} | ||
|
||
function handleKeyup(e) { | ||
if (/27$|38$|40$/.test(e.keyCode) && results.style.display !== 'none' || /^13$|^9$/.test(e.keyCode) && getCurrentResult()) { | ||
e.preventDefault(); | ||
e.stopPropagation(); | ||
switch (e.keyCode) { | ||
case 38: | ||
selectPrevious(); | ||
break; | ||
case 40: | ||
selectNext(); | ||
break; | ||
case 9: | ||
case 13: | ||
chooseResult(); | ||
break; | ||
case 27: | ||
results.style.display = 'none'; | ||
break; | ||
} | ||
} else { | ||
if (input.value.length !== prevLength) { | ||
clearTimeout(timeout); | ||
timeout = setTimeout(fetchSuggestions, options.delay); | ||
prevLength = input.value.length; | ||
} | ||
} | ||
} | ||
|
||
function fetchSuggestions() { | ||
var query = input.value.trim(); | ||
if (query.length >= options.minchars) { | ||
var cached = getCached(query); | ||
if (cached) { | ||
displayResults(cached.items); | ||
} else { | ||
fetch(options.source + '&q=' + encodeURIComponent(query)) | ||
.then(response => response.text()) | ||
.then(data => { | ||
var items = parseResults(data, query); | ||
displayResults(items); | ||
addToCache(query, items, data.length); | ||
}); | ||
} | ||
} else { | ||
results.style.display = 'none'; | ||
} | ||
} | ||
|
||
function getCached(query) { | ||
for (var i = 0; i < cache.length; i++) { | ||
if (cache[i].q === query) { | ||
cache.unshift(cache.splice(i, 1)[0]); | ||
return cache[0]; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
function addToCache(query, items, size) { | ||
while (cache.length && cacheSize + size > options.maxCacheSize) { | ||
var cached = cache.pop(); | ||
cacheSize -= cached.size; | ||
} | ||
cache.push({q: query, size: size, items: items}); | ||
cacheSize += size; | ||
} | ||
|
||
function displayResults(items) { | ||
if (items.length) { | ||
results.innerHTML = ''; | ||
for (var i = 0; i < items.length; i++) { | ||
var li = document.createElement('li'); | ||
li.innerHTML = items[i]; | ||
li.addEventListener('mouseover', function () { | ||
var children = results.children; | ||
for (var j = 0; j < children.length; j++) { | ||
children[j].classList.remove(options.selectClass); | ||
} | ||
this.classList.add(options.selectClass); | ||
}); | ||
li.addEventListener('click', function (e) { | ||
e.preventDefault(); | ||
e.stopPropagation(); | ||
chooseResult(); | ||
}); | ||
results.appendChild(li); | ||
} | ||
results.style.display = 'block'; | ||
} else { | ||
results.style.display = 'none'; | ||
} | ||
} | ||
|
||
function parseResults(data, query) { | ||
var items = data.split(options.delimiter).map(item => item.trim()).filter(item => item); | ||
var regex = new RegExp(query, 'ig'); | ||
return items.map(item => item.replace(regex, function (match) { | ||
return '<span class="' + options.matchClass + '">' + match + '</span>'; | ||
})); | ||
} | ||
|
||
function getCurrentResult() { | ||
var selected = results.querySelector('.' + options.selectClass); | ||
return selected || false; | ||
} | ||
|
||
function chooseResult() { | ||
var currentResult = getCurrentResult(); | ||
if (currentResult) { | ||
input.value = currentResult.textContent; | ||
results.style.display = 'none'; | ||
if (options.onSelect) { | ||
options.onSelect.apply(input, [currentResult.textContent]); | ||
} | ||
} | ||
} | ||
|
||
function selectNext() { | ||
var currentResult = getCurrentResult(); | ||
if (currentResult) { | ||
var next = currentResult.nextElementSibling; | ||
currentResult.classList.remove(options.selectClass); | ||
if (next) { | ||
next.classList.add(options.selectClass); | ||
} else { | ||
results.firstElementChild.classList.add(options.selectClass); | ||
} | ||
} else { | ||
results.firstElementChild.classList.add(options.selectClass); | ||
} | ||
} | ||
|
||
function selectPrevious() { | ||
var currentResult = getCurrentResult(); | ||
if (currentResult) { | ||
var prev = currentResult.previousElementSibling; | ||
currentResult.classList.remove(options.selectClass); | ||
if (prev) { | ||
prev.classList.add(options.selectClass); | ||
} else { | ||
results.lastElementChild.classList.add(options.selectClass); | ||
} | ||
} else { | ||
results.lastElementChild.classList.add(options.selectClass); | ||
} | ||
} | ||
|
||
input.addEventListener('keyup', handleKeyup); | ||
input.addEventListener('blur', function () { | ||
setTimeout(function () { | ||
results.style.display = 'none'; | ||
}, 200); | ||
}); | ||
window.addEventListener('resize', updatePosition); | ||
window.addEventListener('load', updatePosition); | ||
|
||
updatePosition(); | ||
} | ||
|
||
function initSuggest() { | ||
var searchInput = document.querySelector(".search_it-form input[name=search]"); | ||
|
||
if (searchInput) { | ||
suggest(searchInput, { | ||
source: 'index.php?rex-api-call=search_it_autocomplete&rnd=' + Math.random(), | ||
delay: 100, | ||
resultsClass: 'ac_results', | ||
selectClass: 'ac_over', | ||
matchClass: 'ac_match', | ||
minchars: 2, | ||
delimiter: '\n', | ||
onSelect: function (value) { | ||
var searchForm = searchInput.closest('.search_it-form'); | ||
if (searchForm.classList.contains('search_it-form-autocomplete')) { | ||
searchForm.submit(); | ||
return false; | ||
} | ||
|
||
}, | ||
maxCacheSize: 65536 | ||
}); | ||
} | ||
} | ||
|
||
initSuggest(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# Installation Autocomplete | ||
|
||
Es stellt ein "Suggest"-PlugIn für die Autovervollständigung bei | ||
der Suche im Frontend zur Verfügung und generiert einen Code welcher im Template | ||
eingebunden werden muss. | ||
|
||
## Requirements | ||
|
||
* Funktionierendes Suchformular, das die HTML-Klasse "search_it-form", | ||
sowie ein HTML-Eingabefeld für die Suche mit dem Namen "search" beinhaltet. | ||
|
||
## Installation | ||
|
||
1. Autocomplete und aktivieren | ||
2. Konfiguration im Plugin vornehmen und speichern | ||
3. Den generierten Code für das Template herauskopieren und in das Template, | ||
welches für das Suchfeld verwendet wird, vor dem schließenden `</body>` Tag | ||
hinzufügen | ||
4. Sollte das Suchfeld überall verwendet werden, beispielsweise im Kopf der | ||
Seite, muss der generierte Code in das entsprechende Template hinzugefügt | ||
werden | ||
5. Optional: CSS und JS Datei in den eigenen Frontend_prozess einbauen ( z.B. | ||
per Minify oder im Bimmelbam ) | ||
|
||
## Lizenz | ||
|
||
"Autocomplete" von Manétage steht unter MIT Lizenz. | ||
|
||
## Rechtliches | ||
|
||
Verwendung auf eigene Gefahr. | ||
|
||
## Autor | ||
|
||
**Manetage** - Ronny Kemmereit / Pascal Schuchmann | ||
Norbert Micheel | ||
|
||
**Friends Of REDAXO** | ||
|
||
* http://www.redaxo.org | ||
* https://github.com/FriendsOfREDAXO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.