| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- var _ = require('../util')
- var templateParser = require('../parse/template')
- /**
- * Setup the instance's element before compilation.
- * 1. Setup $el
- * 2. Process the template option
- * 3. Resolve <content> insertion points
- *
- * @param {Node|String} el
- */
- exports._initElement = function (el) {
- if (typeof el === 'string') {
- el = document.querySelector(el)
- }
- // If the passed in `el` is a DocumentFragment, the instance is
- // considered a "block instance" which manages not a single element,
- // but multiple elements. A block instance's `$el` is an Array of
- // the elements it manages.
- if (el instanceof window.DocumentFragment) {
- this._isBlock = true
- this.$el = _.toArray(el.childNodes)
- } else {
- this.$el = el
- }
- this._initTemplate()
- this._initContent()
- }
- /**
- * Process the template option.
- * If the replace option is true this will also modify the $el.
- */
- exports._initTemplate = function () {
- var el = this.$el
- var options = this.$options
- var template = options.template
- if (template) {
- var frag = templateParser.parse(template)
- if (!frag) {
- _.warn('Invalid template option: ' + template)
- } else {
- // collect raw content. this wipes out the container el.
- this._collectRawContent()
- frag = frag.cloneNode(true)
- if (options.replace) {
- // replace
- if (frag.childNodes.length > 1) {
- // the template contains multiple nodes
- // in this case the original `el` is simply
- // a placeholder.
- this._isBlock = true
- this.$el = _.toArray(frag.childNodes)
- } else {
- // 1 to 1 replace, we need to copy all the
- // attributes from the original el to the replacer
- this.$el = frag.firstChild
- _.copyAttributes(el, this.$el)
- }
- if (el.parentNode) {
- _.before(this.$el, el)
- _.remove(el)
- }
- } else {
- // simply insert.
- el.appendChild(frag)
- }
- }
- }
- }
- /**
- * Collect raw content inside $el before they are
- * replaced by template content.
- */
- exports._collectRawContent = function () {
- var el = this.$el
- var child
- if (el.hasChildNodes()) {
- this._rawContent = document.createElement('div')
- /* jshint boss: true */
- while (child = el.firstChild) {
- this._rawContent.appendChild(child)
- }
- }
- }
- /**
- * Resolve <content> insertion points per W3C Web Components
- * working draft:
- *
- * http://www.w3.org/TR/2013/WD-components-intro-20130606/#insertion-points
- */
- exports._initContent = function () {
- var outlets = getOutlets(this.$el)
- var raw = this._rawContent
- var i = outlets.length
- var outlet, select, j, main
- if (i) {
- // first pass, collect corresponding content
- // for each outlet.
- while (i--) {
- outlet = outlets[i]
- if (raw) {
- select = outlet.getAttribute('select')
- if (select) { // select content
- outlet.content = _.toArray(raw.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) continue
- insertContentAt(outlet, outlet.content)
- }
- // finally insert the main content
- if (raw && main) {
- insertContentAt(main, _.toArray(raw.childNodes))
- }
- }
- this._rawContent = null
- }
- /**
- * 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.getElementsByTagName('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)
- }
|