Skip to content

Commit b35fc86

Browse files
authored
Merge pull request #2199 from keithcurtis1/master
Five new scripts plus two updates
2 parents c769454 + b142734 commit b35fc86

26 files changed

Lines changed: 12312 additions & 36 deletions

AutoLinker/1.0.1/AutoLinker.js

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
// Script: AutoLinker
2+
// By: Keith Curtis and Mik Holmes
3+
// Contact: https://app.roll20.net/users/162065/keithcurtis
4+
var API_Meta = API_Meta||{};
5+
API_Meta.AutoLinker={offset:Number.MAX_SAFE_INTEGER,lineCount:-1};
6+
{try{throw new Error('');}catch(e){API_Meta.AutoLinker.offset=(parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/,'$1'),10)-6);}}
7+
8+
on("ready", () => {
9+
'use strict';
10+
11+
const version = '1.0.1';
12+
log('-=> AutoLinker v' + version + ' is loaded. Type "!autolinker --help" for examples.');
13+
//Changelog
14+
//1.0.0 Debut
15+
//1.0.1 Added support for Token Reference shorthand: [npc:name] becomes !tokenref <name>
16+
17+
let eventLockout = false;
18+
19+
const autolink = (str, obj) => {
20+
const regex = /\[(?:([^\]|]*)|([^|]*)\|([^\]|]*))\]/g;
21+
if (!str) str = "";
22+
23+
return str.replace(regex, (all, oneWord, link, text) => {
24+
25+
// =====================================================
26+
// HEADER LINK WITHOUT PIPE
27+
// [Handout#Header]
28+
// =====================================================
29+
if (oneWord && oneWord.includes("#")) {
30+
31+
if (!obj || obj.get("_type") !== "handout") return all;
32+
33+
const parts = oneWord.split("#");
34+
const handoutName = parts[0].trim();
35+
const headerText = parts[1] ? parts[1].trim() : "";
36+
if (!headerText) return all;
37+
38+
let targetID = null;
39+
40+
if (handoutName === "") {
41+
targetID = obj.get("id");
42+
} else {
43+
const found = findObjs(
44+
{ _type: "handout", name: handoutName },
45+
{ caseInsensitive: true }
46+
);
47+
if (found && found[0]) targetID = found[0].get("id");
48+
else return all;
49+
}
50+
51+
const cleanHeader = headerText.replace(/<[^>]*>/g, "");
52+
const encodedHeader = cleanHeader.replace(/ /g, "%20");
53+
const url = `http://journal.roll20.net/handout/${targetID}/#${encodedHeader}`;
54+
55+
// Display text defaults to header text
56+
return `<a href='${url}'>${cleanHeader}</a>`;
57+
}
58+
59+
60+
61+
62+
// =====================================================
63+
// SINGLE WORD MODE (namespace links)
64+
// =====================================================
65+
if (oneWord && oneWord.includes(":")) {
66+
const spell = oneWord.split(":");
67+
switch (spell[0]) {
68+
case "5e":
69+
return `<i><a href='https://roll20.net/compendium/dnd5e/${spell[1]}'>${spell[1]}</a></i>`;
70+
case "pf2":
71+
return `<i><a href='https://roll20.net/compendium/pf2/${spell[1]}'>${spell[1]}</a></i>`;
72+
case "npc":
73+
return `<a href="!tokenref ${spell.slice(1).join(":")}">${spell.slice(1).join(":")}</a>`;
74+
case "gr":
75+
return `<a href="\`/gmroll ${spell[1]}">${spell[1]}</a>`;
76+
case "r":
77+
return `<a href="\`/roll ${spell[1]}">${spell[1]}</a>`;
78+
case "sot-quote":
79+
return `<div style="${styles.sot.quote}">${spell[1]}</div>`;
80+
default:
81+
return all;
82+
}
83+
}
84+
85+
// =====================================================
86+
// PIPE MODE
87+
// =====================================================
88+
if (link && text) {
89+
90+
// HEADER LINK WITH PIPE
91+
// [Handout#Header|Text]
92+
if (obj && obj.get("_type") === "handout" && link.includes("#")) {
93+
94+
const parts = link.split("#");
95+
const handoutName = parts[0].trim();
96+
const headerText = parts[1] ? parts[1].trim() : "";
97+
if (!headerText) return all;
98+
99+
let targetID = null;
100+
101+
if (handoutName === "") {
102+
targetID = obj.get("id");
103+
} else {
104+
const found = findObjs(
105+
{ _type: "handout", name: handoutName },
106+
{ caseInsensitive: true }
107+
);
108+
if (found && found[0]) targetID = found[0].get("id");
109+
else return all;
110+
}
111+
112+
const cleanHeader = headerText.replace(/<[^>]*>/g, "");
113+
const encodedHeader = cleanHeader.replace(/ /g, "%20");
114+
const url = `http://journal.roll20.net/handout/${targetID}/#${encodedHeader}`;
115+
116+
return `<a href='${url}'>${text}</a>`;
117+
}
118+
119+
// NAMESPACE LINKS WITH PIPE
120+
if (link.includes(":")) {
121+
const spell = link.split(":");
122+
switch (spell[0]) {
123+
case "5e":
124+
return `<i><a href='https://roll20.net/compendium/dnd5e/${spell[1]}'>${text}</a></i>`;
125+
case "pf2":
126+
return `<i><a href='https://roll20.net/compendium/pf2/${spell[1]}'>${text}</a></i>`;
127+
case "npc":
128+
return `<a href="!tokenref ${spell.slice(1).join(":")}">${text}</a>`;
129+
default:
130+
return all;
131+
}
132+
}
133+
134+
// JOURNAL LINKS
135+
const targetObj = findObjs({ name: link }, { caseInsensitive: true });
136+
if (targetObj[0]) {
137+
const targetID = targetObj[0].get("id");
138+
const targetType = targetObj[0].get("type");
139+
140+
if (targetType === "handout")
141+
return `<a href='http://journal.roll20.net/handout/${targetID}'>${text}</a>`;
142+
else if (targetType === "character")
143+
return `<a href='http://journal.roll20.net/character/${targetID}'>${text}</a>`;
144+
}
145+
}
146+
147+
return all;
148+
});
149+
};
150+
151+
const runAutolink = (obj, field) => {
152+
if (!eventLockout) {
153+
eventLockout = true;
154+
155+
obj.get(field, str => {
156+
const newText = autolink(str, obj);
157+
if (newText !== str) obj.set(field, newText);
158+
eventLockout = false;
159+
});
160+
}
161+
};
162+
163+
164+
/* ============================================================
165+
* AUTOLINKER HELP
166+
* Triggered by: !autolinker --help
167+
* ============================================================ */
168+
169+
const showAutoLinkerHelp = function(playerid) {
170+
171+
let helpText =
172+
"<p><span style='font-weight:bold; font-size:24px;'>Autolinker Help</span></p>" +
173+
"<p>Some examples of the autolinker functionality. These can be used on the notes/gmnotes of any handout or character.</p>" +
174+
"<p>Please note that this script works after you save changes to a handout, " +
175+
"but the handout often reloads before the script is finished. Closing and reopening the handout, or clicking Edit again, should give it enough time to properly link things.</p>" +
176+
"<p><code>[goblin|Jimmy]</code> will make a link with the text 'Jimmy' to the 'goblin' handout.</p>" +
177+
"<p><code>[5e:fireball]</code> will link to the 5e compendium page for fireball.</p>" +
178+
"<p><code>[5e:wall of fire|the wall]</code> will make a link with the text 'the wall' to the 5e compendium page for wall of fire</p>" +
179+
"<p>Currently <code>5e:</code> and <code>pf2:</code> will link to their respective compendiums.</p>" +
180+
"<p><b>Handout Header linking:</b></p>" +
181+
"<p>To link to specific headers in a handout (handouts only) use the # character.</p>" +
182+
"<p><code>[Dungeon of Doom#6. Zombie Chorus|See Room 6]</code> will link the header '6. Zombie Chorus' in the handout 'Dungeon of Doom', with the display text 'See Room 6'.</p>" +
183+
"<p>If the link goes to a header in the same handout, you do not need to specify the handout:</p>" +
184+
"<p><code>[#6. Zombie Chorus|See Room 6]</code> will link the header '6. Zombie Chorus' in the same handout, with the display text 'See Room 6'.</p>" +
185+
"<p>If you do not need the display text of the link to be different from the text of the header, you can omit that part as well:</p>" +
186+
"<p><code>[#6. Zombie Chorus]</code> will link the header '6. Zombie Chorus' in the same handout, with the display text '6. Zombie Chorus'.</p>";
187+
188+
let styledDiv =
189+
"<div style='background-color:#bbb; padding:12px; border-radius:10px; border:2px solid #888; color:#111'>" +
190+
helpText +
191+
"</div>";
192+
193+
let player = getObj("player", playerid);
194+
if (player) {
195+
sendChat("AutoLinker", "/w \"" + player.get("_displayname") + "\" " + styledDiv);
196+
}
197+
};
198+
199+
200+
/* ============================================================
201+
* CHAT HANDLER
202+
* ============================================================ */
203+
204+
on("chat:message", function(msg) {
205+
if (msg.type !== "api") return;
206+
207+
if (msg.content.trim() === "!autolinker --help") {
208+
showAutoLinkerHelp(msg.playerid);
209+
}
210+
});
211+
212+
213+
214+
const registerEventHandlers = () => {
215+
on('change:handout:notes', obj => runAutolink(obj, "notes"));
216+
on('change:handout:gmnotes', obj => runAutolink(obj, "gmnotes"));
217+
on('change:character:bio', obj => runAutolink(obj, "bio"));
218+
on('change:character:gmnotes', obj => runAutolink(obj, "gmnotes"));
219+
};
220+
221+
registerEventHandlers();
222+
});
223+
224+
{try{throw new Error('');}catch(e){API_Meta.AutoLinker.lineCount=(parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/,'$1'),10)-API_Meta.AutoLinker.offset);}}

