added prosemirror binding
This commit is contained in:
parent
e8060de914
commit
32b8fac37f
4
.gitignore
vendored
4
.gitignore
vendored
@ -2,7 +2,7 @@ node_modules
|
||||
bower_components
|
||||
docs
|
||||
/y.*
|
||||
/examples/yjs-dist.js*
|
||||
/examples/*/index.dist.*
|
||||
.vscode
|
||||
.yjsPersisted
|
||||
build
|
||||
build
|
||||
|
67
bindings/BindMapping.js
Normal file
67
bindings/BindMapping.js
Normal file
@ -0,0 +1,67 @@
|
||||
|
||||
/**
|
||||
* Type that maps from Yjs type to Target type.
|
||||
* Used to implement double bindings.
|
||||
*
|
||||
* @template Y
|
||||
* @template T
|
||||
*/
|
||||
export default class BindMapping {
|
||||
/**
|
||||
*/
|
||||
constructor () {
|
||||
/**
|
||||
* @type Map<Y, T>
|
||||
*/
|
||||
this.yt = new Map()
|
||||
/**
|
||||
* @type Map<T, Y>
|
||||
*/
|
||||
this.ty = new Map()
|
||||
}
|
||||
/**
|
||||
* Map y to t. Removes all existing bindings from y and t
|
||||
* @param {Y} y
|
||||
* @param {T} t
|
||||
*/
|
||||
bind (y, t) {
|
||||
const existingT = this.yt.get(y)
|
||||
if (existingT !== undefined) {
|
||||
this.ty.delete(existingT)
|
||||
}
|
||||
const existingY = this.ty.get(t)
|
||||
if (existingY !== undefined) {
|
||||
this.yt.delete(existingY)
|
||||
}
|
||||
this.yt.set(y, t)
|
||||
this.ty.set(t, y)
|
||||
}
|
||||
/**
|
||||
* @param {Y} y
|
||||
* @return {boolean}
|
||||
*/
|
||||
hasY (y) {
|
||||
return this.yt.has(y)
|
||||
}
|
||||
/**
|
||||
* @param {T} t
|
||||
* @return {boolean}
|
||||
*/
|
||||
hasT (t) {
|
||||
return this.ty.has(t)
|
||||
}
|
||||
/**
|
||||
* @param {Y} y
|
||||
* @return {T}
|
||||
*/
|
||||
getY (y) {
|
||||
return this.yt.get(y)
|
||||
}
|
||||
/**
|
||||
* @param {T} t
|
||||
* @return {Y}
|
||||
*/
|
||||
getT (t) {
|
||||
return this.ty.get(t)
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/* eslint-env browser */
|
||||
import YXmlText from '../../Types/YXml/YXmlText.js'
|
||||
import YXmlHook from '../../Types/YXml/YXmlHook.js'
|
||||
import YXmlElement from '../../Types/YXml/YXmlElement.js'
|
||||
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 { createAssociation, domsToTypes } from './util.js'
|
||||
import { filterDomAttributes, defaultFilter } from './filter.js'
|
||||
|
@ -1,4 +1,4 @@
|
||||
import isParentOf from '../../Util/isParentOf.js'
|
||||
import isParentOf from '../../src/Util/isParentOf.js'
|
||||
|
||||
/**
|
||||
* @callback DomFilter
|
@ -1,6 +1,6 @@
|
||||
/* globals getSelection */
|
||||
|
||||
import { getRelativePosition } from '../../Util/relativePosition.js'
|
||||
import { getRelativePosition } from '../../src/Util/relativePosition.js'
|
||||
|
||||
let relativeSelection = null
|
||||
|
@ -1,8 +1,8 @@
|
||||
/* eslint-env browser */
|
||||
/* global getSelection */
|
||||
|
||||
import YXmlText from '../../Types/YXml/YXmlText.js'
|
||||
import YXmlHook from '../../Types/YXml/YXmlHook.js'
|
||||
import YXmlText from '../../src/Types/YXml/YXmlText.js'
|
||||
import YXmlHook from '../../src/Types/YXml/YXmlHook.js'
|
||||
import { removeDomChildrenUntilElementFound } from './util.js'
|
||||
|
||||
function findScrollReference (scrollingElement) {
|
@ -2,9 +2,9 @@
|
||||
import domToType from './domToType.js'
|
||||
|
||||
/**
|
||||
* @typedef {import('../../Types/YXml/YXmlText.js').default} YXmlText
|
||||
* @typedef {import('../../Types/YXml/YXmlElement.js').default} YXmlElement
|
||||
* @typedef {import('../../Types/YXml/YXmlHook.js').default} YXmlHook
|
||||
* @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
|
||||
*/
|
||||
|
190
bindings/ProsemirrorBinding/ProsemirrorBinding.js
Normal file
190
bindings/ProsemirrorBinding/ProsemirrorBinding.js
Normal file
@ -0,0 +1,190 @@
|
||||
import BindMapping from '../BindMapping.js'
|
||||
import * as PModel from 'prosemirror-model'
|
||||
import * as Y from '../../src/index.js'
|
||||
import { createMutex } from '../../lib/mutex.js'
|
||||
|
||||
/**
|
||||
* @typedef {import('prosemirror-view').EditorView} EditorView
|
||||
* @typedef {import('prosemirror-state').EditorState} EditorState
|
||||
* @typedef {BindMapping<Y.Text | Y.XmlElement, PModel.Node>} ProsemirrorMapping
|
||||
*/
|
||||
|
||||
export default class ProsemirrorBinding {
|
||||
/**
|
||||
* @param {Y.XmlFragment} yDomFragment The bind source
|
||||
* @param {EditorView} prosemirror The target binding
|
||||
*/
|
||||
constructor (yDomFragment, prosemirror) {
|
||||
this.type = yDomFragment
|
||||
this.prosemirror = prosemirror
|
||||
const mux = createMutex()
|
||||
this.mux = mux
|
||||
/**
|
||||
* @type {ProsemirrorMapping}
|
||||
*/
|
||||
const mapping = new BindMapping()
|
||||
this.mapping = mapping
|
||||
const oldDispatch = prosemirror.props.dispatchTransaction || null
|
||||
/**
|
||||
* @type {any}
|
||||
*/
|
||||
const updatedProps = {
|
||||
dispatchTransaction: function (tr) {
|
||||
// TODO: remove
|
||||
const time = performance.now()
|
||||
const newState = prosemirror.state.apply(tr)
|
||||
mux(() => {
|
||||
updateYFragment(yDomFragment, newState, mapping)
|
||||
})
|
||||
if (oldDispatch !== null) {
|
||||
oldDispatch.call(this, tr)
|
||||
} else {
|
||||
prosemirror.updateState(newState)
|
||||
}
|
||||
console.info('time for Yjs update: ', performance.now() - time)
|
||||
}
|
||||
}
|
||||
prosemirror.setProps(updatedProps)
|
||||
|
||||
yDomFragment.observeDeep(events => {
|
||||
if (events.length === 0) {
|
||||
return
|
||||
}
|
||||
mux(() => {
|
||||
events.forEach(event => {
|
||||
// recompute node for each parent
|
||||
// except main node, compute main node in the end
|
||||
let target = event.target
|
||||
if (target !== yDomFragment) {
|
||||
do {
|
||||
if (target.constructor === Y.XmlElement) {
|
||||
createNodeFromYElement(target, prosemirror.state.schema, mapping)
|
||||
}
|
||||
target = target._parent
|
||||
} while (target._parent !== yDomFragment)
|
||||
}
|
||||
})
|
||||
const fragmentContent = yDomFragment.toArray().map(t => createNodeIfNotExists(t, prosemirror.state.schema, mapping))
|
||||
const tr = prosemirror.state.tr.replace(0, prosemirror.state.doc.content.size, new PModel.Slice(new PModel.Fragment(fragmentContent), 0, 0))
|
||||
const newState = prosemirror.updateState(prosemirror.state.apply(tr))
|
||||
console.log('state updated', newState, tr)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Y.XmlElement} el
|
||||
* @param {PModel.Schema} schema
|
||||
* @param {ProsemirrorMapping} mapping
|
||||
* @return {PModel.Node}
|
||||
*/
|
||||
export const createNodeIfNotExists = (el, schema, mapping) => {
|
||||
const node = mapping.getY(el)
|
||||
if (node === undefined) {
|
||||
return createNodeFromYElement(el, schema, mapping)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Y.XmlElement} el
|
||||
* @param {PModel.Schema} schema
|
||||
* @param {ProsemirrorMapping} mapping
|
||||
* @return {PModel.Node}
|
||||
*/
|
||||
export const createNodeFromYElement = (el, schema, mapping) => {
|
||||
const children = []
|
||||
el.toArray().forEach(type => {
|
||||
if (type.constructor === Y.XmlElement) {
|
||||
children.push(createNodeIfNotExists(type, schema, mapping))
|
||||
} else {
|
||||
children.concat(createTextNodesFromYText(type, schema, mapping)).forEach(textchild => children.push(textchild))
|
||||
}
|
||||
})
|
||||
const node = schema.node(el.nodeName.toLowerCase(), el.getAttributes(), el.toArray().map(t => createNodeIfNotExists(t, schema, mapping)))
|
||||
mapping.bind(el, node)
|
||||
return node
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Y.Text} text
|
||||
* @param {PModel.Schema} schema
|
||||
* @param {ProsemirrorMapping} mapping
|
||||
* @return {Array<PModel.Node>}
|
||||
*/
|
||||
export const createTextNodesFromYText = (text, schema, mapping) => {
|
||||
const nodes = []
|
||||
const deltas = text.toDelta()
|
||||
for (let i = 0; i < deltas.length; i++) {
|
||||
const delta = deltas[i]
|
||||
const marks = []
|
||||
for (let markName in delta.attributes) {
|
||||
marks.push(schema.mark(markName, delta.attributes[markName]))
|
||||
}
|
||||
nodes.push(schema.text(delta.insert, marks))
|
||||
}
|
||||
if (nodes.length > 0) {
|
||||
mapping.bind(text, nodes[0]) // only map to first child, all following children are also considered bound to this type
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PModel.Node} node
|
||||
* @param {ProsemirrorMapping} mapping
|
||||
* @return {Y.XmlElement | Y.Text}
|
||||
*/
|
||||
export const createTypeFromNode = (node, mapping) => {
|
||||
let type
|
||||
if (node.isText) {
|
||||
type = new Y.Text()
|
||||
const attrs = {}
|
||||
node.marks.forEach(mark => { attrs[mark.type.name] = mark.attrs })
|
||||
type.insert(0, node.text, attrs)
|
||||
} else {
|
||||
type = new Y.XmlElement(node.type.name)
|
||||
for (let key in node.attrs) {
|
||||
type.setAttribute(key, node.attrs[key])
|
||||
}
|
||||
type.insert(0, node.content.content.map(node => createTypeFromNode(node, mapping)))
|
||||
}
|
||||
mapping.bind(type, node)
|
||||
return type
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Y.XmlFragment} yDomFragment
|
||||
* @param {EditorState} state
|
||||
* @param {BindMapping} mapping
|
||||
*/
|
||||
const updateYFragment = (yDomFragment, state, mapping) => {
|
||||
const pChildCnt = state.doc.content.childCount
|
||||
const yChildren = yDomFragment.toArray()
|
||||
const yChildCnt = yChildren.length
|
||||
const minCnt = pChildCnt < yChildCnt ? pChildCnt : yChildCnt
|
||||
let left = 0
|
||||
let right = 0
|
||||
// find number of matching elements from left
|
||||
for (;left < minCnt; left++) {
|
||||
if (state.doc.content.child(left) !== mapping.getY(yChildren[left])) {
|
||||
break
|
||||
}
|
||||
}
|
||||
// find number of matching elements from right
|
||||
for (;right < minCnt; right++) {
|
||||
if (state.doc.content.child(pChildCnt - right - 1) !== mapping.getY(yChildren[yChildCnt - right - 1])) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (left + right > pChildCnt) {
|
||||
// nothing changed
|
||||
return
|
||||
}
|
||||
yDomFragment._y.transact(() => {
|
||||
// now update y to match editor state
|
||||
yDomFragment.delete(left, yChildCnt - left - right)
|
||||
yDomFragment.insert(left, state.doc.content.content.slice(left, pChildCnt - right).map(node => createTypeFromNode(node, mapping)))
|
||||
})
|
||||
console.log(yDomFragment.toDomString())
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
|
||||
import Binding from '../Binding.js'
|
||||
import simpleDiff from '../../../lib/simpleDiff.js'
|
||||
import { getRelativePosition, fromRelativePosition } from '../../Util/relativePosition.js'
|
||||
import simpleDiff from '../../lib/simpleDiff.js'
|
||||
import { getRelativePosition, fromRelativePosition } from '../../src/Util/relativePosition.js'
|
||||
|
||||
function typeObserver () {
|
||||
this._mutualExclude(() => {
|
@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "yjs-examples",
|
||||
"version": "0.0.0",
|
||||
"homepage": "y-js.org",
|
||||
"authors": [
|
||||
"Kevin Jahns <kevin.jahns@rwth-aachen.de>"
|
||||
],
|
||||
"description": "Examples for Yjs",
|
||||
"license": "MIT",
|
||||
"ignore": [],
|
||||
"dependencies": {
|
||||
"quill": "^1.0.0-rc.2",
|
||||
"ace": "~1.2.3",
|
||||
"ace-builds": "~1.2.3",
|
||||
"jquery": "~2.2.2",
|
||||
"d3": "^3.5.16",
|
||||
"codemirror": "^5.25.0"
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
|
||||
import YWebsocketsConnector from '../../src/Connectors/WebsocketsConnector/WebsocketsConnector.js'
|
||||
import Y from '../../src/Y.js'
|
||||
import DomBinding from '../../src/Bindings/DomBinding/DomBinding.js'
|
||||
import DomBinding from '../../bindings/DomBinding/DomBinding.js'
|
||||
import UndoManager from '../../src/Util/UndoManager.js'
|
||||
import YXmlFragment from '../../src/Types/YXml/YXmlFragment.js'
|
||||
import YXmlText from '../../src/Types/YXml/YXmlText.js'
|
||||
|
@ -1,55 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<style>
|
||||
.wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-gap: 7px;
|
||||
}
|
||||
.one {
|
||||
grid-column: 1 ;
|
||||
}
|
||||
.two {
|
||||
grid-column: 2;
|
||||
}
|
||||
.three {
|
||||
grid-column: 3;
|
||||
}
|
||||
textarea {
|
||||
width: calc(100% - 10px)
|
||||
}
|
||||
.editor-container {
|
||||
background-color: #4caf50;
|
||||
padding: 4px 5px 10px 5px;
|
||||
border-radius: 11px;
|
||||
}
|
||||
.editor-container[disconnected] {
|
||||
background-color: red;
|
||||
}
|
||||
.disconnected-info {
|
||||
display: none;
|
||||
}
|
||||
.editor-container[disconnected] .disconnected-info {
|
||||
display: inline;
|
||||
}
|
||||
</style>
|
||||
<div class="wrapper">
|
||||
<div id="container1" class="one editor-container">
|
||||
<h1>Server 1 <span class="disconnected-info">(disconnected)</span></h1>
|
||||
<textarea id="textarea1" rows=40 autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
|
||||
</div>
|
||||
<div id="container2" class="two editor-container">
|
||||
<h1>Server 2 <span class="disconnected-info">(disconnected)</span></h1>
|
||||
<textarea id="textarea2" rows=40 autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
|
||||
</div>
|
||||
<div id="container3" class="three editor-container">
|
||||
<h1>Server 3 <span class="disconnected-info">(disconnected)</span></h1>
|
||||
<textarea id="textarea3" rows=40 autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<script src="../../y.js"></script>
|
||||
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
|
||||
<script src="./index.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,38 +0,0 @@
|
||||
/* global Y */
|
||||
|
||||
function bindYjsInstance (y, suffix) {
|
||||
y.define('textarea', Y.Text).bind(document.getElementById('textarea' + suffix))
|
||||
y.connector.socket.on('connection', function () {
|
||||
document.getElementById('container' + suffix).removeAttribute('disconnected')
|
||||
})
|
||||
y.connector.socket.on('disconnect', function () {
|
||||
document.getElementById('container' + suffix).setAttribute('disconnected', true)
|
||||
})
|
||||
}
|
||||
|
||||
let y1 = new Y('infinite-example', {
|
||||
connector: {
|
||||
name: 'websockets-client',
|
||||
url: 'http://127.0.0.1:1234'
|
||||
}
|
||||
})
|
||||
window.y1 = y1
|
||||
bindYjsInstance(y1, '1')
|
||||
|
||||
let y2 = new Y('infinite-example', {
|
||||
connector: {
|
||||
name: 'websockets-client',
|
||||
url: 'http://127.0.0.1:1234'
|
||||
}
|
||||
})
|
||||
window.y2 = y2
|
||||
bindYjsInstance(y2, '2')
|
||||
|
||||
let y3 = new Y('infinite-example', {
|
||||
connector: {
|
||||
name: 'websockets-client',
|
||||
url: 'http://127.0.0.1:1234'
|
||||
}
|
||||
})
|
||||
window.y3 = y3
|
||||
bindYjsInstance(y1, '3')
|
@ -3,7 +3,7 @@
|
||||
import { createYdbClient } from '../../YdbClient/index.js'
|
||||
import Y from '../../src/Y.dist.js'
|
||||
import * as ydb from '../../YdbClient/YdbClient.js'
|
||||
import DomBinding from '../../src/Bindings/DomBinding/DomBinding.js'
|
||||
import DomBinding from '../../bindings/DomBinding/DomBinding.js'
|
||||
|
||||
const uuidv4 = () => 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
||||
const r = Math.random() * 16 | 0
|
||||
|
@ -1,23 +0,0 @@
|
||||
{
|
||||
"name": "examples",
|
||||
"version": "0.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"dist": "rollup -c",
|
||||
"watch": "rollup -cw"
|
||||
},
|
||||
"author": "Kevin Jahns",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"monaco-editor": "^0.8.3",
|
||||
"rollup": "^0.52.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"standard": "^10.0.2"
|
||||
},
|
||||
"standard": {
|
||||
"ignore": [
|
||||
"bower_components"
|
||||
]
|
||||
}
|
||||
}
|
29
examples/prosemirror/index.html
Normal file
29
examples/prosemirror/index.html
Normal file
@ -0,0 +1,29 @@
|
||||
<!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 }
|
||||
</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>
|
112
examples/prosemirror/index.js
Normal file
112
examples/prosemirror/index.js
Normal file
@ -0,0 +1,112 @@
|
||||
/* eslint-env browser */
|
||||
import * as Y from '../../src/index.js'
|
||||
import ProsemirrorBinding 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 {Plugin} from 'prosemirror-state'
|
||||
import {Decoration, DecorationSet} from 'prosemirror-view'
|
||||
|
||||
let 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) }
|
||||
}
|
||||
})
|
||||
|
||||
function 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
|
||||
}
|
||||
|
||||
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()
|
||||
})
|
||||
|
||||
function 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((accept, fail) => {
|
||||
reader.onload = () => accept(reader.result)
|
||||
reader.onerror = () => fail(reader.error)
|
||||
// Some extra delay to make the asynchronicity visible
|
||||
setTimeout(() => reader.readAsDataURL(file), 1500)
|
||||
})
|
||||
}
|
||||
|
||||
const view = new EditorView(document.querySelector('#editor'), {
|
||||
state: EditorState.create({
|
||||
doc: DOMParser.fromSchema(schema).parse(document.querySelector('#content')),
|
||||
plugins: exampleSetup({schema}).concat(placeholderPlugin)
|
||||
})
|
||||
})
|
||||
|
||||
const provider = new WebsocketProvider('ws://localhost:1234/')
|
||||
const ydocument = provider.get('prosemirror')
|
||||
/**
|
||||
* @type {any}
|
||||
*/
|
||||
const type = ydocument.define('prosemirror', Y.XmlFragment)
|
||||
const prosemirrorBinding = new ProsemirrorBinding(type, view)
|
||||
|
||||
window.view = view
|
||||
window.EditorState = EditorState
|
||||
window.EditorView = EditorView
|
||||
window.Mark = Mark
|
||||
window.Fragment = Fragment
|
||||
window.Node = Node
|
||||
window.Schema = Schema
|
||||
window.Slice = Slice
|
||||
window.prosemirrorBinding = prosemirrorBinding
|
20
examples/prosemirror/rollup.browser.js
Normal file
20
examples/prosemirror/rollup.browser.js
Normal file
@ -0,0 +1,20 @@
|
||||
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()
|
||||
]
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
import nodeResolve from 'rollup-plugin-node-resolve'
|
||||
import commonjs from 'rollup-plugin-commonjs'
|
||||
|
||||
var pkg = require('./package.json')
|
||||
|
||||
export default {
|
||||
input: 'yjs-dist.js',
|
||||
name: 'Y',
|
||||
output: {
|
||||
file: 'yjs-dist.js',
|
||||
format: 'umd'
|
||||
},
|
||||
plugins: [
|
||||
nodeResolve({
|
||||
main: true,
|
||||
module: true,
|
||||
browser: true
|
||||
}),
|
||||
commonjs()
|
||||
],
|
||||
sourcemap: true,
|
||||
banner: `
|
||||
/**
|
||||
* ${pkg.name} - ${pkg.description}
|
||||
* @version v${pkg.version}
|
||||
* @license ${pkg.license}
|
||||
*/
|
||||
`
|
||||
}
|
176
package-lock.json
generated
176
package-lock.json
generated
@ -2012,6 +2012,12 @@
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
|
||||
"dev": true
|
||||
},
|
||||
"crel": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/crel/-/crel-3.1.0.tgz",
|
||||
"integrity": "sha512-VIGY44ERxx8lXVkOEfcB0A49OkjxkQNK+j+fHvoLy7GsGX1KKgAaQ+p9N0YgvQXu+X+ryUWGDeLx/fSI+w7+eg==",
|
||||
"dev": true
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||
@ -5363,6 +5369,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"orderedmap": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-1.0.0.tgz",
|
||||
"integrity": "sha1-2Q/Cuh7QhRkJB9YB3sbmpT+NQbo=",
|
||||
"dev": true
|
||||
},
|
||||
"os-homedir": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
|
||||
@ -5656,6 +5668,158 @@
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"prosemirror-commands": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.0.7.tgz",
|
||||
"integrity": "sha512-IR8yMSdw7XlKuF68tydAak1J9P/lLD5ohsrL7pzoLsJAJAQU7mVPDXtGbQrrm0mesddFjcc1zNo/cJQN3lRYnA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"prosemirror-dropcursor": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.1.1.tgz",
|
||||
"integrity": "sha512-GeUyMO/tOEf8MXrP7Xb7UIMrfK86OGh0fnyBrHfhav4VjY9cw65mNoqHy87CklE5711AhCP5Qzfp8RL/hVKusg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.1.0",
|
||||
"prosemirror-view": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"prosemirror-example-setup": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-example-setup/-/prosemirror-example-setup-1.0.1.tgz",
|
||||
"integrity": "sha512-4NKWpdmm75Zzgq/dIrypRnkBNPx+ONKyoGF42a9g3VIVv0TWglf1CBNxt5kzCgli9xdfut/xE5B42F9DR6BLHw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prosemirror-commands": "^1.0.0",
|
||||
"prosemirror-dropcursor": "^1.0.0",
|
||||
"prosemirror-gapcursor": "^1.0.0",
|
||||
"prosemirror-history": "^1.0.0",
|
||||
"prosemirror-inputrules": "^1.0.0",
|
||||
"prosemirror-keymap": "^1.0.0",
|
||||
"prosemirror-menu": "^1.0.0",
|
||||
"prosemirror-schema-list": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"prosemirror-gapcursor": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.0.3.tgz",
|
||||
"integrity": "sha512-X+hJhr42PcHWiSWL+lI5f/UeOhXCxlBFb8M6O8aG1hssmaRrW7sS2/Fjg5jFV+pTdS1REFkmm1occh01FMdDIQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prosemirror-keymap": "^1.0.0",
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-view": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"prosemirror-history": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.0.3.tgz",
|
||||
"integrity": "sha512-IfFGbhafSx+R3aq7nLJGkXeu2iaUiP8mkU3aRu2uQcIIjU8Fq7RJfuvhIOJ2RNUoSyqF/ANkdTjnZ74F5eHs1Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prosemirror-state": "^1.2.2",
|
||||
"prosemirror-transform": "^1.0.0",
|
||||
"rope-sequence": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"prosemirror-inputrules": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.0.1.tgz",
|
||||
"integrity": "sha512-UHy22NmwxS5WIMQYkzraDttQAF8mpP82FfbJsmKFfx6jwkR/SZa+ZhbkLY0zKQ5fBdJN7euj36JG/B5iAlrpxA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"prosemirror-keymap": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.0.1.tgz",
|
||||
"integrity": "sha512-e79ApE7PXXZMFtPz7WbjycjAFd1NPjgY1MkecVz98tqwlBSggXWXYQnWFk6x7UkmnBYRHHbXHkR/RXmu2wyBJg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"w3c-keyname": "^1.1.8"
|
||||
}
|
||||
},
|
||||
"prosemirror-menu": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.0.5.tgz",
|
||||
"integrity": "sha512-9Vrn7CC191v7FA4QrAkL8W1SrR73V3CRIYCDuk94R8oFVk4VxSFdoKVLHuvGzxZ8b5LCu3DMJfh86YW9uL4RkQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"crel": "^3.0.0",
|
||||
"prosemirror-commands": "^1.0.0",
|
||||
"prosemirror-history": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"prosemirror-model": {
|
||||
"version": "1.6.3",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.6.3.tgz",
|
||||
"integrity": "sha512-iqIml664X9MUVGLz2nzK4xfAofX8+o7gs2mi2/k+pVD0qZ7th1Jm5eG3AsqWoEUIZuWeaOWCKpBl/dPnhIIWew==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"orderedmap": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"prosemirror-schema-basic": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.0.0.tgz",
|
||||
"integrity": "sha512-xTFjtuLZgcRS4MoDbUyI9NSk/k/ACLGKZQcDXH18ctM9BOmP4z5rGZcA014fCF2FnMFOU+lKwusL0JjVrEectQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prosemirror-model": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"prosemirror-schema-list": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.0.1.tgz",
|
||||
"integrity": "sha512-AiLIX6qm6PEeDtMCKZLcSLi55WXo1ls7DnRK+4hSkoi0IIzNdxGsRlecCd3MzEu//DVz3nAEh+zEmslyW+uk8g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-transform": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"prosemirror-state": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.2.2.tgz",
|
||||
"integrity": "sha512-j8aC/kf9BJSCQau485I/9pj39XQoce+TqH5xzekT7WWFARTsRYFLJtiXBcCKakv1VSeev+sC3bJP0pLfz7Ft8g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-transform": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"prosemirror-transform": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.1.3.tgz",
|
||||
"integrity": "sha512-1O6Di5lOL1mp4nuCnQNkHY7l2roIW5y8RH4ZG3hMYmkmDEWzTaFFnxxAAHsE5ipGLBSRcTlP7SsDhYBIdSuLpQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prosemirror-model": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"prosemirror-view": {
|
||||
"version": "1.6.5",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.6.5.tgz",
|
||||
"integrity": "sha512-brg8fExNrmklbLs8VJ7uvmo/Lh93EHErH47alI55hkJ12EF73K+t2+IyrlkJF84tt5wFBJ20LeSxF8HlJHXiYg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prosemirror-model": "^1.1.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"pseudomap": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||
@ -6252,6 +6416,12 @@
|
||||
"require-relative": "0.8.7"
|
||||
}
|
||||
},
|
||||
"rope-sequence": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.2.2.tgz",
|
||||
"integrity": "sha1-ScTlwvVKSOmQsFCSZ3HihxvLMc4=",
|
||||
"dev": true
|
||||
},
|
||||
"run-async": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
|
||||
@ -6950,6 +7120,12 @@
|
||||
"integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==",
|
||||
"dev": true
|
||||
},
|
||||
"w3c-keyname": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-1.1.8.tgz",
|
||||
"integrity": "sha512-2HAdug8GTiu3b4NYhssdtY8PXRue3ICnh1IlxvZYl+hiINRq0GfNWei3XOPDg8L0PsxbmYjWVLuLj6BMRR/9vA==",
|
||||
"dev": true
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-2.0.1.tgz",
|
||||
|
@ -57,6 +57,10 @@
|
||||
"cutest": "^0.1.9",
|
||||
"esdoc": "^1.1.0",
|
||||
"esdoc-standard-plugin": "^1.0.0",
|
||||
"prosemirror-example-setup": "^1.0.1",
|
||||
"prosemirror-schema-basic": "^1.0.0",
|
||||
"prosemirror-state": "^1.2.2",
|
||||
"prosemirror-view": "^1.6.5",
|
||||
"quill": "^1.3.6",
|
||||
"quill-cursors": "^1.0.3",
|
||||
"rollup": "^0.58.2",
|
||||
|
@ -1,56 +0,0 @@
|
||||
|
||||
import Binding from '../Binding.js'
|
||||
import simpleDiff from '../../Util/simpleDiff.js'
|
||||
import { getRelativePosition, fromRelativePosition } from '../../Util/relativePosition.js'
|
||||
|
||||
function typeObserver () {
|
||||
this._mutualExclude(() => {
|
||||
const textarea = this.target
|
||||
const textType = this.type
|
||||
const relativeStart = getRelativePosition(textType, textarea.selectionStart)
|
||||
const relativeEnd = getRelativePosition(textType, textarea.selectionEnd)
|
||||
textarea.value = textType.toString()
|
||||
const start = fromRelativePosition(textType._y, relativeStart)
|
||||
const end = fromRelativePosition(textType._y, relativeEnd)
|
||||
textarea.setSelectionRange(start, end)
|
||||
})
|
||||
}
|
||||
|
||||
function domObserver () {
|
||||
this._mutualExclude(() => {
|
||||
let diff = simpleDiff(this.type.toString(), this.target.value)
|
||||
this.type.delete(diff.pos, diff.remove)
|
||||
this.type.insert(diff.pos, diff.insert)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* A binding that binds a YText to a dom textarea.
|
||||
*
|
||||
* This binding is automatically destroyed when its parent is deleted.
|
||||
*
|
||||
* @example
|
||||
* const textare = document.createElement('textarea')
|
||||
* const type = y.define('textarea', Y.Text)
|
||||
* const binding = new Y.QuillBinding(type, textarea)
|
||||
*
|
||||
*/
|
||||
export default class TextareaBinding extends Binding {
|
||||
constructor (textType, domTextarea) {
|
||||
// Binding handles textType as this.type and domTextarea as this.target
|
||||
super(textType, domTextarea)
|
||||
// set initial value
|
||||
domTextarea.value = textType.toString()
|
||||
// Observers are handled by this class
|
||||
this._typeObserver = typeObserver.bind(this)
|
||||
this._domObserver = domObserver.bind(this)
|
||||
textType.observe(this._typeObserver)
|
||||
domTextarea.addEventListener('input', this._domObserver)
|
||||
}
|
||||
destroy () {
|
||||
// Remove everything that is handled by this class
|
||||
this.type.unobserve(this._typeObserver)
|
||||
this.target.unobserve(this._domObserver)
|
||||
super.destroy()
|
||||
}
|
||||
}
|
@ -499,6 +499,39 @@ export default class YText extends YArray {
|
||||
return str
|
||||
}
|
||||
|
||||
toDomString () {
|
||||
return this.toDelta().map(delta => {
|
||||
const nestedNodes = []
|
||||
for (let nodeName in delta.attributes) {
|
||||
const attrs = []
|
||||
for (let key in delta.attributes[nodeName]) {
|
||||
attrs.push({key, value: delta.attributes[nodeName][key]})
|
||||
}
|
||||
// sort attributes to get a unique order
|
||||
attrs.sort((a, b) => a.key < b.key ? -1 : 1)
|
||||
nestedNodes.push({ nodeName, attrs })
|
||||
}
|
||||
// sort node order to get a unique order
|
||||
nestedNodes.sort((a, b) => a.nodeName < b.nodeName ? -1 : 1)
|
||||
// now convert to dom string
|
||||
let str = ''
|
||||
for (let i = 0; i < nestedNodes.length; i++) {
|
||||
const node = nestedNodes[i]
|
||||
str += `<${node.nodeName}`
|
||||
for (let j = 0; j < node.attrs.length; j++) {
|
||||
const attr = node.attrs[i]
|
||||
str += ` ${attr.key}="${attr.value}"`
|
||||
}
|
||||
str += '>'
|
||||
}
|
||||
str += delta.insert
|
||||
for (let i = nestedNodes.length - 1; i >= 0; i--) {
|
||||
str += `</${nestedNodes[i].nodeName}>`
|
||||
}
|
||||
return str
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a {@link Delta} on this shared YText type.
|
||||
*
|
||||
|
@ -1,6 +1,6 @@
|
||||
import YMap from '../YMap/YMap.js'
|
||||
import YXmlFragment from './YXmlFragment.js'
|
||||
import { createAssociation } from '../../Bindings/DomBinding/util.js'
|
||||
import { createAssociation } from '../../../bindings/DomBinding/util.js'
|
||||
import * as encoding from '../../../lib/encoding.js'
|
||||
import * as decoding from '../../../lib/decoding.js'
|
||||
|
||||
@ -82,6 +82,10 @@ export default class YXmlElement extends YXmlFragment {
|
||||
super._integrate(y)
|
||||
}
|
||||
|
||||
toString () {
|
||||
return this.toDomString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of this YXmlElement.
|
||||
* The attributes are ordered by attribute-name, so you can easily use this
|
||||
@ -91,7 +95,7 @@ export default class YXmlElement extends YXmlFragment {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
toString () {
|
||||
toDomString () {
|
||||
const attrs = this.getAttributes()
|
||||
const stringBuilder = []
|
||||
const keys = []
|
||||
@ -106,7 +110,7 @@ export default class YXmlElement extends YXmlFragment {
|
||||
}
|
||||
const nodeName = this.nodeName.toLocaleLowerCase()
|
||||
const attrsString = stringBuilder.length > 0 ? ' ' + stringBuilder.join(' ') : ''
|
||||
return `<${nodeName}${attrsString}>${super.toString()}</${nodeName}>`
|
||||
return `<${nodeName}${attrsString}>${super.toDomString()}</${nodeName}>`
|
||||
}
|
||||
|
||||
/**
|
||||
@ -170,7 +174,7 @@ export default class YXmlElement extends YXmlFragment {
|
||||
* nodejs)
|
||||
* @param {Object<string, any>} [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 {import('../../../bindings/DomBinding/DomBinding.js').default} [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}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { createAssociation } from '../../Bindings/DomBinding/util.js'
|
||||
import { createAssociation } from '../../../bindings/DomBinding/util.js'
|
||||
import YXmlTreeWalker from './YXmlTreeWalker.js'
|
||||
|
||||
import YArray from '../YArray/YArray.js'
|
||||
@ -7,7 +7,7 @@ import { logItemHelper } from '../../message.js'
|
||||
|
||||
/**
|
||||
* @typedef {import('./YXmlElement.js').default} YXmlElement
|
||||
* @typedef {import('../../Bindings/DomBinding/DomBinding.js').default} DomBinding
|
||||
* @typedef {import('../../../bindings/DomBinding/DomBinding.js').default} DomBinding
|
||||
* @typedef {import('../../Y.js').default} Y
|
||||
*/
|
||||
|
||||
@ -113,13 +113,17 @@ export default class YXmlFragment extends YArray {
|
||||
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.
|
||||
*/
|
||||
toString () {
|
||||
return this.map(xml => xml.toString()).join('')
|
||||
toDomString () {
|
||||
return this.map(xml => xml.toDomString()).join('')
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,10 +1,10 @@
|
||||
import YMap from '../YMap/YMap.js'
|
||||
import { createAssociation } from '../../Bindings/DomBinding/util.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('../../../bindings/DomBinding/DomBinding.js').default} DomBinding
|
||||
* @typedef {import('../../Y.js').default} Y
|
||||
*/
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
import YText from '../YText/YText.js'
|
||||
import { createAssociation } from '../../Bindings/DomBinding/util.js'
|
||||
import { createAssociation } from '../../../bindings/DomBinding/util.js'
|
||||
|
||||
/**
|
||||
* @typedef {import('../../Bindings/DomBinding/DomBinding.js').default} DomBinding
|
||||
* @typedef {import('../../../bindings/DomBinding/DomBinding.js').default} DomBinding
|
||||
* @typedef {import('../../index.js').Y} Y
|
||||
*/
|
||||
|
||||
|
@ -30,12 +30,6 @@ 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 { default as TextareaBinding } from './Bindings/TextareaBinding/TextareaBinding.js'
|
||||
export { default as QuillBinding } from './Bindings/QuillBinding/QuillBinding.js'
|
||||
export { default as DomBinding } from './Bindings/DomBinding/DomBinding.js'
|
||||
|
||||
export { default as domToType } from './Bindings/DomBinding/domToType.js'
|
||||
export { domsToTypes, switchAssociation } from './Bindings/DomBinding/util.js'
|
||||
export * from './message.js'
|
||||
export * from '../lib/encoding.js'
|
||||
export * from '../lib/decoding.js'
|
||||
|
Loading…
x
Reference in New Issue
Block a user