optional_content_config.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * JavaScript code in this page
  4. *
  5. * Copyright 2022 Mozilla Foundation
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. *
  19. * @licend The above is the entire license notice for the
  20. * JavaScript code in this page
  21. */
  22. "use strict";
  23. Object.defineProperty(exports, "__esModule", {
  24. value: true
  25. });
  26. exports.OptionalContentConfig = void 0;
  27. var _util = require("../shared/util.js");
  28. const INTERNAL = Symbol("INTERNAL");
  29. class OptionalContentGroup {
  30. #visible = true;
  31. constructor(name, intent) {
  32. this.name = name;
  33. this.intent = intent;
  34. }
  35. get visible() {
  36. return this.#visible;
  37. }
  38. _setVisible(internal, visible) {
  39. if (internal !== INTERNAL) {
  40. (0, _util.unreachable)("Internal method `_setVisible` called.");
  41. }
  42. this.#visible = visible;
  43. }
  44. }
  45. class OptionalContentConfig {
  46. #cachedHasInitialVisibility = true;
  47. #groups = new Map();
  48. #initialVisibility = null;
  49. #order = null;
  50. constructor(data) {
  51. this.name = null;
  52. this.creator = null;
  53. if (data === null) {
  54. return;
  55. }
  56. this.name = data.name;
  57. this.creator = data.creator;
  58. this.#order = data.order;
  59. for (const group of data.groups) {
  60. this.#groups.set(group.id, new OptionalContentGroup(group.name, group.intent));
  61. }
  62. if (data.baseState === "OFF") {
  63. for (const group of this.#groups.values()) {
  64. group._setVisible(INTERNAL, false);
  65. }
  66. }
  67. for (const on of data.on) {
  68. this.#groups.get(on)._setVisible(INTERNAL, true);
  69. }
  70. for (const off of data.off) {
  71. this.#groups.get(off)._setVisible(INTERNAL, false);
  72. }
  73. this.#initialVisibility = new Map();
  74. for (const [id, group] of this.#groups) {
  75. this.#initialVisibility.set(id, group.visible);
  76. }
  77. }
  78. #evaluateVisibilityExpression(array) {
  79. const length = array.length;
  80. if (length < 2) {
  81. return true;
  82. }
  83. const operator = array[0];
  84. for (let i = 1; i < length; i++) {
  85. const element = array[i];
  86. let state;
  87. if (Array.isArray(element)) {
  88. state = this.#evaluateVisibilityExpression(element);
  89. } else if (this.#groups.has(element)) {
  90. state = this.#groups.get(element).visible;
  91. } else {
  92. (0, _util.warn)(`Optional content group not found: ${element}`);
  93. return true;
  94. }
  95. switch (operator) {
  96. case "And":
  97. if (!state) {
  98. return false;
  99. }
  100. break;
  101. case "Or":
  102. if (state) {
  103. return true;
  104. }
  105. break;
  106. case "Not":
  107. return !state;
  108. default:
  109. return true;
  110. }
  111. }
  112. return operator === "And";
  113. }
  114. isVisible(group) {
  115. if (this.#groups.size === 0) {
  116. return true;
  117. }
  118. if (!group) {
  119. (0, _util.warn)("Optional content group not defined.");
  120. return true;
  121. }
  122. if (group.type === "OCG") {
  123. if (!this.#groups.has(group.id)) {
  124. (0, _util.warn)(`Optional content group not found: ${group.id}`);
  125. return true;
  126. }
  127. return this.#groups.get(group.id).visible;
  128. } else if (group.type === "OCMD") {
  129. if (group.expression) {
  130. return this.#evaluateVisibilityExpression(group.expression);
  131. }
  132. if (!group.policy || group.policy === "AnyOn") {
  133. for (const id of group.ids) {
  134. if (!this.#groups.has(id)) {
  135. (0, _util.warn)(`Optional content group not found: ${id}`);
  136. return true;
  137. }
  138. if (this.#groups.get(id).visible) {
  139. return true;
  140. }
  141. }
  142. return false;
  143. } else if (group.policy === "AllOn") {
  144. for (const id of group.ids) {
  145. if (!this.#groups.has(id)) {
  146. (0, _util.warn)(`Optional content group not found: ${id}`);
  147. return true;
  148. }
  149. if (!this.#groups.get(id).visible) {
  150. return false;
  151. }
  152. }
  153. return true;
  154. } else if (group.policy === "AnyOff") {
  155. for (const id of group.ids) {
  156. if (!this.#groups.has(id)) {
  157. (0, _util.warn)(`Optional content group not found: ${id}`);
  158. return true;
  159. }
  160. if (!this.#groups.get(id).visible) {
  161. return true;
  162. }
  163. }
  164. return false;
  165. } else if (group.policy === "AllOff") {
  166. for (const id of group.ids) {
  167. if (!this.#groups.has(id)) {
  168. (0, _util.warn)(`Optional content group not found: ${id}`);
  169. return true;
  170. }
  171. if (this.#groups.get(id).visible) {
  172. return false;
  173. }
  174. }
  175. return true;
  176. }
  177. (0, _util.warn)(`Unknown optional content policy ${group.policy}.`);
  178. return true;
  179. }
  180. (0, _util.warn)(`Unknown group type ${group.type}.`);
  181. return true;
  182. }
  183. setVisibility(id, visible = true) {
  184. if (!this.#groups.has(id)) {
  185. (0, _util.warn)(`Optional content group not found: ${id}`);
  186. return;
  187. }
  188. this.#groups.get(id)._setVisible(INTERNAL, !!visible);
  189. this.#cachedHasInitialVisibility = null;
  190. }
  191. get hasInitialVisibility() {
  192. if (this.#cachedHasInitialVisibility !== null) {
  193. return this.#cachedHasInitialVisibility;
  194. }
  195. for (const [id, group] of this.#groups) {
  196. const visible = this.#initialVisibility.get(id);
  197. if (group.visible !== visible) {
  198. return this.#cachedHasInitialVisibility = false;
  199. }
  200. }
  201. return this.#cachedHasInitialVisibility = true;
  202. }
  203. getOrder() {
  204. if (!this.#groups.size) {
  205. return null;
  206. }
  207. if (this.#order) {
  208. return this.#order.slice();
  209. }
  210. return [...this.#groups.keys()];
  211. }
  212. getGroups() {
  213. return this.#groups.size > 0 ? (0, _util.objectFromMap)(this.#groups) : null;
  214. }
  215. getGroup(id) {
  216. return this.#groups.get(id) || null;
  217. }
  218. }
  219. exports.OptionalContentConfig = OptionalContentConfig;