198 lines
4.4 KiB
Go
198 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]
|
|
wrap := string(suggestionEmote)
|
|
|
|
if i := strings.LastIndex(newval, string(currentSugType)); i != -1 {
|
|
var offset int
|
|
if currentSugType == suggestionName {
|
|
offset = 1
|
|
wrap = ""
|
|
}
|
|
|
|
newval = newval[:i+offset] + wrap + currentSug + wrap
|
|
}
|
|
|
|
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")
|
|
}
|