cleanup docs
This commit is contained in:
parent
026675b438
commit
6dd43cde17
@ -2,7 +2,7 @@
|
|||||||
import { createMutualExclude } from '../Util/mutualExclude.js'
|
import { createMutualExclude } from '../Util/mutualExclude.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class for bindings
|
* Abstract class for bindings.
|
||||||
*
|
*
|
||||||
* A binding handles data binding from a Yjs type to a data object. For example,
|
* A binding handles data binding from a Yjs type to a data object. For example,
|
||||||
* you can bind a Quill editor instance to a YText instance with the `QuillBinding` class.
|
* you can bind a Quill editor instance to a YText instance with the `QuillBinding` class.
|
||||||
@ -18,16 +18,27 @@ import { createMutualExclude } from '../Util/mutualExclude.js'
|
|||||||
*/
|
*/
|
||||||
export default class Binding {
|
export default class Binding {
|
||||||
/**
|
/**
|
||||||
* @param {YType} type Yjs type
|
* @param {YType} type Yjs type.
|
||||||
* @param {any} target Binding Target
|
* @param {any} target Binding Target.
|
||||||
*/
|
*/
|
||||||
constructor (type, target) {
|
constructor (type, target) {
|
||||||
|
/**
|
||||||
|
* The Yjs type that is bound to `target`
|
||||||
|
* @type {YType}
|
||||||
|
*/
|
||||||
this.type = type
|
this.type = type
|
||||||
|
/**
|
||||||
|
* The target that `type` is bound to.
|
||||||
|
* @type {*}
|
||||||
|
*/
|
||||||
this.target = target
|
this.target = target
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
this._mutualExclude = createMutualExclude()
|
this._mutualExclude = createMutualExclude()
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Remove all data observers (both from the type and th target).
|
* Remove all data observers (both from the type and the target).
|
||||||
*/
|
*/
|
||||||
destroy () {
|
destroy () {
|
||||||
this.type = null
|
this.type = null
|
||||||
|
@ -17,9 +17,9 @@ import { removeAssociation } from './util.js'
|
|||||||
* This binding is automatically destroyed when its parent is deleted.
|
* This binding is automatically destroyed when its parent is deleted.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const div = document.createElement('div')
|
* const div = document.createElement('div')
|
||||||
* const type = y.define('xml', Y.XmlFragment)
|
* const type = y.define('xml', Y.XmlFragment)
|
||||||
* const binding = new Y.QuillBinding(type, div)
|
* const binding = new Y.QuillBinding(type, div)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export default class DomBinding extends Binding {
|
export default class DomBinding extends Binding {
|
||||||
@ -27,12 +27,28 @@ export default class DomBinding extends Binding {
|
|||||||
* @param {YXmlFragment} type The bind source. This is the ultimate source of
|
* @param {YXmlFragment} type The bind source. This is the ultimate source of
|
||||||
* truth.
|
* truth.
|
||||||
* @param {Element} target The bind target. Mirrors the target.
|
* @param {Element} target The bind target. Mirrors the target.
|
||||||
|
* @param {Object} [opts] Optional configurations
|
||||||
|
|
||||||
|
* @param {FilterFunction} [opts.filter=defaultFilter] The filter function to use.
|
||||||
*/
|
*/
|
||||||
constructor (type, target, opts = {}) {
|
constructor (type, target, opts = {}) {
|
||||||
// Binding handles textType as this.type and domTextarea as this.target
|
// Binding handles textType as this.type and domTextarea as this.target
|
||||||
super(type, target)
|
super(type, target)
|
||||||
|
/**
|
||||||
|
* Maps each DOM element to the type that it is associated with.
|
||||||
|
* @type {Map}
|
||||||
|
*/
|
||||||
this.domToType = new Map()
|
this.domToType = new Map()
|
||||||
|
/**
|
||||||
|
* Maps each YXml type to the DOM element that it is associated with.
|
||||||
|
* @type {Map}
|
||||||
|
*/
|
||||||
this.typeToDom = new Map()
|
this.typeToDom = new Map()
|
||||||
|
/**
|
||||||
|
* Defines which DOM attributes and elements to filter out.
|
||||||
|
* Also filters remote changes.
|
||||||
|
* @type {FilterFunction}
|
||||||
|
*/
|
||||||
this.filter = opts.filter || defaultFilter
|
this.filter = opts.filter || defaultFilter
|
||||||
// set initial value
|
// set initial value
|
||||||
target.innerHTML = ''
|
target.innerHTML = ''
|
||||||
@ -103,6 +119,7 @@ export default class DomBinding extends Binding {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* NOTE: currently does not apply filter to existing elements!
|
* NOTE: currently does not apply filter to existing elements!
|
||||||
|
* @param {FilterFunction} filter The filter function to use from now on.
|
||||||
*/
|
*/
|
||||||
setFilter (filter) {
|
setFilter (filter) {
|
||||||
this.filter = filter
|
this.filter = filter
|
||||||
@ -110,7 +127,7 @@ export default class DomBinding extends Binding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all properties that are handled by this class
|
* Remove all properties that are handled by this class.
|
||||||
*/
|
*/
|
||||||
destroy () {
|
destroy () {
|
||||||
this.domToType = null
|
this.domToType = null
|
||||||
@ -125,3 +142,11 @@ export default class DomBinding extends Binding {
|
|||||||
super.destroy()
|
super.destroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A filter defines which elements and attributes to share.
|
||||||
|
* Return null if the node should be filtered. Otherwise return the Map of
|
||||||
|
* accepted attributes.
|
||||||
|
*
|
||||||
|
* @typedef {function(nodeName: String, attrs: Map): Map|null} FilterFunction
|
||||||
|
*/
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
import diff from '../../Util/simpleDiff.js'
|
import diff from '../../Util/simpleDiff.js'
|
||||||
import YXmlFragment from '../../Types/YXml/YXmlFragment.js'
|
import YXmlFragment from '../../Types/YXml/YXmlFragment.js'
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* 1. Check if any of the nodes was deleted
|
* 1. Check if any of the nodes was deleted
|
||||||
* 2. Iterate over the children.
|
* 2. Iterate over the children.
|
||||||
* 2.1 If a node exists that is not yet bound to a type, insert a new node
|
* 2.1 If a node exists that is not yet bound to a type, insert a new node
|
||||||
@ -17,6 +17,7 @@ import YXmlFragment from '../../Types/YXml/YXmlFragment.js'
|
|||||||
* recreate a new yxml element that is bound to that node.
|
* recreate a new yxml element that is bound to that node.
|
||||||
* You can detect that a node was moved because expectedId
|
* You can detect that a node was moved because expectedId
|
||||||
* !== actualId in the list
|
* !== actualId in the list
|
||||||
|
* @private
|
||||||
*/
|
*/
|
||||||
function applyChangesFromDom (binding, dom, yxml, _document) {
|
function applyChangesFromDom (binding, dom, yxml, _document) {
|
||||||
if (yxml == null || yxml === false || yxml.constructor === YXmlHook) {
|
if (yxml == null || yxml === false || yxml.constructor === YXmlHook) {
|
||||||
@ -79,6 +80,9 @@ function applyChangesFromDom (binding, dom, yxml, _document) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
export default function domObserver (mutations, _document) {
|
export default function domObserver (mutations, _document) {
|
||||||
this._mutualExclude(() => {
|
this._mutualExclude(() => {
|
||||||
this.type._y.transact(() => {
|
this.type._y.transact(() => {
|
||||||
|
@ -5,7 +5,11 @@ import { createAssociation } from './util.js'
|
|||||||
/**
|
/**
|
||||||
* Creates a Yjs type (YXml) based on the contents of a DOM Element.
|
* Creates a Yjs type (YXml) based on the contents of a DOM Element.
|
||||||
*
|
*
|
||||||
* @param {Element|TextNode}
|
* @param {Element|TextNode} element The DOM Element
|
||||||
|
* @param {?Document} _document Optional. Provide the global document object.
|
||||||
|
* @param {?DomBinding} binding This property should only be set if the type
|
||||||
|
* is going to be bound with the dom-binding.
|
||||||
|
* @return {YXmlElement | YXmlText}
|
||||||
*/
|
*/
|
||||||
export default function domToType (element, _document = document, binding) {
|
export default function domToType (element, _document = document, binding) {
|
||||||
let type
|
let type
|
||||||
|
@ -1,10 +1,27 @@
|
|||||||
import isParentOf from '../../Util/isParentOf.js'
|
import isParentOf from '../../Util/isParentOf.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default filter method (does nothing).
|
||||||
|
*
|
||||||
|
* @param {String} nodeName The nodeName of the element
|
||||||
|
* @param {Map} attrs Map of key-value pairs that are attributes of the node.
|
||||||
|
* @return {Map | null} The allowed attributes or null, if the element should be
|
||||||
|
* filtered.
|
||||||
|
*/
|
||||||
export function defaultFilter (nodeName, attrs) {
|
export function defaultFilter (nodeName, attrs) {
|
||||||
|
// TODO: implement basic filter that filters out dangerous properties!
|
||||||
return attrs
|
return attrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a filter on a type.
|
||||||
|
*
|
||||||
|
* @param {Y} y The Yjs instance.
|
||||||
|
* @param {DomBinding} binding The DOM binding instance that has the dom filter.
|
||||||
|
* @param {YXmlElement | YXmlFragment } type The type to apply the filter to.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
export function applyFilterOnType (y, binding, type) {
|
export function applyFilterOnType (y, binding, type) {
|
||||||
if (isParentOf(binding.type, type)) {
|
if (isParentOf(binding.type, type)) {
|
||||||
const nodeName = type.nodeName
|
const nodeName = type.nodeName
|
||||||
|
@ -5,6 +5,9 @@ import { getRelativePosition, fromRelativePosition } from '../../Util/relativePo
|
|||||||
let browserSelection = null
|
let browserSelection = null
|
||||||
let relativeSelection = null
|
let relativeSelection = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
export let beforeTransactionSelectionFixer
|
export let beforeTransactionSelectionFixer
|
||||||
if (typeof getSelection !== 'undefined') {
|
if (typeof getSelection !== 'undefined') {
|
||||||
beforeTransactionSelectionFixer = function _beforeTransactionSelectionFixer (y, domBinding, transaction, remote) {
|
beforeTransactionSelectionFixer = function _beforeTransactionSelectionFixer (y, domBinding, transaction, remote) {
|
||||||
@ -30,6 +33,9 @@ if (typeof getSelection !== 'undefined') {
|
|||||||
beforeTransactionSelectionFixer = function _fakeBeforeTransactionSelectionFixer () {}
|
beforeTransactionSelectionFixer = function _fakeBeforeTransactionSelectionFixer () {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
export function afterTransactionSelectionFixer (y, domBinding, transaction, remote) {
|
export function afterTransactionSelectionFixer (y, domBinding, transaction, remote) {
|
||||||
if (relativeSelection === null || !remote) {
|
if (relativeSelection === null || !remote) {
|
||||||
return
|
return
|
||||||
|
@ -3,6 +3,9 @@ import YXmlText from '../../Types/YXml/YXmlText.js'
|
|||||||
import YXmlHook from '../../Types/YXml/YXmlHook.js'
|
import YXmlHook from '../../Types/YXml/YXmlHook.js'
|
||||||
import { removeDomChildrenUntilElementFound } from './util.js'
|
import { removeDomChildrenUntilElementFound } from './util.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
export default function typeObserver (events, _document) {
|
export default function typeObserver (events, _document) {
|
||||||
this._mutualExclude(() => {
|
this._mutualExclude(() => {
|
||||||
events.forEach(event => {
|
events.forEach(event => {
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
|
|
||||||
import domToType from './domToType.js'
|
import domToType from './domToType.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates items until an undeleted item is found.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
export function iterateUntilUndeleted (item) {
|
export function iterateUntilUndeleted (item) {
|
||||||
while (item !== null && item._deleted) {
|
while (item !== null && item._deleted) {
|
||||||
item = item._right
|
item = item._right
|
||||||
@ -8,11 +13,23 @@ export function iterateUntilUndeleted (item) {
|
|||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an association (the information that a DOM element belongs to a
|
||||||
|
* type).
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
export function removeAssociation (domBinding, dom, type) {
|
export function removeAssociation (domBinding, dom, type) {
|
||||||
domBinding.domToType.delete(dom)
|
domBinding.domToType.delete(dom)
|
||||||
domBinding.typeToDom.delete(type)
|
domBinding.typeToDom.delete(type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an association (the information that a DOM element belongs to a
|
||||||
|
* type).
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
export function createAssociation (domBinding, dom, type) {
|
export function createAssociation (domBinding, dom, type) {
|
||||||
if (domBinding !== undefined) {
|
if (domBinding !== undefined) {
|
||||||
domBinding.domToType.set(dom, type)
|
domBinding.domToType.set(dom, type)
|
||||||
@ -31,12 +48,18 @@ export function createAssociation (domBinding, dom, type) {
|
|||||||
* the beginning.
|
* the beginning.
|
||||||
* @param {Array<Element>} doms The Dom elements to insert.
|
* @param {Array<Element>} doms The Dom elements to insert.
|
||||||
* @param {?Document} _document Optional. Provide the global document object.
|
* @param {?Document} _document Optional. Provide the global document object.
|
||||||
|
* @param {DomBinding} binding The dom binding
|
||||||
* @return {Array<YXmlElement>} The YxmlElements that are inserted.
|
* @return {Array<YXmlElement>} The YxmlElements that are inserted.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
*/
|
*/
|
||||||
export function insertDomElementsAfter (type, prev, doms, _document, binding) {
|
export function insertDomElementsAfter (type, prev, doms, _document, binding) {
|
||||||
return type.insertAfter(prev, doms.map(dom => domToType(dom, _document, binding)))
|
return type.insertAfter(prev, doms.map(dom => domToType(dom, _document, binding)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
export function insertNodeHelper (yxml, prevExpectedNode, child, _document, binding) {
|
export function insertNodeHelper (yxml, prevExpectedNode, child, _document, binding) {
|
||||||
let insertedNodes = insertDomElementsAfter(yxml, prevExpectedNode, [child], _document, binding)
|
let insertedNodes = insertDomElementsAfter(yxml, prevExpectedNode, [child], _document, binding)
|
||||||
if (insertedNodes.length > 0) {
|
if (insertedNodes.length > 0) {
|
||||||
@ -54,6 +77,8 @@ export function insertNodeHelper (yxml, prevExpectedNode, child, _document, bind
|
|||||||
* @param {Element} currentChild Start removing elements with `currentChild`. If
|
* @param {Element} currentChild Start removing elements with `currentChild`. If
|
||||||
* `currentChild` is `elem` it won't be removed.
|
* `currentChild` is `elem` it won't be removed.
|
||||||
* @param {Element|null} elem The elemnt to look for.
|
* @param {Element|null} elem The elemnt to look for.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
*/
|
*/
|
||||||
export function removeDomChildrenUntilElementFound (parent, currentChild, elem) {
|
export function removeDomChildrenUntilElementFound (parent, currentChild, elem) {
|
||||||
while (currentChild !== elem) {
|
while (currentChild !== elem) {
|
||||||
|
@ -7,6 +7,8 @@ import { integrateRemoteStructs } from './MessageHandler/integrateRemoteStructs.
|
|||||||
|
|
||||||
import debug from 'debug'
|
import debug from 'debug'
|
||||||
|
|
||||||
|
// TODO: rename Connector
|
||||||
|
|
||||||
export default class AbstractConnector {
|
export default class AbstractConnector {
|
||||||
constructor (y, opts) {
|
constructor (y, opts) {
|
||||||
this.y = y
|
this.y = y
|
||||||
|
@ -14,7 +14,6 @@ function getFreshCnf () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
|
||||||
* Abstract persistence class.
|
* Abstract persistence class.
|
||||||
*/
|
*/
|
||||||
export default class AbstractPersistence {
|
export default class AbstractPersistence {
|
||||||
|
@ -1,26 +1,69 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes that are created within a transaction are bundled and sent as one
|
* A transaction is created for every change on the Yjs model. It is possible
|
||||||
* message to the remote peers. This implies that the changes are applied
|
* to bundle changes on the Yjs model in a single transaction to
|
||||||
* in one flush and at most one {@link YEvent} per type is created.
|
* minimize the number on messages sent and the number of observer calls.
|
||||||
|
* If possible the user of this library should bundle as many changes as
|
||||||
|
* possible. Here is an example to illustrate the advantages of bundling:
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const map = y.define('map', YMap)
|
||||||
|
* // Log content when change is triggered
|
||||||
|
* map.observe(function () {
|
||||||
|
* console.log('change triggered')
|
||||||
|
* })
|
||||||
|
* // Each change on the map type triggers a log message:
|
||||||
|
* map.set('a', 0) // => "change triggered"
|
||||||
|
* map.set('b', 0) // => "change triggered"
|
||||||
|
* // When put in a transaction, it will trigger the log after the transaction:
|
||||||
|
* y.transact(function () {
|
||||||
|
* map.set('a', 1)
|
||||||
|
* map.set('b', 1)
|
||||||
|
* }) // => "change triggered"
|
||||||
*
|
*
|
||||||
* It is best to bundle as many changes in a single Transaction as possible.
|
|
||||||
* This way only few changes need to be computed
|
|
||||||
*/
|
*/
|
||||||
export default class Transaction {
|
export default class Transaction {
|
||||||
constructor (y) {
|
constructor (y) {
|
||||||
|
/**
|
||||||
|
* @type {Y} The Yjs instance.
|
||||||
|
*/
|
||||||
this.y = y
|
this.y = y
|
||||||
// types added during transaction
|
/**
|
||||||
|
* All new types that are added during a transaction.
|
||||||
|
* @type {Set<Item>}
|
||||||
|
*/
|
||||||
this.newTypes = new Set()
|
this.newTypes = new Set()
|
||||||
// changed types (does not include new types)
|
/**
|
||||||
// maps from type to parentSubs (item._parentSub = null for array elements)
|
* All types that were directly modified (property added or child
|
||||||
|
* inserted/deleted). New types are not included in this Set.
|
||||||
|
* Maps from type to parentSubs (`item._parentSub = null` for YArray)
|
||||||
|
* @type {Set<YType,String>}
|
||||||
|
*/
|
||||||
this.changedTypes = new Map()
|
this.changedTypes = new Map()
|
||||||
|
// TODO: rename deletedTypes
|
||||||
|
/**
|
||||||
|
* Set of all deleted Types and Structs.
|
||||||
|
* @type {Set<Item>}
|
||||||
|
*/
|
||||||
this.deletedStructs = new Set()
|
this.deletedStructs = new Set()
|
||||||
|
/**
|
||||||
|
* Saves the old state set of the Yjs instance. If a state was modified,
|
||||||
|
* the original value is saved here.
|
||||||
|
* @type {Map<Number,Number>}
|
||||||
|
*/
|
||||||
this.beforeState = new Map()
|
this.beforeState = new Map()
|
||||||
|
/**
|
||||||
|
* Stores the events for the types that observe also child elements.
|
||||||
|
* It is mainly used by `observeDeep`.
|
||||||
|
* @type {Map<YType,Array<YEvent>>}
|
||||||
|
*/
|
||||||
this.changedParentTypes = new Map()
|
this.changedParentTypes = new Map()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
export function transactionTypeChanged (y, type, sub) {
|
export function transactionTypeChanged (y, type, sub) {
|
||||||
if (type !== y && !type._deleted && !y._transaction.newTypes.has(type)) {
|
if (type !== y && !type._deleted && !y._transaction.newTypes.has(type)) {
|
||||||
const changedTypes = y._transaction.changedTypes
|
const changedTypes = y._transaction.changedTypes
|
||||||
|
@ -9,6 +9,7 @@ const bits8 = 0b11111111
|
|||||||
export default class BinaryEncoder {
|
export default class BinaryEncoder {
|
||||||
constructor () {
|
constructor () {
|
||||||
// TODO: implement chained Uint8Array buffers instead of Array buffer
|
// TODO: implement chained Uint8Array buffers instead of Array buffer
|
||||||
|
// TODO: Rewrite all methods as functions!
|
||||||
this.data = []
|
this.data = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,5 +467,4 @@ export default class Tree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flush () {}
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import ID from './ID/ID.js'
|
import ID from './ID/ID.js'
|
||||||
|
import isParentOf from './isParentOf.js'
|
||||||
|
|
||||||
class ReverseOperation {
|
class ReverseOperation {
|
||||||
constructor (y, transaction) {
|
constructor (y, transaction) {
|
||||||
@ -15,16 +16,6 @@ class ReverseOperation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isStructInScope (y, struct, scope) {
|
|
||||||
while (struct !== y) {
|
|
||||||
if (struct === scope) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
struct = struct._parent
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyReverseOperation (y, scope, reverseBuffer) {
|
function applyReverseOperation (y, scope, reverseBuffer) {
|
||||||
let performedUndo = false
|
let performedUndo = false
|
||||||
y.transact(() => {
|
y.transact(() => {
|
||||||
@ -38,7 +29,7 @@ function applyReverseOperation (y, scope, reverseBuffer) {
|
|||||||
while (op._deleted && op._redone !== null) {
|
while (op._deleted && op._redone !== null) {
|
||||||
op = op._redone
|
op = op._redone
|
||||||
}
|
}
|
||||||
if (op._deleted === false && isStructInScope(y, op, scope)) {
|
if (op._deleted === false && isParentOf(scope, op)) {
|
||||||
performedUndo = true
|
performedUndo = true
|
||||||
op._delete(y)
|
op._delete(y)
|
||||||
}
|
}
|
||||||
@ -46,7 +37,7 @@ function applyReverseOperation (y, scope, reverseBuffer) {
|
|||||||
}
|
}
|
||||||
for (let op of undoOp.deletedStructs) {
|
for (let op of undoOp.deletedStructs) {
|
||||||
if (
|
if (
|
||||||
isStructInScope(y, op, scope) &&
|
isParentOf(scope, op) &&
|
||||||
op._parent !== y &&
|
op._parent !== y &&
|
||||||
(
|
(
|
||||||
op._id.user !== y.userID ||
|
op._id.user !== y.userID ||
|
||||||
|
@ -7,7 +7,15 @@ export default class YEvent {
|
|||||||
* @param {YType} target The changed type.
|
* @param {YType} target The changed type.
|
||||||
*/
|
*/
|
||||||
constructor (target) {
|
constructor (target) {
|
||||||
|
/**
|
||||||
|
* The type on which this event was created on.
|
||||||
|
* @type {YType}
|
||||||
|
*/
|
||||||
this.target = target
|
this.target = target
|
||||||
|
/**
|
||||||
|
* The current target on which the observe callback is called.
|
||||||
|
* @type {YType}
|
||||||
|
*/
|
||||||
this.currentTarget = target
|
this.currentTarget = target
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global crypto */
|
/* global crypto */
|
||||||
|
|
||||||
export function generateUserID () {
|
export function generateRandomUint32 () {
|
||||||
if (typeof crypto !== 'undefined' && crypto.getRandomValue != null) {
|
if (typeof crypto !== 'undefined' && crypto.getRandomValue != null) {
|
||||||
// browser
|
// browser
|
||||||
let arr = new Uint32Array(1)
|
let arr = new Uint32Array(1)
|
@ -5,6 +5,8 @@
|
|||||||
* @param {Type} parent
|
* @param {Type} parent
|
||||||
* @param {Type} child
|
* @param {Type} child
|
||||||
* @return {Boolean} Whether `parent` is a parent of `child`.
|
* @return {Boolean} Whether `parent` is a parent of `child`.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
*/
|
*/
|
||||||
export default function isParentOf (parent, child) {
|
export default function isParentOf (parent, child) {
|
||||||
child = child._parent
|
child = child._parent
|
||||||
|
@ -1,4 +1,22 @@
|
|||||||
|
|
||||||
|
// TODO: rename mutex
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a mutual exclude function with the following property:
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const mutualExclude = createMutualExclude()
|
||||||
|
* mutualExclude(function () {
|
||||||
|
* // This function is immediately executed
|
||||||
|
* mutualExclude(function () {
|
||||||
|
* // This function is never executed, as it is called with the same
|
||||||
|
* // mutualExclude
|
||||||
|
* })
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* @return {Function} A mutual exclude function
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
export function createMutualExclude () {
|
export function createMutualExclude () {
|
||||||
var token = true
|
var token = true
|
||||||
return function mutualExclude (f) {
|
return function mutualExclude (f) {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import ID from './ID/ID.js'
|
import ID from './ID/ID.js'
|
||||||
import RootID from './ID/RootID.js'
|
import RootID from './ID/RootID.js'
|
||||||
|
|
||||||
|
// TODO: Implement function to describe ranges
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A relative position that is based on the Yjs model. In contrast to an
|
* A relative position that is based on the Yjs model. In contrast to an
|
||||||
* absolute position (position by index), the relative position can be
|
* absolute position (position by index), the relative position can be
|
||||||
|
@ -11,16 +11,16 @@
|
|||||||
* a === b // values match
|
* a === b // values match
|
||||||
*
|
*
|
||||||
* @typedef {Object} SimpleDiff
|
* @typedef {Object} SimpleDiff
|
||||||
* @property {NaturalNumber} pos The index where changes were applied
|
* @property {Number} pos The index where changes were applied
|
||||||
* @property {NaturalNumber} delete The number of characters to delete starting
|
* @property {Number} delete The number of characters to delete starting
|
||||||
* at `index`.
|
* at `index`.
|
||||||
* @property {String} insert The new text to insert at `index` after applying
|
* @property {String} insert The new text to insert at `index` after applying
|
||||||
* `delete`
|
* `delete`
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a diff between two strings. This diff implementation is intentionally
|
* Create a diff between two strings. This diff implementation is highly
|
||||||
* not very smart.
|
* efficient, but not very sophisticated.
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
* @param {String} a The old version of the string
|
* @param {String} a The old version of the string
|
||||||
|
@ -12,15 +12,30 @@ import ItemEmbed from '../Struct/ItemEmbed.js'
|
|||||||
const structs = new Map()
|
const structs = new Map()
|
||||||
const references = new Map()
|
const references = new Map()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a new Yjs types. The same type must be defined with the same
|
||||||
|
* reference on all clients!
|
||||||
|
*
|
||||||
|
* @param {Number} reference
|
||||||
|
* @param {class} structConstructor
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
export function registerStruct (reference, structConstructor) {
|
export function registerStruct (reference, structConstructor) {
|
||||||
structs.set(reference, structConstructor)
|
structs.set(reference, structConstructor)
|
||||||
references.set(structConstructor, reference)
|
references.set(structConstructor, reference)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
export function getStruct (reference) {
|
export function getStruct (reference) {
|
||||||
return structs.get(reference)
|
return structs.get(reference)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
export function getStructReference (typeConstructor) {
|
export function getStructReference (typeConstructor) {
|
||||||
return references.get(typeConstructor)
|
return references.get(typeConstructor)
|
||||||
}
|
}
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
|
|
||||||
import YMap from '../Types/YMap'
|
|
||||||
import YArray from '../Types/YArray'
|
|
||||||
|
|
||||||
export function writeObjectToYMap (object, type) {
|
|
||||||
for (var key in object) {
|
|
||||||
var val = object[key]
|
|
||||||
if (Array.isArray(val)) {
|
|
||||||
type.set(key, YArray)
|
|
||||||
writeArrayToYArray(val, type.get(key))
|
|
||||||
} else if (typeof val === 'object') {
|
|
||||||
type.set(key, YMap)
|
|
||||||
writeObjectToYMap(val, type.get(key))
|
|
||||||
} else {
|
|
||||||
type.set(key, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function writeArrayToYArray (array, type) {
|
|
||||||
for (var i = array.length - 1; i >= 0; i--) {
|
|
||||||
var val = array[i]
|
|
||||||
if (Array.isArray(val)) {
|
|
||||||
type.insert(0, [YArray])
|
|
||||||
writeArrayToYArray(val, type.get(0))
|
|
||||||
} else if (typeof val === 'object') {
|
|
||||||
type.insert(0, [YMap])
|
|
||||||
writeObjectToYMap(val, type.get(0))
|
|
||||||
} else {
|
|
||||||
type.insert(0, [val])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
10
src/Y.js
10
src/Y.js
@ -1,19 +1,13 @@
|
|||||||
import DeleteStore from './Store/DeleteStore.js'
|
import DeleteStore from './Store/DeleteStore.js'
|
||||||
import OperationStore from './Store/OperationStore.js'
|
import OperationStore from './Store/OperationStore.js'
|
||||||
import StateStore from './Store/StateStore.js'
|
import StateStore from './Store/StateStore.js'
|
||||||
import { generateUserID } from './Util/generateUserID.js'
|
import { generateRandomUint32 } from './Util/generateRandomUint32.js'
|
||||||
import RootID from './Util/ID/RootID.js'
|
import RootID from './Util/ID/RootID.js'
|
||||||
import NamedEventHandler from './Util/NamedEventHandler.js'
|
import NamedEventHandler from './Util/NamedEventHandler.js'
|
||||||
import Transaction from './Transaction.js'
|
import Transaction from './Transaction.js'
|
||||||
|
|
||||||
export { default as DomBinding } from './Bindings/DomBinding/DomBinding.js'
|
export { default as DomBinding } from './Bindings/DomBinding/DomBinding.js'
|
||||||
|
|
||||||
/**
|
|
||||||
* A positive natural number including zero: 0, 1, 2, ..
|
|
||||||
*
|
|
||||||
* @typedef {number} NaturalNumber
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Anything that can be encoded with `JSON.stringify` and can be decoded with
|
* Anything that can be encoded with `JSON.stringify` and can be decoded with
|
||||||
* `JSON.parse`.
|
* `JSON.parse`.
|
||||||
@ -47,7 +41,7 @@ export default class Y extends NamedEventHandler {
|
|||||||
this._contentReady = false
|
this._contentReady = false
|
||||||
this._opts = opts
|
this._opts = opts
|
||||||
if (typeof opts.userID !== 'number') {
|
if (typeof opts.userID !== 'number') {
|
||||||
this.userID = generateUserID()
|
this.userID = generateRandomUint32()
|
||||||
} else {
|
} else {
|
||||||
this.userID = opts.userID
|
this.userID = opts.userID
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { test } from '../node_modules/cutest/cutest.mjs'
|
import { test } from '../node_modules/cutest/cutest.mjs'
|
||||||
import BinaryEncoder from '../src/Util/Binary/Encoder.js'
|
import BinaryEncoder from '../src/Util/Binary/Encoder.js'
|
||||||
import BinaryDecoder from '../src/Util/Binary/Decoder.js'
|
import BinaryDecoder from '../src/Util/Binary/Decoder.js'
|
||||||
import { generateUserID } from '../src/Util/generateUserID.js'
|
import { generateRandomUint32 } from '../src/Util/generateRandomUint32.js'
|
||||||
import Chance from 'chance'
|
import Chance from 'chance'
|
||||||
|
|
||||||
function testEncoding (t, write, read, val) {
|
function testEncoding (t, write, read, val) {
|
||||||
@ -43,7 +43,7 @@ test('varUint random', async function varUintRandom (t) {
|
|||||||
|
|
||||||
test('varUint random user id', async function varUintRandomUserId (t) {
|
test('varUint random user id', async function varUintRandomUserId (t) {
|
||||||
t.getSeed() // enforces that this test is repeated
|
t.getSeed() // enforces that this test is repeated
|
||||||
testEncoding(t, writeVarUint, readVarUint, generateUserID())
|
testEncoding(t, writeVarUint, readVarUint, generateRandomUint32())
|
||||||
})
|
})
|
||||||
|
|
||||||
const writeVarString = (encoder, val) => encoder.writeVarString(val)
|
const writeVarString = (encoder, val) => encoder.writeVarString(val)
|
||||||
|
@ -156,7 +156,7 @@ export async function initArrays (t, opts) {
|
|||||||
users: []
|
users: []
|
||||||
}
|
}
|
||||||
var chance = opts.chance || new Chance(t.getSeed() * 1000000000)
|
var chance = opts.chance || new Chance(t.getSeed() * 1000000000)
|
||||||
var conn = Object.assign({ room: 'debugging_' + t.name, generateUserId: false, testContext: t, chance }, connector)
|
var conn = Object.assign({ room: 'debugging_' + t.name, testContext: t, chance }, connector)
|
||||||
for (let i = 0; i < opts.users; i++) {
|
for (let i = 0; i < opts.users; i++) {
|
||||||
let connOpts
|
let connOpts
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user