AutoLinker/AutoLinker.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ API_Meta.AutoLinker={offset:Number.MAX_SAFE_INTEGER,lineCount:-1};
88
on("ready", () => {
99
'use strict';
1010

11-
const version = '1.0.0';
11+
const version = '1.0.1';
1212
log('-=> AutoLinker v' + version + ' is loaded. Type "!autolinker --help" for examples.');
1313
//Changelog
1414
//1.0.0 Debut
15+
//1.0.1 Added support for Token Reference shorthand: [npc:name] becomes !tokenref <name>
1516

1617
let eventLockout = false;
1718

@@ -55,6 +56,9 @@ const autolink = (str, obj) => {
5556
return `<a href='${url}'>${cleanHeader}</a>`;
5657
}
5758

59+
60+
61+
5862
// =====================================================
5963
// SINGLE WORD MODE (namespace links)
6064
// =====================================================
@@ -65,6 +69,8 @@ const autolink = (str, obj) => {
6569
return `<i><a href='https://roll20.net/compendium/dnd5e/${spell[1]}'>${spell[1]}</a></i>`;
6670
case "pf2":
6771
return `<i><a href='https://roll20.net/compendium/pf2/${spell[1]}'>${spell[1]}</a></i>`;
72+
case "npc":
73+
return `<a href="!tokenref ${spell.slice(1).join(":")}">${spell.slice(1).join(":")}</a>`;
6874
case "gr":
6975
return `<a href="\`/gmroll ${spell[1]}">${spell[1]}</a>`;
7076
case "r":
@@ -118,7 +124,9 @@ const autolink = (str, obj) => {
118124
return `<i><a href='https://roll20.net/compendium/dnd5e/${spell[1]}'>${text}</a></i>`;
119125
case "pf2":
120126
return `<i><a href='https://roll20.net/compendium/pf2/${spell[1]}'>${text}</a></i>`;
121-
default:
127+
case "npc":
128+
return `<a href="!tokenref ${spell.slice(1).join(":")}">${text}</a>`;
129+
default:
122130
return all;
123131
}
124132
}

AutoLinker/script.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "AutoLinker",
33
"script": "AutoLinker.js",
4-
"version": "1.0.0",
4+
"version": "1.0.1",
55
"description": "# Autolinker\n\n## Purpose\n\nAutolinker converts bracketed shorthand written in the Notes or GMNotes fields of handouts and characters into clickable Roll20 journal or compendium links when the entry is saved. This extends the basic linking functions built into Roll20.\n\n## General Usage\n\nThese formats may be used in the Notes or GMNotes fields of any handout or character.\n\nNote: The script runs after a save event. Because the handout may refresh before processing finishes, you may need to close and reopen the handout (or click Edit again) to see the updated links.\n\n## Journal Links\n\n[goblin|Jimmy]\n\nCreates a link to the handout or character named goblin, displayed as Jimmy.\n\nIf no display text is provided, standard Roll20 journal linking rules apply.\n\n## Compendium Links\n\n[5e:fireball]\n\nLinks to the D&D 5e compendium entry for fireball.\n\n[5e:wall of fire|the wall]\n\nLinks to the D&D 5e compendium entry for wall of fire, displayed as the wall.\n\n### Supported Compendium Prefixes\n\n- 5e: — D&D 5th Edition\n- pf2: — Pathfinder 2nd Edition\n\n## Handout Header Linking\n\nHeader links apply to handouts only and use the # character.\n\n### Link to a Header in Another Handout\n\n[Dungeon of Doom#6. Zombie Chorus|See Room 6]\n\nLinks to the header 6. Zombie Chorus in the handout Dungeon of Doom, displayed as See Room 6.\n\n### Link to a Header in the Same Handout\n\n[#6. Zombie Chorus|See Room 6]\n\nLinks to the header 6. Zombie Chorus in the current handout, displayed as See Room 6.\n\n### Omit Display Text\n\n[#6. Zombie Chorus]\n\nIf no display text is supplied, the header text is used as the link text.",
66
"authors": "Keith Curtis",
77
"roll20userid": "162065",
@@ -12,6 +12,6 @@
1212
},
1313
"conflicts": [],
1414
"previousversions": [
15-
"1.0.0"
15+
"1.0.0", "1.0.1"
1616
]
1717
}

0 commit comments

Comments
 (0)