Evan You 11 лет назад
Родитель
Сommit
93c8139137
2 измененных файлов с 173 добавлено и 0 удалено
  1. 0 0
      src/compile/compile.js
  2. 173 0
      src/compile/transclude.js

+ 0 - 0
src/compile/compile.js


+ 173 - 0
src/compile/transclude.js

@@ -0,0 +1,173 @@
+var _ = require('../util')
+var templateParser = require('../parse/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}
+ */
+
+module.exports = function transclude (el, options) {
+  if (typeof el === 'string') {
+    var selector = el
+    el = document.querySelector(el)
+    if (!el) {
+      _.warn('Cannot find element: ' + selector)
+    }
+  }
+  if (el instanceof DocumentFragment) {
+    return transcludeBlock(el)
+  } else if (options.template) {
+    return transcludeTemplate(el, options)
+  } else {
+    return el
+  }
+}
+
+/**
+ * Mark a block fragment that manages a group of nodes
+ * instead of one element. The group is denoted by
+ * a starting node and an ending node.
+ *
+ * @param {DocumentFragment} frag
+ */
+
+function transcludeBlock (frag) {
+  _.prepend(document.createComment('v-block-start'), frag)
+  frag.appendChild(document.createComment('v-block-end'))
+  return frag
+}
+
+/**
+ * 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 {
+    collectRawContent(el)
+    if (options.replace) {
+      if (frag.childNodes.length > 1) {
+        transcludeBlock(frag)
+        transcludeContent(_.toArray(frag.childNodes))
+        return frag
+      } else {
+        var replacer = frag.firstChild
+        _.copyAttributes(el, replacer)
+        transcludeContent(replacer)
+        return replacer
+      }
+    } else {
+      el.appendChild(frag)
+      return el
+    }
+  }
+}
+
+/**
+ * Collect raw content inside $el before they are
+ * replaced by template content.
+ */
+
+var rawContent
+function collectRawContent (el) {
+  var child
+  rawContent = null
+  if (el.hasChildNodes()) {
+    rawContent = document.createElement('div')
+    /* jshint boss:true */
+    while (child = el.firstChild) {
+      rawContent.appendChild(child)
+    }
+  }
+}
+
+/**
+ * Resolve <content> insertion points mimicking the behavior
+ * of the Shadow DOM spec:
+ *
+ *   http://w3c.github.io/webcomponents/spec/shadow/#insertion-points
+ *
+ * @param {Element|DocumentFragment} el
+ */
+
+function transcludeContent (el) {
+  var outlets = getOutlets(el)
+  var i = outles.length
+  if (!i) return
+  var outlet, select, j, main
+  // first pass, collect corresponding content
+  // for each outlet.
+  while (i--) {
+    outlet = outlets[i]
+    if (rawContent) {
+      select = outlet.getAttribute('select')
+      if (select) {  // select content
+        outlet.content = _.toArray(
+          rawContent.querySelectorAll(select)
+        )
+      } else { // default content
+        main = outlet
+      }
+    } else { // fallback content
+      outlet.content = _.toArray(outlet.childNodes)
+    }
+  }
+  // second pass, actually insert the contents
+  for (i = 0, j = outlets.length; i < j; i++) {
+    outlet = outlets[i]
+    if (outlet !== main) {
+      insertContentAt(outlet, outlet.content)
+    }
+  }
+  // finally insert the main content
+  if (main) {
+    insertContentAt(main, _.toArray(rawContent.childNodes))
+  }
+}
+
+/**
+ * Get <content> outlets from the element/list
+ *
+ * @param {Element|Array} el
+ * @return {Array}
+ */
+
+var concat = [].concat
+function getOutlets (el) {
+  return _.isArray(el)
+    ? concat.apply([], el.map(getOutlets))
+    : _.toArray(el.querySelectorAll('content'))
+}
+
+/**
+ * Insert an array of nodes at outlet,
+ * then remove the outlet.
+ *
+ * @param {Element} outlet
+ * @param {Array} contents
+ */
+
+function insertContentAt (outlet, contents) {
+  // not using util DOM methods here because
+  // parentNode can be cached
+  var parent = outlet.parentNode
+  for (var i = 0, j = contents.length; i < j; i++) {
+    parent.insertBefore(contents[i], outlet)
+  }
+  parent.removeChild(outlet)
+}