Skip to content

Commit 58dd07e

Browse files
committed
{{{ }}} tags for unescaped html
1 parent 1c6dacf commit 58dd07e

File tree

7 files changed

+97
-15
lines changed

7 files changed

+97
-15
lines changed

component.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
"src/directives/repeat.js",
2828
"src/directives/on.js",
2929
"src/directives/model.js",
30-
"src/directives/with.js"
30+
"src/directives/with.js",
31+
"src/directives/html.js"
3132
],
3233
"dependencies": {
3334
"component/emitter": "*"

src/compiler.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ CompilerProto.compileTextNode = function (node) {
357357

358358
for (var i = 0, l = tokens.length; i < l; i++) {
359359
token = tokens[i]
360+
directive = null
360361
if (token.key) { // a binding
361362
if (token.key.charAt(0) === '>') { // a partial
362363
partialId = token.key.slice(1).trim()
@@ -368,14 +369,12 @@ CompilerProto.compileTextNode = function (node) {
368369
partialNodes = slice.call(el.childNodes)
369370
}
370371
} else { // a real binding
371-
if (!token.html) // text binding
372+
if (!token.html) { // text binding
372373
el = document.createTextNode('')
373374
directive = Directive.parse('text', token.key, this, el)
374-
if (directive) {
375-
this.bindDirective(directive)
376-
}
377375
} else { // html binding
378-
376+
el = document.createComment(config.prefix + '-html')
377+
directive = Directive.parse('html', token.key, this, el)
379378
}
380379
}
381380
} else { // a plain string
@@ -384,6 +383,9 @@ CompilerProto.compileTextNode = function (node) {
384383

385384
// insert node
386385
node.parentNode.insertBefore(el, node)
386+
if (directive) {
387+
this.bindDirective(directive)
388+
}
387389

388390
// compile partial after appending, because its children's parentNode
389391
// will change from the fragment to the correct parentNode.

src/directives/html.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
var toText = require('../utils').toText,
2+
slice = Array.prototype.slice
3+
4+
module.exports = {
5+
6+
bind: function () {
7+
// a comment node means this is a binding for
8+
// {{{ inline unescaped html }}}
9+
if (this.el.nodeType === 8) {
10+
// hold nodes
11+
this.holder = document.createElement('div')
12+
this.nodes = []
13+
}
14+
},
15+
16+
update: function (value) {
17+
value = toText(value)
18+
if (this.holder) {
19+
this.swap(value)
20+
} else {
21+
this.el.innerHTML = value
22+
}
23+
},
24+
25+
swap: function (value) {
26+
var parent = this.el.parentNode,
27+
holder = this.holder,
28+
nodes = this.nodes,
29+
i = nodes.length, l
30+
while (i--) {
31+
parent.removeChild(nodes[i])
32+
}
33+
holder.innerHTML = value
34+
nodes = this.nodes = slice.call(holder.childNodes)
35+
for (i = 0, l = nodes.length; i < l; i++) {
36+
parent.insertBefore(nodes[i], this.el)
37+
}
38+
}
39+
}

src/directives/index.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module.exports = {
88
model : require('./model'),
99
'if' : require('./if'),
1010
'with' : require('./with'),
11+
html : require('./html'),
1112

1213
attr: function (value) {
1314
this.el.setAttribute(this.arg, value)
@@ -17,10 +18,6 @@ module.exports = {
1718
this.el.textContent = utils.toText(value)
1819
},
1920

20-
html: function (value) {
21-
this.el.innerHTML = utils.toText(value)
22-
},
23-
2421
show: function (value) {
2522
var el = this.el,
2623
target = value ? '' : 'none',

test/functional/fixtures/expression.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<button v-on="click: noMsg = 'Nah'" class="change">change</button>
2525
</div>
2626
<div id="attrs" data-test="hi {{msg}} ha"></div>
27+
<div id="html">html {{{html}}} work</div>
2728
<script>
2829
Vue.config({debug:true})
2930

@@ -63,6 +64,13 @@
6364
msg: 'ho'
6465
}
6566
})
67+
68+
var html = new Vue({
69+
el: '#html',
70+
data: {
71+
html: '<p>should</p> <a>probably</a>'
72+
}
73+
})
6674
</script>
6775
</body>
6876
</html>

test/functional/specs/expression.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
/* global normal, attrs */
1+
/* global normal, attrs, html */
22

3-
casper.test.begin('Expression', 21, function (test) {
3+
casper.test.begin('Expression', 23, function (test) {
44

55
casper
66
.start('./fixtures/expression.html')
@@ -75,9 +75,22 @@ casper.test.begin('Expression', 21, function (test) {
7575
attrs.msg = 'hoho'
7676
})
7777
.then(function () {
78-
test.assertEval(function () {
79-
return document.getElementById('attrs').dataset.test === 'hi hoho ha'
80-
})
78+
// attr
79+
test.assertEvalEquals(function () {
80+
return document.getElementById('attrs').dataset.test
81+
}, 'hi hoho ha')
82+
// html
83+
test.assertEvalEquals(function () {
84+
return document.getElementById('html').innerHTML
85+
}, 'html <p>should</p> <a>probably</a><!--v-html--> work')
86+
})
87+
.thenEvaluate(function () {
88+
html.html = '<span>should</span> <a>change work</a>'
89+
})
90+
.then(function () {
91+
test.assertEvalEquals(function () {
92+
return document.getElementById('html').innerHTML
93+
}, 'html <span>should</span> <a>change work</a><!--v-html--> work')
8194
})
8295
.run(function () {
8396
test.done()

test/unit/specs/directives.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,28 @@ describe('UNIT: Directives', function () {
7676
assert.strictEqual(dir.el.innerHTML, '')
7777
})
7878

79+
it('should swap html if el is a comment placeholder', function () {
80+
var dir = mockDirective('html'),
81+
comment = document.createComment('hi'),
82+
parent = dir.el
83+
parent.innerHTML = 'what!'
84+
parent.appendChild(comment)
85+
dir.el = comment
86+
87+
dir.bind()
88+
assert.ok(dir.holder)
89+
assert.ok(dir.nodes)
90+
91+
var pre = 'what!',
92+
after = '<!--hi-->',
93+
h1 = '<span>hello</span><span>world</span>',
94+
h2 = '<a>whats</a><a>up</a>'
95+
dir.update(h1)
96+
assert.strictEqual(parent.innerHTML, pre + h1 + after)
97+
dir.update(h2)
98+
assert.strictEqual(parent.innerHTML, pre + h2 + after)
99+
})
100+
79101
})
80102

81103
describe('show', function () {

0 commit comments

Comments
 (0)