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.

143 lines
5.3 KiB

  1. class OwnershipViewer {
  2. // Creates and applies the OwnershipViewer div to each document in a directory - called when each directory renders.
  3. static directoryRendered(obj, html, data) {
  4. if (!game.user.isGM || !obj._getEntryContextOptions) return;
  5. // Get the current directory's right-click context options, then tore the ownership config option
  6. const contextOptions = obj._getEntryContextOptions();
  7. const ownershipOption = contextOptions.find((e) => e.name === "OWNERSHIP.Configure");
  8. // Determine if we are working with a directory or a journal sheet
  9. const isJournalSheet = obj.constructor.name === "JournalSheet";
  10. // Gather all documents in the current directory or journal
  11. const collection = isJournalSheet ? obj.object.collections.pages : obj.constructor.collection;
  12. const documentList = html.find(`li.directory-item${isJournalSheet ? ".level1" : ".document"}`);
  13. // Interate through each directory list item.
  14. for (let li of documentList) {
  15. // Match it to the corresponding document
  16. li = $(li);
  17. const document = collection.get(li.attr(`data-${isJournalSheet ? "page" : "document"}-id`));
  18. const users = [];
  19. // Iterate through each ownership definition on the document
  20. for (let id in document.ownership) {
  21. const ownership = document.ownership[id] ?? 0;
  22. // If the ownership definition isn't 'None'...
  23. if (ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) {
  24. // Create the div for this ownership definition, with the appropriate class based on the ownership level
  25. const user_div = $(`<div data-user-id=${id}></div>`);
  26. // And if the ownership definition isn't 'All Players' (default) or a GM, set 'bg_color' to the user's color
  27. const user = game.users.get(id);
  28. if (id !== "default") {
  29. if (user && !user.isGM) {
  30. user_div.css({ "background-color": user.color });
  31. user_div.attr("data-tooltip", user.name);
  32. } else {
  33. continue;
  34. }
  35. }
  36. const ownerships = invertObject(CONST.DOCUMENT_OWNERSHIP_LEVELS);
  37. user_div.addClass(`ownership-viewer-${ownerships[ownership].toLowerCase()}`);
  38. user_div.attr(
  39. "data-tooltip",
  40. `${user ? user.name + ": " : ""} ${game.i18n.localize("OWNERSHIP." + ownerships[ownership])}`
  41. );
  42. user_div.attr("data-tooltip-direction", "UP");
  43. if (id == "default") {
  44. user_div.addClass("ownership-viewer-all");
  45. } else {
  46. user_div.addClass("ownership-viewer-user");
  47. }
  48. // Store the resulting div and keep iterating through the other ownership definitions on the document
  49. users.push(user_div);
  50. }
  51. }
  52. const div = $('<div class="ownership-viewer"></div>');
  53. // Append the collection of divs to the document's list item, or add the 'none set' icon if empty
  54. if (ownershipOption) {
  55. if (users.length === 0) {
  56. users.push($('<div><i class="fas fa-share-alt" style="color: white;"></i></div>'));
  57. }
  58. const anchor = $(`<a href="#"></a>`);
  59. div.append(anchor);
  60. anchor.append(...users);
  61. } else {
  62. div.append(...users);
  63. }
  64. if (isJournalSheet) {
  65. li.find(".page-ownership").remove();
  66. li.find(".page-heading").append(div);
  67. } else {
  68. li.append(div);
  69. }
  70. }
  71. // Ensure any clicks on the OwnershipViewer div open the ownership config for that document
  72. if (ownershipOption) {
  73. function registerClickEvents() {
  74. html.find(".ownership-viewer").click((event) => {
  75. event.preventDefault();
  76. event.stopPropagation();
  77. const li = $(event.currentTarget).closest("li");
  78. if (li) ownershipOption.callback(li);
  79. });
  80. }
  81. if (isJournalSheet) {
  82. // On journal sheets, delay registering click events until the page is selected and its header is expanded
  83. Hooks.once("renderJournalPageSheet", () => {
  84. registerClickEvents();
  85. });
  86. } else {
  87. registerClickEvents();
  88. }
  89. }
  90. }
  91. // Update the user color in OwnershipViewer divs if the user is edited
  92. static userUpdated(user) {
  93. for (let user_div of $(".ownership-viewer-user")) {
  94. let id = $(user_div).attr("data-user-id");
  95. if (id == user.id) {
  96. $(user_div).css("background-color", user.color);
  97. }
  98. }
  99. }
  100. // Makes the color assigned to each player clearer in the player list if they are inactive.
  101. static playerListRendered(list, html, options) {
  102. if (!options.showOffline) return;
  103. const userIdColorMap = game.users.contents
  104. .filter((user) => !user.active)
  105. .reduce((map, user) => {
  106. map[user.id] = user.color;
  107. return map;
  108. }, {});
  109. const players = html[0].querySelectorAll("span.player-active.inactive");
  110. for (let player of players) {
  111. const id = player.parentElement.dataset.userId;
  112. player.style.borderColor = userIdColorMap[id];
  113. }
  114. }
  115. }
  116. Hooks.on("renderJournalSheet", OwnershipViewer.directoryRendered);
  117. Hooks.on("renderJournalDirectory", OwnershipViewer.directoryRendered);
  118. Hooks.on("renderSceneDirectory", OwnershipViewer.directoryRendered);
  119. Hooks.on("renderActorDirectory", OwnershipViewer.directoryRendered);
  120. Hooks.on("renderItemDirectory", OwnershipViewer.directoryRendered);
  121. Hooks.on("renderMacroDirectory", OwnershipViewer.directoryRendered);
  122. Hooks.on("renderRollTableDirectory", OwnershipViewer.directoryRendered);
  123. Hooks.on("renderCardsDirectory", OwnershipViewer.directoryRendered);
  124. Hooks.on("updateUser", OwnershipViewer.userUpdated);
  125. Hooks.on("renderPlayerList", OwnershipViewer.playerListRendered);