2022-07-16 21:00:02 +00:00
/ *
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation , either version 3 of the
License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
2022-09-04 23:15:37 +00:00
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
2022-07-16 21:00:02 +00:00
GNU Affero General Public License for more details .
You should have received a copy of the GNU Affero General Public License
2022-09-04 23:15:37 +00:00
along with this program . If not , see < https : //www.gnu.org/licenses/>.
2022-07-16 21:00:02 +00:00
2022-07-17 05:02:22 +00:00
Copyright ( C ) 2022 Dr Steven Transmisia , anti - evil engineer ,
2022-09-04 23:15:37 +00:00
2022 Nekobit , king autist
2022-07-16 21:00:02 +00:00
* /
// Status
let emojiEngineStarted = false ;
// DOM stuff
const classesSelectorDOM = document . getElementById ( "emoji-modal-tabs" ) ;
const emojiButtonTemplateDOM = document . getElementById ( "emoji-button-template" ) ;
const emojiResultsDOM = document . getElementById ( "tab-content" ) ;
/** @type {HTMLInputElement[]} */
const emojiSelectSuffixDOMs = document . getElementsByClassName ( "emoji-suffix" ) ;
/** @type {HTMLInputElement[]} */
const emojiSelectPostfixDOMs = document . getElementsByClassName ( "emoji-postfix" ) ;
const emojiNotFoundDOM = document . getElementById ( "no-emojis-found" ) ;
const emojiWorkingDOM = document . getElementById ( "emojis-work" ) ;
const emojiNewUserDOM = document . getElementById ( "emoji-new-user" ) ;
/** @type {HTMLInputElement} */
const emojiSearchBarDOM = document . getElementById ( 'emoji_search' ) ;
/** @type {HTMLInputElement} */
let emojiInputTargetDOM = undefined ;
// Emojis usage stats. I don't really like this format but I'll keep it for backward comp.
const favorite _emojis = JSON . parse ( localStorage . getItem ( "favorite_emojis" ) ) || { } ;
const emojiFirstBoot = Object . keys ( favorite _emojis ) . length === 0 ;
emojiNewUserDOM . hidden = ! emojiFirstBoot ;
/** Associative array of all the emojis' DOM */
let emojiDOMs = { } ;
const EMOIJ _SEARCH _ENGINE _MIN _INTERVAL = 350 ;
let emojiSearcher = {
working : false ,
queries : [ ] ,
2022-09-04 23:15:37 +00:00
2022-07-16 21:00:02 +00:00
addQuery : function ( query )
{
this . queries . push ( query ) ;
if ( ! this . working )
this . work ( ) ;
} ,
2022-09-04 23:15:37 +00:00
2022-07-16 21:00:02 +00:00
work : async function work ( ) {
this . working = true ;
while ( this . queries . length > 0 )
{
const startTime = Date . now ( ) ;
// Get last input
const query = this . queries [ this . queries . length - 1 ] . toLowerCase ( ) ;
this . queries = [ ] ;
// To improve perf we avoid showing all emojis at the same time.
if ( query === "" )
{
await classesSelectorDOM . children [ 0 ] . children [ 0 ] . click ( ) ;
classesSelectorDOM . children [ 0 ] . children [ 0 ] . classList . add ( "active" ) ;
continue ;
}
// Hide welcome message
emojiNewUserDOM . hidden = true ;
// Search
const resultSet = emojisSearchDictionary . completeSearch ( query ) ;
2022-09-04 23:15:37 +00:00
// update stuff
2022-07-16 21:00:02 +00:00
for ( const [ emojiName , emojiDOM ] of Object . entries ( emojiDOMs ) )
emojiDOM . hidden = ! resultSet . has ( emojiName ) ;
emojiNotFoundDOM . hidden = resultSet . size !== 0 ;
let sleepTime = EMOIJ _SEARCH _ENGINE _MIN _INTERVAL - ( Date . now ( ) - startTime ) ;
if ( sleepTime > 0 )
await new Promise ( r => setTimeout ( r , sleepTime ) ) ;
}
this . working = false ;
}
} ;
// tags dictionary. KEEP IT SORT
class EmoijsDictNode
{
constructor ( tag , name ) {
this . tag = tag ;
this . emojiNames = [ name ] ;
}
}
const emojisSearchDictionary = {
dict : [ ] ,
updateTag : function ( tag , emojiName ) {
if ( tag === undefined || emojiName === undefined )
return ;
let low = 0 ;
let high = this . dict . length ;
while ( low < high ) {
let mid = ( low + high ) >>> 1 ;
if ( this . dict [ mid ] . tag < tag )
low = mid + 1 ;
else
high = mid ;
}
let target = low ;
if ( this . dict [ target ] !== undefined && this . dict [ target ] . tag === tag )
this . dict [ target ] . emojiNames . push ( emojiName ) ;
else
this . dict . splice ( target , 0 , new EmoijsDictNode ( tag , emojiName ) ) ;
} ,
/ * *
* We find the name of each emojis that has a tag that starts with query .
2022-09-04 23:15:37 +00:00
*
2022-07-16 21:00:02 +00:00
* Basically I run a binary search to find a tag that starts with a query , then I look left and right
* for other tags tat start with the query . As the array is ordered this algo is sound .
* @ param { String } tag
* @ returns { Set }
* /
searchFor : function ( query ) {
if ( this . dict . length === 0 )
return new Set ( ) ;
const result = new Set ( ) ;
let low = 0 ;
let high = this . dict . length ;
while ( low < high ) {
let mid = ( low + high ) >>> 1 ;
if ( this . dict [ mid ] . tag < query )
low = mid + 1 ;
else
high = mid ;
}
let target = low ;
for ( let i = target ; i >= 0 && this . dict [ i ] . tag . startsWith ( query ) ; i -- )
for ( let j = 0 ; j < this . dict [ i ] . emojiNames . length ; j ++ )
result . add ( this . dict [ i ] . emojiNames [ j ] ) ;
2022-09-04 23:15:37 +00:00
2022-07-16 21:00:02 +00:00
for ( let i = target + 1 ; i < this . dict . length && this . dict [ i ] . tag . startsWith ( query ) ; i ++ )
for ( let j = 0 ; j < this . dict [ i ] . emojiNames . length ; j ++ )
result . add ( this . dict [ i ] . emojiNames [ j ] ) ;
2022-09-04 23:15:37 +00:00
2022-07-16 21:00:02 +00:00
return result ;
} ,
/ * *
* We also check for substrings ! ( sigh )
* @ param { String } tag
* @ returns { Set }
* /
completeSearch : function ( query ) {
const result = new Set ( ) ;
for ( let i = 0 ; i < this . dict . length ; i ++ )
if ( this . dict [ i ] . tag . includes ( query ) )
for ( let j = 0 ; j < this . dict [ i ] . emojiNames . length ; j ++ )
result . add ( this . dict [ i ] . emojiNames [ j ] )
2022-09-04 23:15:37 +00:00
2022-07-16 21:00:02 +00:00
return result ;
}
} ;
// get public emojis list
const emojiRequest = new XMLHttpRequest ( ) ;
emojiRequest . open ( "GET" , '/marsey_list.json' ) ;
emojiRequest . onload = async ( e ) => {
2022-09-04 23:15:37 +00:00
let emojis = JSON . parse ( emojiRequest . response ) ;
2022-07-16 21:00:02 +00:00
if ( ! ( emojis instanceof Array ) )
2022-07-17 17:05:24 +00:00
throw new TypeError ( "[EMOJI DIALOG] rDrama's server should have sent a JSON-coded Array!" ) ;
2022-07-16 21:00:02 +00:00
let classes = new Set ( ) ;
const bussyDOM = document . createElement ( "div" ) ;
let startTime = Date . now ( ) ;
for ( let i = 0 ; i < emojis . length ; i ++ )
{
const emoji = emojis [ i ] ;
emojisSearchDictionary . updateTag ( emoji . name , emoji . name ) ;
if ( emoji . author !== undefined && emoji . author !== null )
emojisSearchDictionary . updateTag ( emoji . author . toLowerCase ( ) , emoji . name ) ;
if ( emoji . tags instanceof Array )
for ( let i = 0 ; i < emoji . tags . length ; i ++ )
emojisSearchDictionary . updateTag ( emoji . tags [ i ] , emoji . name ) ;
classes . add ( emoji . class ) ;
// Create emoji DOM
const emojiDOM = document . importNode ( emojiButtonTemplateDOM . content , true ) . children [ 0 ] ;
emojiDOM . title = emoji . name
if ( emoji . author !== undefined && emoji . author !== null )
emojiDOM . title += "\nauthor\t" + emoji . author
if ( emoji . count !== undefined )
emojiDOM . title += "\nused\t" + emoji . count ;
emojiDOM . dataset . className = emoji . class ;
emojiDOM . dataset . emojiName = emoji . name ;
emojiDOM . onclick = emojiAddToInput ;
2022-09-04 23:15:37 +00:00
emojiDOM . hidden = true ;
2022-07-16 21:00:02 +00:00
const emojiIMGDOM = emojiDOM . children [ 0 ] ;
emojiIMGDOM . src = "/e/" + emoji . name + ".webp" ;
emojiIMGDOM . alt = emoji . name ;
/ * * D i s a b l e i n g l a z y l o a d i n g s e e m s t o r e d u c e c p u u s a g e s o m e h o w ( ? )
2022-09-04 23:15:37 +00:00
* idk it is difficult to benchmark * /
2022-07-16 21:00:02 +00:00
emojiIMGDOM . loading = "lazy" ;
// Save reference
emojiDOMs [ emoji . name ] = emojiDOM ;
// Add to the document!
bussyDOM . appendChild ( emojiDOM ) ;
}
// Create header
for ( let className of classes )
{
let classSelectorDOM = document . createElement ( "li" ) ;
classSelectorDOM . classList . add ( "nav-item" ) ;
let classSelectorLinkDOM = document . createElement ( "a" ) ;
classSelectorLinkDOM . href = "#" ;
classSelectorLinkDOM . classList . add ( "nav-link" , "emojitab" ) ;
classSelectorLinkDOM . dataset . bsToggle = "tab" ;
classSelectorLinkDOM . dataset . className = className ;
classSelectorLinkDOM . innerText = className ;
classSelectorLinkDOM . onclick = switchEmojiTab ;
classSelectorDOM . appendChild ( classSelectorLinkDOM ) ;
classesSelectorDOM . appendChild ( classSelectorDOM ) ;
}
// Show favorite for start.
await classesSelectorDOM . children [ 0 ] . children [ 0 ] . click ( ) ;
2022-09-04 23:15:37 +00:00
// Send it to the render machine!
2022-07-16 21:00:02 +00:00
emojiResultsDOM . appendChild ( bussyDOM ) ;
emojiResultsDOM . hidden = false ;
emojiWorkingDOM . hidden = true ;
emojiSearchBarDOM . disabled = false ;
}
/ * *
2022-09-04 23:15:37 +00:00
*
* @ param { Event } e
* /
2022-07-16 21:00:02 +00:00
function switchEmojiTab ( e )
{
const className = e . currentTarget . dataset . className ;
emojiSearchBarDOM . value = "" ;
emojiSearchBarDOM . focus ( ) ;
emojiNotFoundDOM . hidden = true ;
// Special case: favorites
if ( className === "favorite" )
{
if ( emojiFirstBoot )
emojiNewUserDOM . hidden = false ;
for ( const emojiDOM of Object . values ( emojiDOMs ) )
emojiDOM . hidden = true ;
// copied from the old one
// For new users we show anton-d's emojis
const favs = emojiFirstBoot ? emojisSearchDictionary . searchFor ( "anton-d" ) : Object . keys ( Object . fromEntries (
Object . entries ( favorite _emojis ) . sort ( ( [ , a ] , [ , b ] ) => b - a )
) ) . slice ( 0 , 25 ) ;
2022-09-04 23:15:37 +00:00
2022-07-16 21:00:02 +00:00
for ( const emoji of favs )
if ( emojiDOMs [ emoji ] instanceof HTMLElement )
emojiDOMs [ emoji ] . hidden = false ;
2022-09-04 23:15:37 +00:00
2022-07-16 21:00:02 +00:00
return ;
}
2022-09-04 23:15:37 +00:00
2022-07-16 21:00:02 +00:00
emojiNewUserDOM . hidden = true ;
for ( const emojiDOM of Object . values ( emojiDOMs ) )
emojiDOM . hidden = emojiDOM . dataset . className !== className ;
}
async function start _search ( ) {
2022-09-12 04:38:04 +00:00
emojiSearcher . addQuery ( emojiSearchBarDOM . value . trim ( ) . toLowerCase ( ) ) ;
2022-07-16 21:00:02 +00:00
// Remove any selected tab, now it is meaningless
for ( let i = 0 ; i < classesSelectorDOM . children . length ; i ++ )
classesSelectorDOM . children [ i ] . children [ 0 ] . classList . remove ( "active" ) ;
}
/ * *
2022-09-04 23:15:37 +00:00
* Add the selected emoji to the targeted text area
* @ param { Event } event
* /
2022-07-16 21:00:02 +00:00
function emojiAddToInput ( event )
{
// This should not happen if used properly but whatever
if ( ! ( emojiInputTargetDOM instanceof HTMLTextAreaElement ) && ! ( emojiInputTargetDOM instanceof HTMLInputElement ) )
return ;
2022-09-04 23:15:37 +00:00
2022-07-16 21:00:02 +00:00
// If a range is selected, setRangeText will overwrite it. Maybe better to ask the r-slured if he really wants this behaviour
if ( emojiInputTargetDOM . selectionStart !== emojiInputTargetDOM . selectionEnd && ! confirm ( "You've selected a range of text.\nThe emoji will overwrite it! Do you want to continue?" ) )
return ;
2022-09-04 23:15:37 +00:00
let strToInsert = event . currentTarget . dataset . emojiName ;
2022-07-16 21:00:02 +00:00
for ( let i = 0 ; i < emojiSelectPostfixDOMs . length ; i ++ )
if ( emojiSelectPostfixDOMs [ i ] . checked )
strToInsert = strToInsert + emojiSelectPostfixDOMs [ i ] . value ;
2022-09-04 23:15:37 +00:00
2022-07-16 21:00:02 +00:00
for ( let i = 0 ; i < emojiSelectSuffixDOMs . length ; i ++ )
if ( emojiSelectSuffixDOMs [ i ] . checked )
strToInsert = emojiSelectSuffixDOMs [ i ] . value + strToInsert ;
strToInsert = ":" + strToInsert + ":"
2022-09-04 23:15:37 +00:00
const newPos = emojiInputTargetDOM . selectionStart + strToInsert . length ;
2022-07-16 21:00:02 +00:00
emojiInputTargetDOM . setRangeText ( strToInsert ) ;
2022-09-04 23:15:37 +00:00
2022-07-16 21:00:02 +00:00
// Sir, come out and drink your Chromium complaint web
// I HATE CHROME. I HATE CHROME
if ( window . chrome !== undefined )
setTimeout ( function ( ) {
console . warn ( "Chrome detected, r-slured mode enabled." ) ;
2022-09-04 23:15:37 +00:00
2022-07-16 21:00:02 +00:00
// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
// JUST WORK STUPID CHROME PIECE OF SHIT
emojiInputTargetDOM . focus ( ) ;
for ( let i = 0 ; i < 2 ; i ++ )
emojiInputTargetDOM . setSelectionRange ( newPos , newPos ) ;
2022-09-04 23:15:37 +00:00
2022-07-16 21:00:02 +00:00
emojiInputTargetDOM . focus ( ) ;
for ( let i = 0 ; i < 2 ; i ++ )
emojiInputTargetDOM . setSelectionRange ( newPos , newPos ) ;
} , 1 ) ;
else
emojiInputTargetDOM . setSelectionRange ( newPos , newPos ) ;
2022-09-04 23:15:37 +00:00
2022-07-16 21:00:02 +00:00
// kick-start the preview
emojiInputTargetDOM . dispatchEvent ( new Event ( 'input' ) ) ;
// Update favs. from old code
if ( favorite _emojis [ event . currentTarget . dataset . emojiName ] )
favorite _emojis [ event . currentTarget . dataset . emojiName ] += 1 ;
else
favorite _emojis [ event . currentTarget . dataset . emojiName ] = 1 ;
localStorage . setItem ( "favorite_emojis" , JSON . stringify ( favorite _emojis ) ) ;
}
2022-09-08 17:12:46 +00:00
const insertAt = ( str , sub , pos ) => ` ${ str . slice ( 0 , pos ) } ${ sub } ${ str . slice ( pos ) } ` ;
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
let emoji _typing _state = false ;
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
function update _ghost _div _textarea ( text )
{
let ghostdiv = text . parentNode . querySelector ( ".ghostdiv" ) ;
if ( ! ghostdiv ) return ;
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
ghostdiv . innerText = text . value . substring ( 0 , text . selectionStart ) ;
ghostdiv . innerHTML += "<span></span>" ;
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
// Now lets get coordinates
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
ghostdiv . style . display = "initial" ;
let end = ghostdiv . querySelector ( "span" ) ;
const carot _coords = end . getBoundingClientRect ( ) ;
const ghostdiv _coords = ghostdiv . getBoundingClientRect ( ) ;
ghostdiv . style . display = "none" ;
return { pos : text . selectionStart , x : carot _coords . x , y : carot _coords . y - ghostdiv _coords . y } ;
}
// Used for anything where a user is typing, specifically for the emoji modal
// Just leave it global, I don't care
let speed _carot _modal = document . createElement ( "div" ) ;
speed _carot _modal . id = "speed-carot-modal" ;
speed _carot _modal . style . position = "absolute" ;
speed _carot _modal . style . left = "0px" ;
speed _carot _modal . style . top = "0px" ;
speed _carot _modal . style . display = "none" ;
document . body . appendChild ( speed _carot _modal ) ;
let e
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
let current _word = "" ;
let selecting ;
let emoji _index = 0 ;
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
function curr _word _is _emoji ( )
{
return current _word && current _word . charAt ( 0 ) == ":" &&
current _word . charAt ( current _word . length - 1 ) != ":" ;
}
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
function populate _speed _emoji _modal ( results , textbox )
{
selecting = true ;
2022-07-17 05:02:22 +00:00
2022-09-08 17:12:46 +00:00
if ( ! results || results . size === 0 )
2022-09-04 23:15:37 +00:00
{
2022-09-08 17:12:46 +00:00
speed _carot _modal . style . display = "none" ;
return - 1 ;
2022-09-04 23:15:37 +00:00
}
2022-07-17 05:02:22 +00:00
2022-09-08 17:12:46 +00:00
emoji _index = 0 ;
speed _carot _modal . innerHTML = "" ;
const MAXXX = 25 ;
// Not sure why the results is a Set... but oh well
let i = 0 ;
for ( let result of results )
2022-09-04 23:15:37 +00:00
{
2022-09-08 17:12:46 +00:00
if ( i ++ > MAXXX ) return i ;
let emoji _option = document . createElement ( "div" ) ;
emoji _option . className = "speed-modal-option emoji-option " + ( i === 1 ? "selected" : "" ) ;
emoji _option . tabIndex = 0 ;
let emoji _option _img = document . createElement ( "img" ) ;
emoji _option _img . className = "speed-modal-image emoji-option-image" ;
// This is a bit
emoji _option _img . src = ` /e/ ${ result } .webp ` ;
let emoji _option _text = document . createElement ( "span" ) ;
emoji _option _text . title = result ;
emoji _option _text . innerText = result ;
if ( current _word . includes ( "#" ) ) result = ` # ${ result } `
if ( current _word . includes ( "!" ) ) result = ` ! ${ result } `
emoji _option . onclick = ( e ) => {
selecting = false ;
2022-09-04 23:15:37 +00:00
speed _carot _modal . style . display = "none" ;
2022-09-08 17:12:46 +00:00
textbox . value = textbox . value . replace ( new RegExp ( current _word + "(?=\\s|$)" , "g" ) , ` : ${ result } : ` )
markdown ( textbox )
} ;
// Pack
emoji _option . appendChild ( emoji _option _img ) ;
emoji _option . appendChild ( emoji _option _text ) ;
speed _carot _modal . appendChild ( emoji _option ) ;
2022-09-04 23:15:37 +00:00
}
2022-09-08 17:12:46 +00:00
if ( i === 0 ) speed _carot _modal . style . display = "none" ;
else speed _carot _modal . style . display = "initial" ;
return i ;
}
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
function update _speed _emoji _modal ( event )
{
const box _coords = update _ghost _div _textarea ( event . target ) ;
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
let text = event . target . value ;
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
// Unused, but left incase anyone wants to use this more efficient method for emojos
switch ( event . data )
{
case ':' :
emoji _typing _state = true ;
break ;
case ' ' :
emoji _typing _state = false ;
break ;
default :
break ;
}
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
// Get current word at string, such as ":marse" or "word"
let coords = text . indexOf ( ' ' , box _coords . pos ) ;
current _word = /\S+$/ . exec ( text . slice ( 0 , coords === - 1 ? text . length : coords ) ) ;
if ( current _word ) current _word = current _word . toString ( ) ;
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
/ * W e c o u l d a l s o c h e c k e m o j i _ t y p i n g _ s t a t e h e r e , w h i c h i s l e s s a c c u r a t e b u t m o r e e f f i c i e n t . I ' v e
* kept it unless someone wants to provide an option to toggle it for performance * /
if ( curr _word _is _emoji ( ) && current _word != ":" )
{
loadEmojis ( null ) ;
let modal _pos = event . target . getBoundingClientRect ( ) ;
modal _pos . x += window . scrollX ;
modal _pos . y += window . scrollY ;
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
speed _carot _modal . style . display = "initial" ;
speed _carot _modal . style . left = box _coords . x - 35 + "px" ;
speed _carot _modal . style . top = modal _pos . y + box _coords . y + 14 + "px" ;
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
// Do the search (and do something with it)
populate _speed _emoji _modal ( emojisSearchDictionary . searchFor ( current _word . substr ( 1 ) . replace ( /#/g , "" ) . replace ( /!/g , "" ) ) , event . target ) ;
2022-09-04 23:15:37 +00:00
}
2022-09-08 17:12:46 +00:00
else {
speed _carot _modal . style . display = "none" ;
}
}
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
function speed _carot _navigate ( e )
{
if ( ! selecting ) return ;
let select _items = speed _carot _modal . querySelectorAll ( ".speed-modal-option" ) ;
if ( ! select _items || ! curr _word _is _emoji ( ) ) return false ;
// Up or down arrow or enter
if ( e . keyCode == 38 || e . keyCode == 40 || e . keyCode == 13 )
2022-09-04 23:15:37 +00:00
{
2022-09-08 17:12:46 +00:00
if ( emoji _index > select _items . length )
emoji _index = select _items ;
2022-08-11 17:11:24 +00:00
2022-09-08 17:12:46 +00:00
select _items [ emoji _index ] . classList . remove ( "selected" ) ;
switch ( e . keyCode )
2022-09-04 23:15:37 +00:00
{
2022-09-08 17:12:46 +00:00
case 38 : // Up arrow
if ( emoji _index )
emoji _index -- ;
break ;
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
case 40 : // Down arrow
if ( emoji _index < select _items . length - 1 ) emoji _index ++ ;
break ;
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
case 13 :
select _items [ emoji _index ] . click ( ) ;
2022-09-04 23:15:37 +00:00
2022-09-08 17:12:46 +00:00
default :
break ;
2022-09-04 23:15:37 +00:00
}
2022-09-08 17:12:46 +00:00
select _items [ emoji _index ] . classList . add ( "selected" ) ;
e . preventDefault ( ) ;
}
}
2022-07-17 05:02:22 +00:00
2022-09-08 17:12:46 +00:00
// Let's get it running now
let forms = document . querySelectorAll ( "textarea, .allow-emojis" ) ;
forms . forEach ( i => {
let pseudo _div = document . createElement ( "div" ) ;
pseudo _div . className = "ghostdiv" ;
pseudo _div . style . display = "none" ;
i . after ( pseudo _div ) ;
i . addEventListener ( 'input' , update _speed _emoji _modal , false ) ;
i . addEventListener ( 'keydown' , speed _carot _navigate , false ) ;
} ) ;
2022-07-17 05:02:22 +00:00
2022-07-16 21:00:02 +00:00
function loadEmojis ( inputTargetIDName )
{
if ( ! emojiEngineStarted )
{
emojiEngineStarted = true ;
emojiRequest . send ( ) ;
}
2022-09-04 23:15:37 +00:00
if ( inputTargetIDName )
emojiInputTargetDOM = document . getElementById ( inputTargetIDName ) ;
2022-07-16 21:00:02 +00:00
}
document . getElementById ( 'emojiModal' ) . addEventListener ( 'shown.bs.modal' , function ( ) {
emojiSearchBarDOM . focus ( ) ;
2022-09-08 17:12:46 +00:00
} ) ;