| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- var _ = require('../util')
- var Cache = require('../cache')
- var templateCache = new Cache(1000)
- var idSelectorCache = new Cache(1000)
- var map = {
- _default: [0, '', ''],
- legend: [1, '<fieldset>', '</fieldset>'],
- tr: [2, '<table><tbody>', '</tbody></table>'],
- col: [
- 2,
- '<table><tbody></tbody><colgroup>',
- '</colgroup></table>'
- ]
- }
- map.td =
- map.th = [
- 3,
- '<table><tbody><tr>',
- '</tr></tbody></table>'
- ]
- map.option =
- map.optgroup = [
- 1,
- '<select multiple="multiple">',
- '</select>'
- ]
- map.thead =
- map.tbody =
- map.colgroup =
- map.caption =
- map.tfoot = [1, '<table>', '</table>']
- map.g =
- map.defs =
- map.symbol =
- map.use =
- map.image =
- map.text =
- map.circle =
- map.ellipse =
- map.line =
- map.path =
- map.polygon =
- map.polyline =
- map.rect = [
- 1,
- '<svg ' +
- 'xmlns="http://www.w3.org/2000/svg" ' +
- 'xmlns:xlink="http://www.w3.org/1999/xlink" ' +
- 'xmlns:ev="http://www.w3.org/2001/xml-events"' +
- 'version="1.1">',
- '</svg>'
- ]
- var tagRE = /<([\w:]+)/
- var entityRE = /&\w+;/
- /**
- * Convert a string template to a DocumentFragment.
- * Determines correct wrapping by tag types. Wrapping
- * strategy found in jQuery & component/domify.
- *
- * @param {String} templateString
- * @return {DocumentFragment}
- */
- function stringToFragment (templateString) {
- // try a cache hit first
- var hit = templateCache.get(templateString)
- if (hit) {
- return hit
- }
- var frag = document.createDocumentFragment()
- var tagMatch = templateString.match(tagRE)
- var entityMatch = entityRE.test(templateString)
- if (!tagMatch && !entityMatch) {
- // text only, return a single text node.
- frag.appendChild(
- document.createTextNode(templateString)
- )
- } else {
- var tag = tagMatch && tagMatch[1]
- var wrap = map[tag] || map._default
- var depth = wrap[0]
- var prefix = wrap[1]
- var suffix = wrap[2]
- var node = document.createElement('div')
- node.innerHTML = prefix + templateString.trim() + suffix
- while (depth--) {
- node = node.lastChild
- }
- var child
- /* eslint-disable no-cond-assign */
- while (child = node.firstChild) {
- /* eslint-enable no-cond-assign */
- frag.appendChild(child)
- }
- }
- templateCache.put(templateString, frag)
- return frag
- }
- /**
- * Convert a template node to a DocumentFragment.
- *
- * @param {Node} node
- * @return {DocumentFragment}
- */
- function nodeToFragment (node) {
- // if its a template tag and the browser supports it,
- // its content is already a document fragment.
- if (
- _.isTemplate(node) &&
- node.content instanceof DocumentFragment
- ) {
- return node.content
- }
- // script template
- if (node.tagName === 'SCRIPT') {
- return stringToFragment(node.textContent)
- }
- // normal node, clone it to avoid mutating the original
- var clone = exports.clone(node)
- var frag = document.createDocumentFragment()
- var child
- /* eslint-disable no-cond-assign */
- while (child = clone.firstChild) {
- /* eslint-enable no-cond-assign */
- frag.appendChild(child)
- }
- return frag
- }
- // Test for the presence of the Safari template cloning bug
- // https://bugs.webkit.org/show_bug.cgi?id=137755
- var hasBrokenTemplate = _.inBrowser
- ? (function () {
- var a = document.createElement('div')
- a.innerHTML = '<template>1</template>'
- return !a.cloneNode(true).firstChild.innerHTML
- })()
- : false
- // Test for IE10/11 textarea placeholder clone bug
- var hasTextareaCloneBug = _.inBrowser
- ? (function () {
- var t = document.createElement('textarea')
- t.placeholder = 't'
- return t.cloneNode(true).value === 't'
- })()
- : false
- /**
- * 1. Deal with Safari cloning nested <template> bug by
- * manually cloning all template instances.
- * 2. Deal with IE10/11 textarea placeholder bug by setting
- * the correct value after cloning.
- *
- * @param {Element|DocumentFragment} node
- * @return {Element|DocumentFragment}
- */
- exports.clone = function (node) {
- var res = node.cloneNode(true)
- var i, original, cloned
- /* istanbul ignore if */
- if (hasBrokenTemplate) {
- original = node.querySelectorAll('template')
- if (original.length) {
- cloned = res.querySelectorAll('template')
- i = cloned.length
- while (i--) {
- cloned[i].parentNode.replaceChild(
- original[i].cloneNode(true),
- cloned[i]
- )
- }
- }
- }
- /* istanbul ignore if */
- if (hasTextareaCloneBug) {
- if (node.tagName === 'TEXTAREA') {
- res.value = node.value
- } else {
- original = node.querySelectorAll('textarea')
- if (original.length) {
- cloned = res.querySelectorAll('textarea')
- i = cloned.length
- while (i--) {
- cloned[i].value = original[i].value
- }
- }
- }
- }
- return res
- }
- /**
- * Process the template option and normalizes it into a
- * a DocumentFragment that can be used as a partial or a
- * instance template.
- *
- * @param {*} template
- * Possible values include:
- * - DocumentFragment object
- * - Node object of type Template
- * - id selector: '#some-template-id'
- * - template string: '<div><span>{{msg}}</span></div>'
- * @param {Boolean} clone
- * @param {Boolean} noSelector
- * @return {DocumentFragment|undefined}
- */
- exports.parse = function (template, clone, noSelector) {
- var node, frag
- // if the template is already a document fragment,
- // do nothing
- if (template instanceof DocumentFragment) {
- return clone
- ? template.cloneNode(true)
- : template
- }
- if (typeof template === 'string') {
- // id selector
- if (!noSelector && template.charAt(0) === '#') {
- // id selector can be cached too
- frag = idSelectorCache.get(template)
- if (!frag) {
- node = document.getElementById(template.slice(1))
- if (node) {
- frag = nodeToFragment(node)
- // save selector to cache
- idSelectorCache.put(template, frag)
- }
- }
- } else {
- // normal string template
- frag = stringToFragment(template)
- }
- } else if (template.nodeType) {
- // a direct node
- frag = nodeToFragment(template)
- }
- return frag && clone
- ? exports.clone(frag)
- : frag
- }
|