|
|
@ -15,6 +15,7 @@ import Config from "../../../lib/config"; |
|
|
|
import { i18n, LangPackKey } from "../../../lib/langPack"; |
|
|
|
import { i18n, LangPackKey } from "../../../lib/langPack"; |
|
|
|
import { RichTextProcessor } from "../../../lib/richtextprocessor"; |
|
|
|
import { RichTextProcessor } from "../../../lib/richtextprocessor"; |
|
|
|
import rootScope from "../../../lib/rootScope"; |
|
|
|
import rootScope from "../../../lib/rootScope"; |
|
|
|
|
|
|
|
import { emojiFromCodePoints } from "../../../vendor/emoji"; |
|
|
|
import { putPreloader } from "../../misc"; |
|
|
|
import { putPreloader } from "../../misc"; |
|
|
|
import Scrollable from "../../scrollable"; |
|
|
|
import Scrollable from "../../scrollable"; |
|
|
|
import StickyIntersector from "../../stickyIntersector"; |
|
|
|
import StickyIntersector from "../../stickyIntersector"; |
|
|
@ -53,10 +54,10 @@ export function appendEmoji(emoji: string, container: HTMLElement, prepend = fal |
|
|
|
|
|
|
|
|
|
|
|
if(spanEmoji.firstElementChild && !RichTextProcessor.emojiSupported) { |
|
|
|
if(spanEmoji.firstElementChild && !RichTextProcessor.emojiSupported) { |
|
|
|
const image = spanEmoji.firstElementChild as HTMLImageElement; |
|
|
|
const image = spanEmoji.firstElementChild as HTMLImageElement; |
|
|
|
image.setAttribute('loading', 'lazy'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const url = image.src; |
|
|
|
const url = image.src; |
|
|
|
if(!loadedURLs.has(url)) { |
|
|
|
if(!loadedURLs.has(url)) { |
|
|
|
|
|
|
|
image.setAttribute('loading', 'lazy'); |
|
|
|
const placeholder = document.createElement('span'); |
|
|
|
const placeholder = document.createElement('span'); |
|
|
|
placeholder.classList.add('emoji-placeholder'); |
|
|
|
placeholder.classList.add('emoji-placeholder'); |
|
|
|
|
|
|
|
|
|
|
@ -89,6 +90,8 @@ export function appendEmoji(emoji: string, container: HTMLElement, prepend = fal |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
export function getEmojiFromElement(element: HTMLElement) { |
|
|
|
export function getEmojiFromElement(element: HTMLElement) { |
|
|
|
|
|
|
|
if(!findUpClassName(element, 'super-emoji')) return ''; |
|
|
|
|
|
|
|
|
|
|
|
if(element.nodeType === 3) return element.nodeValue; |
|
|
|
if(element.nodeType === 3) return element.nodeValue; |
|
|
|
if(element.tagName === 'SPAN' && !element.classList.contains('emoji') && element.firstElementChild) { |
|
|
|
if(element.tagName === 'SPAN' && !element.classList.contains('emoji') && element.firstElementChild) { |
|
|
|
element = element.firstElementChild as HTMLElement; |
|
|
|
element = element.firstElementChild as HTMLElement; |
|
|
@ -170,7 +173,7 @@ export default class EmojiTab implements EmoticonsTab { |
|
|
|
console.log('append emoji', emoji, emojiUnicode(emoji)); |
|
|
|
console.log('append emoji', emoji, emojiUnicode(emoji)); |
|
|
|
} */ |
|
|
|
} */ |
|
|
|
|
|
|
|
|
|
|
|
let emoji = unified.split('-').reduce((prev, curr) => prev + String.fromCodePoint(parseInt(curr, 16)), ''); |
|
|
|
let emoji = emojiFromCodePoints(unified); |
|
|
|
//if(emoji.includes('🕵')) {
|
|
|
|
//if(emoji.includes('🕵')) {
|
|
|
|
//console.log('toCodePoints', toCodePoints(emoji));
|
|
|
|
//console.log('toCodePoints', toCodePoints(emoji));
|
|
|
|
//emoji = emoji.replace(/(\u200d[\u2640\u2642\u2695])(?!\ufe0f)/, '\ufe0f$1');
|
|
|
|
//emoji = emoji.replace(/(\u200d[\u2640\u2642\u2695])(?!\ufe0f)/, '\ufe0f$1');
|
|
|
@ -236,64 +239,75 @@ export default class EmojiTab implements EmoticonsTab { |
|
|
|
this.content.addEventListener('click', this.onContentClick); |
|
|
|
this.content.addEventListener('click', this.onContentClick); |
|
|
|
this.stickyIntersector = EmoticonsDropdown.menuOnClick(menu, emojiScroll); |
|
|
|
this.stickyIntersector = EmoticonsDropdown.menuOnClick(menu, emojiScroll); |
|
|
|
this.init = null; |
|
|
|
this.init = null; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onContentClick = (e: MouseEvent) => { |
|
|
|
rootScope.addEventListener('emoji_recent', (emoji) => { |
|
|
|
cancelEvent(e); |
|
|
|
const children = Array.from(this.recentItemsDiv.children) as HTMLElement[]; |
|
|
|
let target = e.target as HTMLElement; |
|
|
|
for(let i = 0, length = children.length; i < length; ++i) { |
|
|
|
//if(target.tagName !== 'SPAN') return;
|
|
|
|
const el = children[i]; |
|
|
|
|
|
|
|
const _emoji = getEmojiFromElement(el); |
|
|
|
|
|
|
|
if(emoji === _emoji) { |
|
|
|
|
|
|
|
if(i === 0) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if(target.tagName === 'SPAN' && !target.classList.contains('emoji')) { |
|
|
|
el.remove(); |
|
|
|
target = findUpClassName(target, 'super-emoji'); |
|
|
|
} |
|
|
|
if(!target) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
target = target.firstChild as HTMLElement; |
|
|
|
appendEmoji(emoji, this.recentItemsDiv, true); |
|
|
|
} else if(target.tagName === 'DIV') return; |
|
|
|
this.recentItemsDiv.parentElement.classList.remove('hide'); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// set selection range
|
|
|
|
onContentClick = (e: MouseEvent) => { |
|
|
|
const savedRange = isTouchSupported ? undefined : emoticonsDropdown.getSavedRange(); |
|
|
|
cancelEvent(e); |
|
|
|
let sel: Selection; |
|
|
|
|
|
|
|
if(savedRange) { |
|
|
|
const emoji = getEmojiFromElement(e.target as HTMLElement); |
|
|
|
sel = document.getSelection(); |
|
|
|
if(!emoji) { |
|
|
|
sel.removeAllRanges(); |
|
|
|
return; |
|
|
|
sel.addRange(savedRange); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const html = RichTextProcessor.emojiSupported ? |
|
|
|
const messageInput = appImManager.chat.input.messageInput; |
|
|
|
(target.nodeType === 3 ? target.nodeValue : target.innerHTML) : |
|
|
|
let inputHTML = messageInput.innerHTML; |
|
|
|
target.outerHTML; |
|
|
|
|
|
|
|
|
|
|
|
const html = RichTextProcessor.wrapEmojiText(emoji); |
|
|
|
|
|
|
|
let inserted = false; |
|
|
|
|
|
|
|
if(window.getSelection) { |
|
|
|
|
|
|
|
const savedRange = isTouchSupported ? undefined : emoticonsDropdown.getSavedRange(); |
|
|
|
|
|
|
|
let sel = window.getSelection(); |
|
|
|
|
|
|
|
if(savedRange) { |
|
|
|
|
|
|
|
sel.removeAllRanges(); |
|
|
|
|
|
|
|
sel.addRange(savedRange); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if((document.activeElement && (document.activeElement.tagName === 'INPUT' || document.activeElement.hasAttribute('contenteditable'))) || |
|
|
|
if(sel.getRangeAt && sel.rangeCount) { |
|
|
|
savedRange) { |
|
|
|
var el = document.createElement('div'); |
|
|
|
document.execCommand('insertHTML', true, html); |
|
|
|
el.innerHTML = html; |
|
|
|
} else { |
|
|
|
var node = el.firstChild; |
|
|
|
appImManager.chat.input.messageInput.innerHTML += html; |
|
|
|
var range = sel.getRangeAt(0); |
|
|
|
|
|
|
|
range.deleteContents(); |
|
|
|
|
|
|
|
//range.insertNode(document.createTextNode(' '));
|
|
|
|
|
|
|
|
range.insertNode(node); |
|
|
|
|
|
|
|
range.setStart(node, 0); |
|
|
|
|
|
|
|
inserted = true; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
|
|
|
range = document.createRange(); |
|
|
|
|
|
|
|
range.setStartAfter(node); |
|
|
|
|
|
|
|
range.collapse(true); |
|
|
|
|
|
|
|
sel.removeAllRanges(); |
|
|
|
|
|
|
|
sel.addRange(range); |
|
|
|
|
|
|
|
}, 0); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* if(sel && isTouchSupported) { |
|
|
|
if(!inserted || messageInput.innerHTML === inputHTML) { |
|
|
|
sel.removeRange(savedRange); |
|
|
|
messageInput.insertAdjacentHTML('beforeend', html); |
|
|
|
blurActiveElement(); |
|
|
|
} |
|
|
|
} */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Recent
|
|
|
|
|
|
|
|
const emoji = getEmojiFromElement(target); |
|
|
|
|
|
|
|
(Array.from(this.recentItemsDiv.children) as HTMLElement[]).forEach((el, idx) => { |
|
|
|
|
|
|
|
const _emoji = getEmojiFromElement(el); |
|
|
|
|
|
|
|
if(emoji === _emoji) { |
|
|
|
|
|
|
|
el.remove(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
appendEmoji(emoji, this.recentItemsDiv, true); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
appEmojiManager.pushRecentEmoji(emoji); |
|
|
|
|
|
|
|
this.recentItemsDiv.parentElement.classList.remove('hide'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Append to input
|
|
|
|
// Append to input
|
|
|
|
const event = new Event('input', {bubbles: true, cancelable: true}); |
|
|
|
const event = new Event('input', {bubbles: true, cancelable: true}); |
|
|
|
appImManager.chat.input.messageInput.dispatchEvent(event); |
|
|
|
messageInput.dispatchEvent(event); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
onClose() { |
|
|
|
onClose() { |
|
|
|