Skip to content

Commit bc59146

Browse files
committed
Provide pluggable AjaxDestroy callbacks
1 parent d77de24 commit bc59146

File tree

5 files changed

+246
-50
lines changed

5 files changed

+246
-50
lines changed

src/ajaxdestroy.js

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,52 @@
11
import {Parser} from "./parser";
22
import $ from 'jquery';
33

4+
function destroy_bootstrap(node) {
5+
let dd = window.bootstrap.Dropdown.getInstance(node);
6+
let tt = window.bootstrap.Tooltip.getInstance(node);
7+
if (dd) {
8+
dd.dispose();
9+
}
10+
if (tt) {
11+
tt.dispose();
12+
}
13+
dd = null;
14+
tt = null;
15+
}
16+
417
/**
518
* DOM parser for destroying JavaScript instances attached to DOM elements
619
* going to be removed.
720
*/
821
export class AjaxDestroy extends Parser {
922

23+
constructor() {
24+
super();
25+
this.callbacks = [];
26+
if (window.bootstrap !== undefined) {
27+
this.register_cb(destroy_bootstrap);
28+
}
29+
}
30+
31+
/**
32+
* Registers a callback to be executed during destruction.
33+
* @param {Function} cb - The destroy callback function.
34+
*/
35+
register_cb(cb) {
36+
this.callbacks.push(cb);
37+
}
38+
39+
/**
40+
* Unregisters a callback.
41+
* @param {Function} cb - The callback to deregister.
42+
*/
43+
unregister_cb(cb) {
44+
const index = this.callbacks.indexOf(cb);
45+
if (index > -1) {
46+
this.callbacks.splice(index, 1);
47+
}
48+
}
49+
1050
parse(node) {
1151
let instances = node._ajax_attached;
1252
if (instances !== undefined) {
@@ -25,18 +65,12 @@ export class AjaxDestroy extends Parser {
2565
let evts = attrs['ajax:bind'];
2666
$(node).off(evts);
2767
}
28-
if (window.bootstrap !== undefined) {
29-
let dd = window.bootstrap.Dropdown.getInstance(node);
30-
let tt = window.bootstrap.Tooltip.getInstance(node);
31-
if (dd) {
32-
dd.dispose();
33-
}
34-
if (tt) {
35-
tt.dispose();
36-
}
37-
dd = null;
38-
tt = null;
68+
69+
// Run all registered callbacks
70+
for (let cb of this.callbacks) {
71+
cb(node);
3972
}
73+
4074
$(node).empty(); // remove retained comments
4175
$(node).off(); // remove event listeners
4276
$(node).removeData(); // remove cached data
@@ -54,9 +88,12 @@ export class AjaxDestroy extends Parser {
5488
* @param {HTMLElement|jQuery} elem - The element to destroy. Can be a jQuery object or a DOM element.
5589
* @returns {void}
5690
*/
57-
export function ajax_destroy(elem) {
91+
export function ajax_destroy(elem, callbacks=[]) {
5892
elem = elem instanceof $ ? elem.get(0) : elem;
5993
let handle = new AjaxDestroy();
94+
for (let cb of callbacks) {
95+
handle.register_cb(cb);
96+
}
6097
handle.walk(elem);
6198
handle = null;
6299
}

tests/test_ajax.js

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import $ from 'jquery';
22
import {
33
Ajax,
44
AjaxAction,
5-
AjaxDestroy,
65
AjaxDispatcher,
76
AjaxEvent,
87
AjaxForm,
@@ -1337,30 +1336,6 @@ QUnit.module('treibstoff.ajax', hooks => {
13371336
assert.deepEqual(path_opts.event.type, 'click');
13381337
});
13391338

1340-
///////////////////////////////////////////////////////////////////////////
1341-
// Test AjaxDestroy
1342-
///////////////////////////////////////////////////////////////////////////
1343-
1344-
QUnit.test('Test AjaxDestroy', assert => {
1345-
class Inst {
1346-
constructor() {
1347-
this.destroyed = false;
1348-
}
1349-
destroy() {
1350-
this.destroyed = true;
1351-
}
1352-
}
1353-
1354-
let inst = new Inst();
1355-
let elem = $('<span />').appendTo(container);
1356-
elem[0]._ajax_attached = [inst];
1357-
1358-
assert.deepEqual(inst.destroyed, false);
1359-
let parser = new AjaxDestroy();
1360-
parser.walk(container[0]);
1361-
assert.deepEqual(inst.destroyed, true);
1362-
});
1363-
13641339
///////////////////////////////////////////////////////////////////////////
13651340
// Test AjaxHandle
13661341
///////////////////////////////////////////////////////////////////////////

tests/test_ajaxdestroy.js

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import $ from 'jquery';
2+
import {AjaxDestroy} from '../src/ajax.js';
3+
import {spinner} from '../src/spinner.js';
4+
import {ajax_destroy} from '../src/ajaxdestroy.js';
5+
6+
QUnit.module('treibstoff.ajaxdestroy', hooks => {
7+
let container;
8+
let ajax_orgin = $.ajax;
9+
10+
hooks.beforeEach(() => {
11+
container = $('<div></div>');
12+
$('body').append(container);
13+
});
14+
15+
hooks.afterEach(() => {
16+
container.remove();
17+
// Ajax binds popstate
18+
$(window).off('popstate');
19+
// Reset $.ajax patch if any
20+
$.ajax = ajax_orgin;
21+
// Force hide spinner
22+
spinner.hide(true);
23+
});
24+
25+
QUnit.test('Test AjaxDestroy', assert => {
26+
class Inst {
27+
constructor() {
28+
this.destroyed = false;
29+
}
30+
destroy() {
31+
this.destroyed = true;
32+
}
33+
}
34+
35+
let inst = new Inst();
36+
let elem = $('<span />').appendTo(container);
37+
elem[0]._ajax_attached = [inst];
38+
39+
assert.deepEqual(inst.destroyed, false);
40+
let parser = new AjaxDestroy();
41+
assert.strictEqual(window.bootstrap, undefined);
42+
assert.strictEqual(parser.callbacks.length, 0);
43+
parser.walk(container[0]);
44+
assert.deepEqual(inst.destroyed, true);
45+
});
46+
47+
QUnit.test('Test AjaxDestroy bootstrap callback', assert => {
48+
class Inst {
49+
constructor() {
50+
this.destroyed = false;
51+
}
52+
destroy() {
53+
this.destroyed = true;
54+
}
55+
}
56+
57+
// bootstrap callback - patch window.bootstrap
58+
window.bootstrap = {
59+
Dropdown: {
60+
getInstance: function() {assert.step('get Dropdown instance')}
61+
},
62+
Tooltip: {
63+
getInstance: function() {assert.step('get Tooltip instance')}
64+
}
65+
}
66+
let inst = new Inst();
67+
let elem = $('<span />').appendTo(container);
68+
elem[0]._ajax_attached = [inst];
69+
70+
assert.deepEqual(inst.destroyed, false);
71+
let parser = new AjaxDestroy();
72+
assert.strictEqual(parser.callbacks.length, 1);
73+
parser.walk(elem[0]);
74+
assert.deepEqual(inst.destroyed, true);
75+
76+
assert.verifySteps(['get Dropdown instance', 'get Tooltip instance']);
77+
window.bootstrap = undefined;
78+
});
79+
80+
QUnit.test('Test AjaxDestroy custom callbacks', assert => {
81+
class Inst {
82+
constructor() {
83+
// ...
84+
}
85+
destroy() {
86+
assert.step('destroy');
87+
}
88+
}
89+
let inst = new Inst();
90+
let elem = $('<span />').appendTo(container);
91+
elem[0]._ajax_attached = [inst];
92+
93+
// custom callback 1
94+
function custom_callback1(node) {
95+
assert.step('custom1');
96+
}
97+
// custom callback 2
98+
function custom_callback2(node) {
99+
assert.step('custom2');
100+
}
101+
102+
// vanilla destroy
103+
let parser = new AjaxDestroy();
104+
assert.strictEqual(parser.callbacks.length, 0);
105+
parser.walk(elem[0]);
106+
assert.verifySteps(['destroy']);
107+
108+
// register callbacks
109+
elem[0]._ajax_attached = [inst]; // reattach instance
110+
parser.register_cb(custom_callback1);
111+
parser.register_cb(custom_callback2);
112+
assert.strictEqual(parser.callbacks.length, 2);
113+
parser.walk(elem[0]);
114+
assert.verifySteps(['destroy', 'custom1', 'custom2']);
115+
116+
// unregister cb 1
117+
elem[0]._ajax_attached = [inst]; // reattach instance
118+
parser.unregister_cb(custom_callback1);
119+
assert.strictEqual(parser.callbacks.length, 1);
120+
parser.walk(elem[0]);
121+
assert.verifySteps(['destroy', 'custom2']);
122+
123+
// unregister cb 2
124+
elem[0]._ajax_attached = [inst]; // reattach instance
125+
parser.unregister_cb(custom_callback2);
126+
assert.strictEqual(parser.callbacks.length, 0);
127+
parser.walk(elem[0]);
128+
assert.verifySteps(['destroy']);
129+
});
130+
131+
QUnit.test('Test AjaxDestroy utility', assert => {
132+
class Inst {
133+
constructor() {
134+
// ...
135+
}
136+
destroy() {
137+
assert.step('destroy');
138+
}
139+
}
140+
let inst = new Inst();
141+
let elem = $('<span />').appendTo(container);
142+
elem[0]._ajax_attached = [inst];
143+
144+
// custom callback 1
145+
function custom_callback1(node) {
146+
assert.step('custom1');
147+
}
148+
// custom callback 2
149+
function custom_callback2(node) {
150+
assert.step('custom2');
151+
}
152+
153+
// vanilla destroy
154+
ajax_destroy(elem);
155+
assert.verifySteps(['destroy']);
156+
157+
// register callbacks
158+
elem[0]._ajax_attached = [inst]; // reattach instance
159+
ajax_destroy(elem, [custom_callback1, custom_callback2]);
160+
assert.verifySteps(['destroy', 'custom1', 'custom2']);
161+
});
162+
});

treibstoff/bundle/treibstoff.bundle.js

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,35 @@ var ts = (function (exports, $) {
554554
}
555555
}
556556

557+
function destroy_bootstrap(node) {
558+
let dd = window.bootstrap.Dropdown.getInstance(node);
559+
let tt = window.bootstrap.Tooltip.getInstance(node);
560+
if (dd) {
561+
dd.dispose();
562+
}
563+
if (tt) {
564+
tt.dispose();
565+
}
566+
dd = null;
567+
tt = null;
568+
}
557569
class AjaxDestroy extends Parser {
570+
constructor() {
571+
super();
572+
this.callbacks = [];
573+
if (window.bootstrap !== undefined) {
574+
this.register_cb(destroy_bootstrap);
575+
}
576+
}
577+
register_cb(cb) {
578+
this.callbacks.push(cb);
579+
}
580+
unregister_cb(cb) {
581+
const index = this.callbacks.indexOf(cb);
582+
if (index > -1) {
583+
this.callbacks.splice(index, 1);
584+
}
585+
}
558586
parse(node) {
559587
let instances = node._ajax_attached;
560588
if (instances !== undefined) {
@@ -572,17 +600,8 @@ var ts = (function (exports, $) {
572600
let evts = attrs['ajax:bind'];
573601
$(node).off(evts);
574602
}
575-
if (window.bootstrap !== undefined) {
576-
let dd = window.bootstrap.Dropdown.getInstance(node);
577-
let tt = window.bootstrap.Tooltip.getInstance(node);
578-
if (dd) {
579-
dd.dispose();
580-
}
581-
if (tt) {
582-
tt.dispose();
583-
}
584-
dd = null;
585-
tt = null;
603+
for (let cb of this.callbacks) {
604+
cb(node);
586605
}
587606
$(node).empty();
588607
$(node).off();
@@ -593,9 +612,12 @@ var ts = (function (exports, $) {
593612
attrs = null;
594613
}
595614
}
596-
function ajax_destroy(elem) {
615+
function ajax_destroy(elem, callbacks=[]) {
597616
elem = elem instanceof $ ? elem.get(0) : elem;
598617
let handle = new AjaxDestroy();
618+
for (let cb of callbacks) {
619+
handle.register_cb(cb);
620+
}
599621
handle.walk(elem);
600622
handle = null;
601623
}

treibstoff/bundle/treibstoff.bundle.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)