lake-html 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -63,6 +63,14 @@ function getBoxRenderers() {
63
63
  }),
64
64
  isVoid: true
65
65
  }),
66
+ media: (boxValue) => ({
67
+ tagName: "video",
68
+ attributes: __spreadProps(__spreadValues(__spreadValues({
69
+ src: boxValue.url
70
+ }, boxValue.width && { width: boxValue.width.replace(/px$/i, "") }), boxValue.height && { height: boxValue.height.replace(/px$/i, "") }), {
71
+ controls: "true"
72
+ })
73
+ }),
66
74
  file: (boxValue, encode) => ({
67
75
  tagName: "a",
68
76
  attributes: {
@@ -106,12 +114,13 @@ function getBoxRenderers() {
106
114
  video: (boxValue) => ({
107
115
  tagName: "iframe",
108
116
  attributes: __spreadProps(__spreadValues({}, boxValue.url && { src: `https://www.youtube.com/embed/${extractIdFromUrl(boxValue.url)}` }), {
117
+ width: boxValue.width ? boxValue.width.replace(/px$/i, "") : "560",
118
+ height: boxValue.height ? boxValue.height.replace(/px$/i, "") : "315",
109
119
  title: "YouTube video player",
110
120
  frameborder: "0",
111
121
  allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",
112
122
  referrerpolicy: "strict-origin-when-cross-origin",
113
- allowfullscreen: "true",
114
- style: "width: 560px; height: 315px;"
123
+ allowfullscreen: "true"
115
124
  })
116
125
  }),
117
126
  twitter: (boxValue) => ({
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Define a generic type for HTML attributes (key-value pairs)\ntype AttributeMap = Record<string, string>;\n\n// Structure representing the output HTML node\ninterface BoxHTMLNode {\n tagName: string;\n attributes?: AttributeMap;\n isVoid?: boolean; // True for self-closing tags like <img />, <hr />\n innerHTML?: string;\n}\n\n// Function signature for encoding HTML entities\ntype EncodeHTMLEntities = (value: string) => string;\n\n// Function signature for encoding HTML entities\ntype RenderBox = (boxValue: AttributeMap, encode: EncodeHTMLEntities) => BoxHTMLNode;\n\n// Registry of renderers for different box types\ntype BoxRenderers = Record<string, RenderBox>;\n\n/**\n * Extracts the ID from a URL.\n * Useful for extracting YouTube or Twitter IDs from clean URLs.\n */\nfunction extractIdFromUrl(url: string): string {\n const result = /[\\w\\-]+$/.exec(url);\n return result ? result[0] : '';\n}\n\n/**\n * Returns the default configuration for rendering various Lake attributes.\n */\nexport function getBoxRenderers(): BoxRenderers {\n return {\n hr: () => ({\n tagName: 'div',\n attributes: {\n class: 'lake-box-block lake-hr',\n },\n innerHTML: '<hr />',\n }),\n\n image: boxValue => ({\n tagName: 'img',\n attributes: {\n src: boxValue.url,\n ...(boxValue.width && { width: boxValue.width }),\n ...(boxValue.height && { height: boxValue.height }),\n ...(boxValue.caption && { alt: boxValue.caption }),\n border: '0',\n },\n isVoid: true,\n }),\n\n file: (boxValue, encode) => ({\n tagName: 'a',\n attributes: {\n href: boxValue.url,\n target: '_blank',\n },\n innerHTML: encode(boxValue.name),\n }),\n\n codeBlock: (boxValue, encode) => ({\n tagName: 'pre',\n attributes: {\n class: `lang-${boxValue.lang}`,\n },\n innerHTML: `<code>${encode(boxValue.code)}</code>`,\n }),\n\n emoji: boxValue => ({\n tagName: 'img',\n attributes: {\n src: boxValue.url,\n width: '32',\n height: '32',\n border: '0',\n },\n isVoid: true,\n }),\n\n equation: boxValue => ({\n tagName: 'code',\n innerHTML: boxValue.code,\n }),\n\n mention: (boxValue, encode) => ({\n tagName: 'a',\n attributes: {\n href: `/${boxValue.name}`,\n target: '_blank',\n },\n innerHTML: encode(boxValue.nickname ?? boxValue.name),\n }),\n\n video: boxValue => ({\n tagName: 'iframe',\n attributes: {\n ...(boxValue.url && { src: `https://www.youtube.com/embed/${extractIdFromUrl(boxValue.url)}` }),\n title: 'YouTube video player',\n frameborder: '0',\n allow: 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share',\n referrerpolicy: 'strict-origin-when-cross-origin',\n allowfullscreen: 'true',\n style: 'width: 560px; height: 315px;',\n },\n }),\n\n twitter: boxValue => ({\n tagName: 'iframe',\n attributes: {\n ...(boxValue.url && { src: `https://platform.twitter.com/embed/Tweet.html?id=${extractIdFromUrl(boxValue.url)}` }),\n title: 'Twitter tweet',\n scrolling: 'no',\n frameborder: '0',\n allowtransparency: 'true',\n allowfullscreen: 'true',\n style: 'width: 550px; height: 300px;',\n },\n }),\n };\n}\n\n// Map for reserved HTML characters\nconst htmlEntityMap = new Map([\n ['&', '&amp;'],\n ['<', '&lt;'],\n ['>', '&gt;'],\n ['\"', '&quot;'],\n ['\\xA0', '&nbsp;'],\n]);\n\n/**\n * Escapes reserved HTML characters to prevent XSS and rendering issues.\n */\nfunction encodeHTMLEntities(value: string): string {\n return value.replace(/[&<>\"\\xA0]/g, match => htmlEntityMap.get(match)!);\n}\n\n/**\n * Decodes a Base64 encoded string with UTF-8 support.\n */\nfunction decodeBase64(value: string): string {\n const binaryString = atob(value);\n const byteArray = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n byteArray[i] = binaryString.charCodeAt(i);\n }\n const decoder = new TextDecoder();\n return decoder.decode(byteArray);\n}\n\n/**\n * Parses HTML tag attributes from a string into a key-value object.\n */\nfunction parseAttributes(tag: string): AttributeMap {\n const attributes: AttributeMap = {};\n // Regex breakdown:\n // Group 1/2: Key=Value (unquoted)\n // Group 3/4: Key=\"Value\" (double quoted)\n // Group 5/6: Key='Value' (single quoted)\n const reg = /\\s+(?:([\\w\\-:]+)=([^\\s\"'<>]+)|([\\w\\-:\"]+)=\"([^\"]*)\"|([\\w\\-:\"]+)='([^']*)')(?=[\\s/>])/g;\n let match: RegExpExecArray | null;\n while ((match = reg.exec(tag))) {\n const key = (match[1] || match[3] || match[5]).toLowerCase();\n const value = match[1] ? match[2] : (match[3] ? match[4] : match[6]);\n attributes[key] = value;\n }\n return attributes;\n}\n\n/**\n * Serializes an attribute map into an HTML attribute string.\n */\nfunction serializeAttributes(attrs: AttributeMap): string {\n const result: string[] = [];\n for (const key of Object.keys(attrs)) {\n const value = String(attrs[key]);\n result.push(`${key}=\"${encodeHTMLEntities(value)}\"`);\n }\n return result.join(' ');\n}\n\n/**\n * Serializes a BoxHTMLNode object into a standard HTML string.\n */\nfunction toBoxHTML(node: BoxHTMLNode): string {\n let result = `<${node.tagName}`;\n if (node.attributes) {\n result += ` ${serializeAttributes(node.attributes)}`;\n }\n if (node.isVoid === true) {\n result += ' />';\n } else {\n result += `>${node.innerHTML ?? ''}</${node.tagName}>`;\n }\n return result;\n}\n\n/**\n * Main function to convert Lake Markup Language (LML) to standard HTML.\n * It processes custom <lake-box> tags and removes internal anchors.\n */\nexport function toHTML(value: string, renderers?: BoxRenderers): string {\n renderers = renderers ?? getBoxRenderers();\n // Regex to match <lake-box>, <anchor>, and <focus> tags\n const combinedRegex = /(<lake-box[^>]+>)[\\s\\S]*?(?:<\\/lake-box>|$)|(<anchor\\s*\\/>)|(<focus\\s*\\/>)/gi;\n return value.replace(combinedRegex, (match, boxOpen) => {\n // Handle lake-box conversion\n if (boxOpen) {\n const attributes = parseAttributes(boxOpen);\n const render = renderers[attributes.name];\n if (render) {\n try {\n const boxValue = attributes.value ? JSON.parse(decodeBase64(attributes.value)) : {};\n const node = render(boxValue, encodeHTMLEntities);\n return toBoxHTML(node);\n } catch (e) {\n console.error('Failed to parse lake-box value:', e);\n }\n }\n }\n // Remove internal selection markers (<anchor /> and <focus />)\n return '';\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Define a generic type for HTML attributes (key-value pairs)\ntype AttributeMap = Record<string, string>;\n\n// Structure representing the output HTML node\ninterface BoxHTMLNode {\n tagName: string;\n attributes?: AttributeMap;\n isVoid?: boolean; // True for self-closing tags like <img />, <hr />\n innerHTML?: string;\n}\n\n// Function signature for encoding HTML entities\ntype EncodeHTMLEntities = (value: string) => string;\n\n// Function signature for encoding HTML entities\ntype RenderBox = (boxValue: AttributeMap, encode: EncodeHTMLEntities) => BoxHTMLNode;\n\n// Registry of renderers for different box types\ntype BoxRenderers = Record<string, RenderBox>;\n\n/**\n * Extracts the ID from a URL.\n * Useful for extracting YouTube or Twitter IDs from clean URLs.\n */\nfunction extractIdFromUrl(url: string): string {\n const result = /[\\w\\-]+$/.exec(url);\n return result ? result[0] : '';\n}\n\n/**\n * Returns the default configuration for rendering various Lake attributes.\n */\nexport function getBoxRenderers(): BoxRenderers {\n return {\n hr: () => ({\n tagName: 'div',\n attributes: {\n class: 'lake-box-block lake-hr',\n },\n innerHTML: '<hr />',\n }),\n\n image: boxValue => ({\n tagName: 'img',\n attributes: {\n src: boxValue.url,\n ...(boxValue.width && { width: boxValue.width }),\n ...(boxValue.height && { height: boxValue.height }),\n ...(boxValue.caption && { alt: boxValue.caption }),\n border: '0',\n },\n isVoid: true,\n }),\n\n media: boxValue => ({\n tagName: 'video',\n attributes: {\n src: boxValue.url,\n ...(boxValue.width && { width: boxValue.width.replace(/px$/i, '') }),\n ...(boxValue.height && { height: boxValue.height.replace(/px$/i, '') }),\n controls: 'true',\n },\n }),\n\n file: (boxValue, encode) => ({\n tagName: 'a',\n attributes: {\n href: boxValue.url,\n target: '_blank',\n },\n innerHTML: encode(boxValue.name),\n }),\n\n codeBlock: (boxValue, encode) => ({\n tagName: 'pre',\n attributes: {\n class: `lang-${boxValue.lang}`,\n },\n innerHTML: `<code>${encode(boxValue.code)}</code>`,\n }),\n\n emoji: boxValue => ({\n tagName: 'img',\n attributes: {\n src: boxValue.url,\n width: '32',\n height: '32',\n border: '0',\n },\n isVoid: true,\n }),\n\n equation: boxValue => ({\n tagName: 'code',\n innerHTML: boxValue.code,\n }),\n\n mention: (boxValue, encode) => ({\n tagName: 'a',\n attributes: {\n href: `/${boxValue.name}`,\n target: '_blank',\n },\n innerHTML: encode(boxValue.nickname ?? boxValue.name),\n }),\n\n video: boxValue => ({\n tagName: 'iframe',\n attributes: {\n ...(boxValue.url && { src: `https://www.youtube.com/embed/${extractIdFromUrl(boxValue.url)}` }),\n width: boxValue.width ? boxValue.width.replace(/px$/i, '') : '560',\n height: boxValue.height ? boxValue.height.replace(/px$/i, '') : '315',\n title: 'YouTube video player',\n frameborder: '0',\n allow: 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share',\n referrerpolicy: 'strict-origin-when-cross-origin',\n allowfullscreen: 'true',\n },\n }),\n\n twitter: boxValue => ({\n tagName: 'iframe',\n attributes: {\n ...(boxValue.url && { src: `https://platform.twitter.com/embed/Tweet.html?id=${extractIdFromUrl(boxValue.url)}` }),\n title: 'Twitter tweet',\n scrolling: 'no',\n frameborder: '0',\n allowtransparency: 'true',\n allowfullscreen: 'true',\n style: 'width: 550px; height: 300px;',\n },\n }),\n };\n}\n\n// Map for reserved HTML characters\nconst htmlEntityMap = new Map([\n ['&', '&amp;'],\n ['<', '&lt;'],\n ['>', '&gt;'],\n ['\"', '&quot;'],\n ['\\xA0', '&nbsp;'],\n]);\n\n/**\n * Escapes reserved HTML characters to prevent XSS and rendering issues.\n */\nfunction encodeHTMLEntities(value: string): string {\n return value.replace(/[&<>\"\\xA0]/g, match => htmlEntityMap.get(match)!);\n}\n\n/**\n * Decodes a Base64 encoded string with UTF-8 support.\n */\nfunction decodeBase64(value: string): string {\n const binaryString = atob(value);\n const byteArray = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n byteArray[i] = binaryString.charCodeAt(i);\n }\n const decoder = new TextDecoder();\n return decoder.decode(byteArray);\n}\n\n/**\n * Parses HTML tag attributes from a string into a key-value object.\n */\nfunction parseAttributes(tag: string): AttributeMap {\n const attributes: AttributeMap = {};\n // Regex breakdown:\n // Group 1/2: Key=Value (unquoted)\n // Group 3/4: Key=\"Value\" (double quoted)\n // Group 5/6: Key='Value' (single quoted)\n const reg = /\\s+(?:([\\w\\-:]+)=([^\\s\"'<>]+)|([\\w\\-:\"]+)=\"([^\"]*)\"|([\\w\\-:\"]+)='([^']*)')(?=[\\s/>])/g;\n let match: RegExpExecArray | null;\n while ((match = reg.exec(tag))) {\n const key = (match[1] || match[3] || match[5]).toLowerCase();\n const value = match[1] ? match[2] : (match[3] ? match[4] : match[6]);\n attributes[key] = value;\n }\n return attributes;\n}\n\n/**\n * Serializes an attribute map into an HTML attribute string.\n */\nfunction serializeAttributes(attrs: AttributeMap): string {\n const result: string[] = [];\n for (const key of Object.keys(attrs)) {\n const value = String(attrs[key]);\n result.push(`${key}=\"${encodeHTMLEntities(value)}\"`);\n }\n return result.join(' ');\n}\n\n/**\n * Serializes a BoxHTMLNode object into a standard HTML string.\n */\nfunction toBoxHTML(node: BoxHTMLNode): string {\n let result = `<${node.tagName}`;\n if (node.attributes) {\n result += ` ${serializeAttributes(node.attributes)}`;\n }\n if (node.isVoid === true) {\n result += ' />';\n } else {\n result += `>${node.innerHTML ?? ''}</${node.tagName}>`;\n }\n return result;\n}\n\n/**\n * Main function to convert Lake Markup Language (LML) to standard HTML.\n * It processes custom <lake-box> tags and removes internal anchors.\n */\nexport function toHTML(value: string, renderers?: BoxRenderers): string {\n renderers = renderers ?? getBoxRenderers();\n // Regex to match <lake-box>, <anchor>, and <focus> tags\n const combinedRegex = /(<lake-box[^>]+>)[\\s\\S]*?(?:<\\/lake-box>|$)|(<anchor\\s*\\/>)|(<focus\\s*\\/>)/gi;\n return value.replace(combinedRegex, (match, boxOpen) => {\n // Handle lake-box conversion\n if (boxOpen) {\n const attributes = parseAttributes(boxOpen);\n const render = renderers[attributes.name];\n if (render) {\n try {\n const boxValue = attributes.value ? JSON.parse(decodeBase64(attributes.value)) : {};\n const node = render(boxValue, encodeHTMLEntities);\n return toBoxHTML(node);\n } catch (e) {\n console.error('Failed to parse lake-box value:', e);\n }\n }\n }\n // Remove internal selection markers (<anchor /> and <focus />)\n return '';\n });\n}\n"],"mappings":"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","names":[]}
package/dist/index.mjs CHANGED
@@ -41,6 +41,14 @@ function getBoxRenderers() {
41
41
  }),
42
42
  isVoid: true
43
43
  }),
44
+ media: (boxValue) => ({
45
+ tagName: "video",
46
+ attributes: __spreadProps(__spreadValues(__spreadValues({
47
+ src: boxValue.url
48
+ }, boxValue.width && { width: boxValue.width.replace(/px$/i, "") }), boxValue.height && { height: boxValue.height.replace(/px$/i, "") }), {
49
+ controls: "true"
50
+ })
51
+ }),
44
52
  file: (boxValue, encode) => ({
45
53
  tagName: "a",
46
54
  attributes: {
@@ -84,12 +92,13 @@ function getBoxRenderers() {
84
92
  video: (boxValue) => ({
85
93
  tagName: "iframe",
86
94
  attributes: __spreadProps(__spreadValues({}, boxValue.url && { src: `https://www.youtube.com/embed/${extractIdFromUrl(boxValue.url)}` }), {
95
+ width: boxValue.width ? boxValue.width.replace(/px$/i, "") : "560",
96
+ height: boxValue.height ? boxValue.height.replace(/px$/i, "") : "315",
87
97
  title: "YouTube video player",
88
98
  frameborder: "0",
89
99
  allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",
90
100
  referrerpolicy: "strict-origin-when-cross-origin",
91
- allowfullscreen: "true",
92
- style: "width: 560px; height: 315px;"
101
+ allowfullscreen: "true"
93
102
  })
94
103
  }),
95
104
  twitter: (boxValue) => ({
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Define a generic type for HTML attributes (key-value pairs)\ntype AttributeMap = Record<string, string>;\n\n// Structure representing the output HTML node\ninterface BoxHTMLNode {\n tagName: string;\n attributes?: AttributeMap;\n isVoid?: boolean; // True for self-closing tags like <img />, <hr />\n innerHTML?: string;\n}\n\n// Function signature for encoding HTML entities\ntype EncodeHTMLEntities = (value: string) => string;\n\n// Function signature for encoding HTML entities\ntype RenderBox = (boxValue: AttributeMap, encode: EncodeHTMLEntities) => BoxHTMLNode;\n\n// Registry of renderers for different box types\ntype BoxRenderers = Record<string, RenderBox>;\n\n/**\n * Extracts the ID from a URL.\n * Useful for extracting YouTube or Twitter IDs from clean URLs.\n */\nfunction extractIdFromUrl(url: string): string {\n const result = /[\\w\\-]+$/.exec(url);\n return result ? result[0] : '';\n}\n\n/**\n * Returns the default configuration for rendering various Lake attributes.\n */\nexport function getBoxRenderers(): BoxRenderers {\n return {\n hr: () => ({\n tagName: 'div',\n attributes: {\n class: 'lake-box-block lake-hr',\n },\n innerHTML: '<hr />',\n }),\n\n image: boxValue => ({\n tagName: 'img',\n attributes: {\n src: boxValue.url,\n ...(boxValue.width && { width: boxValue.width }),\n ...(boxValue.height && { height: boxValue.height }),\n ...(boxValue.caption && { alt: boxValue.caption }),\n border: '0',\n },\n isVoid: true,\n }),\n\n file: (boxValue, encode) => ({\n tagName: 'a',\n attributes: {\n href: boxValue.url,\n target: '_blank',\n },\n innerHTML: encode(boxValue.name),\n }),\n\n codeBlock: (boxValue, encode) => ({\n tagName: 'pre',\n attributes: {\n class: `lang-${boxValue.lang}`,\n },\n innerHTML: `<code>${encode(boxValue.code)}</code>`,\n }),\n\n emoji: boxValue => ({\n tagName: 'img',\n attributes: {\n src: boxValue.url,\n width: '32',\n height: '32',\n border: '0',\n },\n isVoid: true,\n }),\n\n equation: boxValue => ({\n tagName: 'code',\n innerHTML: boxValue.code,\n }),\n\n mention: (boxValue, encode) => ({\n tagName: 'a',\n attributes: {\n href: `/${boxValue.name}`,\n target: '_blank',\n },\n innerHTML: encode(boxValue.nickname ?? boxValue.name),\n }),\n\n video: boxValue => ({\n tagName: 'iframe',\n attributes: {\n ...(boxValue.url && { src: `https://www.youtube.com/embed/${extractIdFromUrl(boxValue.url)}` }),\n title: 'YouTube video player',\n frameborder: '0',\n allow: 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share',\n referrerpolicy: 'strict-origin-when-cross-origin',\n allowfullscreen: 'true',\n style: 'width: 560px; height: 315px;',\n },\n }),\n\n twitter: boxValue => ({\n tagName: 'iframe',\n attributes: {\n ...(boxValue.url && { src: `https://platform.twitter.com/embed/Tweet.html?id=${extractIdFromUrl(boxValue.url)}` }),\n title: 'Twitter tweet',\n scrolling: 'no',\n frameborder: '0',\n allowtransparency: 'true',\n allowfullscreen: 'true',\n style: 'width: 550px; height: 300px;',\n },\n }),\n };\n}\n\n// Map for reserved HTML characters\nconst htmlEntityMap = new Map([\n ['&', '&amp;'],\n ['<', '&lt;'],\n ['>', '&gt;'],\n ['\"', '&quot;'],\n ['\\xA0', '&nbsp;'],\n]);\n\n/**\n * Escapes reserved HTML characters to prevent XSS and rendering issues.\n */\nfunction encodeHTMLEntities(value: string): string {\n return value.replace(/[&<>\"\\xA0]/g, match => htmlEntityMap.get(match)!);\n}\n\n/**\n * Decodes a Base64 encoded string with UTF-8 support.\n */\nfunction decodeBase64(value: string): string {\n const binaryString = atob(value);\n const byteArray = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n byteArray[i] = binaryString.charCodeAt(i);\n }\n const decoder = new TextDecoder();\n return decoder.decode(byteArray);\n}\n\n/**\n * Parses HTML tag attributes from a string into a key-value object.\n */\nfunction parseAttributes(tag: string): AttributeMap {\n const attributes: AttributeMap = {};\n // Regex breakdown:\n // Group 1/2: Key=Value (unquoted)\n // Group 3/4: Key=\"Value\" (double quoted)\n // Group 5/6: Key='Value' (single quoted)\n const reg = /\\s+(?:([\\w\\-:]+)=([^\\s\"'<>]+)|([\\w\\-:\"]+)=\"([^\"]*)\"|([\\w\\-:\"]+)='([^']*)')(?=[\\s/>])/g;\n let match: RegExpExecArray | null;\n while ((match = reg.exec(tag))) {\n const key = (match[1] || match[3] || match[5]).toLowerCase();\n const value = match[1] ? match[2] : (match[3] ? match[4] : match[6]);\n attributes[key] = value;\n }\n return attributes;\n}\n\n/**\n * Serializes an attribute map into an HTML attribute string.\n */\nfunction serializeAttributes(attrs: AttributeMap): string {\n const result: string[] = [];\n for (const key of Object.keys(attrs)) {\n const value = String(attrs[key]);\n result.push(`${key}=\"${encodeHTMLEntities(value)}\"`);\n }\n return result.join(' ');\n}\n\n/**\n * Serializes a BoxHTMLNode object into a standard HTML string.\n */\nfunction toBoxHTML(node: BoxHTMLNode): string {\n let result = `<${node.tagName}`;\n if (node.attributes) {\n result += ` ${serializeAttributes(node.attributes)}`;\n }\n if (node.isVoid === true) {\n result += ' />';\n } else {\n result += `>${node.innerHTML ?? ''}</${node.tagName}>`;\n }\n return result;\n}\n\n/**\n * Main function to convert Lake Markup Language (LML) to standard HTML.\n * It processes custom <lake-box> tags and removes internal anchors.\n */\nexport function toHTML(value: string, renderers?: BoxRenderers): string {\n renderers = renderers ?? getBoxRenderers();\n // Regex to match <lake-box>, <anchor>, and <focus> tags\n const combinedRegex = /(<lake-box[^>]+>)[\\s\\S]*?(?:<\\/lake-box>|$)|(<anchor\\s*\\/>)|(<focus\\s*\\/>)/gi;\n return value.replace(combinedRegex, (match, boxOpen) => {\n // Handle lake-box conversion\n if (boxOpen) {\n const attributes = parseAttributes(boxOpen);\n const render = renderers[attributes.name];\n if (render) {\n try {\n const boxValue = attributes.value ? JSON.parse(decodeBase64(attributes.value)) : {};\n const node = render(boxValue, encodeHTMLEntities);\n return toBoxHTML(node);\n } catch (e) {\n console.error('Failed to parse lake-box value:', e);\n }\n }\n }\n // Remove internal selection markers (<anchor /> and <focus />)\n return '';\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Define a generic type for HTML attributes (key-value pairs)\ntype AttributeMap = Record<string, string>;\n\n// Structure representing the output HTML node\ninterface BoxHTMLNode {\n tagName: string;\n attributes?: AttributeMap;\n isVoid?: boolean; // True for self-closing tags like <img />, <hr />\n innerHTML?: string;\n}\n\n// Function signature for encoding HTML entities\ntype EncodeHTMLEntities = (value: string) => string;\n\n// Function signature for encoding HTML entities\ntype RenderBox = (boxValue: AttributeMap, encode: EncodeHTMLEntities) => BoxHTMLNode;\n\n// Registry of renderers for different box types\ntype BoxRenderers = Record<string, RenderBox>;\n\n/**\n * Extracts the ID from a URL.\n * Useful for extracting YouTube or Twitter IDs from clean URLs.\n */\nfunction extractIdFromUrl(url: string): string {\n const result = /[\\w\\-]+$/.exec(url);\n return result ? result[0] : '';\n}\n\n/**\n * Returns the default configuration for rendering various Lake attributes.\n */\nexport function getBoxRenderers(): BoxRenderers {\n return {\n hr: () => ({\n tagName: 'div',\n attributes: {\n class: 'lake-box-block lake-hr',\n },\n innerHTML: '<hr />',\n }),\n\n image: boxValue => ({\n tagName: 'img',\n attributes: {\n src: boxValue.url,\n ...(boxValue.width && { width: boxValue.width }),\n ...(boxValue.height && { height: boxValue.height }),\n ...(boxValue.caption && { alt: boxValue.caption }),\n border: '0',\n },\n isVoid: true,\n }),\n\n media: boxValue => ({\n tagName: 'video',\n attributes: {\n src: boxValue.url,\n ...(boxValue.width && { width: boxValue.width.replace(/px$/i, '') }),\n ...(boxValue.height && { height: boxValue.height.replace(/px$/i, '') }),\n controls: 'true',\n },\n }),\n\n file: (boxValue, encode) => ({\n tagName: 'a',\n attributes: {\n href: boxValue.url,\n target: '_blank',\n },\n innerHTML: encode(boxValue.name),\n }),\n\n codeBlock: (boxValue, encode) => ({\n tagName: 'pre',\n attributes: {\n class: `lang-${boxValue.lang}`,\n },\n innerHTML: `<code>${encode(boxValue.code)}</code>`,\n }),\n\n emoji: boxValue => ({\n tagName: 'img',\n attributes: {\n src: boxValue.url,\n width: '32',\n height: '32',\n border: '0',\n },\n isVoid: true,\n }),\n\n equation: boxValue => ({\n tagName: 'code',\n innerHTML: boxValue.code,\n }),\n\n mention: (boxValue, encode) => ({\n tagName: 'a',\n attributes: {\n href: `/${boxValue.name}`,\n target: '_blank',\n },\n innerHTML: encode(boxValue.nickname ?? boxValue.name),\n }),\n\n video: boxValue => ({\n tagName: 'iframe',\n attributes: {\n ...(boxValue.url && { src: `https://www.youtube.com/embed/${extractIdFromUrl(boxValue.url)}` }),\n width: boxValue.width ? boxValue.width.replace(/px$/i, '') : '560',\n height: boxValue.height ? boxValue.height.replace(/px$/i, '') : '315',\n title: 'YouTube video player',\n frameborder: '0',\n allow: 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share',\n referrerpolicy: 'strict-origin-when-cross-origin',\n allowfullscreen: 'true',\n },\n }),\n\n twitter: boxValue => ({\n tagName: 'iframe',\n attributes: {\n ...(boxValue.url && { src: `https://platform.twitter.com/embed/Tweet.html?id=${extractIdFromUrl(boxValue.url)}` }),\n title: 'Twitter tweet',\n scrolling: 'no',\n frameborder: '0',\n allowtransparency: 'true',\n allowfullscreen: 'true',\n style: 'width: 550px; height: 300px;',\n },\n }),\n };\n}\n\n// Map for reserved HTML characters\nconst htmlEntityMap = new Map([\n ['&', '&amp;'],\n ['<', '&lt;'],\n ['>', '&gt;'],\n ['\"', '&quot;'],\n ['\\xA0', '&nbsp;'],\n]);\n\n/**\n * Escapes reserved HTML characters to prevent XSS and rendering issues.\n */\nfunction encodeHTMLEntities(value: string): string {\n return value.replace(/[&<>\"\\xA0]/g, match => htmlEntityMap.get(match)!);\n}\n\n/**\n * Decodes a Base64 encoded string with UTF-8 support.\n */\nfunction decodeBase64(value: string): string {\n const binaryString = atob(value);\n const byteArray = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n byteArray[i] = binaryString.charCodeAt(i);\n }\n const decoder = new TextDecoder();\n return decoder.decode(byteArray);\n}\n\n/**\n * Parses HTML tag attributes from a string into a key-value object.\n */\nfunction parseAttributes(tag: string): AttributeMap {\n const attributes: AttributeMap = {};\n // Regex breakdown:\n // Group 1/2: Key=Value (unquoted)\n // Group 3/4: Key=\"Value\" (double quoted)\n // Group 5/6: Key='Value' (single quoted)\n const reg = /\\s+(?:([\\w\\-:]+)=([^\\s\"'<>]+)|([\\w\\-:\"]+)=\"([^\"]*)\"|([\\w\\-:\"]+)='([^']*)')(?=[\\s/>])/g;\n let match: RegExpExecArray | null;\n while ((match = reg.exec(tag))) {\n const key = (match[1] || match[3] || match[5]).toLowerCase();\n const value = match[1] ? match[2] : (match[3] ? match[4] : match[6]);\n attributes[key] = value;\n }\n return attributes;\n}\n\n/**\n * Serializes an attribute map into an HTML attribute string.\n */\nfunction serializeAttributes(attrs: AttributeMap): string {\n const result: string[] = [];\n for (const key of Object.keys(attrs)) {\n const value = String(attrs[key]);\n result.push(`${key}=\"${encodeHTMLEntities(value)}\"`);\n }\n return result.join(' ');\n}\n\n/**\n * Serializes a BoxHTMLNode object into a standard HTML string.\n */\nfunction toBoxHTML(node: BoxHTMLNode): string {\n let result = `<${node.tagName}`;\n if (node.attributes) {\n result += ` ${serializeAttributes(node.attributes)}`;\n }\n if (node.isVoid === true) {\n result += ' />';\n } else {\n result += `>${node.innerHTML ?? ''}</${node.tagName}>`;\n }\n return result;\n}\n\n/**\n * Main function to convert Lake Markup Language (LML) to standard HTML.\n * It processes custom <lake-box> tags and removes internal anchors.\n */\nexport function toHTML(value: string, renderers?: BoxRenderers): string {\n renderers = renderers ?? getBoxRenderers();\n // Regex to match <lake-box>, <anchor>, and <focus> tags\n const combinedRegex = /(<lake-box[^>]+>)[\\s\\S]*?(?:<\\/lake-box>|$)|(<anchor\\s*\\/>)|(<focus\\s*\\/>)/gi;\n return value.replace(combinedRegex, (match, boxOpen) => {\n // Handle lake-box conversion\n if (boxOpen) {\n const attributes = parseAttributes(boxOpen);\n const render = renderers[attributes.name];\n if (render) {\n try {\n const boxValue = attributes.value ? JSON.parse(decodeBase64(attributes.value)) : {};\n const node = render(boxValue, encodeHTMLEntities);\n return toBoxHTML(node);\n } catch (e) {\n console.error('Failed to parse lake-box value:', e);\n }\n }\n }\n // Remove internal selection markers (<anchor /> and <focus />)\n return '';\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwBA,SAAS,iBAAiB,KAAqB;AAC7C,QAAM,SAAS,WAAW,KAAK,GAAG;AAClC,SAAO,SAAS,OAAO,CAAC,IAAI;AAC9B;AAKO,SAAS,kBAAgC;AAC9C,SAAO;AAAA,IACL,IAAI,OAAO;AAAA,MACT,SAAS;AAAA,MACT,YAAY;AAAA,QACV,OAAO;AAAA,MACT;AAAA,MACA,WAAW;AAAA,IACb;AAAA,IAEA,OAAO,eAAa;AAAA,MAClB,SAAS;AAAA,MACT,YAAY;AAAA,QACV,KAAK,SAAS;AAAA,SACV,SAAS,SAAS,EAAE,OAAO,SAAS,MAAM,IAC1C,SAAS,UAAU,EAAE,QAAQ,SAAS,OAAO,IAC7C,SAAS,WAAW,EAAE,KAAK,SAAS,QAAQ,IAJtC;AAAA,QAKV,QAAQ;AAAA,MACV;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,IAEA,OAAO,eAAa;AAAA,MAClB,SAAS;AAAA,MACT,YAAY;AAAA,QACV,KAAK,SAAS;AAAA,SACV,SAAS,SAAS,EAAE,OAAO,SAAS,MAAM,QAAQ,QAAQ,EAAE,EAAE,IAC9D,SAAS,UAAU,EAAE,QAAQ,SAAS,OAAO,QAAQ,QAAQ,EAAE,EAAE,IAH3D;AAAA,QAIV,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,IAEA,MAAM,CAAC,UAAU,YAAY;AAAA,MAC3B,SAAS;AAAA,MACT,YAAY;AAAA,QACV,MAAM,SAAS;AAAA,QACf,QAAQ;AAAA,MACV;AAAA,MACA,WAAW,OAAO,SAAS,IAAI;AAAA,IACjC;AAAA,IAEA,WAAW,CAAC,UAAU,YAAY;AAAA,MAChC,SAAS;AAAA,MACT,YAAY;AAAA,QACV,OAAO,QAAQ,SAAS,IAAI;AAAA,MAC9B;AAAA,MACA,WAAW,SAAS,OAAO,SAAS,IAAI,CAAC;AAAA,IAC3C;AAAA,IAEA,OAAO,eAAa;AAAA,MAClB,SAAS;AAAA,MACT,YAAY;AAAA,QACV,KAAK,SAAS;AAAA,QACd,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,IAEA,UAAU,eAAa;AAAA,MACrB,SAAS;AAAA,MACT,WAAW,SAAS;AAAA,IACtB;AAAA,IAEA,SAAS,CAAC,UAAU,WAAQ;AAjGhC;AAiGoC;AAAA,QAC9B,SAAS;AAAA,QACT,YAAY;AAAA,UACV,MAAM,IAAI,SAAS,IAAI;AAAA,UACvB,QAAQ;AAAA,QACV;AAAA,QACA,WAAW,QAAO,cAAS,aAAT,YAAqB,SAAS,IAAI;AAAA,MACtD;AAAA;AAAA,IAEA,OAAO,eAAa;AAAA,MAClB,SAAS;AAAA,MACT,YAAY,iCACN,SAAS,OAAO,EAAE,KAAK,iCAAiC,iBAAiB,SAAS,GAAG,CAAC,GAAG,IADnF;AAAA,QAEV,OAAO,SAAS,QAAQ,SAAS,MAAM,QAAQ,QAAQ,EAAE,IAAI;AAAA,QAC7D,QAAQ,SAAS,SAAS,SAAS,OAAO,QAAQ,QAAQ,EAAE,IAAI;AAAA,QAChE,OAAO;AAAA,QACP,aAAa;AAAA,QACb,OAAO;AAAA,QACP,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,MACnB;AAAA,IACF;AAAA,IAEA,SAAS,eAAa;AAAA,MACpB,SAAS;AAAA,MACT,YAAY,iCACN,SAAS,OAAO,EAAE,KAAK,oDAAoD,iBAAiB,SAAS,GAAG,CAAC,GAAG,IADtG;AAAA,QAEV,OAAO;AAAA,QACP,WAAW;AAAA,QACX,aAAa;AAAA,QACb,mBAAmB;AAAA,QACnB,iBAAiB;AAAA,QACjB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;AAGA,IAAM,gBAAgB,oBAAI,IAAI;AAAA,EAC5B,CAAC,KAAK,OAAO;AAAA,EACb,CAAC,KAAK,MAAM;AAAA,EACZ,CAAC,KAAK,MAAM;AAAA,EACZ,CAAC,KAAK,QAAQ;AAAA,EACd,CAAC,QAAQ,QAAQ;AACnB,CAAC;AAKD,SAAS,mBAAmB,OAAuB;AACjD,SAAO,MAAM,QAAQ,eAAe,WAAS,cAAc,IAAI,KAAK,CAAE;AACxE;AAKA,SAAS,aAAa,OAAuB;AAC3C,QAAM,eAAe,KAAK,KAAK;AAC/B,QAAM,YAAY,IAAI,WAAW,aAAa,MAAM;AACpD,WAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;AAC5C,cAAU,CAAC,IAAI,aAAa,WAAW,CAAC;AAAA,EAC1C;AACA,QAAM,UAAU,IAAI,YAAY;AAChC,SAAO,QAAQ,OAAO,SAAS;AACjC;AAKA,SAAS,gBAAgB,KAA2B;AAClD,QAAM,aAA2B,CAAC;AAKlC,QAAM,MAAM;AACZ,MAAI;AACJ,SAAQ,QAAQ,IAAI,KAAK,GAAG,GAAI;AAC9B,UAAM,OAAO,MAAM,CAAC,KAAK,MAAM,CAAC,KAAK,MAAM,CAAC,GAAG,YAAY;AAC3D,UAAM,QAAQ,MAAM,CAAC,IAAI,MAAM,CAAC,IAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,MAAM,CAAC;AAClE,eAAW,GAAG,IAAI;AAAA,EACpB;AACA,SAAO;AACT;AAKA,SAAS,oBAAoB,OAA6B;AACxD,QAAM,SAAmB,CAAC;AAC1B,aAAW,OAAO,OAAO,KAAK,KAAK,GAAG;AACpC,UAAM,QAAQ,OAAO,MAAM,GAAG,CAAC;AAC/B,WAAO,KAAK,GAAG,GAAG,KAAK,mBAAmB,KAAK,CAAC,GAAG;AAAA,EACrD;AACA,SAAO,OAAO,KAAK,GAAG;AACxB;AAKA,SAAS,UAAU,MAA2B;AAtM9C;AAuME,MAAI,SAAS,IAAI,KAAK,OAAO;AAC7B,MAAI,KAAK,YAAY;AACnB,cAAU,IAAI,oBAAoB,KAAK,UAAU,CAAC;AAAA,EACpD;AACA,MAAI,KAAK,WAAW,MAAM;AACxB,cAAU;AAAA,EACZ,OAAO;AACL,cAAU,KAAI,UAAK,cAAL,YAAkB,EAAE,KAAK,KAAK,OAAO;AAAA,EACrD;AACA,SAAO;AACT;AAMO,SAAS,OAAO,OAAe,WAAkC;AACtE,cAAY,gCAAa,gBAAgB;AAEzC,QAAM,gBAAgB;AACtB,SAAO,MAAM,QAAQ,eAAe,CAAC,OAAO,YAAY;AAEtD,QAAI,SAAS;AACX,YAAM,aAAa,gBAAgB,OAAO;AAC1C,YAAM,SAAS,UAAU,WAAW,IAAI;AACxC,UAAI,QAAQ;AACV,YAAI;AACF,gBAAM,WAAW,WAAW,QAAQ,KAAK,MAAM,aAAa,WAAW,KAAK,CAAC,IAAI,CAAC;AAClF,gBAAM,OAAO,OAAO,UAAU,kBAAkB;AAChD,iBAAO,UAAU,IAAI;AAAA,QACvB,SAAS,GAAG;AACV,kBAAQ,MAAM,mCAAmC,CAAC;AAAA,QACpD;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT,CAAC;AACH;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lake-html",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "A lightweight utility to convert Lake Markup Language (LML) to HTML",
5
5
  "author": "Luo Longhao <luolonghao@gmail.com>",
6
6
  "license": "MIT",