start rewriting y-richtext
This commit is contained in:
parent
fc500a8247
commit
da748a78f4
@ -73,6 +73,13 @@ export default class Item {
|
|||||||
get _length () {
|
get _length () {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Some elements are not supposed to be addressable. For example, an
|
||||||
|
* ItemFormat should not be retrievable via yarray.get(pos)
|
||||||
|
*/
|
||||||
|
get _countable () {
|
||||||
|
return true
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Splits this struct so that another struct can be inserted in-between.
|
* Splits this struct so that another struct can be inserted in-between.
|
||||||
* This must be overwritten if _length > 1
|
* This must be overwritten if _length > 1
|
||||||
|
45
src/Struct/ItemFormat.js
Normal file
45
src/Struct/ItemFormat.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { default as Item } from './Item.js'
|
||||||
|
import { logID } from '../MessageHandler/messageToString.js'
|
||||||
|
|
||||||
|
export default class ItemString extends Item {
|
||||||
|
constructor () {
|
||||||
|
super()
|
||||||
|
this.key = null
|
||||||
|
this.value = null
|
||||||
|
}
|
||||||
|
_copy (undeleteChildren, copyPosition) {
|
||||||
|
let struct = super._copy(undeleteChildren, copyPosition)
|
||||||
|
struct.key = this.key
|
||||||
|
struct.value = this.value
|
||||||
|
return struct
|
||||||
|
}
|
||||||
|
get _length () {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
get _countable () {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_fromBinary (y, decoder) {
|
||||||
|
let missing = super._fromBinary(y, decoder)
|
||||||
|
this.key = decoder.readVarString()
|
||||||
|
this.value = decoder.readVarString()
|
||||||
|
return missing
|
||||||
|
}
|
||||||
|
_toBinary (encoder) {
|
||||||
|
super._toBinary(encoder)
|
||||||
|
encoder.writeVarString(this.key)
|
||||||
|
encoder.writeVarString(this.value)
|
||||||
|
}
|
||||||
|
_logString () {
|
||||||
|
const left = this._left !== null ? this._left._lastId : null
|
||||||
|
const origin = this._origin !== null ? this._origin._lastId : null
|
||||||
|
return `ItemFormat(id:${logID(this._id)},key:${JSON.stringify(this.key)},value:${JSON.stringify(this.value)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
|
||||||
|
}
|
||||||
|
_splitAt (y, diff) {
|
||||||
|
if (diff === 0) {
|
||||||
|
return this
|
||||||
|
} else {
|
||||||
|
return this._right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -45,7 +45,7 @@ export default class YArray extends Type {
|
|||||||
get (pos) {
|
get (pos) {
|
||||||
let n = this._start
|
let n = this._start
|
||||||
while (n !== null) {
|
while (n !== null) {
|
||||||
if (!n._deleted) {
|
if (!n._deleted && n._countable) {
|
||||||
if (pos < n._length) {
|
if (pos < n._length) {
|
||||||
if (n.constructor === ItemJSON || n.constructor === ItemString) {
|
if (n.constructor === ItemJSON || n.constructor === ItemString) {
|
||||||
return n._content[pos]
|
return n._content[pos]
|
||||||
@ -84,7 +84,7 @@ export default class YArray extends Type {
|
|||||||
let pos = 0
|
let pos = 0
|
||||||
let n = this._start
|
let n = this._start
|
||||||
while (n !== null) {
|
while (n !== null) {
|
||||||
if (!n._deleted) {
|
if (!n._deleted && n._countable) {
|
||||||
if (n instanceof Type) {
|
if (n instanceof Type) {
|
||||||
f(n, pos++, this)
|
f(n, pos++, this)
|
||||||
} else {
|
} else {
|
||||||
@ -103,7 +103,7 @@ export default class YArray extends Type {
|
|||||||
let length = 0
|
let length = 0
|
||||||
let n = this._start
|
let n = this._start
|
||||||
while (n !== null) {
|
while (n !== null) {
|
||||||
if (!n._deleted) {
|
if (!n._deleted && n._countable) {
|
||||||
length += n._length
|
length += n._length
|
||||||
}
|
}
|
||||||
n = n._right
|
n = n._right
|
||||||
@ -144,7 +144,7 @@ export default class YArray extends Type {
|
|||||||
let item = this._start
|
let item = this._start
|
||||||
let count = 0
|
let count = 0
|
||||||
while (item !== null && length > 0) {
|
while (item !== null && length > 0) {
|
||||||
if (!item._deleted) {
|
if (!item._deleted && item._countable) {
|
||||||
if (count <= pos && pos < count + item._length) {
|
if (count <= pos && pos < count + item._length) {
|
||||||
const diffDel = pos - count
|
const diffDel = pos - count
|
||||||
item = item._splitAt(this._y, diffDel)
|
item = item._splitAt(this._y, diffDel)
|
||||||
|
@ -1,7 +1,66 @@
|
|||||||
import ItemString from '../Struct/ItemString.js'
|
import ItemString from '../Struct/ItemString.js'
|
||||||
|
import ItemFormat from '../Struct/ItemFormat.js'
|
||||||
import YArray from './YArray.js'
|
import YArray from './YArray.js'
|
||||||
import { logID } from '../MessageHandler/messageToString.js'
|
import { logID } from '../MessageHandler/messageToString.js'
|
||||||
|
|
||||||
|
function integrateItem (item, parent, y, left, right) {
|
||||||
|
item._origin = left
|
||||||
|
item._left = left
|
||||||
|
item._right = right
|
||||||
|
item._right_origin = right
|
||||||
|
item._parent = parent
|
||||||
|
if (y !== null) {
|
||||||
|
item._integrate(this._y)
|
||||||
|
} else if (left === null) {
|
||||||
|
parent._start = item
|
||||||
|
} else {
|
||||||
|
left._right = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findPosition (parent, pos, attributes) {
|
||||||
|
let currentAttributes = new Map()
|
||||||
|
let left = null
|
||||||
|
let right = parent._start
|
||||||
|
let count = 0
|
||||||
|
while (right !== null) {
|
||||||
|
switch (right.constructor) {
|
||||||
|
// case ItemBlockFormat: do not break..
|
||||||
|
case ItemString:
|
||||||
|
const rightLen = right._deleted ? 0 : (right._length - 1)
|
||||||
|
if (count <= pos && pos <= count + rightLen) {
|
||||||
|
const splitDiff = pos - count
|
||||||
|
right = right._splitAt(parent._y, splitDiff)
|
||||||
|
left = right._left
|
||||||
|
count += splitDiff
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (!right._deleted) {
|
||||||
|
count += right._length
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case ItemFormat:
|
||||||
|
if (right._deleted === false) {
|
||||||
|
const key = right.key
|
||||||
|
const value = right.value
|
||||||
|
if (value === null) {
|
||||||
|
currentAttributes.delete(key)
|
||||||
|
} else if (attributes.hasOwnProperty(key)) {
|
||||||
|
// only set if relevant
|
||||||
|
currentAttributes.set(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
left = right
|
||||||
|
right = right._right
|
||||||
|
}
|
||||||
|
if (pos > count) {
|
||||||
|
throw new Error('Position exceeds array range!')
|
||||||
|
}
|
||||||
|
return [left, right, currentAttributes]
|
||||||
|
}
|
||||||
|
|
||||||
export default class YText extends YArray {
|
export default class YText extends YArray {
|
||||||
constructor (string) {
|
constructor (string) {
|
||||||
super()
|
super()
|
||||||
@ -13,55 +72,128 @@ export default class YText extends YArray {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
toString () {
|
toString () {
|
||||||
const strBuilder = []
|
let str = ''
|
||||||
let n = this._start
|
let n = this._start
|
||||||
while (n !== null) {
|
while (n !== null) {
|
||||||
if (!n._deleted) {
|
if (!n._deleted && n._countable) {
|
||||||
strBuilder.push(n._content)
|
str += n._content
|
||||||
}
|
}
|
||||||
n = n._right
|
n = n._right
|
||||||
}
|
}
|
||||||
return strBuilder.join('')
|
return str
|
||||||
}
|
}
|
||||||
insert (pos, text) {
|
/**
|
||||||
|
* As defined by Quilljs - https://quilljs.com/docs/delta/
|
||||||
|
*/
|
||||||
|
toRichtextDelta () {
|
||||||
|
let ops = []
|
||||||
|
let currentAttributes = new Map()
|
||||||
|
let str = ''
|
||||||
|
let n = this._start
|
||||||
|
function packStr () {
|
||||||
|
if (str.length > 0) {
|
||||||
|
// pack str with attributes to ops
|
||||||
|
let attributes = {}
|
||||||
|
for (let [key, value] of currentAttributes) {
|
||||||
|
attributes[key] = value
|
||||||
|
}
|
||||||
|
ops.push({ insert: str, attributes })
|
||||||
|
str = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (n !== null) {
|
||||||
|
if (!n._deleted) {
|
||||||
|
switch (n.constructor) {
|
||||||
|
case ItemString:
|
||||||
|
str += n._content
|
||||||
|
break
|
||||||
|
case ItemFormat:
|
||||||
|
packStr()
|
||||||
|
const value = n.value
|
||||||
|
const key = n.key
|
||||||
|
if (value === null) {
|
||||||
|
currentAttributes.delete(key)
|
||||||
|
} else {
|
||||||
|
currentAttributes.set(key, value)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n = n._right
|
||||||
|
}
|
||||||
|
packStr()
|
||||||
|
return ops
|
||||||
|
}
|
||||||
|
insert (pos, text, attributes = {}) {
|
||||||
if (text.length <= 0) {
|
if (text.length <= 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this._transact(y => {
|
this._transact(y => {
|
||||||
let left = null
|
let [left, right, currentAttributes] = findPosition(this, pos, attributes)
|
||||||
let right = this._start
|
let negatedAttributes = new Map()
|
||||||
let count = 0
|
// insert format-start items
|
||||||
while (right !== null) {
|
for (let key in attributes) {
|
||||||
const rightLen = right._deleted ? 0 : (right._length - 1)
|
const val = attributes[key]
|
||||||
if (count <= pos && pos <= count + rightLen) {
|
const currentVal = currentAttributes.get(key)
|
||||||
const splitDiff = pos - count
|
if (currentVal !== val) {
|
||||||
right = right._splitAt(this._y, splitDiff)
|
// save negated attribute (set null if currentVal undefined)
|
||||||
left = right._left
|
negatedAttributes.set(key, currentVal || null)
|
||||||
count += splitDiff
|
let format = new ItemFormat()
|
||||||
break
|
format.key = key
|
||||||
|
format.value = val
|
||||||
|
integrateItem(format, this, y, left, right)
|
||||||
|
left = format
|
||||||
}
|
}
|
||||||
if (!right._deleted) {
|
|
||||||
count += right._length
|
|
||||||
}
|
|
||||||
left = right
|
|
||||||
right = right._right
|
|
||||||
}
|
|
||||||
if (pos > count) {
|
|
||||||
throw new Error('Position exceeds array range!')
|
|
||||||
}
|
}
|
||||||
|
// insert text content
|
||||||
let item = new ItemString()
|
let item = new ItemString()
|
||||||
item._origin = left
|
|
||||||
item._left = left
|
|
||||||
item._right = right
|
|
||||||
item._right_origin = right
|
|
||||||
item._parent = this
|
|
||||||
item._content = text
|
item._content = text
|
||||||
if (y !== null) {
|
integrateItem(item, this, y, left, right)
|
||||||
item._integrate(this._y)
|
left = item
|
||||||
} else if (left === null) {
|
// negate applied formats
|
||||||
this._start = item
|
for (let [key, value] of negatedAttributes) {
|
||||||
} else {
|
let format = new ItemFormat()
|
||||||
left._right = item
|
format.key = key
|
||||||
|
format.value = value
|
||||||
|
integrateItem(format, this, y, left, right)
|
||||||
|
left = format
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
format (pos, length, attributes) {
|
||||||
|
this._transact(y => {
|
||||||
|
let [left, _right, currentAttributes] = findPosition(this, pos, attributes)
|
||||||
|
if (_right === null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let negatedAttributes = new Map()
|
||||||
|
// insert format-start items
|
||||||
|
for (let key in attributes) {
|
||||||
|
const val = attributes[key]
|
||||||
|
const currentVal = currentAttributes.get(key)
|
||||||
|
if (currentVal !== val) {
|
||||||
|
// save negated attribute (set null if currentVal undefined)
|
||||||
|
negatedAttributes.set(key, currentVal || null)
|
||||||
|
let format = new ItemFormat()
|
||||||
|
format.key = key
|
||||||
|
format.value = val
|
||||||
|
integrateItem(format, this, y, left, _right)
|
||||||
|
left = format
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// iterate until first non-format or null is found
|
||||||
|
// delete all formats with attributes[format.key] != null
|
||||||
|
while (length > 0 && left !== null) {
|
||||||
|
if (left._deleted === false) {
|
||||||
|
if (left.constructor === ItemFormat) {
|
||||||
|
if (attributes[left.key] != null) {
|
||||||
|
left.delete(y)
|
||||||
|
}
|
||||||
|
} else if (length < left._length) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
left = left._right
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user