domma-cms 1.8.45 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/admin/js/lib/code-format.js +50 -40
- package/admin/js/templates/docs/usage-users-roles.html +4 -0
- package/admin/js/views/page-editor.js +10 -8
- package/bin/lib/left-free-tier.js +83 -0
- package/bin/update.js +24 -0
- package/docs/DommaFeatures.md +47 -7
- package/docs/configuration.md +19 -7
- package/docs/plugin-development.md +7 -0
- package/package.json +1 -1
- package/plugins/_lib/games/names.js +6 -0
- package/plugins/_lib/public/games/motion.css +1 -0
- package/plugins/_lib/public/games/motion.js +28 -0
- package/plugins/free-tier.lock.json +1 -43
- package/server/routes/api/api-endpoints.js +5 -1
- package/server/routes/api/collections.js +4 -3
- package/server/routes/api/forms.js +22 -0
- package/server/routes/api/views.js +18 -0
- package/server/services/collectionAccess.js +97 -15
- package/server/services/permissionRegistry.js +39 -11
- package/plugins/crossword/CLAUDE.md +0 -37
- package/plugins/crossword/admin/css/index.css +0 -18
- package/plugins/crossword/admin/templates/crossword.html +0 -1
- package/plugins/crossword/admin/views/crossword.js +0 -449
- package/plugins/crossword/collections/crossword-plays.json +0 -20
- package/plugins/crossword/collections/crossword-puzzles.json +0 -17
- package/plugins/crossword/config.js +0 -6
- package/plugins/crossword/docs/guide.md +0 -53
- package/plugins/crossword/inject/body.html +0 -1
- package/plugins/crossword/lib/crossword.js +0 -170
- package/plugins/crossword/lib/store.js +0 -56
- package/plugins/crossword/plugin.js +0 -274
- package/plugins/crossword/plugin.json +0 -79
- package/plugins/crossword/public/crossword.css +0 -36
- package/plugins/crossword/public/crossword.js +0 -519
- package/plugins/crossword/public/lib/layout.js +0 -199
- package/plugins/quiz/CLAUDE.md +0 -43
- package/plugins/quiz/admin/css/index.css +0 -13
- package/plugins/quiz/admin/templates/quiz.html +0 -1
- package/plugins/quiz/admin/views/quiz.js +0 -530
- package/plugins/quiz/collections/quiz-plays.json +0 -20
- package/plugins/quiz/collections/quiz-quizzes.json +0 -17
- package/plugins/quiz/config.js +0 -6
- package/plugins/quiz/docs/guide.md +0 -61
- package/plugins/quiz/inject/body.html +0 -1
- package/plugins/quiz/lib/engine.js +0 -115
- package/plugins/quiz/lib/quiz.js +0 -225
- package/plugins/quiz/lib/store.js +0 -56
- package/plugins/quiz/plugin.js +0 -275
- package/plugins/quiz/plugin.json +0 -79
- package/plugins/quiz/public/quiz.css +0 -36
- package/plugins/quiz/public/quiz.js +0 -372
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.9.0] - 2026-10-23
|
|
4
|
+
|
|
5
|
+
- **Security:** a plugin's collections are no longer open through the generic Collections API to someone who holds only part of the plugin's permission. Before, `collections` plus any action of any of the plugin's permissions (a game's View, say) was enough to list every row - quiz answers, puzzle solutions, future daily words and players' play tokens. Now the caller needs the action the permission names as its `collectionAction` in `plugin.json` (the games name `manage`), or, when it names none, every action of that permission. The same check now guards the other ways in: a form named after or pointed at a plugin's collection, a View reading or joining one, and an API Builder endpoint exposing one. A switched-off plugin's collections keep the gate its `plugin.json` declares. Super Admin keeps everything, and a plugin with no permissions still needs only `collections`.
|
|
6
|
+
- **Two new Games, and more in every Game.** Word Search and Daily Word join Quiz and Crossword under Tools > Games, all four Pro Tools that can be gifted. Every Game now has hints for a time penalty, a personal best and a Share button on the finished screen, `board="show|hide|only"` on its shortcode, `[games-leaderboards]` for every Game's boards on one page, and animations (a setting, never for visitors who ask for less motion). The engine carries the Games' shared animations (`plugins/_lib/public/games/motion.css` and `motion.js`); Quiz 1.4.0, Crossword 1.4.0, Word Search 1.1.0 and Daily Word 1.2.0 need this release.
|
|
7
|
+
- **Quiz and Crossword are Pro Tools now, and can be gifted.** They no longer ship inside the engine: a site gets them with a licence, or as a gift from Domma for good or for a number of days. Updating removes the old free copy from a site that is not licensed for it; its data stays, and a licensed or gifted copy is never touched. Quiz and Crossword 1.3.0.
|
|
8
|
+
- **Export and import quizzes and crosswords.** Export one from its menu or its editor, or every one with **Export all**, as a file; **Import…** adds a file's quizzes or crosswords to another site (or the same one). They arrive as drafts with short names of their own, so nothing goes live or changes by surprise; anything that is not valid is skipped and named. Plays and leaderboards stay where they are. Crossword grids come out the same, laid out again from the words. Quiz and Crossword 1.2.0.
|
|
9
|
+
|
|
10
|
+
## [1.8.46] - 2026-10-23
|
|
11
|
+
|
|
12
|
+
- **Format lays HTML out properly.** Shift+Alt+F used to re-indent HTML line by line and nothing more, so `<div><h2>…</h2><p>…</p></div>` stayed on one line and a closer trailing a line threw the rest out. It now puts every block element (div, p, li, h2, section, custom elements…) on a line of its own, indented by nesting, with `{{#if}}` sections the same; text and inline tags (`<a>`, `<strong>`, `<span>`) keep the lines they were written on, so no space appears or goes on the page. Tags get one space between attributes, a tag wider than 120 columns an attribute a line. HTML blocks in the page body are laid out too (new setting *Lay out HTML in the page body*, on), and Enter after an opening block tag in the body goes one deeper. Checked across the fleet's pages (all render the same) and the CMS's own 150 templates.
|
|
13
|
+
|
|
3
14
|
## [1.8.45] - 2026-10-23
|
|
4
15
|
|
|
5
16
|
- Include a free library of 16 editable `domma-block-*` Custom Blocks with every site: introductions, image/text sections, features, services, testimonials, figures, steps, FAQs, contact details, pricing, team profiles, images, logos and calls to action. Each includes scoped responsive styling and examples. Startup adds missing blocks while preserving site customisations.
|
|
@@ -1,44 +1,54 @@
|
|
|
1
|
-
let
|
|
2
|
-
`).filter(
|
|
3
|
-
`)
|
|
4
|
-
|
|
1
|
+
let C=" ";export const DEFAULT_CODE_PREFS=Object.freeze({indent:"spaces",size:2,detect:!0,autoIndent:!0,continueLists:!0,shortTags:!0,tables:!0,html:!0,formatOnSave:!1}),INDENT_SIZES=Object.freeze([2,4,8]);export function cleanCodePrefs(t={}){const n=DEFAULT_CODE_PREFS,e=s=>typeof t?.[s]=="boolean"?t[s]:n[s];return{indent:t?.indent==="tabs"?"tabs":"spaces",size:INDENT_SIZES.includes(Number(t?.size))?Number(t.size):n.size,detect:e("detect"),autoIndent:e("autoIndent"),continueLists:e("continueLists"),shortTags:e("shortTags"),tables:e("tables"),html:e("html"),formatOnSave:e("formatOnSave")}}let E=cleanCodePrefs();export function setCodePrefs(t){E=cleanCodePrefs(t)}export const codePrefs=()=>E,prefUnit=()=>E.indent==="tabs"?" ":" ".repeat(E.size),unitFor=t=>E.detect?detectIndent(t,prefUnit()):prefUnit();export function detectIndent(t,n=" "){const e=String(t).split(`
|
|
2
|
+
`).filter(r=>r.trim()),s=e.filter(r=>r.startsWith(" ")).length,o=e.filter(r=>r.startsWith(" ")).length;if(s>o)return" ";const l=e.map(r=>r.match(/^ */)[0].length).filter(r=>r>0);return l.length?l.filter(r=>r%4===2).length>l.length/5?" ":n===" ".repeat(8)&&l.every(r=>r%8===0)?n:" ":n}export function isFormatKey(t){return!t||!t.altKey||!t.shiftKey||t.ctrlKey||t.metaKey?!1:t.code==="KeyF"||String(t.key||"").toLowerCase()==="f"}export function formatCode(t,n){const e=String(t??"").replace(/\r\n?/g,`
|
|
3
|
+
`),s=e.split(`
|
|
4
|
+
`).filter(o=>/^[ \t]+\S/.test(o)).length;C=n==="md"||n==="html"&&s<3?prefUnit():unitFor(e);try{switch(n){case"json":return{text:formatJson(e)};case"css":return{text:formatCss(e)};case"js":return{text:formatJs(e)};case"html":return{text:formatHtml(e)};case"md":return{text:formatMarkdown(e)};default:return{error:"Nothing to format here."}}}catch(o){return{error:o.message||"Could not format this."}}}export function formatJson(t){if(!t.trim())return t;try{JSON.parse(t)}catch(o){throw new Error(`Not valid JSON: ${o.message}`)}let n="",e=0;const s=()=>`
|
|
5
|
+
`+C.repeat(e);for(let o=0;o<t.length;o++){const l=t[o];if(l==='"'){let a=o+1;for(;t[a]!=='"';)a+=t[a]==="\\"?2:1;n+=t.slice(o,a+1),o=a}else if(l==="{"||l==="["){const a=l==="{"?"}":"]",r=t.slice(o+1).search(/\S/);if(t[o+1+r]===a){n+=l+a,o+=r+1;continue}e++,n+=l+s()}else l==="}"||l==="]"?(e--,n+=s()+l):l===","?n+=","+s():l===":"?n+=": ":/\s/.test(l)||(n+=l)}return n+(t.endsWith(`
|
|
5
6
|
`)?`
|
|
6
|
-
`:"")}export function formatCss(t){if(!t.trim())return t;if(/\{\{\{?\s*[#/^\w]/.test(t))throw new Error("This CSS has {{placeholders}} in it - left as it is.");const
|
|
7
|
-
`);
|
|
8
|
-
`;)
|
|
7
|
+
`:"")}export function formatCss(t){if(!t.trim())return t;if(/\{\{\{?\s*[#/^\w]/.test(t))throw new Error("This CSS has {{placeholders}} in it - left as it is.");const n=[];let e=0,s="",o=!1,l="",a=!1,r=0;const i=()=>C.repeat(e),f=u=>{o&&n.length&&n[n.length-1]!==""&&!n[n.length-1].endsWith("{")&&n.push(""),o=!1,n.push(u)},p=()=>{a&&e===0?(o=!0,a=!1):(l.match(/\n/g)||[]).length>=2&&(o=!0),l=""},d=u=>{const g=u.match(/^(--[\w-]+|[-\w]+)\s*:\s*([\s\S]*)$/);return!g||u.startsWith("@")?u:`${g[1]}: ${g[2]}`},m=u=>{const g=s.trim();if(s="",!!g){if(u==="{"){const y=g.startsWith("@")?[g]:Z(g,",");y.forEach((b,$)=>f(i()+b.trim()+($<y.length-1?",":" {")));return}f(i()+d(g)+";")}};for(let u=0;u<t.length;u++){const g=t[u];if(g==="/"&&t[u+1]==="*"){const y=t.indexOf("*/",u+2),b=y<0?t.length:y+2,$=t.slice(u,b);if(s.trim())s+=$;else{p();const c=$.split(`
|
|
8
|
+
`);f(i()+c[0]),n.push(...c.slice(1))}u=b-1;continue}if(g==='"'||g==="'"){s.trim()||p();let y=u+1;for(;y<t.length&&t[y]!==g&&t[y]!==`
|
|
9
|
+
`;)y+=t[y]==="\\"?2:1;s+=t.slice(u,y+1),u=y;continue}if(/\s/.test(g)){s.trim()?s.endsWith(" ")||(s+=" "):l+=g;continue}if(s.trim()||p(),g==="("?r++:g===")"&&(r=Math.max(0,r-1)),r===0&&g==="{"){m("{"),e++,l="";continue}if(r===0&&g===";"){m(";");continue}if(r===0&&g==="}"){if(m(";"),e--,e<0)throw new Error("There is a } with no { before it.");o=!1,n.push(i()+"}"),l="",e===0&&(a=!0);continue}s+=g}if(s.trim()&&m(";"),e!==0)throw new Error(`${e} { left without a closing }.`);return n.join(`
|
|
9
10
|
`)+`
|
|
10
|
-
`}function
|
|
11
|
-
`),s={comment:!1,str:null,stack:[]},
|
|
12
|
-
`)}const
|
|
13
|
-
`),
|
|
14
|
-
`);let
|
|
15
|
-
`))
|
|
16
|
-
`)
|
|
17
|
-
|
|
18
|
-
`)
|
|
19
|
-
`)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
`)
|
|
11
|
+
`}function Z(t,n){const e=[];let s=0,o=null,l="";for(const a of t){if(o)a===o&&(o=null);else if(a==='"'||a==="'")o=a;else if("([{".includes(a))s++;else if(")]}".includes(a))s--;else if(a===n&&s===0){e.push(l),l="";continue}l+=a}return e.push(l),e}const v=/(^|[(,=:[!&|?{};+\-*%<>~^]|\b(?:return|typeof|case|do|else|in|of|new|delete|void|throw|yield|await))\s*$/;function q(t,n,e){let s=0;if(n.str){let o=0;for(;o<t.length&&t[o]!==n.str;)o+=t[o]==="\\"?2:1;if(o>=t.length){t.endsWith("\\")||(n.str=null);return}n.str=null,s=o+1}for(;s<t.length;){const o=n.stack[n.stack.length-1];if(n.comment){const a=t.indexOf("*/",s);if(a<0)return;n.comment=!1,s=a+2;continue}if(o==="`"){const a=t[s];if(a==="\\"){s+=2;continue}if(a==="`"){n.stack.pop(),s++;continue}if(a==="$"&&t[s+1]==="{"){n.stack.push("${"),s+=2;continue}s++;continue}const l=t[s];if(l==="/"&&t[s+1]==="/")return;if(l==="/"&&t[s+1]==="*"){n.comment=!0,s+=2;continue}if(l==='"'||l==="'"){let a=s+1;for(;a<t.length&&t[a]!==l;)a+=t[a]==="\\"?2:1;if(a>=t.length&&t.endsWith("\\")){n.str=l;return}s=a+1;continue}if(l==="`"){n.stack.push("`"),s++;continue}if(l==="/"&&v.test(t.slice(0,s))){let a=s+1,r=!1;for(;a<t.length;){const i=t[a];if(i==="\\"){a+=2;continue}if(i==="[")r=!0;else if(i==="]")r=!1;else if(i==="/"&&!r)break;a++}s=a+1;continue}if(l==="}"&&o==="${"){n.stack.pop(),s++;continue}l==="{"&&n.stack.length?n.stack.push("{"):l==="}"&&o==="{"&&n.stack.pop(),"{[(".includes(l)?e(l,s,!0):"}])".includes(l)&&e(l,s,!1),s++}}function L(t){return t.replace(/(["'`])(?:\\.|(?!\1).)*\1/g,'""').replace(/\/\*.*?(\*\/|$)/g,"").replace(/\/\/.*$/,"").trimEnd()}export function formatJs(t,n=0){const e=t.split(`
|
|
12
|
+
`),s={comment:!1,str:null,stack:[]},o=[],l=[];let a="";const r=()=>{const i=o[o.length-1];return i?i.at+1+(i.sw&&i.inCase?1:0):0};if(e.forEach((i,f)=>{const p=s.stack[s.stack.length-1]==="`"||!!s.str,d=s.comment,m=i.trim();let u=i;if(p)u=i;else if(!m)u="";else if(d)u=C.repeat(n+r())+(m.startsWith("*")?" ":"")+m;else{const y=Math.min(o.length,(m.match(/^[}\])]+/)||[""])[0].length),b=o[o.length-1],$=/^(case\b.*|default\s*):/.test(m);let c;y?c=o[o.length-y].at:b?.sw&&$?c=b.at+1:c=r();const h=L(m),w=/^(\.(?!\.\.)|\?\.|[+\-*/%]|&&|\|\||\?\?|\?(?!\.)|:(?!:))/.test(h)&&!$||/(=>|[^=!<>]=|[+\-*/%]|&&|\|\||\?\?|\?)$/.test(a)&&!y&&!/^[{[(]/.test(h),x=h.startsWith("/")&&v.test(a);w&&!x&&!/^[}\])]/.test(m)&&c++,u=C.repeat(n+c)+m}l.push(u);const g=Math.round(u.match(/^[ \t]*/)[0].length/C.length)-n;if(p||d||m){const y=o[o.length-1];q(i,s,(b,$,c)=>{if(c){const h=b==="{"&&/\bswitch\s*\(.*\)\s*$/.test(L(i.slice(0,$)));o.push({at:p||d?r():g,sw:h,inCase:!1})}else o.pop()}),y?.sw&&!p&&!d&&/^(case\b.*|default\s*):/.test(m)&&(y.inCase=!0),!p&&!d&&(a=L(m))}}),s.comment||s.str||s.stack.some(i=>i!=="{"))throw new Error("A comment, string or template is left open.");return l.join(`
|
|
13
|
+
`)}const _=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),P=new Set(["pre","textarea","script","style"]),z=new Set(["address","article","aside","blockquote","body","caption","center","colgroup","dd","details","dialog","dir","div","dl","dt","fieldset","figcaption","figure","footer","form","frameset","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","legend","li","main","menu","nav","ol","optgroup","option","p","pre","search","section","summary","table","tbody","td","tfoot","th","thead","tr","ul"]),D=new Set(["base","link","meta","noscript","param","script","source","style","template","title","track"]),H=new Set(["address","article","aside","blockquote","details","dialog","div","dl","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","hr","main","menu","nav","ol","p","pre","search","section","table","ul"]),J=120;export function formatHtml(t,{templates:n=!0}={}){if(!t.trim())return t;const e=[],s=c=>c.includes(`
|
|
14
|
+
`)?`\0${e.push(c)-1}\0`:c,o=c=>C.repeat(c),l=c=>c?.type==="el"&&z.has(c.name)||c?.type==="raw"&&c.name==="pre"||c?.type==="decl",a=c=>{if(c.type!=="el"||!c.open.lineStart||!c.close?.lineEnd)return!1;const h=c.children.map(d).join("");return/^[ \t]*\n/.test(h)&&/\n\s*$/.test(h)},r=(c,h)=>!c||!h||l(c)||l(h)||c.type==="text"&&/\s$/.test(c.text)||h.type==="text"&&/^\s/.test(h.text),i=new WeakSet,f=c=>{c.forEach((h,w)=>{const x=h.type==="el"||h.type==="sec"?h.children:null;if(x&&f(x),l(h)){i.add(h);return}if(!(!(x&&x.some(k=>i.has(k))||a(h)||h.type==="sec"&&h.open.lineStart||h.type==="comment"&&h.lineStart||(h.type==="el"||h.type==="raw"||h.type==="stray")&&(D.has(h.name)||z.has(h.name)))||!r(c[w-1],h)||!r(h,c[w+1]))){if(x&&x.length){const k=(T,j)=>l(T)||T.type==="text"&&j.test(T.text);if(!k(x[0],/^\s/)||!k(x[x.length-1],/\s$/)||x.some((T,j)=>T.type==="else"&&(!r(x[j-1],T)||!r(T,x[j+1]))))return}i.add(h)}})},p=c=>i.has(c),d=c=>{switch(c.type){case"text":return c.text;case"el":return m(c.open.text,-1)[0]+(c.close?""+c.children.map(d).join("")+""+c.close.text:c.children.map(d).join(""));case"sec":return c.open.text+c.children.map(d).join("")+(c.close?.text??"");case"raw":return s(c.open+c.body+c.close);default:return s(c.text)}},m=(c,h)=>{const w=X(c);if(!w)return[s(c)];const x=w.slash?w.attrs.length||w.spaced?" />":"/>":">",S=`<${w.name}${w.attrs.map(k=>" "+k).join("")}${x}`;return h<0||w.attrs.length<2||o(h).length+S.length<=J?[h<0?S:o(h)+S]:[o(h)+"<"+w.name,...w.attrs.map((k,T)=>o(h+1)+k+(T===w.attrs.length-1?x.trim()===">"?">":x:""))]},u=c=>c.type==="text"&&!c.text.trim(),g=(c,h)=>{const w=[];let x=!1;const S=j=>{x&&w.length&&w.push(""),x=!1,w.push(j)};let k=[];const T=()=>{if(!k.length)return;const j=k.map(d).join("").split(`
|
|
15
|
+
`).map(O=>O.trim());k=[];let N=0;j.forEach((O,A)=>{const F=(O.match(/^(?:\u0002<\/[^>]*>)+/)||[""])[0].split("").length-1,K=Math.max(0,N-F);N+=O.split("").length-O.split("").length,O=O.replace(/[\u0001\u0002]/g,""),O?S(o(h+K)+O):A>0&&A<j.length-1&&!j[A-1]&&(x=!0)})};for(const j of c){if(!p(j)){k.push(j);continue}T(),y(j,h).forEach(S)}return T(),w},y=(c,h)=>{if(c.type==="el"){const w=m(c.open.text,h),x=c.close?c.close.text:"";if(c.children.every(u))return w.length===1&&x?[w[0]+x]:x?[...w,o(h)+x]:w;if(w.length===1&&!c.children.some(p)){const S=c.children.map(d).join("").replace(c.close?/$^/:/\s+$/,"");if(!S.includes(`
|
|
16
|
+
`))return[w[0]+S.replace(/[\u0001\u0002]/g,"")+x]}return[...w,...g(c.children,h+1),...x?[o(h)+x]:[]]}if(c.type==="sec"){const w=[o(h)+c.open.text];let x=[];for(const S of c.children){if(S.type!=="else"){x.push(S);continue}w.push(...g(x,h+1),o(h)+S.text),x=[]}return w.push(...g(x,h+1)),c.close&&w.push(o(h)+c.close.text),w}if(c.type==="raw"){if(c.name==="pre"||!c.body.includes(`
|
|
17
|
+
`)||!c.close)return[o(h)+s(c.open+c.body+c.close)];const w=c.body.replace(/^[ \t]*\n/,"").replace(/\n[ \t]*$/,"");let x=null;try{const S=(c.open.match(/\btype\s*=\s*["']?([^"'\s>]*)/i)||[])[1]||"";c.name==="script"&&(!S||/module|javascript|ecmascript/i.test(S))?x=formatJs(w,h+1):c.name==="style"&&(x=Y(formatCss(Q(w)).replace(/\n$/,""),h+1))}catch{x=null}return[o(h)+s(c.open),...(x??s(w)).split(`
|
|
18
|
+
`),o(h)+c.close]}return[o(h)+s(c.text)]},b=V(G(t,n));return f(b),g(b,0).join(`
|
|
19
|
+
`).replace(/[\u0001\u0002]/g,"").replace(/\u0000(\d+)\u0000/g,(c,h)=>e[Number(h)])+(t.endsWith(`
|
|
20
|
+
`)?`
|
|
21
|
+
`:"")}function U(t,n){let e=null;for(let s=n+1;s<t.length;s++){const o=t[s];if(e){o===e&&(e=null);continue}if(o==='"'||o==="'")e=o;else if(o==="{"&&t[s+1]==="{"){const l=t.indexOf("}}",s+2);if(l<0)return-1;s=l+1}else{if(o===">")return s+1;if(o==="<")return-1}}return-1}function G(t,n){const e=[];let s=0,o=0;const l=(a,r)=>{s>o&&e.push({type:"text",text:t.slice(o,s)}),a.lineStart=!t.slice(t.lastIndexOf(`
|
|
22
|
+
`,s-1)+1,s).trim(),a.lineEnd=/^[ \t]*(\n|$)/.test(t.slice(r,r+200)),e.push(a),s=o=r};for(;s<t.length;){const a=t[s],r=t[s+1]||"";if(a==="<"&&t.startsWith("<!--",s)){const i=t.indexOf("-->",s+4);l({type:"comment",text:t.slice(s,i<0?t.length:i+3)},i<0?t.length:i+3);continue}if(a==="<"&&r==="!"){const i=t.indexOf(">",s);if(i>=0){l({type:"decl",text:t.slice(s,i+1)},i+1);continue}}if(a==="<"&&r==="/"){const i=/^<\/([a-zA-Z][\w:.-]*)\s*>/.exec(t.slice(s,s+200));if(i){l({type:"close",name:i[1].toLowerCase(),text:i[0]},s+i[0].length);continue}}if(a==="<"&&/[a-zA-Z]/.test(r)){const i=U(t,s);if(i>0){const f=t.slice(s,i),p=/^<([a-zA-Z][\w:.-]*)/.exec(f)[1].toLowerCase(),d=_.has(p)||B(f);if(P.has(p)&&!d){const m=new RegExp(`</${p}\\s*>`,"i").exec(t.slice(i)),u=m?i+m.index:t.length;l({type:"raw",name:p,open:f,body:t.slice(i,u),close:m?m[0]:""},m?u+m[0].length:u);continue}l({type:"open",name:p,text:f,self:d},i);continue}}if(n&&a==="{"&&r==="{"){if(t.startsWith("{{!--",s)){const f=t.indexOf("--}}",s);s=f<0?t.length:f+4;continue}const i=t.indexOf("}}",s+2);if(i>=0){const f=t.slice(s+2,i).trim();let p,d=null;if((p=/^[#^]\s*([\w.-]+)/.exec(f))?d={type:"hb",kind:"open",name:p[1]}:(p=/^\/\s*([\w.-]+)$/.exec(f))?d={type:"hb",kind:"close",name:p[1]}:/^(else\b|\^$)/.test(f)&&(d={type:"hb",kind:"else"}),d){d.text=t.slice(s,i+2),l(d,i+2);continue}s=i+2;continue}}s++}return s>o&&e.push({type:"text",text:t.slice(o,s)}),e}function V(t){const n={type:"root",children:[]},e=[n],s=()=>e[e.length-1],o=r=>s().children.push(r),l=r=>{for(let i=e.length-1;i>0;i--)if(e[i].type==="sec"&&(!r||e[i].name===r))return i;return-1},a=r=>{const i=s();if(i.type!=="el")return;const f=p=>p==="td"||p==="th";(r==="li"&&i.name==="li"||r==="option"&&i.name==="option"||(r==="dt"||r==="dd")&&(i.name==="dt"||i.name==="dd")||f(r)&&f(i.name)||r==="tr"&&(i.name==="tr"||f(i.name))||i.name==="p"&&H.has(r))&&(e.pop(),r==="tr"&&s().type==="el"&&s().name==="tr"&&e.pop())};for(const r of t)if(r.type==="open"){a(r.name);const i={type:"el",name:r.name,open:r,children:[],close:null};o(i),r.self||e.push(i)}else if(r.type==="close"){let i=e.length-1;for(;i>0&&e[i].type==="el"&&e[i].name!==r.name;)i--;i>0&&e[i].type==="el"?(e[i].close=r,e.length=i):o({type:"stray",name:r.name,text:r.text})}else if(r.type==="hb"){if(r.kind==="open"){const f={type:"sec",name:r.name,open:r,children:[],close:null};o(f),e.push(f);continue}const i=l(r.kind==="close"?r.name:null);if(i<0){o({type:"text",text:r.text});continue}r.kind==="else"?(e.length=i+1,o({type:"else",text:r.text})):(e[i].close=r,e.length=i)}else o(r);return n.children}function B(t){const n=/^<[a-zA-Z][\w:.-]*([\s\S]*?)\/\s*>$/.exec(t);return!!n&&(!n[1].trim()||/[\s"'}]$/.test(n[1])||!/=\s*[^\s"'=]+$/.test(n[1]))}function X(t){const n=/^<([a-zA-Z][\w:.-]*)/.exec(t);if(!n)return null;let e=t.slice(n[0].length,-1);const s=B(t);if(s&&(e=e.replace(/\/\s*$/,"")),e&&!/^\s/.test(e))return null;const o=[];let l="",a=null;for(let r=0;r<e.length;r++){const i=e[r];if(a){if(i===`
|
|
23
|
+
`)return null;l+=i,i===a&&(a=null);continue}if(i==='"'||i==="'"){a=i,l+=i;continue}if(i==="{"&&e[r+1]==="{"){const f=e.indexOf("}}",r+2),p=e.slice(r,f+2);if(p.includes(`
|
|
24
|
+
`))return null;l+=p,r=f+1;continue}if(/\s/.test(i)){const f=e.slice(r).match(/^\s+/)[0];if(l&&(l.endsWith("=")||e[r+f.length]==="=")){r+=f.length-1;continue}l&&o.push(l),l="",r+=f.length-1;continue}l+=i}return a?null:(l&&o.push(l),{name:n[1],attrs:o,slash:s,spaced:/\s\/\s*>$/.test(t)})}function Q(t){const n=t.split(`
|
|
25
|
+
`),e=n.filter(o=>o.trim()).map(o=>o.match(/^[ \t]*/)[0].length),s=e.length?Math.min(...e):0;return n.map(o=>o.slice(Math.min(s,o.match(/^[ \t]*/)[0].length))).join(`
|
|
26
|
+
`).replace(/^\n+|\n+$/g,"")}function Y(t,n){return t.split(`
|
|
27
|
+
`).map(e=>e&&C.repeat(n)+e).join(`
|
|
28
|
+
`)}export const SHORT_TAG_CONTAINERS=new Set(["grid","row","col","card","box","header","footer","tabs","tab","accordion","item","carousel","slide","timeline","event","hero","banner","slideover","center","listgroup","table","dconfig","reveal","animate","pulse","shake","scramble","ripple","breathe","ambient","twinkle","fireworks","scribe","wait","render","redraw"]);export function shortTagLine(t){const n=String(t).trim().match(/^\[(\/?)([a-z][\w-]*)(?:\s[^\]]*)?\]$/i);return!n||!SHORT_TAG_CONTAINERS.has(n[2].toLowerCase())||!n[1]&&/\/\]$/.test(t.trim())?null:{name:n[2].toLowerCase(),close:!!n[1]}}export function shortTagLayout(t){const n=t.map(()=>({tag:!1,depth:0,block:-1,fence:!1})),e=new Set;let s=null;t.forEach((r,i)=>{if(s){e.add(i);const p=r.match(/^\s*(`{3,}|~{3,})\s*$/);p&&p[1][0]===s[0]&&p[1].length>=s.length&&(s=null);return}const f=r.match(/^\s*(`{3,}|~{3,})/);f&&(s=f[1])});const o=(r,i)=>{let f=0;const p=new RegExp(`\\[(\\/?)${r}(?:[\\s\\]])`,"gi");for(let d=i;d<t.length;d++)if(!e.has(d))for(const m of t[d].matchAll(p))if(!m[1])f++;else{if(f===0)return!0;f--}return!1},l=[];t.forEach((r,i)=>{const f=l[l.length-1];if(n[i].depth=l.length,n[i].block=f?f.at:-1,e.has(i)){n[i].fence=!0;return}const p=shortTagLine(r);if(p)if(p.close){const d=l.map(m=>m.name).lastIndexOf(p.name);if(d<0)return;l.length=d,Object.assign(n[i],{tag:!0,depth:l.length})}else o(p.name,i+1)&&(n[i].tag=!0,l.push({name:p.name,at:i}))});const a=new Map;return t.forEach((r,i)=>{const f=n[i];if(f.tag||f.fence||f.block<0||!r.trim())return;const p=r.match(/^[ \t]*/)[0].length;a.set(f.block,Math.min(a.get(f.block)??1/0,p))}),n.map((r,i)=>{const f=t[i].match(/^[ \t]*/)[0].length,p=r.tag?f:r.block<0?0:Math.min(f,a.get(r.block)??0);return{...r,cut:p}})}function tt(t){const n=shortTagLayout(t);return t.map((e,s)=>n[s].cut?e.slice(n[s].cut):e)}function et(t){const n=shortTagLayout(t);return t.map((e,s)=>{const o=n[s];return o.tag?C.repeat(o.depth)+e.trim():!o.depth||!e.trim()?e:C.repeat(o.depth)+e})}export function formatMarkdown(t){if(!t.trim())return t;const n=E.shortTags?tt(t.split(`
|
|
23
29
|
`)):t.split(`
|
|
24
|
-
`),
|
|
30
|
+
`),e=[];let s=null,o=!1;const l=r=>/^( {4}|\t)/.test(r),a=r=>{for(let i=r+1;i<n.length;i++)if(n[i].trim())return n[i];return""};for(let r=0;r<n.length;r++){const i=n[r];if(s){e.push(i);const u=i.match(/^ {0,3}(`{3,}|~{3,})\s*$/);u&&u[1][0]===s[0]&&u[1].length>=s.length&&(s=null);continue}if(o||/^\s*<pre[\s>]/i.test(i)){e.push(i),o=!/<\/pre\s*>/i.test(i);continue}const f=i.match(/^ {0,3}(`{3,}|~{3,})/);if(f){s=f[1],e.push(i.trimEnd());continue}if(E.html&&nt.test(i)){let u=r;for(;u<n.length&&n[u].trim()&&!shortTagLine(n[u]);)u++;const g=n.slice(r,u);if(!g.some(y=>st.test(y))){const y=i.match(/^ */)[0];e.push(...formatHtml(g.join(`
|
|
31
|
+
`),{templates:!1}).split(`
|
|
32
|
+
`).map(b=>b&&y+b)),r=u-1;continue}}if(E.tables&&it(n,r)){let u=r+2;for(;u<n.length&&/\|/.test(n[u])&&n[u].trim();)u++;e.push(...rt(n.slice(r,u))),r=u-1;continue}if(!i.trim()){const u=e.length?e[e.length-1]:"",g=[...e].reverse().find(y=>y.trim())||"";if(!u.trim()&&!(l(g)&&l(a(r))))continue;e.push("");continue}if(l(i)){e.push(i);continue}const p=i.match(/[ \t]*$/)[0],d=i.slice(0,i.length-p.length),m=/ {2,}$/.test(p)&&(n[r+1]||"").trim()&&!/^\s*#/.test(d);e.push(m?d+" ":d)}for(;e.length&&!e[0].trim();)e.shift();for(;e.length&&!e[e.length-1].trim();)e.pop();return(E.shortTags?et(e):e).join(`
|
|
25
33
|
`)+`
|
|
26
|
-
`}const
|
|
27
|
-
`,
|
|
28
|
-
`,
|
|
29
|
-
`):
|
|
30
|
-
`+
|
|
31
|
-
`+
|
|
32
|
-
`+(
|
|
33
|
-
|
|
34
|
-
`+
|
|
35
|
-
|
|
36
|
-
`+
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
`)
|
|
40
|
-
|
|
41
|
-
`)
|
|
42
|
-
`).length
|
|
43
|
-
`)[
|
|
44
|
-
`)
|
|
34
|
+
`}const nt=/^ {0,3}<(?:address|article|aside|blockquote|body|caption|center|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|h[1-6]|header|html|legend|li|main|menu|nav|ol|p|search|section|summary|table|tbody|td|tfoot|th|thead|tr|ul)(?=[\s/>]|$)/i,st=/<\/?(?:pre|textarea|script|style)\b|^\s*(?:`{3,}|~{3,})/i,ot=/^\s*:?-+:?\s*$/;function W(t){let n=t.trim();n.startsWith("|")&&(n=n.slice(1)),n.endsWith("|")&&!n.endsWith("\\|")&&(n=n.slice(0,-1));const e=[];let s="";for(let o=0;o<n.length;o++){if(n[o]==="\\"&&n[o+1]==="|"){s+="\\|",o++;continue}if(n[o]==="|"){e.push(s.trim()),s="";continue}s+=n[o]}return e.push(s.trim()),e}function it(t,n){const e=t[n],s=t[n+1];if(!e||!s||!e.includes("|")||!s.includes("|")||/^\s{4,}/.test(e))return!1;const o=W(s);if(!(o.length>0&&o.every(l=>ot.test(l))&&W(e).length===o.length))return!1;for(let l=n+2;l<t.length&&t[l].includes("|")&&t[l].trim();l++)if(W(t[l]).length>o.length)return!1;return!0}function rt(t){const n=t[0].match(/^\s*/)[0],e=t.map(W),s=Math.max(...e.map(r=>r.length)),o=e[1].map(r=>r.startsWith(":")&&r.endsWith(":")?"c":r.endsWith(":")?"r":r.startsWith(":")?"l":""),l=r=>[...r].length,a=Array.from({length:s},(r,i)=>Math.max(3,...e.map((f,p)=>p===1?3:l(f[i]??""))));return e.map((r,i)=>{const f=Array.from({length:s},(p,d)=>{const m=a[d];if(i===1){const y=o[d]||"",b="-".repeat(m-(y==="c"?2:y?1:0));return y==="c"?`:${b}:`:y==="r"?`${b}:`:y==="l"?`:${b}`:b}const u=r[d]??"",g=m-l(u);return o[d]==="r"?" ".repeat(g)+u:o[d]==="c"?" ".repeat(Math.floor(g/2))+u+" ".repeat(Math.ceil(g/2)):u+" ".repeat(g)});return`${n}| ${f.join(" | ")} |`})}const M=(t,n)=>t.lastIndexOf(`
|
|
35
|
+
`,n-1)+1,I=(t,n)=>{const e=t.indexOf(`
|
|
36
|
+
`,n);return e<0?t.length:e};export function enterEdit(t,n,e,s){const o=M(t,n),l=t.slice(o,I(t,n)),a=t.slice(o,n),r=t.slice(e,I(t,e)),i=(a.match(/^[ \t]*/)||[""])[0],f=(b,$=b.length)=>({start:n,end:e,text:b,selStart:n+$,selEnd:n+$});if(s==="md"||!s){const b=shortTagLine(l);if(b&&n===e&&!r.trim()&&!R(t,o))return!E.shortTags||!E.autoIndent?f(`
|
|
37
|
+
`):f(`
|
|
38
|
+
`+i+(b.close?"":prefUnit()));const $=a.match(/<([a-zA-Z][\w-]*)(?:\s[^<>]*)?>\s*$/);if($&&E.autoIndent&&n===e&&(z.has($[1].toLowerCase())||$[1].includes("-"))&&!_.has($[1].toLowerCase())&&!/\/\s*>\s*$/.test(a)&&!R(t,o)){const c=`
|
|
39
|
+
`+i+prefUnit();if(new RegExp(`^\\s*</${$[1]}\\s*>`,"i").test(r)){const h=(t.slice(e).match(/^[ \t]*/)||[""])[0].length;return{start:n,end:e+h,text:c+`
|
|
40
|
+
`+i,selStart:n+c.length,selEnd:n+c.length}}return f(c)}if(E.continueLists&&!R(t,o)){const c=a.match(/^([ \t]*)((?:>[ \t]?)+)?([ \t]*)([-*+]|(\d+)([.)]))?([ \t]+)(\[[ xX]\][ \t]+)?/),h=c?.[2]||"",w=c?.[4]||"",x=/^\s*([-*_])(\s*\1){2,}\s*$/.test(l);if(c&&(h||w)&&!x&&c[0].length<=a.length){if(!l.slice(c[0].length).trim()&&n===e&&n>=o+c[0].length)return{start:o,end:I(t,n),text:c[1],selStart:o+c[1].length,selEnd:o+c[1].length};if(n>=o+c[0].length-(c[7]?.length||0)){const k=c[5]?`${Number(c[5])+1}${c[6]}`:w,T=`
|
|
41
|
+
`+c[1]+h+(c[3]||"")+(w?k+(c[7]||" ")+(c[8]?"[ ] ":""):c[7]||"");return f(T)}}}return f(`
|
|
42
|
+
`+(E.autoIndent?i:""))}if(!E.autoIndent)return f(`
|
|
43
|
+
`);const p=(s==="html"?a:L(a)).trimEnd().slice(-1),d=r.trimStart()[0]||"",m={"{":"}","[":"]","(":")"};let u=!!m[p],g=u&&d===m[p];if(s==="html"||s==="css"){const b=a.match(/<([a-zA-Z][\w:-]*)(?:\s[^<>]*)?>\s*$/);b&&!_.has(b[1].toLowerCase())&&!/\/\s*>\s*$/.test(a)&&(u=!0,g=new RegExp(`^\\s*</${b[1]}\\s*>`,"i").test(r)),/\{\{\s*#\w+[^}]*\}\}\s*$/.test(a)&&(u=!0,g=/^\s*\{\{\s*\//.test(r))}if(!u)return f(`
|
|
44
|
+
`+i);const y=`
|
|
45
|
+
`+i+unitFor(t);if(g){const b=(t.slice(e).match(/^[ \t]*/)||[""])[0].length;return{start:n,end:e+b,text:y+`
|
|
46
|
+
`+i,selStart:n+y.length,selEnd:n+y.length}}return f(y)}function R(t,n){let e=null;for(const s of t.slice(0,n).split(`
|
|
47
|
+
`)){const o=s.match(/^\s*(`{3,}|~{3,})/);o&&(e?o[1][0]===e[0]&&o[1].length>=e.length&&!s.trim().slice(o[1].length).trim()&&(e=null):e=o[1])}return!!e}export function indentEdit(t,n,e,s,o=" "){const l=M(t,n),a=e>n&&t[e-1]===`
|
|
48
|
+
`?e-1:e,r=I(t,a);if(!t.slice(n,e).includes(`
|
|
49
|
+
`)&&!(s&&n===e))return null;const i=t.slice(l,r),f=i.split(`
|
|
50
|
+
`),p=f.map(m=>s?m.startsWith(o)?m.slice(o.length):m.startsWith(" ")?m.slice(2):m.startsWith(" ")?m.slice(1):m.replace(/^ /,""):m.trim()?o+m:m),d=p.join(`
|
|
51
|
+
`);if(d===i)return null;if(n===e){const m=f[0].length-p[0].length,u=Math.max(l,n-m);return{start:l,end:r,text:d,selStart:u,selEnd:u}}return{start:l,end:r,text:d,selStart:l,selEnd:l+d.length}}export function closerEdit(t,n,e,s,o=""){if(n!==e)return null;const l=M(t,n);let a=t.slice(l,n),r=s;if(o==="html"&&s==="/"&&/^[ \t]*<$/.test(a))a=a.slice(0,-1),r="</";else if(!"}])".includes(s))return null;if(!E.autoIndent)return null;const i=unitFor(t);if(!a||a.trim()||!a.endsWith(i))return null;const f=a.slice(0,-i.length),p=l+f.length+r.length;return{start:l,end:n,text:f+r,selStart:p,selEnd:p}}export const tabUnit=(t,n)=>n==="md"?prefUnit():unitFor(t);export function changedSpan(t,n){let e=0;const s=Math.min(t.length,n.length);for(;e<s&&t[e]===n[e];)e++;let o=0;for(;o<s-e&&t[t.length-1-o]===n[n.length-1-o];)o++;return{start:e,end:t.length-o,text:n.slice(e,n.length-o)}}export function caretAfterFormat(t,n,e){const s=t.slice(0,e).split(`
|
|
52
|
+
`).length-1,o=t.split(`
|
|
53
|
+
`)[s]??"",l=e-M(t,e),a=Math.max(0,l-o.match(/^[ \t]*/)[0].length),r=n.split(`
|
|
54
|
+
`),i=Math.min(s,r.length-1);let f=0;for(let m=0;m<i;m++)f+=r[m].length+1;const p=r[i],d=p.match(/^[ \t]*/)[0].length;return f+Math.min(p.length,d+a)}export function applyEdit(t,n){const{start:e,end:s,text:o}=n;let l=!1;if(typeof document<"u"&&typeof document.execCommand=="function"){t.focus(),t.setSelectionRange(e,s);try{l=o?document.execCommand("insertText",!1,o):e===s||document.execCommand("delete")}catch{l=!1}}l||(t.value=t.value.slice(0,e)+o+t.value.slice(s),t.dispatchEvent(new Event("input",{bubbles:!0}))),t.setSelectionRange(n.selStart,n.selEnd)}export function formatTextarea(t,n){const e=t.value,s=formatCode(e,n);if(s.error)return s;if(s.text===e)return{text:e,unchanged:!0};const o=caretAfterFormat(e,s.text,t.selectionStart),l=changedSpan(e,s.text),a=t.scrollTop;return applyEdit(t,{...l,selStart:o,selEnd:o}),t.scrollTop=a,s}
|
|
@@ -62,6 +62,10 @@
|
|
|
62
62
|
and Blog Editor, for example). They appear on the Roles screen
|
|
63
63
|
like any other and you can edit them; the plugin never overwrites your changes.
|
|
64
64
|
Switching the plugin off keeps its roles; uninstalling it removes them and moves their users to <code>user</code>.</li>
|
|
65
|
+
<li><strong>Plugin data in Collections.</strong> The Collections screen, form submissions,
|
|
66
|
+
Views and API Builder open a plugin's own collections only for someone who can fully manage
|
|
67
|
+
that plugin (for the games, their Manage permission). Someone who may only view a game
|
|
68
|
+
cannot read its answers or players' rows through any of them.</li>
|
|
65
69
|
</ul>
|
|
66
70
|
|
|
67
71
|
<h3>Confining a role to some screens</h3>
|