archive
This commit is contained in:
195
MovieNight/scrapedagain/wasm/suggestions.go
Executable file
195
MovieNight/scrapedagain/wasm/suggestions.go
Executable file
@@ -0,0 +1,195 @@
|
||||
// +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")
|
||||
}
|
||||
Reference in New Issue
Block a user