hyperscript.org
Version:
a small scripting language for the web
1,148 lines (1,038 loc) • 142 kB
JavaScript
//AMD insanity
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
} else {
// Browser globals
root._hyperscript = factory();
}
}(typeof self !== 'undefined' ? self : this, function () {
return (function () {
'use strict';
//====================================================================
// Utilities
//====================================================================
function mergeObjects(obj1, obj2) {
for (var key in obj2) {
if (obj2.hasOwnProperty(key)) {
obj1[key] = obj2[key];
}
}
return obj1;
}
function parseJSON(jString) {
try {
return JSON.parse(jString);
} catch(error) {
logError(error);
return null;
}
}
function logError(msg) {
if(console.error) {
console.error(msg);
} else if (console.log) {
console.log("ERROR: ", msg);
}
}
// https://stackoverflow.com/a/8843181
function varargConstructor(Cls, args) {
return new (Cls.bind.apply(Cls, [Cls].concat(args)));
}
var globalScope = typeof self !== 'undefined' ? self : typeof global !== 'undefined' ? global : this;
//====================================================================
// Lexer
//====================================================================
var _lexer = function () {
var OP_TABLE = {
'+': 'PLUS',
'-': 'MINUS',
'*': 'MULTIPLY',
'/': 'DIVIDE',
'.': 'PERIOD',
'\\': 'BACKSLASH',
':': 'COLON',
'%': 'PERCENT',
'|': 'PIPE',
'!': 'EXCLAMATION',
'?': 'QUESTION',
'#': 'POUND',
'&': 'AMPERSAND',
';': 'SEMI',
',': 'COMMA',
'(': 'L_PAREN',
')': 'R_PAREN',
'<': 'L_ANG',
'>': 'R_ANG',
'<=': 'LTE_ANG',
'>=': 'GTE_ANG',
'==': 'EQ',
'===': 'EQQ',
'!=': 'NEQ',
'!==': 'NEQQ',
'{': 'L_BRACE',
'}': 'R_BRACE',
'[': 'L_BRACKET',
']': 'R_BRACKET',
'=': 'EQUALS'
};
function isValidCSSClassChar(c) {
return isAlpha(c) || isNumeric(c) || c === "-" || c === "_";
}
function isValidCSSIDChar(c) {
return isAlpha(c) || isNumeric(c) || c === "-" || c === "_" || c === ":";
}
function isWhitespace(c) {
return c === " " || c === "\t" || isNewline(c);
}
function positionString(token) {
return "[Line: " + token.line + ", Column: " + token.col + "]"
}
function isNewline(c) {
return c === '\r' || c === '\n';
}
function isNumeric(c) {
return c >= '0' && c <= '9';
}
function isAlpha(c) {
return (c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z');
}
function isIdentifierChar(c) {
return (c === "_" || c === "$");
}
function isReservedChar(c) {
return (c === "`" || c === "^");
}
function makeTokensObject(tokens, consumed, source) {
var ignoreWhiteSpace = true;
matchTokenType("WHITESPACE"); // consume any initial whitespace
function raiseError(tokens, error) {
_parser.raiseParseError(tokens, error);
}
function requireOpToken(value) {
var token = matchOpToken(value);
if (token) {
return token;
} else {
raiseError(this, "Expected '" + value + "' but found '" + currentToken().value + "'");
}
}
function matchAnyOpToken(op1, op2, op3) {
for (var i = 0; i < arguments.length; i++) {
var opToken = arguments[i];
var match = matchOpToken(opToken);
if (match) {
return match;
}
}
}
function matchOpToken(value) {
if (currentToken() && currentToken().op && currentToken().value === value) {
return consumeToken();
}
}
function requireTokenType(type1, type2, type3, type4) {
var token = matchTokenType(type1, type2, type3, type4);
if (token) {
return token;
} else {
raiseError(this, "Expected one of " + JSON.stringify([type1, type2, type3]));
}
}
function matchTokenType(type1, type2, type3, type4) {
if (currentToken() && currentToken().type && [type1, type2, type3, type4].indexOf(currentToken().type) >= 0) {
return consumeToken();
}
}
function requireToken(value, type) {
var token = matchToken(value, type);
if (token) {
return token;
} else {
raiseError(this, "Expected '" + value + "' but found '" + currentToken().value + "'");
}
}
function matchToken(value, type) {
var type = type || "IDENTIFIER";
if (currentToken() && currentToken().value === value && currentToken().type === type) {
return consumeToken();
}
}
function consumeToken() {
var match = tokens.shift();
consumed.push(match);
if(ignoreWhiteSpace) {
matchTokenType("WHITESPACE"); // consume any whitespace until the next token
}
return match;
}
function consumeUntilWhitespace() {
var tokenList = [];
ignoreWhiteSpace = false;
while (currentToken() &&
currentToken().type !== "WHITESPACE" &&
currentToken().type !== "EOF") {
tokenList.push(consumeToken());
}
ignoreWhiteSpace = true;
return tokenList;
}
function hasMore() {
return tokens.length > 0;
}
function currentToken(ignoreWhiteSpace) {
var token;
if (ignoreWhiteSpace) {
var i = 0;
do {
token = tokens[i++]
} while (token && token.type === "WHITESPACE");
} else {
token = tokens[0];
}
if (token) {
return token;
} else {
return {
type:"EOF"
}
}
}
return {
matchAnyOpToken: matchAnyOpToken,
matchOpToken: matchOpToken,
requireOpToken: requireOpToken,
matchTokenType: matchTokenType,
requireTokenType: requireTokenType,
consumeToken: consumeToken,
matchToken: matchToken,
requireToken: requireToken,
list: tokens,
consumed: consumed,
source: source,
hasMore: hasMore,
currentToken: currentToken,
consumeUntilWhitespace: consumeUntilWhitespace
}
}
function tokenize(string) {
var source = string;
var tokens = [];
var position = 0;
var column = 0;
var line = 1;
var lastToken = "<START>";
while (position < source.length) {
if (currentChar() === "-" && nextChar() === "-") {
consumeComment();
} else {
if (isWhitespace(currentChar())) {
tokens.push(consumeWhitespace());
} else if (!possiblePrecedingSymbol() && currentChar() === "." && isAlpha(nextChar())) {
tokens.push(consumeClassReference());
} else if (!possiblePrecedingSymbol() && currentChar() === "#" && isAlpha(nextChar())) {
tokens.push(consumeIdReference());
} else if (isAlpha(currentChar()) || isIdentifierChar(currentChar())) {
tokens.push(consumeIdentifier());
} else if (isNumeric(currentChar())) {
tokens.push(consumeNumber());
} else if (currentChar() === '"' || currentChar() === "'") {
tokens.push(consumeString());
} else if (OP_TABLE[currentChar()]) {
tokens.push(consumeOp());
} else if (isReservedChar(currentChar())) {
tokens.push(makeToken('RESERVED', currentChar))
} else {
if (position < source.length) {
throw Error("Unknown token: " + currentChar() + " ");
}
}
}
}
return makeTokensObject(tokens, [], source);
function makeOpToken(type, value) {
var token = makeToken(type, value);
token.op = true;
return token;
}
function makeToken(type, value) {
return {
type: type,
value: value,
start: position,
end: position + 1,
column: column,
line: line
};
}
function consumeComment() {
while (currentChar() && !isNewline(currentChar())) {
consumeChar();
}
consumeChar();
}
function consumeClassReference() {
var classRef = makeToken("CLASS_REF");
var value = consumeChar();
while (isValidCSSClassChar(currentChar())) {
value += consumeChar();
}
classRef.value = value;
classRef.end = position;
return classRef;
}
function consumeIdReference() {
var idRef = makeToken("ID_REF");
var value = consumeChar();
while (isValidCSSIDChar(currentChar())) {
value += consumeChar();
}
idRef.value = value;
idRef.end = position;
return idRef;
}
function consumeIdentifier() {
var identifier = makeToken("IDENTIFIER");
var value = consumeChar();
while (isAlpha(currentChar()) || isIdentifierChar(currentChar())) {
value += consumeChar();
}
identifier.value = value;
identifier.end = position;
return identifier;
}
function consumeNumber() {
var number = makeToken("NUMBER");
var value = consumeChar();
while (isNumeric(currentChar())) {
value += consumeChar();
}
if (currentChar() === ".") {
value += consumeChar();
}
while (isNumeric(currentChar())) {
value += consumeChar();
}
number.value = value;
number.end = position;
return number;
}
function consumeOp() {
var value = consumeChar(); // consume leading char
while (currentChar() && OP_TABLE[value + currentChar()]) {
value += consumeChar();
}
var op = makeOpToken(OP_TABLE[value], value);
op.value = value;
op.end = position;
return op;
}
function consumeString() {
var string = makeToken("STRING");
var startChar = consumeChar(); // consume leading quote
var value = "";
while (currentChar() && currentChar() !== startChar) {
if (currentChar() === "\\") {
consumeChar(); // consume escape char and move on
}
value += consumeChar();
}
if (currentChar() !== startChar) {
throw Error("Unterminated string at " + positionString(string));
} else {
consumeChar(); // consume final quote
}
string.value = value;
string.end = position;
return string;
}
function currentChar() {
return source.charAt(position);
}
function nextChar() {
return source.charAt(position + 1);
}
function consumeChar() {
lastToken = currentChar();
position++;
column++;
return lastToken;
}
function possiblePrecedingSymbol() {
return isAlpha(lastToken) || isNumeric(lastToken) || lastToken === ")" || lastToken === "}" || lastToken === "]"
}
function consumeWhitespace() {
var whitespace = makeToken("WHITESPACE");
var value = "";
while (currentChar() && isWhitespace(currentChar())) {
if (isNewline(currentChar())) {
column = 0;
line++;
}
value += consumeChar();
}
whitespace.value = value;
whitespace.end = position;
return whitespace;
}
}
return {
tokenize: tokenize,
makeTokensObject: makeTokensObject
}
}();
//====================================================================
// Parser
//====================================================================
var _parser = function () {
var GRAMMAR = {}
var COMMANDS = {}
var FEATURES = {}
var LEAF_EXPRESSIONS = [];
var INDIRECT_EXPRESSIONS = [];
function parseElement(type, tokens, root) {
var elementDefinition = GRAMMAR[type];
if (elementDefinition) return elementDefinition(_parser, _runtime, tokens, root);
}
function requireElement(type, tokens, message, root) {
var result = parseElement(type, tokens, root);
return result || raiseParseError(tokens, message || "Expected " + type.autocapitalize);
}
function parseAnyOf(types, tokens) {
for (var i = 0; i < types.length; i++) {
var type = types[i];
var expression = parseElement(type, tokens);
if (expression) {
return expression;
}
}
}
function addGrammarElement(name, definition) {
GRAMMAR[name] = definition;
}
function addCommand(keyword, definition) {
var commandGrammarType = keyword + "Command";
var commandDefinitionWrapper = function (parser, runtime, tokens) {
var commandElement = definition(parser, runtime, tokens);
if (commandElement) {
commandElement.type = commandGrammarType;
commandElement.execute = function (context) {
return runtime.unifiedExec(this, context);
}
return commandElement;
}
};
GRAMMAR[commandGrammarType] = commandDefinitionWrapper;
COMMANDS[keyword] = commandDefinitionWrapper;
}
function addFeature(keyword, definition) {
var featureGrammarType = keyword + "Feature";
var featureDefinitionWrapper = function (parser, runtime, tokens) {
var featureElement = definition(parser, runtime, tokens);
if (featureElement) {
featureElement.keyword = keyword;
featureElement.type = featureGrammarType;
return featureElement;
}
};
GRAMMAR[featureGrammarType] = featureDefinitionWrapper;
FEATURES[keyword] = featureDefinitionWrapper;
}
function addLeafExpression(name, definition) {
LEAF_EXPRESSIONS.push(name);
addGrammarElement(name, definition);
}
function addIndirectExpression(name, definition) {
INDIRECT_EXPRESSIONS.push(name);
addGrammarElement(name, definition);
}
/* ============================================================================================ */
/* Core hyperscript Grammar Elements */
/* ============================================================================================ */
addGrammarElement("feature", function(parser, runtime, tokens) {
var featureDefinition = FEATURES[tokens.currentToken().value];
if (featureDefinition) {
return featureDefinition(parser, runtime, tokens);
}
})
addGrammarElement("command", function(parser, runtime, tokens) {
var commandDefinition = COMMANDS[tokens.currentToken().value];
if (commandDefinition) {
return commandDefinition(parser, runtime, tokens);
}
})
addGrammarElement("commandList", function(parser, runtime, tokens) {
var cmd = parser.parseElement("command", tokens);
if (cmd) {
tokens.matchToken("then");
cmd.next = parser.parseElement("commandList", tokens);
return cmd;
}
})
addGrammarElement("leaf", function(parser, runtime, tokens) {
var result = parseAnyOf(LEAF_EXPRESSIONS, tokens);
// symbol is last so it doesn't consume any constants
if (result == null) {
return parseElement('symbol', tokens);
} else {
return result;
}
})
addGrammarElement("indirectExpression", function(parser, runtime, tokens, root) {
for (var i = 0; i < INDIRECT_EXPRESSIONS.length; i++) {
var indirect = INDIRECT_EXPRESSIONS[i];
var result = parser.parseElement(indirect, tokens, root);
if(result){
return result;
}
}
return root;
});
addGrammarElement("primaryExpression", function(parser, runtime, tokens) {
var leaf = parser.parseElement("leaf", tokens);
if (leaf) {
return parser.parseElement("indirectExpression", tokens, leaf);
}
parser.raiseParseError(tokens, "Unexpected value: " + tokens.currentToken().value);
});
/* ============================================================================================ */
/* END Core hyperscript Grammar Elements */
/* ============================================================================================ */
function createParserContext(tokens) {
var currentToken = tokens.currentToken();
var source = tokens.source;
var lines = source.split("\n");
var line = currentToken ? currentToken.line - 1 : lines.length - 1;
var contextLine = lines[line];
var offset = currentToken ? currentToken.column : contextLine.length - 1;
return contextLine + "\n" + " ".repeat(offset) + "^^\n\n";
}
function raiseParseError(tokens, message) {
message = (message || "Unexpected Token : " + tokens.currentToken().value) + "\n\n" +
createParserContext(tokens);
var error = new Error(message);
error.tokens = tokens;
throw error
}
function parseHyperScript(tokens) {
return parseElement("hyperscript", tokens)
}
function setParent(elt, parent) {
if (elt) {
elt.parent = parent;
setParent(elt.next, parent);
}
}
function commandBoundary(token) {
if (token.value == "end" || token.value == "then" || COMMANDS[token.value] || FEATURES[token.value] || token.type == "EOF") {
return true;
}
}
return {
// parser API
setParent: setParent,
requireElement: requireElement,
parseElement: parseElement,
commandBoundary: commandBoundary,
parseAnyOf: parseAnyOf,
parseHyperScript: parseHyperScript,
raiseParseError: raiseParseError,
addGrammarElement: addGrammarElement,
addCommand: addCommand,
addFeature: addFeature,
addLeafExpression: addLeafExpression,
addIndirectExpression: addIndirectExpression,
}
}();
//====================================================================
// Runtime
//====================================================================
var _runtime = function () {
function matchesSelector(elt, selector) {
// noinspection JSUnresolvedVariable
var matchesFunction = elt.matches ||
elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector
|| elt.webkitMatchesSelector || elt.oMatchesSelector;
return matchesFunction && matchesFunction.call(elt, selector);
}
function makeEvent(eventName, detail) {
var evt;
if (window.CustomEvent && typeof window.CustomEvent === 'function') {
evt = new CustomEvent(eventName, {bubbles: true, cancelable: true, detail: detail});
} else {
evt = document.createEvent('CustomEvent');
evt.initCustomEvent(eventName, true, true, detail);
}
return evt;
}
function triggerEvent(elt, eventName, detail) {
var detail = detail || {};
detail["sentBy"] = elt;
var event = makeEvent(eventName, detail);
var eventResult = elt.dispatchEvent(event);
return eventResult;
}
function isArrayLike(value) {
return Array.isArray(value) || value instanceof NodeList;
}
function forEach(value, func) {
if (value == null) {
// do nothing
} else if (isArrayLike(value)) {
for (var i = 0; i < value.length; i++) {
func(value[i]);
}
} else {
func(value);
}
}
var ARRAY_SENTINEL = {array_sentinel:true}
function linearize(args) {
var arr = [];
for (var i = 0; i < args.length; i++) {
var arg = args[i];
if (Array.isArray(arg)) {
arr.push(ARRAY_SENTINEL);
for (var j = 0; j < arg.length; j++) {
arr.push(arg[j]);
}
arr.push(ARRAY_SENTINEL);
} else {
arr.push(arg);
}
}
return arr;
}
function delinearize(values){
var arr = [];
for (var i = 0; i < values.length; i++) {
var value = values[i];
if (value === ARRAY_SENTINEL) {
value = values[++i];
var valueArray = [];
arr.push(valueArray);
while (value !== ARRAY_SENTINEL) {
valueArray.push(value);
value = values[++i];
}
} else {
arr.push(value);
}
}
return arr;
}
function unwrapAsyncs(values) {
for (var i = 0; i < values.length; i++) {
var value = values[i];
if (value.asyncWrapper) {
values[i] = value.value;
}
if (Array.isArray(value)) {
for (var j = 0; j < value.length; j++) {
var valueElement = value[j];
if (valueElement.asyncWrapper) {
value[j] = valueElement.value;
}
}
}
}
}
var HALT = {halt_flag:true};
function unifiedExec(command, ctx) {
while(true) {
var next = unifiedEval(command, ctx);
if (next == null) {
console.error(command, " did not return a next element to execute! context: " , ctx)
return;
} else if (next.then) {
next.then(function (resolvedNext) {
unifiedExec(resolvedNext, ctx);
}).catch(function(reason){
if (ctx.meta && ctx.meta.reject) {
ctx.meta.reject(reason);
} else {
// TODO: no meta context to reject with, trigger event?
}
});
return;
} else if (next === HALT) {
// done
return;
} else {
command = next; // move to the next command
}
}
}
function unifiedEval(parseElement, ctx) {
var async = false;
var wrappedAsyncs = false;
var args = [ctx];
if (parseElement.args) {
for (var i = 0; i < parseElement.args.length; i++) {
var argument = parseElement.args[i];
if (argument == null) {
args.push(null);
} else if (Array.isArray(argument)) {
var arr = [];
for (var j = 0; j < argument.length; j++) {
var element = argument[j];
var value = element.evaluate(ctx); // OK
if (value) {
if (value.then) {
async = true;
} else if (value.asyncWrapper) {
wrappedAsyncs = true;
}
}
arr.push(value);
}
args.push(arr);
} else if (argument.evaluate) {
var value = argument.evaluate(ctx); // OK
if (value) {
if (value.then) {
async = true;
} else if (value.asyncWrapper) {
wrappedAsyncs = true;
}
}
args.push(value);
} else {
args.push(argument);
}
}
}
if (async) {
return new Promise(function(resolve, reject){
var linearized = linearize(args);
Promise.all(linearized).then(function(values){
values = delinearize(values);
if (wrappedAsyncs) {
unwrapAsyncs(values);
}
try{
var apply = parseElement.op.apply(parseElement, values);
resolve(apply);
} catch(e) {
reject(e);
}
}).catch(function(reason){
if (ctx.meta && ctx.meta.reject) {
ctx.meta.reject(reason);
} else {
// TODO: no meta context to reject with, trigger event?
}
})
})
} else {
if (wrappedAsyncs) {
unwrapAsyncs(args);
}
try {
return parseElement.op.apply(parseElement, args);
} catch (e) {
if (ctx.meta && ctx.meta.reject) {
ctx.meta.reject(e);
} else {
throw e;
}
}
}
}
var _scriptAttrs = null;
function getScriptAttributes() {
if (_scriptAttrs == null) {
_scriptAttrs = _hyperscript.config.attributes.replace(/ /g,'').split(",")
}
return _scriptAttrs;
}
function getScript(elt) {
for (var i = 0; i < getScriptAttributes().length; i++) {
var scriptAttribute = getScriptAttributes()[i];
if (elt.hasAttribute && elt.hasAttribute(scriptAttribute)) {
return elt.getAttribute(scriptAttribute)
}
}
if (elt.type === "text/hyperscript") {
return elt.innerText;
}
return null;
}
function makeContext(root, elt, event) {
var ctx = {
meta: {
parser: _parser,
lexer: _lexer,
runtime: _runtime,
root: root,
iterators: root
},
me: elt,
event: event,
detail: event ? event.detail : null,
body: 'document' in globalScope ? document.body : null
}
ctx.meta.ctx = ctx;
return ctx;
}
function applyEventListeners(hypeScript, elt) {
forEach(hypeScript.onFeatures, function (onFeature) {
forEach(
onFeature.elsewhere ? [document]
: onFeature.from ? onFeature.from.evaluate({})
: [elt], function(target){ // OK NO PROMISE
target.addEventListener(onFeature.on.evaluate(), function(evt){ // OK NO PROMISE
if (onFeature.elsewhere && elt.contains(evt.target)) return
var ctx = makeContext(onFeature, elt, evt);
onFeature.execute(ctx)
});
})
});
}
function getScriptSelector() {
return getScriptAttributes().map(function (attribute) {
return "[" + attribute + "]";
}).join(", ");
}
function isType(o, type) {
return Object.prototype.toString.call(o) === "[object " + type + "]";
}
function evaluate(typeOrSrc, srcOrCtx, ctxArg) {
if (isType(srcOrCtx, "Object")) {
var src = typeOrSrc;
var ctx = srcOrCtx;
var type = "expression"
} else if (isType(srcOrCtx, "String")) {
var src = srcOrCtx;
var type = typeOrSrc
var ctx = ctxArg;
} else {
var src = typeOrSrc;
var ctx = {};
var type = "expression";
}
ctx = ctx || {};
var compiled = _parser.parseElement(type, _lexer.tokenize(src) );
return compiled.evaluate ? compiled.evaluate(ctx) : compiled.execute(ctx); // OK
}
function processNode(elt) {
var selector = _runtime.getScriptSelector();
if (matchesSelector(elt, selector)) {
initElement(elt);
}
if (elt.querySelectorAll) {
forEach(elt.querySelectorAll(selector), function (elt) {
initElement(elt);
});
}
if (elt.type === "text/hyperscript") {
initElement(elt, document.body);
}
if (elt.querySelectorAll) {
forEach(elt.querySelectorAll("[type=\'text/hyperscript\']"), function (elt) {
initElement(elt, document.body);
});
}
}
function initElement(elt, target) {
var internalData = getInternalData(elt);
if (!internalData.initialized) {
var src = getScript(elt);
if (src) {
try {
internalData.initialized = true;
internalData.script = src;
var tokens = _lexer.tokenize(src);
var hyperScript = _parser.parseHyperScript(tokens);
_runtime.applyEventListeners(hyperScript, target || elt);
setTimeout(function () {
triggerEvent(target || elt, 'load');
}, 1);
} catch(e) {
console.error("hyperscript errors were found on the following element:", elt, "\n\n", e.message, e.stack);
}
}
}
}
function getInternalData(elt) {
var dataProp = 'hyperscript-internal-data';
var data = elt[dataProp];
if (!data) {
data = elt[dataProp] = {};
}
return data;
}
function typeCheck(value, typeString, nullOk) {
if (value == null && nullOk) {
return value;
}
var typeName = Object.prototype.toString.call(value).slice(8, -1);
var typeCheckValue = value && typeName === typeString;
if (typeCheckValue) {
return value;
} else {
throw new Error("Typecheck failed! Expected: " + typeString + ", Found: " + typeName);
}
}
function resolveSymbol(str, context) {
if (str === "me" || str === "my") {
return context["me"];
} if (str === "it" || str === "its") {
return context["it"];
} else {
if (context.meta && context.meta.context) {
var fromMetaContext = context.meta.context[str];
if (typeof fromMetaContext !== "undefined") {
return fromMetaContext;
}
}
var fromContext = context[str];
if (typeof fromContext !== "undefined") {
return fromContext;
} else {
return globalScope[str];
}
}
}
function findNext(command) {
if (command) {
if (command.resolveNext) {
return command.resolveNext();
} else if (command.next) {
return command.next;
} else {
return findNext(command.parent)
}
}
}
function resolveProperty(root, property) {
if (root != null) {
var val = root[property];
if (typeof val !== 'undefined') {
return val;
} else {
if (isArrayLike(root)) {
if (property === "first") {
return root[0];
} else if (property === "last") {
return root[root.length - 1];
} else if (property === "random") {
return root[Math.floor(root.length * Math.random())]
} else {
// flat map
var result = [];
for (var i = 0; i < root.length; i++) {
var component = root[i];
var componentValue = component[property];
if (componentValue) {
result.push(componentValue);
}
}
return result;
}
}
}
}
}
function assignToNamespace(nameSpace, name, value) {
var root = globalScope;
while (nameSpace.length > 0) {
var propertyName = nameSpace.shift();
var newRoot = root[propertyName];
if (newRoot == null) {
newRoot = {};
root[propertyName] = newRoot;
}
root = newRoot;
}
root[name] = value;
}
var hyperscriptUrl = 'document' in globalScope ? document.currentScript.src : null
return {
typeCheck: typeCheck,
forEach: forEach,
triggerEvent: triggerEvent,
matchesSelector: matchesSelector,
getScript: getScript,
applyEventListeners: applyEventListeners,
processNode: processNode,
evaluate: evaluate,
getScriptSelector: getScriptSelector,
resolveSymbol: resolveSymbol,
makeContext: makeContext,
findNext: findNext,
unifiedEval: unifiedEval,
unifiedExec: unifiedExec,
resolveProperty: resolveProperty,
assignToNamespace: assignToNamespace,
hyperscriptUrl: hyperscriptUrl,
HALT: HALT
}
}();
//====================================================================
// Grammar
//====================================================================
{
_parser.addLeafExpression("parenthesized", function(parser, runtime, tokens) {
if (tokens.matchOpToken('(')) {
var expr = parser.requireElement("expression", tokens);
tokens.requireOpToken(")");
return {
type: "parenthesized",
expr: expr,
evaluate: function (context) {
return expr.evaluate(context); //OK
}
}
}
})
_parser.addLeafExpression("string", function(parser, runtime, tokens) {
var stringToken = tokens.matchTokenType('STRING');
if (stringToken) {
return {
type: "string",
token: stringToken,
evaluate: function (context) {
return stringToken.value;
}
}
}
})
_parser.addGrammarElement("nakedString", function(parser, runtime, tokens) {
if (tokens.hasMore()) {
var tokenArr = tokens.consumeUntilWhitespace();
tokens.matchTokenType("WHITESPACE");
return {
type: "nakedString",
tokens: tokenArr,
evaluate: function (context) {
return tokenArr.map(function (t) {return t.value}).join("");
}
}
}
})
_parser.addLeafExpression("number", function(parser, runtime, tokens) {
var number = tokens.matchTokenType('NUMBER');
if (number) {
var numberToken = number;
var value = parseFloat(number.value)
return {
type: "number",
value: value,
numberToken: numberToken,
evaluate: function () {
return value;
}
}
}
})
_parser.addLeafExpression("idRef", function(parser, runtime, tokens) {
var elementId = tokens.matchTokenType('ID_REF');
if (elementId) {
return {
type: "idRef",
value: elementId.value.substr(1),
evaluate: function (context) {
return document.getElementById(this.val