| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- import { parseText } from '../parsers/text'
- import { parseTemplate } from '../parsers/template'
- import {
- warn,
- isTemplate,
- isFragment,
- prepend,
- extractContent,
- createAnchor,
- resolveAsset,
- toArray,
- addClass,
- hasBindAttr
- } from '../util/index'
- const specialCharRE = /[^\w\-:\.]/
- /**
- * 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-for.
- *
- * @param {Element} el
- * @param {Object} options
- * @return {Element|DocumentFragment}
- */
- export function transclude (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 fragment instances)
- if (isTemplate(el)) {
- el = parseTemplate(el)
- }
- if (options) {
- if (options._asComponent && !options.template) {
- options.template = '<slot></slot>'
- }
- if (options.template) {
- options._content = extractContent(el)
- el = transcludeTemplate(el, options)
- }
- }
- if (isFragment(el)) {
- // anchors for fragment 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 = parseTemplate(template, true)
- if (frag) {
- var replacer = frag.firstChild
- var tag = replacer.tagName && replacer.tagName.toLowerCase()
- if (options.replace) {
- /* istanbul ignore if */
- if (el === document.body) {
- process.env.NODE_ENV !== 'production' && warn(
- 'You are mounting an instance with a template to ' +
- '<body>. This will replace <body> entirely. You ' +
- 'should probably use `replace: false` here.'
- )
- }
- // there are many cases where the instance must
- // become a fragment instance: basically anything that
- // can create more than 1 root nodes.
- if (
- // multi-children template
- frag.childNodes.length > 1 ||
- // non-element template
- replacer.nodeType !== 1 ||
- // single nested component
- tag === 'component' ||
- resolveAsset(options, 'components', tag) ||
- hasBindAttr(replacer, 'is') ||
- // element directive
- resolveAsset(options, 'elementDirectives', tag) ||
- // for block
- replacer.hasAttribute('v-for') ||
- // if block
- replacer.hasAttribute('v-if')
- ) {
- return frag
- } else {
- options._replacerAttrs = extractAttrs(replacer)
- mergeAttrs(el, replacer)
- return replacer
- }
- } else {
- el.appendChild(frag)
- return el
- }
- } else {
- process.env.NODE_ENV !== 'production' && warn(
- 'Invalid template option: ' + template
- )
- }
- }
- /**
- * Helper to extract a component container's attributes
- * into a plain object array.
- *
- * @param {Element} el
- * @return {Array}
- */
- function extractAttrs (el) {
- if (el.nodeType === 1 && el.hasAttributes()) {
- return toArray(el.attributes)
- }
- }
- /**
- * 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) && !specialCharRE.test(name)) {
- to.setAttribute(name, value)
- } else if (name === 'class' && !parseText(value) && (value = value.trim())) {
- value.split(/\s+/).forEach(function (cls) {
- addClass(to, cls)
- })
- }
- }
- }
|