Exposing attributes and other entity information
entityData
With Kanka 2.4, a partial JavaScript dump was added for entity attributes and a handful of other information. As of 2.7, this object now includes most of the entity’s information relevant to a character sheet, with the notable exception of abilities. You can access it via the entityData
object, which contains the following fields (further detailed on the official Helper page):
{
"name": "...",
"is_private": true|false,
"type": { ... },
"attributes": { ... },
"tags": [ ... ],
"type_field": "...",
"is_dead": true|false, // Characters and Creatures only
"is_extinct": true|false, // Races and Creatures only
"is_completed": true|false, // Quests only
"is_defunct": true|false, // Organizations only
"is_destroyed": true|false, // Locations only
// The rest are only included for Character entities:
"gender": "...",
"pronouns": "...",
"title": "...",
"age": "...",
"traits": [ ... ], // and appearances
"races": [ ... ],
"families": [ ... ],
"location": { ... }
}
Note that the attribute names used by entityData
are "sluggified" and don’t match their Blade counterparts when it comes to capitalization, which can make interactions between the two languages difficult. On the other hand, it makes it possible to interact with attributes whose name is invalid in Blade, for example those that contain special characters, as long as you know their corresponding slug.
Axios API
Kanka 2.7 introduced tools to access the API via axios
methods that interact with an entity’s attributes and abilities. This allows us to generate a far more thorough attribute dump (including for example attribute types and pinned status) with names that match those used in Blade, as well as a detailed dump of ability information.
However, because axios is only initialized after the page is fully loaded, unlike our character sheet scripts, window.onload
is necessary to defer execution until axios is ready. Any processing that relies on the attribute/ability dumps must therefore also be deferred. Since you have to wait until the page finishes loading to make the API request, then wait for the data to be returned by the API, this introduces a significant delay to your data processing. If you try building an empty layout in Blade and filling it with content using axios-provided data in JavaScript, you will see that it can take more than a second before your layout gets populated, resulting in a poor user experience.
Therefore, it is advisable to do any time-sensitive initial processing and content-building with entityData
or Blade – though again, since attribute names differ between entityData
and the API, you have to be vigilant about your references, and cross-references between the two are more or less impossible unless you are targeting specific attributes for which you know the exact form of both slugs (forget running a forEach
in one using attributes passed by the other!).
All of that said, here is some boilerplate that will fetch attributes and abilities from the API, format the former to match Blade variable names, and either run any API-dependent code or log an error if something goes wrong.
function dumpAttributes (resp, dump = {}) {
// Sanitize attribute names for PHP, removing [range] if applicable, to match Blade variables
resp.forEach(obj => {
dump[obj.name.replace(/\[range:.*\]|[^a-zA-Z0-9_\x80-\xff]/g, "")] = obj;
});
return dump;
}
window.onload = (event) => {
// Collect attributes from the API
axios.get(attributeApis.all.url).then(res => {
window.attributes = dumpAttributes(res.data.data);
console.table(attributes); // optional, useful for debugging
/* Call any functions that require the dump here */
}).catch(err => {
console.error("Failed to create attribute dump");
});
// Collect abilities from the API
axios.get(abilityApis.all.url).then(res => {
window.abilities = res.data.data;
console.table(abilities); // optional, useful for debugging
/* Call any functions that require the dump here */
}).catch(err => {
console.error("Failed to create ability dump");
});
};
Each attribute value can then be accessed with a simple attributes.slug.value
, where slug
matches the attribute’s corresponding Blade variable:
window.alert("Welcome to " + attributes.CharacterName.value + "’s character sheet!");
Remember: entityData
attributes are all lower-case and can be accessed directly. API attributes are case-sensitive and have several properties, so you need to specify .value
to get the value. entityData.attributes.charactername
= attributes.CharacterName.value
.
Several other details are available via attributes.slug
, beyond what is accessible in Blade; for example:
{
"id": 6640309,
"name": "Multiclass Domain[range:Arcana,Blade,Bone,Codex,Grace,Midnight,Sage,Splendor,Valor]", // unparsed string
"name_raw": "Multiclass Domain[range:Arcana,Blade,Bone,Codex,Grace,Midnight,Sage,Splendor,Valor]", // unparsed string (no difference as far as I can tell)
"value": '3 <i class="ra ra-aura" aria-hidden="true"> <iframe src="https://app.kanka.io/w/1/entities/1/attributes-dashboard" data-entity-tags="id-1 dicemacro" data-entity-type="note" class="entity-attributes-render w-full h-full"></iframe>', // parsed entity/attribute mentions and icons (e.g. [icon:ra ra-aura]) - ** EMPTY = "" **
"value_raw": "{Number} [icon:ra ra-aura] [note:1|field:attributes]", // unparsed string - ** EMPTY = null **
"type_id": 1,
"is_section": false,
"is_number": false,
"is_multiline": false,
"is_checkbox": false,
"is_random": false,
"is_private": false,
"is_pinned": true,
"is_hidden": false,
"apis": {
"update": {
"method": "POST",
"url": "https://app.kanka.io/w/1/entities/2/attributes/live-api/6640309"
},
"delete": {
"method": "POST",
"url": "https://app.kanka.io/w/1/entities/2/attributes/live-api/6640309/delete"
}
}
}
Most notably, the apis
object provides the necessary URLs to implement our own live-editing for attributes.
We also get a ton of information about abilities, grouped by their parent field like in the Abilities page of the entity; for example:
{
"groups": [
{
"id": 0,
"name": "Unorganised",
"type": "Abilities are grouped by their parent field, and fallback to being here.",
"abilities": [
{
"id": 174521,
"ability_id": 17663,
"name": "Hypercognition",
"entry": "<p>HYPERCOGNITION</p><p><b>Level:</b> 6 Divination<br><b>Casting Time:</b> 1 half action<br><b>Distance:</b> Personal<br><b>Duration:</b> 1 hour per Casting Level<br><b>Effect:</b> You gain a +6 magic bonus with Search and Sense Motive checks. As a half action, you may end this spell early to gain a +12 magic bonus with a single Investigate check.<br></p>",
"type": "Spell",
"charges": null,
"used_charges": null,
"class": [
" kanka-tag-73914",
" kanka-tag-wip"
],
"note": "",
"tags": [
{
"id": 73914,
"name": "WIP",
"url": "https://app.kanka.io/w/1/entities/2"
}
],
"visibility_id": 1,
"visibility": "Unknown",
"created_by": 22039,
"attributes": [
{
"id": 1175745,
"name": "Level",
"value": "6 Divination",
"type": ""
},
{
"id": 1175746,
"name": "Casting Time",
"value": "1 half action",
"type": ""
}
],
"images": {
"has": false,
"thumb": "",
"url": ""
}
}
]
}
],
"meta": {
"add_url": "https://app.kanka.io/w/1/entities/2/entity_abilities/create",
"user_id": 10101,
"is_admin": true
}
}
Last updated
Was this helpful?