From b8b223fe2efc64ee231aae0b31627e1d14310394 Mon Sep 17 00:00:00 2001 From: Dan Conley Date: Wed, 2 Sep 2020 09:55:37 -0400 Subject: [PATCH] initial commit --- module.json | 14 +++++ scripts/index.js | 28 +++++++++ scripts/journallink.js | 126 +++++++++++++++++++++++++++++++++++++++++ scripts/settings.js | 5 ++ styles.css | 0 5 files changed, 173 insertions(+) create mode 100644 module.json create mode 100644 scripts/index.js create mode 100644 scripts/journallink.js create mode 100644 scripts/settings.js create mode 100644 styles.css diff --git a/module.json b/module.json new file mode 100644 index 0000000..6a27d6b --- /dev/null +++ b/module.json @@ -0,0 +1,14 @@ +{ + "name": "journal-links", + "title": "Journal Links", + "description": "See relationships between journal entries", + "version": "0.1.0", + "author": "dconley", + "esmodules": ["./scripts/index.js"], + "styles": ["./styles.css"], + "packs": [], + "minimumCoreVersion": "0.6.6", + "compatibleCoreVersion": "0.6.6", + "manifest": "https://raw.githubusercontent.com/moo-man/FVTT-DD-Import/master/module.json", + "download": "https://github.com/moo-man/FVTT-DD-Import/archive/master.zip" +} diff --git a/scripts/index.js b/scripts/index.js new file mode 100644 index 0000000..8b579b1 --- /dev/null +++ b/scripts/index.js @@ -0,0 +1,28 @@ +import { JournalLink } from './journallink.js'; +//import { JournalLinkSettings } from './settings.js'; + +const MODULE_NAME = 'journal-link'; +const NAME = 'Journal Link'; +const SETTINGS_NAME = 'journalLinkSettings'; + +Hooks.on("init", () => { + console.log('journal-link | initializing'); + let modulename = MODULE_NAME; + game.settings.register(MODULE_NAME, 'test-setting', { + name : 'Rebuild links on journal save', + scope: 'world', + config: true, + type: Boolean, + default: true + }); + + game.JournalLink = new JournalLink(); +}); + +Hooks.on('updateJournalEntry', (args) => game.JournalLink && game.JournalLink.updateJournalEntry(args)); +Hooks.on('renderJournalSheet', (sheet, html, data) => game.JournalLink && game.JournalLink.includeLinks(sheet, html, data)); +// updateItem +// updateActor +// roll tables have no context of this stuff +// renderActorSheet (is that generic enough?) +// renderItemSheet diff --git a/scripts/journallink.js b/scripts/journallink.js new file mode 100644 index 0000000..6d651fb --- /dev/null +++ b/scripts/journallink.js @@ -0,0 +1,126 @@ +export class JournalLink { + re = /@(\w+)\[(\w+)\]/g; + + entityMap = { + 'JournalEntry': 'journal', + 'Actor': 'actors', + 'Item': 'items', + 'RollTable': 'tables' + }; + + updateJournalEntry({ data }) { + this.update(data, 'JournalEntry'); + } + + // TODO is the lack of async/await here going to bite me? + update(data, entityType) { + console.log('journal-links | updating ' + entityType + ' ' + data.name); + + let references = this.references(data.content); + let existing = (data.flags['journal-links'] && data.flags['journal-links']['references']) || {}; + + let updated = {}; + + for (let reference of references) { + if (!updated[reference.type]) + updated[reference.type] = []; + // if we've linked something multiple times in this entity + if (updated[reference.type].includes(reference.id)) + continue + updated[reference.type].push(reference.id); + + let existingOfType = existing[reference.type] || []; + if (existingOfType.includes(reference.id)) + continue; + + let referenced = game[this.entityMap[reference.type]].get(reference.id); + let links = referenced.getFlag('journal-links', 'referencedBy') || {}; + let linksOfType = links[entityType] || []; + linksOfType.push(data._id); + + links[entityType] = linksOfType; + referenced.setFlag('journal-links', 'referencedBy', links); + } + + for (const [type, values] of Object.entries(existing)) { + let current = updated[type]; + for (let outdated of values.filter(v => !current.includes(v))) { + let entity = game[this.entityMap[type]].get(outdated); + + let links = entity.getFlag('journal-links', 'referencedBy'); + let linksOfType = links[type]; + linksOfType.splice(linksOfType.indexOf(data._id), 1); + + if (linksOfType.length) + links[type] = linksOfType; + else + delete links[type]; + entity.setFlag('journal-links', 'referencedBy', links); + } + }; + + game[this.entityMap[entityType]].get(data._id).setFlag('journal-links', 'references', updated); + } + + includeLinks(sheet, html, data) { + let links = data.entity.flags && data.entity.flags['journal-links'] && data.entity.flags['journal-links']['referencedBy'] || {}; + if (Object.keys(links).length === 0) + return; + + console.log('journal-links | appending links to ' + data.entity.name); + let element = html.find(".editor-content"); + if (element.length === 0) + return; + + let linksDiv = $(''); + linksDiv.append($('

Linked from

')); + for (const [type, values] of Object.entries(links)) { + if (values.length === 0) + continue; + + linksDiv.append($('

' + type + '

')); + let linksList = $(''); + for (let value of values) { + let entity = game[this.entityMap[type]].get(value); + let link = $(''); + link.attr('data-entity', type); + link.attr('data-id', entity._id); + + let icon = 'fas '; + switch (type) { + case 'JournalEntry': + icon += 'fa-book-open'; + break; + case 'Actor': + icon += 'fa-user'; + break; + case 'Item': + icon += 'fa-suitcase'; + break; + case 'RollTable': + icon == 'fa-th-list'; + break; + } + link.append($('')); + link.append(' ' + entity.name); + + let li = $('
  • '); + li.append(link); + linksList.append(li); + } + linksDiv.append(linksList); + } + element.append(linksDiv); + } + + references(text) { + return Array.from(text.matchAll(this.re)).map( + m => { + return { + type: m[1], + id: m[2] + } + } + ); + } +} diff --git a/scripts/settings.js b/scripts/settings.js new file mode 100644 index 0000000..decffda --- /dev/null +++ b/scripts/settings.js @@ -0,0 +1,5 @@ +export class JournalSyncSettings extends FormApplication { + static init() { + game.settings.registerMenu('journal-sync', ' + } +} diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..e69de29