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.

192 lines
9.1 KiB

  1. //injectConfig library by @theripper93
  2. //License: MIT
  3. //Documentation: https://github.com/theripper93/injectConfig
  4. export var injectConfig = {
  5. inject: function injectConfig(app,html,data,object){
  6. this._generateTabStruct(app,html,data,object);
  7. const tabSize = data.tab?.width ?? 100;
  8. object = object || app.object;
  9. const moduleId = data.moduleId;
  10. let injectPoint
  11. if(typeof data.inject === "string"){
  12. injectPoint = html.find(data.inject).first().closest(".form-group");
  13. }else{
  14. injectPoint = data.inject;
  15. }
  16. injectPoint = injectPoint ? $(injectPoint) : (data.tab ? html.find("form > .tab").last() : html.find(".form-group").last());
  17. let injectHtml = "";
  18. for(const [k,v] of Object.entries(data)){
  19. if(k === "moduleId" || k === "inject" || k === "tab") continue;
  20. const elemData = data[k];
  21. const flag = "flags." + moduleId + "." + (k || "");
  22. const flagValue = object?.getFlag(moduleId, k) ?? elemData.default ?? getDefaultFlag(k);
  23. const notes = v.notes ? `<p class="notes">${v.notes}</p>` : "";
  24. v.label = v.units ? v.label+`<span class="units"> (${v.units})</span>` : v.label;
  25. switch(elemData.type){
  26. case "text":
  27. injectHtml += `<div class="form-group">
  28. <label for="${k}">${v.label || ""}</label>
  29. <input type="text" name="${flag}" ${elemData.dType ? `data-dtype="${elemData.dType}"` : ""} value="${flagValue}" placeholder="${v.placeholder || ""}">${notes}
  30. </div>`;
  31. break;
  32. case "number":
  33. injectHtml += `<div class="form-group">
  34. <label for="${k}">${v.label || ""}</label>
  35. <input type="number" name="${flag}" min="${v.min}" max="${v.max}" step="${v.step ?? 1}" value="${flagValue}" placeholder="${v.placeholder || ""}">${notes}
  36. </div>`;
  37. break;
  38. case "checkbox":
  39. injectHtml += `<div class="form-group">
  40. <label for="${k}">${v.label || ""}</label>
  41. <input type="checkbox" name="${flag}" ${flagValue ? "checked" : ""}>${notes}
  42. </div>`;
  43. break;
  44. case "select":
  45. injectHtml += `<div class="form-group">
  46. <label for="${k}">${v.label || ""}</label>
  47. <select name="${flag}" ${elemData.dType ? `data-dtype="${elemData.dType}"` : ""}>`;
  48. for(const [i,j] of Object.entries(v.options)){
  49. injectHtml += `<option value="${i}" ${flagValue == i ? "selected" : ""}>${j}</option>`;
  50. }
  51. injectHtml += `</select>${notes}
  52. </div>`;
  53. break;
  54. case "range":
  55. injectHtml += `<div class="form-group">
  56. <label for="${k}">${v.label || ""}</label>
  57. <div class="form-fields">
  58. <input type="range" name="${flag}" value="${flagValue}" min="${v.min}" max="${v.max}" step="${v.step ?? 1}">
  59. <span class="range-value">${flagValue}</span>${notes}
  60. </div>
  61. </div>`;
  62. break;
  63. case "color":
  64. injectHtml += `<div class="form-group">
  65. <label for="${k}">${v.label || ""}</label>
  66. <div class="form-fields">
  67. <input class="color" type="text" name="${flag}" value="${flagValue}">
  68. <input type="color" data-edit="${flag}" value="${flagValue}">
  69. </div>
  70. ${notes}
  71. </div>`;
  72. break;
  73. case "custom":
  74. injectHtml += v.html;
  75. break;
  76. }
  77. if(elemData.type?.includes("filepicker")){
  78. const fpType = elemData.type.split(".")[1] || "imagevideo";
  79. injectHtml += `<div class="form-group">
  80. <label for="${k}">${v.label || ""}</label>
  81. <div class="form-fields">
  82. <button type="button" class="file-picker" data-extras="${elemData.fpTypes ? elemData.fpTypes.join(",") : ""}" data-type="${fpType}" data-target="${flag}" title="Browse Files" tabindex="-1">
  83. <i class="fas fa-file-import fa-fw"></i>
  84. </button>
  85. <input class="image" type="text" name="${flag}" placeholder="${v.placeholder || ""}" value="${flagValue}">
  86. </div>${notes}
  87. </div>`;
  88. }
  89. }
  90. injectHtml = $(injectHtml);
  91. injectHtml.on("click", ".file-picker", this.fpTypes,_bindFilePicker);
  92. injectHtml.on("change", `input[type="color"]`, _colorChange);
  93. if(data.tab){
  94. const injectTab = createTab(data.tab.name, data.tab.label, data.tab.icon).append(injectHtml);
  95. injectPoint.after(injectTab);
  96. app?.setPosition({"height" : "auto", "width" : data.tab ? app.options.width + tabSize : "auto"});
  97. return injectHtml;
  98. }
  99. injectPoint.after(injectHtml);
  100. if(app)app?.setPosition({"height" : "auto", "width" : data.tab ? app.options.width + tabSize : null});
  101. return injectHtml;
  102. function createTab(name,label,icon){
  103. /*let tabs = html.find(".sheet-tabs").last();
  104. if(!tabs.length) tabs = html.find(`nav[data-group="main"]`);*/
  105. const tabs = html.find(".sheet-tabs").first().find(".item").last();
  106. const tab = `<a class="item" data-tab="${name}"><i class="${icon}"></i> ${label}</a>`
  107. tabs.after(tab);
  108. const tabContainer = `<div class="tab" data-tab="${name}"></div>`
  109. return $(tabContainer);
  110. }
  111. function getDefaultFlag(inputType){
  112. switch(inputType){
  113. case "number":
  114. return 0;
  115. case "checkbox":
  116. return false;
  117. }
  118. return "";
  119. }
  120. function _colorChange(e){
  121. const input = $(e.target);
  122. const edit = input.data("edit");
  123. const value = input.val();
  124. injectHtml.find(`input[name="${edit}"]`).val(value);
  125. }
  126. function _bindFilePicker(event) {
  127. event.preventDefault();
  128. const button = event.currentTarget;
  129. const input = $(button).closest(".form-fields").find("input") || null;
  130. const extraExt = button.dataset.extras ? button.dataset.extras.split(",") : [];
  131. const options = {
  132. field: input[0],
  133. type: button.dataset.type,
  134. current: input.val() || null,
  135. button: button,
  136. }
  137. const fp = new FilePicker(options);
  138. fp.extensions ? fp.extensions.push(...extraExt) : fp.extensions = extraExt;
  139. return fp.browse();
  140. }
  141. },
  142. quickInject: function quickInject(injectData, data){
  143. injectData = Array.isArray(injectData) ? injectData : [injectData];
  144. for(const doc of injectData){
  145. let newData = data
  146. if(doc.inject){
  147. newData = JSON.parse(JSON.stringify(data))
  148. data.inject = doc.inject;
  149. }
  150. Hooks.on(`render${doc.documentName}Config`, (app,html)=>{ injectConfig.inject(app,html,newData) });
  151. }
  152. },
  153. _generateTabStruct : function _generateTabStruct(app,html,data,object){
  154. const isTabs = html.find(".sheet-tabs").length;
  155. const useTabs = data.tab
  156. if(isTabs || !useTabs) return;
  157. const tabSize = data.tab?.width || 100;
  158. const layer = app?.object?.layer?.options?.name
  159. const icon = $(".main-controls").find(`li[data-canvas-layer="${layer}"]`).find("i").attr("class")
  160. const $tabs = $(`<nav class="sheet-tabs tabs">
  161. <a class="item active" data-tab="basic"><i class="${icon}"></i> ${game.i18n.localize("LIGHT.HeaderBasic")}</a>
  162. </nav>
  163. <div class="tab active" data-tab="basic"></div>`);
  164. //move all content of form into tab
  165. const form = html.find("form").first();
  166. form.children().each((i,e)=>{
  167. $($tabs[2]).append(e);
  168. });
  169. form.append($tabs);
  170. const submitButton = html.find("button[type='submit']").first();
  171. form.append(submitButton);
  172. html.on("click", ".item", (e)=>{
  173. html.find(".item").removeClass("active");
  174. $(e.currentTarget).addClass("active");
  175. html.find(".tab").removeClass("active");
  176. html.find(`[data-tab="${e.currentTarget.dataset.tab}"]`).addClass("active");
  177. app.setPosition({"height" : "auto", "width" : data.tab ? app.options.width + tabSize : "auto"});
  178. });
  179. }
  180. }