template.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. var Cache = require('../cache')
  2. var templateCache = new Cache(100)
  3. var map = {
  4. _default : [0, '', ''],
  5. legend : [1, '<fieldset>', '</fieldset>'],
  6. tr : [2, '<table><tbody>', '</tbody></table>'],
  7. col : [
  8. 2,
  9. '<table><tbody></tbody><colgroup>',
  10. '</colgroup></table>'
  11. ]
  12. }
  13. map.td =
  14. map.th = [
  15. 3,
  16. '<table><tbody><tr>',
  17. '</tr></tbody></table>'
  18. ]
  19. map.option =
  20. map.optgroup = [
  21. 1,
  22. '<select multiple="multiple">',
  23. '</select>'
  24. ]
  25. map.thead =
  26. map.tbody =
  27. map.colgroup =
  28. map.caption =
  29. map.tfoot = [1, '<table>', '</table>']
  30. map.g =
  31. map.defs =
  32. map.symbol =
  33. map.use =
  34. map.image =
  35. map.text =
  36. map.circle =
  37. map.ellipse =
  38. map.line =
  39. map.path =
  40. map.polygon =
  41. map.polyline =
  42. map.rect = [
  43. 1,
  44. '<svg ' +
  45. 'xmlns="http://www.w3.org/2000/svg" ' +
  46. 'xmlns:xlink="http://www.w3.org/1999/xlink" ' +
  47. 'xmlns:ev="http://www.w3.org/2001/xml-events"' +
  48. 'version="1.1">',
  49. '</svg>'
  50. ]
  51. var TAG_RE = /<([\w:]+)/
  52. /**
  53. * Convert a string template to a DocumentFragment.
  54. * Determines correct wrapping by tag types. Wrapping
  55. * strategy found in jQuery & component/domify.
  56. *
  57. * @param {String} templateString
  58. * @return {DocumentFragment}
  59. */
  60. function stringToFragment (templateString) {
  61. // try a cache hit first
  62. var hit = templateCache.get(templateString)
  63. if (hit) {
  64. return hit
  65. }
  66. var frag = document.createDocumentFragment()
  67. var tagMatch = TAG_RE.exec(templateString)
  68. if (!tagMatch) {
  69. // text only, return a single text node.
  70. frag.appendChild(
  71. document.createTextNode(templateString)
  72. )
  73. } else {
  74. var tag = tagMatch[1]
  75. var wrap = map[tag] || map._default
  76. var depth = wrap[0]
  77. var prefix = wrap[1]
  78. var suffix = wrap[2]
  79. var node = document.createElement('div')
  80. node.innerHTML = prefix + templateString.trim() + suffix
  81. while (depth--) {
  82. node = node.lastChild
  83. }
  84. var child
  85. /* jshint boss:true */
  86. while (child = node.firstChild) {
  87. frag.appendChild(child)
  88. }
  89. }
  90. templateCache.put(templateString, frag)
  91. return frag
  92. }
  93. /**
  94. * Convert a template node to a DocumentFragment.
  95. *
  96. * @param {Node} node
  97. * @return {DocumentFragment}
  98. */
  99. function nodeToFragment (node) {
  100. var tag = node.tagName
  101. // if its a template tag and the browser supports it,
  102. // its content is already a document fragment.
  103. if (
  104. tag === 'TEMPLATE' &&
  105. node.content instanceof DocumentFragment
  106. ) {
  107. return node.content
  108. }
  109. return tag === 'SCRIPT'
  110. ? stringToFragment(node.textContent)
  111. : stringToFragment(node.innerHTML)
  112. }
  113. /**
  114. * Process the template option and normalizes it into a
  115. * a DocumentFragment that can be used as a partial or a
  116. * instance template.
  117. *
  118. * @param {*} template
  119. * Possible values include:
  120. * - DocumentFragment object
  121. * - Node object of type Template
  122. * - id selector: '#some-template-id'
  123. * - template string: '<div><span>{{msg}}</span></div>'
  124. * @param {Boolean} clone
  125. * @return {DocumentFragment|undefined}
  126. */
  127. exports.parse = function (template, clone) {
  128. var node, frag
  129. // if the template is already a document fragment,
  130. // do nothing
  131. if (template instanceof DocumentFragment) {
  132. return clone
  133. ? template.cloneNode(true)
  134. : template
  135. }
  136. if (typeof template === 'string') {
  137. // id selector
  138. if (template.charAt(0) === '#') {
  139. // id selector can be cached too
  140. frag = templateCache.get(template)
  141. if (!frag) {
  142. node = document.getElementById(template.slice(1))
  143. if (node) {
  144. frag = nodeToFragment(node)
  145. // save selector to cache
  146. templateCache.put(template, frag)
  147. }
  148. }
  149. } else {
  150. // normal string template
  151. frag = stringToFragment(template)
  152. }
  153. } else if (template.nodeType) {
  154. // a direct node
  155. frag = nodeToFragment(template)
  156. }
  157. return frag && clone
  158. ? frag.cloneNode(true)
  159. : frag
  160. }