From 85492ad2e0bc885181c5608d7d9fb65814b5381c Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 13 Dec 2017 12:49:34 +0100 Subject: [PATCH] fix drawing example. Add drawing hook for y-xml --- examples/drawing/index.html | 2 +- examples/drawing/index.js | 21 ++- examples/html-editor-drawing-hook/index.html | 35 +++++ examples/html-editor-drawing-hook/index.js | 134 +++++++++++++++++++ src/Type/YArray.js | 38 +++++- 5 files changed, 216 insertions(+), 14 deletions(-) create mode 100644 examples/html-editor-drawing-hook/index.html create mode 100644 examples/html-editor-drawing-hook/index.js diff --git a/examples/drawing/index.html b/examples/drawing/index.html index 9d250f70..f2717a12 100644 --- a/examples/drawing/index.html +++ b/examples/drawing/index.html @@ -13,7 +13,7 @@ - + diff --git a/examples/drawing/index.js b/examples/drawing/index.js index 78090685..ecb323ed 100644 --- a/examples/drawing/index.js +++ b/examples/drawing/index.js @@ -10,11 +10,11 @@ let y = new Y({ }) window.yDrawing = y -var drawing = y.share.drawing +var drawing = y.define('drawing', Y.Array) var renderPath = d3.svg.line() .x(function (d) { return d[0] }) .y(function (d) { return d[1] }) - .interpolate('basis') + .interpolate('basic') var svg = d3.select('#drawingCanvas') .call(d3.behavior.drag() @@ -33,14 +33,13 @@ function drawLine (yarray) { }) } // call drawLine every time an array is appended -y.share.drawing.observe(function (event) { - event.changedKeys.forEach(function (key) { - let path = y.share.drawing.get(key) - if (path === undefined) { - svg.selectAll('path').remove() - } else { - drawLine(path) - } +drawing.observe(function (event) { + event.removedElements.forEach(function () { + // if one is deleted, all will be deleted!! + svg.selectAll('path').remove() + }) + event.addedElements.forEach(function (path) { + drawLine(path) }) }) // draw all existing content @@ -65,7 +64,7 @@ function drag () { if (sharedLine != null && ignoreDrag == null) { ignoreDrag = window.setTimeout(function () { ignoreDrag = null - }, 33) + }, 10) sharedLine.push([d3.mouse(this)]) } } diff --git a/examples/html-editor-drawing-hook/index.html b/examples/html-editor-drawing-hook/index.html new file mode 100644 index 00000000..f30d883b --- /dev/null +++ b/examples/html-editor-drawing-hook/index.html @@ -0,0 +1,35 @@ + + + + + + + + + + + diff --git a/examples/html-editor-drawing-hook/index.js b/examples/html-editor-drawing-hook/index.js new file mode 100644 index 00000000..adb73c10 --- /dev/null +++ b/examples/html-editor-drawing-hook/index.js @@ -0,0 +1,134 @@ +/* global Y, d3 */ + +window.onload = function () { + window.yXmlType.bindToDom(document.body) +} + +window.addMagicDrawing = function addMagicDrawing () { + let mt = document.createElement('magic-drawing') + mt.dataset.yjsHook = 'magic-drawing' + document.body.append(mt) +} + +var renderPath = d3.svg.line() + .x(function (d) { return d[0] }) + .y(function (d) { return d[1] }) + .interpolate('basic') + +function initDrawingBindings (type, dom) { + dom.contentEditable = 'false' + dom.dataset.yjsHook = 'magic-drawing' + var drawing = type.get('drawing') + if (drawing === undefined) { + drawing = type.set('drawing', new Y.Array()) + } + var canvas = dom.querySelector('.drawingCanvas') + if (canvas == null) { + canvas = document.createElementNS('http://www.w3.org/2000/svg', 'svg') + canvas.setAttribute('class', 'drawingCanvas') + canvas.setAttribute('viewbox', '0 0 100 100') + dom.insertBefore(canvas, null) + } + var clearDrawingButton = dom.querySelector('.clearDrawingButton') + if (clearDrawingButton == null) { + clearDrawingButton = document.createElement('button') + clearDrawingButton.setAttribute('type', 'button') + clearDrawingButton.setAttribute('class', 'clearDrawingButton') + clearDrawingButton.innerText = 'Clear Drawing' + dom.insertBefore(clearDrawingButton, null) + } + var svg = d3.select(canvas) + .call(d3.behavior.drag() + .on('dragstart', dragstart) + .on('drag', drag) + .on('dragend', dragend)) + // create line from a shared array object and update the line when the array changes + function drawLine (yarray, svg) { + var line = svg.append('path').datum(yarray.toArray()) + line.attr('d', renderPath) + yarray.observe(function (event) { + line.remove() + line = svg.append('path').datum(yarray.toArray()) + line.attr('d', renderPath) + }) + } + // call drawLine every time an array is appended + drawing.observe(function (event) { + event.removedElements.forEach(function () { + // if one is deleted, all will be deleted!! + svg.selectAll('path').remove() + }) + event.addedElements.forEach(function (path) { + drawLine(path, svg) + }) + }) + // draw all existing content + for (var i = 0; i < drawing.length; i++) { + drawLine(drawing.get(i), svg) + } + + // clear canvas on request + clearDrawingButton.onclick = function () { + drawing.delete(0, drawing.length) + } + + var sharedLine = null + function dragstart () { + drawing.insert(drawing.length, [Y.Array]) + sharedLine = drawing.get(drawing.length - 1) + } + + // After one dragged event is recognized, we ignore them for 33ms. + var ignoreDrag = null + function drag () { + if (sharedLine != null && ignoreDrag == null) { + ignoreDrag = window.setTimeout(function () { + ignoreDrag = null + }, 10) + sharedLine.push([d3.mouse(this)]) + } + } + + function dragend () { + sharedLine = null + window.clearTimeout(ignoreDrag) + ignoreDrag = null + } +} + +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 + } +}) + +// initialize a shared object. This function call returns a promise! +let y = new Y({ + connector: { + name: 'websockets-client', + url: 'http://127.0.0.1:1234', + room: 'html-editor-example6' + // maxBufferLength: 100 + } +}) +window.yXml = y +window.yXmlType = y.define('xml', Y.XmlFragment) +window.undoManager = new Y.utils.UndoManager(window.yXmlType, { + captureTimeout: 500 +}) + +document.onkeydown = function interceptUndoRedo (e) { + if (e.keyCode === 90 && e.metaKey) { + if (!e.shiftKey) { + window.undoManager.undo() + } else { + window.undoManager.redo() + } + e.preventDefault() + } +} diff --git a/src/Type/YArray.js b/src/Type/YArray.js index 7e6766a2..37eb2c6c 100644 --- a/src/Type/YArray.js +++ b/src/Type/YArray.js @@ -5,15 +5,38 @@ import { logID } from '../MessageHandler/messageToString.js' import YEvent from '../Util/YEvent.js' class YArrayEvent extends YEvent { - constructor (yarray, remote) { + constructor (yarray, remote, transaction) { super(yarray) this.remote = remote + this._transaction = transaction + } + get addedElements () { + const target = this.target + const transaction = this._transaction + const addedElements = new Set() + transaction.newTypes.forEach(function (type) { + if (type._parent === target && !transaction.deletedStructs.has(type)) { + addedElements.add(type) + } + }) + return addedElements + } + get removedElements () { + const target = this.target + const transaction = this._transaction + const removedElements = new Set() + transaction.deletedStructs.forEach(function (struct) { + if (struct._parent === target && !transaction.newTypes.has(struct)) { + removedElements.add(struct) + } + }) + return removedElements } } export default class YArray extends Type { _callObserver (transaction, parentSubs, remote) { - this._callEventHandler(transaction, new YArrayEvent(this, remote)) + this._callEventHandler(transaction, new YArrayEvent(this, remote, transaction)) } get (pos) { let n = this._start @@ -214,6 +237,17 @@ export default class YArray extends Type { } this.insertAfter(left, content) } + push (content) { + let n = this._start + let lastUndeleted = null + while (n !== null) { + if (!n._deleted) { + lastUndeleted = n + } + n = n._right + } + this.insertAfter(lastUndeleted, content) + } _logString () { const left = this._left !== null ? this._left._lastId : null const origin = this._origin !== null ? this._origin._lastId : null