Add dev tools

2.0
Matheus Clemente 2023-11-09 14:14:29 -03:00
parent 0c2add7454
commit 6fb2a40216
26 changed files with 12372 additions and 155 deletions

13
.editorconfig 100644
View File

@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: 2022 Johannes Loher
#
# SPDX-License-Identifier: MIT
root = true
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true

5
.eslintignore 100644
View File

@ -0,0 +1,5 @@
# SPDX-FileCopyrightText: 2022 Johannes Loher
#
# SPDX-License-Identifier: MIT
dist

181
.eslintrc.cjs 100644
View File

@ -0,0 +1,181 @@
// SPDX-FileCopyrightText: 2022 Johannes Loher
// SPDX-FileCopyrightText: 2022 David Archibald
//
// SPDX-License-Identifier: MIT
module.exports = {
parserOptions: {
ecmaVersion: 13,
extraFileExtensions: [".cjs", ".mjs"],
sourceType: "module",
},
env: {
browser: true,
es6: true,
jquery: true,
},
extends: ["eslint:recommended", "@typhonjs-fvtt/eslint-config-foundry.js/0.8.0"],
plugins: [],
rules: {
"array-bracket-spacing": ["warn", "never"],
"array-callback-return": "warn",
"arrow-spacing": "warn",
"comma-dangle": ["warn", "only-multiline"],
"comma-style": "warn",
"computed-property-spacing": "warn",
"constructor-super": "error",
"default-param-last": "warn",
"dot-location": ["warn", "property"],
"eol-last": ["error", "always"],
eqeqeq: ["warn", "smart"],
"func-call-spacing": "warn",
"func-names": ["warn", "never"],
"getter-return": "warn",
"lines-between-class-members": "warn",
"new-parens": ["warn", "always"],
"no-alert": "warn",
"no-array-constructor": "warn",
"no-class-assign": "warn",
"no-compare-neg-zero": "warn",
"no-cond-assign": "warn",
"no-const-assign": "error",
"no-constant-condition": "warn",
"no-constructor-return": "warn",
"no-delete-var": "warn",
"no-dupe-args": "warn",
"no-dupe-class-members": "warn",
"no-dupe-keys": "warn",
"no-duplicate-case": "warn",
"no-duplicate-imports": ["warn", { includeExports: true }],
"no-empty": ["warn", { allowEmptyCatch: true }],
"no-empty-character-class": "warn",
"no-empty-pattern": "warn",
"no-func-assign": "warn",
"no-global-assign": "warn",
"no-implicit-coercion": ["warn", { allow: ["!!"] }],
"no-implied-eval": "warn",
"no-import-assign": "warn",
"no-invalid-regexp": "warn",
"no-irregular-whitespace": "warn",
"no-iterator": "warn",
"no-lone-blocks": "warn",
"no-lonely-if": "warn",
"no-misleading-character-class": "warn",
"no-mixed-operators": "warn",
"no-multi-str": "warn",
"no-multiple-empty-lines": ["warn", { max: 1 }],
"no-new-func": "warn",
"no-new-object": "warn",
"no-new-symbol": "warn",
"no-new-wrappers": "warn",
"no-nonoctal-decimal-escape": "warn",
"no-obj-calls": "warn",
"no-octal": "warn",
"no-octal-escape": "warn",
"no-promise-executor-return": "warn",
"no-proto": "warn",
"no-regex-spaces": "warn",
"no-script-url": "warn",
"no-self-assign": "warn",
"no-self-compare": "warn",
"no-setter-return": "warn",
"no-sequences": "warn",
"no-template-curly-in-string": "warn",
"no-this-before-super": "error",
"no-unexpected-multiline": "warn",
"no-unmodified-loop-condition": "warn",
"no-unneeded-ternary": "warn",
"no-unreachable": "warn",
"no-unreachable-loop": "warn",
"no-unsafe-negation": ["warn", { enforceForOrderingRelations: true }],
"no-unsafe-optional-chaining": ["warn", { disallowArithmeticOperators: true }],
"no-unused-expressions": "warn",
"no-useless-backreference": "warn",
"no-useless-call": "warn",
"no-useless-catch": "warn",
"no-useless-computed-key": ["warn", { enforceForClassMembers: true }],
"no-useless-concat": "warn",
"no-useless-constructor": "warn",
"no-useless-rename": "warn",
"no-useless-return": "warn",
"no-var": "warn",
"no-void": "warn",
"no-whitespace-before-property": "warn",
"prefer-numeric-literals": "warn",
"prefer-object-spread": "warn",
"prefer-regex-literals": "warn",
"prefer-spread": "warn",
"rest-spread-spacing": ["warn", "never"],
"semi-spacing": "warn",
"semi-style": ["warn", "last"],
"space-unary-ops": ["warn", { words: true, nonwords: false }],
"switch-colon-spacing": "warn",
"symbol-description": "warn",
"template-curly-spacing": ["warn", "never"],
"unicode-bom": ["warn", "never"],
"use-isnan": ["warn", { enforceForSwitchCase: true, enforceForIndexOf: true }],
"valid-typeof": ["warn", { requireStringLiterals: true }],
"wrap-iife": ["warn", "inside"],
"arrow-parens": ["warn", "always"],
"comma-spacing": "warn",
"dot-notation": "warn",
"key-spacing": "warn",
"keyword-spacing": ["warn", { overrides: { catch: { before: true, after: false } } }],
"max-len": [
"warn",
{
code: 120,
ignoreComments: true,
ignoreTrailingComments: true,
ignoreUrls: true,
ignoreStrings: true,
ignoreTemplateLiterals: true,
ignoreRegExpLiterals: true,
},
],
"no-extra-boolean-cast": ["warn", { enforceForLogicalOperands: true }],
"no-extra-semi": "warn",
"no-multi-spaces": ["warn", { ignoreEOLComments: true }],
"no-throw-literal": "error",
"no-trailing-spaces": "warn",
"no-useless-escape": "warn",
"no-unused-vars": ["warn", { args: "none" }],
"nonblock-statement-body-position": ["warn", "beside"],
"one-var": ["warn", "never"],
"operator-linebreak": [
"warn",
"before",
{
overrides: { "=": "after", "+=": "after", "-=": "after" },
},
],
"prefer-template": "warn",
"quote-props": ["warn", "as-needed", { keywords: false }],
quotes: ["warn", "double", { avoidEscape: true, allowTemplateLiterals: false }],
semi: "warn",
"space-before-blocks": ["warn", "always"],
"space-before-function-paren": [
"warn",
{
anonymous: "always",
named: "never",
asyncArrow: "always",
},
],
"spaced-comment": "warn",
},
overrides: [
{
files: ["./*.js", "./*.cjs", "./*.mjs"],
env: {
node: true,
},
},
],
};

65
.github/workflows/checks.yml vendored 100644
View File

@ -0,0 +1,65 @@
# SPDX-FileCopyrightText: 2022 Johannes Loher
#
# SPDX-License-Identifier: MIT
name: Checks
on:
- push
- pull_request
env:
node_version: 16
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install node
uses: actions/setup-node@v2
with:
node-version: ${{ env.node_version }}
- name: Cache Node.js modules
uses: actions/cache@v2
with:
path: .npm
key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.OS }}-node-
${{ runner.OS }}-
- name: Install dependencies
run: npm ci --cache .npm --prefer-offline
- name: Lint
run: npm run lint
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install node
uses: actions/setup-node@v2
with:
node-version: ${{ env.node_version }}
- name: Cache Node.js modules
uses: actions/cache@v2
with:
path: .npm
key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.OS }}-node-
${{ runner.OS }}-
- name: Install dependencies
run: npm ci --cache .npm --prefer-offline
- name: Build
run: npm run build

116
.github/workflows/release.yml vendored 100644
View File

@ -0,0 +1,116 @@
# SPDX-FileCopyrightText: 2022 Johannes Loher
#
# SPDX-License-Identifier: MIT
name: Release
on:
release:
types: [published]
env:
package_type: module
node_version: 16
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install node
uses: actions/setup-node@v2
with:
node-version: ${{ env.node_version }}
- name: Cache Node.js modules
uses: actions/cache@v2
with:
path: .npm
key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.OS }}-node-
${{ runner.OS }}-
- name: Install dependencies
run: npm ci --cache .npm --prefer-offline
- name: Lint
run: npm run lint
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install node
uses: actions/setup-node@v2
with:
node-version: ${{ env.node_version }}
- name: Cache Node.js modules
uses: actions/cache@v2
with:
path: .npm
key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.OS }}-node-
${{ runner.OS }}-
- name: Install dependencies
run: npm ci --cache .npm --prefer-offline
- name: Extract tag version number
id: get_version
uses: battila7/get-version-action@v2
- name: Substitute Manifest and Download Links For Versioned Ones
id: sub_manifest_link_version
uses: microsoft/variable-substitution@v1
with:
files: 'src/${{ env.package_type }}.json'
env:
version: ${{ steps.get_version.outputs.version-without-v }}
url: https://github.com/${{ github.repository }}
manifest: https://github.com/${{ github.repository }}/releases/latest/download/${{ env.package_type }}.json
download: https://github.com/${{ github.repository }}/releases/download/${{ github.event.release.tag_name }}/${{ env.package_type }}.zip
- name: Build
run: npm run build
- name: Archive production artifacts
uses: actions/upload-artifact@v2
with:
name: dist
path: dist
publish:
needs:
- lint
- build
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Download production artifacts for publication
uses: actions/download-artifact@v2
with:
name: dist
path: dist
- name: Create zip file
working-directory: ./dist
run: zip -r ../${{ env.package_type }}.zip .
- name: Create release
id: create_version_release
uses: ncipollo/release-action@v1
with:
allowUpdates: true
name: ${{ github.event.release.name }}
token: ${{ secrets.GITHUB_TOKEN }}
artifacts: './dist/${{ env.package_type }}.json, ./${{ env.package_type }}.zip'
body: ${{ github.event.release.body }}

32
.gitignore vendored 100644
View File

@ -0,0 +1,32 @@
# SPDX-FileCopyrightText: 2022 Johannes Loher
#
# SPDX-License-Identifier: MIT
# IDE
.idea/
.vs/
# Node Modules
node_modules/
npm-debug.log
# yarn2
.yarn/*
!.yarn/patches
!.yarn/releases
!.yarn/plugins
!.yarn/sdks
!.yarn/versions
.pnp.*
# Local configuration
foundryconfig.json
# Distribution files
dist
# ESLint
.eslintcache
# Junit results
junit.xml

5
.gulp.json 100644
View File

@ -0,0 +1,5 @@
{
"flags": {
"gulpfile": "gulpfile.mjs"
}
}

View File

@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged

1
.nvmrc 100644
View File

@ -0,0 +1 @@
lts/*

8
.prettierignore 100644
View File

@ -0,0 +1,8 @@
# SPDX-FileCopyrightText: 2022 Johannes Loher
#
# SPDX-License-Identifier: MIT
dist
package-lock.json
.pnp.cjs
.pnp.js

9
.prettierrc.cjs 100644
View File

@ -0,0 +1,9 @@
// SPDX-FileCopyrightText: 2022 Johannes Loher
//
// SPDX-License-Identifier: MIT
module.exports = {
printWidth: 120,
tabWidth: 4,
useTabs: true,
};

65
CONTRIBUTING.md 100644
View File

@ -0,0 +1,65 @@
## Development
### Prerequisites
In order to build this module, recent versions of `node` and `npm` are
required. Most likely, using `yarn` also works, but only `npm` is officially
supported. We recommend using the latest lts version of `node`. If you use `nvm`
to manage your `node` versions, you can simply run
```
nvm install
```
in the project's root directory.
You also need to install the project's dependencies. To do so, run
```
npm install
```
### Building
You can build the project by running
```
npm run build
```
Alternatively, you can run
```
npm run build:watch
```
to watch for changes and automatically build as necessary.
### Linking the built project to Foundry VTT
In order to provide a fluent development experience, it is recommended to link
the built module to your local Foundry VTT installation's data folder. In
order to do so, first add a file called `foundryconfig.json` to the project root
with the following content:
```
{
"dataPath": ["/absolute/path/to/your/FoundryVTT"]
}
```
(if you are using Windows, make sure to use `\` as a path separator instead of
`/`)
Then run
```
npm run link-project
```
On Windows, creating symlinks requires administrator privileges, so
unfortunately you need to run the above command in an administrator terminal for
it to work.
You can also link to multiple data folders by specifying multiple paths in the
`dataPath` array.

View File

@ -4,16 +4,6 @@ Tired of scrolling compendia? Easily browse and filter for spells, feats, items,
Compendium Browser is faster and better-behaved; **it no longer loads all the compendia into memory on start-up** (which sometimes hung servers because of memory or CPU requirements). Instead, it filters and loads on-demand, as well as giving you a Module Setting to control how many rows are loaded at a time. Compendium Browser is faster and better-behaved; **it no longer loads all the compendia into memory on start-up** (which sometimes hung servers because of memory or CPU requirements). Instead, it filters and loads on-demand, as well as giving you a Module Setting to control how many rows are loaded at a time.
## Summary
- **Authors**: Discord: Spetzel#0103; Felix (felix.mueller.86@web.de); ZoltantheDM (Zoltan#8700); eduardopato41
- **Version**: 0.9.0
- **Foundry VTT Compatibility**: 9-10
- **System Compatibility (If applicable)**: dnd5e
- **Translation Support**: en, de (thanks https://github.com/CarnVanBeck), es (thanks https://github.com/JJBocanegra), fr, ja, pt-BR
[Patch Notes](https://github.com/ZoltanTheDM/compendium-browser/blob/master/Patchnotes.md)
## Installation ## Installation
1. Go to the Add-on Modules tab in Foundry Setup 1. Go to the Add-on Modules tab in Foundry Setup
@ -32,6 +22,8 @@ All filters featured in the app are included in this manner and can be found in
## License ## License
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons Licence" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">Compendium Browser - a module for Foundry VTT -</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="https://github.com/syl3r86?tab=repositories" property="cc:attributionName" rel="cc:attributionURL">Felix Müller</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>. This project is a fork of Compendium Browser by [Felix Müller](https://github.com/syl3r86).
This work is licensed under Foundry Virtual Tabletop [EULA - Limited License Agreement for module development v 0.1.6](http://foundryvtt.com/pages/license.html). ## Community Contribution
See the [CONTRIBUTING](/CONTRIBUTING.md) file for information about how you can help this project.

176
gulpfile.mjs 100644
View File

@ -0,0 +1,176 @@
// SPDX-FileCopyrightText: 2022 Johannes Loher
// SPDX-FileCopyrightText: 2022 David Archibald
//
// SPDX-License-Identifier: MIT
import fs from "fs-extra";
import gulp from "gulp";
import less from "gulp-less";
import sourcemaps from "gulp-sourcemaps";
import path from "node:path";
import buffer from "vinyl-buffer";
import source from "vinyl-source-stream";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import rollupStream from "@rollup/stream";
import rollupConfig from "./rollup.config.mjs";
/** ******************/
/* CONFIGURATION */
/** ******************/
const packageId = "compendium-browser";
const sourceDirectory = "./src";
const distDirectory = "./dist";
const stylesDirectory = `${sourceDirectory}/styles`;
const stylesExtension = "less";
const sourceFileExtension = "js";
const staticFiles = ["assets", "fonts", "lang", "packs", "templates", "module.json"];
/** ******************/
/* BUILD */
/** ******************/
let cache;
/**
* Build the distributable JavaScript code
*/
function buildCode() {
return rollupStream({ ...rollupConfig(), cache })
.on("bundle", (bundle) => {
cache = bundle;
})
.pipe(source(`${packageId}.js`))
.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(sourcemaps.write("."))
.pipe(gulp.dest(`${distDirectory}/module`));
}
/**
* Build style sheets
*/
function buildStyles() {
return gulp
.src(`${stylesDirectory}/${packageId}.${stylesExtension}`)
.pipe(less())
.pipe(gulp.dest(`${distDirectory}/styles`));
}
/**
* Copy static files
*/
async function copyFiles() {
for (const file of staticFiles) {
if (fs.existsSync(`${sourceDirectory}/${file}`)) {
await fs.copy(`${sourceDirectory}/${file}`, `${distDirectory}/${file}`);
}
}
}
/**
* Watch for changes for each build step
*/
export function watch() {
gulp.watch(`${sourceDirectory}/**/*.${sourceFileExtension}`, { ignoreInitial: false }, buildCode);
gulp.watch(`${stylesDirectory}/**/*.${stylesExtension}`, { ignoreInitial: false }, buildStyles);
gulp.watch(
staticFiles.map((file) => `${sourceDirectory}/${file}`),
{ ignoreInitial: false },
copyFiles,
);
}
export const build = gulp.series(clean, gulp.parallel(buildCode, buildStyles, copyFiles));
/** ******************/
/* CLEAN */
/** ******************/
/**
* Remove built files from `dist` folder while ignoring source files
*/
export async function clean() {
const files = [...staticFiles, "module"];
if (fs.existsSync(`${stylesDirectory}/${packageId}.${stylesExtension}`)) {
files.push("styles");
}
console.log(" ", "Files to clean:");
console.log(" ", files.join("\n "));
for (const filePath of files) {
await fs.remove(`${distDirectory}/${filePath}`);
}
}
/** ******************/
/* LINK */
/** ******************/
/**
* Get the data paths of Foundry VTT based on what is configured in `foundryconfig.json`
*/
function getDataPaths() {
const config = fs.readJSONSync("foundryconfig.json");
const dataPath = config?.dataPath;
if (dataPath) {
const dataPaths = Array.isArray(dataPath) ? dataPath : [dataPath];
return dataPaths.map((dataPath) => {
if (typeof dataPath !== "string") {
throw new Error(
`Property dataPath in foundryconfig.json is expected to be a string or an array of strings, but found ${dataPath}`,
);
}
if (!fs.existsSync(path.resolve(dataPath))) {
throw new Error(`The dataPath ${dataPath} does not exist on the file system`);
}
return path.resolve(dataPath);
});
} else {
throw new Error("No dataPath defined in foundryconfig.json");
}
}
/**
* Link build to User Data folder
*/
export async function link() {
let destinationDirectory;
if (fs.existsSync(path.resolve(sourceDirectory, "module.json"))) {
destinationDirectory = "modules";
} else {
throw new Error("Could not find module.json");
}
const linkDirectories = getDataPaths().map((dataPath) =>
path.resolve(dataPath, "Data", destinationDirectory, packageId),
);
const argv = yargs(hideBin(process.argv)).option("clean", {
alias: "c",
type: "boolean",
default: false,
}).argv;
const clean = argv.c;
for (const linkDirectory of linkDirectories) {
if (clean) {
console.log(`Removing build in ${linkDirectory}.`);
await fs.remove(linkDirectory);
} else if (!fs.existsSync(linkDirectory)) {
console.log(`Linking dist to ${linkDirectory}.`);
await fs.ensureDir(path.resolve(linkDirectory, ".."));
await fs.symlink(path.resolve(distDirectory), linkDirectory);
} else {
console.log(`Skipped linking to ${linkDirectory}, as it already exists.`);
}
}
}

