JavaScript reference
R, the object every site script can read, and the events it fires as the page changes.
Every Rolea site puts an object called R on the page. It tells your script who is reading, which of their characters they are playing as, and what page they are on. It also tells you when any of that changes.
Everything in R is already on the page by the time your script runs. You do not have to request any of it, and you do not have to wait for it.
What R gives you
| Property | What it is |
|---|---|
R.member | The person reading, or null if nobody is signed in. |
R.characters | All of their characters. An empty list for a visitor. |
R.character | The one they are currently playing as, or null if they have not picked one. |
R.site | The site itself: its name, language and look. |
R.page | Which page is on screen, and what it is about. |
R.version | Which version of this contract the site supports. See Versions. |
R is also available as Rolea, in case something else on the page has already claimed such a short name.
The member
R.member = {
login: "nocturna", // the name they signed up with
role: "administrator", // "member" | "moderator" | "administrator" | "founder"
avatarUrl: "https://…" // or null
}Characters
R.characters is a list, and R.character is whichever of them is selected right now. Both hold the same shape:
R.character = {
id: "6f2a…",
slug: "aldric-vane", // the name in the address bar
name: "Aldric Vane",
state: "active", // "active" | "disabled" | "pending" | "draft"
avatarUrl: "https://…", // or null
faction: { // or null, if the character has no faction
key: "b5c1…", // its identifier, unchanged by a rename
slug: "lobos-de-invierno",// its name, ready for a selector
name: "Lobos de Invierno",
color: "#8a3b2e"
}
}The site
R.site = {
name: "Valdrath",
locale: "es", // "es" | "en"
layout: "modern", // "modern" | "classic"
colorScheme: "auto", // "light" | "dark" | "auto"
skin: { slug: "ravenwood", version: "1.2.0" } // or null
}The page
R.page gives the page a name of its own, so a script that switches on it keeps working even if the address changes:
R.page = {
route: "scene", // the name of this kind of page
path: "/scenes/la-taberna", // the address, without the query string
params: { slug: "la-taberna" }
}Every page has a route. These are the ones you are most likely to want:
route | The page |
|---|---|
home | The front page |
scenes / scene | The scene list / one scene |
characters / character-profile | The character list / one character |
character-dossier | A character’s profile sheet |
forum, forum-section, forum-thread | The forum, a section, a thread |
place | A place |
market, messages, calendar | Market, private messages, calendar |
member-profile | Someone’s member profile |
administration / moderation | The administration and moderation panels |
A page with something worth knowing adds one field of its own. The scene view adds scene:
R.page = {
route: "scene",
path: "/scenes/la-taberna",
params: { slug: "la-taberna" },
scene: {
id: "1c9e…",
slug: "la-taberna",
title: "La taberna del Ancla Rota",
openness: "public", // "public" | "private" | "restricted"
isFinished: false,
place: { slug: "puerto-viejo", name: "Puerto Viejo" },
ownerCharacter: { slug: "aldric-vane", name: "Aldric Vane" },
participantCount: 4,
wordCount: 12840
}
}R.page.scene is only there on the scene view, so check for it before you use it. More pages will carry their own over time; one that carries nothing extra still gives you route, path and params.
Reacting to changes
A Rolea site does not reload as you move around it, so the page-load event fires once and never again. R.on() is how you hear about everything after that.
R.on("page", function (page) {
console.log("now reading", page.route);
});There are three things to listen for:
| Event | Fires when | Your handler gets |
|---|---|---|
"page" | The reader moves to another page | The new R.page |
"character" | They switch character, or clear their choice | The new R.character, or null |
"member" | They sign in or out | The new R.member, or null |
R.on() hands back a function that stops the listening, if you ever need to:
var stop = R.on("character", paintBanner);
// later
stop();Styling without any JavaScript
Before writing a script, check whether CSS already covers it. The site keeps the same context on the <html> element, so a stylesheet can respond to it on its own:
html[data-route="scene"] .skeleton__main { max-width: 70ch }
html[data-auth="guest"] .sidebar__compose { display: none }
html[data-faction="lobos-de-invierno"] { --color-brand-primary: #8a3b2e }The full list is in the CSS reference. Where CSS can do the job, prefer it — it applies before the page is painted.
Recipes
Greet the reader by their character
R.on("character", function (character) {
var slot = document.querySelector(".skeleton__topHeader");
if (!slot) return;
slot.setAttribute(
"data-greeting",
character ? "Bienvenido, " + character.name : ""
);
});Mark finished scenes
R.on("page", function (page) {
document.body.classList.toggle(
"is-finished-scene",
page.route === "scene" && page.scene !== undefined && page.scene.isFinished
);
});Something only staff see
var role = R.member ? R.member.role : null;
if (role === "administrator" || role === "founder") {
document.body.classList.add("has-staff-tools");
}Good practices
- Never request what R already has. The member, their characters and the current page are on the page for free. Fetching them again slows down every visit.
- Write handlers that can run more than once. A
pagehandler may run again for the same page as the rest of its data settles. Setting a class, an attribute or a piece of text is safe to repeat. Appending an element is not, so check whether you already added it. - Read from R when you need it, not once at the top.
R.memberand friends always give you the current value. A copy saved on load goes stale as soon as the reader switches character. - Treat what you get as read-only. The objects R hands you cannot be modified. Copy one before changing anything.
- Always allow for the empty case.
R.member,R.characterand a character’sfactioncan all benull. Most visitors to a public site are signed out. - Do not count on an element being there. Block class names are stable, but what is inside them moves. Guard every
querySelector, and add to what is already on the page instead of rebuilding it. - Leave the panels alone. Nothing you write should touch the administration or moderation pages.
R.page.routetells you when you are on one, and a script that hides part of a panel can lock an administrator out of their own site. - Keep it small. Your file loads on every visit. Header scripts run before the page is drawn, so put anything that is not urgent at the end of the page instead.
- Test with the site switched back to plain. Adding
?noskin=1to any address loads it with no theme, no custom CSS and no custom JavaScript. Use it to tell your own work apart from the site’s, and to get back in if something you wrote goes wrong.
Versions
R.version is the version of this contract the site implements. New fields and new events are added without changing it, so a script written today keeps working. The number only changes if something is ever renamed or removed, and that is announced before it happens.