From be063ec2d709c0423e12724efeb57251190dc63e Mon Sep 17 00:00:00 2001 From: Steve Roberts Date: Fri, 22 May 2026 17:00:13 +0100 Subject: [PATCH] fix: Remove case-colliding duplicate PInNote directory allowing files to continuously show as modified on case-insensitive environments Signed-off-by: Steve Roberts --- PInNote/1.0.0/PinNote.js | 345 ------------------------------------- PInNote/1.0.1/PinNote.js | 359 --------------------------------------- PInNote/PinNote.js | 359 --------------------------------------- PInNote/readme.md | 83 --------- PInNote/script.json | 16 -- 5 files changed, 1162 deletions(-) delete mode 100644 PInNote/1.0.0/PinNote.js delete mode 100644 PInNote/1.0.1/PinNote.js delete mode 100644 PInNote/PinNote.js delete mode 100644 PInNote/readme.md delete mode 100644 PInNote/script.json diff --git a/PInNote/1.0.0/PinNote.js b/PInNote/1.0.0/PinNote.js deleted file mode 100644 index ad8213a89..000000000 --- a/PInNote/1.0.0/PinNote.js +++ /dev/null @@ -1,345 +0,0 @@ -// Script: PinNote -// By: Keith Curtis -// Contact: https://app.roll20.net/users/162065/keithcurtis -var API_Meta = API_Meta||{}; //eslint-disable-line no-var -API_Meta.PinNote={offset:Number.MAX_SAFE_INTEGER,lineCount:-1}; -{try{throw new Error('');}catch(e){API_Meta.PinNote.offset=(parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/,'$1'),10)-6);}} - - -(() => { - 'use strict'; - - const version = '1.0.0'; //version number set here - log('-=> PinNote v' + version + ' is loaded.'); - //Changelog - //1.0.0 Debut - - - const SCRIPT_NAME = 'PinNote'; - - const isGMPlayer = (playerid) => playerIsGM(playerid); - - const getTemplate = (name) => { - if (typeof Supernotes_Templates === 'undefined') { - return null; - } - if (!name) return Supernotes_Templates.generic; - const key = name.toLowerCase(); - return Supernotes_Templates[key] || Supernotes_Templates.generic; - }; - - const sendGenericError = (msg, text) => { - if (typeof Supernotes_Templates === 'undefined') return; - - const t = Supernotes_Templates.generic; - sendChat( - SCRIPT_NAME, - t.boxcode + - t.titlecode + SCRIPT_NAME + - t.textcode + text + - '' + - t.footer + - '' - ); - }; - - /* ============================================================ - * HEADER COLOR ENFORCEMENT - * ============================================================ */ - - const enforceHeaderColor = (html, template) => { - if (!html) return html; - - const colorMatch = template.textcode.match(/color\s*:\s*([^;"]+)/i); - if (!colorMatch) return html; - - const colorValue = colorMatch[1].trim(); - - return html.replace( - /<(h[1-4])\b([^>]*)>/gi, - (match, tag, attrs) => { - - if (/style\s*=/i.test(attrs)) { - return `<${tag}${attrs.replace( - /style\s*=\s*["']([^"']*)["']/i, - (m, styleContent) => - `style="${styleContent}; color: ${colorValue};"` - )}>`; - } - - return `<${tag}${attrs} style="color: ${colorValue};">`; - } - ); - }; - - /* ============================================================ */ - - const parseArgs = (content) => { - const args = {}; - content.replace(/--([^|]+)\|([^\s]+)/gi, (_, k, v) => { - args[k.toLowerCase()] = v.toLowerCase(); - return ''; - }); - return args; - }; - - const extractHandoutSection = ({ handout, subLink, subLinkType }) => { - return new Promise((resolve) => { - - if (!handout) return resolve(null); - - if (!subLink) { - const field = subLinkType === 'headerGM' ? 'gmnotes' : 'notes'; - handout.get(field, (content) => resolve(content || null)); - return; - } - - if (!['headerplayer', 'headergm'].includes(subLinkType?.toLowerCase())) { - return resolve(null); - } - - const field = subLinkType.toLowerCase() === 'headergm' - ? 'gmnotes' - : 'notes'; - - handout.get(field, (content) => { - if (!content) return resolve(null); - - const headerRegex = /<(h[1-4])\b[^>]*>([\s\S]*?)<\/\1>/gi; - let match; - - while ((match = headerRegex.exec(content)) !== null) { - const tagName = match[1]; - const innerHTML = match[2]; - const stripped = innerHTML.replace(/<[^>]+>/g, ''); - - if (stripped === subLink) { - const level = parseInt(tagName[1], 10); - const startIndex = match.index; - - const remainder = content.slice(headerRegex.lastIndex); - - const stopRegex = new RegExp( - `]*>`, - 'i' - ); - - const stopMatch = stopRegex.exec(remainder); - - const endIndex = stopMatch - ? headerRegex.lastIndex + stopMatch.index - : content.length; - - return resolve(content.slice(startIndex, endIndex)); - } - } - - resolve(null); - }); - }); - }; - - const transformBlockquoteMode = (html) => { - - const blockRegex = /]*>([\s\S]*?)<\/blockquote>/gi; - - let match; - let lastIndex = 0; - let playerContent = ''; - let gmContent = ''; - let found = false; - - while ((match = blockRegex.exec(html)) !== null) { - found = true; - gmContent += html.slice(lastIndex, match.index); - playerContent += match[1]; - lastIndex = blockRegex.lastIndex; - } - - gmContent += html.slice(lastIndex); - - if (!found) { - return { player: '', gm: html }; - } - - return { player: playerContent, gm: gmContent }; - }; - - on('chat:message', async (msg) => { - if (msg.type !== 'api' || !msg.content.startsWith('!pinnote')) return; - - if (typeof Supernotes_Templates === 'undefined') { - sendChat(SCRIPT_NAME, `/w gm PinNote requires Supernotes_Templates to be loaded.`); - return; - } - - const args = parseArgs(msg.content); - const isGM = isGMPlayer(msg.playerid); - - if (!msg.selected || msg.selected.length === 0) - return sendGenericError(msg, 'No pin selected.'); - - const sel = msg.selected.find(s => s._type === 'pin'); - if (!sel) - return sendGenericError(msg, 'Selected object is not a pin.'); - - const pin = getObj('pin', sel._id); - if (!pin) - return sendGenericError(msg, 'Selected pin could not be resolved.'); - - const isSynced = - !pin.get('notesDesynced') && - !pin.get('gmNotesDesynced') && - !pin.get('imageDesynced'); - - const linkType = pin.get('linkType'); - - /* ============================================================ - * LINKED HANDOUT MODE - * ============================================================ */ - - if (isSynced && linkType === 'handout') { - - const handoutId = pin.get('link'); - const subLink = pin.get('subLink'); - const subLinkType = pin.get('subLinkType'); - const autoNotesType = pin.get('autoNotesType'); - - const handout = getObj('handout', handoutId); - if (!handout) - return sendGenericError(msg, 'Linked handout not found.'); - - let extracted = await extractHandoutSection({ - handout, - subLink, - subLinkType - }); - - if (!extracted) - return sendGenericError(msg, 'Requested section not found in handout.'); - - const template = getTemplate(args.template); - if (!template) return; - - const sender = pin.get('title') || SCRIPT_NAME; - const titleText = subLink || sender; - - if (subLink) { - const headerStripRegex = /^]*>[\s\S]*?<\/h[1-4]>/i; - extracted = extracted.replace(headerStripRegex, ''); - } - - let to = (args.to || 'pc').toLowerCase(); - if (!isGM) to = 'pc'; - - let whisperPrefix = ''; - const extractingGM = subLinkType?.toLowerCase() === 'headergm'; - - let visibleContent = extracted; - let gmBlock = ''; - - if (autoNotesType === 'blockquote') { - - const transformed = transformBlockquoteMode(extracted); - - visibleContent = enforceHeaderColor(transformed.player, template); - - if (transformed.gm && to !== 'pc') { - gmBlock = - `
` + - enforceHeaderColor(transformed.gm, template) + - `
`; - } - - } else { - visibleContent = enforceHeaderColor(visibleContent, template); - } - - if (extractingGM) { - whisperPrefix = '/w gm '; - } else if (to === 'gm') { - whisperPrefix = '/w gm '; - } else if (to === 'self') { - whisperPrefix = `/w "${msg.who}" `; - } - - const html = - template.boxcode + - template.titlecode + titleText + - template.textcode + - (visibleContent || '') + - gmBlock + - '' + - template.footer + - ''; - - sendChat(sender, whisperPrefix + html); - return; - } - - /* ============================================================ - * CUSTOM PIN MODE - * ============================================================ */ - - if ( - !pin.get('notesDesynced') && - !pin.get('gmNotesDesynced') && - !pin.get('imageDesynced') - ) { - return sendGenericError( - msg, - 'This pin is not desynced from its linked handout.' - ); - } - - const notes = (pin.get('notes') || '').trim(); - if (!notes) - return sendGenericError(msg, 'This pin has no notes to display.'); - - let to = (args.to || 'pc').toLowerCase(); - if (!isGM) to = 'pc'; - - let whisperPrefix = ''; - if (to === 'gm') whisperPrefix = '/w gm '; - else if (to === 'self') whisperPrefix = `/w "${msg.who}" `; - - const template = getTemplate(args.template); - if (!template) return; - - const sender = pin.get('title') || SCRIPT_NAME; - - let imageBlock = ''; - const tooltipImage = pin.get('tooltipImage'); - if (tooltipImage) { - imageBlock = - ``; - } - - const coloredNotes = enforceHeaderColor(notes, template); - - let gmBlock = ''; - if (isGM && to !== 'pc' && pin.get('gmNotes')) { - gmBlock = - `
` + - enforceHeaderColor(pin.get('gmNotes'), template) + - `
`; - } - - const html = - template.boxcode + - template.titlecode + sender + - template.textcode + - imageBlock + - coloredNotes + - gmBlock + - '' + - template.footer + - ''; - - sendChat(sender, whisperPrefix + html); - }); - -})(); - -{try{throw new Error('');}catch(e){API_Meta.PinNote.lineCount=(parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/,'$1'),10)-API_Meta.PinNote.offset);}} diff --git a/PInNote/1.0.1/PinNote.js b/PInNote/1.0.1/PinNote.js deleted file mode 100644 index adfb37ca4..000000000 --- a/PInNote/1.0.1/PinNote.js +++ /dev/null @@ -1,359 +0,0 @@ -// Script: PinNote -// By: Keith Curtis -// Contact: https://app.roll20.net/users/162065/keithcurtis -var API_Meta = API_Meta||{}; //eslint-disable-line no-var -API_Meta.PinNote={offset:Number.MAX_SAFE_INTEGER,lineCount:-1}; -{try{throw new Error('');}catch(e){API_Meta.PinNote.offset=(parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/,'$1'),10)-6);}} - - -(() => { - 'use strict'; - - const version = '1.0.1'; //version number set here - log('-=> PinNote v' + version + ' is loaded.'); - //Changelog - //1.0.1 gmtext fix - //1.0.0 Debut - - - const SCRIPT_NAME = 'PinNote'; - - const isGMPlayer = (playerid) => playerIsGM(playerid); - - const getTemplate = (name) => { - if (typeof Supernotes_Templates === 'undefined') { - return null; - } - if (!name) return Supernotes_Templates.generic; - const key = name.toLowerCase(); - return Supernotes_Templates[key] || Supernotes_Templates.generic; - }; - - const sendGenericError = (msg, text) => { - if (typeof Supernotes_Templates === 'undefined') return; - - const t = Supernotes_Templates.generic; - sendChat( - SCRIPT_NAME, - t.boxcode + - t.titlecode + SCRIPT_NAME + - t.textcode + text + - '' + - t.footer + - '' - ); - }; - - -const normalizeHTML = (html) => { - if (!html) return html; - - return html - .replace(/\r\n/g, '') // Windows line endings - .replace(/\n/g, '') // Unix line endings - .replace(/\r/g, ''); // Old Mac line endings -}; - - - /* ============================================================ - * HEADER COLOR ENFORCEMENT - * ============================================================ */ - - const enforceHeaderColor = (html, template) => { - if (!html) return html; - - const colorMatch = template.textcode.match(/color\s*:\s*([^;"]+)/i); - if (!colorMatch) return html; - - const colorValue = colorMatch[1].trim(); - - return html.replace( - /<(h[1-4])\b([^>]*)>/gi, - (match, tag, attrs) => { - - if (/style\s*=/i.test(attrs)) { - return `<${tag}${attrs.replace( - /style\s*=\s*["']([^"']*)["']/i, - (m, styleContent) => - `style="${styleContent}; color: ${colorValue};"` - )}>`; - } - - return `<${tag}${attrs} style="color: ${colorValue};">`; - } - ); - }; - - /* ============================================================ */ - - const parseArgs = (content) => { - const args = {}; - content.replace(/--([^|]+)\|([^\s]+)/gi, (_, k, v) => { - args[k.toLowerCase()] = v.toLowerCase(); - return ''; - }); - return args; - }; - - const extractHandoutSection = ({ handout, subLink, subLinkType }) => { - return new Promise((resolve) => { - - if (!handout) return resolve(null); - - if (!subLink) { - const field = subLinkType === 'headerGM' ? 'gmnotes' : 'notes'; - handout.get(field, (content) => resolve(content || null)); - return; - } - - if (!['headerplayer', 'headergm'].includes(subLinkType?.toLowerCase())) { - return resolve(null); - } - - const field = subLinkType.toLowerCase() === 'headergm' - ? 'gmnotes' - : 'notes'; - - handout.get(field, (content) => { - if (!content) return resolve(null); - - const headerRegex = /<(h[1-4])\b[^>]*>([\s\S]*?)<\/\1>/gi; - let match; - - while ((match = headerRegex.exec(content)) !== null) { - const tagName = match[1]; - const innerHTML = match[2]; - const stripped = innerHTML.replace(/<[^>]+>/g, ''); - - if (stripped === subLink) { - const level = parseInt(tagName[1], 10); - const startIndex = match.index; - - const remainder = content.slice(headerRegex.lastIndex); - - const stopRegex = new RegExp( - `]*>`, - 'i' - ); - - const stopMatch = stopRegex.exec(remainder); - - const endIndex = stopMatch - ? headerRegex.lastIndex + stopMatch.index - : content.length; - - return resolve(content.slice(startIndex, endIndex)); - } - } - - resolve(null); - }); - }); - }; - - const transformBlockquoteMode = (html) => { - - const blockRegex = /]*>([\s\S]*?)<\/blockquote>/gi; - - let match; - let lastIndex = 0; - let playerContent = ''; - let gmContent = ''; - let found = false; - - while ((match = blockRegex.exec(html)) !== null) { - found = true; - gmContent += html.slice(lastIndex, match.index); - playerContent += match[1]; - lastIndex = blockRegex.lastIndex; - } - - gmContent += html.slice(lastIndex); - - if (!found) { - return { player: '', gm: html }; - } - - return { player: playerContent, gm: gmContent }; - }; - - on('chat:message', async (msg) => { - if (msg.type !== 'api' || !msg.content.startsWith('!pinnote')) return; - - if (typeof Supernotes_Templates === 'undefined') { - sendChat(SCRIPT_NAME, `/w gm PinNote requires Supernotes_Templates to be loaded.`); - return; - } - - const args = parseArgs(msg.content); - const isGM = isGMPlayer(msg.playerid); - - if (!msg.selected || msg.selected.length === 0) - return sendGenericError(msg, 'No pin selected.'); - - const sel = msg.selected.find(s => s._type === 'pin'); - if (!sel) - return sendGenericError(msg, 'Selected object is not a pin.'); - - const pin = getObj('pin', sel._id); - if (!pin) - return sendGenericError(msg, 'Selected pin could not be resolved.'); - - const isSynced = - !pin.get('notesDesynced') && - !pin.get('gmNotesDesynced') && - !pin.get('imageDesynced'); - - const linkType = pin.get('linkType'); - - /* ============================================================ - * LINKED HANDOUT MODE - * ============================================================ */ - - if (isSynced && linkType === 'handout') { - - const handoutId = pin.get('link'); - const subLink = pin.get('subLink'); - const subLinkType = pin.get('subLinkType'); - const autoNotesType = pin.get('autoNotesType'); - - const handout = getObj('handout', handoutId); - if (!handout) - return sendGenericError(msg, 'Linked handout not found.'); - - let extracted = await extractHandoutSection({ - handout, - subLink, - subLinkType - }); - - if (!extracted) - return sendGenericError(msg, 'Requested section not found in handout.'); - - const template = getTemplate(args.template); - if (!template) return; - - const sender = pin.get('title') || SCRIPT_NAME; - const titleText = subLink || sender; - - if (subLink) { - const headerStripRegex = /^]*>[\s\S]*?<\/h[1-4]>/i; - extracted = extracted.replace(headerStripRegex, ''); - } - - let to = (args.to || 'pc').toLowerCase(); - if (!isGM) to = 'pc'; - - let whisperPrefix = ''; - const extractingGM = subLinkType?.toLowerCase() === 'headergm'; - - let visibleContent = extracted; - let gmBlock = ''; - - if (autoNotesType === 'blockquote') { - - const transformed = transformBlockquoteMode(extracted); - - visibleContent = enforceHeaderColor(transformed.player, template); - - if (transformed.gm && to !== 'pc') { - gmBlock = - `
` + - enforceHeaderColor(transformed.gm, template) + - `
`; - } - - } else { - visibleContent = enforceHeaderColor(visibleContent, template); - } - - if (extractingGM) { - whisperPrefix = '/w gm '; - } else if (to === 'gm') { - whisperPrefix = '/w gm '; - } else if (to === 'self') { - whisperPrefix = `/w "${msg.who}" `; - } - - const html = - template.boxcode + - template.titlecode + titleText + - template.textcode + - (visibleContent || '') + - gmBlock + - '' + - template.footer + - ''; - - sendChat(sender, whisperPrefix + normalizeHTML(html)); - - return; - } - - /* ============================================================ - * CUSTOM PIN MODE - * ============================================================ */ - - if ( - !pin.get('notesDesynced') && - !pin.get('gmNotesDesynced') && - !pin.get('imageDesynced') - ) { - return sendGenericError( - msg, - 'This pin is not desynced from its linked handout.' - ); - } - - const notes = (pin.get('notes') || '').trim(); - if (!notes) - return sendGenericError(msg, 'This pin has no notes to display.'); - - let to = (args.to || 'pc').toLowerCase(); - if (!isGM) to = 'pc'; - - let whisperPrefix = ''; - if (to === 'gm') whisperPrefix = '/w gm '; - else if (to === 'self') whisperPrefix = `/w "${msg.who}" `; - - const template = getTemplate(args.template); - if (!template) return; - - const sender = pin.get('title') || SCRIPT_NAME; - - let imageBlock = ''; - const tooltipImage = pin.get('tooltipImage'); - if (tooltipImage) { - imageBlock = - ``; - } - - const coloredNotes = enforceHeaderColor(notes, template); - - let gmBlock = ''; - if (isGM && to !== 'pc' && pin.get('gmNotes')) { - gmBlock = - `
` + - enforceHeaderColor(pin.get('gmNotes'), template) + - `
`; - } - - const html = - template.boxcode + - template.titlecode + sender + - template.textcode + - imageBlock + - coloredNotes + - gmBlock + - '' + - template.footer + - ''; - - sendChat(sender, whisperPrefix + normalizeHTML(html)); - - }); - -})(); - -{try{throw new Error('');}catch(e){API_Meta.PinNote.lineCount=(parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/,'$1'),10)-API_Meta.PinNote.offset);}} diff --git a/PInNote/PinNote.js b/PInNote/PinNote.js deleted file mode 100644 index adfb37ca4..000000000 --- a/PInNote/PinNote.js +++ /dev/null @@ -1,359 +0,0 @@ -// Script: PinNote -// By: Keith Curtis -// Contact: https://app.roll20.net/users/162065/keithcurtis -var API_Meta = API_Meta||{}; //eslint-disable-line no-var -API_Meta.PinNote={offset:Number.MAX_SAFE_INTEGER,lineCount:-1}; -{try{throw new Error('');}catch(e){API_Meta.PinNote.offset=(parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/,'$1'),10)-6);}} - - -(() => { - 'use strict'; - - const version = '1.0.1'; //version number set here - log('-=> PinNote v' + version + ' is loaded.'); - //Changelog - //1.0.1 gmtext fix - //1.0.0 Debut - - - const SCRIPT_NAME = 'PinNote'; - - const isGMPlayer = (playerid) => playerIsGM(playerid); - - const getTemplate = (name) => { - if (typeof Supernotes_Templates === 'undefined') { - return null; - } - if (!name) return Supernotes_Templates.generic; - const key = name.toLowerCase(); - return Supernotes_Templates[key] || Supernotes_Templates.generic; - }; - - const sendGenericError = (msg, text) => { - if (typeof Supernotes_Templates === 'undefined') return; - - const t = Supernotes_Templates.generic; - sendChat( - SCRIPT_NAME, - t.boxcode + - t.titlecode + SCRIPT_NAME + - t.textcode + text + - '' + - t.footer + - '' - ); - }; - - -const normalizeHTML = (html) => { - if (!html) return html; - - return html - .replace(/\r\n/g, '') // Windows line endings - .replace(/\n/g, '') // Unix line endings - .replace(/\r/g, ''); // Old Mac line endings -}; - - - /* ============================================================ - * HEADER COLOR ENFORCEMENT - * ============================================================ */ - - const enforceHeaderColor = (html, template) => { - if (!html) return html; - - const colorMatch = template.textcode.match(/color\s*:\s*([^;"]+)/i); - if (!colorMatch) return html; - - const colorValue = colorMatch[1].trim(); - - return html.replace( - /<(h[1-4])\b([^>]*)>/gi, - (match, tag, attrs) => { - - if (/style\s*=/i.test(attrs)) { - return `<${tag}${attrs.replace( - /style\s*=\s*["']([^"']*)["']/i, - (m, styleContent) => - `style="${styleContent}; color: ${colorValue};"` - )}>`; - } - - return `<${tag}${attrs} style="color: ${colorValue};">`; - } - ); - }; - - /* ============================================================ */ - - const parseArgs = (content) => { - const args = {}; - content.replace(/--([^|]+)\|([^\s]+)/gi, (_, k, v) => { - args[k.toLowerCase()] = v.toLowerCase(); - return ''; - }); - return args; - }; - - const extractHandoutSection = ({ handout, subLink, subLinkType }) => { - return new Promise((resolve) => { - - if (!handout) return resolve(null); - - if (!subLink) { - const field = subLinkType === 'headerGM' ? 'gmnotes' : 'notes'; - handout.get(field, (content) => resolve(content || null)); - return; - } - - if (!['headerplayer', 'headergm'].includes(subLinkType?.toLowerCase())) { - return resolve(null); - } - - const field = subLinkType.toLowerCase() === 'headergm' - ? 'gmnotes' - : 'notes'; - - handout.get(field, (content) => { - if (!content) return resolve(null); - - const headerRegex = /<(h[1-4])\b[^>]*>([\s\S]*?)<\/\1>/gi; - let match; - - while ((match = headerRegex.exec(content)) !== null) { - const tagName = match[1]; - const innerHTML = match[2]; - const stripped = innerHTML.replace(/<[^>]+>/g, ''); - - if (stripped === subLink) { - const level = parseInt(tagName[1], 10); - const startIndex = match.index; - - const remainder = content.slice(headerRegex.lastIndex); - - const stopRegex = new RegExp( - `]*>`, - 'i' - ); - - const stopMatch = stopRegex.exec(remainder); - - const endIndex = stopMatch - ? headerRegex.lastIndex + stopMatch.index - : content.length; - - return resolve(content.slice(startIndex, endIndex)); - } - } - - resolve(null); - }); - }); - }; - - const transformBlockquoteMode = (html) => { - - const blockRegex = /]*>([\s\S]*?)<\/blockquote>/gi; - - let match; - let lastIndex = 0; - let playerContent = ''; - let gmContent = ''; - let found = false; - - while ((match = blockRegex.exec(html)) !== null) { - found = true; - gmContent += html.slice(lastIndex, match.index); - playerContent += match[1]; - lastIndex = blockRegex.lastIndex; - } - - gmContent += html.slice(lastIndex); - - if (!found) { - return { player: '', gm: html }; - } - - return { player: playerContent, gm: gmContent }; - }; - - on('chat:message', async (msg) => { - if (msg.type !== 'api' || !msg.content.startsWith('!pinnote')) return; - - if (typeof Supernotes_Templates === 'undefined') { - sendChat(SCRIPT_NAME, `/w gm PinNote requires Supernotes_Templates to be loaded.`); - return; - } - - const args = parseArgs(msg.content); - const isGM = isGMPlayer(msg.playerid); - - if (!msg.selected || msg.selected.length === 0) - return sendGenericError(msg, 'No pin selected.'); - - const sel = msg.selected.find(s => s._type === 'pin'); - if (!sel) - return sendGenericError(msg, 'Selected object is not a pin.'); - - const pin = getObj('pin', sel._id); - if (!pin) - return sendGenericError(msg, 'Selected pin could not be resolved.'); - - const isSynced = - !pin.get('notesDesynced') && - !pin.get('gmNotesDesynced') && - !pin.get('imageDesynced'); - - const linkType = pin.get('linkType'); - - /* ============================================================ - * LINKED HANDOUT MODE - * ============================================================ */ - - if (isSynced && linkType === 'handout') { - - const handoutId = pin.get('link'); - const subLink = pin.get('subLink'); - const subLinkType = pin.get('subLinkType'); - const autoNotesType = pin.get('autoNotesType'); - - const handout = getObj('handout', handoutId); - if (!handout) - return sendGenericError(msg, 'Linked handout not found.'); - - let extracted = await extractHandoutSection({ - handout, - subLink, - subLinkType - }); - - if (!extracted) - return sendGenericError(msg, 'Requested section not found in handout.'); - - const template = getTemplate(args.template); - if (!template) return; - - const sender = pin.get('title') || SCRIPT_NAME; - const titleText = subLink || sender; - - if (subLink) { - const headerStripRegex = /^]*>[\s\S]*?<\/h[1-4]>/i; - extracted = extracted.replace(headerStripRegex, ''); - } - - let to = (args.to || 'pc').toLowerCase(); - if (!isGM) to = 'pc'; - - let whisperPrefix = ''; - const extractingGM = subLinkType?.toLowerCase() === 'headergm'; - - let visibleContent = extracted; - let gmBlock = ''; - - if (autoNotesType === 'blockquote') { - - const transformed = transformBlockquoteMode(extracted); - - visibleContent = enforceHeaderColor(transformed.player, template); - - if (transformed.gm && to !== 'pc') { - gmBlock = - `
` + - enforceHeaderColor(transformed.gm, template) + - `
`; - } - - } else { - visibleContent = enforceHeaderColor(visibleContent, template); - } - - if (extractingGM) { - whisperPrefix = '/w gm '; - } else if (to === 'gm') { - whisperPrefix = '/w gm '; - } else if (to === 'self') { - whisperPrefix = `/w "${msg.who}" `; - } - - const html = - template.boxcode + - template.titlecode + titleText + - template.textcode + - (visibleContent || '') + - gmBlock + - '' + - template.footer + - ''; - - sendChat(sender, whisperPrefix + normalizeHTML(html)); - - return; - } - - /* ============================================================ - * CUSTOM PIN MODE - * ============================================================ */ - - if ( - !pin.get('notesDesynced') && - !pin.get('gmNotesDesynced') && - !pin.get('imageDesynced') - ) { - return sendGenericError( - msg, - 'This pin is not desynced from its linked handout.' - ); - } - - const notes = (pin.get('notes') || '').trim(); - if (!notes) - return sendGenericError(msg, 'This pin has no notes to display.'); - - let to = (args.to || 'pc').toLowerCase(); - if (!isGM) to = 'pc'; - - let whisperPrefix = ''; - if (to === 'gm') whisperPrefix = '/w gm '; - else if (to === 'self') whisperPrefix = `/w "${msg.who}" `; - - const template = getTemplate(args.template); - if (!template) return; - - const sender = pin.get('title') || SCRIPT_NAME; - - let imageBlock = ''; - const tooltipImage = pin.get('tooltipImage'); - if (tooltipImage) { - imageBlock = - ``; - } - - const coloredNotes = enforceHeaderColor(notes, template); - - let gmBlock = ''; - if (isGM && to !== 'pc' && pin.get('gmNotes')) { - gmBlock = - `
` + - enforceHeaderColor(pin.get('gmNotes'), template) + - `
`; - } - - const html = - template.boxcode + - template.titlecode + sender + - template.textcode + - imageBlock + - coloredNotes + - gmBlock + - '' + - template.footer + - ''; - - sendChat(sender, whisperPrefix + normalizeHTML(html)); - - }); - -})(); - -{try{throw new Error('');}catch(e){API_Meta.PinNote.lineCount=(parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/,'$1'),10)-API_Meta.PinNote.offset);}} diff --git a/PInNote/readme.md b/PInNote/readme.md deleted file mode 100644 index fe52012fa..000000000 --- a/PInNote/readme.md +++ /dev/null @@ -1,83 +0,0 @@ -# PinNote Script - -The **PinNote** script sends information from linked or custom map pins to chat using any Supernotes template. -You must have Supernotes installed. - ---- - -## Arguments - -Arguments are case-insensitive and use the format: - -``` ---key|value -``` - ---- - -### `--to|` - -Controls where the message is sent. - -#### `--to|pc` - -Sends a public message to chat. - -- GM notes are **never included**. - ---- - -#### `--to|gm` - -Whispers the message to the GM only. - -- GM notes **are included**. - ---- - -#### `--to|self` - -Whispers the message to the invoking player. - -- GM notes are included **only if the invoker is a GM**. - ---- - -If a non-GM runs the command, `--to` is ignored and treated as `pc`. - ---- - -### `--template|` (optional) - -Selects a Supernotes display template. - -- If omitted or invalid, the **generic** template is used silently. - ---- - -## Examples - -``` -!pinnote -!pinnote --to|gm -!pinnote --to|self --template|dark -!pinnote --template|wizard -``` - ---- - -## Requirements - -- Exactly **one map pin** must be selected. - - If none are selected, the script reports an error. - - If multiple are selected, only the first pin is used. - -- The pin may be: - - A **linked pin** (pulls data from its linked handout), or - - A **custom pin** (uses its Notes field). - -- A custom pin must contain notes. - - If the Notes field is empty, nothing is sent and an error is shown. - -- **Supernotes must be installed.** - - If missing, the script exits and notifies the GM. diff --git a/PInNote/script.json b/PInNote/script.json deleted file mode 100644 index 0043c26c0..000000000 --- a/PInNote/script.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "PinNote", - "script": "PinNote.js", - "version": "1.0.1", - "description": "# PinNote\n\nPinNote sends information from linked or custom map pins to chat using any Supernotes template. Supernotes must be installed for this script to function.\n\n---\n\n## Arguments\n\nArguments are case-insensitive and use the format:\n\n```\n--key|value\n```\n\n---\n\n### --to|\n\nControls where the message is sent.\n\n#### --to|pc\n\nSends a public message to chat.\n\n- GM notes are never included.\n\n---\n\n#### --to|gm\n\nWhispers the message to the GM only.\n\n- GM notes are included.\n\n---\n\n#### --to|self\n\nWhispers the message to the invoking player.\n\n- GM notes are included only if the invoker is a GM.\n\n---\n\nIf a non-GM runs the command, --to is ignored and treated as pc.\n\n---\n\n### --template| (optional)\n\nSelects a Supernotes display template.\n\n- If omitted or invalid, the generic template is used silently.\n\n---\n\n## Examples\n\n```\n!pinnote\n!pinnote --to|gm\n!pinnote --to|self --template|dark\n!pinnote --template|wizard\n```\n\n---\n\n## Requirements\n\n- Exactly one map pin must be selected.\n - If none are selected, the script reports an error.\n - If multiple are selected, only the first pin is used.\n\n- The pin may be a linked pin or a custom pin.\n - If linked to a handout, the script pulls the relevant section from the handout.\n - If custom, the script uses the Notes field of the pin.\n\n- A custom pin must contain notes.\n - If the Notes field is empty, nothing is sent and an error is shown.\n\n- Supernotes must be installed.\n - If missing, the script exits and notifies the GM.\n\n---\n\nType **!pinnote** in chat to use the script.", - "authors": "Keith Curtis", - "roll20userid": "162065", - "patreon": "https://www.patreon.com/c/KeithCurtis", - "dependencies": ["Supernotes"], - "modifies": { - "pin": "read", - "handout": "read" - }, - "conflicts": [], - "previousversions": ["1.0.0", "1.0.1"] -}