Observable.html 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>The source code</title>
  6. <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7. <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8. <style type="text/css">
  9. .highlight { display: block; background-color: #ddd; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-util-Observable'>/**
  19. </span> * Base class that provides a common interface for publishing events. Subclasses are expected to to have a property
  20. * &quot;events&quot; with all the events defined, and, optionally, a property &quot;listeners&quot; with configured listeners defined.
  21. *
  22. * For example:
  23. *
  24. * Ext.define('Employee', {
  25. * mixins: {
  26. * observable: 'Ext.util.Observable'
  27. * },
  28. *
  29. * constructor: function (config) {
  30. * // The Observable constructor copies all of the properties of `config` on
  31. * // to `this` using {@link Ext#apply}. Further, the `listeners` property is
  32. * // processed to add listeners.
  33. * //
  34. * this.mixins.observable.constructor.call(this, config);
  35. *
  36. * this.addEvents(
  37. * 'fired',
  38. * 'quit'
  39. * );
  40. * }
  41. * });
  42. *
  43. * This could then be used like this:
  44. *
  45. * var newEmployee = new Employee({
  46. * name: employeeName,
  47. * listeners: {
  48. * quit: function() {
  49. * // By default, &quot;this&quot; will be the object that fired the event.
  50. * alert(this.name + &quot; has quit!&quot;);
  51. * }
  52. * }
  53. * });
  54. */
  55. Ext.define('Ext.util.Observable', {
  56. /* Begin Definitions */
  57. requires: ['Ext.util.Event'],
  58. statics: {
  59. <span id='Ext-util-Observable-static-method-releaseCapture'> /**
  60. </span> * Removes **all** added captures from the Observable.
  61. *
  62. * @param {Ext.util.Observable} o The Observable to release
  63. * @static
  64. */
  65. releaseCapture: function(o) {
  66. o.fireEvent = this.prototype.fireEvent;
  67. },
  68. <span id='Ext-util-Observable-static-method-capture'> /**
  69. </span> * Starts capture on the specified Observable. All events will be passed to the supplied function with the event
  70. * name + standard signature of the event **before** the event is fired. If the supplied function returns false,
  71. * the event will not fire.
  72. *
  73. * @param {Ext.util.Observable} o The Observable to capture events from.
  74. * @param {Function} fn The function to call when an event is fired.
  75. * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to
  76. * the Observable firing the event.
  77. * @static
  78. */
  79. capture: function(o, fn, scope) {
  80. o.fireEvent = Ext.Function.createInterceptor(o.fireEvent, fn, scope);
  81. },
  82. <span id='Ext-util-Observable-static-method-observe'> /**
  83. </span> * Sets observability on the passed class constructor.
  84. *
  85. * This makes any event fired on any instance of the passed class also fire a single event through
  86. * the **class** allowing for central handling of events on many instances at once.
  87. *
  88. * Usage:
  89. *
  90. * Ext.util.Observable.observe(Ext.data.Connection);
  91. * Ext.data.Connection.on('beforerequest', function(con, options) {
  92. * console.log('Ajax request made to ' + options.url);
  93. * });
  94. *
  95. * @param {Function} c The class constructor to make observable.
  96. * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
  97. * @static
  98. */
  99. observe: function(cls, listeners) {
  100. if (cls) {
  101. if (!cls.isObservable) {
  102. Ext.applyIf(cls, new this());
  103. this.capture(cls.prototype, cls.fireEvent, cls);
  104. }
  105. if (Ext.isObject(listeners)) {
  106. cls.on(listeners);
  107. }
  108. }
  109. return cls;
  110. },
  111. <span id='Ext-util-Observable-method-prepareClass'> /**
  112. </span> * Prepares a given class for observable instances. This method is called when a
  113. * class derives from this class or uses this class as a mixin.
  114. * @param {Function} T The class constructor to prepare.
  115. * @private
  116. */
  117. prepareClass: function (T, mixin) {
  118. // T.hasListeners is the object to track listeners on class T. This object's
  119. // prototype (__proto__) is the &quot;hasListeners&quot; of T.superclass.
  120. // Instances of T will create &quot;hasListeners&quot; that have T.hasListeners as their
  121. // immediate prototype (__proto__).
  122. if (!T.HasListeners) {
  123. // We create a HasListeners &quot;class&quot; for this class. The &quot;prototype&quot; of the
  124. // HasListeners class is an instance of the HasListeners class associated
  125. // with this class's super class (or with Observable).
  126. var Observable = Ext.util.Observable,
  127. HasListeners = function () {},
  128. SuperHL = T.superclass.HasListeners || (mixin &amp;&amp; mixin.HasListeners) ||
  129. Observable.HasListeners;
  130. // Make the HasListener class available on the class and its prototype:
  131. T.prototype.HasListeners = T.HasListeners = HasListeners;
  132. // And connect its &quot;prototype&quot; to the new HasListeners of our super class
  133. // (which is also the class-level &quot;hasListeners&quot; instance).
  134. HasListeners.prototype = T.hasListeners = new SuperHL();
  135. }
  136. }
  137. },
  138. /* End Definitions */
  139. <span id='Ext-util-Observable-cfg-listeners'> /**
  140. </span> * @cfg {Object} listeners
  141. *
  142. * A config object containing one or more event handlers to be added to this object during initialization. This
  143. * should be a valid listeners config object as specified in the {@link #addListener} example for attaching multiple
  144. * handlers at once.
  145. *
  146. * **DOM events from Ext JS {@link Ext.Component Components}**
  147. *
  148. * While _some_ Ext JS Component classes export selected DOM events (e.g. &quot;click&quot;, &quot;mouseover&quot; etc), this is usually
  149. * only done when extra value can be added. For example the {@link Ext.view.View DataView}'s **`{@link
  150. * Ext.view.View#itemclick itemclick}`** event passing the node clicked on. To access DOM events directly from a
  151. * child element of a Component, we need to specify the `element` option to identify the Component property to add a
  152. * DOM listener to:
  153. *
  154. * new Ext.panel.Panel({
  155. * width: 400,
  156. * height: 200,
  157. * dockedItems: [{
  158. * xtype: 'toolbar'
  159. * }],
  160. * listeners: {
  161. * click: {
  162. * element: 'el', //bind to the underlying el property on the panel
  163. * fn: function(){ console.log('click el'); }
  164. * },
  165. * dblclick: {
  166. * element: 'body', //bind to the underlying body property on the panel
  167. * fn: function(){ console.log('dblclick body'); }
  168. * }
  169. * }
  170. * });
  171. */
  172. <span id='Ext-util-Observable-property-isObservable'> /**
  173. </span> * @property {Boolean} isObservable
  174. * `true` in this class to identify an object as an instantiated Observable, or subclass thereof.
  175. */
  176. isObservable: true,
  177. <span id='Ext-util-Observable-property-eventsSuspended'> /**
  178. </span> * @private
  179. * Initial suspended call count. Incremented when {@link #suspendEvents} is called, decremented when {@link #resumeEvents} is called.
  180. */
  181. eventsSuspended: 0,
  182. <span id='Ext-util-Observable-property-hasListeners'> /**
  183. </span> * @property {Object} hasListeners
  184. * @readonly
  185. * This object holds a key for any event that has a listener. The listener may be set
  186. * directly on the instance, or on its class or a super class (via {@link #observe}) or
  187. * on the {@link Ext.app.EventBus MVC EventBus}. The values of this object are truthy
  188. * (a non-zero number) and falsy (0 or undefined). They do not represent an exact count
  189. * of listeners. The value for an event is truthy if the event must be fired and is
  190. * falsy if there is no need to fire the event.
  191. *
  192. * The intended use of this property is to avoid the expense of fireEvent calls when
  193. * there are no listeners. This can be particularly helpful when one would otherwise
  194. * have to call fireEvent hundreds or thousands of times. It is used like this:
  195. *
  196. * if (this.hasListeners.foo) {
  197. * this.fireEvent('foo', this, arg1);
  198. * }
  199. */
  200. constructor: function(config) {
  201. var me = this;
  202. Ext.apply(me, config);
  203. // The subclass may have already initialized it.
  204. if (!me.hasListeners) {
  205. me.hasListeners = new me.HasListeners();
  206. }
  207. me.events = me.events || {};
  208. if (me.listeners) {
  209. me.on(me.listeners);
  210. me.listeners = null; //Set as an instance property to pre-empt the prototype in case any are set there.
  211. }
  212. if (me.bubbleEvents) {
  213. me.enableBubble(me.bubbleEvents);
  214. }
  215. },
  216. onClassExtended: function (T) {
  217. if (!T.HasListeners) {
  218. // Some classes derive from us and some others derive from those classes. All
  219. // of these are passed to this method.
  220. Ext.util.Observable.prepareClass(T);
  221. }
  222. },
  223. // @private
  224. eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|vertical|horizontal|freezeEvent)$/,
  225. <span id='Ext-util-Observable-method-addManagedListener'> /**
  226. </span> * Adds listeners to any Observable object (or Ext.Element) which are automatically removed when this Component is
  227. * destroyed.
  228. *
  229. * @param {Ext.util.Observable/Ext.Element} item The item to which to add a listener/listeners.
  230. * @param {Object/String} ename The event name, or an object containing event name properties.
  231. * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function.
  232. * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference)
  233. * in which the handler function is executed.
  234. * @param {Object} opt (optional) If the `ename` parameter was an event name, this is the
  235. * {@link Ext.util.Observable#addListener addListener} options.
  236. */
  237. addManagedListener : function(item, ename, fn, scope, options) {
  238. var me = this,
  239. managedListeners = me.managedListeners = me.managedListeners || [],
  240. config;
  241. if (typeof ename !== 'string') {
  242. options = ename;
  243. for (ename in options) {
  244. if (options.hasOwnProperty(ename)) {
  245. config = options[ename];
  246. if (!me.eventOptionsRe.test(ename)) {
  247. me.addManagedListener(item, ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
  248. }
  249. }
  250. }
  251. }
  252. else {
  253. managedListeners.push({
  254. item: item,
  255. ename: ename,
  256. fn: fn,
  257. scope: scope,
  258. options: options
  259. });
  260. item.on(ename, fn, scope, options);
  261. }
  262. },
  263. <span id='Ext-util-Observable-method-removeManagedListener'> /**
  264. </span> * Removes listeners that were added by the {@link #mon} method.
  265. *
  266. * @param {Ext.util.Observable/Ext.Element} item The item from which to remove a listener/listeners.
  267. * @param {Object/String} ename The event name, or an object containing event name properties.
  268. * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function.
  269. * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference)
  270. * in which the handler function is executed.
  271. */
  272. removeManagedListener : function(item, ename, fn, scope) {
  273. var me = this,
  274. options,
  275. config,
  276. managedListeners,
  277. length,
  278. i;
  279. if (typeof ename !== 'string') {
  280. options = ename;
  281. for (ename in options) {
  282. if (options.hasOwnProperty(ename)) {
  283. config = options[ename];
  284. if (!me.eventOptionsRe.test(ename)) {
  285. me.removeManagedListener(item, ename, config.fn || config, config.scope || options.scope);
  286. }
  287. }
  288. }
  289. }
  290. managedListeners = me.managedListeners ? me.managedListeners.slice() : [];
  291. for (i = 0, length = managedListeners.length; i &lt; length; i++) {
  292. me.removeManagedListenerItem(false, managedListeners[i], item, ename, fn, scope);
  293. }
  294. },
  295. <span id='Ext-util-Observable-method-fireEvent'> /**
  296. </span> * Fires the specified event with the passed parameters (minus the event name, plus the `options` object passed
  297. * to {@link #addListener}).
  298. *
  299. * An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget}) by
  300. * calling {@link #enableBubble}.
  301. *
  302. * @param {String} eventName The name of the event to fire.
  303. * @param {Object...} args Variable number of parameters are passed to handlers.
  304. * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
  305. */
  306. fireEvent: function(eventName) {
  307. eventName = eventName.toLowerCase();
  308. var me = this,
  309. events = me.events,
  310. event = events &amp;&amp; events[eventName],
  311. ret = true;
  312. // Only continue firing the event if there are listeners to be informed.
  313. // Bubbled events will always have a listener count, so will be fired.
  314. if (event &amp;&amp; me.hasListeners[eventName]) {
  315. ret = me.continueFireEvent(eventName, Ext.Array.slice(arguments, 1), event.bubble);
  316. }
  317. return ret;
  318. },
  319. <span id='Ext-util-Observable-method-continueFireEvent'> /**
  320. </span> * Continue to fire event.
  321. * @private
  322. *
  323. * @param {String} eventName
  324. * @param {Array} args
  325. * @param {Boolean} bubbles
  326. */
  327. continueFireEvent: function(eventName, args, bubbles) {
  328. var target = this,
  329. queue, event,
  330. ret = true;
  331. do {
  332. if (target.eventsSuspended) {
  333. if ((queue = target.eventQueue)) {
  334. queue.push([eventName, args, bubbles]);
  335. }
  336. return ret;
  337. } else {
  338. event = target.events[eventName];
  339. // Continue bubbling if event exists and it is `true` or the handler didn't returns false and it
  340. // configure to bubble.
  341. if (event &amp;&amp; event != true) {
  342. if ((ret = event.fire.apply(event, args)) === false) {
  343. break;
  344. }
  345. }
  346. }
  347. } while (bubbles &amp;&amp; (target = target.getBubbleParent()));
  348. return ret;
  349. },
  350. <span id='Ext-util-Observable-method-getBubbleParent'> /**
  351. </span> * Gets the bubbling parent for an Observable
  352. * @private
  353. * @return {Ext.util.Observable} The bubble parent. null is returned if no bubble target exists
  354. */
  355. getBubbleParent: function(){
  356. var me = this, parent = me.getBubbleTarget &amp;&amp; me.getBubbleTarget();
  357. if (parent &amp;&amp; parent.isObservable) {
  358. return parent;
  359. }
  360. return null;
  361. },
  362. <span id='Ext-util-Observable-method-addListener'> /**
  363. </span> * Appends an event handler to this object. For example:
  364. *
  365. * myGridPanel.on(&quot;mouseover&quot;, this.onMouseOver, this);
  366. *
  367. * The method also allows for a single argument to be passed which is a config object
  368. * containing properties which specify multiple events. For example:
  369. *
  370. * myGridPanel.on({
  371. * cellClick: this.onCellClick,
  372. * mouseover: this.onMouseOver,
  373. * mouseout: this.onMouseOut,
  374. * scope: this // Important. Ensure &quot;this&quot; is correct during handler execution
  375. * });
  376. *
  377. * One can also specify options for each event handler separately:
  378. *
  379. * myGridPanel.on({
  380. * cellClick: {fn: this.onCellClick, scope: this, single: true},
  381. * mouseover: {fn: panel.onMouseOver, scope: panel}
  382. * });
  383. *
  384. * *Names* of methods in a specified scope may also be used. Note that
  385. * `scope` MUST be specified to use this option:
  386. *
  387. * myGridPanel.on({
  388. * cellClick: {fn: 'onCellClick', scope: this, single: true},
  389. * mouseover: {fn: 'onMouseOver', scope: panel}
  390. * });
  391. *
  392. * @param {String/Object} eventName The name of the event to listen for.
  393. * May also be an object who's property names are event names.
  394. *
  395. * @param {Function} [fn] The method the event invokes, or *if `scope` is specified, the *name* of the method within
  396. * the specified `scope`. Will be called with arguments
  397. * given to {@link #fireEvent} plus the `options` parameter described below.
  398. *
  399. * @param {Object} [scope] The scope (`this` reference) in which the handler function is
  400. * executed. **If omitted, defaults to the object which fired the event.**
  401. *
  402. * @param {Object} [options] An object containing handler configuration.
  403. *
  404. * **Note:** Unlike in ExtJS 3.x, the options object will also be passed as the last
  405. * argument to every event handler.
  406. *
  407. * This object may contain any of the following properties:
  408. *
  409. * @param {Object} options.scope
  410. * The scope (`this` reference) in which the handler function is executed. **If omitted,
  411. * defaults to the object which fired the event.**
  412. *
  413. * @param {Number} options.delay
  414. * The number of milliseconds to delay the invocation of the handler after the event fires.
  415. *
  416. * @param {Boolean} options.single
  417. * True to add a handler to handle just the next firing of the event, and then remove itself.
  418. *
  419. * @param {Number} options.buffer
  420. * Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
  421. * by the specified number of milliseconds. If the event fires again within that time,
  422. * the original handler is _not_ invoked, but the new handler is scheduled in its place.
  423. *
  424. * @param {Ext.util.Observable} options.target
  425. * Only call the handler if the event was fired on the target Observable, _not_ if the event
  426. * was bubbled up from a child Observable.
  427. *
  428. * @param {String} options.element
  429. * **This option is only valid for listeners bound to {@link Ext.Component Components}.**
  430. * The name of a Component property which references an element to add a listener to.
  431. *
  432. * This option is useful during Component construction to add DOM event listeners to elements of
  433. * {@link Ext.Component Components} which will exist only after the Component is rendered.
  434. * For example, to add a click listener to a Panel's body:
  435. *
  436. * new Ext.panel.Panel({
  437. * title: 'The title',
  438. * listeners: {
  439. * click: this.handlePanelClick,
  440. * element: 'body'
  441. * }
  442. * });
  443. *
  444. * **Combining Options**
  445. *
  446. * Using the options argument, it is possible to combine different types of listeners:
  447. *
  448. * A delayed, one-time listener.
  449. *
  450. * myPanel.on('hide', this.handleClick, this, {
  451. * single: true,
  452. * delay: 100
  453. * });
  454. *
  455. */
  456. addListener: function(ename, fn, scope, options) {
  457. var me = this,
  458. config, event, hasListeners,
  459. prevListenerCount = 0;
  460. if (typeof ename !== 'string') {
  461. options = ename;
  462. for (ename in options) {
  463. if (options.hasOwnProperty(ename)) {
  464. config = options[ename];
  465. if (!me.eventOptionsRe.test(ename)) {
  466. me.addListener(ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
  467. }
  468. }
  469. }
  470. } else {
  471. ename = ename.toLowerCase();
  472. event = me.events[ename];
  473. if (event &amp;&amp; event.isEvent) {
  474. prevListenerCount = event.listeners.length;
  475. } else {
  476. me.events[ename] = event = new Ext.util.Event(me, ename);
  477. }
  478. // Allow listeners: { click: 'onClick', scope: myObject }
  479. if (typeof fn === 'string') {
  480. //&lt;debug&gt;
  481. if (!(scope[fn] || me[fn])) {
  482. Ext.Error.raise('No method named &quot;' + fn + '&quot;');
  483. }
  484. //&lt;/debug&gt;
  485. fn = scope[fn] || me[fn];
  486. }
  487. event.addListener(fn, scope, options);
  488. // If a new listener has been added (Event.addListener rejects duplicates of the same fn+scope)
  489. // then increment the hasListeners counter
  490. if (event.listeners.length !== prevListenerCount) {
  491. hasListeners = me.hasListeners;
  492. if (hasListeners.hasOwnProperty(ename)) {
  493. // if we already have listeners at this level, just increment the count...
  494. ++hasListeners[ename];
  495. } else {
  496. // otherwise, start the count at 1 (which hides whatever is in our prototype
  497. // chain)...
  498. hasListeners[ename] = 1;
  499. }
  500. }
  501. }
  502. },
  503. <span id='Ext-util-Observable-method-removeListener'> /**
  504. </span> * Removes an event handler.
  505. *
  506. * @param {String} eventName The type of event the handler was associated with.
  507. * @param {Function} fn The handler to remove. **This must be a reference to the function passed into the
  508. * {@link #addListener} call.**
  509. * @param {Object} scope (optional) The scope originally specified for the handler. It must be the same as the
  510. * scope argument specified in the original call to {@link #addListener} or the listener will not be removed.
  511. */
  512. removeListener: function(ename, fn, scope) {
  513. var me = this,
  514. config,
  515. event,
  516. options;
  517. if (typeof ename !== 'string') {
  518. options = ename;
  519. for (ename in options) {
  520. if (options.hasOwnProperty(ename)) {
  521. config = options[ename];
  522. if (!me.eventOptionsRe.test(ename)) {
  523. me.removeListener(ename, config.fn || config, config.scope || options.scope);
  524. }
  525. }
  526. }
  527. } else {
  528. ename = ename.toLowerCase();
  529. event = me.events[ename];
  530. if (event &amp;&amp; event.isEvent) {
  531. if (event.removeListener(fn, scope) &amp;&amp; !--me.hasListeners[ename]) {
  532. // Delete this entry, since 0 does not mean no one is listening, just
  533. // that no one is *directly&amp; listening. This allows the eventBus or
  534. // class observers to &quot;poke&quot; through and expose their presence.
  535. delete me.hasListeners[ename];
  536. }
  537. }
  538. }
  539. },
  540. <span id='Ext-util-Observable-method-clearListeners'> /**
  541. </span> * Removes all listeners for this object including the managed listeners
  542. */
  543. clearListeners: function() {
  544. var events = this.events,
  545. event,
  546. key;
  547. for (key in events) {
  548. if (events.hasOwnProperty(key)) {
  549. event = events[key];
  550. if (event.isEvent) {
  551. event.clearListeners();
  552. }
  553. }
  554. }
  555. this.clearManagedListeners();
  556. },
  557. //&lt;debug&gt;
  558. purgeListeners : function() {
  559. if (Ext.global.console) {
  560. Ext.global.console.warn('Observable: purgeListeners has been deprecated. Please use clearListeners.');
  561. }
  562. return this.clearListeners.apply(this, arguments);
  563. },
  564. //&lt;/debug&gt;
  565. <span id='Ext-util-Observable-method-clearManagedListeners'> /**
  566. </span> * Removes all managed listeners for this object.
  567. */
  568. clearManagedListeners : function() {
  569. var managedListeners = this.managedListeners || [],
  570. i = 0,
  571. len = managedListeners.length;
  572. for (; i &lt; len; i++) {
  573. this.removeManagedListenerItem(true, managedListeners[i]);
  574. }
  575. this.managedListeners = [];
  576. },
  577. <span id='Ext-util-Observable-method-removeManagedListenerItem'> /**
  578. </span> * Remove a single managed listener item
  579. * @private
  580. * @param {Boolean} isClear True if this is being called during a clear
  581. * @param {Object} managedListener The managed listener item
  582. * See removeManagedListener for other args
  583. */
  584. removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
  585. if (isClear || (managedListener.item === item &amp;&amp; managedListener.ename === ename &amp;&amp; (!fn || managedListener.fn === fn) &amp;&amp; (!scope || managedListener.scope === scope))) {
  586. managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope);
  587. if (!isClear) {
  588. Ext.Array.remove(this.managedListeners, managedListener);
  589. }
  590. }
  591. },
  592. //&lt;debug&gt;
  593. purgeManagedListeners : function() {
  594. if (Ext.global.console) {
  595. Ext.global.console.warn('Observable: purgeManagedListeners has been deprecated. Please use clearManagedListeners.');
  596. }
  597. return this.clearManagedListeners.apply(this, arguments);
  598. },
  599. //&lt;/debug&gt;
  600. <span id='Ext-util-Observable-method-addEvents'> /**
  601. </span> * Adds the specified events to the list of events which this Observable may fire.
  602. *
  603. * @param {Object/String...} eventNames Either an object with event names as properties with
  604. * a value of `true`. For example:
  605. *
  606. * this.addEvents({
  607. * storeloaded: true,
  608. * storecleared: true
  609. * });
  610. *
  611. * Or any number of event names as separate parameters. For example:
  612. *
  613. * this.addEvents('storeloaded', 'storecleared');
  614. *
  615. */
  616. addEvents: function(o) {
  617. var me = this,
  618. events = me.events || (me.events = {}),
  619. arg, args, i;
  620. if (typeof o == 'string') {
  621. for (args = arguments, i = args.length; i--; ) {
  622. arg = args[i];
  623. if (!events[arg]) {
  624. events[arg] = true;
  625. }
  626. }
  627. } else {
  628. Ext.applyIf(me.events, o);
  629. }
  630. },
  631. <span id='Ext-util-Observable-method-hasListener'> /**
  632. </span> * Checks to see if this object has any listeners for a specified event, or whether the event bubbles. The answer
  633. * indicates whether the event needs firing or not.
  634. *
  635. * @param {String} eventName The name of the event to check for
  636. * @return {Boolean} `true` if the event is being listened for or bubbles, else `false`
  637. */
  638. hasListener: function(ename) {
  639. return !!this.hasListeners[ename.toLowerCase()];
  640. },
  641. <span id='Ext-util-Observable-method-suspendEvents'> /**
  642. </span> * Suspends the firing of all events. (see {@link #resumeEvents})
  643. *
  644. * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
  645. * after the {@link #resumeEvents} call instead of discarding all suspended events.
  646. */
  647. suspendEvents: function(queueSuspended) {
  648. this.eventsSuspended += 1;
  649. if (queueSuspended &amp;&amp; !this.eventQueue) {
  650. this.eventQueue = [];
  651. }
  652. },
  653. <span id='Ext-util-Observable-method-resumeEvents'> /**
  654. </span> * Resumes firing events (see {@link #suspendEvents}).
  655. *
  656. * If events were suspended using the `queueSuspended` parameter, then all events fired
  657. * during event suspension will be sent to any listeners now.
  658. */
  659. resumeEvents: function() {
  660. var me = this,
  661. queued = me.eventQueue,
  662. qLen, q;
  663. if (me.eventsSuspended &amp;&amp; ! --me.eventsSuspended) {
  664. delete me.eventQueue;
  665. if (queued) {
  666. qLen = queued.length;
  667. for (q = 0; q &lt; qLen; q++) {
  668. me.continueFireEvent.apply(me, queued[q]);
  669. }
  670. }
  671. }
  672. },
  673. <span id='Ext-util-Observable-method-relayEvents'> /**
  674. </span> * Relays selected events from the specified Observable as if the events were fired by `this`.
  675. *
  676. * For example if you are extending Grid, you might decide to forward some events from store.
  677. * So you can do this inside your initComponent:
  678. *
  679. * this.relayEvents(this.getStore(), ['load']);
  680. *
  681. * The grid instance will then have an observable 'load' event which will be passed the
  682. * parameters of the store's load event and any function fired with the grid's load event
  683. * would have access to the grid using the `this` keyword.
  684. *
  685. * @param {Object} origin The Observable whose events this object is to relay.
  686. * @param {String[]} events Array of event names to relay.
  687. * @param {String} [prefix] A common prefix to prepend to the event names. For example:
  688. *
  689. * this.relayEvents(this.getStore(), ['load', 'clear'], 'store');
  690. *
  691. * Now the grid will forward 'load' and 'clear' events of store as 'storeload' and 'storeclear'.
  692. */
  693. relayEvents : function(origin, events, prefix) {
  694. var me = this,
  695. len = events.length,
  696. i = 0,
  697. oldName,
  698. newName;
  699. for (; i &lt; len; i++) {
  700. oldName = events[i];
  701. newName = prefix ? prefix + oldName : oldName;
  702. // Add the relaying function as a ManagedListener so that it is removed when this.clearListeners is called (usually when _this_ is destroyed)
  703. me.mon(origin, oldName, me.createRelayer(newName));
  704. }
  705. },
  706. <span id='Ext-util-Observable-method-createRelayer'> /**
  707. </span> * @private
  708. * Creates an event handling function which refires the event from this object as the passed event name.
  709. * @param newName
  710. * @param {Array} beginEnd (optional) The caller can specify on which indices to slice
  711. * @returns {Function}
  712. */
  713. createRelayer: function(newName, beginEnd){
  714. var me = this;
  715. return function() {
  716. return me.fireEvent.apply(me, [newName].concat(Array.prototype.slice.apply(arguments, beginEnd || [0, -1])));
  717. };
  718. },
  719. <span id='Ext-util-Observable-method-enableBubble'> /**
  720. </span> * Enables events fired by this Observable to bubble up an owner hierarchy by calling `this.getBubbleTarget()` if
  721. * present. There is no implementation in the Observable base class.
  722. *
  723. * This is commonly used by Ext.Components to bubble events to owner Containers.
  724. * See {@link Ext.Component#getBubbleTarget}. The default implementation in Ext.Component returns the
  725. * Component's immediate owner. But if a known target is required, this can be overridden to access the
  726. * required target more quickly.
  727. *
  728. * Example:
  729. *
  730. * Ext.override(Ext.form.field.Base, {
  731. * // Add functionality to Field's initComponent to enable the change event to bubble
  732. * initComponent : Ext.Function.createSequence(Ext.form.field.Base.prototype.initComponent, function() {
  733. * this.enableBubble('change');
  734. * }),
  735. *
  736. * // We know that we want Field's events to bubble directly to the FormPanel.
  737. * getBubbleTarget : function() {
  738. * if (!this.formPanel) {
  739. * this.formPanel = this.findParentByType('form');
  740. * }
  741. * return this.formPanel;
  742. * }
  743. * });
  744. *
  745. * var myForm = new Ext.formPanel({
  746. * title: 'User Details',
  747. * items: [{
  748. * ...
  749. * }],
  750. * listeners: {
  751. * change: function() {
  752. * // Title goes red if form has been modified.
  753. * myForm.header.setStyle('color', 'red');
  754. * }
  755. * }
  756. * });
  757. *
  758. * @param {String/String[]} eventNames The event name to bubble, or an Array of event names.
  759. */
  760. enableBubble: function(eventNames) {
  761. if (eventNames) {
  762. var me = this,
  763. names = (typeof eventNames == 'string') ? arguments : eventNames,
  764. length = names.length,
  765. events = me.events,
  766. ename, event, i;
  767. for (i = 0; i &lt; length; ++i) {
  768. ename = names[i].toLowerCase();
  769. event = events[ename];
  770. if (!event || typeof event == 'boolean') {
  771. events[ename] = event = new Ext.util.Event(me, ename);
  772. }
  773. // Event must fire if it bubbles (We don't know if anyone up the bubble hierarchy has listeners added)
  774. me.hasListeners[ename] = (me.hasListeners[ename]||0) + 1;
  775. event.bubble = true;
  776. }
  777. }
  778. }
  779. }, function() {
  780. var Observable = this,
  781. proto = Observable.prototype,
  782. HasListeners = function () {},
  783. prepareMixin = function (T) {
  784. if (!T.HasListeners) {
  785. var proto = T.prototype;
  786. // Classes that use us as a mixin (best practice) need to be prepared.
  787. Observable.prepareClass(T, this);
  788. // Now that we are mixed in to class T, we need to watch T for derivations
  789. // and prepare them also.
  790. T.onExtended(function (U) {
  791. Observable.prepareClass(U);
  792. });
  793. // Also, if a class uses us as a mixin and that class is then used as
  794. // a mixin, we need to be notified of that as well.
  795. if (proto.onClassMixedIn) {
  796. // play nice with other potential overrides...
  797. Ext.override(T, {
  798. onClassMixedIn: function (U) {
  799. prepareMixin.call(this, U);
  800. this.callParent(arguments);
  801. }
  802. });
  803. } else {
  804. // just us chickens, so add the method...
  805. proto.onClassMixedIn = function (U) {
  806. prepareMixin.call(this, U);
  807. };
  808. }
  809. }
  810. };
  811. HasListeners.prototype = {
  812. //$$: 42 // to make sure we have a proper prototype
  813. };
  814. proto.HasListeners = Observable.HasListeners = HasListeners;
  815. Observable.createAlias({
  816. <span id='Ext-util-Observable-method-on'> /**
  817. </span> * @method
  818. * Shorthand for {@link #addListener}.
  819. * @inheritdoc Ext.util.Observable#addListener
  820. */
  821. on: 'addListener',
  822. <span id='Ext-util-Observable-method-un'> /**
  823. </span> * @method
  824. * Shorthand for {@link #removeListener}.
  825. * @inheritdoc Ext.util.Observable#removeListener
  826. */
  827. un: 'removeListener',
  828. <span id='Ext-util-Observable-method-mon'> /**
  829. </span> * @method
  830. * Shorthand for {@link #addManagedListener}.
  831. * @inheritdoc Ext.util.Observable#addManagedListener
  832. */
  833. mon: 'addManagedListener',
  834. <span id='Ext-util-Observable-method-mun'> /**
  835. </span> * @method
  836. * Shorthand for {@link #removeManagedListener}.
  837. * @inheritdoc Ext.util.Observable#removeManagedListener
  838. */
  839. mun: 'removeManagedListener'
  840. });
  841. //deprecated, will be removed in 5.0
  842. Observable.observeClass = Observable.observe;
  843. // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
  844. // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
  845. // private
  846. function getMethodEvent(method){
  847. var e = (this.methodEvents = this.methodEvents || {})[method],
  848. returnValue,
  849. v,
  850. cancel,
  851. obj = this,
  852. makeCall;
  853. if (!e) {
  854. this.methodEvents[method] = e = {};
  855. e.originalFn = this[method];
  856. e.methodName = method;
  857. e.before = [];
  858. e.after = [];
  859. makeCall = function(fn, scope, args){
  860. if((v = fn.apply(scope || obj, args)) !== undefined){
  861. if (typeof v == 'object') {
  862. if(v.returnValue !== undefined){
  863. returnValue = v.returnValue;
  864. }else{
  865. returnValue = v;
  866. }
  867. cancel = !!v.cancel;
  868. }
  869. else
  870. if (v === false) {
  871. cancel = true;
  872. }
  873. else {
  874. returnValue = v;
  875. }
  876. }
  877. };
  878. this[method] = function(){
  879. var args = Array.prototype.slice.call(arguments, 0),
  880. b, i, len;
  881. returnValue = v = undefined;
  882. cancel = false;
  883. for(i = 0, len = e.before.length; i &lt; len; i++){
  884. b = e.before[i];
  885. makeCall(b.fn, b.scope, args);
  886. if (cancel) {
  887. return returnValue;
  888. }
  889. }
  890. if((v = e.originalFn.apply(obj, args)) !== undefined){
  891. returnValue = v;
  892. }
  893. for(i = 0, len = e.after.length; i &lt; len; i++){
  894. b = e.after[i];
  895. makeCall(b.fn, b.scope, args);
  896. if (cancel) {
  897. return returnValue;
  898. }
  899. }
  900. return returnValue;
  901. };
  902. }
  903. return e;
  904. }
  905. Ext.apply(proto, {
  906. onClassMixedIn: prepareMixin,
  907. // these are considered experimental
  908. // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
  909. // adds an 'interceptor' called before the original method
  910. beforeMethod : function(method, fn, scope){
  911. getMethodEvent.call(this, method).before.push({
  912. fn: fn,
  913. scope: scope
  914. });
  915. },
  916. // adds a 'sequence' called after the original method
  917. afterMethod : function(method, fn, scope){
  918. getMethodEvent.call(this, method).after.push({
  919. fn: fn,
  920. scope: scope
  921. });
  922. },
  923. removeMethodListener: function(method, fn, scope){
  924. var e = this.getMethodEvent(method),
  925. i, len;
  926. for(i = 0, len = e.before.length; i &lt; len; i++){
  927. if(e.before[i].fn == fn &amp;&amp; e.before[i].scope == scope){
  928. Ext.Array.erase(e.before, i, 1);
  929. return;
  930. }
  931. }
  932. for(i = 0, len = e.after.length; i &lt; len; i++){
  933. if(e.after[i].fn == fn &amp;&amp; e.after[i].scope == scope){
  934. Ext.Array.erase(e.after, i, 1);
  935. return;
  936. }
  937. }
  938. },
  939. toggleEventLogging: function(toggle) {
  940. Ext.util.Observable[toggle ? 'capture' : 'releaseCapture'](this, function(en) {
  941. if (Ext.isDefined(Ext.global.console)) {
  942. Ext.global.console.log(en, arguments);
  943. }
  944. });
  945. }
  946. });
  947. });
  948. </pre>
  949. </body>
  950. </html>