Skip to content

Instantly share code, notes, and snippets.

@jdanyow
Last active August 2, 2016 14:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jdanyow/ad56bbc7ba0011eb9f5a93686693521d to your computer and use it in GitHub Desktop.
Save jdanyow/ad56bbc7ba0011eb9f5a93686693521d to your computer and use it in GitHub Desktop.
Aurelia capture binding command
<template>
<style>
.active {
border-color: blue;
}
</style>
<form>
<h1>Order Pizza:</h1>
<div repeat.for="response of responses">
<h3>Section ${response.id}</h3>
<fieldset>
<legend>Choose Size:</legend>
<label repeat.for="size of sizes & oneTime" for.one-time="size + response.id">
<input type="radio" id.one-time="size + response.id" name.bind="'small' + response.id" value.one-time="size"
checked.bind="response.selectedSize"> ${size}
</label>
</fieldset>
<fieldset>
<legend>Choose Toppings:</legend>
<label repeat.for="topping of toppings & oneTime" for.one-time="topping + response.id">
<input type="checkbox" id.one-time="topping + response.id" value.one-time="topping"
checked.bind="response.selectedToppings"> ${topping}
</label>
</fieldset>
<label for="firstName">First Name: <input id="firstName" value.bind="response.firstName"></label>
</div>
</form>
</template>
export class App {
sizes = ['Small', 'Medium', 'Large'];
toppings = ['Pepperoni', 'Sausage', 'Anchovies', 'Peppers', 'Onions', 'Mushrooms'];
responses = [];
blurred() {
console.log('blurred!');
}
constructor() {
for (let i = 0; i < 500; i++){
this.responses.push(
{ id: i.toString(), selectedSize: 'Small', selectedToppings: ['Anchovies', 'Onions'], firstName: 'test' });
}
}
}
import {DOM} from 'aurelia-pal';
import {EventManager, ListenerExpression} from 'aurelia-binding';
import {SyntaxInterpreter} from 'aurelia-templating-binding';
//Note: path and deepPath are designed to handle v0 and v1 shadow dom specs respectively
function findOriginalEventTarget(event) {
return (event.path && event.path[0]) || (event.deepPath && event.deepPath[0]) || event.target;
}
function interceptStopPropagation(event) {
event.standardStopPropagation = event.stopPropagation;
event.stopPropagation = function() {
this.propagationStopped = true;
this.standardStopPropagation();
};
}
function handleCapturedEvent(event) {
let interceptInstalled = false;
event.propagationStopped = false;
let target = findOriginalEventTarget(event);
while (target && !event.propagationStopped) {
if (target.capturedCallbacks) {
let callback = target.capturedCallbacks[event.type];
if (callback) {
if (!interceptInstalled) {
interceptStopPropagation(event);
interceptInstalled = true;
}
callback(event);
}
}
target = target.parentNode;
}
}
const eventInit = { capture: true };
class CapturedHandlerEntry {
constructor(eventName) {
this.eventName = eventName;
this.count = 0;
}
increment() {
this.count++;
if (this.count === 1) {
document.body.addEventListener(this.eventName, handleCapturedEvent, eventInit);
}
}
decrement() {
this.count--;
if (this.count === 0) {
document.body.removeEventListener(this.eventName, handleCapturedEvent);
}
}
}
// this would actually be integrated into binding's DefaultEventStrategy.subscribe...
// it's currently not exported so we have to override EventManager.addEventListener instead...
class DefaultEventStrategy2 {
capturedHandlers = {};
subscribe(target, targetEvent, callback, delegate) {
const capturedHandlers = this.capturedHandlers;
const handlerEntry = capturedHandlers[targetEvent] || (capturedHandlers[targetEvent] = new CapturedHandlerEntry(targetEvent));
const capturedCallbacks = target.capturedCallbacks || (target.capturedCallbacks = {});
handlerEntry.increment();
capturedCallbacks[targetEvent] = callback;
return function() {
handlerEntry.decrement();
capturedCallbacks[targetEvent] = null;
};
}
}
const strategy2 = new DefaultEventStrategy2();
// quick and dirty defaultEventStrategy override... not how we'd really do this...
EventManager.prototype.addEventListener = function(target, targetEvent, callback, delegate) {
return (this.eventStrategyLookup[targetEvent] || (delegate === 'capture' ? strategy2 : this.defaultEventStrategy))
.subscribe(target, targetEvent, callback, delegate);
}
// add the "capture" binding command:
SyntaxInterpreter.prototype.capture = function(resources, element, info) {
return new ListenerExpression(
this.eventManager,
info.attrName,
this.parser.parse(info.attrValue),
'capture', // hack: passing "capture" into the "delegate" parameter (which expects a boolean)
true,
resources.lookupFunctions
);
};
<!doctype html>
<html>
<head>
<title>Aurelia</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body aurelia-app="main">
<h1>Loading...</h1>
<script src="https://jdanyow.github.io/rjs-bundle/node_modules/requirejs/require.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/config.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/bundles/aurelia.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/bundles/babel.js"></script>
<script>
require(['aurelia-bootstrapper']);
</script>
</body>
</html>
import './capture';
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging();
aurelia.start().then(() => aurelia.setRoot());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment