You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 7-8.RSVP
Socket
Socket
Sign inDemoInstall

y-prosemirror

Package Overview
Dependencies
Maintainers
1
Versions
68
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.0.10 to 0.0.11

331

dist/y-prosemirror.js

@@ -30,3 +30,2 @@ 'use strict';

while (n !== null && type !== n) {
const pNodeSize = (mapping.get(n) || { nodeSize: 0 }).nodeSize;
if (n.constructor === Y.XmlText) {

@@ -50,27 +49,30 @@ if (n._length >= pos) {

}
} else if (n._first !== null && pos < pNodeSize) {
n = /** @type {Y.ContentType} */ (n._first.content).type;
pos--;
} else {
if (pos === 1 && n._length === 0 && pNodeSize > 1) {
// edge case, should end in this paragraph
return new Y.RelativePosition(n._item === null ? null : n._item.id, n._item === null ? Y.findRootTypeKey(n) : null, null)
}
pos -= pNodeSize;
if (n._item !== null && n._item.next !== null) {
n = /** @type {Y.ContentType} */ (n._item.next.content).type;
const pNodeSize = /** @type {any} */ (mapping.get(n) || { nodeSize: 0 }).nodeSize;
if (n._first !== null && pos < pNodeSize) {
n = /** @type {Y.ContentType} */ (n._first.content).type;
pos--;
} else {
if (pos === 0) {
// set to end of n.parent
n = n._item === null ? n : n._item.parent;
if (pos === 1 && n._length === 0 && pNodeSize > 1) {
// edge case, should end in this paragraph
return new Y.RelativePosition(n._item === null ? null : n._item.id, n._item === null ? Y.findRootTypeKey(n) : null, null)
}
do {
n = /** @type {Y.Item} */ (n._item).parent;
pos--;
} while (n !== type && /** @type {Y.Item} */ (n._item).next === null)
// if n is null at this point, we have an unexpected case
if (n !== type) {
// We know that n._item.next is defined because of above loop condition
n = /** @type {Y.ContentType} */ (/** @type {Y.Item} */ (/** @type {Y.Item} */ (n._item).next).content).type;
pos -= pNodeSize;
if (n._item !== null && n._item.next !== null) {
n = /** @type {Y.ContentType} */ (n._item.next.content).type;
} else {
if (pos === 0) {
// set to end of n.parent
n = n._item === null ? n : n._item.parent;
return new Y.RelativePosition(n._item === null ? null : n._item.id, n._item === null ? Y.findRootTypeKey(n) : null, null)
}
do {
n = /** @type {Y.Item} */ (n._item).parent;
pos--;
} while (n !== type && /** @type {Y.Item} */ (n._item).next === null)
// if n is null at this point, we have an unexpected case
if (n !== type) {
// We know that n._item.next is defined because of above loop condition
n = /** @type {Y.ContentType} */ (/** @type {Y.Item} */ (/** @type {Y.Item} */ (n._item).next).content).type;
}
}

@@ -114,3 +116,3 @@ }

} else {
pos += mapping.get(t).nodeSize;
pos += /** @type {any} */ (mapping.get(t)).nodeSize;
}

@@ -139,3 +141,3 @@ }

} else {
pos += mapping.get(contentType).nodeSize;
pos += /** @type {any} */ (mapping.get(contentType)).nodeSize;
}

@@ -158,3 +160,4 @@ }

/**
* @typedef {Map<Y.AbstractType, Object>} ProsemirrorMapping
* Either a node if type is YXmlElement or an Array of text nodes if YXmlText
* @typedef {Map<Y.AbstractType, PModel.Node | Array<PModel.Node>>} ProsemirrorMapping
*/

@@ -176,3 +179,3 @@

*/
const ySyncPlugin = (yXmlFragment) => {
const ySyncPlugin = yXmlFragment => {
let changedInitialContent = false;

@@ -334,6 +337,10 @@ const plugin = new prosemirrorState.Plugin({

this.mux(() => {
const delStruct = (_, struct) => this.mapping.delete(struct);
/**
* @param {any} _
* @param {Y.AbstractType} type
*/
const delType = (_, type) => this.mapping.delete(type);
Y.iterateDeletedStructs(transaction, transaction.deleteSet, this.doc.store, struct => struct.constructor === Y.Item && this.mapping.delete(/** @type {Y.ContentType} */ (/** @type {Y.Item} */ (struct).content).type));
transaction.changed.forEach(delStruct);
transaction.changedParentTypes.forEach(delStruct);
transaction.changed.forEach(delType);
transaction.changedParentTypes.forEach(delType);
const fragmentContent = this.type.toArray().map(t => createNodeIfNotExists(/** @type {Y.XmlElement | Y.XmlHook} */ (t), this.prosemirrorView.state.schema, this.mapping)).filter(n => n !== null);

@@ -351,3 +358,3 @@ let tr = this.prosemirrorView.state.tr.replace(0, this.prosemirrorView.state.doc.content.size, new PModel.Slice(new PModel.Fragment(fragmentContent), 0, 0));

this.mux(() => {
updateYFragment(this.doc, this.type, doc.content, this.mapping);
updateYFragment(this.doc, this.type, doc, this.mapping);
});

@@ -370,3 +377,3 @@ }

const createNodeIfNotExists = (el, schema, mapping, snapshot, prevSnapshot) => {
const node = mapping.get(el);
const node = /** @type {PModel.Node} */ (mapping.get(el));
if (node === undefined) {

@@ -426,3 +433,2 @@ if (el instanceof Y.XmlElement) {

}
let node;
try {

@@ -437,3 +443,5 @@ const attrs = el.getAttributes(_snapshot);

}
node = schema.node(el.nodeName, attrs, children);
const node = schema.node(el.nodeName, attrs, children);
mapping.set(el, node);
return node
} catch (e) {

@@ -444,6 +452,5 @@ // an error occured while creating the node. This is probably a result of a concurrent action.

});
mapping.delete(el);
return null
}
mapping.set(el, node);
return node
};

@@ -472,5 +479,2 @@

}
if (nodes.length > 0) {
mapping.set(text, nodes[0]); // only map to first child, all following children are also considered bound to this type
}
} catch (e) {

@@ -489,31 +493,33 @@ // an error occured while creating the node. This is probably a result of a concurrent action.

* @private
* @param {Object} node prosemirror node
* @param {Array<PModel.Node>} nodes prosemirror node
* @param {ProsemirrorMapping} mapping
* @return {Y.XmlElement | Y.XmlText}
* @return {Y.XmlText}
*/
const createTypeFromNode = (node, mapping) => {
let type;
if (node.isText) {
type = new Y.XmlText();
const attrs = {};
node.marks.forEach(mark => {
if (mark.type.name !== 'ychange') {
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) {
const val = node.attrs[key];
if (val !== null && key !== 'ychange') {
type.setAttribute(key, val);
}
const createTypeFromTextNodes = (nodes, mapping) => {
const type = new Y.XmlText();
const delta = nodes.map(node => ({
// @ts-ignore
insert: node.text,
attributes: marksToAttributes(node.marks)
}));
type.applyDelta(delta);
mapping.set(type, nodes);
return type
};
/**
* @private
* @param {PModel.Node} node prosemirror node
* @param {ProsemirrorMapping} mapping
* @return {Y.XmlElement}
*/
const createTypeFromElementNode = (node, mapping) => {
const type = new Y.XmlElement(node.type.name);
for (let key in node.attrs) {
const val = node.attrs[key];
if (val !== null && key !== 'ychange') {
type.setAttribute(key, val);
}
const ins = [];
for (let i = 0; i < node.childCount; i++) {
ins.push(createTypeFromNode(node.child(i), mapping));
}
type.insert(0, ins);
}
type.insert(0, normalizePNodeContent(node).map(n => createTypeFromTextOrElementNode(n, mapping)));
mapping.set(type, node);

@@ -523,2 +529,10 @@ return type

/**
* @private
* @param {PModel.Node|Array<PModel.Node>} node prosemirror text node
* @param {ProsemirrorMapping} mapping
* @return {Y.XmlElement|Y.XmlText}
*/
const createTypeFromTextOrElementNode = (node, mapping) => node instanceof Array ? createTypeFromTextNodes(node, mapping) : createTypeFromElementNode(node, mapping);
const equalAttrs = (pattrs, yattrs) => {

@@ -536,19 +550,66 @@ const keys = Object.keys(pattrs).filter(key => pattrs[key] !== null);

const equalYTextPText = (ytext, ptext) => {
/**
* @typedef {Array<Array<PModel.Node>|PModel.Node>} NormalizedPNodeContent
*/
/**
* @param {PModel.Node} pnode
* @return {NormalizedPNodeContent}
*/
const normalizePNodeContent = pnode => {
const c = pnode.content.content;
const res = [];
for (let i = 0; i < c.length; i++) {
const n = c[i];
if (n.isText) {
const textNodes = [];
for (let tnode = c[i]; i < c.length && tnode.isText; tnode = c[++i]) {
textNodes.push(tnode);
}
i--;
res.push(textNodes);
} else {
res.push(n);
}
}
return res
};
/**
* @param {Y.XmlText} ytext
* @param {Array<PModel.Node>} ptexts
*/
const equalYTextPText = (ytext, ptexts) => {
const delta = ytext.toDelta();
if (delta.length === 0) {
return ptext.text === ''
return delta.length === ptexts.length && delta.every((d, i) => d.insert === /** @type {any} */ (ptexts[i]).text && object.keys(d.attributes || {}).length === ptexts[i].marks.length && ptexts[i].marks.every(mark => equalAttrs(d.attributes[mark.type.name] || {}, mark.attrs)))
};
/**
* @param {Y.XmlElement|Y.XmlText|Y.XmlHook} ytype
* @param {PModel.Node|Array<PModel.Node>} pnode
*/
const equalYTypePNode = (ytype, pnode) => {
if (ytype instanceof Y.XmlElement && !(pnode instanceof Array) && matchNodeName(ytype, pnode)) {
const normalizedContent = normalizePNodeContent(pnode);
return ytype._length === normalizedContent.length && equalAttrs(ytype.getAttributes(), pnode.attrs) && ytype.toArray().every((ychild, i) => equalYTypePNode(ychild, normalizedContent[i]))
}
const d = delta[0];
return d.insert === ptext.text && object.keys(d.attributes || {}).length === ptext.marks.length && ptext.marks.every(mark => equalAttrs(d.attributes[mark.type.name], mark.attrs))
return ytype instanceof Y.XmlText && pnode instanceof Array && equalYTextPText(ytype, pnode)
};
const equalYTypePNode = (ytype, pnode) =>
ytype.constructor === Y.XmlText
? equalYTextPText(ytype, pnode)
: (matchNodeName(ytype, pnode) && ytype.length === pnode.childCount && equalAttrs(ytype.getAttributes(), pnode.attrs) && ytype.toArray().every((ychild, i) => equalYTypePNode(ychild, pnode.child(i))));
/**
* @param {PModel.Node | Array<PModel.Node> | undefined} mapped
* @param {PModel.Node | Array<PModel.Node>} pcontent
*/
const mappedIdentity = (mapped, pcontent) => mapped === pcontent || (mapped instanceof Array && pcontent instanceof Array && mapped.length === pcontent.length && mapped.every((a, i) => pcontent[i] === a));
/**
* @param {Y.XmlElement} ytype
* @param {PModel.Node} pnode
* @param {ProsemirrorMapping} mapping
* @return {{ foundMappedChild: boolean, equalityFactor: number }}
*/
const computeChildEqualityFactor = (ytype, pnode, mapping) => {
const yChildren = ytype.toArray();
const pChildCnt = pnode.childCount;
const pChildren = normalizePNodeContent(pnode);
const pChildCnt = pChildren.length;
const yChildCnt = yChildren.length;

@@ -561,4 +622,4 @@ const minCnt = math.min(yChildCnt, pChildCnt);

const leftY = yChildren[left];
const leftP = pnode.child(left);
if (mapping.get(leftY) === leftP) {
const leftP = pChildren[left];
if (mappedIdentity(mapping.get(leftY), leftP)) {
foundMappedChild = true;// definite (good) match!

@@ -571,6 +632,6 @@ } else if (!equalYTypePNode(leftY, leftP)) {

const rightY = yChildren[yChildCnt - right - 1];
const rightP = pnode.child(pChildCnt - right - 1);
if (mapping.get(rightY) !== rightP) {
const rightP = pChildren[pChildCnt - right - 1];
if (mappedIdentity(mapping.get(rightY), rightP)) {
foundMappedChild = true;
} else if (!equalYTypePNode(rightP, rightP)) {
} else if (!equalYTypePNode(rightY, rightP)) {
break

@@ -585,18 +646,68 @@ }

const ytextTrans = ytext => {
let str = '';
/**
* @type {Y.Item|null}
*/
let n = ytext._start;
const nAttrs = {};
while (n !== null) {
if (!n.deleted) {
if (n.countable && n.content instanceof Y.ContentString) {
str += n.content.str;
} else if (n.content instanceof Y.ContentFormat) {
nAttrs[n.content.key] = null;
}
}
n = n.right;
}
return {
str,
nAttrs
}
};
/**
* @todo test this more
*
* @param {Y.Text} ytext
* @param {Array<PModel.Node>} ptexts
* @param {ProsemirrorMapping} mapping
*/
const updateYText = (ytext, ptexts, mapping) => {
mapping.set(ytext, ptexts);
const { nAttrs, str } = ytextTrans(ytext);
const content = ptexts.map(p => ({ insert: /** @type {any} */ (p).text, attributes: Object.assign({}, nAttrs, marksToAttributes(p.marks)) }));
const { insert, remove, index } = diff_js.simpleDiff(str, content.map(c => c.insert).join(''));
ytext.delete(index, remove);
ytext.insert(index, insert);
ytext.applyDelta(content.map(c => ({ retain: c.insert.length, attributes: c.attributes })));
};
const marksToAttributes = marks => {
const pattrs = {};
marks.forEach(mark => {
if (mark.type.name !== 'ychange') {
pattrs[mark.type.name] = mark.attrs;
}
});
return pattrs
};
/**
* @private
* @param {Y.Doc} y
* @param {Y.XmlFragment} yDomFragment
* @param {Object} pContent
* @param {PModel.Node} pNode
* @param {ProsemirrorMapping} mapping
*/
const updateYFragment = (y, yDomFragment, pContent, mapping) => {
if (yDomFragment instanceof Y.XmlElement && yDomFragment.nodeName !== pContent.type.name) {
const updateYFragment = (y, yDomFragment, pNode, mapping) => {
if (yDomFragment instanceof Y.XmlElement && yDomFragment.nodeName !== pNode.type.name) {
throw new Error('node name mismatch!')
}
mapping.set(yDomFragment, pContent);
mapping.set(yDomFragment, pNode);
// update attributes
if (yDomFragment instanceof Y.XmlElement) {
const yDomAttrs = yDomFragment.getAttributes();
const pAttrs = pContent.attrs;
const pAttrs = pNode.attrs;
for (let key in pAttrs) {

@@ -619,3 +730,4 @@ if (pAttrs[key] !== null) {

// update children
const pChildCnt = pContent.childCount;
const pChildren = normalizePNodeContent(pNode);
const pChildCnt = pChildren.length;
const yChildren = yDomFragment.toArray();

@@ -629,4 +741,4 @@ const yChildCnt = yChildren.length;

const leftY = yChildren[left];
const leftP = pContent.child(left);
if (mapping.get(leftY) !== leftP) {
const leftP = pChildren[left];
if (!mappedIdentity(mapping.get(leftY), leftP)) {
if (equalYTypePNode(leftY, leftP)) {

@@ -641,6 +753,6 @@ // update mapping

// find number of matching elements from right
for (;right + left < minCnt; right++) {
for (;right + left + 1 < minCnt; right++) {
const rightY = yChildren[yChildCnt - right - 1];
const rightP = pContent.child(pChildCnt - right - 1);
if (mapping.get(rightY) !== rightP) {
const rightP = pChildren[pChildCnt - right - 1];
if (!mappedIdentity(mapping.get(rightY), rightP)) {
if (equalYTypePNode(rightY, rightP)) {

@@ -658,24 +770,8 @@ // update mapping

const leftY = yChildren[left];
const leftP = pContent.child(left);
const leftP = pChildren[left];
const rightY = yChildren[yChildCnt - right - 1];
const rightP = pContent.child(pChildCnt - right - 1);
if (leftY instanceof Y.XmlText && leftP.isText) {
const rightP = pChildren[pChildCnt - right - 1];
if (leftY instanceof Y.XmlText && leftP instanceof Array) {
if (!equalYTextPText(leftY, leftP)) {
// try to apply diff. Only if attrs don't match, delete insert
// TODO: use a single ytext to hold all following Prosemirror Text nodes
const pattrs = {};
leftP.marks.forEach(mark => {
if (mark.type.name !== 'ychange') {
pattrs[mark.type.name] = mark.attrs;
}
});
const delta = leftY.toDelta();
if (delta.length === 1 && delta[0].insert && equalAttrs(pattrs, delta[0].attributes || {})) {
const diff = diff_js.simpleDiff(delta[0].insert, leftP.text);
leftY.delete(diff.index, diff.remove);
leftY.insert(diff.index, diff.insert, delta[0].attributes || {});
} else {
yDomFragment.delete(left, 1);
yDomFragment.insert(left, [createTypeFromNode(leftP, mapping)]);
}
updateYText(leftY, leftP, mapping);
}

@@ -688,4 +784,4 @@ left += 1;

// decide which which element to update
const equalityLeft = computeChildEqualityFactor(leftY, leftP, mapping);
const equalityRight = computeChildEqualityFactor(rightY, rightP, mapping);
const equalityLeft = computeChildEqualityFactor(/** @type {Y.XmlElement} */ (leftY), /** @type {PModel.Node} */ (leftP), mapping);
const equalityRight = computeChildEqualityFactor(/** @type {Y.XmlElement} */ (rightY), /** @type {PModel.Node} */ (rightP), mapping);
if (equalityLeft.foundMappedChild && !equalityRight.foundMappedChild) {

@@ -702,10 +798,10 @@ updateRight = false;

if (updateLeft) {
updateYFragment(y, /** @type {Y.XmlFragment} */ (leftY), leftP, mapping);
updateYFragment(y, /** @type {Y.XmlFragment} */ (leftY), /** @type {PModel.Node} */ (leftP), mapping);
left += 1;
} else if (updateRight) {
updateYFragment(y, /** @type {Y.XmlFragment} */ (rightY), rightP, mapping);
updateYFragment(y, /** @type {Y.XmlFragment} */ (rightY), /** @type {PModel.Node} */ (rightP), mapping);
right += 1;
} else {
yDomFragment.delete(left, 1);
yDomFragment.insert(left, [createTypeFromNode(leftP, mapping)]);
yDomFragment.insert(left, [createTypeFromTextOrElementNode(leftP, mapping)]);
left += 1;

@@ -722,3 +818,3 @@ }

for (let i = left; i < pChildCnt - right; i++) {
ins.push(createTypeFromNode(pContent.child(i), mapping));
ins.push(createTypeFromTextOrElementNode(pChildren[i], mapping));
}

@@ -928,5 +1024,8 @@ yDomFragment.insert(left, ins);

exports.createTextNodesFromYText = createTextNodesFromYText;
exports.createTypeFromNode = createTypeFromNode;
exports.createTypeFromElementNode = createTypeFromElementNode;
exports.createTypeFromTextNodes = createTypeFromTextNodes;
exports.createTypeFromTextOrElementNode = createTypeFromTextOrElementNode;
exports.getRelativeSelection = getRelativeSelection;
exports.isVisible = isVisible;
exports.normalizePNodeContent = normalizePNodeContent;
exports.redo = redo;

@@ -933,0 +1032,0 @@ exports.undo = undo;

{
"name": "y-prosemirror",
"version": "0.0.10",
"version": "0.0.11",
"description": "Prosemirror bindings for Yjs",

@@ -5,0 +5,0 @@ "main": "./dist/y-prosemirror.js",

@@ -5,2 +5,3 @@ import { ProsemirrorMapping } from './plugins/sync-plugin.js' // eslint-disable-line

import * as error from 'lib0/error.js'
import * as PModel from 'prosemirror-model' // eslint-disable-line

@@ -21,3 +22,2 @@ /**

while (n !== null && type !== n) {
const pNodeSize = (mapping.get(n) || { nodeSize: 0 }).nodeSize
if (n.constructor === Y.XmlText) {

@@ -41,27 +41,30 @@ if (n._length >= pos) {

}
} else if (n._first !== null && pos < pNodeSize) {
n = /** @type {Y.ContentType} */ (n._first.content).type
pos--
} else {
if (pos === 1 && n._length === 0 && pNodeSize > 1) {
// edge case, should end in this paragraph
return new Y.RelativePosition(n._item === null ? null : n._item.id, n._item === null ? Y.findRootTypeKey(n) : null, null)
}
pos -= pNodeSize
if (n._item !== null && n._item.next !== null) {
n = /** @type {Y.ContentType} */ (n._item.next.content).type
const pNodeSize = /** @type {any} */ (mapping.get(n) || { nodeSize: 0 }).nodeSize
if (n._first !== null && pos < pNodeSize) {
n = /** @type {Y.ContentType} */ (n._first.content).type
pos--
} else {
if (pos === 0) {
// set to end of n.parent
n = n._item === null ? n : n._item.parent
if (pos === 1 && n._length === 0 && pNodeSize > 1) {
// edge case, should end in this paragraph
return new Y.RelativePosition(n._item === null ? null : n._item.id, n._item === null ? Y.findRootTypeKey(n) : null, null)
}
do {
n = /** @type {Y.Item} */ (n._item).parent
pos--
} while (n !== type && /** @type {Y.Item} */ (n._item).next === null)
// if n is null at this point, we have an unexpected case
if (n !== type) {
// We know that n._item.next is defined because of above loop condition
n = /** @type {Y.ContentType} */ (/** @type {Y.Item} */ (/** @type {Y.Item} */ (n._item).next).content).type
pos -= pNodeSize
if (n._item !== null && n._item.next !== null) {
n = /** @type {Y.ContentType} */ (n._item.next.content).type
} else {
if (pos === 0) {
// set to end of n.parent
n = n._item === null ? n : n._item.parent
return new Y.RelativePosition(n._item === null ? null : n._item.id, n._item === null ? Y.findRootTypeKey(n) : null, null)
}
do {
n = /** @type {Y.Item} */ (n._item).parent
pos--
} while (n !== type && /** @type {Y.Item} */ (n._item).next === null)
// if n is null at this point, we have an unexpected case
if (n !== type) {
// We know that n._item.next is defined because of above loop condition
n = /** @type {Y.ContentType} */ (/** @type {Y.Item} */ (/** @type {Y.Item} */ (n._item).next).content).type
}
}

@@ -105,3 +108,3 @@ }

} else {
pos += mapping.get(t).nodeSize
pos += /** @type {any} */ (mapping.get(t)).nodeSize
}

@@ -130,3 +133,3 @@ }

} else {
pos += mapping.get(contentType).nodeSize
pos += /** @type {any} */ (mapping.get(contentType)).nodeSize
}

@@ -133,0 +136,0 @@ }

@@ -19,3 +19,4 @@ /**

/**
* @typedef {Map<Y.AbstractType, Object>} ProsemirrorMapping
* Either a node if type is YXmlElement or an Array of text nodes if YXmlText
* @typedef {Map<Y.AbstractType, PModel.Node | Array<PModel.Node>>} ProsemirrorMapping
*/

@@ -37,3 +38,3 @@

*/
export const ySyncPlugin = (yXmlFragment) => {
export const ySyncPlugin = yXmlFragment => {
let changedInitialContent = false

@@ -195,6 +196,10 @@ const plugin = new Plugin({

this.mux(() => {
const delStruct = (_, struct) => this.mapping.delete(struct)
/**
* @param {any} _
* @param {Y.AbstractType} type
*/
const delType = (_, type) => this.mapping.delete(type)
Y.iterateDeletedStructs(transaction, transaction.deleteSet, this.doc.store, struct => struct.constructor === Y.Item && this.mapping.delete(/** @type {Y.ContentType} */ (/** @type {Y.Item} */ (struct).content).type))
transaction.changed.forEach(delStruct)
transaction.changedParentTypes.forEach(delStruct)
transaction.changed.forEach(delType)
transaction.changedParentTypes.forEach(delType)
const fragmentContent = this.type.toArray().map(t => createNodeIfNotExists(/** @type {Y.XmlElement | Y.XmlHook} */ (t), this.prosemirrorView.state.schema, this.mapping)).filter(n => n !== null)

@@ -212,3 +217,3 @@ let tr = this.prosemirrorView.state.tr.replace(0, this.prosemirrorView.state.doc.content.size, new PModel.Slice(new PModel.Fragment(fragmentContent), 0, 0))

this.mux(() => {
updateYFragment(this.doc, this.type, doc.content, this.mapping)
updateYFragment(this.doc, this.type, doc, this.mapping)
})

@@ -231,3 +236,3 @@ }

export const createNodeIfNotExists = (el, schema, mapping, snapshot, prevSnapshot) => {
const node = mapping.get(el)
const node = /** @type {PModel.Node} */ (mapping.get(el))
if (node === undefined) {

@@ -287,3 +292,2 @@ if (el instanceof Y.XmlElement) {

}
let node
try {

@@ -298,3 +302,5 @@ const attrs = el.getAttributes(_snapshot)

}
node = schema.node(el.nodeName, attrs, children)
const node = schema.node(el.nodeName, attrs, children)
mapping.set(el, node)
return node
} catch (e) {

@@ -305,6 +311,5 @@ // an error occured while creating the node. This is probably a result of a concurrent action.

})
mapping.delete(el)
return null
}
mapping.set(el, node)
return node
}

@@ -333,5 +338,2 @@

}
if (nodes.length > 0) {
mapping.set(text, nodes[0]) // only map to first child, all following children are also considered bound to this type
}
} catch (e) {

@@ -350,31 +352,33 @@ // an error occured while creating the node. This is probably a result of a concurrent action.

* @private
* @param {Object} node prosemirror node
* @param {Array<PModel.Node>} nodes prosemirror node
* @param {ProsemirrorMapping} mapping
* @return {Y.XmlElement | Y.XmlText}
* @return {Y.XmlText}
*/
export const createTypeFromNode = (node, mapping) => {
let type
if (node.isText) {
type = new Y.XmlText()
const attrs = {}
node.marks.forEach(mark => {
if (mark.type.name !== 'ychange') {
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) {
const val = node.attrs[key]
if (val !== null && key !== 'ychange') {
type.setAttribute(key, val)
}
export const createTypeFromTextNodes = (nodes, mapping) => {
const type = new Y.XmlText()
const delta = nodes.map(node => ({
// @ts-ignore
insert: node.text,
attributes: marksToAttributes(node.marks)
}))
type.applyDelta(delta)
mapping.set(type, nodes)
return type
}
/**
* @private
* @param {PModel.Node} node prosemirror node
* @param {ProsemirrorMapping} mapping
* @return {Y.XmlElement}
*/
export const createTypeFromElementNode = (node, mapping) => {
const type = new Y.XmlElement(node.type.name)
for (let key in node.attrs) {
const val = node.attrs[key]
if (val !== null && key !== 'ychange') {
type.setAttribute(key, val)
}
const ins = []
for (let i = 0; i < node.childCount; i++) {
ins.push(createTypeFromNode(node.child(i), mapping))
}
type.insert(0, ins)
}
type.insert(0, normalizePNodeContent(node).map(n => createTypeFromTextOrElementNode(n, mapping)))
mapping.set(type, node)

@@ -384,2 +388,10 @@ return type

/**
* @private
* @param {PModel.Node|Array<PModel.Node>} node prosemirror text node
* @param {ProsemirrorMapping} mapping
* @return {Y.XmlElement|Y.XmlText}
*/
export const createTypeFromTextOrElementNode = (node, mapping) => node instanceof Array ? createTypeFromTextNodes(node, mapping) : createTypeFromElementNode(node, mapping)
const equalAttrs = (pattrs, yattrs) => {

@@ -397,19 +409,66 @@ const keys = Object.keys(pattrs).filter(key => pattrs[key] !== null)

const equalYTextPText = (ytext, ptext) => {
/**
* @typedef {Array<Array<PModel.Node>|PModel.Node>} NormalizedPNodeContent
*/
/**
* @param {PModel.Node} pnode
* @return {NormalizedPNodeContent}
*/
export const normalizePNodeContent = pnode => {
const c = pnode.content.content
const res = []
for (let i = 0; i < c.length; i++) {
const n = c[i]
if (n.isText) {
const textNodes = []
for (let tnode = c[i]; i < c.length && tnode.isText; tnode = c[++i]) {
textNodes.push(tnode)
}
i--
res.push(textNodes)
} else {
res.push(n)
}
}
return res
}
/**
* @param {Y.XmlText} ytext
* @param {Array<PModel.Node>} ptexts
*/
const equalYTextPText = (ytext, ptexts) => {
const delta = ytext.toDelta()
if (delta.length === 0) {
return ptext.text === ''
return delta.length === ptexts.length && delta.every((d, i) => d.insert === /** @type {any} */ (ptexts[i]).text && object.keys(d.attributes || {}).length === ptexts[i].marks.length && ptexts[i].marks.every(mark => equalAttrs(d.attributes[mark.type.name] || {}, mark.attrs)))
}
/**
* @param {Y.XmlElement|Y.XmlText|Y.XmlHook} ytype
* @param {PModel.Node|Array<PModel.Node>} pnode
*/
const equalYTypePNode = (ytype, pnode) => {
if (ytype instanceof Y.XmlElement && !(pnode instanceof Array) && matchNodeName(ytype, pnode)) {
const normalizedContent = normalizePNodeContent(pnode)
return ytype._length === normalizedContent.length && equalAttrs(ytype.getAttributes(), pnode.attrs) && ytype.toArray().every((ychild, i) => equalYTypePNode(ychild, normalizedContent[i]))
}
const d = delta[0]
return d.insert === ptext.text && object.keys(d.attributes || {}).length === ptext.marks.length && ptext.marks.every(mark => equalAttrs(d.attributes[mark.type.name], mark.attrs))
return ytype instanceof Y.XmlText && pnode instanceof Array && equalYTextPText(ytype, pnode)
}
const equalYTypePNode = (ytype, pnode) =>
ytype.constructor === Y.XmlText
? equalYTextPText(ytype, pnode)
: (matchNodeName(ytype, pnode) && ytype.length === pnode.childCount && equalAttrs(ytype.getAttributes(), pnode.attrs) && ytype.toArray().every((ychild, i) => equalYTypePNode(ychild, pnode.child(i))))
/**
* @param {PModel.Node | Array<PModel.Node> | undefined} mapped
* @param {PModel.Node | Array<PModel.Node>} pcontent
*/
const mappedIdentity = (mapped, pcontent) => mapped === pcontent || (mapped instanceof Array && pcontent instanceof Array && mapped.length === pcontent.length && mapped.every((a, i) => pcontent[i] === a))
/**
* @param {Y.XmlElement} ytype
* @param {PModel.Node} pnode
* @param {ProsemirrorMapping} mapping
* @return {{ foundMappedChild: boolean, equalityFactor: number }}
*/
const computeChildEqualityFactor = (ytype, pnode, mapping) => {
const yChildren = ytype.toArray()
const pChildCnt = pnode.childCount
const pChildren = normalizePNodeContent(pnode)
const pChildCnt = pChildren.length
const yChildCnt = yChildren.length

@@ -422,4 +481,4 @@ const minCnt = math.min(yChildCnt, pChildCnt)

const leftY = yChildren[left]
const leftP = pnode.child(left)
if (mapping.get(leftY) === leftP) {
const leftP = pChildren[left]
if (mappedIdentity(mapping.get(leftY), leftP)) {
foundMappedChild = true// definite (good) match!

@@ -432,6 +491,6 @@ } else if (!equalYTypePNode(leftY, leftP)) {

const rightY = yChildren[yChildCnt - right - 1]
const rightP = pnode.child(pChildCnt - right - 1)
if (mapping.get(rightY) !== rightP) {
const rightP = pChildren[pChildCnt - right - 1]
if (mappedIdentity(mapping.get(rightY), rightP)) {
foundMappedChild = true
} else if (!equalYTypePNode(rightP, rightP)) {
} else if (!equalYTypePNode(rightY, rightP)) {
break

@@ -446,18 +505,68 @@ }

const ytextTrans = ytext => {
let str = ''
/**
* @type {Y.Item|null}
*/
let n = ytext._start
const nAttrs = {}
while (n !== null) {
if (!n.deleted) {
if (n.countable && n.content instanceof Y.ContentString) {
str += n.content.str
} else if (n.content instanceof Y.ContentFormat) {
nAttrs[n.content.key] = null
}
}
n = n.right
}
return {
str,
nAttrs
}
}
/**
* @todo test this more
*
* @param {Y.Text} ytext
* @param {Array<PModel.Node>} ptexts
* @param {ProsemirrorMapping} mapping
*/
const updateYText = (ytext, ptexts, mapping) => {
mapping.set(ytext, ptexts)
const { nAttrs, str } = ytextTrans(ytext)
const content = ptexts.map(p => ({ insert: /** @type {any} */ (p).text, attributes: Object.assign({}, nAttrs, marksToAttributes(p.marks)) }))
const { insert, remove, index } = simpleDiff(str, content.map(c => c.insert).join(''))
ytext.delete(index, remove)
ytext.insert(index, insert)
ytext.applyDelta(content.map(c => ({ retain: c.insert.length, attributes: c.attributes })))
}
const marksToAttributes = marks => {
const pattrs = {}
marks.forEach(mark => {
if (mark.type.name !== 'ychange') {
pattrs[mark.type.name] = mark.attrs
}
})
return pattrs
}
/**
* @private
* @param {Y.Doc} y
* @param {Y.XmlFragment} yDomFragment
* @param {Object} pContent
* @param {PModel.Node} pNode
* @param {ProsemirrorMapping} mapping
*/
const updateYFragment = (y, yDomFragment, pContent, mapping) => {
if (yDomFragment instanceof Y.XmlElement && yDomFragment.nodeName !== pContent.type.name) {
const updateYFragment = (y, yDomFragment, pNode, mapping) => {
if (yDomFragment instanceof Y.XmlElement && yDomFragment.nodeName !== pNode.type.name) {
throw new Error('node name mismatch!')
}
mapping.set(yDomFragment, pContent)
mapping.set(yDomFragment, pNode)
// update attributes
if (yDomFragment instanceof Y.XmlElement) {
const yDomAttrs = yDomFragment.getAttributes()
const pAttrs = pContent.attrs
const pAttrs = pNode.attrs
for (let key in pAttrs) {

@@ -480,3 +589,4 @@ if (pAttrs[key] !== null) {

// update children
const pChildCnt = pContent.childCount
const pChildren = normalizePNodeContent(pNode)
const pChildCnt = pChildren.length
const yChildren = yDomFragment.toArray()

@@ -490,4 +600,4 @@ const yChildCnt = yChildren.length

const leftY = yChildren[left]
const leftP = pContent.child(left)
if (mapping.get(leftY) !== leftP) {
const leftP = pChildren[left]
if (!mappedIdentity(mapping.get(leftY), leftP)) {
if (equalYTypePNode(leftY, leftP)) {

@@ -502,6 +612,6 @@ // update mapping

// find number of matching elements from right
for (;right + left < minCnt; right++) {
for (;right + left + 1 < minCnt; right++) {
const rightY = yChildren[yChildCnt - right - 1]
const rightP = pContent.child(pChildCnt - right - 1)
if (mapping.get(rightY) !== rightP) {
const rightP = pChildren[pChildCnt - right - 1]
if (!mappedIdentity(mapping.get(rightY), rightP)) {
if (equalYTypePNode(rightY, rightP)) {

@@ -519,24 +629,8 @@ // update mapping

const leftY = yChildren[left]
const leftP = pContent.child(left)
const leftP = pChildren[left]
const rightY = yChildren[yChildCnt - right - 1]
const rightP = pContent.child(pChildCnt - right - 1)
if (leftY instanceof Y.XmlText && leftP.isText) {
const rightP = pChildren[pChildCnt - right - 1]
if (leftY instanceof Y.XmlText && leftP instanceof Array) {
if (!equalYTextPText(leftY, leftP)) {
// try to apply diff. Only if attrs don't match, delete insert
// TODO: use a single ytext to hold all following Prosemirror Text nodes
const pattrs = {}
leftP.marks.forEach(mark => {
if (mark.type.name !== 'ychange') {
pattrs[mark.type.name] = mark.attrs
}
})
const delta = leftY.toDelta()
if (delta.length === 1 && delta[0].insert && equalAttrs(pattrs, delta[0].attributes || {})) {
const diff = simpleDiff(delta[0].insert, leftP.text)
leftY.delete(diff.index, diff.remove)
leftY.insert(diff.index, diff.insert, delta[0].attributes || {})
} else {
yDomFragment.delete(left, 1)
yDomFragment.insert(left, [createTypeFromNode(leftP, mapping)])
}
updateYText(leftY, leftP, mapping)
}

@@ -549,4 +643,4 @@ left += 1

// decide which which element to update
const equalityLeft = computeChildEqualityFactor(leftY, leftP, mapping)
const equalityRight = computeChildEqualityFactor(rightY, rightP, mapping)
const equalityLeft = computeChildEqualityFactor(/** @type {Y.XmlElement} */ (leftY), /** @type {PModel.Node} */ (leftP), mapping)
const equalityRight = computeChildEqualityFactor(/** @type {Y.XmlElement} */ (rightY), /** @type {PModel.Node} */ (rightP), mapping)
if (equalityLeft.foundMappedChild && !equalityRight.foundMappedChild) {

@@ -563,10 +657,10 @@ updateRight = false

if (updateLeft) {
updateYFragment(y, /** @type {Y.XmlFragment} */ (leftY), leftP, mapping)
updateYFragment(y, /** @type {Y.XmlFragment} */ (leftY), /** @type {PModel.Node} */ (leftP), mapping)
left += 1
} else if (updateRight) {
updateYFragment(y, /** @type {Y.XmlFragment} */ (rightY), rightP, mapping)
updateYFragment(y, /** @type {Y.XmlFragment} */ (rightY), /** @type {PModel.Node} */ (rightP), mapping)
right += 1
} else {
yDomFragment.delete(left, 1)
yDomFragment.insert(left, [createTypeFromNode(leftP, mapping)])
yDomFragment.insert(left, [createTypeFromTextOrElementNode(leftP, mapping)])
left += 1

@@ -583,3 +677,3 @@ }

for (let i = left; i < pChildCnt - right; i++) {
ins.push(createTypeFromNode(pContent.child(i), mapping))
ins.push(createTypeFromTextOrElementNode(pChildren[i], mapping))
}

@@ -586,0 +680,0 @@ yDomFragment.insert(left, ins)

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc