| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- var _ = require('../util')
- var config = require('../config')
- var templateParser = require('../parsers/template')
- /**
- * Process an element or a DocumentFragment based on a
- * instance option object. This allows us to transclude
- * a template node/fragment before the instance is created,
- * so the processed fragment can then be cloned and reused
- * in v-repeat.
- *
- * @param {Element} el
- * @param {Object} options
- * @return {Element|DocumentFragment}
- */
- exports.transclude = function (el, options) {
- // extract container attributes to pass them down
- // to compiler, because they need to be compiled in
- // parent scope. we are mutating the options object here
- // assuming the same object will be used for compile
- // right after this.
- if (options) {
- options._containerAttrs = extractAttrs(el)
- }
- // for template tags, what we want is its content as
- // a documentFragment (for block instances)
- if (_.isTemplate(el)) {
- el = templateParser.parse(el)
- }
- if (options) {
- if (options._asComponent && !options.template) {
- options.template = '<content></content>'
- }
- if (options.template) {
- options._content = _.extractContent(el)
- el = transcludeTemplate(el, options)
- }
- }
- if (el instanceof DocumentFragment) {
- // anchors for block instance
- // passing in `persist: true` to avoid them being
- // discarded by IE during template cloning
- _.prepend(_.createAnchor('v-start', true), el)
- el.appendChild(_.createAnchor('v-end', true))
- }
- return el
- }
- /**
- * Process the template option.
- * If the replace option is true this will swap the $el.
- *
- * @param {Element} el
- * @param {Object} options
- * @return {Element|DocumentFragment}
- */
- function transcludeTemplate (el, options) {
- var template = options.template
- var frag = templateParser.parse(template, true)
- if (!frag) {
- _.warn('Invalid template option: ' + template)
- } else {
- var replacer = frag.firstChild
- var tag = replacer.tagName && replacer.tagName.toLowerCase()
- if (options.replace) {
- /* istanbul ignore if */
- if (el === document.body) {
- _.warn(
- 'You are mounting an instance with a template to ' +
- '<body>. This will replace <body> entirely. You ' +
- 'should probably use `replace: false` here.'
- )
- }
- if (
- // multi-children template
- frag.childNodes.length > 1 ||
- // non-element template
- replacer.nodeType !== 1 ||
- // when root node is <component>, is an element
- // directive, or has v-repeat, the instance could
- // end up having multiple top-level nodes, thus
- // becoming a block instance.
- tag === 'component' ||
- _.resolveAsset(options, 'elementDirectives', tag) ||
- replacer.hasAttribute(config.prefix + 'repeat')
- ) {
- return frag
- } else {
- options._replacerAttrs = extractAttrs(replacer)
- mergeAttrs(el, replacer)
- return replacer
- }
- } else {
- el.appendChild(frag)
- return el
- }
- }
- }
- /**
- * Helper to extract a component container's attribute names
- * into a map.
- *
- * @param {Element} el
- * @return {Object}
- */
- function extractAttrs (el) {
- if (el.nodeType === 1 && el.hasAttributes()) {
- var attrs = el.attributes
- var res = {}
- var i = attrs.length
- while (i--) {
- res[attrs[i].name] = attrs[i].value
- }
- return res
- }
- }
- /**
- * Merge the attributes of two elements, and make sure
- * the class names are merged properly.
- *
- * @param {Element} from
- * @param {Element} to
- */
- function mergeAttrs (from, to) {
- var attrs = from.attributes
- var i = attrs.length
- var name, value
- while (i--) {
- name = attrs[i].name
- value = attrs[i].value
- if (!to.hasAttribute(name)) {
- to.setAttribute(name, value)
- } else if (name === 'class') {
- to.className = to.className + ' ' + value
- }
- }
- }
|