Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an escape sequence to avoid polymer to bind the content of brackets #3529

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 49 additions & 37 deletions lib/mixins/property-effects.html
Original file line number Diff line number Diff line change
Expand Up @@ -788,7 +788,7 @@
'(?:' + ARGUMENTS + '?' + ')' +
'\\)\\s*' + ')';
const BINDING = '(' + IDENT + '\\s*' + ARGUMENT_LIST + '?' + ')'; // Group 3
const OPEN_BRACKET = '(\\[\\[|{{)' + '\\s*';
const OPEN_BRACKET = '(\\[\\[\\\\*|{{\\\\*)' + '\\s*';
const CLOSE_BRACKET = '(?:]]|}})';
const NEGATE = '(?:(!)\\s*)?'; // Group 2
const EXPRESSION = OPEN_BRACKET + NEGATE + BINDING + CLOSE_BRACKET;
Expand Down Expand Up @@ -2290,51 +2290,63 @@
let parts = [];
let lastIndex = 0;
let m;
// Example: "literal1{{prop}}literal2[[!compute(foo,bar)]]final"
// Regex matches:
// Iteration 1: Iteration 2:
// m[1]: '{{' '[['
// m[2]: '' '!'
// m[3]: 'prop' 'compute(foo,bar)'
// Example: "literal1{{prop}}literal2[[!compute(foo,bar)]]literal3{{\!prop}}final"
// Regex matches:
// Iteration 1: Iteration 2: Iteration 3:
// m[1]: '{{' '[[' '{{\'
// m[2]: '' '!' '!'
// m[3]: 'prop' 'compute(foo,bar)' 'prop'
while ((m = bindingRegex.exec(text)) !== null) {
// Add literal part
if (m.index > lastIndex) {
parts.push({literal: text.slice(lastIndex, m.index)});
}
// Add binding part
let mode = m[1][0];
let negate = Boolean(m[2]);
let source = m[3].trim();
let customEvent, notifyEvent, colon;
if (mode == '{' && (colon = source.indexOf('::')) > 0) {
notifyEvent = source.substring(colon + 2);
source = source.substring(0, colon);
customEvent = true;
}
let signature = parseMethod(source);
let dependencies = [];
if (signature) {
// Inline computed function
let {args, methodName} = signature;
for (let i=0; i<args.length; i++) {
let arg = args[i];
if (!arg.literal) {
dependencies.push(arg);
}

// Check if the expression inside brackets must be treated as literal
const treatAsLiteral = m[1][2] === '\\';

if (!treatAsLiteral) {
// Add binding part
let mode = m[1][0];
let negate = Boolean(m[2]);
let source = m[3].trim();
let customEvent, notifyEvent, colon;
if (mode == '{' && (colon = source.indexOf('::')) > 0) {
notifyEvent = source.substring(colon + 2);
source = source.substring(0, colon);
customEvent = true;
}
let dynamicFns = templateInfo.dynamicFns;
if (dynamicFns && dynamicFns[methodName] || signature.static) {
dependencies.push(methodName);
signature.dynamicFn = true;
let signature = parseMethod(source);
let dependencies = [];
if (signature) {
// Inline computed function
let {args, methodName} = signature;
for (let i=0; i<args.length; i++) {
let arg = args[i];
if (!arg.literal) {
dependencies.push(arg);
}
}
let dynamicFns = templateInfo.dynamicFns;
if (dynamicFns && dynamicFns[methodName] || signature.static) {
dependencies.push(methodName);
signature.dynamicFn = true;
}
} else {
// Property or path
dependencies.push(source);
}
parts.push({
source, mode, negate, customEvent, signature, dependencies,
event: notifyEvent
});
} else {
// Property or path
dependencies.push(source);
// Add expression as literal
let literalExpression = text.slice(m.index, bindingRegex.lastIndex);
// Remove a slash
literalExpression = literalExpression.slice(0, 2) + literalExpression.slice(3);
parts.push({literal: literalExpression});
}
parts.push({
source, mode, negate, customEvent, signature, dependencies,
event: notifyEvent
});
lastIndex = bindingRegex.lastIndex;
}
// Add a final literal part
Expand Down
21 changes: 20 additions & 1 deletion test/unit/dom-bind.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
</dom-bind>

<script>
/* global earlyDomBind earlyBoundChild declaredXBasic1 declaredXBasic2 declarativeDomBind boundTextDiv container needsHost nonUpgrade*/
/* global earlyDomBind earlyBoundChild declaredXBasic1 declaredXBasic2 declarativeDomBind boundTextDiv escapedDoubleMustacheTextDiv1 escapedDoubleMustacheTextDiv2 escapedDoubleMustacheTextDiv3 escapedDoubleMustacheTextDiv4 escapedDoubleBracketsTextDiv1 escapedDoubleBracketsTextDiv2 escapedDoubleBracketsTextDiv3 escapedDoubleBracketsTextDiv4 container needsHost nonUpgrade*/
earlyDomBind.value = 'hi!';
</script>

Expand All @@ -36,6 +36,14 @@
<x-basic id="declaredXBasic2" value="{{value}}" notifyingvalue="{{nvalue}}"></x-basic>
<x-produce-a bind-to-text={{boundText}}></x-produce-a>
<div id="boundTextDiv">{{boundText}}</div>
<div id="escapedDoubleMustacheTextDiv1">{{\boundText}}</div>
<div id="escapedDoubleMustacheTextDiv2">{{\\boundText}}</div>
<div id="escapedDoubleMustacheTextDiv3">{{\!boundText}}</div>
<div id="escapedDoubleMustacheTextDiv4">{{\\!computed(whatever)}}</div>
<div id="escapedDoubleBracketsTextDiv1">[[\boundText]]</div>
<div id="escapedDoubleBracketsTextDiv2">[[\\boundText]]</div>
<div id="escapedDoubleBracketsTextDiv3">[[\!boundText]]</div>
<div id="escapedDoubleBracketsTextDiv4">[[\\!computed(whatever)]]</div>
</template>
</dom-bind>

Expand Down Expand Up @@ -113,6 +121,17 @@
assert.equal(boundTextDiv.textContent, 'this text is bound');
});

test('escaped brackets are treated like literals with one backslash removed', function() {
assert.equal(escapedDoubleMustacheTextDiv1.textContent, '{{boundText}}');
assert.equal(escapedDoubleMustacheTextDiv2.textContent, '{{\\boundText}}');
assert.equal(escapedDoubleMustacheTextDiv3.textContent, '{{!boundText}}');
assert.equal(escapedDoubleMustacheTextDiv4.textContent, '{{\\!computed(whatever)}}');
assert.equal(escapedDoubleBracketsTextDiv1.textContent, '[[boundText]]');
assert.equal(escapedDoubleBracketsTextDiv2.textContent, '[[\\boundText]]');
assert.equal(escapedDoubleBracketsTextDiv3.textContent, '[[!boundText]]');
assert.equal(escapedDoubleBracketsTextDiv4.textContent, '[[\\!computed(whatever)]]');
});

});

suite('imperative dom-bind', function() {
Expand Down