11471
package-lock.json generated 100644

File diff suppressed because it is too large Load Diff

32
package.json 100644
View File

@ -0,0 +1,32 @@
{
"scripts": {
"build": "gulp build",
"build:watch": "gulp watch",
"link-project": "gulp link",
"clean": "gulp clean",
"clean:link": "gulp link --clean",
"lint": "eslint --ext .js,.cjs,.mjs .",
"lint:fix": "eslint --ext .js,.cjs,.mjs --fix .",
"postinstall": "husky install"
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/stream": "^3.0.1",
"@typhonjs-fvtt/eslint-config-foundry.js": "^0.8.0",
"eslint": "^8.53.0",
"fs-extra": "^11.1.1",
"gulp": "^4.0.2",
"gulp-less": "^5.0.0",
"gulp-sourcemaps": "^3.0.0",
"husky": "^8.0.3",
"less": "^3.13.1",
"lint-staged": "^15.0.2",
"rollup": "^2.79.1",
"vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0",
"yargs": "^17.7.2"
},
"lint-staged": {
"*.(js|cjs|mjs)": "eslint --fix"
}
}

16
rollup.config.mjs 100644
View File

@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: 2022 Johannes Loher
// SPDX-FileCopyrightText: 2022 David Archibald
//
// SPDX-License-Identifier: MIT
import { nodeResolve } from "@rollup/plugin-node-resolve";
export default () => ({
input: "src/module/compendium-browser.js",
output: {
dir: "dist/module",
format: "es",
sourcemap: true,
},
plugins: [nodeResolve()],
});

View File

@ -2,16 +2,23 @@
"id": "compendium-browser", "id": "compendium-browser",
"title": "Compendium Browser", "title": "Compendium Browser",
"description": "Easily browse and filter spells, feats, items, and npcs loaded from compendiums!", "description": "Easily browse and filter spells, feats, items, and npcs loaded from compendiums!",
"version": "2.0.0",
"authors": [ "authors": [
{ {
"name": "Matheus Clemente", "name": "Matheus Clemente",
"discord": "mclemente#5524" "discord": "mclemente#5524"
} }
], ],
"systems": ["dnd5e"], "url": "This is auto replaced",
"scripts": ["./compendium-browser.js"], "readme": "https://github.com/mclemente/compendium-browser/blob/master/README.md",
"styles": ["./compendium-browser.css"], "bugs": "https://github.com/mclemente/compendium-browser/issues",
"changelog": "https://github.com/mclemente/compendium-browser/blob/master/Patchnotes.md",
"version": "This is auto replaced",
"compatibility": {
"minimum": "11",
"verified": "11"
},
"esmodules": ["module/compendium-browser.js"],
"styles": ["styles/compendium-browser.css"],
"languages": [ "languages": [
{ {
"lang": "en", "lang": "en",
@ -44,15 +51,14 @@
"path": "lang/de.json" "path": "lang/de.json"
} }
], ],
"url": "https://github.com/mclemente/compendium-browser", "relationships": {
"manifest": "https://github.com/mclemente/compendium-browser/releases/latest/download/module.json", "systems": [{
"download": "https://github.com/mclemente/compendium-browser/releases/download/latest/compendium-browser.zip", "id": "dnd5e",
"compatibility": { "type": "system"
"minimum": "11", }],
"verified": "11" "requires": [],
"conflicts": []
}, },
"allowBugReporter": true, "manifest": "This is auto replaced",
"bugs": "https://github.com/mclemente/compendium-browser/issues", "download": "This is auto replaced"
"readme": "https://github.com/mclemente/compendium-browser/blob/master/README.md",
"changelog": "https://github.com/mclemente/compendium-browser/blob/master/Patchnotes.md"
} }

View File

@ -1,4 +1,7 @@
const STOP_SEARCH = "StopSearchException"; import { preloadTemplates } from "./preloadTemplates.js";
import { registerSettings } from "./settings.js";
const STOP_SEARCH = "StopSearchException";
const NOT_MIGRATED = "NotMigratedException"; const NOT_MIGRATED = "NotMigratedException";
class CompendiumBrowser extends Application { class CompendiumBrowser extends Application {
@ -7,7 +10,7 @@ class CompendiumBrowser extends Application {
title: "CMPBrowser.compendiumBrowser", title: "CMPBrowser.compendiumBrowser",
tabs: [{ navSelector: ".tabs", contentSelector: ".content", initial: "spell" }], tabs: [{ navSelector: ".tabs", contentSelector: ".content", initial: "spell" }],
classes: ["compendium-browser"], classes: ["compendium-browser"],
template: "modules/compendium-browser/template/template.html", template: "modules/compendium-browser/templates/template.html",
width: 800, width: 800,
height: 730, height: 730,
resizable: true, resizable: true,
@ -31,7 +34,7 @@ class CompendiumBrowser extends Application {
this.hookCompendiumList(html); this.hookCompendiumList(html);
}); });
//Reset the filters used in the dialog // Reset the filters used in the dialog
this.spellFilters = { this.spellFilters = {
registeredFilterCategorys: {}, registeredFilterCategorys: {},
activeFilters: {}, activeFilters: {},
@ -59,11 +62,11 @@ class CompendiumBrowser extends Application {
/** @override */ /** @override */
async getData() { async getData() {
//0.4.1 Filter as we load to support new way of filtering // 0.4.1 Filter as we load to support new way of filtering
//Previously loaded all data and filtered in place; now loads minimal (preload) amount, filtered as we go // Previously loaded all data and filtered in place; now loads minimal (preload) amount, filtered as we go
//First time (when you press Compendium Browser button) is called with filters unset // First time (when you press Compendium Browser button) is called with filters unset
//0.4.1k: Don't do any item/npc loading until tab is visible // 0.4.1k: Don't do any item/npc loading until tab is visible
let data = { let data = {
items: [], items: [],
npcs: [], npcs: [],
@ -94,7 +97,7 @@ class CompendiumBrowser extends Application {
}); });
// make draggable // make draggable
//0.4.1: Avoid the game.packs lookup // 0.4.1: Avoid the game.packs lookup
html.find(".draggable").each((i, li) => { html.find(".draggable").each((i, li) => {
li.setAttribute("draggable", true); li.setAttribute("draggable", true);
li.addEventListener( li.addEventListener(
@ -128,7 +131,7 @@ class CompendiumBrowser extends Application {
if (!e.isIntersecting) continue; if (!e.isIntersecting) continue;
const img = e.target; const img = e.target;
// Avatar image // Avatar image
//const img = li.querySelector("img"); // const img = li.querySelector("img");
if (img && img.dataset.src) { if (img && img.dataset.src) {
img.src = img.dataset.src; img.src = img.dataset.src;
delete img.dataset.src; delete img.dataset.src;
@ -201,15 +204,15 @@ class CompendiumBrowser extends Application {
for (let tab of ["spell", "feat", "item", "npc"]) { for (let tab of ["spell", "feat", "item", "npc"]) {
// reset filters and re-render // reset filters and re-render
//0.4.3: Reset ALL filters because when we do a re-render it affects all tabs // 0.4.3: Reset ALL filters because when we do a re-render it affects all tabs
html.find(`#reset-${tab}-filter`).click((ev) => { html.find(`#reset-${tab}-filter`).click((ev) => {
this.resetFilters(); this.resetFilters();
//v0.4.3: Re-render so that we display the filters correctly // v0.4.3: Re-render so that we display the filters correctly
this.refreshList = tab; this.refreshList = tab;
this.render(); this.render();
}); });
//copy Javascript seach to clipboard // copy Javascript seach to clipboard
html.find(`#copy-search-${tab}`).click(async (ev) => { html.find(`#copy-search-${tab}`).click(async (ev) => {
this.copySearchToClipboard(tab); this.copySearchToClipboard(tab);
}); });
@ -246,7 +249,7 @@ class CompendiumBrowser extends Application {
}); });
// activating or deactivating filters // activating or deactivating filters
//0.4.1: Now does a re-load and updates just the data side // 0.4.1: Now does a re-load and updates just the data side
// text filters // text filters
html.find(".filter[data-type=text] input, .filter[data-type=text] select").on("keyup change paste", (ev) => { html.find(".filter[data-type=text] input, .filter[data-type=text] select").on("keyup change paste", (ev) => {
const path = $(ev.target).parents(".filter").data("path"); const path = $(ev.target).parents(".filter").data("path");
@ -365,14 +368,14 @@ class CompendiumBrowser extends Application {
} }
); );
//Just for the loading image // Just for the loading image
if (this.observer) { if (this.observer) {
html.find("img").each((i, img) => this.observer.observe(img)); html.find("img").each((i, img) => this.observer.observe(img));
} }
} }
async checkListsLoaded() { async checkListsLoaded() {
//Provides extra info not in the standard SRD, like which classes can learn a spell // Provides extra info not in the standard SRD, like which classes can learn a spell
if (!this.classList) { if (!this.classList) {
this.classList = await fetch("modules/compendium-browser/spell-classes.json") this.classList = await fetch("modules/compendium-browser/spell-classes.json")
.then((result) => { .then((result) => {
@ -411,23 +414,23 @@ class CompendiumBrowser extends Application {
this.CurrentSeachNumber = seachNumber; this.CurrentSeachNumber = seachNumber;
//0.4.1: Load and filter just one of spells, feats, and items (specified by browserTab) // 0.4.1: Load and filter just one of spells, feats, and items (specified by browserTab)
let unfoundSpells = ""; let unfoundSpells = "";
let numItemsLoaded = 0; let numItemsLoaded = 0;
let compactItems = {}; let compactItems = {};
try { try {
//Filter the full list, but only save the core compendium information + displayed info // Filter the full list, but only save the core compendium information + displayed info
for (let pack of game.packs) { for (let pack of game.packs) {
if (pack.documentName === "Item" && this.settings.loadedSpellCompendium[pack.collection].load) { if (pack.documentName === "Item" && this.settings.loadedSpellCompendium[pack.collection].load) {
//can query just for spells since there is only 1 type // can query just for spells since there is only 1 type
let query = {}; let query = {};
if (browserTab === "spell") { if (browserTab === "spell") {
query = { type: "spell" }; query = { type: "spell" };
} }
//FIXME: How much could we do with the loaded index rather than all content? // FIXME: How much could we do with the loaded index rather than all content?
//OR filter the content up front for the decoratedItem.type?? // OR filter the content up front for the decoratedItem.type??
await pack.getDocuments(query).then((content) => { await pack.getDocuments(query).then((content) => {
if (browserTab === "spell") { if (browserTab === "spell") {
content.reduce( content.reduce(
@ -446,8 +449,8 @@ class CompendiumBrowser extends Application {
const decoratedItem = this.decorateItem(item5e); const decoratedItem = this.decorateItem(item5e);
if ( if (
decoratedItem && decoratedItem
this.passesFilter(decoratedItem, this.spellFilters.activeFilters) && this.passesFilter(decoratedItem, this.spellFilters.activeFilters)
) { ) {
itemsList[item5e.id] = { itemsList[item5e.id] = {
compendium: pack.collection, compendium: pack.collection,
@ -482,9 +485,9 @@ class CompendiumBrowser extends Application {
const decoratedItem = this.decorateItem(item5e); const decoratedItem = this.decorateItem(item5e);
if ( if (
decoratedItem && decoratedItem
["feat", "class", "subclass", "background"].includes(decoratedItem.type) && && ["feat", "class", "subclass", "background"].includes(decoratedItem.type)
this.passesFilter(decoratedItem, this.featFilters.activeFilters) && this.passesFilter(decoratedItem, this.featFilters.activeFilters)
) { ) {
itemsList[item5e.id] = { itemsList[item5e.id] = {
compendium: pack.collection, compendium: pack.collection,
@ -515,11 +518,11 @@ class CompendiumBrowser extends Application {
const decoratedItem = this.decorateItem(item5e); const decoratedItem = this.decorateItem(item5e);
if ( if (
decoratedItem && decoratedItem
!["spell", "feat", "class", "subclass", "background"].includes( && !["spell", "feat", "class", "subclass", "background"].includes(
decoratedItem.type decoratedItem.type
) && )
this.passesFilter(decoratedItem, this.itemFilters.activeFilters) && this.passesFilter(decoratedItem, this.itemFilters.activeFilters)
) { ) {
itemsList[item5e.id] = { itemsList[item5e.id] = {
compendium: pack.collection, compendium: pack.collection,
@ -540,11 +543,11 @@ class CompendiumBrowser extends Application {
updateLoading(numItemsLoaded, false); updateLoading(numItemsLoaded, false);
} }
}); });
} //end if pack entity === Item } // end if pack entity === Item
} //for packs } // for packs
} catch (e) { } catch(e) {
if (e === STOP_SEARCH) { if (e === STOP_SEARCH) {
//stopping search early // stopping search early
} else { } else {
throw e; throw e;
} }
@ -602,8 +605,8 @@ class CompendiumBrowser extends Application {
if (npc5e.name != "#[CF_tempEntity]") { if (npc5e.name != "#[CF_tempEntity]") {
const decoratedNpc = this.decorateNpc(npc5e, indexFields); const decoratedNpc = this.decorateNpc(npc5e, indexFields);
if ( if (
decoratedNpc && decoratedNpc
this.passesFilter(decoratedNpc, this.npcFilters.activeFilters) && this.passesFilter(decoratedNpc, this.npcFilters.activeFilters)
) { ) {
actorsList[npc5e._id] = { actorsList[npc5e._id] = {
compendium: pack.collection, compendium: pack.collection,
@ -628,11 +631,11 @@ class CompendiumBrowser extends Application {
} }
}); });
} }
//0.4.1 Only preload a limited number and fill more in as needed // 0.4.1 Only preload a limited number and fill more in as needed
} }
} catch (e) { } catch(e) {
if (e == STOP_SEARCH) { if (e == STOP_SEARCH) {
//breaking out // breaking out
} else if (e == NOT_MIGRATED) { } else if (e == NOT_MIGRATED) {
console.log("Cannot browse compendium %s as it is not migrated to v10 format", collectionName); console.log("Cannot browse compendium %s as it is not migrated to v10 format", collectionName);
} else { } else {
@ -660,9 +663,9 @@ class CompendiumBrowser extends Application {
// Handle button clicks // Handle button clicks
cbButton.click((ev) => { cbButton.click((ev) => {
ev.preventDefault(); ev.preventDefault();
//0.4.1: Reset filters when you click button // 0.4.1: Reset filters when you click button
this.resetFilters(); this.resetFilters();
//0.4.3: Reset everything (including data) when you press the button - calls afterRender() hook // 0.4.3: Reset everything (including data) when you press the button - calls afterRender() hook
if (!this.refreshList) { if (!this.refreshList) {
if (game.user.isGM || this.settings.allowSpellBrowser) { if (game.user.isGM || this.settings.allowSpellBrowser) {
@ -682,7 +685,7 @@ class CompendiumBrowser extends Application {
/* Hook to load the first data */ /* Hook to load the first data */
static afterRender(cb, html) { static afterRender(cb, html) {
//0.4.3: Because a render always resets ALL the displayed filters (on all tabs) to unselected , we have to blank all the lists as well // 0.4.3: Because a render always resets ALL the displayed filters (on all tabs) to unselected , we have to blank all the lists as well
// (because the current HTML template doesn't set the selected filter values) // (because the current HTML template doesn't set the selected filter values)
if (!cb?.refreshList) { if (!cb?.refreshList) {
return; return;
@ -705,10 +708,10 @@ class CompendiumBrowser extends Application {
} }
async replaceList(html, browserTab, options = { reload: true }) { async replaceList(html, browserTab, options = { reload: true }) {
//After rendering the first time or re-rendering trigger the load/reload of visible data // After rendering the first time or re-rendering trigger the load/reload of visible data
let elements = null; let elements = null;
//0.4.2 Display a Loading... message while the data is being loaded and filtered // 0.4.2 Display a Loading... message while the data is being loaded and filtered
let loadingMessage = null; let loadingMessage = null;
const tabElements = { const tabElements = {
spell: { elements: "ul#CBSpells", message: "#CBSpellsMessage" }, spell: { elements: "ul#CBSpells", message: "#CBSpellsMessage" },
@ -725,7 +728,7 @@ class CompendiumBrowser extends Application {
} }
if (elements?.length) { if (elements?.length) {
//0.4.2b: On a tab-switch, only reload if there isn't any data already // 0.4.2b: On a tab-switch, only reload if there isn't any data already
if (options?.reload || !elements[0].children.length) { if (options?.reload || !elements[0].children.length) {
const updateLoading = async (numLoaded, doneLoading) => { const updateLoading = async (numLoaded, doneLoading) => {
if (loadingMessage.length) { if (loadingMessage.length) {
@ -739,20 +742,20 @@ class CompendiumBrowser extends Application {
} }
}; };
updateLoading(0, false); updateLoading(0, false);
//Uses loadAndFilterItems to read compendia for items which pass the current filters and render on this tab // Uses loadAndFilterItems to read compendia for items which pass the current filters and render on this tab
const newItemsHTML = await this.renderItemData(browserTab, updateLoading); const newItemsHTML = await this.renderItemData(browserTab, updateLoading);
elements[0].innerHTML = newItemsHTML; elements[0].innerHTML = newItemsHTML;
//Re-sort before setting up lazy loading // Re-sort before setting up lazy loading
this.triggerSort(html, browserTab); this.triggerSort(html, browserTab);
//Lazy load images // Lazy load images
if (this.observer) { if (this.observer) {
$(elements) $(elements)
.find("img") .find("img")
.each((i, img) => this.observer.observe(img)); .each((i, img) => this.observer.observe(img));
} }
//Reactivate listeners for clicking and dragging // Reactivate listeners for clicking and dragging
this.activateItemListListeners($(elements)); this.activateItemListListeners($(elements));
} }
} }
@ -761,7 +764,7 @@ class CompendiumBrowser extends Application {
async renderLoading(messageElement, itemType, numLoaded, maxLoaded = false, doneLoading = false) { async renderLoading(messageElement, itemType, numLoaded, maxLoaded = false, doneLoading = false) {
if (!messageElement) return; if (!messageElement) return;
let loadingHTML = await renderTemplate("modules/compendium-browser/template/loading.html", { let loadingHTML = await renderTemplate("modules/compendium-browser/templates/loading.html", {
numLoaded: numLoaded, numLoaded: numLoaded,
itemType: itemType, itemType: itemType,
maxLoaded: maxLoaded, maxLoaded: maxLoaded,
@ -777,14 +780,14 @@ class CompendiumBrowser extends Application {
} else { } else {
listItems = await this.loadAndFilterItems(browserTab, updateLoading); listItems = await this.loadAndFilterItems(browserTab, updateLoading);
} }
const html = await renderTemplate(`modules/compendium-browser/template/${browserTab}-browser-list.html`, { const html = await renderTemplate(`modules/compendium-browser/templates/${browserTab}-browser-list.html`, {
listItems: listItems, listItems: listItems,
}); });
return html; return html;
} }
//SORTING // SORTING
triggerSort(html, browserTab) { triggerSort(html, browserTab) {
if (browserTab === "spell") { if (browserTab === "spell") {
html.find(".spell-browser select[name=sortorder]").trigger("change"); html.find(".spell-browser select[name=sortorder]").trigger("change");
@ -925,7 +928,7 @@ class CompendiumBrowser extends Application {
decorateItem(item5e) { decorateItem(item5e) {
if (!item5e) return null; if (!item5e) return null;
//Decorate and then filter a compendium entry - returns null or the item // Decorate and then filter a compendium entry - returns null or the item
const item = { ...item5e }; const item = { ...item5e };
@ -953,12 +956,12 @@ class CompendiumBrowser extends Application {
.replace(/[^一-龠ぁ-ゔァ-ヴーa-zA-Z0-9---9々〆〤]/g, "") .replace(/[^一-龠ぁ-ゔァ-ヴーa-zA-Z0-9---9々〆〤]/g, "")
.replace("'", "") .replace("'", "")
.replace(/ /g, ""); .replace(/ /g, "");
//let cleanSpellName = spell.name.toLowerCase().replace(/[^a-zA-Z0-9\s:]/g, '').replace("'", '').replace(/ /g, ''); // let cleanSpellName = spell.name.toLowerCase().replace(/[^a-zA-Z0-9\s:]/g, '').replace("'", '').replace(/ /g, '');
if (this.classList[cleanSpellName]) { if (this.classList[cleanSpellName]) {
let classes = this.classList[cleanSpellName]; let classes = this.classList[cleanSpellName];
item.classes = classes.split(","); item.classes = classes.split(",");
} else { } else {
//FIXME: unfoundSpells += cleanSpellName + ','; // FIXME: unfoundSpells += cleanSpellName + ',';
} }
} else if (item.type === "feat" || item.type === "class") { } else if (item.type === "feat" || item.type === "class") {
// getting class // getting class
@ -982,7 +985,7 @@ class CompendiumBrowser extends Application {
// getting uses/ressources status // getting uses/ressources status
item.usesRessources = item5e.hasLimitedUses; item.usesRessources = item5e.hasLimitedUses;
} else if (item.type === "subclass") { } else if (item.type === "subclass") {
//subclasses dont exist lower then version 10 // subclasses dont exist lower then version 10
item.classRequirement = [item.system.classIdentifier]; item.classRequirement = [item.system.classIdentifier];
item.classRequirementString = item.system.classIdentifier; item.classRequirementString = item.system.classIdentifier;
} else { } else {
@ -1012,17 +1015,17 @@ class CompendiumBrowser extends Application {
return npcDict; return npcDict;
}, {}); }, {});
//0.8.0: update for V10 to use actor.system instead of actor.data // 0.8.0: update for V10 to use actor.system instead of actor.data
let npcData = npc.system; let npcData = npc.system;
// cr display // cr display
let cr = npcData.details?.cr; //0.7.2c: Possibly because of getIndex() use we now have to check for existence of details (doesn't for Character-type NPCs) let cr = npcData.details?.cr; // 0.7.2c: Possibly because of getIndex() use we now have to check for existence of details (doesn't for Character-type NPCs)
if (cr === undefined || cr === "") cr = 0; if (cr === undefined || cr === "") cr = 0;
else cr = Number(cr); else cr = Number(cr);
decoratedNpc.orderCR = cr; decoratedNpc.orderCR = cr;
if (cr > 0 && cr < 1) cr = "1/" + 1 / cr; if (cr > 0 && cr < 1) cr = `1/${1 / cr}`;
decoratedNpc.displayCR = cr; decoratedNpc.displayCR = cr;
decoratedNpc.displaySize = "unset"; decoratedNpc.displaySize = "unset";
@ -1059,7 +1062,7 @@ class CompendiumBrowser extends Application {
break; break;
} }
return decoratedNpc; return decoratedNpc;
} catch (e) { } catch(e) {
throw e; throw e;
} }
} }
@ -1116,10 +1119,10 @@ class CompendiumBrowser extends Application {
} else { } else {
if (prop === undefined) return false; if (prop === undefined) return false;
if ( if (
filter.value !== undefined && filter.value !== undefined
prop !== undefined && && prop !== undefined
prop != filter.value && && prop != filter.value
!(filter.value === true && prop) && !(filter.value === true && prop)
) { ) {
return false; return false;
} }
@ -1156,17 +1159,17 @@ class CompendiumBrowser extends Application {
return true; return true;
} }
//incomplete removal of duplicate items // incomplete removal of duplicate items
removeDuplicates(spellList) { removeDuplicates(spellList) {
//sort at n log n // sort at n log n
let sortedList = Object.values(spellList).sort((a, b) => a.name.localeCompare(b.name)); let sortedList = Object.values(spellList).sort((a, b) => a.name.localeCompare(b.name));
//search through sorted list for duplicates // search through sorted list for duplicates
for (let index = 0; index < sortedList.length - 1; ) { for (let index = 0; index < sortedList.length - 1; ) {
//all duplicates will be next to eachother // all duplicates will be next to eachother
if (sortedList[index].name == sortedList[index + 1].name) { if (sortedList[index].name == sortedList[index + 1].name) {
//duplicate something is getting removed // duplicate something is getting removed
//TODO choose what to remove rather then the second // TODO choose what to remove rather then the second
let remove = index + 1; let remove = index + 1;
delete spellList[sortedList[remove].id]; delete spellList[sortedList[remove].id];
@ -1196,13 +1199,13 @@ class CompendiumBrowser extends Application {
if (compendium.documentName === "Item") { if (compendium.documentName === "Item") {
defaultSettings.loadedSpellCompendium[compendium.collection] = { defaultSettings.loadedSpellCompendium[compendium.collection] = {
load: true, load: true,
name: `${compendium["metadata"]["label"]} (${compendium.collection})`, name: `${compendium.metadata.label} (${compendium.collection})`,
}; };
} }
if (compendium.documentName === "Actor") { if (compendium.documentName === "Actor") {
defaultSettings.loadedNpcCompendium[compendium.collection] = { defaultSettings.loadedNpcCompendium[compendium.collection] = {
load: true, load: true,
name: `${compendium["metadata"]["label"]} (${compendium.collection})`, name: `${compendium.metadata.label} (${compendium.collection})`,
}; };
} }
} }
@ -1235,21 +1238,21 @@ class CompendiumBrowser extends Application {
// load settings from container and apply to default settings (available compendie might have changed) // load settings from container and apply to default settings (available compendie might have changed)
let settings = game.settings.get("compendium-browser", "settings"); let settings = game.settings.get("compendium-browser", "settings");
for (let compKey in defaultSettings.loadedSpellCompendium) { for (let compKey in defaultSettings.loadedSpellCompendium) {
//v0.7.1 Check for settings.loadedSpellCompendium // v0.7.1 Check for settings.loadedSpellCompendium
if (settings.loadedSpellCompendium && settings.loadedSpellCompendium[compKey] !== undefined) { if (settings.loadedSpellCompendium && settings.loadedSpellCompendium[compKey] !== undefined) {
defaultSettings.loadedSpellCompendium[compKey].load = settings.loadedSpellCompendium[compKey].load; defaultSettings.loadedSpellCompendium[compKey].load = settings.loadedSpellCompendium[compKey].load;
} }
} }
for (let compKey in defaultSettings.loadedNpcCompendium) { for (let compKey in defaultSettings.loadedNpcCompendium) {
//v0.7.1 Check for settings.loadedNpcCompendium // v0.7.1 Check for settings.loadedNpcCompendium
if (settings.loadedNpcCompendium && settings.loadedNpcCompendium[compKey] !== undefined) { if (settings.loadedNpcCompendium && settings.loadedNpcCompendium[compKey] !== undefined) {
defaultSettings.loadedNpcCompendium[compKey].load = settings.loadedNpcCompendium[compKey].load; defaultSettings.loadedNpcCompendium[compKey].load = settings.loadedNpcCompendium[compKey].load;
} }
} }
defaultSettings.allowSpellBrowser = settings.allowSpellBrowser ? true : false; defaultSettings.allowSpellBrowser = !!settings.allowSpellBrowser;
defaultSettings.allowFeatBrowser = settings.allowFeatBrowser ? true : false; defaultSettings.allowFeatBrowser = !!settings.allowFeatBrowser;
defaultSettings.allowItemBrowser = settings.allowItemBrowser ? true : false; defaultSettings.allowItemBrowser = !!settings.allowItemBrowser;
defaultSettings.allowNpcBrowser = settings.allowNpcBrowser ? true : false; defaultSettings.allowNpcBrowser = !!settings.allowNpcBrowser;
if (game.user.isGM) { if (game.user.isGM) {
game.settings.set("compendium-browser", "settings", defaultSettings); game.settings.set("compendium-browser", "settings", defaultSettings);
@ -1257,8 +1260,8 @@ class CompendiumBrowser extends Application {
this.settings = defaultSettings; this.settings = defaultSettings;
} }
//FILTERS - Added on the Ready hook // FILTERS - Added on the Ready hook
//0.4.0 Make this async so filters can be added all at once // 0.4.0 Make this async so filters can be added all at once
async addFilter(entityType, category, label, path, type, possibleValues = null, valIsArray = false) { async addFilter(entityType, category, label, path, type, possibleValues = null, valIsArray = false) {
let target = `${entityType}Filters`; let target = `${entityType}Filters`;
let filter = {}; let filter = {};
@ -1338,7 +1341,7 @@ class CompendiumBrowser extends Application {
"select", "select",
this._sortPackValues(CONFIG.DND5E.damageTypes) this._sortPackValues(CONFIG.DND5E.damageTypes)
); );
//JV-082: Fix for missing "Class" search feature // JV-082: Fix for missing "Class" search feature
this.addSpellFilter( this.addSpellFilter(
"CMPBrowser.general", "CMPBrowser.general",
"ITEM.TypeClass", "ITEM.TypeClass",
@ -1437,7 +1440,7 @@ class CompendiumBrowser extends Application {
async addFeatFilters() { async addFeatFilters() {
// Feature Filters // Feature Filters
//Foundry v10+ Item#data is now Item#system // Foundry v10+ Item#data is now Item#system
this.addFeatFilter("CMPBrowser.general", "DND5E.Source", "system.source", "text"); this.addFeatFilter("CMPBrowser.general", "DND5E.Source", "system.source", "text");
this.addFeatFilter( this.addFeatFilter(
"CMPBrowser.general", "CMPBrowser.general",
@ -1651,7 +1654,7 @@ class CompendiumBrowser extends Application {
} }
async renderWith(tab = "spell", filters = []) { async renderWith(tab = "spell", filters = []) {
//if there isn't a tab error out // if there isn't a tab error out
if (!this[`${tab}Filters`]) { if (!this[`${tab}Filters`]) {
ui.notifications.warn(`no tab by name ${tab}`); ui.notifications.warn(`no tab by name ${tab}`);
return; return;
@ -1685,8 +1688,8 @@ class CompendiumBrowser extends Application {
this[`${tab}Filters`].activeFilters = activateFilters; this[`${tab}Filters`].activeFilters = activateFilters;
//wait for after the afterRender function to change tabs // wait for after the afterRender function to change tabs
//this avoids some errors when initially opening the window // this avoids some errors when initially opening the window
CompendiumBrowser.postRender = async () => { CompendiumBrowser.postRender = async () => {
CompendiumBrowser.postRender = () => {}; CompendiumBrowser.postRender = () => {};
@ -1719,7 +1722,7 @@ class CompendiumBrowser extends Application {
c.prop("checked", true); c.prop("checked", true);
} }
} else { } else {
ui.notifications.warn(`Unknown filter type?`); ui.notifications.warn("Unknown filter type?");
} }
} }
}; };
@ -1756,7 +1759,7 @@ class CompendiumBrowser extends Application {
try { try {
await navigator.clipboard.writeText(text); await navigator.clipboard.writeText(text);
ui.notifications.info("Javascript Copied to clipboard"); ui.notifications.info("Javascript Copied to clipboard");
} catch (err) { } catch(err) {
ui.notifications.warn("failed to copy javascript to clipboard, check logs for string"); ui.notifications.warn("failed to copy javascript to clipboard, check logs for string");
console.error("Failed to copy: ", err); console.error("Failed to copy: ", err);
} }
@ -1765,9 +1768,9 @@ class CompendiumBrowser extends Application {
getSearchText(tab) { getSearchText(tab) {
const target = `${tab}Filters`; const target = `${tab}Filters`;
//map active filters to their labels // map active filters to their labels
let output = Object.values(this[target].activeFilters).map((filter) => { let output = Object.values(this[target].activeFilters).map((filter) => {
//find Filters from paths // find Filters from paths
let out = this.findFilterR(target, filter); let out = this.findFilterR(target, filter);
if (filter.value) { if (filter.value) {
@ -1795,7 +1798,7 @@ class CompendiumBrowser extends Application {
ui.notifications.warn("Could not find the filter!!"); ui.notifications.warn("Could not find the filter!!");
console.warn(filterTarget); console.warn(filterTarget);
return;
} }
static async addTidySheetButton(cb, html, actor) { static async addTidySheetButton(cb, html, actor) {
@ -1872,7 +1875,7 @@ class CompendiumBrowser extends Application {
}); });
} }
//find the first caster class of the character // find the first caster class of the character
static findCasterClass(character) { static findCasterClass(character) {
const options = ["artificer", "bard", "cleric", "druid", "paladin", "ranger", "sorcerer", "warlock", "wizard"]; const options = ["artificer", "bard", "cleric", "druid", "paladin", "ranger", "sorcerer", "warlock", "wizard"];
@ -1886,9 +1889,9 @@ class CompendiumBrowser extends Application {
} }
static findMaxCasterLevel(character) { static findMaxCasterLevel(character) {
//find max spell level // find max spell level
let maxLevel = Object.keys(character.system.spells).reduce((acc, spell) => { let maxLevel = Object.keys(character.system.spells).reduce((acc, spell) => {
//special case for pact magic // special case for pact magic
if (spell == "pact") { if (spell == "pact") {
return Math.max(character.system.spells[spell].level, acc); return Math.max(character.system.spells[spell].level, acc);
} else { } else {
@ -1916,27 +1919,16 @@ class CompendiumBrowser extends Application {
} }
} }
Hooks.on("init", async () => { Hooks.once("init", async () => {
await loadTemplates([ registerSettings();
"modules/compendium-browser/template/spell-browser.html", await preloadTemplates();
"modules/compendium-browser/template/spell-browser-list.html",
"modules/compendium-browser/template/npc-browser.html",
"modules/compendium-browser/template/npc-browser-list.html",
"modules/compendium-browser/template/feat-browser.html",
"modules/compendium-browser/template/feat-browser-list.html",
"modules/compendium-browser/template/item-browser.html",
"modules/compendium-browser/template/item-browser-list.html",
"modules/compendium-browser/template/filter-container.html",
"modules/compendium-browser/template/settings.html",
"modules/compendium-browser/template/loading.html",
]);
}); });
Hooks.on("ready", () => { Hooks.on("ready", () => {
if (game.compendiumBrowser === undefined) { if (game.compendiumBrowser === undefined) {
game.compendiumBrowser = new CompendiumBrowser(); game.compendiumBrowser = new CompendiumBrowser();
//0.4.0 Defer loading content until we actually use the Compendium Browser // 0.4.0 Defer loading content until we actually use the Compendium Browser
//A compromise approach would be better (periodic loading) except would still create the memory use problem // A compromise approach would be better (periodic loading) except would still create the memory use problem
game.compendiumBrowser.initialize(); game.compendiumBrowser.initialize();
} }
@ -1955,11 +1947,11 @@ function stripDotCharacters(str) {
} }
function set(obj, path, value) { function set(obj, path, value) {
var schema = obj; // a moving reference to internal objects within obj let schema = obj; // a moving reference to internal objects within obj
var pList = path.split("."); let pList = path.split(".");
var len = pList.length; let len = pList.length;
for (var i = 0; i < len - 1; i++) { for (let i = 0; i < len - 1; i++) {
var elem = pList[i]; let elem = pList[i];
if (!schema[elem]) schema[elem] = {}; if (!schema[elem]) schema[elem] = {};
schema = schema[elem]; schema = schema[elem];
} }
@ -1970,13 +1962,13 @@ function set(obj, path, value) {
function getPropByString(obj, propString) { function getPropByString(obj, propString) {
if (!propString) return obj; if (!propString) return obj;
var prop, let prop;
props = propString.split("."); let props = propString.split(".");
for (var i = 0, iLen = props.length - 1; i < iLen; i++) { for (var i = 0, iLen = props.length - 1; i < iLen; i++) {
prop = props[i]; prop = props[i];
var candidate = obj[prop]; let candidate = obj[prop];
if (candidate !== undefined) { if (candidate !== undefined) {
obj = candidate; obj = candidate;
} else { } else {

View File

@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: 2022 Johannes Loher
//
// SPDX-License-Identifier: MIT
export async function preloadTemplates() {
const templatePaths = [
"modules/compendium-browser/templates/spell-browser.html",
"modules/compendium-browser/templates/spell-browser-list.html",
"modules/compendium-browser/templates/npc-browser.html",
"modules/compendium-browser/templates/npc-browser-list.html",
"modules/compendium-browser/templates/feat-browser.html",
"modules/compendium-browser/templates/feat-browser-list.html",
"modules/compendium-browser/templates/item-browser.html",
"modules/compendium-browser/templates/item-browser-list.html",
"modules/compendium-browser/templates/filter-container.html",
"modules/compendium-browser/templates/settings.html",
"modules/compendium-browser/templates/loading.html",
];
return loadTemplates(templatePaths);
}

View File

@ -0,0 +1,7 @@
// SPDX-FileCopyrightText: 2022 Johannes Loher
//
// SPDX-License-Identifier: MIT
export function registerSettings() {
// Register any custom module settings here
}

View File

@ -24,11 +24,11 @@
<button id="reset-feat-filter">{{localize "CMPBrowser.Filters.ResetFilters"}}</button> <button id="reset-feat-filter">{{localize "CMPBrowser.Filters.ResetFilters"}}</button>
<!-- <button id="copy-search-feat">Export to <i class="fa-brands fa-square-js"></i></button> --> <!-- <button id="copy-search-feat">Export to <i class="fa-brands fa-square-js"></i></button> -->
</div> </div>
{{> "modules/compendium-browser/template/filter-container.html" filters=featFilters}} {{> "modules/compendium-browser/templates/filter-container.html" filters=featFilters}}
</div> </div>
<div class="list-area flexcol"> <div class="list-area flexcol">
<ul id="CBFeats"> <ul id="CBFeats">
{{> "modules/compendium-browser/template/feat-browser-list.html" feats=items}} {{> "modules/compendium-browser/templates/feat-browser-list.html" feats=items}}
</ul> </ul>
<span class="loading" id="CBFeatsMessage" style="flex: 0"></span> <span class="loading" id="CBFeatsMessage" style="flex: 0"></span>
</div> </div>

View File

@ -24,11 +24,11 @@
<button id="reset-item-filter">{{localize "CMPBrowser.Filters.ResetFilters"}}</button> <button id="reset-item-filter">{{localize "CMPBrowser.Filters.ResetFilters"}}</button>
<!-- <button id="copy-search-item">Export to <i class="fa-brands fa-square-js"></i></button> --> <!-- <button id="copy-search-item">Export to <i class="fa-brands fa-square-js"></i></button> -->
</div> </div>
{{> "modules/compendium-browser/template/filter-container.html" filters=itemFilters}} {{> "modules/compendium-browser/templates/filter-container.html" filters=itemFilters}}
</div> </div>
<div class="list-area flexcol"> <div class="list-area flexcol">
<ul id="CBItems"> <ul id="CBItems">
{{> "modules/compendium-browser/template/item-browser-list.html" items=items}} {{> "modules/compendium-browser/templates/item-browser-list.html" items=items}}
</ul> </ul>
<span class="loading" id="CBItemsMessage" style="flex: 0"></span> <span class="loading" id="CBItemsMessage" style="flex: 0"></span>
</div> </div>

View File

@ -25,11 +25,11 @@
<button id="reset-npc-filter">{{localize "CMPBrowser.Filters.ResetFilters"}}</button> <button id="reset-npc-filter">{{localize "CMPBrowser.Filters.ResetFilters"}}</button>
<!-- <button id="copy-search-item">Export to <i class="fa-brands fa-square-js"></i></button> --> <!-- <button id="copy-search-item">Export to <i class="fa-brands fa-square-js"></i></button> -->
</div> </div>
{{> "modules/compendium-browser/template/filter-container.html" filters=npcFilters}} {{> "modules/compendium-browser/templates/filter-container.html" filters=npcFilters}}
</div> </div>
<div class="list-area flexcol"> <div class="list-area flexcol">
<ul id="CBNPCs"> <ul id="CBNPCs">
{{> "modules/compendium-browser/template/npc-browser-list.html" npcs=npcs}} {{> "modules/compendium-browser/templates/npc-browser-list.html" npcs=npcs}}
</ul> </ul>
<span class="loading" id="CBNpcsMessage" style="flex: 0"></span> <span class="loading" id="CBNpcsMessage" style="flex: 0"></span>
</div> </div>

View File

@ -24,11 +24,11 @@
<button id="reset-spell-filter">{{localize "CMPBrowser.Filters.ResetFilters"}}</button> <button id="reset-spell-filter">{{localize "CMPBrowser.Filters.ResetFilters"}}</button>
<!-- <button id="copy-search-spell">Export to <i class="fa-brands fa-square-js"></i></button> --> <!-- <button id="copy-search-spell">Export to <i class="fa-brands fa-square-js"></i></button> -->
</div> </div>
{{> "modules/compendium-browser/template/filter-container.html" filters=spellFilters}} {{> "modules/compendium-browser/templates/filter-container.html" filters=spellFilters}}
</div> </div>
<div class="list-area flexcol"> <div class="list-area flexcol">
<ul id="CBSpells"> <ul id="CBSpells">
{{> "modules/compendium-browser/template/spell-browser-list.html" spells=items}} {{> "modules/compendium-browser/templates/spell-browser-list.html" spells=items}}
</ul> </ul>
<span class="loading" id="CBSpellsMessage" style="flex: 0"></span> <span class="loading" id="CBSpellsMessage" style="flex: 0"></span>
</div> </div>

View File

@ -16,23 +16,23 @@
<div class="content"> <div class="content">
<div class="tab" data-tab="spell"> <div class="tab" data-tab="spell">
{{#if showSpellBrowser}}{{> {{#if showSpellBrowser}}{{>
"modules/compendium-browser/template/spell-browser.html"}}{{/if}} "modules/compendium-browser/templates/spell-browser.html"}}{{/if}}
</div> </div>
<div class="tab" data-tab="feat"> <div class="tab" data-tab="feat">
{{#if showFeatBrowser}}{{> {{#if showFeatBrowser}}{{>
"modules/compendium-browser/template/feat-browser.html"}}{{/if}} "modules/compendium-browser/templates/feat-browser.html"}}{{/if}}
</div> </div>
<div class="tab" data-tab="item"> <div class="tab" data-tab="item">
{{#if showItemBrowser}}{{> {{#if showItemBrowser}}{{>
"modules/compendium-browser/template/item-browser.html"}}{{/if}} "modules/compendium-browser/templates/item-browser.html"}}{{/if}}
</div> </div>
<div class="tab" data-tab="npc"> <div class="tab" data-tab="npc">
{{#if showNpcBrowser}} {{> {{#if showNpcBrowser}} {{>
"modules/compendium-browser/template/npc-browser.html"}}{{/if}} "modules/compendium-browser/templates/npc-browser.html"}}{{/if}}
</div> </div>
<div class="tab" data-tab="setting"> <div class="tab" data-tab="setting">
{{#if isGM}} {{> {{#if isGM}} {{>
"modules/compendium-browser/template/settings.html"}}{{/if}} "modules/compendium-browser/templates/settings.html"}}{{/if}}
</div> </div>
</div> </div>
</div> </div>