hooks port to domBinding
This commit is contained in:
		
							parent
							
								
									94933a704d
								
							
						
					
					
						commit
						1fe37c565e
					
				@ -1,7 +1,20 @@
 | 
				
			|||||||
/* global Y, d3 */
 | 
					/* global Y, d3 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const hooks = {
 | 
				
			||||||
 | 
					  "magic-drawing": {
 | 
				
			||||||
 | 
					    fillType: function (dom, type) {
 | 
				
			||||||
 | 
					      initDrawingBindings(type, dom)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    createDom: function (type) {
 | 
				
			||||||
 | 
					      const dom = document.createElement('magic-drawing')
 | 
				
			||||||
 | 
					      initDrawingBindings(type, dom)
 | 
				
			||||||
 | 
					      return dom
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.onload = function () {
 | 
					window.onload = function () {
 | 
				
			||||||
  window.yXmlType.bindToDom(document.body)
 | 
					  window.domBinding = new Y.DomBinding(window.yXmlType, document.body, { hooks })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.addMagicDrawing = function addMagicDrawing () {
 | 
					window.addMagicDrawing = function addMagicDrawing () {
 | 
				
			||||||
@ -96,17 +109,6 @@ function initDrawingBindings (type, dom) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Y.XmlHook.addHook('magic-drawing', {
 | 
					 | 
				
			||||||
  fillType: function (dom, type) {
 | 
					 | 
				
			||||||
    initDrawingBindings(type, dom)
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  createDom: function (type) {
 | 
					 | 
				
			||||||
    const dom = document.createElement('magic-drawing')
 | 
					 | 
				
			||||||
    initDrawingBindings(type, dom)
 | 
					 | 
				
			||||||
    return dom
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let y = new Y('html-editor-drawing-hook-example', {
 | 
					let y = new Y('html-editor-drawing-hook-example', {
 | 
				
			||||||
  connector: {
 | 
					  connector: {
 | 
				
			||||||
    name: 'websockets-client',
 | 
					    name: 'websockets-client',
 | 
				
			||||||
 | 
				
			|||||||
@ -30,6 +30,9 @@ export default class DomBinding extends Binding {
 | 
				
			|||||||
  constructor (type, target, opts = {}) {
 | 
					  constructor (type, target, opts = {}) {
 | 
				
			||||||
    // Binding handles textType as this.type and domTextarea as this.target
 | 
					    // Binding handles textType as this.type and domTextarea as this.target
 | 
				
			||||||
    super(type, target)
 | 
					    super(type, target)
 | 
				
			||||||
 | 
					    this.opts = opts
 | 
				
			||||||
 | 
					    opts.document = opts.document || document
 | 
				
			||||||
 | 
					    opts.hooks = opts.hooks || {}
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Maps each DOM element to the type that it is associated with.
 | 
					     * Maps each DOM element to the type that it is associated with.
 | 
				
			||||||
     * @type {Map}
 | 
					     * @type {Map}
 | 
				
			||||||
@ -49,11 +52,11 @@ export default class DomBinding extends Binding {
 | 
				
			|||||||
    // set initial value
 | 
					    // set initial value
 | 
				
			||||||
    target.innerHTML = ''
 | 
					    target.innerHTML = ''
 | 
				
			||||||
    for (let child of type) {
 | 
					    for (let child of type) {
 | 
				
			||||||
      target.insertBefore(child.toDom(this.domToType, this.typeToDom), null)
 | 
					      target.insertBefore(child.toDom(opts.document, opts.hooks, this), null)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this._typeObserver = typeObserver.bind(this)
 | 
					    this._typeObserver = typeObserver.bind(this)
 | 
				
			||||||
    this._domObserver = (mutations) => {
 | 
					    this._domObserver = (mutations) => {
 | 
				
			||||||
      domObserver.call(this, mutations, opts._document)
 | 
					      domObserver.call(this, mutations, opts.document)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    type.observeDeep(this._typeObserver)
 | 
					    type.observeDeep(this._typeObserver)
 | 
				
			||||||
    this._mutationObserver = new MutationObserver(this._domObserver)
 | 
					    this._mutationObserver = new MutationObserver(this._domObserver)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import { YXmlText, YXmlElement } from '../../Types/YXml/YXml.js'
 | 
					import { YXmlText, YXmlElement, YXmlHook } from '../../Types/YXml/YXml.js'
 | 
				
			||||||
import { createAssociation } from './util.js'
 | 
					import { createAssociation } from './util.js'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@ -15,6 +15,17 @@ export default function domToType (element, _document = document, binding) {
 | 
				
			|||||||
  let type
 | 
					  let type
 | 
				
			||||||
  switch (element.nodeType) {
 | 
					  switch (element.nodeType) {
 | 
				
			||||||
    case _document.ELEMENT_NODE:
 | 
					    case _document.ELEMENT_NODE:
 | 
				
			||||||
 | 
					      let hookName = element.dataset.yjsHook
 | 
				
			||||||
 | 
					      let hook
 | 
				
			||||||
 | 
					      if (hookName !== undefined) {
 | 
				
			||||||
 | 
					        hook = binding.opts.hooks[hookName]
 | 
				
			||||||
 | 
					        if (hook === undefined) {
 | 
				
			||||||
 | 
					          console.error(`Unknown hook "${hookName}". Deleting yjsHook dataset property.`)
 | 
				
			||||||
 | 
					          delete element.dataset.yjsHook
 | 
				
			||||||
 | 
					          hookName = undefined
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (hookName === undefined) {
 | 
				
			||||||
        type = new YXmlElement(element.nodeName)
 | 
					        type = new YXmlElement(element.nodeName)
 | 
				
			||||||
        const attrs = element.attributes
 | 
					        const attrs = element.attributes
 | 
				
			||||||
        for (let i = attrs.length - 1; i >= 0; i--) {
 | 
					        for (let i = attrs.length - 1; i >= 0; i--) {
 | 
				
			||||||
@ -23,6 +34,10 @@ export default function domToType (element, _document = document, binding) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        const children = Array.from(element.childNodes).map(e => domToType(e, _document, binding))
 | 
					        const children = Array.from(element.childNodes).map(e => domToType(e, _document, binding))
 | 
				
			||||||
        type.insert(0, children)
 | 
					        type.insert(0, children)
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        type = new YXmlHook(hookName)
 | 
				
			||||||
 | 
					        hook.fillType(element, type)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      break
 | 
					      break
 | 
				
			||||||
    case _document.TEXT_NODE:
 | 
					    case _document.TEXT_NODE:
 | 
				
			||||||
      type = new YXmlText()
 | 
					      type = new YXmlText()
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ import { removeDomChildrenUntilElementFound } from './util.js'
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * @private
 | 
					 * @private
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export default function typeObserver (events, _document) {
 | 
					export default function typeObserver (events) {
 | 
				
			||||||
  this._mutualExclude(() => {
 | 
					  this._mutualExclude(() => {
 | 
				
			||||||
    events.forEach(event => {
 | 
					    events.forEach(event => {
 | 
				
			||||||
      const yxml = event.target
 | 
					      const yxml = event.target
 | 
				
			||||||
@ -37,11 +37,10 @@ export default function typeObserver (events, _document) {
 | 
				
			|||||||
            let currentChild = dom.firstChild
 | 
					            let currentChild = dom.firstChild
 | 
				
			||||||
            yxml.forEach(childType => {
 | 
					            yxml.forEach(childType => {
 | 
				
			||||||
              const childNode = this.typeToDom.get(childType)
 | 
					              const childNode = this.typeToDom.get(childType)
 | 
				
			||||||
              const binding = this
 | 
					 | 
				
			||||||
              switch (childNode) {
 | 
					              switch (childNode) {
 | 
				
			||||||
                case undefined:
 | 
					                case undefined:
 | 
				
			||||||
                  // Does not exist. Create it.
 | 
					                  // Does not exist. Create it.
 | 
				
			||||||
                  const node = childType.toDom(_document, binding)
 | 
					                  const node = childType.toDom(this.opts.document, this.opts.hooks, this)
 | 
				
			||||||
                  dom.insertBefore(node, currentChild)
 | 
					                  dom.insertBefore(node, currentChild)
 | 
				
			||||||
                  break
 | 
					                  break
 | 
				
			||||||
                case false:
 | 
					                case false:
 | 
				
			||||||
 | 
				
			|||||||
@ -164,6 +164,8 @@ export default class YXmlElement extends YXmlFragment {
 | 
				
			|||||||
   * @param {Document} [_document=document] The document object (you must define
 | 
					   * @param {Document} [_document=document] The document object (you must define
 | 
				
			||||||
   *                                        this when calling this method in
 | 
					   *                                        this when calling this method in
 | 
				
			||||||
   *                                        nodejs)
 | 
					   *                                        nodejs)
 | 
				
			||||||
 | 
					   * @param {Object<key:hookDefinition>} [hooks={}] Optional property to customize how hooks
 | 
				
			||||||
 | 
					   *                                             are presented in the DOM
 | 
				
			||||||
   * @param {DomBinding} [binding] You should not set this property. This is
 | 
					   * @param {DomBinding} [binding] You should not set this property. This is
 | 
				
			||||||
   *                               used if DomBinding wants to create a
 | 
					   *                               used if DomBinding wants to create a
 | 
				
			||||||
   *                               association to the created DOM type.
 | 
					   *                               association to the created DOM type.
 | 
				
			||||||
@ -171,14 +173,14 @@ export default class YXmlElement extends YXmlFragment {
 | 
				
			|||||||
   *
 | 
					   *
 | 
				
			||||||
   * @public
 | 
					   * @public
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  toDom (_document = document, binding) {
 | 
					  toDom (_document = document, hooks = {}, binding) {
 | 
				
			||||||
    const dom = _document.createElement(this.nodeName)
 | 
					    const dom = _document.createElement(this.nodeName)
 | 
				
			||||||
    let attrs = this.getAttributes()
 | 
					    let attrs = this.getAttributes()
 | 
				
			||||||
    for (let key in attrs) {
 | 
					    for (let key in attrs) {
 | 
				
			||||||
      dom.setAttribute(key, attrs[key])
 | 
					      dom.setAttribute(key, attrs[key])
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.forEach(yxml => {
 | 
					    this.forEach(yxml => {
 | 
				
			||||||
      dom.appendChild(yxml.toDom(_document, binding))
 | 
					      dom.appendChild(yxml.toDom(_document, hooks, binding))
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    createAssociation(binding, dom, this)
 | 
					    createAssociation(binding, dom, this)
 | 
				
			||||||
    return dom
 | 
					    return dom
 | 
				
			||||||
 | 
				
			|||||||
@ -136,18 +136,20 @@ export default class YXmlFragment extends YArray {
 | 
				
			|||||||
   * @param {Document} [_document=document] The document object (you must define
 | 
					   * @param {Document} [_document=document] The document object (you must define
 | 
				
			||||||
   *                                        this when calling this method in
 | 
					   *                                        this when calling this method in
 | 
				
			||||||
   *                                        nodejs)
 | 
					   *                                        nodejs)
 | 
				
			||||||
 | 
					   * @param {Object<key:hookDefinition>} [hooks={}] Optional property to customize how hooks
 | 
				
			||||||
 | 
					   *                                             are presented in the DOM
 | 
				
			||||||
   * @param {DomBinding} [binding] You should not set this property. This is
 | 
					   * @param {DomBinding} [binding] You should not set this property. This is
 | 
				
			||||||
   *                               used if DomBinding wants to create a
 | 
					   *                               used if DomBinding wants to create a
 | 
				
			||||||
   *                               association to the created DOM type.
 | 
					   *                               association to the created DOM type
 | 
				
			||||||
   * @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
 | 
					   * @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
   * @public
 | 
					   * @public
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  toDom (_document = document, binding) {
 | 
					  toDom (_document = document, hooks = {}, binding) {
 | 
				
			||||||
    const fragment = _document.createDocumentFragment()
 | 
					    const fragment = _document.createDocumentFragment()
 | 
				
			||||||
    createAssociation(binding, fragment, this)
 | 
					    createAssociation(binding, fragment, this)
 | 
				
			||||||
    this.forEach(xmlType => {
 | 
					    this.forEach(xmlType => {
 | 
				
			||||||
      fragment.insertBefore(xmlType.toDom(_document, binding), null)
 | 
					      fragment.insertBefore(xmlType.toDom(_document, hooks, binding), null)
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    return fragment
 | 
					    return fragment
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,4 @@
 | 
				
			|||||||
import YMap from '../YMap/YMap.js'
 | 
					import YMap from '../YMap/YMap.js'
 | 
				
			||||||
import { getHook, addHook } from './hooks.js'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * You can manage binding to a custom type with YXmlHook.
 | 
					 * You can manage binding to a custom type with YXmlHook.
 | 
				
			||||||
@ -35,16 +34,24 @@ export default class YXmlHook extends YMap {
 | 
				
			|||||||
   * @param {Document} [_document=document] The document object (you must define
 | 
					   * @param {Document} [_document=document] The document object (you must define
 | 
				
			||||||
   *                                        this when calling this method in
 | 
					   *                                        this when calling this method in
 | 
				
			||||||
   *                                        nodejs)
 | 
					   *                                        nodejs)
 | 
				
			||||||
 | 
					   * @param {Object<key:hookDefinition>} [hooks] Optional property to customize how hooks
 | 
				
			||||||
 | 
					   *                                             are presented in the DOM
 | 
				
			||||||
   * @param {DomBinding} [binding] You should not set this property. This is
 | 
					   * @param {DomBinding} [binding] You should not set this property. This is
 | 
				
			||||||
   *                               used if DomBinding wants to create a
 | 
					   *                               used if DomBinding wants to create a
 | 
				
			||||||
   *                               association to the created DOM type.
 | 
					   *                               association to the created DOM type
 | 
				
			||||||
   * @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
 | 
					   * @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
   * @public
 | 
					   * @public
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  toDom (_document = document) {
 | 
					  toDom (_document = document, hooks = {}, binding) {
 | 
				
			||||||
    const dom = getHook(this.hookName).createDom(this)
 | 
					    const hook = hooks[this.hookName]
 | 
				
			||||||
    dom._yjsHook = this.hookName
 | 
					    let dom
 | 
				
			||||||
 | 
					    if (hook !== undefined) {
 | 
				
			||||||
 | 
					      dom = hook.createDom(this)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      dom = document.createElement(this.hookName)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    dom.dataset.yjsHook = this.hookName
 | 
				
			||||||
    return dom
 | 
					    return dom
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -97,4 +104,3 @@ export default class YXmlHook extends YMap {
 | 
				
			|||||||
    super._integrate(y)
 | 
					    super._integrate(y)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
YXmlHook.addHook = addHook
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -14,6 +14,8 @@ export default class YXmlText extends YText {
 | 
				
			|||||||
   * @param {Document} [_document=document] The document object (you must define
 | 
					   * @param {Document} [_document=document] The document object (you must define
 | 
				
			||||||
   *                                        this when calling this method in
 | 
					   *                                        this when calling this method in
 | 
				
			||||||
   *                                        nodejs)
 | 
					   *                                        nodejs)
 | 
				
			||||||
 | 
					   * @param {Object<key:hookDefinition>} [hooks] Optional property to customize how hooks
 | 
				
			||||||
 | 
					   *                                             are presented in the DOM
 | 
				
			||||||
   * @param {DomBinding} [binding] You should not set this property. This is
 | 
					   * @param {DomBinding} [binding] You should not set this property. This is
 | 
				
			||||||
   *                               used if DomBinding wants to create a
 | 
					   *                               used if DomBinding wants to create a
 | 
				
			||||||
   *                               association to the created DOM type.
 | 
					   *                               association to the created DOM type.
 | 
				
			||||||
@ -21,7 +23,7 @@ export default class YXmlText extends YText {
 | 
				
			|||||||
   *
 | 
					   *
 | 
				
			||||||
   * @public
 | 
					   * @public
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  toDom (_document = document, binding) {
 | 
					  toDom (_document = document, hooks, binding) {
 | 
				
			||||||
    const dom = _document.createTextNode(this.toString())
 | 
					    const dom = _document.createTextNode(this.toString())
 | 
				
			||||||
    createAssociation(binding, dom, this)
 | 
					    createAssociation(binding, dom, this)
 | 
				
			||||||
    return dom
 | 
					    return dom
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +0,0 @@
 | 
				
			|||||||
 | 
					 | 
				
			||||||
const xmlHooks = {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function addHook (name, hook) {
 | 
					 | 
				
			||||||
  xmlHooks[name] = hook
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function getHook (name) {
 | 
					 | 
				
			||||||
  const hook = xmlHooks[name]
 | 
					 | 
				
			||||||
  if (hook === undefined) {
 | 
					 | 
				
			||||||
    throw new Error(`The hook "${name}" is not specified! You must not access this hook!`)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return hook
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user