477 lines
16 KiB
JavaScript
477 lines
16 KiB
JavaScript
|
|
/*
|
||
|
|
* Ext JS Library 2.3.0
|
||
|
|
* Copyright(c) 2006-2009, Ext JS, LLC.
|
||
|
|
* licensing@extjs.com
|
||
|
|
*
|
||
|
|
* http://extjs.com/license
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @class Ext.util.Observable
|
||
|
|
* Abstract base class that provides a common interface for publishing events. Subclasses are expected to
|
||
|
|
* to have a property "events" with all the events defined.<br>
|
||
|
|
* For example:
|
||
|
|
* <pre><code>
|
||
|
|
Employee = function(name){
|
||
|
|
this.name = name;
|
||
|
|
this.addEvents({
|
||
|
|
"fired" : true,
|
||
|
|
"quit" : true
|
||
|
|
});
|
||
|
|
}
|
||
|
|
Ext.extend(Employee, Ext.util.Observable);
|
||
|
|
</code></pre>
|
||
|
|
*/
|
||
|
|
Ext.util.Observable = function(){
|
||
|
|
/**
|
||
|
|
* @cfg {Object} listeners (optional) A config object containing one or more event handlers to be added to this
|
||
|
|
* object during initialization. This should be a valid listeners config object as specified in the
|
||
|
|
* {@link #addListener} example for attaching multiple handlers at once.
|
||
|
|
*/
|
||
|
|
if(this.listeners){
|
||
|
|
this.on(this.listeners);
|
||
|
|
delete this.listeners;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
Ext.util.Observable.prototype = {
|
||
|
|
/**
|
||
|
|
* Fires the specified event with the passed parameters (minus the event name).
|
||
|
|
* @param {String} eventName
|
||
|
|
* @param {Object...} args Variable number of parameters are passed to handlers
|
||
|
|
* @return {Boolean} returns false if any of the handlers return false otherwise it returns true
|
||
|
|
*/
|
||
|
|
fireEvent : function(){
|
||
|
|
if(this.eventsSuspended !== true){
|
||
|
|
var ce = this.events[arguments[0].toLowerCase()];
|
||
|
|
if(typeof ce == "object"){
|
||
|
|
return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
},
|
||
|
|
|
||
|
|
// private
|
||
|
|
filterOptRe : /^(?:scope|delay|buffer|single)$/,
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Appends an event handler to this component
|
||
|
|
* @param {String} eventName The type of event to listen for
|
||
|
|
* @param {Function} handler The method the event invokes
|
||
|
|
* @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
|
||
|
|
* <b>If omitted, defaults to the object which fired the event.</b>
|
||
|
|
* @param {Object} options (optional) An object containing handler configuration
|
||
|
|
* properties. This may contain any of the following properties:<ul>
|
||
|
|
* <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
|
||
|
|
* <b>If omitted, defaults to the object which fired the event.</b></div></li>
|
||
|
|
* <li><b>delay</b> : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</div></li>
|
||
|
|
* <li><b>single</b> : Boolean<div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
|
||
|
|
* <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
|
||
|
|
* by the specified number of milliseconds. If the event fires again within that time, the original
|
||
|
|
* handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
|
||
|
|
* <li><b>target</b> : Observable<div class="sub-desc">Only call the handler if the event was fired on the target Observable, <i>not</i>
|
||
|
|
* if the event was bubbled up from a child Observable.</div></li>
|
||
|
|
* </ul><br>
|
||
|
|
* <p>
|
||
|
|
* <b>Combining Options</b><br>
|
||
|
|
* Using the options argument, it is possible to combine different types of listeners:<br>
|
||
|
|
* <br>
|
||
|
|
* A delayed, one-time listener.
|
||
|
|
* <pre><code>
|
||
|
|
myDataView.on('click', this.onClick, this, {
|
||
|
|
single: true,
|
||
|
|
delay: 100
|
||
|
|
});</code></pre>
|
||
|
|
* <p>
|
||
|
|
* <b>Attaching multiple handlers in 1 call</b><br>
|
||
|
|
* The method also allows for a single argument to be passed which is a config object containing properties
|
||
|
|
* which specify multiple handlers.
|
||
|
|
* <p>
|
||
|
|
* <pre><code>
|
||
|
|
myGridPanel.on({
|
||
|
|
'click' : {
|
||
|
|
fn: this.onClick,
|
||
|
|
scope: this,
|
||
|
|
delay: 100
|
||
|
|
},
|
||
|
|
'mouseover' : {
|
||
|
|
fn: this.onMouseOver,
|
||
|
|
scope: this
|
||
|
|
},
|
||
|
|
'mouseout' : {
|
||
|
|
fn: this.onMouseOut,
|
||
|
|
scope: this
|
||
|
|
}
|
||
|
|
});</code></pre>
|
||
|
|
* <p>
|
||
|
|
* Or a shorthand syntax:<br>
|
||
|
|
* <pre><code>
|
||
|
|
myGridPanel.on({
|
||
|
|
'click' : this.onClick,
|
||
|
|
'mouseover' : this.onMouseOver,
|
||
|
|
'mouseout' : this.onMouseOut,
|
||
|
|
scope: this
|
||
|
|
});</code></pre>
|
||
|
|
*/
|
||
|
|
addListener : function(eventName, fn, scope, o){
|
||
|
|
if(typeof eventName == "object"){
|
||
|
|
o = eventName;
|
||
|
|
for(var e in o){
|
||
|
|
if(this.filterOptRe.test(e)){
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if(typeof o[e] == "function"){
|
||
|
|
// shared options
|
||
|
|
this.addListener(e, o[e], o.scope, o);
|
||
|
|
}else{
|
||
|
|
// individual options
|
||
|
|
this.addListener(e, o[e].fn, o[e].scope, o[e]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
o = (!o || typeof o == "boolean") ? {} : o;
|
||
|
|
eventName = eventName.toLowerCase();
|
||
|
|
var ce = this.events[eventName] || true;
|
||
|
|
if(typeof ce == "boolean"){
|
||
|
|
ce = new Ext.util.Event(this, eventName);
|
||
|
|
this.events[eventName] = ce;
|
||
|
|
}
|
||
|
|
ce.addListener(fn, scope, o);
|
||
|
|
},
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Removes a listener
|
||
|
|
* @param {String} eventName The type of event to listen for
|
||
|
|
* @param {Function} handler The handler to remove
|
||
|
|
* @param {Object} scope (optional) The scope (this object) for the handler
|
||
|
|
*/
|
||
|
|
removeListener : function(eventName, fn, scope){
|
||
|
|
var ce = this.events[eventName.toLowerCase()];
|
||
|
|
if(typeof ce == "object"){
|
||
|
|
ce.removeListener(fn, scope);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Removes all listeners for this object
|
||
|
|
*/
|
||
|
|
purgeListeners : function(){
|
||
|
|
for(var evt in this.events){
|
||
|
|
if(typeof this.events[evt] == "object"){
|
||
|
|
this.events[evt].clearListeners();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.
|
||
|
|
* @param {Object} o The Observable whose events this object is to relay.
|
||
|
|
* @param {Array} events Array of event names to relay.
|
||
|
|
*/
|
||
|
|
relayEvents : function(o, events){
|
||
|
|
var createHandler = function(ename){
|
||
|
|
return function(){
|
||
|
|
return this.fireEvent.apply(this, Ext.combine(ename, Array.prototype.slice.call(arguments, 0)));
|
||
|
|
};
|
||
|
|
};
|
||
|
|
for(var i = 0, len = events.length; i < len; i++){
|
||
|
|
var ename = events[i];
|
||
|
|
if(!this.events[ename]){ this.events[ename] = true; };
|
||
|
|
o.on(ename, createHandler(ename), this);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Used to define events on this Observable
|
||
|
|
* @param {Object} object The object with the events defined
|
||
|
|
*/
|
||
|
|
addEvents : function(o){
|
||
|
|
if(!this.events){
|
||
|
|
this.events = {};
|
||
|
|
}
|
||
|
|
if(typeof o == 'string'){
|
||
|
|
for(var i = 0, a = arguments, v; v = a[i]; i++){
|
||
|
|
if(!this.events[a[i]]){
|
||
|
|
this.events[a[i]] = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}else{
|
||
|
|
Ext.applyIf(this.events, o);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Checks to see if this object has any listeners for a specified event
|
||
|
|
* @param {String} eventName The name of the event to check for
|
||
|
|
* @return {Boolean} True if the event is being listened for, else false
|
||
|
|
*/
|
||
|
|
hasListener : function(eventName){
|
||
|
|
var e = this.events[eventName];
|
||
|
|
return typeof e == "object" && e.listeners.length > 0;
|
||
|
|
},
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Suspend the firing of all events. (see {@link #resumeEvents})
|
||
|
|
*/
|
||
|
|
suspendEvents : function(){
|
||
|
|
this.eventsSuspended = true;
|
||
|
|
},
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Resume firing events. (see {@link #suspendEvents})
|
||
|
|
*/
|
||
|
|
resumeEvents : function(){
|
||
|
|
this.eventsSuspended = false;
|
||
|
|
},
|
||
|
|
|
||
|
|
// these are considered experimental
|
||
|
|
// allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
|
||
|
|
// private
|
||
|
|
getMethodEvent : function(method){
|
||
|
|
if(!this.methodEvents){
|
||
|
|
this.methodEvents = {};
|
||
|
|
}
|
||
|
|
var e = this.methodEvents[method];
|
||
|
|
if(!e){
|
||
|
|
e = {};
|
||
|
|
this.methodEvents[method] = e;
|
||
|
|
|
||
|
|
e.originalFn = this[method];
|
||
|
|
e.methodName = method;
|
||
|
|
e.before = [];
|
||
|
|
e.after = [];
|
||
|
|
|
||
|
|
|
||
|
|
var returnValue, v, cancel;
|
||
|
|
var obj = this;
|
||
|
|
|
||
|
|
var makeCall = function(fn, scope, args){
|
||
|
|
if((v = fn.apply(scope || obj, args)) !== undefined){
|
||
|
|
if(typeof v === 'object'){
|
||
|
|
if(v.returnValue !== undefined){
|
||
|
|
returnValue = v.returnValue;
|
||
|
|
}else{
|
||
|
|
returnValue = v;
|
||
|
|
}
|
||
|
|
if(v.cancel === true){
|
||
|
|
cancel = true;
|
||
|
|
}
|
||
|
|
}else if(v === false){
|
||
|
|
cancel = true;
|
||
|
|
}else {
|
||
|
|
returnValue = v;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
this[method] = function(){
|
||
|
|
returnValue = v = undefined; cancel = false;
|
||
|
|
var args = Array.prototype.slice.call(arguments, 0);
|
||
|
|
for(var i = 0, len = e.before.length; i < len; i++){
|
||
|
|
makeCall(e.before[i].fn, e.before[i].scope, args);
|
||
|
|
if(cancel){
|
||
|
|
return returnValue;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if((v = e.originalFn.apply(obj, args)) !== undefined){
|
||
|
|
returnValue = v;
|
||
|
|
}
|
||
|
|
|
||
|
|
for(var i = 0, len = e.after.length; i < len; i++){
|
||
|
|
makeCall(e.after[i].fn, e.after[i].scope, args);
|
||
|
|
if(cancel){
|
||
|
|
return returnValue;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return returnValue;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
return e;
|
||
|
|
},
|
||
|
|
|
||
|
|
// adds an "interceptor" called before the original method
|
||
|
|
beforeMethod : function(method, fn, scope){
|
||
|
|
var e = this.getMethodEvent(method);
|
||
|
|
e.before.push({fn: fn, scope: scope});
|
||
|
|
},
|
||
|
|
|
||
|
|
// adds a "sequence" called after the original method
|
||
|
|
afterMethod : function(method, fn, scope){
|
||
|
|
var e = this.getMethodEvent(method);
|
||
|
|
e.after.push({fn: fn, scope: scope});
|
||
|
|
},
|
||
|
|
|
||
|
|
removeMethodListener : function(method, fn, scope){
|
||
|
|
var e = this.getMethodEvent(method);
|
||
|
|
for(var i = 0, len = e.before.length; i < len; i++){
|
||
|
|
if(e.before[i].fn == fn && e.before[i].scope == scope){
|
||
|
|
e.before.splice(i, 1);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for(var i = 0, len = e.after.length; i < len; i++){
|
||
|
|
if(e.after[i].fn == fn && e.after[i].scope == scope){
|
||
|
|
e.after.splice(i, 1);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
/**
|
||
|
|
* Appends an event handler to this element (shorthand for addListener)
|
||
|
|
* @param {String} eventName The type of event to listen for
|
||
|
|
* @param {Function} handler The method the event invokes
|
||
|
|
* @param {Object} scope (optional) The scope in which to execute the handler
|
||
|
|
* function. The handler function's "this" context.
|
||
|
|
* @param {Object} options (optional)
|
||
|
|
* @method
|
||
|
|
*/
|
||
|
|
Ext.util.Observable.prototype.on = Ext.util.Observable.prototype.addListener;
|
||
|
|
/**
|
||
|
|
* Removes a listener (shorthand for removeListener)
|
||
|
|
* @param {String} eventName The type of event to listen for
|
||
|
|
* @param {Function} handler The handler to remove
|
||
|
|
* @param {Object} scope (optional) The scope (this object) for the handler
|
||
|
|
* @method
|
||
|
|
*/
|
||
|
|
Ext.util.Observable.prototype.un = Ext.util.Observable.prototype.removeListener;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Starts capture on the specified Observable. All events will be passed
|
||
|
|
* to the supplied function with the event name + standard signature of the event
|
||
|
|
* <b>before</b> the event is fired. If the supplied function returns false,
|
||
|
|
* the event will not fire.
|
||
|
|
* @param {Observable} o The Observable to capture
|
||
|
|
* @param {Function} fn The function to call
|
||
|
|
* @param {Object} scope (optional) The scope (this object) for the fn
|
||
|
|
* @static
|
||
|
|
*/
|
||
|
|
Ext.util.Observable.capture = function(o, fn, scope){
|
||
|
|
o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Removes <b>all</b> added captures from the Observable.
|
||
|
|
* @param {Observable} o The Observable to release
|
||
|
|
* @static
|
||
|
|
*/
|
||
|
|
Ext.util.Observable.releaseCapture = function(o){
|
||
|
|
o.fireEvent = Ext.util.Observable.prototype.fireEvent;
|
||
|
|
};
|
||
|
|
|
||
|
|
(function(){
|
||
|
|
|
||
|
|
var createBuffered = function(h, o, scope){
|
||
|
|
var task = new Ext.util.DelayedTask();
|
||
|
|
return function(){
|
||
|
|
task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
|
||
|
|
};
|
||
|
|
};
|
||
|
|
|
||
|
|
var createSingle = function(h, e, fn, scope){
|
||
|
|
return function(){
|
||
|
|
e.removeListener(fn, scope);
|
||
|
|
return h.apply(scope, arguments);
|
||
|
|
};
|
||
|
|
};
|
||
|
|
|
||
|
|
var createDelayed = function(h, o, scope){
|
||
|
|
return function(){
|
||
|
|
var args = Array.prototype.slice.call(arguments, 0);
|
||
|
|
setTimeout(function(){
|
||
|
|
h.apply(scope, args);
|
||
|
|
}, o.delay || 10);
|
||
|
|
};
|
||
|
|
};
|
||
|
|
|
||
|
|
Ext.util.Event = function(obj, name){
|
||
|
|
this.name = name;
|
||
|
|
this.obj = obj;
|
||
|
|
this.listeners = [];
|
||
|
|
};
|
||
|
|
|
||
|
|
Ext.util.Event.prototype = {
|
||
|
|
addListener : function(fn, scope, options){
|
||
|
|
scope = scope || this.obj;
|
||
|
|
if(!this.isListening(fn, scope)){
|
||
|
|
var l = this.createListener(fn, scope, options);
|
||
|
|
if(!this.firing){
|
||
|
|
this.listeners.push(l);
|
||
|
|
}else{ // if we are currently firing this event, don't disturb the listener loop
|
||
|
|
this.listeners = this.listeners.slice(0);
|
||
|
|
this.listeners.push(l);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
createListener : function(fn, scope, o){
|
||
|
|
o = o || {};
|
||
|
|
scope = scope || this.obj;
|
||
|
|
var l = {fn: fn, scope: scope, options: o};
|
||
|
|
var h = fn;
|
||
|
|
if(o.delay){
|
||
|
|
h = createDelayed(h, o, scope);
|
||
|
|
}
|
||
|
|
if(o.single){
|
||
|
|
h = createSingle(h, this, fn, scope);
|
||
|
|
}
|
||
|
|
if(o.buffer){
|
||
|
|
h = createBuffered(h, o, scope);
|
||
|
|
}
|
||
|
|
l.fireFn = h;
|
||
|
|
return l;
|
||
|
|
},
|
||
|
|
|
||
|
|
findListener : function(fn, scope){
|
||
|
|
scope = scope || this.obj;
|
||
|
|
var ls = this.listeners;
|
||
|
|
for(var i = 0, len = ls.length; i < len; i++){
|
||
|
|
var l = ls[i];
|
||
|
|
if(l.fn == fn && l.scope == scope){
|
||
|
|
return i;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
},
|
||
|
|
|
||
|
|
isListening : function(fn, scope){
|
||
|
|
return this.findListener(fn, scope) != -1;
|
||
|
|
},
|
||
|
|
|
||
|
|
removeListener : function(fn, scope){
|
||
|
|
var index;
|
||
|
|
if((index = this.findListener(fn, scope)) != -1){
|
||
|
|
if(!this.firing){
|
||
|
|
this.listeners.splice(index, 1);
|
||
|
|
}else{
|
||
|
|
this.listeners = this.listeners.slice(0);
|
||
|
|
this.listeners.splice(index, 1);
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
},
|
||
|
|
|
||
|
|
clearListeners : function(){
|
||
|
|
this.listeners = [];
|
||
|
|
},
|
||
|
|
|
||
|
|
fire : function(){
|
||
|
|
var ls = this.listeners, scope, len = ls.length;
|
||
|
|
if(len > 0){
|
||
|
|
this.firing = true;
|
||
|
|
var args = Array.prototype.slice.call(arguments, 0);
|
||
|
|
for(var i = 0; i < len; i++){
|
||
|
|
var l = ls[i];
|
||
|
|
if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
|
||
|
|
this.firing = false;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
this.firing = false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
})();
|