// @tag class
/**
*
* @since 6.7.0
* @private
*/
Ext.define('Ext.mixin.Watchable', {
on: function(name, fn, scope) {
return this._watchUpdate(false, '_watchAdd', name, fn, scope);
},
fire: function(event, args) {
var me = this,
watching = me.watching,
watchers = watching && watching[event],
fn, i, r, scope;
if (watchers) {
++watchers.$firing;
for (i = 0; i < watchers.length; ++i) {
scope = watchers[i][0];
fn = watchers[i][1];
if (fn.charAt) {
r = args ? scope[fn].apply(scope, args) : scope[fn]();
}
else {
r = args ? fn.apply(scope, args) : fn.call(scope);
}
if (r === false) {
return r;
}
}
--watchers.$firing;
}
},
fireEvent: function() {
var args = Ext.Array.slice(arguments),
event = args.shift();
return this.fire(event, args);
},
un: function(name, fn, scope) {
return this._watchUpdate(true, '_watchRemove', name, fn, scope);
},
privates: {
watching: null,
$watchOptions: {
destroyable: 1,
scope: 1
},
_watchAdd: function(watching, name, fn, scope, destroyable) {
//<debug>
if (typeof fn === 'string' && !scope[fn]) {
Ext.raise('No such method "' + fn + '" on ' + scope.$className);
}
//</debug>
// eslint-disable-next-line vars-on-top
var watchers = watching[name],
entry = [scope, fn],
i, ent;
if (!watchers) {
watching[name] = watchers = [];
watchers.$firing = 0;
}
else {
// If the scope/fn pair is already registered, don't duplicate it.
for (i = watchers.length; i-- > 0; /* empty */) {
ent = watchers[i];
if (fn === ent[1]) {
if (scope ? ent[0] === scope : !ent[0]) {
return;
}
}
}
if (watchers.$firing) {
watching[name] = watchers = watchers.slice();
watchers.$firing = 0;
}
}
watchers.push(entry);
if (destroyable) {
entry.push(name);
destroyable.items.push(entry);
}
},
_watchRemove: function(watching, name, fn, scope) {
var watchers = watching[name],
i;
if (watchers) {
if (watchers.$firing) {
watching[name] = watchers = watchers.slice();
watchers.$firing = 0;
}
for (i = watchers.length; i-- > 0; /* empty */) {
if (watchers[i][0] === scope && watchers[i][1] === fn) {
watchers.splice(i, 1);
}
}
}
},
_watchUpdate: function(remove, process, name, fn, scope) {
var me = this,
watch = name,
watching = me.watching,
destroyable;
if (!watching) {
if (remove) {
return;
}
me.watching = watching = {};
}
if (typeof name === 'string') {
me[process](watching, name, fn, scope);
}
else {
destroyable = watch.destroyable
? { owner: me, items: [], destroy: me._watcherDestroyer }
: null;
scope = watch.scope;
for (name in watch) {
if (!me.$watchOptions[name]) {
me[process](watching, name, watch[name], scope, destroyable);
}
}
}
return destroyable;
},
_watcherDestroyer: function() {
var me = this.owner,
watching = me.watching,
items = this.items,
entry, i;
for (i = 0; i < items.length; ++i) {
entry = items[i];
me._watchRemove(watching, entry[2], entry[1], entry[0]);
}
}
}
});