large scale refactoring

This commit is contained in:
Kevin Jahns 2018-11-25 03:17:00 +01:00
parent ade3e1949d
commit 9c0da271eb
154 changed files with 1769 additions and 1199 deletions

View File

@ -1,6 +1,7 @@
{
"source": "./src",
"source": ".",
"destination": "./docs",
"excludes": ["build", "node_modules", "tests-lib", "test"],
"plugins": [{
"name": "esdoc-standard-plugin",
"option": {

51
.jsdoc.json Normal file
View File

@ -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"
}
}

View File

@ -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
}
}

1
bindings/dom.js Normal file
View File

@ -0,0 +1 @@
export * from './dom/DomBinding.js'

View File

@ -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<string, string>} 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}
*/

View File

@ -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

View File

@ -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<string, string>} 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}
*/

View File

@ -1,12 +1,11 @@
import isParentOf from '../../src/Util/isParentOf.js'
/**
* @callback DomFilter
* @param {string} nodeName
* @param {Map<string, string>} 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) => {

View File

@ -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)
}

View File

@ -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 => {

View File

@ -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<YXmlElement>} 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

View File

@ -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<Y.Text | Y.XmlElement, PModel.Node>} 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

View File

@ -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
}
}

View File

@ -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
}
}

1
examples/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build

11
examples/examples.json Normal file
View File

@ -0,0 +1,11 @@
{
"prosemirror": {
"title": "Prosemirror Binding"
},
"textarea": {
"title": "Textarea Binding"
},
"quill": {
"title": "Quill Binding"
}
}

75
examples/prosemirror.html Normal file
View File

@ -0,0 +1,75 @@
<!DOCTYPE html>
<html>
<head>
<title>Yjs Prosemirror Example</title>
<link rel=stylesheet href="https://prosemirror.net/css/editor.css">
<style>
placeholder {
display: inline;
border: 1px solid #ccc;
color: #ccc;
}
placeholder:after {
content: "☁";
font-size: 200%;
line-height: 0.1;
font-weight: bold;
}
.ProseMirror img { max-width: 100px }
.ProseMirror-yjs-cursor {
position: absolute;
border-left: black;
border-left-style: solid;
border-left-width: 2px;
border-color: orange;
height: 1em;
}
.ProseMirror-yjs-cursor > div {
position: relative;
top: -1.05em;
font-size: 13px;
background-color: rgb(250, 129, 0);
font-family: serif;
font-style: normal;
font-weight: normal;
line-height: normal;
user-select: none;
color: white;
padding-left: 2px;
padding-right: 2px;
}
</style>
</head>
<body>
<div class="code-html">
<div id="editor" style="margin-bottom: 23px"></div>
<div style="display: none" id="content">
<h3>Hello User</h3>
<p>type here ...</p>
</div>
</div>
<script class="code-js" src="./build/prosemirror.js">
import * as Y from 'yjs'
import { prosemirrorPlugin, cursorPlugin } from 'yjs/bindings/prosemirror.js'
import { WebsocketProvider } from 'yjs/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])
})
})
</script>
</body>
</html>

20
examples/prosemirror.js Normal file
View File

@ -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])
})
})

View File

@ -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) }
}
})

View File

@ -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 .
```

View File

@ -1,51 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<script type="module" src="./index.dist.js"></script>
<link rel=stylesheet href="https://prosemirror.net/css/editor.css">
<style>
placeholder {
display: inline;
border: 1px solid #ccc;
color: #ccc;
}
placeholder:after {
content: "☁";
font-size: 200%;
line-height: 0.1;
font-weight: bold;
}
.ProseMirror img { max-width: 100px }
.ProseMirror-yjs-cursor {
position: absolute;
border-left: black;
border-left-style: solid;
border-left-width: 2px;
border-color: orange;
height: 1em;
}
.ProseMirror-yjs-cursor > div {
position: relative;
top: -1.05em;
font-size: 13px;
background-color: rgb(250, 129, 0);
font-family: serif;
font-style: normal;
font-weight: normal;
line-height: normal;
user-select: none;
color: white;
padding-left: 2px;
padding-right: 2px;
}
</style>
</head>
<body>
<div id="editor" style="margin-bottom: 23px"></div>
<div style="display: none" id="content">
<h3>Hello User</h3>
<p>type something ...</p>
</div>
<div>Insert image: <input type=file id=image-upload></div>
</body>
</html>

View File

@ -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()
})

View File

@ -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()
]
}

54
examples/quill.html Normal file
View File

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<title>Yjs Prosemirror Example</title>
<link rel=stylesheet href="https://prosemirror.net/css/editor.css">
<style>
placeholder {
display: inline;
border: 1px solid #ccc;
color: #ccc;
}
placeholder:after {
content: "☁";
font-size: 200%;
line-height: 0.1;
font-weight: bold;
}
.ProseMirror img { max-width: 100px }
.ProseMirror-yjs-cursor {
position: absolute;
border-left: black;
border-left-style: solid;
border-left-width: 2px;
border-color: orange;
height: 1em;
}
.ProseMirror-yjs-cursor > div {
position: relative;
top: -1.05em;
font-size: 13px;
background-color: rgb(250, 129, 0);
font-family: serif;
font-style: normal;
font-weight: normal;
line-height: normal;
user-select: none;
color: white;
padding-left: 2px;
padding-right: 2px;
}
</style>
</head>
<body>
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
<div class="code-html">
<div id="quill-container">
<div id="quill">
</div>
</div>
</div>
<script class="code-js" src="./build/quill.js">
</script>
</body>
</html>

27
examples/quill.js Normal file
View File

@ -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)

29
examples/style.css Normal file
View File

@ -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;
}

64
examples/textarea.html Normal file
View File

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html>
<head>
<title>Yjs Prosemirror Example</title>
<link rel=stylesheet href="https://prosemirror.net/css/editor.css">
<style>
placeholder {
display: inline;
border: 1px solid #ccc;
color: #ccc;
}
placeholder:after {
content: "☁";
font-size: 200%;
line-height: 0.1;
font-weight: bold;
}
.ProseMirror img { max-width: 100px }
.ProseMirror-yjs-cursor {
position: absolute;
border-left: black;
border-left-style: solid;
border-left-width: 2px;
border-color: orange;
height: 1em;
}
.ProseMirror-yjs-cursor > div {
position: relative;
top: -1.05em;
font-size: 13px;
background-color: rgb(250, 129, 0);
font-family: serif;
font-style: normal;
font-weight: normal;
line-height: normal;
user-select: none;
color: white;
padding-left: 2px;
padding-right: 2px;
}
</style>
</head>
<body>
<div class="code-html">
<textarea style="width:80%;" rows=40 autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
</div>
<script class="code-js" src="./build/textarea.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 TextareaBinding(type, textarea)
window.textareaExample = {
provider, ydocument, type, textarea, binding
}
</script>
</body>
</html>

View File

@ -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

View File

@ -1,7 +0,0 @@
<!DOCTYPE html>
<html>
<body>
<textarea style="width:80%;" rows=40 autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
<script type="module" src="./index.js"></script>
</body>
</html>

View File

@ -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 = ''

View File

@ -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 }

View File

@ -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)

52
index.js Normal file
View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -1,3 +1,6 @@
/**
* @module binary
*/
export const BITS32 = 0xFFFFFFFF
export const BITS21 = (1 << 21) - 1

