use onScreenEvent to prevent memory leaks.

This commit is contained in:
Christopher Jeffrey 2015-05-01 21:58:26 -07:00
parent de4aa27d70
commit c435b03bdc
2 changed files with 64 additions and 28 deletions

View File

@ -501,8 +501,19 @@ The base element.
- __onceKey(name, listener)__ - bind a keypress listener for a specific key
once.
- __unkey(name, listener)__ - remove a keypress listener for a specific key.
- __onScreenEvent(type, listener)__ - same as`el.on('screen', ...)` except this
will automatically cleanup listeners after the element is detached.
- __onScreenEvent(type, handler)__ - same as`el.on('screen', ...)` except this
will automatically keep track of which listeners are bound to the screen
object. for use with `removeScreenEvent()`, `free()`, and `destroy()`.
- __removeScreenEvent(type, handler)__ - same as`el.removeListener('screen',
...)` except this will automatically keep track of which listeners are bound
to the screen object. for use with `onScreenEvent()`, `free()`, and
`destroy()`.
- __free()__ - free up the element. automatically unbind all events that may
have been bound to the screen object. this prevents memory leaks. for use
with `onScreenEvent()`, `removeScreenEvent()`, and `destroy()`.
- __destroy()__ - same as the `detach()` method, except this will automatically
call `free()` and unbind any screen events to prevent memory leaks. for use
with `onScreenEvent()`, `removeScreenEvent()`, and `free()`.
- __setIndex(z)__ - set the z-index of the element (changes rendering order).
- __setFront()__ - put the element in front of its siblings.
- __setBack()__ - put the element in back of its siblings.

View File

@ -2395,7 +2395,7 @@ function Element(options) {
this.setHover(options.hoverText);
}
// TODO: Possibly move this to Node for screen.on('mouse', ...).
// TODO: Possibly move this to Node for onScreenEvent('mouse', ...).
this.on('newListener', function fn(type) {
// type = type.split(' ').slice(1).join(' ');
if (type === 'mouse'
@ -2502,17 +2502,42 @@ Element.prototype.sattr = function(style, fg, bg) {
| colors.convert(bg);
};
Element.prototype.onScreenEvent = function(type, listener) {
var self = this;
if (this.parent) {
this.screen.on(type, listener);
Element.prototype.onScreenEvent = function(type, handler) {
this.screen.on(type, handler);
this._elisteners = this._elisteners || {};
this._elisteners[this.uid] = this._elisteners[this.uid] || [];
this._elisteners[this.uid].push({ type: type, handler: handler });
};
Element.prototype.removeScreenEvent = function(type, handler) {
this.screen.removeListener(type, handler);
this._elisteners = this._elisteners || {};
var listeners = this.screen._elisteners[this.uid] || [];
for (var i = 0; i < listeners.length; i++) {
var listener = listeners[i];
if (listener.type === type && listener.handler === handler) {
listeners.splice(i, 1);
if (this.screen._elisteners[this.uid].length === 0) {
delete this.screen._elisteners[this.uid];
}
return;
}
}
this.on('attach', function() {
self.screen.on(type, listener);
});
this.on('detach', function() {
self.screen.removeListener(type, listener);
});
};
Element.prototype.free = function() {
this._elisteners = this._elisteners || {};
var listeners = this.screen._elisteners[this.uid] || [];
for (var i = 0; i < listeners.length; i++) {
var listener = listeners[i];
this.screen.removeListener(listener.type, listener.handler);
}
};
Element.prototype.destroy = function() {
this.detach();
this.free();
this.screen.render();
};
Element.prototype.hide = function() {
@ -3006,7 +3031,7 @@ Element.prototype.enableDrag = function() {
self.setFront();
});
this.screen.on('mouse', this._dragM = function(data) {
this.onScreenEvent('mouse', this._dragM = function(data) {
if (self.screen._dragging !== self) return;
if (data.action !== 'mousedown') {
@ -3050,7 +3075,7 @@ Element.prototype.disableDrag = function() {
delete this.screen._dragging;
delete this._drag;
this.removeListener('mousedown', this._dragMD);
this.screen.removeListener('mouse', this._dragM);
this.removeScreenEvent('mouse', this._dragM);
return this._draggable = false;
};
@ -4981,7 +5006,7 @@ function ScrollableBox(options) {
self.screen.render();
var smd, smu;
self._scrollingBar = true;
self.screen.on('mousedown', smd = function(data) {
self.onScreenEvent('mousedown', smd = function(data) {
var y = data.y - self.atop;
var perc = y / self.height;
self.setScrollPerc(perc * 100 | 0);
@ -4990,10 +5015,10 @@ function ScrollableBox(options) {
// If mouseup occurs out of the window, no mouseup event fires, and
// scrollbar will drag again on mousedown until another mouseup
// occurs.
self.screen.on('mouseup', smu = function(data) {
self.onScreenEvent('mouseup', smu = function(data) {
self._scrollingBar = false;
self.screen.removeListener('mousedown', smd);
self.screen.removeListener('mouseup', smu);
self.removeScreenEvent('mousedown', smd);
self.removeScreenEvent('mouseup', smu);
});
}
});
@ -7111,7 +7136,7 @@ Question.prototype.ask = function(text, callback) {
this.show();
this.setContent(' ' + text);
this.screen.on('keypress', press = function(ch, key) {
this.onScreenEvent('keypress', press = function(ch, key) {
if (key.name === 'mouse') return;
if (key.name !== 'enter'
&& key.name !== 'escape'
@ -7137,7 +7162,7 @@ Question.prototype.ask = function(text, callback) {
function done(err, data) {
self.hide();
self.screen.restoreFocus();
self.screen.removeListener('keypress', press);
self.removeScreenEvent('keypress', press);
self._.okay.removeListener('press', okay);
self._.cancel.removeListener('press', cancel);
return callback(err, data);
@ -7210,7 +7235,7 @@ Message.prototype.display = function(text, time, callback) {
};
setTimeout(function() {
self.screen.on('keypress', function fn(ch, key) {
self.onScreenEvent('keypress', function fn(ch, key) {
if (key.name === 'mouse') return;
if (self.scrollable) {
if ((key.name === 'up' || (self.options.vi && key.name === 'k'))
@ -7227,13 +7252,13 @@ Message.prototype.display = function(text, time, callback) {
if (self.options.ignoreKeys && ~self.options.ignoreKeys.indexOf(key.name)) {
return;
}
self.screen.removeListener('keypress', fn);
self.removeScreenEvent('keypress', fn);
end();
});
if (!self.options.mouse) return;
self.screen.on('mouse', function fn(data) {
self.onScreenEvent('mouse', function fn(data) {
if (data.action === 'mousemove') return;
self.screen.removeListener('mouse', fn);
self.removeScreenEvent('mouse', fn);
end();
});
}, 10);
@ -7399,7 +7424,7 @@ function Listbar(options) {
}
if (options.autoCommandKeys) {
this.screen.on('keypress', function(ch, key) {
this.onScreenEvent('keypress', function(ch, key) {
if (/^[0-9]$/.test(ch)) {
var i = +ch - 1;
if (!~i) i = 9;
@ -8406,7 +8431,7 @@ Terminal.prototype.bootstrap = function() {
}
});
this.screen.on('mouse', function(data) {
this.onScreenEvent('mouse', function(data) {
if (self.screen.focused !== self) return;
if (data.x < self.aleft + self.ileft) return;
@ -8510,7 +8535,7 @@ Terminal.prototype.bootstrap = function() {
self.emit('exit', code || null);
});
this.screen.on('keypress', function() {
this.onScreenEvent('keypress', function() {
self.screen.render();
});