/* // Usage FgEmojiPicker.init({ trigger: 'button', position: ['bottom', 'right'], dir: 'directory/to/json', (without json name) preFetch: true, //load emoji json when function called emit(emoji) { console.log(emoji); } }); */ const FgEmojiPicker = function (options) { this.options = options; this.emojiJson = null; if (!this.options) { return console.error('You must provide object as a first argument') } this.init = () => { // Generate style this.functions.generateStyle.call(this); this.selectors.trigger = this.options.hasOwnProperty('trigger') ? this.options.trigger : console.error('You must proved trigger element like this - \'EmojiPicker.init({trigger: "selector"})\' '); this.selectors.search = '.fg-emoji-picker-search input'; this.selectors.emojiContainer = '.fg-emoji-picker-grid' this.emojiItems = undefined; this.variable.emit = this.options.emit || null; this.variable.removeOnSelection = this.options.removeOnSelection || false; this.variable.closeButton = this.options.closeButton || true; this.variable.position = this.options.position || null; this.variable.dir = this.options.dir || ''; this.insertInto = this.options.insertInto || undefined; if (!this.selectors.trigger) return; this.bindEvents(); this.options && this.options.preFetch && this.functions.fetchEmojiData(); } this.lib = (el = undefined) => { return { el: document.querySelectorAll(el), on(event, callback, classList = undefined) { if (!classList) { this.el.forEach(item => { item.addEventListener(event, callback.bind(item)) }) } else { this.el.forEach(item => { item.addEventListener(event, (e) => { if (e.target.closest(classList)) { callback.call(e.target.closest(classList), e) } }) }) } } } }, this.variable = { position: null, dir: '', categoryIcons: { 'smileys--people': ' ', 'animals--nature': ' ', 'travel--places': ' ', 'activities': '', 'objects': ' ', 'symbols': ' ', 'flags': '', 'search': ' ', 'close': ' ', } } this.selectors = { emit: null, trigger: null, emojiPicker: '.fg-emoji-picker', emojiFooter: '.fg-emoji-picker-footer', emojiBody: '.fg-emoji-picker-all-categories', emojiHeader: '.fg-emoji-picker-categories', closeButton: '.fg-emoji-picker-close-button' } this.bindEvents = () => { this.lib('body').on('click', this.functions.removeEmojiPicker.bind(this)); this.lib('body').on('click', this.functions.emitEmoji.bind(this)); this.lib('body').on('click', this.functions.openEmojiSelector.bind(this), this.selectors.trigger); this.lib('body').on('input', this.functions.search.bind(this), this.selectors.search); // this.lib('body').on('keydown', this.functions.removeEmojiPicker.bind(this)); this.lib('body').on('click', this.destroy.bind(this), this.selectors.closeButton); window.addEventListener('keydown', e => { if (e.keyCode === 27) { if (document.querySelector(this.selectors.emojiPicker)) { document.querySelectorAll(this.selectors.emojiPicker).forEach(emoji => emoji.remove()) this.emojiItems = undefined } } }) } this.html = { pickerBody: () => { return `
`; } } this.destroy = () => { document.querySelectorAll(this.selectors.emojiPicker).forEach(p => p.remove()) this.emojiItems = undefined; return true; } setCaretPosition = (field, caretPos) => { var elem = field if (elem != null) { if (elem.createTextRange) { var range = elem.createTextRange(); range.move('character', caretPos); range.select(); } else { if (elem.selectionStart) { elem.focus(); elem.setSelectionRange(caretPos, caretPos); } else { elem.focus(); } } } } this.functions = { // Put in place putInPlace(myField, myValue) { if (document.selection) { myField.focus(); sel = document.selection.createRange(); sel.text = myValue; } else if (myField.selectionStart || myField.selectionStart == "0") { const startPos = myField.selectionStart; const endPos = myField.selectionEnd; myField.value = myField.value.substring(0, startPos) + myValue + myField.value.substring(endPos, myField.value.length); setCaretPosition(myField, startPos + 2) } else { myField.value += myValue; myField.focus() } }, // Search search(e) { const val = e.target.value; if (!Array.isArray(this.emojiItems)) { this.emojiItems = Array.from(e.target.closest(this.selectors.emojiPicker).querySelectorAll(`${this.selectors.emojiBody} li`)); } this.emojiItems.filter(emoji => { if (!emoji.getAttribute('data-name').match(val)) { emoji.style.display = 'none' } else { emoji.style.display = '' } }) if (!val.length) this.emojiItems = undefined; }, fetchEmojiData: () => { fetch(`${this.variable.dir}full-emoji-list.json`) .then(response => response.json()) .then(object => { this.emojiJson = object }); }, generateStyle() { document.head.insertAdjacentHTML('beforeend', ` `) }, removeEmojiPicker() { const el = window.event.target; const picker = document.querySelector(this.selectors.emojiPicker); if (!el.closest(this.selectors.emojiPicker)) picker ? picker.remove() : false; this.emojiItems = undefined }, emitEmoji(e) { const el = e.target; if (el.tagName.toLowerCase() == 'a' && el.className.includes('fg-emoji-picker-item')) { e.preventDefault(); let emoji = { emoji: el.getAttribute('href'), code: el.getAttribute('data-code') } if (this.variable.emit) this.variable.emit(emoji, this.triggerer) // If insert into option exists if (this.insertInto) this.functions.putInPlace(this.insertInto, emoji.emoji) if (this.variable.removeOnSelection) { const picker = document.querySelector(this.selectors.emojiPicker); picker.remove(); } } }, // Open omoji picker openEmojiSelector(e) { let el = e.target.closest(this.selectors.trigger) if (el) { e.preventDefault(); // Bounding rect // Trigger position and (trigger) sizes let el = e.target.closest(this.selectors.trigger) if (typeof this.variable.emit === 'function') this.triggerer = el // Insert picker const emojiPickerMain = new DOMParser().parseFromString(this.html.pickerBody.apply(this), 'text/html').body.firstElementChild; document.body.insertAdjacentElement('afterbegin', emojiPickerMain); let positions = { buttonTop: e.pageY, buttonWidth: el.offsetWidth, buttonFromLeft: el.getBoundingClientRect().left, bodyHeight: document.body.offsetHeight, bodyWidth: document.body.offsetWidth, windowScrollPosition: window.pageYOffset, emojiHeight: emojiPickerMain.offsetHeight, emojiWidth: emojiPickerMain.offsetWidth, } // Element position object let position = { top: emojiPickerMain.style.top = positions.buttonTop - positions.emojiHeight, left: emojiPickerMain.style.left = positions.buttonFromLeft - positions.emojiWidth, bottom: emojiPickerMain.style.top = positions.buttonTop, right: emojiPickerMain.style.left = positions.buttonFromLeft + positions.buttonWidth } // Positioning emoji container top if (this.variable.position) { this.variable.position.forEach(elemPos => { if (elemPos === 'right') { emojiPickerMain.style.left = position[elemPos] + 'px'; } else if (elemPos === 'bottom') { emojiPickerMain.style.top = position[elemPos] + 'px'; } else { emojiPickerMain.style[elemPos] = position[elemPos] + 'px'; } }) } // Emoji Picker Promise this.emojiPicker().then(emojiPicker => { // Create node from const node = new DOMParser().parseFromString(emojiPicker, 'text/html').body.firstElementChild; emojiPickerMain.innerHTML = node.innerHTML; const emojiFooter = emojiPickerMain.querySelector(this.selectors.emojiFooter); const emojiBody = emojiPickerMain.querySelector(this.selectors.emojiBody) const emojiPickerHeader = emojiPickerMain.querySelector(this.selectors.emojiHeader); emojiPickerMain.querySelector('input').focus(); // Add event listener on click document.body.querySelector(this.selectors.emojiPicker).onclick = function (e) { e.preventDefault(); let scrollTo = (element, to, duration = 100) => { if (duration <= 0) return; var difference = to - element.scrollTop; var perTick = difference / duration * 10; setTimeout(function () { element.scrollTop = element.scrollTop + perTick; if (element.scrollTop === to) return; scrollTo(element, to, duration - 10); }, 10); } const el = e.target; const filterLlnk = el.closest('a'); document.querySelectorAll('.fg-emoji-picker-categories li').forEach(item => item.classList.remove('active')) if (filterLlnk && filterLlnk.closest('li') && filterLlnk.closest('li').getAttribute('data-index')) { let list = filterLlnk.closest('li'); list.classList.add('active'); let listIndex = list.getAttribute('data-index'); scrollTo(emojiBody, emojiBody.querySelector(`#${listIndex}`).offsetTop - emojiPickerHeader.offsetHeight); } } }) } } }, // Create emoji container / Builder engine this.emojiPicker = () => { let picker = `
%categories%
%pickerContainer%
`; const closeBtn = this.variable.closeButton ? `
  • ${this.variable.categoryIcons.close}
  • ` : ''; let categories = ``; let categoriesInner = ``; let outerUl = `
    %outerUL%
    `; let innerLists = ``; let fetchData = null; if (this.emojiJson) { fetchData = new Promise((resolve, reject) => { let index = 0; // Loop through emoji object let object = this.emojiJson; for (const key in object) { if (object.hasOwnProperty(key)) { // Index count index += 1; let keyToId = key.split(' ').join('-').split('&').join('').toLowerCase(); const categories = object[key]; categoriesInner += `
  • ${this.variable.categoryIcons[keyToId]}
  • ` innerLists += ` `; } } let allSmiles = outerUl.replace('%outerUL%', innerLists) let cats = categories.replace('%categories%', categoriesInner); let pickerContainer = picker.replace('%pickerContainer%', allSmiles) let data = pickerContainer.replace('%categories%', cats); resolve(data); }); } else { fetchData = fetch(`${this.variable.dir}full-emoji-list.json`) .then(response => response.json()) .then(object => { // Index count let index = 0; this.emojiJson = object; // Loop through emoji object for (const key in object) { if (object.hasOwnProperty(key)) { // Index count index += 1; let keyToId = key.split(' ').join('-').split('&').join('').toLowerCase(); const categories = object[key]; categoriesInner += `
  • ${this.variable.categoryIcons[keyToId]}
  • ` innerLists += ` `; } } let allSmiles = outerUl.replace('%outerUL%', innerLists) let cats = categories.replace('%categories%', categoriesInner); let pickerContainer = picker.replace('%pickerContainer%', allSmiles) let data = pickerContainer.replace('%categories%', cats); return data; }) } return fetchData; } this.init(); }