661 lines
22 KiB
JavaScript
661 lines
22 KiB
JavaScript
/**
|
|
* The Application responsible for configuring a single Token document within a parent Scene.
|
|
* @param {TokenDocument|Actor} object The {@link TokenDocument} being configured or an {@link Actor} for whom
|
|
* to configure the {@link PrototypeToken}
|
|
* @param {FormApplicationOptions} [options] Application configuration options.
|
|
*/
|
|
class TokenConfig extends DocumentSheet {
|
|
constructor(object, options) {
|
|
super(object, options);
|
|
|
|
/**
|
|
* The placed Token object in the Scene
|
|
* @type {TokenDocument}
|
|
*/
|
|
this.token = this.object;
|
|
|
|
/**
|
|
* A reference to the Actor which the token depicts
|
|
* @type {Actor}
|
|
*/
|
|
this.actor = this.object.actor;
|
|
|
|
// Configure options
|
|
if ( this.isPrototype ) this.options.sheetConfig = false;
|
|
}
|
|
|
|
/**
|
|
* Maintain a copy of the original to show a real-time preview of changes.
|
|
* @type {TokenDocument|PrototypeToken}
|
|
*/
|
|
preview;
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
static get defaultOptions() {
|
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
|
classes: ["sheet", "token-sheet"],
|
|
template: "templates/scene/token-config.html",
|
|
width: 480,
|
|
height: "auto",
|
|
tabs: [
|
|
{navSelector: '.tabs[data-group="main"]', contentSelector: "form", initial: "character"},
|
|
{navSelector: '.tabs[data-group="light"]', contentSelector: '.tab[data-tab="light"]', initial: "basic"},
|
|
{navSelector: '.tabs[data-group="vision"]', contentSelector: '.tab[data-tab="vision"]', initial: "basic"}
|
|
],
|
|
viewPermission: CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER,
|
|
sheetConfig: true
|
|
});
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* A convenience accessor to test whether we are configuring the prototype Token for an Actor.
|
|
* @type {boolean}
|
|
*/
|
|
get isPrototype() {
|
|
return this.object instanceof foundry.data.PrototypeToken;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @inheritDoc */
|
|
get id() {
|
|
if ( this.isPrototype ) return `${this.constructor.name}-${this.actor.uuid}`;
|
|
else return super.id;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
|
|
/** @inheritdoc */
|
|
get title() {
|
|
if ( this.isPrototype ) return `${game.i18n.localize("TOKEN.TitlePrototype")}: ${this.actor.name}`;
|
|
return `${game.i18n.localize("TOKEN.Title")}: ${this.token.name}`;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
render(force=false, options={}) {
|
|
if ( this.isPrototype ) this.object.actor.apps[this.appId] = this;
|
|
return super.render(force, options);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
async _render(force, options={}) {
|
|
await this._handleTokenPreview(force, options);
|
|
return super._render(force, options);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Handle preview with a token.
|
|
* @param {boolean} force
|
|
* @param {object} options
|
|
* @returns {Promise<void>}
|
|
* @protected
|
|
*/
|
|
async _handleTokenPreview(force, options={}) {
|
|
const states = Application.RENDER_STATES;
|
|
if ( force && [states.CLOSED, states.NONE].includes(this._state) ) {
|
|
if ( this.isPrototype ) {
|
|
this.preview = this.object.clone();
|
|
return;
|
|
}
|
|
if ( !this.document.object ) {
|
|
this.preview = null;
|
|
return;
|
|
}
|
|
if ( !this.preview ) {
|
|
const clone = this.document.object.clone({}, {keepId: true});
|
|
this.preview = clone.document;
|
|
clone.control({releaseOthers: true});
|
|
}
|
|
await this.preview.object.draw();
|
|
this.document.object.renderable = false;
|
|
this.document.object.initializeSources({deleted: true});
|
|
this.preview.object.layer.preview.addChild(this.preview.object);
|
|
this._previewChanges();
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @inheritDoc */
|
|
_canUserView(user) {
|
|
const canView = super._canUserView(user);
|
|
return canView && game.user.can("TOKEN_CONFIGURE");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
async getData(options={}) {
|
|
const alternateImages = await this._getAlternateTokenImages();
|
|
const usesTrackableAttributes = !foundry.utils.isEmpty(CONFIG.Actor.trackableAttributes);
|
|
const attributeSource = (this.actor?.system instanceof foundry.abstract.DataModel) && usesTrackableAttributes
|
|
? this.actor?.type
|
|
: this.actor?.system;
|
|
const attributes = TokenDocument.implementation.getTrackedAttributes(attributeSource);
|
|
const canBrowseFiles = game.user.hasPermission("FILES_BROWSE");
|
|
|
|
// Prepare Token data
|
|
const doc = this.preview ?? this.document;
|
|
const source = doc.toObject();
|
|
const sourceDetectionModes = new Set(source.detectionModes.map(m => m.id));
|
|
const preparedDetectionModes = doc.detectionModes.filter(m => !sourceDetectionModes.has(m.id));
|
|
|
|
// Return rendering context
|
|
return {
|
|
fields: this.document.schema.fields, // Important to use the true document schema,
|
|
lightFields: this.document.schema.fields.light.fields,
|
|
cssClasses: [this.isPrototype ? "prototype" : null].filter(c => !!c).join(" "),
|
|
isPrototype: this.isPrototype,
|
|
hasAlternates: !foundry.utils.isEmpty(alternateImages),
|
|
alternateImages: alternateImages,
|
|
object: source,
|
|
options: this.options,
|
|
gridUnits: (this.isPrototype ? "" : this.document.parent?.grid.units) || game.i18n.localize("GridUnits"),
|
|
barAttributes: TokenDocument.implementation.getTrackedAttributeChoices(attributes),
|
|
bar1: doc.getBarAttribute?.("bar1"),
|
|
bar2: doc.getBarAttribute?.("bar2"),
|
|
colorationTechniques: AdaptiveLightingShader.SHADER_TECHNIQUES,
|
|
visionModes: Object.values(CONFIG.Canvas.visionModes).filter(f => f.tokenConfig),
|
|
detectionModes: Object.values(CONFIG.Canvas.detectionModes).filter(f => f.tokenConfig),
|
|
preparedDetectionModes,
|
|
displayModes: Object.entries(CONST.TOKEN_DISPLAY_MODES).reduce((obj, e) => {
|
|
obj[e[1]] = game.i18n.localize(`TOKEN.DISPLAY_${e[0]}`);
|
|
return obj;
|
|
}, {}),
|
|
hexagonalShapes: Object.entries(CONST.TOKEN_HEXAGONAL_SHAPES).reduce((obj, [k, v]) => {
|
|
obj[v] = game.i18n.localize(`TOKEN.HEXAGONAL_SHAPE_${k}`);
|
|
return obj;
|
|
}, {}),
|
|
showHexagonalShapes: this.isPrototype || !doc.parent || doc.parent.grid.isHexagonal,
|
|
actors: game.actors.reduce((actors, a) => {
|
|
if ( !a.isOwner ) return actors;
|
|
actors.push({_id: a.id, name: a.name});
|
|
return actors;
|
|
}, []).sort((a, b) => a.name.localeCompare(b.name, game.i18n.lang)),
|
|
dispositions: Object.entries(CONST.TOKEN_DISPOSITIONS).reduce((obj, e) => {
|
|
obj[e[1]] = game.i18n.localize(`TOKEN.DISPOSITION.${e[0]}`);
|
|
return obj;
|
|
}, {}),
|
|
lightAnimations: CONFIG.Canvas.lightAnimations,
|
|
isGM: game.user.isGM,
|
|
randomImgEnabled: this.isPrototype && (canBrowseFiles || doc.randomImg),
|
|
scale: Math.abs(doc.texture.scaleX),
|
|
mirrorX: doc.texture.scaleX < 0,
|
|
mirrorY: doc.texture.scaleY < 0,
|
|
textureFitModes: CONST.TEXTURE_DATA_FIT_MODES.reduce((obj, fit) => {
|
|
obj[fit] = game.i18n.localize(`TEXTURE_DATA.FIT.${fit}`);
|
|
return obj;
|
|
}, {}),
|
|
ringEffectsInput: this.#ringEffectsInput.bind(this)
|
|
};
|
|
}
|
|
|
|
/* --------------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
async _renderInner(...args) {
|
|
await loadTemplates([
|
|
"templates/scene/parts/token-lighting.hbs",
|
|
"templates/scene/parts/token-vision.html",
|
|
"templates/scene/parts/token-resources.html"
|
|
]);
|
|
return super._renderInner(...args);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Render the Token ring effects input using a multi-checkbox element.
|
|
* @param {NumberField} field The ring effects field
|
|
* @param {FormInputConfig} inputConfig Form input configuration
|
|
* @returns {HTMLMultiCheckboxElement}
|
|
*/
|
|
#ringEffectsInput(field, inputConfig) {
|
|
const options = [];
|
|
const value = [];
|
|
for ( const [effectName, effectValue] of Object.entries(CONFIG.Token.ring.ringClass.effects) ) {
|
|
const localization = CONFIG.Token.ring.effects[effectName];
|
|
if ( (effectName === "DISABLED") || (effectName === "ENABLED") || !localization ) continue;
|
|
options.push({value: effectName, label: game.i18n.localize(localization)});
|
|
if ( (inputConfig.value & effectValue) !== 0 ) value.push(effectName);
|
|
}
|
|
Object.assign(inputConfig, {name: field.fieldPath, options, value, type: "checkboxes"});
|
|
return foundry.applications.fields.createMultiSelectInput(inputConfig);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Get an Object of image paths and filenames to display in the Token sheet
|
|
* @returns {Promise<object>}
|
|
* @private
|
|
*/
|
|
async _getAlternateTokenImages() {
|
|
if ( !this.actor?.prototypeToken.randomImg ) return {};
|
|
const alternates = await this.actor.getTokenImages();
|
|
return alternates.reduce((obj, img) => {
|
|
obj[img] = img.split("/").pop();
|
|
return obj;
|
|
}, {});
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
/* Event Listeners and Handlers */
|
|
/* -------------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
activateListeners(html) {
|
|
super.activateListeners(html);
|
|
html.find(".action-button").click(this._onClickActionButton.bind(this));
|
|
html.find(".bar-attribute").change(this._onBarChange.bind(this));
|
|
html.find(".alternate-images").change(ev => ev.target.form["texture.src"].value = ev.target.value);
|
|
html.find("button.assign-token").click(this._onAssignToken.bind(this));
|
|
this._disableEditImage();
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
async close(options={}) {
|
|
const states = Application.RENDER_STATES;
|
|
if ( options.force || [states.RENDERED, states.ERROR].includes(this._state) ) {
|
|
this._resetPreview();
|
|
}
|
|
await super.close(options);
|
|
if ( this.isPrototype ) delete this.object.actor.apps?.[this.appId];
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @inheritDoc */
|
|
_getSubmitData(updateData={}) {
|
|
const formData = foundry.utils.expandObject(super._getSubmitData(updateData));
|
|
|
|
// Prototype Token unpacking
|
|
if ( this.document instanceof foundry.data.PrototypeToken ) {
|
|
Object.assign(formData, formData.prototypeToken);
|
|
delete formData.prototypeToken;
|
|
}
|
|
|
|
// Mirror token scale
|
|
if ( "scale" in formData ) {
|
|
formData.texture.scaleX = formData.scale * (formData.mirrorX ? -1 : 1);
|
|
formData.texture.scaleY = formData.scale * (formData.mirrorY ? -1 : 1);
|
|
}
|
|
["scale", "mirrorX", "mirrorY"].forEach(k => delete formData[k]);
|
|
|
|
// Token Ring Effects
|
|
if ( Array.isArray(formData.ring?.effects) ) {
|
|
const TRE = CONFIG.Token.ring.ringClass.effects;
|
|
let effects = formData.ring.enabled ? TRE.ENABLED : TRE.DISABLED;
|
|
for ( const effectName of formData.ring.effects ) {
|
|
const v = TRE[effectName] ?? 0;
|
|
effects |= v;
|
|
}
|
|
formData.ring.effects = effects;
|
|
}
|
|
|
|
// Clear detection modes array
|
|
formData.detectionModes ??= [];
|
|
|
|
// Treat "None" as null for bar attributes
|
|
formData.bar1.attribute ||= null;
|
|
formData.bar2.attribute ||= null;
|
|
return foundry.utils.flattenObject(formData);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @inheritDoc */
|
|
async _onChangeInput(event) {
|
|
await super._onChangeInput(event);
|
|
|
|
// Disable image editing for wildcards
|
|
this._disableEditImage();
|
|
|
|
// Pre-populate vision mode defaults
|
|
const element = event.target;
|
|
if ( element.name === "sight.visionMode" ) {
|
|
const visionDefaults = CONFIG.Canvas.visionModes[element.value]?.vision?.defaults || {};
|
|
const update = fieldName => {
|
|
const field = this.form.querySelector(`[name="sight.${fieldName}"]`);
|
|
if ( fieldName in visionDefaults ) {
|
|
const value = visionDefaults[fieldName];
|
|
if ( value === undefined ) return;
|
|
if ( field.type === "checkbox" ) {
|
|
field.checked = value;
|
|
} else if ( field.type === "range" ) {
|
|
field.value = value;
|
|
const rangeValue = field.parentNode.querySelector(".range-value");
|
|
if ( rangeValue ) rangeValue.innerText = value;
|
|
} else if ( field.classList.contains("color") ) {
|
|
field.value = value;
|
|
const colorInput = field.parentNode.querySelector('input[type="color"]');
|
|
if ( colorInput ) colorInput.value = value;
|
|
} else {
|
|
field.value = value;
|
|
}
|
|
}
|
|
};
|
|
for ( const fieldName of ["color", "attenuation", "brightness", "saturation", "contrast"] ) update(fieldName);
|
|
}
|
|
|
|
// Preview token changes
|
|
const previewData = this._getSubmitData();
|
|
this._previewChanges(previewData);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Mimic changes to the Token document as if they were true document updates.
|
|
* @param {object} [change] The change to preview.
|
|
* @protected
|
|
*/
|
|
_previewChanges(change) {
|
|
if ( !this.preview ) return;
|
|
if ( change ) {
|
|
change = {...change};
|
|
delete change.actorId;
|
|
delete change.actorLink;
|
|
this.preview.updateSource(change);
|
|
}
|
|
if ( !this.isPrototype && (this.preview.object?.destroyed === false) ) {
|
|
this.preview.object.initializeSources();
|
|
this.preview.object.renderFlags.set({refresh: true});
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Reset the temporary preview of the Token when the form is submitted or closed.
|
|
* @protected
|
|
*/
|
|
_resetPreview() {
|
|
if ( !this.preview ) return;
|
|
if ( this.isPrototype ) return this.preview = null;
|
|
if ( this.preview.object?.destroyed === false ) {
|
|
this.preview.object.destroy({children: true});
|
|
}
|
|
this.preview.baseActor?._unregisterDependentToken(this.preview);
|
|
this.preview = null;
|
|
if ( this.document.object?.destroyed === false ) {
|
|
this.document.object.renderable = true;
|
|
this.document.object.initializeSources();
|
|
this.document.object.control();
|
|
this.document.object.renderFlags.set({refresh: true});
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
async _updateObject(event, formData) {
|
|
this._resetPreview();
|
|
return this.token.update(formData);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Handle Token assignment requests to update the default prototype Token
|
|
* @param {MouseEvent} event The left-click event on the assign token button
|
|
* @private
|
|
*/
|
|
async _onAssignToken(event) {
|
|
event.preventDefault();
|
|
|
|
// Get controlled Token data
|
|
let tokens = canvas.ready ? canvas.tokens.controlled : [];
|
|
if ( tokens.length !== 1 ) {
|
|
ui.notifications.warn("TOKEN.AssignWarn", {localize: true});
|
|
return;
|
|
}
|
|
const token = tokens.pop().document.toObject();
|
|
token.tokenId = token.x = token.y = null;
|
|
token.randomImg = this.form.elements.randomImg.checked;
|
|
if ( token.randomImg ) delete token.texture.src;
|
|
|
|
// Update the prototype token for the actor using the existing Token instance
|
|
await this.actor.update({prototypeToken: token}, {diff: false, recursive: false, noHook: true});
|
|
ui.notifications.info(game.i18n.format("TOKEN.AssignSuccess", {name: this.actor.name}));
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Handle changing the attribute bar in the drop-down selector to update the default current and max value
|
|
* @param {Event} event The select input change event
|
|
* @private
|
|
*/
|
|
async _onBarChange(event) {
|
|
const form = event.target.form;
|
|
const doc = this.preview ?? this.document;
|
|
const attr = doc.getBarAttribute("", {alternative: event.target.value});
|
|
const bar = event.target.name.split(".").shift();
|
|
form.querySelector(`input.${bar}-value`).value = attr !== null ? attr.value : "";
|
|
form.querySelector(`input.${bar}-max`).value = ((attr !== null) && (attr.type === "bar")) ? attr.max : "";
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Handle click events on a token configuration sheet action button
|
|
* @param {PointerEvent} event The originating click event
|
|
* @protected
|
|
*/
|
|
_onClickActionButton(event) {
|
|
event.preventDefault();
|
|
const button = event.currentTarget;
|
|
const action = button.dataset.action;
|
|
game.tooltip.deactivate();
|
|
|
|
// Get pending changes to modes
|
|
const modes = Object.values(foundry.utils.expandObject(this._getSubmitData())?.detectionModes || {});
|
|
|
|
// Manipulate the array
|
|
switch ( action ) {
|
|
case "addDetectionMode":
|
|
this._onAddDetectionMode(modes);
|
|
break;
|
|
case "removeDetectionMode":
|
|
const idx = button.closest(".detection-mode").dataset.index;
|
|
this._onRemoveDetectionMode(Number(idx), modes);
|
|
break;
|
|
}
|
|
|
|
this._previewChanges({detectionModes: modes});
|
|
this.render();
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Handle adding a detection mode.
|
|
* @param {object[]} modes The existing detection modes.
|
|
* @protected
|
|
*/
|
|
_onAddDetectionMode(modes) {
|
|
modes.push({id: "", range: 0, enabled: true});
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Handle removing a detection mode.
|
|
* @param {number} index The index of the detection mode to remove.
|
|
* @param {object[]} modes The existing detection modes.
|
|
* @protected
|
|
*/
|
|
_onRemoveDetectionMode(index, modes) {
|
|
modes.splice(index, 1);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Disable the user's ability to edit the token image field if wildcard images are enabled and that user does not have
|
|
* file browser permissions.
|
|
* @private
|
|
*/
|
|
_disableEditImage() {
|
|
const img = this.form.querySelector('[name="texture.src"]');
|
|
const randomImg = this.form.querySelector('[name="randomImg"]');
|
|
if ( randomImg ) img.disabled = !game.user.hasPermission("FILES_BROWSE") && randomImg.checked;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A sheet that alters the values of the default Token configuration used when new Token documents are created.
|
|
* @extends {TokenConfig}
|
|
*/
|
|
class DefaultTokenConfig extends TokenConfig {
|
|
constructor(object, options) {
|
|
const setting = game.settings.get("core", DefaultTokenConfig.SETTING);
|
|
const cls = getDocumentClass("Token");
|
|
object = new cls({name: "Default Token", ...setting}, {strict: false});
|
|
super(object, options);
|
|
}
|
|
|
|
/**
|
|
* The named world setting that stores the default Token configuration
|
|
* @type {string}
|
|
*/
|
|
static SETTING = "defaultToken";
|
|
|
|
/** @inheritdoc */
|
|
static get defaultOptions() {
|
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
|
template: "templates/scene/default-token-config.html",
|
|
sheetConfig: false
|
|
});
|
|
}
|
|
|
|
/* --------------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
get id() {
|
|
return "default-token-config";
|
|
}
|
|
|
|
/* --------------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
get title() {
|
|
return game.i18n.localize("SETTINGS.DefaultTokenN");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
get isEditable() {
|
|
return game.user.can("SETTINGS_MODIFY");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
_canUserView(user) {
|
|
return user.can("SETTINGS_MODIFY");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
async getData(options={}) {
|
|
const context = await super.getData(options);
|
|
return Object.assign(context, {
|
|
object: this.token.toObject(false),
|
|
isDefault: true,
|
|
barAttributes: TokenDocument.implementation.getTrackedAttributeChoices(),
|
|
bar1: this.token.bar1,
|
|
bar2: this.token.bar2
|
|
});
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
_getSubmitData(updateData = {}) {
|
|
const formData = foundry.utils.expandObject(super._getSubmitData(updateData));
|
|
formData.light.color = formData.light.color || undefined;
|
|
formData.bar1.attribute = formData.bar1.attribute || null;
|
|
formData.bar2.attribute = formData.bar2.attribute || null;
|
|
return formData;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
async _updateObject(event, formData) {
|
|
|
|
// Validate the default data
|
|
try {
|
|
this.object.updateSource(formData);
|
|
formData = foundry.utils.filterObject(this.token.toObject(), formData);
|
|
} catch(err) {
|
|
Hooks.onError("DefaultTokenConfig#_updateObject", err, {notify: "error"});
|
|
}
|
|
|
|
// Diff the form data against normal defaults
|
|
const defaults = foundry.documents.BaseToken.cleanData();
|
|
const delta = foundry.utils.diffObject(defaults, formData);
|
|
await game.settings.set("core", DefaultTokenConfig.SETTING, delta);
|
|
return SettingsConfig.reloadConfirm({ world: true });
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
activateListeners(html) {
|
|
super.activateListeners(html);
|
|
html.find('button[data-action="reset"]').click(this.reset.bind(this));
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Reset the form to default values
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async reset() {
|
|
const cls = getDocumentClass("Token");
|
|
this.object = new cls({}, {strict: false});
|
|
this.token = this.object;
|
|
this.render();
|
|
}
|
|
|
|
/* --------------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
async _onBarChange() {}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
_onAddDetectionMode(modes) {
|
|
super._onAddDetectionMode(modes);
|
|
this.document.updateSource({ detectionModes: modes });
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @inheritdoc */
|
|
_onRemoveDetectionMode(index, modes) {
|
|
super._onRemoveDetectionMode(index, modes);
|
|
this.document.updateSource({ detectionModes: modes });
|
|
}
|
|
}
|