From 66f4708643438900bd217e089210fb1f5c5c5bff Mon Sep 17 00:00:00 2001 From: jordangarcia Date: Fri, 4 Jul 2014 14:04:27 -0700 Subject: [PATCH] Allow template nodes to be passed as a template option Split the parsing/switching logic of the type of template option provided into the `template-parser` module and refactored `fragment` module to only convert string -> document fragment --- component.json | 3 +- src/fragment.js | 25 ++-------- src/main.js | 2 +- src/template-parser.js | 46 ++++++++++++++++++ src/utils.js | 9 +++- test/unit/specs/utils.js | 97 ++++++++++++++++++++++++++++++++------ test/unit/utils/prepare.js | 10 +++- 7 files changed, 151 insertions(+), 41 deletions(-) create mode 100644 src/template-parser.js diff --git a/component.json b/component.json index e842a254377..3aa245a2072 100644 --- a/component.json +++ b/component.json @@ -18,6 +18,7 @@ "src/observer.js", "src/directive.js", "src/exp-parser.js", + "src/template-parser.js", "src/text-parser.js", "src/deps-parser.js", "src/filters.js", @@ -34,4 +35,4 @@ "src/directives/partial.js", "src/directives/view.js" ] -} \ No newline at end of file +} diff --git a/src/fragment.js b/src/fragment.js index 7b9790a5535..eba460ee664 100644 --- a/src/fragment.js +++ b/src/fragment.js @@ -30,29 +30,12 @@ map.rect = [1, '','' var TAG_RE = /<([\w:]+)/ -module.exports = function (template) { - - if (typeof template !== 'string') { - return template - } - - // template by ID - if (template.charAt(0) === '#') { - var templateNode = document.getElementById(template.slice(1)) - if (!templateNode) return - // if its a template tag and the browser supports it, - // its content is already a document fragment! - if (templateNode.tagName === 'TEMPLATE' && templateNode.content) { - return templateNode.content - } - template = templateNode.innerHTML - } - +module.exports = function (templateString) { var frag = document.createDocumentFragment(), - m = TAG_RE.exec(template) + m = TAG_RE.exec(templateString) // text only if (!m) { - frag.appendChild(document.createTextNode(template)) + frag.appendChild(document.createTextNode(templateString)) return frag } @@ -63,7 +46,7 @@ module.exports = function (template) { suffix = wrap[2], node = document.createElement('div') - node.innerHTML = prefix + template.trim() + suffix + node.innerHTML = prefix + templateString.trim() + suffix while (depth--) node = node.lastChild // one element diff --git a/src/main.js b/src/main.js index 449a3bc3b1a..f011ea06f68 100644 --- a/src/main.js +++ b/src/main.js @@ -28,7 +28,7 @@ assetTypes.forEach(function (type) { } if (!value) return hash[id] if (type === 'partial') { - value = utils.toFragment(value) + value = utils.parseTemplateOption(value) } else if (type === 'component') { value = utils.toConstructor(value) } else if (type === 'filter') { diff --git a/src/template-parser.js b/src/template-parser.js new file mode 100644 index 00000000000..09b1d83e5ce --- /dev/null +++ b/src/template-parser.js @@ -0,0 +1,46 @@ +var toFragment = require('./fragment'); + +/** + * Parses a template string or node and normalizes it into a + * a node that can be used as a partial of a template option + * + * Possible values include + * id selector: '#some-template-id' + * template string: '
my template
' + * DocumentFragment object + * Node object of type Template + */ +module.exports = function(template) { + var templateNode; + + if (template instanceof window.DocumentFragment) { + // if the template is already a document fragment -- do nothing + return template + } + + if (typeof template === 'string') { + // template by ID + if (template.charAt(0) === '#') { + templateNode = document.getElementById(template.slice(1)) + if (!templateNode) return + } else { + return toFragment(template) + } + } else if (template.nodeType) { + templateNode = template + } else { + return + } + + // if its a template tag and the browser supports it, + // its content is already a document fragment! + if (templateNode.tagName === 'TEMPLATE' && templateNode.content) { + return templateNode.content + } + + if (templateNode.tagName === 'SCRIPT') { + return toFragment(templateNode.innerHTML) + } + + return toFragment(templateNode.outerHTML); +} diff --git a/src/utils.js b/src/utils.js index 09769528932..575cf37a486 100644 --- a/src/utils.js +++ b/src/utils.js @@ -32,6 +32,11 @@ var utils = module.exports = { */ toFragment: require('./fragment'), + /** + * Parse the various types of template options + */ + parseTemplateOption: require('./template-parser.js'), + /** * get a value from an object keypath */ @@ -226,7 +231,7 @@ var utils = module.exports = { } if (partials) { for (key in partials) { - partials[key] = utils.toFragment(partials[key]) + partials[key] = utils.parseTemplateOption(partials[key]) } } if (filters) { @@ -235,7 +240,7 @@ var utils = module.exports = { } } if (template) { - options.template = utils.toFragment(template) + options.template = utils.parseTemplateOption(template) } }, diff --git a/test/unit/specs/utils.js b/test/unit/specs/utils.js index 9557870a2d8..05db0077a49 100644 --- a/test/unit/specs/utils.js +++ b/test/unit/specs/utils.js @@ -1,4 +1,5 @@ describe('Utils', function () { + afterEach(cleanupMocks) var utils = require('vue/src/utils'), config = require('vue/src/config') @@ -215,7 +216,7 @@ describe('Utils', function () { describe('toFragment', function () { - it('should convert a string tempalte to a documentFragment', function () { + it('should convert a string template to a documentFragment', function () { var template = '
hi

ha

', frag = utils.toFragment(template) assert.ok(frag instanceof window.DocumentFragment) @@ -223,20 +224,6 @@ describe('Utils', function () { assert.equal(frag.querySelector('p').textContent, 'ha') }) - it('should also work if the string is an ID selector', function () { - var id = 'utils-template-to-fragment', - template = '
hi

ha

', - el = document.createElement('template') - el.id = id - el.innerHTML = template - document.getElementById('test').appendChild(el) - - var frag = utils.toFragment('#' + id) - assert.ok(frag instanceof window.DocumentFragment) - assert.equal(frag.querySelector('.a').textContent, 'hi') - assert.equal(frag.querySelector('p').textContent, 'ha') - }) - it('should work with table elements', function () { var frag = utils.toFragment('') assert.ok(frag instanceof window.DocumentFragment) @@ -252,6 +239,75 @@ describe('Utils', function () { }) + describe('parseTemplateOption', function () { + + afterEach(cleanupMocks) + + it('should convert a string template to a documentFragment', function () { + var template = '
hi

ha

', + frag = utils.parseTemplateOption(template) + assert.ok(frag instanceof window.DocumentFragment) + assert.equal(frag.querySelector('.a').textContent, 'hi') + assert.equal(frag.querySelector('p').textContent, 'ha') + }) + + describe('id selector', function() { + it('should work with a TEMPLATE tag', function() { + var id = 'utils-template-parse-template-option-template-tag', + template = '
hi

ha

', + el = document.createElement('template') + el.id = id + el.innerHTML = template + appendMock(el) + + var frag = utils.parseTemplateOption('#' + id) + assert.ok(frag instanceof window.DocumentFragment) + assert.equal(frag.querySelector('.a').textContent, 'hi') + assert.equal(frag.querySelector('p').textContent, 'ha') + }) + + it('should work with a DIV tag', function() { + var id = 'utils-template-parse-template-option-div-tag', + template = '
hi

ha

', + el = document.createElement('div') + el.id = id + el.innerHTML = template + appendMock(el) + + var frag = utils.parseTemplateOption('#' + id) + assert.ok(frag instanceof window.DocumentFragment) + assert.equal(frag.querySelector('.a').textContent, 'hi') + assert.equal(frag.querySelector('p').textContent, 'ha') + }) + }) + + describe('when passed a Node', function() { + it('should work with a TEMPLATE tag', function() { + var el = document.createElement('template') + el.innerHTML = '
hi

ha

' + + var frag = utils.parseTemplateOption(el) + assert.ok(frag instanceof window.DocumentFragment) + assert.equal(frag.querySelector('.a').textContent, 'hi') + assert.equal(frag.querySelector('p').textContent, 'ha') + }) + + it('should work with a DIV tag', function() { + var el = document.createElement('div') + el.innerHTML = 'hi

ha

' + + var frag = utils.parseTemplateOption(el) + assert.ok(frag instanceof window.DocumentFragment) + + assert.equal(frag.firstChild.outerHTML, el.outerHTML) + assert.equal(frag.querySelector('.a').textContent, 'hi') + assert.equal(frag.querySelector('p').textContent, 'ha') + }) + }) + + + }) + describe('toConstructor', function () { it('should convert an non-VM object to a VM constructor', function () { @@ -270,6 +326,17 @@ describe('Utils', function () { describe('processOptions', function () { + beforeEach(function() { + var id = 'utils-template-to-fragment', + template = '
hi

ha

', + el = document.createElement('template') + el.id = id + el.innerHTML = template + appendMock(el) + }) + + afterEach(cleanupMocks) + var options = { partials: { a: '#utils-template-to-fragment', diff --git a/test/unit/utils/prepare.js b/test/unit/utils/prepare.js index 5aafad2e7f7..5304449aa49 100644 --- a/test/unit/utils/prepare.js +++ b/test/unit/utils/prepare.js @@ -7,10 +7,18 @@ function mock (id, html, attrs) { el.setAttribute(attr, attrs[attr]) } } - document.getElementById('test').appendChild(el) + appendMock(el) return el } +function appendMock(el) { + document.getElementById('test').appendChild(el) +} + +function cleanupMocks() { + document.getElementById('test').innerHTML = '' +} + function mockHTMLEvent (type) { var e = document.createEvent('HTMLEvents') e.initEvent(type, true, true)