1/*
2 * html.ts
3 *
4 * Copyright (C) 2021 by RStudio, PBC
5 *
6 * Unless you have received this program directly from RStudio pursuant
7 * to the terms of a commercial license agreement with RStudio, then
8 * this program is licensed to you under the terms of version 3 of the
9 * GNU Affero General Public License. This program is distributed WITHOUT
10 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13 *
14 */
15
16import { Node as ProsemirrorNode, Schema, DOMSerializer, Fragment } from 'prosemirror-model';
17
18export const kHTMLCommentRegEx = /(?:^|[^`])(<!--([\s\S]*?)-->)/;
19
20export function isHTMLComment(html: string) {
21  return !!html.match(kHTMLCommentRegEx);
22}
23
24export function isSingleLineHTML(html: string) {
25  return html.trimRight().split('\n').length === 1;
26}
27
28export function asHTMLTag(
29  tag: string,
30  attribs: { [key: string]: string },
31  selfClosing = false,
32  noEmptyAttribs = false,
33) {
34  const attribsHTML = Object.keys(attribs)
35    .filter(name => !noEmptyAttribs || attribs[name])
36    .map(name => `${name}="${escapeHTMLAttribute(attribs[name])}"`)
37    .join(' ');
38  return `<${tag} ${attribsHTML}${selfClosing ? '/' : ''}>`;
39}
40
41export function escapeHTMLAttribute(value: string) {
42  return value
43    .replace(/&/g, '&amp;') // must be first replacement
44    .replace(/'/g, '&apos;')
45    .replace(/"/g, '&quot;')
46    .replace(/</g, '&lt;')
47    .replace(/>/g, '&gt;');
48}
49
50export function nodeToHTML(schema: Schema, node: ProsemirrorNode) {
51  return generateHTML(() => DOMSerializer.fromSchema(schema).serializeNode(node));
52}
53
54export function fragmentToHTML(schema: Schema, fragment: Fragment) {
55  return generateHTML(() => DOMSerializer.fromSchema(schema).serializeFragment(fragment));
56}
57
58function generateHTML(generator: () => Node | DocumentFragment) {
59  const div = document.createElement('div');
60  const output = generator();
61  div.appendChild(output);
62  return div.innerHTML;
63}
64