diff --git a/examples/html-editor-drawing-hook/index.js b/examples/html-editor-drawing-hook/index.js
index 27b58a10..dca6a6d3 100644
--- a/examples/html-editor-drawing-hook/index.js
+++ b/examples/html-editor-drawing-hook/index.js
@@ -1,7 +1,20 @@
/* 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.yXmlType.bindToDom(document.body)
+ window.domBinding = new Y.DomBinding(window.yXmlType, document.body, { hooks })
}
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', {
connector: {
name: 'websockets-client',
diff --git a/src/Bindings/DomBinding/DomBinding.js b/src/Bindings/DomBinding/DomBinding.js
index a2d10bf1..ae5a2894 100644
--- a/src/Bindings/DomBinding/DomBinding.js
+++ b/src/Bindings/DomBinding/DomBinding.js
@@ -30,6 +30,9 @@ export default class DomBinding extends Binding {
constructor (type, target, opts = {}) {
// Binding handles textType as this.type and domTextarea as this.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.
* @type {Map}
@@ -49,11 +52,11 @@ export default class DomBinding extends Binding {
// set initial value
target.innerHTML = ''
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._domObserver = (mutations) => {
- domObserver.call(this, mutations, opts._document)
+ domObserver.call(this, mutations, opts.document)
}
type.observeDeep(this._typeObserver)
this._mutationObserver = new MutationObserver(this._domObserver)
diff --git a/src/Bindings/DomBinding/domToType.js b/src/Bindings/DomBinding/domToType.js
index d95f8d28..fdb939e5 100644
--- a/src/Bindings/DomBinding/domToType.js
+++ b/src/Bindings/DomBinding/domToType.js
@@ -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'
/**
@@ -15,14 +15,29 @@ export default function domToType (element, _document = document, binding) {
let type
switch (element.nodeType) {
case _document.ELEMENT_NODE:
- type = new YXmlElement(element.nodeName)
- const attrs = element.attributes
- for (let i = attrs.length - 1; i >= 0; i--) {
- const attr = attrs[i]
- type.setAttribute(attr.name, attr.value)
+ 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)
+ const attrs = element.attributes
+ for (let i = attrs.length - 1; i >= 0; i--) {
+ const attr = attrs[i]
+ type.setAttribute(attr.name, attr.value)
+ }
+ const children = Array.from(element.childNodes).map(e => domToType(e, _document, binding))
+ type.insert(0, children)
+ } else {
+ type = new YXmlHook(hookName)
+ hook.fillType(element, type)
}
- const children = Array.from(element.childNodes).map(e => domToType(e, _document, binding))
- type.insert(0, children)
break
case _document.TEXT_NODE:
type = new YXmlText()
diff --git a/src/Bindings/DomBinding/typeObserver.js b/src/Bindings/DomBinding/typeObserver.js
index e5b4343a..1417cc4e 100644
--- a/src/Bindings/DomBinding/typeObserver.js
+++ b/src/Bindings/DomBinding/typeObserver.js
@@ -6,7 +6,7 @@ import { removeDomChildrenUntilElementFound } from './util.js'
/**
* @private
*/
-export default function typeObserver (events, _document) {
+export default function typeObserver (events) {
this._mutualExclude(() => {
events.forEach(event => {
const yxml = event.target
@@ -37,11 +37,10 @@ export default function typeObserver (events, _document) {
let currentChild = dom.firstChild
yxml.forEach(childType => {
const childNode = this.typeToDom.get(childType)
- const binding = this
switch (childNode) {
case undefined:
// 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)
break
case false:
diff --git a/src/Types/YXml/YXmlElement.js b/src/Types/YXml/YXmlElement.js
index 0e5463e5..43f73e13 100644
--- a/src/Types/YXml/YXmlElement.js
+++ b/src/Types/YXml/YXmlElement.js
@@ -164,6 +164,8 @@ export default class YXmlElement extends YXmlFragment {
* @param {Document} [_document=document] The document object (you must define
* this when calling this method in
* nodejs)
+ * @param {Object} [hooks={}] Optional property to customize how hooks
+ * are presented in the DOM
* @param {DomBinding} [binding] You should not set this property. This is
* used if DomBinding wants to create a
* association to the created DOM type.
@@ -171,14 +173,14 @@ export default class YXmlElement extends YXmlFragment {
*
* @public
*/
- toDom (_document = document, binding) {
+ toDom (_document = document, hooks = {}, binding) {
const dom = _document.createElement(this.nodeName)
let attrs = this.getAttributes()
for (let key in attrs) {
dom.setAttribute(key, attrs[key])
}
this.forEach(yxml => {
- dom.appendChild(yxml.toDom(_document, binding))
+ dom.appendChild(yxml.toDom(_document, hooks, binding))
})
createAssociation(binding, dom, this)
return dom
diff --git a/src/Types/YXml/YXmlFragment.js b/src/Types/YXml/YXmlFragment.js
index 8bbe932c..827356a0 100644
--- a/src/Types/YXml/YXmlFragment.js
+++ b/src/Types/YXml/YXmlFragment.js
@@ -136,18 +136,20 @@ export default class YXmlFragment extends YArray {
* @param {Document} [_document=document] The document object (you must define
* this when calling this method in
* nodejs)
+ * @param {Object} [hooks={}] Optional property to customize how hooks
+ * are presented in the DOM
* @param {DomBinding} [binding] You should not set this property. This is
* 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}
*
* @public
*/
- toDom (_document = document, binding) {
+ toDom (_document = document, hooks = {}, binding) {
const fragment = _document.createDocumentFragment()
createAssociation(binding, fragment, this)
this.forEach(xmlType => {
- fragment.insertBefore(xmlType.toDom(_document, binding), null)
+ fragment.insertBefore(xmlType.toDom(_document, hooks, binding), null)
})
return fragment
}
diff --git a/src/Types/YXml/YXmlHook.js b/src/Types/YXml/YXmlHook.js
index d840ab11..ec855f8e 100644
--- a/src/Types/YXml/YXmlHook.js
+++ b/src/Types/YXml/YXmlHook.js
@@ -1,5 +1,4 @@
import YMap from '../YMap/YMap.js'
-import { getHook, addHook } from './hooks.js'
/**
* 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
* this when calling this method in
* nodejs)
+ * @param {Object} [hooks] Optional property to customize how hooks
+ * are presented in the DOM
* @param {DomBinding} [binding] You should not set this property. This is
* 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}
*
* @public
*/
- toDom (_document = document) {
- const dom = getHook(this.hookName).createDom(this)
- dom._yjsHook = this.hookName
+ toDom (_document = document, hooks = {}, binding) {
+ const hook = hooks[this.hookName]
+ let dom
+ if (hook !== undefined) {
+ dom = hook.createDom(this)
+ } else {
+ dom = document.createElement(this.hookName)
+ }
+ dom.dataset.yjsHook = this.hookName
return dom
}
@@ -97,4 +104,3 @@ export default class YXmlHook extends YMap {
super._integrate(y)
}
}
-YXmlHook.addHook = addHook
diff --git a/src/Types/YXml/YXmlText.js b/src/Types/YXml/YXmlText.js
index 81c9ee69..c0339806 100644
--- a/src/Types/YXml/YXmlText.js
+++ b/src/Types/YXml/YXmlText.js
@@ -14,6 +14,8 @@ export default class YXmlText extends YText {
* @param {Document} [_document=document] The document object (you must define
* this when calling this method in
* nodejs)
+ * @param {Object} [hooks] Optional property to customize how hooks
+ * are presented in the DOM
* @param {DomBinding} [binding] You should not set this property. This is
* used if DomBinding wants to create a
* association to the created DOM type.
@@ -21,7 +23,7 @@ export default class YXmlText extends YText {
*
* @public
*/
- toDom (_document = document, binding) {
+ toDom (_document = document, hooks, binding) {
const dom = _document.createTextNode(this.toString())
createAssociation(binding, dom, this)
return dom
diff --git a/src/Types/YXml/hooks.js b/src/Types/YXml/hooks.js
deleted file mode 100644
index 18e5de5c..00000000
--- a/src/Types/YXml/hooks.js
+++ /dev/null
@@ -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
-}