Skip to content

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

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

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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
@@ -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");

@@ -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");
};

24 changes: 21 additions & 3 deletions src/core/events.js
Original file line number Diff line number Diff line change
@@ -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.
}
@@ -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);
};

/**
31 changes: 31 additions & 0 deletions src/core/registry.js
Original file line number Diff line number Diff line change
@@ -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
@@ -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.");
});
},
@@ -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) {
10 changes: 10 additions & 0 deletions src/core/utils.js
Original file line number Diff line number Diff line change
@@ -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,
@@ -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.
};