var _ = require('../util')
var Cache = require('../cache')
var templateCache = new Cache(1000)
var idSelectorCache = new Cache(1000)
var map = {
_default : [0, '', ''],
legend : [1, '
']
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,
''
]
var TAG_RE = /<([\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 = TAG_RE.exec(templateString)
if (!tagMatch) {
// text only, return a single text node.
frag.appendChild(
document.createTextNode(templateString)
)
} else {
var tag = 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
/* jshint boss:true */
while (child = node.firstChild) {
frag.appendChild(child)
}
}
templateCache.put(templateString, frag)
return frag
}
/**
* Convert a template node to a DocumentFragment.
*
* @param {Node} node
* @return {DocumentFragment}
*/
function nodeToFragment (node) {
var tag = node.tagName
// if its a template tag and the browser supports it,
// its content is already a document fragment.
if (
tag === 'TEMPLATE' &&
node.content instanceof DocumentFragment
) {
return node.content
}
return tag === 'SCRIPT'
? stringToFragment(node.textContent)
: stringToFragment(node.innerHTML)
}
// 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 = '1'
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 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: '
{{msg}}
'
* @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
}