/ext-4.1.0_b3/docs/extjs/examples/kitchensink/all-classes.js

https://bitbucket.org/srogerf/javascript · JavaScript · 28581 lines · 24127 code · 835 blank · 3619 comment · 863 complexity · 1a8790657b1626f7a21dd493d975bf3f MD5 · raw file

Large files are truncated click here to view the full file

  1. /*
  2. Copyright(c) 2011 Sencha
  3. */
  4. /**
  5. * Base class that provides a common interface for publishing events. Subclasses are expected to to have a property
  6. * "events" with all the events defined, and, optionally, a property "listeners" with configured listeners defined.
  7. *
  8. * For example:
  9. *
  10. * Ext.define('Employee', {
  11. * mixins: {
  12. * observable: 'Ext.util.Observable'
  13. * },
  14. *
  15. * constructor: function (config) {
  16. * // The Observable constructor copies all of the properties of `config` on
  17. * // to `this` using {@link Ext#apply}. Further, the `listeners` property is
  18. * // processed to add listeners.
  19. * //
  20. * this.mixins.observable.constructor.call(this, config);
  21. *
  22. * this.addEvents(
  23. * 'fired',
  24. * 'quit'
  25. * );
  26. * }
  27. * });
  28. *
  29. * This could then be used like this:
  30. *
  31. * var newEmployee = new Employee({
  32. * name: employeeName,
  33. * listeners: {
  34. * quit: function() {
  35. * // By default, "this" will be the object that fired the event.
  36. * alert(this.name + " has quit!");
  37. * }
  38. * }
  39. * });
  40. */
  41. Ext.define('Ext.util.Observable', {
  42. /* Begin Definitions */
  43. requires: ['Ext.util.Event'],
  44. statics: {
  45. /**
  46. * Removes **all** added captures from the Observable.
  47. *
  48. * @param {Ext.util.Observable} o The Observable to release
  49. * @static
  50. */
  51. releaseCapture: function(o) {
  52. o.fireEvent = this.prototype.fireEvent;
  53. },
  54. /**
  55. * Starts capture on the specified Observable. All events will be passed to the supplied function with the event
  56. * name + standard signature of the event **before** the event is fired. If the supplied function returns false,
  57. * the event will not fire.
  58. *
  59. * @param {Ext.util.Observable} o The Observable to capture events from.
  60. * @param {Function} fn The function to call when an event is fired.
  61. * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to
  62. * the Observable firing the event.
  63. * @static
  64. */
  65. capture: function(o, fn, scope) {
  66. o.fireEvent = Ext.Function.createInterceptor(o.fireEvent, fn, scope);
  67. },
  68. /**
  69. * Sets observability on the passed class constructor.
  70. *
  71. * This makes any event fired on any instance of the passed class also fire a single event through
  72. * the **class** allowing for central handling of events on many instances at once.
  73. *
  74. * Usage:
  75. *
  76. * Ext.util.Observable.observe(Ext.data.Connection);
  77. * Ext.data.Connection.on('beforerequest', function(con, options) {
  78. * console.log('Ajax request made to ' + options.url);
  79. * });
  80. *
  81. * @param {Function} c The class constructor to make observable.
  82. * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
  83. * @static
  84. */
  85. observe: function(cls, listeners) {
  86. if (cls) {
  87. if (!cls.isObservable) {
  88. Ext.applyIf(cls, new this());
  89. this.capture(cls.prototype, cls.fireEvent, cls);
  90. }
  91. if (Ext.isObject(listeners)) {
  92. cls.on(listeners);
  93. }
  94. return cls;
  95. }
  96. }
  97. },
  98. /* End Definitions */
  99. /**
  100. * @cfg {Object} listeners
  101. *
  102. * A config object containing one or more event handlers to be added to this object during initialization. This
  103. * should be a valid listeners config object as specified in the {@link #addListener} example for attaching multiple
  104. * handlers at once.
  105. *
  106. * **DOM events from Ext JS {@link Ext.Component Components}**
  107. *
  108. * While _some_ Ext JS Component classes export selected DOM events (e.g. "click", "mouseover" etc), this is usually
  109. * only done when extra value can be added. For example the {@link Ext.view.View DataView}'s **`{@link
  110. * Ext.view.View#itemclick itemclick}`** event passing the node clicked on. To access DOM events directly from a
  111. * child element of a Component, we need to specify the `element` option to identify the Component property to add a
  112. * DOM listener to:
  113. *
  114. * new Ext.panel.Panel({
  115. * width: 400,
  116. * height: 200,
  117. * dockedItems: [{
  118. * xtype: 'toolbar'
  119. * }],
  120. * listeners: {
  121. * click: {
  122. * element: 'el', //bind to the underlying el property on the panel
  123. * fn: function(){ console.log('click el'); }
  124. * },
  125. * dblclick: {
  126. * element: 'body', //bind to the underlying body property on the panel
  127. * fn: function(){ console.log('dblclick body'); }
  128. * }
  129. * }
  130. * });
  131. */
  132. /**
  133. * @property {Boolean} isObservable
  134. * `true` in this class to identify an objact as an instantiated Observable, or subclass thereof.
  135. */
  136. isObservable: true,
  137. constructor: function(config) {
  138. var me = this;
  139. Ext.apply(me, config);
  140. // Hash of event "hasListeners" flags.
  141. // For repeated events in time-critical code, the firing code should use
  142. // if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) { //code... }
  143. // Bubbling the events counts as one listener.
  144. // The subclass may have already initialized it.
  145. me.hasListeners = me.hasListeners || {};
  146. me.events = me.events || {};
  147. if (me.listeners) {
  148. me.on(me.listeners);
  149. me.listeners = null; //Set as an instance property to pre-empt the prototype in case any are set there.
  150. }
  151. if (me.bubbleEvents) {
  152. me.enableBubble(me.bubbleEvents);
  153. }
  154. },
  155. // @private
  156. eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|vertical|horizontal|freezeEvent)$/,
  157. /**
  158. * Adds listeners to any Observable object (or Ext.Element) which are automatically removed when this Component is
  159. * destroyed.
  160. *
  161. * @param {Ext.util.Observable/Ext.Element} item The item to which to add a listener/listeners.
  162. * @param {Object/String} ename The event name, or an object containing event name properties.
  163. * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function.
  164. * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference)
  165. * in which the handler function is executed.
  166. * @param {Object} opt (optional) If the `ename` parameter was an event name, this is the
  167. * {@link Ext.util.Observable#addListener addListener} options.
  168. */
  169. addManagedListener : function(item, ename, fn, scope, options) {
  170. var me = this,
  171. managedListeners = me.managedListeners = me.managedListeners || [],
  172. config;
  173. if (typeof ename !== 'string') {
  174. options = ename;
  175. for (ename in options) {
  176. if (options.hasOwnProperty(ename)) {
  177. config = options[ename];
  178. if (!me.eventOptionsRe.test(ename)) {
  179. me.addManagedListener(item, ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
  180. }
  181. }
  182. }
  183. }
  184. else {
  185. managedListeners.push({
  186. item: item,
  187. ename: ename,
  188. fn: fn,
  189. scope: scope,
  190. options: options
  191. });
  192. item.on(ename, fn, scope, options);
  193. }
  194. },
  195. /**
  196. * Removes listeners that were added by the {@link #mon} method.
  197. *
  198. * @param {Ext.util.Observable/Ext.Element} item The item from which to remove a listener/listeners.
  199. * @param {Object/String} ename The event name, or an object containing event name properties.
  200. * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function.
  201. * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference)
  202. * in which the handler function is executed.
  203. */
  204. removeManagedListener : function(item, ename, fn, scope) {
  205. var me = this,
  206. options,
  207. config,
  208. managedListeners,
  209. length,
  210. i;
  211. if (typeof ename !== 'string') {
  212. options = ename;
  213. for (ename in options) {
  214. if (options.hasOwnProperty(ename)) {
  215. config = options[ename];
  216. if (!me.eventOptionsRe.test(ename)) {
  217. me.removeManagedListener(item, ename, config.fn || config, config.scope || options.scope);
  218. }
  219. }
  220. }
  221. }
  222. managedListeners = me.managedListeners ? me.managedListeners.slice() : [];
  223. for (i = 0, length = managedListeners.length; i < length; i++) {
  224. me.removeManagedListenerItem(false, managedListeners[i], item, ename, fn, scope);
  225. }
  226. },
  227. /**
  228. * Fires the specified event with the passed parameters (minus the event name, plus the `options` object passed
  229. * to {@link #addListener}).
  230. *
  231. * An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget}) by
  232. * calling {@link #enableBubble}.
  233. *
  234. * @param {String} eventName The name of the event to fire.
  235. * @param {Object...} args Variable number of parameters are passed to handlers.
  236. * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
  237. */
  238. fireEvent: function(eventName) {
  239. eventName = eventName.toLowerCase();
  240. var me = this,
  241. events = me.events,
  242. event = events && events[eventName];
  243. // Only continue firing the event if there are listeners to be informed.
  244. // Bubbled events will always have a listener count, so will be fired.
  245. if (event && me.hasListeners[eventName]) {
  246. return me.continueFireEvent(eventName, Ext.Array.slice(arguments, 1), event.bubble);
  247. }
  248. },
  249. /**
  250. * Continue to fire event.
  251. * @private
  252. *
  253. * @param {String} eventName
  254. * @param {Array} args
  255. * @param {Boolean} bubbles
  256. */
  257. continueFireEvent: function(eventName, args, bubbles) {
  258. var target = this,
  259. queue, event,
  260. ret = true;
  261. do {
  262. if (target.eventsSuspended === true) {
  263. if ((queue = target.eventQueue)) {
  264. queue.push([eventName, args, bubbles]);
  265. }
  266. return ret;
  267. } else {
  268. event = target.events[eventName];
  269. // Continue bubbling if event exists and it is `true` or the handler didn't returns false and it
  270. // configure to bubble.
  271. if (event && event != true) {
  272. if ((ret = event.fire.apply(event, args)) === false) {
  273. break;
  274. }
  275. }
  276. }
  277. } while (bubbles && (target = target.getBubbleParent()));
  278. return ret;
  279. },
  280. /**
  281. * Gets the bubbling parent for an Observable
  282. * @private
  283. * @return {Ext.util.Observable} The bubble parent. null is returned if no bubble target exists
  284. */
  285. getBubbleParent: function(){
  286. var me = this, parent = me.getBubbleTarget && me.getBubbleTarget();
  287. if (parent && parent.isObservable) {
  288. return parent;
  289. }
  290. return null;
  291. },
  292. /**
  293. * Appends an event handler to this object. For example:
  294. *
  295. * myGridPanel.on("mouseover", this.onMouseOver, this);
  296. *
  297. * The method also allows for a single argument to be passed which is a config object
  298. * containing properties which specify multiple events. For example:
  299. *
  300. * myGridPanel.on({
  301. * cellClick: this.onCellClick,
  302. * mouseover: this.onMouseOver,
  303. * mouseout: this.onMouseOut,
  304. * scope: this // Important. Ensure "this" is correct during handler execution
  305. * });
  306. *
  307. * One can also specify options for each event handler separately:
  308. *
  309. * myGridPanel.on({
  310. * cellClick: {fn: this.onCellClick, scope: this, single: true},
  311. * mouseover: {fn: panel.onMouseOver, scope: panel}
  312. * });
  313. *
  314. * *Names* of methods in a specified scope may also be used. Note that
  315. * `scope` MUST be specified to use this option:
  316. *
  317. * myGridPanel.on({
  318. * cellClick: {fn: 'onCellClick', scope: this, single: true},
  319. * mouseover: {fn: 'onMouseOver', scope: panel}
  320. * });
  321. *
  322. * @param {String/Object} eventName The name of the event to listen for.
  323. * May also be an object who's property names are event names.
  324. *
  325. * @param {Function} [fn] The method the event invokes, or *if `scope` is specified, the *name* of the method within
  326. * the specified `scope`. Will be called with arguments
  327. * given to {@link #fireEvent} plus the `options` parameter described below.
  328. *
  329. * @param {Object} [scope] The scope (`this` reference) in which the handler function is
  330. * executed. **If omitted, defaults to the object which fired the event.**
  331. *
  332. * @param {Object} [options] An object containing handler configuration.
  333. *
  334. * **Note:** Unlike in ExtJS 3.x, the options object will also be passed as the last
  335. * argument to every event handler.
  336. *
  337. * This object may contain any of the following properties:
  338. *
  339. * @param {Object} options.scope
  340. * The scope (`this` reference) in which the handler function is executed. **If omitted,
  341. * defaults to the object which fired the event.**
  342. *
  343. * @param {Number} options.delay
  344. * The number of milliseconds to delay the invocation of the handler after the event fires.
  345. *
  346. * @param {Boolean} options.single
  347. * True to add a handler to handle just the next firing of the event, and then remove itself.
  348. *
  349. * @param {Number} options.buffer
  350. * Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
  351. * by the specified number of milliseconds. If the event fires again within that time,
  352. * the original handler is _not_ invoked, but the new handler is scheduled in its place.
  353. *
  354. * @param {Ext.util.Observable} options.target
  355. * Only call the handler if the event was fired on the target Observable, _not_ if the event
  356. * was bubbled up from a child Observable.
  357. *
  358. * @param {String} options.element
  359. * **This option is only valid for listeners bound to {@link Ext.Component Components}.**
  360. * The name of a Component property which references an element to add a listener to.
  361. *
  362. * This option is useful during Component construction to add DOM event listeners to elements of
  363. * {@link Ext.Component Components} which will exist only after the Component is rendered.
  364. * For example, to add a click listener to a Panel's body:
  365. *
  366. * new Ext.panel.Panel({
  367. * title: 'The title',
  368. * listeners: {
  369. * click: this.handlePanelClick,
  370. * element: 'body'
  371. * }
  372. * });
  373. *
  374. * **Combining Options**
  375. *
  376. * Using the options argument, it is possible to combine different types of listeners:
  377. *
  378. * A delayed, one-time listener.
  379. *
  380. * myPanel.on('hide', this.handleClick, this, {
  381. * single: true,
  382. * delay: 100
  383. * });
  384. *
  385. */
  386. addListener: function(ename, fn, scope, options) {
  387. var me = this,
  388. config,
  389. event;
  390. if (typeof ename !== 'string') {
  391. options = ename;
  392. for (ename in options) {
  393. if (options.hasOwnProperty(ename)) {
  394. config = options[ename];
  395. if (!me.eventOptionsRe.test(ename)) {
  396. me.addListener(ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
  397. }
  398. }
  399. }
  400. }
  401. else {
  402. ename = ename.toLowerCase();
  403. me.events[ename] = me.events[ename] || true;
  404. event = me.events[ename] || true;
  405. if (Ext.isBoolean(event)) {
  406. me.events[ename] = event = new Ext.util.Event(me, ename);
  407. }
  408. // Allow listeners: { click: 'onClick', scope: myObject }
  409. if (typeof fn === 'string') {
  410. fn = scope[fn] || me.fn;
  411. }
  412. event.addListener(fn, scope, Ext.isObject(options) ? options : {});
  413. // Maintain count of listeners for each event name.
  414. // For repeated events in time-critical code, the firing code should use
  415. // if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) { //code... }
  416. me.hasListeners[ename] = (me.hasListeners[ename]||0) + 1;
  417. }
  418. },
  419. /**
  420. * Removes an event handler.
  421. *
  422. * @param {String} eventName The type of event the handler was associated with.
  423. * @param {Function} fn The handler to remove. **This must be a reference to the function passed into the
  424. * {@link #addListener} call.**
  425. * @param {Object} scope (optional) The scope originally specified for the handler. It must be the same as the
  426. * scope argument specified in the original call to {@link #addListener} or the listener will not be removed.
  427. */
  428. removeListener: function(ename, fn, scope) {
  429. var me = this,
  430. config,
  431. event,
  432. options;
  433. if (typeof ename !== 'string') {
  434. options = ename;
  435. for (ename in options) {
  436. if (options.hasOwnProperty(ename)) {
  437. config = options[ename];
  438. if (!me.eventOptionsRe.test(ename)) {
  439. me.removeListener(ename, config.fn || config, config.scope || options.scope);
  440. }
  441. }
  442. }
  443. } else {
  444. ename = ename.toLowerCase();
  445. event = me.events[ename];
  446. if (event && event.isEvent) {
  447. event.removeListener(fn, scope);
  448. // Maintain count of listeners for each event name.
  449. // For repeated events in time-critical code, the firing code should use
  450. // if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) { //code... }
  451. me.hasListeners[ename]--;
  452. }
  453. }
  454. },
  455. /**
  456. * Removes all listeners for this object including the managed listeners
  457. */
  458. clearListeners: function() {
  459. var events = this.events,
  460. event,
  461. key;
  462. for (key in events) {
  463. if (events.hasOwnProperty(key)) {
  464. event = events[key];
  465. if (event.isEvent) {
  466. event.clearListeners();
  467. }
  468. }
  469. }
  470. this.clearManagedListeners();
  471. },
  472. purgeListeners : function() {
  473. if (Ext.global.console) {
  474. Ext.global.console.warn('Observable: purgeListeners has been deprecated. Please use clearListeners.');
  475. }
  476. return this.clearListeners.apply(this, arguments);
  477. },
  478. /**
  479. * Removes all managed listeners for this object.
  480. */
  481. clearManagedListeners : function() {
  482. var managedListeners = this.managedListeners || [],
  483. i = 0,
  484. len = managedListeners.length;
  485. for (; i < len; i++) {
  486. this.removeManagedListenerItem(true, managedListeners[i]);
  487. }
  488. this.managedListeners = [];
  489. },
  490. /**
  491. * Remove a single managed listener item
  492. * @private
  493. * @param {Boolean} isClear True if this is being called during a clear
  494. * @param {Object} managedListener The managed listener item
  495. * See removeManagedListener for other args
  496. */
  497. removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
  498. if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
  499. managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope);
  500. if (!isClear) {
  501. Ext.Array.remove(this.managedListeners, managedListener);
  502. }
  503. }
  504. },
  505. purgeManagedListeners : function() {
  506. if (Ext.global.console) {
  507. Ext.global.console.warn('Observable: purgeManagedListeners has been deprecated. Please use clearManagedListeners.');
  508. }
  509. return this.clearManagedListeners.apply(this, arguments);
  510. },
  511. /**
  512. * Adds the specified events to the list of events which this Observable may fire.
  513. *
  514. * @param {Object/String...} eventNames Either an object with event names as properties with
  515. * a value of `true`. For example:
  516. *
  517. * this.addEvents({
  518. * storeloaded: true,
  519. * storecleared: true
  520. * });
  521. *
  522. * Or any number of event names as separate parameters. For example:
  523. *
  524. * this.addEvents('storeloaded', 'storecleared');
  525. *
  526. */
  527. addEvents: function(o) {
  528. var me = this,
  529. events = me.events || (me.events = {}),
  530. arg, args, i;
  531. if (typeof o == 'string') {
  532. for (args = arguments, i = args.length; i--; ) {
  533. arg = args[i];
  534. if (!events[arg]) {
  535. events[arg] = true;
  536. }
  537. }
  538. } else {
  539. Ext.applyIf(me.events, o);
  540. }
  541. },
  542. /**
  543. * Checks to see if this object has any listeners for a specified event, or whether the event bubbles. The answer
  544. * indicates whether the event needs firing or not.
  545. *
  546. * @param {String} eventName The name of the event to check for
  547. * @return {Boolean} `true` if the event is being listened for or bubbles, else `false`
  548. */
  549. hasListener: function(ename) {
  550. return !!this.hasListeners[ename.toLowerCase()];
  551. },
  552. /**
  553. * Suspends the firing of all events. (see {@link #resumeEvents})
  554. *
  555. * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
  556. * after the {@link #resumeEvents} call instead of discarding all suspended events.
  557. */
  558. suspendEvents: function(queueSuspended) {
  559. this.eventsSuspended = true;
  560. if (queueSuspended && !this.eventQueue) {
  561. this.eventQueue = [];
  562. }
  563. },
  564. /**
  565. * Resumes firing events (see {@link #suspendEvents}).
  566. *
  567. * If events were suspended using the `queueSuspended` parameter, then all events fired
  568. * during event suspension will be sent to any listeners now.
  569. */
  570. resumeEvents: function() {
  571. var me = this,
  572. queued = me.eventQueue,
  573. qLen, q;
  574. me.eventsSuspended = false;
  575. delete me.eventQueue;
  576. if (queued) {
  577. qLen = queued.length;
  578. for (q = 0; q < qLen; q++) {
  579. me.continueFireEvent.apply(me, queued[q]);
  580. }
  581. }
  582. },
  583. /**
  584. * Relays selected events from the specified Observable as if the events were fired by `this`.
  585. *
  586. * For example if you are extending Grid, you might decide to forward some events from store.
  587. * So you can do this inside your initComponent:
  588. *
  589. * this.relayEvents(this.getStore(), ['load']);
  590. *
  591. * The grid instance will then have an observable 'load' event which will be passed the
  592. * parameters of the store's load event and any function fired with the grid's load event
  593. * would have access to the grid using the `this` keyword.
  594. *
  595. * @param {Object} origin The Observable whose events this object is to relay.
  596. * @param {String[]} events Array of event names to relay.
  597. * @param {String} [prefix] A common prefix to attach to the event names. For example:
  598. *
  599. * this.relayEvents(this.getStore(), ['load', 'clear'], 'store');
  600. *
  601. * Now the grid will forward 'load' and 'clear' events of store as 'storeload' and 'storeclear'.
  602. */
  603. relayEvents : function(origin, events, prefix) {
  604. prefix = prefix || '';
  605. var me = this,
  606. len = events.length,
  607. i = 0,
  608. oldName,
  609. newName;
  610. for (; i < len; i++) {
  611. oldName = events[i];
  612. newName = prefix + oldName;
  613. me.events[newName] = me.events[newName] || true;
  614. origin.on(oldName, me.createRelayer(newName));
  615. }
  616. },
  617. /**
  618. * @private
  619. * Creates an event handling function which refires the event from this object as the passed event name.
  620. * @param newName
  621. * @param {Array} beginEnd (optional) The caller can specify on which indices to slice
  622. * @returns {Function}
  623. */
  624. createRelayer: function(newName, beginEnd){
  625. var me = this;
  626. return function(){
  627. return me.fireEvent.apply(me, [newName].concat(Array.prototype.slice.apply(arguments, beginEnd || [0, -1])));
  628. };
  629. },
  630. /**
  631. * Enables events fired by this Observable to bubble up an owner hierarchy by calling `this.getBubbleTarget()` if
  632. * present. There is no implementation in the Observable base class.
  633. *
  634. * This is commonly used by Ext.Components to bubble events to owner Containers.
  635. * See {@link Ext.Component#getBubbleTarget}. The default implementation in Ext.Component returns the
  636. * Component's immediate owner. But if a known target is required, this can be overridden to access the
  637. * required target more quickly.
  638. *
  639. * Example:
  640. *
  641. * Ext.override(Ext.form.field.Base, {
  642. * // Add functionality to Field's initComponent to enable the change event to bubble
  643. * initComponent : Ext.Function.createSequence(Ext.form.field.Base.prototype.initComponent, function() {
  644. * this.enableBubble('change');
  645. * }),
  646. *
  647. * // We know that we want Field's events to bubble directly to the FormPanel.
  648. * getBubbleTarget : function() {
  649. * if (!this.formPanel) {
  650. * this.formPanel = this.findParentByType('form');
  651. * }
  652. * return this.formPanel;
  653. * }
  654. * });
  655. *
  656. * var myForm = new Ext.formPanel({
  657. * title: 'User Details',
  658. * items: [{
  659. * ...
  660. * }],
  661. * listeners: {
  662. * change: function() {
  663. * // Title goes red if form has been modified.
  664. * myForm.header.setStyle('color', 'red');
  665. * }
  666. * }
  667. * });
  668. *
  669. * @param {String/String[]} eventNames The event name to bubble, or an Array of event names.
  670. */
  671. enableBubble: function(eventNames) {
  672. if (eventNames) {
  673. var me = this,
  674. names = (typeof eventNames == 'string') ? arguments : eventNames,
  675. length = names.length,
  676. events = me.events,
  677. ename, event, i;
  678. for (i = 0; i < length; ++i) {
  679. ename = names[i].toLowerCase();
  680. event = events[ename];
  681. if (!event || typeof event == 'boolean') {
  682. events[ename] = event = new Ext.util.Event(me, ename);
  683. }
  684. // Event must fire if it bubbles (We don't know if anyone up the bubble hierarchy has listeners added)
  685. me.hasListeners[ename] = (me.hasListeners[ename]||0) + 1;
  686. event.bubble = true;
  687. }
  688. }
  689. }
  690. }, function() {
  691. this.createAlias({
  692. /**
  693. * @method
  694. * Shorthand for {@link #addListener}.
  695. * @inheritdoc Ext.util.Observable#addListener
  696. */
  697. on: 'addListener',
  698. /**
  699. * @method
  700. * Shorthand for {@link #removeListener}.
  701. * @inheritdoc Ext.util.Observable#removeListener
  702. */
  703. un: 'removeListener',
  704. /**
  705. * @method
  706. * Shorthand for {@link #addManagedListener}.
  707. * @inheritdoc Ext.util.Observable#addManagedListener
  708. */
  709. mon: 'addManagedListener',
  710. /**
  711. * @method
  712. * Shorthand for {@link #removeManagedListener}.
  713. * @inheritdoc Ext.util.Observable#removeManagedListener
  714. */
  715. mun: 'removeManagedListener'
  716. });
  717. //deprecated, will be removed in 5.0
  718. this.observeClass = this.observe;
  719. Ext.apply(Ext.util.Observable.prototype, function(){
  720. // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
  721. // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
  722. // private
  723. function getMethodEvent(method){
  724. var e = (this.methodEvents = this.methodEvents || {})[method],
  725. returnValue,
  726. v,
  727. cancel,
  728. obj = this;
  729. if (!e) {
  730. this.methodEvents[method] = e = {};
  731. e.originalFn = this[method];
  732. e.methodName = method;
  733. e.before = [];
  734. e.after = [];
  735. var makeCall = function(fn, scope, args){
  736. if((v = fn.apply(scope || obj, args)) !== undefined){
  737. if (typeof v == 'object') {
  738. if(v.returnValue !== undefined){
  739. returnValue = v.returnValue;
  740. }else{
  741. returnValue = v;
  742. }
  743. cancel = !!v.cancel;
  744. }
  745. else
  746. if (v === false) {
  747. cancel = true;
  748. }
  749. else {
  750. returnValue = v;
  751. }
  752. }
  753. };
  754. this[method] = function(){
  755. var args = Array.prototype.slice.call(arguments, 0),
  756. b, i, len;
  757. returnValue = v = undefined;
  758. cancel = false;
  759. for(i = 0, len = e.before.length; i < len; i++){
  760. b = e.before[i];
  761. makeCall(b.fn, b.scope, args);
  762. if (cancel) {
  763. return returnValue;
  764. }
  765. }
  766. if((v = e.originalFn.apply(obj, args)) !== undefined){
  767. returnValue = v;
  768. }
  769. for(i = 0, len = e.after.length; i < len; i++){
  770. b = e.after[i];
  771. makeCall(b.fn, b.scope, args);
  772. if (cancel) {
  773. return returnValue;
  774. }
  775. }
  776. return returnValue;
  777. };
  778. }
  779. return e;
  780. }
  781. return {
  782. // these are considered experimental
  783. // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
  784. // adds an 'interceptor' called before the original method
  785. beforeMethod : function(method, fn, scope){
  786. getMethodEvent.call(this, method).before.push({
  787. fn: fn,
  788. scope: scope
  789. });
  790. },
  791. // adds a 'sequence' called after the original method
  792. afterMethod : function(method, fn, scope){
  793. getMethodEvent.call(this, method).after.push({
  794. fn: fn,
  795. scope: scope
  796. });
  797. },
  798. removeMethodListener: function(method, fn, scope){
  799. var e = this.getMethodEvent(method),
  800. i, len;
  801. for(i = 0, len = e.before.length; i < len; i++){
  802. if(e.before[i].fn == fn && e.before[i].scope == scope){
  803. Ext.Array.erase(e.before, i, 1);
  804. return;
  805. }
  806. }
  807. for(i = 0, len = e.after.length; i < len; i++){
  808. if(e.after[i].fn == fn && e.after[i].scope == scope){
  809. Ext.Array.erase(e.after, i, 1);
  810. return;
  811. }
  812. }
  813. },
  814. toggleEventLogging: function(toggle) {
  815. Ext.util.Observable[toggle ? 'capture' : 'releaseCapture'](this, function(en) {
  816. if (Ext.isDefined(Ext.global.console)) {
  817. Ext.global.console.log(en, arguments);
  818. }
  819. });
  820. }
  821. };
  822. }());
  823. });
  824. /**
  825. * @author Ed Spencer
  826. *
  827. * Associations enable you to express relationships between different {@link Ext.data.Model Models}. Let's say we're
  828. * writing an ecommerce system where Users can make Orders - there's a relationship between these Models that we can
  829. * express like this:
  830. *
  831. * Ext.define('User', {
  832. * extend: 'Ext.data.Model',
  833. * fields: ['id', 'name', 'email'],
  834. *
  835. * hasMany: {model: 'Order', name: 'orders'}
  836. * });
  837. *
  838. * Ext.define('Order', {
  839. * extend: 'Ext.data.Model',
  840. * fields: ['id', 'user_id', 'status', 'price'],
  841. *
  842. * belongsTo: 'User'
  843. * });
  844. *
  845. * We've set up two models - User and Order - and told them about each other. You can set up as many associations on
  846. * each Model as you need using the two default types - {@link Ext.data.HasManyAssociation hasMany} and {@link
  847. * Ext.data.BelongsToAssociation belongsTo}. There's much more detail on the usage of each of those inside their
  848. * documentation pages. If you're not familiar with Models already, {@link Ext.data.Model there is plenty on those too}.
  849. *
  850. * **Further Reading**
  851. *
  852. * - {@link Ext.data.association.HasMany hasMany associations}
  853. * - {@link Ext.data.association.BelongsTo belongsTo associations}
  854. * - {@link Ext.data.association.HasOne hasOne associations}
  855. * - {@link Ext.data.Model using Models}
  856. *
  857. * # Self association models
  858. *
  859. * We can also have models that create parent/child associations between the same type. Below is an example, where
  860. * groups can be nested inside other groups:
  861. *
  862. * // Server Data
  863. * {
  864. * "groups": {
  865. * "id": 10,
  866. * "parent_id": 100,
  867. * "name": "Main Group",
  868. * "parent_group": {
  869. * "id": 100,
  870. * "parent_id": null,
  871. * "name": "Parent Group"
  872. * },
  873. * "child_groups": [{
  874. * "id": 2,
  875. * "parent_id": 10,
  876. * "name": "Child Group 1"
  877. * },{
  878. * "id": 3,
  879. * "parent_id": 10,
  880. * "name": "Child Group 2"
  881. * },{
  882. * "id": 4,
  883. * "parent_id": 10,
  884. * "name": "Child Group 3"
  885. * }]
  886. * }
  887. * }
  888. *
  889. * // Client code
  890. * Ext.define('Group', {
  891. * extend: 'Ext.data.Model',
  892. * fields: ['id', 'parent_id', 'name'],
  893. * proxy: {
  894. * type: 'ajax',
  895. * url: 'data.json',
  896. * reader: {
  897. * type: 'json',
  898. * root: 'groups'
  899. * }
  900. * },
  901. * associations: [{
  902. * type: 'hasMany',
  903. * model: 'Group',
  904. * primaryKey: 'id',
  905. * foreignKey: 'parent_id',
  906. * autoLoad: true,
  907. * associationKey: 'child_groups' // read child data from child_groups
  908. * }, {
  909. * type: 'belongsTo',
  910. * model: 'Group',
  911. * primaryKey: 'id',
  912. * foreignKey: 'parent_id',
  913. * associationKey: 'parent_group' // read parent data from parent_group
  914. * }]
  915. * });
  916. *
  917. * Ext.onReady(function(){
  918. *
  919. * Group.load(10, {
  920. * success: function(group){
  921. * console.log(group.getGroup().get('name'));
  922. *
  923. * group.groups().each(function(rec){
  924. * console.log(rec.get('name'));
  925. * });
  926. * }
  927. * });
  928. *
  929. * });
  930. *
  931. */
  932. Ext.define('Ext.data.association.Association', {
  933. alternateClassName: 'Ext.data.Association',
  934. /**
  935. * @cfg {String} ownerModel (required)
  936. * The string name of the model that owns the association.
  937. */
  938. /**
  939. * @cfg {String} associatedModel (required)
  940. * The string name of the model that is being associated with.
  941. */
  942. /**
  943. * @cfg {String} primaryKey
  944. * The name of the primary key on the associated model. In general this will be the
  945. * {@link Ext.data.Model#idProperty} of the Model.
  946. */
  947. primaryKey: 'id',
  948. /**
  949. * @cfg {Ext.data.reader.Reader} reader
  950. * A special reader to read associated data
  951. */
  952. /**
  953. * @cfg {String} associationKey
  954. * The name of the property in the data to read the association from. Defaults to the name of the associated model.
  955. */
  956. defaultReaderType: 'json',
  957. statics: {
  958. create: function(association){
  959. if (!association.isAssociation) {
  960. if (Ext.isString(association)) {
  961. association = {
  962. type: association
  963. };
  964. }
  965. switch (association.type) {
  966. case 'belongsTo':
  967. return new Ext.data.association.BelongsTo(association);
  968. case 'hasMany':
  969. return new Ext.data.association.HasMany(association);
  970. case 'hasOne':
  971. return new Ext.data.association.HasOne(association);
  972. //TODO Add this back when it's fixed
  973. // case 'polymorphic':
  974. // return Ext.create('Ext.data.PolymorphicAssociation', association);
  975. default:
  976. Ext.Error.raise('Unknown Association type: "' + association.type + '"');
  977. }
  978. }
  979. return association;
  980. }
  981. },
  982. /**
  983. * Creates the Association object.
  984. * @param {Object} [config] Config object.
  985. */
  986. constructor: function(config) {
  987. Ext.apply(this, config);
  988. var types = Ext.ModelManager.types,
  989. ownerName = config.ownerModel,
  990. associatedName = config.associatedModel,
  991. ownerModel = types[ownerName],
  992. associatedModel = types[associatedName],
  993. ownerProto;
  994. if (ownerModel === undefined) {
  995. Ext.Error.raise("The configured ownerModel was not valid (you tried " + ownerName + ")");
  996. }
  997. if (associatedModel === undefined) {
  998. Ext.Error.raise("The configured associatedModel was not valid (you tried " + associatedName + ")");
  999. }
  1000. this.ownerModel = ownerModel;
  1001. this.associatedModel = associatedModel;
  1002. /**
  1003. * @property {String} ownerName
  1004. * The name of the model that 'owns' the association
  1005. */
  1006. /**
  1007. * @property {String} associatedName
  1008. * The name of the model is on the other end of the association (e.g. if a User model hasMany Orders, this is
  1009. * 'Order')
  1010. */
  1011. Ext.applyIf(this, {
  1012. ownerName : ownerName,
  1013. associatedName: associatedName
  1014. });
  1015. },
  1016. /**
  1017. * Get a specialized reader for reading associated data
  1018. * @return {Ext.data.reader.Reader} The reader, null if not supplied
  1019. */
  1020. getReader: function(){
  1021. var me = this,
  1022. reader = me.reader,
  1023. model = me.associatedModel;
  1024. if (reader) {
  1025. if (Ext.isString(reader)) {
  1026. reader = {
  1027. type: reader
  1028. };
  1029. }
  1030. if (reader.isReader) {
  1031. reader.setModel(model);
  1032. } else {
  1033. Ext.applyIf(reader, {
  1034. model: model,
  1035. type : me.defaultReaderType
  1036. });
  1037. }
  1038. me.reader = Ext.createByAlias('reader.' + reader.type, reader);
  1039. }
  1040. return me.reader || null;
  1041. }
  1042. });
  1043. /**
  1044. * @author Don Griffin
  1045. *
  1046. * This class is a base for all id generators. It also provides lookup of id generators by
  1047. * their id.
  1048. *
  1049. * Generally, id generators are used to generate a primary key for new model instances. There
  1050. * are different approaches to solving this problem, so this mechanism has both simple use
  1051. * cases and is open to custom implementations. A {@link Ext.data.Model} requests id generation
  1052. * using the {@link Ext.data.Model#idgen} property.
  1053. *
  1054. * # Identity, Type and Shared IdGenerators
  1055. *
  1056. * It is often desirable to share IdGenerators to ensure uniqueness or common configuration.
  1057. * This is done by giving IdGenerator instances an id property by which they can be looked
  1058. * up using the {@link #get} method. To configure two {@link Ext.data.Model Model} classes
  1059. * to share one {@link Ext.data.SequentialIdGenerator sequential} id generator, you simply
  1060. * assign them the same id:
  1061. *
  1062. * Ext.define('MyApp.data.MyModelA', {
  1063. * extend: 'Ext.data.Model',
  1064. * idgen: {
  1065. * type: 'sequential',
  1066. * id: 'foo'
  1067. * }
  1068. * });
  1069. *
  1070. * Ext.define('MyApp.data.MyModelB', {
  1071. * extend: 'Ext.data.Model',
  1072. * idgen: {
  1073. * type: 'sequential',
  1074. * id: 'foo'
  1075. * }
  1076. * });
  1077. *
  1078. * To make this as simple as possible for generator types that are shared by many (or all)
  1079. * Models, the IdGenerator types (such as 'sequential' or 'uuid') are also reserved as
  1080. * generator id's. This is used by the {@link Ext.data.UuidGenerator} which has an id equal
  1081. * to its type ('uuid'). In other words, the following Models share the same generator:
  1082. *
  1083. * Ext.define('MyApp.data.MyModelX', {
  1084. * extend: 'Ext.data.Model',
  1085. * idgen: 'uuid'
  1086. * });
  1087. *
  1088. * Ext.define('MyApp.data.MyModelY', {
  1089. * extend: 'Ext.data.Model',
  1090. * idgen: 'uuid'
  1091. * });
  1092. *
  1093. * This can be overridden (by specifying the id explicitly), but there is no particularly
  1094. * good reason to do so for this generator type.
  1095. *
  1096. * # Creating Custom Generators
  1097. *
  1098. * An id generator should derive from this class and implement the {@link #generate} method.
  1099. * The constructor will apply config properties on new instances, so a constructor is often
  1100. * not necessary.
  1101. *
  1102. * To register an id generator type, a derived class should provide an `alias` like so:
  1103. *
  1104. * Ext.define('MyApp.data.CustomIdGenerator', {
  1105. * extend: 'Ext.data.IdGenerator',
  1106. * alias: 'idgen.custom',
  1107. *
  1108. * configProp: 42, // some config property w/default value
  1109. *
  1110. * generate: function () {
  1111. * return ... // a new id
  1112. * }
  1113. * });
  1114. *
  1115. * Using the custom id generator is then straightforward:
  1116. *
  1117. * Ext.define('MyApp.data.MyModel', {
  1118. * extend: 'Ext.data.Model',
  1119. * idgen: 'custom'
  1120. * });
  1121. * // or...
  1122. *
  1123. * Ext.define('MyApp.data.MyModel', {
  1124. * extend: 'Ext.data.Model',
  1125. * idgen: {
  1126. * type: 'custom',
  1127. * configProp: value
  1128. * }
  1129. * });
  1130. *
  1131. * It is not recommended to mix shared generators with generator configuration. This leads
  1132. * to unpredictable results unless all configurations match (which is also redundant). In
  1133. * such cases, a custom generator with a default id is the best approach.
  1134. *
  1135. * Ext.define('MyApp.data.CustomIdGenerator', {
  1136. * extend: 'Ext.data.SequentialIdGenerator',
  1137. * alias: 'idgen.custom',
  1138. *
  1139. * id: 'custom', // shared by default
  1140. *
  1141. * prefix: 'ID_',
  1142. * seed: 1000
  1143. * });
  1144. *
  1145. * Ext.define('MyApp.data.MyModelX', {
  1146. * extend: 'Ext.data.Model',
  1147. * idgen: 'custom'
  1148. * });
  1149. *
  1150. * Ext.define('MyApp.data.MyModelY', {
  1151. * extend: 'Ext.data.Model',
  1152. * idgen: 'custom'
  1153. * });
  1154. *
  1155. * // the above models share a generator that produces ID_1000, ID_1001, etc..
  1156. *
  1157. */
  1158. Ext.define('Ext.data.IdGenerator', {
  1159. /**
  1160. * @property {Boolean} isGenerator
  1161. * `true` in this class to identify an objact as an instantiated IdGenerator, or subclass thereof.
  1162. */
  1163. isGenerator: true,
  1164. /**
  1165. * Initializes a new instance.
  1166. * @param {Object} config (optional) Configuration object to be applied to the new instance.
  1167. */
  1168. constructor: function(config) {
  1169. var me = this;
  1170. Ext.apply(me, config);
  1171. if (me.id) {
  1172. Ext.data.IdGenerator.all[me.id] = me;
  1173. }
  1174. },
  1175. /**
  1176. * @cfg {String} id
  1177. * The id by which to register a new instance. This instance can be found using the
  1178. * {@link Ext.data.IdGenerator#get} static method.
  1179. */
  1180. getRecId: function (rec) {
  1181. return rec.modelName + '-' + rec.internalId;
  1182. },
  1183. /**
  1184. * Generates and returns the next id. This method must be implemented by the derived
  1185. * class.
  1186. *
  1187. * @return {String} The next id.
  1188. * @method generate
  1189. * @abstract
  1190. */
  1191. statics: {
  1192. /**
  1193. * @property {Object} all
  1194. * This object is keyed by id to lookup instances.
  1195. * @private
  1196. * @static
  1197. */
  1198. all: {},
  1199. /**
  1200. * Returns the IdGenerator given its config description.
  1201. * @param {String/Object} config If this parameter is an IdGenerator instance, it is
  1202. * simply returned. If this is a string, it is first used as an id for lookup and
  1203. * then, if there is no match, as a type to create a new instance. This parameter
  1204. * can also be a config object that contains a `type` property (among others) that
  1205. * are used to create and configure the instance.
  1206. * @static
  1207. */
  1208. get: function (config) {
  1209. var generator,
  1210. id,
  1211. type;
  1212. if (typeof config == 'string') {
  1213. id = type = config;
  1214. config = null;
  1215. } else if (config.isGenerator) {
  1216. return config;
  1217. } else {
  1218. id = config.id || config.type;
  1219. type = config.type;
  1220. }
  1221. generator = this.all[id];
  1222. if (!generator) {
  1223. generator = Ext.create('idgen.' + type, config);
  1224. }
  1225. return generator;
  1226. }
  1227. }
  1228. });
  1229. /**
  1230. * @author Ed Spencer
  1231. *
  1232. * Represents a single read or write operation performed by a {@link Ext.data.proxy.Proxy Proxy}. Operation objects are
  1233. * used to enable communication between Stores and Proxies. Application developers should rarely need to interact with
  1234. * Operation objects directly.
  1235. *
  1236. * Several Operations can be batched together in a {@link Ext.data.Batch batch}.
  1237. */
  1238. Ext.define('Ext.data.Operation', {
  1239. /**
  1240. * @cfg {Boolean} synchronous
  1241. * True if this Operation is to be executed synchronously. This property is inspected by a
  1242. * {@link Ext.data.Batch Batch} to see if a series of Operations can be executed in parallel or not.
  1243. */
  1244. synchronous: true,
  1245. /**
  1246. * @cfg {String} action
  1247. * The action being performed by this Operation. Should be one of 'create', 'read', 'update' or 'destroy'.
  1248. */
  1249. action: undefined,
  1250. /**
  1251. * @cfg {Ext.util.Filter[]} filters
  1252. * Optional array of filter objects. Only applies to 'read' actions.
  1253. */
  1254. filters: undefined,
  1255. /**
  1256. * @cfg {Ext.util.Sorter[]} sorters
  1257. * Optional array of sorter objects. Only applies to 'read' actions.
  1258. */
  1259. sorters: undefined,
  1260. /**
  1261. * @cfg {Ext.util.Grouper[]} groupers
  1262. * Optional grouping configuration. Only applies to 'read' actions where grouping is desired.
  1263. */
  1264. groupers: undefined,
  1265. /**
  1266. * @cfg {Number} start
  1267. * The start index (offset), used in paging when running a 'read' action.
  1268. */
  1269. start: undefined,
  1270. /**
  1271. * @cfg {Number} limit
  1272. * The number of records to load. Used on 'read' actions when paging is being used.
  1273. */
  1274. limit: undefined,
  1275. /**
  1276. * @cfg {Ext.data.Batch} batch
  1277. * The batch that this Operation is a part of.
  1278. */
  1279. batch: undefined,
  1280. /**
  1281. * @cfg {Function} callback
  1282. * Function to execute when operation completed.
  1283. * @cfg {Ext.data.Model[]} callback.records Array of records.
  1284. * @cfg {Ext.data.Operation} callback.operation The Operation itself.
  1285. * @cfg {Boolean} callback.success True when operation completed successfully.
  1286. */
  1287. callback: undefined,
  1288. /**
  1289. * @cfg {Object} scope
  1290. * Scope for…