json-joy
Version:
Collection of libraries for building collaborative editing apps.
185 lines (184 loc) • 7.17 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.SliceRegistry = void 0;
const json_crdt_patch_1 = require("../../../json-crdt-patch");
const constants_1 = require("../slice/constants");
const slice_1 = require("../slice");
const SliceBehavior_1 = require("./SliceBehavior");
const printTree_1 = require("tree-dump/lib/printTree");
/**
* Slice registry contains a record of possible inline an block formatting
* annotations. Each entry in the registry is a {@link SliceBehavior} that
* specifies the behavior, tag, and other properties of the slice.
*
* @todo Consider moving the registry under the `/transfer` directory. Or maybe
* `/slices` directory.
*/
class SliceRegistry {
constructor() {
this.map = new Map();
this._fromHtml = new Map();
}
clear() {
this.map.clear();
this._fromHtml.clear();
}
add(entry) {
const { tag, fromHtml } = entry;
this.map.set(tag, entry);
const _fromHtml = this._fromHtml;
if (fromHtml) {
for (const htmlTag in fromHtml) {
const converter = fromHtml[htmlTag];
const converters = _fromHtml.get(htmlTag) ?? [];
converters.push([entry, converter]);
_fromHtml.set(htmlTag, converters);
}
}
const tagStr = slice_1.CommonSliceType[tag];
if (tagStr && typeof tagStr === 'string' && (!fromHtml || !(tagStr in fromHtml)))
_fromHtml.set(tagStr, [[entry, () => [tag, null]]]);
}
get(tag) {
return this.map.get(tag);
}
isContainer(tag) {
const entry = this.map.get(tag);
return entry?.container ?? false;
}
toHtml(el) {
const entry = this.map.get(el[0]);
return entry?.toHtml ? entry?.toHtml(el) : void 0;
}
fromHtml(el) {
const tag = el[0] + '';
const converters = this._fromHtml.get(tag);
if (converters) {
for (const [entry, converter] of converters) {
const result = converter(el);
if (result) {
if (entry.isInline()) {
const attr = result[1] ?? (result[1] = {});
attr.inline = entry.isInline();
attr.stacking = !attr.inline ? constants_1.SliceStacking.Marker : (entry.stacking ?? constants_1.SliceStacking.Many);
}
return result;
}
}
}
return;
}
/** ----------------------------------------------------- {@link Printable} */
toString(tab = '') {
return ('SliceRegistry' +
(0, printTree_1.printTree)(tab, [...this.map.values()].map((entry) => (tab) => entry.toString(tab))));
}
}
exports.SliceRegistry = SliceRegistry;
/**
* Creates a new slice registry with common tag registered.
*/
SliceRegistry.withCommon = () => {
const undefSchema = json_crdt_patch_1.s.con(undefined);
const registry = new SliceRegistry();
//------------------------------ Inline elements with "One" stacking behavior
const i0 = (tag, name, fromHtml) => {
registry.add(new SliceBehavior_1.SliceBehavior(constants_1.SliceStacking.One, tag, name, void 0, false, void 0, fromHtml));
};
const i1 = (tag, name, htmlTags) => {
const fromHtml = {};
for (const htmlTag of htmlTags)
fromHtml[htmlTag] = () => [tag, null];
i0(tag, name, fromHtml);
};
i1(-4 /* TAG.i */, 'Italic', ['i', 'em']);
i1(-3 /* TAG.b */, 'Bold', ['b', 'strong']);
i1(-6 /* TAG.s */, 'Strikethrough', ['s', 'strike']);
i0(-5 /* TAG.u */, 'Underline');
i0(-7 /* TAG.code */, 'Code');
i0(-8 /* TAG.mark */, 'Highlight');
i0(-19 /* TAG.kbd */, 'Keyboard');
i0(-11 /* TAG.del */, 'Delete');
i0(-12 /* TAG.ins */, 'Insert');
i0(-13 /* TAG.sup */, 'Superscript');
i0(-14 /* TAG.sub */, 'Subscript');
i0(-15 /* TAG.math */, 'Math');
// --------------------------- Inline elements with "Many" stacking behavior
const aSchema = json_crdt_patch_1.s.obj({}, {
href: json_crdt_patch_1.s.str(''),
title: json_crdt_patch_1.s.str(''),
});
registry.add(new SliceBehavior_1.SliceBehavior(constants_1.SliceStacking.Many, -9 /* TAG.a */, 'Link', aSchema, false, void 0, {
a: (jsonml) => {
const attr = jsonml[1] || {};
const data = {
href: attr.href ?? '',
title: attr.title ?? '',
};
return [-9 /* TAG.a */, { data, inline: true }];
},
}));
const colSchema = json_crdt_patch_1.s.obj({}, {
color: json_crdt_patch_1.s.str(''),
});
registry.add(new SliceBehavior_1.SliceBehavior(constants_1.SliceStacking.Many, -17 /* TAG.col */, 'Color', colSchema, false, void 0, {
col: (jsonml) => {
const attr = jsonml[1] || {};
const data = {
color: attr.color ?? '',
};
return [-17 /* TAG.col */, { data, inline: true }];
},
}));
// TODO: add more default annotations with "Many" stacking behavior
// comment = SliceTypeCon.comment,
// font = SliceTypeCon.font,
// col = SliceTypeCon.col,
// bg = SliceTypeCon.bg,
// hidden = SliceTypeCon.hidden,
// footnote = SliceTypeCon.footnote,
// ref = SliceTypeCon.ref,
// iaside = SliceTypeCon.iaside,
// iembed = SliceTypeCon.iembed,
// bookmark = SliceTypeCon.bookmark,
// -------------------------- Block elements with "Marker" stacking behavior
const commonBlockSchema = json_crdt_patch_1.s.obj({}, {
indent: json_crdt_patch_1.s.con(0),
align: json_crdt_patch_1.s.str('left'),
});
const b0 = (tag, name, container) => {
registry.add(new SliceBehavior_1.SliceBehavior(constants_1.SliceStacking.Marker, tag, name, commonBlockSchema, container));
};
b0(0 /* TAG.p */, 'Paragraph', false);
b0(1 /* TAG.blockquote */, 'Blockquote', true);
b0(2 /* TAG.codeblock */, 'Code block', false);
b0(3 /* TAG.pre */, 'Pre-formatted', false);
b0(4 /* TAG.ul */, 'Unordered list', true);
b0(5 /* TAG.ol */, 'Ordered list', true);
b0(6 /* TAG.tl */, 'Task list', true);
b0(7 /* TAG.li */, 'List item', true);
b0(8 /* TAG.h1 */, 'Heading 1', false);
b0(9 /* TAG.h2 */, 'Heading 2', false);
b0(10 /* TAG.h3 */, 'Heading 3', false);
b0(11 /* TAG.h4 */, 'Heading 4', false);
b0(12 /* TAG.h5 */, 'Heading 5', false);
b0(13 /* TAG.h6 */, 'Heading 6', false);
b0(14 /* TAG.title */, 'Title', false);
b0(15 /* TAG.subtitle */, 'Subtitle', false);
// b0(TAG.br, false);
// b0(TAG.nl, false);
// b0(TAG.hr, false);
// b0(TAG.page, false);
// b0(TAG.aside, true);
// b0(TAG.embed, false);
// b0(TAG.column, true);
// b0(TAG.contents, true);
// b0(TAG.table, true);
// b0(TAG.row, true);
// b0(TAG.cell, true);
// b0(TAG.collapselist, true);
// b0(TAG.collapse, true);
// b0(TAG.note, true);
// b0(TAG.mathblock, false);
return registry;
};
;