(function(globals){ let _unique = new Date().getTime(); let __idles__ = []; class LN { constructor(opts){ } static $each(oa,cb){ if (oa instanceof Array){ let result = []; oa.forEach((value,index)=>{ result.push(cb.call(value,index,value)); }); return result; } else if (oa instanceof Object){ let result = {}; Object.keys(oa).forEach((key)=>{ if (oa.hasOwnProperty(key)){ result[key] = cb.call(oa[key],key,oa[key]); } }); return result; } } static $idle(cb,thisval = null){ let scheduled = __idles__.length > 0; let n=0; for (;n<__idles__.length;n++){ let idle = __idles__[n]; if ((idle[0] == cb) && (idle[1] == thisval)) break; } if (n == __idles__.length) __idles__.push([cb,thisval]); if (!scheduled) setTimeout(()=>{ while (__idles__.length > 0){ let idle = __idles__.pop(); idle[0].call(idle[1]); } },0); } static $unique(){ return _unique++; } static $fetch(url,cb){ return new LN.Promise((resolve,reject)=>{ fetch(url) .then((response => { if (response.status.toString().startsWith("2")) { let t = response.text(); cb && cb(t,null); resolve(t); } else { cb && cb(null, response.statusText); reject(response.statusText); } })); }, `fetch(${url})`); } static $add(classpath,c){ let p = classpath.split("."); if (p.length < 1) throw "invalid classpath"; let container = globals; while (p.length > 1){ let next = p.shift(); if (!container[next]) container[next] = {} container = container[next]; } let prev = container[p[0]]; container[p[0]] = c; if (prev) { LN.$each(prev,(key,value)=>{ c[key] = value; }); } }; } LN.prototypes = {}; LN.$add("LN", LN); })(window); (function(){ /*\ |*| |*| Base64 / binary data / UTF-8 strings utilities (#1) |*| |*| https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding |*| |*| Author: madmurphy |*| |*| Harald Wolff-Thobaben: Small adaptions to create a static class |*| \*/ class Base64 { constructor(){ } static b64ToUint6(nChr){ return nChr > 64 && nChr < 91 ? nChr - 65 : nChr > 96 && nChr < 123 ? nChr - 71 : nChr > 47 && nChr < 58 ? nChr + 4 : nChr === 43 ? 62 : nChr === 47 ? 63 : 0; } static uint6ToB64(nUint6){ return nUint6 < 26 ? nUint6 + 65 : nUint6 < 52 ? nUint6 + 71 : nUint6 < 62 ? nUint6 - 4 : nUint6 === 62 ? 43 : nUint6 === 63 ? 47 : 65; } static encode(aBytes){ var eqLen = (3 - (aBytes.length % 3)) % 3, sB64Enc = ""; for (var nMod3, nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) { nMod3 = nIdx % 3; nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24); if (nMod3 === 2 || aBytes.length - nIdx === 1) { sB64Enc += String.fromCharCode(Base64.uint6ToB64(nUint24 >>> 18 & 63), Base64.uint6ToB64(nUint24 >>> 12 & 63), Base64.uint6ToB64(nUint24 >>> 6 & 63), Base64.uint6ToB64(nUint24 & 63)); nUint24 = 0; } } return eqLen === 0 ? sB64Enc : sB64Enc.substring(0, sB64Enc.length - eqLen) + (eqLen === 1 ? "=" : "=="); } static decode(sBase64, nBlockSize) { var sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length, nOutLen = nBlockSize ? Math.ceil((nInLen * 3 + 1 >>> 2) / nBlockSize) * nBlockSize : nInLen * 3 + 1 >>> 2, aBytes = new Uint8Array(nOutLen); for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) { nMod4 = nInIdx & 3; nUint24 |= Base64.b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4; if (nMod4 === 3 || nInLen - nInIdx === 1) { for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) { aBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255; } nUint24 = 0; } } return aBytes; } } LN.$add("LN.Base64",Base64); })(); (function(){ class LNPromise { constructor(executor, label){ this.promise = new Promise( (resolve,reject) => { this.resolve = (v) => { this._s.state = "ready"; resolve(v); this.release(); }, this.reject = (e) => { this._s.state = "failed"; reject(e); this.release(); } executor && executor( this.resolve, this.reject ); } ); if (!label) label = "N.D."; this._s = { label, state: "waiting" }; this.idx = LN.$unique(); LNPromise.$current[this.idx] = this._s; } label(){ return this._s.label; } state(){ return this._s.state; } release(){ setTimeout(()=>{ Vue.delete(LNPromise.$current, this.idx); },1000); } then(){ return this.promise.then.apply(this.promise, arguments); } static getCurrentPromises(){ return LNPromise.$current; } } LNPromise.$current = {}; LN.$add("LN.Promise",LNPromise); })(); (function(){ class LNVueWebSocket { constructor(lnvue,o){ this.LNVue = lnvue; this.options = Object.assign({},o); if (!this.options.url) this.options.url = this.constructURL(); this._id = 1; this.defaultTimeout = 30000; this.websocket = null; this.callbacks = {}; this._state = "initialized"; this._retry = null; this.closing = false; } constructURL(){ var pageURI = window.location; var scheme = pageURI.scheme == "https" ? "wss:" : "ws:"; var host = pageURI.host; return scheme + "//" + host + "/socket"; } open(){ if (this._retry){ clearTimeout(this._retry); } this.closing = false; this.websocket = new WebSocket(this.options.url); this.websocket.onopen = (e) =>{ console.log("WebSocket connected"); this._state = "ONLINE"; let WSHello = { ApplicationSessionID: this.LNVue.sessionID(), }; console.log("WSHello request",WSHello); this.request("WSHello",WSHello) .then((wsh)=>{ console.log("WSHello response",wsh); this.LNVue.sessionID(wsh.message.ApplicationSessionID); this.LNVue.identity = new LN.Identity(wsh.message.SessionIdentity); }); }; this.websocket.onclose = (e)=>{ this._onclose(e); this._state = "OFFLINE"; }; this.websocket.onerror = (e)=>{ this._onerror(e); this._state = "ERROR"; }; this.websocket.onmessage = (e)=>{ this._onmessage(e); }; return this; } close(){ if (this.websocket){ this.closing = true; this.websocket.close(200,"close() called"); } } request(msgtype,msg,timeout){ let message = { id: this._id++, type: msgtype, message: msg, } if (!timeout) timeout = this.defaultTimeout; if (timeout != -1){ return new Promise((resolve,reject)=>{ let to = setTimeout(()=>{ delete this.callbacks[message.id]; reject("timed out"); },timeout); this.callbacks[message.id] = (msgtype,msg)=>{ clearTimeout(to); delete this.callbacks[message.id]; if (msgtype == "error") reject(msg); else resolve({type: msgtype,message: msg}); }; this.websocket.send( JSON.stringify(message) ); }); } else { new Promise((resolve,reject)=>{ this.websocket.send( JSON.stringify(message) ); resolve(); }); } } _onclose(evt){ this.websocket = null; this.options.onclose && this.options.onclose(evt); if (!this.closing) { this._retry = setTimeout(() => { this._retry = null; console.log("reconnect...") this.open(); }, 5000); } } _onerror(evt){ this.options.onerror && this.options.onerror(evt); } _onmessage(evt){ try { let j = JSON.parse(evt.data); let cb = this.callbacks[ j.id ]; cb && cb(j.type,j.message); } catch(exc){ console.log(exc,evt.data); } } } LN.$add("LN.Vue.WebSocket",LNVueWebSocket); })(); (function (){ class LNVue { constructor(el,options = {}){ this.options = Object.assign({ routes: [], data: {}, }, options ); this._el = el; this.data = Object.assign({}, options.data, { LNVue: this, msg: "Hello World" }); this.promises = []; this.statusText = "LNVue preparing"; Vue.prototype.$LNVue = this; LNVue.$_ = this; this.navigation = {}; this.identity = new LN.Identity(); Promise .all(LNVue.promises) .then(()=>{ this.status("LNVue: starting"); LNVue.vueRouter.addRoutes([{ path: "*", component: { template: `

404 Not Found

The URL you tried to reach is not existing.`, }, }]); }, (cause)=>{ this.status("LNVue: start failed: " + cause); }); this.vue = null; LNVue.$instance.resolve(this); } Start(){ Promise .all(this.promises) .then(()=>{ LN.$idle(()=>{ this.vue = new Vue({ el: this._el, data: this.data, router: LNVue.vueRouter, }); }); }); LN.$idle(()=>{ this.socket = new LN.Vue.WebSocket(this); this.socket.open(); }); LN.$idle(()=>{ LNVue.$start.resolve(this); }); } storage(){ return window.localStorage; } sessionID(){ if (arguments.length == 1){ this.storage().setItem("LNVueSessionID",arguments[0]); console.log("LNVue.SID <= " + arguments[0]); return this; } else { let sid = this.storage().getItem("LNVueSessionID"); console.log("LNVue.SID == " + sid); if (!sid) { sid = "00000000-0000-0000-0000-000000000000"; } return sid; } } Version(){ return "0.2alpha"; }; getCurrentPromises() { return LN.Promise.getCurrentPromises(); } status(){ if (arguments.length == 1){ this.statusText = arguments[0]; return this; } else if (arguments.length == 0){ return this.statusText; } else throw "LNVue.status(): too many arguments"; } addModule(modSpec){ if (modSpec.navigation instanceof Object){ LNVue.deepAssign(modSpec.navigation,this.navigation); } LN.$each(modSpec.routes,(key,route)=>{ if ((route instanceof Object) && route.url) { let p = new LN.Promise((resolve,reject)=>{ LN.$fetch(route.url) .then((src)=>{ this.addRoute(key,{ template: src, data: ()=>{ return this.data; }, }); resolve(); }, (cause)=>{ console.log("loading route.url failed: ",cause); }); },`addModule(${route.url})`); this.promises.push(p); } else if (route instanceof Object){ this.addRoute(key,{ template: route.template, data: ()=>{ return this.data; }, } ); } else { this.addRoute(key,{ template: route, data: ()=>{ return this.data; }, } ); } }); } addRoute(path,component){ LNVue.vueRouter.addRoutes([ { path, component, }, ]); if (this.vue){ let route = this.vue.$route; LNVue.vueRouter.replace("/"); LNVue.vueRouter.replace(route); } } /* Authentication API */ requestChallenges(identityName,secureAttributeTypeName){ return new Promise((resolve,reject)=>{ this.socket.request("AuthenticationRequest",{ IdentityName: identityName, SecureAttributeTypeName: secureAttributeTypeName, }) .then((challenges)=>{ resolve(challenges); }, (error)=>{ console.log("Login challenges could not be retrieved", error); } ); }); } authenticate(identityName,secureAttributeID,challenge,prove){ let authenticationProve = { IdentityName: identityName, SecureAttributeUniqueID: secureAttributeID, Challenge: challenge, Prove: prove, }; this.socket.request("AuthenticationProve", authenticationProve) .then((identity)=>{ this.identity = new LN.Identity(identity.message); }, (error)=>{ this.identity = new LN.Identity(); }); } rpc(moduleName,methodName,parameters){ return new Promise((resolve,reject)=>{ let rpcCall = { module: moduleName, method: methodName, parameters: parameters }; this.socket.request("RPCCall",rpcCall) .then( (result)=>{ if (result.message.error) { console.log("rpc call failed", result.message.error); reject(result.message.error); } else resolve(result.message.Result); }, (error)=>{ console.log("rpc failed", error); reject(error); } ); }); } static $LNVue(){ return LNVue.$_; } } LNVue.$instance = new LN.Promise(()=>{},'LN.Vue Instance Promise'); LNVue.$start = new LN.Promise(()=>{},'LN Vue Startup Promise'); LNVue.vueRouter = new VueRouter({ mode: 'history', routes: [], }); LNVue.deepAssign = function(source,target){ LN.$each(source,function(key){ if (target[key] instanceof Object){ LNVue.deepAssign(src[key],target[key]); } else { target[key] = source[key]; } }); } LNVue.$ = function(src){ let el = document.createElement("parse"); el.innerHTML = src; return el.firstChild; } LNVue.routes = []; LNVue.promises = []; LNVue.encodeHex = (bytes) => bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), ''); LNVue.decodeHex = (hexString) => new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16))); ArrayBuffer.combine = function(...args){ let byteLength = 0; args.forEach((arg,index)=>{ byteLength = byteLength + arg.byteLength; }); let result = new Uint8Array(byteLength); let p = 0; args.forEach((arg,index)=>{ for (let n=0;n