@jsenv/dom 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -5,11 +5,23 @@ export { createPubSub } from "./src/pub_sub.js";
5
5
  // style
6
6
  export { addWillChange, getStyle, setStyles } from "./src/style/dom_styles.js";
7
7
  export { createStyleController } from "./src/style/style_controller.js";
8
+ export { getDefaultStyles } from "./src/style/style_default.js";
8
9
 
9
10
  // attributes
10
11
  export { addAttributeEffect } from "./src/attr/add_attribute_effect.js";
11
12
  export { setAttribute, setAttributes } from "./src/attr/attributes.js";
12
13
 
14
+ // colors
15
+ export { getContrastRatio } from "./src/color/color_constrast.js";
16
+ export { parseCSSColor, stringifyCSSColor } from "./src/color/color_parsing.js";
17
+ export {
18
+ getPreferedColorScheme,
19
+ prefersDarkColors,
20
+ prefersLightColors,
21
+ } from "./src/color/color_scheme.js";
22
+ export { pickLightOrDark } from "./src/color/pick_light_or_dark.js";
23
+ export { resolveCSSColor } from "./src/color/resolve_css_color.js";
24
+
13
25
  // traversal
14
26
  export {
15
27
  findAfter,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/dom",
3
- "version": "0.1.0",
3
+ "version": "0.4.0",
4
4
  "description": "DOM utilities for writing frontend code",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Calculates the contrast ratio between two RGBA colors
3
+ * Based on WCAG 2.1 specification
4
+ * @param {Array<number>} rgba1 - [r, g, b, a] values for first color
5
+ * @param {Array<number>} rgba2 - [r, g, b, a] values for second color
6
+ * @param {Array<number>} [background=[255, 255, 255, 1]] - Background color to composite against when colors have transparency
7
+ * @returns {number} Contrast ratio (1-21)
8
+ */
9
+ export const getContrastRatio = (
10
+ rgba1,
11
+ rgba2,
12
+ background = [255, 255, 255, 1],
13
+ ) => {
14
+ // When colors have transparency (alpha < 1), we need to composite them
15
+ // against a background to get their effective appearance
16
+ const composited1 = compositeColor(rgba1, background);
17
+ const composited2 = compositeColor(rgba2, background);
18
+
19
+ const lum1 = getLuminance(composited1[0], composited1[1], composited1[2]);
20
+ const lum2 = getLuminance(composited2[0], composited2[1], composited2[2]);
21
+ const brightest = Math.max(lum1, lum2);
22
+ const darkest = Math.min(lum1, lum2);
23
+ return (brightest + 0.05) / (darkest + 0.05);
24
+ };
25
+
26
+ /**
27
+ * Composites a color with alpha over a background color
28
+ * @param {Array<number>} foreground - [r, g, b, a] foreground color
29
+ * @param {Array<number>} background - [r, g, b, a] background color
30
+ * @returns {Array<number>} [r, g, b] composited color (alpha is flattened)
31
+ */
32
+ const compositeColor = (foreground, background) => {
33
+ const [fr, fg, fb, fa] = foreground;
34
+ const [br, bg, bb, ba] = background;
35
+
36
+ // No transparency: return the foreground color as-is
37
+ if (fa === 1) {
38
+ return [fr, fg, fb];
39
+ }
40
+
41
+ // Alpha compositing formula: C = αA * CA + αB * (1 - αA) * CB
42
+ const alpha = fa + ba * (1 - fa);
43
+
44
+ if (alpha === 0) {
45
+ return [0, 0, 0];
46
+ }
47
+
48
+ const r = (fa * fr + ba * (1 - fa) * br) / alpha;
49
+ const g = (fa * fg + ba * (1 - fa) * bg) / alpha;
50
+ const b = (fa * fb + ba * (1 - fa) * bb) / alpha;
51
+
52
+ return [Math.round(r), Math.round(g), Math.round(b)];
53
+ };
54
+
55
+ /**
56
+ * Calculates the relative luminance of an RGB color
57
+ * Based on WCAG 2.1 specification
58
+ * @param {number} r - Red component (0-255)
59
+ * @param {number} g - Green component (0-255)
60
+ * @param {number} b - Blue component (0-255)
61
+ * @returns {number} Relative luminance (0-1)
62
+ */
63
+ const getLuminance = (r, g, b) => {
64
+ const [rs, gs, bs] = [r, g, b].map((c) => {
65
+ c = c / 255;
66
+ return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
67
+ });
68
+ return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
69
+ };
@@ -0,0 +1,319 @@
1
+ /**
2
+ * Parses a CSS color string into RGBA values
3
+ * Supports hex (#rgb, #rrggbb, #rrggbbaa), rgb(), rgba(), hsl(), hsla()
4
+ * @param {string} color - CSS color string
5
+ * @returns {Array<number>|null} [r, g, b, a] values or null if parsing fails
6
+ */
7
+ export const parseCSSColor = (color) => {
8
+ if (!color || typeof color !== "string") {
9
+ return null;
10
+ }
11
+
12
+ color = color.trim().toLowerCase();
13
+
14
+ // Hex colors
15
+ if (color.startsWith("#")) {
16
+ const hex = color.slice(1);
17
+ if (hex.length === 3) {
18
+ // #rgb -> #rrggbb
19
+ const r = parseInt(hex[0] + hex[0], 16);
20
+ const g = parseInt(hex[1] + hex[1], 16);
21
+ const b = parseInt(hex[2] + hex[2], 16);
22
+ return [r, g, b, 1];
23
+ }
24
+ if (hex.length === 6) {
25
+ // #rrggbb
26
+ const r = parseInt(hex.slice(0, 2), 16);
27
+ const g = parseInt(hex.slice(2, 4), 16);
28
+ const b = parseInt(hex.slice(4, 6), 16);
29
+ return [r, g, b, 1];
30
+ }
31
+ if (hex.length === 8) {
32
+ // #rrggbbaa
33
+ const r = parseInt(hex.slice(0, 2), 16);
34
+ const g = parseInt(hex.slice(2, 4), 16);
35
+ const b = parseInt(hex.slice(4, 6), 16);
36
+ const a = parseInt(hex.slice(6, 8), 16) / 255;
37
+ return [r, g, b, a];
38
+ }
39
+ }
40
+
41
+ // RGB/RGBA colors
42
+ const rgbMatch = color.match(/rgba?\(([^)]+)\)/);
43
+ if (rgbMatch) {
44
+ const values = rgbMatch[1].split(",").map((v) => parseFloat(v.trim()));
45
+ if (values.length >= 3) {
46
+ const r = values[0];
47
+ const g = values[1];
48
+ const b = values[2];
49
+ const a = values.length >= 4 ? values[3] : 1;
50
+ return [r, g, b, a];
51
+ }
52
+ }
53
+
54
+ // HSL/HSLA colors - convert to RGB
55
+ const hslMatch = color.match(/hsla?\(([^)]+)\)/);
56
+ if (hslMatch) {
57
+ const values = hslMatch[1].split(",").map((v) => parseFloat(v.trim()));
58
+ if (values.length >= 3) {
59
+ const [h, s, l] = values;
60
+ const a = values.length >= 4 ? values[3] : 1;
61
+ const [r, g, b] = hslToRgb(h, s / 100, l / 100);
62
+ return [r, g, b, a];
63
+ }
64
+ }
65
+
66
+ // Named colors (basic set)
67
+ if (namedColors[color]) {
68
+ return [...namedColors[color], 1];
69
+ }
70
+ return null;
71
+ };
72
+
73
+ /**
74
+ * Converts RGBA values back to a CSS color string
75
+ * Prefers named colors when possible, then rgb() for opaque colors, rgba() for transparent
76
+ * @param {Array<number>} rgba - [r, g, b, a] values
77
+ * @returns {string|null} CSS color string or null if invalid input
78
+ */
79
+ export const stringifyCSSColor = (rgba) => {
80
+ if (!Array.isArray(rgba) || rgba.length < 3) {
81
+ return null;
82
+ }
83
+
84
+ const [r, g, b, a = 1] = rgba;
85
+
86
+ // Validate RGB values
87
+ if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
88
+ return null;
89
+ }
90
+
91
+ // Validate alpha value
92
+ if (a < 0 || a > 1) {
93
+ return null;
94
+ }
95
+
96
+ // Round RGB values to integers
97
+ const rInt = Math.round(r);
98
+ const gInt = Math.round(g);
99
+ const bInt = Math.round(b);
100
+
101
+ // Check for named colors (only for fully opaque colors)
102
+ if (a === 1) {
103
+ for (const [name, [nameR, nameG, nameB]] of Object.entries(namedColors)) {
104
+ if (rInt === nameR && gInt === nameG && bInt === nameB) {
105
+ return name;
106
+ }
107
+ }
108
+ }
109
+
110
+ // Use rgb() for opaque colors, rgba() for transparent
111
+ if (a === 1) {
112
+ return `rgb(${rInt}, ${gInt}, ${bInt})`;
113
+ }
114
+ return `rgba(${rInt}, ${gInt}, ${bInt}, ${a})`;
115
+ };
116
+
117
+ const namedColors = {
118
+ // Basic colors
119
+ black: [0, 0, 0],
120
+ white: [255, 255, 255],
121
+ red: [255, 0, 0],
122
+ green: [0, 128, 0],
123
+ blue: [0, 0, 255],
124
+ yellow: [255, 255, 0],
125
+ cyan: [0, 255, 255],
126
+ magenta: [255, 0, 255],
127
+
128
+ // Gray variations
129
+ silver: [192, 192, 192],
130
+ gray: [128, 128, 128],
131
+ grey: [128, 128, 128],
132
+ darkgray: [169, 169, 169],
133
+ darkgrey: [169, 169, 169],
134
+ lightgray: [211, 211, 211],
135
+ lightgrey: [211, 211, 211],
136
+ dimgray: [105, 105, 105],
137
+ dimgrey: [105, 105, 105],
138
+ gainsboro: [220, 220, 220],
139
+ whitesmoke: [245, 245, 245],
140
+
141
+ // Extended basic colors
142
+ maroon: [128, 0, 0],
143
+ olive: [128, 128, 0],
144
+ lime: [0, 255, 0],
145
+ aqua: [0, 255, 255],
146
+ teal: [0, 128, 128],
147
+ navy: [0, 0, 128],
148
+ fuchsia: [255, 0, 255],
149
+ purple: [128, 0, 128],
150
+
151
+ // Red variations
152
+ darkred: [139, 0, 0],
153
+ firebrick: [178, 34, 34],
154
+ crimson: [220, 20, 60],
155
+ indianred: [205, 92, 92],
156
+ lightcoral: [240, 128, 128],
157
+ salmon: [250, 128, 114],
158
+ darksalmon: [233, 150, 122],
159
+ lightsalmon: [255, 160, 122],
160
+
161
+ // Pink variations
162
+ pink: [255, 192, 203],
163
+ lightpink: [255, 182, 193],
164
+ hotpink: [255, 105, 180],
165
+ deeppink: [255, 20, 147],
166
+ mediumvioletred: [199, 21, 133],
167
+ palevioletred: [219, 112, 147],
168
+
169
+ // Orange variations
170
+ orange: [255, 165, 0],
171
+ darkorange: [255, 140, 0],
172
+ orangered: [255, 69, 0],
173
+ tomato: [255, 99, 71],
174
+ coral: [255, 127, 80],
175
+
176
+ // Yellow variations
177
+ gold: [255, 215, 0],
178
+ lightyellow: [255, 255, 224],
179
+ lemonchiffon: [255, 250, 205],
180
+ lightgoldenrodyellow: [250, 250, 210],
181
+ papayawhip: [255, 239, 213],
182
+ moccasin: [255, 228, 181],
183
+ peachpuff: [255, 218, 185],
184
+ palegoldenrod: [238, 232, 170],
185
+ khaki: [240, 230, 140],
186
+ darkkhaki: [189, 183, 107],
187
+
188
+ // Green variations
189
+ darkgreen: [0, 100, 0],
190
+ forestgreen: [34, 139, 34],
191
+ seagreen: [46, 139, 87],
192
+ mediumseagreen: [60, 179, 113],
193
+ springgreen: [0, 255, 127],
194
+ mediumspringgreen: [0, 250, 154],
195
+ lawngreen: [124, 252, 0],
196
+ chartreuse: [127, 255, 0],
197
+ greenyellow: [173, 255, 47],
198
+ limegreen: [50, 205, 50],
199
+ palegreen: [152, 251, 152],
200
+ lightgreen: [144, 238, 144],
201
+ mediumaquamarine: [102, 205, 170],
202
+ aquamarine: [127, 255, 212],
203
+ darkolivegreen: [85, 107, 47],
204
+ olivedrab: [107, 142, 35],
205
+ yellowgreen: [154, 205, 50],
206
+
207
+ // Blue variations
208
+ darkblue: [0, 0, 139],
209
+ mediumblue: [0, 0, 205],
210
+ royalblue: [65, 105, 225],
211
+ steelblue: [70, 130, 180],
212
+ dodgerblue: [30, 144, 255],
213
+ deepskyblue: [0, 191, 255],
214
+ skyblue: [135, 206, 235],
215
+ lightskyblue: [135, 206, 250],
216
+ lightblue: [173, 216, 230],
217
+ powderblue: [176, 224, 230],
218
+ lightcyan: [224, 255, 255],
219
+ paleturquoise: [175, 238, 238],
220
+ darkturquoise: [0, 206, 209],
221
+ mediumturquoise: [72, 209, 204],
222
+ turquoise: [64, 224, 208],
223
+ cadetblue: [95, 158, 160],
224
+ darkcyan: [0, 139, 139],
225
+ lightseagreen: [32, 178, 170],
226
+
227
+ // Purple variations
228
+ indigo: [75, 0, 130],
229
+ darkviolet: [148, 0, 211],
230
+ blueviolet: [138, 43, 226],
231
+ mediumpurple: [147, 112, 219],
232
+ mediumslateblue: [123, 104, 238],
233
+ slateblue: [106, 90, 205],
234
+ darkslateblue: [72, 61, 139],
235
+ lavender: [230, 230, 250],
236
+ thistle: [216, 191, 216],
237
+ plum: [221, 160, 221],
238
+ violet: [238, 130, 238],
239
+ orchid: [218, 112, 214],
240
+ mediumorchid: [186, 85, 211],
241
+ darkorchid: [153, 50, 204],
242
+ darkmagenta: [139, 0, 139],
243
+
244
+ // Brown variations
245
+ brown: [165, 42, 42],
246
+ saddlebrown: [139, 69, 19],
247
+ sienna: [160, 82, 45],
248
+ chocolate: [210, 105, 30],
249
+ darkgoldenrod: [184, 134, 11],
250
+ peru: [205, 133, 63],
251
+ rosybrown: [188, 143, 143],
252
+ goldenrod: [218, 165, 32],
253
+ sandybrown: [244, 164, 96],
254
+ tan: [210, 180, 140],
255
+ burlywood: [222, 184, 135],
256
+ wheat: [245, 222, 179],
257
+ navajowhite: [255, 222, 173],
258
+ bisque: [255, 228, 196],
259
+ blanchedalmond: [255, 235, 205],
260
+ cornsilk: [255, 248, 220],
261
+
262
+ // Special colors
263
+ transparent: [0, 0, 0], // Note: alpha will be 0 for transparent
264
+ aliceblue: [240, 248, 255],
265
+ antiquewhite: [250, 235, 215],
266
+ azure: [240, 255, 255],
267
+ beige: [245, 245, 220],
268
+ honeydew: [240, 255, 240],
269
+ ivory: [255, 255, 240],
270
+ lavenderblush: [255, 240, 245],
271
+ linen: [250, 240, 230],
272
+ mintcream: [245, 255, 250],
273
+ mistyrose: [255, 228, 225],
274
+ oldlace: [253, 245, 230],
275
+ seashell: [255, 245, 238],
276
+ snow: [255, 250, 250],
277
+ };
278
+
279
+ /**
280
+ * Converts HSL color to RGB
281
+ * @param {number} h - Hue (0-360)
282
+ * @param {number} s - Saturation (0-1)
283
+ * @param {number} l - Lightness (0-1)
284
+ * @returns {Array<number>} [r, g, b] values
285
+ */
286
+ const hslToRgb = (h, s, l) => {
287
+ h = h % 360;
288
+ const c = (1 - Math.abs(2 * l - 1)) * s;
289
+ const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
290
+ const m = l - c / 2;
291
+ const createRgb = (r, g, b) => {
292
+ return [
293
+ Math.round((r + m) * 255),
294
+ Math.round((g + m) * 255),
295
+ Math.round((b + m) * 255),
296
+ ];
297
+ };
298
+
299
+ if (h >= 0 && h < 60) {
300
+ return createRgb(c, x, 0);
301
+ }
302
+ if (h >= 60 && h < 120) {
303
+ return createRgb(x, c, 0);
304
+ }
305
+ if (h >= 120 && h < 180) {
306
+ return createRgb(0, c, x);
307
+ }
308
+ if (h >= 180 && h < 240) {
309
+ return createRgb(0, x, c);
310
+ }
311
+ if (h >= 240 && h < 300) {
312
+ return createRgb(x, 0, c);
313
+ }
314
+ if (h >= 300 && h < 360) {
315
+ return createRgb(c, 0, x);
316
+ }
317
+
318
+ return createRgb(0, 0, 0);
319
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Determines if the current color scheme is dark mode
3
+ * @param {Element} [element] - DOM element to check color-scheme against (optional)
4
+ * @returns {boolean} True if dark mode is active
5
+ */
6
+ export const prefersDarkColors = (element) => {
7
+ const colorScheme = getPreferedColorScheme(element);
8
+ return colorScheme.includes("dark");
9
+ };
10
+
11
+ export const prefersLightColors = (element) => {
12
+ return !prefersDarkColors(element);
13
+ };
14
+
15
+ export const getPreferedColorScheme = (element) => {
16
+ const computedStyle = getComputedStyle(element || document.documentElement);
17
+ const colorScheme = computedStyle.colorScheme;
18
+
19
+ // If no explicit color-scheme is set, or it's "normal",
20
+ // fall back to prefers-color-scheme media query
21
+ if (!colorScheme || colorScheme === "normal") {
22
+ return window.matchMedia("(prefers-color-scheme: dark)").matches
23
+ ? "dark"
24
+ : "light";
25
+ }
26
+
27
+ return colorScheme;
28
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Chooses between light and dark colors based on which provides better contrast against a background
3
+ * @param {Element} element - DOM element to resolve CSS variables against
4
+ * @param {string} backgroundColor - CSS color value (hex, rgb, hsl, CSS variable, etc.)
5
+ * @param {string} lightColor - Light color option (typically for dark backgrounds)
6
+ * @param {string} darkColor - Dark color option (typically for light backgrounds)
7
+ * @returns {string} The color that provides better contrast (lightColor or darkColor)
8
+ */
9
+ import { getContrastRatio } from "./color_constrast.js";
10
+ import { resolveCSSColor } from "./resolve_css_color.js";
11
+
12
+ export const pickLightOrDark = (
13
+ element,
14
+ backgroundColor,
15
+ lightColor = "white",
16
+ darkColor = "black",
17
+ ) => {
18
+ const resolvedBgColor = resolveCSSColor(backgroundColor, element);
19
+ const resolvedLightColor = resolveCSSColor(lightColor, element);
20
+ const resolvedDarkColor = resolveCSSColor(darkColor, element);
21
+
22
+ if (!resolvedBgColor || !resolvedLightColor || !resolvedDarkColor) {
23
+ // Fallback to light color if parsing fails
24
+ return lightColor;
25
+ }
26
+
27
+ const contrastWithLight = getContrastRatio(
28
+ resolvedBgColor,
29
+ resolvedLightColor,
30
+ );
31
+ const contrastWithDark = getContrastRatio(resolvedBgColor, resolvedDarkColor);
32
+
33
+ return contrastWithLight > contrastWithDark ? lightColor : darkColor;
34
+ };
@@ -0,0 +1,60 @@
1
+ import { parseCSSColor, stringifyCSSColor } from "./color_parsing.js";
2
+ import { prefersDarkColors } from "./color_scheme.js";
3
+
4
+ /**
5
+ * Resolves a color value, handling CSS custom properties and light-dark() function
6
+ * @param {string} color - CSS color value (may include CSS variables, light-dark())
7
+ * @param {Element} element - DOM element to resolve CSS variables and light-dark() against
8
+ * @param {string} context - Return format: "js" for RGBA array, "css" for CSS string
9
+ * @returns {Array<number>|string|null} [r, g, b, a] values, CSS string, or null if parsing fails
10
+ */
11
+ export const resolveCSSColor = (color, element, context = "js") => {
12
+ if (!color || typeof color !== "string") {
13
+ return null;
14
+ }
15
+
16
+ let resolvedColor = color;
17
+
18
+ // Handle light-dark() function
19
+ const lightDarkMatch = color.match(/light-dark\(([^,]+),([^)]+)\)/);
20
+ if (lightDarkMatch) {
21
+ const lightColor = lightDarkMatch[1].trim();
22
+ const darkColor = lightDarkMatch[2].trim();
23
+
24
+ // Select the appropriate color and recursively resolve it
25
+ const prefersDark = prefersDarkColors(element);
26
+ resolvedColor = prefersDark ? darkColor : lightColor;
27
+ return resolveCSSColor(resolvedColor, element, context);
28
+ }
29
+
30
+ // If it's a CSS custom property, resolve it using getComputedStyle
31
+ if (resolvedColor.includes("var(")) {
32
+ const computedStyle = getComputedStyle(element);
33
+
34
+ // Handle var() syntax
35
+ const varMatch = color.match(/var\(([^,)]+)(?:,([^)]+))?\)/);
36
+ if (varMatch) {
37
+ const propertyName = varMatch[1].trim();
38
+ const fallback = varMatch[2]?.trim();
39
+
40
+ const resolvedValue = computedStyle.getPropertyValue(propertyName).trim();
41
+ if (resolvedValue) {
42
+ // Recursively resolve in case the CSS variable contains light-dark() or other variables
43
+ return resolveCSSColor(resolvedValue, element, context);
44
+ }
45
+ if (fallback) {
46
+ // Recursively resolve fallback (in case it's also a CSS variable)
47
+ return resolveCSSColor(fallback, element, context);
48
+ }
49
+ }
50
+ }
51
+
52
+ // Parse the resolved color and return in the requested format
53
+ const rgba = parseCSSColor(resolvedColor);
54
+
55
+ if (context === "css") {
56
+ return rgba ? stringifyCSSColor(rgba) : null;
57
+ }
58
+
59
+ return rgba;
60
+ };
@@ -318,7 +318,11 @@ export const visibleRectEffect = (element, update) => {
318
318
  export const pickPositionRelativeTo = (
319
319
  element,
320
320
  target,
321
- { alignToViewportEdgeWhenTargetNearEdge = 0, forcePosition } = {},
321
+ {
322
+ alignToViewportEdgeWhenTargetNearEdge = 0,
323
+ minLeft = 0,
324
+ forcePosition,
325
+ } = {},
322
326
  ) => {
323
327
  if (
324
328
  import.meta.dev &&
@@ -392,7 +396,7 @@ export const pickPositionRelativeTo = (
392
396
  const targetIsNearLeftEdge =
393
397
  targetLeft < alignToViewportEdgeWhenTargetNearEdge;
394
398
  if (elementIsWiderThanTarget && targetIsNearLeftEdge) {
395
- elementPositionLeft = 0; // Left edge of viewport
399
+ elementPositionLeft = minLeft; // Left edge of viewport
396
400
  }
397
401
  }
398
402
  }
package/src/pub_sub.js CHANGED
@@ -1,4 +1,4 @@
1
- export const createPubSub = () => {
1
+ export const createPubSub = (clearOnPublish = false) => {
2
2
  const callbackSet = new Set();
3
3
 
4
4
  const publish = (...args) => {
@@ -7,6 +7,9 @@ export const createPubSub = () => {
7
7
  const result = callback(...args);
8
8
  results.push(result);
9
9
  }
10
+ if (clearOnPublish) {
11
+ callbackSet.clear();
12
+ }
10
13
  return results;
11
14
  };
12
15