From d096a35af947afe2bff22143c39336a9b4b8f422 Mon Sep 17 00:00:00 2001 From: anoshenko Date: Sat, 29 Oct 2022 20:16:40 +0300 Subject: [PATCH] Optimisation --- appWasm.go | 56 +++++++------------------------ app_scripts.js | 6 ++++ checkbox.go | 3 +- colorPicker.go | 3 +- datePicker.go | 5 ++- dropDownList.go | 4 +-- editView.go | 18 ++-------- filePicker.go | 3 +- image.go | 2 +- listView.go | 11 ++----- mediaPlayer.go | 16 ++++----- numberPicker.go | 5 ++- popup.go | 14 ++++---- scrollEvent.go | 8 ++--- session.go | 10 ++++++ sessionUtils.go | 30 +++++++---------- tableView.go | 3 +- tabsLayout.go | 4 +-- timePicker.go | 5 ++- view.go | 2 +- viewUtils.go | 8 ++--- webBrige.go | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ 22 files changed, 166 insertions(+), 137 deletions(-) diff --git a/appWasm.go b/appWasm.go index 8268dea..ea7e50b 100644 --- a/appWasm.go +++ b/appWasm.go @@ -4,7 +4,6 @@ package rui import ( _ "embed" - "strings" "syscall/js" ) @@ -22,40 +21,6 @@ func (app *wasmApp) Finish() { app.session.close() } -/* -func (app *wasmApp) ServeHTTP(w http.ResponseWriter, req *http.Request) { - - if ProtocolInDebugLog { - DebugLogF("%s %s", req.Method, req.URL.Path) - } - - switch req.Method { - case "GET": - switch req.URL.Path { - case "/": - w.WriteHeader(http.StatusOK) - io.WriteString(w, app.getStartPage()) - - case "/ws": - if brige := CreateSocketBrige(w, req); brige != nil { - go app.socketReader(brige) - } - - default: - filename := req.URL.Path[1:] - if size := len(filename); size > 0 && filename[size-1] == '/' { - filename = filename[:size-1] - } - - if !serveResourceFile(filename, w, req) && - !serveDownloadFile(filename, w, req) { - w.WriteHeader(http.StatusNotFound) - } - } - } -} -*/ - func (app *wasmApp) startSession(this js.Value, args []js.Value) interface{} { if app.createContentFunc == nil || len(args) == 1 { return nil @@ -99,16 +64,16 @@ func StartApp(addr string, createContentFunc func(Session) SessionContent, param app.brige = createWasmBrige() js.Global().Set("startSession", js.FuncOf(app.startSession)) - script := defaultScripts + wasmScripts - script = strings.ReplaceAll(script, "\\", `\\`) - script = strings.ReplaceAll(script, "\n", `\n`) - script = strings.ReplaceAll(script, "\t", `\t`) - script = strings.ReplaceAll(script, "\"", `\"`) - script = strings.ReplaceAll(script, "'", `\'`) + /* + script := defaultScripts + wasmScripts + script = strings.ReplaceAll(script, "\\", `\\`) + script = strings.ReplaceAll(script, "\n", `\n`) + script = strings.ReplaceAll(script, "\t", `\t`) + script = strings.ReplaceAll(script, "\"", `\"`) + script = strings.ReplaceAll(script, "'", `\'`) - //window := js.Global().Get("window") - //window.Call("execScript", `document.getElementById('ruiscript').text = "`+script+`"`) - js.Global().Call("execScript", `document.getElementById('ruiscript').text += "`+script+`"`) + js.Global().Call("execScript", `document.getElementById('ruiscript').text += "`+script+`"`) + */ document := js.Global().Get("document") body := document.Call("querySelector", "body") @@ -116,7 +81,8 @@ func StartApp(addr string, createContentFunc func(Session) SessionContent, param `) - js.Global().Call("execScript", "initSession()") + //js.Global().Call("execScript", "initSession()") + js.Global().Call("initSession", "") //window.Call("execScript", "initSession()") for true { diff --git a/app_scripts.js b/app_scripts.js index b8466df..ff52a69 100644 --- a/app_scripts.js +++ b/app_scripts.js @@ -1161,6 +1161,12 @@ function blur(elementId) { } } +function blurCurrent() { + if (document.activeElement != document.body) { + document.activeElement.blur(); + } +} + function playerEvent(element, tag) { //event.stopPropagation(); sendMessage(tag + "{session=" + sessionID + ",id=" + element.id + "}"); diff --git a/checkbox.go b/checkbox.go index 83bd860..0bf554c 100644 --- a/checkbox.go +++ b/checkbox.go @@ -1,7 +1,6 @@ package rui import ( - "fmt" "strings" ) @@ -190,7 +189,7 @@ func (button *checkboxData) changedCheckboxState(state bool) { defer freeStringBuilder(buffer) button.htmlCheckbox(buffer, state) - button.Session().runScript(fmt.Sprintf(`updateInnerHTML('%v', '%v');`, button.htmlID()+"checkbox", buffer.String())) + button.Session().runFunc("updateInnerHTML", button.htmlID()+"checkbox", buffer.String()) } func checkboxClickListener(view View) { diff --git a/colorPicker.go b/colorPicker.go index ed0f055..592a5bb 100644 --- a/colorPicker.go +++ b/colorPicker.go @@ -1,7 +1,6 @@ package rui import ( - "fmt" "strings" ) @@ -114,7 +113,7 @@ func (picker *colorPickerData) set(tag string, value any) bool { func (picker *colorPickerData) colorChanged(oldColor Color) { if newColor := GetColorPickerValue(picker); oldColor != newColor { if picker.created { - picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), newColor.rgbString())) + picker.session.runFunc("setInputValue", picker.htmlID(), newColor.rgbString()) } for _, listener := range picker.colorChangedListeners { listener(picker, newColor) diff --git a/datePicker.go b/datePicker.go index 2709b71..75b20be 100644 --- a/datePicker.go +++ b/datePicker.go @@ -1,7 +1,6 @@ package rui import ( - "fmt" "strconv" "strings" "time" @@ -98,7 +97,7 @@ func (picker *datePickerData) remove(tag string) { delete(picker.properties, DatePickerValue) date := GetDatePickerValue(picker) if picker.created { - picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), date.Format(dateFormat))) + picker.session.runFunc("setInputValue", picker.htmlID(), date.Format(dateFormat)) } for _, listener := range picker.dateChangedListeners { listener(picker, date) @@ -224,7 +223,7 @@ func (picker *datePickerData) set(tag string, value any) bool { if date, ok := setTimeValue(DatePickerValue); ok { if date != oldDate { if picker.created { - picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), date.Format(dateFormat))) + picker.session.runFunc("setInputValue", picker.htmlID(), date.Format(dateFormat)) } for _, listener := range picker.dateChangedListeners { listener(picker, date) diff --git a/dropDownList.go b/dropDownList.go index 9658976..ead6587 100644 --- a/dropDownList.go +++ b/dropDownList.go @@ -87,7 +87,7 @@ func (list *dropDownListData) remove(tag string) { delete(list.properties, Current) if oldCurrent != 0 { if list.created { - list.session.runScript(fmt.Sprintf(`selectDropDownListItem('%s', %d)`, list.htmlID(), 0)) + list.session.runFunc("selectDropDownListItem", list.htmlID(), 0) } list.onSelectedItemChanged(0) } @@ -135,7 +135,7 @@ func (list *dropDownListData) set(tag string, value any) bool { if current := GetCurrent(list); oldCurrent != current { if list.created { - list.session.runScript(fmt.Sprintf(`selectDropDownListItem('%s', %d)`, list.htmlID(), current)) + list.session.runFunc("selectDropDownListItem", list.htmlID(), current) } list.onSelectedItemChanged(current) } diff --git a/editView.go b/editView.go index f60f3b4..3acd0db 100644 --- a/editView.go +++ b/editView.go @@ -1,7 +1,6 @@ package rui import ( - "fmt" "strconv" "strings" ) @@ -137,7 +136,7 @@ func (edit *editViewData) remove(tag string) { if oldText != "" { edit.textChanged("") if edit.created { - edit.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, edit.htmlID(), "")) + edit.session.runFunc("setInputValue", edit.htmlID(), "") } } } @@ -211,11 +210,7 @@ func (edit *editViewData) set(tag string, value any) bool { if GetEditViewType(edit) == MultiLineText { updateInnerHTML(edit.htmlID(), edit.Session()) } else { - text = strings.ReplaceAll(text, `"`, `\"`) - text = strings.ReplaceAll(text, `'`, `\'`) - text = strings.ReplaceAll(text, "\n", `\n`) - text = strings.ReplaceAll(text, "\r", `\r`) - edit.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, edit.htmlID(), text)) + edit.session.runFunc("setInputValue", edit.htmlID(), text) } } } @@ -365,14 +360,7 @@ func (edit *editViewData) AppendText(text string) { if textValue, ok := value.(string); ok { textValue += text edit.properties[Text] = textValue - - text := strings.ReplaceAll(text, `"`, `\"`) - text = strings.ReplaceAll(text, `'`, `\'`) - text = strings.ReplaceAll(text, "\n", `\n`) - text = strings.ReplaceAll(text, "\r", `\r`) - - edit.session.runScript(`appendToInnerHTML("` + edit.htmlID() + `", "` + text + `")`) - + edit.session.runFunc("appendToInnerHTML", edit.htmlID(), text) edit.textChanged(textValue) return } diff --git a/filePicker.go b/filePicker.go index 63203eb..ccb9218 100644 --- a/filePicker.go +++ b/filePicker.go @@ -2,7 +2,6 @@ package rui import ( "encoding/base64" - "fmt" "strconv" "strings" "time" @@ -109,7 +108,7 @@ func (picker *filePickerData) LoadFile(file FileInfo, result func(FileInfo, []by for i, info := range picker.files { if info.Name == file.Name && info.Size == file.Size && info.LastModified == file.LastModified { picker.loader[i] = result - picker.Session().runScript(fmt.Sprintf(`loadSelectedFile("%s", %d)`, picker.htmlID(), i)) + picker.Session().runFunc("loadSelectedFile", picker.htmlID(), i) return } } diff --git a/image.go b/image.go index ddb935b..921b642 100644 --- a/image.go +++ b/image.go @@ -76,7 +76,7 @@ func (manager *imageManager) loadImage(url string, onLoaded func(Image), session image.listener = onLoaded image.loadingStatus = ImageLoading manager.images[url] = image - session.runScript("loadImage('" + url + "');") + session.runFunc("loadImage", url) return image } diff --git a/listView.go b/listView.go index e949c14..2fbe15a 100644 --- a/listView.go +++ b/listView.go @@ -819,12 +819,6 @@ func (listView *listViewData) updateCheckboxItem(index int, checked bool) { buffer := allocStringBuilder() defer freeStringBuilder(buffer) - buffer.WriteString(`updateInnerHTML('`) - buffer.WriteString(listView.htmlID()) - buffer.WriteRune('-') - buffer.WriteString(strconv.Itoa(index)) - buffer.WriteString(`', '`) - buffer.WriteString(listView.checkboxItemDiv(listView, checkbox, hCheckboxAlign, vCheckboxAlign)) if checked { buffer.WriteString(onDiv) @@ -842,9 +836,8 @@ func (listView *listViewData) updateCheckboxItem(index int, checked bool) { buffer.WriteString("ERROR: invalid item view") } } - buffer.WriteString(`');`) - - session.runScript(buffer.String()) + buffer.WriteString(``) + session.runFunc("updateInnerHTML", listView.htmlID()+"-"+strconv.Itoa(index), buffer.String()) } func (listView *listViewData) htmlProperties(self View, buffer *strings.Builder) { diff --git a/mediaPlayer.go b/mediaPlayer.go index 9ea43b9..53c98fe 100644 --- a/mediaPlayer.go +++ b/mediaPlayer.go @@ -472,11 +472,7 @@ func (player *mediaPlayerData) propertyChanged(tag string) { case Muted: value, _ := boolProperty(player, tag, player.Session()) - if value { - player.Session().runScript("setMediaMuted('" + player.htmlID() + "', true)") - } else { - player.Session().runScript("setMediaMuted('" + player.htmlID() + "', false)") - } + player.Session().runFunc("setMediaMuted", player.htmlID(), value) case Preload: value, _ := enumProperty(player, tag, player.Session(), 0) @@ -659,15 +655,15 @@ func (player *mediaPlayerData) handleCommand(self View, command string, data Dat } func (player *mediaPlayerData) Play() { - player.session.runScript(fmt.Sprintf(`mediaPlay('%v');`, player.htmlID())) + player.session.runFunc("mediaPlay", player.htmlID()) } func (player *mediaPlayerData) Pause() { - player.session.runScript(fmt.Sprintf(`mediaPause('%v');`, player.htmlID())) + player.session.runFunc("mediaPause", player.htmlID()) } func (player *mediaPlayerData) SetCurrentTime(seconds float64) { - player.session.runScript(fmt.Sprintf(`mediaSetSetCurrentTime('%v', %v);`, player.htmlID(), seconds)) + player.session.runFunc("mediaSetSetCurrentTime", player.htmlID(), seconds) } func (player *mediaPlayerData) getFloatPlayerProperty(tag string) (float64, bool) { @@ -731,7 +727,7 @@ func (player *mediaPlayerData) Duration() float64 { } func (player *mediaPlayerData) SetPlaybackRate(rate float64) { - player.session.runScript(fmt.Sprintf(`mediaSetPlaybackRate('%v', %v);`, player.htmlID(), rate)) + player.session.runFunc("mediaSetPlaybackRate", player.htmlID(), rate) } func (player *mediaPlayerData) PlaybackRate() float64 { @@ -743,7 +739,7 @@ func (player *mediaPlayerData) PlaybackRate() float64 { func (player *mediaPlayerData) SetVolume(volume float64) { if volume >= 0 && volume <= 1 { - player.session.runScript(fmt.Sprintf(`mediaSetVolume('%v', %v);`, player.htmlID(), volume)) + player.session.runFunc("mediaSetVolume", player.htmlID(), volume) } } diff --git a/numberPicker.go b/numberPicker.go index 49bc038..a6144a3 100644 --- a/numberPicker.go +++ b/numberPicker.go @@ -1,7 +1,6 @@ package rui import ( - "fmt" "math" "strconv" "strings" @@ -117,7 +116,7 @@ func (picker *numberPickerData) set(tag string, value any) bool { if f, ok := floatProperty(picker, NumberPickerValue, picker.Session(), min); ok && f != oldValue { newValue, _ := floatTextProperty(picker, NumberPickerValue, picker.Session(), min) if picker.created { - picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), newValue)) + picker.session.runFunc("setInputValue", picker.htmlID(), newValue) } for _, listener := range picker.numberChangedListeners { listener(picker, f) @@ -163,7 +162,7 @@ func (picker *numberPickerData) propertyChanged(tag string) { case NumberPickerValue: value := GetNumberPickerValue(picker) - picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%f')`, picker.htmlID(), value)) + picker.session.runFunc("setInputValue", picker.htmlID(), value) for _, listener := range picker.numberChangedListeners { listener(picker, value) } diff --git a/popup.go b/popup.go index 957318d..fd971e7 100644 --- a/popup.go +++ b/popup.go @@ -610,19 +610,17 @@ func ShowPopup(view View, param Params) Popup { func (manager *popupManager) updatePopupLayerInnerHTML(session Session) { if manager.popups == nil { manager.popups = []Popup{} - session.runScript(`updateInnerHTML('ruiPopupLayer', '');`) + session.runFunc("updateInnerHTML", "ruiPopupLayer", "") return } buffer := allocStringBuilder() defer freeStringBuilder(buffer) - buffer.WriteString(`updateInnerHTML('ruiPopupLayer', '`) - for _, p := range manager.popups { - p.html(buffer) + for _, popup := range manager.popups { + popup.html(buffer) } - buffer.WriteString(`');`) - session.runScript(buffer.String()) + session.runFunc("updateInnerHTML", "ruiPopupLayer", buffer.String()) } func (manager *popupManager) showPopup(popup Popup) { @@ -637,7 +635,7 @@ func (manager *popupManager) showPopup(popup Popup) { manager.popups = append(manager.popups, popup) } - session.runScript(`if (document.activeElement != document.body) document.activeElement.blur();`) + session.runFunc("blurCurrent") manager.updatePopupLayerInnerHTML(session) updateCSSProperty("ruiPopupLayer", "visibility", "visible", session) updateCSSProperty("ruiRoot", "pointer-events", "none", session) @@ -660,7 +658,7 @@ func (manager *popupManager) dismissPopup(popup Popup) { manager.popups = []Popup{} updateCSSProperty("ruiRoot", "pointer-events", "auto", session) updateCSSProperty("ruiPopupLayer", "visibility", "hidden", session) - session.runScript(`updateInnerHTML('ruiPopupLayer', '');`) + session.runFunc("updateInnerHTML", "ruiPopupLayer", "") } else { manager.popups = manager.popups[:count-1] manager.updatePopupLayerInnerHTML(session) diff --git a/scrollEvent.go b/scrollEvent.go index 7afa29f..23522dc 100644 --- a/scrollEvent.go +++ b/scrollEvent.go @@ -1,7 +1,5 @@ package rui -import "fmt" - // ScrollEvent is the constant for "scroll-event" property tag. // The "scroll-event" is fired when the content of the view is scrolled. // The main listener format: @@ -59,7 +57,7 @@ func ScrollViewTo(view View, subviewID string, x, y float64) { view = ViewByID(view, subviewID) } if view != nil { - view.Session().runScript(fmt.Sprintf(`scrollTo("%s", %g, %g)`, view.htmlID(), x, y)) + view.Session().runFunc("scrollTo", view.htmlID(), x, y) } } @@ -70,7 +68,7 @@ func ScrollViewToStart(view View, subviewID ...string) { view = ViewByID(view, subviewID[0]) } if view != nil { - view.Session().runScript(`scrollToStart("` + view.htmlID() + `")`) + view.Session().runFunc("scrollToStart", view.htmlID()) } } @@ -81,6 +79,6 @@ func ScrollViewToEnd(view View, subviewID ...string) { view = ViewByID(view, subviewID[0]) } if view != nil { - view.Session().runScript(`scrollToEnd("` + view.htmlID() + `")`) + view.Session().runFunc("scrollToEnd", view.htmlID()) } } diff --git a/session.go b/session.go index 29d10d6..5a8f510 100644 --- a/session.go +++ b/session.go @@ -8,6 +8,7 @@ import ( ) type webBrige interface { + runFunc(funcName string, args ...any) bool readMessage() (string, bool) writeMessage(text string) bool runGetterScript(script string) DataObject @@ -96,6 +97,7 @@ type Session interface { setBrige(events chan DataObject, brige webBrige) writeInitScript(writer *strings.Builder) + runFunc(funcName string, args ...any) runScript(script string) runGetterScript(script string) DataObject //, answer chan DataObject) handleAnswer(data DataObject) @@ -321,6 +323,14 @@ func (session *sessionData) imageManager() *imageManager { return session.images } +func (session *sessionData) runFunc(funcName string, args ...any) { + if session.brige != nil { + session.brige.runFunc(funcName, args...) + } else { + ErrorLog("No connection") + } +} + func (session *sessionData) runScript(script string) { if session.brige != nil { session.brige.writeMessage(script) diff --git a/sessionUtils.go b/sessionUtils.go index 0fede25..ba8a605 100644 --- a/sessionUtils.go +++ b/sessionUtils.go @@ -39,15 +39,13 @@ func sizeConstant(session Session, tag string) (SizeUnit, bool) { func updateCSSStyle(htmlID string, session Session) { if !session.ignoreViewUpdates() { if view := session.viewByHTMLID(htmlID); view != nil { - var builder viewCSSBuilder - - builder.buffer = allocStringBuilder() - builder.buffer.WriteString(`updateCSSStyle('`) - builder.buffer.WriteString(view.htmlID()) - builder.buffer.WriteString(`', '`) + builder := viewCSSBuilder{buffer: allocStringBuilder()} + //builder.buffer.WriteString(`updateCSSStyle('`) + //builder.buffer.WriteString(view.htmlID()) + //builder.buffer.WriteString(`', '`) view.cssStyle(view, &builder) - builder.buffer.WriteString(`');`) - view.Session().runScript(builder.finish()) + //builder.buffer.WriteString(`');`) + session.runFunc("updateCSSStyle", view.htmlID(), builder.finish()) } } } @@ -66,8 +64,7 @@ func updateInnerHTML(htmlID string, session Session) { script.Grow(32 * 1024) view.htmlSubviews(view, script) - view.Session().runScript(fmt.Sprintf(`updateInnerHTML('%v', '%v');`, view.htmlID(), script.String())) - //view.updateEventHandlers() + session.runFunc("updateInnerHTML", view.htmlID(), script.String()) } } } @@ -75,8 +72,7 @@ func updateInnerHTML(htmlID string, session Session) { func appendToInnerHTML(htmlID, content string, session Session) { if !session.ignoreViewUpdates() { if view := session.viewByHTMLID(htmlID); view != nil { - view.Session().runScript(fmt.Sprintf(`appendToInnerHTML('%v', '%v');`, view.htmlID(), content)) - //view.updateEventHandlers() + session.runFunc("appendToInnerHTML", view.htmlID(), content) } } } @@ -87,7 +83,7 @@ func updateProperty(htmlID, property, value string, session Session) { buffer.WriteString(fmt.Sprintf(`element.setAttribute('%v', '%v');`, property, value)) buffer.WriteRune('\n') } else { - session.runScript(fmt.Sprintf(`updateProperty('%v', '%v', '%v');`, htmlID, property, value)) + session.runFunc("updateProperty", htmlID, property, value) } } } @@ -98,7 +94,7 @@ func updateCSSProperty(htmlID, property, value string, session Session) { buffer.WriteString(fmt.Sprintf(`element.style['%v'] = '%v';`, property, value)) buffer.WriteRune('\n') } else { - session.runScript(fmt.Sprintf(`updateCSSProperty('%v', '%v', '%v');`, htmlID, property, value)) + session.runFunc("updateCSSProperty", htmlID, property, value) } } } @@ -112,10 +108,8 @@ func updateBoolProperty(htmlID, property string, value bool, session Session) { buffer.WriteString(fmt.Sprintf(`element.setAttribute('%v', false);`, property)) } buffer.WriteRune('\n') - } else if value { - session.runScript(fmt.Sprintf(`updateProperty('%v', '%v', true);`, htmlID, property)) } else { - session.runScript(fmt.Sprintf(`updateProperty('%v', '%v', false);`, htmlID, property)) + session.runFunc("updateProperty", htmlID, property, value) } } } @@ -126,7 +120,7 @@ func removeProperty(htmlID, property string, session Session) { buffer.WriteString(fmt.Sprintf(`if (element.hasAttribute('%v')) { element.removeAttribute('%v');}`, property, property)) buffer.WriteRune('\n') } else { - session.runScript(fmt.Sprintf(`removeProperty('%v', '%v');`, htmlID, property)) + session.runFunc("removeProperty", htmlID, property) } } } diff --git a/tableView.go b/tableView.go index 8d68849..edd5335 100644 --- a/tableView.go +++ b/tableView.go @@ -825,8 +825,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) { defer session.setIgnoreViewUpdates(false) } - var cssBuilder viewCSSBuilder - cssBuilder.buffer = allocStringBuilder() + cssBuilder := viewCSSBuilder{buffer: allocStringBuilder()} defer freeStringBuilder(cssBuilder.buffer) var view tableCellView diff --git a/tabsLayout.go b/tabsLayout.go index ca069a2..fb92e6d 100644 --- a/tabsLayout.go +++ b/tabsLayout.go @@ -151,7 +151,7 @@ func (tabsLayout *tabsLayoutData) remove(tag string) { return } if tabsLayout.created { - tabsLayout.session.runScript(fmt.Sprintf("activateTab('%v', %d);", tabsLayout.htmlID(), 0)) + tabsLayout.session.runFunc("activateTab", tabsLayout.htmlID(), 0) for _, listener := range tabsLayout.tabListener { listener(tabsLayout, 0, oldCurrent) } @@ -235,7 +235,7 @@ func (tabsLayout *tabsLayoutData) set(tag string, value any) bool { return true } if tabsLayout.created { - tabsLayout.session.runScript(fmt.Sprintf("activateTab('%v', %d);", tabsLayout.htmlID(), current)) + tabsLayout.session.runFunc("activateTab", tabsLayout.htmlID(), current) for _, listener := range tabsLayout.tabListener { listener(tabsLayout, current, oldCurrent) } diff --git a/timePicker.go b/timePicker.go index 9a603ba..954d652 100644 --- a/timePicker.go +++ b/timePicker.go @@ -1,7 +1,6 @@ package rui import ( - "fmt" "strconv" "strings" "time" @@ -98,7 +97,7 @@ func (picker *timePickerData) remove(tag string) { delete(picker.properties, TimePickerValue) time := GetTimePickerValue(picker) if picker.created { - picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), time.Format(timeFormat))) + picker.session.runFunc("setInputValue", picker.htmlID(), time.Format(timeFormat)) } for _, listener := range picker.timeChangedListeners { listener(picker, time) @@ -212,7 +211,7 @@ func (picker *timePickerData) set(tag string, value any) bool { if time, ok := setTimeValue(TimePickerValue); ok { if time != oldTime { if picker.created { - picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), time.Format(timeFormat))) + picker.session.runFunc("setInputValue", picker.htmlID(), time.Format(timeFormat)) } for _, listener := range picker.timeChangedListeners { listener(picker, time) diff --git a/view.go b/view.go index c29a8cb..59dbeeb 100644 --- a/view.go +++ b/view.go @@ -754,7 +754,7 @@ func viewHTML(view View, buffer *strings.Builder) { buffer.WriteRune('"') } - var cssBuilder viewCSSBuilder + cssBuilder := viewCSSBuilder{buffer: allocStringBuilder()} view.cssStyle(view, &cssBuilder) if style := cssBuilder.finish(); style != "" { diff --git a/viewUtils.go b/viewUtils.go index 9cf0f8d..34073bf 100644 --- a/viewUtils.go +++ b/viewUtils.go @@ -803,7 +803,7 @@ func colorStyledProperty(view View, subviewID []string, tag string, inherit bool // The focused View is the View which will receive keyboard events by default. func FocusView(view View) { if view != nil { - view.Session().runScript("focus('" + view.htmlID() + "')") + view.Session().runFunc("focus", view.htmlID()) } } @@ -811,21 +811,21 @@ func FocusView(view View) { // The focused View is the View which will receive keyboard events by default. func FocusViewByID(viewID string, session Session) { if viewID != "" { - session.runScript("focus('" + viewID + "')") + session.runFunc("focus", viewID) } } // BlurView removes keyboard focus from the specified View. func BlurView(view View) { if view != nil { - view.Session().runScript("blur('" + view.htmlID() + "')") + view.Session().runFunc("blur", view.htmlID()) } } // BlurViewByID removes keyboard focus from the View with the specified viewID. func BlurViewByID(viewID string, session Session) { if viewID != "" { - session.runScript("blur('" + viewID + "')") + session.runFunc("blur", viewID) } } diff --git a/webBrige.go b/webBrige.go index 873c95d..f9e57ce 100644 --- a/webBrige.go +++ b/webBrige.go @@ -3,8 +3,10 @@ package rui import ( + "fmt" "net/http" "strconv" + "strings" "sync" "github.com/gorilla/websocket" @@ -16,6 +18,7 @@ type wsBrige struct { answerID int answerMutex sync.Mutex closed bool + buffer strings.Builder } var upgrader = websocket.Upgrader{ @@ -43,6 +46,90 @@ func (brige *wsBrige) close() { brige.conn.Close() } +func (brige *wsBrige) argToString(arg any) (string, bool) { + switch arg := arg.(type) { + case string: + arg = strings.ReplaceAll(arg, "\\", `\\`) + arg = strings.ReplaceAll(arg, "'", `\'`) + arg = strings.ReplaceAll(arg, "\n", `\n`) + arg = strings.ReplaceAll(arg, "\r", `\r`) + arg = strings.ReplaceAll(arg, "\t", `\t`) + arg = strings.ReplaceAll(arg, "\b", `\b`) + arg = strings.ReplaceAll(arg, "\f", `\f`) + arg = strings.ReplaceAll(arg, "\v", `\v`) + return `'` + arg + `'`, true + + case rune: + switch arg { + case '\t': + return `'\t'`, true + case '\r': + return `'\r'`, true + case '\n': + return `'\n'`, true + case '\b': + return `'\b'`, true + case '\f': + return `'\f'`, true + case '\v': + return `'\v'`, true + case '\'': + return `'\''`, true + case '\\': + return `'\\'`, true + } + if arg < ' ' { + return fmt.Sprintf(`'\x%02d'`, int(arg)), true + } + return `'` + string(arg) + `'`, true + + case bool: + if arg { + return "true", true + } else { + return "false", true + } + + case float32: + return fmt.Sprintf("%g", float64(arg)), true + + case float64: + return fmt.Sprintf("%g", arg), true + + default: + if n, ok := isInt(arg); ok { + return fmt.Sprintf("%d", n), true + } + } + + ErrorLog("Unsupported agument type") + return "", false +} + +func (brige *wsBrige) runFunc(funcName string, args ...any) bool { + brige.buffer.Reset() + brige.buffer.WriteString(funcName) + brige.buffer.WriteRune('(') + for i, arg := range args { + argText, ok := brige.argToString(arg) + if !ok { + return false + } + + if i > 0 { + brige.buffer.WriteString(", ") + } + brige.buffer.WriteString(argText) + } + brige.buffer.WriteString(");") + + if err := brige.conn.WriteMessage(websocket.TextMessage, []byte(brige.buffer.String())); err != nil { + ErrorLog(err.Error()) + return false + } + return true +} + func (brige *wsBrige) readMessage() (string, bool) { _, p, err := brige.conn.ReadMessage() if err != nil {