forked from mbk-lab/rui_orig
Compare commits
5 Commits
Author | SHA1 | Date |
---|---|---|
anoshenko | 6c49f37f68 | |
anoshenko | ebcba7f9c2 | |
Alexei Anoshenko | 30c915d73b | |
Alexei Anoshenko | d07b24c953 | |
Alexei Anoshenko | 5354ec6ea1 |
161
appServer.go
161
appServer.go
|
@ -20,6 +20,9 @@ import (
|
|||
//go:embed app_socket.js
|
||||
var socketScripts string
|
||||
|
||||
//go:embed app_post.js
|
||||
var httpPostScripts string
|
||||
|
||||
func debugLog(text string) {
|
||||
log.Println("\033[34m" + text)
|
||||
}
|
||||
|
@ -28,11 +31,16 @@ func errorLog(text string) {
|
|||
log.Println("\033[31m" + text)
|
||||
}
|
||||
|
||||
type sessionInfo struct {
|
||||
session Session
|
||||
response chan string
|
||||
}
|
||||
|
||||
type application struct {
|
||||
server *http.Server
|
||||
params AppParams
|
||||
createContentFunc func(Session) SessionContent
|
||||
sessions map[int]Session
|
||||
sessions map[int]sessionInfo
|
||||
}
|
||||
|
||||
func (app *application) getStartPage() string {
|
||||
|
@ -40,14 +48,18 @@ func (app *application) getStartPage() string {
|
|||
defer freeStringBuilder(buffer)
|
||||
|
||||
buffer.WriteString("<!DOCTYPE html>\n<html>\n")
|
||||
getStartPage(buffer, app.params, socketScripts)
|
||||
getStartPage(buffer, app.params)
|
||||
buffer.WriteString("\n</html>")
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func (app *application) Finish() {
|
||||
for _, session := range app.sessions {
|
||||
session.close()
|
||||
session.session.close()
|
||||
if session.response != nil {
|
||||
close(session.response)
|
||||
session.response = nil
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
|
@ -69,7 +81,12 @@ func (app *application) nextSessionID() int {
|
|||
}
|
||||
|
||||
func (app *application) removeSession(id int) {
|
||||
delete(app.sessions, id)
|
||||
if info, ok := app.sessions[id]; ok {
|
||||
if info.response != nil {
|
||||
close(info.response)
|
||||
}
|
||||
delete(app.sessions, id)
|
||||
}
|
||||
}
|
||||
|
||||
func (app *application) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
|
@ -79,6 +96,11 @@ func (app *application) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
switch req.Method {
|
||||
case "POST":
|
||||
if req.URL.Path == "/" {
|
||||
app.postHandler(w, req)
|
||||
}
|
||||
|
||||
case "GET":
|
||||
switch req.URL.Path {
|
||||
case "/":
|
||||
|
@ -86,10 +108,20 @@ func (app *application) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
io.WriteString(w, app.getStartPage())
|
||||
|
||||
case "/ws":
|
||||
if bridge := CreateSocketBridge(w, req); bridge != nil {
|
||||
if bridge := createSocketBridge(w, req); bridge != nil {
|
||||
go app.socketReader(bridge)
|
||||
}
|
||||
|
||||
case "/script.js":
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if app.params.NoSocket {
|
||||
io.WriteString(w, httpPostScripts)
|
||||
} else {
|
||||
io.WriteString(w, socketScripts)
|
||||
}
|
||||
io.WriteString(w, "\n")
|
||||
io.WriteString(w, defaultScripts)
|
||||
|
||||
default:
|
||||
filename := req.URL.Path[1:]
|
||||
if size := len(filename); size > 0 && filename[size-1] == '/' {
|
||||
|
@ -104,7 +136,87 @@ func (app *application) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func (app *application) socketReader(bridge webBridge) {
|
||||
func setSessionIDCookie(w http.ResponseWriter, sessionID int) {
|
||||
cookie := http.Cookie{
|
||||
Name: "session",
|
||||
Value: strconv.Itoa(sessionID),
|
||||
HttpOnly: true,
|
||||
}
|
||||
http.SetCookie(w, &cookie)
|
||||
}
|
||||
|
||||
func (app *application) postHandler(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
if reqBody, err := io.ReadAll(req.Body); err == nil {
|
||||
message := string(reqBody)
|
||||
|
||||
if ProtocolInDebugLog {
|
||||
DebugLog(message)
|
||||
}
|
||||
|
||||
if obj := ParseDataText(message); obj != nil {
|
||||
var session Session = nil
|
||||
var response chan string = nil
|
||||
|
||||
if cookie, err := req.Cookie("session"); err == nil {
|
||||
sessionID, err := strconv.Atoi(cookie.Value)
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
} else if info, ok := app.sessions[sessionID]; ok && info.response != nil {
|
||||
response = info.response
|
||||
session = info.session
|
||||
}
|
||||
}
|
||||
|
||||
command := obj.Tag()
|
||||
startSession := false
|
||||
|
||||
if session == nil || command == "startSession" {
|
||||
events := make(chan DataObject, 1024)
|
||||
bridge := createHttpBridge(req)
|
||||
response = bridge.response
|
||||
answer := ""
|
||||
session, answer = app.startSession(obj, events, bridge, response)
|
||||
|
||||
bridge.writeMessage(answer)
|
||||
session.onStart()
|
||||
if command == "session-resume" {
|
||||
session.onResume()
|
||||
}
|
||||
bridge.sendResponse()
|
||||
|
||||
setSessionIDCookie(w, session.ID())
|
||||
startSession = true
|
||||
|
||||
go sessionEventHandler(session, events, bridge)
|
||||
}
|
||||
|
||||
if !startSession {
|
||||
switch command {
|
||||
case "nop":
|
||||
session.sendResponse()
|
||||
|
||||
case "session-close":
|
||||
session.onFinish()
|
||||
session.App().removeSession(session.ID())
|
||||
return
|
||||
|
||||
default:
|
||||
if !session.handleAnswer(command, obj) {
|
||||
session.addToEventsQueue(obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
io.WriteString(w, <-response)
|
||||
for len(response) > 0 {
|
||||
io.WriteString(w, <-response)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (app *application) socketReader(bridge *wsBridge) {
|
||||
var session Session
|
||||
events := make(chan DataObject, 1024)
|
||||
|
||||
|
@ -116,7 +228,7 @@ func (app *application) socketReader(bridge webBridge) {
|
|||
}
|
||||
|
||||
if ProtocolInDebugLog {
|
||||
DebugLog(message)
|
||||
DebugLog("🖥️ -> " + message)
|
||||
}
|
||||
|
||||
if obj := ParseDataText(message); obj != nil {
|
||||
|
@ -124,7 +236,7 @@ func (app *application) socketReader(bridge webBridge) {
|
|||
switch command {
|
||||
case "startSession":
|
||||
answer := ""
|
||||
if session, answer = app.startSession(obj, events, bridge); session != nil {
|
||||
if session, answer = app.startSession(obj, events, bridge, nil); session != nil {
|
||||
if !bridge.writeMessage(answer) {
|
||||
return
|
||||
}
|
||||
|
@ -135,7 +247,8 @@ func (app *application) socketReader(bridge webBridge) {
|
|||
case "reconnect":
|
||||
if sessionText, ok := obj.PropertyValue("session"); ok {
|
||||
if sessionID, err := strconv.Atoi(sessionText); err == nil {
|
||||
if session = app.sessions[sessionID]; session != nil {
|
||||
if info, ok := app.sessions[sessionID]; ok {
|
||||
session := info.session
|
||||
session.setBridge(events, bridge)
|
||||
answer := allocStringBuilder()
|
||||
defer freeStringBuilder(answer)
|
||||
|
@ -157,7 +270,7 @@ func (app *application) socketReader(bridge webBridge) {
|
|||
}
|
||||
|
||||
answer := ""
|
||||
if session, answer = app.startSession(obj, events, bridge); session != nil {
|
||||
if session, answer = app.startSession(obj, events, bridge, nil); session != nil {
|
||||
if !bridge.writeMessage(answer) {
|
||||
return
|
||||
}
|
||||
|
@ -165,23 +278,16 @@ func (app *application) socketReader(bridge webBridge) {
|
|||
go sessionEventHandler(session, events, bridge)
|
||||
}
|
||||
|
||||
case "answer":
|
||||
session.handleAnswer(obj)
|
||||
|
||||
case "imageLoaded":
|
||||
session.imageManager().imageLoaded(obj, session)
|
||||
|
||||
case "imageError":
|
||||
session.imageManager().imageLoadError(obj, session)
|
||||
|
||||
default:
|
||||
events <- obj
|
||||
if !session.handleAnswer(command, obj) {
|
||||
events <- obj
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sessionEventHandler(session Session, events chan DataObject, bridge webBridge) {
|
||||
func sessionEventHandler(session Session, events chan DataObject, bridge bridge) {
|
||||
for {
|
||||
data := <-events
|
||||
|
||||
|
@ -201,7 +307,9 @@ func sessionEventHandler(session Session, events chan DataObject, bridge webBrid
|
|||
}
|
||||
}
|
||||
|
||||
func (app *application) startSession(params DataObject, events chan DataObject, bridge webBridge) (Session, string) {
|
||||
func (app *application) startSession(params DataObject, events chan DataObject,
|
||||
bridge bridge, response chan string) (Session, string) {
|
||||
|
||||
if app.createContentFunc == nil {
|
||||
return nil, ""
|
||||
}
|
||||
|
@ -212,7 +320,10 @@ func (app *application) startSession(params DataObject, events chan DataObject,
|
|||
return nil, ""
|
||||
}
|
||||
|
||||
app.sessions[session.ID()] = session
|
||||
app.sessions[session.ID()] = sessionInfo{
|
||||
session: session,
|
||||
response: response,
|
||||
}
|
||||
|
||||
answer := allocStringBuilder()
|
||||
defer freeStringBuilder(answer)
|
||||
|
@ -236,7 +347,7 @@ var apps = []*application{}
|
|||
func StartApp(addr string, createContentFunc func(Session) SessionContent, params AppParams) {
|
||||
app := new(application)
|
||||
app.params = params
|
||||
app.sessions = map[int]Session{}
|
||||
app.sessions = map[int]sessionInfo{}
|
||||
app.createContentFunc = createContentFunc
|
||||
apps = append(apps, app)
|
||||
|
||||
|
@ -295,7 +406,7 @@ func OpenBrowser(url string) bool {
|
|||
case "linux":
|
||||
for _, provider := range []string{"xdg-open", "x-www-browser", "www-browser"} {
|
||||
if _, err = exec.LookPath(provider); err == nil {
|
||||
if exec.Command(provider, url).Start(); err == nil {
|
||||
if err = exec.Command(provider, url).Start(); err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
15
appWasm.go
15
appWasm.go
|
@ -17,7 +17,7 @@ type wasmApp struct {
|
|||
params AppParams
|
||||
createContentFunc func(Session) SessionContent
|
||||
session Session
|
||||
bridge webBridge
|
||||
bridge bridge
|
||||
close chan DataObject
|
||||
}
|
||||
|
||||
|
@ -44,17 +44,10 @@ func (app *wasmApp) handleMessage(this js.Value, args []js.Value) any {
|
|||
case "session-close":
|
||||
app.close <- obj
|
||||
|
||||
case "answer":
|
||||
app.session.handleAnswer(obj)
|
||||
|
||||
case "imageLoaded":
|
||||
app.session.imageManager().imageLoaded(obj, app.session)
|
||||
|
||||
case "imageError":
|
||||
app.session.imageManager().imageLoadError(obj, app.session)
|
||||
|
||||
default:
|
||||
app.session.handleEvent(command, obj)
|
||||
if !app.session.handleAnswer(command, obj) {
|
||||
app.session.handleEvent(command, obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
function sendMessage(message) {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', '/', true);
|
||||
xhr.onreadystatechange = function() {
|
||||
const script = this.responseText
|
||||
if (script != "") {
|
||||
window.eval(script)
|
||||
//sendMessage("nop{session=" + sessionID +"}")
|
||||
}
|
||||
|
||||
}
|
||||
xhr.send(message);
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
sendMessage( sessionInfo() );
|
||||
}
|
||||
|
||||
window.onfocus = function() {
|
||||
windowFocus = true
|
||||
sendMessage( "session-resume{}" );
|
||||
}
|
442
app_scripts.js
442
app_scripts.js
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
|||
var socket
|
||||
var socketUrl
|
||||
let socket
|
||||
let socketUrl
|
||||
|
||||
function sendMessage(message) {
|
||||
if (socket) {
|
||||
|
@ -10,7 +10,7 @@ function sendMessage(message) {
|
|||
window.onload = function() {
|
||||
socketUrl = document.location.protocol == "https:" ? "wss://" : "ws://"
|
||||
socketUrl += document.location.hostname
|
||||
var port = document.location.port
|
||||
const port = document.location.port
|
||||
if (port) {
|
||||
socketUrl += ":" + port
|
||||
}
|
||||
|
|
|
@ -38,9 +38,12 @@ type AppParams struct {
|
|||
KeyFile string
|
||||
// Redirect80 - if true then the function of redirect from port 80 to 443 is created
|
||||
Redirect80 bool
|
||||
// NoSocket - if true then WebSockets will not be used and information exchange
|
||||
// between the client and the server will be carried out only via http.
|
||||
NoSocket bool
|
||||
}
|
||||
|
||||
func getStartPage(buffer *strings.Builder, params AppParams, addScripts string) {
|
||||
func getStartPage(buffer *strings.Builder, params AppParams) {
|
||||
buffer.WriteString(`<head>
|
||||
<meta charset="utf-8">
|
||||
<title>`)
|
||||
|
@ -67,11 +70,7 @@ func getStartPage(buffer *strings.Builder, params AppParams, addScripts string)
|
|||
buffer.WriteString(appStyles)
|
||||
buffer.WriteString(`</style>
|
||||
<style id="ruiAnimations"></style>
|
||||
<script>
|
||||
`)
|
||||
buffer.WriteString(defaultScripts)
|
||||
buffer.WriteString(addScripts)
|
||||
buffer.WriteString(`</script>
|
||||
<script src="/script.js"></script>
|
||||
</head>
|
||||
<body id="body" onkeydown="keyDownEvent(this, event)">
|
||||
<div class="ruiRoot" id="ruiRootView"></div>
|
||||
|
|
5
image.go
5
image.go
|
@ -80,10 +80,11 @@ func (manager *imageManager) loadImage(url string, onLoaded func(Image), session
|
|||
manager.images[url] = image
|
||||
|
||||
session.callFunc("loadImage", url)
|
||||
session.sendResponse()
|
||||
return image
|
||||
}
|
||||
|
||||
func (manager *imageManager) imageLoaded(obj DataObject, session Session) {
|
||||
func (manager *imageManager) imageLoaded(obj DataObject) {
|
||||
if manager.images == nil {
|
||||
manager.images = make(map[string]*imageData)
|
||||
return
|
||||
|
@ -109,7 +110,7 @@ func (manager *imageManager) imageLoaded(obj DataObject, session Session) {
|
|||
}
|
||||
}
|
||||
|
||||
func (manager *imageManager) imageLoadError(obj DataObject, session Session) {
|
||||
func (manager *imageManager) imageLoadError(obj DataObject) {
|
||||
if manager.images == nil {
|
||||
manager.images = make(map[string]*imageData)
|
||||
return
|
||||
|
|
32
listView.go
32
listView.go
|
@ -589,7 +589,7 @@ func (listView *listViewData) getItemFrames() []Frame {
|
|||
return listView.itemFrame
|
||||
}
|
||||
|
||||
func (listView *listViewData) itemAlign(self View, buffer *strings.Builder) {
|
||||
func (listView *listViewData) itemAlign(buffer *strings.Builder) {
|
||||
values := enumProperties[ItemHorizontalAlign].cssValues
|
||||
if hAlign := GetListItemHorizontalAlign(listView); hAlign >= 0 && hAlign < len(values) {
|
||||
buffer.WriteString(" justify-items: ")
|
||||
|
@ -605,7 +605,7 @@ func (listView *listViewData) itemAlign(self View, buffer *strings.Builder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (listView *listViewData) itemSize(self View, buffer *strings.Builder) {
|
||||
func (listView *listViewData) itemSize(buffer *strings.Builder) {
|
||||
if itemWidth := GetListItemWidth(listView); itemWidth.Type != Auto {
|
||||
buffer.WriteString(` min-width: `)
|
||||
buffer.WriteString(itemWidth.cssString("", listView.Session()))
|
||||
|
@ -619,14 +619,14 @@ func (listView *listViewData) itemSize(self View, buffer *strings.Builder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (listView *listViewData) getDivs(self View, checkbox, hCheckboxAlign, vCheckboxAlign int) (string, string, string) {
|
||||
func (listView *listViewData) getDivs(checkbox, hCheckboxAlign, vCheckboxAlign int) (string, string, string) {
|
||||
session := listView.Session()
|
||||
|
||||
contentBuilder := allocStringBuilder()
|
||||
defer freeStringBuilder(contentBuilder)
|
||||
|
||||
contentBuilder.WriteString(`<div style="display: grid;`)
|
||||
listView.itemAlign(self, contentBuilder)
|
||||
listView.itemAlign(contentBuilder)
|
||||
|
||||
onDivBuilder := allocStringBuilder()
|
||||
defer freeStringBuilder(onDivBuilder)
|
||||
|
@ -681,7 +681,7 @@ func (listView *listViewData) getDivs(self View, checkbox, hCheckboxAlign, vChec
|
|||
return onDivBuilder.String(), offDivBuilder.String(), contentBuilder.String()
|
||||
}
|
||||
|
||||
func (listView *listViewData) checkboxItemDiv(self View, checkbox, hCheckboxAlign, vCheckboxAlign int) string {
|
||||
func (listView *listViewData) checkboxItemDiv(checkbox, hCheckboxAlign, vCheckboxAlign int) string {
|
||||
itemStyleBuilder := allocStringBuilder()
|
||||
defer freeStringBuilder(itemStyleBuilder)
|
||||
|
||||
|
@ -760,15 +760,15 @@ func (listView *listViewData) currentInactiveStyle() string {
|
|||
return listView.itemStyle(CurrentInactiveStyle, "ruiListItemSelected")
|
||||
}
|
||||
|
||||
func (listView *listViewData) checkboxSubviews(self View, buffer *strings.Builder, checkbox int) {
|
||||
func (listView *listViewData) checkboxSubviews(buffer *strings.Builder, checkbox int) {
|
||||
count := listView.adapter.ListSize()
|
||||
listViewID := listView.htmlID()
|
||||
|
||||
hCheckboxAlign := GetListViewCheckboxHorizontalAlign(listView)
|
||||
vCheckboxAlign := GetListViewCheckboxVerticalAlign(listView)
|
||||
|
||||
itemDiv := listView.checkboxItemDiv(self, checkbox, hCheckboxAlign, vCheckboxAlign)
|
||||
onDiv, offDiv, contentDiv := listView.getDivs(self, checkbox, hCheckboxAlign, vCheckboxAlign)
|
||||
itemDiv := listView.checkboxItemDiv(checkbox, hCheckboxAlign, vCheckboxAlign)
|
||||
onDiv, offDiv, contentDiv := listView.getDivs(checkbox, hCheckboxAlign, vCheckboxAlign)
|
||||
|
||||
current := GetCurrent(listView)
|
||||
checkedItems := GetListViewCheckedItems(listView)
|
||||
|
@ -784,7 +784,7 @@ func (listView *listViewData) checkboxSubviews(self View, buffer *strings.Builde
|
|||
buffer.WriteString(listView.currentInactiveStyle())
|
||||
}
|
||||
buffer.WriteString(`" onclick="listItemClickEvent(this, event)" data-left="0" data-top="0" data-width="0" data-height="0" style="display: grid; justify-items: stretch; align-items: stretch;`)
|
||||
listView.itemSize(self, buffer)
|
||||
listView.itemSize(buffer)
|
||||
if !listView.adapter.IsListItemEnabled(i) {
|
||||
buffer.WriteString(`" data-disabled="1`)
|
||||
}
|
||||
|
@ -815,7 +815,7 @@ func (listView *listViewData) checkboxSubviews(self View, buffer *strings.Builde
|
|||
}
|
||||
}
|
||||
|
||||
func (listView *listViewData) noneCheckboxSubviews(self View, buffer *strings.Builder) {
|
||||
func (listView *listViewData) noneCheckboxSubviews(buffer *strings.Builder) {
|
||||
count := listView.adapter.ListSize()
|
||||
listViewID := listView.htmlID()
|
||||
|
||||
|
@ -824,8 +824,8 @@ func (listView *listViewData) noneCheckboxSubviews(self View, buffer *strings.Bu
|
|||
|
||||
itemStyleBuilder.WriteString(`data-left="0" data-top="0" data-width="0" data-height="0" style="max-width: 100%; max-height: 100%; display: grid;`)
|
||||
|
||||
listView.itemAlign(self, itemStyleBuilder)
|
||||
listView.itemSize(self, itemStyleBuilder)
|
||||
listView.itemAlign(itemStyleBuilder)
|
||||
listView.itemSize(itemStyleBuilder)
|
||||
|
||||
itemStyleBuilder.WriteString(`" onclick="listItemClickEvent(this, event)"`)
|
||||
itemStyle := itemStyleBuilder.String()
|
||||
|
@ -865,12 +865,12 @@ func (listView *listViewData) updateCheckboxItem(index int, checked bool) {
|
|||
checkbox := GetListViewCheckbox(listView)
|
||||
hCheckboxAlign := GetListViewCheckboxHorizontalAlign(listView)
|
||||
vCheckboxAlign := GetListViewCheckboxVerticalAlign(listView)
|
||||
onDiv, offDiv, contentDiv := listView.getDivs(listView, checkbox, hCheckboxAlign, vCheckboxAlign)
|
||||
onDiv, offDiv, contentDiv := listView.getDivs(checkbox, hCheckboxAlign, vCheckboxAlign)
|
||||
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
|
||||
buffer.WriteString(listView.checkboxItemDiv(listView, checkbox, hCheckboxAlign, vCheckboxAlign))
|
||||
buffer.WriteString(listView.checkboxItemDiv(checkbox, hCheckboxAlign, vCheckboxAlign))
|
||||
if checked {
|
||||
buffer.WriteString(onDiv)
|
||||
} else {
|
||||
|
@ -1061,9 +1061,9 @@ func (listView *listViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
|
||||
checkbox := GetListViewCheckbox(listView)
|
||||
if checkbox == NoneCheckbox {
|
||||
listView.noneCheckboxSubviews(self, buffer)
|
||||
listView.noneCheckboxSubviews(buffer)
|
||||
} else {
|
||||
listView.checkboxSubviews(self, buffer, checkbox)
|
||||
listView.checkboxSubviews(buffer, checkbox)
|
||||
}
|
||||
|
||||
buffer.WriteString(`</div>`)
|
||||
|
|
74
session.go
74
session.go
|
@ -7,7 +7,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
type webBridge interface {
|
||||
type bridge interface {
|
||||
startUpdateScript(htmlID string) bool
|
||||
finishUpdateScript(htmlID string)
|
||||
callFunc(funcName string, args ...any) bool
|
||||
|
@ -16,10 +16,9 @@ type webBridge interface {
|
|||
updateCSSProperty(htmlID, property, value string)
|
||||
updateProperty(htmlID, property string, value any)
|
||||
removeProperty(htmlID, property string)
|
||||
readMessage() (string, bool)
|
||||
writeMessage(text string) bool
|
||||
addAnimationCSS(css string)
|
||||
clearAnimation()
|
||||
sendResponse()
|
||||
setAnimationCSS(css string)
|
||||
appendAnimationCSS(css string)
|
||||
canvasStart(htmlID string)
|
||||
callCanvasFunc(funcName string, args ...any)
|
||||
callCanvasVarFunc(v any, funcName string, args ...any)
|
||||
|
@ -124,7 +123,7 @@ type Session interface {
|
|||
nextViewID() string
|
||||
styleProperty(styleTag, property string) any
|
||||
|
||||
setBridge(events chan DataObject, bridge webBridge)
|
||||
setBridge(events chan DataObject, bridge bridge)
|
||||
writeInitScript(writer *strings.Builder)
|
||||
callFunc(funcName string, args ...any)
|
||||
updateInnerHTML(htmlID, html string)
|
||||
|
@ -134,6 +133,7 @@ type Session interface {
|
|||
removeProperty(htmlID, property string)
|
||||
startUpdateScript(htmlID string) bool
|
||||
finishUpdateScript(htmlID string)
|
||||
sendResponse()
|
||||
addAnimationCSS(css string)
|
||||
clearAnimation()
|
||||
canvasStart(htmlID string)
|
||||
|
@ -145,7 +145,8 @@ type Session interface {
|
|||
canvasFinish()
|
||||
canvasTextMetrics(htmlID, font, text string) TextMetrics
|
||||
htmlPropertyValue(htmlID, name string) string
|
||||
handleAnswer(data DataObject)
|
||||
addToEventsQueue(data DataObject)
|
||||
handleAnswer(command string, data DataObject) bool
|
||||
handleRootSize(data DataObject)
|
||||
handleResize(data DataObject)
|
||||
handleEvent(command string, data DataObject)
|
||||
|
@ -189,7 +190,7 @@ type sessionData struct {
|
|||
ignoreUpdates bool
|
||||
popups *popupManager
|
||||
images *imageManager
|
||||
bridge webBridge
|
||||
bridge bridge
|
||||
events chan DataObject
|
||||
animationCounter int
|
||||
animationCSS string
|
||||
|
@ -237,7 +238,7 @@ func (session *sessionData) ID() int {
|
|||
return session.sessionID
|
||||
}
|
||||
|
||||
func (session *sessionData) setBridge(events chan DataObject, bridge webBridge) {
|
||||
func (session *sessionData) setBridge(events chan DataObject, bridge bridge) {
|
||||
session.events = events
|
||||
session.bridge = bridge
|
||||
}
|
||||
|
@ -330,23 +331,19 @@ func (session *sessionData) updateTooltipConstants() {
|
|||
}
|
||||
|
||||
func (session *sessionData) reload() {
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
|
||||
css := appStyles + session.getCurrentTheme().cssText(session) + session.animationCSS
|
||||
css = strings.ReplaceAll(css, "\n", `\n`)
|
||||
css = strings.ReplaceAll(css, "\t", `\t`)
|
||||
buffer.WriteString(`document.querySelector('style').textContent = "`)
|
||||
buffer.WriteString(css)
|
||||
buffer.WriteString("\";\n")
|
||||
session.bridge.callFunc("setStyles", css)
|
||||
|
||||
if session.rootView != nil {
|
||||
buffer.WriteString(`document.getElementById('ruiRootView').innerHTML = '`)
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
|
||||
viewHTML(session.rootView, buffer)
|
||||
buffer.WriteString("';\nscanElementsSize();")
|
||||
session.bridge.updateInnerHTML("ruiRootView", buffer.String())
|
||||
session.bridge.callFunc("scanElementsSize")
|
||||
}
|
||||
|
||||
session.bridge.writeMessage(buffer.String())
|
||||
session.updateTooltipConstants()
|
||||
}
|
||||
|
||||
|
@ -447,15 +444,21 @@ func (session *sessionData) finishUpdateScript(htmlID string) {
|
|||
}
|
||||
}
|
||||
|
||||
func (session *sessionData) sendResponse() {
|
||||
if session.bridge != nil {
|
||||
session.bridge.sendResponse()
|
||||
}
|
||||
}
|
||||
|
||||
func (session *sessionData) addAnimationCSS(css string) {
|
||||
if session.bridge != nil {
|
||||
session.bridge.addAnimationCSS(css)
|
||||
session.bridge.appendAnimationCSS(css)
|
||||
}
|
||||
}
|
||||
|
||||
func (session *sessionData) clearAnimation() {
|
||||
if session.bridge != nil {
|
||||
session.bridge.clearAnimation()
|
||||
session.bridge.setAnimationCSS("")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -520,8 +523,27 @@ func (session *sessionData) htmlPropertyValue(htmlID, name string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (session *sessionData) handleAnswer(data DataObject) {
|
||||
session.bridge.answerReceived(data)
|
||||
func (session *sessionData) handleAnswer(command string, data DataObject) bool {
|
||||
switch command {
|
||||
case "answer":
|
||||
if session.bridge != nil {
|
||||
session.bridge.answerReceived(data)
|
||||
}
|
||||
|
||||
case "imageLoaded":
|
||||
session.imageManager().imageLoaded(data)
|
||||
|
||||
case "imageError":
|
||||
session.imageManager().imageLoadError(data)
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
if session.bridge != nil {
|
||||
session.bridge.sendResponse()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (session *sessionData) handleRootSize(data DataObject) {
|
||||
|
@ -672,6 +694,8 @@ func (session *sessionData) handleEvent(command string, data DataObject) {
|
|||
ErrorLog(`"id" property not found. Event: ` + command)
|
||||
}
|
||||
}
|
||||
|
||||
session.bridge.sendResponse()
|
||||
}
|
||||
|
||||
func (session *sessionData) hotKey(event KeyEvent) {
|
||||
|
@ -769,3 +793,7 @@ func (session *sessionData) RemoveAllClientItems() {
|
|||
session.clientStorage = map[string]string{}
|
||||
session.bridge.callFunc("localStorageClear")
|
||||
}
|
||||
|
||||
func (session *sessionData) addToEventsQueue(data DataObject) {
|
||||
session.events <- data
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ type wasmBridge struct {
|
|||
closeEvent chan DataObject
|
||||
}
|
||||
|
||||
func createWasmBridge(close chan DataObject) webBridge {
|
||||
func createWasmBridge(close chan DataObject) bridge {
|
||||
bridge := new(wasmBridge)
|
||||
bridge.answerID = 1
|
||||
bridge.answer = make(map[int]chan DataObject)
|
||||
|
@ -102,10 +102,6 @@ func (bridge *wasmBridge) close() {
|
|||
bridge.closeEvent <- NewDataObject("close")
|
||||
}
|
||||
|
||||
func (bridge *wasmBridge) readMessage() (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
func (bridge *wasmBridge) writeMessage(script string) bool {
|
||||
if ProtocolInDebugLog {
|
||||
DebugLog("Run script:")
|
||||
|
@ -118,21 +114,24 @@ func (bridge *wasmBridge) writeMessage(script string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (bridge *wasmBridge) addAnimationCSS(css string) {
|
||||
func (bridge *wasmBridge) prepareCSS(css string) string {
|
||||
css = strings.ReplaceAll(css, `\t`, "\t")
|
||||
css = strings.ReplaceAll(css, `\n`, "\n")
|
||||
css = strings.ReplaceAll(css, `\'`, "'")
|
||||
css = strings.ReplaceAll(css, `\"`, "\"")
|
||||
css = strings.ReplaceAll(css, `\\`, "\\")
|
||||
|
||||
styles := js.Global().Get("document").Call("getElementById", "ruiAnimations")
|
||||
content := styles.Get("textContent").String()
|
||||
styles.Set("textContent", content+"\n"+css)
|
||||
return css
|
||||
}
|
||||
|
||||
func (bridge *wasmBridge) clearAnimation() {
|
||||
func (bridge *wasmBridge) appendAnimationCSS(css string) {
|
||||
styles := js.Global().Get("document").Call("getElementById", "ruiAnimations")
|
||||
styles.Set("textContent", "")
|
||||
content := styles.Get("textContent").String()
|
||||
styles.Set("textContent", content+"\n"+bridge.prepareCSS(css))
|
||||
}
|
||||
|
||||
func (bridge *wasmBridge) setAnimationCSS(css string) {
|
||||
styles := js.Global().Get("document").Call("getElementById", "ruiAnimations")
|
||||
styles.Set("textContent", bridge.prepareCSS(css))
|
||||
}
|
||||
|
||||
func (bridge *wasmBridge) canvasStart(htmlID string) {
|
||||
|
@ -276,3 +275,6 @@ func (bridge *wasmBridge) answerReceived(answer DataObject) {
|
|||
func (bridge *wasmBridge) remoteAddr() string {
|
||||
return "localhost"
|
||||
}
|
||||
|
||||
func (bridge *wasmBridge) sendResponse() {
|
||||
}
|
||||
|
|
318
webBridge.go
318
webBridge.go
|
@ -12,16 +12,30 @@ import (
|
|||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type webBridge struct {
|
||||
answer map[int]chan DataObject
|
||||
answerID int
|
||||
answerMutex sync.Mutex
|
||||
writeMutex sync.Mutex
|
||||
closed bool
|
||||
canvasBuffer strings.Builder
|
||||
canvasVarNumber int
|
||||
updateScripts map[string]*strings.Builder
|
||||
writeMessage func(string) bool
|
||||
callFuncImmediately func(funcName string, args ...any) bool
|
||||
}
|
||||
|
||||
type wsBridge struct {
|
||||
conn *websocket.Conn
|
||||
answer map[int]chan DataObject
|
||||
answerID int
|
||||
answerMutex sync.Mutex
|
||||
closed bool
|
||||
buffer strings.Builder
|
||||
canvasBuffer strings.Builder
|
||||
canvasVarNumber int
|
||||
updateScripts map[string]*strings.Builder
|
||||
webBridge
|
||||
conn *websocket.Conn
|
||||
}
|
||||
|
||||
type httpBridge struct {
|
||||
webBridge
|
||||
responseBuffer strings.Builder
|
||||
response chan string
|
||||
remoteAddress string
|
||||
//conn *websocket.Conn
|
||||
}
|
||||
|
||||
type canvasVar struct {
|
||||
|
@ -33,7 +47,7 @@ var upgrader = websocket.Upgrader{
|
|||
WriteBufferSize: 8096,
|
||||
}
|
||||
|
||||
func CreateSocketBridge(w http.ResponseWriter, req *http.Request) webBridge {
|
||||
func createSocketBridge(w http.ResponseWriter, req *http.Request) *wsBridge {
|
||||
conn, err := upgrader.Upgrade(w, req, nil)
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
|
@ -41,41 +55,84 @@ func CreateSocketBridge(w http.ResponseWriter, req *http.Request) webBridge {
|
|||
}
|
||||
|
||||
bridge := new(wsBridge)
|
||||
bridge.answerID = 1
|
||||
bridge.answer = make(map[int]chan DataObject)
|
||||
bridge.initBridge()
|
||||
bridge.conn = conn
|
||||
bridge.closed = false
|
||||
bridge.updateScripts = map[string]*strings.Builder{}
|
||||
bridge.writeMessage = func(script string) bool {
|
||||
if ProtocolInDebugLog {
|
||||
DebugLog("🖥️ <- " + script)
|
||||
}
|
||||
|
||||
if bridge.conn == nil {
|
||||
ErrorLog("No connection")
|
||||
return false
|
||||
}
|
||||
|
||||
bridge.writeMutex.Lock()
|
||||
err := bridge.conn.WriteMessage(websocket.TextMessage, []byte(script))
|
||||
bridge.writeMutex.Unlock()
|
||||
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
bridge.callFuncImmediately = bridge.callFunc
|
||||
return bridge
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) close() {
|
||||
bridge.closed = true
|
||||
bridge.conn.Close()
|
||||
func createHttpBridge(req *http.Request) *httpBridge {
|
||||
bridge := new(httpBridge)
|
||||
bridge.initBridge()
|
||||
bridge.response = make(chan string, 10)
|
||||
bridge.writeMessage = func(script string) bool {
|
||||
if script != "" {
|
||||
if ProtocolInDebugLog {
|
||||
DebugLog(script)
|
||||
}
|
||||
|
||||
if bridge.responseBuffer.Len() > 0 {
|
||||
bridge.responseBuffer.WriteRune('\n')
|
||||
}
|
||||
bridge.responseBuffer.WriteString(script)
|
||||
}
|
||||
return true
|
||||
}
|
||||
bridge.callFuncImmediately = bridge.callImmediately
|
||||
bridge.remoteAddress = req.RemoteAddr
|
||||
return bridge
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) startUpdateScript(htmlID string) bool {
|
||||
func (bridge *webBridge) initBridge() {
|
||||
bridge.answerID = 1
|
||||
bridge.answer = make(map[int]chan DataObject)
|
||||
bridge.closed = false
|
||||
bridge.updateScripts = map[string]*strings.Builder{}
|
||||
}
|
||||
|
||||
func (bridge *webBridge) startUpdateScript(htmlID string) bool {
|
||||
if _, ok := bridge.updateScripts[htmlID]; ok {
|
||||
return false
|
||||
}
|
||||
buffer := allocStringBuilder()
|
||||
bridge.updateScripts[htmlID] = buffer
|
||||
buffer.WriteString("var element = document.getElementById('")
|
||||
buffer.WriteString("{\nlet element = document.getElementById('")
|
||||
buffer.WriteString(htmlID)
|
||||
buffer.WriteString("');\nif (element) {\n")
|
||||
return true
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) finishUpdateScript(htmlID string) {
|
||||
func (bridge *webBridge) finishUpdateScript(htmlID string) {
|
||||
if buffer, ok := bridge.updateScripts[htmlID]; ok {
|
||||
buffer.WriteString("scanElementsSize();\n}\n")
|
||||
buffer.WriteString("scanElementsSize();\n}\n}\n")
|
||||
bridge.writeMessage(buffer.String())
|
||||
|
||||
freeStringBuilder(buffer)
|
||||
delete(bridge.updateScripts, htmlID)
|
||||
}
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) argToString(arg any) (string, bool) {
|
||||
func (bridge *webBridge) argToString(arg any) (string, bool) {
|
||||
switch arg := arg.(type) {
|
||||
case string:
|
||||
arg = strings.ReplaceAll(arg, "\\", `\\`)
|
||||
|
@ -150,43 +207,44 @@ func (bridge *wsBridge) argToString(arg any) (string, bool) {
|
|||
return "", false
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) callFunc(funcName string, args ...any) bool {
|
||||
bridge.buffer.Reset()
|
||||
bridge.buffer.WriteString(funcName)
|
||||
bridge.buffer.WriteRune('(')
|
||||
func (bridge *webBridge) callFuncScript(funcName string, args ...any) (string, bool) {
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
|
||||
buffer.WriteString(funcName)
|
||||
buffer.WriteRune('(')
|
||||
for i, arg := range args {
|
||||
argText, ok := bridge.argToString(arg)
|
||||
if !ok {
|
||||
return false
|
||||
return "", false
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
bridge.buffer.WriteString(", ")
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
bridge.buffer.WriteString(argText)
|
||||
buffer.WriteString(argText)
|
||||
}
|
||||
bridge.buffer.WriteString(");")
|
||||
buffer.WriteString(");")
|
||||
|
||||
funcText := bridge.buffer.String()
|
||||
if ProtocolInDebugLog {
|
||||
DebugLog("Run func: " + funcText)
|
||||
}
|
||||
if err := bridge.conn.WriteMessage(websocket.TextMessage, []byte(funcText)); err != nil {
|
||||
ErrorLog(err.Error())
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return buffer.String(), true
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) updateInnerHTML(htmlID, html string) {
|
||||
func (bridge *webBridge) callFunc(funcName string, args ...any) bool {
|
||||
if funcText, ok := bridge.callFuncScript(funcName, args...); ok {
|
||||
return bridge.writeMessage(funcText)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (bridge *webBridge) updateInnerHTML(htmlID, html string) {
|
||||
bridge.callFunc("updateInnerHTML", htmlID, html)
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) appendToInnerHTML(htmlID, html string) {
|
||||
func (bridge *webBridge) appendToInnerHTML(htmlID, html string) {
|
||||
bridge.callFunc("appendToInnerHTML", htmlID, html)
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) updateCSSProperty(htmlID, property, value string) {
|
||||
func (bridge *webBridge) updateCSSProperty(htmlID, property, value string) {
|
||||
if buffer, ok := bridge.updateScripts[htmlID]; ok {
|
||||
buffer.WriteString(`element.style['`)
|
||||
buffer.WriteString(property)
|
||||
|
@ -198,7 +256,7 @@ func (bridge *wsBridge) updateCSSProperty(htmlID, property, value string) {
|
|||
}
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) updateProperty(htmlID, property string, value any) {
|
||||
func (bridge *webBridge) updateProperty(htmlID, property string, value any) {
|
||||
if buffer, ok := bridge.updateScripts[htmlID]; ok {
|
||||
if val, ok := bridge.argToString(value); ok {
|
||||
buffer.WriteString(`element.setAttribute('`)
|
||||
|
@ -212,7 +270,7 @@ func (bridge *wsBridge) updateProperty(htmlID, property string, value any) {
|
|||
}
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) removeProperty(htmlID, property string) {
|
||||
func (bridge *webBridge) removeProperty(htmlID, property string) {
|
||||
if buffer, ok := bridge.updateScripts[htmlID]; ok {
|
||||
buffer.WriteString(`if (element.hasAttribute('`)
|
||||
buffer.WriteString(property)
|
||||
|
@ -224,28 +282,34 @@ func (bridge *wsBridge) removeProperty(htmlID, property string) {
|
|||
}
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) addAnimationCSS(css string) {
|
||||
bridge.writeMessage(`var styles = document.getElementById('ruiAnimations');
|
||||
if (styles) {
|
||||
styles.textContent += '` + css + `';
|
||||
func (bridge *webBridge) appendAnimationCSS(css string) {
|
||||
//bridge.callFunc("appendAnimationCSS", css)
|
||||
bridge.writeMessage(`{
|
||||
let styles = document.getElementById('ruiAnimations');
|
||||
if (styles) {
|
||||
styles.textContent += '` + css + `';
|
||||
}
|
||||
}`)
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) clearAnimation() {
|
||||
bridge.writeMessage(`var styles = document.getElementById('ruiAnimations');
|
||||
if (styles) {
|
||||
styles.textContent = '';
|
||||
func (bridge *webBridge) setAnimationCSS(css string) {
|
||||
//bridge.callFunc("setAnimationCSS", css)
|
||||
bridge.writeMessage(`{
|
||||
let styles = document.getElementById('ruiAnimations');
|
||||
if (styles) {
|
||||
styles.textContent = '` + css + `';
|
||||
}
|
||||
}`)
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) canvasStart(htmlID string) {
|
||||
func (bridge *webBridge) canvasStart(htmlID string) {
|
||||
bridge.canvasBuffer.Reset()
|
||||
bridge.canvasBuffer.WriteString(`const ctx = getCanvasContext('`)
|
||||
bridge.canvasBuffer.WriteString("{\nconst ctx = getCanvasContext('")
|
||||
bridge.canvasBuffer.WriteString(htmlID)
|
||||
bridge.canvasBuffer.WriteString(`');`)
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) callCanvasFunc(funcName string, args ...any) {
|
||||
func (bridge *webBridge) callCanvasFunc(funcName string, args ...any) {
|
||||
bridge.canvasBuffer.WriteString("\nctx.")
|
||||
bridge.canvasBuffer.WriteString(funcName)
|
||||
bridge.canvasBuffer.WriteRune('(')
|
||||
|
@ -259,7 +323,7 @@ func (bridge *wsBridge) callCanvasFunc(funcName string, args ...any) {
|
|||
bridge.canvasBuffer.WriteString(");")
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) updateCanvasProperty(property string, value any) {
|
||||
func (bridge *webBridge) updateCanvasProperty(property string, value any) {
|
||||
bridge.canvasBuffer.WriteString("\nctx.")
|
||||
bridge.canvasBuffer.WriteString(property)
|
||||
bridge.canvasBuffer.WriteString(" = ")
|
||||
|
@ -268,10 +332,10 @@ func (bridge *wsBridge) updateCanvasProperty(property string, value any) {
|
|||
bridge.canvasBuffer.WriteString(";")
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) createCanvasVar(funcName string, args ...any) any {
|
||||
func (bridge *webBridge) createCanvasVar(funcName string, args ...any) any {
|
||||
bridge.canvasVarNumber++
|
||||
result := canvasVar{name: fmt.Sprintf("v%d", bridge.canvasVarNumber)}
|
||||
bridge.canvasBuffer.WriteString("\nvar ")
|
||||
bridge.canvasBuffer.WriteString("\nlet ")
|
||||
bridge.canvasBuffer.WriteString(result.name)
|
||||
bridge.canvasBuffer.WriteString(" = ctx.")
|
||||
bridge.canvasBuffer.WriteString(funcName)
|
||||
|
@ -287,7 +351,7 @@ func (bridge *wsBridge) createCanvasVar(funcName string, args ...any) any {
|
|||
return result
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) callCanvasVarFunc(v any, funcName string, args ...any) {
|
||||
func (bridge *webBridge) callCanvasVarFunc(v any, funcName string, args ...any) {
|
||||
varName, ok := v.(canvasVar)
|
||||
if !ok {
|
||||
return
|
||||
|
@ -307,7 +371,7 @@ func (bridge *wsBridge) callCanvasVarFunc(v any, funcName string, args ...any) {
|
|||
bridge.canvasBuffer.WriteString(");")
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) callCanvasImageFunc(url string, property string, funcName string, args ...any) {
|
||||
func (bridge *webBridge) callCanvasImageFunc(url string, property string, funcName string, args ...any) {
|
||||
|
||||
bridge.canvasBuffer.WriteString("\nimg = images.get('")
|
||||
bridge.canvasBuffer.WriteString(url)
|
||||
|
@ -328,51 +392,12 @@ func (bridge *wsBridge) callCanvasImageFunc(url string, property string, funcNam
|
|||
bridge.canvasBuffer.WriteString(");\n}")
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) canvasFinish() {
|
||||
bridge.canvasBuffer.WriteString("\n")
|
||||
script := bridge.canvasBuffer.String()
|
||||
if ProtocolInDebugLog {
|
||||
DebugLog("Run script:")
|
||||
DebugLog(script)
|
||||
}
|
||||
if bridge.conn == nil {
|
||||
ErrorLog("No connection")
|
||||
} else if err := bridge.conn.WriteMessage(websocket.TextMessage, []byte(script)); err != nil {
|
||||
ErrorLog(err.Error())
|
||||
}
|
||||
func (bridge *webBridge) canvasFinish() {
|
||||
bridge.canvasBuffer.WriteString("\n}\n")
|
||||
bridge.writeMessage(bridge.canvasBuffer.String())
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) readMessage() (string, bool) {
|
||||
_, p, err := bridge.conn.ReadMessage()
|
||||
if err != nil {
|
||||
if !bridge.closed {
|
||||
ErrorLog(err.Error())
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
return string(p), true
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) writeMessage(script string) bool {
|
||||
if ProtocolInDebugLog {
|
||||
DebugLog("Run script:")
|
||||
DebugLog(script)
|
||||
}
|
||||
if bridge.conn == nil {
|
||||
ErrorLog("No connection")
|
||||
return false
|
||||
}
|
||||
if err := bridge.conn.WriteMessage(websocket.TextMessage, []byte(script)); err != nil {
|
||||
ErrorLog(err.Error())
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) canvasTextMetrics(htmlID, font, text string) TextMetrics {
|
||||
result := TextMetrics{}
|
||||
|
||||
func (bridge *webBridge) removeValue(funcName, htmlID string, args ...string) (DataObject, bool) {
|
||||
bridge.answerMutex.Lock()
|
||||
answerID := bridge.answerID
|
||||
bridge.answerID++
|
||||
|
@ -381,36 +406,40 @@ func (bridge *wsBridge) canvasTextMetrics(htmlID, font, text string) TextMetrics
|
|||
answer := make(chan DataObject)
|
||||
bridge.answer[answerID] = answer
|
||||
|
||||
if bridge.callFunc("canvasTextMetrics", answerID, htmlID, font, text) {
|
||||
data := <-answer
|
||||
result.Width = dataFloatProperty(data, "width")
|
||||
funcArgs := []any{answerID, htmlID}
|
||||
for _, arg := range args {
|
||||
funcArgs = append(funcArgs, arg)
|
||||
}
|
||||
|
||||
var result DataObject = nil
|
||||
ok := bridge.callFuncImmediately(funcName, funcArgs...)
|
||||
if ok {
|
||||
result = <-answer
|
||||
}
|
||||
|
||||
close(answer)
|
||||
delete(bridge.answer, answerID)
|
||||
return result, true
|
||||
}
|
||||
|
||||
func (bridge *webBridge) canvasTextMetrics(htmlID, font, text string) TextMetrics {
|
||||
result := TextMetrics{}
|
||||
if data, ok := bridge.removeValue("canvasTextMetrics", htmlID, font, text); ok {
|
||||
result.Width = dataFloatProperty(data, "width")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) htmlPropertyValue(htmlID, name string) string {
|
||||
bridge.answerMutex.Lock()
|
||||
answerID := bridge.answerID
|
||||
bridge.answerID++
|
||||
bridge.answerMutex.Unlock()
|
||||
|
||||
answer := make(chan DataObject)
|
||||
bridge.answer[answerID] = answer
|
||||
|
||||
if bridge.callFunc("getPropertyValue", answerID, htmlID, name) {
|
||||
data := <-answer
|
||||
func (bridge *webBridge) htmlPropertyValue(htmlID, name string) string {
|
||||
if data, ok := bridge.removeValue("getPropertyValue", htmlID, name); ok {
|
||||
if value, ok := data.PropertyValue("value"); ok {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
delete(bridge.answer, answerID)
|
||||
return ""
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) answerReceived(answer DataObject) {
|
||||
func (bridge *webBridge) answerReceived(answer DataObject) {
|
||||
if text, ok := answer.PropertyValue("answerID"); ok {
|
||||
if id, err := strconv.Atoi(text); err == nil {
|
||||
if chanel, ok := bridge.answer[id]; ok {
|
||||
|
@ -427,6 +456,55 @@ func (bridge *wsBridge) answerReceived(answer DataObject) {
|
|||
}
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) close() {
|
||||
bridge.closed = true
|
||||
defer bridge.conn.Close()
|
||||
bridge.conn = nil
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) readMessage() (string, bool) {
|
||||
_, p, err := bridge.conn.ReadMessage()
|
||||
if err != nil {
|
||||
if !bridge.closed {
|
||||
ErrorLog(err.Error())
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
return string(p), true
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) sendResponse() {
|
||||
}
|
||||
|
||||
func (bridge *wsBridge) remoteAddr() string {
|
||||
return bridge.conn.RemoteAddr().String()
|
||||
}
|
||||
|
||||
func (bridge *httpBridge) close() {
|
||||
bridge.closed = true
|
||||
// TODO
|
||||
}
|
||||
|
||||
func (bridge *httpBridge) callImmediately(funcName string, args ...any) bool {
|
||||
if funcText, ok := bridge.callFuncScript(funcName, args...); ok {
|
||||
if ProtocolInDebugLog {
|
||||
DebugLog("Run func: " + funcText)
|
||||
}
|
||||
bridge.response <- funcText
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (bridge *httpBridge) sendResponse() {
|
||||
bridge.writeMutex.Lock()
|
||||
text := bridge.responseBuffer.String()
|
||||
bridge.responseBuffer.Reset()
|
||||
bridge.writeMutex.Unlock()
|
||||
bridge.response <- text
|
||||
}
|
||||
|
||||
func (bridge *httpBridge) remoteAddr() string {
|
||||
return bridge.remoteAddress
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue