transclude.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. var _ = require('../util')
  2. var config = require('../config')
  3. var templateParser = require('../parsers/template')
  4. /**
  5. * Process an element or a DocumentFragment based on a
  6. * instance option object. This allows us to transclude
  7. * a template node/fragment before the instance is created,
  8. * so the processed fragment can then be cloned and reused
  9. * in v-repeat.
  10. *
  11. * @param {Element} el
  12. * @param {Object} options
  13. * @return {Element|DocumentFragment}
  14. */
  15. exports.transclude = function (el, options) {
  16. // extract container attributes to pass them down
  17. // to compiler, because they need to be compiled in
  18. // parent scope. we are mutating the options object here
  19. // assuming the same object will be used for compile
  20. // right after this.
  21. if (options) {
  22. options._containerAttrs = extractAttrs(el)
  23. }
  24. // for template tags, what we want is its content as
  25. // a documentFragment (for block instances)
  26. if (_.isTemplate(el)) {
  27. el = templateParser.parse(el)
  28. }
  29. if (options && options.template) {
  30. el = transcludeTemplate(el, options)
  31. }
  32. if (el instanceof DocumentFragment) {
  33. // anchors for block instance
  34. // passing in `persist: true` to avoid them being
  35. // discarded by IE during template cloning
  36. _.prepend(_.createAnchor('v-start', true), el)
  37. el.appendChild(_.createAnchor('v-end', true))
  38. }
  39. return el
  40. }
  41. /**
  42. * Process the template option.
  43. * If the replace option is true this will swap the $el.
  44. *
  45. * @param {Element} el
  46. * @param {Object} options
  47. * @return {Element|DocumentFragment}
  48. */
  49. function transcludeTemplate (el, options) {
  50. var template = options.template
  51. var frag = templateParser.parse(template, true)
  52. if (!frag) {
  53. _.warn('Invalid template option: ' + template)
  54. } else {
  55. options._content = _.extractContent(el)
  56. var replacer = frag.firstChild
  57. if (options.replace) {
  58. if (
  59. frag.childNodes.length > 1 ||
  60. replacer.nodeType !== 1 ||
  61. // when root node has v-repeat, the instance ends up
  62. // having multiple top-level nodes, thus becoming a
  63. // block instance. (#835)
  64. replacer.hasAttribute(config.prefix + 'repeat')
  65. ) {
  66. return frag
  67. } else {
  68. options._replacerAttrs = extractAttrs(replacer)
  69. mergeAttrs(el, replacer)
  70. return replacer
  71. }
  72. } else {
  73. el.appendChild(frag)
  74. return el
  75. }
  76. }
  77. }
  78. /**
  79. * Helper to extract a component container's attribute names
  80. * into a map.
  81. *
  82. * @param {Element} el
  83. * @return {Object}
  84. */
  85. function extractAttrs (el) {
  86. if (el.nodeType === 1 && el.hasAttributes()) {
  87. var attrs = el.attributes
  88. var res = {}
  89. var i = attrs.length
  90. while (i--) {
  91. res[attrs[i].name] = attrs[i].value
  92. }
  93. return res
  94. }
  95. }
  96. /**
  97. * Merge the attributes of two elements, and make sure
  98. * the class names are merged properly.
  99. *
  100. * @param {Element} from
  101. * @param {Element} to
  102. */
  103. function mergeAttrs (from, to) {
  104. var attrs = from.attributes
  105. var i = attrs.length
  106. var name, value
  107. while (i--) {
  108. name = attrs[i].name
  109. value = attrs[i].value
  110. if (!to.hasAttribute(name)) {
  111. to.setAttribute(name, value)
  112. } else if (name === 'class') {
  113. to.className = to.className + ' ' + value
  114. }
  115. }
  116. }