|
470 | 470 | var content = this.ref.content; |
471 | 471 | var map = content.bindingMap_; |
472 | 472 | if (!map) { |
| 473 | + var delegatePrepareBindingFn = |
| 474 | + delegate && typeof delegate.prepareBinding === 'function' ? |
| 475 | + delegate.prepareBinding : undefined; |
473 | 476 | // TODO(rafaelw): Setup a MutationObserver on content to detect |
474 | 477 | // when the instanceMap is invalid. |
475 | | - map = createInstanceBindingMap(content) || []; |
| 478 | + map = createInstanceBindingMap(content, delegatePrepareBindingFn) || []; |
476 | 479 | content.bindingMap_ = map; |
477 | 480 | } |
478 | 481 |
|
479 | 482 | var instance = map.hasSubTemplate ? |
480 | 483 | deepCloneIgnoreTemplateContent(content) : content.cloneNode(true); |
481 | 484 |
|
482 | | - var delegateGetBindingFn = |
483 | | - delegate && typeof delegate.getBinding === 'function' ? |
484 | | - delegate.getBinding : undefined; |
485 | | - |
486 | | - addMapBindings(instance, map, model, delegate, delegateGetBindingFn, |
487 | | - bound); |
| 485 | + addMapBindings(instance, map, model, delegate, bound); |
488 | 486 | // TODO(rafaelw): We can do this more lazily, but setting a sentinel |
489 | 487 | // in the parent of the template element, and creating it when it's |
490 | 488 | // asked for by walking back to find the iterating template. |
|
532 | 530 |
|
533 | 531 | // Returns |
534 | 532 | // a) undefined if there are no mustaches. |
535 | | - // b) [TEXT, (PATH, TEXT)+] if there is at least one mustache. |
536 | | - function parseMustaches(s) { |
| 533 | + // b) [TEXT, (PATH, DELEGATE_FN, TEXT)+] if there is at least one mustache. |
| 534 | + function parseMustaches(s, name, node, delegatePrepareBindingFn) { |
537 | 535 | if (!s || !s.length) |
538 | 536 | return; |
539 | 537 |
|
|
554 | 552 |
|
555 | 553 | tokens = tokens || []; |
556 | 554 | tokens.push(s.slice(lastIndex, startIndex)); // TEXT |
557 | | - tokens.push(s.slice(startIndex + 2, endIndex).trim()); // PATH |
| 555 | + var pathString = s.slice(startIndex + 2, endIndex).trim(); |
| 556 | + tokens.push(Path.get(pathString)); // PATH |
| 557 | + var delegateFn = delegatePrepareBindingFn && |
| 558 | + delegatePrepareBindingFn(pathString, name, node) |
| 559 | + tokens.push(delegateFn); // DELEGATE_FN |
558 | 560 | lastIndex = endIndex + 2; |
559 | 561 | } |
560 | 562 |
|
561 | 563 | if (lastIndex === length) |
562 | 564 | tokens.push(''); // TEXT |
563 | 565 |
|
564 | | - tokens.hasOnePath = tokens.length === 3; |
| 566 | + tokens.hasOnePath = tokens.length === 4; |
565 | 567 | tokens.isSimplePath = tokens.hasOnePath && |
566 | 568 | tokens[0] == '' && |
567 | | - tokens[2] == ''; |
| 569 | + tokens[3] == ''; |
568 | 570 |
|
569 | 571 | tokens.combinator = function(values) { |
570 | 572 | var newValue = tokens[0]; |
571 | 573 |
|
572 | | - for (var i = 1; i < tokens.length; i += 2) { |
573 | | - var value = tokens.hasOnePath ? values : values[(i-1)/ 2]; |
| 574 | + for (var i = 1; i < tokens.length; i += 3) { |
| 575 | + var value = tokens.hasOnePath ? values : values[(i - 1) / 3]; |
574 | 576 | if (value !== undefined) |
575 | 577 | newValue += value; |
576 | | - newValue += tokens[i + 1]; |
| 578 | + newValue += tokens[i + 2]; |
577 | 579 | } |
578 | 580 |
|
579 | 581 | return newValue; |
|
582 | 584 | return tokens; |
583 | 585 | } |
584 | 586 |
|
585 | | - function processBindings(bindings, node, model, delegateGetBindingFn, bound) { |
| 587 | + var valuePath = Path.get('value'); |
| 588 | + |
| 589 | + function processBindings(bindings, node, model, bound) { |
586 | 590 | for (var i = 0; i < bindings.length; i += 2) { |
587 | 591 | var name = bindings[i]; |
588 | 592 | var tokens = bindings[i + 1]; |
589 | 593 | var bindingModel = model; |
590 | 594 | var bindingPath = tokens[1]; |
591 | 595 | if (tokens.hasOnePath) { |
592 | | - var dm = delegateGetBindingFn && delegateGetBindingFn(bindingModel, |
593 | | - bindingPath, |
594 | | - name, |
595 | | - node); |
596 | | - if (dm !== undefined) { |
597 | | - bindingModel = dm; |
598 | | - bindingPath = 'value'; |
| 596 | + var delegateFn = tokens[2]; |
| 597 | + var delegateBinding = delegateFn && delegateFn(model, name, node); |
| 598 | + |
| 599 | + if (delegateBinding !== undefined) { |
| 600 | + bindingModel = delegateBinding; |
| 601 | + bindingPath = valuePath; |
599 | 602 | } |
600 | 603 |
|
601 | 604 | if (!tokens.isSimplePath) { |
602 | 605 | bindingModel = new PathObserver(bindingModel, bindingPath, undefined, |
603 | 606 | undefined, |
604 | 607 | undefined, |
605 | 608 | tokens.combinator); |
606 | | - bindingPath = 'value'; |
| 609 | + bindingPath = valuePath; |
607 | 610 | } |
608 | 611 | } else { |
609 | 612 | var observer = new CompoundPathObserver(undefined, |
|
612 | 615 | tokens.combinator); |
613 | 616 |
|
614 | 617 |
|
615 | | - for (var i = 1; i < tokens.length; i = i + 2) { |
| 618 | + for (var i = 1; i < tokens.length; i = i + 3) { |
616 | 619 | var subModel = model; |
617 | 620 | var subPath = tokens[i]; |
618 | | - var dm = delegateGetBindingFn && delegateGetBindingFn(subModel, |
619 | | - subPath, |
620 | | - name, |
621 | | - node); |
622 | | - if (dm !== undefined) { |
623 | | - subModel = dm; |
624 | | - subPath = 'value'; |
| 621 | + var delegateFn = tokens[i + 1]; |
| 622 | + var delegateBinding = delegateFn && delegateFn(subModel, name, node); |
| 623 | + |
| 624 | + if (delegateBinding !== undefined) { |
| 625 | + subModel = delegateBinding; |
| 626 | + subPath = valuePath; |
625 | 627 | } |
626 | 628 |
|
627 | 629 | observer.addPath(subModel, subPath); |
628 | 630 | } |
629 | 631 |
|
630 | 632 | observer.start(); |
631 | 633 | bindingModel = observer; |
632 | | - bindingPath = 'value'; |
| 634 | + bindingPath = valuePath; |
633 | 635 | } |
634 | 636 |
|
635 | 637 | var binding = node.bind(name, bindingModel, bindingPath); |
|
638 | 640 | } |
639 | 641 | } |
640 | 642 |
|
641 | | - function parseAttributeBindings(element) { |
| 643 | + function parseAttributeBindings(element, delegatePrepareBindingFn) { |
642 | 644 | assert(element); |
643 | 645 |
|
644 | 646 | var bindings; |
|
661 | 663 | } |
662 | 664 | } |
663 | 665 |
|
664 | | - var tokens = parseMustaches(value); |
| 666 | + var tokens = parseMustaches(value, name, element, |
| 667 | + delegatePrepareBindingFn); |
665 | 668 | if (!tokens) |
666 | 669 | continue; |
667 | 670 |
|
|
672 | 675 | // Treat <template if> as <template bind if> |
673 | 676 | if (ifFound && !bindFound) { |
674 | 677 | bindings = bindings || []; |
675 | | - bindings.push(BIND, parseMustaches('{{}}')); |
| 678 | + bindings.push(BIND, parseMustaches('{{}}', BIND, element, |
| 679 | + delegatePrepareBindingFn)); |
676 | 680 | } |
677 | 681 |
|
678 | 682 | return bindings; |
679 | 683 | } |
680 | 684 |
|
681 | | - function getBindings(node) { |
| 685 | + function getBindings(node, delegatePrepareBindingFn) { |
682 | 686 | if (node.nodeType === Node.ELEMENT_NODE) |
683 | | - return parseAttributeBindings(node); |
| 687 | + return parseAttributeBindings(node, delegatePrepareBindingFn); |
684 | 688 |
|
685 | 689 | if (node.nodeType === Node.TEXT_NODE) { |
686 | | - var tokens = parseMustaches(node.data); |
| 690 | + var tokens = parseMustaches(node.data, 'textContent', node, |
| 691 | + delegatePrepareBindingFn); |
687 | 692 | if (tokens) |
688 | 693 | return ['textContent', tokens]; |
689 | 694 | } |
690 | 695 | } |
691 | 696 |
|
692 | | - function addMapBindings(node, bindings, model, delegate, delegateGetBindingFn, |
693 | | - bound) { |
| 697 | + function addMapBindings(node, bindings, model, delegate, bound) { |
694 | 698 | if (!bindings) |
695 | 699 | return; |
696 | 700 |
|
|
702 | 706 | } |
703 | 707 |
|
704 | 708 | if (bindings.length) |
705 | | - processBindings(bindings, node, model, delegateGetBindingFn, bound); |
| 709 | + processBindings(bindings, node, model, bound); |
706 | 710 |
|
707 | 711 | if (!bindings.children) |
708 | 712 | return; |
709 | 713 |
|
710 | 714 | var i = 0; |
711 | 715 | for (var child = node.firstChild; child; child = child.nextSibling) { |
712 | | - addMapBindings(child, bindings.children[i++], model, delegate, |
713 | | - delegateGetBindingFn, |
714 | | - bound); |
| 716 | + addMapBindings(child, bindings.children[i++], model, delegate, bound); |
715 | 717 | } |
716 | 718 | } |
717 | 719 |
|
718 | 720 | function addBindings(node, model, delegate) { |
719 | 721 | assert(node); |
720 | 722 |
|
721 | | - var delegateGetBindingFn = |
722 | | - delegate && typeof delegate.getBinding === 'function' ? |
723 | | - delegate.getBinding : undefined; |
| 723 | + var delegatePrepareBindingFn = |
| 724 | + delegate && typeof delegate.prepareBinding === 'function' ? |
| 725 | + delegate.prepareBinding : undefined; |
724 | 726 |
|
725 | | - var bindings = getBindings(node); |
| 727 | + var bindings = getBindings(node, delegatePrepareBindingFn); |
726 | 728 | if (bindings) |
727 | | - processBindings(bindings, node, model, delegateGetBindingFn); |
| 729 | + processBindings(bindings, node, model); |
728 | 730 |
|
729 | 731 | for (var child = node.firstChild; child ; child = child.nextSibling) |
730 | 732 | addBindings(child, model, delegate); |
|
743 | 745 | return clone; |
744 | 746 | } |
745 | 747 |
|
746 | | - function createInstanceBindingMap(node) { |
747 | | - var map = getBindings(node); |
| 748 | + function createInstanceBindingMap(node, delegatePrepareBindingFn) { |
| 749 | + var map = getBindings(node, delegatePrepareBindingFn); |
748 | 750 | if (isTemplate(node)) { |
749 | 751 | map = map || []; |
750 | 752 | map.templateRef = node; |
|
753 | 755 |
|
754 | 756 | var child = node.firstChild, index = 0; |
755 | 757 | for (; child; child = child.nextSibling, index++) { |
756 | | - var childMap = createInstanceBindingMap(child); |
| 758 | + var childMap = createInstanceBindingMap(child, delegatePrepareBindingFn); |
757 | 759 | if (!childMap) |
758 | 760 | continue; |
759 | 761 |
|
|
0 commit comments