Observable.js
上传用户:dawnssy
上传日期:2022-08-06
资源大小:9345k
文件大小:17k
源码类别:

JavaScript

开发平台:

JavaScript

  1. /*!
  2.  * Ext JS Library 3.1.0
  3.  * Copyright(c) 2006-2009 Ext JS, LLC
  4.  * licensing@extjs.com
  5.  * http://www.extjs.com/license
  6.  */
  7. (function(){
  8. var EXTUTIL = Ext.util,
  9.     TOARRAY = Ext.toArray,
  10.     EACH = Ext.each,
  11.     ISOBJECT = Ext.isObject,
  12.     TRUE = true,
  13.     FALSE = false;
  14. /**
  15.  * @class Ext.util.Observable
  16.  * Base class that provides a common interface for publishing events. Subclasses are expected to
  17.  * to have a property "events" with all the events defined, and, optionally, a property "listeners"
  18.  * with configured listeners defined.<br>
  19.  * For example:
  20.  * <pre><code>
  21. Employee = Ext.extend(Ext.util.Observable, {
  22.     constructor: function(config){
  23.         this.name = config.name;
  24.         this.addEvents({
  25.             "fired" : true,
  26.             "quit" : true
  27.         });
  28.         // Copy configured listeners into *this* object so that the base class&#39;s
  29.         // constructor will add them.
  30.         this.listeners = config.listeners;
  31.         // Call our superclass constructor to complete construction process.
  32.         Employee.superclass.constructor.call(config)
  33.     }
  34. });
  35. </code></pre>
  36.  * This could then be used like this:<pre><code>
  37. var newEmployee = new Employee({
  38.     name: employeeName,
  39.     listeners: {
  40.         quit: function() {
  41.             // By default, "this" will be the object that fired the event.
  42.             alert(this.name + " has quit!");
  43.         }
  44.     }
  45. });
  46. </code></pre>
  47.  */
  48. EXTUTIL.Observable = function(){
  49.     /**
  50.      * @cfg {Object} listeners (optional) <p>A config object containing one or more event handlers to be added to this
  51.      * object during initialization.  This should be a valid listeners config object as specified in the
  52.      * {@link #addListener} example for attaching multiple handlers at once.</p>
  53.      * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
  54.      * <br><p>While <i>some</i> ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this
  55.      * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s
  56.      * <b><code>{@link Ext.DataView#click click}</code></b> event passing the node clicked on. To access DOM
  57.      * events directly from a Component's HTMLElement, listeners must be added to the <i>{@link Ext.Component#getEl Element}</i> after the Component
  58.      * has been rendered. A plugin can simplify this step:<pre><code>
  59. // Plugin is configured with a listeners config object.
  60. // The Component is appended to the argument list of all handler functions.
  61. Ext.DomObserver = Ext.extend(Object, {
  62.     constructor: function(config) {
  63.         this.listeners = config.listeners ? config.listeners : config;
  64.     },
  65.     // Component passes itself into plugin&#39;s init method
  66.     init: function(c) {
  67.         var p, l = this.listeners;
  68.         for (p in l) {
  69.             if (Ext.isFunction(l[p])) {
  70.                 l[p] = this.createHandler(l[p], c);
  71.             } else {
  72.                 l[p].fn = this.createHandler(l[p].fn, c);
  73.             }
  74.         }
  75.         // Add the listeners to the Element immediately following the render call
  76.         c.render = c.render.{@link Function#createSequence createSequence}(function() {
  77.             var e = c.getEl();
  78.             if (e) {
  79.                 e.on(l);
  80.             }
  81.         });
  82.     },
  83.     createHandler: function(fn, c) {
  84.         return function(e) {
  85.             fn.call(this, e, c);
  86.         };
  87.     }
  88. });
  89. var combo = new Ext.form.ComboBox({
  90.     // Collapse combo when its element is clicked on
  91.     plugins: [ new Ext.DomObserver({
  92.         click: function(evt, comp) {
  93.             comp.collapse();
  94.         }
  95.     })],
  96.     store: myStore,
  97.     typeAhead: true,
  98.     mode: 'local',
  99.     triggerAction: 'all'
  100. });
  101.      * </code></pre></p>
  102.      */
  103.     var me = this, e = me.events;
  104.     if(me.listeners){
  105.         me.on(me.listeners);
  106.         delete me.listeners;
  107.     }
  108.     me.events = e || {};
  109. };
  110. EXTUTIL.Observable.prototype = {
  111.     // private
  112.     filterOptRe : /^(?:scope|delay|buffer|single)$/,
  113.     /**
  114.      * <p>Fires the specified event with the passed parameters (minus the event name).</p>
  115.      * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
  116.      * by calling {@link #enableBubble}.</p>
  117.      * @param {String} eventName The name of the event to fire.
  118.      * @param {Object...} args Variable number of parameters are passed to handlers.
  119.      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
  120.      */
  121.     fireEvent : function(){
  122.         var a = TOARRAY(arguments),
  123.             ename = a[0].toLowerCase(),
  124.             me = this,
  125.             ret = TRUE,
  126.             ce = me.events[ename],
  127.             q,
  128.             c;
  129.         if (me.eventsSuspended === TRUE) {
  130.             if (q = me.eventQueue) {
  131.                 q.push(a);
  132.             }
  133.         }
  134.         else if(ISOBJECT(ce) && ce.bubble){
  135.             if(ce.fire.apply(ce, a.slice(1)) === FALSE) {
  136.                 return FALSE;
  137.             }
  138.             c = me.getBubbleTarget && me.getBubbleTarget();
  139.             if(c && c.enableBubble) {
  140.                 if(!c.events[ename] || !Ext.isObject(c.events[ename]) || !c.events[ename].bubble) {
  141.                     c.enableBubble(ename);
  142.                 }
  143.                 return c.fireEvent.apply(c, a);
  144.             }
  145.         }
  146.         else {
  147.             if (ISOBJECT(ce)) {
  148.                 a.shift();
  149.                 ret = ce.fire.apply(ce, a);
  150.             }
  151.         }
  152.         return ret;
  153.     },
  154.     /**
  155.      * Appends an event handler to this object.
  156.      * @param {String}   eventName The name of the event to listen for.
  157.      * @param {Function} handler The method the event invokes.
  158.      * @param {Object}   scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
  159.      * <b>If omitted, defaults to the object which fired the event.</b>
  160.      * @param {Object}   options (optional) An object containing handler configuration.
  161.      * properties. This may contain any of the following properties:<ul>
  162.      * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
  163.      * <b>If omitted, defaults to the object which fired the event.</b></div></li>
  164.      * <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>
  165.      * <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>
  166.      * <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
  167.      * by the specified number of milliseconds. If the event fires again within that time, the original
  168.      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
  169.      * <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>
  170.      * if the event was bubbled up from a child Observable.</div></li>
  171.      * </ul><br>
  172.      * <p>
  173.      * <b>Combining Options</b><br>
  174.      * Using the options argument, it is possible to combine different types of listeners:<br>
  175.      * <br>
  176.      * A delayed, one-time listener.
  177.      * <pre><code>
  178. myDataView.on('click', this.onClick, this, {
  179. single: true,
  180. delay: 100
  181. });</code></pre>
  182.      * <p>
  183.      * <b>Attaching multiple handlers in 1 call</b><br>
  184.      * The method also allows for a single argument to be passed which is a config object containing properties
  185.      * which specify multiple handlers.
  186.      * <p>
  187.      * <pre><code>
  188. myGridPanel.on({
  189. 'click' : {
  190.     fn: this.onClick,
  191.     scope: this,
  192.     delay: 100
  193. },
  194. 'mouseover' : {
  195.     fn: this.onMouseOver,
  196.     scope: this
  197. },
  198. 'mouseout' : {
  199.     fn: this.onMouseOut,
  200.     scope: this
  201. }
  202. });</code></pre>
  203.  * <p>
  204.  * Or a shorthand syntax:<br>
  205.  * <pre><code>
  206. myGridPanel.on({
  207. 'click' : this.onClick,
  208. 'mouseover' : this.onMouseOver,
  209. 'mouseout' : this.onMouseOut,
  210.  scope: this
  211. });</code></pre>
  212.      */
  213.     addListener : function(eventName, fn, scope, o){
  214.         var me = this,
  215.             e,
  216.             oe,
  217.             isF,
  218.         ce;
  219.         if (ISOBJECT(eventName)) {
  220.             o = eventName;
  221.             for (e in o){
  222.                 oe = o[e];
  223.                 if (!me.filterOptRe.test(e)) {
  224.                     me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
  225.                 }
  226.             }
  227.         } else {
  228.             eventName = eventName.toLowerCase();
  229.             ce = me.events[eventName] || TRUE;
  230.             if (Ext.isBoolean(ce)) {
  231.                 me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
  232.             }
  233.             ce.addListener(fn, scope, ISOBJECT(o) ? o : {});
  234.         }
  235.     },
  236.     /**
  237.      * Removes an event handler.
  238.      * @param {String}   eventName The type of event the handler was associated with.
  239.      * @param {Function} handler   The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
  240.      * @param {Object}   scope     (optional) The scope originally specified for the handler.
  241.      */
  242.     removeListener : function(eventName, fn, scope){
  243.         var ce = this.events[eventName.toLowerCase()];
  244.         if (ISOBJECT(ce)) {
  245.             ce.removeListener(fn, scope);
  246.         }
  247.     },
  248.     /**
  249.      * Removes all listeners for this object
  250.      */
  251.     purgeListeners : function(){
  252.         var events = this.events,
  253.             evt,
  254.             key;
  255.         for(key in events){
  256.             evt = events[key];
  257.             if(ISOBJECT(evt)){
  258.                 evt.clearListeners();
  259.             }
  260.         }
  261.     },
  262.     /**
  263.      * Adds the specified events to the list of events which this Observable may fire.
  264.      * @param {Object|String} o Either an object with event names as properties with a value of <code>true</code>
  265.      * or the first event name string if multiple event names are being passed as separate parameters.
  266.      * @param {string} Optional. Event name if multiple event names are being passed as separate parameters.
  267.      * Usage:<pre><code>
  268. this.addEvents('storeloaded', 'storecleared');
  269. </code></pre>
  270.      */
  271.     addEvents : function(o){
  272.         var me = this;
  273.         me.events = me.events || {};
  274.         if (Ext.isString(o)) {
  275.             var a = arguments,
  276.                 i = a.length;
  277.             while(i--) {
  278.                 me.events[a[i]] = me.events[a[i]] || TRUE;
  279.             }
  280.         } else {
  281.             Ext.applyIf(me.events, o);
  282.         }
  283.     },
  284.     /**
  285.      * Checks to see if this object has any listeners for a specified event
  286.      * @param {String} eventName The name of the event to check for
  287.      * @return {Boolean} True if the event is being listened for, else false
  288.      */
  289.     hasListener : function(eventName){
  290.         var e = this.events[eventName];
  291.         return ISOBJECT(e) && e.listeners.length > 0;
  292.     },
  293.     /**
  294.      * Suspend the firing of all events. (see {@link #resumeEvents})
  295.      * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
  296.      * after the {@link #resumeEvents} call instead of discarding all suspended events;
  297.      */
  298.     suspendEvents : function(queueSuspended){
  299.         this.eventsSuspended = TRUE;
  300.         if(queueSuspended && !this.eventQueue){
  301.             this.eventQueue = [];
  302.         }
  303.     },
  304.     /**
  305.      * Resume firing events. (see {@link #suspendEvents})
  306.      * If events were suspended using the <tt><b>queueSuspended</b></tt> parameter, then all
  307.      * events fired during event suspension will be sent to any listeners now.
  308.      */
  309.     resumeEvents : function(){
  310.         var me = this,
  311.             queued = me.eventQueue || [];
  312.         me.eventsSuspended = FALSE;
  313.         delete me.eventQueue;
  314.         EACH(queued, function(e) {
  315.             me.fireEvent.apply(me, e);
  316.         });
  317.     }
  318. };
  319. var OBSERVABLE = EXTUTIL.Observable.prototype;
  320. /**
  321.  * Appends an event handler to this object (shorthand for {@link #addListener}.)
  322.  * @param {String}   eventName     The type of event to listen for
  323.  * @param {Function} handler       The method the event invokes
  324.  * @param {Object}   scope         (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
  325.  * <b>If omitted, defaults to the object which fired the event.</b>
  326.  * @param {Object}   options       (optional) An object containing handler configuration.
  327.  * @method
  328.  */
  329. OBSERVABLE.on = OBSERVABLE.addListener;
  330. /**
  331.  * Removes an event handler (shorthand for {@link #removeListener}.)
  332.  * @param {String}   eventName     The type of event the handler was associated with.
  333.  * @param {Function} handler       The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
  334.  * @param {Object}   scope         (optional) The scope originally specified for the handler.
  335.  * @method
  336.  */
  337. OBSERVABLE.un = OBSERVABLE.removeListener;
  338. /**
  339.  * Removes <b>all</b> added captures from the Observable.
  340.  * @param {Observable} o The Observable to release
  341.  * @static
  342.  */
  343. EXTUTIL.Observable.releaseCapture = function(o){
  344.     o.fireEvent = OBSERVABLE.fireEvent;
  345. };
  346. function createTargeted(h, o, scope){
  347.     return function(){
  348.         if(o.target == arguments[0]){
  349.             h.apply(scope, TOARRAY(arguments));
  350.         }
  351.     };
  352. };
  353. function createBuffered(h, o, fn, scope){
  354.     fn.task = new EXTUTIL.DelayedTask();
  355.     return function(){
  356.         fn.task.delay(o.buffer, h, scope, TOARRAY(arguments));
  357.     };
  358. }
  359. function createSingle(h, e, fn, scope){
  360.     return function(){
  361.         e.removeListener(fn, scope);
  362.         return h.apply(scope, arguments);
  363.     };
  364. }
  365. function createDelayed(h, o, fn, scope){
  366.     return function(){
  367.         var task = new EXTUTIL.DelayedTask();
  368.         if(!fn.tasks) {
  369.             fn.tasks = [];
  370.         }
  371.         fn.tasks.push(task);
  372.         task.delay(o.delay || 10, h, scope, TOARRAY(arguments));
  373.     };
  374. };
  375. EXTUTIL.Event = function(obj, name){
  376.     this.name = name;
  377.     this.obj = obj;
  378.     this.listeners = [];
  379. };
  380. EXTUTIL.Event.prototype = {
  381.     addListener : function(fn, scope, options){
  382.         var me = this,
  383.             l;
  384.         scope = scope || me.obj;
  385.         if(!me.isListening(fn, scope)){
  386.             l = me.createListener(fn, scope, options);
  387.             if(me.firing){ // if we are currently firing this event, don't disturb the listener loop
  388.                 me.listeners = me.listeners.slice(0);
  389.             }
  390.             me.listeners.push(l);
  391.         }
  392.     },
  393.     createListener: function(fn, scope, o){
  394.         o = o || {}, scope = scope || this.obj;
  395.         var l = {
  396.             fn: fn,
  397.             scope: scope,
  398.             options: o
  399.         }, h = fn;
  400.         if(o.target){
  401.             h = createTargeted(h, o, scope);
  402.         }
  403.         if(o.delay){
  404.             h = createDelayed(h, o, fn, scope);
  405.         }
  406.         if(o.single){
  407.             h = createSingle(h, this, fn, scope);
  408.         }
  409.         if(o.buffer){
  410.             h = createBuffered(h, o, fn, scope);
  411.         }
  412.         l.fireFn = h;
  413.         return l;
  414.     },
  415.     findListener : function(fn, scope){
  416.         var list = this.listeners,
  417.             i = list.length,
  418.             l,
  419.             s;
  420.         while(i--) {
  421.             l = list[i];
  422.             if(l) {
  423.                 s = l.scope;
  424.                 if(l.fn == fn && (s == scope || s == this.obj)){
  425.                     return i;
  426.                 }
  427.             }
  428.         }
  429.         return -1;
  430.     },
  431.     isListening : function(fn, scope){
  432.         return this.findListener(fn, scope) != -1;
  433.     },
  434.     removeListener : function(fn, scope){
  435.         var index,
  436.             l,
  437.             k,
  438.             me = this,
  439.             ret = FALSE;
  440.         if((index = me.findListener(fn, scope)) != -1){
  441.             if (me.firing) {
  442.                 me.listeners = me.listeners.slice(0);
  443.             }
  444.             l = me.listeners[index].fn;
  445.             // Cancel buffered tasks
  446.             if(l.task) {
  447.                 l.task.cancel();
  448.                 delete l.task;
  449.             }
  450.             // Cancel delayed tasks
  451.             k = l.tasks && l.tasks.length;
  452.             if(k) {
  453.                 while(k--) {
  454.                     l.tasks[k].cancel();
  455.                 }
  456.                 delete l.tasks;
  457.             }
  458.             me.listeners.splice(index, 1);
  459.             ret = TRUE;
  460.         }
  461.         return ret;
  462.     },
  463.     // Iterate to stop any buffered/delayed events
  464.     clearListeners : function(){
  465.         var me = this,
  466.             l = me.listeners,
  467.             i = l.length;
  468.         while(i--) {
  469.             me.removeListener(l[i].fn, l[i].scope);
  470.         }
  471.     },
  472.     fire : function(){
  473.         var me = this,
  474.             args = TOARRAY(arguments),
  475.             listeners = me.listeners,
  476.             len = listeners.length,
  477.             i = 0,
  478.             l;
  479.         if(len > 0){
  480.             me.firing = TRUE;
  481.             for (; i < len; i++) {
  482.                 l = listeners[i];
  483.                 if(l && l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
  484.                     return (me.firing = FALSE);
  485.                 }
  486.             }
  487.         }
  488.         me.firing = FALSE;
  489.         return TRUE;
  490.     }
  491. };
  492. })();