diff --git a/.esdoc.json b/.esdoc.json index 90752511..eed42cb0 100644 --- a/.esdoc.json +++ b/.esdoc.json @@ -1,6 +1,7 @@ { - "source": "./src", + "source": ".", "destination": "./docs", + "excludes": ["build", "node_modules", "tests-lib", "test"], "plugins": [{ "name": "esdoc-standard-plugin", "option": { diff --git a/.jsdoc.json b/.jsdoc.json new file mode 100644 index 00000000..c663854c --- /dev/null +++ b/.jsdoc.json @@ -0,0 +1,51 @@ +{ + "sourceType": "module", + "tags": { + "allowUnknownTags": true, + "dictionaries": ["jsdoc"] + }, + "source": { + "include": ["types", "utils/UndoManager.js", "utils/Y.js", "provider", "bindings"], + "includePattern": ".js$", + "excludePattern": "(node_modules/|docs|build|examples)" + }, + "plugins": [ + "plugins/markdown" + ], + "templates": { + "referenceTitle": "Yjs", + "disableSort": false, + "useCollapsibles": true, + "collapse": true, + "resources": { + "y-js.org": "y-js.org" + }, + "logo": { + "url": "http://y-js.org/images/yjs.png", + "width": "162px", + "height": "162px", + "link": "/" + }, + "tabNames": { + "api": "API", + "tutorials": "Examples" + }, + "footerText": "Shared Editing", + "css": [ + "./style.css" + ], + "default": { + "staticFiles": { + "include": ["examples/"] + } + } + }, + "opts": { + "destination": "./docs/", + "encoding": "utf8", + "private": false, + "recurse": true, + "template": "./node_modules/tui-jsdoc-template", + "tutorials": "./examples" + } +} diff --git a/bindings/Binding.js b/bindings/Binding.js deleted file mode 100644 index 5a97eb08..00000000 --- a/bindings/Binding.js +++ /dev/null @@ -1,47 +0,0 @@ - -import { createMutex } from '../lib/mutex.js' - -/** - * Abstract class for bindings. - * - * 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. - * - * It is expected that a concrete implementation accepts two parameters - * (type and binding target). - * - * @example - * const quill = new Quill(document.createElement('div')) - * const type = y.define('quill', Y.Text) - * const binding = new Y.QuillBinding(quill, type) - * - */ -export default class Binding { - /** - * @param {YType} type Yjs type. - * @param {any} target Binding Target. - */ - constructor (type, target) { - /** - * The Yjs type that is bound to `target` - * @type {YType} - */ - this.type = type - /** - * The target that `type` is bound to. - * @type {*} - */ - this.target = target - /** - * @private - */ - this._mutualExclude = createMutex() - } - /** - * Remove all data observers (both from the type and the target). - */ - destroy () { - this.type = null - this.target = null - } -} diff --git a/bindings/dom.js b/bindings/dom.js new file mode 100644 index 00000000..4eb4d91f --- /dev/null +++ b/bindings/dom.js @@ -0,0 +1 @@ +export * from './dom/DomBinding.js' diff --git a/bindings/DomBinding/DomBinding.js b/bindings/dom/DomBinding.js similarity index 83% rename from bindings/DomBinding/DomBinding.js rename to bindings/dom/DomBinding.js index d486ffc7..4c12cead 100644 --- a/bindings/DomBinding/DomBinding.js +++ b/bindings/dom/DomBinding.js @@ -1,15 +1,23 @@ +/** + * @module bindings/dom + */ + /* global MutationObserver, getSelection */ -import { fromRelativePosition } from '../../src/Util/relativePosition.js' -import Binding from '../Binding.js' +import { fromRelativePosition } from '../../utils/relativePosition.js' +import { createMutex } from '../../lib/mutex.js' import { createAssociation, removeAssociation } from './util.js' import { beforeTransactionSelectionFixer, afterTransactionSelectionFixer, getCurrentRelativeSelection } from './selection.js' import { defaultFilter, applyFilterOnType } from './filter.js' -import typeObserver from './typeObserver.js' -import domObserver from './domObserver.js' +import { typeObserver } from './typeObserver.js' +import { domObserver } from './domObserver.js' +import { YXmlFragment } from '../../types/YXmlElement.js' // eslint-disable-line /** - * @typedef {import('./filter.js').DomFilter} DomFilter + * @callback DomFilter + * @param {string} nodeName + * @param {Map} attrs + * @return {Map | null} */ /** @@ -22,8 +30,9 @@ import domObserver from './domObserver.js' * const type = y.define('xml', Y.XmlFragment) * const binding = new Y.QuillBinding(type, div) * + * @class */ -export default class DomBinding extends Binding { +export class DomBinding { /** * @param {YXmlFragment} type The bind source. This is the ultimate source of * truth. @@ -31,10 +40,26 @@ export default class DomBinding extends Binding { * @param {Object} [opts] Optional configurations * @param {DomFilter} [opts.filter=defaultFilter] The filter function to use. + * @param {Document} [opts.document=document] The filter function to use. + * @param {Object} [opts.hooks] The filter function to use. + * @param {Element} [opts.scrollingElement=null] The filter function to use. */ constructor (type, target, opts = {}) { // Binding handles textType as this.type and domTextarea as this.target - super(type, target) + /** + * The Yjs type that is bound to `target` + * @type {YXmlFragment} + */ + this.type = type + /** + * The target that `type` is bound to. + * @type {Element} + */ + this.target = target + /** + * @private + */ + this._mutualExclude = createMutex() this.opts = opts opts.document = opts.document || document opts.hooks = opts.hooks || {} @@ -81,16 +106,16 @@ export default class DomBinding extends Binding { this.y = y // Force flush dom changes before Type changes are applied (they might // modify the dom) - this._beforeTransactionHandler = (y, transaction, remote) => { + this._beforeTransactionHandler = y => { this._domObserver(this._mutationObserver.takeRecords()) this._mutualExclude(() => { - beforeTransactionSelectionFixer(this, remote) + beforeTransactionSelectionFixer(this) }) } y.on('beforeTransaction', this._beforeTransactionHandler) - this._afterTransactionHandler = (y, transaction, remote) => { + this._afterTransactionHandler = (y, transaction) => { this._mutualExclude(() => { - afterTransactionSelectionFixer(this, remote) + afterTransactionSelectionFixer(this) }) // remove associations // TODO: this could be done more efficiently @@ -206,13 +231,18 @@ export default class DomBinding extends Binding { y.off('beforeObserverCalls', this._beforeObserverCallsHandler) y.off('afterTransaction', this._afterTransactionHandler) document.removeEventListener('selectionchange', this._selectionchange) - super.destroy() + this.type = null + this.target = null } } + /** * 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 + * @callback FilterFunction + * @param {string} nodeName + * @param {Map} attrs + * @return {Map|null} */ diff --git a/bindings/DomBinding/domObserver.js b/bindings/dom/domObserver.js similarity index 92% rename from bindings/DomBinding/domObserver.js rename to bindings/dom/domObserver.js index b162b288..38c073c7 100644 --- a/bindings/DomBinding/domObserver.js +++ b/bindings/dom/domObserver.js @@ -1,11 +1,14 @@ +/** + * @module bindings/dom + */ -import YXmlHook from '../../src/Types/YXml/YXmlHook.js' +import { YXmlHook } from '../../types/YXmlHook.js' import { iterateUntilUndeleted, removeAssociation, insertNodeHelper } from './util.js' -import diff from '../../lib/simpleDiff.js' -import YXmlFragment from '../../src/Types/YXml/YXmlFragment.js' +import { simpleDiff } from '../../lib/diff.js' +import { YXmlFragment } from '../../types/YXmlElement.js' /** * 1. Check if any of the nodes was deleted @@ -19,7 +22,7 @@ import YXmlFragment from '../../src/Types/YXml/YXmlFragment.js' * !== actualId in the list * @private */ -function applyChangesFromDom (binding, dom, yxml, _document) { +const applyChangesFromDom = (binding, dom, yxml, _document) => { if (yxml == null || yxml === false || yxml.constructor === YXmlHook) { return } @@ -32,7 +35,7 @@ function applyChangesFromDom (binding, dom, yxml, _document) { } } // 1. Check if any of the nodes was deleted - yxml.forEach(function (childType) { + yxml.forEach(childType => { if (knownChildren.has(childType) === false) { childType._delete(y) removeAssociation(binding, binding.typeToDom.get(childType), childType) @@ -83,7 +86,7 @@ function applyChangesFromDom (binding, dom, yxml, _document) { /** * @private */ -export default function domObserver (mutations, _document) { +export function domObserver (mutations, _document) { this._mutualExclude(() => { this.type._y.transact(() => { let diffChildren = new Set() @@ -107,7 +110,7 @@ export default function domObserver (mutations, _document) { } switch (mutation.type) { case 'characterData': - var change = diff(yxml.toString(), dom.nodeValue) + var change = simpleDiff(yxml.toString(), dom.nodeValue) yxml.delete(change.pos, change.remove) yxml.insert(change.pos, change.insert) break diff --git a/bindings/DomBinding/domToType.js b/bindings/dom/domToType.js similarity index 80% rename from bindings/DomBinding/domToType.js rename to bindings/dom/domToType.js index c0bb9361..d43b6ccd 100644 --- a/bindings/DomBinding/domToType.js +++ b/bindings/dom/domToType.js @@ -1,13 +1,20 @@ +/** + * @module bindings/dom + */ + /* eslint-env browser */ -import YXmlText from '../../src/Types/YXml/YXmlText.js' -import YXmlHook from '../../src/Types/YXml/YXmlHook.js' -import YXmlElement from '../../src/Types/YXml/YXmlElement.js' +import { YXmlText } from '../../types/YXmlText.js' +import { YXmlHook } from '../../types/YXmlHook.js' +import { YXmlElement } from '../../types/YXmlElement.js' import { createAssociation, domsToTypes } from './util.js' import { filterDomAttributes, defaultFilter } from './filter.js' +import { DomBinding } from './DomBinding.js' // eslint-disable-line /** - * @typedef {import('./filter.js').DomFilter} DomFilter - * @typedef {import('./DomBinding.js').default} DomBinding + * @callback DomFilter + * @param {string} nodeName + * @param {Map} attrs + * @return {Map | null} */ /** @@ -20,7 +27,7 @@ import { filterDomAttributes, defaultFilter } from './filter.js' * @param {?DomBinding} binding Warning: This property is for internal use only! * @return {YXmlElement | YXmlText | false} */ -export default function domToType (element, _document = document, hooks = {}, filter = defaultFilter, binding) { +export const domToType = (element, _document = document, hooks = {}, filter = defaultFilter, binding) => { /** * @type {any} */ diff --git a/bindings/DomBinding/filter.js b/bindings/dom/filter.js similarity index 71% rename from bindings/DomBinding/filter.js rename to bindings/dom/filter.js index 38136584..d8d3ca45 100644 --- a/bindings/DomBinding/filter.js +++ b/bindings/dom/filter.js @@ -1,12 +1,11 @@ -import isParentOf from '../../src/Util/isParentOf.js' - /** - * @callback DomFilter - * @param {string} nodeName - * @param {Map} attrs - * @return {Map | null} + * @module bindings/dom */ +import { isParentOf } from '../../utils/isParentOf.js' +import * as Y from '../../index.js' +import { DomBinding } from './DomBinding.js' // eslint-disable-line + /** * Default filter method (does nothing). * @@ -15,7 +14,7 @@ import isParentOf from '../../src/Util/isParentOf.js' * @return {Map | null} The allowed attributes or null, if the element should be * filtered. */ -export function defaultFilter (nodeName, attrs) { +export const defaultFilter = (nodeName, attrs) => { // TODO: implement basic filter that filters out dangerous properties! return attrs } @@ -23,7 +22,7 @@ export function defaultFilter (nodeName, attrs) { /** * */ -export function filterDomAttributes (dom, filter) { +export const filterDomAttributes = (dom, filter) => { const attrs = new Map() for (let i = dom.attributes.length - 1; i >= 0; i--) { const attr = dom.attributes[i] @@ -35,14 +34,14 @@ export function filterDomAttributes (dom, filter) { /** * Applies a filter on a type. * - * @param {Y} y The Yjs instance. + * @param {Y.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. + * @param {Y.XmlElement | Y.XmlFragment } type The type to apply the filter to. * * @private */ -export function applyFilterOnType (y, binding, type) { - if (isParentOf(binding.type, type)) { +export const applyFilterOnType = (y, binding, type) => { + if (isParentOf(binding.type, type) && type instanceof Y.XmlElement) { const nodeName = type.nodeName let attributes = new Map() if (type.getAttributes !== undefined) { @@ -53,7 +52,7 @@ export function applyFilterOnType (y, binding, type) { } const filteredAttributes = binding.filter(nodeName, new Map(attributes)) if (filteredAttributes === null) { - type._delete(y) + type._delete(y, true) } else { // iterate original attributes attributes.forEach((value, key) => { diff --git a/bindings/DomBinding/selection.js b/bindings/dom/selection.js similarity index 76% rename from bindings/DomBinding/selection.js rename to bindings/dom/selection.js index 6e1cdec6..96bdf753 100644 --- a/bindings/DomBinding/selection.js +++ b/bindings/dom/selection.js @@ -1,10 +1,14 @@ +/** + * @module bindings/dom + */ + /* globals getSelection */ -import { getRelativePosition } from '../../src/Util/relativePosition.js' +import { getRelativePosition } from '../../utils/relativePosition.js' let relativeSelection = null -function _getCurrentRelativeSelection (domBinding) { +const _getCurrentRelativeSelection = domBinding => { const { baseNode, baseOffset, extentNode, extentOffset } = getSelection() const baseNodeType = domBinding.domToType.get(baseNode) const extentNodeType = domBinding.domToType.get(extentNode) @@ -19,7 +23,7 @@ function _getCurrentRelativeSelection (domBinding) { export const getCurrentRelativeSelection = typeof getSelection !== 'undefined' ? _getCurrentRelativeSelection : domBinding => null -export function beforeTransactionSelectionFixer (domBinding) { +export const beforeTransactionSelectionFixer = domBinding => { relativeSelection = getCurrentRelativeSelection(domBinding) } @@ -28,7 +32,7 @@ export function beforeTransactionSelectionFixer (domBinding) { * This prevents any collapsing issues with the local selection. * @private */ -export function afterTransactionSelectionFixer (domBinding) { +export const afterTransactionSelectionFixer = domBinding => { if (relativeSelection !== null) { domBinding.restoreSelection(relativeSelection) } diff --git a/bindings/DomBinding/typeObserver.js b/bindings/dom/typeObserver.js similarity index 92% rename from bindings/DomBinding/typeObserver.js rename to bindings/dom/typeObserver.js index 5e64f8c6..5767a932 100644 --- a/bindings/DomBinding/typeObserver.js +++ b/bindings/dom/typeObserver.js @@ -1,11 +1,15 @@ +/** + * @module bindings/dom + */ + /* eslint-env browser */ /* global getSelection */ -import YXmlText from '../../src/Types/YXml/YXmlText.js' -import YXmlHook from '../../src/Types/YXml/YXmlHook.js' +import { YXmlText } from '../../types/YXmlText.js' +import { YXmlHook } from '../../types/YXmlHook.js' import { removeDomChildrenUntilElementFound } from './util.js' -function findScrollReference (scrollingElement) { +const findScrollReference = scrollingElement => { if (scrollingElement !== null) { let anchor = getSelection().anchorNode if (anchor == null) { @@ -34,7 +38,7 @@ function findScrollReference (scrollingElement) { return null } -function fixScroll (scrollingElement, ref) { +const fixScroll = (scrollingElement, ref) => { if (ref !== null) { const { elem, top } = ref const currentTop = elem.getBoundingClientRect().top @@ -48,7 +52,7 @@ function fixScroll (scrollingElement, ref) { /** * @private */ -export default function typeObserver (events) { +export const typeObserver = function (events) { this._mutualExclude(() => { const scrollRef = findScrollReference(this.scrollingElement) events.forEach(event => { diff --git a/bindings/DomBinding/util.js b/bindings/dom/util.js similarity index 72% rename from bindings/DomBinding/util.js rename to bindings/dom/util.js index 591e8f95..e6c0d1ef 100644 --- a/bindings/DomBinding/util.js +++ b/bindings/dom/util.js @@ -1,19 +1,19 @@ - -import domToType from './domToType.js' - /** - * @typedef {import('../../src/Types/YXml/YXmlText.js').default} YXmlText - * @typedef {import('../../src/Types/YXml/YXmlElement.js').default} YXmlElement - * @typedef {import('../../src/Types/YXml/YXmlHook.js').default} YXmlHook - * @typedef {import('./DomBinding.js').default} DomBinding + * @module bindings/dom */ +import { domToType } from './domToType.js' +import { DomBinding } from './DomBinding.js' // eslint-disable-line +import { YXmlHook } from '../../types/YXmlHook.js' // eslint-disable-line +import { YXmlText } from '../../types/YXmlText.js' // eslint-disable-line +import { YXmlElement, YXmlFragment } from '../../types/YXmlElement.js' // eslint-disable-line + /** * Iterates items until an undeleted item is found. * * @private */ -export function iterateUntilUndeleted (item) { +export const iterateUntilUndeleted = item => { while (item !== null && item._deleted) { item = item._right } @@ -24,12 +24,13 @@ export function iterateUntilUndeleted (item) { * Removes an association (the information that a DOM element belongs to a * type). * + * @private * @param {DomBinding} domBinding The binding object * @param {Element} dom The dom that is to be associated with type * @param {YXmlElement|YXmlHook} type The type that is to be associated with dom * */ -export function removeAssociation (domBinding, dom, type) { +export const removeAssociation = (domBinding, dom, type) => { domBinding.domToType.delete(dom) domBinding.typeToDom.delete(type) } @@ -38,12 +39,13 @@ export function removeAssociation (domBinding, dom, type) { * Creates an association (the information that a DOM element belongs to a * type). * + * @private * @param {DomBinding} domBinding The binding object * @param {DocumentFragment|Element|Text} dom The dom that is to be associated with type - * @param {YXmlElement|YXmlHook|YXmlText} type The type that is to be associated with dom + * @param {YXmlFragment|YXmlElement|YXmlHook|YXmlText} type The type that is to be associated with dom * */ -export function createAssociation (domBinding, dom, type) { +export const createAssociation = (domBinding, dom, type) => { if (domBinding !== undefined) { domBinding.domToType.set(dom, type) domBinding.typeToDom.set(type, dom) @@ -54,11 +56,12 @@ export function createAssociation (domBinding, dom, type) { * If oldDom is associated with a type, associate newDom with the type and * forget about oldDom. If oldDom is not associated with any type, nothing happens. * + * @private * @param {DomBinding} domBinding The binding object * @param {Element} oldDom The existing dom * @param {Element} newDom The new dom object */ -export function switchAssociation (domBinding, oldDom, newDom) { +export const switchAssociation = (domBinding, oldDom, newDom) => { if (domBinding !== undefined) { const type = domBinding.domToType.get(oldDom) if (type !== undefined) { @@ -73,6 +76,7 @@ export function switchAssociation (domBinding, oldDom, newDom) { * The Dom elements will be bound to a new YXmlElement and inserted at the * specified position. * + * @private * @param {YXmlElement} type The type in which to insert DOM elements. * @param {YXmlElement|null} prev The reference node. New YxmlElements are * inserted after this node. Set null to insert at @@ -81,15 +85,13 @@ export function switchAssociation (domBinding, oldDom, newDom) { * @param {?Document} _document Optional. Provide the global document object. * @param {DomBinding} binding The dom binding * @return {Array} The YxmlElements that are inserted. - * - * @private */ -export function insertDomElementsAfter (type, prev, doms, _document, binding) { +export const insertDomElementsAfter = (type, prev, doms, _document, binding) => { const types = domsToTypes(doms, _document, binding.opts.hooks, binding.filter, binding) return type.insertAfter(prev, types) } -export function domsToTypes (doms, _document, hooks, filter, binding) { +export const domsToTypes = (doms, _document, hooks, filter, binding) => { const types = [] for (let dom of doms) { const t = domToType(dom, _document, hooks, filter, binding) @@ -103,7 +105,7 @@ export function domsToTypes (doms, _document, hooks, filter, binding) { /** * @private */ -export function insertNodeHelper (yxml, prevExpectedNode, child, _document, binding) { +export const insertNodeHelper = (yxml, prevExpectedNode, child, _document, binding) => { let insertedNodes = insertDomElementsAfter(yxml, prevExpectedNode, [child], _document, binding) if (insertedNodes.length > 0) { return insertedNodes[0] @@ -115,14 +117,13 @@ export function insertNodeHelper (yxml, prevExpectedNode, child, _document, bind /** * Remove children until `elem` is found. * + * @private * @param {Element} parent The parent of `elem` and `currentChild`. - * @param {Element} currentChild Start removing elements with `currentChild`. If + * @param {Node} currentChild Start removing elements with `currentChild`. If * `currentChild` is `elem` it won't be removed. * @param {Element|null} elem The elemnt to look for. - * - * @private */ -export function removeDomChildrenUntilElementFound (parent, currentChild, elem) { +export const removeDomChildrenUntilElementFound = (parent, currentChild, elem) => { while (currentChild !== elem) { const del = currentChild currentChild = currentChild.nextSibling diff --git a/bindings/ProsemirrorBinding/ProsemirrorBinding.js b/bindings/prosemirror.js similarity index 92% rename from bindings/ProsemirrorBinding/ProsemirrorBinding.js rename to bindings/prosemirror.js index 94050984..8d73442f 100644 --- a/bindings/ProsemirrorBinding/ProsemirrorBinding.js +++ b/bindings/prosemirror.js @@ -1,7 +1,11 @@ -import BindMapping from '../BindMapping.js' +/** + * @module bindings/prosemirror + */ + +import { BindMapping } from '../utils/BindMapping.js' +import * as Y from '../index.js' +import { createMutex } from '../lib/mutex.js' import * as PModel from 'prosemirror-model' -import * as Y from '../../src/index.js' -import { createMutex } from '../../lib/mutex.js' import { Plugin, PluginKey } from 'prosemirror-state' import { Decoration, DecorationSet } from 'prosemirror-view' @@ -11,6 +15,11 @@ import { Decoration, DecorationSet } from 'prosemirror-view' * @typedef {BindMapping} ProsemirrorMapping */ +/** + * The unique prosemirror plugin key for prosemirrorPlugin. + * + * @public + */ export const prosemirrorPluginKey = new PluginKey('yjs') /** @@ -18,6 +27,7 @@ export const prosemirrorPluginKey = new PluginKey('yjs') * * This plugin also keeps references to the type and the shared document so other plugins can access it. * @param {Y.XmlFragment} yXmlFragment + * @return {Plugin} Returns a prosemirror plugin that binds to this type */ export const prosemirrorPlugin = yXmlFragment => { const pluginState = { @@ -51,8 +61,19 @@ export const prosemirrorPlugin = yXmlFragment => { return plugin } +/** + * The unique prosemirror plugin key for cursorPlugin. + * + * @public + */ export const cursorPluginKey = new PluginKey('yjs-cursor') +/** + * A prosemirror plugin that listens to awareness information on Yjs. + * This requires that a `prosemirrorPlugin` is also bound to the prosemirror. + * + * @public + */ export const cursorPlugin = new Plugin({ key: cursorPluginKey, props: { @@ -104,7 +125,12 @@ export const cursorPlugin = new Plugin({ } }) -export default class ProsemirrorBinding { +/** + * Binding for prosemirror. + * + * @protected + */ +export class ProsemirrorBinding { /** * @param {Y.XmlFragment} yXmlFragment The bind source * @param {EditorView} prosemirrorView The target binding @@ -154,6 +180,7 @@ export default class ProsemirrorBinding { } /** + * @private * @param {Y.XmlElement} el * @param {PModel.Schema} schema * @param {ProsemirrorMapping} mapping @@ -168,6 +195,7 @@ export const createNodeIfNotExists = (el, schema, mapping) => { } /** + * @private * @param {Y.XmlElement} el * @param {PModel.Schema} schema * @param {ProsemirrorMapping} mapping @@ -188,6 +216,7 @@ export const createNodeFromYElement = (el, schema, mapping) => { } /** + * @private * @param {Y.Text} text * @param {PModel.Schema} schema * @param {ProsemirrorMapping} mapping @@ -211,6 +240,7 @@ export const createTextNodesFromYText = (text, schema, mapping) => { } /** + * @private * @param {PModel.Node} node * @param {ProsemirrorMapping} mapping * @return {Y.XmlElement | Y.Text} @@ -234,6 +264,7 @@ export const createTypeFromNode = (node, mapping) => { } /** + * @private * @param {Y.XmlFragment} yDomFragment * @param {EditorState} state * @param {BindMapping} mapping diff --git a/bindings/QuillBinding/QuillBinding.js b/bindings/quill.js similarity index 70% rename from bindings/QuillBinding/QuillBinding.js rename to bindings/quill.js index 02995ec9..4cb99da7 100644 --- a/bindings/QuillBinding/QuillBinding.js +++ b/bindings/quill.js @@ -1,10 +1,14 @@ -import Binding from '../Binding.js' +/** + * @module bindings/quill + */ -function typeObserver (event) { +import { createMutex } from '../lib/mutex.js' + +const typeObserver = function (event) { const quill = this.target // Force flush Quill changes. quill.update('yjs') - this._mutualExclude(function () { + this._mutualExclude(() => { // Apply computed delta. quill.updateContents(event.delta, 'yjs') // Force flush Quill changes. Ignore applied changes. @@ -12,7 +16,7 @@ function typeObserver (event) { }) } -function quillObserver (delta) { +const quillObserver = function (delta) { this._mutualExclude(() => { this.type.applyDelta(delta.ops) }) @@ -28,14 +32,28 @@ function quillObserver (delta) { * // Now modifications on the DOM will be reflected in the Type, and the other * // way around! */ -export default class QuillBinding extends Binding { +export class QuillBinding { /** * @param {YText} textType * @param {Quill} quill */ constructor (textType, quill) { // Binding handles textType as this.type and quill as this.target. - super(textType, quill) + /** + * The Yjs type that is bound to `target` + * @type {YText} + */ + this.type = textType + /** + * The target that `type` is bound to. + * @type {Quill} + */ + this.target = quill + /** + * @private + */ + this._mutualExclude = createMutex() + // Set initial value. quill.setContents(textType.toDelta(), 'yjs') // Observers are handled by this class. @@ -48,6 +66,7 @@ export default class QuillBinding extends Binding { // Remove everything that is handled by this class. this.type.unobserve(this._typeObserver) this.target.off('text-change', this._quillObserver) - super.destroy() + this.type = null + this.target = null } } diff --git a/bindings/TextareaBinding/TextareaBinding.js b/bindings/textarea.js similarity index 72% rename from bindings/TextareaBinding/TextareaBinding.js rename to bindings/textarea.js index bdb55abe..73c4c087 100644 --- a/bindings/TextareaBinding/TextareaBinding.js +++ b/bindings/textarea.js @@ -1,7 +1,10 @@ +/** + * @module bindings/textarea + */ -import Binding from '../Binding.js' -import simpleDiff from '../../lib/simpleDiff.js' -import { getRelativePosition, fromRelativePosition } from '../../src/Util/relativePosition.js' +import { simpleDiff } from '../lib/diff.js' +import { getRelativePosition, fromRelativePosition } from '../utils/relativePosition.js' +import { createMutex } from '../lib/mutex.js' function typeObserver () { this._mutualExclude(() => { @@ -35,10 +38,22 @@ function domObserver () { * const binding = new Y.QuillBinding(type, textarea) * */ -export default class TextareaBinding extends Binding { +export class TextareaBinding { constructor (textType, domTextarea) { - // Binding handles textType as this.type and domTextarea as this.target - super(textType, domTextarea) + /** + * The Yjs type that is bound to `target` + * @type {Type} + */ + this.type = textType + /** + * The target that `type` is bound to. + * @type {*} + */ + this.target = domTextarea + /** + * @private + */ + this._mutualExclude = createMutex() // set initial value domTextarea.value = textType.toString() // Observers are handled by this class @@ -51,6 +66,7 @@ export default class TextareaBinding extends Binding { // Remove everything that is handled by this class this.type.unobserve(this._typeObserver) this.target.unobserve(this._domObserver) - super.destroy() + this.type = null + this.target = null } } diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 00000000..378eac25 --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1 @@ +build diff --git a/examples/examples.json b/examples/examples.json new file mode 100644 index 00000000..31e1a19c --- /dev/null +++ b/examples/examples.json @@ -0,0 +1,11 @@ +{ + "prosemirror": { + "title": "Prosemirror Binding" + }, + "textarea": { + "title": "Textarea Binding" + }, + "quill": { + "title": "Quill Binding" + } +} \ No newline at end of file diff --git a/examples/prosemirror.html b/examples/prosemirror.html new file mode 100644 index 00000000..f454a5ab --- /dev/null +++ b/examples/prosemirror.html @@ -0,0 +1,75 @@ + + + + Yjs Prosemirror Example + + + + +
+ +
+ +
+ + + \ No newline at end of file diff --git a/examples/prosemirror.js b/examples/prosemirror.js new file mode 100644 index 00000000..9b9c86b0 --- /dev/null +++ b/examples/prosemirror.js @@ -0,0 +1,20 @@ +import * as Y from '../index.js' +import { prosemirrorPlugin, cursorPlugin } from '../bindings/prosemirror.js' +import { WebsocketProvider } from '../provider/websocket.js' + +import { EditorState } from 'prosemirror-state' +import { EditorView } from 'prosemirror-view' +import { DOMParser } from 'prosemirror-model' +import { schema } from 'prosemirror-schema-basic' +import { exampleSetup } from 'prosemirror-example-setup' + +const provider = new WebsocketProvider('ws://localhost:1234/') +const ydocument = provider.get('prosemirror') +const type = ydocument.define('prosemirror', Y.XmlFragment) + +window.prosemirrorView = new EditorView(document.querySelector('#editor'), { + state: EditorState.create({ + doc: DOMParser.fromSchema(schema).parse(document.querySelector('#content')), + plugins: exampleSetup({schema}).concat([prosemirrorPlugin(type), cursorPlugin]) + }) +}) diff --git a/examples/prosemirror/PlaceholderPlugin.js b/examples/prosemirror/PlaceholderPlugin.js deleted file mode 100644 index fe00368f..00000000 --- a/examples/prosemirror/PlaceholderPlugin.js +++ /dev/null @@ -1,73 +0,0 @@ -/* eslint-env browser */ - -import {Plugin} from 'prosemirror-state' -import {Decoration, DecorationSet} from 'prosemirror-view' -import {schema} from 'prosemirror-schema-basic' - -const findPlaceholder = (state, id) => { - let decos = PlaceholderPlugin.getState(state) - let found = decos.find(null, null, spec => spec.id === id) - return found.length ? found[0].from : null -} - -export const startImageUpload = (view, file) => { - // A fresh object to act as the ID for this upload - let id = {} - - // Replace the selection with a placeholder - let tr = view.state.tr - if (!tr.selection.empty) tr.deleteSelection() - tr.setMeta(PlaceholderPlugin, {add: {id, pos: tr.selection.from}}) - view.dispatch(tr) - - uploadFile(file).then(url => { - let pos = findPlaceholder(view.state, id) - // If the content around the placeholder has been deleted, drop - // the image - if (pos == null) return - // Otherwise, insert it at the placeholder's position, and remove - // the placeholder - view.dispatch(view.state.tr - .replaceWith(pos, pos, schema.nodes.image.create({src: url})) - .setMeta(PlaceholderPlugin, {remove: {id}})) - }, () => { - // On failure, just clean up the placeholder - view.dispatch(tr.setMeta(PlaceholderPlugin, {remove: {id}})) - }) -} - -// This is just a dummy that loads the file and creates a data URL. -// You could swap it out with a function that does an actual upload -// and returns a regular URL for the uploaded file. -function uploadFile (file) { - let reader = new FileReader() - return new Promise((resolve, reject) => { - reader.onload = () => resolve(reader.result) - reader.onerror = () => reject(reader.error) - // Some extra delay to make the asynchronicity visible - setTimeout(() => reader.readAsDataURL(file), 1500) - }) -} - -export const PlaceholderPlugin = new Plugin({ - state: { - init () { return DecorationSet.empty }, - apply (tr, set) { - // Adjust decoration positions to changes made by the transaction - set = set.map(tr.mapping, tr.doc) - // See if the transaction adds or removes any placeholders - let action = tr.getMeta(this) - if (action && action.add) { - let widget = document.createElement('placeholder') - let deco = Decoration.widget(action.add.pos, widget, {id: action.add.id}) - set = set.add(tr.doc, [deco]) - } else if (action && action.remove) { - set = set.remove(set.find(null, null, spec => spec.id === action.remove.id)) - } - return set - } - }, - props: { - decorations (state) { return this.getState(state) } - } -}) diff --git a/examples/prosemirror/README.md b/examples/prosemirror/README.md deleted file mode 100644 index a898b77d..00000000 --- a/examples/prosemirror/README.md +++ /dev/null @@ -1,21 +0,0 @@ - -# Prosemirror Example - -### Run basic websockets server - -```sh -node /provider/websocket/server.js -``` - -### Bundle Prosemirror Example - -This example requires external modules and needs to be bundled before shipping it to the browser. - -```sh -cd /examples/prosemirror/ -# bundle prosemirror example -npx rollup -wc -# serve example -npx serve . -``` - diff --git a/examples/prosemirror/index.html b/examples/prosemirror/index.html deleted file mode 100644 index 2ae69ddc..00000000 --- a/examples/prosemirror/index.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - ​
- -
Insert image:
- - diff --git a/examples/prosemirror/index.js b/examples/prosemirror/index.js deleted file mode 100644 index b0b53976..00000000 --- a/examples/prosemirror/index.js +++ /dev/null @@ -1,45 +0,0 @@ -/* eslint-env browser */ -import * as Y from '../../src/index.js' -import { prosemirrorPlugin, cursorPlugin } from '../../bindings/ProsemirrorBinding/ProsemirrorBinding.js' -import WebsocketProvider from '../../provider/websocket/WebSocketProvider.js' - -import {EditorState} from 'prosemirror-state' -import {EditorView} from 'prosemirror-view' -import {Schema, DOMParser, Mark, Fragment, Node, Slice} from 'prosemirror-model' -import {schema} from 'prosemirror-schema-basic' -import {exampleSetup} from 'prosemirror-example-setup' -import { PlaceholderPlugin, startImageUpload } from './PlaceholderPlugin.js' - -const provider = new WebsocketProvider('ws://localhost:1234/') -const ydocument = provider.get('prosemirror') - -/** - * @type {any} - */ -const type = ydocument.define('prosemirror', Y.XmlFragment) - -const view = new EditorView(document.querySelector('#editor'), { - state: EditorState.create({ - doc: DOMParser.fromSchema(schema).parse(document.querySelector('#content')), - plugins: exampleSetup({schema}).concat([PlaceholderPlugin, prosemirrorPlugin(type), cursorPlugin]) - }) -}) - -window.provider = provider -window.ydocument = ydocument -window.type = type -window.view = view -window.EditorState = EditorState -window.EditorView = EditorView -window.Mark = Mark -window.Fragment = Fragment -window.Node = Node -window.Schema = Schema -window.Slice = Slice - -document.querySelector('#image-upload').addEventListener('change', e => { - if (view.state.selection.$from.parent.inlineContent && e.target.files.length) { - startImageUpload(view, e.target.files[0]) - } - view.focus() -}) diff --git a/examples/prosemirror/rollup.config.js b/examples/prosemirror/rollup.config.js deleted file mode 100644 index 3d115b17..00000000 --- a/examples/prosemirror/rollup.config.js +++ /dev/null @@ -1,20 +0,0 @@ -import nodeResolve from 'rollup-plugin-node-resolve' -import commonjs from 'rollup-plugin-commonjs' - -export default { - input: './index.js', - output: { - name: 'index', - file: 'index.dist.js', - format: 'umd', - sourcemap: true - }, - plugins: [ - nodeResolve({ - main: true, - module: true, - browser: true - }), - commonjs() - ] -} diff --git a/examples/quill.html b/examples/quill.html new file mode 100644 index 00000000..e9740b83 --- /dev/null +++ b/examples/quill.html @@ -0,0 +1,54 @@ + + + + Yjs Prosemirror Example + + + + + +
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/examples/quill.js b/examples/quill.js new file mode 100644 index 00000000..3aabff33 --- /dev/null +++ b/examples/quill.js @@ -0,0 +1,27 @@ +import * as Y from '../index.js' +import { QuillBinding } from '../bindings/quill.js' +import { WebsocketProvider } from '../provider/websocket.js' +import Quill from 'quill' + +const provider = new WebsocketProvider('ws://localhost:1234/') +const ydocument = provider.get('quill') +const ytext = ydocument.define('quill', Y.Text) + +const quill = new Quill('#quill-container', { + modules: { + toolbar: [ + [{ header: [1, 2, false] }], + ['bold', 'italic', 'underline'], + ['image', 'code-block'], + [{ color: [] }, { background: [] }], // Snow theme fills in values + [{ script: 'sub' }, { script: 'super' }], + ['link', 'image'], + ['link', 'code-block'], + [{ list: 'ordered' }, { list: 'bullet' }] + ] + }, + placeholder: 'Compose an epic...', + theme: 'snow' // or 'bubble' +}) + +window.quillBinding = new QuillBinding(ytext, quill) diff --git a/examples/style.css b/examples/style.css new file mode 100644 index 00000000..cf68ca83 --- /dev/null +++ b/examples/style.css @@ -0,0 +1,29 @@ +footer img { + display: none; +} + +nav .title h1 a { + display: none; +} + +footer { + background-color: #b93c1d; +} + +#resizer { + background-color: #b93c1d; +} + +.main section article.readme h1:first-child img { + display: none; +} + +.main section article.readme h1:first-child { + margin-bottom: 16px; + margin-top: 30px; +} + +.main section article.readme h1:first-child::before { + content: "The Shared Editing Library"; + font-size: 2em; +} \ No newline at end of file diff --git a/examples/textarea.html b/examples/textarea.html new file mode 100644 index 00000000..f7c7989b --- /dev/null +++ b/examples/textarea.html @@ -0,0 +1,64 @@ + + + + Yjs Prosemirror Example + + + + +
+ + +
+ + + \ No newline at end of file diff --git a/examples/textarea/index.js b/examples/textarea.js similarity index 58% rename from examples/textarea/index.js rename to examples/textarea.js index 99a949cc..dad2bcb2 100644 --- a/examples/textarea/index.js +++ b/examples/textarea.js @@ -1,12 +1,12 @@ -/* eslint-env browser */ -import * as Y from '../../src/index.js' -import WebsocketProvider from '../../provider/websocket/WebSocketProvider.js' +import * as Y from '../index.js' +import { TextareaBinding } from '../bindings/textarea.js' +import { WebsocketProvider } from '../provider/websocket.js' const provider = new WebsocketProvider('ws://localhost:1234/') const ydocument = provider.get('textarea') const type = ydocument.define('textarea', Y.Text) const textarea = document.querySelector('textarea') -const binding = new Y.TextareaBinding(type, textarea) +const binding = new TextareaBinding(type, textarea) window.textareaExample = { provider, ydocument, type, textarea, binding diff --git a/examples/textarea/index.html b/examples/textarea/index.html deleted file mode 100644 index 6a811663..00000000 --- a/examples/textarea/index.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/examples/ace/index.html b/examples_all/ace/index.html similarity index 100% rename from examples/ace/index.html rename to examples_all/ace/index.html diff --git a/examples/ace/index.js b/examples_all/ace/index.js similarity index 100% rename from examples/ace/index.js rename to examples_all/ace/index.js diff --git a/examples/chat/index.html b/examples_all/chat/index.html similarity index 100% rename from examples/chat/index.html rename to examples_all/chat/index.html diff --git a/examples/chat/index.js b/examples_all/chat/index.js similarity index 94% rename from examples/chat/index.js rename to examples_all/chat/index.js index 6e040fda..eb0f1d03 100644 --- a/examples/chat/index.js +++ b/examples_all/chat/index.js @@ -14,7 +14,7 @@ let chatprotocol = y.define('chatprotocol', Y.Array) let chatcontainer = document.querySelector('#chat') // This functions inserts a message at the specified position in the DOM -function appendMessage (message, position) { +const appendMessage = (message, position) => { var p = document.createElement('p') var uname = document.createElement('span') uname.appendChild(document.createTextNode(message.username + ': ')) @@ -25,7 +25,7 @@ function appendMessage (message, position) { // This function makes sure that only 7 messages exist in the chat history. // The rest is deleted -function cleanupChat () { +const cleanupChat = () => { if (chatprotocol.length > 7) { chatprotocol.delete(0, chatprotocol.length - 7) } @@ -36,7 +36,7 @@ cleanupChat() chatprotocol.toArray().forEach(appendMessage) // whenever content changes, make sure to reflect the changes in the DOM -chatprotocol.observe(function (event) { +chatprotocol.observe(event => { // concurrent insertions may result in a history > 7, so cleanup here cleanupChat() chatcontainer.innerHTML = '' diff --git a/examples/codemirror/index.html b/examples_all/codemirror/index.html similarity index 100% rename from examples/codemirror/index.html rename to examples_all/codemirror/index.html diff --git a/examples/codemirror/index.js b/examples_all/codemirror/index.js similarity index 100% rename from examples/codemirror/index.js rename to examples_all/codemirror/index.js diff --git a/examples/drawing/index.html b/examples_all/drawing/index.html similarity index 100% rename from examples/drawing/index.html rename to examples_all/drawing/index.html diff --git a/examples/drawing/index.js b/examples_all/drawing/index.js similarity index 100% rename from examples/drawing/index.js rename to examples_all/drawing/index.js diff --git a/examples/html-editor-drawing-hook/index.html b/examples_all/html-editor-drawing-hook/index.html similarity index 100% rename from examples/html-editor-drawing-hook/index.html rename to examples_all/html-editor-drawing-hook/index.html diff --git a/examples/html-editor-drawing-hook/index.js b/examples_all/html-editor-drawing-hook/index.js similarity index 100% rename from examples/html-editor-drawing-hook/index.js rename to examples_all/html-editor-drawing-hook/index.js diff --git a/examples/html-editor/index.html b/examples_all/html-editor/index.html similarity index 100% rename from examples/html-editor/index.html rename to examples_all/html-editor/index.html diff --git a/examples/html-editor/index.js b/examples_all/html-editor/index.js similarity index 100% rename from examples/html-editor/index.js rename to examples_all/html-editor/index.js diff --git a/examples/indexeddb/index.html b/examples_all/indexeddb/index.html similarity index 100% rename from examples/indexeddb/index.html rename to examples_all/indexeddb/index.html diff --git a/examples/indexeddb/index.js b/examples_all/indexeddb/index.js similarity index 100% rename from examples/indexeddb/index.js rename to examples_all/indexeddb/index.js diff --git a/examples/jigsaw/index.html b/examples_all/jigsaw/index.html similarity index 100% rename from examples/jigsaw/index.html rename to examples_all/jigsaw/index.html diff --git a/examples/jigsaw/index.js b/examples_all/jigsaw/index.js similarity index 100% rename from examples/jigsaw/index.js rename to examples_all/jigsaw/index.js diff --git a/examples/monaco/index.html b/examples_all/monaco/index.html similarity index 100% rename from examples/monaco/index.html rename to examples_all/monaco/index.html diff --git a/examples/monaco/index.js b/examples_all/monaco/index.js similarity index 100% rename from examples/monaco/index.js rename to examples_all/monaco/index.js diff --git a/examples/notes/index.html b/examples_all/notes/index.html similarity index 100% rename from examples/notes/index.html rename to examples_all/notes/index.html diff --git a/examples/notes/index.js b/examples_all/notes/index.js similarity index 100% rename from examples/notes/index.js rename to examples_all/notes/index.js diff --git a/examples/notes/style.css b/examples_all/notes/style.css similarity index 100% rename from examples/notes/style.css rename to examples_all/notes/style.css diff --git a/examples/quill-cursors/index.html b/examples_all/quill-cursors/index.html similarity index 100% rename from examples/quill-cursors/index.html rename to examples_all/quill-cursors/index.html diff --git a/examples/quill-cursors/index.js b/examples_all/quill-cursors/index.js similarity index 90% rename from examples/quill-cursors/index.js rename to examples_all/quill-cursors/index.js index 76b270f0..2a767bf1 100644 --- a/examples/quill-cursors/index.js +++ b/examples_all/quill-cursors/index.js @@ -36,13 +36,13 @@ let quill = new Quill('#quill-container', { let cursors = quill.getModule('cursors') -function drawCursors () { +const drawCursors = () => { cursors.clearCursors() users.map((user, userId) => { if (user !== myUserInfo) { let relativeRange = user.get('range') - let lastUpdated = new Date(user.get('last updated')) - if (lastUpdated != null && new Date() - lastUpdated < 20000 && relativeRange != null) { + let lastUpdated = new Date(user.get('last updated')).getTime() + if (lastUpdated != null && new Date().getTime() - lastUpdated < 20000 && relativeRange != null) { let start = Y.utils.fromRelativePosition(y, relativeRange.start).offset let end = Y.utils.fromRelativePosition(y, relativeRange.end).offset let range = { index: start, length: end - start } diff --git a/examples/quill/index.html b/examples_all/quill/index.html similarity index 100% rename from examples/quill/index.html rename to examples_all/quill/index.html diff --git a/examples/quill/index.js b/examples_all/quill/index.js similarity index 82% rename from examples/quill/index.js rename to examples_all/quill/index.js index c84b9e6d..2634c0d8 100644 --- a/examples/quill/index.js +++ b/examples_all/quill/index.js @@ -26,8 +26,4 @@ let quill = new Quill('#quill-container', { let yText = y.define('quill', Y.Text) -let quillBinding = new Y.QuillBinding(yText, quill) -window.quillBinding = quillBinding -window.yText = yText -window.y = y -window.quill = quill +window.quillBinding = new Y.QuillBinding(yText, quill) diff --git a/examples/serviceworker/index.html b/examples_all/serviceworker/index.html similarity index 100% rename from examples/serviceworker/index.html rename to examples_all/serviceworker/index.html diff --git a/examples/serviceworker/index.js b/examples_all/serviceworker/index.js similarity index 100% rename from examples/serviceworker/index.js rename to examples_all/serviceworker/index.js diff --git a/examples/serviceworker/yjs-sw-template.js b/examples_all/serviceworker/yjs-sw-template.js similarity index 100% rename from examples/serviceworker/yjs-sw-template.js rename to examples_all/serviceworker/yjs-sw-template.js diff --git a/examples/xml/index.html b/examples_all/xml/index.html similarity index 100% rename from examples/xml/index.html rename to examples_all/xml/index.html diff --git a/examples/xml/index.js b/examples_all/xml/index.js similarity index 100% rename from examples/xml/index.js rename to examples_all/xml/index.js diff --git a/index.js b/index.js new file mode 100644 index 00000000..7efbaa72 --- /dev/null +++ b/index.js @@ -0,0 +1,52 @@ + +import { Delete } from './structs/Delete.js' +import { ItemJSON } from './structs/ItemJSON.js' +import { ItemString } from './structs/ItemString.js' +import { ItemFormat } from './structs/ItemFormat.js' +import { ItemEmbed } from './structs/ItemEmbed.js' +import { GC } from './structs/GC.js' + +import { YArray } from './types/YArray.js' +import { YMap } from './types/YMap.js' +import { YText } from './types/YText.js' +import { YXmlText } from './types/YXmlText.js' +import { YXmlHook } from './types/YXmlHook.js' +import { YXmlElement, YXmlFragment } from './types/YXmlElement.js' + +import { registerStruct } from './utils/structReferences.js' + +export { Y } from './utils/Y.js' +export { UndoManager } from './utils/UndoManager.js' +export { Transaction } from './utils/Transaction.js' + +export { YArray as Array } from './types/YArray.js' +export { YMap as Map } from './types/YMap.js' +export { YText as Text } from './types/YText.js' +export { YXmlText as XmlText } from './types/YXmlText.js' +export { YXmlHook as XmlHook } from './types/YXmlHook.js' +export { YXmlElement as XmlElement, YXmlFragment as XmlFragment } from './types/YXmlElement.js' + +export { getRelativePosition, fromRelativePosition } from './utils/relativePosition.js' +export { registerStruct } from './utils/structReferences.js' +export * from './protocols/syncProtocol.js' +export * from './protocols/awarenessProtocol.js' +export * from './lib/encoding.js' +export * from './lib/decoding.js' +export * from './lib/mutex.js' + +export { WebsocketProvider } from './provider/websocket/WebSocketProvider.js' + +registerStruct(0, GC) +registerStruct(1, ItemJSON) +registerStruct(2, ItemString) +registerStruct(3, ItemFormat) +registerStruct(4, Delete) + +registerStruct(5, YArray) +registerStruct(6, YMap) +registerStruct(7, YText) +registerStruct(8, YXmlFragment) +registerStruct(9, YXmlElement) +registerStruct(10, YXmlText) +registerStruct(11, YXmlHook) +registerStruct(12, ItemEmbed) diff --git a/lib/NamedEventHandler.js b/lib/NamedEventHandler.js index 8eee778c..e1d10bbd 100644 --- a/lib/NamedEventHandler.js +++ b/lib/NamedEventHandler.js @@ -2,7 +2,7 @@ /** * Handles named events. */ -export default class NamedEventHandler { +export class NamedEventHandler { constructor () { this._eventListener = new Map() this._stateListener = new Map() @@ -57,7 +57,7 @@ export default class NamedEventHandler { let state = this._stateListener.get(name) if (state === undefined) { state = {} - state.promise = new Promise(function (resolve) { + state.promise = new Promise(resolve => { state.resolve = resolve }) this._stateListener.set(name, state) diff --git a/lib/Tree.js b/lib/Tree.js index 4f15e618..f189fe74 100644 --- a/lib/Tree.js +++ b/lib/Tree.js @@ -1,5 +1,8 @@ +/** + * @module tree + */ -function rotate (tree, parent, newParent, n) { +const rotate = (tree, parent, newParent, n) => { if (parent === null) { tree.root = newParent newParent._parent = null @@ -111,10 +114,16 @@ class N { } } +const isBlack = node => + node !== null ? node.isBlack() : true + +const isRed = (node) => + node !== null ? node.isRed() : false + /* * This is a Red Black Tree implementation */ -export default class Tree { +export class Tree { constructor () { this.root = null this.length = 0 @@ -310,12 +319,6 @@ export default class Tree { } } _fixDelete (n) { - function isBlack (node) { - return node !== null ? node.isBlack() : true - } - function isRed (node) { - return node !== null ? node.isRed() : false - } if (n.parent === null) { // this can only be called after the first iteration of fixDelete. return diff --git a/lib/binary.js b/lib/binary.js index 722eac4c..d554ba65 100644 --- a/lib/binary.js +++ b/lib/binary.js @@ -1,3 +1,6 @@ +/** + * @module binary + */ export const BITS32 = 0xFFFFFFFF export const BITS21 = (1 << 21) - 1 diff --git a/lib/decoding.js b/lib/decoding.js index fcbe3e7f..384577cb 100644 --- a/lib/decoding.js +++ b/lib/decoding.js @@ -1,3 +1,6 @@ +/** + * @module decoding + */ /* global Buffer */ @@ -17,17 +20,26 @@ export class Decoder { } /** + * @function * @param {ArrayBuffer} buffer * @return {Decoder} */ export const createDecoder = buffer => new Decoder(buffer) +/** + * @function + * @param {Decoder} decoder + * @return {boolean} + */ export const hasContent = decoder => decoder.pos !== decoder.arr.length /** * Clone a decoder instance. * Optionally set a new position parameter. + * + * @function * @param {Decoder} decoder The decoder instance + * @param {number} [newPos] Defaults to current position * @return {Decoder} A clone of `decoder` */ export const clone = (decoder, newPos = decoder.pos) => { @@ -38,6 +50,7 @@ export const clone = (decoder, newPos = decoder.pos) => { /** * Read `len` bytes as an ArrayBuffer. + * @function * @param {Decoder} decoder The decoder instance * @param {number} len The length of bytes to read * @return {ArrayBuffer} @@ -52,6 +65,7 @@ export const readArrayBuffer = (decoder, len) => { /** * Read variable length payload as ArrayBuffer + * @function * @param {Decoder} decoder * @return {ArrayBuffer} */ @@ -59,6 +73,7 @@ export const readPayload = decoder => readArrayBuffer(decoder, readVarUint(decod /** * Read the rest of the content as an ArrayBuffer + * @function * @param {Decoder} decoder * @return {ArrayBuffer} */ @@ -66,6 +81,7 @@ export const readTail = decoder => readArrayBuffer(decoder, decoder.arr.length - /** * Skip one byte, jump to the next position. + * @function * @param {Decoder} decoder The decoder instance * @return {number} The next position */ @@ -73,6 +89,7 @@ export const skip8 = decoder => decoder.pos++ /** * Read one byte as unsigned integer. + * @function * @param {Decoder} decoder The decoder instance * @return {number} Unsigned 8-bit integer */ @@ -81,6 +98,7 @@ export const readUint8 = decoder => decoder.arr[decoder.pos++] /** * Read 4 bytes as unsigned integer. * + * @function * @param {Decoder} decoder * @return {number} An unsigned integer. */ @@ -98,6 +116,7 @@ export const readUint32 = decoder => { * Look ahead without incrementing position. * to the next byte and read it as unsigned integer. * + * @function * @param {Decoder} decoder * @return {number} An unsigned integer. */ @@ -109,6 +128,7 @@ export const peekUint8 = decoder => decoder.arr[decoder.pos] * * numbers < 2^7 is stored in one bytlength * * numbers < 2^14 is stored in two bylength * + * @function * @param {Decoder} decoder * @return {number} An unsigned integer.length */ @@ -137,6 +157,7 @@ export const readVarUint = decoder => { * But most environments have a maximum number of arguments per functions. * For effiency reasons we apply a maximum of 10000 characters at once. * + * @function * @param {Decoder} decoder * @return {String} The read String. */ @@ -157,6 +178,8 @@ export const readVarString = decoder => { /** * Look ahead and read varString without incrementing position + * + * @function * @param {Decoder} decoder * @return {string} */ diff --git a/lib/simpleDiff.js b/lib/diff.js similarity index 96% rename from lib/simpleDiff.js rename to lib/diff.js index 1bc8e344..e364715b 100644 --- a/lib/simpleDiff.js +++ b/lib/diff.js @@ -1,3 +1,6 @@ +/** + * @module diff + */ /** * A SimpleDiff describes a change on a String. @@ -27,7 +30,7 @@ * @param {String} b The updated version of the string * @return {SimpleDiff} The diff description. */ -export default function simpleDiff (a, b) { +export const simpleDiff = (a, b) => { let left = 0 // number of same characters counting from left let right = 0 // number of same characters counting from right while (left < a.length && left < b.length && a[left] === b[left]) { diff --git a/lib/encoding.js b/lib/encoding.js index 5b94c952..5de8d260 100644 --- a/lib/encoding.js +++ b/lib/encoding.js @@ -1,4 +1,6 @@ - +/** + * @module encoding + */ import * as globals from './globals.js' const bits7 = 0b1111111 @@ -15,10 +17,18 @@ export class Encoder { } } +/** + * @function + * @return {Encoder} + */ export const createEncoder = () => new Encoder() /** * The current length of the encoded data. + * + * @function + * @param {Encoder} encoder + * @return {number} */ export const length = encoder => { let len = encoder.cpos @@ -30,6 +40,8 @@ export const length = encoder => { /** * Transform to ArrayBuffer. TODO: rename to .toArrayBuffer + * + * @function * @param {Encoder} encoder * @return {ArrayBuffer} The created ArrayBuffer. */ @@ -48,6 +60,7 @@ export const toBuffer = encoder => { /** * Write one byte to the encoder. * + * @function * @param {Encoder} encoder * @param {number} num The byte that is to be encoded. */ @@ -64,6 +77,7 @@ export const write = (encoder, num) => { * Write one byte at a specific position. * Position must already be written (i.e. encoder.length > pos) * + * @function * @param {Encoder} encoder * @param {number} pos Position to which to write data * @param {number} num Unsigned 8-bit integer @@ -89,6 +103,7 @@ export const set = (encoder, pos, num) => { /** * Write one byte as an unsigned integer. * + * @function * @param {Encoder} encoder * @param {number} num The number that is to be encoded. */ @@ -97,6 +112,7 @@ export const writeUint8 = (encoder, num) => write(encoder, num & bits8) /** * Write one byte as an unsigned Integer at a specific location. * + * @function * @param {Encoder} encoder * @param {number} pos The location where the data will be written. * @param {number} num The number that is to be encoded. @@ -106,6 +122,7 @@ export const setUint8 = (encoder, pos, num) => set(encoder, pos, num & bits8) /** * Write two bytes as an unsigned integer. * + * @function * @param {Encoder} encoder * @param {number} num The number that is to be encoded. */ @@ -116,6 +133,7 @@ export const writeUint16 = (encoder, num) => { /** * Write two bytes as an unsigned integer at a specific location. * + * @function * @param {Encoder} encoder * @param {number} pos The location where the data will be written. * @param {number} num The number that is to be encoded. @@ -128,6 +146,7 @@ export const setUint16 = (encoder, pos, num) => { /** * Write two bytes as an unsigned integer * + * @function * @param {Encoder} encoder * @param {number} num The number that is to be encoded. */ @@ -141,6 +160,7 @@ export const writeUint32 = (encoder, num) => { /** * Write two bytes as an unsigned integer at a specific location. * + * @function * @param {Encoder} encoder * @param {number} pos The location where the data will be written. * @param {number} num The number that is to be encoded. @@ -157,6 +177,7 @@ export const setUint32 = (encoder, pos, num) => { * * Encodes integers in the range from [0, 4294967295] / [0, 0xffffffff]. (max 32 bit unsigned integer) * + * @function * @param {Encoder} encoder * @param {number} num The number that is to be encoded. */ @@ -171,6 +192,7 @@ export const writeVarUint = (encoder, num) => { /** * Write a variable length string. * + * @function * @param {Encoder} encoder * @param {String} str The string that is to be encoded. */ @@ -188,6 +210,7 @@ export const writeVarString = (encoder, str) => { * * TODO: can be improved! * + * @function * @param {Encoder} encoder The enUint8Arr * @param {Encoder} append The BinaryEncoder to be written. */ @@ -196,6 +219,7 @@ export const writeBinaryEncoder = (encoder, append) => writeArrayBuffer(encoder, /** * Append an arrayBuffer to the encoder. * + * @function * @param {Encoder} encoder * @param {ArrayBuffer} arrayBuffer */ @@ -209,6 +233,7 @@ export const writeArrayBuffer = (encoder, arrayBuffer) => { } /** + * @function * @param {Encoder} encoder * @param {ArrayBuffer} arrayBuffer */ diff --git a/lib/globals.js b/lib/globals.js index ee57b827..43bdfe3e 100644 --- a/lib/globals.js +++ b/lib/globals.js @@ -1,3 +1,7 @@ +/** + * @module globals + */ + /* eslint-env browser */ export const Uint8Array_ = Uint8Array diff --git a/lib/idb.js b/lib/idb.js index 8136fd2c..72a00fef 100644 --- a/lib/idb.js +++ b/lib/idb.js @@ -1,3 +1,7 @@ +/** + * @module idb + */ + /* eslint-env browser */ import * as globals from './globals.js' @@ -115,7 +119,7 @@ export const getAllKeysValues = (store, range) => * Iterate on keys and values * @param {IDBObjectStore} store * @param {IDBKeyRange?} keyrange - * @param {function(any, any)} f Return true in order to continue the cursor + * @param {Function} f Return true in order to continue the cursor */ export const iterate = (store, keyrange, f) => globals.createPromise((resolve, reject) => { const request = store.openCursor(keyrange) @@ -135,9 +139,10 @@ export const iterate = (store, keyrange, f) => globals.createPromise((resolve, r /** * Iterate on the keys (no values) + * * @param {IDBObjectStore} store * @param {IDBKeyRange} keyrange - * @param {function(IDBCursor)} f Call `idbcursor.continue()` to iterate further + * @param {function} f Call `idbcursor.continue()` to iterate further */ export const iterateKeys = (store, keyrange, f) => { /** diff --git a/lib/idb.test.js b/lib/idb.test.js index ac9e82a1..e96a1352 100644 --- a/lib/idb.test.js +++ b/lib/idb.test.js @@ -1,4 +1,4 @@ -import * as test from './test.js' +import * as test from './testing.js' import * as idb from './idb.js' import * as logging from './logging.js' diff --git a/lib/logging.js b/lib/logging.js index e06d4dad..6af7e49b 100644 --- a/lib/logging.js +++ b/lib/logging.js @@ -1,3 +1,6 @@ +/** + * @module logging + */ import * as globals from './globals.js' diff --git a/lib/math.js b/lib/math.js index 818d05be..93af24e6 100644 --- a/lib/math.js +++ b/lib/math.js @@ -1,2 +1,4 @@ - +/** + * @module math + */ export const floor = Math.floor diff --git a/lib/mutex.js b/lib/mutex.js index d35b1061..307c4cd0 100644 --- a/lib/mutex.js +++ b/lib/mutex.js @@ -4,11 +4,10 @@ * * @example * const mutex = createMutex() - * mutex(function () { + * mutex(() => { * // This function is immediately executed - * mutex(function () { - * // This function is never executed, as it is called with the same - * // mutex function + * mutex(() => { + * // This function is not executed, as the mutex is already active. * }) * }) * diff --git a/lib/number.js b/lib/number.js index 56194de7..541930ee 100644 --- a/lib/number.js +++ b/lib/number.js @@ -1,2 +1,6 @@ +/** + * @module number + */ + export const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER export const MIN_SAFE_INTEGER = Number.MIN_SAFE_INTEGER diff --git a/lib/random/PRNG/Mt19937.js b/lib/prng/PRNG/Mt19937.js similarity index 90% rename from lib/random/PRNG/Mt19937.js rename to lib/prng/PRNG/Mt19937.js index 78d3aca4..c1aa9e77 100644 --- a/lib/random/PRNG/Mt19937.js +++ b/lib/prng/PRNG/Mt19937.js @@ -1,11 +1,12 @@ +/** + * @module prng + */ const N = 624 const M = 397 -function twist (u, v) { - return ((((u & 0x80000000) | (v & 0x7fffffff)) >>> 1) ^ ((v & 1) ? 0x9908b0df : 0)) -} +const twist = (u, v) => ((((u & 0x80000000) | (v & 0x7fffffff)) >>> 1) ^ ((v & 1) ? 0x9908b0df : 0)) -function nextState (state) { +const nextState = (state) => { let p = 0 let j for (j = N - M + 1; --j; p++) { @@ -29,7 +30,7 @@ function nextState (state) { * * @public */ -export default class Mt19937 { +export class Mt19937 { /** * @param {Number} seed The starting point for the random number generation. If you use the same seed, the generator will return the same sequence of random numbers. */ diff --git a/lib/random/PRNG/PRNG.tests.js b/lib/prng/PRNG/PRNG.tests.js similarity index 86% rename from lib/random/PRNG/PRNG.tests.js rename to lib/prng/PRNG/PRNG.tests.js index 1979d66a..f524edd3 100644 --- a/lib/random/PRNG/PRNG.tests.js +++ b/lib/prng/PRNG/PRNG.tests.js @@ -1,13 +1,16 @@ +/** + * @module prng + */ -import Mt19937 from './Mt19937.js' -import Xoroshiro128plus from './Xoroshiro128plus.js' -import Xorshift32 from './Xorshift32.js' +import { Mt19937 } from './Mt19937.js' +import { Xoroshiro128plus } from './Xoroshiro128plus.js' +import { Xorshift32 } from './Xorshift32.js' import * as time from '../../time.js' const DIAMETER = 300 const NUMBERS = 10000 -function runPRNG (name, Gen) { +const runPRNG = (name, Gen) => { console.log('== ' + name + ' ==') const gen = new Gen(1234) let head = 0 diff --git a/lib/random/PRNG/README.md b/lib/prng/PRNG/README.md similarity index 100% rename from lib/random/PRNG/README.md rename to lib/prng/PRNG/README.md diff --git a/lib/random/PRNG/Xoroshiro128plus.js b/lib/prng/PRNG/Xoroshiro128plus.js similarity index 96% rename from lib/random/PRNG/Xoroshiro128plus.js rename to lib/prng/PRNG/Xoroshiro128plus.js index 56ccf50c..13b97156 100644 --- a/lib/random/PRNG/Xoroshiro128plus.js +++ b/lib/prng/PRNG/Xoroshiro128plus.js @@ -1,5 +1,8 @@ +/** + * @module prng + */ -import Xorshift32 from './Xorshift32.js' +import { Xorshift32 } from './Xorshift32.js' /** * This is a variant of xoroshiro128plus - the fastest full-period generator passing BigCrush without systematic failures. @@ -14,7 +17,7 @@ import Xorshift32 from './Xorshift32.js' * * [Reference implementation](http://vigna.di.unimi.it/xorshift/xoroshiro128plus.c) */ -export default class Xoroshiro128plus { +export class Xoroshiro128plus { constructor (seed) { this.seed = seed // This is a variant of Xoroshiro128plus to fill the initial state diff --git a/lib/random/PRNG/Xorshift32.js b/lib/prng/PRNG/Xorshift32.js similarity index 91% rename from lib/random/PRNG/Xorshift32.js rename to lib/prng/PRNG/Xorshift32.js index 6b3ec9a0..ad76a34e 100644 --- a/lib/random/PRNG/Xorshift32.js +++ b/lib/prng/PRNG/Xorshift32.js @@ -1,8 +1,11 @@ +/** + * @module prng + */ /** * Xorshift32 is a very simple but elegang PRNG with a period of `2^32-1`. */ -export default class Xorshift32 { +export class Xorshift32 { /** * @param {number} seed The starting point for the random number generation. If you use the same seed, the generator will return the same sequence of random numbers. */ diff --git a/lib/random/random.js b/lib/prng/prng.js similarity index 97% rename from lib/random/random.js rename to lib/prng/prng.js index 3e1f6062..ae9e898e 100644 --- a/lib/random/random.js +++ b/lib/prng/prng.js @@ -1,10 +1,13 @@ +/** + * @module prng + */ import * as binary from '../binary.js' import { fromCharCode, fromCodePoint } from '../string.js' import { MAX_SAFE_INTEGER, MIN_SAFE_INTEGER } from '../number.js' import * as math from '../math.js' -import DefaultPRNG from './PRNG/Xoroshiro128plus.js' +import { Xoroshiro128plus as DefaultPRNG } from './PRNG/Xoroshiro128plus.js' /** * Description of the function diff --git a/lib/random/random.test.js b/lib/prng/prng.test.js similarity index 99% rename from lib/random/random.test.js rename to lib/prng/prng.test.js index 69782150..00b5ee66 100644 --- a/lib/random/random.test.js +++ b/lib/prng/prng.test.js @@ -1,3 +1,7 @@ +/** + * @module prng + */ + /** *TODO: enable tests import * as rt from '../rich-text/formatters.mjs' diff --git a/lib/random.js b/lib/random.js new file mode 100644 index 00000000..79447ad6 --- /dev/null +++ b/lib/random.js @@ -0,0 +1,3 @@ +/** + * @module random + */ diff --git a/lib/string.js b/lib/string.js index 756829bb..68016c07 100644 --- a/lib/string.js +++ b/lib/string.js @@ -1,2 +1,6 @@ +/** + * @module string + */ + export const fromCharCode = String.fromCharCode export const fromCodePoint = String.fromCodePoint diff --git a/lib/test.js b/lib/testing.js similarity index 93% rename from lib/test.js rename to lib/testing.js index f19a947b..43c85a2a 100644 --- a/lib/test.js +++ b/lib/testing.js @@ -1,5 +1,9 @@ +/** + * @module testing + */ + import * as logging from './logging.js' -import simpleDiff from './simpleDiff.js' +import { simpleDiff } from './diff.js' export const run = async (name, f) => { console.log(`%cStart:%c ${name}`, 'color:blue;', '') diff --git a/package-lock.json b/package-lock.json index d5c2ab7d..63c71757 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,63 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "@types/estree": { "version": "0.0.38", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.38.tgz", @@ -110,17 +167,6 @@ "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", "dev": true }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true, - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - } - }, "ansi-escapes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", @@ -1577,6 +1623,12 @@ "integrity": "sha1-SOyNFt9Dd+rl+liEaCSAr02Vx3Q=", "dev": true }, + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", + "dev": true + }, "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -1631,12 +1683,6 @@ "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", "dev": true }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true - }, "camelcase-keys": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", @@ -1662,22 +1708,13 @@ "dev": true, "optional": true }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "catharsis": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz", + "integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=", "dev": true, "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" - }, - "dependencies": { - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true - } + "underscore-contrib": "~0.3.0" } }, "chalk": { @@ -1761,17 +1798,6 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - } - }, "clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", @@ -3079,9 +3105,9 @@ } }, "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, "extend-shallow": { @@ -3984,6 +4010,12 @@ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -4585,6 +4617,15 @@ "dev": true, "optional": true }, + "jest-worker": { + "version": "23.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-23.2.0.tgz", + "integrity": "sha1-+vcGqNo2+uYOsmlXJX+ntdjqArk=", + "dev": true, + "requires": { + "merge-stream": "^1.0.1" + } + }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -4609,12 +4650,69 @@ } } }, + "js2xmlparser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-3.0.0.tgz", + "integrity": "sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=", + "dev": true, + "requires": { + "xmlcreate": "^1.0.1" + } + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true }, + "jsdoc": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.5.tgz", + "integrity": "sha512-6PxB65TAU4WO0Wzyr/4/YhlGovXl0EVYfpKbpSroSj0qBxT4/xod/l40Opkm38dRHRdQgdeY836M0uVnJQG7kg==", + "dev": true, + "requires": { + "babylon": "7.0.0-beta.19", + "bluebird": "~3.5.0", + "catharsis": "~0.8.9", + "escape-string-regexp": "~1.0.5", + "js2xmlparser": "~3.0.0", + "klaw": "~2.0.0", + "marked": "~0.3.6", + "mkdirp": "~0.5.1", + "requizzle": "~0.2.1", + "strip-json-comments": "~2.0.1", + "taffydb": "2.6.2", + "underscore": "~1.8.3" + }, + "dependencies": { + "babylon": { + "version": "7.0.0-beta.19", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.19.tgz", + "integrity": "sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A==", + "dev": true + }, + "klaw": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-2.0.0.tgz", + "integrity": "sha1-WcEo4Nxc5BAgEVEZTuucv4WGUPY=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "taffydb": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", + "dev": true + } + } + }, + "jsdoc-template": { + "version": "git+ssh://git@github.com/braintree/jsdoc-template.git#ec0e3c0ec95de6b4e928af3285bbf69aac2f25b6", + "from": "git+ssh://git@github.com/braintree/jsdoc-template.git#3.3.0", + "dev": true + }, "jsdom": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-7.2.2.tgz", @@ -5009,12 +5107,6 @@ "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=", "dev": true }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true - }, "loose-envify": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", @@ -5120,6 +5212,15 @@ } } }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, "micromatch": { "version": "2.3.11", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", @@ -5424,7 +5525,7 @@ }, "parchment": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", + "resolved": "http://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==", "dev": true }, @@ -5848,7 +5949,7 @@ }, "quill": { "version": "1.3.6", - "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.6.tgz", + "resolved": "http://registry.npmjs.org/quill/-/quill-1.3.6.tgz", "integrity": "sha512-K0mvhimWZN6s+9OQ249CH2IEPZ9JmkFuCQeHAOQax3EZ2nDJ3wfGh59mnlQaZV2i7u8eFarx6wAtvQKgShojug==", "dev": true, "requires": { @@ -5879,14 +5980,6 @@ "deep-equal": "^1.0.1", "extend": "^3.0.2", "fast-diff": "1.1.2" - }, - "dependencies": { - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - } } }, "randomatic": { @@ -6198,6 +6291,23 @@ "resolve-from": "^1.0.0" } }, + "requizzle": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz", + "integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=", + "dev": true, + "requires": { + "underscore": "~1.6.0" + }, + "dependencies": { + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", + "dev": true + } + } + }, "resolve": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.3.3.tgz", @@ -6233,15 +6343,6 @@ "signal-exit": "^3.0.2" } }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, - "requires": { - "align-text": "^0.1.1" - } - }, "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", @@ -6380,12 +6481,46 @@ } }, "rollup-plugin-uglify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-uglify/-/rollup-plugin-uglify-1.0.2.tgz", - "integrity": "sha1-1KpvXfE1Iurhuhd4DHxMcJYDg1k=", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-uglify/-/rollup-plugin-uglify-6.0.0.tgz", + "integrity": "sha512-XtzZd159QuOaXNvcxyBcbUCSoBsv5YYWK+7ZwUyujSmISst8avRfjWlp7cGu8T2O52OJnpEBvl+D4WLV1k1iQQ==", "dev": true, "requires": { - "uglify-js": "^2.6.1" + "@babel/code-frame": "^7.0.0", + "jest-worker": "^23.2.0", + "serialize-javascript": "^1.5.0", + "uglify-js": "^3.4.9" + } + }, + "rollup-plugin-uglify-es": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-uglify-es/-/rollup-plugin-uglify-es-0.0.1.tgz", + "integrity": "sha1-5FZE8raFpZq9uTY0ByB6A6e1qbc=", + "dev": true, + "requires": { + "uglify-es": "3.0.3" + }, + "dependencies": { + "commander": { + "version": "2.9.0", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, + "requires": { + "graceful-readlink": ">= 1.0.0" + } + }, + "uglify-es": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.0.3.tgz", + "integrity": "sha1-Y8yEqpRos0lzpIh3h8ZMAaiodXY=", + "dev": true, + "requires": { + "commander": "~2.9.0", + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0" + } + } } }, "rollup-pluginutils": { @@ -6483,6 +6618,12 @@ "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", "dev": true }, + "serialize-javascript": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", + "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", + "dev": true + }, "serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", @@ -6974,6 +7115,15 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "tui-jsdoc-template": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tui-jsdoc-template/-/tui-jsdoc-template-1.2.2.tgz", + "integrity": "sha512-oqw0IYaot86VJ2owKBozJnilgta0Z55x8r9PeHj7vb+jDoSvJGRUQUcgs56SZh9HE20fx54Pe75p84X85/ygLA==", + "dev": true, + "requires": { + "cheerio": "^0.22.0" + } + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -7012,14 +7162,27 @@ "dev": true }, "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "version": "3.4.9", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", + "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", "dev": true, "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "commander": "~2.17.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "uglify-to-browserify": { @@ -7029,6 +7192,29 @@ "dev": true, "optional": true }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", + "dev": true + }, + "underscore-contrib": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz", + "integrity": "sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=", + "dev": true, + "requires": { + "underscore": "1.6.0" + }, + "dependencies": { + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", + "dev": true + } + } + }, "uniq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", @@ -7173,18 +7359,6 @@ "isexe": "^2.0.0" } }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true - }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -7215,6 +7389,12 @@ "dev": true, "optional": true }, + "xmlcreate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz", + "integrity": "sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8=", + "dev": true + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", @@ -7226,18 +7406,6 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } } } } diff --git a/package.json b/package.json index f0d22d64..85ca675b 100644 --- a/package.json +++ b/package.json @@ -2,17 +2,17 @@ "name": "yjs", "version": "13.0.0-72", "description": "A framework for real-time p2p shared editing on any data", - "main": "./build/node/index.js", - "module": "./src/index.js", + "main": "./build/yjs.js", + "module": "./index.js", "scripts": { "test": "npm run lint", - "debug": "concurrently 'rollup -wc rollup.test.js' 'cutest-serve y.test.js -o'", - "lint": "standard src/**/*.js test/**/*.js tests-lib/**/*.js", - "docs": "esdoc", + "build": "rollup -c", + "watch": "rollup -wc", + "debug": "concurrently 'rollup -wc' 'cutest-serve build/y.test.js -o'", + "lint": "standard **/*.js", + "docs": "rm -rf docs && jsdoc --configure .jsdoc.json --verbose --readme ./README.md --package ./package.json", "serve-docs": "npm run docs && serve ./docs/", - "dist": "rollup -c rollup.browser.js; rollup -c rollup.node.js", - "watch": "concurrently 'rollup -wc rollup.browser.js' 'rollup -wc rollup.node.js'", - "postversion": "npm run dist" + "postversion": "npm run build" }, "files": [ "src/*", @@ -28,8 +28,10 @@ }, "standard": { "ignore": [ - "/y.js", - "/y.js.map" + "/build", + "/node_modules", + "/rollup.test.js", + "/rollup.test.js" ] }, "repository": { @@ -62,6 +64,8 @@ "cutest": "^0.1.9", "esdoc": "^1.1.0", "esdoc-standard-plugin": "^1.0.0", + "jsdoc": "^3.5.5", + "jsdoc-template": "git@github.com:braintree/jsdoc-template.git#3.3.0", "prosemirror-example-setup": "^1.0.1", "prosemirror-schema-basic": "^1.0.0", "prosemirror-state": "^1.2.2", @@ -74,10 +78,12 @@ "rollup-plugin-inject": "^2.2.0", "rollup-plugin-multi-entry": "^2.0.2", "rollup-plugin-node-resolve": "^3.4.0", - "rollup-plugin-uglify": "^1.0.2", + "rollup-plugin-uglify": "^6.0.0", + "rollup-plugin-uglify-es": "0.0.1", "rollup-regenerator-runtime": "^6.23.1", "rollup-watch": "^3.2.2", - "standard": "^11.0.1" + "standard": "^11.0.1", + "tui-jsdoc-template": "^1.2.2" }, "dependencies": { "ws": "^6.1.0" diff --git a/src/Persistences/FilePersistence.js b/persistences/FilePersistence.js similarity index 88% rename from src/Persistences/FilePersistence.js rename to persistences/FilePersistence.js index 499cc591..0c22ed43 100644 --- a/src/Persistences/FilePersistence.js +++ b/persistences/FilePersistence.js @@ -1,8 +1,9 @@ +/* import fs from 'fs' import path from 'path' -import * as encoding from '../../lib/encoding.js' -import * as decoding from '../../lib/decoding.js' -import { createMutualExclude } from '../../lib/mutualExclude.js' +import * as encoding from '../lib/encoding.js' +import * as decoding from '../lib/decoding.js' +import { createMutex } from '../lib/mutex.js' import { encodeUpdate, encodeStructsDS, decodePersisted } from './decodePersisted.js' function createFilePath (persistence, roomName) { @@ -10,10 +11,10 @@ function createFilePath (persistence, roomName) { return path.join(persistence.dir, roomName) } -export default class FilePersistence { +export class FilePersistence { constructor (dir) { this.dir = dir - this._mutex = createMutualExclude() + this._mutex = createMutex() } setRemoteUpdateCounter (roomName, remoteUpdateCounter) { // TODO: implement @@ -70,3 +71,4 @@ export default class FilePersistence { }) } } +*/ diff --git a/src/Persistences/IndexedDBPersistence.js b/persistences/IndexedDBPersistence.js similarity index 80% rename from src/Persistences/IndexedDBPersistence.js rename to persistences/IndexedDBPersistence.js index 37f37757..78a2a955 100644 --- a/src/Persistences/IndexedDBPersistence.js +++ b/persistences/IndexedDBPersistence.js @@ -1,13 +1,9 @@ -/* global indexedDB, location, BroadcastChannel */ -import Y from '../Y.js' -import { createMutualExclude } from '../../lib/mutualExclude.js' -import { decodePersisted, encodeStructsDS, encodeUpdate, PERSIST_STRUCTS_DS, PERSIST_UPDATE } from './decodePersisted.js' -import * as decoding from '../../lib/decoding.js' -import * as encoding from '../../lib/encoding.js' /* - * Request to Promise transformer - */ +import { Y } from '../utils/Y.js' +import { createMutex } from '../lib/mutex.js' +import { decodePersisted, encodeStructsDS, encodeUpdate, PERSIST_STRUCTS_DS, PERSIST_UPDATE } from './decodePersisted.js' + function rtop (request) { return new Promise(function (resolve, reject) { request.onerror = function (event) { @@ -50,21 +46,21 @@ function persist (room) { let t = room.db.transaction(['updates'], 'readwrite') let updatesStore = t.objectStore('updates') return rtop(updatesStore.getAll()) - .then(updates => { - // apply all previous updates before deleting them - room.mutex(() => { - updates.forEach(update => { - decodePersisted(y, new BinaryDecoder(update)) + .then(updates => { + // apply all previous updates before deleting them + room.mutex(() => { + updates.forEach(update => { + decodePersisted(y, new BinaryDecoder(update)) + }) + }) + const encoder = new BinaryEncoder() + encodeStructsDS(y, encoder) + // delete all pending updates + rtop(updatesStore.clear()).then(() => { + // write current model + updatesStore.put(encoder.createBuffer()) }) }) - const encoder = new BinaryEncoder() - encodeStructsDS(y, encoder) - // delete all pending updates - rtop(updatesStore.clear()).then(() => { - // write current model - updatesStore.put(encoder.createBuffer()) - }) - }) } function saveUpdate (room, updateBuffer) { @@ -99,7 +95,7 @@ function registerRoomInPersistence (documentsDB, roomName) { const PREFERRED_TRIM_SIZE = 400 -export default class IndexedDBPersistence { +export class IndexedDBPersistence { constructor () { this._rooms = new Map() this._documentsDB = new Promise(function (resolve, reject) { @@ -115,7 +111,17 @@ export default class IndexedDBPersistence { reject(new Error(event.target.error)) } request.onblocked = function () { - location.reload() + location.reload()EventListener' is not defined. + /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:160:36: 'BinaryDecoder' is not defined. + /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:167:25: 'BinaryEncoder' is not defined. + /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:178:25: 'BinaryEncoder' is not defined. + /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:203:34: 'BinaryDecoder' is not defined. + /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:213:17: 'encoder' is assigned a value but never used. + /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:213:31: 'BinaryEncoder' is not defined. + /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:214:30: 'BinaryEncoder' is not defined. + /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:230:12: Trailing spaces not allowed. + /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:237:5: Return statement should not contain assignment. + /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:243:29: 'BinaryEncoder' i } request.onsuccess = function (event) { const db = event.target.result @@ -194,7 +200,7 @@ export default class IndexedDBPersistence { db: null, dbPromise: null, channel: null, - mutex: createMutualExclude(), + mutex: createMutex(), y } if (typeof BroadcastChannel !== 'undefined') { @@ -228,7 +234,7 @@ export default class IndexedDBPersistence { }) // register document in documentsDB this._documentsDB.then( - db => + db => rtop(db.transaction(['documents'], 'readonly').objectStore('documents').get(roomName)) .then( doc => doc === undefined && rtop(db.transaction(['documents'], 'readwrite').objectStore('documents').add({ roomName, serverUpdateCounter: -1 })) @@ -273,9 +279,10 @@ export default class IndexedDBPersistence { * Automatically destroys all Yjs all Yjs instances that persist to * the room. If `destroyYjsInstances = false` the persistence functionality * will be removed from the Yjs instances. - */ + * removePersistedData (roomName, destroyYjsInstances = true) { this.disconnectY(roomName) return rtop(indexedDB.deleteDatabase(roomName)) } } +*/ diff --git a/src/Persistences/decodePersisted.js b/persistences/decodePersisted.js similarity index 68% rename from src/Persistences/decodePersisted.js rename to persistences/decodePersisted.js index 009de0bd..20d92320 100644 --- a/src/Persistences/decodePersisted.js +++ b/persistences/decodePersisted.js @@ -1,3 +1,4 @@ +/* import { integrateRemoteStructs } from '../MessageHandler/integrateRemoteStructs.js' import { writeStructs } from '../MessageHandler/syncStep1.js' import { writeDeleteSet, readDeleteSet } from '../MessageHandler/deleteSet.js' @@ -6,10 +7,10 @@ export const PERSIST_UPDATE = 0 /** * Write an update to an encoder. * - * @param {Yjs} y A Yjs instance - * @param {BinaryEncoder} updateEncoder I.e. transaction.encodedStructs - */ -export function encodeUpdate (y, updateEncoder, encoder) { + * @param {Y} y A Yjs instance + * @param {Encoder} updateEncoder I.e. transaction.encodedStructs + * +export const encodeUpdate = (y, updateEncoder, encoder) => { encoder.writeVarUint(PERSIST_UPDATE) encoder.writeBinaryEncoder(updateEncoder) } @@ -19,10 +20,10 @@ export const PERSIST_STRUCTS_DS = 1 /** * Write the current Yjs data model to an encoder. * - * @param {Yjs} y A Yjs instance - * @param {BinaryEncoder} encoder An encoder to write to - */ -export function encodeStructsDS (y, encoder) { + * @param {Y} y A Yjs instance + * @param {Encoder} encoder An encoder to write to + * +export const encodeStructsDS = (y, encoder) => { encoder.writeVarUint(PERSIST_STRUCTS_DS) writeStructs(y, encoder, new Map()) writeDeleteSet(y, encoder) @@ -30,10 +31,10 @@ export function encodeStructsDS (y, encoder) { /** * Feed the Yjs instance with the persisted state - * @param {Yjs} y A Yjs instance. - * @param {BinaryDecoder} decoder A Decoder instance that holds the file content. - */ -export function decodePersisted (y, decoder) { + * @param {Y} y A Yjs instance. + * @param {Decoder} decoder A Decoder instance that holds the file content. + * +export const decodePersisted = (y, decoder) => { y.transact(() => { while (decoder.hasContent()) { const contentType = decoder.readVarUint() @@ -49,3 +50,4 @@ export function decodePersisted (y, decoder) { } }, true) } +*/ diff --git a/src/protocols/awarenessProtocol.js b/protocols/awarenessProtocol.js similarity index 92% rename from src/protocols/awarenessProtocol.js rename to protocols/awarenessProtocol.js index 4e590b87..a2ca0c80 100644 --- a/src/protocols/awarenessProtocol.js +++ b/protocols/awarenessProtocol.js @@ -1,6 +1,10 @@ +/** + * @module awareness-protocol + */ -import * as encoding from '../../lib/encoding.js' -import * as decoding from '../../lib/decoding.js' +import * as encoding from '../lib/encoding.js' +import * as decoding from '../lib/decoding.js' +import { Y } from '../utils/Y.js' // eslint-disable-line const messageUsersStateChanged = 0 @@ -77,6 +81,7 @@ export const forwardUsersStateChange = (decoder, encoder) => { /** * @param {decoding.Decoder} decoder + * @param {Y} y */ export const readAwarenessMessage = (decoder, y) => { switch (decoding.readVarUint(decoder)) { diff --git a/src/protocols/syncProtocol.js b/protocols/syncProtocol.js similarity index 96% rename from src/protocols/syncProtocol.js rename to protocols/syncProtocol.js index 7a8ceae8..d8b63e87 100644 --- a/src/protocols/syncProtocol.js +++ b/protocols/syncProtocol.js @@ -1,17 +1,18 @@ +/** + * @module sync-protocol + */ -import * as encoding from '../../lib/encoding.js' -import * as decoding from '../../lib/decoding.js' -import * as ID from '../Util/ID.js' -import { getStruct } from '../Util/structReferences.js' -import { deleteItemRange } from '../Struct/Delete.js' -import { integrateRemoteStruct } from '../Util/integrateRemoteStructs.js' -import Item from '../Struct/Item.js' +import * as encoding from '../lib/encoding.js' +import * as decoding from '../lib/decoding.js' +import * as ID from '../utils/ID.js' +import { getStruct } from '../utils/structReferences.js' +import { deleteItemRange } from '../structs/Delete.js' +import { integrateRemoteStruct } from '../utils/integrateRemoteStructs.js' +import { Y } from '../utils/Y.js' // eslint-disable-line +import { Item } from '../structs/Item.js' /** - * @typedef {import('../Store/StateStore.js').default} StateStore - * @typedef {import('../Y.js').default} Y - * @typedef {import('../Struct/Item.js').default} Item - * @typedef {import('../Store/StateStore.js').StateSet} StateSet + * @typedef {Map} StateSet */ /** @@ -82,7 +83,8 @@ export const writeDeleteSet = (encoder, y) => { const gc = n.gc if (currentUser !== user) { numberOfUsers++ - // a new user was found + // a new user was foundimport { StateSet } from '../Store/StateStore.js' // eslint-disable-line + if (currentUser !== null) { // happens on first iteration encoding.setUint32(encoder, lastLenPos, currentLength) } @@ -201,8 +203,8 @@ export const stringifyStateSet = decoder => { /** * Write StateSet to Encoder * - * @param {Y} y * @param {encoding.Encoder} encoder + * @param {Y} y */ export const writeStateSet = (encoder, y) => { const state = y.ss.state @@ -426,6 +428,7 @@ export const stringifyUpdate = (decoder, y) => /** * @param {encoding.Encoder} encoder + * @param {number} numOfStructs * @param {encoding.Encoder} updates */ export const writeUpdate = (encoder, numOfStructs, updates) => { diff --git a/provider/websocket.js b/provider/websocket.js new file mode 100644 index 00000000..03a23052 --- /dev/null +++ b/provider/websocket.js @@ -0,0 +1,5 @@ +/** + * @module provider/websocket + */ + +export * from './websocket/WebSocketProvider.js' diff --git a/provider/websocket/WebSocketProvider.js b/provider/websocket/WebSocketProvider.js index ead344c5..68f94e7a 100644 --- a/provider/websocket/WebSocketProvider.js +++ b/provider/websocket/WebSocketProvider.js @@ -1,7 +1,11 @@ +/** + * @module provider/websocket + */ + /* eslint-env browser */ -import * as Y from '../../src/index.js' -export * from '../../src/index.js' +import * as Y from '../../index.js' +export * from '../../index.js' const messageSync = 0 const messageAwareness = 1 @@ -95,7 +99,7 @@ class WebsocketsSharedDocument extends Y.Y { } } -export default class WebsocketProvider { +export class WebsocketProvider { constructor (url) { // ensure that url is always ends with / while (url[url.length - 1] === '/') { diff --git a/provider/websocket/server.js b/provider/websocket/server.js index 5de9ed7f..c14d4b4b 100644 --- a/provider/websocket/server.js +++ b/provider/websocket/server.js @@ -1,4 +1,8 @@ -const Y = require('../../build/node/index.js') +/** + * @module provider/websocket + */ + +const Y = require('../../build/yjs.umd.js') const WebSocket = require('ws') const wss = new WebSocket.Server({ port: 1234 }) const docs = new Map() diff --git a/src/YdbClient/NamedEventHandler.js b/provider/ydb/NamedEventHandler.js similarity index 93% rename from src/YdbClient/NamedEventHandler.js rename to provider/ydb/NamedEventHandler.js index 0fa4d085..7474dfad 100644 --- a/src/YdbClient/NamedEventHandler.js +++ b/provider/ydb/NamedEventHandler.js @@ -1,3 +1,6 @@ +/** + * @module provider/ydb + */ import * as globals from './globals.js' diff --git a/src/YdbClient/README.md b/provider/ydb/README.md similarity index 100% rename from src/YdbClient/README.md rename to provider/ydb/README.md diff --git a/src/YdbClient/TODO.md b/provider/ydb/TODO.md similarity index 100% rename from src/YdbClient/TODO.md rename to provider/ydb/TODO.md diff --git a/src/YdbClient/YdbClient.js b/provider/ydb/YdbClient.js similarity index 99% rename from src/YdbClient/YdbClient.js rename to provider/ydb/YdbClient.js index abf240c7..39da5cf6 100644 --- a/src/YdbClient/YdbClient.js +++ b/provider/ydb/YdbClient.js @@ -1,3 +1,7 @@ +/** + * @module provider/ydb + */ + /* eslint-env browser */ import * as idbactions from './idbactions.js' import * as globals from '../../lib/globals.js' @@ -7,7 +11,7 @@ import * as encoding from '../../lib/encoding.js' import * as logging from '../../lib/logging.js' import * as idb from '../../lib/idb.js' import * as decoding from '../../lib/decoding.js' -import Y from '../Y.js' +import { Y } from '../../utils/Y.js' import { integrateRemoteStruct } from '../MessageHandler/integrateRemoteStructs.js' import { createMutualExclude } from '../../lib/mutualExclude.js' diff --git a/src/YdbClient/YdbClient.test.js b/provider/ydb/YdbClient.test.js similarity index 98% rename from src/YdbClient/YdbClient.test.js rename to provider/ydb/YdbClient.test.js index fd981fff..9468e811 100644 --- a/src/YdbClient/YdbClient.test.js +++ b/provider/ydb/YdbClient.test.js @@ -1,3 +1,7 @@ +/** + * @module provider/ydb + */ + /* eslint-env browser */ import * as test from './test.js' diff --git a/src/YdbClient/broadcastchannel.js b/provider/ydb/broadcastchannel.js similarity index 94% rename from src/YdbClient/broadcastchannel.js rename to provider/ydb/broadcastchannel.js index be5dc0cf..5badfd4e 100644 --- a/src/YdbClient/broadcastchannel.js +++ b/provider/ydb/broadcastchannel.js @@ -1,8 +1,12 @@ +/** + * @module provider/ydb + */ + /* eslint-env browser */ -import * as decoding from './decoding.js' -import * as encoding from './encoding.js' -import * as globals from './globals.js' +import * as decoding from '../../lib/decoding.js' +import * as encoding from '../../lib/encoding.js' +import * as globals from '../../lib/globals.js' import * as NamedEventHandler from './NamedEventHandler.js' const bc = new BroadcastChannel('ydb-client') @@ -52,17 +56,17 @@ const fireRoomStateUpdate = (ydb, room) => { if (roomStatesUpdating.length === 1) { // first time this is called, trigger actual publisher // setTimeout(() => { - const updated = new Map() - const unconfirmedRooms = getUnconfirmedRooms(ydb) - roomStatesUpdating.forEach(room => { - if (!updated.has(room)) { - updated.set(room, computeRoomState(ydb, unconfirmedRooms, room)) - } - }) - NamedEventHandler.fire(ydb, 'syncstate', { - updated - }) - roomStatesUpdating = [] + const updated = new Map() + const unconfirmedRooms = getUnconfirmedRooms(ydb) + roomStatesUpdating.forEach(room => { + if (!updated.has(room)) { + updated.set(room, computeRoomState(ydb, unconfirmedRooms, room)) + } + }) + NamedEventHandler.fire(ydb, 'syncstate', { + updated + }) + roomStatesUpdating = [] // }, 0) } } @@ -274,7 +278,7 @@ export const _broadcastYdbSyncFromServer = subs => { /** * @param {string} room - * @param {function(ArrayBuffer)} f + * @param {Function} f */ export const subscribeRoomData = (room, f) => { let rsubs = datasubs.get(room) diff --git a/src/YdbClient/idbactions.js b/provider/ydb/idbactions.js similarity index 99% rename from src/YdbClient/idbactions.js rename to provider/ydb/idbactions.js index 9bf99a1f..fb65c6f9 100644 --- a/src/YdbClient/idbactions.js +++ b/provider/ydb/idbactions.js @@ -1,3 +1,7 @@ +/** + * @module provider/ydb + */ + /* eslint-env browser */ /** diff --git a/src/YdbClient/idbactions.test.js b/provider/ydb/idbactions.test.js similarity index 91% rename from src/YdbClient/idbactions.test.js rename to provider/ydb/idbactions.test.js index 745b5d9b..41cf61c3 100644 --- a/src/YdbClient/idbactions.test.js +++ b/provider/ydb/idbactions.test.js @@ -1,6 +1,6 @@ -import * as globals from './globals.js' +import * as globals from '../../lib/globals.js' import * as idbactions from './idbactions.js' -import * as test from './test.js' +import * as test from '../../lib/testing.js' idbactions.deleteDB().then(() => idbactions.openDB()).then(db => { test.run('update lifetime 1', async (testname) => { diff --git a/src/YdbClient/index.js b/provider/ydb/index.js similarity index 84% rename from src/YdbClient/index.js rename to provider/ydb/index.js index a7b99102..12cb6b17 100644 --- a/src/YdbClient/index.js +++ b/provider/ydb/index.js @@ -1,3 +1,7 @@ +/** + * @module provider/ydb + */ + import * as ydbclient from './YdbClient.js' /** diff --git a/src/YdbClient/message.js b/provider/ydb/message.js similarity index 99% rename from src/YdbClient/message.js rename to provider/ydb/message.js index a076c366..86af478b 100644 --- a/src/YdbClient/message.js +++ b/provider/ydb/message.js @@ -1,3 +1,7 @@ +/** + * @module provider/ydb + */ + import * as encoding from './encoding.js' import * as decoding from './decoding.js' import * as idbactions from './idbactions.js' diff --git a/rollup.browser.js b/rollup.browser.js deleted file mode 100644 index 0fa9b06a..00000000 --- a/rollup.browser.js +++ /dev/null @@ -1,31 +0,0 @@ -import babel from 'rollup-plugin-babel' -import uglify from 'rollup-plugin-uglify' -import nodeResolve from 'rollup-plugin-node-resolve' -import commonjs from 'rollup-plugin-commonjs' -var pkg = require('./package.json') - -export default { - input: 'src/index.js', - name: 'Y', - sourcemap: true, - output: { - file: 'build/umd/index.js', - format: 'umd' - }, - plugins: [ - nodeResolve({ - main: true, - module: true, - browser: true - }), - commonjs(), - babel(), - ], - banner: ` -/** - * ${pkg.name} - ${pkg.description} - * @version v${pkg.version} - * @license ${pkg.license} - */ -` -} diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 00000000..cc132536 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,80 @@ +import nodeResolve from 'rollup-plugin-node-resolve' +import commonjs from 'rollup-plugin-commonjs' +import babel from 'rollup-plugin-babel' +import uglify from 'rollup-plugin-uglify-es' + +export default [{ + input: './index.js', + output: { + name: 'Y', + file: 'build/yjs.umd.js', + format: 'umd', + sourcemap: true + } +}, { + input: 'tests/index.js', + output: { + file: 'build/y.test.js', + format: 'iife', + name: 'ytests', + sourcemap: true + }, + plugins: [ + nodeResolve({ + main: true, + module: true + }), + commonjs() + ] +}, { + input: './examples/prosemirror.js', + output: { + name: 'prosemirror', + file: 'examples/build/prosemirror.js', + format: 'iife', + sourcemap: true + }, + plugins: [ + nodeResolve({ + sourcemap: true, + module: true + }), + commonjs(), + babel(), + uglify() + ] +}, { + input: './examples/textarea.js', + output: { + name: 'textarea', + file: 'examples/build/textarea.js', + format: 'iife', + sourcemap: true + }, + plugins: [ + nodeResolve({ + sourcemap: true, + module: true + }), + commonjs(), + babel(), + uglify() + ] +}, { + input: './examples/quill.js', + output: { + name: 'textarea', + file: 'examples/build/quill.js', + format: 'iife', + sourcemap: true + }, + plugins: [ + nodeResolve({ + sourcemap: true, + module: true + }), + commonjs(), + babel(), + uglify() + ] +}] diff --git a/rollup.node.js b/rollup.node.js deleted file mode 100644 index 46f1bdcf..00000000 --- a/rollup.node.js +++ /dev/null @@ -1,18 +0,0 @@ -const pkg = require('./package.json') - -export default { - input: 'src/index.js', - output: { - name: 'Y', - file: 'build/node/index.js', - format: 'cjs', - sourcemap: true, - banner: ` -/** - * ${pkg.name} - ${pkg.description} - * @version v${pkg.version} - * @license ${pkg.license} - */ -` - } -} diff --git a/rollup.test.js b/rollup.test.js deleted file mode 100644 index f4072276..00000000 --- a/rollup.test.js +++ /dev/null @@ -1,22 +0,0 @@ -import nodeResolve from 'rollup-plugin-node-resolve' -import commonjs from 'rollup-plugin-commonjs' -import multiEntry from 'rollup-plugin-multi-entry' - -export default { - input: 'test/index.js', - name: 'y-tests', - sourcemap: true, - output: { - file: 'y.test.js', - format: 'umd' - }, - plugins: [ - multiEntry(), - nodeResolve({ - main: true, - module: true, - browser: true - }), - commonjs() - ] -} diff --git a/src/Persistences/AbstractPersistence.js b/src/Persistences/AbstractPersistence.js deleted file mode 100644 index 7407c21d..00000000 --- a/src/Persistences/AbstractPersistence.js +++ /dev/null @@ -1,2 +0,0 @@ - -export default class AbstractPersistence {} diff --git a/src/Types/YXml/YXmlFragment.js b/src/Types/YXml/YXmlFragment.js deleted file mode 100644 index 0329f2ea..00000000 --- a/src/Types/YXml/YXmlFragment.js +++ /dev/null @@ -1,161 +0,0 @@ -import { createAssociation } from '../../../bindings/DomBinding/util.js' -import YXmlTreeWalker from './YXmlTreeWalker.js' - -import YArray from '../YArray/YArray.js' -import YXmlEvent from './YXmlEvent.js' -import { logItemHelper } from '../../protocols/syncProtocol.js' - -/** - * @typedef {import('./YXmlElement.js').default} YXmlElement - * @typedef {import('../../../bindings/DomBinding/DomBinding.js').default} DomBinding - * @typedef {import('../../Y.js').default} Y - */ - -/** - * Dom filter function. - * - * @callback domFilter - * @param {string} nodeName The nodeName of the element - * @param {Map} attributes The map of attributes. - * @return {boolean} Whether to include the Dom node in the YXmlElement. - */ - -/** - * Define the elements to which a set of CSS queries apply. - * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|CSS_Selectors} - * - * @example - * query = '.classSelector' - * query = 'nodeSelector' - * query = '#idSelector' - * - * @typedef {string} CSS_Selector - */ - -/** - * Represents a list of {@link YXmlElement}.and {@link YXmlText} types. - * A YxmlFragment is similar to a {@link YXmlElement}, but it does not have a - * nodeName and it does not have attributes. Though it can be bound to a DOM - * element - in this case the attributes and the nodeName are not shared. - * - * @public - */ -export default class YXmlFragment extends YArray { - /** - * Create a subtree of childNodes. - * - * @example - * const walker = elem.createTreeWalker(dom => dom.nodeName === 'div') - * for (let node in walker) { - * // `node` is a div node - * nop(node) - * } - * - * @param {Function} filter Function that is called on each child element and - * returns a Boolean indicating whether the child - * is to be included in the subtree. - * @return {YXmlTreeWalker} A subtree and a position within it. - * - * @public - */ - createTreeWalker (filter) { - return new YXmlTreeWalker(this, filter) - } - - /** - * Returns the first YXmlElement that matches the query. - * Similar to DOM's {@link querySelector}. - * - * Query support: - * - tagname - * TODO: - * - id - * - attribute - * - * @param {CSS_Selector} query The query on the children. - * @return {?import('./YXmlElement.js')} The first element that matches the query or null. - * - * @public - */ - querySelector (query) { - query = query.toUpperCase() - const iterator = new YXmlTreeWalker(this, element => element.nodeName === query) - const next = iterator.next() - if (next.done) { - return null - } else { - return next.value - } - } - - /** - * Returns all YXmlElements that match the query. - * Similar to Dom's {@link querySelectorAll}. - * - * TODO: Does not yet support all queries. Currently only query by tagName. - * - * @param {CSS_Selector} query The query on the children - * @return {Array} The elements that match this query. - * - * @public - */ - querySelectorAll (query) { - query = query.toUpperCase() - return Array.from(new YXmlTreeWalker(this, element => element.nodeName === query)) - } - - /** - * Creates YArray Event and calls observers. - * - * @private - */ - _callObserver (transaction, parentSubs, remote) { - this._callEventHandler(transaction, new YXmlEvent(this, parentSubs, remote, transaction)) - } - - toString () { - return this.toDomString() - } - - /** - * Get the string representation of all the children of this YXmlFragment. - * - * @return {string} The string representation of all children. - */ - toDomString () { - return this.map(xml => xml.toDomString()).join('') - } - - /** - * Creates a Dom Element that mirrors this YXmlElement. - * - * @param {Document} [_document=document] The document object (you must define - * this when calling this method in - * nodejs) - * @param {Object.} [hooks={}] Optional property to customize how hooks - * are presented in the DOM - * @param {DomBinding} [binding] You should not set this property. This is - * used if DomBinding wants to create a - * association to the created DOM type - * @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element} - * - * @public - */ - toDom (_document = document, hooks = {}, binding) { - const fragment = _document.createDocumentFragment() - createAssociation(binding, fragment, this) - this.forEach(xmlType => { - fragment.insertBefore(xmlType.toDom(_document, hooks, binding), null) - }) - return fragment - } - /** - * Transform this YXml Type to a readable format. - * Useful for logging as all Items and Delete implement this method. - * - * @private - */ - _logString () { - return logItemHelper('YXml', this) - } -} diff --git a/src/index.js b/src/index.js deleted file mode 100644 index b6e72cd4..00000000 --- a/src/index.js +++ /dev/null @@ -1,54 +0,0 @@ - -import Delete from './Struct/Delete.js' -import ItemJSON from './Struct/ItemJSON.js' -import ItemString from './Struct/ItemString.js' -import ItemFormat from './Struct/ItemFormat.js' -import ItemEmbed from './Struct/ItemEmbed.js' -import GC from './Struct/GC.js' - -import YArray from './Types/YArray/YArray.js' -import YMap from './Types/YMap/YMap.js' -import YText from './Types/YText/YText.js' -import YXmlText from './Types/YXml/YXmlText.js' -import YXmlHook from './Types/YXml/YXmlHook.js' -import YXmlFragment from './Types/YXml/YXmlFragment.js' -import YXmlElement from './Types/YXml/YXmlElement.js' - -import { registerStruct } from './Util/structReferences.js' - -export { default as Y } from './Y.js' -export { default as UndoManager } from './Util/UndoManager.js' -export { default as Transaction } from './Util/Transaction.js' - -export { default as Array } from './Types/YArray/YArray.js' -export { default as Map } from './Types/YMap/YMap.js' -export { default as Text } from './Types/YText/YText.js' -export { default as XmlText } from './Types/YXml/YXmlText.js' -export { default as XmlHook } from './Types/YXml/YXmlHook.js' -export { default as XmlFragment } from './Types/YXml/YXmlFragment.js' -export { default as XmlElement } from './Types/YXml/YXmlElement.js' - -export { getRelativePosition, fromRelativePosition } from './Util/relativePosition.js' -export { registerStruct as registerType } from './Util/structReferences.js' -export * from './protocols/syncProtocol.js' -export * from './protocols/awarenessProtocol.js' -export * from '../lib/encoding.js' -export * from '../lib/decoding.js' -export * from '../lib/mutex.js' - -// TODO: reorder (Item* should have low numbers) -registerStruct(0, ItemJSON) -registerStruct(1, ItemString) -registerStruct(10, ItemFormat) -registerStruct(11, ItemEmbed) -registerStruct(2, Delete) - -registerStruct(3, YArray) -registerStruct(4, YMap) -registerStruct(5, YText) -registerStruct(6, YXmlFragment) -registerStruct(7, YXmlElement) -registerStruct(8, YXmlText) -registerStruct(9, YXmlHook) - -registerStruct(12, GC) diff --git a/src/Struct/Delete.js b/structs/Delete.js similarity index 85% rename from src/Struct/Delete.js rename to structs/Delete.js index 1a3c3aa0..d0bc3aca 100644 --- a/src/Struct/Delete.js +++ b/structs/Delete.js @@ -1,9 +1,15 @@ -import { getStructReference } from '../Util/structReferences.js' -import * as ID from '../Util/ID.js' +/** + * @module structs + */ + +import { getStructReference } from '../utils/structReferences.js' +import * as ID from '../utils/ID.js' import { stringifyID } from '../protocols/syncProtocol.js' -import { writeStructToTransaction } from '../Util/Transaction.js' -import * as decoding from '../../lib/decoding.js' -import * as encoding from '../../lib/encoding.js' +import { writeStructToTransaction } from '../utils/Transaction.js' +import * as decoding from '../lib/decoding.js' +import * as encoding from '../lib/encoding.js' +import { Item } from './Item.js' // eslint-disable-line +import { Y } from '../utils/Y.js' // eslint-disable-line /** * @private @@ -11,7 +17,7 @@ import * as encoding from '../../lib/encoding.js' * Does not create delete operations! * TODO: implement getItemCleanStartNode for better performance (only one lookup). */ -export function deleteItemRange (y, user, clock, range, gcChildren) { +export const deleteItemRange = (y, user, clock, range, gcChildren) => { let item = y.os.getItemCleanStart(ID.createID(user, clock)) if (item !== null) { if (!item._deleted) { @@ -44,14 +50,14 @@ export function deleteItemRange (y, user, clock, range, gcChildren) { * Item. The only difference is that it will not be saved in the ItemStore * (OperationStore), but instead it is safed in the DeleteStore. */ -export default class Delete { +export class Delete { constructor () { /** * @type {ID.ID} */ this._targetID = null /** - * @type {import('./Item.js').default} + * @type {Item} */ this._target = null this._length = null @@ -63,7 +69,7 @@ export default class Delete { * * This is called when data is received from a remote peer. * - * @param {import('../Y.js').default} y The Yjs instance that this Item belongs to. + * @param {Y} y The Yjs instance that this Item belongs to. * @param {decoding.Decoder} decoder The decoder object to read data from. */ _fromBinary (y, decoder) { diff --git a/src/Struct/GC.js b/structs/GC.js similarity index 84% rename from src/Struct/GC.js rename to structs/GC.js index 3496b809..55c94db7 100644 --- a/src/Struct/GC.js +++ b/structs/GC.js @@ -1,11 +1,16 @@ -import { getStructReference } from '../Util/structReferences.js' -import * as ID from '../Util/ID.js' -import { writeStructToTransaction } from '../Util/Transaction.js' -import * as decoding from '../../lib/decoding.js' -import * as encoding from '../../lib/encoding.js' +/** + * @module structs + */ + +import { getStructReference } from '../utils/structReferences.js' +import * as ID from '../utils/ID.js' +import { writeStructToTransaction } from '../utils/Transaction.js' +import * as decoding from '../lib/decoding.js' +import * as encoding from '../lib/encoding.js' +import { Y } from '../utils/Y.js' // eslint-disable-line // TODO should have the same base class as Item -export default class GC { +export class GC { constructor () { /** * @type {ID.ID} @@ -66,7 +71,7 @@ export default class GC { * * This is called when data is received from a remote peer. * - * @param {import('../Y.js').default} y The Yjs instance that this Item belongs to. + * @param {Y} y The Yjs instance that this Item belongs to. * @param {decoding.Decoder} decoder The decoder object to read data from. * @private */ diff --git a/src/Struct/Item.js b/structs/Item.js similarity index 96% rename from src/Struct/Item.js rename to structs/Item.js index 081c0188..7ea72def 100644 --- a/src/Struct/Item.js +++ b/structs/Item.js @@ -1,16 +1,17 @@ -import { getStructReference } from '../Util/structReferences.js' -import * as ID from '../Util/ID.js' -import Delete from './Delete.js' -import { transactionTypeChanged, writeStructToTransaction } from '../Util/Transaction.js' -import GC from './GC.js' -import * as encoding from '../../lib/encoding.js' -import * as decoding from '../../lib/decoding.js' -import Y from '../Y.js' - /** - * @typedef {import('./Type.js').default} YType + * @module structs */ +import { getStructReference } from '../utils/structReferences.js' +import * as ID from '../utils/ID.js' +import { Delete } from './Delete.js' +import { transactionTypeChanged, writeStructToTransaction } from '../utils/Transaction.js' +import { GC } from './GC.js' +import * as encoding from '../lib/encoding.js' +import * as decoding from '../lib/decoding.js' +import { Y } from '../utils/Y.js' +import { Type } from './Type.js' // eslint-disable-line + /** * @private * Helper utility to split an Item (see {@link Item#_splitAt}) @@ -19,7 +20,7 @@ import Y from '../Y.js' * - assigns the correct _id * - saves b to os */ -export function splitHelper (y, a, b, diff) { +export const splitHelper = (y, a, b, diff) => { const aID = a._id b._id = ID.createID(aID.user, aID.clock + diff) b._origin = a @@ -59,7 +60,7 @@ export function splitHelper (y, a, b, diff) { /** * Abstract class that represents any content. */ -export default class Item { +export class Item { constructor () { /** * The uniqe identifier of this type. @@ -88,7 +89,7 @@ export default class Item { this._right_origin = null /** * The parent type. - * @type {Y|YType} + * @type {Y|Type} */ this._parent = null /** @@ -107,7 +108,7 @@ export default class Item { /** * If this type's effect is reundone this type refers to the type that undid * this operation. - * @type {YType} + * @type {Type} */ this._redone = null } @@ -126,6 +127,7 @@ export default class Item { * Redoes the effect of this operation. * * @param {Y} y The Yjs instance. + * @param {Array} redoitems * * @private */ diff --git a/src/Struct/ItemEmbed.js b/structs/ItemEmbed.js similarity index 79% rename from src/Struct/ItemEmbed.js rename to structs/ItemEmbed.js index 71a75508..52974acd 100644 --- a/src/Struct/ItemEmbed.js +++ b/structs/ItemEmbed.js @@ -1,13 +1,14 @@ -import Item from './Item.js' -import { logItemHelper } from '../protocols/syncProtocol.js' -import * as encoding from '../../lib/encoding.js' -import * as decoding from '../../lib/decoding.js' - /** - * @typedef {import('../index.js').Y} Y + * @module structs */ -export default class ItemEmbed extends Item { +import { Item } from './Item.js' +import { logItemHelper } from '../protocols/syncProtocol.js' +import * as encoding from '../lib/encoding.js' +import * as decoding from '../lib/decoding.js' +import { Y } from '../utils/Y.js' // eslint-disable-line + +export class ItemEmbed extends Item { constructor () { super() this.embed = null diff --git a/src/Struct/ItemFormat.js b/structs/ItemFormat.js similarity index 82% rename from src/Struct/ItemFormat.js rename to structs/ItemFormat.js index ed700cb4..f002c7bb 100644 --- a/src/Struct/ItemFormat.js +++ b/structs/ItemFormat.js @@ -1,13 +1,14 @@ -import Item from './Item.js' -import { logItemHelper } from '../protocols/syncProtocol.js' -import * as encoding from '../../lib/encoding.js' -import * as decoding from '../../lib/decoding.js' - /** - * @typedef {import('../index.js').Y} Y + * @module structs */ -export default class ItemFormat extends Item { +import { Item } from './Item.js' +import { logItemHelper } from '../protocols/syncProtocol.js' +import * as encoding from '../lib/encoding.js' +import * as decoding from '../lib/decoding.js' +import { Y } from '../utils/Y.js' // eslint-disable-line + +export class ItemFormat extends Item { constructor () { super() this.key = null diff --git a/src/Struct/ItemJSON.js b/structs/ItemJSON.js similarity index 87% rename from src/Struct/ItemJSON.js rename to structs/ItemJSON.js index 49e3294c..1d5036a8 100644 --- a/src/Struct/ItemJSON.js +++ b/structs/ItemJSON.js @@ -1,13 +1,14 @@ -import Item, { splitHelper } from './Item.js' -import { logItemHelper } from '../protocols/syncProtocol.js' -import * as encoding from '../../lib/encoding.js' -import * as decoding from '../../lib/decoding.js' - /** - * @typedef {import('../index.js').Y} Y + * @module structs */ -export default class ItemJSON extends Item { +import { Item, splitHelper } from './Item.js' +import { logItemHelper } from '../protocols/syncProtocol.js' +import * as encoding from '../lib/encoding.js' +import * as decoding from '../lib/decoding.js' +import { Y } from '../utils/Y.js' // eslint-disable-line + +export class ItemJSON extends Item { constructor () { super() this._content = null diff --git a/src/Struct/ItemString.js b/structs/ItemString.js similarity index 82% rename from src/Struct/ItemString.js rename to structs/ItemString.js index 16a51d79..ed6a0b1f 100644 --- a/src/Struct/ItemString.js +++ b/structs/ItemString.js @@ -1,13 +1,14 @@ -import Item, { splitHelper } from './Item.js' -import { logItemHelper } from '../protocols/syncProtocol.js' -import * as encoding from '../../lib/encoding.js' -import * as decoding from '../../lib/decoding.js' - /** - * @typedef {import('../index.js').Y} Y + * @module structs */ -export default class ItemString extends Item { +import { Item, splitHelper } from './Item.js' +import { logItemHelper } from '../protocols/syncProtocol.js' +import * as encoding from '../lib/encoding.js' +import * as decoding from '../lib/decoding.js' +import { Y } from '../utils/Y.js' // eslint-disable-line + +export class ItemString extends Item { constructor () { super() this._content = null diff --git a/src/Struct/Type.js b/structs/Type.js similarity index 93% rename from src/Struct/Type.js rename to structs/Type.js index bdc1d0f7..7e8fe15d 100644 --- a/src/Struct/Type.js +++ b/structs/Type.js @@ -1,14 +1,15 @@ -import Item from './Item.js' -import EventHandler from '../Util/EventHandler.js' -import { createID } from '../Util/ID.js' -import YEvent from '../Util/YEvent.js' - /** - * @typedef {import("../Y.js").default} Y + * @module structs */ +import { Item } from './Item.js' +import { EventHandler } from '../utils/EventHandler.js' +import { createID } from '../utils/ID.js' +import { YEvent } from '../utils/YEvent.js' +import { Y } from '../utils/Y.js' // eslint-disable-line + // restructure children as if they were inserted one after another -function integrateChildren (y, start) { +const integrateChildren = (y, start) => { let right do { right = start._right @@ -20,7 +21,7 @@ function integrateChildren (y, start) { } while (right !== null) } -export function getListItemIDByPosition (type, i) { +export const getListItemIDByPosition = (type, i) => { let pos = 0 let n = type._start while (n !== null) { @@ -35,7 +36,7 @@ export function getListItemIDByPosition (type, i) { } } -function gcChildren (y, item) { +const gcChildren = (y, item) => { while (item !== null) { item._delete(y, false, true) item._gc(y) @@ -46,7 +47,7 @@ function gcChildren (y, item) { /** * Abstract Yjs Type class */ -export default class Type extends Item { +export class Type extends Item { constructor () { super() this._map = new Map() diff --git a/test/DeleteStore.tests.js b/tests/DeleteStore.tests.js similarity index 90% rename from test/DeleteStore.tests.js rename to tests/DeleteStore.tests.js index 0c41921c..2317587e 100644 --- a/test/DeleteStore.tests.js +++ b/tests/DeleteStore.tests.js @@ -1,7 +1,7 @@ -import { test } from '../node_modules/cutest/cutest.mjs' -import * as random from '../lib/random/random.js' -import DeleteStore from '../src/Store/DeleteStore.js' -import * as ID from '../src/Util/ID.js' +import { test } from 'cutest' +import * as random from '../lib/prng/prng.js' +import { DeleteStore } from '../utils/DeleteStore.js' +import * as ID from '../utils/ID.js' /** * Converts a DS to an array of length 10. @@ -18,7 +18,7 @@ import * as ID from '../src/Util/ID.js' function dsToArray (ds) { const array = [] let i = 0 - ds.iterate(null, null, function (n) { + ds.iterate(null, null, n => { // fill with null while (i < n._id.clock) { array[i++] = null diff --git a/test/diff.tests.js b/tests/diff.tests.js similarity index 87% rename from test/diff.tests.js rename to tests/diff.tests.js index 217759fe..f8e91f6b 100644 --- a/test/diff.tests.js +++ b/tests/diff.tests.js @@ -1,6 +1,6 @@ -import { test } from '../node_modules/cutest/cutest.mjs' -import simpleDiff from '../lib/simpleDiff.js' -import * as random from '../lib/random/random.js' +import { test } from 'cutest' +import { simpleDiff } from '../lib/diff.js' +import * as random from '../lib/prng/prng.js' function runDiffTest (t, a, b, expected) { let result = simpleDiff(a, b) diff --git a/test/encode-decode.tests.js b/tests/encode-decode.tests.js similarity index 93% rename from test/encode-decode.tests.js rename to tests/encode-decode.tests.js index f0c37854..e6fc7b17 100644 --- a/test/encode-decode.tests.js +++ b/tests/encode-decode.tests.js @@ -1,8 +1,8 @@ -import { test } from '../node_modules/cutest/cutest.mjs' -import { generateRandomUint32 } from '../src/Util/generateRandomUint32.js' +import { test } from 'cutest' +import { generateRandomUint32 } from '../utils/generateRandomUint32.js' import * as encoding from '../lib/encoding.js' import * as decoding from '../lib/decoding.js' -import * as random from '../lib/random/random.js' +import * as random from '../lib/prng/prng.js' function testEncoding (t, write, read, val) { let encoder = encoding.createEncoder() diff --git a/tests-lib/helper.js b/tests/helper.js similarity index 93% rename from tests-lib/helper.js rename to tests/helper.js index ad28d4dc..6e3d35ed 100644 --- a/tests-lib/helper.js +++ b/tests/helper.js @@ -1,19 +1,19 @@ -import * as Y from '../src/index.js' -import ItemJSON from '../src/Struct/ItemJSON.js' -import ItemString from '../src/Struct/ItemString.js' -import { defragmentItemContent } from '../src/Util/defragmentItemContent.js' +import * as Y from '../index.js' +import { ItemJSON } from '../structs/ItemJSON.js' +import { ItemString } from '../structs/ItemString.js' +import { defragmentItemContent } from '../utils/defragmentItemContent.js' import Quill from 'quill' -import GC from '../src/Struct/GC.js' -import * as random from '../lib/random/random.js' -import * as syncProtocol from '../src/protocols/syncProtocol.js' +import { GC } from '../structs/GC.js' +import * as random from '../lib/prng/prng.js' +import * as syncProtocol from '../protocols/syncProtocol.js' import * as encoding from '../lib/encoding.js' import * as decoding from '../lib/decoding.js' import { createMutex } from '../lib/mutex.js' -import QuillBinding from '../bindings/QuillBinding/QuillBinding.js' -import DomBinding from '../bindings/DomBinding/DomBinding.js' +import { QuillBinding } from '../bindings/quill.js' +import { DomBinding } from '../bindings/dom/DomBinding.js' -export * from '../src/index.js' +export * from '../index.js' /** * @param {TestYInstance} y @@ -230,7 +230,7 @@ const getDeleteSet = y => { * @type {Object} */ var ds = {} - y.ds.iterate(null, null, function (n) { + y.ds.iterate(null, null, n => { var user = n._id.user var counter = n._id.clock var len = n.len @@ -255,7 +255,7 @@ const getDeleteSet = y => { * @param {any} t * @param {Array} users */ -export function compareUsers (t, users) { +export const compareUsers = (t, users) => { users.forEach(u => u.connect()) do { users.forEach(u => { @@ -277,7 +277,7 @@ export function compareUsers (t, users) { defragmentItemContent(u) var data = {} let ops = [] - u.os.iterate(null, null, function (op) { + u.os.iterate(null, null, op => { let json if (op.constructor === GC) { json = { @@ -336,7 +336,7 @@ export function compareUsers (t, users) { * @param {Map} attrs * @return {null|Map} */ -function filter (nodeName, attrs) { +const filter = (nodeName, attrs) => { if (nodeName === 'HIDDEN') { return null } @@ -349,7 +349,7 @@ function filter (nodeName, attrs) { * @param {any} opts * @return {any} */ -export function initArrays (t, opts) { +export const initArrays = (t, opts) => { var result = { users: [] } @@ -380,7 +380,7 @@ export function initArrays (t, opts) { return result } -export function applyRandomTests (t, mods, iterations) { +export const applyRandomTests = (t, mods, iterations) => { const prng = random.createPRNG(t.getSeed()) const result = initArrays(t, { users: 5, prng }) const { testConnector, users } = result diff --git a/test/index.html b/tests/index.html similarity index 100% rename from test/index.html rename to tests/index.html diff --git a/test/index.js b/tests/index.js similarity index 100% rename from test/index.js rename to tests/index.js diff --git a/test/prosemirror.test.js b/tests/prosemirror.test.js similarity index 76% rename from test/prosemirror.test.js rename to tests/prosemirror.test.js index 0eff2f03..7a005207 100644 --- a/test/prosemirror.test.js +++ b/tests/prosemirror.test.js @@ -1,11 +1,10 @@ -import { test } from '../node_modules/cutest/cutest.mjs' -import * as random from '../lib/random/random.js' -import * as Y from '../src/index.js' +import { test } from 'cutest' +import * as random from '../lib/prng/prng.js' +import * as Y from '../index.js' -import { prosemirrorPlugin, cursorPlugin } from '../bindings/ProsemirrorBinding/ProsemirrorBinding.js' +import { prosemirrorPlugin } from '../bindings/prosemirror.js' import {EditorState} from 'prosemirror-state' import {EditorView} from 'prosemirror-view' -import {Schema, DOMParser, Mark, Fragment, Node, Slice} from 'prosemirror-model' import {schema} from 'prosemirror-schema-basic' import {exampleSetup} from 'prosemirror-example-setup' diff --git a/test/red-black-tree.js b/tests/red-black-tree.js similarity index 88% rename from test/red-black-tree.js rename to tests/red-black-tree.js index 6afd839b..857550f0 100644 --- a/test/red-black-tree.js +++ b/tests/red-black-tree.js @@ -1,15 +1,15 @@ -import RedBlackTree from '../lib/Tree.js' -import * as ID from '../src/Util/ID.js' -import { test, proxyConsole } from '../node_modules/cutest/cutest.mjs' -import * as random from '../lib/random/random.js' +import { Tree as RedBlackTree } from '../lib/Tree.js' +import * as ID from '../utils/ID.js' +import { test, proxyConsole } from 'cutest' +import * as random from '../lib/prng/prng.js' proxyConsole() var numberOfRBTreeTests = 10000 -function checkRedNodesDoNotHaveBlackChildren (t, tree) { +const checkRedNodesDoNotHaveBlackChildren = (t, tree) => { let correct = true - function traverse (n) { + const traverse = n => { if (n == null) { return } @@ -28,9 +28,9 @@ function checkRedNodesDoNotHaveBlackChildren (t, tree) { t.assert(correct, 'Red nodes do not have black children') } -function checkBlackHeightOfSubTreesAreEqual (t, tree) { +const checkBlackHeightOfSubTreesAreEqual = (t, tree) => { let correct = true - function traverse (n) { + const traverse = n => { if (n == null) { return 0 } @@ -49,7 +49,7 @@ function checkBlackHeightOfSubTreesAreEqual (t, tree) { t.assert(correct, 'Black-height of sub-trees are equal') } -function checkRootNodeIsBlack (t, tree) { +const checkRootNodeIsBlack = (t, tree) => { t.assert(tree.root == null || tree.root.isBlack(), 'root node is black') } @@ -88,7 +88,7 @@ test(`random tests (${numberOfRBTreeTests})`, async function randomRBTree (t) { } else if (elements.length > 0) { // ~20% chance to delete an element var elem = random.oneOf(prng, elements) - elements = elements.filter(function (e) { + elements = elements.filter(e => { return !e.equals(elem) }) tree.delete(elem) @@ -125,7 +125,7 @@ test(`random tests (${numberOfRBTreeTests})`, async function randomRBTree (t) { elements.indexOf(e) === pos ).length let actualResults = 0 - tree.iterate(lowerBound, null, function (val) { + tree.iterate(lowerBound, null, val => { if (val == null) { t.assert(false, 'val is undefined!') } @@ -140,7 +140,7 @@ test(`random tests (${numberOfRBTreeTests})`, async function randomRBTree (t) { elements.indexOf(e) === pos ).length actualResults = 0 - tree.iterate(null, null, function (val) { + tree.iterate(null, null, val => { if (val == null) { t.assert(false, 'val is undefined!') } @@ -157,7 +157,7 @@ test(`random tests (${numberOfRBTreeTests})`, async function randomRBTree (t) { elements.indexOf(e) === pos ).length actualResults = 0 - tree.iterate(null, upperBound, function (val) { + tree.iterate(null, upperBound, val => { if (val == null) { t.assert(false, 'val is undefined!') } @@ -179,7 +179,7 @@ test(`random tests (${numberOfRBTreeTests})`, async function randomRBTree (t) { elements.indexOf(e) === pos ).length actualResults = 0 - tree.iterate(lowerBound, upperBound, function (val) { + tree.iterate(lowerBound, upperBound, val => { if (val == null) { t.assert(false, 'val is undefined!') } diff --git a/test/y-array.tests.js b/tests/y-array.tests.js similarity index 96% rename from test/y-array.tests.js rename to tests/y-array.tests.js index 8c57444b..b59d96f8 100644 --- a/test/y-array.tests.js +++ b/tests/y-array.tests.js @@ -1,7 +1,7 @@ -import { initArrays, compareUsers, applyRandomTests } from '../tests-lib/helper.js' -import * as Y from '../src/index.js' +import { initArrays, compareUsers, applyRandomTests } from './helper.js' +import * as Y from '../index.js' import { test, proxyConsole } from 'cutest' -import * as random from '../lib/random/random.js' +import * as random from '../lib/prng/prng.js' proxyConsole() test('basic spec', async function array0 (t) { @@ -118,7 +118,7 @@ function compareEvent (t, is, should) { test('insert & delete events', async function array8 (t) { var { array0, users } = await initArrays(t, { users: 2 }) var event - array0.observe(function (e) { + array0.observe(e => { event = e }) array0.insert(0, [0, 1, 2]) @@ -139,7 +139,7 @@ test('insert & delete events', async function array8 (t) { test('insert & delete events for types', async function array9 (t) { var { array0, users } = await initArrays(t, { users: 2 }) var event - array0.observe(function (e) { + array0.observe(e => { event = e }) array0.insert(0, [Y.Array]) @@ -156,7 +156,7 @@ test('insert & delete events for types', async function array9 (t) { test('insert & delete events for types (2)', async function array10 (t) { var { array0, users } = await initArrays(t, { users: 2 }) var events = [] - array0.observe(function (e) { + array0.observe(e => { events.push(e) }) array0.insert(0, ['hi', Y.Map]) @@ -186,7 +186,7 @@ test('garbage collector', async function gc1 (t) { test('event target is set correctly (local)', async function array11 (t) { let { array0, users } = await initArrays(t, { users: 3 }) var event - array0.observe(function (e) { + array0.observe(e => { event = e }) array0.insert(0, ['stuff']) @@ -197,7 +197,7 @@ test('event target is set correctly (local)', async function array11 (t) { test('event target is set correctly (remote user)', async function array12 (t) { let { testConnector, array0, array1, users } = await initArrays(t, { users: 3 }) var event - array0.observe(function (e) { + array0.observe(e => { event = e }) array1.insert(0, ['stuff']) diff --git a/test/y-map.tests.js b/tests/y-map.tests.js similarity index 96% rename from test/y-map.tests.js rename to tests/y-map.tests.js index e3518383..3e9fefb5 100644 --- a/test/y-map.tests.js +++ b/tests/y-map.tests.js @@ -1,7 +1,7 @@ -import { initArrays, compareUsers, applyRandomTests } from '../tests-lib/helper.js' -import * as Y from '../src/index.js' +import { initArrays, compareUsers, applyRandomTests } from './helper.js' +import * as Y from '../index.js' import { test, proxyConsole } from 'cutest' -import * as random from '../lib/random/random.js' +import * as random from '../lib/prng/prng.js' proxyConsole() @@ -165,7 +165,7 @@ test('Basic get&set&delete of Map property (handle three conflicts)', async func test('observePath properties', async function map10 (t) { let { users, map0, map1, map2 } = await initArrays(t, { users: 3 }) let map - map0.observePath(['map'], function (map) { + map0.observePath(['map'], map => { if (map != null) { map.set('yay', 4) } @@ -183,7 +183,7 @@ test('observe deep properties', async function map11 (t) { var _map1 = map1.set('map', new Y.Map()) var calls = 0 var dmapid - map1.observeDeep(function (events) { + map1.observeDeep(events => { events.forEach(event => { calls++ t.assert(event.keysChanged.has('deepmap')) @@ -213,7 +213,7 @@ test('observes using observeDeep', async function map12 (t) { let { users, map0 } = await initArrays(t, { users: 2 }) var pathes = [] var calls = 0 - map0.observeDeep(function (events) { + map0.observeDeep(events => { events.forEach(event => { pathes.push(event.path) }) @@ -238,7 +238,7 @@ test('throws add & update & delete events (with type and primitive content)', as let { users, map0 } = await initArrays(t, { users: 2 }) var event await flushAll(t, users) - map0.observe(function (e) { + map0.observe(e => { event = e // just put it on event, should be thrown synchronously anyway }) map0.set('stuff', 4) @@ -277,7 +277,7 @@ test('event has correct value when setting a primitive on a YMap (same user)', a let { users, map0 } = await initArrays(t, { users: 3 }) var event await flushAll(t, users) - map0.observe(function (e) { + map0.observe(e => { event = e }) map0.set('stuff', 2) @@ -289,7 +289,7 @@ test('event has correct value when setting a primitive on a YMap (received from let { users, map0, map1 } = await initArrays(t, { users: 3 }) var event await flushAll(t, users) - map0.observe(function (e) { + map0.observe(e => { event = e }) map1.set('stuff', 2) diff --git a/test/y-text.tests.js b/tests/y-text.tests.js similarity index 95% rename from test/y-text.tests.js rename to tests/y-text.tests.js index 48c6c265..118c8087 100644 --- a/test/y-text.tests.js +++ b/tests/y-text.tests.js @@ -1,4 +1,4 @@ -import { initArrays, compareUsers } from '../tests-lib/helper.js' +import { initArrays, compareUsers } from './helper.js' import { test, proxyConsole } from 'cutest' proxyConsole() @@ -7,7 +7,7 @@ test('basic insert delete', async function text0 (t) { let { users, text0 } = await initArrays(t, { users: 2 }) let delta - text0.observe(function (event) { + text0.observe(event => { delta = event.delta }) @@ -32,7 +32,7 @@ test('basic insert delete', async function text0 (t) { test('basic format', async function text1 (t) { let { users, text0 } = await initArrays(t, { users: 2 }) let delta - text0.observe(function (event) { + text0.observe(event => { delta = event.delta }) text0.insert(0, 'abc', { bold: true }) @@ -76,7 +76,7 @@ test('quill issue 1', async function quill1 (t) { test('quill issue 2', async function quill2 (t) { let { testConnector, users, quill0, text0 } = await initArrays(t, { users: 2 }) let delta - text0.observe(function (event) { + text0.observe(event => { delta = event.delta }) quill0.insertText(0, 'abc', 'bold', true) diff --git a/test/y-xml.tests.js b/tests/y-xml.tests.js similarity index 98% rename from test/y-xml.tests.js rename to tests/y-xml.tests.js index 6fba7470..f963cd6a 100644 --- a/test/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -1,7 +1,7 @@ -import { initArrays, compareUsers, applyRandomTests } from '../../yjs/tests-lib/helper.js' +import { initArrays, compareUsers, applyRandomTests } from './helper.js' import { test } from 'cutest' -import * as Y from '../src/index.js' -import * as random from '../lib/random/random.js' +import * as Y from '../index.js' +import * as random from '../lib/prng/prng.js' test('set property', async function xml0 (t) { var { testConnector, users, xml0, xml1 } = await initArrays(t, { users: 2 }) @@ -16,13 +16,13 @@ test('events', async function xml1 (t) { var { testConnector, users, xml0, xml1 } = await initArrays(t, { users: 2 }) var event = { attributesChanged: new Set() } var remoteEvent = { attributesChanged: new Set() } - xml0.observe(function (e) { + xml0.observe(e => { delete e._content delete e.nodes delete e.values event = e }) - xml1.observe(function (e) { + xml1.observe(e => { delete e._content delete e.nodes delete e.values diff --git a/src/Types/YArray/YArray.js b/types/YArray.js similarity index 94% rename from src/Types/YArray/YArray.js rename to types/YArray.js index 5625b81d..13e1c62a 100644 --- a/src/Types/YArray/YArray.js +++ b/types/YArray.js @@ -1,15 +1,15 @@ -import Type from '../../Struct/Type.js' -import ItemJSON from '../../Struct/ItemJSON.js' -import ItemString from '../../Struct/ItemString.js' -import { stringifyItemID, logItemHelper } from '../../protocols/syncProtocol.js' -import YEvent from '../../Util/YEvent.js' - /** - * @typedef {import('../../Struct/Item.js').default} Item - * @typedef {import('../../Util/Transaction.js').default} Transaction - * @typedef {import('../../Y.js').default} Y + * @module types */ +import { Type } from '../structs/Type.js' +import { ItemJSON } from '../structs/ItemJSON.js' +import { ItemString } from '../structs/ItemString.js' +import { stringifyItemID, logItemHelper } from '../protocols/syncProtocol.js' +import { YEvent } from '../utils/YEvent.js' +import { Transaction } from '../utils/Transaction.js' // eslint-disable-line +import { Item } from '../structs/Item.js' // eslint-disable-line + /** * Event that describes the changes on a YArray * @@ -36,7 +36,7 @@ export class YArrayEvent extends YEvent { const target = this.target const transaction = this._transaction const addedElements = new Set() - transaction.newTypes.forEach(function (type) { + transaction.newTypes.forEach(type => { if (type._parent === target && !transaction.deletedStructs.has(type)) { addedElements.add(type) } @@ -56,7 +56,7 @@ export class YArrayEvent extends YEvent { const target = this.target const transaction = this._transaction const removedElements = new Set() - transaction.deletedStructs.forEach(function (struct) { + transaction.deletedStructs.forEach(struct => { if (struct._parent === target && !transaction.newTypes.has(struct)) { removedElements.add(struct) } @@ -70,7 +70,7 @@ export class YArrayEvent extends YEvent { /** * A shared Array implementation. */ -export default class YArray extends Type { +export class YArray extends Type { /** * @private * Creates YArray Event and calls observers. diff --git a/src/Types/YMap/YMap.js b/types/YMap.js similarity index 82% rename from src/Types/YMap/YMap.js rename to types/YMap.js index 0130caa6..cd5d2e0d 100644 --- a/src/Types/YMap/YMap.js +++ b/types/YMap.js @@ -1,14 +1,13 @@ -import Item from '../../Struct/Item.js' -import Type from '../../Struct/Type.js' -import ItemJSON from '../../Struct/ItemJSON.js' -import { logItemHelper } from '../../protocols/syncProtocol.js' -import YEvent from '../../Util/YEvent.js' - /** - * @typedef {import('../../Y.js').encodable} encodable - * @typedef {import('../../Struct/Type.js')} YType + * @module types */ +import { Item } from '../structs/Item.js' +import { Type } from '../structs/Type.js' +import { ItemJSON } from '../structs/ItemJSON.js' +import { logItemHelper } from '../protocols/syncProtocol.js' +import { YEvent } from '../utils/YEvent.js' + /** * Event that describes the changes on a YMap. * @@ -27,7 +26,7 @@ export class YMapEvent extends YEvent { /** * A shared Map implementation. */ -export default class YMap extends Type { +export class YMap extends Type { /** * @private * Creates YMap Event and calls observers. @@ -80,7 +79,7 @@ export default class YMap extends Type { /** * Remove a specified element from this YMap. * - * @param {encodable} key The key of the element to remove. + * @param {string} key The key of the element to remove. */ delete (key) { this._transact((y) => { @@ -94,9 +93,8 @@ export default class YMap extends Type { /** * Adds or updates an element with a specified key and value. * - * @param {encodable} key The key of the element to add to this YMap. - * @param {encodable | YType} value The value of the element to add to this - * YMap. + * @param {string} key The key of the element to add to this YMap + * @param {Object | string | number | Type} value The value of the element to add */ set (key, value) { this._transact(y => { @@ -140,7 +138,7 @@ export default class YMap extends Type { /** * Returns a specified element from this YMap. * - * @param {encodable} key The key of the element to return. + * @param {string} key The key of the element to return. */ get (key) { let v = this._map.get(key) @@ -157,7 +155,7 @@ export default class YMap extends Type { /** * Returns a boolean indicating whether the specified key exists or not. * - * @param {encodable} key The key to test. + * @param {string} key The key to test. */ has (key) { let v = this._map.get(key) diff --git a/src/Types/YText/YText.js b/types/YText.js similarity index 94% rename from src/Types/YText/YText.js rename to types/YText.js index 69804f44..646c5e76 100644 --- a/src/Types/YText/YText.js +++ b/types/YText.js @@ -1,13 +1,17 @@ -import ItemEmbed from '../../Struct/ItemEmbed.js' -import ItemString from '../../Struct/ItemString.js' -import ItemFormat from '../../Struct/ItemFormat.js' -import { logItemHelper } from '../../protocols/syncProtocol.js' -import { YArrayEvent, default as YArray } from '../YArray/YArray.js' +/** + * @module types + */ + +import { ItemEmbed } from '../structs/ItemEmbed.js' +import { ItemString } from '../structs/ItemString.js' +import { ItemFormat } from '../structs/ItemFormat.js' +import { logItemHelper } from '../protocols/syncProtocol.js' +import { YArrayEvent, YArray } from './YArray.js' /** * @private */ -function integrateItem (item, parent, y, left, right) { +const integrateItem = (item, parent, y, left, right) => { item._origin = left item._left = left item._right = right @@ -25,7 +29,7 @@ function integrateItem (item, parent, y, left, right) { /** * @private */ -function findNextPosition (currentAttributes, parent, left, right, count) { +const findNextPosition = (currentAttributes, parent, left, right, count) => { while (right !== null && count > 0) { switch (right.constructor) { case ItemEmbed: @@ -55,7 +59,7 @@ function findNextPosition (currentAttributes, parent, left, right, count) { /** * @private */ -function findPosition (parent, index) { +const findPosition = (parent, index) => { let currentAttributes = new Map() let left = null let right = parent._start @@ -67,7 +71,7 @@ function findPosition (parent, index) { * * @private */ -function insertNegatedAttributes (y, parent, left, right, negatedAttributes) { +const insertNegatedAttributes = (y, parent, left, right, negatedAttributes) => { // check if we really need to remove attributes while ( right !== null && ( @@ -96,7 +100,7 @@ function insertNegatedAttributes (y, parent, left, right, negatedAttributes) { /** * @private */ -function updateCurrentAttributes (currentAttributes, item) { +const updateCurrentAttributes = (currentAttributes, item) => { const value = item.value const key = item.key if (value === null) { @@ -109,7 +113,7 @@ function updateCurrentAttributes (currentAttributes, item) { /** * @private */ -function minimizeAttributeChanges (left, right, currentAttributes, attributes) { +const minimizeAttributeChanges = (left, right, currentAttributes, attributes) => { // go right while attributes[right.key] === right.value (or right is deleted) while (true) { if (right === null) { @@ -131,7 +135,7 @@ function minimizeAttributeChanges (left, right, currentAttributes, attributes) { /** * @private */ -function insertAttributes (y, parent, left, right, attributes, currentAttributes) { +const insertAttributes = (y, parent, left, right, attributes, currentAttributes) => { const negatedAttributes = new Map() // insert format-start items for (let key in attributes) { @@ -153,7 +157,7 @@ function insertAttributes (y, parent, left, right, attributes, currentAttributes /** * @private */ -function insertText (y, text, parent, left, right, currentAttributes, attributes) { +const insertText = (y, text, parent, left, right, currentAttributes, attributes) => { for (let [key] of currentAttributes) { if (attributes[key] === undefined) { attributes[key] = null @@ -179,7 +183,7 @@ function insertText (y, text, parent, left, right, currentAttributes, attributes /** * @private */ -function formatText (y, length, parent, left, right, currentAttributes, attributes) { +const formatText = (y, length, parent, left, right, currentAttributes, attributes) => { [left, right] = minimizeAttributeChanges(left, right, currentAttributes, attributes) let negatedAttributes [left, right, negatedAttributes] = insertAttributes(y, parent, left, right, attributes, currentAttributes) @@ -216,7 +220,7 @@ function formatText (y, length, parent, left, right, currentAttributes, attribut /** * @private */ -function deleteText (y, length, parent, left, right, currentAttributes) { +const deleteText = (y, length, parent, left, right, currentAttributes) => { while (length > 0 && right !== null) { if (right._deleted === false) { switch (right.constructor) { @@ -460,7 +464,7 @@ class YTextEvent extends YArrayEvent { * * @param {String} string The initial value of the YText. */ -export default class YText extends YArray { +export class YText extends YArray { constructor (string) { super() if (typeof string === 'string') { diff --git a/src/Types/YXml/YXmlElement.js b/types/YXmlElement.js similarity index 50% rename from src/Types/YXml/YXmlElement.js rename to types/YXmlElement.js index 92f64e67..75165d2e 100644 --- a/src/Types/YXml/YXmlElement.js +++ b/types/YXmlElement.js @@ -1,13 +1,173 @@ -import YMap from '../YMap/YMap.js' -import YXmlFragment from './YXmlFragment.js' -import { createAssociation } from '../../../bindings/DomBinding/util.js' -import * as encoding from '../../../lib/encoding.js' -import * as decoding from '../../../lib/decoding.js' +/** + * @module types + */ + +import { YMap } from './YMap.js' +import { createAssociation } from '../bindings/dom/util.js' +import * as encoding from '../lib/encoding.js' +import * as decoding from '../lib/decoding.js' +import { Y } from '../utils/Y.js' // eslint-disable-line +import { DomBinding } from '../bindings/dom/DomBinding.js' // eslint-disable-line +import { YXmlTreeWalker } from './YXmlTreeWalker.js' +import { YArray } from './YArray.js' +import { YXmlEvent } from './YXmlEvent.js' +import { logItemHelper } from '../protocols/syncProtocol.js' /** - * @typedef {import('../../Y.js').default} Y + * Dom filter function. + * + * @callback domFilter + * @param {string} nodeName The nodeName of the element + * @param {Map} attributes The map of attributes. + * @return {boolean} Whether to include the Dom node in the YXmlElement. */ +/** + * Define the elements to which a set of CSS queries apply. + * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|CSS_Selectors} + * + * @example + * query = '.classSelector' + * query = 'nodeSelector' + * query = '#idSelector' + * + * @typedef {string} CSS_Selector + *//** + * @module types + */ + +/** + * Represents a list of {@link YXmlElement}.and {@link YXmlText} types. + * A YxmlFragment is similar to a {@link YXmlElement}, but it does not have a + * nodeName and it does not have attributes. Though it can be bound to a DOM + * element - in this case the attributes and the nodeName are not shared. + * + * @public + */ +export class YXmlFragment extends YArray { + /** + * Create a subtree of childNodes. + * + * @example + * const walker = elem.createTreeWalker(dom => dom.nodeName === 'div') + * for (let node in walker) { + * // `node` is a div node + * nop(node) + * } + * + * @param {Function} filter Function that is called on each child element and + * returns a Boolean indicating whether the child + * is to be included in the subtree. + * @return {YXmlTreeWalker} A subtree and a position within it. + * + * @public + */ + createTreeWalker (filter) { + return new YXmlTreeWalker(this, filter) + } + + /** + * Returns the first YXmlElement that matches the query. + * Similar to DOM's {@link querySelector}. + * + * Query support: + * - tagname + * TODO: + * - id + * - attribute + * + * @param {CSS_Selector} query The query on the children. + * @return {YXmlElement} The first element that matches the query or null. + * + * @public + */ + querySelector (query) { + query = query.toUpperCase() + const iterator = new YXmlTreeWalker(this, element => element.nodeName === query) + const next = iterator.next() + if (next.done) { + return null + } else { + return next.value + } + } + + /** + * Returns all YXmlElements that match the query. + * Similar to Dom's {@link querySelectorAll}. + * + * TODO: Does not yet support all queries. Currently only query by tagName. + * + * @param {CSS_Selector} query The query on the children + * @return {Array} The elements that match this query. + * + * @public + */ + querySelectorAll (query) { + query = query.toUpperCase() + return Array.from(new YXmlTreeWalker(this, element => element.nodeName === query)) + } + + /** + * Creates YArray Event and calls observers. + * + * @private + */ + _callObserver (transaction, parentSubs, remote) { + this._callEventHandler(transaction, new YXmlEvent(this, parentSubs, remote, transaction)) + } + + toString () { + return this.toDomString() + } + + /** + * Get the string representation of all the children of this YXmlFragment. + * + * @return {string} The string representation of all children. + */ + toDomString () { + return this.map(xml => xml.toDomString()).join('') + } + + /** + * Creates a Dom Element that mirrors this YXmlElement. + * + * @param {Document} [_document=document] The document object (you must define + * this when calling this method in + * nodejs) + * @param {Object.} [hooks={}] Optional property to customize how hooks + * are presented in the // TODO: include all tests + + * @param {DomBinding} [binding] You should not set this property. T// TODO: include all tests + + * used if DomBinding wants to create // TODO: include all tests + + * association to the created DOM type// TODO: include all tests + + * @return {DocumentFragment} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element} + * + * @public + */ + toDom (_document = document, hooks = {}, binding) { + const fragment = _document.createDocumentFragment() + createAssociation(binding, fragment, this) + this.forEach(xmlType => { + fragment.insertBefore(xmlType.toDom(_document, hooks, binding), null) + }) + return fragment + } + /** + * Transform this YXml Type to a readable format. + * Useful for logging as all Items and Delete implement this method. + * + * @private + */ + _logString () { + return logItemHelper('YXml', this) + } +} + /** * An YXmlElement imitates the behavior of a * {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}. @@ -15,7 +175,7 @@ import * as decoding from '../../../lib/decoding.js' * * An YXmlElement has attributes (key value pairs) * * An YXmlElement has childElements that must inherit from YXmlElement */ -export default class YXmlElement extends YXmlFragment { +export class YXmlElement extends YXmlFragment { constructor (nodeName = 'UNDEFINED') { super() this.nodeName = nodeName.toUpperCase() @@ -174,7 +334,7 @@ export default class YXmlElement extends YXmlFragment { * nodejs) * @param {Object} [hooks={}] Optional property to customize how hooks * are presented in the DOM - * @param {import('../../../bindings/DomBinding/DomBinding.js').default} [binding] You should not set this property. This is + * @param {DomBinding} [binding] You should not set this property. This is * used if DomBinding wants to create a * association to the created DOM type. * @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element} @@ -194,13 +354,3 @@ export default class YXmlElement extends YXmlFragment { return dom } } - -// reassign yxmlfragment to {any} type to prevent warnings -// assign yxmlelement to YXmlFragment so it has a reference to YXmlElement. - -/** - * @type {any} - */ -const _reasgn = YXmlFragment - -_reasgn._YXmlElement = YXmlElement diff --git a/src/Types/YXml/YXmlEvent.js b/types/YXmlEvent.js similarity index 78% rename from src/Types/YXml/YXmlEvent.js rename to types/YXmlEvent.js index 747a50a0..e41d9f26 100644 --- a/src/Types/YXml/YXmlEvent.js +++ b/types/YXmlEvent.js @@ -1,18 +1,20 @@ -import YEvent from '../../Util/YEvent.js' - /** - * @typedef {import('../../Struct/Type.js').default} YType - * @typedef {import('../../Util/Transaction.js').default} Transaction + * @module types */ +import { YEvent } from '../utils/YEvent.js' + +import { Type } from '../structs/Type.js' // eslint-disable-line +import { Transaction } from '../utils/Transaction.js' // eslint-disable-line + /** * An Event that describes changes on a YXml Element or Yxml Fragment * * @protected */ -export default class YXmlEvent extends YEvent { +export class YXmlEvent extends YEvent { /** - * @param {YType} target The target on which the event is created. + * @param {Type} target The target on which the event is created. * @param {Set} subs The set of changed attributes. `null` is included if the * child list changed. * @param {Boolean} remote Whether this change was created by a remote peer. diff --git a/src/Types/YXml/YXmlHook.js b/types/YXmlHook.js similarity index 88% rename from src/Types/YXml/YXmlHook.js rename to types/YXmlHook.js index 6bb7ea39..ad45f1eb 100644 --- a/src/Types/YXml/YXmlHook.js +++ b/types/YXmlHook.js @@ -1,19 +1,21 @@ -import YMap from '../YMap/YMap.js' -import { createAssociation } from '../../../bindings/DomBinding/util.js' -import * as encoding from '../../../lib/encoding.js' -import * as decoding from '../../../lib/decoding.js' - /** - * @typedef {import('../../../bindings/DomBinding/DomBinding.js').default} DomBinding - * @typedef {import('../../Y.js').default} Y + * @module types */ +import { YMap } from './YMap.js' +import { createAssociation } from '../bindings/dom/util.js' +import * as encoding from '../lib/encoding.js' +import * as decoding from '../lib/decoding.js' + +import { DomBinding } from '../bindings/dom/DomBinding.js' // eslint-disable-line +import { Y } from '../utils/Y.js' // eslint-disable-line + /** * You can manage binding to a custom type with YXmlHook. * * @public */ -export default class YXmlHook extends YMap { +export class YXmlHook extends YMap { /** * @param {String} hookName nodeName of the Dom Node. */ diff --git a/src/Types/YXml/YXmlText.js b/types/YXmlText.js similarity index 85% rename from src/Types/YXml/YXmlText.js rename to types/YXmlText.js index 2d38affd..673c7509 100644 --- a/src/Types/YXml/YXmlText.js +++ b/types/YXmlText.js @@ -1,18 +1,19 @@ -import YText from '../YText/YText.js' -import { createAssociation } from '../../../bindings/DomBinding/util.js' - /** - * @typedef {import('../../../bindings/DomBinding/DomBinding.js').default} DomBinding - * @typedef {import('../../index.js').Y} Y + * @module types */ +import { YText } from './YText.js' +import { createAssociation } from '../bindings/dom/util.js' +import { Y } from '../index.js' // eslint-disable-line +import { DomBinding } from '../bindings/dom/DomBinding.js' // eslint-disable-line + /** * Represents text in a Dom Element. In the future this type will also handle * simple formatting information like bold and italic. * * @param {String} arg1 Initial value. */ -export default class YXmlText extends YText { +export class YXmlText extends YText { /** * Creates a Dom Element that mirrors this YXmlText. * diff --git a/src/Types/YXml/YXmlTreeWalker.js b/types/YXmlTreeWalker.js similarity index 83% rename from src/Types/YXml/YXmlTreeWalker.js rename to types/YXmlTreeWalker.js index 6b0dc992..605266c7 100644 --- a/src/Types/YXml/YXmlTreeWalker.js +++ b/types/YXmlTreeWalker.js @@ -1,4 +1,8 @@ -import YXmlFragment from './YXmlFragment.js' +/** + * @module types + */ + +import { YXmlElement, YXmlFragment } from './YXmlElement.js' // eslint-disable-line /** * Define the elements to which a set of CSS queries apply. @@ -20,7 +24,7 @@ import YXmlFragment from './YXmlFragment.js' * * @public */ -export default class YXmlTreeWalker { +export class YXmlTreeWalker { constructor (root, f) { this._filter = f || (() => true) this._root = root @@ -33,7 +37,7 @@ export default class YXmlTreeWalker { /** * Get the next node. * - * @return {import('./YXmlElement.js').default} The next node. + * @return {YXmlElement} The next node. * * @public */ @@ -46,7 +50,7 @@ export default class YXmlTreeWalker { } } do { - if (!n._deleted && (n.constructor === YXmlFragment._YXmlElement || n.constructor === YXmlFragment) && n._start !== null) { + if (!n._deleted && (n.constructor === YXmlElement || n.constructor === YXmlFragment) && n._start !== null) { // walk down in the tree n = n._start } else { diff --git a/bindings/BindMapping.js b/utils/BindMapping.js similarity index 96% rename from bindings/BindMapping.js rename to utils/BindMapping.js index 22565f6c..43568314 100644 --- a/bindings/BindMapping.js +++ b/utils/BindMapping.js @@ -1,12 +1,12 @@ - /** * Type that maps from Yjs type to Target type. * Used to implement double bindings. * + * @private * @template Y * @template T */ -export default class BindMapping { +export class BindMapping { /** */ constructor () { diff --git a/src/Store/DeleteStore.js b/utils/DeleteStore.js similarity index 94% rename from src/Store/DeleteStore.js rename to utils/DeleteStore.js index a795653e..00329b89 100644 --- a/src/Store/DeleteStore.js +++ b/utils/DeleteStore.js @@ -1,6 +1,9 @@ +/** + * @module utils + */ -import Tree from '../../lib/Tree.js' -import * as ID from '../Util/ID.js' +import { Tree } from '../lib/Tree.js' +import * as ID from './ID.js' class DSNode { constructor (id, len, gc) { @@ -13,10 +16,10 @@ class DSNode { } } -export default class DeleteStore extends Tree { +export class DeleteStore extends Tree { logTable () { const deletes = [] - this.iterate(null, null, function (n) { + this.iterate(null, null, n => { deletes.push({ user: n._id.user, clock: n._id.clock, diff --git a/src/Util/EventHandler.js b/utils/EventHandler.js similarity index 92% rename from src/Util/EventHandler.js rename to utils/EventHandler.js index c82c0f46..245c00b3 100644 --- a/src/Util/EventHandler.js +++ b/utils/EventHandler.js @@ -1,8 +1,11 @@ +/** + * @module utils + */ /** * General event handler implementation. */ -export default class EventHandler { +export class EventHandler { constructor () { this.eventListeners = [] } @@ -32,9 +35,7 @@ export default class EventHandler { * {@link EventHandler#addEventListener} */ removeEventListener (f) { - this.eventListeners = this.eventListeners.filter(function (g) { - return f !== g - }) + this.eventListeners = this.eventListeners.filter(g => f !== g) } /** diff --git a/src/Util/ID.js b/utils/ID.js similarity index 95% rename from src/Util/ID.js rename to utils/ID.js index da022bbe..cd421309 100644 --- a/src/Util/ID.js +++ b/utils/ID.js @@ -1,6 +1,10 @@ +/** + * @module utils + */ + import { getStructReference } from './structReferences.js' -import * as decoding from '../../lib/decoding.js' -import * as encoding from '../../lib/encoding.js' +import * as decoding from '../lib/decoding.js' +import * as encoding from '../lib/encoding.js' export class ID { constructor (user, clock) { diff --git a/src/Store/OperationStore.js b/utils/OperationStore.js similarity index 89% rename from src/Store/OperationStore.js rename to utils/OperationStore.js index b5cea63b..d473dd19 100644 --- a/src/Store/OperationStore.js +++ b/utils/OperationStore.js @@ -1,17 +1,21 @@ -import Tree from '../../lib/Tree.js' -import * as ID from '../Util/ID.js' -import { getStruct } from '../Util/structReferences.js' -import { stringifyID, stringifyItemID } from '../protocols/syncProtocol.js' -import GC from '../Struct/GC.js' +/** + * @module utils + */ -export default class OperationStore extends Tree { +import { Tree } from '../lib/Tree.js' +import * as ID from '../utils/ID.js' +import { getStruct } from '../utils/structReferences.js' +import { stringifyID, stringifyItemID } from '../protocols/syncProtocol.js' +import { GC } from '../structs/GC.js' + +export class OperationStore extends Tree { constructor (y) { super() this.y = y } logTable () { const items = [] - this.iterate(null, null, function (item) { + this.iterate(null, null, item => { if (item.constructor === GC) { items.push({ id: stringifyItemID(item), diff --git a/src/Store/StateStore.js b/utils/StateStore.js similarity index 92% rename from src/Store/StateStore.js rename to utils/StateStore.js index 43ef713d..fad983df 100644 --- a/src/Store/StateStore.js +++ b/utils/StateStore.js @@ -1,10 +1,14 @@ -import * as ID from '../Util/ID.js' +/** + * @module utils + */ + +import * as ID from '../utils/ID.js' /** * @typedef {Map} StateSet */ -export default class StateStore { +export class StateStore { constructor (y) { this.y = y this.state = new Map() diff --git a/src/Util/Transaction.js b/utils/Transaction.js similarity index 78% rename from src/Util/Transaction.js rename to utils/Transaction.js index 2507e431..ce34d5f0 100644 --- a/src/Util/Transaction.js +++ b/utils/Transaction.js @@ -1,11 +1,12 @@ -import * as encoding from '../../lib/encoding.js' /** - * @typedef {import("../Y.js").default} Y - * @typedef {import("../Struct/Type.js").default} YType - * @typedef {import("../Struct/Item.js").default} Item - * @typedef {import("./YEvent.js").default} YEvent + * @module utils */ +import * as encoding from '../lib/encoding.js' +import { Y } from '../utils/Y.js' // eslint-disable-line +import { Type } from '../structs/Type.js' // eslint-disable-line +import { Item } from '../structs/Item.js' // eslint-disable-line +import { YEvent } from './YEvent.js' // eslint-disable-line /** * A transaction is created for every change on the Yjs model. It is possible * to bundle changes on the Yjs model in a single transaction to @@ -16,23 +17,23 @@ import * as encoding from '../../lib/encoding.js' * @example * const map = y.define('map', YMap) * // Log content when change is triggered - * map.observe(function () { + * map.observe(() => { * 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 () { + * y.transact(() => { * map.set('a', 1) * map.set('b', 1) * }) // => "change triggered" * */ -export default class Transaction { +export class Transaction { constructor (y) { /** - * @type {import("../Y.js")} The Yjs instance. + * @type {Y} The Yjs instance. */ this.y = y /** @@ -44,7 +45,7 @@ export default class Transaction { * 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 {Map} + * @type {Map} */ this.changedTypes = new Map() // TODO: rename deletedTypes @@ -62,7 +63,7 @@ export default class Transaction { /** * Stores the events for the types that observe also child elements. * It is mainly used by `observeDeep`. - * @type {Map>} + * @type {Map>} */ this.changedParentTypes = new Map() this.encodedStructsLen = 0 @@ -70,7 +71,7 @@ export default class Transaction { } } -export function writeStructToTransaction (transaction, struct) { +export const writeStructToTransaction = (transaction, struct) => { transaction.encodedStructsLen++ struct._toBinary(transaction.encodedStructs) } @@ -78,7 +79,7 @@ export function writeStructToTransaction (transaction, struct) { /** * @private */ -export function transactionTypeChanged (y, type, sub) { +export const transactionTypeChanged = (y, type, sub) => { if (type !== y && !type._deleted && !y._transaction.newTypes.has(type)) { const changedTypes = y._transaction.changedTypes let subs = changedTypes.get(type) diff --git a/src/Util/UndoManager.js b/utils/UndoManager.js similarity index 98% rename from src/Util/UndoManager.js rename to utils/UndoManager.js index 93bba58a..4eae011c 100644 --- a/src/Util/UndoManager.js +++ b/utils/UndoManager.js @@ -1,5 +1,6 @@ + import * as ID from './ID.js' -import isParentOf from './isParentOf.js' +import { isParentOf } from './isParentOf.js' class ReverseOperation { constructor (y, transaction, bindingInfos) { @@ -86,7 +87,7 @@ function applyReverseOperation (y, scope, reverseBuffer) { * Saves a history of locally applied operations. The UndoManager handles the * undoing and redoing of locally created changes. */ -export default class UndoManager { +export class UndoManager { /** * @param {YType} scope The scope on which to listen for changes. * @param {Object} options Optionally provided configuration. diff --git a/src/Y.js b/utils/Y.js similarity index 86% rename from src/Y.js rename to utils/Y.js index 7debe579..732a24a9 100644 --- a/src/Y.js +++ b/utils/Y.js @@ -1,18 +1,15 @@ -import DeleteStore from './Store/DeleteStore.js' -import OperationStore from './Store/OperationStore.js' -import StateStore from './Store/StateStore.js' -import { generateRandomUint32 } from './Util/generateRandomUint32.js' -import { createRootID } from './Util/ID.js' -import NamedEventHandler from '../lib/NamedEventHandler.js' -import Transaction from './Util/Transaction.js' +import { DeleteStore } from './DeleteStore.js' +import { OperationStore } from './OperationStore.js' +import { StateStore } from './StateStore.js' +import { generateRandomUint32 } from './generateRandomUint32.js' +import { createRootID } from './ID.js' +import { NamedEventHandler } from '../lib/NamedEventHandler.js' +import { Transaction } from './Transaction.js' import * as encoding from '../lib/encoding.js' -import * as message from './protocols/syncProtocol.js' -import { integrateRemoteStructs } from './Util/integrateRemoteStructs.js' - -/** - * @typedef {import('./Struct/Type.js').default} YType - * @typedef {import('../lib/decoding.js').Decoder} Decoder - */ +import * as message from '../protocols/syncProtocol.js' +import { integrateRemoteStructs } from './integrateRemoteStructs.js' +import { Type } from '../structs/Type.js' // eslint-disable-line +import { Decoder } from '../lib/decoding.js' // eslint-disable-line /** * Anything that can be encoded with `JSON.stringify` and can be decoded with @@ -32,7 +29,7 @@ import { integrateRemoteStructs } from './Util/integrateRemoteStructs.js' * @param {string} room Users in the same room share the same content * @param {Object} conf configuration */ -export default class Y extends NamedEventHandler { +export class Y extends NamedEventHandler { constructor (room, conf = {}) { super() this.gcEnabled = conf.gc || false @@ -65,7 +62,7 @@ export default class Y extends NamedEventHandler { * @param {Decoder} decoder The BinaryDecoder to read from. */ importModel (decoder) { - this.transact(function () { + this.transact(() => { integrateRemoteStructs(decoder, this) message.readDeleteSet(decoder, this) }) @@ -111,12 +108,12 @@ export default class Y extends NamedEventHandler { const transaction = this._transaction this._transaction = null // emit change events on changed types - transaction.changedTypes.forEach(function (subs, type) { + transaction.changedTypes.forEach((subs, type) => { if (!type._deleted) { type._callObserver(transaction, subs, remote) } }) - transaction.changedParentTypes.forEach(function (events, type) { + transaction.changedParentTypes.forEach((events, type) => { if (!type._deleted) { events = events .filter(event => @@ -180,7 +177,7 @@ export default class Y extends NamedEventHandler { * * @param {String} name * @param {Function} TypeConstructor The constructor of the type definition - * @returns {YType} The created type. Constructed with TypeConstructor + * @returns {Type} The created type. Constructed with TypeConstructor */ define (name, TypeConstructor) { let id = createRootID(name, TypeConstructor) diff --git a/src/Util/YEvent.js b/utils/YEvent.js similarity index 65% rename from src/Util/YEvent.js rename to utils/YEvent.js index 92a1939e..78bccb26 100644 --- a/src/Util/YEvent.js +++ b/utils/YEvent.js @@ -1,25 +1,25 @@ /** - * @typedef {import("../Y.js").default} Y - * @typedef {import("../Struct/Type.js").default} YType - * @typedef {import("../Struct/Item.js").default} Item + * @module utils */ +import { Type } from '../structs/Type.js' // eslint-disable-line + /** * YEvent describes the changes on a YType. */ -export default class YEvent { +export class YEvent { /** - * @param {YType} target The changed type. + * @param {Type} target The changed type. */ constructor (target) { /** * The type on which this event was created on. - * @type {YType} + * @type {Type} */ this.target = target /** * The current target on which the observe callback is called. - * @type {YType} + * @type {Type} */ this.currentTarget = target } @@ -30,7 +30,7 @@ export default class YEvent { * The following property holds: * @example * let type = y - * event.path.forEach(function (dir) { + * event.path.forEach(dir => { * type = type.get(dir) * }) * type === event.target // => true diff --git a/src/Util/defragmentItemContent.js b/utils/defragmentItemContent.js similarity index 87% rename from src/Util/defragmentItemContent.js rename to utils/defragmentItemContent.js index 537098b6..19c57b84 100644 --- a/src/Util/defragmentItemContent.js +++ b/utils/defragmentItemContent.js @@ -1,7 +1,11 @@ -import * as ID from '../Util/ID.js' -import ItemJSON from '../Struct/ItemJSON.js' -import ItemString from '../Struct/ItemString.js' +/** + * @module utils + */ + +import * as ID from '../utils/ID.js' +import { ItemJSON } from '../structs/ItemJSON.js' +import { ItemString } from '../structs/ItemString.js' /** * Try to merge all items in os with their successors. @@ -16,7 +20,7 @@ import ItemString from '../Struct/ItemString.js' * This is why all deletions must be performed after the traversal. * */ -export function defragmentItemContent (y) { +export const defragmentItemContent = y => { const os = y.os if (os.length < 2) { return diff --git a/src/Util/generateRandomUint32.js b/utils/generateRandomUint32.js similarity index 85% rename from src/Util/generateRandomUint32.js rename to utils/generateRandomUint32.js index 0342aaf7..8d007c15 100644 --- a/src/Util/generateRandomUint32.js +++ b/utils/generateRandomUint32.js @@ -1,6 +1,10 @@ +/** + * @module utils + */ + /* global crypto */ -export function generateRandomUint32 () { +export const generateRandomUint32 = () => { if (typeof crypto !== 'undefined' && crypto.getRandomValues != null) { // browser let arr = new Uint32Array(1) diff --git a/src/Util/integrateRemoteStructs.js b/utils/integrateRemoteStructs.js similarity index 90% rename from src/Util/integrateRemoteStructs.js rename to utils/integrateRemoteStructs.js index d9904836..265cad2f 100644 --- a/src/Util/integrateRemoteStructs.js +++ b/utils/integrateRemoteStructs.js @@ -1,12 +1,13 @@ -import { getStruct } from '../Util/structReferences.js' -import * as decoding from '../../lib/decoding.js' -import GC from '../Struct/GC.js' - /** - * @typedef {import('../index').Y} Y - * @typedef {import('../Struct/Item.js').default} YItem + * @module utils */ +import { getStruct } from '../utils/structReferences.js' +import * as decoding from '../lib/decoding.js' +import { GC } from '../structs/GC.js' +import { Y } from '../utils/Y.js' // eslint-disable-line +import { Item } from '../structs/Item.js' // eslint-disable-line + class MissingEntry { constructor (decoder, missing, struct) { this.decoder = decoder @@ -21,7 +22,7 @@ class MissingEntry { * When a remote struct is integrated, other structs might be ready to ready to * integrate. * @param {Y} y - * @param {YItem} struct + * @param {Item} struct */ function _integrateRemoteStructHelper (y, struct) { const id = struct._id @@ -74,7 +75,7 @@ function _integrateRemoteStructHelper (y, struct) { * @param {decoding.Decoder} decoder * @param {Y} y */ -export function integrateRemoteStructs (decoder, y) { +export const integrateRemoteStructs = (decoder, y) => { const len = decoding.readUint32(decoder) for (let i = 0; i < len; i++) { let reference = decoding.readVarUint(decoder) @@ -113,7 +114,7 @@ export function integrateRemoteStructs (decoder, y) { * @param {decoding.Decoder} decoder * @param {Y} y */ -export function integrateRemoteStruct (decoder, y) { +export const integrateRemoteStruct = (decoder, y) => { let reference = decoding.readVarUint(decoder) let Constr = getStruct(reference) let struct = new Constr() diff --git a/src/Util/isParentOf.js b/utils/isParentOf.js similarity index 54% rename from src/Util/isParentOf.js rename to utils/isParentOf.js index c2eee374..d4833209 100644 --- a/src/Util/isParentOf.js +++ b/utils/isParentOf.js @@ -1,19 +1,20 @@ - /** - * @typedef {import('../Struct/Type.js').default} YType - * @typedef {import('../Y.js').default} Y + * @module utils */ +import { Y } from '../utils/Y.js' // eslint-disable-line +import { Type } from '../structs/Type.js' // eslint-disable-line + /** * Check if `parent` is a parent of `child`. * - * @param {YType | Y} parent - * @param {YType | Y} child + * @param {Type | Y} parent + * @param {Type | Y} child * @return {Boolean} Whether `parent` is a parent of `child`. * * @public */ -export default function isParentOf (parent, child) { +export const isParentOf = (parent, child) => { child = child._parent while (child !== null) { if (child === parent) { diff --git a/src/Util/relativePosition.js b/utils/relativePosition.js similarity index 95% rename from src/Util/relativePosition.js rename to utils/relativePosition.js index e11720bf..8f15b2e1 100644 --- a/src/Util/relativePosition.js +++ b/utils/relativePosition.js @@ -1,5 +1,9 @@ +/** + * @module utils + */ + import * as ID from './ID.js' -import GC from '../Struct/GC.js' +import { GC } from '../structs/GC.js' // TODO: Implement function to describe ranges @@ -38,7 +42,7 @@ import GC from '../Struct/GC.js' * @param {YType} type The base type (e.g. YText or YArray). * @param {Integer} offset The absolute position. */ -export function getRelativePosition (type, offset) { +export const getRelativePosition = (type, offset) => { // TODO: rename to createRelativePosition let t = type._start while (t !== null) { @@ -67,7 +71,7 @@ export function getRelativePosition (type, offset) { * @return {AbsolutePosition} The absolute position in the Yjs model * (type + offset). */ -export function fromRelativePosition (y, rpos) { +export const fromRelativePosition = (y, rpos) => { if (rpos[0] === 'endof') { let id if (rpos[3] === null) { diff --git a/src/Util/structReferences.js b/utils/structReferences.js similarity index 70% rename from src/Util/structReferences.js rename to utils/structReferences.js index 4d1f40a6..fce9453a 100644 --- a/src/Util/structReferences.js +++ b/utils/structReferences.js @@ -1,3 +1,7 @@ +/** + * @module utils + */ + const structs = new Map() const references = new Map() @@ -10,7 +14,7 @@ const references = new Map() * * @public */ -export function registerStruct (reference, structConstructor) { +export const registerStruct = (reference, structConstructor) => { structs.set(reference, structConstructor) references.set(structConstructor, reference) } @@ -18,13 +22,13 @@ export function registerStruct (reference, structConstructor) { /** * @private */ -export function getStruct (reference) { +export const getStruct = (reference) => { return structs.get(reference) } /** * @private */ -export function getStructReference (typeConstructor) { +export const getStructReference = (typeConstructor) => { return references.get(typeConstructor) }