+/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+XObject = imports.XObject.XObject;
+/**
+ * @class Observable
+ * 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 = XObject.define(
+ function(name){
+ this.name = name;
+ this.addEvents({
+ "fired" : true,
+ "quit" : true
+ });
+ },
+ Observable, {});
+
+</code></pre>
+ * @param {Object} config properties to use (incuding events / listeners)
+ */
+
+
+Observable = XObject.define(
+ function(cfg){
+
+ cfg = cfg|| {};
+ this.addEvents(cfg.events || {});
+ if (cfg.events) {
+ delete cfg.events; // make sure
+ }
+
+ XObject.extend(this, cfg);
+
+ if(this.listeners){
+ this.on(this.listeners);
+ delete this.listeners;
+ }
+ },
+ Object,
+ {
+ /**
+ * @cfg {Object} listeners list of events and functions to call for this object,
+ * For example :
+ * <pre><code>
+ listeners : {
+ 'click' : function(e) {
+ .....
+ } ,
+ ....
+ }
+ </code></pre>
+ */
+
+
+ /**
+ * 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(){
+ var ce = this.events[arguments[0].toLowerCase()];
+ if(typeof ce == "object"){
+ return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
+ }else{
+ return true;
+ }
+ },
+
+ // private - blcoks these types of signals?
+ 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 in which to execute the handler
+ * function. The handler function's "this" context.
+ * @param {Object} options (optional) An object containing handler configuration
+ * properties. This may contain any of the following properties:<ul>
+ * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
+ * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.NOT AVAIALBLE</li>
+ * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
+ * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link DelayedEvent} delayed NOT AVAIALBLE
+ * 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.</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 normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
+ <pre><code>
+ el.on('click', this.onClick, this, {
+ single: true,
+ delay: 100,
+ forumId: 4
+ });
+ </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.
+ * <pre><code>
+ el.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 which passes the same scope object to all handlers:
+ <pre><code>
+ el.on({
+ 'click': this.onClick,
+ 'mouseover': this.onMouseOver,
+ 'mouseout': this.onMouseOut,
+ scope: this
+ });
+ </code></pre>
+ */
+ on : 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.on(e, o[e], o.scope, o);
+ }else{
+ // individual options
+ this.on(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 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
+ */
+ un : 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();
+ }
+ }
+ },
+ _combine : function(){
+ var as = arguments, l = as.length, r = [];
+ for(var i = 0; i < l; i++){
+ var a = as[i];
+ if(a instanceof Array){
+ r = r.concat(a);
+ }else if(a.length !== undefined && !a.substr){
+ r = r.concat(Array.prototype.slice.call(a, 0));
+ }else{
+ r.push(a);
+ }
+ }
+ return r;
+ },
+
+ relayEvents : function(o, events){
+ var createHandler = function(ename){
+ return function(){
+ return this.fireEvent.apply(this, Event.prototype.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 = {};
+ }
+ XObject.extendIf(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;
+ }
+});
+
+/**
+ * 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
+ */
+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
+ */
+Observable.releaseCapture = function(o){
+ o.fireEvent = Observable.prototype.fireEvent;
+};
+
+
+
+var createSingle = function(h, e, fn, scope){
+ return function(){
+ e.removeListener(fn, scope);
+ return h.apply(scope, arguments);
+ };
+};
+
+// NOT SUPPORTED YET>
+//var createBuffered = function(h, o, scope){
+// var task = new DelayedTask();
+// return function(){
+// task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
+// };
+//};
+
+
+//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);
+// };
+//};
+
+
+/**
+ * Event Object - manages a specific event.
+ *
+ *
+ *
+ */
+
+
+
+Event = XObject.define(
+ function(obj, name){
+ this.name = name;
+ this.obj = obj;
+ this.listeners = [];
+ },
+ Object,
+ {
+ addListener : function(fn, scope, options){
+ var o = options || {};
+ scope = scope || this.obj;
+ if(!this.isListening(fn, scope)){
+ 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;
+ if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
+ this.listeners.push(l);
+ }else{
+ this.listeners = this.listeners.slice(0);
+ this.listeners.push(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;
+ }
+ }
+);