Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Call destroy to do cleanup work when pattern element is removed. #1013

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/core/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
*/
import "regenerator-runtime/runtime"; // needed for ``await`` support
import $ from "jquery";
import Registry from "./registry";
import Registry, { PATTERN_INSTANCE_REGISTRY } from "./registry";
import logging from "./logging";
import mockupParser from "./mockup-parser";
import utils from "./utils";

const log = logging.getLogger("Patternslib Base");

Expand Down Expand Up @@ -50,10 +51,15 @@ const Base = async function ($el, options, trigger) {
this.options = $.extend(true, {}, this.defaults || {}, options || {});
await this.init($el, options, trigger);

this.id = utils.unique_id(); // Generate a unique id

// Store pattern instance on element
this.$el.data(`pattern-${this.name}`, this);
this.el[`pattern-${this.name}`] = this;

// Add Pattern instance to PATTERN_INSTANCE_REGISTRY
PATTERN_INSTANCE_REGISTRY.push(this);

this.emit("init");
};

Expand Down
24 changes: 21 additions & 3 deletions src/core/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,19 @@ const event_listener_map = {};
* @param {string} id - A unique id under which the event is registered.
* @param {function} cb - The event handler / callback function.
* @param {Object} opts - Options for the addEventListener API.
*
* @param {Function} remove_condition - If this function evaluates to true,
* the event listener will be deregistered and not called.
* Defaults to always return ``false``, so no check is actually done.
* Can be used to unregister event handlers automatically.
*/
const add_event_listener = (el, event_type, id, cb, opts = {}) => {
const add_event_listener = (
el,
event_type,
id,
cb,
opts = {},
remove_condition = () => false
) => {
if (!el?.addEventListener) {
return; // nothing to do.
}
Expand All @@ -26,7 +36,15 @@ const add_event_listener = (el, event_type, id, cb, opts = {}) => {
event_listener_map[el] = {};
}
event_listener_map[el][id] = [event_type, cb, opts.capture ? opts : undefined]; // prettier-ignore
el.addEventListener(event_type, cb, opts);
const _cb = () => {
if (remove_condition()) {
remove_event_listener(el, id);
} else {
cb();
}
};

el.addEventListener(event_type, _cb, opts);
};

/**
Expand Down
31 changes: 31 additions & 0 deletions src/core/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,23 @@ if (typeof window.__patternslib_registry_initialized === "undefined") {
window.__patternslib_registry_initialized = false;
}

/**
* Global pattern instance registry.
*
* If your Pattern uses the base pattern base class, any concrete instance of
* the pattern will be stored in this registry.
* When the element on which the pattern is defined is removed, we call the
* ``destroy`` method on the pattern instance and remove the pattern instance
* from this registry.
*/
if (typeof window.__patternslib_instance_registry === "undefined") {
window.__patternslib_instance_registry = [];
}
export const PATTERN_INSTANCE_REGISTRY = window.__patternslib_instance_registry;

const registry = {
patterns: PATTERN_REGISTRY, // reference to global patterns registry
patterns_instances: PATTERN_INSTANCE_REGISTRY, // reference to global pattern instance registry
// as long as the registry is not initialized, pattern
// registration just registers a pattern. Once init is called,
// the DOM is scanned. After that registering a new pattern
Expand All @@ -69,6 +84,19 @@ const registry = {
window.__patternslib_registry_initialized = true;
log.debug("Loaded: " + Object.keys(registry.patterns).sort().join(", "));
registry.scan(document.body);

// Call the Pattern instance's destroy method when a Pattern element
// is removed.
const remove_observer = new MutationObserver((mutations, observer) => {
registry.pattern_instances.forEach((instance, idx) => {
if (!document.body.contains(instance.el)) {
instance?.destroy();
delete registry.pattern_instances[idx];
}
});
});
remove_observer.observe(document.body, { childList: true });

log.debug("Finished initial scan.");
});
},
Expand All @@ -79,6 +107,9 @@ const registry = {
for (const name in registry.patterns) {
delete registry.patterns[name];
}
registry.pattern_instances.forEach((instance, idx) => {
delete registry.pattern_instances[idx];
});
},

transformPattern(name, content) {
Expand Down
10 changes: 10 additions & 0 deletions src/core/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,15 @@ const is_iso_date_time = (value, optional_time = false) => {
return re_date_time.test(value);
};

/**
* Generate a unique id.
*
* @return {String} - A unique string.
*/
const unique_id = () => {
return Math.floor((1 + Math.random()) * 0x1000000000000).toString(16);
};

var utils = {
// pattern pimping - own module?
jqueryPlugin: jqueryPlugin,
Expand Down Expand Up @@ -673,6 +682,7 @@ var utils = {
escape_html: escape_html,
unescape_html: unescape_html,
is_iso_date_time: is_iso_date_time,
unique_id: unique_id,
getCSSValue: dom.get_css_value, // BBB: moved to dom. TODO: Remove in upcoming version.
};

Expand Down