diff --git a/src/compile/nodes/EventHandler.ts b/src/compile/nodes/EventHandler.ts index 9676923d81..69a76a48e9 100644 --- a/src/compile/nodes/EventHandler.ts +++ b/src/compile/nodes/EventHandler.ts @@ -35,130 +35,13 @@ export default class EventHandler extends Node { this.dependencies = new Set(); if (info.expression) { - this.validateExpression(info.expression); - - this.callee = flattenReference(info.expression.callee); - - this.insertionPoint = info.expression.start; - - this.usesComponent = !validCalleeObjects.has(this.callee.name); - this.usesContext = false; - this.usesEventObject = this.callee.name === 'event'; - - this.args = info.expression.arguments.map(param => { - const expression = new Expression(component, this, scope, param); - addToSet(this.dependencies, expression.dependencies); - if (expression.usesContext) this.usesContext = true; - if (expression.usesEvent) this.usesEventObject = true; - return expression; - }); - - this.snippet = `[✂${info.expression.start}-${info.expression.end}✂];`; + this.expression = new Expression(component, parent, scope, info.expression); + this.snippet = this.expression.snippet; } else { - this.callee = null; - this.insertionPoint = null; - - this.args = null; - this.usesComponent = true; - this.usesContext = false; - this.usesEventObject = true; - this.snippet = null; // TODO handle shorthand events here? } this.isCustomEvent = component.events.has(this.name); this.shouldHoist = !this.isCustomEvent && parent.hasAncestor('EachBlock'); } - - render(component, block, context, hoisted) { // TODO hoist more event handlers - if (this.insertionPoint === null) return; // TODO handle shorthand events here? - - if (!validCalleeObjects.has(this.callee.name)) { - const component_name = hoisted ? `component` : block.alias(`component`); - - // allow event.stopPropagation(), this.select() etc - // TODO verify that it's a valid callee (i.e. built-in or declared method) - if (this.callee.name[0] === '$' && !component.methods.has(this.callee.name)) { - component.code.overwrite( - this.insertionPoint, - this.insertionPoint + 1, - `${component_name}.store.` - ); - } else { - component.code.prependRight( - this.insertionPoint, - `${component_name}.` - ); - } - } - - if (this.isCustomEvent) { - this.args.forEach(arg => { - arg.overwriteThis(context); - }); - - if (this.callee && this.callee.name === 'this') { - const node = this.callee.nodes[0]; - component.code.overwrite(node.start, node.end, context, { - storeName: true, - contentOnly: true - }); - } - } - } - - validateExpression(expression) { - const { callee, type } = expression; - - if (type !== 'CallExpression') { - this.component.error(expression, { - code: `invalid-event-handler`, - message: `Expected a call expression` - }); - } - - const { component } = this; - const { name } = flattenReference(callee); - - if (validCalleeObjects.has(name) || name === 'options') return; - - if (name === 'refs') { - this.component.refCallees.push(callee); - return; - } - - if ( - (callee.type === 'Identifier' && validBuiltins.has(name)) || - this.component.methods.has(name) - ) { - return; - } - - if (name[0] === '$') { - // assume it's a store method - return; - } - - const validCallees = ['this.*', 'refs.*', 'event.*', 'options.*', 'console.*'].concat( - Array.from(validBuiltins), - Array.from(this.component.methods.keys()) - ); - - let message = `'${component.source.slice(callee.start, callee.end)}' is an invalid callee ` ; - - if (name === 'store') { - message += `(did you mean '$${component.source.slice(callee.start + 6, callee.end)}(...)'?)`; - } else { - message += `(should be one of ${list(validCallees)})`; - - if (callee.type === 'Identifier' && component.helpers.has(callee.name)) { - message += `. '${callee.name}' exists on 'helpers', did you put it in the wrong place?`; - } - } - - component.warn(expression, { - code: `invalid-callee`, - message - }); - } } \ No newline at end of file diff --git a/src/compile/nodes/shared/Expression.ts b/src/compile/nodes/shared/Expression.ts index dc6f77d82e..0281a6c62c 100644 --- a/src/compile/nodes/shared/Expression.ts +++ b/src/compile/nodes/shared/Expression.ts @@ -84,7 +84,6 @@ export default class Expression { let { map, scope: currentScope } = createScopes(info); - const isEventHandler = parent.type === 'EventHandler'; const expression = this; const isSynthetic = parent.isSynthetic; @@ -108,11 +107,6 @@ export default class Expression { if (isReference(node, parent)) { const { name, nodes } = flattenReference(node); - if (name === 'event' && isEventHandler) { - expression.usesEvent = true; - return; - } - if (currentScope.has(name)) return; if (component.helpers.has(name)) { diff --git a/src/compile/render-dom/wrappers/Element/index.ts b/src/compile/render-dom/wrappers/Element/index.ts index d951fb445c..c2bf6b0434 100644 --- a/src/compile/render-dom/wrappers/Element/index.ts +++ b/src/compile/render-dom/wrappers/Element/index.ts @@ -608,11 +608,6 @@ export default class ElementWrapper extends Wrapper { this.node.handlers.forEach(handler => { const isCustomEvent = component.events.has(handler.name); - if (handler.callee) { - // TODO move handler render method into a wrapper - handler.render(this.renderer.component, block, this.var, handler.shouldHoist); - } - const target = handler.shouldHoist ? 'this' : this.var; // get a name for the event handler that is globally unique @@ -656,7 +651,7 @@ export default class ElementWrapper extends Wrapper { const handlerFunction = deindent` function ${handlerName}(event) { ${modifiers} - ${handlerBody} + (${handlerBody})(event); } `; diff --git a/src/compile/render-dom/wrappers/Window.ts b/src/compile/render-dom/wrappers/Window.ts index c26cf5ecb1..00cbd93379 100644 --- a/src/compile/render-dom/wrappers/Window.ts +++ b/src/compile/render-dom/wrappers/Window.ts @@ -47,12 +47,10 @@ export default class WindowWrapper extends Wrapper { let usesState = handler.dependencies.size > 0; - handler.render(component, block, 'window', false); // TODO hoist? - const handlerName = block.getUniqueName(`onwindow${handler.name}`); const handlerBody = deindent` ${usesState && `var ctx = #component.get();`} - ${handler.snippet}; + ${handler.snippet} `; if (isCustomEvent) { @@ -61,7 +59,7 @@ export default class WindowWrapper extends Wrapper { block.builders.hydrate.addBlock(deindent` ${handlerName} = %events-${handler.name}.call(#component, window, function(event) { - ${handlerBody} + (${handlerBody})(event); }); `); @@ -71,7 +69,7 @@ export default class WindowWrapper extends Wrapper { } else { block.builders.init.addBlock(deindent` function ${handlerName}(event) { - ${handlerBody} + (${handlerBody})(event); } window.addEventListener("${handler.name}", ${handlerName}); `); diff --git a/src/parse/state/tag.ts b/src/parse/state/tag.ts index dcb5530fd7..31f773b431 100644 --- a/src/parse/state/tag.ts +++ b/src/parse/state/tag.ts @@ -413,6 +413,7 @@ function readAttribute(parser: Parser, uniqueNames: Set) { function get_directive_type(name) { if (name === 'use') return 'Action'; + if (name === 'on') return 'EventHandler'; throw new Error(`TODO directive ${name}`); } diff --git a/test/runtime/samples/action-update/_config.js b/test/runtime/samples/action-update/_config.js index 1c56399629..c98cb59d7f 100644 --- a/test/runtime/samples/action-update/_config.js +++ b/test/runtime/samples/action-update/_config.js @@ -3,25 +3,25 @@ export default { `, - test (assert, component, target, window) { + async test (assert, component, target, window) { const button = target.querySelector('button'); - const eventEnter = new window.MouseEvent('mouseenter'); - const eventLeave = new window.MouseEvent('mouseleave'); + const enter = new window.MouseEvent('mouseenter'); + const leave = new window.MouseEvent('mouseleave'); const ctrlPress = new window.KeyboardEvent('keydown', { ctrlKey: true }); - button.dispatchEvent(eventEnter); + await button.dispatchEvent(enter); assert.htmlEqual(target.innerHTML, `
Perform an Action
`); - window.dispatchEvent(ctrlPress); + await window.dispatchEvent(ctrlPress); assert.htmlEqual(target.innerHTML, `
Perform an augmented Action
`); - button.dispatchEvent(eventLeave); + await button.dispatchEvent(leave); assert.htmlEqual(target.innerHTML, ` `); diff --git a/test/runtime/samples/action-update/main.html b/test/runtime/samples/action-update/main.html index 608d1a3b59..abeec0dbcb 100644 --- a/test/runtime/samples/action-update/main.html +++ b/test/runtime/samples/action-update/main.html @@ -2,10 +2,11 @@ export let text = 'Perform an Action'; function checkForCtrl(event) { + console.log(`!!! ${event.ctrlKey}`); if (event.ctrlKey) { - tooltip = 'Perform an augmented Action'; + text = 'Perform an augmented Action'; } else { - tooltip = 'Perform an Action'; + text = 'Perform an Action'; } }