View File

@ -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}
*/

View File

@ -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]) {

View File

@ -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
*/

View File

@ -1,3 +1,7 @@
/**
* @module globals
*/
/* eslint-env browser */
export const Uint8Array_ = Uint8Array

View File

@ -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) => {
/**

View File

@ -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'

View File

@ -1,3 +1,6 @@
/**
* @module logging
*/
import * as globals from './globals.js'

View File

@ -1,2 +1,4 @@
/**
* @module math
*/
export const floor = Math.floor

View File

@ -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.
* })
* })
*

View File

@ -1,2 +1,6 @@
/**
* @module number
*/
export const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER
export const MIN_SAFE_INTEGER = Number.MIN_SAFE_INTEGER

View File

@ -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.
*/

View File

@ -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

View File

@ -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

View File

@ -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.
*/

View File

@ -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

View File

@ -1,3 +1,7 @@
/**
* @module prng
*/
/**
*TODO: enable tests
import * as rt from '../rich-text/formatters.mjs'

3
lib/random.js Normal file
View File

@ -0,0 +1,3 @@
/**
* @module random
*/

View File

@ -1,2 +1,6 @@
/**
* @module string
*/
export const fromCharCode = String.fromCharCode
export const fromCodePoint = String.fromCodePoint

View File

@ -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;', '')

376
package-lock.json generated
View File

@ -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"
}
}
}
}

View File

@ -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"

View File

@ -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 {
})
}
}
*/

View File

@ -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))
}
}
*/

View File

@ -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)
}
*/

View File

@ -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)) {

View File

@ -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<number, number>} 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) => {

5
provider/websocket.js Normal file
View File

@ -0,0 +1,5 @@
/**
* @module provider/websocket
*/
export * from './websocket/WebSocketProvider.js'

View File

@ -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] === '/') {

View File

@ -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()

View File

@ -1,3 +1,6 @@
/**
* @module provider/ydb
*/
import * as globals from './globals.js'

View File

@ -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'

View File

@ -1,3 +1,7 @@
/**
* @module provider/ydb
*/
/* eslint-env browser */
import * as test from './test.js'

View File

@ -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)

View File

@ -1,3 +1,7 @@
/**
* @module provider/ydb
*/
/* eslint-env browser */
/**

Some files were not shown because too many files have changed in this diff Show More