PageRenderTime 26291ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 1ms

/ext-4.0.7/pkgs/classes.js

https://bitbucket.org/srogerf/javascript
JavaScript | 14596 lines | 6692 code | 1488 blank | 6416 comment | 1428 complexity | a081e388c7e0f979768b033e444b0e49 MD5 | raw file
  1. /*
  2. This file is part of Ext JS 4
  3. Copyright (c) 2011 Sencha Inc
  4. Contact: http://www.sencha.com/contact
  5. GNU General Public License Usage
  6. This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
  7. If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
  8. */
  9. /**
  10. * Base class that provides a common interface for publishing events. Subclasses are expected to to have a property
  11. * "events" with all the events defined, and, optionally, a property "listeners" with configured listeners defined.
  12. *
  13. * For example:
  14. *
  15. * Ext.define('Employee', {
  16. * extend: 'Ext.util.Observable',
  17. * constructor: function(config){
  18. * this.name = config.name;
  19. * this.addEvents({
  20. * "fired" : true,
  21. * "quit" : true
  22. * });
  23. *
  24. * // Copy configured listeners into *this* object so that the base class's
  25. * // constructor will add them.
  26. * this.listeners = config.listeners;
  27. *
  28. * // Call our superclass constructor to complete construction process.
  29. * this.callParent(arguments)
  30. * }
  31. * });
  32. *
  33. * This could then be used like this:
  34. *
  35. * var newEmployee = new Employee({
  36. * name: employeeName,
  37. * listeners: {
  38. * quit: function() {
  39. * // By default, "this" will be the object that fired the event.
  40. * alert(this.name + " has quit!");
  41. * }
  42. * }
  43. * });
  44. */
  45. Ext.define('Ext.util.Observable', {
  46. /* Begin Definitions */
  47. requires: ['Ext.util.Event'],
  48. statics: {
  49. /**
  50. * Removes **all** added captures from the Observable.
  51. *
  52. * @param {Ext.util.Observable} o The Observable to release
  53. * @static
  54. */
  55. releaseCapture: function(o) {
  56. o.fireEvent = this.prototype.fireEvent;
  57. },
  58. /**
  59. * Starts capture on the specified Observable. All events will be passed to the supplied function with the event
  60. * name + standard signature of the event **before** the event is fired. If the supplied function returns false,
  61. * the event will not fire.
  62. *
  63. * @param {Ext.util.Observable} o The Observable to capture events from.
  64. * @param {Function} fn The function to call when an event is fired.
  65. * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to
  66. * the Observable firing the event.
  67. * @static
  68. */
  69. capture: function(o, fn, scope) {
  70. o.fireEvent = Ext.Function.createInterceptor(o.fireEvent, fn, scope);
  71. },
  72. /**
  73. * Sets observability on the passed class constructor.
  74. *
  75. * This makes any event fired on any instance of the passed class also fire a single event through
  76. * the **class** allowing for central handling of events on many instances at once.
  77. *
  78. * Usage:
  79. *
  80. * Ext.util.Observable.observe(Ext.data.Connection);
  81. * Ext.data.Connection.on('beforerequest', function(con, options) {
  82. * console.log('Ajax request made to ' + options.url);
  83. * });
  84. *
  85. * @param {Function} c The class constructor to make observable.
  86. * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
  87. * @static
  88. */
  89. observe: function(cls, listeners) {
  90. if (cls) {
  91. if (!cls.isObservable) {
  92. Ext.applyIf(cls, new this());
  93. this.capture(cls.prototype, cls.fireEvent, cls);
  94. }
  95. if (Ext.isObject(listeners)) {
  96. cls.on(listeners);
  97. }
  98. return cls;
  99. }
  100. }
  101. },
  102. /* End Definitions */
  103. /**
  104. * @cfg {Object} listeners
  105. *
  106. * A config object containing one or more event handlers to be added to this object during initialization. This
  107. * should be a valid listeners config object as specified in the {@link #addListener} example for attaching multiple
  108. * handlers at once.
  109. *
  110. * **DOM events from Ext JS {@link Ext.Component Components}**
  111. *
  112. * While _some_ Ext JS Component classes export selected DOM events (e.g. "click", "mouseover" etc), this is usually
  113. * only done when extra value can be added. For example the {@link Ext.view.View DataView}'s **`{@link
  114. * Ext.view.View#itemclick itemclick}`** event passing the node clicked on. To access DOM events directly from a
  115. * child element of a Component, we need to specify the `element` option to identify the Component property to add a
  116. * DOM listener to:
  117. *
  118. * new Ext.panel.Panel({
  119. * width: 400,
  120. * height: 200,
  121. * dockedItems: [{
  122. * xtype: 'toolbar'
  123. * }],
  124. * listeners: {
  125. * click: {
  126. * element: 'el', //bind to the underlying el property on the panel
  127. * fn: function(){ console.log('click el'); }
  128. * },
  129. * dblclick: {
  130. * element: 'body', //bind to the underlying body property on the panel
  131. * fn: function(){ console.log('dblclick body'); }
  132. * }
  133. * }
  134. * });
  135. */
  136. // @private
  137. isObservable: true,
  138. constructor: function(config) {
  139. var me = this;
  140. Ext.apply(me, config);
  141. if (me.listeners) {
  142. me.on(me.listeners);
  143. delete me.listeners;
  144. }
  145. me.events = me.events || {};
  146. if (me.bubbleEvents) {
  147. me.enableBubble(me.bubbleEvents);
  148. }
  149. },
  150. // @private
  151. eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|vertical|horizontal|freezeEvent)$/,
  152. /**
  153. * Adds listeners to any Observable object (or Ext.Element) which are automatically removed when this Component is
  154. * destroyed.
  155. *
  156. * @param {Ext.util.Observable/Ext.Element} item The item to which to add a listener/listeners.
  157. * @param {Object/String} ename The event name, or an object containing event name properties.
  158. * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function.
  159. * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference)
  160. * in which the handler function is executed.
  161. * @param {Object} opt (optional) If the `ename` parameter was an event name, this is the
  162. * {@link Ext.util.Observable#addListener addListener} options.
  163. */
  164. addManagedListener : function(item, ename, fn, scope, options) {
  165. var me = this,
  166. managedListeners = me.managedListeners = me.managedListeners || [],
  167. config;
  168. if (typeof ename !== 'string') {
  169. options = ename;
  170. for (ename in options) {
  171. if (options.hasOwnProperty(ename)) {
  172. config = options[ename];
  173. if (!me.eventOptionsRe.test(ename)) {
  174. me.addManagedListener(item, ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
  175. }
  176. }
  177. }
  178. }
  179. else {
  180. managedListeners.push({
  181. item: item,
  182. ename: ename,
  183. fn: fn,
  184. scope: scope,
  185. options: options
  186. });
  187. item.on(ename, fn, scope, options);
  188. }
  189. },
  190. /**
  191. * Removes listeners that were added by the {@link #mon} method.
  192. *
  193. * @param {Ext.util.Observable/Ext.Element} item The item from which to remove a listener/listeners.
  194. * @param {Object/String} ename The event name, or an object containing event name properties.
  195. * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function.
  196. * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference)
  197. * in which the handler function is executed.
  198. */
  199. removeManagedListener : function(item, ename, fn, scope) {
  200. var me = this,
  201. options,
  202. config,
  203. managedListeners,
  204. length,
  205. i;
  206. if (typeof ename !== 'string') {
  207. options = ename;
  208. for (ename in options) {
  209. if (options.hasOwnProperty(ename)) {
  210. config = options[ename];
  211. if (!me.eventOptionsRe.test(ename)) {
  212. me.removeManagedListener(item, ename, config.fn || config, config.scope || options.scope);
  213. }
  214. }
  215. }
  216. }
  217. managedListeners = me.managedListeners ? me.managedListeners.slice() : [];
  218. for (i = 0, length = managedListeners.length; i < length; i++) {
  219. me.removeManagedListenerItem(false, managedListeners[i], item, ename, fn, scope);
  220. }
  221. },
  222. /**
  223. * Fires the specified event with the passed parameters (minus the event name, plus the `options` object passed
  224. * to {@link #addListener}).
  225. *
  226. * An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget}) by
  227. * calling {@link #enableBubble}.
  228. *
  229. * @param {String} eventName The name of the event to fire.
  230. * @param {Object...} args Variable number of parameters are passed to handlers.
  231. * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
  232. */
  233. fireEvent: function(eventName) {
  234. var name = eventName.toLowerCase(),
  235. events = this.events,
  236. event = events && events[name],
  237. bubbles = event && event.bubble;
  238. return this.continueFireEvent(name, Ext.Array.slice(arguments, 1), bubbles);
  239. },
  240. /**
  241. * Continue to fire event.
  242. * @private
  243. *
  244. * @param {String} eventName
  245. * @param {Array} args
  246. * @param {Boolean} bubbles
  247. */
  248. continueFireEvent: function(eventName, args, bubbles) {
  249. var target = this,
  250. queue, event,
  251. ret = true;
  252. do {
  253. if (target.eventsSuspended === true) {
  254. if ((queue = target.eventQueue)) {
  255. queue.push([eventName, args, bubbles]);
  256. }
  257. return ret;
  258. } else {
  259. event = target.events[eventName];
  260. // Continue bubbling if event exists and it is `true` or the handler didn't returns false and it
  261. // configure to bubble.
  262. if (event && event != true) {
  263. if ((ret = event.fire.apply(event, args)) === false) {
  264. break;
  265. }
  266. }
  267. }
  268. } while (bubbles && (target = target.getBubbleParent()));
  269. return ret;
  270. },
  271. /**
  272. * Gets the bubbling parent for an Observable
  273. * @private
  274. * @return {Ext.util.Observable} The bubble parent. null is returned if no bubble target exists
  275. */
  276. getBubbleParent: function(){
  277. var me = this, parent = me.getBubbleTarget && me.getBubbleTarget();
  278. if (parent && parent.isObservable) {
  279. return parent;
  280. }
  281. return null;
  282. },
  283. /**
  284. * Appends an event handler to this object.
  285. *
  286. * @param {String} eventName The name of the event to listen for. May also be an object who's property names are
  287. * event names.
  288. * @param {Function} fn The method the event invokes. Will be called with arguments given to
  289. * {@link #fireEvent} plus the `options` parameter described below.
  290. * @param {Object} [scope] The scope (`this` reference) in which the handler function is executed. **If
  291. * omitted, defaults to the object which fired the event.**
  292. * @param {Object} [options] An object containing handler configuration.
  293. *
  294. * **Note:** Unlike in ExtJS 3.x, the options object will also be passed as the last argument to every event handler.
  295. *
  296. * This object may contain any of the following properties:
  297. *
  298. * - **scope** : Object
  299. *
  300. * The scope (`this` reference) in which the handler function is executed. **If omitted, defaults to the object
  301. * which fired the event.**
  302. *
  303. * - **delay** : Number
  304. *
  305. * The number of milliseconds to delay the invocation of the handler after the event fires.
  306. *
  307. * - **single** : Boolean
  308. *
  309. * True to add a handler to handle just the next firing of the event, and then remove itself.
  310. *
  311. * - **buffer** : Number
  312. *
  313. * Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed by the specified number of
  314. * milliseconds. If the event fires again within that time, the original handler is _not_ invoked, but the new
  315. * handler is scheduled in its place.
  316. *
  317. * - **target** : Observable
  318. *
  319. * Only call the handler if the event was fired on the target Observable, _not_ if the event was bubbled up from a
  320. * child Observable.
  321. *
  322. * - **element** : String
  323. *
  324. * **This option is only valid for listeners bound to {@link Ext.Component Components}.** The name of a Component
  325. * property which references an element to add a listener to.
  326. *
  327. * This option is useful during Component construction to add DOM event listeners to elements of
  328. * {@link Ext.Component Components} which will exist only after the Component is rendered.
  329. * For example, to add a click listener to a Panel's body:
  330. *
  331. * new Ext.panel.Panel({
  332. * title: 'The title',
  333. * listeners: {
  334. * click: this.handlePanelClick,
  335. * element: 'body'
  336. * }
  337. * });
  338. *
  339. * **Combining Options**
  340. *
  341. * Using the options argument, it is possible to combine different types of listeners:
  342. *
  343. * A delayed, one-time listener.
  344. *
  345. * myPanel.on('hide', this.handleClick, this, {
  346. * single: true,
  347. * delay: 100
  348. * });
  349. *
  350. * **Attaching multiple handlers in 1 call**
  351. *
  352. * The method also allows for a single argument to be passed which is a config object containing properties which
  353. * specify multiple events. For example:
  354. *
  355. * myGridPanel.on({
  356. * cellClick: this.onCellClick,
  357. * mouseover: this.onMouseOver,
  358. * mouseout: this.onMouseOut,
  359. * scope: this // Important. Ensure "this" is correct during handler execution
  360. * });
  361. *
  362. * One can also specify options for each event handler separately:
  363. *
  364. * myGridPanel.on({
  365. * cellClick: {fn: this.onCellClick, scope: this, single: true},
  366. * mouseover: {fn: panel.onMouseOver, scope: panel}
  367. * });
  368. *
  369. */
  370. addListener: function(ename, fn, scope, options) {
  371. var me = this,
  372. config,
  373. event;
  374. if (typeof ename !== 'string') {
  375. options = ename;
  376. for (ename in options) {
  377. if (options.hasOwnProperty(ename)) {
  378. config = options[ename];
  379. if (!me.eventOptionsRe.test(ename)) {
  380. me.addListener(ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
  381. }
  382. }
  383. }
  384. }
  385. else {
  386. ename = ename.toLowerCase();
  387. me.events[ename] = me.events[ename] || true;
  388. event = me.events[ename] || true;
  389. if (Ext.isBoolean(event)) {
  390. me.events[ename] = event = new Ext.util.Event(me, ename);
  391. }
  392. event.addListener(fn, scope, Ext.isObject(options) ? options : {});
  393. }
  394. },
  395. /**
  396. * Removes an event handler.
  397. *
  398. * @param {String} eventName The type of event the handler was associated with.
  399. * @param {Function} fn The handler to remove. **This must be a reference to the function passed into the
  400. * {@link #addListener} call.**
  401. * @param {Object} scope (optional) The scope originally specified for the handler. It must be the same as the
  402. * scope argument specified in the original call to {@link #addListener} or the listener will not be removed.
  403. */
  404. removeListener: function(ename, fn, scope) {
  405. var me = this,
  406. config,
  407. event,
  408. options;
  409. if (typeof ename !== 'string') {
  410. options = ename;
  411. for (ename in options) {
  412. if (options.hasOwnProperty(ename)) {
  413. config = options[ename];
  414. if (!me.eventOptionsRe.test(ename)) {
  415. me.removeListener(ename, config.fn || config, config.scope || options.scope);
  416. }
  417. }
  418. }
  419. } else {
  420. ename = ename.toLowerCase();
  421. event = me.events[ename];
  422. if (event && event.isEvent) {
  423. event.removeListener(fn, scope);
  424. }
  425. }
  426. },
  427. /**
  428. * Removes all listeners for this object including the managed listeners
  429. */
  430. clearListeners: function() {
  431. var events = this.events,
  432. event,
  433. key;
  434. for (key in events) {
  435. if (events.hasOwnProperty(key)) {
  436. event = events[key];
  437. if (event.isEvent) {
  438. event.clearListeners();
  439. }
  440. }
  441. }
  442. this.clearManagedListeners();
  443. },
  444. //<debug>
  445. purgeListeners : function() {
  446. if (Ext.global.console) {
  447. Ext.global.console.warn('Observable: purgeListeners has been deprecated. Please use clearListeners.');
  448. }
  449. return this.clearListeners.apply(this, arguments);
  450. },
  451. //</debug>
  452. /**
  453. * Removes all managed listeners for this object.
  454. */
  455. clearManagedListeners : function() {
  456. var managedListeners = this.managedListeners || [],
  457. i = 0,
  458. len = managedListeners.length;
  459. for (; i < len; i++) {
  460. this.removeManagedListenerItem(true, managedListeners[i]);
  461. }
  462. this.managedListeners = [];
  463. },
  464. /**
  465. * Remove a single managed listener item
  466. * @private
  467. * @param {Boolean} isClear True if this is being called during a clear
  468. * @param {Object} managedListener The managed listener item
  469. * See removeManagedListener for other args
  470. */
  471. removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
  472. if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
  473. managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope);
  474. if (!isClear) {
  475. Ext.Array.remove(this.managedListeners, managedListener);
  476. }
  477. }
  478. },
  479. //<debug>
  480. purgeManagedListeners : function() {
  481. if (Ext.global.console) {
  482. Ext.global.console.warn('Observable: purgeManagedListeners has been deprecated. Please use clearManagedListeners.');
  483. }
  484. return this.clearManagedListeners.apply(this, arguments);
  485. },
  486. //</debug>
  487. /**
  488. * Adds the specified events to the list of events which this Observable may fire.
  489. *
  490. * @param {Object/String} o Either an object with event names as properties with a value of `true` or the first
  491. * event name string if multiple event names are being passed as separate parameters. Usage:
  492. *
  493. * this.addEvents({
  494. * storeloaded: true,
  495. * storecleared: true
  496. * });
  497. *
  498. * @param {String...} more (optional) Additional event names if multiple event names are being passed as separate
  499. * parameters. Usage:
  500. *
  501. * this.addEvents('storeloaded', 'storecleared');
  502. *
  503. */
  504. addEvents: function(o) {
  505. var me = this,
  506. args,
  507. len,
  508. i;
  509. me.events = me.events || {};
  510. if (Ext.isString(o)) {
  511. args = arguments;
  512. i = args.length;
  513. while (i--) {
  514. me.events[args[i]] = me.events[args[i]] || true;
  515. }
  516. } else {
  517. Ext.applyIf(me.events, o);
  518. }
  519. },
  520. /**
  521. * Checks to see if this object has any listeners for a specified event
  522. *
  523. * @param {String} eventName The name of the event to check for
  524. * @return {Boolean} True if the event is being listened for, else false
  525. */
  526. hasListener: function(ename) {
  527. var event = this.events[ename.toLowerCase()];
  528. return event && event.isEvent === true && event.listeners.length > 0;
  529. },
  530. /**
  531. * Suspends the firing of all events. (see {@link #resumeEvents})
  532. *
  533. * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
  534. * after the {@link #resumeEvents} call instead of discarding all suspended events.
  535. */
  536. suspendEvents: function(queueSuspended) {
  537. this.eventsSuspended = true;
  538. if (queueSuspended && !this.eventQueue) {
  539. this.eventQueue = [];
  540. }
  541. },
  542. /**
  543. * Resumes firing events (see {@link #suspendEvents}).
  544. *
  545. * If events were suspended using the `queueSuspended` parameter, then all events fired
  546. * during event suspension will be sent to any listeners now.
  547. */
  548. resumeEvents: function() {
  549. var me = this,
  550. queued = me.eventQueue;
  551. me.eventsSuspended = false;
  552. delete me.eventQueue;
  553. if (queued) {
  554. Ext.each(queued, function(e) {
  555. me.continueFireEvent.apply(me, e);
  556. });
  557. }
  558. },
  559. /**
  560. * Relays selected events from the specified Observable as if the events were fired by `this`.
  561. *
  562. * @param {Object} origin The Observable whose events this object is to relay.
  563. * @param {String[]} events Array of event names to relay.
  564. * @param {String} prefix
  565. */
  566. relayEvents : function(origin, events, prefix) {
  567. prefix = prefix || '';
  568. var me = this,
  569. len = events.length,
  570. i = 0,
  571. oldName,
  572. newName;
  573. for (; i < len; i++) {
  574. oldName = events[i].substr(prefix.length);
  575. newName = prefix + oldName;
  576. me.events[newName] = me.events[newName] || true;
  577. origin.on(oldName, me.createRelayer(newName));
  578. }
  579. },
  580. /**
  581. * @private
  582. * Creates an event handling function which refires the event from this object as the passed event name.
  583. * @param newName
  584. * @returns {Function}
  585. */
  586. createRelayer: function(newName){
  587. var me = this;
  588. return function(){
  589. return me.fireEvent.apply(me, [newName].concat(Array.prototype.slice.call(arguments, 0, -1)));
  590. };
  591. },
  592. /**
  593. * Enables events fired by this Observable to bubble up an owner hierarchy by calling `this.getBubbleTarget()` if
  594. * present. There is no implementation in the Observable base class.
  595. *
  596. * This is commonly used by Ext.Components to bubble events to owner Containers.
  597. * See {@link Ext.Component#getBubbleTarget}. The default implementation in Ext.Component returns the
  598. * Component's immediate owner. But if a known target is required, this can be overridden to access the
  599. * required target more quickly.
  600. *
  601. * Example:
  602. *
  603. * Ext.override(Ext.form.field.Base, {
  604. * // Add functionality to Field's initComponent to enable the change event to bubble
  605. * initComponent : Ext.Function.createSequence(Ext.form.field.Base.prototype.initComponent, function() {
  606. * this.enableBubble('change');
  607. * }),
  608. *
  609. * // We know that we want Field's events to bubble directly to the FormPanel.
  610. * getBubbleTarget : function() {
  611. * if (!this.formPanel) {
  612. * this.formPanel = this.findParentByType('form');
  613. * }
  614. * return this.formPanel;
  615. * }
  616. * });
  617. *
  618. * var myForm = new Ext.formPanel({
  619. * title: 'User Details',
  620. * items: [{
  621. * ...
  622. * }],
  623. * listeners: {
  624. * change: function() {
  625. * // Title goes red if form has been modified.
  626. * myForm.header.setStyle('color', 'red');
  627. * }
  628. * }
  629. * });
  630. *
  631. * @param {String/String[]} events The event name to bubble, or an Array of event names.
  632. */
  633. enableBubble: function(events) {
  634. var me = this;
  635. if (!Ext.isEmpty(events)) {
  636. events = Ext.isArray(events) ? events: Ext.Array.toArray(arguments);
  637. Ext.each(events,
  638. function(ename) {
  639. ename = ename.toLowerCase();
  640. var ce = me.events[ename] || true;
  641. if (Ext.isBoolean(ce)) {
  642. ce = new Ext.util.Event(me, ename);
  643. me.events[ename] = ce;
  644. }
  645. ce.bubble = true;
  646. });
  647. }
  648. }
  649. }, function() {
  650. this.createAlias({
  651. /**
  652. * @method
  653. * Shorthand for {@link #addListener}.
  654. * @alias Ext.util.Observable#addListener
  655. */
  656. on: 'addListener',
  657. /**
  658. * @method
  659. * Shorthand for {@link #removeListener}.
  660. * @alias Ext.util.Observable#removeListener
  661. */
  662. un: 'removeListener',
  663. /**
  664. * @method
  665. * Shorthand for {@link #addManagedListener}.
  666. * @alias Ext.util.Observable#addManagedListener
  667. */
  668. mon: 'addManagedListener',
  669. /**
  670. * @method
  671. * Shorthand for {@link #removeManagedListener}.
  672. * @alias Ext.util.Observable#removeManagedListener
  673. */
  674. mun: 'removeManagedListener'
  675. });
  676. //deprecated, will be removed in 5.0
  677. this.observeClass = this.observe;
  678. Ext.apply(Ext.util.Observable.prototype, function(){
  679. // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
  680. // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
  681. // private
  682. function getMethodEvent(method){
  683. var e = (this.methodEvents = this.methodEvents || {})[method],
  684. returnValue,
  685. v,
  686. cancel,
  687. obj = this;
  688. if (!e) {
  689. this.methodEvents[method] = e = {};
  690. e.originalFn = this[method];
  691. e.methodName = method;
  692. e.before = [];
  693. e.after = [];
  694. var makeCall = function(fn, scope, args){
  695. if((v = fn.apply(scope || obj, args)) !== undefined){
  696. if (typeof v == 'object') {
  697. if(v.returnValue !== undefined){
  698. returnValue = v.returnValue;
  699. }else{
  700. returnValue = v;
  701. }
  702. cancel = !!v.cancel;
  703. }
  704. else
  705. if (v === false) {
  706. cancel = true;
  707. }
  708. else {
  709. returnValue = v;
  710. }
  711. }
  712. };
  713. this[method] = function(){
  714. var args = Array.prototype.slice.call(arguments, 0),
  715. b, i, len;
  716. returnValue = v = undefined;
  717. cancel = false;
  718. for(i = 0, len = e.before.length; i < len; i++){
  719. b = e.before[i];
  720. makeCall(b.fn, b.scope, args);
  721. if (cancel) {
  722. return returnValue;
  723. }
  724. }
  725. if((v = e.originalFn.apply(obj, args)) !== undefined){
  726. returnValue = v;
  727. }
  728. for(i = 0, len = e.after.length; i < len; i++){
  729. b = e.after[i];
  730. makeCall(b.fn, b.scope, args);
  731. if (cancel) {
  732. return returnValue;
  733. }
  734. }
  735. return returnValue;
  736. };
  737. }
  738. return e;
  739. }
  740. return {
  741. // these are considered experimental
  742. // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
  743. // adds an 'interceptor' called before the original method
  744. beforeMethod : function(method, fn, scope){
  745. getMethodEvent.call(this, method).before.push({
  746. fn: fn,
  747. scope: scope
  748. });
  749. },
  750. // adds a 'sequence' called after the original method
  751. afterMethod : function(method, fn, scope){
  752. getMethodEvent.call(this, method).after.push({
  753. fn: fn,
  754. scope: scope
  755. });
  756. },
  757. removeMethodListener: function(method, fn, scope){
  758. var e = this.getMethodEvent(method),
  759. i, len;
  760. for(i = 0, len = e.before.length; i < len; i++){
  761. if(e.before[i].fn == fn && e.before[i].scope == scope){
  762. Ext.Array.erase(e.before, i, 1);
  763. return;
  764. }
  765. }
  766. for(i = 0, len = e.after.length; i < len; i++){
  767. if(e.after[i].fn == fn && e.after[i].scope == scope){
  768. Ext.Array.erase(e.after, i, 1);
  769. return;
  770. }
  771. }
  772. },
  773. toggleEventLogging: function(toggle) {
  774. Ext.util.Observable[toggle ? 'capture' : 'releaseCapture'](this, function(en) {
  775. if (Ext.isDefined(Ext.global.console)) {
  776. Ext.global.console.log(en, arguments);
  777. }
  778. });
  779. }
  780. };
  781. }());
  782. });
  783. /**
  784. * @class Ext.util.Animate
  785. * This animation class is a mixin.
  786. *
  787. * Ext.util.Animate provides an API for the creation of animated transitions of properties and styles.
  788. * This class is used as a mixin and currently applied to {@link Ext.Element}, {@link Ext.CompositeElement},
  789. * {@link Ext.draw.Sprite}, {@link Ext.draw.CompositeSprite}, and {@link Ext.Component}. Note that Components
  790. * have a limited subset of what attributes can be animated such as top, left, x, y, height, width, and
  791. * opacity (color, paddings, and margins can not be animated).
  792. *
  793. * ## Animation Basics
  794. *
  795. * All animations require three things - `easing`, `duration`, and `to` (the final end value for each property)
  796. * you wish to animate. Easing and duration are defaulted values specified below.
  797. * Easing describes how the intermediate values used during a transition will be calculated.
  798. * {@link Ext.fx.Anim#easing Easing} allows for a transition to change speed over its duration.
  799. * You may use the defaults for easing and duration, but you must always set a
  800. * {@link Ext.fx.Anim#to to} property which is the end value for all animations.
  801. *
  802. * Popular element 'to' configurations are:
  803. *
  804. * - opacity
  805. * - x
  806. * - y
  807. * - color
  808. * - height
  809. * - width
  810. *
  811. * Popular sprite 'to' configurations are:
  812. *
  813. * - translation
  814. * - path
  815. * - scale
  816. * - stroke
  817. * - rotation
  818. *
  819. * The default duration for animations is 250 (which is a 1/4 of a second). Duration is denoted in
  820. * milliseconds. Therefore 1 second is 1000, 1 minute would be 60000, and so on. The default easing curve
  821. * used for all animations is 'ease'. Popular easing functions are included and can be found in {@link Ext.fx.Anim#easing Easing}.
  822. *
  823. * For example, a simple animation to fade out an element with a default easing and duration:
  824. *
  825. * var p1 = Ext.get('myElementId');
  826. *
  827. * p1.animate({
  828. * to: {
  829. * opacity: 0
  830. * }
  831. * });
  832. *
  833. * To make this animation fade out in a tenth of a second:
  834. *
  835. * var p1 = Ext.get('myElementId');
  836. *
  837. * p1.animate({
  838. * duration: 100,
  839. * to: {
  840. * opacity: 0
  841. * }
  842. * });
  843. *
  844. * ## Animation Queues
  845. *
  846. * By default all animations are added to a queue which allows for animation via a chain-style API.
  847. * For example, the following code will queue 4 animations which occur sequentially (one right after the other):
  848. *
  849. * p1.animate({
  850. * to: {
  851. * x: 500
  852. * }
  853. * }).animate({
  854. * to: {
  855. * y: 150
  856. * }
  857. * }).animate({
  858. * to: {
  859. * backgroundColor: '#f00' //red
  860. * }
  861. * }).animate({
  862. * to: {
  863. * opacity: 0
  864. * }
  865. * });
  866. *
  867. * You can change this behavior by calling the {@link Ext.util.Animate#syncFx syncFx} method and all
  868. * subsequent animations for the specified target will be run concurrently (at the same time).
  869. *
  870. * p1.syncFx(); //this will make all animations run at the same time
  871. *
  872. * p1.animate({
  873. * to: {
  874. * x: 500
  875. * }
  876. * }).animate({
  877. * to: {
  878. * y: 150
  879. * }
  880. * }).animate({
  881. * to: {
  882. * backgroundColor: '#f00' //red
  883. * }
  884. * }).animate({
  885. * to: {
  886. * opacity: 0
  887. * }
  888. * });
  889. *
  890. * This works the same as:
  891. *
  892. * p1.animate({
  893. * to: {
  894. * x: 500,
  895. * y: 150,
  896. * backgroundColor: '#f00' //red
  897. * opacity: 0
  898. * }
  899. * });
  900. *
  901. * The {@link Ext.util.Animate#stopAnimation stopAnimation} method can be used to stop any
  902. * currently running animations and clear any queued animations.
  903. *
  904. * ## Animation Keyframes
  905. *
  906. * You can also set up complex animations with {@link Ext.fx.Anim#keyframes keyframes} which follow the
  907. * CSS3 Animation configuration pattern. Note rotation, translation, and scaling can only be done for sprites.
  908. * The previous example can be written with the following syntax:
  909. *
  910. * p1.animate({
  911. * duration: 1000, //one second total
  912. * keyframes: {
  913. * 25: { //from 0 to 250ms (25%)
  914. * x: 0
  915. * },
  916. * 50: { //from 250ms to 500ms (50%)
  917. * y: 0
  918. * },
  919. * 75: { //from 500ms to 750ms (75%)
  920. * backgroundColor: '#f00' //red
  921. * },
  922. * 100: { //from 750ms to 1sec
  923. * opacity: 0
  924. * }
  925. * }
  926. * });
  927. *
  928. * ## Animation Events
  929. *
  930. * Each animation you create has events for {@link Ext.fx.Anim#beforeanimate beforeanimate},
  931. * {@link Ext.fx.Anim#afteranimate afteranimate}, and {@link Ext.fx.Anim#lastframe lastframe}.
  932. * Keyframed animations adds an additional {@link Ext.fx.Animator#keyframe keyframe} event which
  933. * fires for each keyframe in your animation.
  934. *
  935. * All animations support the {@link Ext.util.Observable#listeners listeners} configuration to attact functions to these events.
  936. *
  937. * startAnimate: function() {
  938. * var p1 = Ext.get('myElementId');
  939. * p1.animate({
  940. * duration: 100,
  941. * to: {
  942. * opacity: 0
  943. * },
  944. * listeners: {
  945. * beforeanimate: function() {
  946. * // Execute my custom method before the animation
  947. * this.myBeforeAnimateFn();
  948. * },
  949. * afteranimate: function() {
  950. * // Execute my custom method after the animation
  951. * this.myAfterAnimateFn();
  952. * },
  953. * scope: this
  954. * });
  955. * },
  956. * myBeforeAnimateFn: function() {
  957. * // My custom logic
  958. * },
  959. * myAfterAnimateFn: function() {
  960. * // My custom logic
  961. * }
  962. *
  963. * Due to the fact that animations run asynchronously, you can determine if an animation is currently
  964. * running on any target by using the {@link Ext.util.Animate#getActiveAnimation getActiveAnimation}
  965. * method. This method will return false if there are no active animations or return the currently
  966. * running {@link Ext.fx.Anim} instance.
  967. *
  968. * In this example, we're going to wait for the current animation to finish, then stop any other
  969. * queued animations before we fade our element's opacity to 0:
  970. *
  971. * var curAnim = p1.getActiveAnimation();
  972. * if (curAnim) {
  973. * curAnim.on('afteranimate', function() {
  974. * p1.stopAnimation();
  975. * p1.animate({
  976. * to: {
  977. * opacity: 0
  978. * }
  979. * });
  980. * });
  981. * }
  982. *
  983. * @docauthor Jamie Avins <jamie@sencha.com>
  984. */
  985. Ext.define('Ext.util.Animate', {
  986. uses: ['Ext.fx.Manager', 'Ext.fx.Anim'],
  987. /**
  988. * <p>Perform custom animation on this object.<p>
  989. * <p>This method is applicable to both the {@link Ext.Component Component} class and the {@link Ext.Element Element} class.
  990. * It performs animated transitions of certain properties of this object over a specified timeline.</p>
  991. * <p>The sole parameter is an object which specifies start property values, end property values, and properties which
  992. * describe the timeline. Of the properties listed below, only <b><code>to</code></b> is mandatory.</p>
  993. * <p>Properties include<ul>
  994. * <li><code>from</code> <div class="sub-desc">An object which specifies start values for the properties being animated.
  995. * If not supplied, properties are animated from current settings. The actual properties which may be animated depend upon
  996. * ths object being animated. See the sections below on Element and Component animation.<div></li>
  997. * <li><code>to</code> <div class="sub-desc">An object which specifies end values for the properties being animated.</div></li>
  998. * <li><code>duration</code><div class="sub-desc">The duration <b>in milliseconds</b> for which the animation will run.</div></li>
  999. * <li><code>easing</code> <div class="sub-desc">A string value describing an easing type to modify the rate of change from the default linear to non-linear. Values may be one of:<code><ul>
  1000. * <li>ease</li>
  1001. * <li>easeIn</li>
  1002. * <li>easeOut</li>
  1003. * <li>easeInOut</li>
  1004. * <li>backIn</li>
  1005. * <li>backOut</li>
  1006. * <li>elasticIn</li>
  1007. * <li>elasticOut</li>
  1008. * <li>bounceIn</li>
  1009. * <li>bounceOut</li>
  1010. * </ul></code></div></li>
  1011. * <li><code>keyframes</code> <div class="sub-desc">This is an object which describes the state of animated properties at certain points along the timeline.
  1012. * it is an object containing properties who's names are the percentage along the timeline being described and who's values specify the animation state at that point.</div></li>
  1013. * <li><code>listeners</code> <div class="sub-desc">This is a standard {@link Ext.util.Observable#listeners listeners} configuration object which may be used
  1014. * to inject behaviour at either the <code>beforeanimate</code> event or the <code>afteranimate</code> event.</div></li>
  1015. * </ul></p>
  1016. * <h3>Animating an {@link Ext.Element Element}</h3>
  1017. * When animating an Element, the following properties may be specified in <code>from</code>, <code>to</code>, and <code>keyframe</code> objects:<ul>
  1018. * <li><code>x</code> <div class="sub-desc">The page X position in pixels.</div></li>
  1019. * <li><code>y</code> <div class="sub-desc">The page Y position in pixels</div></li>
  1020. * <li><code>left</code> <div class="sub-desc">The element's CSS <code>left</code> value. Units must be supplied.</div></li>
  1021. * <li><code>top</code> <div class="sub-desc">The element's CSS <code>top</code> value. Units must be supplied.</div></li>
  1022. * <li><code>width</code> <div class="sub-desc">The element's CSS <code>width</code> value. Units must be supplied.</div></li>
  1023. * <li><code>height</code> <div class="sub-desc">The element's CSS <code>height</code> value. Units must be supplied.</div></li>
  1024. * <li><code>scrollLeft</code> <div class="sub-desc">The element's <code>scrollLeft</code> value.</div></li>
  1025. * <li><code>scrollTop</code> <div class="sub-desc">The element's <code>scrollLeft</code> value.</div></li>
  1026. * <li><code>opacity</code> <div class="sub-desc">The element's <code>opacity</code> value. This must be a value between <code>0</code> and <code>1</code>.</div></li>
  1027. * </ul>
  1028. * <p><b>Be aware than animating an Element which is being used by an Ext Component without in some way informing the Component about the changed element state
  1029. * will result in incorrect Component behaviour. This is because the Component will be using the old state of the element. To avoid this problem, it is now possible to
  1030. * directly animate certain properties of Components.</b></p>
  1031. * <h3>Animating a {@link Ext.Component Component}</h3>
  1032. * When animating an Element, the following properties may be specified in <code>from</code>, <code>to</code>, and <code>keyframe</code> objects:<ul>
  1033. * <li><code>x</code> <div class="sub-desc">The Component's page X position in pixels.</div></li>
  1034. * <li><code>y</code> <div class="sub-desc">The Component's page Y position in pixels</div></li>
  1035. * <li><code>left</code> <div class="sub-desc">The Component's <code>left</code> value in pixels.</div></li>
  1036. * <li><code>top</code> <div class="sub-desc">The Component's <code>top</code> value in pixels.</div></li>
  1037. * <li><code>width</code> <div class="sub-desc">The Component's <code>width</code> value in pixels.</div></li>
  1038. * <li><code>width</code> <div class="sub-desc">The Component's <code>width</code> value in pixels.</div></li>
  1039. * <li><code>dynamic</code> <div class="sub-desc">Specify as true to update the Component's layout (if it is a Container) at every frame
  1040. * of the animation. <i>Use sparingly as laying out on every intermediate size change is an expensive operation</i>.</div></li>
  1041. * </ul>
  1042. * <p>For example, to animate a Window to a new size, ensuring that its internal layout, and any shadow is correct:</p>
  1043. * <pre><code>
  1044. myWindow = Ext.create('Ext.window.Window', {
  1045. title: 'Test Component animation',
  1046. width: 500,
  1047. height: 300,
  1048. layout: {
  1049. type: 'hbox',
  1050. align: 'stretch'
  1051. },
  1052. items: [{
  1053. title: 'Left: 33%',
  1054. margins: '5 0 5 5',
  1055. flex: 1
  1056. }, {
  1057. title: 'Left: 66%',
  1058. margins: '5 5 5 5',
  1059. flex: 2
  1060. }]
  1061. });
  1062. myWindow.show();
  1063. myWindow.header.el.on('click', function() {
  1064. myWindow.animate({
  1065. to: {
  1066. width: (myWindow.getWidth() == 500) ? 700 : 500,
  1067. height: (myWindow.getHeight() == 300) ? 400 : 300,
  1068. }
  1069. });
  1070. });
  1071. </code></pre>
  1072. * <p>For performance reasons, by default, the internal layout is only updated when the Window reaches its final <code>"to"</code> size. If dynamic updating of the Window's child
  1073. * Components is required, then configure the animation with <code>dynamic: true</code> and the two child items will maintain their proportions during the animation.</p>
  1074. * @param {Object} config An object containing properties which describe the animation's start and end states, and the timeline of the animation.
  1075. * @return {Object} this
  1076. */
  1077. animate: function(animObj) {
  1078. var me = this;
  1079. if (Ext.fx.Manager.hasFxBlock(me.id)) {
  1080. return me;
  1081. }
  1082. Ext.fx.Manager.queueFx(Ext.create('Ext.fx.Anim', me.anim(animObj)));
  1083. return this;
  1084. },
  1085. // @private - process the passed fx configuration.
  1086. anim: function(config) {
  1087. if (!Ext.isObject(config)) {
  1088. return (config) ? {} : false;
  1089. }
  1090. var me = this;
  1091. if (config.stopAnimation) {
  1092. me.stopAnimation();
  1093. }
  1094. Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));
  1095. return Ext.apply({
  1096. target: me,
  1097. paused: true
  1098. }, config);
  1099. },
  1100. /**
  1101. * @deprecated 4.0 Replaced by {@link #stopAnimation}
  1102. * Stops any running effects and clears this object's internal effects queue if it contains
  1103. * any additional effects that haven't started yet.
  1104. * @return {Ext.Element} The Element
  1105. * @method
  1106. */
  1107. stopFx: Ext.Function.alias(Ext.util.Animate, 'stopAnimation'),
  1108. /**
  1109. * Stops any running effects and clears this object's internal effects queue if it contains
  1110. * any additional effects that haven't started yet.
  1111. * @return {Ext.Element} The Element
  1112. */
  1113. stopAnimation: function() {
  1114. Ext.fx.Manager.stopAnimation(this.id);
  1115. return this;
  1116. },
  1117. /**
  1118. * Ensures that all effects queued after syncFx is called on this object are
  1119. * run concurrently. This is the opposite of {@link #sequenceFx}.
  1120. * @return {Object} this
  1121. */
  1122. syncFx: function() {
  1123. Ext.fx.Manager.setFxDefaults(this.id, {
  1124. concurrent: true
  1125. });
  1126. return this;
  1127. },
  1128. /**
  1129. * Ensures that all effects queued after sequenceFx is called on this object are
  1130. * run in sequence. This is the opposite of {@link #syncFx}.
  1131. * @return {Object} this
  1132. */
  1133. sequenceFx: function() {
  1134. Ext.fx.Manager.setFxDefaults(this.id, {
  1135. concurrent: false
  1136. });
  1137. return this;
  1138. },
  1139. /**
  1140. * @deprecated 4.0 Replaced by {@link #getActiveAnimation}
  1141. * @alias Ext.util.Animate#getActiveAnimation
  1142. * @method
  1143. */
  1144. hasActiveFx: Ext.Function.alias(Ext.util.Animate, 'getActiveAnimation'),
  1145. /**
  1146. * Returns the current animation if this object has any effects actively running or queued, else returns false.
  1147. * @return {Ext.fx.Anim/Boolean} Anim if element has active effects, else false
  1148. */
  1149. getActiveAnimation: function() {
  1150. return Ext.fx.Manager.getActiveAnimation(this.id);
  1151. }
  1152. }, function(){
  1153. // Apply Animate mixin manually until Element is defined in the proper 4.x way
  1154. Ext.applyIf(Ext.Element.prototype, this.prototype);
  1155. // We need to call this again so the animation methods get copied over to CE
  1156. Ext.CompositeElementLite.importElementMethods();
  1157. });
  1158. /**
  1159. * @class Ext.state.Provider
  1160. * <p>Abstract base class for state provider implementations. The provider is responsible
  1161. * for setting values and extracting values to/from the underlying storage source. The
  1162. * storage source can vary and the details should be implemented in a subclass. For example
  1163. * a provider could use a server side database or the browser localstorage where supported.</p>
  1164. *
  1165. * <p>This class provides methods for encoding and decoding <b>typed</b> variables including
  1166. * dates and defines the Provider interface. By default these methods put the value and the
  1167. * type information into a delimited string that can be stored. These should be overridden in
  1168. * a subclass if you want to change the format of the encoded value and subsequent decoding.</p>
  1169. */
  1170. Ext.define('Ext.state.Provider', {
  1171. mixins: {
  1172. observable: 'Ext.util.Observable'
  1173. },
  1174. /**
  1175. * @cfg {String} prefix A string to prefix to items stored in the underlying state store.
  1176. * Defaults to <tt>'ext-'</tt>
  1177. */
  1178. prefix: 'ext-',
  1179. constructor : function(config){
  1180. config = config || {};
  1181. var me = this;
  1182. Ext.apply(me, config);
  1183. /**
  1184. * @event statechange
  1185. * Fires when a state change occurs.
  1186. * @param {Ext.state.Provider} this This state provider
  1187. * @param {String} key The state key which was changed
  1188. * @param {String} value The encoded value for the state
  1189. */
  1190. me.addEvents("statechange");
  1191. me.state = {};
  1192. me.mixins.observable.constructor.call(me);
  1193. },
  1194. /**
  1195. * Returns the current value for a key
  1196. * @param {String} name The key name
  1197. * @param {Object} defaultValue A default value to return if the key's value is not found
  1198. * @return {Object} The state data
  1199. */
  1200. get : function(name, defaultValue){
  1201. return typeof this.state[name] == "undefined" ?
  1202. defaultValue : this.state[name];
  1203. },
  1204. /**
  1205. * Clears a value from the state
  1206. * @param {String} name The key name
  1207. */
  1208. clear : function(name){
  1209. var me = this;
  1210. delete me.state[name];
  1211. me.fireEvent("statechange", me, name, null);
  1212. },
  1213. /**
  1214. * Sets the value for a key
  1215. * @param {String} name The key name
  1216. * @param {Object} value The value to set
  1217. */
  1218. set : function(name, value){
  1219. var me = this;
  1220. me.state[name] = value;
  1221. me.fireEvent("statechange", me, name, value);
  1222. },
  1223. /**
  1224. * Decodes a string previously encoded with {@link #encodeValue}.
  1225. * @param {String} value The value to decode
  1226. * @return {Object} The decoded value
  1227. */
  1228. decodeValue : function(value){
  1229. // a -> Array
  1230. // n -> Number
  1231. // d -> Date
  1232. // b -> Boolean
  1233. // s -> String
  1234. // o -> Object
  1235. // -> Empty (null)
  1236. var me = this,
  1237. re = /^(a|n|d|b|s|o|e)\:(.*)$/,
  1238. matches = re.exec(unescape(value)),
  1239. all,
  1240. type,
  1241. value,
  1242. keyValue;
  1243. if(!matches || !matches[1]){
  1244. return; // non state
  1245. }
  1246. type = matches[1];
  1247. value = matches[2];
  1248. switch (type) {
  1249. case 'e':
  1250. return null;
  1251. case 'n':
  1252. return parseFloat(value);
  1253. case 'd':
  1254. return new Date(Date.parse(value));
  1255. case 'b':
  1256. return (value == '1');
  1257. case 'a':
  1258. all = [];
  1259. if(value != ''){
  1260. Ext.each(value.split('^'), function(val){
  1261. all.push(me.decodeValue(val));
  1262. }, me);
  1263. }
  1264. return all;
  1265. case 'o':
  1266. all = {};
  1267. if(value != ''){
  1268. Ext.each(value.split('^'), function(val){
  1269. keyValue = val.split('=');
  1270. all[keyValue[0]] = me.decodeValue(keyValue[1]);
  1271. }, me);
  1272. }
  1273. return all;
  1274. default:
  1275. return value;
  1276. }
  1277. },
  1278. /**
  1279. * Encodes a value including type information. Decode with {@link #decodeValue}.
  1280. * @param {Object} value The value to encode
  1281. * @return {String} The encoded value
  1282. */
  1283. encodeValue : function(value){
  1284. var flat = '',
  1285. i = 0,
  1286. enc,
  1287. len,
  1288. key;
  1289. if (value == null) {
  1290. return 'e:1';
  1291. } else if(typeof value == 'number') {
  1292. enc = 'n:' + value;
  1293. } else if(typeof value == 'boolean') {
  1294. enc = 'b:' + (value ? '1' : '0');
  1295. } else if(Ext.isDate(value)) {
  1296. enc = 'd:' + value.toGMTString();
  1297. } else if(Ext.isArray(value)) {
  1298. for (len = value.length; i < len; i++) {
  1299. flat += this.encodeValue(value[i]);
  1300. if (i != len - 1) {
  1301. flat += '^';
  1302. }
  1303. }
  1304. enc = 'a:' + flat;
  1305. } else if (typeof value == 'object') {
  1306. for (key in value) {
  1307. if (typeof value[key] != 'function' && value[key] !== undefined) {
  1308. flat += key + '=' + this.encodeValue(value[key]) + '^';
  1309. }
  1310. }
  1311. enc = 'o:' + flat.substring(0, flat.length-1);
  1312. } else {
  1313. enc = 's:' + value;
  1314. }
  1315. return escape(enc);
  1316. }
  1317. });
  1318. /**
  1319. * Provides searching of Components within Ext.ComponentManager (globally) or a specific
  1320. * Ext.container.Container on the document with a similar syntax to a CSS selector.
  1321. *
  1322. * Components can be retrieved by using their {@link Ext.Component xtype} with an optional . prefix
  1323. *
  1324. * - `component` or `.component`
  1325. * - `gridpanel` or `.gridpanel`
  1326. *
  1327. * An itemId or id must be prefixed with a #
  1328. *
  1329. * - `#myContainer`
  1330. *
  1331. * Attributes must be wrapped in brackets
  1332. *
  1333. * - `component[autoScroll]`
  1334. * - `panel[title="Test"]`
  1335. *
  1336. * Member expressions from candidate Components may be tested. If the expression returns a *truthy* value,
  1337. * the candidate Component will be included in the query:
  1338. *
  1339. * var disabledFields = myFormPanel.query("{isDisabled()}");
  1340. *
  1341. * Pseudo classes may be used to filter results in the same way as in {@link Ext.DomQuery DomQuery}:
  1342. *
  1343. * // Function receives array and returns a filtered array.
  1344. * Ext.ComponentQuery.pseudos.invalid = function(items) {
  1345. * var i = 0, l = items.length, c, result = [];
  1346. * for (; i < l; i++) {
  1347. * if (!(c = items[i]).isValid()) {
  1348. * result.push(c);
  1349. * }
  1350. * }
  1351. * return result;
  1352. * };
  1353. *
  1354. * var invalidFields = myFormPanel.query('field:invalid');
  1355. * if (invalidFields.length) {
  1356. * invalidFields[0].getEl().scrollIntoView(myFormPanel.body);
  1357. * for (var i = 0, l = invalidFields.length; i < l; i++) {
  1358. * invalidFields[i].getEl().frame("red");
  1359. * }
  1360. * }
  1361. *
  1362. * Default pseudos include:
  1363. *
  1364. * - not
  1365. * - last
  1366. *
  1367. * Queries return an array of components.
  1368. * Here are some example queries.
  1369. *
  1370. * // retrieve all Ext.Panels in the document by xtype
  1371. * var panelsArray = Ext.ComponentQuery.query('panel');
  1372. *
  1373. * // retrieve all Ext.Panels within the container with an id myCt
  1374. * var panelsWithinmyCt = Ext.ComponentQuery.query('#myCt panel');
  1375. *
  1376. * // retrieve all direct children which are Ext.Panels within myCt
  1377. * var directChildPanel = Ext.ComponentQuery.query('#myCt > panel');
  1378. *
  1379. * // retrieve all grids and trees
  1380. * var gridsAndTrees = Ext.ComponentQuery.query('gridpanel, treepanel');
  1381. *
  1382. * For easy access to queries based from a particular Container see the {@link Ext.container.Container#query},
  1383. * {@link Ext.container.Container#down} and {@link Ext.container.Container#child} methods. Also see
  1384. * {@link Ext.Component#up}.
  1385. */
  1386. Ext.define('Ext.ComponentQuery', {
  1387. singleton: true,
  1388. uses: ['Ext.ComponentManager']
  1389. }, function() {
  1390. var cq = this,
  1391. // A function source code pattern with a placeholder which accepts an expression which yields a truth value when applied
  1392. // as a member on each item in the passed array.
  1393. filterFnPattern = [
  1394. 'var r = [],',
  1395. 'i = 0,',
  1396. 'it = items,',
  1397. 'l = it.length,',
  1398. 'c;',
  1399. 'for (; i < l; i++) {',
  1400. 'c = it[i];',
  1401. 'if (c.{0}) {',
  1402. 'r.push(c);',
  1403. '}',
  1404. '}',
  1405. 'return r;'
  1406. ].join(''),
  1407. filterItems = function(items, operation) {
  1408. // Argument list for the operation is [ itemsArray, operationArg1, operationArg2...]
  1409. // The operation's method loops over each item in the candidate array and
  1410. // returns an array of items which match its criteria
  1411. return operation.method.apply(this, [ items ].concat(operation.args));
  1412. },
  1413. getItems = function(items, mode) {
  1414. var result = [],
  1415. i = 0,
  1416. length = items.length,
  1417. candidate,
  1418. deep = mode !== '>';
  1419. for (; i < length; i++) {
  1420. candidate = items[i];
  1421. if (candidate.getRefItems) {
  1422. result = result.concat(candidate.getRefItems(deep));
  1423. }
  1424. }
  1425. return result;
  1426. },
  1427. getAncestors = function(items) {
  1428. var result = [],
  1429. i = 0,
  1430. length = items.length,
  1431. candidate;
  1432. for (; i < length; i++) {
  1433. candidate = items[i];
  1434. while (!!(candidate = (candidate.ownerCt || candidate.floatParent))) {
  1435. result.push(candidate);
  1436. }
  1437. }
  1438. return result;
  1439. },
  1440. // Filters the passed candidate array and returns only items which match the passed xtype
  1441. filterByXType = function(items, xtype, shallow) {
  1442. if (xtype === '*') {
  1443. return items.slice();
  1444. }
  1445. else {
  1446. var result = [],
  1447. i = 0,
  1448. length = items.length,
  1449. candidate;
  1450. for (; i < length; i++) {
  1451. candidate = items[i];
  1452. if (candidate.isXType(xtype, shallow)) {
  1453. result.push(candidate);
  1454. }
  1455. }
  1456. return result;
  1457. }
  1458. },
  1459. // Filters the passed candidate array and returns only items which have the passed className
  1460. filterByClassName = function(items, className) {
  1461. var EA = Ext.Array,
  1462. result = [],
  1463. i = 0,
  1464. length = items.length,
  1465. candidate;
  1466. for (; i < length; i++) {
  1467. candidate = items[i];
  1468. if (candidate.el ? candidate.el.hasCls(className) : EA.contains(candidate.initCls(), className)) {
  1469. result.push(candidate);
  1470. }
  1471. }
  1472. return result;
  1473. },
  1474. // Filters the passed candidate array and returns only items which have the specified property match
  1475. filterByAttribute = function(items, property, operator, value) {
  1476. var result = [],
  1477. i = 0,
  1478. length = items.length,
  1479. candidate;
  1480. for (; i < length; i++) {
  1481. candidate = items[i];
  1482. if (!value ? !!candidate[property] : (String(candidate[property]) === value)) {
  1483. result.push(candidate);
  1484. }
  1485. }
  1486. return result;
  1487. },
  1488. // Filters the passed candidate array and returns only items which have the specified itemId or id
  1489. filterById = function(items, id) {
  1490. var result = [],
  1491. i = 0,
  1492. length = items.length,
  1493. candidate;
  1494. for (; i < length; i++) {
  1495. candidate = items[i];
  1496. if (candidate.getItemId() === id) {
  1497. result.push(candidate);
  1498. }
  1499. }
  1500. return result;
  1501. },
  1502. // Filters the passed candidate array and returns only items which the named pseudo class matcher filters in
  1503. filterByPseudo = function(items, name, value) {
  1504. return cq.pseudos[name](items, value);
  1505. },
  1506. // Determines leading mode
  1507. // > for direct child, and ^ to switch to ownerCt axis
  1508. modeRe = /^(\s?([>\^])\s?|\s|$)/,
  1509. // Matches a token with possibly (true|false) appended for the "shallow" parameter
  1510. tokenRe = /^(#)?([\w\-]+|\*)(?:\((true|false)\))?/,
  1511. matchers = [{
  1512. // Checks for .xtype with possibly (true|false) appended for the "shallow" parameter
  1513. re: /^\.([\w\-]+)(?:\((true|false)\))?/,
  1514. method: filterByXType
  1515. },{
  1516. // checks for [attribute=value]
  1517. re: /^(?:[\[](?:@)?([\w\-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]])/,
  1518. method: filterByAttribute
  1519. }, {
  1520. // checks for #cmpItemId
  1521. re: /^#([\w\-]+)/,
  1522. method: filterById
  1523. }, {
  1524. // checks for :<pseudo_class>(<selector>)
  1525. re: /^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/,
  1526. method: filterByPseudo
  1527. }, {
  1528. // checks for {<member_expression>}
  1529. re: /^(?:\{([^\}]+)\})/,
  1530. method: filterFnPattern
  1531. }];
  1532. // @class Ext.ComponentQuery.Query
  1533. // This internal class is completely hidden in documentation.
  1534. cq.Query = Ext.extend(Object, {
  1535. constructor: function(cfg) {
  1536. cfg = cfg || {};
  1537. Ext.apply(this, cfg);
  1538. },
  1539. // Executes this Query upon the selected root.
  1540. // The root provides the initial source of candidate Component matches which are progressively
  1541. // filtered by iterating through this Query's operations cache.
  1542. // If no root is provided, all registered Components are searched via the ComponentManager.
  1543. // root may be a Container who's descendant Components are filtered
  1544. // root may be a Component with an implementation of getRefItems which provides some nested Components such as the
  1545. // docked items within a Panel.
  1546. // root may be an array of candidate Components to filter using this Query.
  1547. execute : function(root) {
  1548. var operations = this.operations,
  1549. i = 0,
  1550. length = operations.length,
  1551. operation,
  1552. workingItems;
  1553. // no root, use all Components in the document
  1554. if (!root) {
  1555. workingItems = Ext.ComponentManager.all.getArray();
  1556. }
  1557. // Root is a candidate Array
  1558. else if (Ext.isArray(root)) {
  1559. workingItems = root;
  1560. }
  1561. // We are going to loop over our operations and take care of them
  1562. // one by one.
  1563. for (; i < length; i++) {
  1564. operation = operations[i];
  1565. // The mode operation requires some custom handling.
  1566. // All other operations essentially filter down our current
  1567. // working items, while mode replaces our current working
  1568. // items by getting children from each one of our current
  1569. // working items. The type of mode determines the type of
  1570. // children we get. (e.g. > only gets direct children)
  1571. if (operation.mode === '^') {
  1572. workingItems = getAncestors(workingItems || [root]);
  1573. }
  1574. else if (operation.mode) {
  1575. workingItems = getItems(workingItems || [root], operation.mode);
  1576. }
  1577. else {
  1578. workingItems = filterItems(workingItems || getItems([root]), operation);
  1579. }
  1580. // If this is the last operation, it means our current working
  1581. // items are the final matched items. Thus return them!
  1582. if (i === length -1) {
  1583. return workingItems;
  1584. }
  1585. }
  1586. return [];
  1587. },
  1588. is: function(component) {
  1589. var operations = this.operations,
  1590. components = Ext.isArray(component) ? component : [component],
  1591. originalLength = components.length,
  1592. lastOperation = operations[operations.length-1],
  1593. ln, i;
  1594. components = filterItems(components, lastOperation);
  1595. if (components.length === originalLength) {
  1596. if (operations.length > 1) {
  1597. for (i = 0, ln = components.length; i < ln; i++) {
  1598. if (Ext.Array.indexOf(this.execute(), components[i]) === -1) {
  1599. return false;
  1600. }
  1601. }
  1602. }
  1603. return true;
  1604. }
  1605. return false;
  1606. }
  1607. });
  1608. Ext.apply(this, {
  1609. // private cache of selectors and matching ComponentQuery.Query objects
  1610. cache: {},
  1611. // private cache of pseudo class filter functions
  1612. pseudos: {
  1613. not: function(components, selector){
  1614. var CQ = Ext.ComponentQuery,
  1615. i = 0,
  1616. length = components.length,
  1617. results = [],
  1618. index = -1,
  1619. component;
  1620. for(; i < length; ++i) {
  1621. component = components[i];
  1622. if (!CQ.is(component, selector)) {
  1623. results[++index] = component;
  1624. }
  1625. }
  1626. return results;
  1627. },
  1628. last: function(components) {
  1629. return components[components.length - 1];
  1630. }
  1631. },
  1632. /**
  1633. * Returns an array of matched Components from within the passed root object.
  1634. *
  1635. * This method filters returned Components in a similar way to how CSS selector based DOM
  1636. * queries work using a textual selector string.
  1637. *
  1638. * See class summary for details.
  1639. *
  1640. * @param {String} selector The selector string to filter returned Components
  1641. * @param {Ext.container.Container} root The Container within which to perform the query.
  1642. * If omitted, all Components within the document are included in the search.
  1643. *
  1644. * This parameter may also be an array of Components to filter according to the selector.</p>
  1645. * @returns {Ext.Component[]} The matched Components.
  1646. *
  1647. * @member Ext.ComponentQuery
  1648. */
  1649. query: function(selector, root) {
  1650. var selectors = selector.split(','),
  1651. length = selectors.length,
  1652. i = 0,
  1653. results = [],
  1654. noDupResults = [],
  1655. dupMatcher = {},
  1656. query, resultsLn, cmp;
  1657. for (; i < length; i++) {
  1658. selector = Ext.String.trim(selectors[i]);
  1659. query = this.cache[selector];
  1660. if (!query) {
  1661. this.cache[selector] = query = this.parse(selector);
  1662. }
  1663. results = results.concat(query.execute(root));
  1664. }
  1665. // multiple selectors, potential to find duplicates
  1666. // lets filter them out.
  1667. if (length > 1) {
  1668. resultsLn = results.length;
  1669. for (i = 0; i < resultsLn; i++) {
  1670. cmp = results[i];
  1671. if (!dupMatcher[cmp.id]) {
  1672. noDupResults.push(cmp);
  1673. dupMatcher[cmp.id] = true;
  1674. }
  1675. }
  1676. results = noDupResults;
  1677. }
  1678. return results;
  1679. },
  1680. /**
  1681. * Tests whether the passed Component matches the selector string.
  1682. * @param {Ext.Component} component The Component to test
  1683. * @param {String} selector The selector string to test against.
  1684. * @return {Boolean} True if the Component matches the selector.
  1685. * @member Ext.ComponentQuery
  1686. */
  1687. is: function(component, selector) {
  1688. if (!selector) {
  1689. return true;
  1690. }
  1691. var query = this.cache[selector];
  1692. if (!query) {
  1693. this.cache[selector] = query = this.parse(selector);
  1694. }
  1695. return query.is(component);
  1696. },
  1697. parse: function(selector) {
  1698. var operations = [],
  1699. length = matchers.length,
  1700. lastSelector,
  1701. tokenMatch,
  1702. matchedChar,
  1703. modeMatch,
  1704. selectorMatch,
  1705. i, matcher, method;
  1706. // We are going to parse the beginning of the selector over and
  1707. // over again, slicing off the selector any portions we converted into an
  1708. // operation, until it is an empty string.
  1709. while (selector && lastSelector !== selector) {
  1710. lastSelector = selector;
  1711. // First we check if we are dealing with a token like #, * or an xtype
  1712. tokenMatch = selector.match(tokenRe);
  1713. if (tokenMatch) {
  1714. matchedChar = tokenMatch[1];
  1715. // If the token is prefixed with a # we push a filterById operation to our stack
  1716. if (matchedChar === '#') {
  1717. operations.push({
  1718. method: filterById,
  1719. args: [Ext.String.trim(tokenMatch[2])]
  1720. });
  1721. }
  1722. // If the token is prefixed with a . we push a filterByClassName operation to our stack
  1723. // FIXME: Not enabled yet. just needs \. adding to the tokenRe prefix
  1724. else if (matchedChar === '.') {
  1725. operations.push({
  1726. method: filterByClassName,
  1727. args: [Ext.String.trim(tokenMatch[2])]
  1728. });
  1729. }
  1730. // If the token is a * or an xtype string, we push a filterByXType
  1731. // operation to the stack.
  1732. else {
  1733. operations.push({
  1734. method: filterByXType,
  1735. args: [Ext.String.trim(tokenMatch[2]), Boolean(tokenMatch[3])]
  1736. });
  1737. }
  1738. // Now we slice of the part we just converted into an operation
  1739. selector = selector.replace(tokenMatch[0], '');
  1740. }
  1741. // If the next part of the query is not a space or > or ^, it means we
  1742. // are going to check for more things that our current selection
  1743. // has to comply to.
  1744. while (!(modeMatch = selector.match(modeRe))) {
  1745. // Lets loop over each type of matcher and execute it
  1746. // on our current selector.
  1747. for (i = 0; selector && i < length; i++) {
  1748. matcher = matchers[i];
  1749. selectorMatch = selector.match(matcher.re);
  1750. method = matcher.method;
  1751. // If we have a match, add an operation with the method
  1752. // associated with this matcher, and pass the regular
  1753. // expression matches are arguments to the operation.
  1754. if (selectorMatch) {
  1755. operations.push({
  1756. method: Ext.isString(matcher.method)
  1757. // Turn a string method into a function by formatting the string with our selector matche expression
  1758. // A new method is created for different match expressions, eg {id=='textfield-1024'}
  1759. // Every expression may be different in different selectors.
  1760. ? Ext.functionFactory('items', Ext.String.format.apply(Ext.String, [method].concat(selectorMatch.slice(1))))
  1761. : matcher.method,
  1762. args: selectorMatch.slice(1)
  1763. });
  1764. selector = selector.replace(selectorMatch[0], '');
  1765. break; // Break on match
  1766. }
  1767. //<debug>
  1768. // Exhausted all matches: It's an error
  1769. if (i === (length - 1)) {
  1770. Ext.Error.raise('Invalid ComponentQuery selector: "' + arguments[0] + '"');
  1771. }
  1772. //</debug>
  1773. }
  1774. }
  1775. // Now we are going to check for a mode change. This means a space
  1776. // or a > to determine if we are going to select all the children
  1777. // of the currently matched items, or a ^ if we are going to use the
  1778. // ownerCt axis as the candidate source.
  1779. if (modeMatch[1]) { // Assignment, and test for truthiness!
  1780. operations.push({
  1781. mode: modeMatch[2]||modeMatch[1]
  1782. });
  1783. selector = selector.replace(modeMatch[0], '');
  1784. }
  1785. }
  1786. // Now that we have all our operations in an array, we are going
  1787. // to create a new Query using these operations.
  1788. return new cq.Query({
  1789. operations: operations
  1790. });
  1791. }
  1792. });
  1793. });
  1794. /**
  1795. * @class Ext.util.HashMap
  1796. * <p>
  1797. * Represents a collection of a set of key and value pairs. Each key in the HashMap
  1798. * must be unique, the same key cannot exist twice. Access to items is provided via
  1799. * the key only. Sample usage:
  1800. * <pre><code>
  1801. var map = new Ext.util.HashMap();
  1802. map.add('key1', 1);
  1803. map.add('key2', 2);
  1804. map.add('key3', 3);
  1805. map.each(function(key, value, length){
  1806. console.log(key, value, length);
  1807. });
  1808. * </code></pre>
  1809. * </p>
  1810. *
  1811. * <p>The HashMap is an unordered class,
  1812. * there is no guarantee when iterating over the items that they will be in any particular
  1813. * order. If this is required, then use a {@link Ext.util.MixedCollection}.
  1814. * </p>
  1815. */
  1816. Ext.define('Ext.util.HashMap', {
  1817. mixins: {
  1818. observable: 'Ext.util.Observable'
  1819. },
  1820. /**
  1821. * @cfg {Function} keyFn A function that is used to retrieve a default key for a passed object.
  1822. * A default is provided that returns the <b>id</b> property on the object. This function is only used
  1823. * if the add method is called with a single argument.
  1824. */
  1825. /**
  1826. * Creates new HashMap.
  1827. * @param {Object} config (optional) Config object.
  1828. */
  1829. constructor: function(config) {
  1830. config = config || {};
  1831. var me = this,
  1832. keyFn = config.keyFn;
  1833. me.addEvents(
  1834. /**
  1835. * @event add
  1836. * Fires when a new item is added to the hash
  1837. * @param {Ext.util.HashMap} this.
  1838. * @param {String} key The key of the added item.
  1839. * @param {Object} value The value of the added item.
  1840. */
  1841. 'add',
  1842. /**
  1843. * @event clear
  1844. * Fires when the hash is cleared.
  1845. * @param {Ext.util.HashMap} this.
  1846. */
  1847. 'clear',
  1848. /**
  1849. * @event remove
  1850. * Fires when an item is removed from the hash.
  1851. * @param {Ext.util.HashMap} this.
  1852. * @param {String} key The key of the removed item.
  1853. * @param {Object} value The value of the removed item.
  1854. */
  1855. 'remove',
  1856. /**
  1857. * @event replace
  1858. * Fires when an item is replaced in the hash.
  1859. * @param {Ext.util.HashMap} this.
  1860. * @param {String} key The key of the replaced item.
  1861. * @param {Object} value The new value for the item.
  1862. * @param {Object} old The old value for the item.
  1863. */
  1864. 'replace'
  1865. );
  1866. me.mixins.observable.constructor.call(me, config);
  1867. me.clear(true);
  1868. if (keyFn) {
  1869. me.getKey = keyFn;
  1870. }
  1871. },
  1872. /**
  1873. * Gets the number of items in the hash.
  1874. * @return {Number} The number of items in the hash.
  1875. */
  1876. getCount: function() {
  1877. return this.length;
  1878. },
  1879. /**
  1880. * Implementation for being able to extract the key from an object if only
  1881. * a single argument is passed.
  1882. * @private
  1883. * @param {String} key The key
  1884. * @param {Object} value The value
  1885. * @return {Array} [key, value]
  1886. */
  1887. getData: function(key, value) {
  1888. // if we have no value, it means we need to get the key from the object
  1889. if (value === undefined) {
  1890. value = key;
  1891. key = this.getKey(value);
  1892. }
  1893. return [key, value];
  1894. },
  1895. /**
  1896. * Extracts the key from an object. This is a default implementation, it may be overridden
  1897. * @param {Object} o The object to get the key from
  1898. * @return {String} The key to use.
  1899. */
  1900. getKey: function(o) {
  1901. return o.id;
  1902. },
  1903. /**
  1904. * Adds an item to the collection. Fires the {@link #add} event when complete.
  1905. * @param {String} key <p>The key to associate with the item, or the new item.</p>
  1906. * <p>If a {@link #getKey} implementation was specified for this HashMap,
  1907. * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
  1908. * the HashMap will be able to <i>derive</i> the key for the new item.
  1909. * In this case just pass the new item in this parameter.</p>
  1910. * @param {Object} o The item to add.
  1911. * @return {Object} The item added.
  1912. */
  1913. add: function(key, value) {
  1914. var me = this,
  1915. data;
  1916. if (arguments.length === 1) {
  1917. value = key;
  1918. key = me.getKey(value);
  1919. }
  1920. if (me.containsKey(key)) {
  1921. return me.replace(key, value);
  1922. }
  1923. data = me.getData(key, value);
  1924. key = data[0];
  1925. value = data[1];
  1926. me.map[key] = value;
  1927. ++me.length;
  1928. me.fireEvent('add', me, key, value);
  1929. return value;
  1930. },
  1931. /**
  1932. * Replaces an item in the hash. If the key doesn't exist, the
  1933. * {@link #add} method will be used.
  1934. * @param {String} key The key of the item.
  1935. * @param {Object} value The new value for the item.
  1936. * @return {Object} The new value of the item.
  1937. */
  1938. replace: function(key, value) {
  1939. var me = this,
  1940. map = me.map,
  1941. old;
  1942. if (!me.containsKey(key)) {
  1943. me.add(key, value);
  1944. }
  1945. old = map[key];
  1946. map[key] = value;
  1947. me.fireEvent('replace', me, key, value, old);
  1948. return value;
  1949. },
  1950. /**
  1951. * Remove an item from the hash.
  1952. * @param {Object} o The value of the item to remove.
  1953. * @return {Boolean} True if the item was successfully removed.
  1954. */
  1955. remove: function(o) {
  1956. var key = this.findKey(o);
  1957. if (key !== undefined) {
  1958. return this.removeAtKey(key);
  1959. }
  1960. return false;
  1961. },
  1962. /**
  1963. * Remove an item from the hash.
  1964. * @param {String} key The key to remove.
  1965. * @return {Boolean} True if the item was successfully removed.
  1966. */
  1967. removeAtKey: function(key) {
  1968. var me = this,
  1969. value;
  1970. if (me.containsKey(key)) {
  1971. value = me.map[key];
  1972. delete me.map[key];
  1973. --me.length;
  1974. me.fireEvent('remove', me, key, value);
  1975. return true;
  1976. }
  1977. return false;
  1978. },
  1979. /**
  1980. * Retrieves an item with a particular key.
  1981. * @param {String} key The key to lookup.
  1982. * @return {Object} The value at that key. If it doesn't exist, <tt>undefined</tt> is returned.
  1983. */
  1984. get: function(key) {
  1985. return this.map[key];
  1986. },
  1987. /**
  1988. * Removes all items from the hash.
  1989. * @return {Ext.util.HashMap} this
  1990. */
  1991. clear: function(/* private */ initial) {
  1992. var me = this;
  1993. me.map = {};
  1994. me.length = 0;
  1995. if (initial !== true) {
  1996. me.fireEvent('clear', me);
  1997. }
  1998. return me;
  1999. },
  2000. /**
  2001. * Checks whether a key exists in the hash.
  2002. * @param {String} key The key to check for.
  2003. * @return {Boolean} True if they key exists in the hash.
  2004. */
  2005. containsKey: function(key) {
  2006. return this.map[key] !== undefined;
  2007. },
  2008. /**
  2009. * Checks whether a value exists in the hash.
  2010. * @param {Object} value The value to check for.
  2011. * @return {Boolean} True if the value exists in the dictionary.
  2012. */
  2013. contains: function(value) {
  2014. return this.containsKey(this.findKey(value));
  2015. },
  2016. /**
  2017. * Return all of the keys in the hash.
  2018. * @return {Array} An array of keys.
  2019. */
  2020. getKeys: function() {
  2021. return this.getArray(true);
  2022. },
  2023. /**
  2024. * Return all of the values in the hash.
  2025. * @return {Array} An array of values.
  2026. */
  2027. getValues: function() {
  2028. return this.getArray(false);
  2029. },
  2030. /**
  2031. * Gets either the keys/values in an array from the hash.
  2032. * @private
  2033. * @param {Boolean} isKey True to extract the keys, otherwise, the value
  2034. * @return {Array} An array of either keys/values from the hash.
  2035. */
  2036. getArray: function(isKey) {
  2037. var arr = [],
  2038. key,
  2039. map = this.map;
  2040. for (key in map) {
  2041. if (map.hasOwnProperty(key)) {
  2042. arr.push(isKey ? key: map[key]);
  2043. }
  2044. }
  2045. return arr;
  2046. },
  2047. /**
  2048. * Executes the specified function once for each item in the hash.
  2049. * Returning false from the function will cease iteration.
  2050. *
  2051. * The paramaters passed to the function are:
  2052. * <div class="mdetail-params"><ul>
  2053. * <li><b>key</b> : String<p class="sub-desc">The key of the item</p></li>
  2054. * <li><b>value</b> : Number<p class="sub-desc">The value of the item</p></li>
  2055. * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the hash</p></li>
  2056. * </ul></div>
  2057. * @param {Function} fn The function to execute.
  2058. * @param {Object} scope The scope to execute in. Defaults to <tt>this</tt>.
  2059. * @return {Ext.util.HashMap} this
  2060. */
  2061. each: function(fn, scope) {
  2062. // copy items so they may be removed during iteration.
  2063. var items = Ext.apply({}, this.map),
  2064. key,
  2065. length = this.length;
  2066. scope = scope || this;
  2067. for (key in items) {
  2068. if (items.hasOwnProperty(key)) {
  2069. if (fn.call(scope, key, items[key], length) === false) {
  2070. break;
  2071. }
  2072. }
  2073. }
  2074. return this;
  2075. },
  2076. /**
  2077. * Performs a shallow copy on this hash.
  2078. * @return {Ext.util.HashMap} The new hash object.
  2079. */
  2080. clone: function() {
  2081. var hash = new this.self(),
  2082. map = this.map,
  2083. key;
  2084. hash.suspendEvents();
  2085. for (key in map) {
  2086. if (map.hasOwnProperty(key)) {
  2087. hash.add(key, map[key]);
  2088. }
  2089. }
  2090. hash.resumeEvents();
  2091. return hash;
  2092. },
  2093. /**
  2094. * @private
  2095. * Find the key for a value.
  2096. * @param {Object} value The value to find.
  2097. * @return {Object} The value of the item. Returns <tt>undefined</tt> if not found.
  2098. */
  2099. findKey: function(value) {
  2100. var key,
  2101. map = this.map;
  2102. for (key in map) {
  2103. if (map.hasOwnProperty(key) && map[key] === value) {
  2104. return key;
  2105. }
  2106. }
  2107. return undefined;
  2108. }
  2109. });
  2110. /**
  2111. * @class Ext.state.Manager
  2112. * This is the global state manager. By default all components that are "state aware" check this class
  2113. * for state information if you don't pass them a custom state provider. In order for this class
  2114. * to be useful, it must be initialized with a provider when your application initializes. Example usage:
  2115. <pre><code>
  2116. // in your initialization function
  2117. init : function(){
  2118. Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
  2119. var win = new Window(...);
  2120. win.restoreState();
  2121. }
  2122. </code></pre>
  2123. * This class passes on calls from components to the underlying {@link Ext.state.Provider} so that
  2124. * there is a common interface that can be used without needing to refer to a specific provider instance
  2125. * in every component.
  2126. * @singleton
  2127. * @docauthor Evan Trimboli <evan@sencha.com>
  2128. */
  2129. Ext.define('Ext.state.Manager', {
  2130. singleton: true,
  2131. requires: ['Ext.state.Provider'],
  2132. constructor: function() {
  2133. this.provider = Ext.create('Ext.state.Provider');
  2134. },
  2135. /**
  2136. * Configures the default state provider for your application
  2137. * @param {Ext.state.Provider} stateProvider The state provider to set
  2138. */
  2139. setProvider : function(stateProvider){
  2140. this.provider = stateProvider;
  2141. },
  2142. /**
  2143. * Returns the current value for a key
  2144. * @param {String} name The key name
  2145. * @param {Object} defaultValue The default value to return if the key lookup does not match
  2146. * @return {Object} The state data
  2147. */
  2148. get : function(key, defaultValue){
  2149. return this.provider.get(key, defaultValue);
  2150. },
  2151. /**
  2152. * Sets the value for a key
  2153. * @param {String} name The key name
  2154. * @param {Object} value The state data
  2155. */
  2156. set : function(key, value){
  2157. this.provider.set(key, value);
  2158. },
  2159. /**
  2160. * Clears a value from the state
  2161. * @param {String} name The key name
  2162. */
  2163. clear : function(key){
  2164. this.provider.clear(key);
  2165. },
  2166. /**
  2167. * Gets the currently configured state provider
  2168. * @return {Ext.state.Provider} The state provider
  2169. */
  2170. getProvider : function(){
  2171. return this.provider;
  2172. }
  2173. });
  2174. /**
  2175. * @class Ext.state.Stateful
  2176. * A mixin for being able to save the state of an object to an underlying
  2177. * {@link Ext.state.Provider}.
  2178. */
  2179. Ext.define('Ext.state.Stateful', {
  2180. /* Begin Definitions */
  2181. mixins: {
  2182. observable: 'Ext.util.Observable'
  2183. },
  2184. requires: ['Ext.state.Manager'],
  2185. /* End Definitions */
  2186. /**
  2187. * @cfg {Boolean} stateful
  2188. * <p>A flag which causes the object to attempt to restore the state of
  2189. * internal properties from a saved state on startup. The object must have
  2190. * a <code>{@link #stateId}</code> for state to be managed.
  2191. * Auto-generated ids are not guaranteed to be stable across page loads and
  2192. * cannot be relied upon to save and restore the same state for a object.<p>
  2193. * <p>For state saving to work, the state manager's provider must have been
  2194. * set to an implementation of {@link Ext.state.Provider} which overrides the
  2195. * {@link Ext.state.Provider#set set} and {@link Ext.state.Provider#get get}
  2196. * methods to save and recall name/value pairs. A built-in implementation,
  2197. * {@link Ext.state.CookieProvider} is available.</p>
  2198. * <p>To set the state provider for the current page:</p>
  2199. * <pre><code>
  2200. Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
  2201. expires: new Date(new Date().getTime()+(1000*60*60*24*7)), //7 days from now
  2202. }));
  2203. * </code></pre>
  2204. * <p>A stateful object attempts to save state when one of the events
  2205. * listed in the <code>{@link #stateEvents}</code> configuration fires.</p>
  2206. * <p>To save state, a stateful object first serializes its state by
  2207. * calling <b><code>{@link #getState}</code></b>. By default, this function does
  2208. * nothing. The developer must provide an implementation which returns an
  2209. * object hash which represents the restorable state of the object.</p>
  2210. * <p>The value yielded by getState is passed to {@link Ext.state.Manager#set}
  2211. * which uses the configured {@link Ext.state.Provider} to save the object
  2212. * keyed by the <code>{@link #stateId}</code>.</p>
  2213. * <p>During construction, a stateful object attempts to <i>restore</i>
  2214. * its state by calling {@link Ext.state.Manager#get} passing the
  2215. * <code>{@link #stateId}</code></p>
  2216. * <p>The resulting object is passed to <b><code>{@link #applyState}</code></b>.
  2217. * The default implementation of <code>{@link #applyState}</code> simply copies
  2218. * properties into the object, but a developer may override this to support
  2219. * more behaviour.</p>
  2220. * <p>You can perform extra processing on state save and restore by attaching
  2221. * handlers to the {@link #beforestaterestore}, {@link #staterestore},
  2222. * {@link #beforestatesave} and {@link #statesave} events.</p>
  2223. */
  2224. stateful: true,
  2225. /**
  2226. * @cfg {String} stateId
  2227. * The unique id for this object to use for state management purposes.
  2228. * <p>See {@link #stateful} for an explanation of saving and restoring state.</p>
  2229. */
  2230. /**
  2231. * @cfg {String[]} stateEvents
  2232. * <p>An array of events that, when fired, should trigger this object to
  2233. * save its state. Defaults to none. <code>stateEvents</code> may be any type
  2234. * of event supported by this object, including browser or custom events
  2235. * (e.g., <tt>['click', 'customerchange']</tt>).</p>
  2236. * <p>See <code>{@link #stateful}</code> for an explanation of saving and
  2237. * restoring object state.</p>
  2238. */
  2239. /**
  2240. * @cfg {Number} saveDelay
  2241. * A buffer to be applied if many state events are fired within a short period.
  2242. */
  2243. saveDelay: 100,
  2244. autoGenIdRe: /^((\w+-)|(ext-comp-))\d{4,}$/i,
  2245. constructor: function(config) {
  2246. var me = this;
  2247. config = config || {};
  2248. if (Ext.isDefined(config.stateful)) {
  2249. me.stateful = config.stateful;
  2250. }
  2251. if (Ext.isDefined(config.saveDelay)) {
  2252. me.saveDelay = config.saveDelay;
  2253. }
  2254. me.stateId = me.stateId || config.stateId;
  2255. if (!me.stateEvents) {
  2256. me.stateEvents = [];
  2257. }
  2258. if (config.stateEvents) {
  2259. me.stateEvents.concat(config.stateEvents);
  2260. }
  2261. this.addEvents(
  2262. /**
  2263. * @event beforestaterestore
  2264. * Fires before the state of the object is restored. Return false from an event handler to stop the restore.
  2265. * @param {Ext.state.Stateful} this
  2266. * @param {Object} state The hash of state values returned from the StateProvider. If this
  2267. * event is not vetoed, then the state object is passed to <b><tt>applyState</tt></b>. By default,
  2268. * that simply copies property values into this object. The method maybe overriden to
  2269. * provide custom state restoration.
  2270. */
  2271. 'beforestaterestore',
  2272. /**
  2273. * @event staterestore
  2274. * Fires after the state of the object is restored.
  2275. * @param {Ext.state.Stateful} this
  2276. * @param {Object} state The hash of state values returned from the StateProvider. This is passed
  2277. * to <b><tt>applyState</tt></b>. By default, that simply copies property values into this
  2278. * object. The method maybe overriden to provide custom state restoration.
  2279. */
  2280. 'staterestore',
  2281. /**
  2282. * @event beforestatesave
  2283. * Fires before the state of the object is saved to the configured state provider. Return false to stop the save.
  2284. * @param {Ext.state.Stateful} this
  2285. * @param {Object} state The hash of state values. This is determined by calling
  2286. * <b><tt>getState()</tt></b> on the object. This method must be provided by the
  2287. * developer to return whetever representation of state is required, by default, Ext.state.Stateful
  2288. * has a null implementation.
  2289. */
  2290. 'beforestatesave',
  2291. /**
  2292. * @event statesave
  2293. * Fires after the state of the object is saved to the configured state provider.
  2294. * @param {Ext.state.Stateful} this
  2295. * @param {Object} state The hash of state values. This is determined by calling
  2296. * <b><tt>getState()</tt></b> on the object. This method must be provided by the
  2297. * developer to return whetever representation of state is required, by default, Ext.state.Stateful
  2298. * has a null implementation.
  2299. */
  2300. 'statesave'
  2301. );
  2302. me.mixins.observable.constructor.call(me);
  2303. if (me.stateful !== false) {
  2304. me.initStateEvents();
  2305. me.initState();
  2306. }
  2307. },
  2308. /**
  2309. * Initializes any state events for this object.
  2310. * @private
  2311. */
  2312. initStateEvents: function() {
  2313. this.addStateEvents(this.stateEvents);
  2314. },
  2315. /**
  2316. * Add events that will trigger the state to be saved.
  2317. * @param {String/String[]} events The event name or an array of event names.
  2318. */
  2319. addStateEvents: function(events){
  2320. if (!Ext.isArray(events)) {
  2321. events = [events];
  2322. }
  2323. var me = this,
  2324. i = 0,
  2325. len = events.length;
  2326. for (; i < len; ++i) {
  2327. me.on(events[i], me.onStateChange, me);
  2328. }
  2329. },
  2330. /**
  2331. * This method is called when any of the {@link #stateEvents} are fired.
  2332. * @private
  2333. */
  2334. onStateChange: function(){
  2335. var me = this,
  2336. delay = me.saveDelay;
  2337. if (delay > 0) {
  2338. if (!me.stateTask) {
  2339. me.stateTask = Ext.create('Ext.util.DelayedTask', me.saveState, me);
  2340. }
  2341. me.stateTask.delay(me.saveDelay);
  2342. } else {
  2343. me.saveState();
  2344. }
  2345. },
  2346. /**
  2347. * Saves the state of the object to the persistence store.
  2348. * @private
  2349. */
  2350. saveState: function() {
  2351. var me = this,
  2352. id,
  2353. state;
  2354. if (me.stateful !== false) {
  2355. id = me.getStateId();
  2356. if (id) {
  2357. state = me.getState();
  2358. if (me.fireEvent('beforestatesave', me, state) !== false) {
  2359. Ext.state.Manager.set(id, state);
  2360. me.fireEvent('statesave', me, state);
  2361. }
  2362. }
  2363. }
  2364. },
  2365. /**
  2366. * Gets the current state of the object. By default this function returns null,
  2367. * it should be overridden in subclasses to implement methods for getting the state.
  2368. * @return {Object} The current state
  2369. */
  2370. getState: function(){
  2371. return null;
  2372. },
  2373. /**
  2374. * Applies the state to the object. This should be overridden in subclasses to do
  2375. * more complex state operations. By default it applies the state properties onto
  2376. * the current object.
  2377. * @param {Object} state The state
  2378. */
  2379. applyState: function(state) {
  2380. if (state) {
  2381. Ext.apply(this, state);
  2382. }
  2383. },
  2384. /**
  2385. * Gets the state id for this object.
  2386. * @return {String} The state id, null if not found.
  2387. */
  2388. getStateId: function() {
  2389. var me = this,
  2390. id = me.stateId;
  2391. if (!id) {
  2392. id = me.autoGenIdRe.test(String(me.id)) ? null : me.id;
  2393. }
  2394. return id;
  2395. },
  2396. /**
  2397. * Initializes the state of the object upon construction.
  2398. * @private
  2399. */
  2400. initState: function(){
  2401. var me = this,
  2402. id = me.getStateId(),
  2403. state;
  2404. if (me.stateful !== false) {
  2405. if (id) {
  2406. state = Ext.state.Manager.get(id);
  2407. if (state) {
  2408. state = Ext.apply({}, state);
  2409. if (me.fireEvent('beforestaterestore', me, state) !== false) {
  2410. me.applyState(state);
  2411. me.fireEvent('staterestore', me, state);
  2412. }
  2413. }
  2414. }
  2415. }
  2416. },
  2417. /**
  2418. * Conditionally saves a single property from this object to the given state object.
  2419. * The idea is to only save state which has changed from the initial state so that
  2420. * current software settings do not override future software settings. Only those
  2421. * values that are user-changed state should be saved.
  2422. *
  2423. * @param {String} propName The name of the property to save.
  2424. * @param {Object} state The state object in to which to save the property.
  2425. * @param {String} stateName (optional) The name to use for the property in state.
  2426. * @return {Boolean} True if the property was saved, false if not.
  2427. */
  2428. savePropToState: function (propName, state, stateName) {
  2429. var me = this,
  2430. value = me[propName],
  2431. config = me.initialConfig;
  2432. if (me.hasOwnProperty(propName)) {
  2433. if (!config || config[propName] !== value) {
  2434. if (state) {
  2435. state[stateName || propName] = value;
  2436. }
  2437. return true;
  2438. }
  2439. }
  2440. return false;
  2441. },
  2442. savePropsToState: function (propNames, state) {
  2443. var me = this;
  2444. Ext.each(propNames, function (propName) {
  2445. me.savePropToState(propName, state);
  2446. });
  2447. return state;
  2448. },
  2449. /**
  2450. * Destroys this stateful object.
  2451. */
  2452. destroy: function(){
  2453. var task = this.stateTask;
  2454. if (task) {
  2455. task.cancel();
  2456. }
  2457. this.clearListeners();
  2458. }
  2459. });
  2460. /**
  2461. * Base Manager class
  2462. */
  2463. Ext.define('Ext.AbstractManager', {
  2464. /* Begin Definitions */
  2465. requires: ['Ext.util.HashMap'],
  2466. /* End Definitions */
  2467. typeName: 'type',
  2468. constructor: function(config) {
  2469. Ext.apply(this, config || {});
  2470. /**
  2471. * @property {Ext.util.HashMap} all
  2472. * Contains all of the items currently managed
  2473. */
  2474. this.all = Ext.create('Ext.util.HashMap');
  2475. this.types = {};
  2476. },
  2477. /**
  2478. * Returns an item by id.
  2479. * For additional details see {@link Ext.util.HashMap#get}.
  2480. * @param {String} id The id of the item
  2481. * @return {Object} The item, undefined if not found.
  2482. */
  2483. get : function(id) {
  2484. return this.all.get(id);
  2485. },
  2486. /**
  2487. * Registers an item to be managed
  2488. * @param {Object} item The item to register
  2489. */
  2490. register: function(item) {
  2491. //<debug>
  2492. var all = this.all,
  2493. key = all.getKey(item);
  2494. if (all.containsKey(key)) {
  2495. Ext.Error.raise('Registering duplicate id "' + key + '" with this manager');
  2496. }
  2497. //</debug>
  2498. this.all.add(item);
  2499. },
  2500. /**
  2501. * Unregisters an item by removing it from this manager
  2502. * @param {Object} item The item to unregister
  2503. */
  2504. unregister: function(item) {
  2505. this.all.remove(item);
  2506. },
  2507. /**
  2508. * Registers a new item constructor, keyed by a type key.
  2509. * @param {String} type The mnemonic string by which the class may be looked up.
  2510. * @param {Function} cls The new instance class.
  2511. */
  2512. registerType : function(type, cls) {
  2513. this.types[type] = cls;
  2514. cls[this.typeName] = type;
  2515. },
  2516. /**
  2517. * Checks if an item type is registered.
  2518. * @param {String} type The mnemonic string by which the class may be looked up
  2519. * @return {Boolean} Whether the type is registered.
  2520. */
  2521. isRegistered : function(type){
  2522. return this.types[type] !== undefined;
  2523. },
  2524. /**
  2525. * Creates and returns an instance of whatever this manager manages, based on the supplied type and
  2526. * config object.
  2527. * @param {Object} config The config object
  2528. * @param {String} defaultType If no type is discovered in the config object, we fall back to this type
  2529. * @return {Object} The instance of whatever this manager is managing
  2530. */
  2531. create: function(config, defaultType) {
  2532. var type = config[this.typeName] || config.type || defaultType,
  2533. Constructor = this.types[type];
  2534. //<debug>
  2535. if (Constructor === undefined) {
  2536. Ext.Error.raise("The '" + type + "' type has not been registered with this manager");
  2537. }
  2538. //</debug>
  2539. return new Constructor(config);
  2540. },
  2541. /**
  2542. * Registers a function that will be called when an item with the specified id is added to the manager.
  2543. * This will happen on instantiation.
  2544. * @param {String} id The item id
  2545. * @param {Function} fn The callback function. Called with a single parameter, the item.
  2546. * @param {Object} scope The scope (this reference) in which the callback is executed.
  2547. * Defaults to the item.
  2548. */
  2549. onAvailable : function(id, fn, scope){
  2550. var all = this.all,
  2551. item;
  2552. if (all.containsKey(id)) {
  2553. item = all.get(id);
  2554. fn.call(scope || item, item);
  2555. } else {
  2556. all.on('add', function(map, key, item){
  2557. if (key == id) {
  2558. fn.call(scope || item, item);
  2559. all.un('add', fn, scope);
  2560. }
  2561. });
  2562. }
  2563. },
  2564. /**
  2565. * Executes the specified function once for each item in the collection.
  2566. * @param {Function} fn The function to execute.
  2567. * @param {String} fn.key The key of the item
  2568. * @param {Number} fn.value The value of the item
  2569. * @param {Number} fn.length The total number of items in the collection
  2570. * @param {Boolean} fn.return False to cease iteration.
  2571. * @param {Object} scope The scope to execute in. Defaults to `this`.
  2572. */
  2573. each: function(fn, scope){
  2574. this.all.each(fn, scope || this);
  2575. },
  2576. /**
  2577. * Gets the number of items in the collection.
  2578. * @return {Number} The number of items in the collection.
  2579. */
  2580. getCount: function(){
  2581. return this.all.getCount();
  2582. }
  2583. });
  2584. /**
  2585. * @class Ext.ComponentManager
  2586. * @extends Ext.AbstractManager
  2587. * <p>Provides a registry of all Components (instances of {@link Ext.Component} or any subclass
  2588. * thereof) on a page so that they can be easily accessed by {@link Ext.Component component}
  2589. * {@link Ext.Component#id id} (see {@link #get}, or the convenience method {@link Ext#getCmp Ext.getCmp}).</p>
  2590. * <p>This object also provides a registry of available Component <i>classes</i>
  2591. * indexed by a mnemonic code known as the Component's {@link Ext.Component#xtype xtype}.
  2592. * The <code>xtype</code> provides a way to avoid instantiating child Components
  2593. * when creating a full, nested config object for a complete Ext page.</p>
  2594. * <p>A child Component may be specified simply as a <i>config object</i>
  2595. * as long as the correct <code>{@link Ext.Component#xtype xtype}</code> is specified so that if and when the Component
  2596. * needs rendering, the correct type can be looked up for lazy instantiation.</p>
  2597. * <p>For a list of all available <code>{@link Ext.Component#xtype xtypes}</code>, see {@link Ext.Component}.</p>
  2598. * @singleton
  2599. */
  2600. Ext.define('Ext.ComponentManager', {
  2601. extend: 'Ext.AbstractManager',
  2602. alternateClassName: 'Ext.ComponentMgr',
  2603. singleton: true,
  2604. typeName: 'xtype',
  2605. /**
  2606. * Creates a new Component from the specified config object using the
  2607. * config object's xtype to determine the class to instantiate.
  2608. * @param {Object} config A configuration object for the Component you wish to create.
  2609. * @param {Function} defaultType (optional) The constructor to provide the default Component type if
  2610. * the config object does not contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
  2611. * @return {Ext.Component} The newly instantiated Component.
  2612. */
  2613. create: function(component, defaultType){
  2614. if (component instanceof Ext.AbstractComponent) {
  2615. return component;
  2616. }
  2617. else if (Ext.isString(component)) {
  2618. return Ext.createByAlias('widget.' + component);
  2619. }
  2620. else {
  2621. var type = component.xtype || defaultType,
  2622. config = component;
  2623. return Ext.createByAlias('widget.' + type, config);
  2624. }
  2625. },
  2626. registerType: function(type, cls) {
  2627. this.types[type] = cls;
  2628. cls[this.typeName] = type;
  2629. cls.prototype[this.typeName] = type;
  2630. }
  2631. });
  2632. /**
  2633. * An abstract base class which provides shared methods for Components across the Sencha product line.
  2634. *
  2635. * Please refer to sub class's documentation
  2636. * @private
  2637. */
  2638. Ext.define('Ext.AbstractComponent', {
  2639. /* Begin Definitions */
  2640. requires: [
  2641. 'Ext.ComponentQuery',
  2642. 'Ext.ComponentManager'
  2643. ],
  2644. mixins: {
  2645. observable: 'Ext.util.Observable',
  2646. animate: 'Ext.util.Animate',
  2647. state: 'Ext.state.Stateful'
  2648. },
  2649. // The "uses" property specifies class which are used in an instantiated AbstractComponent.
  2650. // They do *not* have to be loaded before this class may be defined - that is what "requires" is for.
  2651. uses: [
  2652. 'Ext.PluginManager',
  2653. 'Ext.ComponentManager',
  2654. 'Ext.Element',
  2655. 'Ext.DomHelper',
  2656. 'Ext.XTemplate',
  2657. 'Ext.ComponentQuery',
  2658. 'Ext.ComponentLoader',
  2659. 'Ext.EventManager',
  2660. 'Ext.layout.Layout',
  2661. 'Ext.layout.component.Auto',
  2662. 'Ext.LoadMask',
  2663. 'Ext.ZIndexManager'
  2664. ],
  2665. statics: {
  2666. AUTO_ID: 1000
  2667. },
  2668. /* End Definitions */
  2669. isComponent: true,
  2670. getAutoId: function() {
  2671. return ++Ext.AbstractComponent.AUTO_ID;
  2672. },
  2673. /**
  2674. * @cfg {String} id
  2675. * The **unique id of this component instance.**
  2676. *
  2677. * It should not be necessary to use this configuration except for singleton objects in your application. Components
  2678. * created with an id may be accessed globally using {@link Ext#getCmp Ext.getCmp}.
  2679. *
  2680. * Instead of using assigned ids, use the {@link #itemId} config, and {@link Ext.ComponentQuery ComponentQuery}
  2681. * which provides selector-based searching for Sencha Components analogous to DOM querying. The {@link
  2682. * Ext.container.Container Container} class contains {@link Ext.container.Container#down shortcut methods} to query
  2683. * its descendant Components by selector.
  2684. *
  2685. * Note that this id will also be used as the element id for the containing HTML element that is rendered to the
  2686. * page for this component. This allows you to write id-based CSS rules to style the specific instance of this
  2687. * component uniquely, and also to select sub-elements using this component's id as the parent.
  2688. *
  2689. * **Note**: to avoid complications imposed by a unique id also see `{@link #itemId}`.
  2690. *
  2691. * **Note**: to access the container of a Component see `{@link #ownerCt}`.
  2692. *
  2693. * Defaults to an {@link #getId auto-assigned id}.
  2694. */
  2695. /**
  2696. * @cfg {String} itemId
  2697. * An itemId can be used as an alternative way to get a reference to a component when no object reference is
  2698. * available. Instead of using an `{@link #id}` with {@link Ext}.{@link Ext#getCmp getCmp}, use `itemId` with
  2699. * {@link Ext.container.Container}.{@link Ext.container.Container#getComponent getComponent} which will retrieve
  2700. * `itemId`'s or {@link #id}'s. Since `itemId`'s are an index to the container's internal MixedCollection, the
  2701. * `itemId` is scoped locally to the container -- avoiding potential conflicts with {@link Ext.ComponentManager}
  2702. * which requires a **unique** `{@link #id}`.
  2703. *
  2704. * var c = new Ext.panel.Panel({ //
  2705. * {@link Ext.Component#height height}: 300,
  2706. * {@link #renderTo}: document.body,
  2707. * {@link Ext.container.Container#layout layout}: 'auto',
  2708. * {@link Ext.container.Container#items items}: [
  2709. * {
  2710. * itemId: 'p1',
  2711. * {@link Ext.panel.Panel#title title}: 'Panel 1',
  2712. * {@link Ext.Component#height height}: 150
  2713. * },
  2714. * {
  2715. * itemId: 'p2',
  2716. * {@link Ext.panel.Panel#title title}: 'Panel 2',
  2717. * {@link Ext.Component#height height}: 150
  2718. * }
  2719. * ]
  2720. * })
  2721. * p1 = c.{@link Ext.container.Container#getComponent getComponent}('p1'); // not the same as {@link Ext#getCmp Ext.getCmp()}
  2722. * p2 = p1.{@link #ownerCt}.{@link Ext.container.Container#getComponent getComponent}('p2'); // reference via a sibling
  2723. *
  2724. * Also see {@link #id}, `{@link Ext.container.Container#query}`, `{@link Ext.container.Container#down}` and
  2725. * `{@link Ext.container.Container#child}`.
  2726. *
  2727. * **Note**: to access the container of an item see {@link #ownerCt}.
  2728. */
  2729. /**
  2730. * @property {Ext.Container} ownerCt
  2731. * This Component's owner {@link Ext.container.Container Container} (is set automatically
  2732. * when this Component is added to a Container). Read-only.
  2733. *
  2734. * **Note**: to access items within the Container see {@link #itemId}.
  2735. */
  2736. /**
  2737. * @property {Boolean} layoutManagedWidth
  2738. * @private
  2739. * Flag set by the container layout to which this Component is added.
  2740. * If the layout manages this Component's width, it sets the value to 1.
  2741. * If it does NOT manage the width, it sets it to 2.
  2742. * If the layout MAY affect the width, but only if the owning Container has a fixed width, this is set to 0.
  2743. */
  2744. /**
  2745. * @property {Boolean} layoutManagedHeight
  2746. * @private
  2747. * Flag set by the container layout to which this Component is added.
  2748. * If the layout manages this Component's height, it sets the value to 1.
  2749. * If it does NOT manage the height, it sets it to 2.
  2750. * If the layout MAY affect the height, but only if the owning Container has a fixed height, this is set to 0.
  2751. */
  2752. /**
  2753. * @cfg {String/Object} autoEl
  2754. * A tag name or {@link Ext.DomHelper DomHelper} spec used to create the {@link #getEl Element} which will
  2755. * encapsulate this Component.
  2756. *
  2757. * You do not normally need to specify this. For the base classes {@link Ext.Component} and
  2758. * {@link Ext.container.Container}, this defaults to **'div'**. The more complex Sencha classes use a more
  2759. * complex DOM structure specified by their own {@link #renderTpl}s.
  2760. *
  2761. * This is intended to allow the developer to create application-specific utility Components encapsulated by
  2762. * different DOM elements. Example usage:
  2763. *
  2764. * {
  2765. * xtype: 'component',
  2766. * autoEl: {
  2767. * tag: 'img',
  2768. * src: 'http://www.example.com/example.jpg'
  2769. * }
  2770. * }, {
  2771. * xtype: 'component',
  2772. * autoEl: {
  2773. * tag: 'blockquote',
  2774. * html: 'autoEl is cool!'
  2775. * }
  2776. * }, {
  2777. * xtype: 'container',
  2778. * autoEl: 'ul',
  2779. * cls: 'ux-unordered-list',
  2780. * items: {
  2781. * xtype: 'component',
  2782. * autoEl: 'li',
  2783. * html: 'First list item'
  2784. * }
  2785. * }
  2786. */
  2787. /**
  2788. * @cfg {Ext.XTemplate/String/String[]} renderTpl
  2789. * An {@link Ext.XTemplate XTemplate} used to create the internal structure inside this Component's encapsulating
  2790. * {@link #getEl Element}.
  2791. *
  2792. * You do not normally need to specify this. For the base classes {@link Ext.Component} and
  2793. * {@link Ext.container.Container}, this defaults to **`null`** which means that they will be initially rendered
  2794. * with no internal structure; they render their {@link #getEl Element} empty. The more specialized ExtJS and Touch
  2795. * classes which use a more complex DOM structure, provide their own template definitions.
  2796. *
  2797. * This is intended to allow the developer to create application-specific utility Components with customized
  2798. * internal structure.
  2799. *
  2800. * Upon rendering, any created child elements may be automatically imported into object properties using the
  2801. * {@link #renderSelectors} and {@link #childEls} options.
  2802. */
  2803. renderTpl: null,
  2804. /**
  2805. * @cfg {Object} renderData
  2806. *
  2807. * The data used by {@link #renderTpl} in addition to the following property values of the component:
  2808. *
  2809. * - id
  2810. * - ui
  2811. * - uiCls
  2812. * - baseCls
  2813. * - componentCls
  2814. * - frame
  2815. *
  2816. * See {@link #renderSelectors} and {@link #childEls} for usage examples.
  2817. */
  2818. /**
  2819. * @cfg {Object} renderSelectors
  2820. * An object containing properties specifying {@link Ext.DomQuery DomQuery} selectors which identify child elements
  2821. * created by the render process.
  2822. *
  2823. * After the Component's internal structure is rendered according to the {@link #renderTpl}, this object is iterated through,
  2824. * and the found Elements are added as properties to the Component using the `renderSelector` property name.
  2825. *
  2826. * For example, a Component which renderes a title and description into its element:
  2827. *
  2828. * Ext.create('Ext.Component', {
  2829. * renderTo: Ext.getBody(),
  2830. * renderTpl: [
  2831. * '<h1 class="title">{title}</h1>',
  2832. * '<p>{desc}</p>'
  2833. * ],
  2834. * renderData: {
  2835. * title: "Error",
  2836. * desc: "Something went wrong"
  2837. * },
  2838. * renderSelectors: {
  2839. * titleEl: 'h1.title',
  2840. * descEl: 'p'
  2841. * },
  2842. * listeners: {
  2843. * afterrender: function(cmp){
  2844. * // After rendering the component will have a titleEl and descEl properties
  2845. * cmp.titleEl.setStyle({color: "red"});
  2846. * }
  2847. * }
  2848. * });
  2849. *
  2850. * For a faster, but less flexible, alternative that achieves the same end result (properties for child elements on the
  2851. * Component after render), see {@link #childEls} and {@link #addChildEls}.
  2852. */
  2853. /**
  2854. * @cfg {Object[]} childEls
  2855. * An array describing the child elements of the Component. Each member of the array
  2856. * is an object with these properties:
  2857. *
  2858. * - `name` - The property name on the Component for the child element.
  2859. * - `itemId` - The id to combine with the Component's id that is the id of the child element.
  2860. * - `id` - The id of the child element.
  2861. *
  2862. * If the array member is a string, it is equivalent to `{ name: m, itemId: m }`.
  2863. *
  2864. * For example, a Component which renders a title and body text:
  2865. *
  2866. * Ext.create('Ext.Component', {
  2867. * renderTo: Ext.getBody(),
  2868. * renderTpl: [
  2869. * '<h1 id="{id}-title">{title}</h1>',
  2870. * '<p>{msg}</p>',
  2871. * ],
  2872. * renderData: {
  2873. * title: "Error",
  2874. * msg: "Something went wrong"
  2875. * },
  2876. * childEls: ["title"],
  2877. * listeners: {
  2878. * afterrender: function(cmp){
  2879. * // After rendering the component will have a title property
  2880. * cmp.title.setStyle({color: "red"});
  2881. * }
  2882. * }
  2883. * });
  2884. *
  2885. * A more flexible, but somewhat slower, approach is {@link #renderSelectors}.
  2886. */
  2887. /**
  2888. * @cfg {String/HTMLElement/Ext.Element} renderTo
  2889. * Specify the id of the element, a DOM element or an existing Element that this component will be rendered into.
  2890. *
  2891. * **Notes:**
  2892. *
  2893. * Do *not* use this option if the Component is to be a child item of a {@link Ext.container.Container Container}.
  2894. * It is the responsibility of the {@link Ext.container.Container Container}'s
  2895. * {@link Ext.container.Container#layout layout manager} to render and manage its child items.
  2896. *
  2897. * When using this config, a call to render() is not required.
  2898. *
  2899. * See `{@link #render}` also.
  2900. */
  2901. /**
  2902. * @cfg {Boolean} frame
  2903. * Specify as `true` to have the Component inject framing elements within the Component at render time to provide a
  2904. * graphical rounded frame around the Component content.
  2905. *
  2906. * This is only necessary when running on outdated, or non standard-compliant browsers such as Microsoft's Internet
  2907. * Explorer prior to version 9 which do not support rounded corners natively.
  2908. *
  2909. * The extra space taken up by this framing is available from the read only property {@link #frameSize}.
  2910. */
  2911. /**
  2912. * @property {Object} frameSize
  2913. * Read-only property indicating the width of any framing elements which were added within the encapsulating element
  2914. * to provide graphical, rounded borders. See the {@link #frame} config.
  2915. *
  2916. * This is an object containing the frame width in pixels for all four sides of the Component containing the
  2917. * following properties:
  2918. *
  2919. * @property {Number} frameSize.top The width of the top framing element in pixels.
  2920. * @property {Number} frameSize.right The width of the right framing element in pixels.
  2921. * @property {Number} frameSize.bottom The width of the bottom framing element in pixels.
  2922. * @property {Number} frameSize.left The width of the left framing element in pixels.
  2923. */
  2924. /**
  2925. * @cfg {String/Object} componentLayout
  2926. * The sizing and positioning of a Component's internal Elements is the responsibility of the Component's layout
  2927. * manager which sizes a Component's internal structure in response to the Component being sized.
  2928. *
  2929. * Generally, developers will not use this configuration as all provided Components which need their internal
  2930. * elements sizing (Such as {@link Ext.form.field.Base input fields}) come with their own componentLayout managers.
  2931. *
  2932. * The {@link Ext.layout.container.Auto default layout manager} will be used on instances of the base Ext.Component
  2933. * class which simply sizes the Component's encapsulating element to the height and width specified in the
  2934. * {@link #setSize} method.
  2935. */
  2936. /**
  2937. * @cfg {Ext.XTemplate/Ext.Template/String/String[]} tpl
  2938. * An {@link Ext.Template}, {@link Ext.XTemplate} or an array of strings to form an Ext.XTemplate. Used in
  2939. * conjunction with the `{@link #data}` and `{@link #tplWriteMode}` configurations.
  2940. */
  2941. /**
  2942. * @cfg {Object} data
  2943. * The initial set of data to apply to the `{@link #tpl}` to update the content area of the Component.
  2944. */
  2945. /**
  2946. * @cfg {String} xtype
  2947. * The `xtype` configuration option can be used to optimize Component creation and rendering. It serves as a
  2948. * shortcut to the full componet name. For example, the component `Ext.button.Button` has an xtype of `button`.
  2949. *
  2950. * You can define your own xtype on a custom {@link Ext.Component component} by specifying the
  2951. * {@link Ext.Class#alias alias} config option with a prefix of `widget`. For example:
  2952. *
  2953. * Ext.define('PressMeButton', {
  2954. * extend: 'Ext.button.Button',
  2955. * alias: 'widget.pressmebutton',
  2956. * text: 'Press Me'
  2957. * })
  2958. *
  2959. * Any Component can be created implicitly as an object config with an xtype specified, allowing it to be
  2960. * declared and passed into the rendering pipeline without actually being instantiated as an object. Not only is
  2961. * rendering deferred, but the actual creation of the object itself is also deferred, saving memory and resources
  2962. * until they are actually needed. In complex, nested layouts containing many Components, this can make a
  2963. * noticeable improvement in performance.
  2964. *
  2965. * // Explicit creation of contained Components:
  2966. * var panel = new Ext.Panel({
  2967. * ...
  2968. * items: [
  2969. * Ext.create('Ext.button.Button', {
  2970. * text: 'OK'
  2971. * })
  2972. * ]
  2973. * };
  2974. *
  2975. * // Implicit creation using xtype:
  2976. * var panel = new Ext.Panel({
  2977. * ...
  2978. * items: [{
  2979. * xtype: 'button',
  2980. * text: 'OK'
  2981. * }]
  2982. * };
  2983. *
  2984. * In the first example, the button will always be created immediately during the panel's initialization. With
  2985. * many added Components, this approach could potentially slow the rendering of the page. In the second example,
  2986. * the button will not be created or rendered until the panel is actually displayed in the browser. If the panel
  2987. * is never displayed (for example, if it is a tab that remains hidden) then the button will never be created and
  2988. * will never consume any resources whatsoever.
  2989. */
  2990. /**
  2991. * @cfg {String} tplWriteMode
  2992. * The Ext.(X)Template method to use when updating the content area of the Component.
  2993. * See `{@link Ext.XTemplate#overwrite}` for information on default mode.
  2994. */
  2995. tplWriteMode: 'overwrite',
  2996. /**
  2997. * @cfg {String} [baseCls='x-component']
  2998. * The base CSS class to apply to this components's element. This will also be prepended to elements within this
  2999. * component like Panel's body will get a class x-panel-body. This means that if you create a subclass of Panel, and
  3000. * you want it to get all the Panels styling for the element and the body, you leave the baseCls x-panel and use
  3001. * componentCls to add specific styling for this component.
  3002. */
  3003. baseCls: Ext.baseCSSPrefix + 'component',
  3004. /**
  3005. * @cfg {String} componentCls
  3006. * CSS Class to be added to a components root level element to give distinction to it via styling.
  3007. */
  3008. /**
  3009. * @cfg {String} [cls='']
  3010. * An optional extra CSS class that will be added to this component's Element. This can be useful
  3011. * for adding customized styles to the component or any of its children using standard CSS rules.
  3012. */
  3013. /**
  3014. * @cfg {String} [overCls='']
  3015. * An optional extra CSS class that will be added to this component's Element when the mouse moves over the Element,
  3016. * and removed when the mouse moves out. This can be useful for adding customized 'active' or 'hover' styles to the
  3017. * component or any of its children using standard CSS rules.
  3018. */
  3019. /**
  3020. * @cfg {String} [disabledCls='x-item-disabled']
  3021. * CSS class to add when the Component is disabled. Defaults to 'x-item-disabled'.
  3022. */
  3023. disabledCls: Ext.baseCSSPrefix + 'item-disabled',
  3024. /**
  3025. * @cfg {String/String[]} ui
  3026. * A set style for a component. Can be a string or an Array of multiple strings (UIs)
  3027. */
  3028. ui: 'default',
  3029. /**
  3030. * @cfg {String[]} uiCls
  3031. * An array of of classNames which are currently applied to this component
  3032. * @private
  3033. */
  3034. uiCls: [],
  3035. /**
  3036. * @cfg {String} style
  3037. * A custom style specification to be applied to this component's Element. Should be a valid argument to
  3038. * {@link Ext.Element#applyStyles}.
  3039. *
  3040. * new Ext.panel.Panel({
  3041. * title: 'Some Title',
  3042. * renderTo: Ext.getBody(),
  3043. * width: 400, height: 300,
  3044. * layout: 'form',
  3045. * items: [{
  3046. * xtype: 'textarea',
  3047. * style: {
  3048. * width: '95%',
  3049. * marginBottom: '10px'
  3050. * }
  3051. * },
  3052. * new Ext.button.Button({
  3053. * text: 'Send',
  3054. * minWidth: '100',
  3055. * style: {
  3056. * marginBottom: '10px'
  3057. * }
  3058. * })
  3059. * ]
  3060. * });
  3061. */
  3062. /**
  3063. * @cfg {Number} width
  3064. * The width of this component in pixels.
  3065. */
  3066. /**
  3067. * @cfg {Number} height
  3068. * The height of this component in pixels.
  3069. */
  3070. /**
  3071. * @cfg {Number/String} border
  3072. * Specifies the border for this component. The border can be a single numeric value to apply to all sides or it can
  3073. * be a CSS style specification for each style, for example: '10 5 3 10'.
  3074. */
  3075. /**
  3076. * @cfg {Number/String} padding
  3077. * Specifies the padding for this component. The padding can be a single numeric value to apply to all sides or it
  3078. * can be a CSS style specification for each style, for example: '10 5 3 10'.
  3079. */
  3080. /**
  3081. * @cfg {Number/String} margin
  3082. * Specifies the margin for this component. The margin can be a single numeric value to apply to all sides or it can
  3083. * be a CSS style specification for each style, for example: '10 5 3 10'.
  3084. */
  3085. /**
  3086. * @cfg {Boolean} hidden
  3087. * True to hide the component.
  3088. */
  3089. hidden: false,
  3090. /**
  3091. * @cfg {Boolean} disabled
  3092. * True to disable the component.
  3093. */
  3094. disabled: false,
  3095. /**
  3096. * @cfg {Boolean} [draggable=false]
  3097. * Allows the component to be dragged.
  3098. */
  3099. /**
  3100. * @property {Boolean} draggable
  3101. * Read-only property indicating whether or not the component can be dragged
  3102. */
  3103. draggable: false,
  3104. /**
  3105. * @cfg {Boolean} floating
  3106. * Create the Component as a floating and use absolute positioning.
  3107. *
  3108. * The z-index of floating Components is handled by a ZIndexManager. If you simply render a floating Component into the DOM, it will be managed
  3109. * by the global {@link Ext.WindowManager WindowManager}.
  3110. *
  3111. * If you include a floating Component as a child item of a Container, then upon render, ExtJS will seek an ancestor floating Component to house a new
  3112. * ZIndexManager instance to manage its descendant floaters. If no floating ancestor can be found, the global WindowManager will be used.
  3113. *
  3114. * When a floating Component which has a ZindexManager managing descendant floaters is destroyed, those descendant floaters will also be destroyed.
  3115. */
  3116. floating: false,
  3117. /**
  3118. * @cfg {String} hideMode
  3119. * A String which specifies how this Component's encapsulating DOM element will be hidden. Values may be:
  3120. *
  3121. * - `'display'` : The Component will be hidden using the `display: none` style.
  3122. * - `'visibility'` : The Component will be hidden using the `visibility: hidden` style.
  3123. * - `'offsets'` : The Component will be hidden by absolutely positioning it out of the visible area of the document.
  3124. * This is useful when a hidden Component must maintain measurable dimensions. Hiding using `display` results in a
  3125. * Component having zero dimensions.
  3126. */
  3127. hideMode: 'display',
  3128. /**
  3129. * @cfg {String} contentEl
  3130. * Specify an existing HTML element, or the `id` of an existing HTML element to use as the content for this component.
  3131. *
  3132. * This config option is used to take an existing HTML element and place it in the layout element of a new component
  3133. * (it simply moves the specified DOM element _after the Component is rendered_ to use as the content.
  3134. *
  3135. * **Notes:**
  3136. *
  3137. * The specified HTML element is appended to the layout element of the component _after any configured
  3138. * {@link #html HTML} has been inserted_, and so the document will not contain this element at the time
  3139. * the {@link #render} event is fired.
  3140. *
  3141. * The specified HTML element used will not participate in any **`{@link Ext.container.Container#layout layout}`**
  3142. * scheme that the Component may use. It is just HTML. Layouts operate on child
  3143. * **`{@link Ext.container.Container#items items}`**.
  3144. *
  3145. * Add either the `x-hidden` or the `x-hide-display` CSS class to prevent a brief flicker of the content before it
  3146. * is rendered to the panel.
  3147. */
  3148. /**
  3149. * @cfg {String/Object} [html='']
  3150. * An HTML fragment, or a {@link Ext.DomHelper DomHelper} specification to use as the layout element content.
  3151. * The HTML content is added after the component is rendered, so the document will not contain this HTML at the time
  3152. * the {@link #render} event is fired. This content is inserted into the body _before_ any configured {@link #contentEl}
  3153. * is appended.
  3154. */
  3155. /**
  3156. * @cfg {Boolean} styleHtmlContent
  3157. * True to automatically style the html inside the content target of this component (body for panels).
  3158. */
  3159. styleHtmlContent: false,
  3160. /**
  3161. * @cfg {String} [styleHtmlCls='x-html']
  3162. * The class that is added to the content target when you set styleHtmlContent to true.
  3163. */
  3164. styleHtmlCls: Ext.baseCSSPrefix + 'html',
  3165. /**
  3166. * @cfg {Number} minHeight
  3167. * The minimum value in pixels which this Component will set its height to.
  3168. *
  3169. * **Warning:** This will override any size management applied by layout managers.
  3170. */
  3171. /**
  3172. * @cfg {Number} minWidth
  3173. * The minimum value in pixels which this Component will set its width to.
  3174. *
  3175. * **Warning:** This will override any size management applied by layout managers.
  3176. */
  3177. /**
  3178. * @cfg {Number} maxHeight
  3179. * The maximum value in pixels which this Component will set its height to.
  3180. *
  3181. * **Warning:** This will override any size management applied by layout managers.
  3182. */
  3183. /**
  3184. * @cfg {Number} maxWidth
  3185. * The maximum value in pixels which this Component will set its width to.
  3186. *
  3187. * **Warning:** This will override any size management applied by layout managers.
  3188. */
  3189. /**
  3190. * @cfg {Ext.ComponentLoader/Object} loader
  3191. * A configuration object or an instance of a {@link Ext.ComponentLoader} to load remote content for this Component.
  3192. */
  3193. /**
  3194. * @cfg {Boolean} autoShow
  3195. * True to automatically show the component upon creation. This config option may only be used for
  3196. * {@link #floating} components or components that use {@link #autoRender}. Defaults to false.
  3197. */
  3198. autoShow: false,
  3199. /**
  3200. * @cfg {Boolean/String/HTMLElement/Ext.Element} autoRender
  3201. * This config is intended mainly for non-{@link #floating} Components which may or may not be shown. Instead of using
  3202. * {@link #renderTo} in the configuration, and rendering upon construction, this allows a Component to render itself
  3203. * upon first _{@link #show}_. If {@link #floating} is true, the value of this config is omited as if it is `true`.
  3204. *
  3205. * Specify as `true` to have this Component render to the document body upon first show.
  3206. *
  3207. * Specify as an element, or the ID of an element to have this Component render to a specific element upon first
  3208. * show.
  3209. *
  3210. * **This defaults to `true` for the {@link Ext.window.Window Window} class.**
  3211. */
  3212. autoRender: false,
  3213. needsLayout: false,
  3214. // @private
  3215. allowDomMove: true,
  3216. /**
  3217. * @cfg {Object/Object[]} plugins
  3218. * An object or array of objects that will provide custom functionality for this component. The only requirement for
  3219. * a valid plugin is that it contain an init method that accepts a reference of type Ext.Component. When a component
  3220. * is created, if any plugins are available, the component will call the init method on each plugin, passing a
  3221. * reference to itself. Each plugin can then call methods or respond to events on the component as needed to provide
  3222. * its functionality.
  3223. */
  3224. /**
  3225. * @property {Boolean} rendered
  3226. * Read-only property indicating whether or not the component has been rendered.
  3227. */
  3228. rendered: false,
  3229. /**
  3230. * @property {Number} componentLayoutCounter
  3231. * @private
  3232. * The number of component layout calls made on this object.
  3233. */
  3234. componentLayoutCounter: 0,
  3235. weight: 0,
  3236. trimRe: /^\s+|\s+$/g,
  3237. spacesRe: /\s+/,
  3238. /**
  3239. * @property {Boolean} maskOnDisable
  3240. * This is an internal flag that you use when creating custom components. By default this is set to true which means
  3241. * that every component gets a mask when its disabled. Components like FieldContainer, FieldSet, Field, Button, Tab
  3242. * override this property to false since they want to implement custom disable logic.
  3243. */
  3244. maskOnDisable: true,
  3245. /**
  3246. * Creates new Component.
  3247. * @param {Object} config (optional) Config object.
  3248. */
  3249. constructor : function(config) {
  3250. var me = this,
  3251. i, len;
  3252. config = config || {};
  3253. me.initialConfig = config;
  3254. Ext.apply(me, config);
  3255. me.addEvents(
  3256. /**
  3257. * @event beforeactivate
  3258. * Fires before a Component has been visually activated. Returning false from an event listener can prevent
  3259. * the activate from occurring.
  3260. * @param {Ext.Component} this
  3261. */
  3262. 'beforeactivate',
  3263. /**
  3264. * @event activate
  3265. * Fires after a Component has been visually activated.
  3266. * @param {Ext.Component} this
  3267. */
  3268. 'activate',
  3269. /**
  3270. * @event beforedeactivate
  3271. * Fires before a Component has been visually deactivated. Returning false from an event listener can
  3272. * prevent the deactivate from occurring.
  3273. * @param {Ext.Component} this
  3274. */
  3275. 'beforedeactivate',
  3276. /**
  3277. * @event deactivate
  3278. * Fires after a Component has been visually deactivated.
  3279. * @param {Ext.Component} this
  3280. */
  3281. 'deactivate',
  3282. /**
  3283. * @event added
  3284. * Fires after a Component had been added to a Container.
  3285. * @param {Ext.Component} this
  3286. * @param {Ext.container.Container} container Parent Container
  3287. * @param {Number} pos position of Component
  3288. */
  3289. 'added',
  3290. /**
  3291. * @event disable
  3292. * Fires after the component is disabled.
  3293. * @param {Ext.Component} this
  3294. */
  3295. 'disable',
  3296. /**
  3297. * @event enable
  3298. * Fires after the component is enabled.
  3299. * @param {Ext.Component} this
  3300. */
  3301. 'enable',
  3302. /**
  3303. * @event beforeshow
  3304. * Fires before the component is shown when calling the {@link #show} method. Return false from an event
  3305. * handler to stop the show.
  3306. * @param {Ext.Component} this
  3307. */
  3308. 'beforeshow',
  3309. /**
  3310. * @event show
  3311. * Fires after the component is shown when calling the {@link #show} method.
  3312. * @param {Ext.Component} this
  3313. */
  3314. 'show',
  3315. /**
  3316. * @event beforehide
  3317. * Fires before the component is hidden when calling the {@link #hide} method. Return false from an event
  3318. * handler to stop the hide.
  3319. * @param {Ext.Component} this
  3320. */
  3321. 'beforehide',
  3322. /**
  3323. * @event hide
  3324. * Fires after the component is hidden. Fires after the component is hidden when calling the {@link #hide}
  3325. * method.
  3326. * @param {Ext.Component} this
  3327. */
  3328. 'hide',
  3329. /**
  3330. * @event removed
  3331. * Fires when a component is removed from an Ext.container.Container
  3332. * @param {Ext.Component} this
  3333. * @param {Ext.container.Container} ownerCt Container which holds the component
  3334. */
  3335. 'removed',
  3336. /**
  3337. * @event beforerender
  3338. * Fires before the component is {@link #rendered}. Return false from an event handler to stop the
  3339. * {@link #render}.
  3340. * @param {Ext.Component} this
  3341. */
  3342. 'beforerender',
  3343. /**
  3344. * @event render
  3345. * Fires after the component markup is {@link #rendered}.
  3346. * @param {Ext.Component} this
  3347. */
  3348. 'render',
  3349. /**
  3350. * @event afterrender
  3351. * Fires after the component rendering is finished.
  3352. *
  3353. * The afterrender event is fired after this Component has been {@link #rendered}, been postprocesed by any
  3354. * afterRender method defined for the Component.
  3355. * @param {Ext.Component} this
  3356. */
  3357. 'afterrender',
  3358. /**
  3359. * @event beforedestroy
  3360. * Fires before the component is {@link #destroy}ed. Return false from an event handler to stop the
  3361. * {@link #destroy}.
  3362. * @param {Ext.Component} this
  3363. */
  3364. 'beforedestroy',
  3365. /**
  3366. * @event destroy
  3367. * Fires after the component is {@link #destroy}ed.
  3368. * @param {Ext.Component} this
  3369. */
  3370. 'destroy',
  3371. /**
  3372. * @event resize
  3373. * Fires after the component is resized.
  3374. * @param {Ext.Component} this
  3375. * @param {Number} adjWidth The box-adjusted width that was set
  3376. * @param {Number} adjHeight The box-adjusted height that was set
  3377. */
  3378. 'resize',
  3379. /**
  3380. * @event move
  3381. * Fires after the component is moved.
  3382. * @param {Ext.Component} this
  3383. * @param {Number} x The new x position
  3384. * @param {Number} y The new y position
  3385. */
  3386. 'move'
  3387. );
  3388. me.getId();
  3389. me.mons = [];
  3390. me.additionalCls = [];
  3391. me.renderData = me.renderData || {};
  3392. me.renderSelectors = me.renderSelectors || {};
  3393. if (me.plugins) {
  3394. me.plugins = [].concat(me.plugins);
  3395. me.constructPlugins();
  3396. }
  3397. me.initComponent();
  3398. // ititComponent gets a chance to change the id property before registering
  3399. Ext.ComponentManager.register(me);
  3400. // Dont pass the config so that it is not applied to 'this' again
  3401. me.mixins.observable.constructor.call(me);
  3402. me.mixins.state.constructor.call(me, config);
  3403. // Save state on resize.
  3404. this.addStateEvents('resize');
  3405. // Move this into Observable?
  3406. if (me.plugins) {
  3407. me.plugins = [].concat(me.plugins);
  3408. for (i = 0, len = me.plugins.length; i < len; i++) {
  3409. me.plugins[i] = me.initPlugin(me.plugins[i]);
  3410. }
  3411. }
  3412. me.loader = me.getLoader();
  3413. if (me.renderTo) {
  3414. me.render(me.renderTo);
  3415. // EXTJSIV-1935 - should be a way to do afterShow or something, but that
  3416. // won't work. Likewise, rendering hidden and then showing (w/autoShow) has
  3417. // implications to afterRender so we cannot do that.
  3418. }
  3419. if (me.autoShow) {
  3420. me.show();
  3421. }
  3422. //<debug>
  3423. if (Ext.isDefined(me.disabledClass)) {
  3424. if (Ext.isDefined(Ext.global.console)) {
  3425. Ext.global.console.warn('Ext.Component: disabledClass has been deprecated. Please use disabledCls.');
  3426. }
  3427. me.disabledCls = me.disabledClass;
  3428. delete me.disabledClass;
  3429. }
  3430. //</debug>
  3431. },
  3432. initComponent: function () {
  3433. // This is called again here to allow derived classes to add plugin configs to the
  3434. // plugins array before calling down to this, the base initComponent.
  3435. this.constructPlugins();
  3436. },
  3437. /**
  3438. * The supplied default state gathering method for the AbstractComponent class.
  3439. *
  3440. * This method returns dimension settings such as `flex`, `anchor`, `width` and `height` along with `collapsed`
  3441. * state.
  3442. *
  3443. * Subclasses which implement more complex state should call the superclass's implementation, and apply their state
  3444. * to the result if this basic state is to be saved.
  3445. *
  3446. * Note that Component state will only be saved if the Component has a {@link #stateId} and there as a StateProvider
  3447. * configured for the document.
  3448. *
  3449. * @return {Object}
  3450. */
  3451. getState: function() {
  3452. var me = this,
  3453. layout = me.ownerCt ? (me.shadowOwnerCt || me.ownerCt).getLayout() : null,
  3454. state = {
  3455. collapsed: me.collapsed
  3456. },
  3457. width = me.width,
  3458. height = me.height,
  3459. cm = me.collapseMemento,
  3460. anchors;
  3461. // If a Panel-local collapse has taken place, use remembered values as the dimensions.
  3462. // TODO: remove this coupling with Panel's privates! All collapse/expand logic should be refactored into one place.
  3463. if (me.collapsed && cm) {
  3464. if (Ext.isDefined(cm.data.width)) {
  3465. width = cm.width;
  3466. }
  3467. if (Ext.isDefined(cm.data.height)) {
  3468. height = cm.height;
  3469. }
  3470. }
  3471. // If we have flex, only store the perpendicular dimension.
  3472. if (layout && me.flex) {
  3473. state.flex = me.flex;
  3474. if (layout.perpendicularPrefix) {
  3475. state[layout.perpendicularPrefix] = me['get' + layout.perpendicularPrefixCap]();
  3476. } else {
  3477. //<debug>
  3478. if (Ext.isDefined(Ext.global.console)) {
  3479. Ext.global.console.warn('Ext.Component: Specified a flex value on a component not inside a Box layout');
  3480. }
  3481. //</debug>
  3482. }
  3483. }
  3484. // If we have anchor, only store dimensions which are *not* being anchored
  3485. else if (layout && me.anchor) {
  3486. state.anchor = me.anchor;
  3487. anchors = me.anchor.split(' ').concat(null);
  3488. if (!anchors[0]) {
  3489. if (me.width) {
  3490. state.width = width;
  3491. }
  3492. }
  3493. if (!anchors[1]) {
  3494. if (me.height) {
  3495. state.height = height;
  3496. }
  3497. }
  3498. }
  3499. // Store dimensions.
  3500. else {
  3501. if (me.width) {
  3502. state.width = width;
  3503. }
  3504. if (me.height) {
  3505. state.height = height;
  3506. }
  3507. }
  3508. // Don't save dimensions if they are unchanged from the original configuration.
  3509. if (state.width == me.initialConfig.width) {
  3510. delete state.width;
  3511. }
  3512. if (state.height == me.initialConfig.height) {
  3513. delete state.height;
  3514. }
  3515. // If a Box layout was managing the perpendicular dimension, don't save that dimension
  3516. if (layout && layout.align && (layout.align.indexOf('stretch') !== -1)) {
  3517. delete state[layout.perpendicularPrefix];
  3518. }
  3519. return state;
  3520. },
  3521. show: Ext.emptyFn,
  3522. animate: function(animObj) {
  3523. var me = this,
  3524. to;
  3525. animObj = animObj || {};
  3526. to = animObj.to || {};
  3527. if (Ext.fx.Manager.hasFxBlock(me.id)) {
  3528. return me;
  3529. }
  3530. // Special processing for animating Component dimensions.
  3531. if (!animObj.dynamic && (to.height || to.width)) {
  3532. var curWidth = me.getWidth(),
  3533. w = curWidth,
  3534. curHeight = me.getHeight(),
  3535. h = curHeight,
  3536. needsResize = false;
  3537. if (to.height && to.height > curHeight) {
  3538. h = to.height;
  3539. needsResize = true;
  3540. }
  3541. if (to.width && to.width > curWidth) {
  3542. w = to.width;
  3543. needsResize = true;
  3544. }
  3545. // If any dimensions are being increased, we must resize the internal structure
  3546. // of the Component, but then clip it by sizing its encapsulating element back to original dimensions.
  3547. // The animation will then progressively reveal the larger content.
  3548. if (needsResize) {
  3549. var clearWidth = !Ext.isNumber(me.width),
  3550. clearHeight = !Ext.isNumber(me.height);
  3551. me.componentLayout.childrenChanged = true;
  3552. me.setSize(w, h, me.ownerCt);
  3553. me.el.setSize(curWidth, curHeight);
  3554. if (clearWidth) {
  3555. delete me.width;
  3556. }
  3557. if (clearHeight) {
  3558. delete me.height;
  3559. }
  3560. }
  3561. }
  3562. return me.mixins.animate.animate.apply(me, arguments);
  3563. },
  3564. /**
  3565. * This method finds the topmost active layout who's processing will eventually determine the size and position of
  3566. * this Component.
  3567. *
  3568. * This method is useful when dynamically adding Components into Containers, and some processing must take place
  3569. * after the final sizing and positioning of the Component has been performed.
  3570. *
  3571. * @return {Ext.Component}
  3572. */
  3573. findLayoutController: function() {
  3574. return this.findParentBy(function(c) {
  3575. // Return true if we are at the root of the Container tree
  3576. // or this Container's layout is busy but the next one up is not.
  3577. return !c.ownerCt || (c.layout.layoutBusy && !c.ownerCt.layout.layoutBusy);
  3578. });
  3579. },
  3580. onShow : function() {
  3581. // Layout if needed
  3582. var needsLayout = this.needsLayout;
  3583. if (Ext.isObject(needsLayout)) {
  3584. this.doComponentLayout(needsLayout.width, needsLayout.height, needsLayout.isSetSize, needsLayout.ownerCt);
  3585. }
  3586. },
  3587. constructPlugin: function(plugin) {
  3588. if (plugin.ptype && typeof plugin.init != 'function') {
  3589. plugin.cmp = this;
  3590. plugin = Ext.PluginManager.create(plugin);
  3591. }
  3592. else if (typeof plugin == 'string') {
  3593. plugin = Ext.PluginManager.create({
  3594. ptype: plugin,
  3595. cmp: this
  3596. });
  3597. }
  3598. return plugin;
  3599. },
  3600. /**
  3601. * Ensures that the plugins array contains fully constructed plugin instances. This converts any configs into their
  3602. * appropriate instances.
  3603. */
  3604. constructPlugins: function() {
  3605. var me = this,
  3606. plugins = me.plugins,
  3607. i, len;
  3608. if (plugins) {
  3609. for (i = 0, len = plugins.length; i < len; i++) {
  3610. // this just returns already-constructed plugin instances...
  3611. plugins[i] = me.constructPlugin(plugins[i]);
  3612. }
  3613. }
  3614. },
  3615. // @private
  3616. initPlugin : function(plugin) {
  3617. plugin.init(this);
  3618. return plugin;
  3619. },
  3620. /**
  3621. * Handles autoRender. Floating Components may have an ownerCt. If they are asking to be constrained, constrain them
  3622. * within that ownerCt, and have their z-index managed locally. Floating Components are always rendered to
  3623. * document.body
  3624. */
  3625. doAutoRender: function() {
  3626. var me = this;
  3627. if (me.floating) {
  3628. me.render(document.body);
  3629. } else {
  3630. me.render(Ext.isBoolean(me.autoRender) ? Ext.getBody() : me.autoRender);
  3631. }
  3632. },
  3633. // @private
  3634. render : function(container, position) {
  3635. var me = this;
  3636. if (!me.rendered && me.fireEvent('beforerender', me) !== false) {
  3637. // Flag set during the render process.
  3638. // It can be used to inhibit event-driven layout calls during the render phase
  3639. me.rendering = true;
  3640. // If this.el is defined, we want to make sure we are dealing with
  3641. // an Ext Element.
  3642. if (me.el) {
  3643. me.el = Ext.get(me.el);
  3644. }
  3645. // Perform render-time processing for floating Components
  3646. if (me.floating) {
  3647. me.onFloatRender();
  3648. }
  3649. container = me.initContainer(container);
  3650. me.onRender(container, position);
  3651. // Tell the encapsulating element to hide itself in the way the Component is configured to hide
  3652. // This means DISPLAY, VISIBILITY or OFFSETS.
  3653. me.el.setVisibilityMode(Ext.Element[me.hideMode.toUpperCase()]);
  3654. if (me.overCls) {
  3655. me.el.hover(me.addOverCls, me.removeOverCls, me);
  3656. }
  3657. me.fireEvent('render', me);
  3658. me.initContent();
  3659. me.afterRender(container);
  3660. me.fireEvent('afterrender', me);
  3661. me.initEvents();
  3662. if (me.hidden) {
  3663. // Hiding during the render process should not perform any ancillary
  3664. // actions that the full hide process does; It is not hiding, it begins in a hidden state.'
  3665. // So just make the element hidden according to the configured hideMode
  3666. me.el.hide();
  3667. }
  3668. if (me.disabled) {
  3669. // pass silent so the event doesn't fire the first time.
  3670. me.disable(true);
  3671. }
  3672. // Delete the flag once the rendering is done.
  3673. delete me.rendering;
  3674. }
  3675. return me;
  3676. },
  3677. // @private
  3678. onRender : function(container, position) {
  3679. var me = this,
  3680. el = me.el,
  3681. styles = me.initStyles(),
  3682. renderTpl, renderData, i;
  3683. position = me.getInsertPosition(position);
  3684. if (!el) {
  3685. if (position) {
  3686. el = Ext.DomHelper.insertBefore(position, me.getElConfig(), true);
  3687. }
  3688. else {
  3689. el = Ext.DomHelper.append(container, me.getElConfig(), true);
  3690. }
  3691. }
  3692. else if (me.allowDomMove !== false) {
  3693. if (position) {
  3694. container.dom.insertBefore(el.dom, position);
  3695. } else {
  3696. container.dom.appendChild(el.dom);
  3697. }
  3698. }
  3699. if (Ext.scopeResetCSS && !me.ownerCt) {
  3700. // If this component's el is the body element, we add the reset class to the html tag
  3701. if (el.dom == Ext.getBody().dom) {
  3702. el.parent().addCls(Ext.baseCSSPrefix + 'reset');
  3703. }
  3704. else {
  3705. // Else we wrap this element in an element that adds the reset class.
  3706. me.resetEl = el.wrap({
  3707. cls: Ext.baseCSSPrefix + 'reset'
  3708. });
  3709. }
  3710. }
  3711. me.setUI(me.ui);
  3712. el.addCls(me.initCls());
  3713. el.setStyle(styles);
  3714. // Here we check if the component has a height set through style or css.
  3715. // If it does then we set the this.height to that value and it won't be
  3716. // considered an auto height component
  3717. // if (this.height === undefined) {
  3718. // var height = el.getHeight();
  3719. // // This hopefully means that the panel has an explicit height set in style or css
  3720. // if (height - el.getPadding('tb') - el.getBorderWidth('tb') > 0) {
  3721. // this.height = height;
  3722. // }
  3723. // }
  3724. me.el = el;
  3725. me.initFrame();
  3726. renderTpl = me.initRenderTpl();
  3727. if (renderTpl) {
  3728. renderData = me.initRenderData();
  3729. renderTpl.append(me.getTargetEl(), renderData);
  3730. }
  3731. me.applyRenderSelectors();
  3732. me.rendered = true;
  3733. },
  3734. // @private
  3735. afterRender : function() {
  3736. var me = this,
  3737. pos,
  3738. xy;
  3739. me.getComponentLayout();
  3740. // Set the size if a size is configured, or if this is the outermost Container.
  3741. // Also, if this is a collapsed Panel, it needs an initial component layout
  3742. // to lay out its header so that it can have a height determined.
  3743. if (me.collapsed || (!me.ownerCt || (me.height || me.width))) {
  3744. me.setSize(me.width, me.height);
  3745. } else {
  3746. // It is expected that child items be rendered before this method returns and
  3747. // the afterrender event fires. Since we aren't going to do the layout now, we
  3748. // must render the child items. This is handled implicitly above in the layout
  3749. // caused by setSize.
  3750. me.renderChildren();
  3751. }
  3752. // For floaters, calculate x and y if they aren't defined by aligning
  3753. // the sized element to the center of either the container or the ownerCt
  3754. if (me.floating && (me.x === undefined || me.y === undefined)) {
  3755. if (me.floatParent) {
  3756. xy = me.el.getAlignToXY(me.floatParent.getTargetEl(), 'c-c');
  3757. pos = me.floatParent.getTargetEl().translatePoints(xy[0], xy[1]);
  3758. } else {
  3759. xy = me.el.getAlignToXY(me.container, 'c-c');
  3760. pos = me.container.translatePoints(xy[0], xy[1]);
  3761. }
  3762. me.x = me.x === undefined ? pos.left: me.x;
  3763. me.y = me.y === undefined ? pos.top: me.y;
  3764. }
  3765. if (Ext.isDefined(me.x) || Ext.isDefined(me.y)) {
  3766. me.setPosition(me.x, me.y);
  3767. }
  3768. if (me.styleHtmlContent) {
  3769. me.getTargetEl().addCls(me.styleHtmlCls);
  3770. }
  3771. },
  3772. /**
  3773. * @private
  3774. * Called by Component#doAutoRender
  3775. *
  3776. * Register a Container configured `floating: true` with this Component's {@link Ext.ZIndexManager ZIndexManager}.
  3777. *
  3778. * Components added in ths way will not participate in any layout, but will be rendered
  3779. * upon first show in the way that {@link Ext.window.Window Window}s are.
  3780. */
  3781. registerFloatingItem: function(cmp) {
  3782. var me = this;
  3783. if (!me.floatingItems) {
  3784. me.floatingItems = Ext.create('Ext.ZIndexManager', me);
  3785. }
  3786. me.floatingItems.register(cmp);
  3787. },
  3788. renderChildren: function () {
  3789. var me = this,
  3790. layout = me.getComponentLayout();
  3791. me.suspendLayout = true;
  3792. layout.renderChildren();
  3793. delete me.suspendLayout;
  3794. },
  3795. frameCls: Ext.baseCSSPrefix + 'frame',
  3796. frameIdRegex: /[-]frame\d+[TMB][LCR]$/,
  3797. frameElementCls: {
  3798. tl: [],
  3799. tc: [],
  3800. tr: [],
  3801. ml: [],
  3802. mc: [],
  3803. mr: [],
  3804. bl: [],
  3805. bc: [],
  3806. br: []
  3807. },
  3808. frameTpl: [
  3809. '<tpl if="top">',
  3810. '<tpl if="left"><div id="{fgid}TL" class="{frameCls}-tl {baseCls}-tl {baseCls}-{ui}-tl<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tl</tpl></tpl>" style="background-position: {tl}; padding-left: {frameWidth}px" role="presentation"></tpl>',
  3811. '<tpl if="right"><div id="{fgid}TR" class="{frameCls}-tr {baseCls}-tr {baseCls}-{ui}-tr<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tr</tpl></tpl>" style="background-position: {tr}; padding-right: {frameWidth}px" role="presentation"></tpl>',
  3812. '<div id="{fgid}TC" class="{frameCls}-tc {baseCls}-tc {baseCls}-{ui}-tc<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tc</tpl></tpl>" style="background-position: {tc}; height: {frameWidth}px" role="presentation"></div>',
  3813. '<tpl if="right"></div></tpl>',
  3814. '<tpl if="left"></div></tpl>',
  3815. '</tpl>',
  3816. '<tpl if="left"><div id="{fgid}ML" class="{frameCls}-ml {baseCls}-ml {baseCls}-{ui}-ml<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-ml</tpl></tpl>" style="background-position: {ml}; padding-left: {frameWidth}px" role="presentation"></tpl>',
  3817. '<tpl if="right"><div id="{fgid}MR" class="{frameCls}-mr {baseCls}-mr {baseCls}-{ui}-mr<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mr</tpl></tpl>" style="background-position: {mr}; padding-right: {frameWidth}px" role="presentation"></tpl>',
  3818. '<div id="{fgid}MC" class="{frameCls}-mc {baseCls}-mc {baseCls}-{ui}-mc<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mc</tpl></tpl>" role="presentation"></div>',
  3819. '<tpl if="right"></div></tpl>',
  3820. '<tpl if="left"></div></tpl>',
  3821. '<tpl if="bottom">',
  3822. '<tpl if="left"><div id="{fgid}BL" class="{frameCls}-bl {baseCls}-bl {baseCls}-{ui}-bl<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bl</tpl></tpl>" style="background-position: {bl}; padding-left: {frameWidth}px" role="presentation"></tpl>',
  3823. '<tpl if="right"><div id="{fgid}BR" class="{frameCls}-br {baseCls}-br {baseCls}-{ui}-br<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-br</tpl></tpl>" style="background-position: {br}; padding-right: {frameWidth}px" role="presentation"></tpl>',
  3824. '<div id="{fgid}BC" class="{frameCls}-bc {baseCls}-bc {baseCls}-{ui}-bc<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bc</tpl></tpl>" style="background-position: {bc}; height: {frameWidth}px" role="presentation"></div>',
  3825. '<tpl if="right"></div></tpl>',
  3826. '<tpl if="left"></div></tpl>',
  3827. '</tpl>'
  3828. ],
  3829. frameTableTpl: [
  3830. '<table><tbody>',
  3831. '<tpl if="top">',
  3832. '<tr>',
  3833. '<tpl if="left"><td id="{fgid}TL" class="{frameCls}-tl {baseCls}-tl {baseCls}-{ui}-tl<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tl</tpl></tpl>" style="background-position: {tl}; padding-left:{frameWidth}px" role="presentation"></td></tpl>',
  3834. '<td id="{fgid}TC" class="{frameCls}-tc {baseCls}-tc {baseCls}-{ui}-tc<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tc</tpl></tpl>" style="background-position: {tc}; height: {frameWidth}px" role="presentation"></td>',
  3835. '<tpl if="right"><td id="{fgid}TR" class="{frameCls}-tr {baseCls}-tr {baseCls}-{ui}-tr<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tr</tpl></tpl>" style="background-position: {tr}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
  3836. '</tr>',
  3837. '</tpl>',
  3838. '<tr>',
  3839. '<tpl if="left"><td id="{fgid}ML" class="{frameCls}-ml {baseCls}-ml {baseCls}-{ui}-ml<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-ml</tpl></tpl>" style="background-position: {ml}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
  3840. '<td id="{fgid}MC" class="{frameCls}-mc {baseCls}-mc {baseCls}-{ui}-mc<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mc</tpl></tpl>" style="background-position: 0 0;" role="presentation"></td>',
  3841. '<tpl if="right"><td id="{fgid}MR" class="{frameCls}-mr {baseCls}-mr {baseCls}-{ui}-mr<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mr</tpl></tpl>" style="background-position: {mr}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
  3842. '</tr>',
  3843. '<tpl if="bottom">',
  3844. '<tr>',
  3845. '<tpl if="left"><td id="{fgid}BL" class="{frameCls}-bl {baseCls}-bl {baseCls}-{ui}-bl<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bl</tpl></tpl>" style="background-position: {bl}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
  3846. '<td id="{fgid}BC" class="{frameCls}-bc {baseCls}-bc {baseCls}-{ui}-bc<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bc</tpl></tpl>" style="background-position: {bc}; height: {frameWidth}px" role="presentation"></td>',
  3847. '<tpl if="right"><td id="{fgid}BR" class="{frameCls}-br {baseCls}-br {baseCls}-{ui}-br<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-br</tpl></tpl>" style="background-position: {br}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
  3848. '</tr>',
  3849. '</tpl>',
  3850. '</tbody></table>'
  3851. ],
  3852. /**
  3853. * @private
  3854. */
  3855. initFrame : function() {
  3856. if (Ext.supports.CSS3BorderRadius) {
  3857. return false;
  3858. }
  3859. var me = this,
  3860. frameInfo = me.getFrameInfo(),
  3861. frameWidth = frameInfo.width,
  3862. frameTpl = me.getFrameTpl(frameInfo.table),
  3863. frameGenId;
  3864. if (me.frame) {
  3865. // since we render id's into the markup and id's NEED to be unique, we have a
  3866. // simple strategy for numbering their generations.
  3867. me.frameGenId = frameGenId = (me.frameGenId || 0) + 1;
  3868. frameGenId = me.id + '-frame' + frameGenId;
  3869. // Here we render the frameTpl to this component. This inserts the 9point div or the table framing.
  3870. frameTpl.insertFirst(me.el, Ext.apply({}, {
  3871. fgid: frameGenId,
  3872. ui: me.ui,
  3873. uiCls: me.uiCls,
  3874. frameCls: me.frameCls,
  3875. baseCls: me.baseCls,
  3876. frameWidth: frameWidth,
  3877. top: !!frameInfo.top,
  3878. left: !!frameInfo.left,
  3879. right: !!frameInfo.right,
  3880. bottom: !!frameInfo.bottom
  3881. }, me.getFramePositions(frameInfo)));
  3882. // The frameBody is returned in getTargetEl, so that layouts render items to the correct target.=
  3883. me.frameBody = me.el.down('.' + me.frameCls + '-mc');
  3884. // Clean out the childEls for the old frame elements (the majority of the els)
  3885. me.removeChildEls(function (c) {
  3886. return c.id && me.frameIdRegex.test(c.id);
  3887. });
  3888. // Add the childEls for each of the new frame elements
  3889. Ext.each(['TL','TC','TR','ML','MC','MR','BL','BC','BR'], function (suffix) {
  3890. me.childEls.push({ name: 'frame' + suffix, id: frameGenId + suffix });
  3891. });
  3892. }
  3893. },
  3894. updateFrame: function() {
  3895. if (Ext.supports.CSS3BorderRadius) {
  3896. return false;
  3897. }
  3898. var me = this,
  3899. wasTable = this.frameSize && this.frameSize.table,
  3900. oldFrameTL = this.frameTL,
  3901. oldFrameBL = this.frameBL,
  3902. oldFrameML = this.frameML,
  3903. oldFrameMC = this.frameMC,
  3904. newMCClassName;
  3905. this.initFrame();
  3906. if (oldFrameMC) {
  3907. if (me.frame) {
  3908. // Reapply render selectors
  3909. delete me.frameTL;
  3910. delete me.frameTC;
  3911. delete me.frameTR;
  3912. delete me.frameML;
  3913. delete me.frameMC;
  3914. delete me.frameMR;
  3915. delete me.frameBL;
  3916. delete me.frameBC;
  3917. delete me.frameBR;
  3918. this.applyRenderSelectors();
  3919. // Store the class names set on the new mc
  3920. newMCClassName = this.frameMC.dom.className;
  3921. // Replace the new mc with the old mc
  3922. oldFrameMC.insertAfter(this.frameMC);
  3923. this.frameMC.remove();
  3924. // Restore the reference to the old frame mc as the framebody
  3925. this.frameBody = this.frameMC = oldFrameMC;
  3926. // Apply the new mc classes to the old mc element
  3927. oldFrameMC.dom.className = newMCClassName;
  3928. // Remove the old framing
  3929. if (wasTable) {
  3930. me.el.query('> table')[1].remove();
  3931. }
  3932. else {
  3933. if (oldFrameTL) {
  3934. oldFrameTL.remove();
  3935. }
  3936. if (oldFrameBL) {
  3937. oldFrameBL.remove();
  3938. }
  3939. oldFrameML.remove();
  3940. }
  3941. }
  3942. else {
  3943. // We were framed but not anymore. Move all content from the old frame to the body
  3944. }
  3945. }
  3946. else if (me.frame) {
  3947. this.applyRenderSelectors();
  3948. }
  3949. },
  3950. getFrameInfo: function() {
  3951. if (Ext.supports.CSS3BorderRadius) {
  3952. return false;
  3953. }
  3954. var me = this,
  3955. left = me.el.getStyle('background-position-x'),
  3956. top = me.el.getStyle('background-position-y'),
  3957. info, frameInfo = false, max;
  3958. // Some browsers dont support background-position-x and y, so for those
  3959. // browsers let's split background-position into two parts.
  3960. if (!left && !top) {
  3961. info = me.el.getStyle('background-position').split(' ');
  3962. left = info[0];
  3963. top = info[1];
  3964. }
  3965. // We actually pass a string in the form of '[type][tl][tr]px [type][br][bl]px' as
  3966. // the background position of this.el from the css to indicate to IE that this component needs
  3967. // framing. We parse it here and change the markup accordingly.
  3968. if (parseInt(left, 10) >= 1000000 && parseInt(top, 10) >= 1000000) {
  3969. max = Math.max;
  3970. frameInfo = {
  3971. // Table markup starts with 110, div markup with 100.
  3972. table: left.substr(0, 3) == '110',
  3973. // Determine if we are dealing with a horizontal or vertical component
  3974. vertical: top.substr(0, 3) == '110',
  3975. // Get and parse the different border radius sizes
  3976. top: max(left.substr(3, 2), left.substr(5, 2)),
  3977. right: max(left.substr(5, 2), top.substr(3, 2)),
  3978. bottom: max(top.substr(3, 2), top.substr(5, 2)),
  3979. left: max(top.substr(5, 2), left.substr(3, 2))
  3980. };
  3981. frameInfo.width = max(frameInfo.top, frameInfo.right, frameInfo.bottom, frameInfo.left);
  3982. // Just to be sure we set the background image of the el to none.
  3983. me.el.setStyle('background-image', 'none');
  3984. }
  3985. // This happens when you set frame: true explicitly without using the x-frame mixin in sass.
  3986. // This way IE can't figure out what sizes to use and thus framing can't work.
  3987. if (me.frame === true && !frameInfo) {
  3988. //<debug error>
  3989. Ext.Error.raise("You have set frame: true explicity on this component while it doesn't have any " +
  3990. "framing defined in the CSS template. In this case IE can't figure out what sizes " +
  3991. "to use and thus framing on this component will be disabled.");
  3992. //</debug>
  3993. }
  3994. me.frame = me.frame || !!frameInfo;
  3995. me.frameSize = frameInfo || false;
  3996. return frameInfo;
  3997. },
  3998. getFramePositions: function(frameInfo) {
  3999. var me = this,
  4000. frameWidth = frameInfo.width,
  4001. dock = me.dock,
  4002. positions, tc, bc, ml, mr;
  4003. if (frameInfo.vertical) {
  4004. tc = '0 -' + (frameWidth * 0) + 'px';
  4005. bc = '0 -' + (frameWidth * 1) + 'px';
  4006. if (dock && dock == "right") {
  4007. tc = 'right -' + (frameWidth * 0) + 'px';
  4008. bc = 'right -' + (frameWidth * 1) + 'px';
  4009. }
  4010. positions = {
  4011. tl: '0 -' + (frameWidth * 0) + 'px',
  4012. tr: '0 -' + (frameWidth * 1) + 'px',
  4013. bl: '0 -' + (frameWidth * 2) + 'px',
  4014. br: '0 -' + (frameWidth * 3) + 'px',
  4015. ml: '-' + (frameWidth * 1) + 'px 0',
  4016. mr: 'right 0',
  4017. tc: tc,
  4018. bc: bc
  4019. };
  4020. } else {
  4021. ml = '-' + (frameWidth * 0) + 'px 0';
  4022. mr = 'right 0';
  4023. if (dock && dock == "bottom") {
  4024. ml = 'left bottom';
  4025. mr = 'right bottom';
  4026. }
  4027. positions = {
  4028. tl: '0 -' + (frameWidth * 2) + 'px',
  4029. tr: 'right -' + (frameWidth * 3) + 'px',
  4030. bl: '0 -' + (frameWidth * 4) + 'px',
  4031. br: 'right -' + (frameWidth * 5) + 'px',
  4032. ml: ml,
  4033. mr: mr,
  4034. tc: '0 -' + (frameWidth * 0) + 'px',
  4035. bc: '0 -' + (frameWidth * 1) + 'px'
  4036. };
  4037. }
  4038. return positions;
  4039. },
  4040. /**
  4041. * @private
  4042. */
  4043. getFrameTpl : function(table) {
  4044. return table ? this.getTpl('frameTableTpl') : this.getTpl('frameTpl');
  4045. },
  4046. /**
  4047. * Creates an array of class names from the configurations to add to this Component's `el` on render.
  4048. *
  4049. * Private, but (possibly) used by ComponentQuery for selection by class name if Component is not rendered.
  4050. *
  4051. * @return {String[]} An array of class names with which the Component's element will be rendered.
  4052. * @private
  4053. */
  4054. initCls: function() {
  4055. var me = this,
  4056. cls = [];
  4057. cls.push(me.baseCls);
  4058. //<deprecated since=0.99>
  4059. if (Ext.isDefined(me.cmpCls)) {
  4060. if (Ext.isDefined(Ext.global.console)) {
  4061. Ext.global.console.warn('Ext.Component: cmpCls has been deprecated. Please use componentCls.');
  4062. }
  4063. me.componentCls = me.cmpCls;
  4064. delete me.cmpCls;
  4065. }
  4066. //</deprecated>
  4067. if (me.componentCls) {
  4068. cls.push(me.componentCls);
  4069. } else {
  4070. me.componentCls = me.baseCls;
  4071. }
  4072. if (me.cls) {
  4073. cls.push(me.cls);
  4074. delete me.cls;
  4075. }
  4076. return cls.concat(me.additionalCls);
  4077. },
  4078. /**
  4079. * Sets the UI for the component. This will remove any existing UIs on the component. It will also loop through any
  4080. * uiCls set on the component and rename them so they include the new UI
  4081. * @param {String} ui The new UI for the component
  4082. */
  4083. setUI: function(ui) {
  4084. var me = this,
  4085. oldUICls = Ext.Array.clone(me.uiCls),
  4086. newUICls = [],
  4087. classes = [],
  4088. cls,
  4089. i;
  4090. //loop through all exisiting uiCls and update the ui in them
  4091. for (i = 0; i < oldUICls.length; i++) {
  4092. cls = oldUICls[i];
  4093. classes = classes.concat(me.removeClsWithUI(cls, true));
  4094. newUICls.push(cls);
  4095. }
  4096. if (classes.length) {
  4097. me.removeCls(classes);
  4098. }
  4099. //remove the UI from the element
  4100. me.removeUIFromElement();
  4101. //set the UI
  4102. me.ui = ui;
  4103. //add the new UI to the elemend
  4104. me.addUIToElement();
  4105. //loop through all exisiting uiCls and update the ui in them
  4106. classes = [];
  4107. for (i = 0; i < newUICls.length; i++) {
  4108. cls = newUICls[i];
  4109. classes = classes.concat(me.addClsWithUI(cls, true));
  4110. }
  4111. if (classes.length) {
  4112. me.addCls(classes);
  4113. }
  4114. },
  4115. /**
  4116. * Adds a cls to the uiCls array, which will also call {@link #addUIClsToElement} and adds to all elements of this
  4117. * component.
  4118. * @param {String/String[]} cls A string or an array of strings to add to the uiCls
  4119. * @param {Object} skip (Boolean) skip True to skip adding it to the class and do it later (via the return)
  4120. */
  4121. addClsWithUI: function(cls, skip) {
  4122. var me = this,
  4123. classes = [],
  4124. i;
  4125. if (!Ext.isArray(cls)) {
  4126. cls = [cls];
  4127. }
  4128. for (i = 0; i < cls.length; i++) {
  4129. if (cls[i] && !me.hasUICls(cls[i])) {
  4130. me.uiCls = Ext.Array.clone(me.uiCls);
  4131. me.uiCls.push(cls[i]);
  4132. classes = classes.concat(me.addUIClsToElement(cls[i]));
  4133. }
  4134. }
  4135. if (skip !== true) {
  4136. me.addCls(classes);
  4137. }
  4138. return classes;
  4139. },
  4140. /**
  4141. * Removes a cls to the uiCls array, which will also call {@link #removeUIClsFromElement} and removes it from all
  4142. * elements of this component.
  4143. * @param {String/String[]} cls A string or an array of strings to remove to the uiCls
  4144. */
  4145. removeClsWithUI: function(cls, skip) {
  4146. var me = this,
  4147. classes = [],
  4148. i;
  4149. if (!Ext.isArray(cls)) {
  4150. cls = [cls];
  4151. }
  4152. for (i = 0; i < cls.length; i++) {
  4153. if (cls[i] && me.hasUICls(cls[i])) {
  4154. me.uiCls = Ext.Array.remove(me.uiCls, cls[i]);
  4155. classes = classes.concat(me.removeUIClsFromElement(cls[i]));
  4156. }
  4157. }
  4158. if (skip !== true) {
  4159. me.removeCls(classes);
  4160. }
  4161. return classes;
  4162. },
  4163. /**
  4164. * Checks if there is currently a specified uiCls
  4165. * @param {String} cls The cls to check
  4166. */
  4167. hasUICls: function(cls) {
  4168. var me = this,
  4169. uiCls = me.uiCls || [];
  4170. return Ext.Array.contains(uiCls, cls);
  4171. },
  4172. /**
  4173. * Method which adds a specified UI + uiCls to the components element. Can be overridden to remove the UI from more
  4174. * than just the components element.
  4175. * @param {String} ui The UI to remove from the element
  4176. */
  4177. addUIClsToElement: function(cls, force) {
  4178. var me = this,
  4179. result = [],
  4180. frameElementCls = me.frameElementCls;
  4181. result.push(Ext.baseCSSPrefix + cls);
  4182. result.push(me.baseCls + '-' + cls);
  4183. result.push(me.baseCls + '-' + me.ui + '-' + cls);
  4184. if (!force && me.frame && !Ext.supports.CSS3BorderRadius) {
  4185. // define each element of the frame
  4186. var els = ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'],
  4187. classes, i, j, el;
  4188. // loop through each of them, and if they are defined add the ui
  4189. for (i = 0; i < els.length; i++) {
  4190. el = me['frame' + els[i].toUpperCase()];
  4191. classes = [me.baseCls + '-' + me.ui + '-' + els[i], me.baseCls + '-' + me.ui + '-' + cls + '-' + els[i]];
  4192. if (el && el.dom) {
  4193. el.addCls(classes);
  4194. } else {
  4195. for (j = 0; j < classes.length; j++) {
  4196. if (Ext.Array.indexOf(frameElementCls[els[i]], classes[j]) == -1) {
  4197. frameElementCls[els[i]].push(classes[j]);
  4198. }
  4199. }
  4200. }
  4201. }
  4202. }
  4203. me.frameElementCls = frameElementCls;
  4204. return result;
  4205. },
  4206. /**
  4207. * Method which removes a specified UI + uiCls from the components element. The cls which is added to the element
  4208. * will be: `this.baseCls + '-' + ui`
  4209. * @param {String} ui The UI to add to the element
  4210. */
  4211. removeUIClsFromElement: function(cls, force) {
  4212. var me = this,
  4213. result = [],
  4214. frameElementCls = me.frameElementCls;
  4215. result.push(Ext.baseCSSPrefix + cls);
  4216. result.push(me.baseCls + '-' + cls);
  4217. result.push(me.baseCls + '-' + me.ui + '-' + cls);
  4218. if (!force && me.frame && !Ext.supports.CSS3BorderRadius) {
  4219. // define each element of the frame
  4220. var els = ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'],
  4221. i, el;
  4222. cls = me.baseCls + '-' + me.ui + '-' + cls + '-' + els[i];
  4223. // loop through each of them, and if they are defined add the ui
  4224. for (i = 0; i < els.length; i++) {
  4225. el = me['frame' + els[i].toUpperCase()];
  4226. if (el && el.dom) {
  4227. el.removeCls(cls);
  4228. } else {
  4229. Ext.Array.remove(frameElementCls[els[i]], cls);
  4230. }
  4231. }
  4232. }
  4233. me.frameElementCls = frameElementCls;
  4234. return result;
  4235. },
  4236. /**
  4237. * Method which adds a specified UI to the components element.
  4238. * @private
  4239. */
  4240. addUIToElement: function(force) {
  4241. var me = this,
  4242. frameElementCls = me.frameElementCls;
  4243. me.addCls(me.baseCls + '-' + me.ui);
  4244. if (me.frame && !Ext.supports.CSS3BorderRadius) {
  4245. // define each element of the frame
  4246. var els = ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'],
  4247. i, el, cls;
  4248. // loop through each of them, and if they are defined add the ui
  4249. for (i = 0; i < els.length; i++) {
  4250. el = me['frame' + els[i].toUpperCase()];
  4251. cls = me.baseCls + '-' + me.ui + '-' + els[i];
  4252. if (el) {
  4253. el.addCls(cls);
  4254. } else {
  4255. if (!Ext.Array.contains(frameElementCls[els[i]], cls)) {
  4256. frameElementCls[els[i]].push(cls);
  4257. }
  4258. }
  4259. }
  4260. }
  4261. },
  4262. /**
  4263. * Method which removes a specified UI from the components element.
  4264. * @private
  4265. */
  4266. removeUIFromElement: function() {
  4267. var me = this,
  4268. frameElementCls = me.frameElementCls;
  4269. me.removeCls(me.baseCls + '-' + me.ui);
  4270. if (me.frame && !Ext.supports.CSS3BorderRadius) {
  4271. // define each element of the frame
  4272. var els = ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'],
  4273. i, j, el, cls;
  4274. // loop through each of them, and if they are defined add the ui
  4275. for (i = 0; i < els.length; i++) {
  4276. el = me['frame' + els[i].toUpperCase()];
  4277. cls = me.baseCls + '-' + me.ui + '-' + els[i];
  4278. if (el) {
  4279. el.removeCls(cls);
  4280. } else {
  4281. Ext.Array.remove(frameElementCls[els[i]], cls);
  4282. }
  4283. }
  4284. }
  4285. },
  4286. getElConfig : function() {
  4287. if (Ext.isString(this.autoEl)) {
  4288. this.autoEl = {
  4289. tag: this.autoEl
  4290. };
  4291. }
  4292. var result = this.autoEl || {tag: 'div'};
  4293. result.id = this.id;
  4294. return result;
  4295. },
  4296. /**
  4297. * This function takes the position argument passed to onRender and returns a DOM element that you can use in the
  4298. * insertBefore.
  4299. * @param {String/Number/Ext.Element/HTMLElement} position Index, element id or element you want to put this
  4300. * component before.
  4301. * @return {HTMLElement} DOM element that you can use in the insertBefore
  4302. */
  4303. getInsertPosition: function(position) {
  4304. // Convert the position to an element to insert before
  4305. if (position !== undefined) {
  4306. if (Ext.isNumber(position)) {
  4307. position = this.container.dom.childNodes[position];
  4308. }
  4309. else {
  4310. position = Ext.getDom(position);
  4311. }
  4312. }
  4313. return position;
  4314. },
  4315. /**
  4316. * Adds ctCls to container.
  4317. * @return {Ext.Element} The initialized container
  4318. * @private
  4319. */
  4320. initContainer: function(container) {
  4321. var me = this;
  4322. // If you render a component specifying the el, we get the container
  4323. // of the el, and make sure we dont move the el around in the dom
  4324. // during the render
  4325. if (!container && me.el) {
  4326. container = me.el.dom.parentNode;
  4327. me.allowDomMove = false;
  4328. }
  4329. me.container = Ext.get(container);
  4330. if (me.ctCls) {
  4331. me.container.addCls(me.ctCls);
  4332. }
  4333. return me.container;
  4334. },
  4335. /**
  4336. * Initialized the renderData to be used when rendering the renderTpl.
  4337. * @return {Object} Object with keys and values that are going to be applied to the renderTpl
  4338. * @private
  4339. */
  4340. initRenderData: function() {
  4341. var me = this;
  4342. return Ext.applyIf(me.renderData, {
  4343. id: me.id,
  4344. ui: me.ui,
  4345. uiCls: me.uiCls,
  4346. baseCls: me.baseCls,
  4347. componentCls: me.componentCls,
  4348. frame: me.frame
  4349. });
  4350. },
  4351. /**
  4352. * @private
  4353. */
  4354. getTpl: function(name) {
  4355. var me = this,
  4356. prototype = me.self.prototype,
  4357. ownerPrototype,
  4358. tpl;
  4359. if (me.hasOwnProperty(name)) {
  4360. tpl = me[name];
  4361. if (tpl && !(tpl instanceof Ext.XTemplate)) {
  4362. me[name] = Ext.ClassManager.dynInstantiate('Ext.XTemplate', tpl);
  4363. }
  4364. return me[name];
  4365. }
  4366. if (!(prototype[name] instanceof Ext.XTemplate)) {
  4367. ownerPrototype = prototype;
  4368. do {
  4369. if (ownerPrototype.hasOwnProperty(name)) {
  4370. tpl = ownerPrototype[name];
  4371. if (tpl && !(tpl instanceof Ext.XTemplate)) {
  4372. ownerPrototype[name] = Ext.ClassManager.dynInstantiate('Ext.XTemplate', tpl);
  4373. break;
  4374. }
  4375. }
  4376. ownerPrototype = ownerPrototype.superclass;
  4377. } while (ownerPrototype);
  4378. }
  4379. return prototype[name];
  4380. },
  4381. /**
  4382. * Initializes the renderTpl.
  4383. * @return {Ext.XTemplate} The renderTpl XTemplate instance.
  4384. * @private
  4385. */
  4386. initRenderTpl: function() {
  4387. return this.getTpl('renderTpl');
  4388. },
  4389. /**
  4390. * Converts style definitions to String.
  4391. * @return {String} A CSS style string with style, padding, margin and border.
  4392. * @private
  4393. */
  4394. initStyles: function() {
  4395. var style = {},
  4396. me = this,
  4397. Element = Ext.Element;
  4398. if (Ext.isString(me.style)) {
  4399. style = Element.parseStyles(me.style);
  4400. } else {
  4401. style = Ext.apply({}, me.style);
  4402. }
  4403. // Convert the padding, margin and border properties from a space seperated string
  4404. // into a proper style string
  4405. if (me.padding !== undefined) {
  4406. style.padding = Element.unitizeBox((me.padding === true) ? 5 : me.padding);
  4407. }
  4408. if (me.margin !== undefined) {
  4409. style.margin = Element.unitizeBox((me.margin === true) ? 5 : me.margin);
  4410. }
  4411. delete me.style;
  4412. return style;
  4413. },
  4414. /**
  4415. * Initializes this components contents. It checks for the properties html, contentEl and tpl/data.
  4416. * @private
  4417. */
  4418. initContent: function() {
  4419. var me = this,
  4420. target = me.getTargetEl(),
  4421. contentEl,
  4422. pre;
  4423. if (me.html) {
  4424. target.update(Ext.DomHelper.markup(me.html));
  4425. delete me.html;
  4426. }
  4427. if (me.contentEl) {
  4428. contentEl = Ext.get(me.contentEl);
  4429. pre = Ext.baseCSSPrefix;
  4430. contentEl.removeCls([pre + 'hidden', pre + 'hide-display', pre + 'hide-offsets', pre + 'hide-nosize']);
  4431. target.appendChild(contentEl.dom);
  4432. }
  4433. if (me.tpl) {
  4434. // Make sure this.tpl is an instantiated XTemplate
  4435. if (!me.tpl.isTemplate) {
  4436. me.tpl = Ext.create('Ext.XTemplate', me.tpl);
  4437. }
  4438. if (me.data) {
  4439. me.tpl[me.tplWriteMode](target, me.data);
  4440. delete me.data;
  4441. }
  4442. }
  4443. },
  4444. // @private
  4445. initEvents : function() {
  4446. var me = this,
  4447. afterRenderEvents = me.afterRenderEvents,
  4448. el,
  4449. property,
  4450. fn = function(listeners){
  4451. me.mon(el, listeners);
  4452. };
  4453. if (afterRenderEvents) {
  4454. for (property in afterRenderEvents) {
  4455. if (afterRenderEvents.hasOwnProperty(property)) {
  4456. el = me[property];
  4457. if (el && el.on) {
  4458. Ext.each(afterRenderEvents[property], fn);
  4459. }
  4460. }
  4461. }
  4462. }
  4463. },
  4464. /**
  4465. * Adds each argument passed to this method to the {@link #childEls} array.
  4466. */
  4467. addChildEls: function () {
  4468. var me = this,
  4469. childEls = me.childEls || (me.childEls = []);
  4470. childEls.push.apply(childEls, arguments);
  4471. },
  4472. /**
  4473. * Removes items in the childEls array based on the return value of a supplied test function. The function is called
  4474. * with a entry in childEls and if the test function return true, that entry is removed. If false, that entry is
  4475. * kept.
  4476. * @param {Function} testFn The test function.
  4477. */
  4478. removeChildEls: function (testFn) {
  4479. var me = this,
  4480. old = me.childEls,
  4481. keepers = (me.childEls = []),
  4482. n, i, cel;
  4483. for (i = 0, n = old.length; i < n; ++i) {
  4484. cel = old[i];
  4485. if (!testFn(cel)) {
  4486. keepers.push(cel);
  4487. }
  4488. }
  4489. },
  4490. /**
  4491. * Sets references to elements inside the component. This applies {@link #renderSelectors}
  4492. * as well as {@link #childEls}.
  4493. * @private
  4494. */
  4495. applyRenderSelectors: function() {
  4496. var me = this,
  4497. childEls = me.childEls,
  4498. selectors = me.renderSelectors,
  4499. el = me.el,
  4500. dom = el.dom,
  4501. baseId, childName, childId, i, selector;
  4502. if (childEls) {
  4503. baseId = me.id + '-';
  4504. for (i = childEls.length; i--; ) {
  4505. childName = childId = childEls[i];
  4506. if (typeof(childName) != 'string') {
  4507. childId = childName.id || (baseId + childName.itemId);
  4508. childName = childName.name;
  4509. } else {
  4510. childId = baseId + childId;
  4511. }
  4512. // We don't use Ext.get because that is 3x (or more) slower on IE6-8. Since
  4513. // we know the el's are children of our el we use getById instead:
  4514. me[childName] = el.getById(childId);
  4515. }
  4516. }
  4517. // We still support renderSelectors. There are a few places in the framework that
  4518. // need them and they are a documented part of the API. In fact, we support mixing
  4519. // childEls and renderSelectors (no reason not to).
  4520. if (selectors) {
  4521. for (selector in selectors) {
  4522. if (selectors.hasOwnProperty(selector) && selectors[selector]) {
  4523. me[selector] = Ext.get(Ext.DomQuery.selectNode(selectors[selector], dom));
  4524. }
  4525. }
  4526. }
  4527. },
  4528. /**
  4529. * Tests whether this Component matches the selector string.
  4530. * @param {String} selector The selector string to test against.
  4531. * @return {Boolean} True if this Component matches the selector.
  4532. */
  4533. is: function(selector) {
  4534. return Ext.ComponentQuery.is(this, selector);
  4535. },
  4536. /**
  4537. * Walks up the `ownerCt` axis looking for an ancestor Container which matches the passed simple selector.
  4538. *
  4539. * Example:
  4540. *
  4541. * var owningTabPanel = grid.up('tabpanel');
  4542. *
  4543. * @param {String} [selector] The simple selector to test.
  4544. * @return {Ext.container.Container} The matching ancestor Container (or `undefined` if no match was found).
  4545. */
  4546. up: function(selector) {
  4547. var result = this.ownerCt;
  4548. if (selector) {
  4549. for (; result; result = result.ownerCt) {
  4550. if (Ext.ComponentQuery.is(result, selector)) {
  4551. return result;
  4552. }
  4553. }
  4554. }
  4555. return result;
  4556. },
  4557. /**
  4558. * Returns the next sibling of this Component.
  4559. *
  4560. * Optionally selects the next sibling which matches the passed {@link Ext.ComponentQuery ComponentQuery} selector.
  4561. *
  4562. * May also be refered to as **`next()`**
  4563. *
  4564. * Note that this is limited to siblings, and if no siblings of the item match, `null` is returned. Contrast with
  4565. * {@link #nextNode}
  4566. * @param {String} [selector] A {@link Ext.ComponentQuery ComponentQuery} selector to filter the following items.
  4567. * @return {Ext.Component} The next sibling (or the next sibling which matches the selector).
  4568. * Returns null if there is no matching sibling.
  4569. */
  4570. nextSibling: function(selector) {
  4571. var o = this.ownerCt, it, last, idx, c;
  4572. if (o) {
  4573. it = o.items;
  4574. idx = it.indexOf(this) + 1;
  4575. if (idx) {
  4576. if (selector) {
  4577. for (last = it.getCount(); idx < last; idx++) {
  4578. if ((c = it.getAt(idx)).is(selector)) {
  4579. return c;
  4580. }
  4581. }
  4582. } else {
  4583. if (idx < it.getCount()) {
  4584. return it.getAt(idx);
  4585. }
  4586. }
  4587. }
  4588. }
  4589. return null;
  4590. },
  4591. /**
  4592. * Returns the previous sibling of this Component.
  4593. *
  4594. * Optionally selects the previous sibling which matches the passed {@link Ext.ComponentQuery ComponentQuery}
  4595. * selector.
  4596. *
  4597. * May also be refered to as **`prev()`**
  4598. *
  4599. * Note that this is limited to siblings, and if no siblings of the item match, `null` is returned. Contrast with
  4600. * {@link #previousNode}
  4601. * @param {String} [selector] A {@link Ext.ComponentQuery ComponentQuery} selector to filter the preceding items.
  4602. * @return {Ext.Component} The previous sibling (or the previous sibling which matches the selector).
  4603. * Returns null if there is no matching sibling.
  4604. */
  4605. previousSibling: function(selector) {
  4606. var o = this.ownerCt, it, idx, c;
  4607. if (o) {
  4608. it = o.items;
  4609. idx = it.indexOf(this);
  4610. if (idx != -1) {
  4611. if (selector) {
  4612. for (--idx; idx >= 0; idx--) {
  4613. if ((c = it.getAt(idx)).is(selector)) {
  4614. return c;
  4615. }
  4616. }
  4617. } else {
  4618. if (idx) {
  4619. return it.getAt(--idx);
  4620. }
  4621. }
  4622. }
  4623. }
  4624. return null;
  4625. },
  4626. /**
  4627. * Returns the previous node in the Component tree in tree traversal order.
  4628. *
  4629. * Note that this is not limited to siblings, and if invoked upon a node with no matching siblings, will walk the
  4630. * tree in reverse order to attempt to find a match. Contrast with {@link #previousSibling}.
  4631. * @param {String} [selector] A {@link Ext.ComponentQuery ComponentQuery} selector to filter the preceding nodes.
  4632. * @return {Ext.Component} The previous node (or the previous node which matches the selector).
  4633. * Returns null if there is no matching node.
  4634. */
  4635. previousNode: function(selector, includeSelf) {
  4636. var node = this,
  4637. result,
  4638. it, len, i;
  4639. // If asked to include self, test me
  4640. if (includeSelf && node.is(selector)) {
  4641. return node;
  4642. }
  4643. result = this.prev(selector);
  4644. if (result) {
  4645. return result;
  4646. }
  4647. if (node.ownerCt) {
  4648. for (it = node.ownerCt.items.items, i = Ext.Array.indexOf(it, node) - 1; i > -1; i--) {
  4649. if (it[i].query) {
  4650. result = it[i].query(selector);
  4651. result = result[result.length - 1];
  4652. if (result) {
  4653. return result;
  4654. }
  4655. }
  4656. }
  4657. return node.ownerCt.previousNode(selector, true);
  4658. }
  4659. },
  4660. /**
  4661. * Returns the next node in the Component tree in tree traversal order.
  4662. *
  4663. * Note that this is not limited to siblings, and if invoked upon a node with no matching siblings, will walk the
  4664. * tree to attempt to find a match. Contrast with {@link #nextSibling}.
  4665. * @param {String} [selector] A {@link Ext.ComponentQuery ComponentQuery} selector to filter the following nodes.
  4666. * @return {Ext.Component} The next node (or the next node which matches the selector).
  4667. * Returns null if there is no matching node.
  4668. */
  4669. nextNode: function(selector, includeSelf) {
  4670. var node = this,
  4671. result,
  4672. it, len, i;
  4673. // If asked to include self, test me
  4674. if (includeSelf && node.is(selector)) {
  4675. return node;
  4676. }
  4677. result = this.next(selector);
  4678. if (result) {
  4679. return result;
  4680. }
  4681. if (node.ownerCt) {
  4682. for (it = node.ownerCt.items, i = it.indexOf(node) + 1, it = it.items, len = it.length; i < len; i++) {
  4683. if (it[i].down) {
  4684. result = it[i].down(selector);
  4685. if (result) {
  4686. return result;
  4687. }
  4688. }
  4689. }
  4690. return node.ownerCt.nextNode(selector);
  4691. }
  4692. },
  4693. /**
  4694. * Retrieves the id of this component. Will autogenerate an id if one has not already been set.
  4695. * @return {String}
  4696. */
  4697. getId : function() {
  4698. return this.id || (this.id = 'ext-comp-' + (this.getAutoId()));
  4699. },
  4700. getItemId : function() {
  4701. return this.itemId || this.id;
  4702. },
  4703. /**
  4704. * Retrieves the top level element representing this component.
  4705. * @return {Ext.core.Element}
  4706. */
  4707. getEl : function() {
  4708. return this.el;
  4709. },
  4710. /**
  4711. * This is used to determine where to insert the 'html', 'contentEl' and 'items' in this component.
  4712. * @private
  4713. */
  4714. getTargetEl: function() {
  4715. return this.frameBody || this.el;
  4716. },
  4717. /**
  4718. * Tests whether or not this Component is of a specific xtype. This can test whether this Component is descended
  4719. * from the xtype (default) or whether it is directly of the xtype specified (shallow = true).
  4720. *
  4721. * **If using your own subclasses, be aware that a Component must register its own xtype to participate in
  4722. * determination of inherited xtypes.**
  4723. *
  4724. * For a list of all available xtypes, see the {@link Ext.Component} header.
  4725. *
  4726. * Example usage:
  4727. *
  4728. * var t = new Ext.form.field.Text();
  4729. * var isText = t.isXType('textfield'); // true
  4730. * var isBoxSubclass = t.isXType('field'); // true, descended from Ext.form.field.Base
  4731. * var isBoxInstance = t.isXType('field', true); // false, not a direct Ext.form.field.Base instance
  4732. *
  4733. * @param {String} xtype The xtype to check for this Component
  4734. * @param {Boolean} [shallow=false] True to check whether this Component is directly of the specified xtype, false to
  4735. * check whether this Component is descended from the xtype.
  4736. * @return {Boolean} True if this component descends from the specified xtype, false otherwise.
  4737. */
  4738. isXType: function(xtype, shallow) {
  4739. //assume a string by default
  4740. if (Ext.isFunction(xtype)) {
  4741. xtype = xtype.xtype;
  4742. //handle being passed the class, e.g. Ext.Component
  4743. } else if (Ext.isObject(xtype)) {
  4744. xtype = xtype.statics().xtype;
  4745. //handle being passed an instance
  4746. }
  4747. return !shallow ? ('/' + this.getXTypes() + '/').indexOf('/' + xtype + '/') != -1: this.self.xtype == xtype;
  4748. },
  4749. /**
  4750. * Returns this Component's xtype hierarchy as a slash-delimited string. For a list of all available xtypes, see the
  4751. * {@link Ext.Component} header.
  4752. *
  4753. * **If using your own subclasses, be aware that a Component must register its own xtype to participate in
  4754. * determination of inherited xtypes.**
  4755. *
  4756. * Example usage:
  4757. *
  4758. * var t = new Ext.form.field.Text();
  4759. * alert(t.getXTypes()); // alerts 'component/field/textfield'
  4760. *
  4761. * @return {String} The xtype hierarchy string
  4762. */
  4763. getXTypes: function() {
  4764. var self = this.self,
  4765. xtypes, parentPrototype, parentXtypes;
  4766. if (!self.xtypes) {
  4767. xtypes = [];
  4768. parentPrototype = this;
  4769. while (parentPrototype) {
  4770. parentXtypes = parentPrototype.xtypes;
  4771. if (parentXtypes !== undefined) {
  4772. xtypes.unshift.apply(xtypes, parentXtypes);
  4773. }
  4774. parentPrototype = parentPrototype.superclass;
  4775. }
  4776. self.xtypeChain = xtypes;
  4777. self.xtypes = xtypes.join('/');
  4778. }
  4779. return self.xtypes;
  4780. },
  4781. /**
  4782. * Update the content area of a component.
  4783. * @param {String/Object} htmlOrData If this component has been configured with a template via the tpl config then
  4784. * it will use this argument as data to populate the template. If this component was not configured with a template,
  4785. * the components content area will be updated via Ext.Element update
  4786. * @param {Boolean} [loadScripts=false] Only legitimate when using the html configuration.
  4787. * @param {Function} [callback] Only legitimate when using the html configuration. Callback to execute when
  4788. * scripts have finished loading
  4789. */
  4790. update : function(htmlOrData, loadScripts, cb) {
  4791. var me = this;
  4792. if (me.tpl && !Ext.isString(htmlOrData)) {
  4793. me.data = htmlOrData;
  4794. if (me.rendered) {
  4795. me.tpl[me.tplWriteMode](me.getTargetEl(), htmlOrData || {});
  4796. }
  4797. } else {
  4798. me.html = Ext.isObject(htmlOrData) ? Ext.DomHelper.markup(htmlOrData) : htmlOrData;
  4799. if (me.rendered) {
  4800. me.getTargetEl().update(me.html, loadScripts, cb);
  4801. }
  4802. }
  4803. if (me.rendered) {
  4804. me.doComponentLayout();
  4805. }
  4806. },
  4807. /**
  4808. * Convenience function to hide or show this component by boolean.
  4809. * @param {Boolean} visible True to show, false to hide
  4810. * @return {Ext.Component} this
  4811. */
  4812. setVisible : function(visible) {
  4813. return this[visible ? 'show': 'hide']();
  4814. },
  4815. /**
  4816. * Returns true if this component is visible.
  4817. *
  4818. * @param {Boolean} [deep=false] Pass `true` to interrogate the visibility status of all parent Containers to
  4819. * determine whether this Component is truly visible to the user.
  4820. *
  4821. * Generally, to determine whether a Component is hidden, the no argument form is needed. For example when creating
  4822. * dynamically laid out UIs in a hidden Container before showing them.
  4823. *
  4824. * @return {Boolean} True if this component is visible, false otherwise.
  4825. */
  4826. isVisible: function(deep) {
  4827. var me = this,
  4828. child = me,
  4829. visible = !me.hidden,
  4830. ancestor = me.ownerCt;
  4831. // Clear hiddenOwnerCt property
  4832. me.hiddenAncestor = false;
  4833. if (me.destroyed) {
  4834. return false;
  4835. }
  4836. if (deep && visible && me.rendered && ancestor) {
  4837. while (ancestor) {
  4838. // If any ancestor is hidden, then this is hidden.
  4839. // If an ancestor Panel (only Panels have a collapse method) is collapsed,
  4840. // then its layoutTarget (body) is hidden, so this is hidden unless its within a
  4841. // docked item; they are still visible when collapsed (Unless they themseves are hidden)
  4842. if (ancestor.hidden || (ancestor.collapsed &&
  4843. !(ancestor.getDockedItems && Ext.Array.contains(ancestor.getDockedItems(), child)))) {
  4844. // Store hiddenOwnerCt property if needed
  4845. me.hiddenAncestor = ancestor;
  4846. visible = false;
  4847. break;
  4848. }
  4849. child = ancestor;
  4850. ancestor = ancestor.ownerCt;
  4851. }
  4852. }
  4853. return visible;
  4854. },
  4855. /**
  4856. * Enable the component
  4857. * @param {Boolean} [silent=false] Passing true will supress the 'enable' event from being fired.
  4858. */
  4859. enable: function(silent) {
  4860. var me = this;
  4861. if (me.rendered) {
  4862. me.el.removeCls(me.disabledCls);
  4863. me.el.dom.disabled = false;
  4864. me.onEnable();
  4865. }
  4866. me.disabled = false;
  4867. if (silent !== true) {
  4868. me.fireEvent('enable', me);
  4869. }
  4870. return me;
  4871. },
  4872. /**
  4873. * Disable the component.
  4874. * @param {Boolean} [silent=false] Passing true will supress the 'disable' event from being fired.
  4875. */
  4876. disable: function(silent) {
  4877. var me = this;
  4878. if (me.rendered) {
  4879. me.el.addCls(me.disabledCls);
  4880. me.el.dom.disabled = true;
  4881. me.onDisable();
  4882. }
  4883. me.disabled = true;
  4884. if (silent !== true) {
  4885. me.fireEvent('disable', me);
  4886. }
  4887. return me;
  4888. },
  4889. // @private
  4890. onEnable: function() {
  4891. if (this.maskOnDisable) {
  4892. this.el.unmask();
  4893. }
  4894. },
  4895. // @private
  4896. onDisable : function() {
  4897. if (this.maskOnDisable) {
  4898. this.el.mask();
  4899. }
  4900. },
  4901. /**
  4902. * Method to determine whether this Component is currently disabled.
  4903. * @return {Boolean} the disabled state of this Component.
  4904. */
  4905. isDisabled : function() {
  4906. return this.disabled;
  4907. },
  4908. /**
  4909. * Enable or disable the component.
  4910. * @param {Boolean} disabled True to disable.
  4911. */
  4912. setDisabled : function(disabled) {
  4913. return this[disabled ? 'disable': 'enable']();
  4914. },
  4915. /**
  4916. * Method to determine whether this Component is currently set to hidden.
  4917. * @return {Boolean} the hidden state of this Component.
  4918. */
  4919. isHidden : function() {
  4920. return this.hidden;
  4921. },
  4922. /**
  4923. * Adds a CSS class to the top level element representing this component.
  4924. * @param {String} cls The CSS class name to add
  4925. * @return {Ext.Component} Returns the Component to allow method chaining.
  4926. */
  4927. addCls : function(className) {
  4928. var me = this;
  4929. if (!className) {
  4930. return me;
  4931. }
  4932. if (!Ext.isArray(className)){
  4933. className = className.replace(me.trimRe, '').split(me.spacesRe);
  4934. }
  4935. if (me.rendered) {
  4936. me.el.addCls(className);
  4937. }
  4938. else {
  4939. me.additionalCls = Ext.Array.unique(me.additionalCls.concat(className));
  4940. }
  4941. return me;
  4942. },
  4943. /**
  4944. * Adds a CSS class to the top level element representing this component.
  4945. * @param {String} cls The CSS class name to add
  4946. * @return {Ext.Component} Returns the Component to allow method chaining.
  4947. */
  4948. addClass : function() {
  4949. return this.addCls.apply(this, arguments);
  4950. },
  4951. /**
  4952. * Removes a CSS class from the top level element representing this component.
  4953. * @param {Object} className
  4954. * @return {Ext.Component} Returns the Component to allow method chaining.
  4955. */
  4956. removeCls : function(className) {
  4957. var me = this;
  4958. if (!className) {
  4959. return me;
  4960. }
  4961. if (!Ext.isArray(className)){
  4962. className = className.replace(me.trimRe, '').split(me.spacesRe);
  4963. }
  4964. if (me.rendered) {
  4965. me.el.removeCls(className);
  4966. }
  4967. else if (me.additionalCls.length) {
  4968. Ext.each(className, function(cls) {
  4969. Ext.Array.remove(me.additionalCls, cls);
  4970. });
  4971. }
  4972. return me;
  4973. },
  4974. //<debug>
  4975. removeClass : function() {
  4976. if (Ext.isDefined(Ext.global.console)) {
  4977. Ext.global.console.warn('Ext.Component: removeClass has been deprecated. Please use removeCls.');
  4978. }
  4979. return this.removeCls.apply(this, arguments);
  4980. },
  4981. //</debug>
  4982. addOverCls: function() {
  4983. var me = this;
  4984. if (!me.disabled) {
  4985. me.el.addCls(me.overCls);
  4986. }
  4987. },
  4988. removeOverCls: function() {
  4989. this.el.removeCls(this.overCls);
  4990. },
  4991. addListener : function(element, listeners, scope, options) {
  4992. var me = this,
  4993. fn,
  4994. option;
  4995. if (Ext.isString(element) && (Ext.isObject(listeners) || options && options.element)) {
  4996. if (options.element) {
  4997. fn = listeners;
  4998. listeners = {};
  4999. listeners[element] = fn;
  5000. element = options.element;
  5001. if (scope) {
  5002. listeners.scope = scope;
  5003. }
  5004. for (option in options) {
  5005. if (options.hasOwnProperty(option)) {
  5006. if (me.eventOptionsRe.test(option)) {
  5007. listeners[option] = options[option];
  5008. }
  5009. }
  5010. }
  5011. }
  5012. // At this point we have a variable called element,
  5013. // and a listeners object that can be passed to on
  5014. if (me[element] && me[element].on) {
  5015. me.mon(me[element], listeners);
  5016. } else {
  5017. me.afterRenderEvents = me.afterRenderEvents || {};
  5018. if (!me.afterRenderEvents[element]) {
  5019. me.afterRenderEvents[element] = [];
  5020. }
  5021. me.afterRenderEvents[element].push(listeners);
  5022. }
  5023. }
  5024. return me.mixins.observable.addListener.apply(me, arguments);
  5025. },
  5026. // inherit docs
  5027. removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
  5028. var me = this,
  5029. element = managedListener.options ? managedListener.options.element : null;
  5030. if (element) {
  5031. element = me[element];
  5032. if (element && element.un) {
  5033. if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
  5034. element.un(managedListener.ename, managedListener.fn, managedListener.scope);
  5035. if (!isClear) {
  5036. Ext.Array.remove(me.managedListeners, managedListener);
  5037. }
  5038. }
  5039. }
  5040. } else {
  5041. return me.mixins.observable.removeManagedListenerItem.apply(me, arguments);
  5042. }
  5043. },
  5044. /**
  5045. * Provides the link for Observable's fireEvent method to bubble up the ownership hierarchy.
  5046. * @return {Ext.container.Container} the Container which owns this Component.
  5047. */
  5048. getBubbleTarget : function() {
  5049. return this.ownerCt;
  5050. },
  5051. /**
  5052. * Method to determine whether this Component is floating.
  5053. * @return {Boolean} the floating state of this component.
  5054. */
  5055. isFloating : function() {
  5056. return this.floating;
  5057. },
  5058. /**
  5059. * Method to determine whether this Component is draggable.
  5060. * @return {Boolean} the draggable state of this component.
  5061. */
  5062. isDraggable : function() {
  5063. return !!this.draggable;
  5064. },
  5065. /**
  5066. * Method to determine whether this Component is droppable.
  5067. * @return {Boolean} the droppable state of this component.
  5068. */
  5069. isDroppable : function() {
  5070. return !!this.droppable;
  5071. },
  5072. /**
  5073. * @private
  5074. * Method to manage awareness of when components are added to their
  5075. * respective Container, firing an added event.
  5076. * References are established at add time rather than at render time.
  5077. * @param {Ext.container.Container} container Container which holds the component
  5078. * @param {Number} pos Position at which the component was added
  5079. */
  5080. onAdded : function(container, pos) {
  5081. this.ownerCt = container;
  5082. this.fireEvent('added', this, container, pos);
  5083. },
  5084. /**
  5085. * @private
  5086. * Method to manage awareness of when components are removed from their
  5087. * respective Container, firing an removed event. References are properly
  5088. * cleaned up after removing a component from its owning container.
  5089. */
  5090. onRemoved : function() {
  5091. var me = this;
  5092. me.fireEvent('removed', me, me.ownerCt);
  5093. delete me.ownerCt;
  5094. },
  5095. // @private
  5096. beforeDestroy : Ext.emptyFn,
  5097. // @private
  5098. // @private
  5099. onResize : Ext.emptyFn,
  5100. /**
  5101. * Sets the width and height of this Component. This method fires the {@link #resize} event. This method can accept
  5102. * either width and height as separate arguments, or you can pass a size object like `{width:10, height:20}`.
  5103. *
  5104. * @param {Number/String/Object} width The new width to set. This may be one of:
  5105. *
  5106. * - A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).
  5107. * - A String used to set the CSS width style.
  5108. * - A size object in the format `{width: widthValue, height: heightValue}`.
  5109. * - `undefined` to leave the width unchanged.
  5110. *
  5111. * @param {Number/String} height The new height to set (not required if a size object is passed as the first arg).
  5112. * This may be one of:
  5113. *
  5114. * - A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).
  5115. * - A String used to set the CSS height style. Animation may **not** be used.
  5116. * - `undefined` to leave the height unchanged.
  5117. *
  5118. * @return {Ext.Component} this
  5119. */
  5120. setSize : function(width, height) {
  5121. var me = this,
  5122. layoutCollection;
  5123. // support for standard size objects
  5124. if (Ext.isObject(width)) {
  5125. height = width.height;
  5126. width = width.width;
  5127. }
  5128. // Constrain within configured maxima
  5129. if (Ext.isNumber(width)) {
  5130. width = Ext.Number.constrain(width, me.minWidth, me.maxWidth);
  5131. }
  5132. if (Ext.isNumber(height)) {
  5133. height = Ext.Number.constrain(height, me.minHeight, me.maxHeight);
  5134. }
  5135. if (!me.rendered || !me.isVisible()) {
  5136. // If an ownerCt is hidden, add my reference onto the layoutOnShow stack. Set the needsLayout flag.
  5137. if (me.hiddenAncestor) {
  5138. layoutCollection = me.hiddenAncestor.layoutOnShow;
  5139. layoutCollection.remove(me);
  5140. layoutCollection.add(me);
  5141. }
  5142. me.needsLayout = {
  5143. width: width,
  5144. height: height,
  5145. isSetSize: true
  5146. };
  5147. if (!me.rendered) {
  5148. me.width = (width !== undefined) ? width : me.width;
  5149. me.height = (height !== undefined) ? height : me.height;
  5150. }
  5151. return me;
  5152. }
  5153. me.doComponentLayout(width, height, true);
  5154. return me;
  5155. },
  5156. isFixedWidth: function() {
  5157. var me = this,
  5158. layoutManagedWidth = me.layoutManagedWidth;
  5159. if (Ext.isDefined(me.width) || layoutManagedWidth == 1) {
  5160. return true;
  5161. }
  5162. if (layoutManagedWidth == 2) {
  5163. return false;
  5164. }
  5165. return (me.ownerCt && me.ownerCt.isFixedWidth());
  5166. },
  5167. isFixedHeight: function() {
  5168. var me = this,
  5169. layoutManagedHeight = me.layoutManagedHeight;
  5170. if (Ext.isDefined(me.height) || layoutManagedHeight == 1) {
  5171. return true;
  5172. }
  5173. if (layoutManagedHeight == 2) {
  5174. return false;
  5175. }
  5176. return (me.ownerCt && me.ownerCt.isFixedHeight());
  5177. },
  5178. setCalculatedSize : function(width, height, callingContainer) {
  5179. var me = this,
  5180. layoutCollection;
  5181. // support for standard size objects
  5182. if (Ext.isObject(width)) {
  5183. callingContainer = width.ownerCt;
  5184. height = width.height;
  5185. width = width.width;
  5186. }
  5187. // Constrain within configured maxima
  5188. if (Ext.isNumber(width)) {
  5189. width = Ext.Number.constrain(width, me.minWidth, me.maxWidth);
  5190. }
  5191. if (Ext.isNumber(height)) {
  5192. height = Ext.Number.constrain(height, me.minHeight, me.maxHeight);
  5193. }
  5194. if (!me.rendered || !me.isVisible()) {
  5195. // If an ownerCt is hidden, add my reference onto the layoutOnShow stack. Set the needsLayout flag.
  5196. if (me.hiddenAncestor) {
  5197. layoutCollection = me.hiddenAncestor.layoutOnShow;
  5198. layoutCollection.remove(me);
  5199. layoutCollection.add(me);
  5200. }
  5201. me.needsLayout = {
  5202. width: width,
  5203. height: height,
  5204. isSetSize: false,
  5205. ownerCt: callingContainer
  5206. };
  5207. return me;
  5208. }
  5209. me.doComponentLayout(width, height, false, callingContainer);
  5210. return me;
  5211. },
  5212. /**
  5213. * This method needs to be called whenever you change something on this component that requires the Component's
  5214. * layout to be recalculated.
  5215. * @param {Object} width
  5216. * @param {Object} height
  5217. * @param {Object} isSetSize
  5218. * @param {Object} callingContainer
  5219. * @return {Ext.container.Container} this
  5220. */
  5221. doComponentLayout : function(width, height, isSetSize, callingContainer) {
  5222. var me = this,
  5223. componentLayout = me.getComponentLayout(),
  5224. lastComponentSize = componentLayout.lastComponentSize || {
  5225. width: undefined,
  5226. height: undefined
  5227. };
  5228. // collapsed state is not relevant here, so no testing done.
  5229. // Only Panels have a collapse method, and that just sets the width/height such that only
  5230. // a single docked Header parallel to the collapseTo side are visible, and the Panel body is hidden.
  5231. if (me.rendered && componentLayout) {
  5232. // If no width passed, then only insert a value if the Component is NOT ALLOWED to autowidth itself.
  5233. if (!Ext.isDefined(width)) {
  5234. if (me.isFixedWidth()) {
  5235. width = Ext.isDefined(me.width) ? me.width : lastComponentSize.width;
  5236. }
  5237. }
  5238. // If no height passed, then only insert a value if the Component is NOT ALLOWED to autoheight itself.
  5239. if (!Ext.isDefined(height)) {
  5240. if (me.isFixedHeight()) {
  5241. height = Ext.isDefined(me.height) ? me.height : lastComponentSize.height;
  5242. }
  5243. }
  5244. if (isSetSize) {
  5245. me.width = width;
  5246. me.height = height;
  5247. }
  5248. componentLayout.layout(width, height, isSetSize, callingContainer);
  5249. }
  5250. return me;
  5251. },
  5252. /**
  5253. * Forces this component to redo its componentLayout.
  5254. */
  5255. forceComponentLayout: function () {
  5256. this.doComponentLayout();
  5257. },
  5258. // @private
  5259. setComponentLayout : function(layout) {
  5260. var currentLayout = this.componentLayout;
  5261. if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
  5262. currentLayout.setOwner(null);
  5263. }
  5264. this.componentLayout = layout;
  5265. layout.setOwner(this);
  5266. },
  5267. getComponentLayout : function() {
  5268. var me = this;
  5269. if (!me.componentLayout || !me.componentLayout.isLayout) {
  5270. me.setComponentLayout(Ext.layout.Layout.create(me.componentLayout, 'autocomponent'));
  5271. }
  5272. return me.componentLayout;
  5273. },
  5274. /**
  5275. * Occurs after componentLayout is run.
  5276. * @param {Number} adjWidth The box-adjusted width that was set
  5277. * @param {Number} adjHeight The box-adjusted height that was set
  5278. * @param {Boolean} isSetSize Whether or not the height/width are stored on the component permanently
  5279. * @param {Ext.Component} callingContainer Container requesting the layout. Only used when isSetSize is false.
  5280. */
  5281. afterComponentLayout: function(width, height, isSetSize, callingContainer) {
  5282. var me = this,
  5283. layout = me.componentLayout,
  5284. oldSize = me.preLayoutSize;
  5285. ++me.componentLayoutCounter;
  5286. if (!oldSize || ((width !== oldSize.width) || (height !== oldSize.height))) {
  5287. me.fireEvent('resize', me, width, height);
  5288. }
  5289. },
  5290. /**
  5291. * Occurs before componentLayout is run. Returning false from this method will prevent the componentLayout from
  5292. * being executed.
  5293. * @param {Number} adjWidth The box-adjusted width that was set
  5294. * @param {Number} adjHeight The box-adjusted height that was set
  5295. * @param {Boolean} isSetSize Whether or not the height/width are stored on the component permanently
  5296. * @param {Ext.Component} callingContainer Container requesting sent the layout. Only used when isSetSize is false.
  5297. */
  5298. beforeComponentLayout: function(width, height, isSetSize, callingContainer) {
  5299. this.preLayoutSize = this.componentLayout.lastComponentSize;
  5300. return true;
  5301. },
  5302. /**
  5303. * Sets the left and top of the component. To set the page XY position instead, use
  5304. * {@link Ext.Component#setPagePosition setPagePosition}. This method fires the {@link #move} event.
  5305. * @param {Number} left The new left
  5306. * @param {Number} top The new top
  5307. * @return {Ext.Component} this
  5308. */
  5309. setPosition : function(x, y) {
  5310. var me = this;
  5311. if (Ext.isObject(x)) {
  5312. y = x.y;
  5313. x = x.x;
  5314. }
  5315. if (!me.rendered) {
  5316. return me;
  5317. }
  5318. if (x !== undefined || y !== undefined) {
  5319. me.el.setBox(x, y);
  5320. me.onPosition(x, y);
  5321. me.fireEvent('move', me, x, y);
  5322. }
  5323. return me;
  5324. },
  5325. /**
  5326. * @private
  5327. * Called after the component is moved, this method is empty by default but can be implemented by any
  5328. * subclass that needs to perform custom logic after a move occurs.
  5329. * @param {Number} x The new x position
  5330. * @param {Number} y The new y position
  5331. */
  5332. onPosition: Ext.emptyFn,
  5333. /**
  5334. * Sets the width of the component. This method fires the {@link #resize} event.
  5335. *
  5336. * @param {Number} width The new width to setThis may be one of:
  5337. *
  5338. * - A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).
  5339. * - A String used to set the CSS width style.
  5340. *
  5341. * @return {Ext.Component} this
  5342. */
  5343. setWidth : function(width) {
  5344. return this.setSize(width);
  5345. },
  5346. /**
  5347. * Sets the height of the component. This method fires the {@link #resize} event.
  5348. *
  5349. * @param {Number} height The new height to set. This may be one of:
  5350. *
  5351. * - A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).
  5352. * - A String used to set the CSS height style.
  5353. * - _undefined_ to leave the height unchanged.
  5354. *
  5355. * @return {Ext.Component} this
  5356. */
  5357. setHeight : function(height) {
  5358. return this.setSize(undefined, height);
  5359. },
  5360. /**
  5361. * Gets the current size of the component's underlying element.
  5362. * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
  5363. */
  5364. getSize : function() {
  5365. return this.el.getSize();
  5366. },
  5367. /**
  5368. * Gets the current width of the component's underlying element.
  5369. * @return {Number}
  5370. */
  5371. getWidth : function() {
  5372. return this.el.getWidth();
  5373. },
  5374. /**
  5375. * Gets the current height of the component's underlying element.
  5376. * @return {Number}
  5377. */
  5378. getHeight : function() {
  5379. return this.el.getHeight();
  5380. },
  5381. /**
  5382. * Gets the {@link Ext.ComponentLoader} for this Component.
  5383. * @return {Ext.ComponentLoader} The loader instance, null if it doesn't exist.
  5384. */
  5385. getLoader: function(){
  5386. var me = this,
  5387. autoLoad = me.autoLoad ? (Ext.isObject(me.autoLoad) ? me.autoLoad : {url: me.autoLoad}) : null,
  5388. loader = me.loader || autoLoad;
  5389. if (loader) {
  5390. if (!loader.isLoader) {
  5391. me.loader = Ext.create('Ext.ComponentLoader', Ext.apply({
  5392. target: me,
  5393. autoLoad: autoLoad
  5394. }, loader));
  5395. } else {
  5396. loader.setTarget(me);
  5397. }
  5398. return me.loader;
  5399. }
  5400. return null;
  5401. },
  5402. /**
  5403. * This method allows you to show or hide a LoadMask on top of this component.
  5404. *
  5405. * @param {Boolean/Object/String} load True to show the default LoadMask, a config object that will be passed to the
  5406. * LoadMask constructor, or a message String to show. False to hide the current LoadMask.
  5407. * @param {Boolean} [targetEl=false] True to mask the targetEl of this Component instead of the `this.el`. For example,
  5408. * setting this to true on a Panel will cause only the body to be masked.
  5409. * @return {Ext.LoadMask} The LoadMask instance that has just been shown.
  5410. */
  5411. setLoading : function(load, targetEl) {
  5412. var me = this,
  5413. config;
  5414. if (me.rendered) {
  5415. if (load !== false && !me.collapsed) {
  5416. if (Ext.isObject(load)) {
  5417. config = load;
  5418. }
  5419. else if (Ext.isString(load)) {
  5420. config = {msg: load};
  5421. }
  5422. else {
  5423. config = {};
  5424. }
  5425. me.loadMask = me.loadMask || Ext.create('Ext.LoadMask', targetEl ? me.getTargetEl() : me.el, config);
  5426. me.loadMask.show();
  5427. } else if (me.loadMask) {
  5428. Ext.destroy(me.loadMask);
  5429. me.loadMask = null;
  5430. }
  5431. }
  5432. return me.loadMask;
  5433. },
  5434. /**
  5435. * Sets the dock position of this component in its parent panel. Note that this only has effect if this item is part
  5436. * of the dockedItems collection of a parent that has a DockLayout (note that any Panel has a DockLayout by default)
  5437. * @param {Object} dock The dock position.
  5438. * @param {Boolean} [layoutParent=false] True to re-layout parent.
  5439. * @return {Ext.Component} this
  5440. */
  5441. setDocked : function(dock, layoutParent) {
  5442. var me = this;
  5443. me.dock = dock;
  5444. if (layoutParent && me.ownerCt && me.rendered) {
  5445. me.ownerCt.doComponentLayout();
  5446. }
  5447. return me;
  5448. },
  5449. onDestroy : function() {
  5450. var me = this;
  5451. if (me.monitorResize && Ext.EventManager.resizeEvent) {
  5452. Ext.EventManager.resizeEvent.removeListener(me.setSize, me);
  5453. }
  5454. // Destroying the floatingItems ZIndexManager will also destroy descendant floating Components
  5455. Ext.destroy(
  5456. me.componentLayout,
  5457. me.loadMask,
  5458. me.floatingItems
  5459. );
  5460. },
  5461. /**
  5462. * Remove any references to elements added via renderSelectors/childEls
  5463. * @private
  5464. */
  5465. cleanElementRefs: function(){
  5466. var me = this,
  5467. i = 0,
  5468. childEls = me.childEls,
  5469. selectors = me.renderSelectors,
  5470. selector,
  5471. name,
  5472. len;
  5473. if (me.rendered) {
  5474. if (childEls) {
  5475. for (len = childEls.length; i < len; ++i) {
  5476. name = childEls[i];
  5477. if (typeof(name) != 'string') {
  5478. name = name.name;
  5479. }
  5480. delete me[name];
  5481. }
  5482. }
  5483. if (selectors) {
  5484. for (selector in selectors) {
  5485. if (selectors.hasOwnProperty(selector)) {
  5486. delete me[selector];
  5487. }
  5488. }
  5489. }
  5490. }
  5491. delete me.rendered;
  5492. delete me.el;
  5493. delete me.frameBody;
  5494. },
  5495. /**
  5496. * Destroys the Component.
  5497. */
  5498. destroy : function() {
  5499. var me = this;
  5500. if (!me.isDestroyed) {
  5501. if (me.fireEvent('beforedestroy', me) !== false) {
  5502. me.destroying = true;
  5503. me.beforeDestroy();
  5504. if (me.floating) {
  5505. delete me.floatParent;
  5506. // A zIndexManager is stamped into a *floating* Component when it is added to a Container.
  5507. // If it has no zIndexManager at render time, it is assigned to the global Ext.WindowManager instance.
  5508. if (me.zIndexManager) {
  5509. me.zIndexManager.unregister(me);
  5510. }
  5511. } else if (me.ownerCt && me.ownerCt.remove) {
  5512. me.ownerCt.remove(me, false);
  5513. }
  5514. me.onDestroy();
  5515. // Attempt to destroy all plugins
  5516. Ext.destroy(me.plugins);
  5517. if (me.rendered) {
  5518. me.el.remove();
  5519. }
  5520. me.fireEvent('destroy', me);
  5521. Ext.ComponentManager.unregister(me);
  5522. me.mixins.state.destroy.call(me);
  5523. me.clearListeners();
  5524. // make sure we clean up the element references after removing all events
  5525. me.cleanElementRefs();
  5526. me.destroying = false;
  5527. me.isDestroyed = true;
  5528. }
  5529. }
  5530. },
  5531. /**
  5532. * Retrieves a plugin by its pluginId which has been bound to this component.
  5533. * @param {Object} pluginId
  5534. * @return {Ext.AbstractPlugin} plugin instance.
  5535. */
  5536. getPlugin: function(pluginId) {
  5537. var i = 0,
  5538. plugins = this.plugins,
  5539. ln = plugins.length;
  5540. for (; i < ln; i++) {
  5541. if (plugins[i].pluginId === pluginId) {
  5542. return plugins[i];
  5543. }
  5544. }
  5545. },
  5546. /**
  5547. * Determines whether this component is the descendant of a particular container.
  5548. * @param {Ext.Container} container
  5549. * @return {Boolean} True if it is.
  5550. */
  5551. isDescendantOf: function(container) {
  5552. return !!this.findParentBy(function(p){
  5553. return p === container;
  5554. });
  5555. }
  5556. }, function() {
  5557. this.createAlias({
  5558. on: 'addListener',
  5559. prev: 'previousSibling',
  5560. next: 'nextSibling'
  5561. });
  5562. });
  5563. /**
  5564. * The AbstractPlugin class is the base class from which user-implemented plugins should inherit.
  5565. *
  5566. * This class defines the essential API of plugins as used by Components by defining the following methods:
  5567. *
  5568. * - `init` : The plugin initialization method which the owning Component calls at Component initialization time.
  5569. *
  5570. * The Component passes itself as the sole parameter.
  5571. *
  5572. * Subclasses should set up bidirectional links between the plugin and its client Component here.
  5573. *
  5574. * - `destroy` : The plugin cleanup method which the owning Component calls at Component destruction time.
  5575. *
  5576. * Use this method to break links between the plugin and the Component and to free any allocated resources.
  5577. *
  5578. * - `enable` : The base implementation just sets the plugin's `disabled` flag to `false`
  5579. *
  5580. * - `disable` : The base implementation just sets the plugin's `disabled` flag to `true`
  5581. */
  5582. Ext.define('Ext.AbstractPlugin', {
  5583. disabled: false,
  5584. constructor: function(config) {
  5585. //<debug>
  5586. if (!config.cmp && Ext.global.console) {
  5587. Ext.global.console.warn("Attempted to attach a plugin ");
  5588. }
  5589. //</debug>
  5590. Ext.apply(this, config);
  5591. },
  5592. getCmp: function() {
  5593. return this.cmp;
  5594. },
  5595. /**
  5596. * @method
  5597. * The init method is invoked after initComponent method has been run for the client Component.
  5598. *
  5599. * The supplied implementation is empty. Subclasses should perform plugin initialization, and set up bidirectional
  5600. * links between the plugin and its client Component in their own implementation of this method.
  5601. * @param {Ext.Component} client The client Component which owns this plugin.
  5602. */
  5603. init: Ext.emptyFn,
  5604. /**
  5605. * @method
  5606. * The destroy method is invoked by the owning Component at the time the Component is being destroyed.
  5607. *
  5608. * The supplied implementation is empty. Subclasses should perform plugin cleanup in their own implementation of
  5609. * this method.
  5610. */
  5611. destroy: Ext.emptyFn,
  5612. /**
  5613. * The base implementation just sets the plugin's `disabled` flag to `false`
  5614. *
  5615. * Plugin subclasses which need more complex processing may implement an overriding implementation.
  5616. */
  5617. enable: function() {
  5618. this.disabled = false;
  5619. },
  5620. /**
  5621. * The base implementation just sets the plugin's `disabled` flag to `true`
  5622. *
  5623. * Plugin subclasses which need more complex processing may implement an overriding implementation.
  5624. */
  5625. disable: function() {
  5626. this.disabled = true;
  5627. }
  5628. });
  5629. /**
  5630. * The Connection class encapsulates a connection to the page's originating domain, allowing requests to be made either
  5631. * to a configured URL, or to a URL specified at request time.
  5632. *
  5633. * Requests made by this class are asynchronous, and will return immediately. No data from the server will be available
  5634. * to the statement immediately following the {@link #request} call. To process returned data, use a success callback
  5635. * in the request options object, or an {@link #requestcomplete event listener}.
  5636. *
  5637. * # File Uploads
  5638. *
  5639. * File uploads are not performed using normal "Ajax" techniques, that is they are not performed using XMLHttpRequests.
  5640. * Instead the form is submitted in the standard manner with the DOM &lt;form&gt; element temporarily modified to have its
  5641. * target set to refer to a dynamically generated, hidden &lt;iframe&gt; which is inserted into the document but removed
  5642. * after the return data has been gathered.
  5643. *
  5644. * The server response is parsed by the browser to create the document for the IFRAME. If the server is using JSON to
  5645. * send the return object, then the Content-Type header must be set to "text/html" in order to tell the browser to
  5646. * insert the text unchanged into the document body.
  5647. *
  5648. * Characters which are significant to an HTML parser must be sent as HTML entities, so encode `<` as `&lt;`, `&` as
  5649. * `&amp;` etc.
  5650. *
  5651. * The response text is retrieved from the document, and a fake XMLHttpRequest object is created containing a
  5652. * responseText property in order to conform to the requirements of event handlers and callbacks.
  5653. *
  5654. * Be aware that file upload packets are sent with the content type multipart/form and some server technologies
  5655. * (notably JEE) may require some custom processing in order to retrieve parameter names and parameter values from the
  5656. * packet content.
  5657. *
  5658. * Also note that it's not possible to check the response code of the hidden iframe, so the success handler will ALWAYS fire.
  5659. */
  5660. Ext.define('Ext.data.Connection', {
  5661. mixins: {
  5662. observable: 'Ext.util.Observable'
  5663. },
  5664. statics: {
  5665. requestId: 0
  5666. },
  5667. url: null,
  5668. async: true,
  5669. method: null,
  5670. username: '',
  5671. password: '',
  5672. /**
  5673. * @cfg {Boolean} disableCaching
  5674. * True to add a unique cache-buster param to GET requests.
  5675. */
  5676. disableCaching: true,
  5677. /**
  5678. * @cfg {Boolean} withCredentials
  5679. * True to set `withCredentials = true` on the XHR object
  5680. */
  5681. withCredentials: false,
  5682. /**
  5683. * @cfg {Boolean} cors
  5684. * True to enable CORS support on the XHR object. Currently the only effect of this option
  5685. * is to use the XDomainRequest object instead of XMLHttpRequest if the browser is IE8 or above.
  5686. */
  5687. cors: false,
  5688. /**
  5689. * @cfg {String} disableCachingParam
  5690. * Change the parameter which is sent went disabling caching through a cache buster.
  5691. */
  5692. disableCachingParam: '_dc',
  5693. /**
  5694. * @cfg {Number} timeout
  5695. * The timeout in milliseconds to be used for requests.
  5696. */
  5697. timeout : 30000,
  5698. /**
  5699. * @cfg {Object} extraParams
  5700. * Any parameters to be appended to the request.
  5701. */
  5702. useDefaultHeader : true,
  5703. defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8',
  5704. useDefaultXhrHeader : true,
  5705. defaultXhrHeader : 'XMLHttpRequest',
  5706. constructor : function(config) {
  5707. config = config || {};
  5708. Ext.apply(this, config);
  5709. this.addEvents(
  5710. /**
  5711. * @event beforerequest
  5712. * Fires before a network request is made to retrieve a data object.
  5713. * @param {Ext.data.Connection} conn This Connection object.
  5714. * @param {Object} options The options config object passed to the {@link #request} method.
  5715. */
  5716. 'beforerequest',
  5717. /**
  5718. * @event requestcomplete
  5719. * Fires if the request was successfully completed.
  5720. * @param {Ext.data.Connection} conn This Connection object.
  5721. * @param {Object} response The XHR object containing the response data.
  5722. * See [The XMLHttpRequest Object](http://www.w3.org/TR/XMLHttpRequest/) for details.
  5723. * @param {Object} options The options config object passed to the {@link #request} method.
  5724. */
  5725. 'requestcomplete',
  5726. /**
  5727. * @event requestexception
  5728. * Fires if an error HTTP status was returned from the server.
  5729. * See [HTTP Status Code Definitions](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)
  5730. * for details of HTTP status codes.
  5731. * @param {Ext.data.Connection} conn This Connection object.
  5732. * @param {Object} response The XHR object containing the response data.
  5733. * See [The XMLHttpRequest Object](http://www.w3.org/TR/XMLHttpRequest/) for details.
  5734. * @param {Object} options The options config object passed to the {@link #request} method.
  5735. */
  5736. 'requestexception'
  5737. );
  5738. this.requests = {};
  5739. this.mixins.observable.constructor.call(this);
  5740. },
  5741. /**
  5742. * Sends an HTTP request to a remote server.
  5743. *
  5744. * **Important:** Ajax server requests are asynchronous, and this call will
  5745. * return before the response has been received. Process any returned data
  5746. * in a callback function.
  5747. *
  5748. * Ext.Ajax.request({
  5749. * url: 'ajax_demo/sample.json',
  5750. * success: function(response, opts) {
  5751. * var obj = Ext.decode(response.responseText);
  5752. * console.dir(obj);
  5753. * },
  5754. * failure: function(response, opts) {
  5755. * console.log('server-side failure with status code ' + response.status);
  5756. * }
  5757. * });
  5758. *
  5759. * To execute a callback function in the correct scope, use the `scope` option.
  5760. *
  5761. * @param {Object} options An object which may contain the following properties:
  5762. *
  5763. * (The options object may also contain any other property which might be needed to perform
  5764. * postprocessing in a callback because it is passed to callback functions.)
  5765. *
  5766. * @param {String/Function} options.url The URL to which to send the request, or a function
  5767. * to call which returns a URL string. The scope of the function is specified by the `scope` option.
  5768. * Defaults to the configured `url`.
  5769. *
  5770. * @param {Object/String/Function} options.params An object containing properties which are
  5771. * used as parameters to the request, a url encoded string or a function to call to get either. The scope
  5772. * of the function is specified by the `scope` option.
  5773. *
  5774. * @param {String} options.method The HTTP method to use
  5775. * for the request. Defaults to the configured method, or if no method was configured,
  5776. * "GET" if no parameters are being sent, and "POST" if parameters are being sent. Note that
  5777. * the method name is case-sensitive and should be all caps.
  5778. *
  5779. * @param {Function} options.callback The function to be called upon receipt of the HTTP response.
  5780. * The callback is called regardless of success or failure and is passed the following parameters:
  5781. * @param {Object} options.callback.options The parameter to the request call.
  5782. * @param {Boolean} options.callback.success True if the request succeeded.
  5783. * @param {Object} options.callback.response The XMLHttpRequest object containing the response data.
  5784. * See [www.w3.org/TR/XMLHttpRequest/](http://www.w3.org/TR/XMLHttpRequest/) for details about
  5785. * accessing elements of the response.
  5786. *
  5787. * @param {Function} options.success The function to be called upon success of the request.
  5788. * The callback is passed the following parameters:
  5789. * @param {Object} options.success.response The XMLHttpRequest object containing the response data.
  5790. * @param {Object} options.success.options The parameter to the request call.
  5791. *
  5792. * @param {Function} options.failure The function to be called upon success of the request.
  5793. * The callback is passed the following parameters:
  5794. * @param {Object} options.failure.response The XMLHttpRequest object containing the response data.
  5795. * @param {Object} options.failure.options The parameter to the request call.
  5796. *
  5797. * @param {Object} options.scope The scope in which to execute the callbacks: The "this" object for
  5798. * the callback function. If the `url`, or `params` options were specified as functions from which to
  5799. * draw values, then this also serves as the scope for those function calls. Defaults to the browser
  5800. * window.
  5801. *
  5802. * @param {Number} options.timeout The timeout in milliseconds to be used for this request.
  5803. * Defaults to 30 seconds.
  5804. *
  5805. * @param {Ext.Element/HTMLElement/String} options.form The `<form>` Element or the id of the `<form>`
  5806. * to pull parameters from.
  5807. *
  5808. * @param {Boolean} options.isUpload **Only meaningful when used with the `form` option.**
  5809. *
  5810. * True if the form object is a file upload (will be set automatically if the form was configured
  5811. * with **`enctype`** `"multipart/form-data"`).
  5812. *
  5813. * File uploads are not performed using normal "Ajax" techniques, that is they are **not**
  5814. * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
  5815. * DOM `<form>` element temporarily modified to have its [target][] set to refer to a dynamically
  5816. * generated, hidden `<iframe>` which is inserted into the document but removed after the return data
  5817. * has been gathered.
  5818. *
  5819. * The server response is parsed by the browser to create the document for the IFRAME. If the
  5820. * server is using JSON to send the return object, then the [Content-Type][] header must be set to
  5821. * "text/html" in order to tell the browser to insert the text unchanged into the document body.
  5822. *
  5823. * The response text is retrieved from the document, and a fake XMLHttpRequest object is created
  5824. * containing a `responseText` property in order to conform to the requirements of event handlers
  5825. * and callbacks.
  5826. *
  5827. * Be aware that file upload packets are sent with the content type [multipart/form][] and some server
  5828. * technologies (notably JEE) may require some custom processing in order to retrieve parameter names
  5829. * and parameter values from the packet content.
  5830. *
  5831. * [target]: http://www.w3.org/TR/REC-html40/present/frames.html#adef-target
  5832. * [Content-Type]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17
  5833. * [multipart/form]: http://www.faqs.org/rfcs/rfc2388.html
  5834. *
  5835. * @param {Object} options.headers Request headers to set for the request.
  5836. *
  5837. * @param {Object} options.xmlData XML document to use for the post. Note: This will be used instead
  5838. * of params for the post data. Any params will be appended to the URL.
  5839. *
  5840. * @param {Object/String} options.jsonData JSON data to use as the post. Note: This will be used
  5841. * instead of params for the post data. Any params will be appended to the URL.
  5842. *
  5843. * @param {Boolean} options.disableCaching True to add a unique cache-buster param to GET requests.
  5844. *
  5845. * @param {Boolean} options.withCredentials True to add the withCredentials property to the XHR object
  5846. *
  5847. * @return {Object} The request object. This may be used to cancel the request.
  5848. */
  5849. request : function(options) {
  5850. options = options || {};
  5851. var me = this,
  5852. scope = options.scope || window,
  5853. username = options.username || me.username,
  5854. password = options.password || me.password || '',
  5855. async,
  5856. requestOptions,
  5857. request,
  5858. headers,
  5859. xhr;
  5860. if (me.fireEvent('beforerequest', me, options) !== false) {
  5861. requestOptions = me.setOptions(options, scope);
  5862. if (this.isFormUpload(options) === true) {
  5863. this.upload(options.form, requestOptions.url, requestOptions.data, options);
  5864. return null;
  5865. }
  5866. // if autoabort is set, cancel the current transactions
  5867. if (options.autoAbort === true || me.autoAbort) {
  5868. me.abort();
  5869. }
  5870. // create a connection object
  5871. if ((options.cors === true || me.cors === true) && Ext.isIe && Ext.ieVersion >= 8) {
  5872. xhr = new XDomainRequest();
  5873. } else {
  5874. xhr = this.getXhrInstance();
  5875. }
  5876. async = options.async !== false ? (options.async || me.async) : false;
  5877. // open the request
  5878. if (username) {
  5879. xhr.open(requestOptions.method, requestOptions.url, async, username, password);
  5880. } else {
  5881. xhr.open(requestOptions.method, requestOptions.url, async);
  5882. }
  5883. if (options.withCredentials === true || me.withCredentials === true) {
  5884. xhr.withCredentials = true;
  5885. }
  5886. headers = me.setupHeaders(xhr, options, requestOptions.data, requestOptions.params);
  5887. // create the transaction object
  5888. request = {
  5889. id: ++Ext.data.Connection.requestId,
  5890. xhr: xhr,
  5891. headers: headers,
  5892. options: options,
  5893. async: async,
  5894. timeout: setTimeout(function() {
  5895. request.timedout = true;
  5896. me.abort(request);
  5897. }, options.timeout || me.timeout)
  5898. };
  5899. me.requests[request.id] = request;
  5900. me.latestId = request.id;
  5901. // bind our statechange listener
  5902. if (async) {
  5903. xhr.onreadystatechange = Ext.Function.bind(me.onStateChange, me, [request]);
  5904. }
  5905. // start the request!
  5906. xhr.send(requestOptions.data);
  5907. if (!async) {
  5908. return this.onComplete(request);
  5909. }
  5910. return request;
  5911. } else {
  5912. Ext.callback(options.callback, options.scope, [options, undefined, undefined]);
  5913. return null;
  5914. }
  5915. },
  5916. /**
  5917. * Uploads a form using a hidden iframe.
  5918. * @param {String/HTMLElement/Ext.Element} form The form to upload
  5919. * @param {String} url The url to post to
  5920. * @param {String} params Any extra parameters to pass
  5921. * @param {Object} options The initial options
  5922. */
  5923. upload: function(form, url, params, options) {
  5924. form = Ext.getDom(form);
  5925. options = options || {};
  5926. var id = Ext.id(),
  5927. frame = document.createElement('iframe'),
  5928. hiddens = [],
  5929. encoding = 'multipart/form-data',
  5930. buf = {
  5931. target: form.target,
  5932. method: form.method,
  5933. encoding: form.encoding,
  5934. enctype: form.enctype,
  5935. action: form.action
  5936. }, hiddenItem;
  5937. /*
  5938. * Originally this behaviour was modified for Opera 10 to apply the secure URL after
  5939. * the frame had been added to the document. It seems this has since been corrected in
  5940. * Opera so the behaviour has been reverted, the URL will be set before being added.
  5941. */
  5942. Ext.fly(frame).set({
  5943. id: id,
  5944. name: id,
  5945. cls: Ext.baseCSSPrefix + 'hide-display',
  5946. src: Ext.SSL_SECURE_URL
  5947. });
  5948. document.body.appendChild(frame);
  5949. // This is required so that IE doesn't pop the response up in a new window.
  5950. if (document.frames) {
  5951. document.frames[id].name = id;
  5952. }
  5953. Ext.fly(form).set({
  5954. target: id,
  5955. method: 'POST',
  5956. enctype: encoding,
  5957. encoding: encoding,
  5958. action: url || buf.action
  5959. });
  5960. // add dynamic params
  5961. if (params) {
  5962. Ext.iterate(Ext.Object.fromQueryString(params), function(name, value){
  5963. hiddenItem = document.createElement('input');
  5964. Ext.fly(hiddenItem).set({
  5965. type: 'hidden',
  5966. value: value,
  5967. name: name
  5968. });
  5969. form.appendChild(hiddenItem);
  5970. hiddens.push(hiddenItem);
  5971. });
  5972. }
  5973. Ext.fly(frame).on('load', Ext.Function.bind(this.onUploadComplete, this, [frame, options]), null, {single: true});
  5974. form.submit();
  5975. Ext.fly(form).set(buf);
  5976. Ext.each(hiddens, function(h) {
  5977. Ext.removeNode(h);
  5978. });
  5979. },
  5980. /**
  5981. * @private
  5982. * Callback handler for the upload function. After we've submitted the form via the iframe this creates a bogus
  5983. * response object to simulate an XHR and populates its responseText from the now-loaded iframe's document body
  5984. * (or a textarea inside the body). We then clean up by removing the iframe
  5985. */
  5986. onUploadComplete: function(frame, options) {
  5987. var me = this,
  5988. // bogus response object
  5989. response = {
  5990. responseText: '',
  5991. responseXML: null
  5992. }, doc, firstChild;
  5993. try {
  5994. doc = frame.contentWindow.document || frame.contentDocument || window.frames[frame.id].document;
  5995. if (doc) {
  5996. if (doc.body) {
  5997. if (/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)) { // json response wrapped in textarea
  5998. response.responseText = firstChild.value;
  5999. } else {
  6000. response.responseText = doc.body.innerHTML;
  6001. }
  6002. }
  6003. //in IE the document may still have a body even if returns XML.
  6004. response.responseXML = doc.XMLDocument || doc;
  6005. }
  6006. } catch (e) {
  6007. }
  6008. me.fireEvent('requestcomplete', me, response, options);
  6009. Ext.callback(options.success, options.scope, [response, options]);
  6010. Ext.callback(options.callback, options.scope, [options, true, response]);
  6011. setTimeout(function(){
  6012. Ext.removeNode(frame);
  6013. }, 100);
  6014. },
  6015. /**
  6016. * Detects whether the form is intended to be used for an upload.
  6017. * @private
  6018. */
  6019. isFormUpload: function(options){
  6020. var form = this.getForm(options);
  6021. if (form) {
  6022. return (options.isUpload || (/multipart\/form-data/i).test(form.getAttribute('enctype')));
  6023. }
  6024. return false;
  6025. },
  6026. /**
  6027. * Gets the form object from options.
  6028. * @private
  6029. * @param {Object} options The request options
  6030. * @return {HTMLElement} The form, null if not passed
  6031. */
  6032. getForm: function(options){
  6033. return Ext.getDom(options.form) || null;
  6034. },
  6035. /**
  6036. * Sets various options such as the url, params for the request
  6037. * @param {Object} options The initial options
  6038. * @param {Object} scope The scope to execute in
  6039. * @return {Object} The params for the request
  6040. */
  6041. setOptions: function(options, scope){
  6042. var me = this,
  6043. params = options.params || {},
  6044. extraParams = me.extraParams,
  6045. urlParams = options.urlParams,
  6046. url = options.url || me.url,
  6047. jsonData = options.jsonData,
  6048. method,
  6049. disableCache,
  6050. data;
  6051. // allow params to be a method that returns the params object
  6052. if (Ext.isFunction(params)) {
  6053. params = params.call(scope, options);
  6054. }
  6055. // allow url to be a method that returns the actual url
  6056. if (Ext.isFunction(url)) {
  6057. url = url.call(scope, options);
  6058. }
  6059. url = this.setupUrl(options, url);
  6060. //<debug>
  6061. if (!url) {
  6062. Ext.Error.raise({
  6063. options: options,
  6064. msg: 'No URL specified'
  6065. });
  6066. }
  6067. //</debug>
  6068. // check for xml or json data, and make sure json data is encoded
  6069. data = options.rawData || options.xmlData || jsonData || null;
  6070. if (jsonData && !Ext.isPrimitive(jsonData)) {
  6071. data = Ext.encode(data);
  6072. }
  6073. // make sure params are a url encoded string and include any extraParams if specified
  6074. if (Ext.isObject(params)) {
  6075. params = Ext.Object.toQueryString(params);
  6076. }
  6077. if (Ext.isObject(extraParams)) {
  6078. extraParams = Ext.Object.toQueryString(extraParams);
  6079. }
  6080. params = params + ((extraParams) ? ((params) ? '&' : '') + extraParams : '');
  6081. urlParams = Ext.isObject(urlParams) ? Ext.Object.toQueryString(urlParams) : urlParams;
  6082. params = this.setupParams(options, params);
  6083. // decide the proper method for this request
  6084. method = (options.method || me.method || ((params || data) ? 'POST' : 'GET')).toUpperCase();
  6085. this.setupMethod(options, method);
  6086. disableCache = options.disableCaching !== false ? (options.disableCaching || me.disableCaching) : false;
  6087. // if the method is get append date to prevent caching
  6088. if (method === 'GET' && disableCache) {
  6089. url = Ext.urlAppend(url, (options.disableCachingParam || me.disableCachingParam) + '=' + (new Date().getTime()));
  6090. }
  6091. // if the method is get or there is json/xml data append the params to the url
  6092. if ((method == 'GET' || data) && params) {
  6093. url = Ext.urlAppend(url, params);
  6094. params = null;
  6095. }
  6096. // allow params to be forced into the url
  6097. if (urlParams) {
  6098. url = Ext.urlAppend(url, urlParams);
  6099. }
  6100. return {
  6101. url: url,
  6102. method: method,
  6103. data: data || params || null
  6104. };
  6105. },
  6106. /**
  6107. * Template method for overriding url
  6108. * @template
  6109. * @private
  6110. * @param {Object} options
  6111. * @param {String} url
  6112. * @return {String} The modified url
  6113. */
  6114. setupUrl: function(options, url){
  6115. var form = this.getForm(options);
  6116. if (form) {
  6117. url = url || form.action;
  6118. }
  6119. return url;
  6120. },
  6121. /**
  6122. * Template method for overriding params
  6123. * @template
  6124. * @private
  6125. * @param {Object} options
  6126. * @param {String} params
  6127. * @return {String} The modified params
  6128. */
  6129. setupParams: function(options, params) {
  6130. var form = this.getForm(options),
  6131. serializedForm;
  6132. if (form && !this.isFormUpload(options)) {
  6133. serializedForm = Ext.Element.serializeForm(form);
  6134. params = params ? (params + '&' + serializedForm) : serializedForm;
  6135. }
  6136. return params;
  6137. },
  6138. /**
  6139. * Template method for overriding method
  6140. * @template
  6141. * @private
  6142. * @param {Object} options
  6143. * @param {String} method
  6144. * @return {String} The modified method
  6145. */
  6146. setupMethod: function(options, method){
  6147. if (this.isFormUpload(options)) {
  6148. return 'POST';
  6149. }
  6150. return method;
  6151. },
  6152. /**
  6153. * Setup all the headers for the request
  6154. * @private
  6155. * @param {Object} xhr The xhr object
  6156. * @param {Object} options The options for the request
  6157. * @param {Object} data The data for the request
  6158. * @param {Object} params The params for the request
  6159. */
  6160. setupHeaders: function(xhr, options, data, params){
  6161. var me = this,
  6162. headers = Ext.apply({}, options.headers || {}, me.defaultHeaders || {}),
  6163. contentType = me.defaultPostHeader,
  6164. jsonData = options.jsonData,
  6165. xmlData = options.xmlData,
  6166. key,
  6167. header;
  6168. if (!headers['Content-Type'] && (data || params)) {
  6169. if (data) {
  6170. if (options.rawData) {
  6171. contentType = 'text/plain';
  6172. } else {
  6173. if (xmlData && Ext.isDefined(xmlData)) {
  6174. contentType = 'text/xml';
  6175. } else if (jsonData && Ext.isDefined(jsonData)) {
  6176. contentType = 'application/json';
  6177. }
  6178. }
  6179. }
  6180. headers['Content-Type'] = contentType;
  6181. }
  6182. if (me.useDefaultXhrHeader && !headers['X-Requested-With']) {
  6183. headers['X-Requested-With'] = me.defaultXhrHeader;
  6184. }
  6185. // set up all the request headers on the xhr object
  6186. try{
  6187. for (key in headers) {
  6188. if (headers.hasOwnProperty(key)) {
  6189. header = headers[key];
  6190. xhr.setRequestHeader(key, header);
  6191. }
  6192. }
  6193. } catch(e) {
  6194. me.fireEvent('exception', key, header);
  6195. }
  6196. return headers;
  6197. },
  6198. /**
  6199. * Creates the appropriate XHR transport for the browser.
  6200. * @private
  6201. */
  6202. getXhrInstance: (function(){
  6203. var options = [function(){
  6204. return new XMLHttpRequest();
  6205. }, function(){
  6206. return new ActiveXObject('MSXML2.XMLHTTP.3.0');
  6207. }, function(){
  6208. return new ActiveXObject('MSXML2.XMLHTTP');
  6209. }, function(){
  6210. return new ActiveXObject('Microsoft.XMLHTTP');
  6211. }], i = 0,
  6212. len = options.length,
  6213. xhr;
  6214. for(; i < len; ++i) {
  6215. try{
  6216. xhr = options[i];
  6217. xhr();
  6218. break;
  6219. }catch(e){}
  6220. }
  6221. return xhr;
  6222. })(),
  6223. /**
  6224. * Determines whether this object has a request outstanding.
  6225. * @param {Object} [request] Defaults to the last transaction
  6226. * @return {Boolean} True if there is an outstanding request.
  6227. */
  6228. isLoading : function(request) {
  6229. if (!request) {
  6230. request = this.getLatest();
  6231. }
  6232. if (!(request && request.xhr)) {
  6233. return false;
  6234. }
  6235. // if there is a connection and readyState is not 0 or 4
  6236. var state = request.xhr.readyState;
  6237. return !(state === 0 || state == 4);
  6238. },
  6239. /**
  6240. * Aborts an active request.
  6241. * @param {Object} [request] Defaults to the last request
  6242. */
  6243. abort : function(request) {
  6244. var me = this;
  6245. if (!request) {
  6246. request = me.getLatest();
  6247. }
  6248. if (request && me.isLoading(request)) {
  6249. /*
  6250. * Clear out the onreadystatechange here, this allows us
  6251. * greater control, the browser may/may not fire the function
  6252. * depending on a series of conditions.
  6253. */
  6254. request.xhr.onreadystatechange = null;
  6255. request.xhr.abort();
  6256. me.clearTimeout(request);
  6257. if (!request.timedout) {
  6258. request.aborted = true;
  6259. }
  6260. me.onComplete(request);
  6261. me.cleanup(request);
  6262. }
  6263. },
  6264. /**
  6265. * Aborts all active requests
  6266. */
  6267. abortAll: function(){
  6268. var requests = this.requests,
  6269. id;
  6270. for (id in requests) {
  6271. if (requests.hasOwnProperty(id)) {
  6272. this.abort(requests[id]);
  6273. }
  6274. }
  6275. },
  6276. /**
  6277. * Gets the most recent request
  6278. * @private
  6279. * @return {Object} The request. Null if there is no recent request
  6280. */
  6281. getLatest: function(){
  6282. var id = this.latestId,
  6283. request;
  6284. if (id) {
  6285. request = this.requests[id];
  6286. }
  6287. return request || null;
  6288. },
  6289. /**
  6290. * Fires when the state of the xhr changes
  6291. * @private
  6292. * @param {Object} request The request
  6293. */
  6294. onStateChange : function(request) {
  6295. if (request.xhr.readyState == 4) {
  6296. this.clearTimeout(request);
  6297. this.onComplete(request);
  6298. this.cleanup(request);
  6299. }
  6300. },
  6301. /**
  6302. * Clears the timeout on the request
  6303. * @private
  6304. * @param {Object} The request
  6305. */
  6306. clearTimeout: function(request){
  6307. clearTimeout(request.timeout);
  6308. delete request.timeout;
  6309. },
  6310. /**
  6311. * Cleans up any left over information from the request
  6312. * @private
  6313. * @param {Object} The request
  6314. */
  6315. cleanup: function(request){
  6316. request.xhr = null;
  6317. delete request.xhr;
  6318. },
  6319. /**
  6320. * To be called when the request has come back from the server
  6321. * @private
  6322. * @param {Object} request
  6323. * @return {Object} The response
  6324. */
  6325. onComplete : function(request) {
  6326. var me = this,
  6327. options = request.options,
  6328. result,
  6329. success,
  6330. response;
  6331. try {
  6332. result = me.parseStatus(request.xhr.status);
  6333. } catch (e) {
  6334. // in some browsers we can't access the status if the readyState is not 4, so the request has failed
  6335. result = {
  6336. success : false,
  6337. isException : false
  6338. };
  6339. }
  6340. success = result.success;
  6341. if (success) {
  6342. response = me.createResponse(request);
  6343. me.fireEvent('requestcomplete', me, response, options);
  6344. Ext.callback(options.success, options.scope, [response, options]);
  6345. } else {
  6346. if (result.isException || request.aborted || request.timedout) {
  6347. response = me.createException(request);
  6348. } else {
  6349. response = me.createResponse(request);
  6350. }
  6351. me.fireEvent('requestexception', me, response, options);
  6352. Ext.callback(options.failure, options.scope, [response, options]);
  6353. }
  6354. Ext.callback(options.callback, options.scope, [options, success, response]);
  6355. delete me.requests[request.id];
  6356. return response;
  6357. },
  6358. /**
  6359. * Checks if the response status was successful
  6360. * @param {Number} status The status code
  6361. * @return {Object} An object containing success/status state
  6362. */
  6363. parseStatus: function(status) {
  6364. // see: https://prototype.lighthouseapp.com/projects/8886/tickets/129-ie-mangles-http-response-status-code-204-to-1223
  6365. status = status == 1223 ? 204 : status;
  6366. var success = (status >= 200 && status < 300) || status == 304,
  6367. isException = false;
  6368. if (!success) {
  6369. switch (status) {
  6370. case 12002:
  6371. case 12029:
  6372. case 12030:
  6373. case 12031:
  6374. case 12152:
  6375. case 13030:
  6376. isException = true;
  6377. break;
  6378. }
  6379. }
  6380. return {
  6381. success: success,
  6382. isException: isException
  6383. };
  6384. },
  6385. /**
  6386. * Creates the response object
  6387. * @private
  6388. * @param {Object} request
  6389. */
  6390. createResponse : function(request) {
  6391. var xhr = request.xhr,
  6392. headers