@odoo/owl 2.0.0-beta.2 → 2.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/owl.cjs.js CHANGED
@@ -267,10 +267,14 @@ function createElementHandler(evName, capture = false) {
267
267
  this[eventKey] = data;
268
268
  this.addEventListener(evName, listener, { capture });
269
269
  }
270
+ function remove() {
271
+ delete this[eventKey];
272
+ this.removeEventListener(evName, listener, { capture });
273
+ }
270
274
  function update(data) {
271
275
  this[eventKey] = data;
272
276
  }
273
- return { setup, update };
277
+ return { setup, update, remove };
274
278
  }
275
279
  // Synthetic handler: a form of event delegation that allows placing only one
276
280
  // listener per event type.
@@ -287,7 +291,10 @@ function createSyntheticHandler(evName, capture = false) {
287
291
  _data[currentId] = data;
288
292
  this[eventKey] = _data;
289
293
  }
290
- return { setup, update: setup };
294
+ function remove() {
295
+ delete this[eventKey];
296
+ }
297
+ return { setup, update: setup, remove };
291
298
  }
292
299
  function nativeToSyntheticEvent(eventKey, event) {
293
300
  let dom = event.target;
@@ -1282,6 +1289,75 @@ function html(str) {
1282
1289
  return new VHtml(str);
1283
1290
  }
1284
1291
 
1292
+ function createCatcher(eventsSpec) {
1293
+ let setupFns = [];
1294
+ let removeFns = [];
1295
+ for (let name in eventsSpec) {
1296
+ let index = eventsSpec[name];
1297
+ let { setup, remove } = createEventHandler(name);
1298
+ setupFns[index] = setup;
1299
+ removeFns[index] = remove;
1300
+ }
1301
+ let n = setupFns.length;
1302
+ class VCatcher {
1303
+ constructor(child, handlers) {
1304
+ this.afterNode = null;
1305
+ this.child = child;
1306
+ this.handlers = handlers;
1307
+ }
1308
+ mount(parent, afterNode) {
1309
+ this.parentEl = parent;
1310
+ this.afterNode = afterNode;
1311
+ this.child.mount(parent, afterNode);
1312
+ for (let i = 0; i < n; i++) {
1313
+ let origFn = this.handlers[i][0];
1314
+ const self = this;
1315
+ this.handlers[i][0] = function (ev) {
1316
+ const target = ev.target;
1317
+ let currentNode = self.child.firstNode();
1318
+ const afterNode = self.afterNode;
1319
+ while (currentNode !== afterNode) {
1320
+ if (currentNode.contains(target)) {
1321
+ return origFn.call(this, ev);
1322
+ }
1323
+ currentNode = currentNode.nextSibling;
1324
+ }
1325
+ };
1326
+ setupFns[i].call(parent, this.handlers[i]);
1327
+ }
1328
+ }
1329
+ moveBefore(other, afterNode) {
1330
+ this.afterNode = null;
1331
+ this.child.moveBefore(other ? other.child : null, afterNode);
1332
+ }
1333
+ patch(other, withBeforeRemove) {
1334
+ if (this === other) {
1335
+ return;
1336
+ }
1337
+ this.handlers = other.handlers;
1338
+ this.child.patch(other.child, withBeforeRemove);
1339
+ }
1340
+ beforeRemove() {
1341
+ this.child.beforeRemove();
1342
+ }
1343
+ remove() {
1344
+ for (let i = 0; i < n; i++) {
1345
+ removeFns[i].call(this.parentEl);
1346
+ }
1347
+ this.child.remove();
1348
+ }
1349
+ firstNode() {
1350
+ return this.child.firstNode();
1351
+ }
1352
+ toString() {
1353
+ return this.child.toString();
1354
+ }
1355
+ }
1356
+ return function (child, handlers) {
1357
+ return new VCatcher(child, handlers);
1358
+ };
1359
+ }
1360
+
1285
1361
  function mount$1(vnode, fixture, afterNode = null) {
1286
1362
  vnode.mount(fixture, afterNode);
1287
1363
  }
@@ -2734,24 +2810,31 @@ const TOKENIZERS = [
2734
2810
  function tokenize(expr) {
2735
2811
  const result = [];
2736
2812
  let token = true;
2737
- while (token) {
2738
- expr = expr.trim();
2739
- if (expr) {
2740
- for (let tokenizer of TOKENIZERS) {
2741
- token = tokenizer(expr);
2742
- if (token) {
2743
- result.push(token);
2744
- expr = expr.slice(token.size || token.value.length);
2745
- break;
2813
+ let error;
2814
+ let current = expr;
2815
+ try {
2816
+ while (token) {
2817
+ current = current.trim();
2818
+ if (current) {
2819
+ for (let tokenizer of TOKENIZERS) {
2820
+ token = tokenizer(current);
2821
+ if (token) {
2822
+ result.push(token);
2823
+ current = current.slice(token.size || token.value.length);
2824
+ break;
2825
+ }
2746
2826
  }
2747
2827
  }
2828
+ else {
2829
+ token = false;
2830
+ }
2748
2831
  }
2749
- else {
2750
- token = false;
2751
- }
2752
2832
  }
2753
- if (expr.length) {
2754
- throw new Error(`Tokenizer error: could not tokenize "${expr}"`);
2833
+ catch (e) {
2834
+ error = e; // Silence all errors and throw a generic error below
2835
+ }
2836
+ if (current.length || error) {
2837
+ throw new Error(`Tokenizer error: could not tokenize \`${expr}\``);
2755
2838
  }
2756
2839
  return result;
2757
2840
  }
@@ -2956,7 +3039,7 @@ function createContext(parentCtx, params) {
2956
3039
  }, params);
2957
3040
  }
2958
3041
  class CodeTarget {
2959
- constructor(name) {
3042
+ constructor(name, on) {
2960
3043
  this.indentLevel = 0;
2961
3044
  this.loopLevel = 0;
2962
3045
  this.code = [];
@@ -2967,6 +3050,7 @@ class CodeTarget {
2967
3050
  this.refInfo = {};
2968
3051
  this.shouldProtectScope = false;
2969
3052
  this.name = name;
3053
+ this.on = on || null;
2970
3054
  }
2971
3055
  addLine(line, idx) {
2972
3056
  const prefix = new Array(this.indentLevel + 2).join(" ");
@@ -3016,7 +3100,7 @@ class CodeGenerator {
3016
3100
  this.targets = [];
3017
3101
  this.target = new CodeTarget("template");
3018
3102
  this.translatableAttributes = TRANSLATABLE_ATTRS;
3019
- this.staticCalls = [];
3103
+ this.staticDefs = [];
3020
3104
  this.helpers = new Set();
3021
3105
  this.translateFn = options.translateFn || ((s) => s);
3022
3106
  if (options.translatableAttributes) {
@@ -3059,8 +3143,8 @@ class CodeGenerator {
3059
3143
  if (this.templateName) {
3060
3144
  mainCode.push(`// Template name: "${this.templateName}"`);
3061
3145
  }
3062
- for (let { id, template } of this.staticCalls) {
3063
- mainCode.push(`const ${id} = getTemplate(${template});`);
3146
+ for (let { id, expr } of this.staticDefs) {
3147
+ mainCode.push(`const ${id} = ${expr};`);
3064
3148
  }
3065
3149
  // define all blocks
3066
3150
  if (this.blocks.length) {
@@ -3096,19 +3180,21 @@ class CodeGenerator {
3096
3180
  }
3097
3181
  return code;
3098
3182
  }
3099
- compileInNewTarget(prefix, ast, ctx) {
3183
+ compileInNewTarget(prefix, ast, ctx, on) {
3100
3184
  const name = this.generateId(prefix);
3101
3185
  const initialTarget = this.target;
3102
- const target = new CodeTarget(name);
3186
+ const target = new CodeTarget(name, on);
3103
3187
  this.targets.push(target);
3104
3188
  this.target = target;
3105
- const subCtx = createContext(ctx);
3106
- this.compileAST(ast, subCtx);
3189
+ this.compileAST(ast, createContext(ctx));
3107
3190
  this.target = initialTarget;
3108
3191
  return name;
3109
3192
  }
3110
- addLine(line) {
3111
- this.target.addLine(line);
3193
+ addLine(line, idx) {
3194
+ this.target.addLine(line, idx);
3195
+ }
3196
+ define(varName, expr) {
3197
+ this.addLine(`const ${varName} = ${expr};`);
3112
3198
  }
3113
3199
  generateId(prefix = "") {
3114
3200
  this.ids[prefix] = (this.ids[prefix] || 0) + 1;
@@ -3150,10 +3236,13 @@ class CodeGenerator {
3150
3236
  blockExpr = `toggler(${tKeyExpr}, ${blockExpr})`;
3151
3237
  }
3152
3238
  if (block.isRoot && !ctx.preventRoot) {
3239
+ if (this.target.on) {
3240
+ blockExpr = this.wrapWithEventCatcher(blockExpr, this.target.on);
3241
+ }
3153
3242
  this.addLine(`return ${blockExpr};`);
3154
3243
  }
3155
3244
  else {
3156
- this.addLine(`let ${block.varName} = ${blockExpr};`);
3245
+ this.define(block.varName, blockExpr);
3157
3246
  }
3158
3247
  }
3159
3248
  /**
@@ -3181,7 +3270,7 @@ class CodeGenerator {
3181
3270
  if (!mapping.has(tok.varName)) {
3182
3271
  const varId = this.generateId("v");
3183
3272
  mapping.set(tok.varName, varId);
3184
- this.addLine(`const ${varId} = ${tok.value};`);
3273
+ this.define(varId, tok.value);
3185
3274
  }
3186
3275
  tok.value = mapping.get(tok.varName);
3187
3276
  }
@@ -3320,7 +3409,7 @@ class CodeGenerator {
3320
3409
  this.blocks.push(block);
3321
3410
  if (ast.dynamicTag) {
3322
3411
  const tagExpr = this.generateId("tag");
3323
- this.addLine(`let ${tagExpr} = ${compileExpr(ast.dynamicTag)};`);
3412
+ this.define(tagExpr, compileExpr(ast.dynamicTag));
3324
3413
  block.dynamicTagName = tagExpr;
3325
3414
  }
3326
3415
  }
@@ -3402,10 +3491,10 @@ class CodeGenerator {
3402
3491
  const { hasDynamicChildren, baseExpr, expr, eventType, shouldNumberize, shouldTrim, targetAttr, specialInitTargetAttr, } = ast.model;
3403
3492
  const baseExpression = compileExpr(baseExpr);
3404
3493
  const bExprId = this.generateId("bExpr");
3405
- this.addLine(`const ${bExprId} = ${baseExpression};`);
3494
+ this.define(bExprId, baseExpression);
3406
3495
  const expression = compileExpr(expr);
3407
3496
  const exprId = this.generateId("expr");
3408
- this.addLine(`const ${exprId} = ${expression};`);
3497
+ this.define(exprId, expression);
3409
3498
  const fullExpression = `${bExprId}[${exprId}]`;
3410
3499
  let idx;
3411
3500
  if (specialInitTargetAttr) {
@@ -3415,7 +3504,7 @@ class CodeGenerator {
3415
3504
  else if (hasDynamicChildren) {
3416
3505
  const bValueId = this.generateId("bValue");
3417
3506
  tModelSelectedExpr = `${bValueId}`;
3418
- this.addLine(`let ${tModelSelectedExpr} = ${fullExpression}`);
3507
+ this.define(tModelSelectedExpr, fullExpression);
3419
3508
  }
3420
3509
  else {
3421
3510
  idx = block.insertData(`${fullExpression}`, "attr");
@@ -3463,14 +3552,14 @@ class CodeGenerator {
3463
3552
  const children = block.children.slice();
3464
3553
  let current = children.shift();
3465
3554
  for (let i = codeIdx; i < code.length; i++) {
3466
- if (code[i].trimStart().startsWith(`let ${current.varName} `)) {
3467
- code[i] = code[i].replace(`let ${current.varName}`, current.varName);
3555
+ if (code[i].trimStart().startsWith(`const ${current.varName} `)) {
3556
+ code[i] = code[i].replace(`const ${current.varName}`, current.varName);
3468
3557
  current = children.shift();
3469
3558
  if (!current)
3470
3559
  break;
3471
3560
  }
3472
3561
  }
3473
- this.target.addLine(`let ${block.children.map((c) => c.varName)};`, codeIdx);
3562
+ this.addLine(`let ${block.children.map((c) => c.varName)};`, codeIdx);
3474
3563
  }
3475
3564
  }
3476
3565
  }
@@ -3558,14 +3647,14 @@ class CodeGenerator {
3558
3647
  const children = block.children.slice();
3559
3648
  let current = children.shift();
3560
3649
  for (let i = codeIdx; i < code.length; i++) {
3561
- if (code[i].trimStart().startsWith(`let ${current.varName} `)) {
3562
- code[i] = code[i].replace(`let ${current.varName}`, current.varName);
3650
+ if (code[i].trimStart().startsWith(`const ${current.varName} `)) {
3651
+ code[i] = code[i].replace(`const ${current.varName}`, current.varName);
3563
3652
  current = children.shift();
3564
3653
  if (!current)
3565
3654
  break;
3566
3655
  }
3567
3656
  }
3568
- this.target.addLine(`let ${block.children.map((c) => c.varName)};`, codeIdx);
3657
+ this.addLine(`let ${block.children.map((c) => c.varName)};`, codeIdx);
3569
3658
  }
3570
3659
  // note: this part is duplicated from end of compilemulti:
3571
3660
  const args = block.children.map((c) => c.varName).join(", ");
@@ -3586,10 +3675,10 @@ class CodeGenerator {
3586
3675
  const l = `l_block${block.id}`;
3587
3676
  const c = `c_block${block.id}`;
3588
3677
  this.helpers.add("prepareList");
3589
- this.addLine(`const [${keys}, ${vals}, ${l}, ${c}] = prepareList(${compileExpr(ast.collection)});`);
3678
+ this.define(`[${keys}, ${vals}, ${l}, ${c}]`, `prepareList(${compileExpr(ast.collection)});`);
3590
3679
  // Throw errors on duplicate keys in dev mode
3591
3680
  if (this.dev) {
3592
- this.addLine(`const keys${block.id} = new Set();`);
3681
+ this.define(`keys${block.id}`, `new Set()`);
3593
3682
  }
3594
3683
  this.addLine(`for (let ${loopVar} = 0; ${loopVar} < ${l}; ${loopVar}++) {`);
3595
3684
  this.target.indentLevel++;
@@ -3606,7 +3695,7 @@ class CodeGenerator {
3606
3695
  if (!ast.hasNoValue) {
3607
3696
  this.addLine(`ctx[\`${ast.elem}_value\`] = ${keys}[${loopVar}];`);
3608
3697
  }
3609
- this.addLine(`let key${this.target.loopLevel} = ${ast.key ? compileExpr(ast.key) : loopVar};`);
3698
+ this.define(`key${this.target.loopLevel}`, ast.key ? compileExpr(ast.key) : loopVar);
3610
3699
  if (this.dev) {
3611
3700
  // Throw error on duplicate keys in dev mode
3612
3701
  this.addLine(`if (keys${block.id}.has(key${this.target.loopLevel})) { throw new Error(\`Got duplicate key in t-foreach: \${key${this.target.loopLevel}}\`)}`);
@@ -3616,8 +3705,8 @@ class CodeGenerator {
3616
3705
  if (ast.memo) {
3617
3706
  this.target.hasCache = true;
3618
3707
  id = this.generateId();
3619
- this.addLine(`let memo${id} = ${compileExpr(ast.memo)}`);
3620
- this.addLine(`let vnode${id} = cache[key${this.target.loopLevel}];`);
3708
+ this.define(`memo${id}`, compileExpr(ast.memo));
3709
+ this.define(`vnode${id}`, `cache[key${this.target.loopLevel}];`);
3621
3710
  this.addLine(`if (vnode${id}) {`);
3622
3711
  this.target.indentLevel++;
3623
3712
  this.addLine(`if (shallowEqual(vnode${id}.memo, memo${id})) {`);
@@ -3645,7 +3734,7 @@ class CodeGenerator {
3645
3734
  }
3646
3735
  compileTKey(ast, ctx) {
3647
3736
  const tKeyExpr = this.generateId("tKey_");
3648
- this.addLine(`const ${tKeyExpr} = ${compileExpr(ast.expr)};`);
3737
+ this.define(tKeyExpr, compileExpr(ast.expr));
3649
3738
  ctx = createContext(ctx, {
3650
3739
  tKeyExpr,
3651
3740
  block: ctx.block,
@@ -3690,14 +3779,14 @@ class CodeGenerator {
3690
3779
  const children = block.children.slice();
3691
3780
  let current = children.shift();
3692
3781
  for (let i = codeIdx; i < code.length; i++) {
3693
- if (code[i].trimStart().startsWith(`let ${current.varName} `)) {
3694
- code[i] = code[i].replace(`let ${current.varName}`, current.varName);
3782
+ if (code[i].trimStart().startsWith(`const ${current.varName} `)) {
3783
+ code[i] = code[i].replace(`const ${current.varName}`, current.varName);
3695
3784
  current = children.shift();
3696
3785
  if (!current)
3697
3786
  break;
3698
3787
  }
3699
3788
  }
3700
- this.target.addLine(`let ${block.children.map((c) => c.varName)};`, codeIdx);
3789
+ this.addLine(`let ${block.children.map((c) => c.varName)};`, codeIdx);
3701
3790
  }
3702
3791
  }
3703
3792
  const args = block.children.map((c) => c.varName).join(", ");
@@ -3728,7 +3817,7 @@ class CodeGenerator {
3728
3817
  const key = `key + \`${this.generateComponentKey()}\``;
3729
3818
  if (isDynamic) {
3730
3819
  const templateVar = this.generateId("template");
3731
- this.addLine(`const ${templateVar} = ${subTemplate};`);
3820
+ this.define(templateVar, subTemplate);
3732
3821
  block = this.createBlock(block, "multi", ctx);
3733
3822
  this.helpers.add("call");
3734
3823
  this.insertBlock(`call(this, ${templateVar}, ctx, node, ${key})`, block, {
@@ -3739,7 +3828,7 @@ class CodeGenerator {
3739
3828
  else {
3740
3829
  const id = this.generateId(`callTemplate_`);
3741
3830
  this.helpers.add("getTemplate");
3742
- this.staticCalls.push({ id, template: subTemplate });
3831
+ this.staticDefs.push({ id, expr: `getTemplate(${subTemplate})` });
3743
3832
  block = this.createBlock(block, "multi", ctx);
3744
3833
  this.insertBlock(`${id}.call(this, ctx, node, ${key})`, block, {
3745
3834
  ...ctx,
@@ -3833,26 +3922,25 @@ class CodeGenerator {
3833
3922
  compileComponent(ast, ctx) {
3834
3923
  let { block } = ctx;
3835
3924
  // props
3836
- const hasSlotsProp = "slots" in ast.props;
3925
+ const hasSlotsProp = "slots" in (ast.props || {});
3837
3926
  const props = [];
3838
- const propExpr = this.formatPropObject(ast.props);
3927
+ const propExpr = this.formatPropObject(ast.props || {});
3839
3928
  if (propExpr) {
3840
3929
  props.push(propExpr);
3841
3930
  }
3842
3931
  // slots
3843
- const hasSlot = !!Object.keys(ast.slots).length;
3844
3932
  let slotDef = "";
3845
- if (hasSlot) {
3933
+ if (ast.slots) {
3846
3934
  let ctxStr = "ctx";
3847
3935
  if (this.target.loopLevel || !this.hasSafeContext) {
3848
3936
  ctxStr = this.generateId("ctx");
3849
3937
  this.helpers.add("capture");
3850
- this.addLine(`const ${ctxStr} = capture(ctx);`);
3938
+ this.define(ctxStr, `capture(ctx)`);
3851
3939
  }
3852
3940
  let slotStr = [];
3853
3941
  for (let slotName in ast.slots) {
3854
- const slotAst = ast.slots[slotName].content;
3855
- const name = this.compileInNewTarget("slot", slotAst, ctx);
3942
+ const slotAst = ast.slots[slotName];
3943
+ const name = this.compileInNewTarget("slot", slotAst.content, ctx, slotAst.on);
3856
3944
  const params = [`__render: ${name}, __ctx: ${ctxStr}`];
3857
3945
  const scope = ast.slots[slotName].scope;
3858
3946
  if (scope) {
@@ -3878,7 +3966,7 @@ class CodeGenerator {
3878
3966
  let propVar;
3879
3967
  if ((slotDef && (ast.dynamicProps || hasSlotsProp)) || this.dev) {
3880
3968
  propVar = this.generateId("props");
3881
- this.addLine(`const ${propVar} = ${propString};`);
3969
+ this.define(propVar, propString);
3882
3970
  propString = propVar;
3883
3971
  }
3884
3972
  if (slotDef && (ast.dynamicProps || hasSlotsProp)) {
@@ -3890,7 +3978,7 @@ class CodeGenerator {
3890
3978
  let expr;
3891
3979
  if (ast.isDynamic) {
3892
3980
  expr = this.generateId("Comp");
3893
- this.addLine(`let ${expr} = ${compileExpr(ast.name)};`);
3981
+ this.define(expr, compileExpr(ast.name));
3894
3982
  }
3895
3983
  else {
3896
3984
  expr = `\`${ast.name}\``;
@@ -3911,9 +3999,28 @@ class CodeGenerator {
3911
3999
  if (ast.isDynamic) {
3912
4000
  blockExpr = `toggler(${expr}, ${blockExpr})`;
3913
4001
  }
4002
+ // event handling
4003
+ if (ast.on) {
4004
+ blockExpr = this.wrapWithEventCatcher(blockExpr, ast.on);
4005
+ }
3914
4006
  block = this.createBlock(block, "multi", ctx);
3915
4007
  this.insertBlock(blockExpr, block, ctx);
3916
4008
  }
4009
+ wrapWithEventCatcher(expr, on) {
4010
+ this.helpers.add("createCatcher");
4011
+ let name = this.generateId("catcher");
4012
+ let spec = {};
4013
+ let handlers = [];
4014
+ for (let ev in on) {
4015
+ let handlerId = this.generateId("hdlr");
4016
+ let idx = handlers.push(handlerId) - 1;
4017
+ spec[ev] = idx;
4018
+ const handler = this.generateHandlerCode(ev, on[ev]);
4019
+ this.define(handlerId, handler);
4020
+ }
4021
+ this.staticDefs.push({ id: name, expr: `createCatcher(${JSON.stringify(spec)})` });
4022
+ return `${name}(${expr}, [${handlers.join(",")}])`;
4023
+ }
3917
4024
  compileTSlot(ast, ctx) {
3918
4025
  this.helpers.add("callSlot");
3919
4026
  let { block } = ctx;
@@ -3935,13 +4042,17 @@ class CodeGenerator {
3935
4042
  else {
3936
4043
  if (dynamic) {
3937
4044
  let name = this.generateId("slot");
3938
- this.addLine(`const ${name} = ${slotName};`);
4045
+ this.define(name, slotName);
3939
4046
  blockString = `toggler(${name}, callSlot(ctx, node, key, ${name}), ${dynamic}, ${scope})`;
3940
4047
  }
3941
4048
  else {
3942
4049
  blockString = `callSlot(ctx, node, key, ${slotName}, ${dynamic}, ${scope})`;
3943
4050
  }
3944
4051
  }
4052
+ // event handling
4053
+ if (ast.on) {
4054
+ blockString = this.wrapWithEventCatcher(blockString, ast.on);
4055
+ }
3945
4056
  if (block) {
3946
4057
  this.insertAnchor(block);
3947
4058
  }
@@ -3962,7 +4073,7 @@ class CodeGenerator {
3962
4073
  if (this.target.loopLevel || !this.hasSafeContext) {
3963
4074
  ctxStr = this.generateId("ctx");
3964
4075
  this.helpers.add("capture");
3965
- this.addLine(`const ${ctxStr} = capture(ctx);`);
4076
+ this.define(ctxStr, `capture(ctx);`);
3966
4077
  }
3967
4078
  const blockString = `component(Portal, {target: ${ast.target},slots: {'default': {__render: ${name}, __ctx: ${ctxStr}}}}, key + \`${key}\`, node, ctx)`;
3968
4079
  if (block) {
@@ -4096,8 +4207,8 @@ function parseDOMNode(node, ctx) {
4096
4207
  const ref = node.getAttribute("t-ref");
4097
4208
  node.removeAttribute("t-ref");
4098
4209
  const nodeAttrsNames = node.getAttributeNames();
4099
- const attrs = {};
4100
- const on = {};
4210
+ let attrs = null;
4211
+ let on = null;
4101
4212
  let model = null;
4102
4213
  for (let attr of nodeAttrsNames) {
4103
4214
  const value = node.getAttribute(attr);
@@ -4105,6 +4216,7 @@ function parseDOMNode(node, ctx) {
4105
4216
  if (attr === "t-on") {
4106
4217
  throw new Error("Missing event name with t-on directive");
4107
4218
  }
4219
+ on = on || {};
4108
4220
  on[attr.slice(5)] = value;
4109
4221
  }
4110
4222
  else if (attr.startsWith("t-model")) {
@@ -4142,6 +4254,7 @@ function parseDOMNode(node, ctx) {
4142
4254
  targetAttr: isCheckboxInput ? "checked" : "value",
4143
4255
  specialInitTargetAttr: isRadioInput ? "checked" : null,
4144
4256
  eventType,
4257
+ hasDynamicChildren: false,
4145
4258
  shouldTrim: hasTrimMod && (isOtherInput || isTextarea),
4146
4259
  shouldNumberize: hasNumberMod && (isOtherInput || isTextarea),
4147
4260
  };
@@ -4162,6 +4275,7 @@ function parseDOMNode(node, ctx) {
4162
4275
  if (tModel && ["t-att-value", "t-attf-value"].includes(attr)) {
4163
4276
  tModel.hasDynamicChildren = true;
4164
4277
  }
4278
+ attrs = attrs || {};
4165
4279
  attrs[attr] = value;
4166
4280
  }
4167
4281
  }
@@ -4312,7 +4426,7 @@ function parseTCall(node, ctx) {
4312
4426
  if (ast && ast.type === 11 /* TComponent */) {
4313
4427
  return {
4314
4428
  ...ast,
4315
- slots: { default: { content: tcall } },
4429
+ slots: { default: { content: tcall, scope: null, on: null, attrs: null } },
4316
4430
  };
4317
4431
  }
4318
4432
  }
@@ -4396,7 +4510,6 @@ function parseTSetNode(node, ctx) {
4396
4510
  // -----------------------------------------------------------------------------
4397
4511
  // Error messages when trying to use an unsupported directive on a component
4398
4512
  const directiveErrorMap = new Map([
4399
- ["t-on", "t-on is no longer supported on components. Consider passing a callback in props."],
4400
4513
  [
4401
4514
  "t-ref",
4402
4515
  "t-ref is no longer supported on components. Consider exposing only the public part of the component's API through a callback prop.",
@@ -4425,18 +4538,26 @@ function parseComponent(node, ctx) {
4425
4538
  node.removeAttribute("t-props");
4426
4539
  const defaultSlotScope = node.getAttribute("t-slot-scope");
4427
4540
  node.removeAttribute("t-slot-scope");
4428
- const props = {};
4541
+ let on = null;
4542
+ let props = null;
4429
4543
  for (let name of node.getAttributeNames()) {
4430
4544
  const value = node.getAttribute(name);
4431
4545
  if (name.startsWith("t-")) {
4432
- const message = directiveErrorMap.get(name.split("-").slice(0, 2).join("-"));
4433
- throw new Error(message || `unsupported directive on Component: ${name}`);
4546
+ if (name.startsWith("t-on-")) {
4547
+ on = on || {};
4548
+ on[name.slice(5)] = value;
4549
+ }
4550
+ else {
4551
+ const message = directiveErrorMap.get(name.split("-").slice(0, 2).join("-"));
4552
+ throw new Error(message || `unsupported directive on Component: ${name}`);
4553
+ }
4434
4554
  }
4435
4555
  else {
4556
+ props = props || {};
4436
4557
  props[name] = value;
4437
4558
  }
4438
4559
  }
4439
- const slots = {};
4560
+ let slots = null;
4440
4561
  if (node.hasChildNodes()) {
4441
4562
  const clone = node.cloneNode(true);
4442
4563
  // named slots
@@ -4464,32 +4585,36 @@ function parseComponent(node, ctx) {
4464
4585
  slotNode.remove();
4465
4586
  const slotAst = parseNode(slotNode, ctx);
4466
4587
  if (slotAst) {
4467
- const slotInfo = { content: slotAst };
4468
- const attrs = {};
4588
+ let on = null;
4589
+ let attrs = null;
4590
+ let scope = null;
4469
4591
  for (let attributeName of slotNode.getAttributeNames()) {
4470
4592
  const value = slotNode.getAttribute(attributeName);
4471
4593
  if (attributeName === "t-slot-scope") {
4472
- slotInfo.scope = value;
4594
+ scope = value;
4473
4595
  continue;
4474
4596
  }
4475
- attrs[attributeName] = value;
4476
- }
4477
- if (Object.keys(attrs).length) {
4478
- slotInfo.attrs = attrs;
4597
+ else if (attributeName.startsWith("t-on-")) {
4598
+ on = on || {};
4599
+ on[attributeName.slice(5)] = value;
4600
+ }
4601
+ else {
4602
+ attrs = attrs || {};
4603
+ attrs[attributeName] = value;
4604
+ }
4479
4605
  }
4480
- slots[name] = slotInfo;
4606
+ slots = slots || {};
4607
+ slots[name] = { content: slotAst, on, attrs, scope };
4481
4608
  }
4482
4609
  }
4483
4610
  // default slot
4484
4611
  const defaultContent = parseChildNodes(clone, ctx);
4485
4612
  if (defaultContent) {
4486
- slots.default = { content: defaultContent };
4487
- if (defaultSlotScope) {
4488
- slots.default.scope = defaultSlotScope;
4489
- }
4613
+ slots = slots || {};
4614
+ slots.default = { content: defaultContent, on, attrs: null, scope: defaultSlotScope };
4490
4615
  }
4491
4616
  }
4492
- return { type: 11 /* TComponent */, name, isDynamic, dynamicProps, props, slots };
4617
+ return { type: 11 /* TComponent */, name, isDynamic, dynamicProps, props, slots, on };
4493
4618
  }
4494
4619
  // -----------------------------------------------------------------------------
4495
4620
  // Slots
@@ -4500,15 +4625,24 @@ function parseTSlot(node, ctx) {
4500
4625
  }
4501
4626
  const name = node.getAttribute("t-slot");
4502
4627
  node.removeAttribute("t-slot");
4503
- const attrs = {};
4628
+ let attrs = null;
4629
+ let on = null;
4504
4630
  for (let attributeName of node.getAttributeNames()) {
4505
4631
  const value = node.getAttribute(attributeName);
4506
- attrs[attributeName] = value;
4632
+ if (attributeName.startsWith("t-on-")) {
4633
+ on = on || {};
4634
+ on[attributeName.slice(5)] = value;
4635
+ }
4636
+ else {
4637
+ attrs = attrs || {};
4638
+ attrs[attributeName] = value;
4639
+ }
4507
4640
  }
4508
4641
  return {
4509
4642
  type: 14 /* TSlot */,
4510
4643
  name,
4511
4644
  attrs,
4645
+ on,
4512
4646
  defaultContent: parseChildNodes(node, ctx),
4513
4647
  };
4514
4648
  }
@@ -4768,20 +4902,22 @@ function onWillRender(fn) {
4768
4902
  const node = getCurrent();
4769
4903
  const renderFn = node.renderFn;
4770
4904
  const decorate = node.app.dev ? wrapError : (fn) => fn;
4771
- node.renderFn = decorate(() => {
4772
- fn.call(node.component);
4905
+ fn = decorate(fn.bind(node.component), "onWillRender");
4906
+ node.renderFn = () => {
4907
+ fn();
4773
4908
  return renderFn();
4774
- }, "onWillRender");
4909
+ };
4775
4910
  }
4776
4911
  function onRendered(fn) {
4777
4912
  const node = getCurrent();
4778
4913
  const renderFn = node.renderFn;
4779
4914
  const decorate = node.app.dev ? wrapError : (fn) => fn;
4780
- node.renderFn = decorate(() => {
4915
+ fn = decorate(fn.bind(node.component), "onRendered");
4916
+ node.renderFn = () => {
4781
4917
  const result = renderFn();
4782
- fn.call(node.component);
4918
+ fn();
4783
4919
  return result;
4784
- }, "onRendered");
4920
+ };
4785
4921
  }
4786
4922
  function onError(callback) {
4787
4923
  const node = getCurrent();
@@ -4883,7 +5019,7 @@ function callSlot(ctx, parent, key, name, dynamic, extra, defaultContent) {
4883
5019
  const { __render, __ctx, __scope } = slots[name] || {};
4884
5020
  const slotScope = Object.create(__ctx || {});
4885
5021
  if (__scope) {
4886
- slotScope[__scope] = extra || {};
5022
+ slotScope[__scope] = extra;
4887
5023
  }
4888
5024
  const slotBDom = __render ? __render.call(__ctx.__owl__.component, slotScope, parent, key) : null;
4889
5025
  if (defaultContent) {
@@ -5039,6 +5175,7 @@ const helpers = {
5039
5175
  LazyValue,
5040
5176
  safeOutput,
5041
5177
  bind,
5178
+ createCatcher,
5042
5179
  };
5043
5180
 
5044
5181
  const bdom = { text, createBlock, list, multi, html, toggler, component, comment };
@@ -5389,7 +5526,7 @@ exports.whenReady = whenReady;
5389
5526
  exports.xml = xml;
5390
5527
 
5391
5528
 
5392
- __info__.version = '2.0.0-beta.2';
5393
- __info__.date = '2022-03-03T15:22:11.176Z';
5394
- __info__.hash = 'e4fdd32';
5529
+ __info__.version = '2.0.0-beta.3';
5530
+ __info__.date = '2022-03-08T11:47:49.105Z';
5531
+ __info__.hash = 'c356351';
5395
5532
  __info__.url = 'https://github.com/odoo/owl';