mf2dom 0.1.11
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/LICENSE +17 -0
- package/README.md +90 -0
- package/dist/components.d.ts +93 -0
- package/dist/index.d.ts +25 -0
- package/dist/mf2dom.cjs +2 -0
- package/dist/mf2dom.cjs.map +7 -0
- package/dist/mf2dom.esm.js +2 -0
- package/dist/mf2dom.esm.js.map +7 -0
- package/dist/mf2dom.min.js +2 -0
- package/dist/mf2dom.min.js.map +7 -0
- package/dist/render-cli.d.ts +8 -0
- package/dist/render-standalone.d.ts +8 -0
- package/dist/renderer.d.ts +45 -0
- package/dist/types.d.ts +43 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
GNU AFFERO GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 3, 19 November 2007
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2026 Beto Dealmeida
|
|
5
|
+
|
|
6
|
+
This program is free software: you can redistribute it and/or modify
|
|
7
|
+
it under the terms of the GNU Affero General Public License as published
|
|
8
|
+
by the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
(at your option) any later version.
|
|
10
|
+
|
|
11
|
+
This program is distributed in the hope that it will be useful,
|
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
GNU Affero General Public License for more details.
|
|
15
|
+
|
|
16
|
+
You should have received a copy of the GNU Affero General Public License
|
|
17
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# mf2dom
|
|
2
|
+
|
|
3
|
+
Render [Microformats2](https://microformats.org/wiki/microformats2) JSON to semantic HTML.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install mf2dom
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Browser (ESM)
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
import { render, renderItems, renderItemElement } from 'mf2dom';
|
|
17
|
+
|
|
18
|
+
// Render a full mf2 document
|
|
19
|
+
const html = render(mf2Document);
|
|
20
|
+
|
|
21
|
+
// Render multiple items to a DocumentFragment
|
|
22
|
+
const fragment = renderItems(items, { topHeading: 2 });
|
|
23
|
+
document.querySelector('main').appendChild(fragment);
|
|
24
|
+
|
|
25
|
+
// Render a single item to an HTMLElement
|
|
26
|
+
const element = renderItemElement(item, { topHeading: 2 });
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Browser (IIFE)
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<script src="https://unpkg.com/mf2dom/dist/mf2dom.min.js"></script>
|
|
33
|
+
<script>
|
|
34
|
+
const html = mf2dom.render(mf2Document);
|
|
35
|
+
</script>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Node.js
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
import { render } from 'mf2dom';
|
|
42
|
+
import { JSDOM } from 'jsdom';
|
|
43
|
+
|
|
44
|
+
// Set up global document for Node.js
|
|
45
|
+
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>');
|
|
46
|
+
global.document = dom.window.document;
|
|
47
|
+
|
|
48
|
+
const html = render(mf2Document);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## API
|
|
52
|
+
|
|
53
|
+
### `render(doc, options?)`
|
|
54
|
+
|
|
55
|
+
Render an mf2 document to HTML.
|
|
56
|
+
|
|
57
|
+
- `doc`: Microformats2 document with `items` array
|
|
58
|
+
- `options.asElement`: Return HTMLElement instead of string (default: false)
|
|
59
|
+
- `options.topHeading`: Starting heading level for names (1-6)
|
|
60
|
+
|
|
61
|
+
### `renderItems(items, options?)`
|
|
62
|
+
|
|
63
|
+
Render multiple mf2 items to a DocumentFragment.
|
|
64
|
+
|
|
65
|
+
- `items`: Array of mf2 items
|
|
66
|
+
- `options.relUrls`: rel-urls from parsed document
|
|
67
|
+
- `options.topHeading`: Starting heading level
|
|
68
|
+
|
|
69
|
+
### `renderItemElement(item, options?)`
|
|
70
|
+
|
|
71
|
+
Render a single mf2 item to an HTMLElement.
|
|
72
|
+
|
|
73
|
+
- `item`: Single mf2 item
|
|
74
|
+
- `options.relUrls`: rel-urls from parsed document
|
|
75
|
+
- `options.topHeading`: Starting heading level
|
|
76
|
+
|
|
77
|
+
## Features
|
|
78
|
+
|
|
79
|
+
- Renders h-entry, h-card, h-feed, h-event, and other microformat types
|
|
80
|
+
- Semantic HTML output (article, address, time, etc.)
|
|
81
|
+
- Proper heading hierarchy with `topHeading` option
|
|
82
|
+
- Handles embedded items (author h-cards, location, etc.)
|
|
83
|
+
- Ruby annotations for names with IPA pronunciation
|
|
84
|
+
- Linked names when URL matches
|
|
85
|
+
- Media handling (photo, video, audio)
|
|
86
|
+
- Date/time formatting
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
AGPL-3.0-or-later
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Components for mf2 rendering
|
|
3
|
+
*
|
|
4
|
+
* Custom Elements that render mf2json data efficiently in the browser.
|
|
5
|
+
* Perfect for use with sql-wasm.js and client-side databases.
|
|
6
|
+
*/
|
|
7
|
+
import type { Mf2Item, Mf2Document, RelUrl } from "./types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Custom element that renders a single mf2 item.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* ```html
|
|
13
|
+
* <mf2-item></mf2-item>
|
|
14
|
+
* <script>
|
|
15
|
+
* const el = document.querySelector('mf2-item');
|
|
16
|
+
* el.item = { type: ['h-card'], properties: { name: ['Alice'] } };
|
|
17
|
+
* </script>
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* Or with JSON attribute:
|
|
21
|
+
* ```html
|
|
22
|
+
* <mf2-item data-json='{"type":["h-card"],"properties":{"name":["Alice"]}}'></mf2-item>
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare class Mf2ItemElement extends HTMLElement {
|
|
26
|
+
#private;
|
|
27
|
+
static observedAttributes: string[];
|
|
28
|
+
constructor();
|
|
29
|
+
/** The mf2 item to render */
|
|
30
|
+
get item(): Mf2Item | null;
|
|
31
|
+
set item(value: Mf2Item | null);
|
|
32
|
+
/** Optional rel-urls for link relations */
|
|
33
|
+
get relUrls(): Record<string, RelUrl> | undefined;
|
|
34
|
+
set relUrls(value: Record<string, RelUrl> | undefined);
|
|
35
|
+
connectedCallback(): void;
|
|
36
|
+
attributeChangedCallback(name: string, _oldValue: string, newValue: string): void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Custom element that renders an entire mf2 document.
|
|
40
|
+
*
|
|
41
|
+
* Usage:
|
|
42
|
+
* ```html
|
|
43
|
+
* <mf2-document></mf2-document>
|
|
44
|
+
* <script>
|
|
45
|
+
* const el = document.querySelector('mf2-document');
|
|
46
|
+
* el.document = { items: [...], rels: {}, 'rel-urls': {} };
|
|
47
|
+
* </script>
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare class Mf2DocumentElement extends HTMLElement {
|
|
51
|
+
#private;
|
|
52
|
+
static observedAttributes: string[];
|
|
53
|
+
constructor();
|
|
54
|
+
get document(): Mf2Document | null;
|
|
55
|
+
set document(value: Mf2Document | null);
|
|
56
|
+
connectedCallback(): void;
|
|
57
|
+
attributeChangedCallback(name: string, _oldValue: string, newValue: string): void;
|
|
58
|
+
}
|
|
59
|
+
export interface FeedOptions {
|
|
60
|
+
/** Number of items to render at once */
|
|
61
|
+
batchSize?: number;
|
|
62
|
+
/** Callback when more items are needed */
|
|
63
|
+
onLoadMore?: () => Promise<Mf2Item[]>;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Custom element for rendering large mf2 feeds with virtual scrolling support.
|
|
67
|
+
* Optimized for use with sql-wasm.js pagination.
|
|
68
|
+
*
|
|
69
|
+
* Usage:
|
|
70
|
+
* ```javascript
|
|
71
|
+
* const feed = document.querySelector('mf2-feed');
|
|
72
|
+
* feed.configure({ batchSize: 20, onLoadMore: () => fetchMore() });
|
|
73
|
+
* feed.items = initialItems;
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare class Mf2FeedElement extends HTMLElement {
|
|
77
|
+
#private;
|
|
78
|
+
constructor();
|
|
79
|
+
get items(): Mf2Item[];
|
|
80
|
+
set items(value: Mf2Item[]);
|
|
81
|
+
get relUrls(): Record<string, RelUrl> | undefined;
|
|
82
|
+
set relUrls(value: Record<string, RelUrl> | undefined);
|
|
83
|
+
configure(options: FeedOptions): void;
|
|
84
|
+
/** Append more items without re-rendering existing ones */
|
|
85
|
+
appendItems(items: Mf2Item[]): void;
|
|
86
|
+
connectedCallback(): void;
|
|
87
|
+
disconnectedCallback(): void;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Register all mf2 custom elements.
|
|
91
|
+
* Call this once at application startup.
|
|
92
|
+
*/
|
|
93
|
+
export declare function registerElements(): void;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mf2dom-ts - TypeScript Microformats2 Renderer
|
|
3
|
+
*
|
|
4
|
+
* Efficiently renders mf2json to semantic HTML using modern DOM APIs.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { render, renderItemElement, registerElements } from 'mf2dom-ts';
|
|
9
|
+
*
|
|
10
|
+
* // Render document to HTML string
|
|
11
|
+
* const html = render(mf2Document);
|
|
12
|
+
*
|
|
13
|
+
* // Render directly to DOM element
|
|
14
|
+
* const main = render(mf2Document, { asElement: true });
|
|
15
|
+
* document.body.appendChild(main);
|
|
16
|
+
*
|
|
17
|
+
* // Use custom elements
|
|
18
|
+
* registerElements();
|
|
19
|
+
* const el = document.createElement('mf2-item');
|
|
20
|
+
* el.item = { type: ['h-card'], properties: { name: ['Alice'] } };
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export type { Mf2Document, Mf2Item, PropertyValue, EValue, UrlObject, RelUrl, } from "./types.js";
|
|
24
|
+
export { render, renderItemElement, parseHtmlFragment } from "./renderer.js";
|
|
25
|
+
export { Mf2ItemElement, Mf2DocumentElement, Mf2FeedElement, registerElements, type FeedOptions, } from "./components.js";
|
package/dist/mf2dom.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var O=Object.defineProperty;var K=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var B=Object.prototype.hasOwnProperty;var z=(e,n)=>{for(var t in n)O(e,t,{get:n[t],enumerable:!0})},G=(e,n,t,i)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of q(n))!B.call(e,r)&&r!==t&&O(e,r,{get:()=>n[r],enumerable:!(i=K(n,r))||i.enumerable});return e};var J=e=>G(O({},"__esModule",{value:!0}),e);var he={};z(he,{parseHtmlFragment:()=>U,render:()=>fe,renderItemElement:()=>ge,renderItems:()=>me});module.exports=J(he);var V={"h-entry":"article","h-feed":"section","h-event":"article","h-product":"article","h-recipe":"article","h-review":"article","h-resume":"article","h-adr":"address","h-cite":"blockquote","h-geo":"data"},Q={"p-adr":"address","p-street-address":"span","p-extended-address":"span","p-locality":"span","p-region":"span","p-postal-code":"span","p-country-name":"span","p-name":"strong","p-summary":"p","p-note":"p","p-content":"p","p-description":"p","p-author":"span"},P=new Set(["url","uid","photo","logo","video","audio","syndication","in-reply-to","like-of","repost-of","bookmark-of","follow-of","read-of","tag-of","location"]),k=new Set(["email"]),$=new Set(["tel"]),I=new Set(["published","updated","start","end","duration","bday","anniversary","rev"]),w=["photo","logo","featured","name","honorific-prefix","given-name","additional-name","family-name","sort-string","honorific-suffix","nickname","ipa","author","summary","note","content","description","published","updated","start","end","duration","bday","anniversary","rev","location","url","uid","syndication","in-reply-to","like-of","repost-of","bookmark-of","follow-of","read-of","read-status","email","tel","impp","adr","geo","latitude","longitude","altitude","street-address","extended-address","locality","region","postal-code","country-name","label","org","job-title","role","category","rsvp","attendee","key","sex","gender-identity"];function a(e,n,t){let i=document.createElement(e);if(n)for(let[r,d]of Object.entries(n))d!=null&&i.setAttribute(r,d);if(t)for(let r of t)i.append(r);return i}function N(e){return document.createTextNode(e)}function U(e){let n=document.createElement("template");return n.innerHTML=e,n.content}function X(e){return typeof e=="object"&&e!==null&&"type"in e&&"properties"in e}function Y(e){return typeof e=="object"&&e!==null&&"html"in e}function Z(e){return typeof e=="object"&&e!==null&&"value"in e&&("alt"in e||"srcset"in e)}function C(e){return e.startsWith("http://")||e.startsWith("https://")||e.startsWith("/")}function v(e){let n=w.indexOf(e);return n>=0?n:w.length}function D(e){return Object.keys(e).sort((n,t)=>v(n)-v(t))}function g(...e){return e.filter(Boolean).join(" ")||void 0}function W(e,n){let t=n?.[e]?.rels;return t?.length?t.join(" "):void 0}function ee(e){for(let n of e)if(n in V)return V[n];return"div"}function ne(e,n,t){return a("img",{class:t,src:n})}function te(e,n,t){return a("video",{class:t,src:n})}function re(e,n,t){return a("audio",{class:t,src:n})}function ie(e,n,t,i){return a("a",{class:n,href:e,rel:i},[t??e])}function se(e,n){let t=e.startsWith("mailto:")?e:`mailto:${e}`,i=e.replace(/^mailto:/,"");return a("a",{class:n,href:t},[i])}function oe(e,n){let t=e.startsWith("tel:")?e:`tel:${e}`;return a("a",{class:n,href:t},[e])}function ae(e,n){return a("time",{class:n,datetime:e},[e])}function le(e,n,t){let i=Q[`p-${e}`]??"span";return a(i,{class:t},[n])}function de(e,n){let t={class:`u-${e}`,src:n.value,alt:n.alt};return n.srcset&&Object.keys(n.srcset).length&&(t.srcset=Object.entries(n.srcset).sort(([i],[r])=>i.localeCompare(r)).map(([i,r])=>`${r} ${i}`).join(", ")),a("img",t)}function ce(e,n){let t=a("div",{class:`e-${e}`});return n.html?t.appendChild(U(n.html)):n.value&&(t.textContent=n.value),t}function pe(e,n){let t=a("ruby",{"aria-hidden":"true"});t.appendChild(a("strong",{class:"p-name"},[e])),t.appendChild(a("rp",void 0,["("]));let i=a("rt");return i.append(N("/ "),a("span",{class:"p-ipa"},[n]),N(" /")),t.appendChild(i),t.appendChild(a("rp",void 0,[")"])),t}function A(e,n,t,i){let r=e[0];if(r==="photo"||r==="logo")return ne(r,n,g(...e.map(s=>`u-${s}`)));if(r==="video")return te(r,n,g(...e.map(s=>`u-${s}`)));if(r==="audio")return re(r,n,g(...e.map(s=>`u-${s}`)));if(P.has(r)&&C(n))return ie(n,g(...e.map(s=>`u-${s}`)),n,W(n,t));if(k.has(r))return se(n,g(...e.map(s=>`u-${s}`)));if($.has(r))return oe(n,g(...e.map(s=>`p-${s}`)));if(I.has(r))return ae(n,g(...e.map(s=>`dt-${s}`)));let d=g(...e.map(s=>`p-${s}`));return r==="name"&&i!=null?a(`h${i}`,{class:d},[n]):le(r,n,d)}function ue(e){return typeof e.html=="string"?"e":typeof e.value=="object"&&e.value!==null?"u":"p"}function R(e,n={}){let{extraClasses:t=[],asProperty:i=!1,propertyPrefix:r,relUrls:d,headingLevel:s}=n,u=e.properties,_=e.children??[],c=a(ee(e.type),{id:e.id,class:g(...t,...e.type)});if(i&&(r==="p"||r==="dt")&&"value"in e&&typeof e.value!="object"&&c.appendChild(a("data",{class:"value",value:String(e.value)})),i&&r==="e"&&typeof e.html=="string")return c.appendChild(U(e.html)),c;let j=i?e.value:void 0,m=new Set,T=null,M=u.name??[],x=u.ipa??[];if(M.length&&x.length){let l=typeof M[0]=="string"?M[0]:null,h=typeof x[0]=="string"?x[0]:null;l&&h&&(T=[l,h],m.add("name"),m.add("ipa"))}let L=null;if(!T&&!i){let l=u.url??[];if(M.length===1&&l.length===1){let h=typeof M[0]=="string"?M[0]:null,o=typeof l[0]=="string"?l[0]:null;if(h&&o&&C(o)){let p=["url"],y=u.uid??[];y.length===1&&y[0]===o&&(p.push("uid"),m.add("uid")),m.add("name"),m.add("url"),L=[h,o,p]}}}let S=new Set;for(let l of D(u)){if(l==="name"&&T){let o=pe(...T);if(s!=null){let p=a(`h${s}`);p.appendChild(o),c.appendChild(p)}else c.appendChild(o);T=null}if(l==="name"&&L){let[o,p,y]=L,b=g("p-name",...y.map(H=>`u-${H}`)),f=W(p,d),E=a("a",{class:b,href:p,rel:f},[o]);if(s!=null){let H=a(`h${s}`);H.appendChild(E),c.appendChild(H)}else c.appendChild(E);L=null}if(m.has(l))continue;let h=s!=null?Math.min(s+1,6):void 0;for(let o of u[l]){if(X(o)){let p=ue(o);c.appendChild(R(o,{extraClasses:[`${p}-${l}`],asProperty:!0,propertyPrefix:p,relUrls:d,headingLevel:h}));continue}if(Y(o)){c.appendChild(ce(l,o));continue}if(Z(o)){c.appendChild(de(l,o));continue}if(i&&r==="p"&&l==="name"&&typeof j=="string"&&typeof o=="string"&&o!==j&&!o.startsWith("http://")&&!o.startsWith("https://")){c.appendChild(a("time",{class:"dt-name"},[o]));continue}if(typeof o=="string"){let p=P.has(l)&&C(o)?"url":k.has(l)?"email":$.has(l)?"tel":I.has(l)?"datetime":`text:${l}`,y=`${o}\0${p}`;if(S.has(y))continue;S.add(y);let b=[];for(let f of D(u))if(!m.has(f))for(let E of u[f]){if(typeof E!="string"||E!==o)continue;(P.has(f)&&C(E)?"url":k.has(f)?"email":$.has(f)?"tel":I.has(f)?"datetime":`text:${f}`)===p&&!b.includes(f)&&b.push(f)}c.appendChild(A(b,o,d,s));continue}c.appendChild(A([l],String(o),d,s))}}let F=s!=null?Math.min(s+1,6):void 0;for(let l of _)c.appendChild(R(l,{relUrls:d,headingLevel:F}));return c}function fe(e,n={}){let t=e["rel-urls"],{topHeading:i}=n,r=a("main");for(let d of e.items)r.appendChild(R(d,{relUrls:t,headingLevel:i}));if(t&&Object.keys(t).length){let d=a("nav");for(let s of Object.keys(t).sort()){let u=t[s];d.appendChild(a("a",{href:s,rel:u.rels?.length?u.rels.join(" "):void 0},[u.text??s]))}r.appendChild(d)}return n.asElement?r:r.outerHTML}function ge(e,n={}){let{relUrls:t,topHeading:i}=n;return R(e,{relUrls:t,headingLevel:i})}function me(e,n={}){let{relUrls:t,topHeading:i}=n,r=document.createDocumentFragment();for(let d of e)r.appendChild(R(d,{relUrls:t,headingLevel:i}));return r}
|
|
2
|
+
//# sourceMappingURL=mf2dom.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../renderer.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Microformats2 Renderer - Pure Programmatic DOM Building\n *\n * Renders mf2 JSON to semantic HTML by building the DOM tree programmatically.\n * Uses DocumentFragment for efficient batch updates with minimal reflows.\n */\n\nimport type {\n Mf2Document,\n Mf2Item,\n PropertyValue,\n EValue,\n UrlObject,\n RelUrl,\n} from \"./types.js\";\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nconst SEMANTIC_ROOTS: Record<string, string> = {\n \"h-entry\": \"article\",\n \"h-feed\": \"section\",\n \"h-event\": \"article\",\n \"h-product\": \"article\",\n \"h-recipe\": \"article\",\n \"h-review\": \"article\",\n \"h-resume\": \"article\",\n \"h-adr\": \"address\",\n \"h-cite\": \"blockquote\",\n \"h-geo\": \"data\",\n};\n\nconst SEMANTIC_PROPS: Record<string, string> = {\n // Address components use address element\n \"p-adr\": \"address\",\n \"p-street-address\": \"span\",\n \"p-extended-address\": \"span\",\n \"p-locality\": \"span\",\n \"p-region\": \"span\",\n \"p-postal-code\": \"span\",\n \"p-country-name\": \"span\",\n // Name properties use strong for emphasis\n \"p-name\": \"strong\",\n // Paragraph-like properties\n \"p-summary\": \"p\",\n \"p-note\": \"p\",\n \"p-content\": \"p\",\n \"p-description\": \"p\",\n // Author info\n \"p-author\": \"span\",\n};\n\nconst URL_PROPS = new Set([\n \"url\", \"uid\", \"photo\", \"logo\", \"video\", \"audio\",\n \"syndication\", \"in-reply-to\", \"like-of\", \"repost-of\", \"bookmark-of\",\n \"follow-of\", \"read-of\", \"tag-of\", \"location\",\n]);\nconst EMAIL_PROPS = new Set([\"email\"]);\nconst TEL_PROPS = new Set([\"tel\"]);\nconst DT_PROPS = new Set([\n \"published\", \"updated\", \"start\", \"end\", \"duration\", \"bday\", \"anniversary\", \"rev\",\n]);\n\n// Semantic property ordering based on microformats.org wiki\n// Properties are grouped by semantic meaning for good display across types:\n// 1. Visual identity (photo, logo)\n// 2. Name/identity\n// 3. Author (for h-entry)\n// 4. Description/content\n// 5. Dates (important for h-entry, h-event)\n// 6. Location (for h-event, h-card)\n// 7. URLs and links\n// 8. Contact info (email, tel)\n// 9. Address details\n// 10. Organization/role\n// 11. Categories and other metadata\nconst PROP_ORDER = [\n // Visual identity first\n \"photo\",\n \"logo\",\n \"featured\",\n // Name properties\n \"name\",\n \"honorific-prefix\",\n \"given-name\",\n \"additional-name\",\n \"family-name\",\n \"sort-string\",\n \"honorific-suffix\",\n \"nickname\",\n \"ipa\",\n // Author (important for h-entry)\n \"author\",\n // Description/content\n \"summary\",\n \"note\",\n \"content\",\n \"description\",\n // Dates (prominent for h-entry, h-event)\n \"published\",\n \"updated\",\n \"start\",\n \"end\",\n \"duration\",\n \"bday\",\n \"anniversary\",\n \"rev\",\n // Location (for h-event)\n \"location\",\n // URLs and links\n \"url\",\n \"uid\",\n \"syndication\",\n \"in-reply-to\",\n \"like-of\",\n \"repost-of\",\n \"bookmark-of\",\n \"follow-of\",\n \"read-of\",\n \"read-status\",\n // Contact info\n \"email\",\n \"tel\",\n \"impp\",\n // Address details\n \"adr\",\n \"geo\",\n \"latitude\",\n \"longitude\",\n \"altitude\",\n \"street-address\",\n \"extended-address\",\n \"locality\",\n \"region\",\n \"postal-code\",\n \"country-name\",\n \"label\",\n // Organization/role\n \"org\",\n \"job-title\",\n \"role\",\n // Categories and metadata\n \"category\",\n \"rsvp\",\n \"attendee\",\n \"key\",\n \"sex\",\n \"gender-identity\",\n];\n\n// ============================================================================\n// DOM Builder Helpers\n// ============================================================================\n\n/** Create an element with attributes */\nfunction h<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attrs?: Record<string, string | undefined>,\n children?: (Node | string)[]\n): HTMLElementTagNameMap[K];\nfunction h(\n tag: string,\n attrs?: Record<string, string | undefined>,\n children?: (Node | string)[]\n): HTMLElement;\nfunction h(\n tag: string,\n attrs?: Record<string, string | undefined>,\n children?: (Node | string)[]\n): HTMLElement {\n const el = document.createElement(tag);\n\n if (attrs) {\n for (const [k, v] of Object.entries(attrs)) {\n if (v != null) el.setAttribute(k, v);\n }\n }\n\n if (children) {\n for (const child of children) {\n el.append(child);\n }\n }\n\n return el;\n}\n\n/** Create a text node */\nfunction text(content: string): Text {\n return document.createTextNode(content);\n}\n\n/** Parse HTML string to DocumentFragment (only for e-* content) */\nfunction parseHtml(html: string): DocumentFragment {\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = html;\n return tpl.content;\n}\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\nfunction isMf2Item(v: unknown): v is Mf2Item {\n return typeof v === \"object\" && v !== null && \"type\" in v && \"properties\" in v;\n}\n\nfunction isEValue(v: unknown): v is EValue {\n return typeof v === \"object\" && v !== null && \"html\" in v;\n}\n\nfunction isUrlObject(v: unknown): v is UrlObject {\n return typeof v === \"object\" && v !== null && \"value\" in v && (\"alt\" in v || \"srcset\" in v);\n}\n\nfunction isUrl(s: string): boolean {\n return s.startsWith(\"http://\") || s.startsWith(\"https://\") || s.startsWith(\"/\");\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction propOrder(p: string): number {\n const i = PROP_ORDER.indexOf(p);\n return i >= 0 ? i : PROP_ORDER.length;\n}\n\nfunction sortProps(props: Record<string, PropertyValue[]>): string[] {\n return Object.keys(props).sort((a, b) => propOrder(a) - propOrder(b));\n}\n\nfunction classes(...parts: (string | undefined)[]): string | undefined {\n const cls = parts.filter(Boolean).join(\" \");\n return cls || undefined;\n}\n\nfunction getRels(url: string, relUrls?: Record<string, RelUrl>): string | undefined {\n const rels = relUrls?.[url]?.rels;\n return rels?.length ? rels.join(\" \") : undefined;\n}\n\nfunction getTag(types: string[]): string {\n for (const t of types) {\n if (t in SEMANTIC_ROOTS) return SEMANTIC_ROOTS[t];\n }\n return \"div\";\n}\n\n// ============================================================================\n// Property Renderers\n// ============================================================================\n\nfunction renderPhoto(prop: string, url: string, cls: string): HTMLImageElement {\n // Don't include alt=\"\" - it changes the parsed output from string to {value, alt}\n return h(\"img\", { class: cls, src: url });\n}\n\nfunction renderVideo(prop: string, url: string, cls: string): HTMLVideoElement {\n return h(\"video\", { class: cls, src: url });\n}\n\nfunction renderAudio(prop: string, url: string, cls: string): HTMLAudioElement {\n return h(\"audio\", { class: cls, src: url });\n}\n\nfunction renderLink(\n url: string,\n cls: string,\n displayText?: string,\n rel?: string\n): HTMLAnchorElement {\n return h(\"a\", { class: cls, href: url, rel }, [displayText ?? url]);\n}\n\nfunction renderEmail(value: string, cls: string): HTMLAnchorElement {\n const href = value.startsWith(\"mailto:\") ? value : `mailto:${value}`;\n const display = value.replace(/^mailto:/, \"\");\n return h(\"a\", { class: cls, href }, [display]);\n}\n\nfunction renderTel(value: string, cls: string): HTMLAnchorElement {\n const href = value.startsWith(\"tel:\") ? value : `tel:${value}`;\n return h(\"a\", { class: cls, href }, [value]);\n}\n\nfunction renderTime(value: string, cls: string): HTMLTimeElement {\n return h(\"time\", { class: cls, datetime: value }, [value]);\n}\n\nfunction renderText(prop: string, value: string, cls: string): HTMLElement {\n const tag = SEMANTIC_PROPS[`p-${prop}`] ?? \"span\";\n return h(tag, { class: cls }, [value]);\n}\n\nfunction renderUrlObject(prop: string, obj: UrlObject): HTMLImageElement {\n const attrs: Record<string, string | undefined> = {\n class: `u-${prop}`,\n src: obj.value,\n alt: obj.alt,\n };\n\n if (obj.srcset && Object.keys(obj.srcset).length) {\n attrs.srcset = Object.entries(obj.srcset)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => `${v} ${k}`)\n .join(\", \");\n }\n\n return h(\"img\", attrs);\n}\n\nfunction renderEProperty(prop: string, value: EValue): HTMLDivElement {\n const div = h(\"div\", { class: `e-${prop}` });\n if (value.html) {\n div.appendChild(parseHtml(value.html));\n } else if (value.value) {\n div.textContent = value.value;\n }\n return div;\n}\n\nfunction renderRuby(name: string, ipa: string): HTMLElement {\n const ruby = h(\"ruby\", { \"aria-hidden\": \"true\" });\n\n ruby.appendChild(h(\"strong\", { class: \"p-name\" }, [name]));\n ruby.appendChild(h(\"rp\", undefined, [\"(\"]));\n\n const rt = h(\"rt\");\n rt.append(text(\"/ \"), h(\"span\", { class: \"p-ipa\" }, [ipa]), text(\" /\"));\n ruby.appendChild(rt);\n\n ruby.appendChild(h(\"rp\", undefined, [\")\"]));\n\n return ruby;\n}\n\n/** Render a string property value */\nfunction renderStringProp(\n props: string[],\n value: string,\n relUrls?: Record<string, RelUrl>,\n headingLevel?: number\n): HTMLElement {\n const prop = props[0];\n\n // Media elements\n if (prop === \"photo\" || prop === \"logo\") {\n return renderPhoto(prop, value, classes(...props.map(p => `u-${p}`))!);\n }\n if (prop === \"video\") {\n return renderVideo(prop, value, classes(...props.map(p => `u-${p}`))!);\n }\n if (prop === \"audio\") {\n return renderAudio(prop, value, classes(...props.map(p => `u-${p}`))!);\n }\n\n // URL properties\n if (URL_PROPS.has(prop) && isUrl(value)) {\n return renderLink(\n value,\n classes(...props.map(p => `u-${p}`))!,\n value,\n getRels(value, relUrls)\n );\n }\n\n // Email\n if (EMAIL_PROPS.has(prop)) {\n return renderEmail(value, classes(...props.map(p => `u-${p}`))!);\n }\n\n // Telephone\n if (TEL_PROPS.has(prop)) {\n return renderTel(value, classes(...props.map(p => `p-${p}`))!);\n }\n\n // Datetime\n if (DT_PROPS.has(prop)) {\n return renderTime(value, classes(...props.map(p => `dt-${p}`))!);\n }\n\n // Default text - use heading tag for name property if headingLevel is set\n const cls = classes(...props.map(p => `p-${p}`))!;\n if (prop === \"name\" && headingLevel != null) {\n return h(`h${headingLevel}` as keyof HTMLElementTagNameMap, { class: cls }, [value]);\n }\n return renderText(prop, value, cls);\n}\n\n// ============================================================================\n// Item Renderer\n// ============================================================================\n\ninterface ItemOptions {\n extraClasses?: string[];\n asProperty?: boolean;\n propertyPrefix?: string;\n relUrls?: Record<string, RelUrl>;\n headingLevel?: number;\n}\n\nfunction embeddedPrefix(item: Mf2Item): string {\n if (typeof item.html === \"string\") return \"e\";\n if (typeof item.value === \"object\" && item.value !== null) return \"u\";\n return \"p\";\n}\n\nfunction renderItem(item: Mf2Item, opts: ItemOptions = {}): HTMLElement {\n const { extraClasses = [], asProperty = false, propertyPrefix, relUrls, headingLevel } = opts;\n const props = item.properties;\n const children = item.children ?? [];\n\n // Create root element\n const el = h(getTag(item.type), {\n id: item.id,\n class: classes(...extraClasses, ...item.type),\n });\n\n // VCP data node for p/dt embedded items\n if (asProperty && (propertyPrefix === \"p\" || propertyPrefix === \"dt\")) {\n if (\"value\" in item && typeof item.value !== \"object\") {\n el.appendChild(h(\"data\", { class: \"value\", value: String(item.value) }));\n }\n }\n\n // e-* property with HTML\n if (asProperty && propertyPrefix === \"e\" && typeof item.html === \"string\") {\n el.appendChild(parseHtml(item.html));\n return el;\n }\n\n // Get embedded value for special name handling\n const embeddedValue = asProperty ? item.value : undefined;\n\n // Check for name+ipa ruby rendering\n const consumed = new Set<string>();\n let ruby: [string, string] | null = null;\n\n const names = props.name ?? [];\n const ipas = props.ipa ?? [];\n if (names.length && ipas.length) {\n const name = typeof names[0] === \"string\" ? names[0] : null;\n const ipa = typeof ipas[0] === \"string\" ? ipas[0] : null;\n if (name && ipa) {\n ruby = [name, ipa];\n consumed.add(\"name\");\n consumed.add(\"ipa\");\n }\n }\n\n // Check if name should be rendered as a link (single name + single URL, no ruby)\n // Don't apply when rendering as a property (changes value extraction on re-parse)\n // (name, url, list of url properties to include in class)\n let linkedName: [string, string, string[]] | null = null;\n if (!ruby && !asProperty) {\n const urls = props.url ?? [];\n if (names.length === 1 && urls.length === 1) {\n const nameVal = typeof names[0] === \"string\" ? names[0] : null;\n const urlVal = typeof urls[0] === \"string\" ? urls[0] : null;\n if (nameVal && urlVal && isUrl(urlVal)) {\n // Collect URL properties that share this URL value (like uid)\n const urlProps = [\"url\"];\n const uids = props.uid ?? [];\n if (uids.length === 1 && uids[0] === urlVal) {\n urlProps.push(\"uid\");\n consumed.add(\"uid\");\n }\n consumed.add(\"name\");\n consumed.add(\"url\");\n linkedName = [nameVal, urlVal, urlProps];\n }\n }\n }\n\n // Track rendered (value, category) pairs for grouping\n const rendered = new Set<string>();\n\n for (const prop of sortProps(props)) {\n // Insert ruby at name position\n if (prop === \"name\" && ruby) {\n const rubyEl = renderRuby(...ruby);\n if (headingLevel != null) {\n const heading = h(`h${headingLevel}` as keyof HTMLElementTagNameMap);\n heading.appendChild(rubyEl);\n el.appendChild(heading);\n } else {\n el.appendChild(rubyEl);\n }\n ruby = null;\n }\n\n // Render linked name at the position where \"name\" would appear\n if (prop === \"name\" && linkedName) {\n const [nameVal, urlVal, urlProps] = linkedName;\n const cls = classes(\"p-name\", ...urlProps.map(p => `u-${p}`));\n const rel = getRels(urlVal, relUrls);\n const linkEl = h(\"a\", { class: cls, href: urlVal, rel }, [nameVal]);\n if (headingLevel != null) {\n const heading = h(`h${headingLevel}` as keyof HTMLElementTagNameMap);\n heading.appendChild(linkEl);\n el.appendChild(heading);\n } else {\n el.appendChild(linkEl);\n }\n linkedName = null;\n }\n\n if (consumed.has(prop)) continue;\n\n // Calculate next heading level for embedded items (increment, cap at 6)\n const childHeading = headingLevel != null ? Math.min(headingLevel + 1, 6) : undefined;\n\n for (const v of props[prop]) {\n // Embedded mf2 item\n if (isMf2Item(v)) {\n const prefix = embeddedPrefix(v);\n el.appendChild(renderItem(v, {\n extraClasses: [`${prefix}-${prop}`],\n asProperty: true,\n propertyPrefix: prefix,\n relUrls,\n headingLevel: childHeading,\n }));\n continue;\n }\n\n // e-* HTML content\n if (isEValue(v)) {\n el.appendChild(renderEProperty(prop, v));\n continue;\n }\n\n // URL object (img with alt/srcset)\n if (isUrlObject(v)) {\n el.appendChild(renderUrlObject(prop, v));\n continue;\n }\n\n // If this item is itself embedded as a property, prefer dt-* for `name`\n // when its representative value differs from its `properties.name[0]`.\n if (\n asProperty &&\n propertyPrefix === \"p\" &&\n prop === \"name\" &&\n typeof embeddedValue === \"string\" &&\n typeof v === \"string\" &&\n v !== embeddedValue &&\n !v.startsWith(\"http://\") &&\n !v.startsWith(\"https://\")\n ) {\n // Render as time element with dt-name class (no datetime attribute)\n el.appendChild(h(\"time\", { class: \"dt-name\" }, [v]));\n continue;\n }\n\n // String value - group by value for merged classes\n if (typeof v === \"string\") {\n const cat = URL_PROPS.has(prop) && isUrl(v) ? \"url\"\n : EMAIL_PROPS.has(prop) ? \"email\"\n : TEL_PROPS.has(prop) ? \"tel\"\n : DT_PROPS.has(prop) ? \"datetime\"\n : `text:${prop}`;\n\n const key = `${v}\\0${cat}`;\n if (rendered.has(key)) continue;\n rendered.add(key);\n\n // Collect all props with same value+category\n const group: string[] = [];\n for (const p of sortProps(props)) {\n if (consumed.has(p)) continue;\n for (const pv of props[p]) {\n if (typeof pv !== \"string\" || pv !== v) continue;\n const pCat = URL_PROPS.has(p) && isUrl(pv) ? \"url\"\n : EMAIL_PROPS.has(p) ? \"email\"\n : TEL_PROPS.has(p) ? \"tel\"\n : DT_PROPS.has(p) ? \"datetime\"\n : `text:${p}`;\n if (pCat === cat && !group.includes(p)) {\n group.push(p);\n }\n }\n }\n\n el.appendChild(renderStringProp(group, v, relUrls, headingLevel));\n continue;\n }\n\n // Fallback\n el.appendChild(renderStringProp([prop], String(v), relUrls, headingLevel));\n }\n }\n\n // Render children - calculate next heading level (increment, cap at 6)\n const childrenHeading = headingLevel != null ? Math.min(headingLevel + 1, 6) : undefined;\n for (const child of children) {\n el.appendChild(renderItem(child, { relUrls, headingLevel: childrenHeading }));\n }\n\n return el;\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport interface RenderOptions {\n /** Return HTMLElement instead of string */\n asElement?: boolean;\n /** If set, render name properties as heading elements starting at this level (1-6).\n * Names in nested items use incrementing levels (capped at h6).\n * Default is undefined (render as <strong>). */\n topHeading?: number;\n}\n\n/**\n * Render an mf2 document to HTML.\n */\nexport function render(doc: Mf2Document, opts?: { asElement: true; topHeading?: number }): HTMLElement;\nexport function render(doc: Mf2Document, opts?: { asElement?: false; topHeading?: number }): string;\nexport function render(doc: Mf2Document, opts: RenderOptions = {}): string | HTMLElement {\n const relUrls = doc[\"rel-urls\"];\n const { topHeading } = opts;\n const main = h(\"main\");\n\n // Render items\n for (const item of doc.items) {\n main.appendChild(renderItem(item, { relUrls, headingLevel: topHeading }));\n }\n\n // Render rel-urls as nav\n if (relUrls && Object.keys(relUrls).length) {\n const nav = h(\"nav\");\n for (const url of Object.keys(relUrls).sort()) {\n const info = relUrls[url];\n nav.appendChild(h(\"a\", {\n href: url,\n rel: info.rels?.length ? info.rels.join(\" \") : undefined,\n }, [info.text ?? url]));\n }\n main.appendChild(nav);\n }\n\n return opts.asElement ? main : main.outerHTML;\n}\n\nexport interface RenderItemElementOptions {\n relUrls?: Record<string, RelUrl>;\n topHeading?: number;\n}\n\n/**\n * Render a single mf2 item to an HTMLElement.\n */\nexport function renderItemElement(\n item: Mf2Item,\n options: RenderItemElementOptions = {}\n): HTMLElement {\n const { relUrls, topHeading } = options;\n return renderItem(item, { relUrls, headingLevel: topHeading });\n}\n\nexport interface RenderItemsOptions {\n relUrls?: Record<string, RelUrl>;\n topHeading?: number;\n}\n\n/**\n * Render multiple items efficiently using DocumentFragment.\n */\nexport function renderItems(\n items: Mf2Item[],\n options: RenderItemsOptions = {}\n): DocumentFragment {\n const { relUrls, topHeading } = options;\n const fragment = document.createDocumentFragment();\n for (const item of items) {\n fragment.appendChild(renderItem(item, { relUrls, headingLevel: topHeading }));\n }\n return fragment;\n}\n\nexport { parseHtml as parseHtmlFragment };\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["renderer_exports", "__export", "parseHtml", "render", "renderItemElement", "renderItems", "__toCommonJS", "SEMANTIC_ROOTS", "SEMANTIC_PROPS", "URL_PROPS", "EMAIL_PROPS", "TEL_PROPS", "DT_PROPS", "PROP_ORDER", "h", "tag", "attrs", "children", "el", "k", "v", "child", "text", "content", "html", "tpl", "isMf2Item", "isEValue", "isUrlObject", "isUrl", "s", "propOrder", "p", "i", "sortProps", "props", "a", "b", "classes", "parts", "getRels", "url", "relUrls", "rels", "getTag", "types", "t", "renderPhoto", "prop", "cls", "renderVideo", "renderAudio", "renderLink", "displayText", "rel", "renderEmail", "value", "href", "display", "renderTel", "renderTime", "renderText", "renderUrlObject", "obj", "renderEProperty", "div", "renderRuby", "name", "ipa", "ruby", "rt", "renderStringProp", "headingLevel", "embeddedPrefix", "item", "renderItem", "opts", "extraClasses", "asProperty", "propertyPrefix", "embeddedValue", "consumed", "names", "ipas", "linkedName", "urls", "nameVal", "urlVal", "urlProps", "uids", "rendered", "rubyEl", "heading", "linkEl", "childHeading", "prefix", "cat", "key", "group", "pv", "childrenHeading", "doc", "topHeading", "main", "nav", "info", "options", "items", "fragment"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var j={"h-entry":"article","h-feed":"section","h-event":"article","h-product":"article","h-recipe":"article","h-review":"article","h-resume":"article","h-adr":"address","h-cite":"blockquote","h-geo":"data"},F={"p-adr":"address","p-street-address":"span","p-extended-address":"span","p-locality":"span","p-region":"span","p-postal-code":"span","p-country-name":"span","p-name":"strong","p-summary":"p","p-note":"p","p-content":"p","p-description":"p","p-author":"span"},O=new Set(["url","uid","photo","logo","video","audio","syndication","in-reply-to","like-of","repost-of","bookmark-of","follow-of","read-of","tag-of","location"]),P=new Set(["email"]),k=new Set(["tel"]),$=new Set(["published","updated","start","end","duration","bday","anniversary","rev"]),S=["photo","logo","featured","name","honorific-prefix","given-name","additional-name","family-name","sort-string","honorific-suffix","nickname","ipa","author","summary","note","content","description","published","updated","start","end","duration","bday","anniversary","rev","location","url","uid","syndication","in-reply-to","like-of","repost-of","bookmark-of","follow-of","read-of","read-status","email","tel","impp","adr","geo","latitude","longitude","altitude","street-address","extended-address","locality","region","postal-code","country-name","label","org","job-title","role","category","rsvp","attendee","key","sex","gender-identity"];function a(e,n,t){let i=document.createElement(e);if(n)for(let[r,d]of Object.entries(n))d!=null&&i.setAttribute(r,d);if(t)for(let r of t)i.append(r);return i}function V(e){return document.createTextNode(e)}function D(e){let n=document.createElement("template");return n.innerHTML=e,n.content}function K(e){return typeof e=="object"&&e!==null&&"type"in e&&"properties"in e}function q(e){return typeof e=="object"&&e!==null&&"html"in e}function B(e){return typeof e=="object"&&e!==null&&"value"in e&&("alt"in e||"srcset"in e)}function C(e){return e.startsWith("http://")||e.startsWith("https://")||e.startsWith("/")}function w(e){let n=S.indexOf(e);return n>=0?n:S.length}function N(e){return Object.keys(e).sort((n,t)=>w(n)-w(t))}function g(...e){return e.filter(Boolean).join(" ")||void 0}function A(e,n){let t=n?.[e]?.rels;return t?.length?t.join(" "):void 0}function z(e){for(let n of e)if(n in j)return j[n];return"div"}function G(e,n,t){return a("img",{class:t,src:n})}function J(e,n,t){return a("video",{class:t,src:n})}function Q(e,n,t){return a("audio",{class:t,src:n})}function X(e,n,t,i){return a("a",{class:n,href:e,rel:i},[t??e])}function Y(e,n){let t=e.startsWith("mailto:")?e:`mailto:${e}`,i=e.replace(/^mailto:/,"");return a("a",{class:n,href:t},[i])}function Z(e,n){let t=e.startsWith("tel:")?e:`tel:${e}`;return a("a",{class:n,href:t},[e])}function ee(e,n){return a("time",{class:n,datetime:e},[e])}function ne(e,n,t){let i=F[`p-${e}`]??"span";return a(i,{class:t},[n])}function te(e,n){let t={class:`u-${e}`,src:n.value,alt:n.alt};return n.srcset&&Object.keys(n.srcset).length&&(t.srcset=Object.entries(n.srcset).sort(([i],[r])=>i.localeCompare(r)).map(([i,r])=>`${r} ${i}`).join(", ")),a("img",t)}function re(e,n){let t=a("div",{class:`e-${e}`});return n.html?t.appendChild(D(n.html)):n.value&&(t.textContent=n.value),t}function ie(e,n){let t=a("ruby",{"aria-hidden":"true"});t.appendChild(a("strong",{class:"p-name"},[e])),t.appendChild(a("rp",void 0,["("]));let i=a("rt");return i.append(V("/ "),a("span",{class:"p-ipa"},[n]),V(" /")),t.appendChild(i),t.appendChild(a("rp",void 0,[")"])),t}function v(e,n,t,i){let r=e[0];if(r==="photo"||r==="logo")return G(r,n,g(...e.map(s=>`u-${s}`)));if(r==="video")return J(r,n,g(...e.map(s=>`u-${s}`)));if(r==="audio")return Q(r,n,g(...e.map(s=>`u-${s}`)));if(O.has(r)&&C(n))return X(n,g(...e.map(s=>`u-${s}`)),n,A(n,t));if(P.has(r))return Y(n,g(...e.map(s=>`u-${s}`)));if(k.has(r))return Z(n,g(...e.map(s=>`p-${s}`)));if($.has(r))return ee(n,g(...e.map(s=>`dt-${s}`)));let d=g(...e.map(s=>`p-${s}`));return r==="name"&&i!=null?a(`h${i}`,{class:d},[n]):ne(r,n,d)}function se(e){return typeof e.html=="string"?"e":typeof e.value=="object"&&e.value!==null?"u":"p"}function R(e,n={}){let{extraClasses:t=[],asProperty:i=!1,propertyPrefix:r,relUrls:d,headingLevel:s}=n,u=e.properties,W=e.children??[],c=a(z(e.type),{id:e.id,class:g(...t,...e.type)});if(i&&(r==="p"||r==="dt")&&"value"in e&&typeof e.value!="object"&&c.appendChild(a("data",{class:"value",value:String(e.value)})),i&&r==="e"&&typeof e.html=="string")return c.appendChild(D(e.html)),c;let I=i?e.value:void 0,m=new Set,T=null,M=u.name??[],x=u.ipa??[];if(M.length&&x.length){let l=typeof M[0]=="string"?M[0]:null,h=typeof x[0]=="string"?x[0]:null;l&&h&&(T=[l,h],m.add("name"),m.add("ipa"))}let L=null;if(!T&&!i){let l=u.url??[];if(M.length===1&&l.length===1){let h=typeof M[0]=="string"?M[0]:null,o=typeof l[0]=="string"?l[0]:null;if(h&&o&&C(o)){let p=["url"],y=u.uid??[];y.length===1&&y[0]===o&&(p.push("uid"),m.add("uid")),m.add("name"),m.add("url"),L=[h,o,p]}}}let U=new Set;for(let l of N(u)){if(l==="name"&&T){let o=ie(...T);if(s!=null){let p=a(`h${s}`);p.appendChild(o),c.appendChild(p)}else c.appendChild(o);T=null}if(l==="name"&&L){let[o,p,y]=L,b=g("p-name",...y.map(H=>`u-${H}`)),f=A(p,d),E=a("a",{class:b,href:p,rel:f},[o]);if(s!=null){let H=a(`h${s}`);H.appendChild(E),c.appendChild(H)}else c.appendChild(E);L=null}if(m.has(l))continue;let h=s!=null?Math.min(s+1,6):void 0;for(let o of u[l]){if(K(o)){let p=se(o);c.appendChild(R(o,{extraClasses:[`${p}-${l}`],asProperty:!0,propertyPrefix:p,relUrls:d,headingLevel:h}));continue}if(q(o)){c.appendChild(re(l,o));continue}if(B(o)){c.appendChild(te(l,o));continue}if(i&&r==="p"&&l==="name"&&typeof I=="string"&&typeof o=="string"&&o!==I&&!o.startsWith("http://")&&!o.startsWith("https://")){c.appendChild(a("time",{class:"dt-name"},[o]));continue}if(typeof o=="string"){let p=O.has(l)&&C(o)?"url":P.has(l)?"email":k.has(l)?"tel":$.has(l)?"datetime":`text:${l}`,y=`${o}\0${p}`;if(U.has(y))continue;U.add(y);let b=[];for(let f of N(u))if(!m.has(f))for(let E of u[f]){if(typeof E!="string"||E!==o)continue;(O.has(f)&&C(E)?"url":P.has(f)?"email":k.has(f)?"tel":$.has(f)?"datetime":`text:${f}`)===p&&!b.includes(f)&&b.push(f)}c.appendChild(v(b,o,d,s));continue}c.appendChild(v([l],String(o),d,s))}}let _=s!=null?Math.min(s+1,6):void 0;for(let l of W)c.appendChild(R(l,{relUrls:d,headingLevel:_}));return c}function oe(e,n={}){let t=e["rel-urls"],{topHeading:i}=n,r=a("main");for(let d of e.items)r.appendChild(R(d,{relUrls:t,headingLevel:i}));if(t&&Object.keys(t).length){let d=a("nav");for(let s of Object.keys(t).sort()){let u=t[s];d.appendChild(a("a",{href:s,rel:u.rels?.length?u.rels.join(" "):void 0},[u.text??s]))}r.appendChild(d)}return n.asElement?r:r.outerHTML}function ae(e,n={}){let{relUrls:t,topHeading:i}=n;return R(e,{relUrls:t,headingLevel:i})}function le(e,n={}){let{relUrls:t,topHeading:i}=n,r=document.createDocumentFragment();for(let d of e)r.appendChild(R(d,{relUrls:t,headingLevel:i}));return r}export{D as parseHtmlFragment,oe as render,ae as renderItemElement,le as renderItems};
|
|
2
|
+
//# sourceMappingURL=mf2dom.esm.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../renderer.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Microformats2 Renderer - Pure Programmatic DOM Building\n *\n * Renders mf2 JSON to semantic HTML by building the DOM tree programmatically.\n * Uses DocumentFragment for efficient batch updates with minimal reflows.\n */\n\nimport type {\n Mf2Document,\n Mf2Item,\n PropertyValue,\n EValue,\n UrlObject,\n RelUrl,\n} from \"./types.js\";\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nconst SEMANTIC_ROOTS: Record<string, string> = {\n \"h-entry\": \"article\",\n \"h-feed\": \"section\",\n \"h-event\": \"article\",\n \"h-product\": \"article\",\n \"h-recipe\": \"article\",\n \"h-review\": \"article\",\n \"h-resume\": \"article\",\n \"h-adr\": \"address\",\n \"h-cite\": \"blockquote\",\n \"h-geo\": \"data\",\n};\n\nconst SEMANTIC_PROPS: Record<string, string> = {\n // Address components use address element\n \"p-adr\": \"address\",\n \"p-street-address\": \"span\",\n \"p-extended-address\": \"span\",\n \"p-locality\": \"span\",\n \"p-region\": \"span\",\n \"p-postal-code\": \"span\",\n \"p-country-name\": \"span\",\n // Name properties use strong for emphasis\n \"p-name\": \"strong\",\n // Paragraph-like properties\n \"p-summary\": \"p\",\n \"p-note\": \"p\",\n \"p-content\": \"p\",\n \"p-description\": \"p\",\n // Author info\n \"p-author\": \"span\",\n};\n\nconst URL_PROPS = new Set([\n \"url\", \"uid\", \"photo\", \"logo\", \"video\", \"audio\",\n \"syndication\", \"in-reply-to\", \"like-of\", \"repost-of\", \"bookmark-of\",\n \"follow-of\", \"read-of\", \"tag-of\", \"location\",\n]);\nconst EMAIL_PROPS = new Set([\"email\"]);\nconst TEL_PROPS = new Set([\"tel\"]);\nconst DT_PROPS = new Set([\n \"published\", \"updated\", \"start\", \"end\", \"duration\", \"bday\", \"anniversary\", \"rev\",\n]);\n\n// Semantic property ordering based on microformats.org wiki\n// Properties are grouped by semantic meaning for good display across types:\n// 1. Visual identity (photo, logo)\n// 2. Name/identity\n// 3. Author (for h-entry)\n// 4. Description/content\n// 5. Dates (important for h-entry, h-event)\n// 6. Location (for h-event, h-card)\n// 7. URLs and links\n// 8. Contact info (email, tel)\n// 9. Address details\n// 10. Organization/role\n// 11. Categories and other metadata\nconst PROP_ORDER = [\n // Visual identity first\n \"photo\",\n \"logo\",\n \"featured\",\n // Name properties\n \"name\",\n \"honorific-prefix\",\n \"given-name\",\n \"additional-name\",\n \"family-name\",\n \"sort-string\",\n \"honorific-suffix\",\n \"nickname\",\n \"ipa\",\n // Author (important for h-entry)\n \"author\",\n // Description/content\n \"summary\",\n \"note\",\n \"content\",\n \"description\",\n // Dates (prominent for h-entry, h-event)\n \"published\",\n \"updated\",\n \"start\",\n \"end\",\n \"duration\",\n \"bday\",\n \"anniversary\",\n \"rev\",\n // Location (for h-event)\n \"location\",\n // URLs and links\n \"url\",\n \"uid\",\n \"syndication\",\n \"in-reply-to\",\n \"like-of\",\n \"repost-of\",\n \"bookmark-of\",\n \"follow-of\",\n \"read-of\",\n \"read-status\",\n // Contact info\n \"email\",\n \"tel\",\n \"impp\",\n // Address details\n \"adr\",\n \"geo\",\n \"latitude\",\n \"longitude\",\n \"altitude\",\n \"street-address\",\n \"extended-address\",\n \"locality\",\n \"region\",\n \"postal-code\",\n \"country-name\",\n \"label\",\n // Organization/role\n \"org\",\n \"job-title\",\n \"role\",\n // Categories and metadata\n \"category\",\n \"rsvp\",\n \"attendee\",\n \"key\",\n \"sex\",\n \"gender-identity\",\n];\n\n// ============================================================================\n// DOM Builder Helpers\n// ============================================================================\n\n/** Create an element with attributes */\nfunction h<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attrs?: Record<string, string | undefined>,\n children?: (Node | string)[]\n): HTMLElementTagNameMap[K];\nfunction h(\n tag: string,\n attrs?: Record<string, string | undefined>,\n children?: (Node | string)[]\n): HTMLElement;\nfunction h(\n tag: string,\n attrs?: Record<string, string | undefined>,\n children?: (Node | string)[]\n): HTMLElement {\n const el = document.createElement(tag);\n\n if (attrs) {\n for (const [k, v] of Object.entries(attrs)) {\n if (v != null) el.setAttribute(k, v);\n }\n }\n\n if (children) {\n for (const child of children) {\n el.append(child);\n }\n }\n\n return el;\n}\n\n/** Create a text node */\nfunction text(content: string): Text {\n return document.createTextNode(content);\n}\n\n/** Parse HTML string to DocumentFragment (only for e-* content) */\nfunction parseHtml(html: string): DocumentFragment {\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = html;\n return tpl.content;\n}\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\nfunction isMf2Item(v: unknown): v is Mf2Item {\n return typeof v === \"object\" && v !== null && \"type\" in v && \"properties\" in v;\n}\n\nfunction isEValue(v: unknown): v is EValue {\n return typeof v === \"object\" && v !== null && \"html\" in v;\n}\n\nfunction isUrlObject(v: unknown): v is UrlObject {\n return typeof v === \"object\" && v !== null && \"value\" in v && (\"alt\" in v || \"srcset\" in v);\n}\n\nfunction isUrl(s: string): boolean {\n return s.startsWith(\"http://\") || s.startsWith(\"https://\") || s.startsWith(\"/\");\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction propOrder(p: string): number {\n const i = PROP_ORDER.indexOf(p);\n return i >= 0 ? i : PROP_ORDER.length;\n}\n\nfunction sortProps(props: Record<string, PropertyValue[]>): string[] {\n return Object.keys(props).sort((a, b) => propOrder(a) - propOrder(b));\n}\n\nfunction classes(...parts: (string | undefined)[]): string | undefined {\n const cls = parts.filter(Boolean).join(\" \");\n return cls || undefined;\n}\n\nfunction getRels(url: string, relUrls?: Record<string, RelUrl>): string | undefined {\n const rels = relUrls?.[url]?.rels;\n return rels?.length ? rels.join(\" \") : undefined;\n}\n\nfunction getTag(types: string[]): string {\n for (const t of types) {\n if (t in SEMANTIC_ROOTS) return SEMANTIC_ROOTS[t];\n }\n return \"div\";\n}\n\n// ============================================================================\n// Property Renderers\n// ============================================================================\n\nfunction renderPhoto(prop: string, url: string, cls: string): HTMLImageElement {\n // Don't include alt=\"\" - it changes the parsed output from string to {value, alt}\n return h(\"img\", { class: cls, src: url });\n}\n\nfunction renderVideo(prop: string, url: string, cls: string): HTMLVideoElement {\n return h(\"video\", { class: cls, src: url });\n}\n\nfunction renderAudio(prop: string, url: string, cls: string): HTMLAudioElement {\n return h(\"audio\", { class: cls, src: url });\n}\n\nfunction renderLink(\n url: string,\n cls: string,\n displayText?: string,\n rel?: string\n): HTMLAnchorElement {\n return h(\"a\", { class: cls, href: url, rel }, [displayText ?? url]);\n}\n\nfunction renderEmail(value: string, cls: string): HTMLAnchorElement {\n const href = value.startsWith(\"mailto:\") ? value : `mailto:${value}`;\n const display = value.replace(/^mailto:/, \"\");\n return h(\"a\", { class: cls, href }, [display]);\n}\n\nfunction renderTel(value: string, cls: string): HTMLAnchorElement {\n const href = value.startsWith(\"tel:\") ? value : `tel:${value}`;\n return h(\"a\", { class: cls, href }, [value]);\n}\n\nfunction renderTime(value: string, cls: string): HTMLTimeElement {\n return h(\"time\", { class: cls, datetime: value }, [value]);\n}\n\nfunction renderText(prop: string, value: string, cls: string): HTMLElement {\n const tag = SEMANTIC_PROPS[`p-${prop}`] ?? \"span\";\n return h(tag, { class: cls }, [value]);\n}\n\nfunction renderUrlObject(prop: string, obj: UrlObject): HTMLImageElement {\n const attrs: Record<string, string | undefined> = {\n class: `u-${prop}`,\n src: obj.value,\n alt: obj.alt,\n };\n\n if (obj.srcset && Object.keys(obj.srcset).length) {\n attrs.srcset = Object.entries(obj.srcset)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => `${v} ${k}`)\n .join(\", \");\n }\n\n return h(\"img\", attrs);\n}\n\nfunction renderEProperty(prop: string, value: EValue): HTMLDivElement {\n const div = h(\"div\", { class: `e-${prop}` });\n if (value.html) {\n div.appendChild(parseHtml(value.html));\n } else if (value.value) {\n div.textContent = value.value;\n }\n return div;\n}\n\nfunction renderRuby(name: string, ipa: string): HTMLElement {\n const ruby = h(\"ruby\", { \"aria-hidden\": \"true\" });\n\n ruby.appendChild(h(\"strong\", { class: \"p-name\" }, [name]));\n ruby.appendChild(h(\"rp\", undefined, [\"(\"]));\n\n const rt = h(\"rt\");\n rt.append(text(\"/ \"), h(\"span\", { class: \"p-ipa\" }, [ipa]), text(\" /\"));\n ruby.appendChild(rt);\n\n ruby.appendChild(h(\"rp\", undefined, [\")\"]));\n\n return ruby;\n}\n\n/** Render a string property value */\nfunction renderStringProp(\n props: string[],\n value: string,\n relUrls?: Record<string, RelUrl>,\n headingLevel?: number\n): HTMLElement {\n const prop = props[0];\n\n // Media elements\n if (prop === \"photo\" || prop === \"logo\") {\n return renderPhoto(prop, value, classes(...props.map(p => `u-${p}`))!);\n }\n if (prop === \"video\") {\n return renderVideo(prop, value, classes(...props.map(p => `u-${p}`))!);\n }\n if (prop === \"audio\") {\n return renderAudio(prop, value, classes(...props.map(p => `u-${p}`))!);\n }\n\n // URL properties\n if (URL_PROPS.has(prop) && isUrl(value)) {\n return renderLink(\n value,\n classes(...props.map(p => `u-${p}`))!,\n value,\n getRels(value, relUrls)\n );\n }\n\n // Email\n if (EMAIL_PROPS.has(prop)) {\n return renderEmail(value, classes(...props.map(p => `u-${p}`))!);\n }\n\n // Telephone\n if (TEL_PROPS.has(prop)) {\n return renderTel(value, classes(...props.map(p => `p-${p}`))!);\n }\n\n // Datetime\n if (DT_PROPS.has(prop)) {\n return renderTime(value, classes(...props.map(p => `dt-${p}`))!);\n }\n\n // Default text - use heading tag for name property if headingLevel is set\n const cls = classes(...props.map(p => `p-${p}`))!;\n if (prop === \"name\" && headingLevel != null) {\n return h(`h${headingLevel}` as keyof HTMLElementTagNameMap, { class: cls }, [value]);\n }\n return renderText(prop, value, cls);\n}\n\n// ============================================================================\n// Item Renderer\n// ============================================================================\n\ninterface ItemOptions {\n extraClasses?: string[];\n asProperty?: boolean;\n propertyPrefix?: string;\n relUrls?: Record<string, RelUrl>;\n headingLevel?: number;\n}\n\nfunction embeddedPrefix(item: Mf2Item): string {\n if (typeof item.html === \"string\") return \"e\";\n if (typeof item.value === \"object\" && item.value !== null) return \"u\";\n return \"p\";\n}\n\nfunction renderItem(item: Mf2Item, opts: ItemOptions = {}): HTMLElement {\n const { extraClasses = [], asProperty = false, propertyPrefix, relUrls, headingLevel } = opts;\n const props = item.properties;\n const children = item.children ?? [];\n\n // Create root element\n const el = h(getTag(item.type), {\n id: item.id,\n class: classes(...extraClasses, ...item.type),\n });\n\n // VCP data node for p/dt embedded items\n if (asProperty && (propertyPrefix === \"p\" || propertyPrefix === \"dt\")) {\n if (\"value\" in item && typeof item.value !== \"object\") {\n el.appendChild(h(\"data\", { class: \"value\", value: String(item.value) }));\n }\n }\n\n // e-* property with HTML\n if (asProperty && propertyPrefix === \"e\" && typeof item.html === \"string\") {\n el.appendChild(parseHtml(item.html));\n return el;\n }\n\n // Get embedded value for special name handling\n const embeddedValue = asProperty ? item.value : undefined;\n\n // Check for name+ipa ruby rendering\n const consumed = new Set<string>();\n let ruby: [string, string] | null = null;\n\n const names = props.name ?? [];\n const ipas = props.ipa ?? [];\n if (names.length && ipas.length) {\n const name = typeof names[0] === \"string\" ? names[0] : null;\n const ipa = typeof ipas[0] === \"string\" ? ipas[0] : null;\n if (name && ipa) {\n ruby = [name, ipa];\n consumed.add(\"name\");\n consumed.add(\"ipa\");\n }\n }\n\n // Check if name should be rendered as a link (single name + single URL, no ruby)\n // Don't apply when rendering as a property (changes value extraction on re-parse)\n // (name, url, list of url properties to include in class)\n let linkedName: [string, string, string[]] | null = null;\n if (!ruby && !asProperty) {\n const urls = props.url ?? [];\n if (names.length === 1 && urls.length === 1) {\n const nameVal = typeof names[0] === \"string\" ? names[0] : null;\n const urlVal = typeof urls[0] === \"string\" ? urls[0] : null;\n if (nameVal && urlVal && isUrl(urlVal)) {\n // Collect URL properties that share this URL value (like uid)\n const urlProps = [\"url\"];\n const uids = props.uid ?? [];\n if (uids.length === 1 && uids[0] === urlVal) {\n urlProps.push(\"uid\");\n consumed.add(\"uid\");\n }\n consumed.add(\"name\");\n consumed.add(\"url\");\n linkedName = [nameVal, urlVal, urlProps];\n }\n }\n }\n\n // Track rendered (value, category) pairs for grouping\n const rendered = new Set<string>();\n\n for (const prop of sortProps(props)) {\n // Insert ruby at name position\n if (prop === \"name\" && ruby) {\n const rubyEl = renderRuby(...ruby);\n if (headingLevel != null) {\n const heading = h(`h${headingLevel}` as keyof HTMLElementTagNameMap);\n heading.appendChild(rubyEl);\n el.appendChild(heading);\n } else {\n el.appendChild(rubyEl);\n }\n ruby = null;\n }\n\n // Render linked name at the position where \"name\" would appear\n if (prop === \"name\" && linkedName) {\n const [nameVal, urlVal, urlProps] = linkedName;\n const cls = classes(\"p-name\", ...urlProps.map(p => `u-${p}`));\n const rel = getRels(urlVal, relUrls);\n const linkEl = h(\"a\", { class: cls, href: urlVal, rel }, [nameVal]);\n if (headingLevel != null) {\n const heading = h(`h${headingLevel}` as keyof HTMLElementTagNameMap);\n heading.appendChild(linkEl);\n el.appendChild(heading);\n } else {\n el.appendChild(linkEl);\n }\n linkedName = null;\n }\n\n if (consumed.has(prop)) continue;\n\n // Calculate next heading level for embedded items (increment, cap at 6)\n const childHeading = headingLevel != null ? Math.min(headingLevel + 1, 6) : undefined;\n\n for (const v of props[prop]) {\n // Embedded mf2 item\n if (isMf2Item(v)) {\n const prefix = embeddedPrefix(v);\n el.appendChild(renderItem(v, {\n extraClasses: [`${prefix}-${prop}`],\n asProperty: true,\n propertyPrefix: prefix,\n relUrls,\n headingLevel: childHeading,\n }));\n continue;\n }\n\n // e-* HTML content\n if (isEValue(v)) {\n el.appendChild(renderEProperty(prop, v));\n continue;\n }\n\n // URL object (img with alt/srcset)\n if (isUrlObject(v)) {\n el.appendChild(renderUrlObject(prop, v));\n continue;\n }\n\n // If this item is itself embedded as a property, prefer dt-* for `name`\n // when its representative value differs from its `properties.name[0]`.\n if (\n asProperty &&\n propertyPrefix === \"p\" &&\n prop === \"name\" &&\n typeof embeddedValue === \"string\" &&\n typeof v === \"string\" &&\n v !== embeddedValue &&\n !v.startsWith(\"http://\") &&\n !v.startsWith(\"https://\")\n ) {\n // Render as time element with dt-name class (no datetime attribute)\n el.appendChild(h(\"time\", { class: \"dt-name\" }, [v]));\n continue;\n }\n\n // String value - group by value for merged classes\n if (typeof v === \"string\") {\n const cat = URL_PROPS.has(prop) && isUrl(v) ? \"url\"\n : EMAIL_PROPS.has(prop) ? \"email\"\n : TEL_PROPS.has(prop) ? \"tel\"\n : DT_PROPS.has(prop) ? \"datetime\"\n : `text:${prop}`;\n\n const key = `${v}\\0${cat}`;\n if (rendered.has(key)) continue;\n rendered.add(key);\n\n // Collect all props with same value+category\n const group: string[] = [];\n for (const p of sortProps(props)) {\n if (consumed.has(p)) continue;\n for (const pv of props[p]) {\n if (typeof pv !== \"string\" || pv !== v) continue;\n const pCat = URL_PROPS.has(p) && isUrl(pv) ? \"url\"\n : EMAIL_PROPS.has(p) ? \"email\"\n : TEL_PROPS.has(p) ? \"tel\"\n : DT_PROPS.has(p) ? \"datetime\"\n : `text:${p}`;\n if (pCat === cat && !group.includes(p)) {\n group.push(p);\n }\n }\n }\n\n el.appendChild(renderStringProp(group, v, relUrls, headingLevel));\n continue;\n }\n\n // Fallback\n el.appendChild(renderStringProp([prop], String(v), relUrls, headingLevel));\n }\n }\n\n // Render children - calculate next heading level (increment, cap at 6)\n const childrenHeading = headingLevel != null ? Math.min(headingLevel + 1, 6) : undefined;\n for (const child of children) {\n el.appendChild(renderItem(child, { relUrls, headingLevel: childrenHeading }));\n }\n\n return el;\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport interface RenderOptions {\n /** Return HTMLElement instead of string */\n asElement?: boolean;\n /** If set, render name properties as heading elements starting at this level (1-6).\n * Names in nested items use incrementing levels (capped at h6).\n * Default is undefined (render as <strong>). */\n topHeading?: number;\n}\n\n/**\n * Render an mf2 document to HTML.\n */\nexport function render(doc: Mf2Document, opts?: { asElement: true; topHeading?: number }): HTMLElement;\nexport function render(doc: Mf2Document, opts?: { asElement?: false; topHeading?: number }): string;\nexport function render(doc: Mf2Document, opts: RenderOptions = {}): string | HTMLElement {\n const relUrls = doc[\"rel-urls\"];\n const { topHeading } = opts;\n const main = h(\"main\");\n\n // Render items\n for (const item of doc.items) {\n main.appendChild(renderItem(item, { relUrls, headingLevel: topHeading }));\n }\n\n // Render rel-urls as nav\n if (relUrls && Object.keys(relUrls).length) {\n const nav = h(\"nav\");\n for (const url of Object.keys(relUrls).sort()) {\n const info = relUrls[url];\n nav.appendChild(h(\"a\", {\n href: url,\n rel: info.rels?.length ? info.rels.join(\" \") : undefined,\n }, [info.text ?? url]));\n }\n main.appendChild(nav);\n }\n\n return opts.asElement ? main : main.outerHTML;\n}\n\nexport interface RenderItemElementOptions {\n relUrls?: Record<string, RelUrl>;\n topHeading?: number;\n}\n\n/**\n * Render a single mf2 item to an HTMLElement.\n */\nexport function renderItemElement(\n item: Mf2Item,\n options: RenderItemElementOptions = {}\n): HTMLElement {\n const { relUrls, topHeading } = options;\n return renderItem(item, { relUrls, headingLevel: topHeading });\n}\n\nexport interface RenderItemsOptions {\n relUrls?: Record<string, RelUrl>;\n topHeading?: number;\n}\n\n/**\n * Render multiple items efficiently using DocumentFragment.\n */\nexport function renderItems(\n items: Mf2Item[],\n options: RenderItemsOptions = {}\n): DocumentFragment {\n const { relUrls, topHeading } = options;\n const fragment = document.createDocumentFragment();\n for (const item of items) {\n fragment.appendChild(renderItem(item, { relUrls, headingLevel: topHeading }));\n }\n return fragment;\n}\n\nexport { parseHtml as parseHtmlFragment };\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["SEMANTIC_ROOTS", "SEMANTIC_PROPS", "URL_PROPS", "EMAIL_PROPS", "TEL_PROPS", "DT_PROPS", "PROP_ORDER", "h", "tag", "attrs", "children", "el", "k", "v", "child", "text", "content", "parseHtml", "html", "tpl", "isMf2Item", "isEValue", "isUrlObject", "isUrl", "s", "propOrder", "p", "i", "sortProps", "props", "a", "b", "classes", "parts", "getRels", "url", "relUrls", "rels", "getTag", "types", "t", "renderPhoto", "prop", "cls", "renderVideo", "renderAudio", "renderLink", "displayText", "rel", "renderEmail", "value", "href", "display", "renderTel", "renderTime", "renderText", "renderUrlObject", "obj", "renderEProperty", "div", "renderRuby", "name", "ipa", "ruby", "rt", "renderStringProp", "headingLevel", "embeddedPrefix", "item", "renderItem", "opts", "extraClasses", "asProperty", "propertyPrefix", "embeddedValue", "consumed", "names", "ipas", "linkedName", "urls", "nameVal", "urlVal", "urlProps", "uids", "rendered", "rubyEl", "heading", "linkEl", "childHeading", "prefix", "cat", "key", "group", "pv", "childrenHeading", "render", "doc", "topHeading", "main", "nav", "info", "renderItemElement", "options", "renderItems", "items", "fragment"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var mf2dom=(()=>{var O=Object.defineProperty;var K=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var B=Object.prototype.hasOwnProperty;var z=(e,n)=>{for(var t in n)O(e,t,{get:n[t],enumerable:!0})},G=(e,n,t,i)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of q(n))!B.call(e,r)&&r!==t&&O(e,r,{get:()=>n[r],enumerable:!(i=K(n,r))||i.enumerable});return e};var J=e=>G(O({},"__esModule",{value:!0}),e);var he={};z(he,{parseHtmlFragment:()=>U,render:()=>fe,renderItemElement:()=>ge,renderItems:()=>me});var V={"h-entry":"article","h-feed":"section","h-event":"article","h-product":"article","h-recipe":"article","h-review":"article","h-resume":"article","h-adr":"address","h-cite":"blockquote","h-geo":"data"},Q={"p-adr":"address","p-street-address":"span","p-extended-address":"span","p-locality":"span","p-region":"span","p-postal-code":"span","p-country-name":"span","p-name":"strong","p-summary":"p","p-note":"p","p-content":"p","p-description":"p","p-author":"span"},P=new Set(["url","uid","photo","logo","video","audio","syndication","in-reply-to","like-of","repost-of","bookmark-of","follow-of","read-of","tag-of","location"]),k=new Set(["email"]),$=new Set(["tel"]),I=new Set(["published","updated","start","end","duration","bday","anniversary","rev"]),w=["photo","logo","featured","name","honorific-prefix","given-name","additional-name","family-name","sort-string","honorific-suffix","nickname","ipa","author","summary","note","content","description","published","updated","start","end","duration","bday","anniversary","rev","location","url","uid","syndication","in-reply-to","like-of","repost-of","bookmark-of","follow-of","read-of","read-status","email","tel","impp","adr","geo","latitude","longitude","altitude","street-address","extended-address","locality","region","postal-code","country-name","label","org","job-title","role","category","rsvp","attendee","key","sex","gender-identity"];function a(e,n,t){let i=document.createElement(e);if(n)for(let[r,d]of Object.entries(n))d!=null&&i.setAttribute(r,d);if(t)for(let r of t)i.append(r);return i}function N(e){return document.createTextNode(e)}function U(e){let n=document.createElement("template");return n.innerHTML=e,n.content}function X(e){return typeof e=="object"&&e!==null&&"type"in e&&"properties"in e}function Y(e){return typeof e=="object"&&e!==null&&"html"in e}function Z(e){return typeof e=="object"&&e!==null&&"value"in e&&("alt"in e||"srcset"in e)}function C(e){return e.startsWith("http://")||e.startsWith("https://")||e.startsWith("/")}function v(e){let n=w.indexOf(e);return n>=0?n:w.length}function D(e){return Object.keys(e).sort((n,t)=>v(n)-v(t))}function g(...e){return e.filter(Boolean).join(" ")||void 0}function W(e,n){let t=n?.[e]?.rels;return t?.length?t.join(" "):void 0}function ee(e){for(let n of e)if(n in V)return V[n];return"div"}function ne(e,n,t){return a("img",{class:t,src:n})}function te(e,n,t){return a("video",{class:t,src:n})}function re(e,n,t){return a("audio",{class:t,src:n})}function ie(e,n,t,i){return a("a",{class:n,href:e,rel:i},[t??e])}function se(e,n){let t=e.startsWith("mailto:")?e:`mailto:${e}`,i=e.replace(/^mailto:/,"");return a("a",{class:n,href:t},[i])}function oe(e,n){let t=e.startsWith("tel:")?e:`tel:${e}`;return a("a",{class:n,href:t},[e])}function ae(e,n){return a("time",{class:n,datetime:e},[e])}function le(e,n,t){let i=Q[`p-${e}`]??"span";return a(i,{class:t},[n])}function de(e,n){let t={class:`u-${e}`,src:n.value,alt:n.alt};return n.srcset&&Object.keys(n.srcset).length&&(t.srcset=Object.entries(n.srcset).sort(([i],[r])=>i.localeCompare(r)).map(([i,r])=>`${r} ${i}`).join(", ")),a("img",t)}function ce(e,n){let t=a("div",{class:`e-${e}`});return n.html?t.appendChild(U(n.html)):n.value&&(t.textContent=n.value),t}function pe(e,n){let t=a("ruby",{"aria-hidden":"true"});t.appendChild(a("strong",{class:"p-name"},[e])),t.appendChild(a("rp",void 0,["("]));let i=a("rt");return i.append(N("/ "),a("span",{class:"p-ipa"},[n]),N(" /")),t.appendChild(i),t.appendChild(a("rp",void 0,[")"])),t}function A(e,n,t,i){let r=e[0];if(r==="photo"||r==="logo")return ne(r,n,g(...e.map(s=>`u-${s}`)));if(r==="video")return te(r,n,g(...e.map(s=>`u-${s}`)));if(r==="audio")return re(r,n,g(...e.map(s=>`u-${s}`)));if(P.has(r)&&C(n))return ie(n,g(...e.map(s=>`u-${s}`)),n,W(n,t));if(k.has(r))return se(n,g(...e.map(s=>`u-${s}`)));if($.has(r))return oe(n,g(...e.map(s=>`p-${s}`)));if(I.has(r))return ae(n,g(...e.map(s=>`dt-${s}`)));let d=g(...e.map(s=>`p-${s}`));return r==="name"&&i!=null?a(`h${i}`,{class:d},[n]):le(r,n,d)}function ue(e){return typeof e.html=="string"?"e":typeof e.value=="object"&&e.value!==null?"u":"p"}function R(e,n={}){let{extraClasses:t=[],asProperty:i=!1,propertyPrefix:r,relUrls:d,headingLevel:s}=n,u=e.properties,_=e.children??[],c=a(ee(e.type),{id:e.id,class:g(...t,...e.type)});if(i&&(r==="p"||r==="dt")&&"value"in e&&typeof e.value!="object"&&c.appendChild(a("data",{class:"value",value:String(e.value)})),i&&r==="e"&&typeof e.html=="string")return c.appendChild(U(e.html)),c;let j=i?e.value:void 0,m=new Set,T=null,M=u.name??[],x=u.ipa??[];if(M.length&&x.length){let l=typeof M[0]=="string"?M[0]:null,h=typeof x[0]=="string"?x[0]:null;l&&h&&(T=[l,h],m.add("name"),m.add("ipa"))}let L=null;if(!T&&!i){let l=u.url??[];if(M.length===1&&l.length===1){let h=typeof M[0]=="string"?M[0]:null,o=typeof l[0]=="string"?l[0]:null;if(h&&o&&C(o)){let p=["url"],y=u.uid??[];y.length===1&&y[0]===o&&(p.push("uid"),m.add("uid")),m.add("name"),m.add("url"),L=[h,o,p]}}}let S=new Set;for(let l of D(u)){if(l==="name"&&T){let o=pe(...T);if(s!=null){let p=a(`h${s}`);p.appendChild(o),c.appendChild(p)}else c.appendChild(o);T=null}if(l==="name"&&L){let[o,p,y]=L,b=g("p-name",...y.map(H=>`u-${H}`)),f=W(p,d),E=a("a",{class:b,href:p,rel:f},[o]);if(s!=null){let H=a(`h${s}`);H.appendChild(E),c.appendChild(H)}else c.appendChild(E);L=null}if(m.has(l))continue;let h=s!=null?Math.min(s+1,6):void 0;for(let o of u[l]){if(X(o)){let p=ue(o);c.appendChild(R(o,{extraClasses:[`${p}-${l}`],asProperty:!0,propertyPrefix:p,relUrls:d,headingLevel:h}));continue}if(Y(o)){c.appendChild(ce(l,o));continue}if(Z(o)){c.appendChild(de(l,o));continue}if(i&&r==="p"&&l==="name"&&typeof j=="string"&&typeof o=="string"&&o!==j&&!o.startsWith("http://")&&!o.startsWith("https://")){c.appendChild(a("time",{class:"dt-name"},[o]));continue}if(typeof o=="string"){let p=P.has(l)&&C(o)?"url":k.has(l)?"email":$.has(l)?"tel":I.has(l)?"datetime":`text:${l}`,y=`${o}\0${p}`;if(S.has(y))continue;S.add(y);let b=[];for(let f of D(u))if(!m.has(f))for(let E of u[f]){if(typeof E!="string"||E!==o)continue;(P.has(f)&&C(E)?"url":k.has(f)?"email":$.has(f)?"tel":I.has(f)?"datetime":`text:${f}`)===p&&!b.includes(f)&&b.push(f)}c.appendChild(A(b,o,d,s));continue}c.appendChild(A([l],String(o),d,s))}}let F=s!=null?Math.min(s+1,6):void 0;for(let l of _)c.appendChild(R(l,{relUrls:d,headingLevel:F}));return c}function fe(e,n={}){let t=e["rel-urls"],{topHeading:i}=n,r=a("main");for(let d of e.items)r.appendChild(R(d,{relUrls:t,headingLevel:i}));if(t&&Object.keys(t).length){let d=a("nav");for(let s of Object.keys(t).sort()){let u=t[s];d.appendChild(a("a",{href:s,rel:u.rels?.length?u.rels.join(" "):void 0},[u.text??s]))}r.appendChild(d)}return n.asElement?r:r.outerHTML}function ge(e,n={}){let{relUrls:t,topHeading:i}=n;return R(e,{relUrls:t,headingLevel:i})}function me(e,n={}){let{relUrls:t,topHeading:i}=n,r=document.createDocumentFragment();for(let d of e)r.appendChild(R(d,{relUrls:t,headingLevel:i}));return r}return J(he);})();
|
|
2
|
+
//# sourceMappingURL=mf2dom.min.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../renderer.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Microformats2 Renderer - Pure Programmatic DOM Building\n *\n * Renders mf2 JSON to semantic HTML by building the DOM tree programmatically.\n * Uses DocumentFragment for efficient batch updates with minimal reflows.\n */\n\nimport type {\n Mf2Document,\n Mf2Item,\n PropertyValue,\n EValue,\n UrlObject,\n RelUrl,\n} from \"./types.js\";\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nconst SEMANTIC_ROOTS: Record<string, string> = {\n \"h-entry\": \"article\",\n \"h-feed\": \"section\",\n \"h-event\": \"article\",\n \"h-product\": \"article\",\n \"h-recipe\": \"article\",\n \"h-review\": \"article\",\n \"h-resume\": \"article\",\n \"h-adr\": \"address\",\n \"h-cite\": \"blockquote\",\n \"h-geo\": \"data\",\n};\n\nconst SEMANTIC_PROPS: Record<string, string> = {\n // Address components use address element\n \"p-adr\": \"address\",\n \"p-street-address\": \"span\",\n \"p-extended-address\": \"span\",\n \"p-locality\": \"span\",\n \"p-region\": \"span\",\n \"p-postal-code\": \"span\",\n \"p-country-name\": \"span\",\n // Name properties use strong for emphasis\n \"p-name\": \"strong\",\n // Paragraph-like properties\n \"p-summary\": \"p\",\n \"p-note\": \"p\",\n \"p-content\": \"p\",\n \"p-description\": \"p\",\n // Author info\n \"p-author\": \"span\",\n};\n\nconst URL_PROPS = new Set([\n \"url\", \"uid\", \"photo\", \"logo\", \"video\", \"audio\",\n \"syndication\", \"in-reply-to\", \"like-of\", \"repost-of\", \"bookmark-of\",\n \"follow-of\", \"read-of\", \"tag-of\", \"location\",\n]);\nconst EMAIL_PROPS = new Set([\"email\"]);\nconst TEL_PROPS = new Set([\"tel\"]);\nconst DT_PROPS = new Set([\n \"published\", \"updated\", \"start\", \"end\", \"duration\", \"bday\", \"anniversary\", \"rev\",\n]);\n\n// Semantic property ordering based on microformats.org wiki\n// Properties are grouped by semantic meaning for good display across types:\n// 1. Visual identity (photo, logo)\n// 2. Name/identity\n// 3. Author (for h-entry)\n// 4. Description/content\n// 5. Dates (important for h-entry, h-event)\n// 6. Location (for h-event, h-card)\n// 7. URLs and links\n// 8. Contact info (email, tel)\n// 9. Address details\n// 10. Organization/role\n// 11. Categories and other metadata\nconst PROP_ORDER = [\n // Visual identity first\n \"photo\",\n \"logo\",\n \"featured\",\n // Name properties\n \"name\",\n \"honorific-prefix\",\n \"given-name\",\n \"additional-name\",\n \"family-name\",\n \"sort-string\",\n \"honorific-suffix\",\n \"nickname\",\n \"ipa\",\n // Author (important for h-entry)\n \"author\",\n // Description/content\n \"summary\",\n \"note\",\n \"content\",\n \"description\",\n // Dates (prominent for h-entry, h-event)\n \"published\",\n \"updated\",\n \"start\",\n \"end\",\n \"duration\",\n \"bday\",\n \"anniversary\",\n \"rev\",\n // Location (for h-event)\n \"location\",\n // URLs and links\n \"url\",\n \"uid\",\n \"syndication\",\n \"in-reply-to\",\n \"like-of\",\n \"repost-of\",\n \"bookmark-of\",\n \"follow-of\",\n \"read-of\",\n \"read-status\",\n // Contact info\n \"email\",\n \"tel\",\n \"impp\",\n // Address details\n \"adr\",\n \"geo\",\n \"latitude\",\n \"longitude\",\n \"altitude\",\n \"street-address\",\n \"extended-address\",\n \"locality\",\n \"region\",\n \"postal-code\",\n \"country-name\",\n \"label\",\n // Organization/role\n \"org\",\n \"job-title\",\n \"role\",\n // Categories and metadata\n \"category\",\n \"rsvp\",\n \"attendee\",\n \"key\",\n \"sex\",\n \"gender-identity\",\n];\n\n// ============================================================================\n// DOM Builder Helpers\n// ============================================================================\n\n/** Create an element with attributes */\nfunction h<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attrs?: Record<string, string | undefined>,\n children?: (Node | string)[]\n): HTMLElementTagNameMap[K];\nfunction h(\n tag: string,\n attrs?: Record<string, string | undefined>,\n children?: (Node | string)[]\n): HTMLElement;\nfunction h(\n tag: string,\n attrs?: Record<string, string | undefined>,\n children?: (Node | string)[]\n): HTMLElement {\n const el = document.createElement(tag);\n\n if (attrs) {\n for (const [k, v] of Object.entries(attrs)) {\n if (v != null) el.setAttribute(k, v);\n }\n }\n\n if (children) {\n for (const child of children) {\n el.append(child);\n }\n }\n\n return el;\n}\n\n/** Create a text node */\nfunction text(content: string): Text {\n return document.createTextNode(content);\n}\n\n/** Parse HTML string to DocumentFragment (only for e-* content) */\nfunction parseHtml(html: string): DocumentFragment {\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = html;\n return tpl.content;\n}\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\nfunction isMf2Item(v: unknown): v is Mf2Item {\n return typeof v === \"object\" && v !== null && \"type\" in v && \"properties\" in v;\n}\n\nfunction isEValue(v: unknown): v is EValue {\n return typeof v === \"object\" && v !== null && \"html\" in v;\n}\n\nfunction isUrlObject(v: unknown): v is UrlObject {\n return typeof v === \"object\" && v !== null && \"value\" in v && (\"alt\" in v || \"srcset\" in v);\n}\n\nfunction isUrl(s: string): boolean {\n return s.startsWith(\"http://\") || s.startsWith(\"https://\") || s.startsWith(\"/\");\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction propOrder(p: string): number {\n const i = PROP_ORDER.indexOf(p);\n return i >= 0 ? i : PROP_ORDER.length;\n}\n\nfunction sortProps(props: Record<string, PropertyValue[]>): string[] {\n return Object.keys(props).sort((a, b) => propOrder(a) - propOrder(b));\n}\n\nfunction classes(...parts: (string | undefined)[]): string | undefined {\n const cls = parts.filter(Boolean).join(\" \");\n return cls || undefined;\n}\n\nfunction getRels(url: string, relUrls?: Record<string, RelUrl>): string | undefined {\n const rels = relUrls?.[url]?.rels;\n return rels?.length ? rels.join(\" \") : undefined;\n}\n\nfunction getTag(types: string[]): string {\n for (const t of types) {\n if (t in SEMANTIC_ROOTS) return SEMANTIC_ROOTS[t];\n }\n return \"div\";\n}\n\n// ============================================================================\n// Property Renderers\n// ============================================================================\n\nfunction renderPhoto(prop: string, url: string, cls: string): HTMLImageElement {\n // Don't include alt=\"\" - it changes the parsed output from string to {value, alt}\n return h(\"img\", { class: cls, src: url });\n}\n\nfunction renderVideo(prop: string, url: string, cls: string): HTMLVideoElement {\n return h(\"video\", { class: cls, src: url });\n}\n\nfunction renderAudio(prop: string, url: string, cls: string): HTMLAudioElement {\n return h(\"audio\", { class: cls, src: url });\n}\n\nfunction renderLink(\n url: string,\n cls: string,\n displayText?: string,\n rel?: string\n): HTMLAnchorElement {\n return h(\"a\", { class: cls, href: url, rel }, [displayText ?? url]);\n}\n\nfunction renderEmail(value: string, cls: string): HTMLAnchorElement {\n const href = value.startsWith(\"mailto:\") ? value : `mailto:${value}`;\n const display = value.replace(/^mailto:/, \"\");\n return h(\"a\", { class: cls, href }, [display]);\n}\n\nfunction renderTel(value: string, cls: string): HTMLAnchorElement {\n const href = value.startsWith(\"tel:\") ? value : `tel:${value}`;\n return h(\"a\", { class: cls, href }, [value]);\n}\n\nfunction renderTime(value: string, cls: string): HTMLTimeElement {\n return h(\"time\", { class: cls, datetime: value }, [value]);\n}\n\nfunction renderText(prop: string, value: string, cls: string): HTMLElement {\n const tag = SEMANTIC_PROPS[`p-${prop}`] ?? \"span\";\n return h(tag, { class: cls }, [value]);\n}\n\nfunction renderUrlObject(prop: string, obj: UrlObject): HTMLImageElement {\n const attrs: Record<string, string | undefined> = {\n class: `u-${prop}`,\n src: obj.value,\n alt: obj.alt,\n };\n\n if (obj.srcset && Object.keys(obj.srcset).length) {\n attrs.srcset = Object.entries(obj.srcset)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => `${v} ${k}`)\n .join(\", \");\n }\n\n return h(\"img\", attrs);\n}\n\nfunction renderEProperty(prop: string, value: EValue): HTMLDivElement {\n const div = h(\"div\", { class: `e-${prop}` });\n if (value.html) {\n div.appendChild(parseHtml(value.html));\n } else if (value.value) {\n div.textContent = value.value;\n }\n return div;\n}\n\nfunction renderRuby(name: string, ipa: string): HTMLElement {\n const ruby = h(\"ruby\", { \"aria-hidden\": \"true\" });\n\n ruby.appendChild(h(\"strong\", { class: \"p-name\" }, [name]));\n ruby.appendChild(h(\"rp\", undefined, [\"(\"]));\n\n const rt = h(\"rt\");\n rt.append(text(\"/ \"), h(\"span\", { class: \"p-ipa\" }, [ipa]), text(\" /\"));\n ruby.appendChild(rt);\n\n ruby.appendChild(h(\"rp\", undefined, [\")\"]));\n\n return ruby;\n}\n\n/** Render a string property value */\nfunction renderStringProp(\n props: string[],\n value: string,\n relUrls?: Record<string, RelUrl>,\n headingLevel?: number\n): HTMLElement {\n const prop = props[0];\n\n // Media elements\n if (prop === \"photo\" || prop === \"logo\") {\n return renderPhoto(prop, value, classes(...props.map(p => `u-${p}`))!);\n }\n if (prop === \"video\") {\n return renderVideo(prop, value, classes(...props.map(p => `u-${p}`))!);\n }\n if (prop === \"audio\") {\n return renderAudio(prop, value, classes(...props.map(p => `u-${p}`))!);\n }\n\n // URL properties\n if (URL_PROPS.has(prop) && isUrl(value)) {\n return renderLink(\n value,\n classes(...props.map(p => `u-${p}`))!,\n value,\n getRels(value, relUrls)\n );\n }\n\n // Email\n if (EMAIL_PROPS.has(prop)) {\n return renderEmail(value, classes(...props.map(p => `u-${p}`))!);\n }\n\n // Telephone\n if (TEL_PROPS.has(prop)) {\n return renderTel(value, classes(...props.map(p => `p-${p}`))!);\n }\n\n // Datetime\n if (DT_PROPS.has(prop)) {\n return renderTime(value, classes(...props.map(p => `dt-${p}`))!);\n }\n\n // Default text - use heading tag for name property if headingLevel is set\n const cls = classes(...props.map(p => `p-${p}`))!;\n if (prop === \"name\" && headingLevel != null) {\n return h(`h${headingLevel}` as keyof HTMLElementTagNameMap, { class: cls }, [value]);\n }\n return renderText(prop, value, cls);\n}\n\n// ============================================================================\n// Item Renderer\n// ============================================================================\n\ninterface ItemOptions {\n extraClasses?: string[];\n asProperty?: boolean;\n propertyPrefix?: string;\n relUrls?: Record<string, RelUrl>;\n headingLevel?: number;\n}\n\nfunction embeddedPrefix(item: Mf2Item): string {\n if (typeof item.html === \"string\") return \"e\";\n if (typeof item.value === \"object\" && item.value !== null) return \"u\";\n return \"p\";\n}\n\nfunction renderItem(item: Mf2Item, opts: ItemOptions = {}): HTMLElement {\n const { extraClasses = [], asProperty = false, propertyPrefix, relUrls, headingLevel } = opts;\n const props = item.properties;\n const children = item.children ?? [];\n\n // Create root element\n const el = h(getTag(item.type), {\n id: item.id,\n class: classes(...extraClasses, ...item.type),\n });\n\n // VCP data node for p/dt embedded items\n if (asProperty && (propertyPrefix === \"p\" || propertyPrefix === \"dt\")) {\n if (\"value\" in item && typeof item.value !== \"object\") {\n el.appendChild(h(\"data\", { class: \"value\", value: String(item.value) }));\n }\n }\n\n // e-* property with HTML\n if (asProperty && propertyPrefix === \"e\" && typeof item.html === \"string\") {\n el.appendChild(parseHtml(item.html));\n return el;\n }\n\n // Get embedded value for special name handling\n const embeddedValue = asProperty ? item.value : undefined;\n\n // Check for name+ipa ruby rendering\n const consumed = new Set<string>();\n let ruby: [string, string] | null = null;\n\n const names = props.name ?? [];\n const ipas = props.ipa ?? [];\n if (names.length && ipas.length) {\n const name = typeof names[0] === \"string\" ? names[0] : null;\n const ipa = typeof ipas[0] === \"string\" ? ipas[0] : null;\n if (name && ipa) {\n ruby = [name, ipa];\n consumed.add(\"name\");\n consumed.add(\"ipa\");\n }\n }\n\n // Check if name should be rendered as a link (single name + single URL, no ruby)\n // Don't apply when rendering as a property (changes value extraction on re-parse)\n // (name, url, list of url properties to include in class)\n let linkedName: [string, string, string[]] | null = null;\n if (!ruby && !asProperty) {\n const urls = props.url ?? [];\n if (names.length === 1 && urls.length === 1) {\n const nameVal = typeof names[0] === \"string\" ? names[0] : null;\n const urlVal = typeof urls[0] === \"string\" ? urls[0] : null;\n if (nameVal && urlVal && isUrl(urlVal)) {\n // Collect URL properties that share this URL value (like uid)\n const urlProps = [\"url\"];\n const uids = props.uid ?? [];\n if (uids.length === 1 && uids[0] === urlVal) {\n urlProps.push(\"uid\");\n consumed.add(\"uid\");\n }\n consumed.add(\"name\");\n consumed.add(\"url\");\n linkedName = [nameVal, urlVal, urlProps];\n }\n }\n }\n\n // Track rendered (value, category) pairs for grouping\n const rendered = new Set<string>();\n\n for (const prop of sortProps(props)) {\n // Insert ruby at name position\n if (prop === \"name\" && ruby) {\n const rubyEl = renderRuby(...ruby);\n if (headingLevel != null) {\n const heading = h(`h${headingLevel}` as keyof HTMLElementTagNameMap);\n heading.appendChild(rubyEl);\n el.appendChild(heading);\n } else {\n el.appendChild(rubyEl);\n }\n ruby = null;\n }\n\n // Render linked name at the position where \"name\" would appear\n if (prop === \"name\" && linkedName) {\n const [nameVal, urlVal, urlProps] = linkedName;\n const cls = classes(\"p-name\", ...urlProps.map(p => `u-${p}`));\n const rel = getRels(urlVal, relUrls);\n const linkEl = h(\"a\", { class: cls, href: urlVal, rel }, [nameVal]);\n if (headingLevel != null) {\n const heading = h(`h${headingLevel}` as keyof HTMLElementTagNameMap);\n heading.appendChild(linkEl);\n el.appendChild(heading);\n } else {\n el.appendChild(linkEl);\n }\n linkedName = null;\n }\n\n if (consumed.has(prop)) continue;\n\n // Calculate next heading level for embedded items (increment, cap at 6)\n const childHeading = headingLevel != null ? Math.min(headingLevel + 1, 6) : undefined;\n\n for (const v of props[prop]) {\n // Embedded mf2 item\n if (isMf2Item(v)) {\n const prefix = embeddedPrefix(v);\n el.appendChild(renderItem(v, {\n extraClasses: [`${prefix}-${prop}`],\n asProperty: true,\n propertyPrefix: prefix,\n relUrls,\n headingLevel: childHeading,\n }));\n continue;\n }\n\n // e-* HTML content\n if (isEValue(v)) {\n el.appendChild(renderEProperty(prop, v));\n continue;\n }\n\n // URL object (img with alt/srcset)\n if (isUrlObject(v)) {\n el.appendChild(renderUrlObject(prop, v));\n continue;\n }\n\n // If this item is itself embedded as a property, prefer dt-* for `name`\n // when its representative value differs from its `properties.name[0]`.\n if (\n asProperty &&\n propertyPrefix === \"p\" &&\n prop === \"name\" &&\n typeof embeddedValue === \"string\" &&\n typeof v === \"string\" &&\n v !== embeddedValue &&\n !v.startsWith(\"http://\") &&\n !v.startsWith(\"https://\")\n ) {\n // Render as time element with dt-name class (no datetime attribute)\n el.appendChild(h(\"time\", { class: \"dt-name\" }, [v]));\n continue;\n }\n\n // String value - group by value for merged classes\n if (typeof v === \"string\") {\n const cat = URL_PROPS.has(prop) && isUrl(v) ? \"url\"\n : EMAIL_PROPS.has(prop) ? \"email\"\n : TEL_PROPS.has(prop) ? \"tel\"\n : DT_PROPS.has(prop) ? \"datetime\"\n : `text:${prop}`;\n\n const key = `${v}\\0${cat}`;\n if (rendered.has(key)) continue;\n rendered.add(key);\n\n // Collect all props with same value+category\n const group: string[] = [];\n for (const p of sortProps(props)) {\n if (consumed.has(p)) continue;\n for (const pv of props[p]) {\n if (typeof pv !== \"string\" || pv !== v) continue;\n const pCat = URL_PROPS.has(p) && isUrl(pv) ? \"url\"\n : EMAIL_PROPS.has(p) ? \"email\"\n : TEL_PROPS.has(p) ? \"tel\"\n : DT_PROPS.has(p) ? \"datetime\"\n : `text:${p}`;\n if (pCat === cat && !group.includes(p)) {\n group.push(p);\n }\n }\n }\n\n el.appendChild(renderStringProp(group, v, relUrls, headingLevel));\n continue;\n }\n\n // Fallback\n el.appendChild(renderStringProp([prop], String(v), relUrls, headingLevel));\n }\n }\n\n // Render children - calculate next heading level (increment, cap at 6)\n const childrenHeading = headingLevel != null ? Math.min(headingLevel + 1, 6) : undefined;\n for (const child of children) {\n el.appendChild(renderItem(child, { relUrls, headingLevel: childrenHeading }));\n }\n\n return el;\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport interface RenderOptions {\n /** Return HTMLElement instead of string */\n asElement?: boolean;\n /** If set, render name properties as heading elements starting at this level (1-6).\n * Names in nested items use incrementing levels (capped at h6).\n * Default is undefined (render as <strong>). */\n topHeading?: number;\n}\n\n/**\n * Render an mf2 document to HTML.\n */\nexport function render(doc: Mf2Document, opts?: { asElement: true; topHeading?: number }): HTMLElement;\nexport function render(doc: Mf2Document, opts?: { asElement?: false; topHeading?: number }): string;\nexport function render(doc: Mf2Document, opts: RenderOptions = {}): string | HTMLElement {\n const relUrls = doc[\"rel-urls\"];\n const { topHeading } = opts;\n const main = h(\"main\");\n\n // Render items\n for (const item of doc.items) {\n main.appendChild(renderItem(item, { relUrls, headingLevel: topHeading }));\n }\n\n // Render rel-urls as nav\n if (relUrls && Object.keys(relUrls).length) {\n const nav = h(\"nav\");\n for (const url of Object.keys(relUrls).sort()) {\n const info = relUrls[url];\n nav.appendChild(h(\"a\", {\n href: url,\n rel: info.rels?.length ? info.rels.join(\" \") : undefined,\n }, [info.text ?? url]));\n }\n main.appendChild(nav);\n }\n\n return opts.asElement ? main : main.outerHTML;\n}\n\nexport interface RenderItemElementOptions {\n relUrls?: Record<string, RelUrl>;\n topHeading?: number;\n}\n\n/**\n * Render a single mf2 item to an HTMLElement.\n */\nexport function renderItemElement(\n item: Mf2Item,\n options: RenderItemElementOptions = {}\n): HTMLElement {\n const { relUrls, topHeading } = options;\n return renderItem(item, { relUrls, headingLevel: topHeading });\n}\n\nexport interface RenderItemsOptions {\n relUrls?: Record<string, RelUrl>;\n topHeading?: number;\n}\n\n/**\n * Render multiple items efficiently using DocumentFragment.\n */\nexport function renderItems(\n items: Mf2Item[],\n options: RenderItemsOptions = {}\n): DocumentFragment {\n const { relUrls, topHeading } = options;\n const fragment = document.createDocumentFragment();\n for (const item of items) {\n fragment.appendChild(renderItem(item, { relUrls, headingLevel: topHeading }));\n }\n return fragment;\n}\n\nexport { parseHtml as parseHtmlFragment };\n"],
|
|
5
|
+
"mappings": "0bAAA,IAAAA,GAAA,GAAAC,EAAAD,GAAA,uBAAAE,EAAA,WAAAC,GAAA,sBAAAC,GAAA,gBAAAC,KAoBA,IAAMC,EAAyC,CAC7C,UAAW,UACX,SAAU,UACV,UAAW,UACX,YAAa,UACb,WAAY,UACZ,WAAY,UACZ,WAAY,UACZ,QAAS,UACT,SAAU,aACV,QAAS,MACX,EAEMC,EAAyC,CAE7C,QAAS,UACT,mBAAoB,OACpB,qBAAsB,OACtB,aAAc,OACd,WAAY,OACZ,gBAAiB,OACjB,iBAAkB,OAElB,SAAU,SAEV,YAAa,IACb,SAAU,IACV,YAAa,IACb,gBAAiB,IAEjB,WAAY,MACd,EAEMC,EAAY,IAAI,IAAI,CACxB,MAAO,MAAO,QAAS,OAAQ,QAAS,QACxC,cAAe,cAAe,UAAW,YAAa,cACtD,YAAa,UAAW,SAAU,UACpC,CAAC,EACKC,EAAc,IAAI,IAAI,CAAC,OAAO,CAAC,EAC/BC,EAAY,IAAI,IAAI,CAAC,KAAK,CAAC,EAC3BC,EAAW,IAAI,IAAI,CACvB,YAAa,UAAW,QAAS,MAAO,WAAY,OAAQ,cAAe,KAC7E,CAAC,EAeKC,EAAa,CAEjB,QACA,OACA,WAEA,OACA,mBACA,aACA,kBACA,cACA,cACA,mBACA,WACA,MAEA,SAEA,UACA,OACA,UACA,cAEA,YACA,UACA,QACA,MACA,WACA,OACA,cACA,MAEA,WAEA,MACA,MACA,cACA,cACA,UACA,YACA,cACA,YACA,UACA,cAEA,QACA,MACA,OAEA,MACA,MACA,WACA,YACA,WACA,iBACA,mBACA,WACA,SACA,cACA,eACA,QAEA,MACA,YACA,OAEA,WACA,OACA,WACA,MACA,MACA,iBACF,EAiBA,SAASC,EACPC,EACAC,EACAC,EACa,CACb,IAAMC,EAAK,SAAS,cAAcH,CAAG,EAErC,GAAIC,EACF,OAAW,CAACG,EAAGC,CAAC,IAAK,OAAO,QAAQJ,CAAK,EACnCI,GAAK,MAAMF,EAAG,aAAaC,EAAGC,CAAC,EAIvC,GAAIH,EACF,QAAWI,KAASJ,EAClBC,EAAG,OAAOG,CAAK,EAInB,OAAOH,CACT,CAGA,SAASI,EAAKC,EAAuB,CACnC,OAAO,SAAS,eAAeA,CAAO,CACxC,CAGA,SAASpB,EAAUqB,EAAgC,CACjD,IAAMC,EAAM,SAAS,cAAc,UAAU,EAC7C,OAAAA,EAAI,UAAYD,EACTC,EAAI,OACb,CAMA,SAASC,EAAUN,EAA0B,CAC3C,OAAO,OAAOA,GAAM,UAAYA,IAAM,MAAQ,SAAUA,GAAK,eAAgBA,CAC/E,CAEA,SAASO,EAASP,EAAyB,CACzC,OAAO,OAAOA,GAAM,UAAYA,IAAM,MAAQ,SAAUA,CAC1D,CAEA,SAASQ,EAAYR,EAA4B,CAC/C,OAAO,OAAOA,GAAM,UAAYA,IAAM,MAAQ,UAAWA,IAAM,QAASA,GAAK,WAAYA,EAC3F,CAEA,SAASS,EAAMC,EAAoB,CACjC,OAAOA,EAAE,WAAW,SAAS,GAAKA,EAAE,WAAW,UAAU,GAAKA,EAAE,WAAW,GAAG,CAChF,CAMA,SAASC,EAAUC,EAAmB,CACpC,IAAMC,EAAIpB,EAAW,QAAQmB,CAAC,EAC9B,OAAOC,GAAK,EAAIA,EAAIpB,EAAW,MACjC,CAEA,SAASqB,EAAUC,EAAkD,CACnE,OAAO,OAAO,KAAKA,CAAK,EAAE,KAAK,CAACC,EAAGC,IAAMN,EAAUK,CAAC,EAAIL,EAAUM,CAAC,CAAC,CACtE,CAEA,SAASC,KAAWC,EAAmD,CAErE,OADYA,EAAM,OAAO,OAAO,EAAE,KAAK,GAAG,GAC5B,MAChB,CAEA,SAASC,EAAQC,EAAaC,EAAsD,CAClF,IAAMC,EAAOD,IAAUD,CAAG,GAAG,KAC7B,OAAOE,GAAM,OAASA,EAAK,KAAK,GAAG,EAAI,MACzC,CAEA,SAASC,GAAOC,EAAyB,CACvC,QAAWC,KAAKD,EACd,GAAIC,KAAKvC,EAAgB,OAAOA,EAAeuC,CAAC,EAElD,MAAO,KACT,CAMA,SAASC,GAAYC,EAAcP,EAAaQ,EAA+B,CAE7E,OAAOnC,EAAE,MAAO,CAAE,MAAOmC,EAAK,IAAKR,CAAI,CAAC,CAC1C,CAEA,SAASS,GAAYF,EAAcP,EAAaQ,EAA+B,CAC7E,OAAOnC,EAAE,QAAS,CAAE,MAAOmC,EAAK,IAAKR,CAAI,CAAC,CAC5C,CAEA,SAASU,GAAYH,EAAcP,EAAaQ,EAA+B,CAC7E,OAAOnC,EAAE,QAAS,CAAE,MAAOmC,EAAK,IAAKR,CAAI,CAAC,CAC5C,CAEA,SAASW,GACPX,EACAQ,EACAI,EACAC,EACmB,CACnB,OAAOxC,EAAE,IAAK,CAAE,MAAOmC,EAAK,KAAMR,EAAK,IAAAa,CAAI,EAAG,CAACD,GAAeZ,CAAG,CAAC,CACpE,CAEA,SAASc,GAAYC,EAAeP,EAAgC,CAClE,IAAMQ,EAAOD,EAAM,WAAW,SAAS,EAAIA,EAAQ,UAAUA,CAAK,GAC5DE,EAAUF,EAAM,QAAQ,WAAY,EAAE,EAC5C,OAAO1C,EAAE,IAAK,CAAE,MAAOmC,EAAK,KAAAQ,CAAK,EAAG,CAACC,CAAO,CAAC,CAC/C,CAEA,SAASC,GAAUH,EAAeP,EAAgC,CAChE,IAAMQ,EAAOD,EAAM,WAAW,MAAM,EAAIA,EAAQ,OAAOA,CAAK,GAC5D,OAAO1C,EAAE,IAAK,CAAE,MAAOmC,EAAK,KAAAQ,CAAK,EAAG,CAACD,CAAK,CAAC,CAC7C,CAEA,SAASI,GAAWJ,EAAeP,EAA8B,CAC/D,OAAOnC,EAAE,OAAQ,CAAE,MAAOmC,EAAK,SAAUO,CAAM,EAAG,CAACA,CAAK,CAAC,CAC3D,CAEA,SAASK,GAAWb,EAAcQ,EAAeP,EAA0B,CACzE,IAAMlC,EAAMP,EAAe,KAAKwC,CAAI,EAAE,GAAK,OAC3C,OAAOlC,EAAEC,EAAK,CAAE,MAAOkC,CAAI,EAAG,CAACO,CAAK,CAAC,CACvC,CAEA,SAASM,GAAgBd,EAAce,EAAkC,CACvE,IAAM/C,EAA4C,CAChD,MAAO,KAAKgC,CAAI,GAChB,IAAKe,EAAI,MACT,IAAKA,EAAI,GACX,EAEA,OAAIA,EAAI,QAAU,OAAO,KAAKA,EAAI,MAAM,EAAE,SACxC/C,EAAM,OAAS,OAAO,QAAQ+C,EAAI,MAAM,EACrC,KAAK,CAAC,CAAC3B,CAAC,EAAG,CAACC,CAAC,IAAMD,EAAE,cAAcC,CAAC,CAAC,EACrC,IAAI,CAAC,CAAClB,EAAGC,CAAC,IAAM,GAAGA,CAAC,IAAID,CAAC,EAAE,EAC3B,KAAK,IAAI,GAGPL,EAAE,MAAOE,CAAK,CACvB,CAEA,SAASgD,GAAgBhB,EAAcQ,EAA+B,CACpE,IAAMS,EAAMnD,EAAE,MAAO,CAAE,MAAO,KAAKkC,CAAI,EAAG,CAAC,EAC3C,OAAIQ,EAAM,KACRS,EAAI,YAAY9D,EAAUqD,EAAM,IAAI,CAAC,EAC5BA,EAAM,QACfS,EAAI,YAAcT,EAAM,OAEnBS,CACT,CAEA,SAASC,GAAWC,EAAcC,EAA0B,CAC1D,IAAMC,EAAOvD,EAAE,OAAQ,CAAE,cAAe,MAAO,CAAC,EAEhDuD,EAAK,YAAYvD,EAAE,SAAU,CAAE,MAAO,QAAS,EAAG,CAACqD,CAAI,CAAC,CAAC,EACzDE,EAAK,YAAYvD,EAAE,KAAM,OAAW,CAAC,GAAG,CAAC,CAAC,EAE1C,IAAMwD,EAAKxD,EAAE,IAAI,EACjB,OAAAwD,EAAG,OAAOhD,EAAK,IAAI,EAAGR,EAAE,OAAQ,CAAE,MAAO,OAAQ,EAAG,CAACsD,CAAG,CAAC,EAAG9C,EAAK,IAAI,CAAC,EACtE+C,EAAK,YAAYC,CAAE,EAEnBD,EAAK,YAAYvD,EAAE,KAAM,OAAW,CAAC,GAAG,CAAC,CAAC,EAEnCuD,CACT,CAGA,SAASE,EACPpC,EACAqB,EACAd,EACA8B,EACa,CACb,IAAMxB,EAAOb,EAAM,CAAC,EAGpB,GAAIa,IAAS,SAAWA,IAAS,OAC/B,OAAOD,GAAYC,EAAMQ,EAAOlB,EAAQ,GAAGH,EAAM,IAAIH,GAAK,KAAKA,CAAC,EAAE,CAAC,CAAE,EAEvE,GAAIgB,IAAS,QACX,OAAOE,GAAYF,EAAMQ,EAAOlB,EAAQ,GAAGH,EAAM,IAAIH,GAAK,KAAKA,CAAC,EAAE,CAAC,CAAE,EAEvE,GAAIgB,IAAS,QACX,OAAOG,GAAYH,EAAMQ,EAAOlB,EAAQ,GAAGH,EAAM,IAAIH,GAAK,KAAKA,CAAC,EAAE,CAAC,CAAE,EAIvE,GAAIvB,EAAU,IAAIuC,CAAI,GAAKnB,EAAM2B,CAAK,EACpC,OAAOJ,GACLI,EACAlB,EAAQ,GAAGH,EAAM,IAAIH,GAAK,KAAKA,CAAC,EAAE,CAAC,EACnCwB,EACAhB,EAAQgB,EAAOd,CAAO,CACxB,EAIF,GAAIhC,EAAY,IAAIsC,CAAI,EACtB,OAAOO,GAAYC,EAAOlB,EAAQ,GAAGH,EAAM,IAAIH,GAAK,KAAKA,CAAC,EAAE,CAAC,CAAE,EAIjE,GAAIrB,EAAU,IAAIqC,CAAI,EACpB,OAAOW,GAAUH,EAAOlB,EAAQ,GAAGH,EAAM,IAAIH,GAAK,KAAKA,CAAC,EAAE,CAAC,CAAE,EAI/D,GAAIpB,EAAS,IAAIoC,CAAI,EACnB,OAAOY,GAAWJ,EAAOlB,EAAQ,GAAGH,EAAM,IAAIH,GAAK,MAAMA,CAAC,EAAE,CAAC,CAAE,EAIjE,IAAMiB,EAAMX,EAAQ,GAAGH,EAAM,IAAIH,GAAK,KAAKA,CAAC,EAAE,CAAC,EAC/C,OAAIgB,IAAS,QAAUwB,GAAgB,KAC9B1D,EAAE,IAAI0D,CAAY,GAAmC,CAAE,MAAOvB,CAAI,EAAG,CAACO,CAAK,CAAC,EAE9EK,GAAWb,EAAMQ,EAAOP,CAAG,CACpC,CAcA,SAASwB,GAAeC,EAAuB,CAC7C,OAAI,OAAOA,EAAK,MAAS,SAAiB,IACtC,OAAOA,EAAK,OAAU,UAAYA,EAAK,QAAU,KAAa,IAC3D,GACT,CAEA,SAASC,EAAWD,EAAeE,EAAoB,CAAC,EAAgB,CACtE,GAAM,CAAE,aAAAC,EAAe,CAAC,EAAG,WAAAC,EAAa,GAAO,eAAAC,EAAgB,QAAArC,EAAS,aAAA8B,CAAa,EAAII,EACnFzC,EAAQuC,EAAK,WACbzD,EAAWyD,EAAK,UAAY,CAAC,EAG7BxD,EAAKJ,EAAE8B,GAAO8B,EAAK,IAAI,EAAG,CAC9B,GAAIA,EAAK,GACT,MAAOpC,EAAQ,GAAGuC,EAAc,GAAGH,EAAK,IAAI,CAC9C,CAAC,EAUD,GAPII,IAAeC,IAAmB,KAAOA,IAAmB,OAC1D,UAAWL,GAAQ,OAAOA,EAAK,OAAU,UAC3CxD,EAAG,YAAYJ,EAAE,OAAQ,CAAE,MAAO,QAAS,MAAO,OAAO4D,EAAK,KAAK,CAAE,CAAC,CAAC,EAKvEI,GAAcC,IAAmB,KAAO,OAAOL,EAAK,MAAS,SAC/D,OAAAxD,EAAG,YAAYf,EAAUuE,EAAK,IAAI,CAAC,EAC5BxD,EAIT,IAAM8D,EAAgBF,EAAaJ,EAAK,MAAQ,OAG1CO,EAAW,IAAI,IACjBZ,EAAgC,KAE9Ba,EAAQ/C,EAAM,MAAQ,CAAC,EACvBgD,EAAOhD,EAAM,KAAO,CAAC,EAC3B,GAAI+C,EAAM,QAAUC,EAAK,OAAQ,CAC/B,IAAMhB,EAAO,OAAOe,EAAM,CAAC,GAAM,SAAWA,EAAM,CAAC,EAAI,KACjDd,EAAM,OAAOe,EAAK,CAAC,GAAM,SAAWA,EAAK,CAAC,EAAI,KAChDhB,GAAQC,IACVC,EAAO,CAACF,EAAMC,CAAG,EACjBa,EAAS,IAAI,MAAM,EACnBA,EAAS,IAAI,KAAK,EAEtB,CAKA,IAAIG,EAAgD,KACpD,GAAI,CAACf,GAAQ,CAACS,EAAY,CACxB,IAAMO,EAAOlD,EAAM,KAAO,CAAC,EAC3B,GAAI+C,EAAM,SAAW,GAAKG,EAAK,SAAW,EAAG,CAC3C,IAAMC,EAAU,OAAOJ,EAAM,CAAC,GAAM,SAAWA,EAAM,CAAC,EAAI,KACpDK,EAAS,OAAOF,EAAK,CAAC,GAAM,SAAWA,EAAK,CAAC,EAAI,KACvD,GAAIC,GAAWC,GAAU1D,EAAM0D,CAAM,EAAG,CAEtC,IAAMC,EAAW,CAAC,KAAK,EACjBC,EAAOtD,EAAM,KAAO,CAAC,EACvBsD,EAAK,SAAW,GAAKA,EAAK,CAAC,IAAMF,IACnCC,EAAS,KAAK,KAAK,EACnBP,EAAS,IAAI,KAAK,GAEpBA,EAAS,IAAI,MAAM,EACnBA,EAAS,IAAI,KAAK,EAClBG,EAAa,CAACE,EAASC,EAAQC,CAAQ,CACzC,CACF,CACF,CAGA,IAAME,EAAW,IAAI,IAErB,QAAW1C,KAAQd,EAAUC,CAAK,EAAG,CAEnC,GAAIa,IAAS,QAAUqB,EAAM,CAC3B,IAAMsB,EAASzB,GAAW,GAAGG,CAAI,EACjC,GAAIG,GAAgB,KAAM,CACxB,IAAMoB,EAAU9E,EAAE,IAAI0D,CAAY,EAAiC,EACnEoB,EAAQ,YAAYD,CAAM,EAC1BzE,EAAG,YAAY0E,CAAO,CACxB,MACE1E,EAAG,YAAYyE,CAAM,EAEvBtB,EAAO,IACT,CAGA,GAAIrB,IAAS,QAAUoC,EAAY,CACjC,GAAM,CAACE,EAASC,EAAQC,CAAQ,EAAIJ,EAC9BnC,EAAMX,EAAQ,SAAU,GAAGkD,EAAS,IAAIxD,GAAK,KAAKA,CAAC,EAAE,CAAC,EACtDsB,EAAMd,EAAQ+C,EAAQ7C,CAAO,EAC7BmD,EAAS/E,EAAE,IAAK,CAAE,MAAOmC,EAAK,KAAMsC,EAAQ,IAAAjC,CAAI,EAAG,CAACgC,CAAO,CAAC,EAClE,GAAId,GAAgB,KAAM,CACxB,IAAMoB,EAAU9E,EAAE,IAAI0D,CAAY,EAAiC,EACnEoB,EAAQ,YAAYC,CAAM,EAC1B3E,EAAG,YAAY0E,CAAO,CACxB,MACE1E,EAAG,YAAY2E,CAAM,EAEvBT,EAAa,IACf,CAEA,GAAIH,EAAS,IAAIjC,CAAI,EAAG,SAGxB,IAAM8C,EAAetB,GAAgB,KAAO,KAAK,IAAIA,EAAe,EAAG,CAAC,EAAI,OAE5E,QAAWpD,KAAKe,EAAMa,CAAI,EAAG,CAE3B,GAAItB,EAAUN,CAAC,EAAG,CAChB,IAAM2E,EAAStB,GAAerD,CAAC,EAC/BF,EAAG,YAAYyD,EAAWvD,EAAG,CAC3B,aAAc,CAAC,GAAG2E,CAAM,IAAI/C,CAAI,EAAE,EAClC,WAAY,GACZ,eAAgB+C,EAChB,QAAArD,EACA,aAAcoD,CAChB,CAAC,CAAC,EACF,QACF,CAGA,GAAInE,EAASP,CAAC,EAAG,CACfF,EAAG,YAAY8C,GAAgBhB,EAAM5B,CAAC,CAAC,EACvC,QACF,CAGA,GAAIQ,EAAYR,CAAC,EAAG,CAClBF,EAAG,YAAY4C,GAAgBd,EAAM5B,CAAC,CAAC,EACvC,QACF,CAIA,GACE0D,GACAC,IAAmB,KACnB/B,IAAS,QACT,OAAOgC,GAAkB,UACzB,OAAO5D,GAAM,UACbA,IAAM4D,GACN,CAAC5D,EAAE,WAAW,SAAS,GACvB,CAACA,EAAE,WAAW,UAAU,EACxB,CAEAF,EAAG,YAAYJ,EAAE,OAAQ,CAAE,MAAO,SAAU,EAAG,CAACM,CAAC,CAAC,CAAC,EACnD,QACF,CAGA,GAAI,OAAOA,GAAM,SAAU,CACzB,IAAM4E,EAAMvF,EAAU,IAAIuC,CAAI,GAAKnB,EAAMT,CAAC,EAAI,MAC1CV,EAAY,IAAIsC,CAAI,EAAI,QACxBrC,EAAU,IAAIqC,CAAI,EAAI,MACtBpC,EAAS,IAAIoC,CAAI,EAAI,WACrB,QAAQA,CAAI,GAEViD,EAAM,GAAG7E,CAAC,KAAK4E,CAAG,GACxB,GAAIN,EAAS,IAAIO,CAAG,EAAG,SACvBP,EAAS,IAAIO,CAAG,EAGhB,IAAMC,EAAkB,CAAC,EACzB,QAAWlE,KAAKE,EAAUC,CAAK,EAC7B,GAAI,CAAA8C,EAAS,IAAIjD,CAAC,EAClB,QAAWmE,KAAMhE,EAAMH,CAAC,EAAG,CACzB,GAAI,OAAOmE,GAAO,UAAYA,IAAO/E,EAAG,UAC3BX,EAAU,IAAIuB,CAAC,GAAKH,EAAMsE,CAAE,EAAI,MACzCzF,EAAY,IAAIsB,CAAC,EAAI,QACrBrB,EAAU,IAAIqB,CAAC,EAAI,MACnBpB,EAAS,IAAIoB,CAAC,EAAI,WAClB,QAAQA,CAAC,MACAgE,GAAO,CAACE,EAAM,SAASlE,CAAC,GACnCkE,EAAM,KAAKlE,CAAC,CAEhB,CAGFd,EAAG,YAAYqD,EAAiB2B,EAAO9E,EAAGsB,EAAS8B,CAAY,CAAC,EAChE,QACF,CAGAtD,EAAG,YAAYqD,EAAiB,CAACvB,CAAI,EAAG,OAAO5B,CAAC,EAAGsB,EAAS8B,CAAY,CAAC,CAC3E,CACF,CAGA,IAAM4B,EAAkB5B,GAAgB,KAAO,KAAK,IAAIA,EAAe,EAAG,CAAC,EAAI,OAC/E,QAAWnD,KAASJ,EAClBC,EAAG,YAAYyD,EAAWtD,EAAO,CAAE,QAAAqB,EAAS,aAAc0D,CAAgB,CAAC,CAAC,EAG9E,OAAOlF,CACT,CAoBO,SAASd,GAAOiG,EAAkBzB,EAAsB,CAAC,EAAyB,CACvF,IAAMlC,EAAU2D,EAAI,UAAU,EACxB,CAAE,WAAAC,CAAW,EAAI1B,EACjB2B,EAAOzF,EAAE,MAAM,EAGrB,QAAW4D,KAAQ2B,EAAI,MACrBE,EAAK,YAAY5B,EAAWD,EAAM,CAAE,QAAAhC,EAAS,aAAc4D,CAAW,CAAC,CAAC,EAI1E,GAAI5D,GAAW,OAAO,KAAKA,CAAO,EAAE,OAAQ,CAC1C,IAAM8D,EAAM1F,EAAE,KAAK,EACnB,QAAW2B,KAAO,OAAO,KAAKC,CAAO,EAAE,KAAK,EAAG,CAC7C,IAAM+D,EAAO/D,EAAQD,CAAG,EACxB+D,EAAI,YAAY1F,EAAE,IAAK,CACrB,KAAM2B,EACN,IAAKgE,EAAK,MAAM,OAASA,EAAK,KAAK,KAAK,GAAG,EAAI,MACjD,EAAG,CAACA,EAAK,MAAQhE,CAAG,CAAC,CAAC,CACxB,CACA8D,EAAK,YAAYC,CAAG,CACtB,CAEA,OAAO5B,EAAK,UAAY2B,EAAOA,EAAK,SACtC,CAUO,SAASlG,GACdqE,EACAgC,EAAoC,CAAC,EACxB,CACb,GAAM,CAAE,QAAAhE,EAAS,WAAA4D,CAAW,EAAII,EAChC,OAAO/B,EAAWD,EAAM,CAAE,QAAAhC,EAAS,aAAc4D,CAAW,CAAC,CAC/D,CAUO,SAAShG,GACdqG,EACAD,EAA8B,CAAC,EACb,CAClB,GAAM,CAAE,QAAAhE,EAAS,WAAA4D,CAAW,EAAII,EAC1BE,EAAW,SAAS,uBAAuB,EACjD,QAAWlC,KAAQiC,EACjBC,EAAS,YAAYjC,EAAWD,EAAM,CAAE,QAAAhC,EAAS,aAAc4D,CAAW,CAAC,CAAC,EAE9E,OAAOM,CACT",
|
|
6
|
+
"names": ["renderer_exports", "__export", "parseHtml", "render", "renderItemElement", "renderItems", "SEMANTIC_ROOTS", "SEMANTIC_PROPS", "URL_PROPS", "EMAIL_PROPS", "TEL_PROPS", "DT_PROPS", "PROP_ORDER", "h", "tag", "attrs", "children", "el", "k", "v", "child", "text", "content", "html", "tpl", "isMf2Item", "isEValue", "isUrlObject", "isUrl", "s", "propOrder", "p", "i", "sortProps", "props", "a", "b", "classes", "parts", "getRels", "url", "relUrls", "rels", "getTag", "types", "t", "renderPhoto", "prop", "cls", "renderVideo", "renderAudio", "renderLink", "displayText", "rel", "renderEmail", "value", "href", "display", "renderTel", "renderTime", "renderText", "renderUrlObject", "obj", "renderEProperty", "div", "renderRuby", "name", "ipa", "ruby", "rt", "renderStringProp", "headingLevel", "embeddedPrefix", "item", "renderItem", "opts", "extraClasses", "asProperty", "propertyPrefix", "embeddedValue", "consumed", "names", "ipas", "linkedName", "urls", "nameVal", "urlVal", "urlProps", "uids", "rendered", "rubyEl", "heading", "linkEl", "childHeading", "prefix", "cat", "key", "group", "pv", "childrenHeading", "doc", "topHeading", "main", "nav", "info", "options", "items", "fragment"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Microformats2 Renderer - Pure Programmatic DOM Building
|
|
3
|
+
*
|
|
4
|
+
* Renders mf2 JSON to semantic HTML by building the DOM tree programmatically.
|
|
5
|
+
* Uses DocumentFragment for efficient batch updates with minimal reflows.
|
|
6
|
+
*/
|
|
7
|
+
import type { Mf2Document, Mf2Item, RelUrl } from "./types.js";
|
|
8
|
+
/** Parse HTML string to DocumentFragment (only for e-* content) */
|
|
9
|
+
declare function parseHtml(html: string): DocumentFragment;
|
|
10
|
+
export interface RenderOptions {
|
|
11
|
+
/** Return HTMLElement instead of string */
|
|
12
|
+
asElement?: boolean;
|
|
13
|
+
/** If set, render name properties as heading elements starting at this level (1-6).
|
|
14
|
+
* Names in nested items use incrementing levels (capped at h6).
|
|
15
|
+
* Default is undefined (render as <strong>). */
|
|
16
|
+
topHeading?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Render an mf2 document to HTML.
|
|
20
|
+
*/
|
|
21
|
+
export declare function render(doc: Mf2Document, opts?: {
|
|
22
|
+
asElement: true;
|
|
23
|
+
topHeading?: number;
|
|
24
|
+
}): HTMLElement;
|
|
25
|
+
export declare function render(doc: Mf2Document, opts?: {
|
|
26
|
+
asElement?: false;
|
|
27
|
+
topHeading?: number;
|
|
28
|
+
}): string;
|
|
29
|
+
export interface RenderItemElementOptions {
|
|
30
|
+
relUrls?: Record<string, RelUrl>;
|
|
31
|
+
topHeading?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Render a single mf2 item to an HTMLElement.
|
|
35
|
+
*/
|
|
36
|
+
export declare function renderItemElement(item: Mf2Item, options?: RenderItemElementOptions): HTMLElement;
|
|
37
|
+
export interface RenderItemsOptions {
|
|
38
|
+
relUrls?: Record<string, RelUrl>;
|
|
39
|
+
topHeading?: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Render multiple items efficiently using DocumentFragment.
|
|
43
|
+
*/
|
|
44
|
+
export declare function renderItems(items: Mf2Item[], options?: RenderItemsOptions): DocumentFragment;
|
|
45
|
+
export { parseHtml as parseHtmlFragment };
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript types for mf2json (Microformats2 JSON)
|
|
3
|
+
* Based on https://microformats.org/wiki/microformats2-json
|
|
4
|
+
*/
|
|
5
|
+
/** URL object with optional alt text and srcset */
|
|
6
|
+
export interface UrlObject {
|
|
7
|
+
value: string;
|
|
8
|
+
alt?: string;
|
|
9
|
+
srcset?: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
/** Embedded HTML content (e-* properties) */
|
|
12
|
+
export interface EValue {
|
|
13
|
+
value?: string;
|
|
14
|
+
html: string;
|
|
15
|
+
lang?: string;
|
|
16
|
+
}
|
|
17
|
+
/** Rel URL information */
|
|
18
|
+
export interface RelUrl {
|
|
19
|
+
rels: string[];
|
|
20
|
+
text?: string;
|
|
21
|
+
media?: string;
|
|
22
|
+
hreflang?: string;
|
|
23
|
+
type?: string;
|
|
24
|
+
title?: string;
|
|
25
|
+
}
|
|
26
|
+
/** Property value types */
|
|
27
|
+
export type PropertyValue = string | UrlObject | EValue | Mf2Item;
|
|
28
|
+
/** Microformat item */
|
|
29
|
+
export interface Mf2Item {
|
|
30
|
+
type: string[];
|
|
31
|
+
properties: Record<string, PropertyValue[]>;
|
|
32
|
+
id?: string;
|
|
33
|
+
children?: Mf2Item[];
|
|
34
|
+
value?: PropertyValue;
|
|
35
|
+
html?: string;
|
|
36
|
+
lang?: string;
|
|
37
|
+
}
|
|
38
|
+
/** Top-level mf2 document */
|
|
39
|
+
export interface Mf2Document {
|
|
40
|
+
items: Mf2Item[];
|
|
41
|
+
rels: Record<string, string[]>;
|
|
42
|
+
"rel-urls": Record<string, RelUrl>;
|
|
43
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mf2dom",
|
|
3
|
+
"version": "0.1.11",
|
|
4
|
+
"description": "Render Microformats2 JSON to semantic HTML",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/mf2dom.cjs",
|
|
7
|
+
"module": "dist/mf2dom.esm.js",
|
|
8
|
+
"types": "dist/renderer.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/renderer.d.ts",
|
|
12
|
+
"import": "./dist/mf2dom.esm.js",
|
|
13
|
+
"require": "./dist/mf2dom.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "npm run build:esm && npm run build:cjs && npm run build:iife && npm run build:types",
|
|
21
|
+
"build:esm": "esbuild renderer.ts --bundle --format=esm --minify --sourcemap --outfile=dist/mf2dom.esm.js",
|
|
22
|
+
"build:cjs": "esbuild renderer.ts --bundle --format=cjs --minify --sourcemap --outfile=dist/mf2dom.cjs",
|
|
23
|
+
"build:iife": "esbuild renderer.ts --bundle --format=iife --minify --sourcemap --global-name=mf2dom --outfile=dist/mf2dom.min.js",
|
|
24
|
+
"build:types": "tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
25
|
+
"dev": "esbuild renderer.ts --bundle --format=esm --sourcemap --watch --outfile=dist/mf2dom.esm.js",
|
|
26
|
+
"prepublishOnly": "npm run build"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"microformats",
|
|
30
|
+
"microformats2",
|
|
31
|
+
"mf2",
|
|
32
|
+
"indieweb",
|
|
33
|
+
"html",
|
|
34
|
+
"renderer",
|
|
35
|
+
"h-entry",
|
|
36
|
+
"h-card",
|
|
37
|
+
"h-feed"
|
|
38
|
+
],
|
|
39
|
+
"author": "Beto Dealmeida <contact@robida.net>",
|
|
40
|
+
"license": "AGPL-3.0-or-later",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/betodealmeida/mf2dom.git"
|
|
44
|
+
},
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/betodealmeida/mf2dom/issues"
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://github.com/betodealmeida/mf2dom#readme",
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/jsdom": "^21.0.0",
|
|
51
|
+
"@types/node": "^20.0.0",
|
|
52
|
+
"esbuild": "^0.27.2",
|
|
53
|
+
"jsdom": "^24.0.0",
|
|
54
|
+
"ts-node": "^10.9.0",
|
|
55
|
+
"tsx": "^4.21.0",
|
|
56
|
+
"typescript": "^5.0.0"
|
|
57
|
+
}
|
|
58
|
+
}
|