@magicpages/pagemoji 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mount.d.ts +2 -1
- package/dist/mount.js +18 -6
- package/dist/pagemoji.js +2 -2
- package/package.json +1 -1
package/dist/mount.d.ts
CHANGED
|
@@ -7,7 +7,8 @@ export interface MountPoint {
|
|
|
7
7
|
* `.gh-content` is the post body in Ghost's official themes, even on a members-only post that
|
|
8
8
|
* shows only a signup prompt. Elsewhere it's the element with the most paragraphs as direct
|
|
9
9
|
* children, which holds for Ghost's `{{content}}` and WordPress's `the_content()` whatever the
|
|
10
|
-
* theme calls the wrapper.
|
|
10
|
+
* theme calls the wrapper. A short post ties with banners and calls to action that hold one
|
|
11
|
+
* paragraph too, so a tie goes to the element in an `<article>`, then in `<main>`.
|
|
11
12
|
*/
|
|
12
13
|
export declare function findPostBody(root: Document): Element | null;
|
|
13
14
|
/** `auto`, or `before:`, `after:`, `prepend:` or `append:` and a CSS selector. */
|
package/dist/mount.js
CHANGED
|
@@ -4,7 +4,8 @@ const POST_PAGE = 'body.post-template, body.single-post';
|
|
|
4
4
|
* `.gh-content` is the post body in Ghost's official themes, even on a members-only post that
|
|
5
5
|
* shows only a signup prompt. Elsewhere it's the element with the most paragraphs as direct
|
|
6
6
|
* children, which holds for Ghost's `{{content}}` and WordPress's `the_content()` whatever the
|
|
7
|
-
* theme calls the wrapper.
|
|
7
|
+
* theme calls the wrapper. A short post ties with banners and calls to action that hold one
|
|
8
|
+
* paragraph too, so a tie goes to the element in an `<article>`, then in `<main>`.
|
|
8
9
|
*/
|
|
9
10
|
export function findPostBody(root) {
|
|
10
11
|
if (!root.querySelector(POST_PAGE))
|
|
@@ -13,21 +14,32 @@ export function findPostBody(root) {
|
|
|
13
14
|
if (ghost)
|
|
14
15
|
return ghost;
|
|
15
16
|
const counts = new Map();
|
|
16
|
-
let best = null;
|
|
17
|
-
let bestCount = 0;
|
|
18
17
|
for (const paragraph of root.querySelectorAll('body p')) {
|
|
19
18
|
const parent = paragraph.parentElement;
|
|
20
19
|
if (!parent || parent.closest('header, footer, nav, aside, form'))
|
|
21
20
|
continue;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
counts.set(parent, (counts.get(parent) ?? 0) + 1);
|
|
22
|
+
}
|
|
23
|
+
let best = null;
|
|
24
|
+
let bestCount = 0;
|
|
25
|
+
let bestLandmark = -1;
|
|
26
|
+
for (const [parent, count] of counts) {
|
|
27
|
+
if (count < bestCount)
|
|
28
|
+
continue;
|
|
29
|
+
const rank = landmark(parent);
|
|
30
|
+
if (count > bestCount || rank > bestLandmark) {
|
|
25
31
|
best = parent;
|
|
26
32
|
bestCount = count;
|
|
33
|
+
bestLandmark = rank;
|
|
27
34
|
}
|
|
28
35
|
}
|
|
29
36
|
return best;
|
|
30
37
|
}
|
|
38
|
+
function landmark(element) {
|
|
39
|
+
if (element.closest('article'))
|
|
40
|
+
return 2;
|
|
41
|
+
return element.closest('main') ? 1 : 0;
|
|
42
|
+
}
|
|
31
43
|
/** `auto`, or `before:`, `after:`, `prepend:` or `append:` and a CSS selector. */
|
|
32
44
|
export function resolveMount(spec, root) {
|
|
33
45
|
if (spec === 'auto') {
|
package/dist/pagemoji.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function(){"use strict";const w=/^[A-Za-z0-9._:-]{1,200}$/;function S(a){return w.test(a)}function m(a){const t=a.toLowerCase().replace(/\.$/,"");return t.startsWith("www.")?t.slice(4):t}function
|
|
1
|
+
(function(){"use strict";const w=/^[A-Za-z0-9._:-]{1,200}$/;function S(a){return w.test(a)}function m(a){const t=a.toLowerCase().replace(/\.$/,"");return t.startsWith("www.")?t.slice(4):t}function k(a,t){const e=m(a.hostname);return t?{host:e,key:`id:${t}`}:{host:e,key:a.pathname.replace(/\/+$/,"")||"/"}}const A=["❤️","👏","💡"],g={credentials:"omit",referrerPolicy:"strict-origin"},x=`
|
|
2
2
|
:host {
|
|
3
3
|
--_accent: var(--pagemoji-accent, var(--ghost-accent-color, currentColor));
|
|
4
4
|
display: block;
|
|
@@ -48,4 +48,4 @@ button:focus-visible { outline: 2px solid var(--_accent); outline-offset: 2px; }
|
|
|
48
48
|
.g { transition: transform 0.15s ease-out; }
|
|
49
49
|
button:hover .g { transform: scale(1.15); }
|
|
50
50
|
}
|
|
51
|
-
`;let b;function y(a){try{return new URL(a,document.baseURI)}catch{return null}}const v=6;class u extends HTMLElement{static#i=[];#p=this.attachShadow({mode:"open"});#n="";#e=null;#o=null;#u="";#s=[];#r=new Map;#t={};#a=new Set;#c=new Set;connectedCallback(){if(this.#e)return;const t=y(this.getAttribute("api")??b??"");if(!t)return;this.#n=t.href.endsWith("/")?t.href:`${t.href}/`;const e=new URL(location.href);let s=e;const
|
|
51
|
+
`;let b;function y(a){try{return new URL(a,document.baseURI)}catch{return null}}const v=6;class u extends HTMLElement{static#i=[];#p=this.attachShadow({mode:"open"});#n="";#e=null;#o=null;#u="";#s=[];#r=new Map;#t={};#a=new Set;#c=new Set;connectedCallback(){if(this.#e)return;const t=y(this.getAttribute("api")??b??"");if(!t)return;this.#n=t.href.endsWith("/")?t.href:`${t.href}/`;const e=new URL(location.href);let s=e;const i=this.getAttribute("url")??document.querySelector('link[rel="canonical"]')?.href,o=i?y(i):null;o&&m(o.hostname)===m(e.hostname)&&(s=o);const n=this.getAttribute("page");this.#o=n&&S(n)?n:null,this.#e=k(s,this.#o),this.#u=e.origin+s.pathname;const r=new Set((this.getAttribute("emojis")??"").split(/\s+/).filter(Boolean));this.#s=r.size?[...r].slice(0,5):A,u.#i.push(this)===1&&queueMicrotask(()=>void u.#d())}static async#d(){const t=u.#i;u.#i=[];const e=new Map;for(const s of t){const i=[s.#n,s.#e?.host,...s.#s].join(" ");let o=e.get(i);o||e.set(i,o=[]),o.push(s)}await Promise.all([...e.values()].map(s=>u.#h(s)))}static async#h(t){const[e]=t,s=e?e.#e?.host:void 0;if(!e||!s)return;const i=[...new Set(t.map(o=>o.#e?.key??""))];for(let o=0;o<i.length;o+=100){const n=new URLSearchParams({host:s});for(const r of i.slice(o,o+100))n.append("key",r);for(const r of e.#s)n.append("emoji",r);try{const r=await fetch(new URL(`v1/counts?${n}`,e.#n),g);if(!r.ok)continue;const{counts:c}=await r.json();for(const l of t){const h=l.#e?.key??"";h in c&&l.#m(c[h]??{})}}catch{}}}#m(t){this.#t=t,this.#r.size===0&&this.#f(),this.#l()}#f(){const t=this.hasAttribute("readonly"),e=document.createElement("div");e.className="r",e.setAttribute("part","list");const s=this.getAttribute("label")?.trim();if(s){const o=document.createElement("span");o.className="l",o.setAttribute("part","label"),o.textContent=s,e.append(o)}for(const o of this.#s){const n=document.createElement(t?"span":"button"),r=document.createElement("span"),c=document.createElement("span"),l=document.createElement("span");r.className="e",c.className="g",c.setAttribute("part","emoji"),l.className="c",l.setAttribute("part","count"),c.textContent=o,r.append(c),n.append(r,l),n instanceof HTMLButtonElement?(n.type="button",n.addEventListener("click",()=>void this.#b(o))):(n.className="i",n.setAttribute("part","item")),this.#r.set(o,{element:n,burst:r,glyph:c,count:l}),e.append(n)}const i=document.createElement("style");i.textContent=x,this.#p.replaceChildren(i,e)}#l(){const t=this.hasAttribute("readonly"),e=new Intl.NumberFormat(void 0,{notation:"compact"});for(const[s,{element:i,count:o}]of this.#r){const n=this.#t[s]??0;if(o.textContent=n>0?e.format(n):"",t)i.hidden=n===0;else{const r=this.#a.has(s);i.setAttribute("aria-pressed",String(r)),i.setAttribute("part",r?"button pressed":"button")}}}#g(t,e){const s=this.#r.get(t);if(!s||!window.matchMedia?.("(prefers-reduced-motion: no-preference)").matches)return;const{burst:i,glyph:o,count:n}=s;for(const c of o.getAnimations())c.cancel();if(o.animate({transform:e?["scale(1)","scale(1.4)","scale(0.92)","scale(1)"]:["scale(1)","scale(0.8)","scale(1)"]},{duration:e?400:200,easing:"ease-out"}),n.textContent&&n.animate({transform:[`translateY(${e?.4:-.4}em)`,"none"],opacity:[0,1]},{duration:200,easing:"ease-out"}),!e)return;const r=Math.random();for(let c=0;c<v;c++){const l=(c+r)/v*2*Math.PI,h=1.3+Math.random()*.5,p=document.createElement("span");p.className="p",p.textContent=t,p.setAttribute("aria-hidden","true"),i.append(p),p.animate({transform:["scale(0.6)",`translate(${Math.cos(l)*h}em, ${Math.sin(l)*h}em) scale(0.3)`],opacity:[1,1,0]},{duration:450,easing:"cubic-bezier(0.2, 0.7, 0.3, 1)"}).onfinish=()=>p.remove()}}async#b(t){if(this.#c.has(t))return;this.#c.add(t);const e=!this.#a.has(t),s=this.#t[t]??0,i=n=>{n?this.#a.add(t):this.#a.delete(t)};i(e),this.#t={...this.#t,[t]:Math.max(0,s+(e?1:-1))},this.#l(),this.#g(t,e);const o={url:this.#u,emoji:t,action:e?"add":"remove",...this.#o?{page:this.#o}:{}};try{const n=await fetch(new URL("v1/reactions",this.#n),{...g,method:"POST",headers:{"content-type":"text/plain;charset=UTF-8"},body:JSON.stringify(o)});if(!n.ok)throw new Error(`HTTP ${n.status}`);const r=await n.json();this.#t=r.counts}catch{i(!e),this.#t={...this.#t,[t]:s}}finally{this.#c.delete(t),this.#l()}}}function M(a={}){a.api&&(b=a.api),customElements.get("pagemoji-reactions")||customElements.define("pagemoji-reactions",u)}const _="body.post-template, body.single-post";function T(a){if(!a.querySelector(_))return null;const t=a.querySelector(".gh-content");if(t)return t;const e=new Map;for(const n of a.querySelectorAll("body p")){const r=n.parentElement;!r||r.closest("header, footer, nav, aside, form")||e.set(r,(e.get(r)??0)+1)}let s=null,i=0,o=-1;for(const[n,r]of e){if(r<i)continue;const c=j(n);(r>i||c>o)&&(s=n,i=r,o=c)}return s}function j(a){return a.closest("article")?2:a.closest("main")?1:0}function L(a,t){if(a==="auto"){const n=T(t);return n?{target:n,where:"append"}:null}const e=a.match(/^(before|after|prepend|append):(.+)$/);if(!e)return null;const[,s="",i=""]=e,o=t.querySelector(i);return o&&C(s)?{target:o,where:s}:null}function C(a){return a==="before"||a==="after"||a==="prepend"||a==="append"}const f=document.currentScript,d=f instanceof HTMLScriptElement&&f.src?f:null;M(d?{api:new URL("../",d.src).href}:{});const E=d?.dataset.mount;if(d&&E){const a=()=>{const t=L(E,document);if(!t)return;const e=document.createElement("pagemoji-reactions");for(const s of["emojis","page","variant","label"]){const i=d.dataset[s];i&&e.setAttribute(s,i)}t.target[t.where](e)};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",a):a()}})();
|