Rwebgui
This commit is contained in:
parent
91ac6a17e8
commit
64d898c31b
@ -1,7 +1,7 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = boeh
|
name = rwebgui
|
||||||
version = 1.0.0
|
version = 1.0.0
|
||||||
description = Service that says boeh when Joe talks.
|
description = RWebGui
|
||||||
author = retoor
|
author = retoor
|
||||||
author_email = retoor@molodetz.nl
|
author_email = retoor@molodetz.nl
|
||||||
license = MIT
|
license = MIT
|
||||||
@ -15,11 +15,10 @@ package_dir =
|
|||||||
python_requires = >=3.7
|
python_requires = >=3.7
|
||||||
install_requires =
|
install_requires =
|
||||||
app @ git+https://retoor.molodetz.nl/retoor/app
|
app @ git+https://retoor.molodetz.nl/retoor/app
|
||||||
matrix-nio
|
|
||||||
|
|
||||||
[options.packages.find]
|
[options.packages.find]
|
||||||
where = src
|
where = src
|
||||||
|
|
||||||
[options.entry_points]
|
[options.entry_points]
|
||||||
console_scripts =
|
console_scripts =
|
||||||
boeh = boeh.__main__:main
|
rwebgui.serve = rwebgui.__main__:main
|
||||||
|
0
src/rwebgui/__init__.py
Normal file
0
src/rwebgui/__init__.py
Normal file
17
src/rwebgui/__main__.py
Normal file
17
src/rwebgui/__main__.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
from rwebgui.app import Application
|
||||||
|
from aiohttp import web
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from concurrent.futures import ThreadPoolExecutor as Executor
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
app = Application()
|
||||||
|
executor = Executor(max_workers=20)
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
loop.set_default_executor(executor)
|
||||||
|
web.run_app(app, loop=loop)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
172
src/rwebgui/app.py
Normal file
172
src/rwebgui/app.py
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
import pathlib
|
||||||
|
from aiohttp import web
|
||||||
|
import uuid
|
||||||
|
from app.app import Application as BaseApplication
|
||||||
|
from rwebgui.component import Component
|
||||||
|
import traceback
|
||||||
|
import time
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
|
||||||
|
class EvalBox(Component):
|
||||||
|
|
||||||
|
async def on_change(self, value):
|
||||||
|
|
||||||
|
try:
|
||||||
|
if value and value.strip().endswith("="):
|
||||||
|
value = value.strip()[:-1]
|
||||||
|
try:
|
||||||
|
result = eval(value)
|
||||||
|
value = value + "= " + str(result)
|
||||||
|
await self.set_attr("value",value)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
except AttributeError as ex:
|
||||||
|
print(value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Button(Component):
|
||||||
|
|
||||||
|
async def on_click(self, event):
|
||||||
|
component = self.app.search
|
||||||
|
await component.set_attr("value","Woeiii")
|
||||||
|
|
||||||
|
class Button1(Component):
|
||||||
|
|
||||||
|
async def on_click(self,event):
|
||||||
|
field = self.app.search
|
||||||
|
await field.toggle()
|
||||||
|
value = await field.get_style("display","block")
|
||||||
|
await self.set_attr("innerText", value)
|
||||||
|
|
||||||
|
class RandomString(Component):
|
||||||
|
|
||||||
|
|
||||||
|
async def task_random(self):
|
||||||
|
import random
|
||||||
|
rand_bytes = [random.choice("abcdefghijklmnopqrstuvwxyz") for _ in range(15)]
|
||||||
|
random_data = "".join(rand_bytes)
|
||||||
|
while True:
|
||||||
|
remember = random_data[0]
|
||||||
|
random_data = random_data[1:] + remember
|
||||||
|
await self.set_attr("innerHTML",random_data)
|
||||||
|
await asyncio.sleep(0.01)
|
||||||
|
|
||||||
|
class Counter(Component):
|
||||||
|
|
||||||
|
async def task_test(self):
|
||||||
|
while True:
|
||||||
|
await asyncio.sleep(10)
|
||||||
|
print("Slow task")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async def task_increment(self):
|
||||||
|
if not self.value:
|
||||||
|
self.value = 0
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
self.value = int(self.value)
|
||||||
|
except:
|
||||||
|
self.value = 0
|
||||||
|
self.value += 1
|
||||||
|
await self.set_attr("value",self.value)
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
|
class GPT(Component):
|
||||||
|
|
||||||
|
class Children:
|
||||||
|
prompt = Component
|
||||||
|
answer = Component
|
||||||
|
class submit(Component):
|
||||||
|
async def trigger(self, id_, event, data):
|
||||||
|
print("GOGOG",event,data)
|
||||||
|
return await super().trigger(id_, event, data)
|
||||||
|
async def on_click(self,data):
|
||||||
|
from xmlrpc.client import ServerProxy
|
||||||
|
client = ServerProxy("https://api.molodetz.nl/rpc")
|
||||||
|
prompt = await self.app.prompt.get_attr("value")
|
||||||
|
print(prompt)
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
await self.answer.set_attr("value",client.gpt4o(prompt))
|
||||||
|
return {"event_id":data['event_id'],"success":True}
|
||||||
|
|
||||||
|
class SpeedMeter(Component):
|
||||||
|
|
||||||
|
def __init__(self, app, id_, description=None, ws = None):
|
||||||
|
self.time_start = time.time()
|
||||||
|
self.bytes_received = 0
|
||||||
|
super().__init__(app, id_, description, ws)
|
||||||
|
|
||||||
|
async def task_update(self):
|
||||||
|
while True:
|
||||||
|
bytes_received = self.bytes_received
|
||||||
|
self.bytes_received = 0
|
||||||
|
|
||||||
|
await self.set_attr("value","{} kb/s".format(bytes_received / 1000))
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
|
async def trigger(self, id_, event, data):
|
||||||
|
super().trigger(id_, event, data)
|
||||||
|
print("JAA")
|
||||||
|
self.bytes_received += len(json.dumps(data))
|
||||||
|
|
||||||
|
|
||||||
|
class App(Component):
|
||||||
|
|
||||||
|
class Children:
|
||||||
|
eval_box = EvalBox
|
||||||
|
search = Component
|
||||||
|
teller1 = Counter
|
||||||
|
teller2 = Counter
|
||||||
|
teller3 = Counter
|
||||||
|
teller4 = Counter
|
||||||
|
teller5 = Counter
|
||||||
|
teller6 = Counter
|
||||||
|
teller7 = Counter
|
||||||
|
link1 = Button
|
||||||
|
random1 = RandomString
|
||||||
|
speed = SpeedMeter
|
||||||
|
toggle = Button1
|
||||||
|
gpt = GPT
|
||||||
|
|
||||||
|
_service = None
|
||||||
|
|
||||||
|
|
||||||
|
class Application(BaseApplication):
|
||||||
|
def __init__(self):
|
||||||
|
self.location = pathlib.Path(__file__).parent
|
||||||
|
self.location_static = self.location.joinpath("static")
|
||||||
|
self.template_path = self.location.joinpath("templates")
|
||||||
|
super().__init__(template_path=self.template_path)
|
||||||
|
self.router.add_static('/static', self.location_static)
|
||||||
|
self.router.add_get("/", self.index_handler)
|
||||||
|
self.router.add_get("/ws/{uuid}", self.websocket_handler)
|
||||||
|
|
||||||
|
async def websocket_handler(self, request):
|
||||||
|
# Extract the UUID from the route
|
||||||
|
uuid_value = request.match_info['uuid']
|
||||||
|
|
||||||
|
# Validate if it's a valid UUID
|
||||||
|
try:
|
||||||
|
uuid_obj = uuid.UUID(uuid_value)
|
||||||
|
except ValueError:
|
||||||
|
return web.Response(text="Invalid UUID", status=400)
|
||||||
|
|
||||||
|
# Upgrade the connection to WebSocket
|
||||||
|
ws = web.WebSocketResponse()
|
||||||
|
await ws.prepare(request)
|
||||||
|
|
||||||
|
print(f"WebSocket connection established with UUID: {uuid_obj}")
|
||||||
|
component = App(self, "app", ws=ws)
|
||||||
|
await component.service()
|
||||||
|
|
||||||
|
return ws
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async def index_handler(self, request):
|
||||||
|
return await self.render_template("index.html",request,{})
|
239
src/rwebgui/component.py
Normal file
239
src/rwebgui/component.py
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
import uuid
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import aiohttp
|
||||||
|
import asyncio
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
class Component:
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def define(cls):
|
||||||
|
return cls
|
||||||
|
|
||||||
|
def __init__(self,app, id_, description=None,ws: web.WebSocketResponse=None):
|
||||||
|
|
||||||
|
self.id = id_
|
||||||
|
self.ws = ws
|
||||||
|
self.app = app
|
||||||
|
self.description = description
|
||||||
|
self.children = []
|
||||||
|
self._callbacks = {}
|
||||||
|
self.value = None
|
||||||
|
self._running = False
|
||||||
|
if not hasattr(self,"Children"):
|
||||||
|
return
|
||||||
|
for name in dir(self.Children):
|
||||||
|
if name.startswith("__"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
obj = getattr(self.Children, name)
|
||||||
|
|
||||||
|
instance = obj(app=self.app,id_=name,ws=ws )
|
||||||
|
self.add_child(instance)
|
||||||
|
instance.app = self
|
||||||
|
instance.ws = self.ws
|
||||||
|
setattr(self, name, instance)
|
||||||
|
@classmethod
|
||||||
|
def from_json(cls, json):
|
||||||
|
obj = cls(None, None)
|
||||||
|
obj.__dict__ = json
|
||||||
|
return obj
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def to_json(cls):
|
||||||
|
obj = cls.__dict__ .copy()
|
||||||
|
return obj
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clone(cls):
|
||||||
|
return cls.from_json(cls.to_json())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def running(self):
|
||||||
|
if not hasattr(self.app, "_running"):
|
||||||
|
return self._running
|
||||||
|
return self.app._running
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tasks(self):
|
||||||
|
tasks_ = [getattr(self, name) for name in dir(self) if name.startswith("task_") and hasattr(self, name)]
|
||||||
|
for child in self.children:
|
||||||
|
tasks_ += child.tasks#.extend(await child.get_tasks())
|
||||||
|
return tasks_
|
||||||
|
|
||||||
|
async def communicate(self, event_id=None):
|
||||||
|
|
||||||
|
async for msg in self.ws:
|
||||||
|
if msg.type == web.WSMsgType.TEXT:
|
||||||
|
# Echo the message back to the client
|
||||||
|
#print(f"Received message: {msg.data}")
|
||||||
|
data = msg.json()
|
||||||
|
if not event_id:
|
||||||
|
pass
|
||||||
|
#return data
|
||||||
|
else:
|
||||||
|
if data.get("event_id") == event_id:
|
||||||
|
return data
|
||||||
|
|
||||||
|
@property
|
||||||
|
def callbacks(self):
|
||||||
|
return hasattr(self.app, "callbacks") and self.app.callbacks or self._callbacks
|
||||||
|
|
||||||
|
|
||||||
|
async def trigger(self,id_, event,data):
|
||||||
|
if self.id == id_:
|
||||||
|
method_name = "on_"+event
|
||||||
|
if hasattr(self, method_name):
|
||||||
|
method = getattr(self, method_name)
|
||||||
|
await method(data)
|
||||||
|
print("JAAJ")
|
||||||
|
for child in self.children:
|
||||||
|
await child.trigger(id_,event,data)
|
||||||
|
|
||||||
|
async def register_callback(self, event_id, callback):
|
||||||
|
self.callbacks[event_id] = callback
|
||||||
|
|
||||||
|
async def call(self, method, args=None,id_=None, callback=True):
|
||||||
|
while not self.running:
|
||||||
|
await asyncio.sleep(0.1)
|
||||||
|
if not args:
|
||||||
|
args= []
|
||||||
|
event_id = str(uuid.uuid4())
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
|
future = loop.create_future()
|
||||||
|
|
||||||
|
self.callbacks[event_id] = lambda data: future.set_result(data)
|
||||||
|
await self.ws.send_json({
|
||||||
|
"event_id": event_id,
|
||||||
|
"event": "call",
|
||||||
|
"id": id_ and id_ or self.id,
|
||||||
|
"method": method,
|
||||||
|
"args": args,
|
||||||
|
"callback": callback
|
||||||
|
})
|
||||||
|
if callback:
|
||||||
|
response = await self.communicate(event_id=event_id)
|
||||||
|
return response['result']
|
||||||
|
#print("GLUKT")
|
||||||
|
#return response['result']
|
||||||
|
|
||||||
|
return True
|
||||||
|
#return await future
|
||||||
|
|
||||||
|
|
||||||
|
async def get_attr(self, key, default=None):
|
||||||
|
result = await self.call("getAttr", [self.id, key],True)
|
||||||
|
return result or default
|
||||||
|
|
||||||
|
async def set_attr(self, key, value):
|
||||||
|
result = await self.call("setAttr", [self.id,key,value],callback=False)
|
||||||
|
return result
|
||||||
|
|
||||||
|
async def get(self, id_):
|
||||||
|
if self.id == id_:
|
||||||
|
return self
|
||||||
|
for child in self.children:
|
||||||
|
child = await child.get(id_)
|
||||||
|
if child:
|
||||||
|
return child
|
||||||
|
|
||||||
|
async def set_data(self, key, value):
|
||||||
|
result = await self.call("setData", [self.id, key,value], callback=False)
|
||||||
|
return result
|
||||||
|
|
||||||
|
async def get_data(self, key, default=None):
|
||||||
|
result = await self.call("getData", [self.id,key], default,True)
|
||||||
|
return result or default
|
||||||
|
|
||||||
|
async def set_style(self, key, value):
|
||||||
|
result = await self.call("setStyle", [self.id, key,value], callback=False)
|
||||||
|
return result
|
||||||
|
|
||||||
|
async def toggle(self):
|
||||||
|
value = await self.get_style("display", "block")
|
||||||
|
|
||||||
|
if value == "none":
|
||||||
|
value = ""
|
||||||
|
else:
|
||||||
|
value = "none"
|
||||||
|
await self.set_style("display", value)
|
||||||
|
|
||||||
|
async def get_style(self, key, default=None):
|
||||||
|
result = await self.call("getStyle", [self.id,key], default)
|
||||||
|
return result or default
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async def on_keyup(self,event):
|
||||||
|
value = await self.get_attr("value")
|
||||||
|
if self.value != value:
|
||||||
|
if hasattr(self, "on_change"):
|
||||||
|
value = await self.on_change(value)
|
||||||
|
self.value = value
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
|
async def get_tasks(self):
|
||||||
|
tasks = self.tasks
|
||||||
|
for child in self.children:
|
||||||
|
tasks += child.tasks#.extend(await child.get_tasks())
|
||||||
|
return tasks
|
||||||
|
|
||||||
|
async def set_running(self):
|
||||||
|
self._running = True
|
||||||
|
|
||||||
|
async def get_message(self):
|
||||||
|
async for msg in self.ws:
|
||||||
|
return msg
|
||||||
|
|
||||||
|
async def service(self):
|
||||||
|
tasks = self.tasks
|
||||||
|
tasks.append(self.set_running)
|
||||||
|
|
||||||
|
async def events():
|
||||||
|
try:
|
||||||
|
async for msg in self.ws:
|
||||||
|
if msg.type == web.WSMsgType.TEXT:
|
||||||
|
# Echo the message back to the client
|
||||||
|
#print(f"Received message: {msg.data}")
|
||||||
|
data = msg.json()
|
||||||
|
response = {"event_id":data['event_id'],"success":True}
|
||||||
|
response['time_start'] = time.time()
|
||||||
|
if self.callbacks.get(data['event_id']):
|
||||||
|
self.callbacks[data['event_id']](data['result'])
|
||||||
|
elif data.get('data') and not data['data'].get('id'):
|
||||||
|
response['handled'] = False
|
||||||
|
elif data.get('data'):
|
||||||
|
response['handled'] = True
|
||||||
|
response['data'] = await self.trigger(data['data']['id'], data['event'],data['data'])
|
||||||
|
response['cancel'] = True
|
||||||
|
|
||||||
|
response['time_end'] = time.time()
|
||||||
|
response['time_duration'] = response['time_end'] - response['time_start']
|
||||||
|
await self.ws.send_json(response)
|
||||||
|
|
||||||
|
#await ws.send_str(f"Echo: {msg.data}")
|
||||||
|
elif msg.type == web.WSMsgType.ERROR:
|
||||||
|
print(f"WebSocket error: {self.ws.exception()}")
|
||||||
|
except Exception as ex:
|
||||||
|
print(ex)
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
#async def the_task():
|
||||||
|
# while True:
|
||||||
|
# time.sleep(1)
|
||||||
|
#while True:
|
||||||
|
tasks.append(events)
|
||||||
|
await asyncio.gather(*[task() for task in tasks])
|
||||||
|
#await asyncio.create_task(asyncio.gather(*[task() for task in tasks]))
|
||||||
|
#await tasks()
|
||||||
|
print("AFTERR")
|
||||||
|
|
||||||
|
|
||||||
|
def add_child(self, child):
|
||||||
|
child.app = self.app
|
||||||
|
child.ws = self.ws
|
||||||
|
self.children.append(child)
|
295
src/rwebgui/static/rwebgui.js
Normal file
295
src/rwebgui/static/rwebgui.js
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
|
||||||
|
function elToObject(el){
|
||||||
|
obj = {}
|
||||||
|
if(el.targetElement)
|
||||||
|
el = el.targetElement
|
||||||
|
el.getAttributeNames().forEach(name => {
|
||||||
|
obj[name] = el.getAttribute(name)
|
||||||
|
if(el[name])
|
||||||
|
obj[name] = el[name]
|
||||||
|
})
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrapElement(app, el){
|
||||||
|
const allEvents = [
|
||||||
|
'click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseenter',
|
||||||
|
'mouseleave', 'mouseover', 'mouseout', 'keydown', 'keyup', 'keypress',
|
||||||
|
'focus', 'blur', 'change', 'input', 'submit', 'reset', 'resize',
|
||||||
|
'contextmenu', 'drag', 'drop', 'dragstart', 'dragend', 'dragover',
|
||||||
|
'dragenter', 'dragleave', 'touchstart', 'touchmove', 'touchend',
|
||||||
|
'touchcancel', 'pointerdown', 'pointerup', 'pointermove', 'pointerover',
|
||||||
|
'pointerout', 'pointerenter', 'pointerleave', 'wheel'/*'scroll',*/
|
||||||
|
// Add more as needed
|
||||||
|
];
|
||||||
|
const props = [
|
||||||
|
'data',
|
||||||
|
'id',
|
||||||
|
'isTrusted',
|
||||||
|
'altKey',
|
||||||
|
'ctrlKey',
|
||||||
|
'layerX',
|
||||||
|
'layerY',
|
||||||
|
'movementX',
|
||||||
|
'movementY',
|
||||||
|
'offsetX',
|
||||||
|
'offsetY',
|
||||||
|
'pageX',
|
||||||
|
'pageY',
|
||||||
|
'screenX',
|
||||||
|
'screenY',
|
||||||
|
'shiftKey',
|
||||||
|
'metaKey',
|
||||||
|
'value',
|
||||||
|
'code',
|
||||||
|
'keyCode',
|
||||||
|
'key'
|
||||||
|
]
|
||||||
|
|
||||||
|
el.app = app
|
||||||
|
allEvents.forEach(event => {
|
||||||
|
|
||||||
|
el.addEventListener(event, async(e) => {
|
||||||
|
if(el.app.suppress)
|
||||||
|
return
|
||||||
|
obj = {}
|
||||||
|
obj["id"] = el.id ? el.id : el._uuid
|
||||||
|
obj["uuid"] = el._uuid
|
||||||
|
obj["event"] = event
|
||||||
|
|
||||||
|
obj['attrs'] = elToObject(el)
|
||||||
|
obj['data'] = {}
|
||||||
|
props.forEach(prop => {
|
||||||
|
if(e[prop] != undefined){
|
||||||
|
obj['data'][prop] = e[prop]
|
||||||
|
}
|
||||||
|
if(e["targetElement"] && e["targetElement"][prop] != undefined){
|
||||||
|
obj['data'][prop] = e["targetElement"][prop]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//obj["data"] = JSON.stringify(e)
|
||||||
|
obj["nr"] = el.app.inc()
|
||||||
|
response = await el.app.emit(event, obj);
|
||||||
|
|
||||||
|
},false)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HTMLElement.prototype.rWebGui = function(){
|
||||||
|
|
||||||
|
const rWebGui = this
|
||||||
|
|
||||||
|
const config = { attributes: true, childList: true, subtree: true };
|
||||||
|
const callback = (mutationList, observer) => {
|
||||||
|
for (const mutation of mutationList) {
|
||||||
|
|
||||||
|
if (mutation.type === "childList") {
|
||||||
|
mutation.addedNodes.forEach(child => {
|
||||||
|
|
||||||
|
wrapElement(rWebGui, child)
|
||||||
|
if(!child._uuid){
|
||||||
|
child._uuid = rWebGui.createUUID()
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
} else if (mutation.type === "attributes") {
|
||||||
|
obj = {}
|
||||||
|
obj["uuid"] = mutation.target._uuid
|
||||||
|
|
||||||
|
obj[mutation.attributeName] = mutation.target.getAttribute(mutation.attributeName)
|
||||||
|
rWebGui.emit("attributeChanged", obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Create an observer instance linked to the callback function
|
||||||
|
const observer = new MutationObserver(callback);
|
||||||
|
|
||||||
|
// Start observing the target node for configured mutations
|
||||||
|
observer.observe(rWebGui, config);
|
||||||
|
|
||||||
|
// Later, you can stop observing
|
||||||
|
//observer.disconnect();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MyCustomElement extends HTMLElement {
|
||||||
|
static observedAttributes = ["color", "size"];
|
||||||
|
_ready = false
|
||||||
|
_uuid = null
|
||||||
|
ws = null
|
||||||
|
_inc = 0;
|
||||||
|
connected = false
|
||||||
|
callbacks = {}
|
||||||
|
suppress = false
|
||||||
|
inc() {
|
||||||
|
this._inc++;
|
||||||
|
return this._inc
|
||||||
|
}
|
||||||
|
|
||||||
|
get isReady() {
|
||||||
|
return this.app._ready && this.app.connected
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
// Always call super first in constructor
|
||||||
|
super();
|
||||||
|
if(!this.parent || !this.parent.app){
|
||||||
|
this.ws = new WebSocket(`ws://${window.location.host}/ws/${this.uuid}`)
|
||||||
|
const me = this
|
||||||
|
this.ws.onopen = ()=>{
|
||||||
|
me.connected = true;
|
||||||
|
}
|
||||||
|
this.ws.onmessage = (e)=>{
|
||||||
|
const data = JSON.parse(e.data)
|
||||||
|
|
||||||
|
if(data.event_id){
|
||||||
|
if(me.callbacks[data.event_id])
|
||||||
|
{
|
||||||
|
me.callbacks[data.event_id](data)
|
||||||
|
}else if(data.event == "call"){
|
||||||
|
const method = me[data.method]
|
||||||
|
if(!method){
|
||||||
|
data['success'] = false;
|
||||||
|
data['result'] = null;
|
||||||
|
}else{
|
||||||
|
let response = method(...data.args)
|
||||||
|
data['result'] = response
|
||||||
|
data['success'] = true
|
||||||
|
}
|
||||||
|
if(data['callback'])
|
||||||
|
me.ws.send(JSON.stringify(
|
||||||
|
data
|
||||||
|
))
|
||||||
|
}else {
|
||||||
|
if(data.event == "set_attr"){
|
||||||
|
const el = document.getElementById(data['id'])
|
||||||
|
if(el){
|
||||||
|
data['data'].forEach(attr=>{
|
||||||
|
el.setAttribute(attr['name'], attr['value'])
|
||||||
|
el[attr['name']] = attr['value']
|
||||||
|
})}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getAttr(id,key){
|
||||||
|
const el = document.getElementById(id)
|
||||||
|
if (el[key] != undefined)
|
||||||
|
return el[key]
|
||||||
|
return el.getAttribute(key)
|
||||||
|
}
|
||||||
|
getData(id,key){
|
||||||
|
const el = document.getElementById(id)
|
||||||
|
return el.dataset[key]
|
||||||
|
}
|
||||||
|
setData(id,key,value){
|
||||||
|
const el = document.getElementById(id)
|
||||||
|
el.dataset[key] = value
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
setStyle(id, key, value){
|
||||||
|
const el = document.getElementById(id)
|
||||||
|
el.style[key] = value
|
||||||
|
}
|
||||||
|
getStyle(id, key){
|
||||||
|
return document.getElementById(id).style[key]
|
||||||
|
}
|
||||||
|
setAttr(id, key, value) {
|
||||||
|
try{
|
||||||
|
document.getElementById(id).setAttribute(key, value)
|
||||||
|
document.getElementById(id)[key] = value
|
||||||
|
}catch(e){
|
||||||
|
console.error("Element not found:", key)
|
||||||
|
console.error("Failed to set value:", value)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
get isApp(){
|
||||||
|
return !this.parent || !this.parent.app
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(event, data) {
|
||||||
|
if(!this.app.isReady)
|
||||||
|
|
||||||
|
return false;
|
||||||
|
const me = this
|
||||||
|
return new Promise(resolve => {
|
||||||
|
data["event_id"] = me.inc()
|
||||||
|
me.callbacks[data["event_id"]] = resolve
|
||||||
|
me.app.ws.send(JSON.stringify({"uuid":obj.uuid,"event_id":data["event_id"], "event": event, "data": data}))
|
||||||
|
|
||||||
|
}).then(res => {
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
get uuid() {
|
||||||
|
if(!this._uuid){
|
||||||
|
this._uuid = this.createUUID()
|
||||||
|
}
|
||||||
|
return this._uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
createUUID(){
|
||||||
|
const uuid = crypto.randomUUID();
|
||||||
|
return uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
get app() {
|
||||||
|
if(!this.parent)
|
||||||
|
return this
|
||||||
|
if(!this.parent.app)
|
||||||
|
return this
|
||||||
|
return this.parent.app
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
console.log("Custom element added to page.");
|
||||||
|
|
||||||
|
|
||||||
|
this.rWebGui()
|
||||||
|
if(!this.isApp){
|
||||||
|
this._uuid = this.generateUUID()
|
||||||
|
this._ready = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// this.querySelectorAll("*").forEach(child => {
|
||||||
|
// child.rWebGui()
|
||||||
|
// })
|
||||||
|
/// this.dataset.uuid = this.generateUUID();
|
||||||
|
this._ready = true
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
console.log("Custom element removed from page.");
|
||||||
|
}
|
||||||
|
|
||||||
|
adoptedCallback() {
|
||||||
|
console.log("Custom element moved to new page.");
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
this.emit("attributeChanged", {aa:123})
|
||||||
|
console.log(`Attribute ${name} has changed.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("rwebgui-app", MyCustomElement);
|
||||||
|
|
||||||
|
/*
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
console.log("DOM fully loaded and parsed");
|
||||||
|
document.querySelectorAll("*").forEach(child => {
|
||||||
|
child.rWebGui()
|
||||||
|
})
|
||||||
|
})*/;
|
38
src/rwebgui/templates/index.html
Normal file
38
src/rwebgui/templates/index.html
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="/static/rwebgui.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<rwebgui-app id="app">
|
||||||
|
|
||||||
|
<h1>I do stuff</h1>
|
||||||
|
<h2 id="header2">I do stuff</h1>
|
||||||
|
<h3>I do stuff</h1>
|
||||||
|
<h4 id="header4">I do stuff</h1>
|
||||||
|
<input id="eval_box" type="text" value="wiii" />
|
||||||
|
<input id="search" type="text" value="wiii" />
|
||||||
|
<input id="teller1" type="text" value="wiii" />
|
||||||
|
<input id="teller2" type="text" value="wiii" />
|
||||||
|
<input id="teller3" type="text" value="wiii" />
|
||||||
|
<input id="teller4" type="text" value="wiii" />
|
||||||
|
<input id="teller5" type="text" value="wiii" />
|
||||||
|
<input id="teller6" type="text" value="wiii" />
|
||||||
|
<input id="teller7" type="text" value="wiii" />
|
||||||
|
<a href="#" id="link1">I do stuff</a>
|
||||||
|
<a href="#" id="toggle">I do stuff</a>
|
||||||
|
<input id="speed" type="text" value="wiii" />
|
||||||
|
|
||||||
|
<div id="random1"></div>
|
||||||
|
|
||||||
|
<div >
|
||||||
|
<textarea id="prompt" type="text" value=""></textarea>
|
||||||
|
<textarea id="answer" type="text" value=""></textarea>
|
||||||
|
<input id="submit" type="button" value="Submit" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</rwebgui-app>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user