Created
March 3, 2020 07:05
-
-
Save sanity/5a378159796c4dabf40dfcda35c2e468 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<script src="kweb_native_assets/jquery/jquery-3.1.1.min.js" crossorigin="anonymous"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script> | |
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.css"> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.js"></script> | |
<script language="JavaScript"> | |
function toWSUrl(s) { | |
let l = window.location; | |
return (l.protocol === "https:" ? "wss://" : "ws://") + l.host + "/" + s; | |
} | |
let kwebClientId = "G31dWi"; | |
let websocketEstablished = false; | |
let preWSMsgQueue = []; | |
let socket; | |
function handleInboundMessage(msg) { | |
const yourId = msg["yourId"]; | |
const debugToken = msg["debugToken"]; | |
if (kwebClientId != yourId) { | |
console.error( | |
"Received message from incorrect clientId, was " + | |
yourId + | |
", should be " + | |
kwebClientId | |
); | |
} | |
const execute = msg["execute"]; | |
if (execute !== undefined) { | |
try { | |
eval(execute["js"]); | |
console.debug("Executed `" + execute["js"] + "`"); | |
} catch (err) { | |
if (debugToken != undefined) { | |
console.error("Error evaluating [" + execute["js"] + "] : " + err); | |
var error = { | |
debugToken: debugToken, | |
error: { name: err.name, message: err.message } | |
}; | |
var message = { id: kwebClientId, error: error }; | |
sendMessage(JSON.stringify(message)); | |
} else { | |
throw err; | |
} | |
} | |
} | |
let evaluate = msg["evaluate"]; | |
if (evaluate !== undefined) { | |
try { | |
const data = eval(evaluate["js"]); | |
console.debug("Evaluated [" + evaluate["js"] + "]", data); | |
const callback = { callbackId: evaluate["callbackId"], data: data }; | |
const message = { id: kwebClientId, callback: callback }; | |
sendMessage(JSON.stringify(message)); | |
} catch (err) { | |
if (debugToken != undefined) { | |
console.error("Error evaluating `" + evaluate["js"] + "`: " + err); | |
const error = { | |
debugToken: debugToken, | |
error: { name: err.name, message: err.message } | |
}; | |
const message = { id: kwebClientId, error: error }; | |
sendMessage(JSON.stringify(message)); | |
} else { | |
throw err; | |
} | |
} | |
} | |
const instructions = msg["instructions"]; | |
if (instructions !== undefined) { | |
for (let i = 0; i < instructions.length; i++) { | |
const instruction = instructions[i]; | |
if (instruction.type === "SetAttribute") { | |
document | |
.getElementById(instruction.parameters[0]) | |
.setAttribute(instruction.parameters[1], instruction.parameters[2]); | |
} else if (instruction.type === "RemoveAttribute") { | |
const id = instruction.parameters[0]; | |
const attribute = instruction.parameters[1]; | |
document.getElementById(id).removeAttribute(attribute); | |
} else if (instruction.type === "CreateElement") { | |
const tag = instruction.parameters[0]; | |
const attributes = instruction.parameters[1]; | |
const myId = instruction.parameters[2]; | |
const parentId = instruction.parameters[3]; | |
const position = instruction.parameters[4]; | |
const newEl = document.createElement(tag); | |
newEl.setAttribute("id", myId); | |
for (const key in attributes) { | |
if (key !== "id") { | |
newEl.setAttribute(key, attributes[key]); | |
} | |
} | |
let parentElement = document.getElementById(parentId); | |
if (position > -1) { | |
parentElement.insertBefore(newEl, parentElement.childNodes[position]); | |
} else { | |
parentElement.appendChild(newEl); | |
} | |
} else if (instruction.type === "AddText") { | |
const id = instruction.parameters[0]; | |
const text = instruction.parameters[1]; | |
const textNode = document.createTextNode(text); | |
document.getElementById(id).appendChild(textNode); | |
} else if (instruction.type === "SetText") { | |
const id = instruction.parameters[0]; | |
const text = instruction.parameters[1]; | |
document.getElementById(id).textContent = text | |
} | |
} | |
} | |
} | |
function connectWs() { | |
var wsURL = toWSUrl("ws"); | |
console.debug("Establishing websocket connection", wsURL); | |
socket = new WebSocket(wsURL); | |
if (window.WebSocket === undefined) { | |
document.body.innerHTML = | |
"<h1>Unfortunately this website requires a browser that supports websockets (all modern browsers do)</h1>"; | |
console.error("Browser doesn't support window.WebSocket"); | |
} else { | |
socket.onopen = function() { | |
console.debug("socket.onopen event received"); | |
websocketEstablished = true; | |
console.debug("Websocket established", wsURL); | |
sendMessage(JSON.stringify({ id: kwebClientId, hello: true })); | |
while (preWSMsgQueue.length > 0) { | |
sendMessage(preWSMsgQueue.shift()); | |
} | |
}; | |
socket.onmessage = function(event) { | |
var msg = JSON.parse(event.data); | |
console.debug("Message received from socket: ", event.data); | |
handleInboundMessage(msg); | |
}; | |
socket.onclose = function(evt) { | |
console.debug("Socket closed"); | |
var explanation = ""; | |
if (evt.reason && evt.reason.length > 0) { | |
explanation = "reason: " + evt.reason; | |
} else { | |
explanation = "without a reason specified"; | |
} | |
console.error("WebSocket was closed", explanation, evt); | |
websocketEstablished = false; | |
if (evt.wasClean){ | |
console.warn("Attempting reconnect...") | |
connectWs() | |
} else { | |
console.warn("Forcing page reload"); | |
location.reload(true); | |
} | |
// setTimeout(function() { location.reload(true); }, 5000); | |
}; | |
socket.onerror = function(evt) { | |
console.error("WebSocket error", evt); | |
websocketEstablished = false; | |
console.warn("Forcing page reload"); | |
location.reload(true); | |
// setTimeout(function() { location.reload(true); }, 5000); | |
}; | |
} | |
} | |
function sendMessage(msg) { | |
if (websocketEstablished) { | |
console.debug("Sending WebSocket message", msg); | |
socket.send(msg); | |
} else { | |
console.debug( | |
"Queueing WebSocket message as connection isn't established", | |
msg | |
); | |
preWSMsgQueue.push(msg); | |
} | |
} | |
function callbackWs(callbackId, data) { | |
var msg = JSON.stringify({ | |
id: kwebClientId, | |
callback: { callbackId: callbackId, data: JSON.stringify(data) } | |
}); | |
sendMessage(msg); | |
} | |
/* | |
* Utility functions | |
*/ | |
function hasClass(el, className) { | |
if (el.classList) return el.classList.contains(className); | |
else | |
return !!el.className.render(new RegExp("(\\s|^)" + className + "(\\s|$)")); | |
} | |
function addClass(el, className) { | |
if (el.classList) el.classList.add(className); | |
else if (!hasClass(el, className)) el.className += " " + className; | |
} | |
function removeClass(el, className) { | |
if (el.classList) el.classList.remove(className); | |
else if (hasClass(el, className)) { | |
var reg = new RegExp("(\\s|^)" + className + "(\\s|$)"); | |
el.className = el.className.replace(reg, " "); | |
} | |
} | |
function removeElementByIdIfExists(id) { | |
var e = document.getElementById(id); | |
if (e) { | |
e.parentNode.removeChild(e); | |
} | |
} | |
var docCookies = { | |
getItem: function(sKey) { | |
if (!sKey || !this.hasItem(sKey)) { | |
return "__COOKIE_NOT_FOUND_TOKEN__"; | |
} | |
return unescape( | |
document.cookie.replace( | |
new RegExp( | |
"(?:^|.*;\\s*)" + | |
escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + | |
"\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*" | |
), | |
"$1" | |
) | |
); | |
}, | |
/** | |
* docCookies.setString(sKey, sValue, vEnd, sPath, sDomain, bSecure) | |
* | |
* @argument sKey (String): the firstName of the cookie; | |
* @argument sValue (String): the value of the cookie; | |
* @optional argument vEnd (Number, String, Date Object or null): the max-age in seconds (e.g., 31536e3 for a year) or the | |
* expires date in GMTString format or in Date Object format; if not specified it will expire at the end of session; | |
* @optional argument sPath (String or null): e.g., "/", "/mydir"; if not specified, defaults to the current pathSegments of the current document location; | |
* @optional argument sDomain (String or null): e.g., "example.com", ".example.com" (includes all subdomains) or "subdomain.example.com"; if not | |
* specified, defaults to the host portion of the current document location; | |
* @optional argument bSecure (Boolean or null): cookie will be transmitted only over secure protocol as https; | |
* @return undefined; | |
**/ | |
setItem: function(sKey, sValue, vEnd, sPath, sDomain, bSecure) { | |
if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/.test(sKey)) { | |
return; | |
} | |
var sExpires = ""; | |
if (vEnd) { | |
switch (typeof vEnd) { | |
case "number": | |
sExpires = "; max-age=" + vEnd; | |
break; | |
case "string": | |
sExpires = "; expires=" + vEnd; | |
break; | |
case "object": | |
if (vEnd.hasOwnProperty("toGMTString")) { | |
sExpires = "; expires=" + vEnd.toGMTString(); | |
} | |
break; | |
} | |
} | |
document.cookie = | |
escape(sKey) + | |
"=" + | |
escape(sValue) + | |
sExpires + | |
(sDomain ? "; domain=" + sDomain : "") + | |
(sPath ? "; path=" + sPath : "") + | |
(bSecure ? "; secure" : ""); | |
}, | |
removeItem: function(sKey) { | |
if (!sKey || !this.hasItem(sKey)) { | |
return; | |
} | |
var oExpDate = new Date(); | |
oExpDate.setDate(oExpDate.getDate() - 1); | |
document.cookie = | |
encodeURIComponent(sKey) + "=; expires=" + oExpDate.toGMTString() + "; path=/"; | |
}, | |
hasItem: function(sKey) { | |
return new RegExp( | |
"(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=" | |
).test(document.cookie); | |
} | |
}; | |
window.addEventListener("pageshow", function(event) { | |
if (window.performance.navigation.type === 2) { | |
location.reload(true); | |
} | |
}); | |
function buildPage() { | |
handleInboundMessage({"yourId":"G31dWi","debugToken":"726edec539beb06a","evaluate":{"js":"document.origin","callbackId":1369705618}}); | |
handleInboundMessage({"yourId":"G31dWi","debugToken":"29165255f353e7d1","execute":{"js":"document.getElementById(\"K4\").innerHTML\u003d\"A simple demo of \u003ca href\u003d\\\"https:\\/\\/docs.kweb.io\\/\\\"\u003eKweb\u003c\\/a\u003e, add and delete items from a\\nto do list.\\n\u003cp\\/\u003e\\nTry visiting this URL in another browser window and make some changes.\\n\u003cp\\/\u003e\\nYou may find the source code for this app\\n\u003ca href\u003d\\\"https:\\/\\/github.com\\/kwebio\\/core\\/tree\\/master\\/src\\/main\\/kotlin\\/io\\/kweb\\/demos\\/todo\\\"\u003ehere\u003c\\/a\u003e.\";"}}); | |
handleInboundMessage({"yourId":"G31dWi","debugToken":"5966a2df23364205","execute":{"js":"document.getElementById(\"Kf\")\n .addEventListener(\"click\", function(event) {\n callbackWs(657780482, {\"altKey\" : event.altKey, \"button\" : event.button, \"buttons\" : event.buttons, \"clientX\" : event.clientX, \"clientY\" : event.clientY, \"ctrlKey\" : event.ctrlKey, \"detail\" : event.detail, \"metaKey\" : event.metaKey, \"movementX\" : event.movementX, \"movementY\" : event.movementY, \"region\" : event.region, \"retrieved\" : event.retrieved, \"screenX\" : event.screenX, \"screenY\" : event.screenY, \"shiftKey\" : event.shiftKey, \"type\" : event.type, \"x\" : event.x, \"y\" : event.y});\n });\n "}}); | |
handleInboundMessage({"yourId":"G31dWi","debugToken":"32d198f3beac8e2e","execute":{"js":"document.getElementById(\"Kk\")\n .addEventListener(\"click\", function(event) {\n callbackWs(622908227, {\"altKey\" : event.altKey, \"button\" : event.button, \"buttons\" : event.buttons, \"clientX\" : event.clientX, \"clientY\" : event.clientY, \"ctrlKey\" : event.ctrlKey, \"detail\" : event.detail, \"metaKey\" : event.metaKey, \"movementX\" : event.movementX, \"movementY\" : event.movementY, \"region\" : event.region, \"retrieved\" : event.retrieved, \"screenX\" : event.screenX, \"screenY\" : event.screenY, \"shiftKey\" : event.shiftKey, \"type\" : event.type, \"x\" : event.x, \"y\" : event.y});\n });\n "}}); | |
handleInboundMessage({"yourId":"G31dWi","debugToken":"15bec4aa8042e7","execute":{"js":"document.getElementById(\"Ko\")\n .addEventListener(\"keypress\", function(event) {\n callbackWs(340913554, {\"altKey\" : event.altKey, \"code\" : event.code, \"ctrlKey\" : event.ctrlKey, \"detail\" : event.detail, \"isComposing\" : event.isComposing, \"key\" : event.key, \"locale\" : event.locale, \"location\" : event.location, \"metaKey\" : event.metaKey, \"retrieved\" : event.retrieved, \"shiftKey\" : event.shiftKey, \"type\" : event.type});\n });\n "}}); | |
handleInboundMessage({"yourId":"G31dWi","debugToken":"f17f2fc96b55fb6","execute":{"js":"document.getElementById(\"Kp\")\n .addEventListener(\"click\", function(event) {\n callbackWs(889151310, {\"altKey\" : event.altKey, \"button\" : event.button, \"buttons\" : event.buttons, \"clientX\" : event.clientX, \"clientY\" : event.clientY, \"ctrlKey\" : event.ctrlKey, \"detail\" : event.detail, \"metaKey\" : event.metaKey, \"movementX\" : event.movementX, \"movementY\" : event.movementY, \"region\" : event.region, \"retrieved\" : event.retrieved, \"screenX\" : event.screenX, \"screenY\" : event.screenY, \"shiftKey\" : event.shiftKey, \"type\" : event.type, \"x\" : event.x, \"y\" : event.y});\n });\n "}}); | |
handleInboundMessage({"yourId":"G31dWi","debugToken":"2b2c426bfe645973","execute":{"js":""}}); | |
connectWs(); | |
} | |
</script> | |
<title id="K9">To Do List #KVar(18e827c)</title> | |
</head> | |
<body onload="buildPage()"> | |
<noscript> | |
This page is built with <a href="https://kweb.io/">Kweb</a>, which requires JavaScript to be enabled. | |
</noscript> | |
<div id="K0" class="ui main container"> | |
<div id="K1" class="column"> | |
<div id="K2" class="ui vertical segment"> | |
<div id="K3" class="ui message"> | |
<p id="K4"></p> | |
</div> | |
</div> | |
<div id="K5" class="ui vertical segment"> | |
<h1 id="K6" class="ui dividing header">To do List</h1> | |
<div id="K7" class="content"> | |
<span id="K8"><span id="Ka"><h3 id="Kb"></h3> | |
<div id="Kc" class="ui middle aligned divided list"> | |
<div id="Kd" class="item"> | |
<div id="Ke" class="right floated content"> | |
<button type="button" class="mini ui icon button" id="Kf"><i id="Kg" class="trash icon"></i></button> | |
</div> | |
<div id="Kh" class="content"> | |
One | |
</div> | |
</div> | |
<div id="Ki" class="item"> | |
<div id="Kj" class="right floated content"> | |
<button type="button" class="mini ui icon button" id="Kk"><i id="Kl" class="trash icon"></i></button> | |
</div> | |
<div id="Km" class="content"> | |
Two | |
</div> | |
</div> | |
</div> | |
<div id="Kn" class="ui action input"> | |
<input type="text" id="Ko" placeholder="Add Item"><button type="button" class="ui button" id="Kp">Add</button> | |
</div></span></span> | |
</div> | |
</div> | |
</div> | |
</div> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment