94a4f08467
The suggestions popup will now insert wrapped emotes instead of bare word emotes. This is so the autocompletion will still work if the WrappedEmotesOnly setting is set to true.
196 lines
4.4 KiB
Go
196 lines
4.4 KiB
Go
// +build js,wasm
|
|
|
|
package main
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"syscall/js"
|
|
|
|
"github.com/zorchenhimer/MovieNight/common"
|
|
)
|
|
|
|
const (
|
|
keyTab = 9
|
|
keyEnter = 13
|
|
keyEsc = 27
|
|
keySpace = 32
|
|
keyUp = 38
|
|
keyDown = 40
|
|
suggestionName = '@'
|
|
suggestionEmote = ':'
|
|
)
|
|
|
|
var (
|
|
currentSugType rune
|
|
currentSug string
|
|
filteredSug []string
|
|
names []string
|
|
emoteNames []string
|
|
emotes map[string]string
|
|
)
|
|
|
|
// The returned value is a bool deciding to prevent the event from propagating
|
|
func processMessageKey(this js.Value, v []js.Value) interface{} {
|
|
startIdx := v[0].Get("target").Get("selectionStart").Int()
|
|
keyCode := v[0].Get("keyCode").Int()
|
|
ctrl := v[0].Get("ctrlKey").Bool()
|
|
|
|
if ctrl && keyCode == keySpace {
|
|
processMessage(nil)
|
|
return true
|
|
}
|
|
|
|
if len(filteredSug) == 0 || currentSug == "" {
|
|
return false
|
|
}
|
|
|
|
switch keyCode {
|
|
case keyEsc:
|
|
filteredSug = nil
|
|
currentSug = ""
|
|
currentSugType = 0
|
|
case keyUp, keyDown:
|
|
newidx := 0
|
|
for i, n := range filteredSug {
|
|
if n == currentSug {
|
|
newidx = i
|
|
if keyCode == keyDown {
|
|
newidx = i + 1
|
|
if newidx == len(filteredSug) {
|
|
newidx--
|
|
}
|
|
} else if keyCode == keyUp {
|
|
newidx = i - 1
|
|
if newidx < 0 {
|
|
newidx = 0
|
|
}
|
|
}
|
|
break
|
|
}
|
|
}
|
|
currentSug = filteredSug[newidx]
|
|
case keyTab, keyEnter:
|
|
msg := global.Get("msg")
|
|
val := msg.Get("value").String()
|
|
newval := val[:startIdx]
|
|
|
|
if i := strings.LastIndex(newval, string(currentSugType)); i != -1 {
|
|
var offset int
|
|
if currentSugType == suggestionName {
|
|
offset = 1
|
|
}
|
|
|
|
newval = newval[:i+offset] + ":" + currentSug + ":"
|
|
}
|
|
|
|
endVal := val[startIdx:]
|
|
if len(val) == startIdx || val[startIdx:][0] != ' ' {
|
|
// insert a space into val so selection indexing can be one line
|
|
endVal = " " + endVal
|
|
}
|
|
msg.Set("value", newval+endVal)
|
|
msg.Set("selectionStart", len(newval)+1)
|
|
msg.Set("selectionEnd", len(newval)+1)
|
|
|
|
// Clear out filtered names since it is no longer needed
|
|
filteredSug = nil
|
|
default:
|
|
// We only want to handle the caught keys, so return early
|
|
return false
|
|
}
|
|
|
|
updateSuggestionDiv()
|
|
return true
|
|
}
|
|
|
|
func processMessage(v []js.Value) {
|
|
msg := global.Get("msg")
|
|
text := strings.ToLower(msg.Get("value").String())
|
|
startIdx := msg.Get("selectionStart").Int()
|
|
|
|
filteredSug = nil
|
|
if len(text) != 0 {
|
|
if len(names) > 0 || len(emoteNames) > 0 {
|
|
var caretIdx int
|
|
textParts := strings.Split(text, " ")
|
|
|
|
for i, word := range textParts {
|
|
// Increase caret index at beginning if not first word to account for spaces
|
|
if i != 0 {
|
|
caretIdx++
|
|
}
|
|
|
|
// It is possible to have a double space " ", which will lead to an
|
|
// empty string element in the slice. Also check that the index of the
|
|
// cursor is between the start of the word and the end
|
|
if len(word) > 0 && caretIdx <= startIdx && startIdx <= caretIdx+len(word) {
|
|
var suggestions []string
|
|
if word[0] == suggestionName {
|
|
currentSugType = suggestionName
|
|
suggestions = names
|
|
} else if word[0] == suggestionEmote {
|
|
suggestions = emoteNames
|
|
currentSugType = suggestionEmote
|
|
}
|
|
|
|
for _, s := range suggestions {
|
|
if len(word) == 1 || strings.Contains(strings.ToLower(s), word[1:]) {
|
|
filteredSug = append(filteredSug, s)
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(filteredSug) > 0 {
|
|
currentSug = ""
|
|
break
|
|
}
|
|
|
|
caretIdx += len(word)
|
|
}
|
|
}
|
|
}
|
|
|
|
updateSuggestionDiv()
|
|
}
|
|
|
|
func updateSuggestionDiv() {
|
|
const selectedClass = ` class="selectedName"`
|
|
|
|
var divs []string
|
|
if len(filteredSug) > 0 {
|
|
// set current name to first if not set already
|
|
if currentSug == "" {
|
|
currentSug = filteredSug[len(filteredSug)-1]
|
|
}
|
|
|
|
var hascurrentSuggestion bool
|
|
divs = make([]string, len(filteredSug))
|
|
|
|
// Create inner body of html
|
|
for i := range filteredSug {
|
|
divs[i] = "<div"
|
|
|
|
sug := filteredSug[i]
|
|
if sug == currentSug {
|
|
hascurrentSuggestion = true
|
|
divs[i] += selectedClass
|
|
}
|
|
divs[i] += ">"
|
|
|
|
if currentSugType == suggestionEmote {
|
|
divs[i] += common.EmoteToHtml(emotes[sug], sug)
|
|
}
|
|
|
|
divs[i] += sug + "</div>"
|
|
}
|
|
|
|
if !hascurrentSuggestion {
|
|
divs[0] = divs[0][:4] + selectedClass + divs[0][4:]
|
|
}
|
|
}
|
|
// The \n is so it's easier to read th source in web browsers for the dev
|
|
global.Get("suggestions").Set("innerHTML", strings.Join(divs, "\n"))
|
|
global.Call("updateSuggestionScroll")
|
|
}
|