|
|
|
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',*/
|
|
];
|
|
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)
|
|
}
|
|
}
|
|
};
|
|
const observer = new MutationObserver(callback);
|
|
|
|
observer.observe(rWebGui, config);
|
|
|
|
|
|
}
|
|
|
|
|
|
class RWebGuiApp extends HTMLElement {
|
|
_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
|
|
}
|
|
|
|
get url() {
|
|
const protocol = window.location.protocol === "https:" ? "wss" : "ws";
|
|
return `${protocol}://${window.location.host}/ws/${this.uuid}`
|
|
}
|
|
|
|
constructor() {
|
|
// Always call super first in constructor
|
|
super();
|
|
if(!this.parent || !this.parent.app){
|
|
this.ws = new WebSocket(this.url)
|
|
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() {
|
|
|
|
|
|
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
|
|
}
|
|
|
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
this.emit("attributeChanged", {aa:123})
|
|
}
|
|
}
|
|
|
|
customElements.define("rwebgui-app",RWebGuiApp);
|
|
|
|
/*
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
document.querySelectorAll("*").forEach(child => {
|
|
child.rWebGui()
|
|
})
|
|
})*/;
|