Skip to content

Instantly share code, notes, and snippets.

@jcmoore
Created May 31, 2017 05:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcmoore/6dc1490f81ac7385a64cc56388352f0e to your computer and use it in GitHub Desktop.
Save jcmoore/6dc1490f81ac7385a64cc56388352f0e to your computer and use it in GitHub Desktop.
uidget.js
var Ui = (function (s8) {
var Uidget = s8("Uidget", {
uiml: Error(),
uimlUpdate: Error(),
uimlAssemble: Error(),
uimlProcess: Error(),
precedingSlashCount: Error(),
nextSignificantToken: Error(),
terminateSequence: Error(),
syntaxErrMsg: Error(),
parseFragment: Error(),
parseElement: Error(),
parseKVPs: Error(),
parseText: Error(),
parseRaw: Error(),
parseQuantity: Error(),
getContext: Error(),
modelCache: Error(),
youngState: Error(),
instances: Error(),
render: Error(),
});
Uidget[Uidget.uiml] = function (texts) {
var cache = this[Uidget.modelCache];
var state = this[Uidget.youngState];
var instances = this[Uidget.instances];
var model = cache.get(texts) || cache.set(texts, {
template: [],
}).get(texts);
var value = instances.get(texts) || instances.set(texts, {
nodes: [],
updaters: [],
oldStates: [],
}).get(texts);
var fns = value.updaters;
var limit = arguments.length - 1;
var at = 0;
if (!model.template.length) Uidget[Uidget.uimlProcess](model, texts);
if (!value.nodes.length) {
for (at = 0; at < limit; at++) value.oldStates[at] = state[arguments[at + 1]];
Uidget[Uidget.uimlAssemble](model, value);
} else for (at; at < limit; at++) {
Uidget[Uidget.uimlUpdate](fns[at], value.oldStates[at], arguments[at + 1], state, value);
}
for (at = 0; at < limit; at++) value.oldStates[at] = state[arguments[at + 1]];
return this;
};
Uidget[Uidget.precedingSlashCount] = function (literal, offset) {
var index = 0|offset;
var count = 0;
while (index-- > 0 && literal.charCodeAt(index) === 92) ; // \
return count;
};
Uidget[Uidget.advanceLexer] = function (lexer) {
if (++lexer.at < lexer.texts.length) lexer.literal = lexer.texts[lexer.at];
else lexer.at = -1;
return 0|lexer.at;
};
Uidget[Uidget.nextSignificantToken] = function (filter, lexer) {
var at = 0;
var size = filter.length;
var literal = lexer.literal;
var count = literal.length;
var index = 0|lexer.offset;
var match = 0;
for (index; index < count; index++) switch (match = literal.charCodeAt(index)) {
case 34: // ""
case 39: // ''
case 44: // ,
case 58: // :
case 91: // [
case 93: // ]
case 123: // {
case 125: // }
for (at; at < size; at++) {
if (match === filter.charCodeAt(at)) {
lexer.offset = index + 1;
return 0|match;
}
}
literal = JSON.stringify(String.fromCharCode(match));
throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml unexected token ("+literal+")", lexer));
default:
literal = JSON.stringify(String.fromCharCode(match));
throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml unexected token ("+literal+")", lexer));
case 9: // \t
case 10: // \n
case 13: // \r
case 32: //
continue;
}
lexer.offset = 0;
return -1;
};
Uidget[Uidget.terminateSequence] = function (code, terminator, lexer) {
switch (code) {
case 125: if ("}" === terminator) return 0|true;
case 93: if ("]" === terminator) return 0|true;
case 44: return 0|false; // ,
}
throw new Error("uiml unhandled sequence token ("+JSON.stringify(String.fromCharCode(code))+")");
};
Uidget[Uidget.syntaxErrMsg] = function (prefix, lexer) {
return prefix + "\n[" + (0|lexer.at) + " , " + (0|lexer.offset) + "]\n" + "`" + lexer.texts.join("${}") + "`";
};
Uidget[Uidget.parseQuantity] = function (lexer) {
var literal = lexer.literal;
var amount = literal.length;
var start = 0|lexer.offset;
var end = start;
while (end < amount) switch (literal.charCodeAt(end)) {
default: break;
case 46: // .
case 48: // 0
case 49: // 1
case 50: // 2
case 51: // 3
case 52: // 4
case 53: // 5
case 54: // 6
case 55: // 7
case 56: // 8
case 57: // 9
case 66: // B
case 98: // b
case 88: // X
case 120: // x
case 69: // E
case 101: // e
end += 1;
}
lexer.offset = end;
return Number(literal.slice(start - 1, end));
};
Uidget[Uidget.parseRaw] = function (match, literal, offset, lexer) {
var at = 0|lexer.offset;
var literal = lexer.literal;
var count = literal.length - at;
var index = 0;
if (count < match.length) return 0|false;
else if (count = match.length) {
while (count > 0) {
count -= 1;
if (match.charCodeAt(index++) !== literal.charCodeAt(at++)) return 0|false;
}
return 0|(count === 0);
} else return 0|true;
};
Uidget[Uidget.parseText] = function (terminator, lexer) {
var literal = lexer.literal;
var start = 0|lexer.offset;
var end = -1 + start;
do {
end = literal.indexOf(terminator, end + 1);
if (end < 0) {
throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml interrupted/unterminated text literal", lexer));
} else if (0 === Uidget[Uidget.precedingSlashCount](literal, end) % 2) break;
} while (end > 0)
lexer.offset = end + 1;
return literal.slice(start, end);
};
Uidget[Uidget.parseKVPs] = function (kvps, lexer) {
var code = -1;
var num = 0;
do {
code = 0|Uidget[Uidget.nextSignificantToken]("}\"\'[", lexer);
if (code < 0) {
if (0 > Uidget[Uidget.advanceLexer](lexer)) {
throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml terminated mid attrs", lexer));
} else {
code = 0|Uidget[Uidget.nextSignificantToken]("},:", lexer);
if (code < 0) throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml malformed attr", lexer));
else switch (code) {
default:
throw new Error("uiml unhandled filter ("+JSON.stringify(String.fromCharCode(code))+")");
case 125: // }
kvps.push(void 0);
return kvps;
case 58: // :
kvps.push([void 0]);
break;
case 44: // ,
kvps.push(void 0);
break;
}
if (44 === code) continue; // ,
}
} else switch (code) {
default:
throw new Error("uiml unhandled filter ("+JSON.stringify(String.fromCharCode(code))+")");
case 125: // }
return kvps;
case 39: // ''
kvps.push([ Uidget[Uidget.parseText]("\'", lexer) ]);
break;
case 34: // ""
kvps.push([ Uidget[Uidget.parseText]("\"", lexer) ]);
break;
}
if (58 !== code) { // :
code = 0|Uidget[Uidget.nextSignificantToken](":", lexer);
if (code < 0) {
throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml attr separator missing (\':\')", lexer));
} else if (58 !== code) { // :
throw new Error("uiml unhandled filter ("+JSON.stringify(String.fromCharCode(code))+")");
}
}
code = 0|Uidget[Uidget.nextSignificantToken]("\"\'", lexer);
if (code < 0) {
if (0 > Uidget[Uidget.advanceLexer](lexer)) {
throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml terminated mid attrs", lexer));
} else kvps[kvps.length - 1].push(void 0);
} else switch (code) {
default:
throw new Error("uiml unhandled filter ("+JSON.stringify(String.fromCharCode(code))+")");
case 39: // ''
kvps[kvps.length - 1].push(Uidget[Uidget.parseText]("\'", lexer));
break;
case 34: // ""
kvps[kvps.length - 1].push(Uidget[Uidget.parseText]("\"", lexer));
break;
case 102: // f
if (Uidget[Uidget.parseRaw]("alse", lexer)) {
kvps[kvps.length - 1].push(false);
lexer.offset += 5;
} else throw new Error("uiml malformed keyword (\'false\')");
case 116: // t
if (Uidget[Uidget.parseRaw]("rue", lexer)) {
kvps[kvps.length - 1].push(true);
lexer.offset += 4;
} else throw new Error("uiml malformed keyword (\'true\')");
case 110: // n
if (Uidget[Uidget.parseRaw]("ull", lexer)) {
kvps[kvps.length - 1].push(null);
lexer.offset += 4;
} else throw new Error("uiml malformed keyword (\'null\')");
break;
case 46: // .
case 48: // 0
case 49: // 1
case 50: // 2
case 51: // 3
case 52: // 4
case 53: // 5
case 54: // 6
case 55: // 7
case 56: // 8
case 57: // 9
if (!isNaN(num = Uidget[Uidget.parseQuantity](lexer))) kvps[kvps.length - 1].push(num);
else throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml malformed numeric attr", lexer));
break;
}
code = 0|Uidget[Uidget.nextSignificantToken]("},", lexer);
if (code < 0) {
throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml attrs missing delimiter (\',\')", lexer));
} else if (Uidget[Uidget.terminateSequence](code, "}", lexer)) return kvps;
} while (lexer.offset > 0)
throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml exception in attrs", lexer));
};
Uidget[Uidget.parseElement] = function (lexer) {
var tuple = ["", null, null, ""];
var code = -1;
code = 0|Uidget[Uidget.nextSignificantToken]("\"\'", lexer);
if (code < 0) throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml element interrupted before <tag>", lexer));
else switch (code) {
default:
throw new Error("uiml unhandled filter ("+JSON.stringify(String.fromCharCode(code))+")");
case 39: // ''
tuple[0] = Uidget[Uidget.parseText]("\'", lexer);
break;
case 34: // ""
tuple[0] = Uidget[Uidget.parseText]("\"", lexer);
break;
}
code = 0|Uidget[Uidget.nextSignificantToken]("],", lexer);
if (code < 0) throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml element interrupted before attrs", lexer));
else if (Uidget[Uidget.terminateSequence](code, "]", lexer)) return tuple;
code = 0|Uidget[Uidget.nextSignificantToken]("]{", lexer);
if (code < 0) {
if (0 < Uidget[Uidget.advanceLexer](lexer)) tuple[1] = void 0;
else throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml terminated mid fragment", lexer));
} else switch (code) {
default:
throw new Error("uiml unhandled filter ("+JSON.stringify(String.fromCharCode(code))+")");
case 93: // ]
return tuple;
case 123: // {
tuple[1] = Uidget[Uidget.parseKVPs]([], lexer);
break;
}
code = 0|Uidget[Uidget.nextSignificantToken]("],", lexer);
if (code < 0) throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml element interrupted before children", lexer));
else if (Uidget[Uidget.terminateSequence](code, "]", lexer)) return tuple;
code = 0|Uidget[Uidget.nextSignificantToken]("]\"\'[", lexer);
if (code < 0) {
if (0 < Uidget[Uidget.advanceLexer](lexer)) tuple[2] = void 0;
else throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml terminated mid fragment", lexer));
} else switch (code) {
default:
throw new Error("uiml unhandled filter ("+JSON.stringify(String.fromCharCode(code))+")");
case 93: // ]
return tuple;
case 91: // [
tuple[2] = Uidget[Uidget.parseFragment]([], lexer);
break;
case 39: // ''
tuple[2] = [ Uidget[Uidget.parseText]("\'", lexer) ];
break;
case 34: // ""
tuple[2] = [ Uidget[Uidget.parseText]("\"", lexer) ];
break;
}
code = 0|Uidget[Uidget.nextSignificantToken]("],", lexer);
if (code < 0) throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml element interrupted before </tag>", lexer));
else if (Uidget[Uidget.terminateSequence](code, "]", lexer)) return tuple;
code = 0|Uidget[Uidget.nextSignificantToken]("]\"\'", lexer);
if (code < 0) throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml element interrupted before </tag>", lexer));
else switch (code) {
default:
throw new Error("uiml unhandled filter ("+JSON.stringify(String.fromCharCode(code))+")");
case 93: // ]
return tuple;
case 39: // ''
tuple[3] = Uidget[Uidget.parseText]("\'", lexer);
break;
case 34: // ""
tuple[3] = Uidget[Uidget.parseText]("\"", lexer);
break;
}
code = 0|Uidget[Uidget.nextSignificantToken]("],", lexer);
if (code < 0) throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml element interrupted before attrs", lexer));
else if (Uidget[Uidget.terminateSequence](code, "]", lexer)) return tuple;
throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml exception in element", lexer));
};
Uidget[Uidget.parseFragment] = function (fragment, lexer) {
var code = -1;
do {
code = 0|Uidget[Uidget.nextSignificantToken]("]\"\'[", lexer);
if (code < 0) {
if (0 < Uidget[Uidget.advanceLexer](lexer)) fragment.push(void 0);
else throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml terminated mid fragment", lexer));
} else switch (code) {
default:
throw new Error("uiml unhandled filter ("+JSON.stringify(String.fromCharCode(code))+")");
case 93: // ]
return fragment;
case 91: // [
fragment.push(Uidget[Uidget.parseElement](lexer));
break;
case 39: // ''
fragment.push(Uidget[Uidget.parseText]("\'", lexer));
break;
case 34: // ""
fragment.push(Uidget[Uidget.parseText]("\"", lexer));
break;
}
code = 0|Uidget[Uidget.nextSignificantToken]("],", lexer);
if (code < 0) {
throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml fragment missing delimiter (\',\')", lexer));
} else if (Uidget[Uidget.terminateSequence](code, "]", lexer)) return fragment;
} while (lexer.offset > 0)
throw new Error(Uidget[Uidget.syntaxErrMsg]("uiml exception in fragment", lexer));
};
Uidget[Uidget.uimlProcess] = function (model, texts) {
var literal = texts[0];
var offset = literal.indexOf("[") + 1;
if (offset < 0) throw new Error("uiml did not start with a fragment literal (\'[\')");
Uidget[Uidget.parseFragment](model.template, {
at: 0,
offset: offset,
literal: literal,
texts: texts,
});
};
Uidget[Uidget.uimlAssemble] = function (model, value) {
var count = value.oldStates.length;
var index = 0;
for (index; index < count; index++) value.updaters.push(function TODO () {
});
};
Uidget[Uidget.uimlUpdate] = function (processor, before, key, state, value) {
var after = state[key];
if (after !== before && (!isNaN(before) || !isNaN(after))) {
if ("function" === typeof after) {
processor(after(), "function" === typeof before ? before() : before, key, value);
} else processor(after, "function" === typeof before ? before() : before, key, value);
}
};
Uidget[Uidget.getContext] = function (modelCache, initial, doc) {
var ctx = (doc || document).createElement("template");
ctx[Uidget.uiml] = Uidget[Uidget.uiml];
ctx[Uidget.modelCache] = modelCache;
ctx[Uidget.youngState] = Object.assign({}, initial || {});
ctx[Uidget.instances] = new Map();
return ctx;
};
Uidget[Uidget.modelCache] = new Map();
Uidget[Uidget.render] = function (reuse, overrides, doc) {
var ctx = reuse || Uidget[Uidget.getContext](Uidget[Uidget.modelCache], {}, doc || void 0);
Object.assign(ctx[Uidget.youngState], overrides);
return ctx[Uidget.uiml]`
[['div', {
'key': 'value',
'attribute': 'pairs',
}, [
'textNode',
['span', {}, ['child'], 'span'],
], 'div']]
`;
};
return {
Uidget: Object.assign(Object.create(null), Uidget),
};
}(sigilate)); // https://gist.github.com/jcmoore/6694730af52717b6ee4c5f98738219df#file-sigilate-js
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment