fixed selection handler befor/after transactions

This commit is contained in:
Kevin Jahns
2017-10-28 23:02:48 +02:00
parent c619aa33d9
commit c545118637
13 changed files with 177 additions and 95 deletions

View File

@@ -13,9 +13,17 @@ export default class YArray extends Type {
_callObserver (parentSubs, remote) {
this._eventHandler.callEventListeners(new YArrayEvent(this, remote))
}
get (i) {
// TODO: This can be improved!
return this.toArray()[i]
get (pos) {
let n = this._start
while (n !== null) {
if (!n._deleted) {
if (pos < n._length) {
return n._content[n._length - pos]
}
pos -= n._length
}
n = n._right
}
}
toArray () {
return this.map(c => c)

View File

@@ -26,10 +26,10 @@ export default class YXmlElement extends YXmlFragment {
_setDom (dom) {
if (this._dom != null) {
throw new Error('Only call this method if you know what you are doing ;)')
} else if (dom.__yxml != null) { // TODO do i need to check this? - no.. but for dev purps..
} else if (dom._yxml != null) { // TODO do i need to check this? - no.. but for dev purps..
throw new Error('Already bound to an YXml type')
} else {
dom.__yxml = this
dom._yxml = this
// tag is already set in constructor
// set attributes
let attrNames = []
@@ -112,7 +112,7 @@ export default class YXmlElement extends YXmlFragment {
let dom = this._dom
if (dom == null) {
dom = document.createElement(this.nodeName)
dom.__yxml = this
dom._yxml = this
let attrs = this.getAttributes()
for (let key in attrs) {
dom.setAttribute(key, attrs[key])

View File

@@ -10,8 +10,8 @@ import { logID } from '../../MessageHandler/messageToString.js'
function domToYXml (parent, doms) {
const types = []
doms.forEach(d => {
if (d.__yxml != null && d.__yxml !== false) {
d.__yxml._unbindFromDom()
if (d._yxml != null && d._yxml !== false) {
d._yxml._unbindFromDom()
}
if (parent._domFilter(d, []) !== null) {
let type
@@ -25,7 +25,7 @@ function domToYXml (parent, doms) {
type.enableSmartScrolling(parent._scrollElement)
types.push(type)
} else {
d.__yxml = false
d._yxml = false
}
})
return types
@@ -83,7 +83,7 @@ export default class YXmlFragment extends YArray {
this._domObserver = null
}
if (this._dom != null) {
this._dom.__yxml = null
this._dom._yxml = null
this._dom = null
}
}
@@ -97,12 +97,15 @@ export default class YXmlFragment extends YArray {
this.insert(pos, types)
return types
}
getDom () {
return this._dom
}
bindToDom (dom) {
if (this._dom != null) {
this._unbindFromDom()
}
if (dom.__yxml != null) {
dom.__yxml._unbindFromDom()
if (dom._yxml != null) {
dom._yxml._unbindFromDom()
}
if (MutationObserver == null) {
throw new Error('Not able to bind to a DOM element, because MutationObserver is not available!')
@@ -112,7 +115,7 @@ export default class YXmlFragment extends YArray {
dom.insertBefore(t.getDom(), null)
})
this._dom = dom
dom.__yxml = this
dom._yxml = this
this._bindToDom(dom)
}
// binds to a dom element

View File

@@ -1,20 +1,9 @@
/* global getSelection, MutationObserver */
/* global MutationObserver */
import diff from 'fast-diff'
import YText from '../YText.js'
import { getAnchorViewPosition, fixScrollPosition, getBoundingClientRect } from './utils.js'
function fixPosition (event, pos) {
if (event.index <= pos) {
if (event.type === 'delete') {
return pos - Math.min(pos - event.index, event.length)
} else {
return pos + 1
}
} else {
return pos
}
}
import { beforeTransactionSelectionFixer, afterTransactionSelectionFixer } from './selection.js'
export default class YXmlText extends YText {
constructor (arg1) {
@@ -53,25 +42,6 @@ export default class YXmlText extends YText {
if (this._dom != null) {
const dom = this._dom
this._mutualExclude(() => {
let selection = null
let shouldUpdateSelection = false
let anchorNode = null
let anchorOffset = null
let focusNode = null
let focusOffset = null
if (typeof getSelection !== 'undefined') {
selection = getSelection()
if (selection.anchorNode === dom) {
anchorNode = selection.anchorNode
anchorOffset = fixPosition(event, selection.anchorOffset)
shouldUpdateSelection = true
}
if (selection.focusNode === dom) {
focusNode = selection.focusNode
focusOffset = fixPosition(event, selection.focusOffset)
shouldUpdateSelection = true
}
}
let anchorViewPosition = getAnchorViewPosition(this._scrollElement)
let anchorViewFix
if (anchorViewPosition !== null && (anchorViewPosition.anchor !== null || getBoundingClientRect(this._dom).top <= 0)) {
@@ -81,19 +51,15 @@ export default class YXmlText extends YText {
}
dom.nodeValue = this.toString()
fixScrollPosition(this._scrollElement, anchorViewFix)
if (shouldUpdateSelection) {
selection.setBaseAndExtent(
anchorNode || selection.anchorNode,
anchorOffset || selection.anchorOffset,
focusNode || selection.focusNode,
focusOffset || selection.focusOffset
)
}
})
}
})
}
_integrate (y) {
super._integrate(y)
y.on('beforeTransaction', beforeTransactionSelectionFixer)
y.on('afterTransaction', afterTransactionSelectionFixer)
}
setDomFilter () {}
enableSmartScrolling (scrollElement) {
this._scrollElement = scrollElement
@@ -102,12 +68,12 @@ export default class YXmlText extends YText {
if (this._dom != null) {
this._unbindFromDom()
}
if (dom.__yxml != null) {
dom.__yxml._unbindFromDom()
if (dom._yxml != null) {
dom._yxml._unbindFromDom()
}
// set marker
this._dom = dom
dom.__yxml = this
dom._yxml = this
if (typeof MutationObserver === 'undefined') {
return
}
@@ -154,7 +120,7 @@ export default class YXmlText extends YText {
this._domObserver = null
}
if (this._dom != null) {
this._dom.__yxml = null
this._dom._yxml = null
this._dom = null
}
}

View File

@@ -0,0 +1,73 @@
/* globals getSelection */
import { getRelativePosition, fromRelativePosition } from '../../Util/relativePosition.js'
let browserSelection = null
let relativeSelection = null
export let beforeTransactionSelectionFixer
if (typeof getSelection !== 'undefined') {
beforeTransactionSelectionFixer = function _beforeTransactionSelectionFixer (y, remote) {
if (!remote) {
return
}
relativeSelection = { from: null, to: null, fromY: null, toY: null }
browserSelection = getSelection()
const anchorNode = browserSelection.anchorNode
if (anchorNode !== null && anchorNode._yxml != null) {
const yxml = anchorNode._yxml
relativeSelection.from = getRelativePosition(yxml, browserSelection.anchorOffset)
relativeSelection.fromY = yxml._y
}
const focusNode = browserSelection.focusNode
if (focusNode !== null && focusNode._yxml != null) {
const yxml = anchorNode._yxml
relativeSelection.to = getRelativePosition(yxml, browserSelection.focusOffset)
relativeSelection.toY = yxml._y
}
}
} else {
beforeTransactionSelectionFixer = function _fakeBeforeTransactionSelectionFixer () {}
}
export function afterTransactionSelectionFixer (y, remote) {
if (relativeSelection === null || !remote) {
return
}
const to = relativeSelection.to
const from = relativeSelection.from
const fromY = relativeSelection.fromY
const toY = relativeSelection.toY
let shouldUpdate = false
let anchorNode = browserSelection.anchorNode
let anchorOffset = browserSelection.anchorOffset
let focusNode = browserSelection.focusNode
let focusOffset = browserSelection.focusOffset
if (from !== null) {
let sel = fromRelativePosition(fromY, from)
if (sel !== null) {
shouldUpdate = true
anchorNode = sel.type.getDom()
anchorOffset = sel.offset
}
}
if (to !== null) {
let sel = fromRelativePosition(toY, to)
if (sel !== null) {
focusNode = sel.type.getDom()
focusOffset = sel.offset
shouldUpdate = true
}
}
if (shouldUpdate) {
browserSelection.setBaseAndExtent(
anchorNode,
anchorOffset,
focusNode,
focusOffset
)
}
// delete, so the objects can be gc'd
relativeSelection = null
browserSelection = null
}

View File

@@ -73,7 +73,7 @@ function _insertNodeHelper (yxml, prevExpectedNode, child) {
/*
* 1. Check if any of the nodes was deleted
* 2. Iterate over the children.
* 2.1 If a node exists without __yxml property, insert a new node
* 2.1 If a node exists without _yxml property, insert a new node
* 2.2 If _contents.length < dom.childNodes.length, fill the
* rest of _content with childNodes
* 2.3 If a node was moved, delete it and
@@ -85,7 +85,7 @@ export function applyChangesFromDom (yxml) {
const y = yxml._y
let knownChildren =
new Set(
Array.prototype.map.call(yxml._dom.childNodes, child => child.__yxml)
Array.prototype.map.call(yxml._dom.childNodes, child => child._yxml)
.filter(id => id !== undefined)
)
// 1. Check if any of the nodes was deleted
@@ -101,7 +101,7 @@ export function applyChangesFromDom (yxml) {
let expectedNode = iterateUntilUndeleted(yxml._start)
for (let domCnt = 0; domCnt < len; domCnt++) {
const child = childNodes[domCnt]
const childYXml = child.__yxml
const childYXml = child._yxml
if (childYXml != null) {
if (childYXml === false) {
// should be ignored or is going to be deleted
@@ -112,7 +112,7 @@ export function applyChangesFromDom (yxml) {
// 2.3 Not expected node
if (childYXml._parent !== this) {
// element is going to be deleted by its previous parent
child.__yxml = null
child._yxml = null
} else {
childYXml._delete(y)
}