import Awesomplete from 'awesomplete'; import he from 'he'; /** * Find a parent element according to its tag and its attributes * * @param element Element where to start the search * @param tagName Expected parent tag name * @param attributes Associative array of expected attributes (name=>value). * * @returns Found element or null. */ function findParent(element, tagName, attributes) { const parentMatch = (key) => attributes[key] !== '' && element.getAttribute(key).indexOf(attributes[key]) !== -1; while (element) { if (element.tagName.toLowerCase() === tagName) { if (Object.keys(attributes).find(parentMatch)) { return element; } } element = element.parentElement; } return null; } /** * Ajax request to refresh the CSRF token. */ function refreshToken(basePath, callback) { const xhr = new XMLHttpRequest(); xhr.open('GET', `${basePath}/admin/token`); xhr.onload = () => { const elements = document.querySelectorAll('input[name="token"]'); [...elements].forEach((element) => { element.setAttribute('value', xhr.responseText); }); if (callback) { callback(xhr.response); } }; xhr.send(); } function createAwesompleteInstance(element, separator, tags = []) { const awesome = new Awesomplete(Awesomplete.$(element)); // Tags are separated by separator. Ignore leading search flags awesome.filter = (text, input) => { let filterFunc = Awesomplete.FILTER_CONTAINS; let term = input.match(new RegExp(`[^${separator}]*$`))[0]; const termFlagged = term.replace(/^[-~+]/, ''); if (term !== termFlagged) { term = termFlagged; filterFunc = Awesomplete.FILTER_STARTSWITH; } return filterFunc(text, term); }; // Insert new selected tag in the input awesome.replace = (text) => { const before = awesome.input.value.match(new RegExp(`^(.+${separator}+)?[-~+]?|`))[0]; awesome.input.value = `${before}${text}${separator}`; }; // Highlight found items awesome.item = (text, input) => Awesomplete.ITEM(text, input.match(new RegExp(`[^${separator}]*$`))[0]); // Don't display already selected items // WARNING: pseudo classes does not seem to work with string litterals... const reg = new RegExp(`([^${separator}]+)${separator}`, 'g'); let match; awesome.data = (item, input) => { while ((match = reg.exec(input))) { if (item === match[1]) { return ''; } } return item; }; awesome.minChars = 1; if (tags.length) { awesome.list = tags; } return awesome; } /** * Update awesomplete list of tag for all elements matching the given selector * * @param selector CSS selector * @param tags Array of tags * @param instances List of existing awesomplete instances * @param separator Tags separator character */ function updateAwesompleteList(selector, tags, instances, separator) { if (instances.length === 0) { // First load: create Awesomplete instances const elements = document.querySelectorAll(selector); [...elements].forEach((element) => { instances.push(createAwesompleteInstance(element, separator, tags)); }); } else { // Update awesomplete tag list instances.map((item) => { item.list = tags; return item; }); } return instances; } /** * Add the class 'hidden' to city options not attached to the current selected continent. * * @param cities List of