All user data for FoundryVTT. Includes worlds, systems, modules, and any asset in the "foundryuserdata" directory. Does NOT include the FoundryVTT installation itself.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

317 lines
9.6 KiB

  1. import { toggleTemplateOnSelected } from '../scripts/hooks/effectMappingHooks.js';
  2. import { CORE_TEMPLATES } from '../scripts/mappingTemplates.js';
  3. import { TVA_CONFIG, updateSettings } from '../scripts/settings.js';
  4. import { BASE_IMAGE_CATEGORIES, uploadTokenImage } from '../scripts/utils.js';
  5. import { sortMappingsToGroups } from './effectMappingForm.js';
  6. import { Templates } from './templates.js';
  7. import TokenCustomConfig from './tokenCustomConfig.js';
  8. // Edit overlay configuration as a json string
  9. export function showOverlayJsonConfigDialog(overlayConfig, callback) {
  10. const config = deepClone(overlayConfig || {});
  11. delete config.effect;
  12. let content = `<div style="height: 300px;" class="form-group stacked command"><textarea style="height: 300px;" class="configJson">${JSON.stringify(
  13. config,
  14. null,
  15. 2
  16. )}</textarea></div>`;
  17. new Dialog({
  18. title: `Overlay Configuration`,
  19. content: content,
  20. buttons: {
  21. yes: {
  22. icon: "<i class='fas fa-save'></i>",
  23. label: 'Save',
  24. callback: (html) => {
  25. let json = $(html).find('.configJson').val();
  26. if (json) {
  27. try {
  28. json = JSON.parse(json);
  29. } catch (e) {
  30. console.warn(`TVA |`, e);
  31. json = {};
  32. }
  33. } else {
  34. json = {};
  35. }
  36. callback(json);
  37. },
  38. },
  39. },
  40. default: 'yes',
  41. }).render(true);
  42. }
  43. // Change categories assigned to a path
  44. export async function showPathSelectCategoryDialog(event) {
  45. event.preventDefault();
  46. const typesInput = $(event.target).closest('.path-category').find('input');
  47. const selectedTypes = typesInput.val().split(',');
  48. const categories = BASE_IMAGE_CATEGORIES.concat(TVA_CONFIG.customImageCategories);
  49. let content = '<div class="token-variants-popup-settings">';
  50. // Split into rows of 4
  51. const splits = [];
  52. let currSplit = [];
  53. for (let i = 0; i < categories.length; i++) {
  54. if (i > 0 && i + 1 != categories.length && i % 4 == 0) {
  55. splits.push(currSplit);
  56. currSplit = [];
  57. }
  58. currSplit.push(categories[i]);
  59. }
  60. if (currSplit.length) splits.push(currSplit);
  61. for (const split of splits) {
  62. content += '<header class="table-header flexrow">';
  63. for (const type of split) {
  64. content += `<label>${type}</label>`;
  65. }
  66. content += '</header><ul class="setting-list"><li class="setting form-group"><div class="form-fields">';
  67. for (const type of split) {
  68. content += `<input class="category" type="checkbox" name="${type}" data-dtype="Boolean" ${
  69. selectedTypes.includes(type) ? 'checked' : ''
  70. }>`;
  71. }
  72. content += '</div></li></ul>';
  73. }
  74. content += '</div>';
  75. new Dialog({
  76. title: `Image Categories/Filters`,
  77. content: content,
  78. buttons: {
  79. yes: {
  80. icon: "<i class='fas fa-save'></i>",
  81. label: 'Apply',
  82. callback: (html) => {
  83. const types = [];
  84. $(html)
  85. .find('.category')
  86. .each(function () {
  87. if ($(this).is(':checked')) {
  88. types.push($(this).attr('name'));
  89. }
  90. });
  91. typesInput.val(types.join(','));
  92. },
  93. },
  94. },
  95. default: 'yes',
  96. }).render(true);
  97. }
  98. // Change configs assigned to a path
  99. export async function showPathSelectConfigForm(event) {
  100. event.preventDefault();
  101. const configInput = $(event.target).closest('.path-config').find('input');
  102. let config = {};
  103. try {
  104. config = JSON.parse(configInput.val());
  105. } catch (e) {}
  106. const setting = game.settings.get('core', DefaultTokenConfig.SETTING);
  107. const data = new foundry.data.PrototypeToken(setting);
  108. const token = new TokenDocument(data, { actor: null });
  109. new TokenCustomConfig(
  110. token,
  111. {},
  112. null,
  113. null,
  114. (conf) => {
  115. if (!conf) conf = {};
  116. if (conf.flags == null || isEmpty(conf.flags)) delete conf.flags;
  117. configInput.val(JSON.stringify(conf));
  118. const cog = configInput.siblings('.select-config');
  119. if (isEmpty(conf)) cog.removeClass('active');
  120. else cog.addClass('active');
  121. },
  122. config
  123. ).render(true);
  124. }
  125. export async function showTokenCaptureDialog(token) {
  126. if (!token) return;
  127. let content = `<form>
  128. <div class="form-group">
  129. <label>Image Name</label>
  130. <input type="text" name="name" value="${token.name}">
  131. </div>
  132. <div class="form-group">
  133. <label>Image Path</label>
  134. <div class="form-fields">
  135. <input type="text" name="path" value="modules/token-variants/">
  136. <button type="button" class="file-picker" data-type="folder" data-target="path" title="Browse Folders" tabindex="-1">
  137. <i class="fas fa-file-import fa-fw"></i>
  138. </button>
  139. </div>
  140. </div>
  141. <div class="form-group slim">
  142. <label>Width <span class="units">(pixels)</span></label>
  143. <div class="form-fields">
  144. <input type="number" step="1" name="width" value="${token.mesh.texture.width}">
  145. </div>
  146. </div>
  147. <div class="form-group slim">
  148. <label>Height <span class="units">(pixels)</span></label>
  149. <div class="form-fields">
  150. <input type="number" step="1" name="height" value="${token.mesh.texture.height}">
  151. </div>
  152. </div>
  153. <div class="form-group slim">
  154. <label>Scale</label>
  155. <div class="form-fields">
  156. <input type="number" step="any" name="scale" value="3">
  157. </div>
  158. </div>
  159. </form>`;
  160. new Dialog({
  161. title: `Save Token/Overlay Image`,
  162. content: content,
  163. buttons: {
  164. yes: {
  165. icon: "<i class='fas fa-save'></i>",
  166. label: 'Save',
  167. callback: (html) => {
  168. const options = {};
  169. $(html)
  170. .find('[name]')
  171. .each(function () {
  172. let val = parseFloat(this.value);
  173. if (isNaN(val)) val = this.value;
  174. options[this.name] = val;
  175. });
  176. uploadTokenImage(token, options);
  177. },
  178. },
  179. },
  180. render: (html) => {
  181. html.find('.file-picker').click(() => {
  182. new FilePicker({
  183. type: 'folder',
  184. current: html.find('[name="path"]').val(),
  185. callback: (path) => {
  186. html.find('[name="path"]').val(path);
  187. },
  188. }).render();
  189. });
  190. },
  191. default: 'yes',
  192. }).render(true);
  193. }
  194. export function showMappingSelectDialog(
  195. mappings,
  196. { title1 = 'Mappings', title2 = 'Select Mappings', buttonTitle = 'Confirm', callback = null } = {}
  197. ) {
  198. if (!mappings || !mappings.length) return;
  199. let content = `<form style="overflow-y: scroll; height:400px;"><h2>${title2}</h2>`;
  200. const [_, mappingGroups] = sortMappingsToGroups(mappings);
  201. for (const [group, obj] of Object.entries(mappingGroups)) {
  202. if (obj.list.length) {
  203. content += `<h4 style="text-align:center;"><b>${group}</b></h4>`;
  204. for (const mapping of obj.list) {
  205. content += `
  206. <div class="form-group">
  207. <label>${mapping.label}</label>
  208. <div class="form-fields">
  209. <input type="checkbox" name="${mapping.id}" data-dtype="Boolean">
  210. </div>
  211. </div>
  212. `;
  213. }
  214. }
  215. }
  216. content += `</form><div class="form-group"><button type="button" class="select-all">Select all</div>`;
  217. new Dialog({
  218. title: title1,
  219. content: content,
  220. buttons: {
  221. Ok: {
  222. label: buttonTitle,
  223. callback: async (html) => {
  224. if (!callback) return;
  225. const selectedMappings = [];
  226. html.find('input[type="checkbox"]').each(function () {
  227. if (this.checked) {
  228. const mapping = mappings.find((m) => m.id === this.name);
  229. if (mapping) {
  230. const cMapping = deepClone(mapping);
  231. selectedMappings.push(cMapping);
  232. delete cMapping.targetActors;
  233. }
  234. }
  235. });
  236. callback(selectedMappings);
  237. },
  238. },
  239. },
  240. close: () => callback(null),
  241. render: (html) => {
  242. html.find('.select-all').click(() => {
  243. html.find('input[type="checkbox"]').prop('checked', true);
  244. });
  245. },
  246. }).render(true);
  247. }
  248. export function showUserTemplateCreateDialog(mappings) {
  249. let content = `
  250. <div class="form-group">
  251. <label>Template Name</label>
  252. <div class="form-fields">
  253. <input type="text" name="templateName" data-dtype="String" value="">
  254. </div>
  255. </div>
  256. <div class="form-group">
  257. <label>Hover Text (optional)</label>
  258. <div class="form-fields">
  259. <input type="text" name="templateHint" data-dtype="String" value="">
  260. </div>
  261. </div>
  262. <div class="form-group">
  263. <label>Hover Image (optional)</label>
  264. <div class="form-fields">
  265. <input type="text" name="img" data-dtype="String" value="">
  266. </div>
  267. </div>`;
  268. let dialog;
  269. dialog = new Dialog({
  270. title: 'Mapping Templates',
  271. content,
  272. buttons: {
  273. create: {
  274. label: 'Create Template',
  275. callback: (html) => {
  276. const name = html.find('[name="templateName"]').val().trim();
  277. const hint = html.find('[name="templateHint"]').val().trim();
  278. const img = html.find('[name="img"]').val().trim();
  279. if (name.trim()) {
  280. TVA_CONFIG.templateMappings.push({ id: randomID(), name, hint, img, mappings: deepClone(mappings) });
  281. updateSettings({ templateMappings: TVA_CONFIG.templateMappings });
  282. }
  283. },
  284. },
  285. cancel: {
  286. label: 'Cancel',
  287. },
  288. },
  289. default: 'cancel',
  290. });
  291. dialog.render(true);
  292. }
  293. export function toggleTemplateDialog() {
  294. new Templates({ callback: (templateName, mappings) => toggleTemplateOnSelected(templateName, mappings) }).render(
  295. true
  296. );
  297. }