import { getTokenConfig, setTokenConfig } from '../scripts/utils.js'; export default class TokenCustomConfig extends TokenConfig { constructor(object, options, imgSrc, imgName, callback, config) { let token; if (object instanceof Actor) { token = new TokenDocument(object.token, { actor: object, }); } else { token = new TokenDocument(object.document, { actor: game.actors.get(object.actorId), }); } super(token, options); this.imgSrc = imgSrc; this.imgName = imgName; this.callback = callback; this.config = config; if (this.config) { this.flags = this.config.flags; this.tv_script = this.config.tv_script; } } _getSubmitData(updateData = {}) { if (!this.form) throw new Error('The FormApplication subclass has no registered form element'); const fd = new FormDataExtended(this.form, { editors: this.editors }); let data = fd.object; if (updateData) data = foundry.utils.flattenObject(foundry.utils.mergeObject(data, updateData)); // Clear detection modes array if (!('detectionModes.0.id' in data)) data.detectionModes = []; // Treat "None" as null for bar attributes data['bar1.attribute'] ||= null; data['bar2.attribute'] ||= null; return data; } async _updateObject(event, formData) { const filtered = {}; const form = $(event.target).closest('form'); form.find('.form-group').each(function (_) { const tva_checkbox = $(this).find('.tva-config-checkbox > input'); if (tva_checkbox.length && tva_checkbox.is(':checked')) { $(this) .find('[name]') .each(function (_) { const name = $(this).attr('name'); filtered[name] = formData[name]; }); } }); if (this.tv_script) { filtered.tv_script = this.tv_script; } if (this.config) { let config = expandObject(filtered); config.flags = config.flags ? mergeObject(this.flags || {}, config.flags) : this.flags; if (this.callback) this.callback(config); } else { const saved = setTokenConfig(this.imgSrc, this.imgName, filtered); if (this.callback) this.callback(saved); } } applyCustomConfig() { const tokenConfig = flattenObject(this.config || getTokenConfig(this.imgSrc, this.imgName)); const form = $(this.form); for (const key of Object.keys(tokenConfig)) { const el = form.find(`[name="${key}"]`); if (el.is(':checkbox')) { el.prop('checked', tokenConfig[key]); } else { el.val(tokenConfig[key]); } el.trigger('change'); } } // ************* // consider moving html injection to: // _replaceHTML | _injectHTML async activateListeners(html) { await super.activateListeners(html); // Disable image path controls $(html).find('.token-variants-image-select-button').prop('disabled', true); $(html).find('.file-picker').prop('disabled', true); $(html).find('.image').prop('disabled', true); // Remove 'Assign Token' button $(html).find('.assign-token').remove(); // Add checkboxes to control inclusion of specific tabs in the custom config const tokenConfig = this.config || getTokenConfig(this.imgSrc, this.imgName); this.tv_script = tokenConfig.tv_script; $(html).on('change', '.tva-config-checkbox', this._onCheckboxChange); const processFormGroup = function (formGroup) { // Checkbox is not added for the Image Path group if (!$(formGroup).find('[name="img"]').length) { let savedField = false; if (tokenConfig) { const flatConfig = flattenObject(tokenConfig); $(formGroup) .find('[name]') .each(function (_) { const name = $(this).attr('name'); if (name in flatConfig) { savedField = true; } }); } const checkbox = $( `
` ); if ($(formGroup).find('p.hint').length) { $(formGroup).find('p.hint').before(checkbox); } else { $(formGroup).append(checkbox); } checkbox.find('input').trigger('change'); } }; // Add checkboxes to each form-group to control highlighting and which fields will are to be saved $(html) .find('.form-group') .each(function (index) { processFormGroup(this); }); // Add 'update' and 'remove' config buttons $(html).find('.sheet-footer > button').remove(); $(html) .find('.sheet-footer') .append(''); if (tokenConfig) { $(html) .find('.sheet-footer') .append(''); html.find('.remove-config').click(this._onRemoveConfig.bind(this)); } // Pre-select image or appearance tab $(html).find('.tabs > .item[data-tab="image"] > i').trigger('click'); $(html).find('.tabs > .item[data-tab="appearance"] > i').trigger('click'); document.activeElement.blur(); // Hack fix for key UP/DOWN effects not registering after config has been opened // TokenConfig might be changed by some modules after activateListeners is processed // Look out for these updates and add checkboxes for any newly added form-groups const mutate = (mutations) => { mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { if (node.nodeName === 'DIV' && node.className === 'form-group') { processFormGroup(node); this.applyCustomConfig(); } }); }); }; const observer = new MutationObserver(mutate); observer.observe(html[0], { characterData: false, attributes: false, childList: true, subtree: true, }); // On any field being changed we want to automatically select the form-group to be included in the update $(html).on('change', 'input, select', onInputChange); $(html).on('click', 'button', onInputChange); this.applyCustomConfig(); } async _onCheckboxChange(event) { const checkbox = $(event.target); checkbox.closest('.form-group').css({ 'outline-color': checkbox.is(':checked') ? 'green' : '#ffcc6e', 'outline-width': '2px', 'outline-style': 'dotted', 'margin-bottom': '5px', }); checkbox.closest('.tva-config-checkbox').css({ 'outline-color': checkbox.is(':checked') ? 'green' : '#ffcc6e', 'outline-width': '2px', 'outline-style': 'solid', }); } async _onRemoveConfig(event) { if (this.config) { if (this.callback) this.callback({}); } else { const saved = setTokenConfig(this.imgSrc, this.imgName, null); if (this.callback) this.callback(saved); } this.close(); } get id() { return `token-custom-config-${this.object.id}`; } _getHeaderButtons() { const buttons = super._getHeaderButtons(); return buttons; } } // Toggle checkbox if input has been detected inside it's form-group async function onInputChange(event) { if (event.target.parentNode.className === 'tva-config-checkbox') return; $(event.target).closest('.form-group').find('.tva-config-checkbox input').prop('checked', true); }