json-joy
Version:
Collection of libraries for building collaborative editing apps.
179 lines • 7.11 kB
JavaScript
import { s } from '../../../json-crdt-patch';
import { SliceStacking } from '../slice/constants';
import { CommonSliceType } from '../slice';
import { SliceBehavior } from './SliceBehavior';
import { printTree } from '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.
*/
export class SliceRegistry {
/**
* Creates a new slice registry with common tag registered.
*/
static withCommon = () => {
const undefSchema = s.con(undefined);
const registry = new SliceRegistry();
//----------------------------- Inline elements with "One" stacking behavior
const i0 = (tag, name, fromHtml) => {
registry.add(new SliceBehavior(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(-6 /* TAG.i */, 'Italic', ['i', 'em']);
i1(-3 /* TAG.b */, 'Bold', ['b', 'strong']);
i1(-12 /* TAG.s */, 'Strikethrough', ['s', 'strike']);
i0(-9 /* TAG.u */, 'Underline');
i0(-15 /* TAG.code */, 'Code');
i0(-16 /* TAG.mark */, 'Highlight');
i0(-33 /* TAG.kbd */, 'Keyboard');
i0(-22 /* TAG.del */, 'Delete');
i0(-23 /* TAG.ins */, 'Insert');
i0(-24 /* TAG.sup */, 'Superscript');
i0(-25 /* TAG.sub */, 'Subscript');
i0(-27 /* TAG.math */, 'Math');
// --------------------------- Inline elements with "Many" stacking behavior
const aSchema = s.obj({}, {
href: s.str(''),
title: s.str(''),
});
registry.add(new SliceBehavior(SliceStacking.Many, -17 /* TAG.a */, 'Link', aSchema, false, void 0, {
a: (jsonml) => {
const attr = jsonml[1] || {};
const data = {
href: attr.href ?? '',
title: attr.title ?? '',
};
return [-17 /* TAG.a */, { data, inline: true }];
},
}));
const colSchema = s.obj({}, {
color: s.str(''),
});
registry.add(new SliceBehavior(SliceStacking.Many, -29 /* TAG.col */, 'Color', colSchema, false, void 0, {
col: (jsonml) => {
const attr = jsonml[1] || {};
const data = {
color: attr.color ?? '',
};
return [-29 /* 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 = s.obj({}, {
indent: s.con(0),
align: s.str('left'),
});
const b0 = (tag, name, container) => {
registry.add(new SliceBehavior(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(5 /* TAG.pre */, 'Pre-formatted', false);
b0(6 /* TAG.ul */, 'Unordered list', true);
b0(7 /* TAG.ol */, 'Ordered list', true);
b0(8 /* TAG.tl */, 'Task list', true);
b0(9 /* TAG.li */, 'List item', true);
b0(11 /* TAG.h1 */, 'Heading 1', false);
b0(12 /* TAG.h2 */, 'Heading 2', false);
b0(13 /* TAG.h3 */, 'Heading 3', false);
b0(14 /* TAG.h4 */, 'Heading 4', false);
b0(15 /* TAG.h5 */, 'Heading 5', false);
b0(16 /* TAG.h6 */, 'Heading 6', false);
b0(19 /* TAG.title */, 'Title', false);
b0(20 /* 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;
};
map = new Map();
_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 = 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 ? SliceStacking.Marker : (entry.stacking ?? SliceStacking.Many);
}
return result;
}
}
}
return;
}
/** ----------------------------------------------------- {@link Printable} */
toString(tab = '') {
return ('SliceRegistry' +
printTree(tab, [...this.map.values()].map((entry) => (tab) => entry.toString(tab))));
}
}
//# sourceMappingURL=SliceRegistry.js.map