ln.identities

master
Harald Wolff 2019-11-29 09:29:30 +01:00
parent 4f67439042
commit 6ff597c106
5 changed files with 301 additions and 251 deletions

View File

@ -137,6 +137,10 @@ body {
font-size: 12px; font-size: 12px;
} }
.block {
display: block;
}
.ln-view { .ln-view {
position: absolute; position: absolute;
top: 0px; top: 0px;

View File

@ -12,6 +12,7 @@
<script type="text/javascript" src="js/ln.vue.components.js"></script> <script type="text/javascript" src="js/ln.vue.components.js"></script>
<script type="text/javascript" src="js/ln.vue.table.js"></script> <script type="text/javascript" src="js/ln.vue.table.js"></script>
<script type="text/javascript" src="js/ln.vue.webauthn.js"></script> <script type="text/javascript" src="js/ln.vue.webauthn.js"></script>
<script type="text/javascript" src="js/ln.identities.js"></script>
<style> <style>
table#controls > tbody > tr > td:nth-child(3) { table#controls > tbody > tr > td:nth-child(3) {
@ -217,7 +218,7 @@
app.Start(); app.Start();
LNVue.onidle(()=>{ LN.$idle(()=>{
message.value = "idle time reached. Message written."; message.value = "idle time reached. Message written.";
setTimeout(()=>{ setTimeout(()=>{
message.value = "fired from the idle job: new message after 1000ms."; message.value = "fired from the idle job: new message after 1000ms.";
@ -225,11 +226,11 @@
}); });
for (let n=5;n<15;n++){ for (let n=5;n<15;n++){
LNVue.LNPromise(n + " sec promise test",(resolve,reject)=>{ new LN.Promise((resolve,reject)=>{
setTimeout(()=>{ setTimeout(()=>{
resolve(); resolve();
},n * 1000); },n * 1000);
}); },n + " sec promise test");
} }
</script> </script>

View File

@ -0,0 +1,49 @@
(function(){
class LNIdentity
{
constructor(src){
if (!src)
src = {};
this.IdentityName = src.IdentityName || "";
this.UniqueID = src.UniqueID || null;
this.Roles = src.AssignedRoles || [];
}
findRolesByName(identityName){
this.Roles.forEach(role => {
if (role.IdentityName == identityName)
return role.Roles;
});
return 0;
}
findRolesByID(identityUniqueID){
this.Roles.forEach(role => {
if (role.UniqueID == identityUniqueID)
return role.Roles;
});
return 0;
}
hasRole(role,identityName){
let roles = this.findRolesByName(identityName);
return (roles & role) == role;
}
}
LNIdentity.VIEW = (1<<0);
LNIdentity.USE = LNIdentity.VIEW | (1<<1);
LNIdentity.CONTROL = LNIdentity.VIEW | (1<<2);
LNIdentity.MANAGE = LNIdentity.CONTROL | (1<<3);
LNIdentity.ADMIN = 0x0000FFFF;
LNIdentity.MANAGEROLES = (1<<16);
LNIdentity.IMPERSONATE = (1<<24);
LNIdentity.OWN = 0x0FFFFFFF;
LNIdentity.BE = 0x0000FFFF;
LNIdentity.SUPER = 0x7FFFFFFF;
LN.$add("LN.Identity",LNIdentity);
})();

View File

@ -32,7 +32,7 @@ function tooltipHide(ev,tooltip){
function tooltipMouseOver(ev){ function tooltipMouseOver(ev){
if (!document.body.contains(tooltipEl)){ if (!document.body.contains(tooltipEl)){
document.body.appendChild(tooltipEl); document.body.appendChild(tooltipEl);
LNVue.onidle(()=>{ tooltipMouseOver(ev);}); LN.$idle(()=>{ tooltipMouseOver(ev);});
return; return;
} }
@ -92,9 +92,14 @@ Vue.directive('tooltip',{
Vue.component('ln-statusbar',{ Vue.component('ln-statusbar',{
data: function(){
return {
current: LN.Promise.getCurrentPromises(),
};
},
computed: { computed: {
CurrentPromises: function(){ CurrentPromises: function(){
return LNVue.$_.getCurrentPromises(); return this.current;
} }
}, },
template: ` template: `
@ -118,6 +123,7 @@ Vue.component('ln-statusbar',{
v-if="Object.keys(CurrentPromises).length" v-if="Object.keys(CurrentPromises).length"
> >
<div <div
class="block"
v-for="promise in CurrentPromises" v-for="promise in CurrentPromises"
:state="promise.state" :state="promise.state"
>{{ promise.label }}</div> >{{ promise.label }}</div>
@ -487,7 +493,7 @@ Vue.component('ln-select',{
prepared: { prepared: {
get: function(){ get: function(){
let prepared = {}; let prepared = {};
LNVue.each(this.items,(element,index) => { LN.$each(this.items,(element,index) => {
prepared[index] = element; prepared[index] = element;
}); });
return prepared; return prepared;
@ -559,6 +565,7 @@ Vue.component('ln-identity',{
</div> </div>
<div <div
v-if="!LNVue.$_.identity.UniqueID" v-if="!LNVue.$_.identity.UniqueID"
is="ln-login-pane"
> >
</div> </div>
</div> </div>
@ -594,14 +601,14 @@ Vue.component('ln-login-pane',{
crypto.subtle.digest("SHA-256",secretSource) crypto.subtle.digest("SHA-256",secretSource)
.then((secret)=>{ .then((secret)=>{
let challengebytes = LNVue.decodeB64(challenge.Challenge); let challengebytes = LN.Base64.decode(challenge.Challenge);
secret = new Uint8Array(secret); secret = new Uint8Array(secret);
let proveSource = ArrayBuffer.combine(challengebytes, secret, challengebytes); let proveSource = ArrayBuffer.combine(challengebytes, secret, challengebytes);
crypto.subtle.digest("SHA-256",proveSource) crypto.subtle.digest("SHA-256",proveSource)
.then((prove)=>{ .then((prove)=>{
prove = LNVue.encodeB64(new Uint8Array(prove)); prove = LN.Base64.encode(new Uint8Array(prove));
LNVue.$_.authenticate(this.identityName,challenge.SecureAttributeID,challenge.Challenge,prove); LNVue.$_.authenticate(this.identityName,challenge.SecureAttributeID,challenge.Challenge,prove);
}); });
}); });

View File

@ -1,11 +1,100 @@
var LNVue = (function (){ (function(globals){
let _unique = new Date().getTime(); let _unique = new Date().getTime();
function uniqueID(){ let __idles__ = [];
return _unique++;
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 = {};
let currentPromises = {}; LN.$add("LN", LN);
})(window);
(function(){
/*\ /*\
|*| |*|
|*| Base64 / binary data / UTF-8 strings utilities (#1) |*| Base64 / binary data / UTF-8 strings utilities (#1)
@ -14,112 +103,101 @@
|*| |*|
|*| Author: madmurphy |*| Author: madmurphy
|*| |*|
|*| Harald Wolff-Thobaben: Small adaptions to create a static class
|*|
\*/ \*/
class Base64
/* Array of bytes to base64 string decoding */
function 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;
}
function base64DecToArr (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 |= 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;
}
/* Base64 string to array encoding */
function uint6ToB64 (nUint6) {
return nUint6 < 26 ?
nUint6 + 65
: nUint6 < 52 ?
nUint6 + 71
: nUint6 < 62 ?
nUint6 - 4
: nUint6 === 62 ?
43
: nUint6 === 63 ?
47
:
65;
}
function base64EncArr (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;
/* Uncomment the following line in order to split the output in lines 76-character long: */
/*
if (nIdx > 0 && (nIdx * 4 / 3) % 76 === 0) { sB64Enc += "\r\n"; }
*/
nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24);
if (nMod3 === 2 || aBytes.length - nIdx === 1) {
sB64Enc += String.fromCharCode(uint6ToB64(nUint24 >>> 18 & 63), uint6ToB64(nUint24 >>> 12 & 63), uint6ToB64(nUint24 >>> 6 & 63), uint6ToB64(nUint24 & 63));
nUint24 = 0;
}
}
return eqLen === 0 ?
sB64Enc
:
sB64Enc.substring(0, sB64Enc.length - eqLen) + (eqLen === 1 ? "=" : "==");
}
class LNVuePromise
{ {
constructor(label,executor){ 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( this.promise = new Promise(
(resolve,reject) => { (resolve,reject) => {
executor( this.resolve = (v) => {
(v) => { this._s.state = "ready";
this._s.state = "ready"; resolve(v);
resolve(v); this.release();
this.release(); },
}, this.reject = (e) => {
(e) => { this._s.state = "failed";
this._s.state = "failed"; reject(e);
reject(e); this.release();
this.release(); }
} executor && executor(
this.resolve,
this.reject
); );
} }
); );
@ -132,9 +210,8 @@
state: "waiting" state: "waiting"
}; };
this.idx = uniqueID(); this.idx = LN.$unique();
LNPromise.$current[this.idx] = this._s;
currentPromises[this.idx] = this._s;
} }
label(){ label(){
@ -147,15 +224,22 @@
release(){ release(){
setTimeout(()=>{ setTimeout(()=>{
Vue.delete(currentPromises, this.idx); Vue.delete(LNPromise.$current, this.idx);
},1000); },1000);
} }
then(){ then(){
return this.promise.then.apply(this.promise, arguments); return this.promise.then.apply(this.promise, arguments);
} }
}
static getCurrentPromises(){ return LNPromise.$current; }
}
LNPromise.$current = {};
LN.$add("LN.Promise",LNPromise);
})();
(function(){
class LNVueWebSocket class LNVueWebSocket
{ {
constructor(lnvue,o){ constructor(lnvue,o){
@ -205,6 +289,7 @@
.then((wsh)=>{ .then((wsh)=>{
console.log("WSHello response",wsh); console.log("WSHello response",wsh);
this.LNVue.sessionID(wsh.message.ApplicationSessionID); 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.onclose = (e)=>{ this._onclose(e); this._state = "OFFLINE"; };
@ -289,7 +374,10 @@
} }
} }
LN.$add("LN.Vue.WebSocket",LNVueWebSocket);
})();
(function (){
class LNVue class LNVue
{ {
constructor(el,options = {}){ constructor(el,options = {}){
@ -302,20 +390,13 @@
this.data = Object.assign({}, options.data, { LNVue: this, msg: "Hello World" }); this.data = Object.assign({}, options.data, { LNVue: this, msg: "Hello World" });
this.promises = []; this.promises = [];
this.globals = {
currentPromises,
};
this.statusText = "LNVue preparing"; this.statusText = "LNVue preparing";
Vue.prototype.$LNVue = this; Vue.prototype.$LNVue = this;
LNVue.$_ = this; LNVue.$_ = this;
this.navigation = {}; this.navigation = {};
this.identity = { this.identity = new LN.Identity();
UniqueID: null,
IdentityName: "",
};
Promise Promise
.all(LNVue.promises) .all(LNVue.promises)
@ -335,14 +416,14 @@
this.vue = null; this.vue = null;
ln_ip_resolve(this); LNVue.$instance.resolve(this);
} }
Start(){ Start(){
Promise Promise
.all(this.promises) .all(this.promises)
.then(()=>{ .then(()=>{
LNVue.onidle(()=>{ LN.$idle(()=>{
this.vue = new Vue({ this.vue = new Vue({
el: this._el, el: this._el,
data: this.data, data: this.data,
@ -350,12 +431,12 @@
}); });
}); });
}); });
LNVue.onidle(()=>{ LN.$idle(()=>{
this.socket = new LNVueWebSocket(this); this.socket = new LN.Vue.WebSocket(this);
this.socket.open(); this.socket.open();
}); });
LNVue.onidle(()=>{ LN.$idle(()=>{
ln_sp_resolve(this); LNVue.$start.resolve(this);
}); });
} }
@ -382,7 +463,7 @@
Version(){ return "0.2alpha"; }; Version(){ return "0.2alpha"; };
getCurrentPromises() { getCurrentPromises() {
return this.globals.currentPromises; return LN.Promise.getCurrentPromises();
} }
status(){ status(){
@ -400,12 +481,11 @@
LNVue.deepAssign(modSpec.navigation,this.navigation); LNVue.deepAssign(modSpec.navigation,this.navigation);
} }
LNVue.$each(modSpec.routes,(key,route)=>{ LN.$each(modSpec.routes,(key,route)=>{
if ((route instanceof Object) && route.url) if ((route instanceof Object) && route.url)
{ {
let p = LNVue.LNPromise( "addModule()", (resolve,reject)=>{ let p = new LN.Promise((resolve,reject)=>{
LNVue LN.$fetch(route.url)
.fetch(route.url)
.then((src)=>{ .then((src)=>{
this.addRoute(key,{ template: src, data: ()=>{ return this.data; }, }); this.addRoute(key,{ template: src, data: ()=>{ return this.data; }, });
resolve(); resolve();
@ -413,7 +493,7 @@
(cause)=>{ (cause)=>{
console.log("loading route.url failed: ",cause); console.log("loading route.url failed: ",cause);
}); });
}); },`addModule(${route.url})`);
this.promises.push(p); this.promises.push(p);
} else if (route instanceof Object){ } else if (route instanceof Object){
this.addRoute(key,{ template: route.template, data: ()=>{ return this.data; }, } ); this.addRoute(key,{ template: route.template, data: ()=>{ return this.data; }, } );
@ -427,7 +507,7 @@
LNVue.vueRouter.addRoutes([ LNVue.vueRouter.addRoutes([
{ path, component, }, { path, component, },
]); ]);
console.log("forced update");
if (this.vue){ if (this.vue){
let route = this.vue.$route; let route = this.vue.$route;
LNVue.vueRouter.replace("/"); LNVue.vueRouter.replace("/");
@ -460,30 +540,32 @@
Challenge: challenge, Challenge: challenge,
Prove: prove, Prove: prove,
}; };
console.log(authenticationProve);
this.socket.request("AuthenticationProve", authenticationProve) this.socket.request("AuthenticationProve", authenticationProve)
.then((identity)=>{ .then((identity)=>{
this.identity = identity.message; this.identity = new LN.Identity(identity.message);
}, },
(error)=>{ (error)=>{
this.identity = { this.identity = new LN.Identity();
IdentityName: "",
UniqueID: null,
}
console.log("auth error",error);
}); });
} }
rpc(moduleName,methodName,parameters){ rpc(moduleName,methodName,parameters){
return new Promise((resolve,reject)=>{ return new Promise((resolve,reject)=>{
this.socket.request("RPCCall",{ let rpcCall = {
module: moduleName, module: moduleName,
method: methodName, method: methodName,
parameters: parameters parameters: parameters
}) };
this.socket.request("RPCCall",rpcCall)
.then( .then(
(result)=>{ (result)=>{
resolve(result.message.Result); if (result.message.error)
{
console.log("rpc call failed", result.message.error);
reject(result.message.error);
}
else
resolve(result.message.Result);
}, },
(error)=>{ (error)=>{
console.log("rpc failed", error); console.log("rpc failed", error);
@ -493,78 +575,22 @@
}); });
} }
static $LNVue(){
return LNVue.$_;
}
} }
LNVue.$instance = new LN.Promise(()=>{},'LN.Vue Instance Promise');
LNVue.$start = new LN.Promise(()=>{},'LN Vue Startup Promise');
Object.defineProperty( LNVue, '$LNVue', {
get: ()=>{ return LNVue.$_; },
});
ln_ip_resolve = null;
ln_ip_reject = null;
LNVue.instance = new Promise((resolve,reject)=>{
ln_ip_resolve = resolve;
ln_ip_reject = reject;
});
ln_sp_resolve = null;
ln_sp_reject = null;
LNVue.started = new Promise((resolve,reject)=>{
ln_sp_resolve = resolve;
ln_sp_reject = reject;
});
LNVue.vueRouter = new VueRouter({ LNVue.vueRouter = new VueRouter({
mode: 'history', mode: 'history',
routes: [], routes: [],
}); });
LNVue.$idles = [];
LNVue.onidle = function(cb,thisval = null){
let scheduled = LNVue.$idles.length > 0;
let n=0;
for (;n<LNVue.$idles.length;n++){
let idle = LNVue.$idles[n];
if ((idle[0] == cb) && (idle[1] == thisval))
break;
}
if (n == LNVue.$idles.length)
LNVue.$idles.push([cb,thisval]);
if (!scheduled)
setTimeout(()=>{
while (LNVue.$idles.length > 0){
let idle = LNVue.$idles.pop();
idle[0].call(idle[1]);
}
},0);
}
LNVue.$each = function(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;
}
}
LNVue.deepAssign = function(source,target){ LNVue.deepAssign = function(source,target){
LNVue.$each(source,function(key){ LN.$each(source,function(key){
if (target[key] instanceof Object){ if (target[key] instanceof Object){
LNVue.deepAssign(src[key],target[key]); LNVue.deepAssign(src[key],target[key]);
} else { } else {
@ -582,41 +608,6 @@
LNVue.routes = []; LNVue.routes = [];
LNVue.promises = []; LNVue.promises = [];
LNVue.fetch = function(url,cb){
return LNVue.LNPromise("loading",(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);
}
}));
});
}
LNVue.each = function(o,cb){
if (o instanceof Array)
o.forEach((value,index)=> cb(value,index) );
else
Object.keys(o).forEach((key)=>{
if (o.hasOwnProperty(key))
cb(o[key],key);
});
};
LNVue.LNPromise = function(label, action){
console.log(label,action);
return new LNVuePromise(label, action);
}
LNVue.encodeB64 = base64EncArr;
LNVue.decodeB64 = base64DecToArr;
LNVue.encodeHex = (bytes) => bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), ''); 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))); LNVue.decodeHex = (hexString) => new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
@ -638,8 +629,6 @@
return result.buffer return result.buffer
}; };
LNVue.prototypes = {}; LN.$add("LN.Vue",LNVue);
LNVue.prototypes.LNVuePromise = LNVuePromise; LN.$add("LNVue",LNVue);
return LNVue;
})(); })();