/** * @module Y */ import { StructStore, AbstractType, YArray, YText, YMap, YXmlFragment, transact, Item, Transaction, YEvent // eslint-disable-line } from '../internals.js' import { Observable } from 'lib0/observable.js' import * as random from 'lib0/random.js' import * as map from 'lib0/map.js' /** * A Yjs instance handles the state of shared data. * @extends Observable */ export class Doc extends Observable { /** * @param {Object|undefined} conf configuration */ constructor (conf = {}) { super() this.gc = conf.gc || true this.clientID = random.uint32() /** * @type {Map>} */ this.share = new Map() this.store = new StructStore() /** * @type {Transaction | null} * @private */ this._transaction = null /** * @type {Array} * @private */ this._transactionCleanups = [] } /** * Changes that happen inside of a transaction are bundled. This means that * the observer fires _after_ the transaction is finished and that all changes * that happened inside of the transaction are sent as one message to the * other peers. * * @param {function(Transaction):void} f The function that should be executed as a transaction * @param {any} [origin] Origin of who started the transaction. Will be stored on transaction.origin * * @public */ transact (f, origin = null) { transact(this, f, origin) } /** * Define a shared data type. * * Multiple calls of `y.get(name, TypeConstructor)` yield the same result * and do not overwrite each other. I.e. * `y.define(name, Y.Array) === y.define(name, Y.Array)` * * After this method is called, the type is also available on `y.share.get(name)`. * * *Best Practices:* * Define all types right after the Yjs instance is created and store them in a separate object. * Also use the typed methods `getText(name)`, `getArray(name)`, .. * * @example * const y = new Y(..) * const appState = { * document: y.getText('document') * comments: y.getArray('comments') * } * * @param {string} name * @param {Function} TypeConstructor The constructor of the type definition. E.g. Y.Text, Y.Array, Y.Map, ... * @return {AbstractType} The created type. Constructed with TypeConstructor * * @public */ get (name, TypeConstructor = AbstractType) { const type = map.setIfUndefined(this.share, name, () => { // @ts-ignore const t = new TypeConstructor() t._integrate(this, null) return t }) const Constr = type.constructor if (TypeConstructor !== AbstractType && Constr !== TypeConstructor) { if (Constr === AbstractType) { // @ts-ignore const t = new TypeConstructor() t._map = type._map type._map.forEach(/** @param {Item?} n */ n => { for (; n !== null; n = n.left) { n.parent = t } }) t._start = type._start for (let n = t._start; n !== null; n = n.right) { n.parent = t } t._length = type._length this.share.set(name, t) t._integrate(this, null) return t } else { throw new Error(`Type with the name ${name} has already been defined with a different constructor`) } } return type } /** * @template T * @param {string} name * @return {YArray} * * @public */ getArray (name) { // @ts-ignore return this.get(name, YArray) } /** * @param {string} name * @return {YText} * * @public */ getText (name) { // @ts-ignore return this.get(name, YText) } /** * @param {string} name * @return {YMap} * * @public */ getMap (name) { // @ts-ignore return this.get(name, YMap) } /** * @param {string} name * @return {YXmlFragment} * * @public */ getXmlFragment (name) { // @ts-ignore return this.get(name, YXmlFragment) } /** * Emit `destroy` event and unregister all event handlers. * * @protected */ destroy () { this.emit('destroyed', [true]) super.destroy() } /** * @param {string} eventName * @param {function} f */ on (eventName, f) { super.on(eventName, f) } /** * @param {string} eventName * @param {function} f */ off (eventName, f) { super.off(eventName, f) } }