fixed selection handler befor/after transactions
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
73
src/Type/y-xml/selection.js
Normal file
73
src/Type/y-xml/selection.js
Normal 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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user