<!DOCTYPE html>
|
<html>
|
<head>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<title>The source code</title>
|
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
|
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
|
<style type="text/css">
|
.highlight { display: block; background-color: #ddd; }
|
</style>
|
<script type="text/javascript">
|
function highlight() {
|
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
|
}
|
</script>
|
</head>
|
<body onload="prettyPrint(); highlight();">
|
<pre class="prettyprint lang-js">// @tag dom,core
|
<span id='Ext-util-Event'>/**
|
</span> * Represents single event type that an Observable object listens to.
|
* All actual listeners are tracked inside here. When the event fires,
|
* it calls all the registered listener functions.
|
*
|
* @private
|
*/
|
Ext.define('Ext.util.Event', function() {
|
var arraySlice = Array.prototype.slice,
|
arrayInsert = Ext.Array.insert,
|
toArray = Ext.Array.toArray,
|
DelayedTask = Ext.util.DelayedTask;
|
|
return {
|
requires: 'Ext.util.DelayedTask',
|
|
<span id='Ext-util-Event-property-isEvent'> /**
|
</span> * @property {Boolean} isEvent
|
* `true` in this class to identify an object as an instantiated Event, or subclass thereof.
|
*/
|
isEvent: true,
|
|
// Private. Event suspend count
|
suspended: 0,
|
|
noOptions: {},
|
|
constructor: function(observable, name) {
|
this.name = name;
|
this.observable = observable;
|
this.listeners = [];
|
},
|
|
addListener: function(fn, scope, options) {
|
var me = this,
|
listeners, listener, priority, isNegativePriority, highestNegativePriorityIndex,
|
hasNegativePriorityIndex, length, index, i, listenerPriority;
|
|
scope = scope || me.observable;
|
|
//<debug error>
|
if (!fn) {
|
Ext.Error.raise({
|
sourceClass: Ext.getClassName(this.observable),
|
sourceMethod: "addListener",
|
msg: "The specified callback function is undefined"
|
});
|
}
|
//</debug>
|
|
if (!me.isListening(fn, scope)) {
|
listener = me.createListener(fn, scope, options);
|
if (me.firing) {
|
// if we are currently firing this event, don't disturb the listener loop
|
me.listeners = me.listeners.slice(0);
|
}
|
listeners = me.listeners;
|
index = length = listeners.length;
|
priority = options && options.priority;
|
highestNegativePriorityIndex = me._highestNegativePriorityIndex;
|
hasNegativePriorityIndex = (highestNegativePriorityIndex !== undefined);
|
if (priority) {
|
// Find the index at which to insert the listener into the listeners array,
|
// sorted by priority highest to lowest.
|
isNegativePriority = (priority < 0);
|
if (!isNegativePriority || hasNegativePriorityIndex) {
|
// If the priority is a positive number, or if it is a negative number
|
// and there are other existing negative priority listenrs, then we
|
// need to calcuate the listeners priority-order index.
|
// If the priority is a negative number, begin the search for priority
|
// order index at the index of the highest existing negative priority
|
// listener, otherwise begin at 0
|
for(i = (isNegativePriority ? highestNegativePriorityIndex : 0); i < length; i++) {
|
// Listeners created without options will have no "o" property
|
listenerPriority = listeners[i].o ? listeners[i].o.priority||0 : 0;
|
if (listenerPriority < priority) {
|
index = i;
|
break;
|
}
|
}
|
} else {
|
// if the priority is a negative number, and there are no other negative
|
// priority listeners, then no calculation is needed - the negative
|
// priority listener gets appended to the end of the listeners array.
|
me._highestNegativePriorityIndex = index;
|
}
|
} else if (hasNegativePriorityIndex) {
|
// listeners with a priority of 0 or undefined are appended to the end of
|
// the listeners array unless there are negative priority listeners in the
|
// listeners array, then they are inserted before the highest negative
|
// priority listener.
|
index = highestNegativePriorityIndex;
|
}
|
|
if (!isNegativePriority && index <= highestNegativePriorityIndex) {
|
me._highestNegativePriorityIndex ++;
|
}
|
if (index === length) {
|
me.listeners[length] = listener;
|
} else {
|
arrayInsert(me.listeners, index, [listener]);
|
}
|
}
|
},
|
|
createListener: function(fn, scope, o) {
|
scope = scope || this.observable;
|
|
var me = this,
|
listener = {
|
fn: fn,
|
scope: scope,
|
ev: me
|
},
|
handler = fn;
|
|
// The order is important. The 'single' wrapper must be wrapped by the 'buffer' and 'delayed' wrapper
|
// because the event removal that the single listener does destroys the listener's DelayedTask(s)
|
if (o) {
|
listener.o = o;
|
if (o.single) {
|
handler = me.createSingle(handler, listener, o, scope);
|
}
|
if (o.target) {
|
handler = me.createTargeted(handler, listener, o, scope);
|
}
|
if (o.delay) {
|
handler = me.createDelayed(handler, listener, o, scope);
|
}
|
if (o.buffer) {
|
handler = me.createBuffered(handler, listener, o, scope);
|
}
|
}
|
|
listener.fireFn = handler;
|
return listener;
|
},
|
|
findListener: function(fn, scope) {
|
var listeners = this.listeners,
|
i = listeners.length,
|
listener,
|
s;
|
|
while (i--) {
|
listener = listeners[i];
|
if (listener) {
|
s = listener.scope;
|
|
// Compare the listener's scope with *JUST THE PASSED SCOPE* if one is passed, and only fall back to the owning Observable if none is passed.
|
// We cannot use the test (s == scope || s == this.observable)
|
// Otherwise, if the Observable itself adds Ext.emptyFn as a listener, and then Ext.emptyFn is added under another scope, there will be a false match.
|
if (listener.fn == fn && (s == (scope || this.observable))) {
|
return i;
|
}
|
}
|
}
|
|
return - 1;
|
},
|
|
isListening: function(fn, scope) {
|
return this.findListener(fn, scope) !== -1;
|
},
|
|
removeListener: function(fn, scope) {
|
var me = this,
|
index,
|
listener,
|
highestNegativePriorityIndex,
|
k;
|
index = me.findListener(fn, scope);
|
if (index != -1) {
|
listener = me.listeners[index];
|
highestNegativePriorityIndex = me._highestNegativePriorityIndex;
|
|
if (me.firing) {
|
me.listeners = me.listeners.slice(0);
|
}
|
|
// cancel and remove a buffered handler that hasn't fired yet
|
if (listener.task) {
|
listener.task.cancel();
|
delete listener.task;
|
}
|
|
// cancel and remove all delayed handlers that haven't fired yet
|
k = listener.tasks && listener.tasks.length;
|
if (k) {
|
while (k--) {
|
listener.tasks[k].cancel();
|
}
|
delete listener.tasks;
|
}
|
|
// Remove this listener from the listeners array
|
// We can use splice directly. The IE8 bug which Ext.Array works around only affects *insertion*
|
// http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/6e946d03-e09f-4b22-a4dd-cd5e276bf05a/
|
me.listeners.splice(index, 1);
|
|
// if the listeners array contains negative priority listeners, adjust the
|
// internal index if needed.
|
if (highestNegativePriorityIndex) {
|
if (index < highestNegativePriorityIndex) {
|
me._highestNegativePriorityIndex --;
|
} else if (index === highestNegativePriorityIndex && index === me.listeners.length) {
|
delete me._highestNegativePriorityIndex;
|
}
|
}
|
return true;
|
}
|
|
return false;
|
},
|
|
// Iterate to stop any buffered/delayed events
|
clearListeners: function() {
|
var listeners = this.listeners,
|
i = listeners.length;
|
|
while (i--) {
|
this.removeListener(listeners[i].fn, listeners[i].scope);
|
}
|
},
|
|
suspend: function() {
|
this.suspended += 1;
|
},
|
|
resume: function() {
|
if (this.suspended) {
|
this.suspended--;
|
}
|
},
|
|
fire: function() {
|
var me = this,
|
listeners = me.listeners,
|
count = listeners.length,
|
i,
|
args,
|
listener,
|
len;
|
|
if (!me.suspended && count > 0) {
|
me.firing = true;
|
args = arguments.length ? arraySlice.call(arguments, 0) : []
|
len = args.length;
|
for (i = 0; i < count; i++) {
|
listener = listeners[i];
|
if (listener.o) {
|
args[len] = listener.o;
|
}
|
if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
|
return (me.firing = false);
|
}
|
}
|
}
|
me.firing = false;
|
return true;
|
},
|
|
createTargeted: function (handler, listener, o, scope) {
|
return function(){
|
if (o.target === arguments[0]){
|
handler.apply(scope, arguments);
|
}
|
};
|
},
|
|
createBuffered: function (handler, listener, o, scope) {
|
listener.task = new DelayedTask();
|
return function() {
|
listener.task.delay(o.buffer, handler, scope, toArray(arguments));
|
};
|
},
|
|
createDelayed: function (handler, listener, o, scope) {
|
return function() {
|
var task = new DelayedTask();
|
if (!listener.tasks) {
|
listener.tasks = [];
|
}
|
listener.tasks.push(task);
|
task.delay(o.delay || 10, handler, scope, toArray(arguments));
|
};
|
},
|
|
createSingle: function (handler, listener, o, scope) {
|
return function() {
|
var event = listener.ev;
|
|
if (event.removeListener(listener.fn, scope) && event.observable) {
|
// Removing from a regular Observable-owned, named event (not an anonymous
|
// event such as Ext's readyEvent): Decrement the listeners count
|
event.observable.hasListeners[event.name]--;
|
}
|
|
return handler.apply(scope, arguments);
|
};
|
}
|
};
|
});
|
</pre>
|
</body>
|
</html>
|