Created
May 31, 2017 05:57
-
-
Save jcmoore/6dc1490f81ac7385a64cc56388352f0e to your computer and use it in GitHub Desktop.
uidget.js
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
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