/ext-4.1.0_b3/docs/extjs/examples/kitchensink/all-classes.js
JavaScript | 14401 lines | 9947 code | 835 blank | 3619 comment | 856 complexity | 1a8790657b1626f7a21dd493d975bf3f MD5 | raw file
- /*
- Copyright(c) 2011 Sencha
- */
- /**
- * Base class that provides a common interface for publishing events. Subclasses are expected to to have a property
- * "events" with all the events defined, and, optionally, a property "listeners" with configured listeners defined.
- *
- * For example:
- *
- * Ext.define('Employee', {
- * mixins: {
- * observable: 'Ext.util.Observable'
- * },
- *
- * constructor: function (config) {
- * // The Observable constructor copies all of the properties of `config` on
- * // to `this` using {@link Ext#apply}. Further, the `listeners` property is
- * // processed to add listeners.
- * //
- * this.mixins.observable.constructor.call(this, config);
- *
- * this.addEvents(
- * 'fired',
- * 'quit'
- * );
- * }
- * });
- *
- * This could then be used like this:
- *
- * var newEmployee = new Employee({
- * name: employeeName,
- * listeners: {
- * quit: function() {
- * // By default, "this" will be the object that fired the event.
- * alert(this.name + " has quit!");
- * }
- * }
- * });
- */
- Ext.define('Ext.util.Observable', {
- /* Begin Definitions */
- requires: ['Ext.util.Event'],
- statics: {
- /**
- * Removes **all** added captures from the Observable.
- *
- * @param {Ext.util.Observable} o The Observable to release
- * @static
- */
- releaseCapture: function(o) {
- o.fireEvent = this.prototype.fireEvent;
- },
- /**
- * Starts capture on the specified Observable. All events will be passed to the supplied function with the event
- * name + standard signature of the event **before** the event is fired. If the supplied function returns false,
- * the event will not fire.
- *
- * @param {Ext.util.Observable} o The Observable to capture events from.
- * @param {Function} fn The function to call when an event is fired.
- * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to
- * the Observable firing the event.
- * @static
- */
- capture: function(o, fn, scope) {
- o.fireEvent = Ext.Function.createInterceptor(o.fireEvent, fn, scope);
- },
- /**
- * Sets observability on the passed class constructor.
- *
- * This makes any event fired on any instance of the passed class also fire a single event through
- * the **class** allowing for central handling of events on many instances at once.
- *
- * Usage:
- *
- * Ext.util.Observable.observe(Ext.data.Connection);
- * Ext.data.Connection.on('beforerequest', function(con, options) {
- * console.log('Ajax request made to ' + options.url);
- * });
- *
- * @param {Function} c The class constructor to make observable.
- * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
- * @static
- */
- observe: function(cls, listeners) {
- if (cls) {
- if (!cls.isObservable) {
- Ext.applyIf(cls, new this());
- this.capture(cls.prototype, cls.fireEvent, cls);
- }
- if (Ext.isObject(listeners)) {
- cls.on(listeners);
- }
- return cls;
- }
- }
- },
- /* End Definitions */
- /**
- * @cfg {Object} listeners
- *
- * A config object containing one or more event handlers to be added to this object during initialization. This
- * should be a valid listeners config object as specified in the {@link #addListener} example for attaching multiple
- * handlers at once.
- *
- * **DOM events from Ext JS {@link Ext.Component Components}**
- *
- * While _some_ Ext JS Component classes export selected DOM events (e.g. "click", "mouseover" etc), this is usually
- * only done when extra value can be added. For example the {@link Ext.view.View DataView}'s **`{@link
- * Ext.view.View#itemclick itemclick}`** event passing the node clicked on. To access DOM events directly from a
- * child element of a Component, we need to specify the `element` option to identify the Component property to add a
- * DOM listener to:
- *
- * new Ext.panel.Panel({
- * width: 400,
- * height: 200,
- * dockedItems: [{
- * xtype: 'toolbar'
- * }],
- * listeners: {
- * click: {
- * element: 'el', //bind to the underlying el property on the panel
- * fn: function(){ console.log('click el'); }
- * },
- * dblclick: {
- * element: 'body', //bind to the underlying body property on the panel
- * fn: function(){ console.log('dblclick body'); }
- * }
- * }
- * });
- */
- /**
- * @property {Boolean} isObservable
- * `true` in this class to identify an objact as an instantiated Observable, or subclass thereof.
- */
- isObservable: true,
- constructor: function(config) {
- var me = this;
- Ext.apply(me, config);
- // Hash of event "hasListeners" flags.
- // For repeated events in time-critical code, the firing code should use
- // if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) { //code... }
- // Bubbling the events counts as one listener.
- // The subclass may have already initialized it.
- me.hasListeners = me.hasListeners || {};
- me.events = me.events || {};
- if (me.listeners) {
- me.on(me.listeners);
- me.listeners = null; //Set as an instance property to pre-empt the prototype in case any are set there.
- }
- if (me.bubbleEvents) {
- me.enableBubble(me.bubbleEvents);
- }
- },
- // @private
- eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|vertical|horizontal|freezeEvent)$/,
- /**
- * Adds listeners to any Observable object (or Ext.Element) which are automatically removed when this Component is
- * destroyed.
- *
- * @param {Ext.util.Observable/Ext.Element} item The item to which to add a listener/listeners.
- * @param {Object/String} ename The event name, or an object containing event name properties.
- * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function.
- * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference)
- * in which the handler function is executed.
- * @param {Object} opt (optional) If the `ename` parameter was an event name, this is the
- * {@link Ext.util.Observable#addListener addListener} options.
- */
- addManagedListener : function(item, ename, fn, scope, options) {
- var me = this,
- managedListeners = me.managedListeners = me.managedListeners || [],
- config;
- if (typeof ename !== 'string') {
- options = ename;
- for (ename in options) {
- if (options.hasOwnProperty(ename)) {
- config = options[ename];
- if (!me.eventOptionsRe.test(ename)) {
- me.addManagedListener(item, ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
- }
- }
- }
- }
- else {
- managedListeners.push({
- item: item,
- ename: ename,
- fn: fn,
- scope: scope,
- options: options
- });
- item.on(ename, fn, scope, options);
- }
- },
- /**
- * Removes listeners that were added by the {@link #mon} method.
- *
- * @param {Ext.util.Observable/Ext.Element} item The item from which to remove a listener/listeners.
- * @param {Object/String} ename The event name, or an object containing event name properties.
- * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function.
- * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference)
- * in which the handler function is executed.
- */
- removeManagedListener : function(item, ename, fn, scope) {
- var me = this,
- options,
- config,
- managedListeners,
- length,
- i;
- if (typeof ename !== 'string') {
- options = ename;
- for (ename in options) {
- if (options.hasOwnProperty(ename)) {
- config = options[ename];
- if (!me.eventOptionsRe.test(ename)) {
- me.removeManagedListener(item, ename, config.fn || config, config.scope || options.scope);
- }
- }
- }
- }
- managedListeners = me.managedListeners ? me.managedListeners.slice() : [];
- for (i = 0, length = managedListeners.length; i < length; i++) {
- me.removeManagedListenerItem(false, managedListeners[i], item, ename, fn, scope);
- }
- },
- /**
- * Fires the specified event with the passed parameters (minus the event name, plus the `options` object passed
- * to {@link #addListener}).
- *
- * An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget}) by
- * calling {@link #enableBubble}.
- *
- * @param {String} eventName The name of the event to fire.
- * @param {Object...} args Variable number of parameters are passed to handlers.
- * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
- */
- fireEvent: function(eventName) {
- eventName = eventName.toLowerCase();
- var me = this,
- events = me.events,
- event = events && events[eventName];
- // Only continue firing the event if there are listeners to be informed.
- // Bubbled events will always have a listener count, so will be fired.
- if (event && me.hasListeners[eventName]) {
- return me.continueFireEvent(eventName, Ext.Array.slice(arguments, 1), event.bubble);
- }
- },
- /**
- * Continue to fire event.
- * @private
- *
- * @param {String} eventName
- * @param {Array} args
- * @param {Boolean} bubbles
- */
- continueFireEvent: function(eventName, args, bubbles) {
- var target = this,
- queue, event,
- ret = true;
- do {
- if (target.eventsSuspended === true) {
- if ((queue = target.eventQueue)) {
- queue.push([eventName, args, bubbles]);
- }
- return ret;
- } else {
- event = target.events[eventName];
- // Continue bubbling if event exists and it is `true` or the handler didn't returns false and it
- // configure to bubble.
- if (event && event != true) {
- if ((ret = event.fire.apply(event, args)) === false) {
- break;
- }
- }
- }
- } while (bubbles && (target = target.getBubbleParent()));
- return ret;
- },
- /**
- * Gets the bubbling parent for an Observable
- * @private
- * @return {Ext.util.Observable} The bubble parent. null is returned if no bubble target exists
- */
- getBubbleParent: function(){
- var me = this, parent = me.getBubbleTarget && me.getBubbleTarget();
- if (parent && parent.isObservable) {
- return parent;
- }
- return null;
- },
- /**
- * Appends an event handler to this object. For example:
- *
- * myGridPanel.on("mouseover", this.onMouseOver, this);
- *
- * The method also allows for a single argument to be passed which is a config object
- * containing properties which specify multiple events. For example:
- *
- * myGridPanel.on({
- * cellClick: this.onCellClick,
- * mouseover: this.onMouseOver,
- * mouseout: this.onMouseOut,
- * scope: this // Important. Ensure "this" is correct during handler execution
- * });
- *
- * One can also specify options for each event handler separately:
- *
- * myGridPanel.on({
- * cellClick: {fn: this.onCellClick, scope: this, single: true},
- * mouseover: {fn: panel.onMouseOver, scope: panel}
- * });
- *
- * *Names* of methods in a specified scope may also be used. Note that
- * `scope` MUST be specified to use this option:
- *
- * myGridPanel.on({
- * cellClick: {fn: 'onCellClick', scope: this, single: true},
- * mouseover: {fn: 'onMouseOver', scope: panel}
- * });
- *
- * @param {String/Object} eventName The name of the event to listen for.
- * May also be an object who's property names are event names.
- *
- * @param {Function} [fn] The method the event invokes, or *if `scope` is specified, the *name* of the method within
- * the specified `scope`. Will be called with arguments
- * given to {@link #fireEvent} plus the `options` parameter described below.
- *
- * @param {Object} [scope] The scope (`this` reference) in which the handler function is
- * executed. **If omitted, defaults to the object which fired the event.**
- *
- * @param {Object} [options] An object containing handler configuration.
- *
- * **Note:** Unlike in ExtJS 3.x, the options object will also be passed as the last
- * argument to every event handler.
- *
- * This object may contain any of the following properties:
- *
- * @param {Object} options.scope
- * The scope (`this` reference) in which the handler function is executed. **If omitted,
- * defaults to the object which fired the event.**
- *
- * @param {Number} options.delay
- * The number of milliseconds to delay the invocation of the handler after the event fires.
- *
- * @param {Boolean} options.single
- * True to add a handler to handle just the next firing of the event, and then remove itself.
- *
- * @param {Number} options.buffer
- * Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
- * by the specified number of milliseconds. If the event fires again within that time,
- * the original handler is _not_ invoked, but the new handler is scheduled in its place.
- *
- * @param {Ext.util.Observable} options.target
- * Only call the handler if the event was fired on the target Observable, _not_ if the event
- * was bubbled up from a child Observable.
- *
- * @param {String} options.element
- * **This option is only valid for listeners bound to {@link Ext.Component Components}.**
- * The name of a Component property which references an element to add a listener to.
- *
- * This option is useful during Component construction to add DOM event listeners to elements of
- * {@link Ext.Component Components} which will exist only after the Component is rendered.
- * For example, to add a click listener to a Panel's body:
- *
- * new Ext.panel.Panel({
- * title: 'The title',
- * listeners: {
- * click: this.handlePanelClick,
- * element: 'body'
- * }
- * });
- *
- * **Combining Options**
- *
- * Using the options argument, it is possible to combine different types of listeners:
- *
- * A delayed, one-time listener.
- *
- * myPanel.on('hide', this.handleClick, this, {
- * single: true,
- * delay: 100
- * });
- *
- */
- addListener: function(ename, fn, scope, options) {
- var me = this,
- config,
- event;
- if (typeof ename !== 'string') {
- options = ename;
- for (ename in options) {
- if (options.hasOwnProperty(ename)) {
- config = options[ename];
- if (!me.eventOptionsRe.test(ename)) {
- me.addListener(ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
- }
- }
- }
- }
- else {
- ename = ename.toLowerCase();
- me.events[ename] = me.events[ename] || true;
- event = me.events[ename] || true;
- if (Ext.isBoolean(event)) {
- me.events[ename] = event = new Ext.util.Event(me, ename);
- }
- // Allow listeners: { click: 'onClick', scope: myObject }
- if (typeof fn === 'string') {
- fn = scope[fn] || me.fn;
- }
- event.addListener(fn, scope, Ext.isObject(options) ? options : {});
- // Maintain count of listeners for each event name.
- // For repeated events in time-critical code, the firing code should use
- // if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) { //code... }
- me.hasListeners[ename] = (me.hasListeners[ename]||0) + 1;
- }
- },
- /**
- * Removes an event handler.
- *
- * @param {String} eventName The type of event the handler was associated with.
- * @param {Function} fn The handler to remove. **This must be a reference to the function passed into the
- * {@link #addListener} call.**
- * @param {Object} scope (optional) The scope originally specified for the handler. It must be the same as the
- * scope argument specified in the original call to {@link #addListener} or the listener will not be removed.
- */
- removeListener: function(ename, fn, scope) {
- var me = this,
- config,
- event,
- options;
- if (typeof ename !== 'string') {
- options = ename;
- for (ename in options) {
- if (options.hasOwnProperty(ename)) {
- config = options[ename];
- if (!me.eventOptionsRe.test(ename)) {
- me.removeListener(ename, config.fn || config, config.scope || options.scope);
- }
- }
- }
- } else {
- ename = ename.toLowerCase();
- event = me.events[ename];
- if (event && event.isEvent) {
- event.removeListener(fn, scope);
- // Maintain count of listeners for each event name.
- // For repeated events in time-critical code, the firing code should use
- // if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) { //code... }
- me.hasListeners[ename]--;
- }
- }
- },
- /**
- * Removes all listeners for this object including the managed listeners
- */
- clearListeners: function() {
- var events = this.events,
- event,
- key;
- for (key in events) {
- if (events.hasOwnProperty(key)) {
- event = events[key];
- if (event.isEvent) {
- event.clearListeners();
- }
- }
- }
- this.clearManagedListeners();
- },
- purgeListeners : function() {
- if (Ext.global.console) {
- Ext.global.console.warn('Observable: purgeListeners has been deprecated. Please use clearListeners.');
- }
- return this.clearListeners.apply(this, arguments);
- },
- /**
- * Removes all managed listeners for this object.
- */
- clearManagedListeners : function() {
- var managedListeners = this.managedListeners || [],
- i = 0,
- len = managedListeners.length;
- for (; i < len; i++) {
- this.removeManagedListenerItem(true, managedListeners[i]);
- }
- this.managedListeners = [];
- },
- /**
- * Remove a single managed listener item
- * @private
- * @param {Boolean} isClear True if this is being called during a clear
- * @param {Object} managedListener The managed listener item
- * See removeManagedListener for other args
- */
- removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
- if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
- managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope);
- if (!isClear) {
- Ext.Array.remove(this.managedListeners, managedListener);
- }
- }
- },
- purgeManagedListeners : function() {
- if (Ext.global.console) {
- Ext.global.console.warn('Observable: purgeManagedListeners has been deprecated. Please use clearManagedListeners.');
- }
- return this.clearManagedListeners.apply(this, arguments);
- },
- /**
- * Adds the specified events to the list of events which this Observable may fire.
- *
- * @param {Object/String...} eventNames Either an object with event names as properties with
- * a value of `true`. For example:
- *
- * this.addEvents({
- * storeloaded: true,
- * storecleared: true
- * });
- *
- * Or any number of event names as separate parameters. For example:
- *
- * this.addEvents('storeloaded', 'storecleared');
- *
- */
- addEvents: function(o) {
- var me = this,
- events = me.events || (me.events = {}),
- arg, args, i;
- if (typeof o == 'string') {
- for (args = arguments, i = args.length; i--; ) {
- arg = args[i];
- if (!events[arg]) {
- events[arg] = true;
- }
- }
- } else {
- Ext.applyIf(me.events, o);
- }
- },
- /**
- * Checks to see if this object has any listeners for a specified event, or whether the event bubbles. The answer
- * indicates whether the event needs firing or not.
- *
- * @param {String} eventName The name of the event to check for
- * @return {Boolean} `true` if the event is being listened for or bubbles, else `false`
- */
- hasListener: function(ename) {
- return !!this.hasListeners[ename.toLowerCase()];
- },
- /**
- * Suspends the firing of all events. (see {@link #resumeEvents})
- *
- * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
- * after the {@link #resumeEvents} call instead of discarding all suspended events.
- */
- suspendEvents: function(queueSuspended) {
- this.eventsSuspended = true;
- if (queueSuspended && !this.eventQueue) {
- this.eventQueue = [];
- }
- },
- /**
- * Resumes firing events (see {@link #suspendEvents}).
- *
- * If events were suspended using the `queueSuspended` parameter, then all events fired
- * during event suspension will be sent to any listeners now.
- */
- resumeEvents: function() {
- var me = this,
- queued = me.eventQueue,
- qLen, q;
- me.eventsSuspended = false;
- delete me.eventQueue;
- if (queued) {
- qLen = queued.length;
- for (q = 0; q < qLen; q++) {
- me.continueFireEvent.apply(me, queued[q]);
- }
- }
- },
- /**
- * Relays selected events from the specified Observable as if the events were fired by `this`.
- *
- * For example if you are extending Grid, you might decide to forward some events from store.
- * So you can do this inside your initComponent:
- *
- * this.relayEvents(this.getStore(), ['load']);
- *
- * The grid instance will then have an observable 'load' event which will be passed the
- * parameters of the store's load event and any function fired with the grid's load event
- * would have access to the grid using the `this` keyword.
- *
- * @param {Object} origin The Observable whose events this object is to relay.
- * @param {String[]} events Array of event names to relay.
- * @param {String} [prefix] A common prefix to attach to the event names. For example:
- *
- * this.relayEvents(this.getStore(), ['load', 'clear'], 'store');
- *
- * Now the grid will forward 'load' and 'clear' events of store as 'storeload' and 'storeclear'.
- */
- relayEvents : function(origin, events, prefix) {
- prefix = prefix || '';
- var me = this,
- len = events.length,
- i = 0,
- oldName,
- newName;
- for (; i < len; i++) {
- oldName = events[i];
- newName = prefix + oldName;
- me.events[newName] = me.events[newName] || true;
- origin.on(oldName, me.createRelayer(newName));
- }
- },
- /**
- * @private
- * Creates an event handling function which refires the event from this object as the passed event name.
- * @param newName
- * @param {Array} beginEnd (optional) The caller can specify on which indices to slice
- * @returns {Function}
- */
- createRelayer: function(newName, beginEnd){
- var me = this;
- return function(){
- return me.fireEvent.apply(me, [newName].concat(Array.prototype.slice.apply(arguments, beginEnd || [0, -1])));
- };
- },
- /**
- * Enables events fired by this Observable to bubble up an owner hierarchy by calling `this.getBubbleTarget()` if
- * present. There is no implementation in the Observable base class.
- *
- * This is commonly used by Ext.Components to bubble events to owner Containers.
- * See {@link Ext.Component#getBubbleTarget}. The default implementation in Ext.Component returns the
- * Component's immediate owner. But if a known target is required, this can be overridden to access the
- * required target more quickly.
- *
- * Example:
- *
- * Ext.override(Ext.form.field.Base, {
- * // Add functionality to Field's initComponent to enable the change event to bubble
- * initComponent : Ext.Function.createSequence(Ext.form.field.Base.prototype.initComponent, function() {
- * this.enableBubble('change');
- * }),
- *
- * // We know that we want Field's events to bubble directly to the FormPanel.
- * getBubbleTarget : function() {
- * if (!this.formPanel) {
- * this.formPanel = this.findParentByType('form');
- * }
- * return this.formPanel;
- * }
- * });
- *
- * var myForm = new Ext.formPanel({
- * title: 'User Details',
- * items: [{
- * ...
- * }],
- * listeners: {
- * change: function() {
- * // Title goes red if form has been modified.
- * myForm.header.setStyle('color', 'red');
- * }
- * }
- * });
- *
- * @param {String/String[]} eventNames The event name to bubble, or an Array of event names.
- */
- enableBubble: function(eventNames) {
- if (eventNames) {
- var me = this,
- names = (typeof eventNames == 'string') ? arguments : eventNames,
- length = names.length,
- events = me.events,
- ename, event, i;
- for (i = 0; i < length; ++i) {
- ename = names[i].toLowerCase();
- event = events[ename];
- if (!event || typeof event == 'boolean') {
- events[ename] = event = new Ext.util.Event(me, ename);
- }
- // Event must fire if it bubbles (We don't know if anyone up the bubble hierarchy has listeners added)
- me.hasListeners[ename] = (me.hasListeners[ename]||0) + 1;
- event.bubble = true;
- }
- }
- }
- }, function() {
- this.createAlias({
- /**
- * @method
- * Shorthand for {@link #addListener}.
- * @inheritdoc Ext.util.Observable#addListener
- */
- on: 'addListener',
- /**
- * @method
- * Shorthand for {@link #removeListener}.
- * @inheritdoc Ext.util.Observable#removeListener
- */
- un: 'removeListener',
- /**
- * @method
- * Shorthand for {@link #addManagedListener}.
- * @inheritdoc Ext.util.Observable#addManagedListener
- */
- mon: 'addManagedListener',
- /**
- * @method
- * Shorthand for {@link #removeManagedListener}.
- * @inheritdoc Ext.util.Observable#removeManagedListener
- */
- mun: 'removeManagedListener'
- });
- //deprecated, will be removed in 5.0
- this.observeClass = this.observe;
- Ext.apply(Ext.util.Observable.prototype, function(){
- // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
- // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
- // private
- function getMethodEvent(method){
- var e = (this.methodEvents = this.methodEvents || {})[method],
- returnValue,
- v,
- cancel,
- obj = this;
- if (!e) {
- this.methodEvents[method] = e = {};
- e.originalFn = this[method];
- e.methodName = method;
- e.before = [];
- e.after = [];
- var makeCall = function(fn, scope, args){
- if((v = fn.apply(scope || obj, args)) !== undefined){
- if (typeof v == 'object') {
- if(v.returnValue !== undefined){
- returnValue = v.returnValue;
- }else{
- returnValue = v;
- }
- cancel = !!v.cancel;
- }
- else
- if (v === false) {
- cancel = true;
- }
- else {
- returnValue = v;
- }
- }
- };
- this[method] = function(){
- var args = Array.prototype.slice.call(arguments, 0),
- b, i, len;
- returnValue = v = undefined;
- cancel = false;
- for(i = 0, len = e.before.length; i < len; i++){
- b = e.before[i];
- makeCall(b.fn, b.scope, args);
- if (cancel) {
- return returnValue;
- }
- }
- if((v = e.originalFn.apply(obj, args)) !== undefined){
- returnValue = v;
- }
- for(i = 0, len = e.after.length; i < len; i++){
- b = e.after[i];
- makeCall(b.fn, b.scope, args);
- if (cancel) {
- return returnValue;
- }
- }
- return returnValue;
- };
- }
- return e;
- }
- return {
- // these are considered experimental
- // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
- // adds an 'interceptor' called before the original method
- beforeMethod : function(method, fn, scope){
- getMethodEvent.call(this, method).before.push({
- fn: fn,
- scope: scope
- });
- },
- // adds a 'sequence' called after the original method
- afterMethod : function(method, fn, scope){
- getMethodEvent.call(this, method).after.push({
- fn: fn,
- scope: scope
- });
- },
- removeMethodListener: function(method, fn, scope){
- var e = this.getMethodEvent(method),
- i, len;
- for(i = 0, len = e.before.length; i < len; i++){
- if(e.before[i].fn == fn && e.before[i].scope == scope){
- Ext.Array.erase(e.before, i, 1);
- return;
- }
- }
- for(i = 0, len = e.after.length; i < len; i++){
- if(e.after[i].fn == fn && e.after[i].scope == scope){
- Ext.Array.erase(e.after, i, 1);
- return;
- }
- }
- },
- toggleEventLogging: function(toggle) {
- Ext.util.Observable[toggle ? 'capture' : 'releaseCapture'](this, function(en) {
- if (Ext.isDefined(Ext.global.console)) {
- Ext.global.console.log(en, arguments);
- }
- });
- }
- };
- }());
- });
- /**
- * @author Ed Spencer
- *
- * Associations enable you to express relationships between different {@link Ext.data.Model Models}. Let's say we're
- * writing an ecommerce system where Users can make Orders - there's a relationship between these Models that we can
- * express like this:
- *
- * Ext.define('User', {
- * extend: 'Ext.data.Model',
- * fields: ['id', 'name', 'email'],
- *
- * hasMany: {model: 'Order', name: 'orders'}
- * });
- *
- * Ext.define('Order', {
- * extend: 'Ext.data.Model',
- * fields: ['id', 'user_id', 'status', 'price'],
- *
- * belongsTo: 'User'
- * });
- *
- * We've set up two models - User and Order - and told them about each other. You can set up as many associations on
- * each Model as you need using the two default types - {@link Ext.data.HasManyAssociation hasMany} and {@link
- * Ext.data.BelongsToAssociation belongsTo}. There's much more detail on the usage of each of those inside their
- * documentation pages. If you're not familiar with Models already, {@link Ext.data.Model there is plenty on those too}.
- *
- * **Further Reading**
- *
- * - {@link Ext.data.association.HasMany hasMany associations}
- * - {@link Ext.data.association.BelongsTo belongsTo associations}
- * - {@link Ext.data.association.HasOne hasOne associations}
- * - {@link Ext.data.Model using Models}
- *
- * # Self association models
- *
- * We can also have models that create parent/child associations between the same type. Below is an example, where
- * groups can be nested inside other groups:
- *
- * // Server Data
- * {
- * "groups": {
- * "id": 10,
- * "parent_id": 100,
- * "name": "Main Group",
- * "parent_group": {
- * "id": 100,
- * "parent_id": null,
- * "name": "Parent Group"
- * },
- * "child_groups": [{
- * "id": 2,
- * "parent_id": 10,
- * "name": "Child Group 1"
- * },{
- * "id": 3,
- * "parent_id": 10,
- * "name": "Child Group 2"
- * },{
- * "id": 4,
- * "parent_id": 10,
- * "name": "Child Group 3"
- * }]
- * }
- * }
- *
- * // Client code
- * Ext.define('Group', {
- * extend: 'Ext.data.Model',
- * fields: ['id', 'parent_id', 'name'],
- * proxy: {
- * type: 'ajax',
- * url: 'data.json',
- * reader: {
- * type: 'json',
- * root: 'groups'
- * }
- * },
- * associations: [{
- * type: 'hasMany',
- * model: 'Group',
- * primaryKey: 'id',
- * foreignKey: 'parent_id',
- * autoLoad: true,
- * associationKey: 'child_groups' // read child data from child_groups
- * }, {
- * type: 'belongsTo',
- * model: 'Group',
- * primaryKey: 'id',
- * foreignKey: 'parent_id',
- * associationKey: 'parent_group' // read parent data from parent_group
- * }]
- * });
- *
- * Ext.onReady(function(){
- *
- * Group.load(10, {
- * success: function(group){
- * console.log(group.getGroup().get('name'));
- *
- * group.groups().each(function(rec){
- * console.log(rec.get('name'));
- * });
- * }
- * });
- *
- * });
- *
- */
- Ext.define('Ext.data.association.Association', {
- alternateClassName: 'Ext.data.Association',
- /**
- * @cfg {String} ownerModel (required)
- * The string name of the model that owns the association.
- */
- /**
- * @cfg {String} associatedModel (required)
- * The string name of the model that is being associated with.
- */
- /**
- * @cfg {String} primaryKey
- * The name of the primary key on the associated model. In general this will be the
- * {@link Ext.data.Model#idProperty} of the Model.
- */
- primaryKey: 'id',
- /**
- * @cfg {Ext.data.reader.Reader} reader
- * A special reader to read associated data
- */
-
- /**
- * @cfg {String} associationKey
- * The name of the property in the data to read the association from. Defaults to the name of the associated model.
- */
- defaultReaderType: 'json',
- statics: {
- create: function(association){
- if (!association.isAssociation) {
- if (Ext.isString(association)) {
- association = {
- type: association
- };
- }
- switch (association.type) {
- case 'belongsTo':
- return new Ext.data.association.BelongsTo(association);
- case 'hasMany':
- return new Ext.data.association.HasMany(association);
- case 'hasOne':
- return new Ext.data.association.HasOne(association);
- //TODO Add this back when it's fixed
- // case 'polymorphic':
- // return Ext.create('Ext.data.PolymorphicAssociation', association);
- default:
- Ext.Error.raise('Unknown Association type: "' + association.type + '"');
- }
- }
- return association;
- }
- },
- /**
- * Creates the Association object.
- * @param {Object} [config] Config object.
- */
- constructor: function(config) {
- Ext.apply(this, config);
- var types = Ext.ModelManager.types,
- ownerName = config.ownerModel,
- associatedName = config.associatedModel,
- ownerModel = types[ownerName],
- associatedModel = types[associatedName],
- ownerProto;
- if (ownerModel === undefined) {
- Ext.Error.raise("The configured ownerModel was not valid (you tried " + ownerName + ")");
- }
- if (associatedModel === undefined) {
- Ext.Error.raise("The configured associatedModel was not valid (you tried " + associatedName + ")");
- }
- this.ownerModel = ownerModel;
- this.associatedModel = associatedModel;
- /**
- * @property {String} ownerName
- * The name of the model that 'owns' the association
- */
- /**
- * @property {String} associatedName
- * The name of the model is on the other end of the association (e.g. if a User model hasMany Orders, this is
- * 'Order')
- */
- Ext.applyIf(this, {
- ownerName : ownerName,
- associatedName: associatedName
- });
- },
- /**
- * Get a specialized reader for reading associated data
- * @return {Ext.data.reader.Reader} The reader, null if not supplied
- */
- getReader: function(){
- var me = this,
- reader = me.reader,
- model = me.associatedModel;
- if (reader) {
- if (Ext.isString(reader)) {
- reader = {
- type: reader
- };
- }
- if (reader.isReader) {
- reader.setModel(model);
- } else {
- Ext.applyIf(reader, {
- model: model,
- type : me.defaultReaderType
- });
- }
- me.reader = Ext.createByAlias('reader.' + reader.type, reader);
- }
- return me.reader || null;
- }
- });
- /**
- * @author Don Griffin
- *
- * This class is a base for all id generators. It also provides lookup of id generators by
- * their id.
- *
- * Generally, id generators are used to generate a primary key for new model instances. There
- * are different approaches to solving this problem, so this mechanism has both simple use
- * cases and is open to custom implementations. A {@link Ext.data.Model} requests id generation
- * using the {@link Ext.data.Model#idgen} property.
- *
- * # Identity, Type and Shared IdGenerators
- *
- * It is often desirable to share IdGenerators to ensure uniqueness or common configuration.
- * This is done by giving IdGenerator instances an id property by which they can be looked
- * up using the {@link #get} method. To configure two {@link Ext.data.Model Model} classes
- * to share one {@link Ext.data.SequentialIdGenerator sequential} id generator, you simply
- * assign them the same id:
- *
- * Ext.define('MyApp.data.MyModelA', {
- * extend: 'Ext.data.Model',
- * idgen: {
- * type: 'sequential',
- * id: 'foo'
- * }
- * });
- *
- * Ext.define('MyApp.data.MyModelB', {
- * extend: 'Ext.data.Model',
- * idgen: {
- * type: 'sequential',
- * id: 'foo'
- * }
- * });
- *
- * To make this as simple as possible for generator types that are shared by many (or all)
- * Models, the IdGenerator types (such as 'sequential' or 'uuid') are also reserved as
- * generator id's. This is used by the {@link Ext.data.UuidGenerator} which has an id equal
- * to its type ('uuid'). In other words, the following Models share the same generator:
- *
- * Ext.define('MyApp.data.MyModelX', {
- * extend: 'Ext.data.Model',
- * idgen: 'uuid'
- * });
- *
- * Ext.define('MyApp.data.MyModelY', {
- * extend: 'Ext.data.Model',
- * idgen: 'uuid'
- * });
- *
- * This can be overridden (by specifying the id explicitly), but there is no particularly
- * good reason to do so for this generator type.
- *
- * # Creating Custom Generators
- *
- * An id generator should derive from this class and implement the {@link #generate} method.
- * The constructor will apply config properties on new instances, so a constructor is often
- * not necessary.
- *
- * To register an id generator type, a derived class should provide an `alias` like so:
- *
- * Ext.define('MyApp.data.CustomIdGenerator', {
- * extend: 'Ext.data.IdGenerator',
- * alias: 'idgen.custom',
- *
- * configProp: 42, // some config property w/default value
- *
- * generate: function () {
- * return ... // a new id
- * }
- * });
- *
- * Using the custom id generator is then straightforward:
- *
- * Ext.define('MyApp.data.MyModel', {
- * extend: 'Ext.data.Model',
- * idgen: 'custom'
- * });
- * // or...
- *
- * Ext.define('MyApp.data.MyModel', {
- * extend: 'Ext.data.Model',
- * idgen: {
- * type: 'custom',
- * configProp: value
- * }
- * });
- *
- * It is not recommended to mix shared generators with generator configuration. This leads
- * to unpredictable results unless all configurations match (which is also redundant). In
- * such cases, a custom generator with a default id is the best approach.
- *
- * Ext.define('MyApp.data.CustomIdGenerator', {
- * extend: 'Ext.data.SequentialIdGenerator',
- * alias: 'idgen.custom',
- *
- * id: 'custom', // shared by default
- *
- * prefix: 'ID_',
- * seed: 1000
- * });
- *
- * Ext.define('MyApp.data.MyModelX', {
- * extend: 'Ext.data.Model',
- * idgen: 'custom'
- * });
- *
- * Ext.define('MyApp.data.MyModelY', {
- * extend: 'Ext.data.Model',
- * idgen: 'custom'
- * });
- *
- * // the above models share a generator that produces ID_1000, ID_1001, etc..
- *
- */
- Ext.define('Ext.data.IdGenerator', {
- /**
- * @property {Boolean} isGenerator
- * `true` in this class to identify an objact as an instantiated IdGenerator, or subclass thereof.
- */
- isGenerator: true,
- /**
- * Initializes a new instance.
- * @param {Object} config (optional) Configuration object to be applied to the new instance.
- */
- constructor: function(config) {
- var me = this;
- Ext.apply(me, config);
- if (me.id) {
- Ext.data.IdGenerator.all[me.id] = me;
- }
- },
- /**
- * @cfg {String} id
- * The id by which to register a new instance. This instance can be found using the
- * {@link Ext.data.IdGenerator#get} static method.
- */
- getRecId: function (rec) {
- return rec.modelName + '-' + rec.internalId;
- },
- /**
- * Generates and returns the next id. This method must be implemented by the derived
- * class.
- *
- * @return {String} The next id.
- * @method generate
- * @abstract
- */
- statics: {
- /**
- * @property {Object} all
- * This object is keyed by id to lookup instances.
- * @private
- * @static
- */
- all: {},
- /**
- * Returns the IdGenerator given its config description.
- * @param {String/Object} config If this parameter is an IdGenerator instance, it is
- * simply returned. If this is a string, it is first used as an id for lookup and
- * then, if there is no match, as a type to create a new instance. This parameter
- * can also be a config object that contains a `type` property (among others) that
- * are used to create and configure the instance.
- * @static
- */
- get: function (config) {
- var generator,
- id,
- type;
- if (typeof config == 'string') {
- id = type = config;
- config = null;
- } else if (config.isGenerator) {
- return config;
- } else {
- id = config.id || config.type;
- type = config.type;
- }
- generator = this.all[id];
- if (!generator) {
- generator = Ext.create('idgen.' + type, config);
- }
- return generator;
- }
- }
- });
- /**
- * @author Ed Spencer
- *
- * Represents a single read or write operation performed by a {@link Ext.data.proxy.Proxy Proxy}. Operation objects are
- * used to enable communication between Stores and Proxies. Application developers should rarely need to interact with
- * Operation objects directly.
- *
- * Several Operations can be batched together in a {@link Ext.data.Batch batch}.
- */
- Ext.define('Ext.data.Operation', {
- /**
- * @cfg {Boolean} synchronous
- * True if this Operation is to be executed synchronously. This property is inspected by a
- * {@link Ext.data.Batch Batch} to see if a series of Operations can be executed in parallel or not.
- */
- synchronous: true,
- /**
- * @cfg {String} action
- * The action being performed by this Operation. Should be one of 'create', 'read', 'update' or 'destroy'.
- */
- action: undefined,
- /**
- * @cfg {Ext.util.Filter[]} filters
- * Optional array of filter objects. Only applies to 'read' actions.
- */
- filters: undefined,
- /**
- * @cfg {Ext.util.Sorter[]} sorters
- * Optional array of sorter objects. Only applies to 'read' actions.
- */
- sorters: undefined,
- /**
- * @cfg {Ext.util.Grouper[]} groupers
- * Optional grouping configuration. Only applies to 'read' actions where grouping is desired.
- */
- groupers: undefined,
- /**
- * @cfg {Number} start
- * The start index (offset), used in paging when running a 'read' action.
- */
- start: undefined,
- /**
- * @cfg {Number} limit
- * The number of records to load. Used on 'read' actions when paging is being used.
- */
- limit: undefined,
- /**
- * @cfg {Ext.data.Batch} batch
- * The batch that this Operation is a part of.
- */
- batch: undefined,
- /**
- * @cfg {Function} callback
- * Function to execute when operation completed.
- * @cfg {Ext.data.Model[]} callback.records Array of records.
- * @cfg {Ext.data.Operation} callback.operation The Operation itself.
- * @cfg {Boolean} callback.success True when operation completed successfully.
- */
- callback: undefined,
- /**
- * @cfg {Object} scope
- * Scope for the {@link #callback} function.
- */
- scope: undefined,
- /**
- * @property {Boolean} started
- * The start status of this Operation. Use {@link #isStarted}.
- * @readonly
- * @private
- */
- started: false,
- /**
- * @property {Boolean} running
- * The run status of this Operation. Use {@link #isRunning}.
- * @readonly
- * @private
- */
- running: false,
- /**
- * @property {Boolean} complete
- * The completion status of this Operation. Use {@link #isComplete}.
- * @readonly
- * @private
- */
- complete: false,
- /**
- * @property {Boolean} success
- * Whether the Operation was successful or not. This starts as undefined and is set to true
- * or false by the Proxy that is executing the Operation. It is also set to false by {@link #setException}. Use
- * {@link #wasSuccessful} to query success status.
- * @readonly
- * @private
- */
- success: undefined,
- /**
- * @property {Boolean} exception
- * The exception status of this Operation. Use {@link #hasException} and see {@link #getError}.
- * @readonly
- * @private
- */
- exception: false,
- /**
- * @property {String/Object} error
- * The error object passed when {@link #setException} was called. This could be any object or primitive.
- * @private
- */
- error: undefined,
- /**
- * @property {RegExp} actionCommitRecordsRe
- * The RegExp used to categorize actions that require record commits.
- */
- actionCommitRecordsRe: /^(?:create|update)$/i,
- /**
- * @property {RegExp} actionSkipSyncRe
- * The RegExp used to categorize actions that skip local record synchronization. This defaults
- * to match 'destroy'.
- */
- actionSkipSyncRe: /^destroy$/i,
- /**
- * Creates new Operation object.
- * @param {Object} config (optional) Config object.
- */
- constructor: function(config) {
- Ext.apply(this, config || {});
- },
- /**
- * This method is called to commit data to this instance's records given the records in
- * the server response. This is followed by calling {@link Ext.data.Model#commit} on all
- * those records (for 'create' and 'update' actions).
- *
- * If this {@link #action} is 'destroy', any server records are ignored and the
- * {@link Ext.data.Model#commit} method is not called.
- *
- * @param {Ext.data.Model[]} serverRecords An array of {@link Ext.data.Model} objects returned by
- * the server.
- * @markdown
- */
- commitRecords: function (serverRecords) {
- var me = this,
- mc, index, clientRecords, serverRec, clientRec;
- if (!me.actionSkipSyncRe.test(me.action)) {
- clientRecords = me.records;
- if (clientRecords && clientRecords.length) {
- if(clientRecords.length > 1) {
- // if this operation has multiple records, client records need to be matched up with server records
- // so that any data returned from the server can be updated in the client records.
- mc = new Ext.util.MixedCollection();
- mc.addAll(serverRecords);
- for (index = clientRecords.length; index--; ) {
- clientRec = clientRecords[index];
- serverRec = mc.findBy(function(record) {
- var clientRecordId = clientRec.getId();
- if(clientRecordId && record.getId() === clientRecordId) {
- return true;
- }
- // if the server record cannot be found by id, find by internalId.
- // this allows client records that did not previously exist on the server
- // to be updated with the correct server id and data.
- return record.internalId === clientRec.internalId;
- });
- // replace client record data with server record data
- me.updateClientRecord(clientRec, serverRec);
- }
- } else {
- // operation only has one record, so just match the first client record up with the first server record
- clientRec = clientRecords[0];
- serverRec = serverRecords[0];
- // if the client record is not a phantom, make sure the ids match before replacing the client data with server data.
- if(serverRec && (clientRec.phantom || clientRec.getId() === serverRec.getId())) {
- me.updateClientRecord(clientRec, serverRec);
- }
- }
- if (me.actionCommitRecordsRe.test(me.action)) {
- for (index = clientRecords.length; index--; ) {
- clientRecords[index].commit();
- }
- }
- }
- }
- },
- /**
- * Replaces the data in a client record with the data from a server record. If either record is undefined, does nothing.
- * Since non-persistent fields will have default values in the server record, this method only replaces data for persistent
- * fields to avoid overwriting the client record's data with default values from the server record.
- * @private
- * @param {Ext.data.Model} [clientRecord]
- * @param {Ext.data.Model} [serverRecord]
- */
- updateClientRecord: function(clientRecord, serverRecord) {
- if (clientRecord && serverRecord) {
- clientRecord.beginEdit();
- var fields = clientRecord.fields.items,
- fLen = fields.length,
- field, f;
- for (f = 0; f < fLen; f++) {
- field = fields[f];
- if (field.persist) {
- clientRecord.set(field.name, serverRecord.get(field.name));
- }
- }
- if(clientRecord.phantom) {
- clientRecord.setId(serverRecord.getId());
- }
- clientRecord.endEdit(true);
- }
- },
- /**
- * Marks the Operation as started.
- */
- setStarted: function() {
- this.started = true;
- this.running = true;
- },
- /**
- * Marks the Operation as completed.
- */
- setCompleted: function() {
- this.complete = true;
- this.running = false;
- },
- /**
- * Marks the Operation as successful.
- */
- setSuccessful: function() {
- this.success = true;
- },
- /**
- * Marks the Operation as having experienced an exception. Can be supplied with an option error message/object.
- * @param {String/Object} error (optional) error string/object
- */
- setException: function(error) {
- this.exception = true;
- this.success = false;
- this.running = false;
- this.error = error;
- },
- /**
- * Returns true if this Operation encountered an exception (see also {@link #getError})
- * @return {Boolean} True if there was an exception
- */
- hasException: function() {
- return this.exception === true;
- },
- /**
- * Returns the error string or object that was set using {@link #setException}
- * @return {String/Object} The error object
- */
- getError: function() {
- return this.error;
- },
- /**
- * Returns the {@link Ext.data.Model record}s associated with this operation. For read operations the records as set by the {@link Ext.data.proxy.Proxy Proxy} will be returned (returns `null` if the proxy has not yet set the records).
- * For create, update, and destroy operations the operation's initially configured records will be returned, although the proxy may modify these records' data at some point after the operation is initialized.
- * @return {Ext.data.Model[]}
- */
- getRecords: function() {
- var resultSet = this.getResultSet();
- return this.records || (resultSet ? resultSet.records : null);
- },
- /**
- * Returns the ResultSet object (if set by the Proxy). This object will contain the {@link Ext.data.Model model}
- * instances as well as meta data such as number of instances fetched, number available etc
- * @return {Ext.data.ResultSet} The ResultSet object
- */
- getResultSet: function() {
- return this.resultSet;
- },
- /**
- * Returns true if the Operation has been started. Note that the Operation may have started AND completed, see
- * {@link #isRunning} to test if the Operation is currently running.
- * @return {Boolean} True if the Operation has started
- */
- isStarted: function() {
- return this.started === true;
- },
- /**
- * Returns true if the Operation has been started but has not yet completed.
- * @return {Boolean} True if the Operation is currently running
- */
- isRunning: function() {
- return this.running === true;
- },
- /**
- * Returns true if the Operation has been completed
- * @return {Boolean} True if the Operation is complete
- */
- isComplete: function() {
- return this.complete === true;
- },
- /**
- * Returns true if the Operation has completed and was successful
- * @return {Boolean} True if successful
- */
- wasSuccessful: function() {
- return this.isComplete() && this.success === true;
- },
- /**
- * @private
- * Associates this Operation with a Batch
- * @param {Ext.data.Batch} batch The batch
- */
- setBatch: function(batch) {
- this.batch = batch;
- },
- /**
- * Checks whether this operation should cause writing to occur.
- * @return {Boolean} Whether the operation should cause a write to occur.
- */
- allowWrite: function() {
- return this.action != 'read';
- }
- });
- /**
- * @author Ed Spencer
- *
- * This singleton contains a set of validation functions that can be used to validate any type of data. They are most
- * often used in {@link Ext.data.Model Models}, where they are automatically set up and executed.
- */
- Ext.define('Ext.data.validations', {
- singleton: true,
-
- /**
- * @property {String} presenceMessage
- * The default error message used when a presence validation fails.
- */
- presenceMessage: 'must be present',
-
- /**
- * @property {String} lengthMessage
- * The default error message used when a length validation fails.
- */
- lengthMessage: 'is the wrong length',
-
- /**
- * @property {Boolean} formatMessage
- * The default error message used when a format validation fails.
- */
- formatMessage: 'is the wrong format',
-
- /**
- * @property {String} inclusionMessage
- * The default error message used when an inclusion validation fails.
- */
- inclusionMessage: 'is not included in the list of acceptable values',
-
- /**
- * @property {String} exclusionMessage
- * The default error message used when an exclusion validation fails.
- */
- exclusionMessage: 'is not an acceptable value',
-
- /**
- * @property {String} emailMessage
- * The default error message used when an email validation fails
- */
- emailMessage: 'is not a valid email address',
-
- /**
- * @property {RegExp} emailRe
- * The regular expression used to validate email addresses
- */
- emailRe: /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,
-
- /**
- * Validates that the given value is present.
- * For example:
- *
- * validations: [{type: 'presence', field: 'age'}]
- *
- * @param {Object} config Config object
- * @param {Object} value The value to validate
- * @return {Boolean} True if validation passed
- */
- presence: function(config, value) {
- if (value === undefined) {
- value = config;
- }
-
- //we need an additional check for zero here because zero is an acceptable form of present data
- return !!value || value === 0;
- },
-
- /**
- * Returns true if the given value is between the configured min and max values.
- * For example:
- *
- * validations: [{type: 'length', field: 'name', min: 2}]
- *
- * @param {Object} config Config object
- * @param {String} value The value to validate
- * @return {Boolean} True if the value passes validation
- */
- length: function(config, value) {
- if (value === undefined || value === null) {
- return false;
- }
-
- var length = value.length,
- min = config.min,
- max = config.max;
-
- if ((min && length < min) || (max && length > max)) {
- return false;
- } else {
- return true;
- }
- },
-
- /**
- * Validates that an email string is in the correct format
- * @param {Object} config Config object
- * @param {String} email The email address
- * @return {Boolean} True if the value passes validation
- */
- email: function(config, email) {
- return Ext.data.validations.emailRe.test(email);
- },
-
- /**
- * Returns true if the given value passes validation against the configured `matcher` regex.
- * For example:
- *
- * validations: [{type: 'format', field: 'username', matcher: /([a-z]+)[0-9]{2,3}/}]
- *
- * @param {Object} config Config object
- * @param {String} value The value to validate
- * @return {Boolean} True if the value passes the format validation
- */
- format: function(config, value) {
- return !!(config.matcher && config.matcher.test(value));
- },
-
- /**
- * Validates that the given value is present in the configured `list`.
- * For example:
- *
- * validations: [{type: 'inclusion', field: 'gender', list: ['Male', 'Female']}]
- *
- * @param {Object} config Config object
- * @param {String} value The value to validate
- * @return {Boolean} True if the value is present in the list
- */
- inclusion: function(config, value) {
- return config.list && Ext.Array.indexOf(config.list,value) != -1;
- },
-
- /**
- * Validates that the given value is present in the configured `list`.
- * For example:
- *
- * validations: [{type: 'exclusion', field: 'username', list: ['Admin', 'Operator']}]
- *
- * @param {Object} config Config object
- * @param {String} value The value to validate
- * @return {Boolean} True if the value is not present in the list
- */
- exclusion: function(config, value) {
- return config.list && Ext.Array.indexOf(config.list,value) == -1;
- }
- });
- /**
- * @class Ext.util.HashMap
- * <p>
- * Represents a collection of a set of key and value pairs. Each key in the HashMap
- * must be unique, the same key cannot exist twice. Access to items is provided via
- * the key only. Sample usage:
- * <pre><code>
- var map = new Ext.util.HashMap();
- map.add('key1', 1);
- map.add('key2', 2);
- map.add('key3', 3);
- map.each(function(key, value, length){
- console.log(key, value, length);
- });
- * </code></pre>
- * </p>
- *
- * <p>The HashMap is an unordered class,
- * there is no guarantee when iterating over the items that they will be in any particular
- * order. If this is required, then use a {@link Ext.util.MixedCollection}.
- * </p>
- */
- Ext.define('Ext.util.HashMap', {
- mixins: {
- observable: 'Ext.util.Observable'
- },
- /**
- * @cfg {Function} keyFn A function that is used to retrieve a default key for a passed object.
- * A default is provided that returns the <b>id</b> property on the object. This function is only used
- * if the add method is called with a single argument.
- */
- /**
- * Creates new HashMap.
- * @param {Object} config (optional) Config object.
- */
- constructor: function(config) {
- config = config || {};
- var me = this,
- keyFn = config.keyFn;
- me.addEvents(
- /**
- * @event add
- * Fires when a new item is added to the hash
- * @param {Ext.util.HashMap} this.
- * @param {String} key The key of the added item.
- * @param {Object} value The value of the added item.
- */
- 'add',
- /**
- * @event clear
- * Fires when the hash is cleared.
- * @param {Ext.util.HashMap} this.
- */
- 'clear',
- /**
- * @event remove
- * Fires when an item is removed from the hash.
- * @param {Ext.util.HashMap} this.
- * @param {String} key The key of the removed item.
- * @param {Object} value The value of the removed item.
- */
- 'remove',
- /**
- * @event replace
- * Fires when an item is replaced in the hash.
- * @param {Ext.util.HashMap} this.
- * @param {String} key The key of the replaced item.
- * @param {Object} value The new value for the item.
- * @param {Object} old The old value for the item.
- */
- 'replace'
- );
- me.mixins.observable.constructor.call(me, config);
- me.clear(true);
- if (keyFn) {
- me.getKey = keyFn;
- }
- },
- /**
- * Gets the number of items in the hash.
- * @return {Number} The number of items in the hash.
- */
- getCount: function() {
- return this.length;
- },
- /**
- * Implementation for being able to extract the key from an object if only
- * a single argument is passed.
- * @private
- * @param {String} key The key
- * @param {Object} value The value
- * @return {Array} [key, value]
- */
- getData: function(key, value) {
- // if we have no value, it means we need to get the key from the object
- if (value === undefined) {
- value = key;
- key = this.getKey(value);
- }
- return [key, value];
- },
- /**
- * Extracts the key from an object. This is a default implementation, it may be overridden
- * @param {Object} o The object to get the key from
- * @return {String} The key to use.
- */
- getKey: function(o) {
- return o.id;
- },
- /**
- * Adds an item to the collection. Fires the {@link #event-add} event when complete.
- * @param {String} key <p>The key to associate with the item, or the new item.</p>
- * <p>If a {@link #getKey} implementation was specified for this HashMap,
- * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
- * the HashMap will be able to <i>derive</i> the key for the new item.
- * In this case just pass the new item in this parameter.</p>
- * @param {Object} o The item to add.
- * @return {Object} The item added.
- */
- add: function(key, value) {
- var me = this,
- data;
- if (arguments.length === 1) {
- value = key;
- key = me.getKey(value);
- }
- if (me.containsKey(key)) {
- return me.replace(key, value);
- }
- data = me.getData(key, value);
- key = data[0];
- value = data[1];
- me.map[key] = value;
- ++me.length;
- if (me.hasListeners.add) {
- me.fireEvent('add', me, key, value);
- }
- return value;
- },
- /**
- * Replaces an item in the hash. If the key doesn't exist, the
- * {@link #method-add} method will be used.
- * @param {String} key The key of the item.
- * @param {Object} value The new value for the item.
- * @return {Object} The new value of the item.
- */
- replace: function(key, value) {
- var me = this,
- map = me.map,
- old;
- if (!me.containsKey(key)) {
- me.add(key, value);
- }
- old = map[key];
- map[key] = value;
- if (me.hasListeners.replace) {
- me.fireEvent('replace', me, key, value, old);
- }
- return value;
- },
- /**
- * Remove an item from the hash.
- * @param {Object} o The value of the item to remove.
- * @return {Boolean} True if the item was successfully removed.
- */
- remove: function(o) {
- var key = this.findKey(o);
- if (key !== undefined) {
- return this.removeAtKey(key);
- }
- return false;
- },
- /**
- * Remove an item from the hash.
- * @param {String} key The key to remove.
- * @return {Boolean} True if the item was successfully removed.
- */
- removeAtKey: function(key) {
- var me = this,
- value;
- if (me.containsKey(key)) {
- value = me.map[key];
- delete me.map[key];
- --me.length;
- if (me.hasListeners.remove) {
- me.fireEvent('remove', me, key, value);
- }
- return true;
- }
- return false;
- },
- /**
- * Retrieves an item with a particular key.
- * @param {String} key The key to lookup.
- * @return {Object} The value at that key. If it doesn't exist, <tt>undefined</tt> is returned.
- */
- get: function(key) {
- return this.map[key];
- },
- /**
- * Removes all items from the hash.
- * @return {Ext.util.HashMap} this
- */
- clear: function(/* private */ initial) {
- var me = this;
- me.map = {};
- me.length = 0;
- if (initial !== true && me.hasListeners.clear) {
- me.fireEvent('clear', me);
- }
- return me;
- },
- /**
- * Checks whether a key exists in the hash.
- * @param {String} key The key to check for.
- * @return {Boolean} True if they key exists in the hash.
- */
- containsKey: function(key) {
- return this.map[key] !== undefined;
- },
- /**
- * Checks whether a value exists in the hash.
- * @param {Object} value The value to check for.
- * @return {Boolean} True if the value exists in the dictionary.
- */
- contains: function(value) {
- return this.containsKey(this.findKey(value));
- },
- /**
- * Return all of the keys in the hash.
- * @return {Array} An array of keys.
- */
- getKeys: function() {
- return this.getArray(true);
- },
- /**
- * Return all of the values in the hash.
- * @return {Array} An array of values.
- */
- getValues: function() {
- return this.getArray(false);
- },
- /**
- * Gets either the keys/values in an array from the hash.
- * @private
- * @param {Boolean} isKey True to extract the keys, otherwise, the value
- * @return {Array} An array of either keys/values from the hash.
- */
- getArray: function(isKey) {
- var arr = [],
- key,
- map = this.map;
- for (key in map) {
- if (map.hasOwnProperty(key)) {
- arr.push(isKey ? key: map[key]);
- }
- }
- return arr;
- },
- /**
- * Executes the specified function once for each item in the hash.
- * Returning false from the function will cease iteration.
- *
- * The paramaters passed to the function are:
- * <div class="mdetail-params"><ul>
- * <li><b>key</b> : String<p class="sub-desc">The key of the item</p></li>
- * <li><b>value</b> : Number<p class="sub-desc">The value of the item</p></li>
- * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the hash</p></li>
- * </ul></div>
- * @param {Function} fn The function to execute.
- * @param {Object} scope The scope to execute in. Defaults to <tt>this</tt>.
- * @return {Ext.util.HashMap} this
- */
- each: function(fn, scope) {
- // copy items so they may be removed during iteration.
- var items = Ext.apply({}, this.map),
- key,
- length = this.length;
- scope = scope || this;
- for (key in items) {
- if (items.hasOwnProperty(key)) {
- if (fn.call(scope, key, items[key], length) === false) {
- break;
- }
- }
- }
- return this;
- },
- /**
- * Performs a shallow copy on this hash.
- * @return {Ext.util.HashMap} The new hash object.
- */
- clone: function() {
- var hash = new this.self(),
- map = this.map,
- key;
- hash.suspendEvents();
- for (key in map) {
- if (map.hasOwnProperty(key)) {
- hash.add(key, map[key]);
- }
- }
- hash.resumeEvents();
- return hash;
- },
- /**
- * @private
- * Find the key for a value.
- * @param {Object} value The value to find.
- * @return {Object} The value of the item. Returns <tt>undefined</tt> if not found.
- */
- findKey: function(value) {
- var key,
- map = this.map;
- for (key in map) {
- if (map.hasOwnProperty(key) && map[key] === value) {
- return key;
- }
- }
- return undefined;
- }
- });
- /**
- * @class Ext.data.SortTypes
- * This class defines a series of static methods that are used on a
- * {@link Ext.data.Field} for performing sorting. The methods cast the
- * underlying values into a data type that is appropriate for sorting on
- * that particular field. If a {@link Ext.data.Field#type} is specified,
- * the sortType will be set to a sane default if the sortType is not
- * explicitly defined on the field. The sortType will make any necessary
- * modifications to the value and return it.
- * <ul>
- * <li><b>asText</b> - Removes any tags and converts the value to a string</li>
- * <li><b>asUCText</b> - Removes any tags and converts the value to an uppercase string</li>
- * <li><b>asUCText</b> - Converts the value to an uppercase string</li>
- * <li><b>asDate</b> - Converts the value into Unix epoch time</li>
- * <li><b>asFloat</b> - Converts the value to a floating point number</li>
- * <li><b>asInt</b> - Converts the value to an integer number</li>
- * </ul>
- * <p>
- * It is also possible to create a custom sortType that can be used throughout
- * an application.
- * <pre><code>
- Ext.apply(Ext.data.SortTypes, {
- asPerson: function(person){
- // expects an object with a first and last name property
- return person.lastName.toUpperCase() + person.firstName.toLowerCase();
- }
- });
- Ext.define('Employee', {
- extend: 'Ext.data.Model',
- fields: [{
- name: 'person',
- sortType: 'asPerson'
- }, {
- name: 'salary',
- type: 'float' // sortType set to asFloat
- }]
- });
- * </code></pre>
- * </p>
- * @singleton
- * @docauthor Evan Trimboli <evan@sencha.com>
- */
- Ext.define('Ext.data.SortTypes', {
-
- singleton: true,
-
- /**
- * Default sort that does nothing
- * @param {Object} s The value being converted
- * @return {Object} The comparison value
- */
- none : function(s) {
- return s;
- },
- /**
- * The regular expression used to strip tags
- * @type {RegExp}
- * @property
- */
- stripTagsRE : /<\/?[^>]+>/gi,
- /**
- * Strips all HTML tags to sort on text only
- * @param {Object} s The value being converted
- * @return {String} The comparison value
- */
- asText : function(s) {
- return String(s).replace(this.stripTagsRE, "");
- },
- /**
- * Strips all HTML tags to sort on text only - Case insensitive
- * @param {Object} s The value being converted
- * @return {String} The comparison value
- */
- asUCText : function(s) {
- return String(s).toUpperCase().replace(this.stripTagsRE, "");
- },
- /**
- * Case insensitive string
- * @param {Object} s The value being converted
- * @return {String} The comparison value
- */
- asUCString : function(s) {
- return String(s).toUpperCase();
- },
- /**
- * Date sorting
- * @param {Object} s The value being converted
- * @return {Number} The comparison value
- */
- asDate : function(s) {
- if(!s){
- return 0;
- }
- if(Ext.isDate(s)){
- return s.getTime();
- }
- return Date.parse(String(s));
- },
- /**
- * Float sorting
- * @param {Object} s The value being converted
- * @return {Number} The comparison value
- */
- asFloat : function(s) {
- var val = parseFloat(String(s).replace(/,/g, ""));
- return isNaN(val) ? 0 : val;
- },
- /**
- * Integer sorting
- * @param {Object} s The value being converted
- * @return {Number} The comparison value
- */
- asInt : function(s) {
- var val = parseInt(String(s).replace(/,/g, ""), 10);
- return isNaN(val) ? 0 : val;
- }
- });
- /**
- * A mixin to add floating capability to a Component.
- */
- Ext.define('Ext.util.Floating', {
- uses: ['Ext.Layer', 'Ext.window.Window'],
- /**
- * @cfg {Boolean} focusOnToFront
- * Specifies whether the floated component should be automatically {@link Ext.Component#method-focus focused} when
- * it is {@link #toFront brought to the front}.
- */
- focusOnToFront: true,
- /**
- * @cfg {String/Boolean} shadow
- * Specifies whether the floating component should be given a shadow. Set to true to automatically create an
- * {@link Ext.Shadow}, or a string indicating the shadow's display {@link Ext.Shadow#mode}. Set to false to
- * disable the shadow.
- */
- shadow: 'sides',
- /**
- * @cfg {String/Boolean} shadowOffset
- * Number of pixels to offset the shadow.
- */
- constructor: function (dom) {
- var me = this;
- me.el = new Ext.Layer(Ext.apply({
- hideMode : me.hideMode,
- hidden : me.hidden,
- shadow : (typeof me.shadow != 'undefined') ? me.shadow : 'sides',
- shadowOffset : me.shadowOffset,
- constrain : false,
- shim : (me.shim === false) ? false : undefined
- }, me.floating), dom);
- // release config object (if it was one)
- me.floating = true;
-
- // Register with the configured ownerCt.
- // With this we acquire a floatParent for relative positioning, and a zIndexParent which is an
- // ancestor floater which provides zIndex management.
- me.registerWithOwnerCt();
- },
- registerWithOwnerCt: function() {
- var me = this;
- if (me.zIndexParent) {
- me.zIndexParent.unregisterFloatingItem(me);
- }
- // Acquire a zIndexParent by traversing the ownerCt axis for the nearest floating ancestor
- me.zIndexParent = me.up('[floating]');
- me.setFloatParent(me.ownerCt);
- delete me.ownerCt;
- if (me.zIndexParent) {
- me.zIndexParent.registerFloatingItem(me);
- } else {
- Ext.WindowManager.register(me);
- }
- },
- setFloatParent: function(floatParent) {
- var me = this;
- // Remove listeners from previous floatParent
- if (me.floatParent) {
- me.mun(me.floatParent, {
- hide: me.onFloatParentHide,
- show: me.onFloatParentShow,
- scope: me
- });
- }
- me.floatParent = floatParent;
- // Floating Components as children of Containers must hide when their parent hides.
- if (floatParent) {
- me.mon(me.floatParent, {
- hide: me.onFloatParentHide,
- show: me.onFloatParentShow,
- scope: me
- });
- }
- // If a floating Component is configured to be constrained, but has no configured
- // constrainTo setting, set its constrainTo to be it's ownerCt before rendering.
- if ((me.constrain || me.constrainHeader) && !me.constrainTo) {
- me.constrainTo = floatParent ? floatParent.getTargetEl() : me.container;
- }
- },
- onFloatParentHide: function() {
- var me = this;
- if (me.hideOnParentHide !== false && me.isVisible()) {
- me.hide();
- me.showOnParentShow = true;
- }
- },
- onFloatParentShow: function() {
- if (this.showOnParentShow) {
- delete this.showOnParentShow;
- this.show();
- }
- },
- // private
- // z-index is managed by the zIndexManager and may be overwritten at any time.
- // Returns the next z-index to be used.
- // If this is a Container, then it will have rebased any managed floating Components,
- // and so the next available z-index will be approximately 10000 above that.
- setZIndex: function(index) {
- var me = this;
- me.el.setZIndex(index);
- // Next item goes 10 above;
- index += 10;
- // When a Container with floating items has its z-index set, it rebases any floating items it is managing.
- // The returned value is a round number approximately 10000 above the last z-index used.
- if (me.floatingItems) {
- index = Math.floor(me.floatingItems.setBase(index) / 100) * 100 + 10000;
- }
- return index;
- },
- /**
- * Moves this floating Component into a constrain region.
- *
- * By default, this Component is constrained to be within the container it was added to, or the element it was
- * rendered to.
- *
- * An alternative constraint may be passed.
- * @param {String/HTMLElement/Ext.Element/Ext.util.Region} [constrainTo] The Element or {@link Ext.util.Region Region}
- * into which this Component is to be constrained. Defaults to the element into which this floating Component
- * was rendered.
- */
- doConstrain: function(constrainTo) {
- var me = this,
- // Calculate the constrain vector to coerce our position to within our
- // constrainTo setting. getConstrainVector will provide a default constraint
- // region if there is no explicit constrainTo, *and* there is no floatParent owner Component.
- vector = me.getConstrainVector(constrainTo),
- xy;
- if (vector) {
- xy = me.getPosition();
- xy[0] += vector[0];
- xy[1] += vector[1];
- me.setPosition(xy);
- }
- },
- /**
- * Gets the x/y offsets to constrain this float
- * @private
- * @param {String/HTMLElement/Ext.Element/Ext.util.Region} [constrainTo] The Element or {@link Ext.util.Region Region}
- * into which this Component is to be constrained.
- * @return {Number[]} The x/y constraints
- */
- getConstrainVector: function(constrainTo){
- var me = this;
- if (me.constrain || me.constrainHeader) {
- constrainTo = constrainTo || (me.floatParent && me.floatParent.getTargetEl()) || me.container || me.el.getScopeParent();
- return (me.constrainHeader ? me.header.el : me.el).getConstrainVector(constrainTo);
- }
- },
- /**
- * Aligns this floating Component to the specified element
- *
- * @param {Ext.Component/Ext.Element/HTMLElement/String} element
- * The element or {@link Ext.Component} to align to. If passing a component, it must be a
- * component instance. If a string id is passed, it will be used as an element id.
- * @param {String} [position="tl-bl?"] The position to align to
- * (see {@link Ext.Element#alignTo} for more details).
- * @param {Number[]} [offsets] Offset the positioning by [x, y]
- * @return {Ext.Component} this
- */
- alignTo: function(element, position, offsets) {
- // element may be a Component, so first attempt to use its el to align to.
- // When aligning to an Element's X,Y position, we must use setPagePosition which disregards any floatParent
- this.setPagePosition(this.el.getAlignToXY(element.el || element, position, offsets));
- return this;
- },
- /**
- * Brings this floating Component to the front of any other visible, floating Components managed by the same
- * {@link Ext.ZIndexManager ZIndexManager}
- *
- * If this Component is modal, inserts the modal mask just below this Component in the z-index stack.
- *
- * @param {Boolean} [preventFocus=false] Specify `true` to prevent the Component from being focused.
- * @return {Ext.Component} this
- */
- toFront: function(preventFocus) {
- var me = this;
- // Find the floating Component which provides the base for this Component's zIndexing.
- // That must move to front to then be able to rebase its zIndex stack and move this to the front
- if (me.zIndexParent && me.bringParentToFront !== false) {
- me.zIndexParent.toFront(true);
- }
-
- if (!Ext.isDefined(preventFocus)) {
- preventFocus = !me.focusOnToFront;
- }
-
- if (preventFocus) {
- me.preventFocusOnActivate = true;
- }
- if (me.zIndexManager.bringToFront(me)) {
- if (!preventFocus) {
- // Kick off a delayed focus request.
- // If another floating Component is toFronted before the delay expires
- // this will not receive focus.
- me.focus(false, true);
- }
- }
- delete me.preventFocusOnActivate;
- return me;
- },
- /**
- * This method is called internally by {@link Ext.ZIndexManager} to signal that a floating Component has either been
- * moved to the top of its zIndex stack, or pushed from the top of its zIndex stack.
- *
- * If a _Window_ is superceded by another Window, deactivating it hides its shadow.
- *
- * This method also fires the {@link Ext.Component#activate activate} or
- * {@link Ext.Component#deactivate deactivate} event depending on which action occurred.
- *
- * @param {Boolean} [active=false] True to activate the Component, false to deactivate it.
- * @param {Ext.Component} [newActive] The newly active Component which is taking over topmost zIndex position.
- */
- setActive: function(active, newActive) {
- var me = this;
-
- if (active) {
- if (me.el.shadow && !me.maximized) {
- me.el.enableShadow(true);
- }
- if (me.modal && !me.preventFocusOnActivate) {
- me.focus(false, true);
- }
- me.fireEvent('activate', me);
- } else {
- // Only the *Windows* in a zIndex stack share a shadow. All other types of floaters
- // can keep their shadows all the time
- if (me.isWindow && (newActive && newActive.isWindow)) {
- me.el.disableShadow();
- }
- me.fireEvent('deactivate', me);
- }
- },
- /**
- * Sends this Component to the back of (lower z-index than) any other visible windows
- * @return {Ext.Component} this
- */
- toBack: function() {
- this.zIndexManager.sendToBack(this);
- return this;
- },
- /**
- * Center this Component in its container.
- * @return {Ext.Component} this
- */
- center: function() {
- var me = this,
- xy;
-
- if (me.isVisible()) {
- xy = me.el.getAlignToXY(me.container, 'c-c');
- me.setPagePosition(xy);
- } else {
- me.needsCenter = true;
- }
- return me;
- },
-
- onFloatShow: function(){
- if (this.needsCenter) {
- this.center();
- }
- delete this.needsCenter;
- },
- // private
- syncShadow : function(){
- if (this.floating) {
- this.el.sync(true);
- }
- },
- // private
- fitContainer: function() {
- var parent = this.floatParent,
- container = parent ? parent.getTargetEl() : this.container,
- size = container.getViewSize(false);
- this.setSize(size);
- }
- });
- /**
- * The Connection class encapsulates a connection to the page's originating domain, allowing requests to be made either
- * to a configured URL, or to a URL specified at request time.
- *
- * Requests made by this class are asynchronous, and will return immediately. No data from the server will be available
- * to the statement immediately following the {@link #request} call. To process returned data, use a success callback
- * in the request options object, or an {@link #requestcomplete event listener}.
- *
- * # File Uploads
- *
- * File uploads are not performed using normal "Ajax" techniques, that is they are not performed using XMLHttpRequests.
- * Instead the form is submitted in the standard manner with the DOM <form> element temporarily modified to have its
- * target set to refer to a dynamically generated, hidden <iframe> which is inserted into the document but removed
- * after the return data has been gathered.
- *
- * The server response is parsed by the browser to create the document for the IFRAME. If the server is using JSON to
- * send the return object, then the Content-Type header must be set to "text/html" in order to tell the browser to
- * insert the text unchanged into the document body.
- *
- * Characters which are significant to an HTML parser must be sent as HTML entities, so encode `<` as `<`, `&` as
- * `&` etc.
- *
- * The response text is retrieved from the document, and a fake XMLHttpRequest object is created containing a
- * responseText property in order to conform to the requirements of event handlers and callbacks.
- *
- * Be aware that file upload packets are sent with the content type multipart/form and some server technologies
- * (notably JEE) may require some custom processing in order to retrieve parameter names and parameter values from the
- * packet content.
- *
- * Also note that it's not possible to check the response code of the hidden iframe, so the success handler will ALWAYS fire.
- */
- Ext.define('Ext.data.Connection', {
- mixins: {
- observable: 'Ext.util.Observable'
- },
- statics: {
- requestId: 0
- },
- url: null,
- async: true,
- method: null,
- username: '',
- password: '',
- /**
- * @cfg {Boolean} disableCaching
- * True to add a unique cache-buster param to GET requests.
- */
- disableCaching: true,
- /**
- * @cfg {Boolean} withCredentials
- * True to set `withCredentials = true` on the XHR object
- */
- withCredentials: false,
- /**
- * @cfg {Boolean} cors
- * True to enable CORS support on the XHR object. Currently the only effect of this option
- * is to use the XDomainRequest object instead of XMLHttpRequest if the browser is IE8 or above.
- */
- cors: false,
- /**
- * @cfg {String} disableCachingParam
- * Change the parameter which is sent went disabling caching through a cache buster.
- */
- disableCachingParam: '_dc',
- /**
- * @cfg {Number} timeout
- * The timeout in milliseconds to be used for requests.
- */
- timeout : 30000,
- /**
- * @cfg {Object} extraParams
- * Any parameters to be appended to the request.
- */
- useDefaultHeader : true,
- defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8',
- useDefaultXhrHeader : true,
- defaultXhrHeader : 'XMLHttpRequest',
- constructor : function(config) {
- config = config || {};
- Ext.apply(this, config);
- /**
- * @event beforerequest
- * Fires before a network request is made to retrieve a data object.
- * @param {Ext.data.Connection} conn This Connection object.
- * @param {Object} options The options config object passed to the {@link #request} method.
- */
- /**
- * @event requestcomplete
- * Fires if the request was successfully completed.
- * @param {Ext.data.Connection} conn This Connection object.
- * @param {Object} response The XHR object containing the response data.
- * See [The XMLHttpRequest Object](http://www.w3.org/TR/XMLHttpRequest/) for details.
- * @param {Object} options The options config object passed to the {@link #request} method.
- */
- /**
- * @event requestexception
- * Fires if an error HTTP status was returned from the server.
- * See [HTTP Status Code Definitions](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)
- * for details of HTTP status codes.
- * @param {Ext.data.Connection} conn This Connection object.
- * @param {Object} response The XHR object containing the response data.
- * See [The XMLHttpRequest Object](http://www.w3.org/TR/XMLHttpRequest/) for details.
- * @param {Object} options The options config object passed to the {@link #request} method.
- */
- this.requests = {};
- this.mixins.observable.constructor.call(this);
- },
- /**
- * Sends an HTTP request to a remote server.
- *
- * **Important:** Ajax server requests are asynchronous, and this call will
- * return before the response has been received. Process any returned data
- * in a callback function.
- *
- * Ext.Ajax.request({
- * url: 'ajax_demo/sample.json',
- * success: function(response, opts) {
- * var obj = Ext.decode(response.responseText);
- * console.dir(obj);
- * },
- * failure: function(response, opts) {
- * console.log('server-side failure with status code ' + response.status);
- * }
- * });
- *
- * To execute a callback function in the correct scope, use the `scope` option.
- *
- * @param {Object} options An object which may contain the following properties:
- *
- * (The options object may also contain any other property which might be needed to perform
- * postprocessing in a callback because it is passed to callback functions.)
- *
- * @param {String/Function} options.url The URL to which to send the request, or a function
- * to call which returns a URL string. The scope of the function is specified by the `scope` option.
- * Defaults to the configured `url`.
- *
- * @param {Object/String/Function} options.params An object containing properties which are
- * used as parameters to the request, a url encoded string or a function to call to get either. The scope
- * of the function is specified by the `scope` option.
- *
- * @param {String} options.method The HTTP method to use
- * for the request. Defaults to the configured method, or if no method was configured,
- * "GET" if no parameters are being sent, and "POST" if parameters are being sent. Note that
- * the method name is case-sensitive and should be all caps.
- *
- * @param {Function} options.callback The function to be called upon receipt of the HTTP response.
- * The callback is called regardless of success or failure and is passed the following parameters:
- * @param {Object} options.callback.options The parameter to the request call.
- * @param {Boolean} options.callback.success True if the request succeeded.
- * @param {Object} options.callback.response The XMLHttpRequest object containing the response data.
- * See [www.w3.org/TR/XMLHttpRequest/](http://www.w3.org/TR/XMLHttpRequest/) for details about
- * accessing elements of the response.
- *
- * @param {Function} options.success The function to be called upon success of the request.
- * The callback is passed the following parameters:
- * @param {Object} options.success.response The XMLHttpRequest object containing the response data.
- * @param {Object} options.success.options The parameter to the request call.
- *
- * @param {Function} options.failure The function to be called upon success of the request.
- * The callback is passed the following parameters:
- * @param {Object} options.failure.response The XMLHttpRequest object containing the response data.
- * @param {Object} options.failure.options The parameter to the request call.
- *
- * @param {Object} options.scope The scope in which to execute the callbacks: The "this" object for
- * the callback function. If the `url`, or `params` options were specified as functions from which to
- * draw values, then this also serves as the scope for those function calls. Defaults to the browser
- * window.
- *
- * @param {Number} options.timeout The timeout in milliseconds to be used for this request.
- * Defaults to 30 seconds.
- *
- * @param {Ext.Element/HTMLElement/String} options.form The `<form>` Element or the id of the `<form>`
- * to pull parameters from.
- *
- * @param {Boolean} options.isUpload **Only meaningful when used with the `form` option.**
- *
- * True if the form object is a file upload (will be set automatically if the form was configured
- * with **`enctype`** `"multipart/form-data"`).
- *
- * File uploads are not performed using normal "Ajax" techniques, that is they are **not**
- * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
- * DOM `<form>` element temporarily modified to have its [target][] set to refer to a dynamically
- * generated, hidden `<iframe>` which is inserted into the document but removed after the return data
- * has been gathered.
- *
- * The server response is parsed by the browser to create the document for the IFRAME. If the
- * server is using JSON to send the return object, then the [Content-Type][] header must be set to
- * "text/html" in order to tell the browser to insert the text unchanged into the document body.
- *
- * The response text is retrieved from the document, and a fake XMLHttpRequest object is created
- * containing a `responseText` property in order to conform to the requirements of event handlers
- * and callbacks.
- *
- * Be aware that file upload packets are sent with the content type [multipart/form][] and some server
- * technologies (notably JEE) may require some custom processing in order to retrieve parameter names
- * and parameter values from the packet content.
- *
- * [target]: http://www.w3.org/TR/REC-html40/present/frames.html#adef-target
- * [Content-Type]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17
- * [multipart/form]: http://www.faqs.org/rfcs/rfc2388.html
- *
- * @param {Object} options.headers Request headers to set for the request.
- *
- * @param {Object} options.xmlData XML document to use for the post. Note: This will be used instead
- * of params for the post data. Any params will be appended to the URL.
- *
- * @param {Object/String} options.jsonData JSON data to use as the post. Note: This will be used
- * instead of params for the post data. Any params will be appended to the URL.
- *
- * @param {Boolean} options.disableCaching True to add a unique cache-buster param to GET requests.
- *
- * @param {Boolean} options.withCredentials True to add the withCredentials property to the XHR object
- *
- * @return {Object} The request object. This may be used to cancel the request.
- */
- request : function(options) {
- options = options || {};
- var me = this,
- scope = options.scope || window,
- username = options.username || me.username,
- password = options.password || me.password || '',
- async,
- requestOptions,
- request,
- headers,
- xhr;
- if (me.fireEvent('beforerequest', me, options) !== false) {
- requestOptions = me.setOptions(options, scope);
- if (me.isFormUpload(options)) {
- me.upload(options.form, requestOptions.url, requestOptions.data, options);
- return null;
- }
- // if autoabort is set, cancel the current transactions
- if (options.autoAbort || me.autoAbort) {
- me.abort();
- }
- // create a connection object
- async = options.async !== false ? (options.async || me.async) : false;
- xhr = me.openRequest(options, requestOptions, async, username, password);
- headers = me.setupHeaders(xhr, options, requestOptions.data, requestOptions.params);
- // create the transaction object
- request = {
- id: ++Ext.data.Connection.requestId,
- xhr: xhr,
- headers: headers,
- options: options,
- async: async,
- timeout: setTimeout(function() {
- request.timedout = true;
- me.abort(request);
- }, options.timeout || me.timeout)
- };
- me.requests[request.id] = request;
- me.latestId = request.id;
- // bind our statechange listener
- if (async) {
- xhr.onreadystatechange = Ext.Function.bind(me.onStateChange, me, [request]);
- }
- // start the request!
- xhr.send(requestOptions.data);
- if (!async) {
- return me.onComplete(request);
- }
- return request;
- } else {
- Ext.callback(options.callback, options.scope, [options, undefined, undefined]);
- return null;
- }
- },
- /**
- * Uploads a form using a hidden iframe.
- * @param {String/HTMLElement/Ext.Element} form The form to upload
- * @param {String} url The url to post to
- * @param {String} params Any extra parameters to pass
- * @param {Object} options The initial options
- */
- upload: function(form, url, params, options) {
- form = Ext.getDom(form);
- options = options || {};
- var id = Ext.id(),
- frame = document.createElement('iframe'),
- hiddens = [],
- encoding = 'multipart/form-data',
- buf = {
- target: form.target,
- method: form.method,
- encoding: form.encoding,
- enctype: form.enctype,
- action: form.action
- },
- addField = function(name, value) {
- hiddenItem = document.createElement('input');
- Ext.fly(hiddenItem).set({
- type: 'hidden',
- value: value,
- name: name
- });
- form.appendChild(hiddenItem);
- hiddens.push(hiddenItem);
- },
- hiddenItem, obj, value, name, vLen, v, hLen, h;
- /*
- * Originally this behaviour was modified for Opera 10 to apply the secure URL after
- * the frame had been added to the document. It seems this has since been corrected in
- * Opera so the behaviour has been reverted, the URL will be set before being added.
- */
- Ext.fly(frame).set({
- id: id,
- name: id,
- cls: Ext.baseCSSPrefix + 'hide-display',
- src: Ext.SSL_SECURE_URL
- });
- document.body.appendChild(frame);
- // This is required so that IE doesn't pop the response up in a new window.
- if (document.frames) {
- document.frames[id].name = id;
- }
- Ext.fly(form).set({
- target: id,
- method: 'POST',
- enctype: encoding,
- encoding: encoding,
- action: url || buf.action
- });
- // add dynamic params
- if (params) {
- obj = Ext.Object.fromQueryString(params) || {};
- for (name in obj) {
- value = obj[name];
- if (obj.hasOwnProperty(value)) {
- if (Ext.isArray(value)) {
- vLen = value.length;
- for (v = 0; v < vLen; v++) {
- addField(name, value[v]);
- }
- } else {
- addField(name, value);
- }
- }
- }
- }
- Ext.fly(frame).on('load', Ext.Function.bind(this.onUploadComplete, this, [frame, options]), null, {single: true});
- form.submit();
- Ext.fly(form).set(buf);
- hLen = hiddens.length;
- for (h = 0; h < hLen; h++) {
- Ext.removeNode(hiddens[h]);
- }
- },
- /**
- * @private
- * Callback handler for the upload function. After we've submitted the form via the iframe this creates a bogus
- * response object to simulate an XHR and populates its responseText from the now-loaded iframe's document body
- * (or a textarea inside the body). We then clean up by removing the iframe
- */
- onUploadComplete: function(frame, options) {
- var me = this,
- // bogus response object
- response = {
- responseText: '',
- responseXML: null
- }, doc, firstChild;
- try {
- doc = frame.contentWindow.document || frame.contentDocument || window.frames[frame.id].document;
- if (doc) {
- if (doc.body) {
- if (/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)) { // json response wrapped in textarea
- response.responseText = firstChild.value;
- } else {
- response.responseText = doc.body.innerHTML;
- }
- }
- //in IE the document may still have a body even if returns XML.
- response.responseXML = doc.XMLDocument || doc;
- }
- } catch (e) {
- }
- me.fireEvent('requestcomplete', me, response, options);
- Ext.callback(options.success, options.scope, [response, options]);
- Ext.callback(options.callback, options.scope, [options, true, response]);
- setTimeout(function() {
- Ext.removeNode(frame);
- }, 100);
- },
- /**
- * Detects whether the form is intended to be used for an upload.
- * @private
- */
- isFormUpload: function(options) {
- var form = this.getForm(options);
- if (form) {
- return (options.isUpload || (/multipart\/form-data/i).test(form.getAttribute('enctype')));
- }
- return false;
- },
- /**
- * Gets the form object from options.
- * @private
- * @param {Object} options The request options
- * @return {HTMLElement} The form, null if not passed
- */
- getForm: function(options) {
- return Ext.getDom(options.form) || null;
- },
- /**
- * Sets various options such as the url, params for the request
- * @param {Object} options The initial options
- * @param {Object} scope The scope to execute in
- * @return {Object} The params for the request
- */
- setOptions: function(options, scope) {
- var me = this,
- params = options.params || {},
- extraParams = me.extraParams,
- urlParams = options.urlParams,
- url = options.url || me.url,
- jsonData = options.jsonData,
- method,
- disableCache,
- data;
- // allow params to be a method that returns the params object
- if (Ext.isFunction(params)) {
- params = params.call(scope, options);
- }
- // allow url to be a method that returns the actual url
- if (Ext.isFunction(url)) {
- url = url.call(scope, options);
- }
- url = this.setupUrl(options, url);
- if (!url) {
- Ext.Error.raise({
- options: options,
- msg: 'No URL specified'
- });
- }
- // check for xml or json data, and make sure json data is encoded
- data = options.rawData || options.xmlData || jsonData || null;
- if (jsonData && !Ext.isPrimitive(jsonData)) {
- data = Ext.encode(data);
- }
- // make sure params are a url encoded string and include any extraParams if specified
- if (Ext.isObject(params)) {
- params = Ext.Object.toQueryString(params);
- }
- if (Ext.isObject(extraParams)) {
- extraParams = Ext.Object.toQueryString(extraParams);
- }
- params = params + ((extraParams) ? ((params) ? '&' : '') + extraParams : '');
- urlParams = Ext.isObject(urlParams) ? Ext.Object.toQueryString(urlParams) : urlParams;
- params = this.setupParams(options, params);
- // decide the proper method for this request
- method = (options.method || me.method || ((params || data) ? 'POST' : 'GET')).toUpperCase();
- this.setupMethod(options, method);
- disableCache = options.disableCaching !== false ? (options.disableCaching || me.disableCaching) : false;
- // if the method is get append date to prevent caching
- if (method === 'GET' && disableCache) {
- url = Ext.urlAppend(url, (options.disableCachingParam || me.disableCachingParam) + '=' + (new Date().getTime()));
- }
- // if the method is get or there is json/xml data append the params to the url
- if ((method == 'GET' || data) && params) {
- url = Ext.urlAppend(url, params);
- params = null;
- }
- // allow params to be forced into the url
- if (urlParams) {
- url = Ext.urlAppend(url, urlParams);
- }
- return {
- url: url,
- method: method,
- data: data || params || null
- };
- },
- /**
- * Template method for overriding url
- * @template
- * @private
- * @param {Object} options
- * @param {String} url
- * @return {String} The modified url
- */
- setupUrl: function(options, url) {
- var form = this.getForm(options);
- if (form) {
- url = url || form.action;
- }
- return url;
- },
- /**
- * Template method for overriding params
- * @template
- * @private
- * @param {Object} options
- * @param {String} params
- * @return {String} The modified params
- */
- setupParams: function(options, params) {
- var form = this.getForm(options),
- serializedForm;
- if (form && !this.isFormUpload(options)) {
- serializedForm = Ext.Element.serializeForm(form);
- params = params ? (params + '&' + serializedForm) : serializedForm;
- }
- return params;
- },
- /**
- * Template method for overriding method
- * @template
- * @private
- * @param {Object} options
- * @param {String} method
- * @return {String} The modified method
- */
- setupMethod: function(options, method) {
- if (this.isFormUpload(options)) {
- return 'POST';
- }
- return method;
- },
- /**
- * Setup all the headers for the request
- * @private
- * @param {Object} xhr The xhr object
- * @param {Object} options The options for the request
- * @param {Object} data The data for the request
- * @param {Object} params The params for the request
- */
- setupHeaders: function(xhr, options, data, params) {
- var me = this,
- headers = Ext.apply({}, options.headers || {}, me.defaultHeaders || {}),
- contentType = me.defaultPostHeader,
- jsonData = options.jsonData,
- xmlData = options.xmlData,
- key,
- header;
- if (!headers['Content-Type'] && (data || params)) {
- if (data) {
- if (options.rawData) {
- contentType = 'text/plain';
- } else {
- if (xmlData && Ext.isDefined(xmlData)) {
- contentType = 'text/xml';
- } else if (jsonData && Ext.isDefined(jsonData)) {
- contentType = 'application/json';
- }
- }
- }
- headers['Content-Type'] = contentType;
- }
- if (me.useDefaultXhrHeader && !headers['X-Requested-With']) {
- headers['X-Requested-With'] = me.defaultXhrHeader;
- }
- // set up all the request headers on the xhr object
- try {
- for (key in headers) {
- if (headers.hasOwnProperty(key)) {
- header = headers[key];
- xhr.setRequestHeader(key, header);
- }
- }
- } catch(e) {
- me.fireEvent('exception', key, header);
- }
- return headers;
- },
- /**
- * Creates the appropriate XHR transport for a given request on this browser. On IE
- * this may be an `XDomainRequest` rather than an `XMLHttpRequest`.
- * @private
- */
- newRequest: function (options) {
- var xhr;
- if ((options.cors || this.cors) && Ext.isIE && Ext.ieVersion >= 8) {
- xhr = new XDomainRequest();
- } else {
- xhr = this.getXhrInstance();
- }
- return xhr;
- },
- /**
- * Creates and opens an appropriate XHR transport for a given request on this browser.
- * This logic is contained in an individual method to allow for overrides to process all
- * of the parameters and options and return a suitable, open connection.
- * @private
- */
- openRequest: function (options, requestOptions, async, username, password) {
- var xhr = this.newRequest(options);
- if (username) {
- xhr.open(requestOptions.method, requestOptions.url, async, username, password);
- } else {
- xhr.open(requestOptions.method, requestOptions.url, async);
- }
- if (options.withCredentials || this.withCredentials) {
- xhr.withCredentials = true;
- }
- return xhr;
- },
- /**
- * Creates the appropriate XHR transport for this browser.
- * @private
- */
- getXhrInstance: (function() {
- var options = [function() {
- return new XMLHttpRequest();
- }, function() {
- return new ActiveXObject('MSXML2.XMLHTTP.3.0');
- }, function() {
- return new ActiveXObject('MSXML2.XMLHTTP');
- }, function() {
- return new ActiveXObject('Microsoft.XMLHTTP');
- }], i = 0,
- len = options.length,
- xhr;
- for (; i < len; ++i) {
- try {
- xhr = options[i];
- xhr();
- break;
- } catch(e) {
- }
- }
- return xhr;
- })(),
- /**
- * Determines whether this object has a request outstanding.
- * @param {Object} [request] Defaults to the last transaction
- * @return {Boolean} True if there is an outstanding request.
- */
- isLoading : function(request) {
- if (!request) {
- request = this.getLatest();
- }
- if (!(request && request.xhr)) {
- return false;
- }
- // if there is a connection and readyState is not 0 or 4
- var state = request.xhr.readyState;
- return !(state === 0 || state == 4);
- },
- /**
- * Aborts an active request.
- * @param {Object} [request] Defaults to the last request
- */
- abort : function(request) {
- var me = this,
- xhr;
-
- if (!request) {
- request = me.getLatest();
- }
- if (request && me.isLoading(request)) {
- /*
- * Clear out the onreadystatechange here, this allows us
- * greater control, the browser may/may not fire the function
- * depending on a series of conditions.
- */
- xhr = request.xhr;
- try {
- xhr.onreadystatechange = null;
- } catch (e) {
- // Setting onreadystatechange to null can cause problems in IE, see
- // http://www.quirksmode.org/blog/archives/2005/09/xmlhttp_notes_a_1.html
- xhr = Ext.emptyFn;
- }
- xhr.abort();
- me.clearTimeout(request);
- if (!request.timedout) {
- request.aborted = true;
- }
- me.onComplete(request);
- me.cleanup(request);
- }
- },
-
- /**
- * Aborts all active requests
- */
- abortAll: function(){
- var requests = this.requests,
- id;
-
- for (id in requests) {
- if (requests.hasOwnProperty(id)) {
- this.abort(requests[id]);
- }
- }
- },
-
- /**
- * Gets the most recent request
- * @private
- * @return {Object} The request. Null if there is no recent request
- */
- getLatest: function(){
- var id = this.latestId,
- request;
-
- if (id) {
- request = this.requests[id];
- }
- return request || null;
- },
- /**
- * Fires when the state of the xhr changes
- * @private
- * @param {Object} request The request
- */
- onStateChange : function(request) {
- if (request.xhr.readyState == 4) {
- this.clearTimeout(request);
- this.onComplete(request);
- this.cleanup(request);
- }
- },
- /**
- * Clears the timeout on the request
- * @private
- * @param {Object} The request
- */
- clearTimeout: function(request) {
- clearTimeout(request.timeout);
- delete request.timeout;
- },
- /**
- * Cleans up any left over information from the request
- * @private
- * @param {Object} The request
- */
- cleanup: function(request) {
- request.xhr = null;
- delete request.xhr;
- },
- /**
- * To be called when the request has come back from the server
- * @private
- * @param {Object} request
- * @return {Object} The response
- */
- onComplete : function(request) {
- var me = this,
- options = request.options,
- result,
- success,
- response;
- try {
- result = me.parseStatus(request.xhr.status);
- } catch (e) {
- // in some browsers we can't access the status if the readyState is not 4, so the request has failed
- result = {
- success : false,
- isException : false
- };
- }
- success = result.success;
- if (success) {
- response = me.createResponse(request);
- me.fireEvent('requestcomplete', me, response, options);
- Ext.callback(options.success, options.scope, [response, options]);
- } else {
- if (result.isException || request.aborted || request.timedout) {
- response = me.createException(request);
- } else {
- response = me.createResponse(request);
- }
- me.fireEvent('requestexception', me, response, options);
- Ext.callback(options.failure, options.scope, [response, options]);
- }
- Ext.callback(options.callback, options.scope, [options, success, response]);
- delete me.requests[request.id];
- return response;
- },
- /**
- * Checks if the response status was successful
- * @param {Number} status The status code
- * @return {Object} An object containing success/status state
- */
- parseStatus: function(status) {
- // see: https://prototype.lighthouseapp.com/projects/8886/tickets/129-ie-mangles-http-response-status-code-204-to-1223
- status = status == 1223 ? 204 : status;
- var success = (status >= 200 && status < 300) || status == 304,
- isException = false;
- if (!success) {
- switch (status) {
- case 12002:
- case 12029:
- case 12030:
- case 12031:
- case 12152:
- case 13030:
- isException = true;
- break;
- }
- }
- return {
- success: success,
- isException: isException
- };
- },
- /**
- * Creates the response object
- * @private
- * @param {Object} request
- */
- createResponse : function(request) {
- var xhr = request.xhr,
- headers = {},
- lines = xhr.getAllResponseHeaders().replace(/\r\n/g, '\n').split('\n'),
- count = lines.length,
- line, index, key, value, response;
- while (count--) {
- line = lines[count];
- index = line.indexOf(':');
- if (index >= 0) {
- key = line.substr(0, index).toLowerCase();
- if (line.charAt(index + 1) == ' ') {
- ++index;
- }
- headers[key] = line.substr(index + 1);
- }
- }
- request.xhr = null;
- delete request.xhr;
- response = {
- request: request,
- requestId : request.id,
- status : xhr.status,
- statusText : xhr.statusText,
- getResponseHeader : function(header) {
- return headers[header.toLowerCase()];
- },
- getAllResponseHeaders : function() {
- return headers;
- },
- responseText : xhr.responseText,
- responseXML : xhr.responseXML
- };
- // If we don't explicitly tear down the xhr reference, IE6/IE7 will hold this in the closure of the
- // functions created with getResponseHeader/getAllResponseHeaders
- xhr = null;
- return response;
- },
- /**
- * Creates the exception object
- * @private
- * @param {Object} request
- */
- createException : function(request) {
- return {
- request : request,
- requestId : request.id,
- status : request.aborted ? -1 : 0,
- statusText : request.aborted ? 'transaction aborted' : 'communication failure',
- aborted: request.aborted,
- timedout: request.timedout
- };
- }
- });
- /**
- * Represents a filter that can be applied to a {@link Ext.util.MixedCollection MixedCollection}. Can either simply
- * filter on a property/value pair or pass in a filter function with custom logic. Filters are always used in the
- * context of MixedCollections, though {@link Ext.data.Store Store}s frequently create them when filtering and searching
- * on their records. Example usage:
- *
- * //set up a fictional MixedCollection containing a few people to filter on
- * var allNames = new Ext.util.MixedCollection();
- * allNames.addAll([
- * {id: 1, name: 'Ed', age: 25},
- * {id: 2, name: 'Jamie', age: 37},
- * {id: 3, name: 'Abe', age: 32},
- * {id: 4, name: 'Aaron', age: 26},
- * {id: 5, name: 'David', age: 32}
- * ]);
- *
- * var ageFilter = new Ext.util.Filter({
- * property: 'age',
- * value : 32
- * });
- *
- * var longNameFilter = new Ext.util.Filter({
- * filterFn: function(item) {
- * return item.name.length > 4;
- * }
- * });
- *
- * //a new MixedCollection with the 3 names longer than 4 characters
- * var longNames = allNames.filter(longNameFilter);
- *
- * //a new MixedCollection with the 2 people of age 24:
- * var youngFolk = allNames.filter(ageFilter);
- *
- */
- Ext.define('Ext.util.Filter', {
- /* Begin Definitions */
- /* End Definitions */
- /**
- * @cfg {String} property
- * The property to filter on. Required unless a {@link #filterFn} is passed
- */
-
- /**
- * @cfg {Function} filterFn
- * A custom filter function which is passed each item in the {@link Ext.util.MixedCollection} in turn. Should return
- * true to accept each item or false to reject it
- */
-
- /**
- * @cfg {Boolean} anyMatch
- * True to allow any match - no regex start/end line anchors will be added.
- */
- anyMatch: false,
-
- /**
- * @cfg {Boolean} exactMatch
- * True to force exact match (^ and $ characters added to the regex). Ignored if anyMatch is true.
- */
- exactMatch: false,
-
- /**
- * @cfg {Boolean} caseSensitive
- * True to make the regex case sensitive (adds 'i' switch to regex).
- */
- caseSensitive: false,
-
- /**
- * @cfg {String} root
- * Optional root property. This is mostly useful when filtering a Store, in which case we set the root to 'data' to
- * make the filter pull the {@link #property} out of the data object of each item
- */
- /**
- * Creates new Filter.
- * @param {Object} [config] Config object
- */
- constructor: function(config) {
- var me = this;
- Ext.apply(me, config);
-
- //we're aliasing filter to filterFn mostly for API cleanliness reasons, despite the fact it dirties the code here.
- //Ext.util.Sorter takes a sorterFn property but allows .sort to be called - we do the same here
- me.filter = me.filter || me.filterFn;
-
- if (me.filter === undefined) {
- if (me.property === undefined || me.value === undefined) {
- // Commented this out temporarily because it stops us using string ids in models. TODO: Remove this once
- // Model has been updated to allow string ids
-
- // Ext.Error.raise("A Filter requires either a property or a filterFn to be set");
- } else {
- me.filter = me.createFilterFn();
- }
-
- me.filterFn = me.filter;
- }
- },
-
- /**
- * @private
- * Creates a filter function for the configured property/value/anyMatch/caseSensitive options for this Filter
- */
- createFilterFn: function() {
- var me = this,
- matcher = me.createValueMatcher(),
- property = me.property;
-
- return function(item) {
- var value = me.getRoot.call(me, item)[property];
- return matcher === null ? value === null : matcher.test(value);
- };
- },
-
- /**
- * @private
- * Returns the root property of the given item, based on the configured {@link #root} property
- * @param {Object} item The item
- * @return {Object} The root property of the object
- */
- getRoot: function(item) {
- var root = this.root;
- return root === undefined ? item : item[root];
- },
-
- /**
- * @private
- * Returns a regular expression based on the given value and matching options
- */
- createValueMatcher : function() {
- var me = this,
- value = me.value,
- anyMatch = me.anyMatch,
- exactMatch = me.exactMatch,
- caseSensitive = me.caseSensitive,
- escapeRe = Ext.String.escapeRegex;
-
- if (value === null) {
- return value;
- }
-
- if (!value.exec) { // not a regex
- value = String(value);
- if (anyMatch === true) {
- value = escapeRe(value);
- } else {
- value = '^' + escapeRe(value);
- if (exactMatch === true) {
- value += '$';
- }
- }
- value = new RegExp(value, caseSensitive ? '' : 'i');
- }
-
- return value;
- }
- });
- /**
- * Represents a single sorter that can be applied to a Store. The sorter is used
- * to compare two values against each other for the purpose of ordering them. Ordering
- * is achieved by specifying either:
- *
- * - {@link #property A sorting property}
- * - {@link #sorterFn A sorting function}
- *
- * As a contrived example, we can specify a custom sorter that sorts by rank:
- *
- * Ext.define('Person', {
- * extend: 'Ext.data.Model',
- * fields: ['name', 'rank']
- * });
- *
- * Ext.create('Ext.data.Store', {
- * model: 'Person',
- * proxy: 'memory',
- * sorters: [{
- * sorterFn: function(o1, o2){
- * var getRank = function(o){
- * var name = o.get('rank');
- * if (name === 'first') {
- * return 1;
- * } else if (name === 'second') {
- * return 2;
- * } else {
- * return 3;
- * }
- * },
- * rank1 = getRank(o1),
- * rank2 = getRank(o2);
- *
- * if (rank1 === rank2) {
- * return 0;
- * }
- *
- * return rank1 < rank2 ? -1 : 1;
- * }
- * }],
- * data: [{
- * name: 'Person1',
- * rank: 'second'
- * }, {
- * name: 'Person2',
- * rank: 'third'
- * }, {
- * name: 'Person3',
- * rank: 'first'
- * }]
- * });
- */
- Ext.define('Ext.util.Sorter', {
- /**
- * @cfg {String} property
- * The property to sort by. Required unless {@link #sorterFn} is provided. The property is extracted from the object
- * directly and compared for sorting using the built in comparison operators.
- */
-
- /**
- * @cfg {Function} sorterFn
- * A specific sorter function to execute. Can be passed instead of {@link #property}. This sorter function allows
- * for any kind of custom/complex comparisons. The sorterFn receives two arguments, the objects being compared. The
- * function should return:
- *
- * - -1 if o1 is "less than" o2
- * - 0 if o1 is "equal" to o2
- * - 1 if o1 is "greater than" o2
- */
-
- /**
- * @cfg {String} root
- * Optional root property. This is mostly useful when sorting a Store, in which case we set the root to 'data' to
- * make the filter pull the {@link #property} out of the data object of each item
- */
-
- /**
- * @cfg {Function} transform
- * A function that will be run on each value before it is compared in the sorter. The function will receive a single
- * argument, the value.
- */
-
- /**
- * @cfg {String} direction
- * The direction to sort by.
- */
- direction: "ASC",
-
- constructor: function(config) {
- var me = this;
-
- Ext.apply(me, config);
-
- if (me.property === undefined && me.sorterFn === undefined) {
- Ext.Error.raise("A Sorter requires either a property or a sorter function");
- }
-
- me.updateSortFunction();
- },
-
- /**
- * @private
- * Creates and returns a function which sorts an array by the given property and direction
- * @return {Function} A function which sorts by the property/direction combination provided
- */
- createSortFunction: function(sorterFn) {
- var me = this,
- property = me.property,
- direction = me.direction || "ASC",
- modifier = direction.toUpperCase() == "DESC" ? -1 : 1;
-
- //create a comparison function. Takes 2 objects, returns 1 if object 1 is greater,
- //-1 if object 2 is greater or 0 if they are equal
- return function(o1, o2) {
- return modifier * sorterFn.call(me, o1, o2);
- };
- },
-
- /**
- * @private
- * Basic default sorter function that just compares the defined property of each object
- */
- defaultSorterFn: function(o1, o2) {
- var me = this,
- transform = me.transform,
- v1 = me.getRoot(o1)[me.property],
- v2 = me.getRoot(o2)[me.property];
-
- if (transform) {
- v1 = transform(v1);
- v2 = transform(v2);
- }
- return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
- },
-
- /**
- * @private
- * Returns the root property of the given item, based on the configured {@link #root} property
- * @param {Object} item The item
- * @return {Object} The root property of the object
- */
- getRoot: function(item) {
- return this.root === undefined ? item : item[this.root];
- },
-
- /**
- * Set the sorting direction for this sorter.
- * @param {String} direction The direction to sort in. Should be either 'ASC' or 'DESC'.
- */
- setDirection: function(direction) {
- var me = this;
- me.direction = direction;
- me.updateSortFunction();
- },
-
- /**
- * Toggles the sorting direction for this sorter.
- */
- toggle: function() {
- var me = this;
- me.direction = Ext.String.toggle(me.direction, "ASC", "DESC");
- me.updateSortFunction();
- },
-
- /**
- * Update the sort function for this sorter.
- * @param {Function} [fn] A new sorter function for this sorter. If not specified it will use the default
- * sorting function.
- */
- updateSortFunction: function(fn) {
- var me = this;
- fn = fn || me.sorterFn || me.defaultSorterFn;
- me.sort = me.createSortFunction(fn);
- }
- });
- /**
- * This animation class is a mixin.
- *
- * Ext.util.Animate provides an API for the creation of animated transitions of properties and styles.
- * This class is used as a mixin and currently applied to {@link Ext.Element}, {@link Ext.CompositeElement},
- * {@link Ext.draw.Sprite}, {@link Ext.draw.CompositeSprite}, and {@link Ext.Component}. Note that Components
- * have a limited subset of what attributes can be animated such as top, left, x, y, height, width, and
- * opacity (color, paddings, and margins can not be animated).
- *
- * ## Animation Basics
- *
- * All animations require three things - `easing`, `duration`, and `to` (the final end value for each property)
- * you wish to animate. Easing and duration are defaulted values specified below.
- * Easing describes how the intermediate values used during a transition will be calculated.
- * {@link Ext.fx.Anim#easing Easing} allows for a transition to change speed over its duration.
- * You may use the defaults for easing and duration, but you must always set a
- * {@link Ext.fx.Anim#to to} property which is the end value for all animations.
- *
- * Popular element 'to' configurations are:
- *
- * - opacity
- * - x
- * - y
- * - color
- * - height
- * - width
- *
- * Popular sprite 'to' configurations are:
- *
- * - translation
- * - path
- * - scale
- * - stroke
- * - rotation
- *
- * The default duration for animations is 250 (which is a 1/4 of a second). Duration is denoted in
- * milliseconds. Therefore 1 second is 1000, 1 minute would be 60000, and so on. The default easing curve
- * used for all animations is 'ease'. Popular easing functions are included and can be found in {@link Ext.fx.Anim#easing Easing}.
- *
- * For example, a simple animation to fade out an element with a default easing and duration:
- *
- * var p1 = Ext.get('myElementId');
- *
- * p1.animate({
- * to: {
- * opacity: 0
- * }
- * });
- *
- * To make this animation fade out in a tenth of a second:
- *
- * var p1 = Ext.get('myElementId');
- *
- * p1.animate({
- * duration: 100,
- * to: {
- * opacity: 0
- * }
- * });
- *
- * ## Animation Queues
- *
- * By default all animations are added to a queue which allows for animation via a chain-style API.
- * For example, the following code will queue 4 animations which occur sequentially (one right after the other):
- *
- * p1.animate({
- * to: {
- * x: 500
- * }
- * }).animate({
- * to: {
- * y: 150
- * }
- * }).animate({
- * to: {
- * backgroundColor: '#f00' //red
- * }
- * }).animate({
- * to: {
- * opacity: 0
- * }
- * });
- *
- * You can change this behavior by calling the {@link Ext.util.Animate#syncFx syncFx} method and all
- * subsequent animations for the specified target will be run concurrently (at the same time).
- *
- * p1.syncFx(); //this will make all animations run at the same time
- *
- * p1.animate({
- * to: {
- * x: 500
- * }
- * }).animate({
- * to: {
- * y: 150
- * }
- * }).animate({
- * to: {
- * backgroundColor: '#f00' //red
- * }
- * }).animate({
- * to: {
- * opacity: 0
- * }
- * });
- *
- * This works the same as:
- *
- * p1.animate({
- * to: {
- * x: 500,
- * y: 150,
- * backgroundColor: '#f00' //red
- * opacity: 0
- * }
- * });
- *
- * The {@link Ext.util.Animate#stopAnimation stopAnimation} method can be used to stop any
- * currently running animations and clear any queued animations.
- *
- * ## Animation Keyframes
- *
- * You can also set up complex animations with {@link Ext.fx.Anim#keyframes keyframes} which follow the
- * CSS3 Animation configuration pattern. Note rotation, translation, and scaling can only be done for sprites.
- * The previous example can be written with the following syntax:
- *
- * p1.animate({
- * duration: 1000, //one second total
- * keyframes: {
- * 25: { //from 0 to 250ms (25%)
- * x: 0
- * },
- * 50: { //from 250ms to 500ms (50%)
- * y: 0
- * },
- * 75: { //from 500ms to 750ms (75%)
- * backgroundColor: '#f00' //red
- * },
- * 100: { //from 750ms to 1sec
- * opacity: 0
- * }
- * }
- * });
- *
- * ## Animation Events
- *
- * Each animation you create has events for {@link Ext.fx.Anim#beforeanimate beforeanimate},
- * {@link Ext.fx.Anim#afteranimate afteranimate}, and {@link Ext.fx.Anim#lastframe lastframe}.
- * Keyframed animations adds an additional {@link Ext.fx.Animator#keyframe keyframe} event which
- * fires for each keyframe in your animation.
- *
- * All animations support the {@link Ext.util.Observable#listeners listeners} configuration to attact functions to these events.
- *
- * startAnimate: function() {
- * var p1 = Ext.get('myElementId');
- * p1.animate({
- * duration: 100,
- * to: {
- * opacity: 0
- * },
- * listeners: {
- * beforeanimate: function() {
- * // Execute my custom method before the animation
- * this.myBeforeAnimateFn();
- * },
- * afteranimate: function() {
- * // Execute my custom method after the animation
- * this.myAfterAnimateFn();
- * },
- * scope: this
- * });
- * },
- * myBeforeAnimateFn: function() {
- * // My custom logic
- * },
- * myAfterAnimateFn: function() {
- * // My custom logic
- * }
- *
- * Due to the fact that animations run asynchronously, you can determine if an animation is currently
- * running on any target by using the {@link Ext.util.Animate#getActiveAnimation getActiveAnimation}
- * method. This method will return false if there are no active animations or return the currently
- * running {@link Ext.fx.Anim} instance.
- *
- * In this example, we're going to wait for the current animation to finish, then stop any other
- * queued animations before we fade our element's opacity to 0:
- *
- * var curAnim = p1.getActiveAnimation();
- * if (curAnim) {
- * curAnim.on('afteranimate', function() {
- * p1.stopAnimation();
- * p1.animate({
- * to: {
- * opacity: 0
- * }
- * });
- * });
- * }
- */
- Ext.define('Ext.util.Animate', {
- uses: ['Ext.fx.Manager', 'Ext.fx.Anim'],
- /**
- * Perform custom animation on this object.
- *
- * This method is applicable to both the {@link Ext.Component Component} class and the {@link Ext.Element Element}
- * class. It performs animated transitions of certain properties of this object over a specified timeline.
- *
- * The sole parameter is an object which specifies start property values, end property values, and properties which
- * describe the timeline.
- *
- * ### Animating an {@link Ext.Element Element}
- *
- * When animating an Element, the following properties may be specified in `from`, `to`, and `keyframe` objects:
- *
- * - `x` - The page X position in pixels.
- *
- * - `y` - The page Y position in pixels
- *
- * - `left` - The element's CSS `left` value. Units must be supplied.
- *
- * - `top` - The element's CSS `top` value. Units must be supplied.
- *
- * - `width` - The element's CSS `width` value. Units must be supplied.
- *
- * - `height` - The element's CSS `height` value. Units must be supplied.
- *
- * - `scrollLeft` - The element's `scrollLeft` value.
- *
- * - `scrollTop` - The element's `scrollLeft` value.
- *
- * - `opacity` - The element's `opacity` value. This must be a value between `0` and `1`.
- *
- * **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 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 directly
- * animate certain properties of Components.**
- *
- * ### Animating a {@link Ext.Component Component}
- *
- * When animating a Component, the following properties may be specified in `from`, `to`, and `keyframe` objects:
- *
- * - `x` - The Component's page X position in pixels.
- *
- * - `y` - The Component's page Y position in pixels
- *
- * - `left` - The Component's `left` value in pixels.
- *
- * - `top` - The Component's `top` value in pixels.
- *
- * - `width` - The Component's `width` value in pixels.
- *
- * - `width` - The Component's `width` value in pixels.
- *
- * - `dynamic` - Specify as true to update the Component's layout (if it is a Container) at every frame of the animation.
- * *Use sparingly as laying out on every intermediate size change is an expensive operation.*
- *
- * For example, to animate a Window to a new size, ensuring that its internal layout, and any shadow is correct:
- *
- * myWindow = Ext.create('Ext.window.Window', {
- * title: 'Test Component animation',
- * width: 500,
- * height: 300,
- * layout: {
- * type: 'hbox',
- * align: 'stretch'
- * },
- * items: [{
- * title: 'Left: 33%',
- * margins: '5 0 5 5',
- * flex: 1
- * }, {
- * title: 'Left: 66%',
- * margins: '5 5 5 5',
- * flex: 2
- * }]
- * });
- * myWindow.show();
- * myWindow.header.el.on('click', function() {
- * myWindow.animate({
- * to: {
- * width: (myWindow.getWidth() == 500) ? 700 : 500,
- * height: (myWindow.getHeight() == 300) ? 400 : 300,
- * }
- * });
- * });
- *
- * For performance reasons, by default, the internal layout is only updated when the Window reaches its final `"to"`
- * size. If dynamic updating of the Window's child Components is required, then configure the animation with
- * `dynamic: true` and the two child items will maintain their proportions during the animation.
- *
- * @param {Object} config An object containing properties which describe the animation's start and end states, and
- * the timeline of the animation. Of the properties listed below, only **`to`** is mandatory.
- *
- * Properties include:
- *
- * @param {Object} config.from
- * An object which specifies start values for the properties being animated. If not supplied, properties are
- * animated from current settings. The actual properties which may be animated depend upon ths object being
- * animated. See the sections below on Element and Component animation.
- *
- * @param {Object} config.to
- * An object which specifies end values for the properties being animated.
- *
- * @param {Number} config.duration
- * The duration **in milliseconds** for which the animation will run.
- *
- * @param {String} config.easing
- * 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:
- *
- * - ease
- * - easeIn
- * - easeOut
- * - easeInOut
- * - backIn
- * - backOut
- * - elasticIn
- * - elasticOut
- * - bounceIn
- * - bounceOut
- *
- * @param {Object} config.keyframes
- * This is an object which describes the state of animated properties at certain points along the timeline. 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.
- *
- * @param {Object} config.listeners
- * This is a standard {@link Ext.util.Observable#listeners listeners} configuration object which may be used to
- * inject behaviour at either the `beforeanimate` event or the `afteranimate` event.
- *
- * @return {Object} this
- */
- animate: function(animObj) {
- var me = this;
- if (Ext.fx.Manager.hasFxBlock(me.id)) {
- return me;
- }
- Ext.fx.Manager.queueFx(new Ext.fx.Anim(me.anim(animObj)));
- return this;
- },
- // @private - process the passed fx configuration.
- anim: function(config) {
- if (!Ext.isObject(config)) {
- return (config) ? {} : false;
- }
- var me = this;
- if (config.stopAnimation) {
- me.stopAnimation();
- }
- Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));
- return Ext.apply({
- target: me,
- paused: true
- }, config);
- },
- /**
- * Stops any running effects and clears this object's internal effects queue if it contains any additional effects
- * that haven't started yet.
- * @deprecated 4.0 Replaced by {@link #stopAnimation}
- * @return {Ext.Element} The Element
- * @method
- */
- stopFx: Ext.Function.alias(Ext.util.Animate, 'stopAnimation'),
- /**
- * Stops any running effects and clears this object's internal effects queue if it contains any additional effects
- * that haven't started yet.
- * @return {Ext.Element} The Element
- */
- stopAnimation: function() {
- Ext.fx.Manager.stopAnimation(this.id);
- return this;
- },
- /**
- * Ensures that all effects queued after syncFx is called on this object are run concurrently. This is the opposite
- * of {@link #sequenceFx}.
- * @return {Object} this
- */
- syncFx: function() {
- Ext.fx.Manager.setFxDefaults(this.id, {
- concurrent: true
- });
- return this;
- },
- /**
- * Ensures that all effects queued after sequenceFx is called on this object are run in sequence. This is the
- * opposite of {@link #syncFx}.
- * @return {Object} this
- */
- sequenceFx: function() {
- Ext.fx.Manager.setFxDefaults(this.id, {
- concurrent: false
- });
- return this;
- },
- /**
- * @deprecated 4.0 Replaced by {@link #getActiveAnimation}
- * @inheritdoc Ext.util.Animate#getActiveAnimation
- * @method
- */
- hasActiveFx: Ext.Function.alias(Ext.util.Animate, 'getActiveAnimation'),
- /**
- * Returns the current animation if this object has any effects actively running or queued, else returns false.
- * @return {Ext.fx.Anim/Boolean} Anim if element has active effects, else false
- */
- getActiveAnimation: function() {
- return Ext.fx.Manager.getActiveAnimation(this.id);
- }
- }, function(){
- // Apply Animate mixin manually until Element is defined in the proper 4.x way
- Ext.applyIf(Ext.Element.prototype, this.prototype);
- // We need to call this again so the animation methods get copied over to CE
- Ext.CompositeElementLite.importElementMethods();
- });
- /**
- * This mixin enables classes to declare relationships to child elements and provides the
- * mechanics for acquiring the {@link Ext.Element elements} and storing them on an object
- * instance as properties.
- *
- * This class is used by {@link Ext.Component components} and {@link Ext.layout.container.Container container layouts} to
- * manage their child elements.
- *
- * A typical component that uses these features might look something like this:
- *
- * Ext.define('Ext.ux.SomeComponent', {
- * extend: 'Ext.Component',
- *
- * childEls: [
- * 'bodyEl'
- * ],
- *
- * renderTpl: [
- * '<div id="{id}-bodyEl"></div>'
- * ],
- *
- * // ...
- * });
- *
- * The `childEls` array lists one or more relationships to child elements managed by the
- * component. The items in this array can be either of the following types:
- *
- * - String: the id suffix and property name in one. For example, "bodyEl" in the above
- * example means a "bodyEl" property will be added to the instance with the result of
- * {@link Ext#get} given "componentId-bodyEl" where "componentId" is the component instance's
- * id.
- * - Object: with a `name` property that names the instance property for the element, and
- * one of the following additional properties:
- * - `id`: The full id of the child element.
- * - `itemId`: The suffix part of the id to which "componentId-" is prepended.
- * - `select`: A selector that will be passed to {@link Ext#select}.
- * - `selectNode`: A selector that will be passed to {@link Ext.DomQuery#selectNode}.
- *
- * The example above could have used this instead to achieve the same result:
- *
- * childEls: [
- * { name: 'bodyEl', itemId: 'bodyEl' }
- * ]
- *
- * When using `select`, the property will be an instance of {@link Ext.CompositeElement}. In
- * all other cases, the property will be an {@link Ext.Element} or `null` if not found.
- *
- * Care should be taken when using `select` or `selectNode` to find child elements. The
- * following issues should be considered:
- *
- * - Performance: using selectors can be slower than id lookup by a factor 10x or more.
- * - Over-selecting: selectors are applied after the DOM elements for all children have
- * been rendered, so selectors can match elements from child components (including nested
- * versions of the same component) accidentally.
- *
- * This above issues are most important when using `select` since it returns multiple
- * elements.
- *
- * **IMPORTANT**
- * Unlike a `renderTpl` where there is a single value for an instance, `childEls` are aggregated
- * up the class hierarchy so that they are effectively inherited. In other words, if a
- * class where to derive from `Ext.ux.SomeComponent` in the example above, it could also
- * have a `childEls` property in the same way as `Ext.ux.SomeComponent`.
- *
- * Ext.define('Ext.ux.AnotherComponent', {
- * extend: 'Ext.ux.SomeComponent',
- *
- * childEls: [
- * // 'bodyEl' is inherited
- * 'innerEl'
- * ],
- *
- * renderTpl: [
- * '<div id="{id}-bodyEl">'
- * '<div id="{id}-innerEl"></div>'
- * '</div>'
- * ],
- *
- * // ...
- * });
- *
- * The `renderTpl` contains both child elements and unites them in the desired markup, but
- * the `childEls` only contains the new child element. The {@link #applyChildEls} method
- * takes care of looking up all `childEls` for an instance and considers `childEls`
- * properties on all the super classes and mixins.
- *
- * @private
- */
- Ext.define('Ext.util.ElementContainer', {
- childEls: [
- // empty - this solves a couple problems:
- // 1. It ensures that all classes have a childEls (avoid null ptr)
- // 2. It prevents mixins from smashing on their own childEls (these are gathered
- // specifically)
- ],
- constructor: function () {
- var me = this,
- childEls;
- // if we have configured childEls, we need to merge them with those from this
- // class, its bases and the set of mixins...
- if (me.hasOwnProperty('childEls')) {
- childEls = me.childEls;
- delete me.childEls;
- me.addChildEls.apply(me, childEls);
- }
- },
- destroy: function () {
- var me = this,
- childEls = me.getChildEls(),
- child, childName, i, k;
- for (i = childEls.length; i--; ) {
- childName = childEls[i];
- if (typeof childName != 'string') {
- childName = childName.name;
- }
- child = me[childName];
- if (child) {
- me[childName] = null; // better than delete since that changes the "shape"
- child.remove();
- }
- }
- },
- /**
- * Adds each argument passed to this method to the {@link #childEls} array.
- */
- addChildEls: function () {
- var me = this,
- args = arguments;;
- if (me.hasOwnProperty('childEls')) {
- me.childEls.push.apply(me.childEls, args);
- } else {
- me.childEls = me.getChildEls().concat(Array.prototype.slice.call(args));
- }
-
- me.prune(me.childEls, false);
- },
- /**
- * Sets references to elements inside the component.
- * @private
- */
- applyChildEls: function(el, id) {
- var me = this,
- childEls = me.getChildEls(),
- baseId, childName, i, selector, value;
- baseId = (id || me.id) + '-';
- for (i = childEls.length; i--; ) {
- childName = childEls[i];
- if (typeof childName == 'string') {
- // We don't use Ext.get because that is 3x (or more) slower on IE6-8. Since
- // we know the el's are children of our el we use getById instead:
- value = el.getById(baseId + childName);
- } else {
- if ((selector = childName.select)) {
- value = Ext.select(selector, true, el.dom); // a CompositeElement
- } else if ((selector = childName.selectNode)) {
- value = Ext.get(Ext.DomQuery.selectNode(selector, el.dom));
- } else {
- // see above re:getById...
- value = el.getById(childName.id || (baseId + childName.itemId));
- }
- childName = childName.name;
- }
- me[childName] = value;
- }
- },
- getChildEls: function () {
- var me = this,
- self;
- // If an instance has its own childEls, that is the complete set:
- if (me.hasOwnProperty('childEls')) {
- return me.childEls;
- }
- // Typically, however, the childEls is a class-level concept, so check to see if
- // we have cached the complete set on the class:
- self = me.self;
- return self.$childEls || me.getClassChildEls(self);
- },
- getClassChildEls: function (cls) {
- var me = this,
- result = cls.$childEls,
- childEls, i, length, forked, mixin, mixins, name, parts, proto, supr, superMixins;
- if (!result) {
- // We put the various childEls arrays into parts in the order of superclass,
- // new mixins and finally from cls. These parts can be null or undefined and
- // we will skip them later.
- supr = cls.superclass;
- if (supr) {
- supr = supr.self;
- parts = [supr.$childEls || me.getClassChildEls(supr)]; // super+mixins
- superMixins = supr.prototype.mixins || {};
- } else {
- parts = [];
- superMixins = {};
- }
- proto = cls.prototype;
- mixins = proto.mixins; // since we are a mixin, there will be at least us
- for (name in mixins) {
- if (mixins.hasOwnProperty(name) && !superMixins.hasOwnProperty(name)) {
- mixin = mixins[name].self;
- parts.push(mixin.$childEls || me.getClassChildEls(mixin));
- }
- }
- parts.push(proto.hasOwnProperty('childEls') && proto.childEls);
- for (i = 0, length = parts.length; i < length; ++i) {
- childEls = parts[i];
- if (childEls && childEls.length) {
- if (!result) {
- result = childEls;
- } else {
- if (!forked) {
- forked = true;
- result = result.slice(0);
- }
- result.push.apply(result, childEls);
- }
- }
- }
- cls.$childEls = result = (result ? me.prune(result, !forked) : []);
- }
- return result;
- },
- prune: function (childEls, shared) {
- var index = childEls.length,
- map = {},
- name;
- while (index--) {
- name = childEls[index];
- if (typeof name != 'string') {
- name = name.name;
- }
- if (!map[name]) {
- map[name] = 1;
- } else {
- if (shared) {
- shared = false;
- childEls = childEls.slice(0);
- }
- Ext.Array.erase(childEls, index, 1);
- }
- }
- return childEls;
- },
- /**
- * Removes items in the childEls array based on the return value of a supplied test
- * function. The function is called with a entry in childEls and if the test function
- * return true, that entry is removed. If false, that entry is kept.
- *
- * @param {Function} testFn The test function.
- */
- removeChildEls: function (testFn) {
- var me = this,
- old = me.getChildEls(),
- keepers = (me.childEls = []),
- n, i, cel;
- for (i = 0, n = old.length; i < n; ++i) {
- cel = old[i];
- if (!testFn(cel)) {
- keepers.push(cel);
- }
- }
- }
- });
- /**
- * Given a component hierarchy of this:
- *
- * {
- * xtype: 'panel',
- * id: 'ContainerA',
- * layout: 'hbox',
- * renderTo: Ext.getBody(),
- * items: [
- * {
- * id: 'ContainerB',
- * xtype: 'container',
- * items: [
- * { id: 'ComponentA' }
- * ]
- * }
- * ]
- * }
- *
- * The rendering of the above proceeds roughly like this:
- *
- * - ContainerA's initComponent calls #render passing the `renderTo` property as the
- * container argument.
- * - `render` calls the `getRenderTree` method to get a complete {@link Ext.DomHelper} spec.
- * - `getRenderTree` fires the "beforerender" event and calls the #beforeRender
- * method. Its result is obtained by calling #getElConfig.
- * - The #getElConfig method uses the `renderTpl` and its render data as the content
- * of the `autoEl` described element.
- * - The result of `getRenderTree` is passed to {@link Ext.DomHelper#append}.
- * - The `renderTpl` contains calls to render things like docked items, container items
- * and raw markup (such as the `html` or `tpl` config properties). These calls are to
- * methods added to the {@link Ext.XTemplate} instance by #setupRenderTpl.
- * - The #setupRenderTpl method adds methods such as `renderItems`, `renderContent`, etc.
- * to the template. These are directed to "doRenderItems", "doRenderContent" etc..
- * - The #setupRenderTpl calls traverse from components to their {@link Ext.layout.Layout}
- * object.
- * - When a container is rendered, it also has a `renderTpl`. This is processed when the
- * `renderContainer` method is called in the component's `renderTpl`. This call goes to
- * Ext.layout.container.Container#doRenderContainer. This method repeats this
- * process for all components in the container.
- * - After the top-most component's markup is generated and placed in to the DOM, the next
- * step is to link elements to their components and finish calling the component methods
- * `onRender` and `afterRender` as well as fire the corresponding events.
- * - The first step in this is to call #finishRender. This method descends the
- * component hierarchy and calls `onRender` and fires the `render` event. These calls
- * are delivered top-down to approximate the timing of these calls/events from previous
- * versions.
- * - During the pass, the component's `el` is set. Likewise, the `renderSelectors` and
- * `childEls` are applied to capture references to the component's elements.
- * - These calls are also made on the {@link Ext.layout.container.Container} layout to
- * capture its elements. Both of these classes use {@link Ext.util.ElementContainer} to
- * handle `childEls` processing.
- * - Once this is complete, a similar pass is made by calling #finishAfterRender.
- * This call also descends the component hierarchy, but this time the calls are made in
- * a bottom-up order to `afterRender`.
- *
- * @private
- */
- Ext.define('Ext.util.Renderable', {
- requires: [
- 'Ext.dom.Element'
- ],
- frameCls: Ext.baseCSSPrefix + 'frame',
- frameIdRegex: /[\-]frame\d+[TMB][LCR]$/,
- frameElementCls: {
- tl: [],
- tc: [],
- tr: [],
- ml: [],
- mc: [],
- mr: [],
- bl: [],
- bc: [],
- br: []
- },
- frameElNames: ['TL','TC','TR','ML','MC','MR','BL','BC','BR'],
- frameTpl: [
- '{%this.renderDockedItems(out,values,0);%}',
- '<tpl if="top">',
- '<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>',
- '<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>',
- '<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>',
- '<tpl if="right"></div></tpl>',
- '<tpl if="left"></div></tpl>',
- '</tpl>',
- '<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>',
- '<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>',
- '<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">',
- '{%this.applyRenderTpl(out, values)%}',
- '</div>',
- '<tpl if="right"></div></tpl>',
- '<tpl if="left"></div></tpl>',
- '<tpl if="bottom">',
- '<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>',
- '<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>',
- '<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>',
- '<tpl if="right"></div></tpl>',
- '<tpl if="left"></div></tpl>',
- '</tpl>',
- '{%this.renderDockedItems(out,values,1);%}'
- ],
- frameTableTpl: [
- '{%this.renderDockedItems(out,values,0);%}',
- '<table><tbody>',
- '<tpl if="top">',
- '<tr>',
- '<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>',
- '<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>',
- '<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>',
- '</tr>',
- '</tpl>',
- '<tr>',
- '<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>',
- '<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">',
- '{%this.applyRenderTpl(out, values)%}',
- '</td>',
- '<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>',
- '</tr>',
- '<tpl if="bottom">',
- '<tr>',
- '<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>',
- '<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>',
- '<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>',
- '</tr>',
- '</tpl>',
- '</tbody></table>',
- '{%this.renderDockedItems(out,values,1);%}'
- ],
- /**
- * Allows addition of behavior after rendering is complete. At this stage the Component’s Element
- * will have been styled according to the configuration, will have had any configured CSS class
- * names added, and will be in the configured visibility and the configured enable state.
- *
- * @template
- * @protected
- */
- afterRender : function() {
- var me = this,
- data = {},
- protoEl = me.protoEl,
- target = me.getTargetEl(),
- item;
- me.finishRenderChildren();
- if (me.styleHtmlContent) {
- target.addCls(me.styleHtmlCls);
- }
-
- protoEl.writeTo(data);
-
- // Here we apply any styles that were set on the protoEl during the rendering phase
- // A majority of times this will not happen, but we still need to handle it
-
- item = data.removed;
- if (item) {
- target.removeCls(item);
- }
-
- item = data.cls;
- if (item.length) {
- target.addCls(item);
- }
-
- item = data.style;
- if (data.style) {
- target.setStyle(item);
- }
-
- me.protoEl = null;
- // If this is the outermost Container, lay it out as soon as it is rendered.
- if (!me.ownerCt) {
- me.updateLayout();
- }
- },
- afterFirstLayout : function() {
- var me = this,
- hasX = Ext.isDefined(me.x),
- hasY = Ext.isDefined(me.y),
- pos, xy;
- // For floaters, calculate x and y if they aren't defined by aligning
- // the sized element to the center of either the container or the ownerCt
- if (me.floating && (!hasX || !hasY)) {
- if (me.floatParent) {
- xy = me.el.getAlignToXY(me.floatParent.getTargetEl(), 'c-c');
- pos = me.floatParent.getTargetEl().translatePoints(xy[0], xy[1]);
- } else {
- xy = me.el.getAlignToXY(me.container, 'c-c');
- pos = me.container.translatePoints(xy[0], xy[1]);
- }
- me.x = hasX ? me.x : pos.left;
- me.y = hasY ? me.y : pos.top;
- hasX = hasY = true;
- }
- if (hasX || hasY) {
- me.setPosition(me.x, me.y);
- }
- me.onBoxReady();
- if (me.hasListeners.boxready) {
- me.fireEvent('boxready', me);
- }
- },
-
- onBoxReady: Ext.emptyFn,
- /**
- * Sets references to elements inside the component. This applies {@link #renderSelectors}
- * as well as {@link #childEls}.
- * @private
- */
- applyRenderSelectors: function() {
- var me = this,
- selectors = me.renderSelectors,
- el = me.el,
- dom = el.dom,
- selector;
- me.applyChildEls(el);
- // We still support renderSelectors. There are a few places in the framework that
- // need them and they are a documented part of the API. In fact, we support mixing
- // childEls and renderSelectors (no reason not to).
- if (selectors) {
- for (selector in selectors) {
- if (selectors.hasOwnProperty(selector) && selectors[selector]) {
- me[selector] = Ext.get(Ext.DomQuery.selectNode(selectors[selector], dom));
- }
- }
- }
- },
- beforeRender: function () {
- var me = this,
- layout = me.getComponentLayout();
- if (!layout.initialized) {
- layout.initLayout();
- }
- me.setUI(me.ui);
- if (me.disabled) {
- // pass silent so the event doesn't fire the first time.
- me.disable(true);
- }
- },
- /**
- * @private
- * Called from the selected frame generation template to insert this Component's inner structure inside the framing structure.
- *
- * When framing is used, a selected frame generation template is used as the primary template of the #getElConfig instead
- * of the configured {@link #renderTpl}. The {@link #renderTpl} is invoked by this method which is injected into the framing template.
- */
- doApplyRenderTpl: function(out, values) {
- // Careful! This method is bolted on to the frameTpl so all we get for context is
- // the renderData! The "this" pointer is the frameTpl instance!
- var me = values.$comp,
- tpl;
- // Don't do this if the component is already rendered:
- if (!me.rendered) {
- tpl = me.initRenderTpl();
- tpl.applyOut(values.renderData, out);
- }
- },
- /**
- * Handles autoRender.
- * Floating Components may have an ownerCt. If they are asking to be constrained, constrain them within that
- * ownerCt, and have their z-index managed locally. Floating Components are always rendered to document.body
- */
- doAutoRender: function() {
- var me = this;
- if (!me.rendered) {
- if (me.floating) {
- me.render(document.body);
- } else {
- me.render(Ext.isBoolean(me.autoRender) ? Ext.getBody() : me.autoRender);
- }
- }
- },
- doRenderContent: function (out, renderData) {
- // Careful! This method is bolted on to the renderTpl so all we get for context is
- // the renderData! The "this" pointer is the renderTpl instance!
- var me = renderData.$comp;
- if (me.html) {
- Ext.DomHelper.generateMarkup(me.html, out);
- delete me.html;
- }
- if (me.tpl) {
- // Make sure this.tpl is an instantiated XTemplate
- if (!me.tpl.isTemplate) {
- me.tpl = new Ext.XTemplate(me.tpl);
- }
- if (me.data) {
- //me.tpl[me.tplWriteMode](target, me.data);
- me.tpl.applyOut(me.data, out);
- delete me.data;
- }
- }
- },
- doRenderFramingDockedItems: function (out, renderData, after) {
- // Careful! This method is bolted on to the frameTpl so all we get for context is
- // the renderData! The "this" pointer is the frameTpl instance!
- var me = renderData.$comp;
- // Most components don't have dockedItems, so check for doRenderDockedItems on the
- // component (also, don't do this if the component is already rendered):
- if (!me.rendered && me.doRenderDockedItems) {
- // The "renderData" property is placed in scope for the renderTpl, but we don't
- // want to render docked items at that level in addition to the framing level:
- renderData.renderData.$skipDockedItems = true;
- // doRenderDockedItems requires the $comp property on renderData, but this is
- // set on the frameTpl's renderData as well:
- me.doRenderDockedItems.call(this, out, renderData, after);
- }
- },
- /**
- * This method visits the rendered component tree in a "top-down" order. That is, this
- * code runs on a parent component before running on a child. This method calls the
- * {@link #onRender} method of each component.
- * @param {Number} containerIdx The index into the Container items of this Component.
- *
- * @private
- */
- finishRender: function(containerIdx) {
- var me = this,
- tpl, data, contentEl, el, pre, hide, target;
- // We are typically called w/me.el==null as a child of some ownerCt that is being
- // rendered. We are also called by render for a normal component (w/o a configured
- // me.el). In this case, render sets me.el and me.rendering (indirectly). Lastly
- // we are also called on a component (like a Viewport) that has a configured me.el
- // (body for a Viewport) when render is called. In this case, it is not flagged as
- // "me.rendering" yet becasue it does not produce a renderTree. We use this to know
- // not to regen the renderTpl.
- if (!me.el || me.$pid) {
- if (me.container) {
- el = me.container.getById(me.id, true);
- } else {
- el = Ext.getDom(me.id);
- }
- if (!me.el) {
- // Typical case: we produced the el during render
- me.wrapPrimaryEl(el);
- } else {
- // We were configured with an el and created a proxy, so now we can swap
- // the proxy for me.el:
- delete me.$pid;
- if (!me.el.dom) {
- // make sure me.el is an Element
- me.wrapPrimaryEl(me.el);
- }
- el.parentNode.insertBefore(me.el.dom, el);
- Ext.removeNode(el); // remove placeholder el
- // TODO - what about class/style?
- }
- } else if (!me.rendering) {
- // We were configured with an el and then told to render (e.g., Viewport). We
- // need to generate the proper DOM. Insert first because the layout system
- // insists that child Component elements indices match the Component indices.
- tpl = me.initRenderTpl();
- if (tpl) {
- data = me.initRenderData();
- tpl.insertFirst(me.getTargetEl(), data);
- }
- }
- // else we are rendering
- if (!me.container) {
- // top-level rendered components will already have me.container set up
- me.container = Ext.get(me.el.dom.parentNode);
- }
- if (me.ctCls) {
- me.container.addCls(me.ctCls);
- }
- // Sets the rendered flag and clears the redering flag
- me.onRender(me.container, containerIdx);
- // Initialize with correct overflow attributes
- target = me.getTargetEl();
- target.setStyle(me.getOverflowStyle());
- // Tell the encapsulating element to hide itself in the way the Component is configured to hide
- // This means DISPLAY, VISIBILITY or OFFSETS.
- me.el.setVisibilityMode(Ext.Element[me.hideMode.toUpperCase()]);
- if (me.overCls) {
- me.el.hover(me.addOverCls, me.removeOverCls, me);
- }
- if (me.hasListeners.render) {
- me.fireEvent('render', me);
- }
- if (me.contentEl) {
- pre = Ext.baseCSSPrefix;
- hide = pre + 'hide-';
- contentEl = Ext.get(me.contentEl);
- contentEl.removeCls([pre+'hidden', hide+'display', hide+'offsets', hide+'nosize']);
- target.appendChild(contentEl.dom);
- }
- me.afterRender(); // this can cause a layout
- if (me.hasListeners.afterrender) {
- me.fireEvent('afterrender', me);
- }
- me.initEvents();
- if (me.hidden) {
- // Hiding during the render process should not perform any ancillary
- // actions that the full hide process does; It is not hiding, it begins in a hidden state.'
- // So just make the element hidden according to the configured hideMode
- me.el.hide();
- }
- },
- finishRenderChildren: function () {
- var layout = this.getComponentLayout();
- layout.finishRender();
- },
- getElConfig : function() {
- var me = this,
- autoEl = me.autoEl,
- frameInfo = me.getFrameInfo(),
- config = {
- tag: 'div',
- id: me.id,
- tpl: frameInfo ? me.initFramingTpl(frameInfo.table) : me.initRenderTpl()
- };
- me.initStyles(me.protoEl);
- me.protoEl.writeTo(config);
- me.protoEl.flush();
- if (Ext.isString(autoEl)) {
- config.tag = autoEl;
- } else {
- Ext.apply(config, autoEl); // harmless if !autoEl
- }
- if (config.tpl) {
- // Use the framingTpl as the main content creating template. It will call out to this.applyRenderTpl(out, values)
- if (frameInfo) {
- var i,
- frameElNames = me.frameElNames,
- len = frameElNames.length,
- suffix,
- frameGenId = me.id + '-frame1';
- me.frameGenId = 1;
- config.tplData = Ext.apply({}, {
- $comp: me,
- fgid: frameGenId,
- ui: me.ui,
- uiCls: me.uiCls,
- frameCls: me.frameCls,
- baseCls: me.baseCls,
- frameWidth: frameInfo.maxWidth,
- top: !!frameInfo.top,
- left: !!frameInfo.left,
- right: !!frameInfo.right,
- bottom: !!frameInfo.bottom,
- renderData: me.initRenderData()
- }, me.getFramePositions(frameInfo));
- // Add the childEls for each of the frame elements
- for (i = 0; i < len; i++) {
- suffix = frameElNames[i];
- me.addChildEls({ name: 'frame' + suffix, id: frameGenId + suffix });
- }
- // Panel must have a frameBody
- me.addChildEls({
- name: 'frameBody',
- id: frameGenId + 'MC'
- });
- } else {
- config.tplData = me.initRenderData();
- }
- }
- return config;
- },
- // Create the framingTpl from the string.
- // Poke in a reference to applyRenderTpl(frameInfo, out)
- initFramingTpl: function(table) {
- var tpl = table ? this.getTpl('frameTableTpl') : this.getTpl('frameTpl');
- if (tpl && !tpl.applyRenderTpl) {
- this.setupFramingTpl(tpl);
- }
- return tpl;
- },
- /**
- * @private
- * Inject a reference to the function which applies the render template into the framing template. The framing template
- * wraps the content.
- */
- setupFramingTpl: function(frameTpl) {
- frameTpl.applyRenderTpl = this.doApplyRenderTpl;
- frameTpl.renderDockedItems = this.doRenderFramingDockedItems;
- },
- /**
- * This function takes the position argument passed to onRender and returns a
- * DOM element that you can use in the insertBefore.
- * @param {String/Number/Ext.dom.Element/HTMLElement} position Index, element id or element you want
- * to put this component before.
- * @return {HTMLElement} DOM element that you can use in the insertBefore
- */
- getInsertPosition: function(position) {
- // Convert the position to an element to insert before
- if (position !== undefined) {
- if (Ext.isNumber(position)) {
- position = this.container.dom.childNodes[position];
- }
- else {
- position = Ext.getDom(position);
- }
- }
- return position;
- },
- getRenderTree: function() {
- var me = this;
- me.beforeRender();
- if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) {
- // Flag to let the layout's finishRenderItems and afterFinishRenderItems
- // know which items to process
- me.rendering = true;
- if (me.el) {
- // Since we are producing a render tree, we produce a "proxy el" that will
- // sit in the rendered DOM precisely where me.el belongs. We replace the
- // proxy el in the finishRender phase.
- return {
- tag: 'div',
- id: (me.$pid = Ext.id())
- };
- }
- return me.getElConfig();
- }
- return null;
- },
- initContainer: function(container) {
- var me = this;
- // If you render a component specifying the el, we get the container
- // of the el, and make sure we dont move the el around in the dom
- // during the render
- if (!container && me.el) {
- container = me.el.dom.parentNode;
- me.allowDomMove = false;
- }
- me.container = container.dom ? container : Ext.get(container);
- return me.container;
- },
- /**
- * Initialized the renderData to be used when rendering the renderTpl.
- * @return {Object} Object with keys and values that are going to be applied to the renderTpl
- * @private
- */
- initRenderData: function() {
- var me = this;
- return Ext.apply({
- $comp: me,
- id: me.id,
- ui: me.ui,
- uiCls: me.uiCls,
- baseCls: me.baseCls,
- componentCls: me.componentCls,
- frame: me.frame
- }, me.renderData);
- },
- /**
- * Initializes the renderTpl.
- * @return {Ext.XTemplate} The renderTpl XTemplate instance.
- * @private
- */
- initRenderTpl: function() {
- var tpl = this.getTpl('renderTpl');
- if (tpl && !tpl.renderContent) {
- this.setupRenderTpl(tpl);
- }
- return tpl;
- },
- /**
- * Template method called when this Component's DOM structure is created.
- *
- * At this point, this Component's (and all descendants') DOM structure *exists* but it has not
- * been layed out (positioned and sized).
- *
- * Subclasses which override this to gain access to the structure at render time should
- * call the parent class's method before attempting to access any child elements of the Component.
- *
- * @param {Ext.core.Element} parentNode The parent Element in which this Component's encapsulating element is contained.
- * @param {Number} containerIdx The index within the parent Container's child collection of this Component.
- *
- * @template
- * @protected
- */
- onRender: function(parentNode, containerIdx) {
- var me = this,
- x = me.x,
- y = me.y,
- lastBox, width, height,
- el = me.el;
- // After the container property has been collected, we can wrap the Component in a reset wraper if necessary
- if (Ext.scopeResetCSS && !me.ownerCt) {
- // If this component's el is the body element, we add the reset class to the html tag
- if (el.dom == Ext.getBody().dom) {
- el.parent().addCls(Ext.resetCls);
- }
- else {
- // Else we wrap this element in an element that adds the reset class.
- me.resetEl = el.wrap({
- cls: Ext.resetCls
- });
- }
- }
- me.applyRenderSelectors();
- // Flag set on getRenderTree to flag to the layout's postprocessing routine that
- // the Component is in the process of being rendered and needs postprocessing.
- delete me.rendering;
- me.rendered = true;
- // We need to remember these to avoid writing them during the initial layout:
- lastBox = null;
- if (x !== undefined) {
- lastBox = lastBox || {};
- lastBox.x = x;
- }
- if (y !== undefined) {
- lastBox = lastBox || {};
- lastBox.y = y;
- }
- // Framed components need their width/height to apply to the frame, which is
- // best handled in layout at present.
- // If we're using the content box model, we also cannot assign initial sizes since we do not know the border widths to subtract
- if (!me.getFrameInfo() && Ext.isBorderBox) {
- width = me.width;
- height = me.height;
- if (typeof width == 'number') {
- lastBox = lastBox || {};
- lastBox.width = width;
- }
- if (typeof height == 'number') {
- lastBox = lastBox || {};
- lastBox.height = height;
- }
- }
- me.lastBox = me.el.lastBox = lastBox;
- },
- render: function(container, position) {
- var me = this,
- el = me.el && (me.el = Ext.get(me.el)), // ensure me.el is wrapped
- tree,
- nextSibling;
- Ext.suspendLayouts();
- container = me.initContainer(container);
- nextSibling = me.getInsertPosition(position);
- if (!el) {
- tree = me.getRenderTree();
- if (nextSibling) {
- el = Ext.DomHelper.insertBefore(nextSibling, tree);
- } else {
- el = Ext.DomHelper.append(container, tree);
- }
- me.wrapPrimaryEl(el);
- } else {
- // Set configured styles on pre-rendered Component's element
- me.initStyles(el);
- if (me.allowDomMove !== false) {
- //debugger; // TODO
- if (nextSibling) {
- container.dom.insertBefore(el.dom, nextSibling);
- } else {
- container.dom.appendChild(el.dom);
- }
- }
- }
- me.finishRender(position);
- Ext.resumeLayouts(!container.isDetachedBody);
- },
- /**
- * Ensures that this component is attached to `document.body`. If the component was
- * rendered to {@link Ext#getDetachedBody}, then it will be appended to `document.body`.
- * Any configured position is also restored.
- * @param {Boolean} [runLayout=false] True to run the component's layout.
- */
- ensureAttachedToBody: function (runLayout) {
- var comp = this,
- body;
- while (comp.ownerCt) {
- comp = comp.ownerCt;
- }
- if (comp.container.isDetachedBody) {
- comp.container = body = Ext.getBody();
- body.appendChild(comp.el.dom);
- if (runLayout) {
- comp.updateLayout();
- }
- if (typeof comp.x == 'number' || typeof comp.y == 'number') {
- comp.setPosition(comp.x, comp.y);
- }
- }
- },
- setupRenderTpl: function (renderTpl) {
- renderTpl.renderBody = renderTpl.renderContent = this.doRenderContent;
- },
- wrapPrimaryEl: function (dom) {
- this.el = Ext.get(dom, true);
- },
- /**
- * @private
- */
- initFrame : function() {
- if (Ext.supports.CSS3BorderRadius) {
- return;
- }
- var me = this,
- frameInfo = me.getFrameInfo(),
- frameWidth, frameTpl, frameGenId,
- i,
- frameElNames = me.frameElNames,
- len = frameElNames.length,
- suffix;
- if (frameInfo) {
- frameWidth = frameInfo.maxWidth;
- frameTpl = me.getFrameTpl(frameInfo.table);
- // since we render id's into the markup and id's NEED to be unique, we have a
- // simple strategy for numbering their generations.
- me.frameGenId = frameGenId = (me.frameGenId || 0) + 1;
- frameGenId = me.id + '-frame' + frameGenId;
- // Here we render the frameTpl to this component. This inserts the 9point div or the table framing.
- frameTpl.insertFirst(me.el, Ext.apply({
- $comp: me,
- fgid: frameGenId,
- ui: me.ui,
- uiCls: me.uiCls,
- frameCls: me.frameCls,
- baseCls: me.baseCls,
- frameWidth: frameWidth,
- top: !!frameInfo.top,
- left: !!frameInfo.left,
- right: !!frameInfo.right,
- bottom: !!frameInfo.bottom
- }, me.getFramePositions(frameInfo)));
- // The frameBody is returned in getTargetEl, so that layouts render items to the correct target.
- me.frameBody = me.el.down('.' + me.frameCls + '-mc');
- // Clean out the childEls for the old frame elements (the majority of the els)
- me.removeChildEls(function (c) {
- return c.id && me.frameIdRegex.test(c.id);
- });
- // Grab references to the childEls for each of the new frame elements
- for (i = 0; i < len; i++) {
- suffix = frameElNames[i];
- me['frame' + suffix] = me.el.getById(frameGenId + suffix);
- }
- }
- },
- updateFrame: function() {
- if (Ext.supports.CSS3BorderRadius) {
- return;
- }
- var me = this,
- wasTable = this.frameSize && this.frameSize.table,
- oldFrameTL = this.frameTL,
- oldFrameBL = this.frameBL,
- oldFrameML = this.frameML,
- oldFrameMC = this.frameMC,
- newMCClassName;
- this.initFrame();
- if (oldFrameMC) {
- if (me.frame) {
- // Store the class names set on the new MC
- newMCClassName = this.frameMC.dom.className;
- // Framing elements have been selected in initFrame, no need to run applyRenderSelectors
- // Replace the new mc with the old mc
- oldFrameMC.insertAfter(this.frameMC);
- this.frameMC.remove();
- // Restore the reference to the old frame mc as the framebody
- this.frameBody = this.frameMC = oldFrameMC;
- // Apply the new mc classes to the old mc element
- oldFrameMC.dom.className = newMCClassName;
- // Remove the old framing
- if (wasTable) {
- me.el.query('> table')[1].remove();
- }
- else {
- if (oldFrameTL) {
- oldFrameTL.remove();
- }
- if (oldFrameBL) {
- oldFrameBL.remove();
- }
- if (oldFrameML) {
- oldFrameML.remove();
- }
- }
- }
- else {
- // We were framed but not anymore. Move all content from the old frame to the body
- }
- }
- else if (me.frame) {
- this.applyRenderSelectors();
- }
- },
- /**
- * @private
- * On render, reads an encoded style attribute, "background-position" from the style of this Component's element.
- * This information is memoized based upon the CSS class name of this Component's element.
- * Because child Components are rendered as textual HTML as part of the topmost Container, a dummy div is inserted
- * into the document to receive the document element's CSS class name, and therefore style attributes.
- */
- getFrameInfo: function() {
- // If native framing can be used, or this Component is not configured (or written) to be framed,
- // then do not attempt to read CSS framing info.
- if (Ext.supports.CSS3BorderRadius) {
- return false;
- }
- var me = this,
- frameInfoCache = me.frameInfoCache,
- el = me.el || me.protoEl,
- cls = el.dom ? el.dom.className : el.classList.join(' '),
- frameInfo = frameInfoCache[cls],
- styleEl, left, top, info;
- if (frameInfo == null) {
- // Get the singleton frame style proxy with our el class name stamped into it.
- styleEl = Ext.fly(me.getStyleProxy(cls), 'frame-style-el');
- left = styleEl.getStyle('background-position-x');
- top = styleEl.getStyle('background-position-y');
- // Some browsers don't support background-position-x and y, so for those
- // browsers let's split background-position into two parts.
- if (!left && !top) {
- info = styleEl.getStyle('background-position').split(' ');
- left = info[0];
- top = info[1];
- }
- frameInfo = me.calculateFrame(left, top);
- if (frameInfo) {
- // Just to be sure we set the background image of the el to none.
- el.setStyle('background-image', 'none');
- }
- // This happens when you set frame: true explicitly without using the x-frame mixin in sass.
- // This way IE can't figure out what sizes to use and thus framing can't work.
- if (me.frame === true && !frameInfo) {
- Ext.log.error('You have set frame: true explicity on this component (' + me.getXType() + ') and it ' +
- 'does not have any framing defined in the CSS template. In this case IE cannot figure out ' +
- 'what sizes to use and thus framing on this component will be disabled.');
- }
- frameInfoCache[cls] = frameInfo;
- }
- me.frame = !!frameInfo;
- me.frameSize = frameInfo;
- return frameInfo;
- },
-
- calculateFrame: function(left, top){
- // We actually pass a string in the form of '[type][tl][tr]px [direction][br][bl]px' as
- // the background position of this.el from the CSS to indicate to IE that this component needs
- // framing. We parse it here.
- if (!(parseInt(left, 10) >= 1000000 && parseInt(top, 10) >= 1000000)) {
- return false;
- }
- var max = Math.max,
- tl = parseInt(left.substr(3, 2), 10),
- tr = parseInt(left.substr(5, 2), 10),
- br = parseInt(top.substr(3, 2), 10),
- bl = parseInt(top.substr(5, 2), 10),
- frameInfo = {
- // Table markup starts with 110, div markup with 100.
- table: left.substr(0, 3) == '110',
- // Determine if we are dealing with a horizontal or vertical component
- vertical: top.substr(0, 3) == '110',
- // Get and parse the different border radius sizes
- top: max(tl, tr),
- right: max(tr, br),
- bottom: max(bl, br),
- left: max(tl, bl)
- };
- frameInfo.maxWidth = max(frameInfo.top, frameInfo.right, frameInfo.bottom, frameInfo.left);
- frameInfo.width = frameInfo.left + frameInfo.right;
- frameInfo.height = frameInfo.top + frameInfo.bottom;
- return frameInfo;
- },
- /**
- * @private
- * Returns an offscreen div with the same class name as the element this is being rendered.
- * This is because child item rendering takes place in a detached div which, being ot part of the document, has no styling.
- */
- getStyleProxy: function(cls) {
- var result = this.styleProxyEl || (Ext.AbstractComponent.prototype.styleProxyEl = Ext.getBody().createChild({
- style: {
- position: 'absolute',
- top: '-10000px'
- }
- }, null, true));
- result.className = cls;
- return result;
- },
- getFramePositions: function(frameInfo) {
- var me = this,
- frameWidth = frameInfo.maxWidth,
- dock = me.dock,
- positions, tc, bc, ml, mr;
- if (frameInfo.vertical) {
- tc = '0 -' + (frameWidth * 0) + 'px';
- bc = '0 -' + (frameWidth * 1) + 'px';
- if (dock && dock == "right") {
- tc = 'right -' + (frameWidth * 0) + 'px';
- bc = 'right -' + (frameWidth * 1) + 'px';
- }
- positions = {
- tl: '0 -' + (frameWidth * 0) + 'px',
- tr: '0 -' + (frameWidth * 1) + 'px',
- bl: '0 -' + (frameWidth * 2) + 'px',
- br: '0 -' + (frameWidth * 3) + 'px',
- ml: '-' + (frameWidth * 1) + 'px 0',
- mr: 'right 0',
- tc: tc,
- bc: bc
- };
- } else {
- ml = '-' + (frameWidth * 0) + 'px 0';
- mr = 'right 0';
- if (dock && dock == "bottom") {
- ml = 'left bottom';
- mr = 'right bottom';
- }
- positions = {
- tl: '0 -' + (frameWidth * 2) + 'px',
- tr: 'right -' + (frameWidth * 3) + 'px',
- bl: '0 -' + (frameWidth * 4) + 'px',
- br: 'right -' + (frameWidth * 5) + 'px',
- ml: ml,
- mr: mr,
- tc: '0 -' + (frameWidth * 0) + 'px',
- bc: '0 -' + (frameWidth * 1) + 'px'
- };
- }
- return positions;
- },
- /**
- * @private
- */
- getFrameTpl : function(table) {
- return this.getTpl(table ? 'frameTableTpl' : 'frameTpl');
- },
- // Cache the frame information object so as not to cause style recalculations
- frameInfoCache: {}
- });
- /**
- * Provides searching of Components within Ext.ComponentManager (globally) or a specific
- * Ext.container.Container on the document with a similar syntax to a CSS selector.
- *
- * Components can be retrieved by using their {@link Ext.Component xtype} with an optional . prefix
- *
- * - `component` or `.component`
- * - `gridpanel` or `.gridpanel`
- *
- * An itemId or id must be prefixed with a #
- *
- * - `#myContainer`
- *
- * Attributes must be wrapped in brackets
- *
- * - `component[autoScroll]`
- * - `panel[title="Test"]`
- *
- * Member expressions from candidate Components may be tested. If the expression returns a *truthy* value,
- * the candidate Component will be included in the query:
- *
- * var disabledFields = myFormPanel.query("{isDisabled()}");
- *
- * Pseudo classes may be used to filter results in the same way as in {@link Ext.DomQuery DomQuery}:
- *
- * // Function receives array and returns a filtered array.
- * Ext.ComponentQuery.pseudos.invalid = function(items) {
- * var i = 0, l = items.length, c, result = [];
- * for (; i < l; i++) {
- * if (!(c = items[i]).isValid()) {
- * result.push(c);
- * }
- * }
- * return result;
- * };
- *
- * var invalidFields = myFormPanel.query('field:invalid');
- * if (invalidFields.length) {
- * invalidFields[0].getEl().scrollIntoView(myFormPanel.body);
- * for (var i = 0, l = invalidFields.length; i < l; i++) {
- * invalidFields[i].getEl().frame("red");
- * }
- * }
- *
- * Default pseudos include:
- *
- * - not
- * - last
- *
- * Queries return an array of components.
- * Here are some example queries.
- *
- * // retrieve all Ext.Panels in the document by xtype
- * var panelsArray = Ext.ComponentQuery.query('panel');
- *
- * // retrieve all Ext.Panels within the container with an id myCt
- * var panelsWithinmyCt = Ext.ComponentQuery.query('#myCt panel');
- *
- * // retrieve all direct children which are Ext.Panels within myCt
- * var directChildPanel = Ext.ComponentQuery.query('#myCt > panel');
- *
- * // retrieve all grids and trees
- * var gridsAndTrees = Ext.ComponentQuery.query('gridpanel, treepanel');
- *
- * For easy access to queries based from a particular Container see the {@link Ext.container.Container#query},
- * {@link Ext.container.Container#down} and {@link Ext.container.Container#child} methods. Also see
- * {@link Ext.Component#up}.
- */
- Ext.define('Ext.ComponentQuery', {
- singleton: true,
- uses: ['Ext.ComponentManager']
- }, function() {
- var cq = this,
- // A function source code pattern with a placeholder which accepts an expression which yields a truth value when applied
- // as a member on each item in the passed array.
- filterFnPattern = [
- 'var r = [],',
- 'i = 0,',
- 'it = items,',
- 'l = it.length,',
- 'c;',
- 'for (; i < l; i++) {',
- 'c = it[i];',
- 'if (c.{0}) {',
- 'r.push(c);',
- '}',
- '}',
- 'return r;'
- ].join(''),
- filterItems = function(items, operation) {
- // Argument list for the operation is [ itemsArray, operationArg1, operationArg2...]
- // The operation's method loops over each item in the candidate array and
- // returns an array of items which match its criteria
- return operation.method.apply(this, [ items ].concat(operation.args));
- },
- getItems = function(items, mode) {
- var result = [],
- i = 0,
- length = items.length,
- candidate,
- deep = mode !== '>';
-
- for (; i < length; i++) {
- candidate = items[i];
- if (candidate.getRefItems) {
- result = result.concat(candidate.getRefItems(deep));
- }
- }
- return result;
- },
- getAncestors = function(items) {
- var result = [],
- i = 0,
- length = items.length,
- candidate;
- for (; i < length; i++) {
- candidate = items[i];
- while (!!(candidate = (candidate.ownerCt || candidate.floatParent))) {
- result.push(candidate);
- }
- }
- return result;
- },
- // Filters the passed candidate array and returns only items which match the passed xtype
- filterByXType = function(items, xtype, shallow) {
- if (xtype === '*') {
- return items.slice();
- }
- else {
- var result = [],
- i = 0,
- length = items.length,
- candidate;
- for (; i < length; i++) {
- candidate = items[i];
- if (candidate.isXType(xtype, shallow)) {
- result.push(candidate);
- }
- }
- return result;
- }
- },
- // Filters the passed candidate array and returns only items which have the passed className
- filterByClassName = function(items, className) {
- var EA = Ext.Array,
- result = [],
- i = 0,
- length = items.length,
- candidate;
- for (; i < length; i++) {
- candidate = items[i];
- if (candidate.hasCls(className)) {
- result.push(candidate);
- }
- }
- return result;
- },
- // Filters the passed candidate array and returns only items which have the specified property match
- filterByAttribute = function(items, property, operator, value) {
- var result = [],
- i = 0,
- length = items.length,
- candidate;
- for (; i < length; i++) {
- candidate = items[i];
- if (!value ? !!candidate[property] : (String(candidate[property]) === value)) {
- result.push(candidate);
- }
- }
- return result;
- },
- // Filters the passed candidate array and returns only items which have the specified itemId or id
- filterById = function(items, id) {
- var result = [],
- i = 0,
- length = items.length,
- candidate;
- for (; i < length; i++) {
- candidate = items[i];
- if (candidate.getItemId() === id) {
- result.push(candidate);
- }
- }
- return result;
- },
- // Filters the passed candidate array and returns only items which the named pseudo class matcher filters in
- filterByPseudo = function(items, name, value) {
- return cq.pseudos[name](items, value);
- },
- // Determines leading mode
- // > for direct child, and ^ to switch to ownerCt axis
- modeRe = /^(\s?([>\^])\s?|\s|$)/,
- // Matches a token with possibly (true|false) appended for the "shallow" parameter
- tokenRe = /^(#)?([\w\-]+|\*)(?:\((true|false)\))?/,
- matchers = [{
- // Checks for .xtype with possibly (true|false) appended for the "shallow" parameter
- re: /^\.([\w\-]+)(?:\((true|false)\))?/,
- method: filterByXType
- },{
- // checks for [attribute=value]
- re: /^(?:[\[](?:@)?([\w\-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]])/,
- method: filterByAttribute
- }, {
- // checks for #cmpItemId
- re: /^#([\w\-]+)/,
- method: filterById
- }, {
- // checks for :<pseudo_class>(<selector>)
- re: /^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/,
- method: filterByPseudo
- }, {
- // checks for {<member_expression>}
- re: /^(?:\{([^\}]+)\})/,
- method: filterFnPattern
- }];
- // Internal class Ext.ComponentQuery.Query
- cq.Query = Ext.extend(Object, {
- constructor: function(cfg) {
- cfg = cfg || {};
- Ext.apply(this, cfg);
- },
- // Executes this Query upon the selected root.
- // The root provides the initial source of candidate Component matches which are progressively
- // filtered by iterating through this Query's operations cache.
- // If no root is provided, all registered Components are searched via the ComponentManager.
- // root may be a Container who's descendant Components are filtered
- // root may be a Component with an implementation of getRefItems which provides some nested Components such as the
- // docked items within a Panel.
- // root may be an array of candidate Components to filter using this Query.
- execute : function(root) {
- var operations = this.operations,
- i = 0,
- length = operations.length,
- operation,
- workingItems;
- // no root, use all Components in the document
- if (!root) {
- workingItems = Ext.ComponentManager.all.getArray();
- }
- // Root is a candidate Array
- else if (Ext.isArray(root)) {
- workingItems = root;
- }
- // We are going to loop over our operations and take care of them
- // one by one.
- for (; i < length; i++) {
- operation = operations[i];
- // The mode operation requires some custom handling.
- // All other operations essentially filter down our current
- // working items, while mode replaces our current working
- // items by getting children from each one of our current
- // working items. The type of mode determines the type of
- // children we get. (e.g. > only gets direct children)
- if (operation.mode === '^') {
- workingItems = getAncestors(workingItems || [root]);
- }
- else if (operation.mode) {
- workingItems = getItems(workingItems || [root], operation.mode);
- }
- else {
- workingItems = filterItems(workingItems || getItems([root]), operation);
- }
- // If this is the last operation, it means our current working
- // items are the final matched items. Thus return them!
- if (i === length -1) {
- return workingItems;
- }
- }
- return [];
- },
- is: function(component) {
- var operations = this.operations,
- components = Ext.isArray(component) ? component : [component],
- originalLength = components.length,
- lastOperation = operations[operations.length-1],
- ln, i;
- components = filterItems(components, lastOperation);
- if (components.length === originalLength) {
- if (operations.length > 1) {
- for (i = 0, ln = components.length; i < ln; i++) {
- if (Ext.Array.indexOf(this.execute(), components[i]) === -1) {
- return false;
- }
- }
- }
- return true;
- }
- return false;
- }
- });
- Ext.apply(this, {
- // private cache of selectors and matching ComponentQuery.Query objects
- cache: {},
- // private cache of pseudo class filter functions
- pseudos: {
- not: function(components, selector){
- var CQ = Ext.ComponentQuery,
- i = 0,
- length = components.length,
- results = [],
- index = -1,
- component;
-
- for(; i < length; ++i) {
- component = components[i];
- if (!CQ.is(component, selector)) {
- results[++index] = component;
- }
- }
- return results;
- },
- last: function(components) {
- return components[components.length - 1];
- }
- },
- /**
- * Returns an array of matched Components from within the passed root object.
- *
- * This method filters returned Components in a similar way to how CSS selector based DOM
- * queries work using a textual selector string.
- *
- * See class summary for details.
- *
- * @param {String} selector The selector string to filter returned Components
- * @param {Ext.container.Container} root The Container within which to perform the query.
- * If omitted, all Components within the document are included in the search.
- *
- * This parameter may also be an array of Components to filter according to the selector.</p>
- * @returns {Ext.Component[]} The matched Components.
- *
- * @member Ext.ComponentQuery
- */
- query: function(selector, root) {
- var selectors = selector.split(','),
- length = selectors.length,
- i = 0,
- results = [],
- noDupResults = [],
- dupMatcher = {},
- query, resultsLn, cmp;
- for (; i < length; i++) {
- selector = Ext.String.trim(selectors[i]);
- query = this.cache[selector];
- if (!query) {
- this.cache[selector] = query = this.parse(selector);
- }
- results = results.concat(query.execute(root));
- }
- // multiple selectors, potential to find duplicates
- // lets filter them out.
- if (length > 1) {
- resultsLn = results.length;
- for (i = 0; i < resultsLn; i++) {
- cmp = results[i];
- if (!dupMatcher[cmp.id]) {
- noDupResults.push(cmp);
- dupMatcher[cmp.id] = true;
- }
- }
- results = noDupResults;
- }
- return results;
- },
- /**
- * Tests whether the passed Component matches the selector string.
- * @param {Ext.Component} component The Component to test
- * @param {String} selector The selector string to test against.
- * @return {Boolean} True if the Component matches the selector.
- * @member Ext.ComponentQuery
- */
- is: function(component, selector) {
- if (!selector) {
- return true;
- }
- var query = this.cache[selector];
- if (!query) {
- this.cache[selector] = query = this.parse(selector);
- }
- return query.is(component);
- },
- parse: function(selector) {
- var operations = [],
- length = matchers.length,
- lastSelector,
- tokenMatch,
- matchedChar,
- modeMatch,
- selectorMatch,
- i, matcher, method;
- // We are going to parse the beginning of the selector over and
- // over again, slicing off the selector any portions we converted into an
- // operation, until it is an empty string.
- while (selector && lastSelector !== selector) {
- lastSelector = selector;
- // First we check if we are dealing with a token like #, * or an xtype
- tokenMatch = selector.match(tokenRe);
- if (tokenMatch) {
- matchedChar = tokenMatch[1];
- // If the token is prefixed with a # we push a filterById operation to our stack
- if (matchedChar === '#') {
- operations.push({
- method: filterById,
- args: [Ext.String.trim(tokenMatch[2])]
- });
- }
- // If the token is prefixed with a . we push a filterByClassName operation to our stack
- // FIXME: Not enabled yet. just needs \. adding to the tokenRe prefix
- else if (matchedChar === '.') {
- operations.push({
- method: filterByClassName,
- args: [Ext.String.trim(tokenMatch[2])]
- });
- }
- // If the token is a * or an xtype string, we push a filterByXType
- // operation to the stack.
- else {
- operations.push({
- method: filterByXType,
- args: [Ext.String.trim(tokenMatch[2]), Boolean(tokenMatch[3])]
- });
- }
- // Now we slice of the part we just converted into an operation
- selector = selector.replace(tokenMatch[0], '');
- }
- // If the next part of the query is not a space or > or ^, it means we
- // are going to check for more things that our current selection
- // has to comply to.
- while (!(modeMatch = selector.match(modeRe))) {
- // Lets loop over each type of matcher and execute it
- // on our current selector.
- for (i = 0; selector && i < length; i++) {
- matcher = matchers[i];
- selectorMatch = selector.match(matcher.re);
- method = matcher.method;
- // If we have a match, add an operation with the method
- // associated with this matcher, and pass the regular
- // expression matches are arguments to the operation.
- if (selectorMatch) {
- operations.push({
- method: Ext.isString(matcher.method)
- // Turn a string method into a function by formatting the string with our selector matche expression
- // A new method is created for different match expressions, eg {id=='textfield-1024'}
- // Every expression may be different in different selectors.
- ? Ext.functionFactory('items', Ext.String.format.apply(Ext.String, [method].concat(selectorMatch.slice(1))))
- : matcher.method,
- args: selectorMatch.slice(1)
- });
- selector = selector.replace(selectorMatch[0], '');
- break; // Break on match
- }
- // Exhausted all matches: It's an error
- if (i === (length - 1)) {
- Ext.Error.raise('Invalid ComponentQuery selector: "' + arguments[0] + '"');
- }
- }
- }
- // Now we are going to check for a mode change. This means a space
- // or a > to determine if we are going to select all the children
- // of the currently matched items, or a ^ if we are going to use the
- // ownerCt axis as the candidate source.
- if (modeMatch[1]) { // Assignment, and test for truthiness!
- operations.push({
- mode: modeMatch[2]||modeMatch[1]
- });
- selector = selector.replace(modeMatch[0], '');
- }
- }
- // Now that we have all our operations in an array, we are going
- // to create a new Query using these operations.
- return new cq.Query({
- operations: operations
- });
- }
- });
- });
- /**
- * Manages certain element-like data prior to rendering. These values are passed
- * on to the render process. This is currently used to manage the "class" and "style" attributes
- * of a component's primary el as well as the bodyEl of panels. This allows things like
- * addBodyCls in Panel to share logic with addCls in AbstractComponent.
- * @private
- */
- /*
- * The dirty implementation in this class is quite naive. The reasoning for this is that the dirty state
- * will only be used in very specific circumstances, specifically, after the render process has begun but
- * the component is not yet rendered to the DOM. As such, we want it to perform as quickly as possible
- * so it's not as fully featured as you may expect.
- */
- Ext.define('Ext.util.ProtoElement', function () {
- var splitWords = Ext.String.splitWords,
- toMap = Ext.Array.toMap;
- return {
-
- isProtoEl: true,
-
- /**
- * The property name for the className on the data object passed to {@link #writeTo}.
- */
- clsProp: 'cls',
- /**
- * The property name for the style on the data object passed to {@link #writeTo}.
- */
- styleProp: 'style',
-
- /**
- * The property name for the removed classes on the data object passed to {@link #writeTo}.
- */
- removedProp: 'removed',
- /**
- * True if the style must be converted to text during {@link #writeTo}. When used to
- * populate tpl data, this will be true. When used to populate {@link Ext.DomHelper}
- * specs, this will be false (the default).
- */
- styleIsText: false,
- constructor: function (config) {
- var me = this;
- Ext.apply(me, config);
- me.classList = splitWords(me.cls);
- me.classMap = toMap(me.classList);
- delete me.cls;
- if (Ext.isFunction(me.style)) {
- me.styleFn = me.style;
- delete me.style;
- } else if (typeof me.style == 'string') {
- me.style = Ext.Element.parseStyles(me.style);
- } else if (me.style) {
- me.style = Ext.apply({}, me.style); // don't edit the given object
- }
- },
-
- /**
- * Indicates that the current state of the object has been flushed to the DOM, so we need
- * to track any subsequent changes
- */
- flush: function(){
- this.flushClassList = [];
- this.removedClasses = {};
- // clear the style, it will be recreated if we add anything new
- delete this.style;
- },
- /**
- * Adds class to the element.
- * @param {String} cls One or more classnames separated with spaces.
- * @return {Ext.util.ProtoElement} this
- */
- addCls: function (cls) {
- var me = this,
- add = splitWords(cls),
- length = add.length,
- list = me.classList,
- map = me.classMap,
- flushList = me.flushClassList,
- i = 0,
- c;
- for (; i < length; ++i) {
- c = add[i];
- if (!map[c]) {
- map[c] = true;
- list.push(c);
- if (flushList) {
- flushList.push(c);
- delete me.removedClasses[c];
- }
- }
- }
- return me;
- },
- /**
- * True if the element has given class.
- * @param {String} cls
- * @return {Boolean}
- */
- hasCls: function (cls) {
- return cls in this.classMap;
- },
- /**
- * Removes class from the element.
- * @param {String} cls One or more classnames separated with spaces.
- * @return {Ext.util.ProtoElement} this
- */
- removeCls: function (cls) {
- var me = this,
- list = me.classList,
- newList = (me.classList = []),
- remove = toMap(splitWords(cls)),
- length = list.length,
- map = me.classMap,
- removedClasses = me.removedClasses,
- i, c;
- for (i = 0; i < length; ++i) {
- c = list[i];
- if (remove[c]) {
- if (removedClasses) {
- if (map[c]) {
- removedClasses[c] = true;
- Ext.Array.remove(me.flushClassList, c);
- }
- }
- delete map[c];
- } else {
- newList.push(c);
- }
- }
- return me;
- },
- /**
- * Adds styles to the element.
- * @param {String/Object} prop The style property to be set, or an object of multiple styles.
- * @param {String} [value] The value to apply to the given property.
- * @return {Ext.util.ProtoElement} this
- */
- setStyle: function (prop, value) {
- var me = this,
- style = me.style || (me.style = {});
- if (typeof prop == 'string') {
- if (arguments.length === 1) {
- me.setStyle(Ext.Element.parseStyles(prop));
- } else {
- style[prop] = value;
- }
- } else {
- Ext.apply(style, prop);
- }
- return me;
- },
- /**
- * Writes style and class properties to given object.
- * Styles will be written to {@link #styleProp} and class names to {@link #clsProp}.
- * @param {Object} to
- * @return {Object} to
- */
- writeTo: function (to) {
- var me = this,
- classList = me.flushClassList || me.classList,
- removedClasses = me.removedClasses,
- style;
- if (me.styleFn) {
- style = Ext.apply({}, me.styleFn());
- Ext.apply(style, me.style);
- } else {
- style = me.style;
- }
- to[me.clsProp] = classList.join(' ');
- if (style) {
- to[me.styleProp] = me.styleIsText ? Ext.DomHelper.generateStyles(style) : style;
- }
-
- if (removedClasses) {
- removedClasses = Ext.Object.getKeys(removedClasses);
- if (removedClasses.length) {
- to[me.removedProp] = removedClasses.join(' ');
- }
- }
- return to;
- }
- };
- }());
- /**
- * @author Ed Spencer
- *
- * Base Writer class used by most subclasses of {@link Ext.data.proxy.Server}. This class is responsible for taking a
- * set of {@link Ext.data.Operation} objects and a {@link Ext.data.Request} object and modifying that request based on
- * the Operations.
- *
- * For example a Ext.data.writer.Json would format the Operations and their {@link Ext.data.Model} instances based on
- * the config options passed to the JsonWriter's constructor.
- *
- * Writers are not needed for any kind of local storage - whether via a {@link Ext.data.proxy.WebStorage Web Storage
- * proxy} (see {@link Ext.data.proxy.LocalStorage localStorage} and {@link Ext.data.proxy.SessionStorage
- * sessionStorage}) or just in memory via a {@link Ext.data.proxy.Memory MemoryProxy}.
- */
- Ext.define('Ext.data.writer.Writer', {
- alias: 'writer.base',
- alternateClassName: ['Ext.data.DataWriter', 'Ext.data.Writer'],
-
- /**
- * @cfg {Boolean} writeAllFields
- * True to write all fields from the record to the server. If set to false it will only send the fields that were
- * modified. Note that any fields that have {@link Ext.data.Field#persist} set to false will still be ignored.
- */
- writeAllFields: true,
-
- /**
- * @cfg {String} nameProperty
- * This property is used to read the key for each value that will be sent to the server. For example:
- *
- * Ext.define('Person', {
- * extend: 'Ext.data.Model',
- * fields: [{
- * name: 'first',
- * mapping: 'firstName'
- * }, {
- * name: 'last',
- * mapping: 'lastName'
- * }, {
- * name: 'age'
- * }]
- * });
- * new Ext.data.writer.Writer({
- * writeAllFields: true,
- * nameProperty: 'mapping'
- * });
- *
- * // This will be sent to the server
- * {
- * firstName: 'first name value',
- * lastName: 'last name value',
- * age: 1
- * }
- *
- * If the value is not present, the field name will always be used.
- */
- nameProperty: 'name',
- /**
- * Creates new Writer.
- * @param {Object} [config] Config object.
- */
- constructor: function(config) {
- Ext.apply(this, config);
- },
- /**
- * Prepares a Proxy's Ext.data.Request object
- * @param {Ext.data.Request} request The request object
- * @return {Ext.data.Request} The modified request object
- */
- write: function(request) {
- var operation = request.operation,
- records = operation.records || [],
- len = records.length,
- i = 0,
- data = [];
- for (; i < len; i++) {
- data.push(this.getRecordData(records[i], operation));
- }
- return this.writeRecords(request, data);
- },
- /**
- * Formats the data for each record before sending it to the server. This
- * method should be overridden to format the data in a way that differs from the default.
- * @param {Ext.data.Model} record The record that we are writing to the server.
- * @param {Ext.data.Operation} [operation] An operation object.
- * @return {Object} An object literal of name/value keys to be written to the server.
- * By default this method returns the data property on the record.
- */
- getRecordData: function(record, operation) {
- var isPhantom = record.phantom === true,
- writeAll = this.writeAllFields || isPhantom,
- nameProperty = this.nameProperty,
- fields = record.fields,
- fieldItems = fields.items,
- data = {},
- changes,
- name,
- field,
- key,
- f, fLen;
-
- if (writeAll) {
- fLen = fieldItems.length;
- for (f = 0; f < fLen; f++) {
- field = fieldItems[f];
- if (field.persist) {
- name = field[nameProperty] || field.name;
- data[name] = record.get(field.name);
- }
- }
- } else {
- // Only write the changes
- changes = record.getChanges();
- for (key in changes) {
- if (changes.hasOwnProperty(key)) {
- field = fields.get(key);
- name = field[nameProperty] || field.name;
- data[name] = changes[key];
- }
- }
- }
- if(isPhantom) {
- if(operation && operation.records.length > 1) {
- // include clientId for phantom records, if multiple records are being written to the server in one operation.
- // The server can then return the clientId with each record so the operation can match the server records with the client records
- data[record.clientIdProperty] = record.internalId;
- }
- } else {
- // always include the id for non phantoms
- data[record.idProperty] = record.getId();
- }
- return data;
- }
- });
- /**
- * Handles mapping key events to handling functions for an element or a Component. One KeyMap can be used for multiple
- * actions.
- *
- * A KeyMap must be configured with a {@link #target} as an event source which may be an Element or a Component.
- *
- * If the target is an element, then the `keydown` event will trigger the invocation of {@link #binding}s.
- *
- * It is possible to configure the KeyMap with a custom {@link #eventName} to listen for. This may be useful when the
- * {@link #target} is a Component.
- *
- * The KeyMap's event handling requires that the first parameter passed is a key event. So if the Component's event
- * signature is different, specify a {@link #processEvent} configuration which accepts the event's parameters and
- * returns a key event.
- *
- * Functions specified in {@link #binding}s are called with this signature : `(String key, Ext.EventObject e)` (if the
- * match is a multi-key combination the callback will still be called only once). A KeyMap can also handle a string
- * representation of keys. By default KeyMap starts enabled.
- *
- * Usage:
- *
- * // map one key by key code
- * var map = new Ext.util.KeyMap({
- * target: "my-element",
- * key: 13, // or Ext.EventObject.ENTER
- * fn: myHandler,
- * scope: myObject
- * });
- *
- * // map multiple keys to one action by string
- * var map = new Ext.util.KeyMap({
- * target: "my-element",
- * key: "a\r\n\t",
- * fn: myHandler,
- * scope: myObject
- * });
- *
- * // map multiple keys to multiple actions by strings and array of codes
- * var map = new Ext.util.KeyMap({
- * target: "my-element",
- * binding: [{
- * key: [10,13],
- * fn: function(){ alert("Return was pressed"); }
- * }, {
- * key: "abc",
- * fn: function(){ alert('a, b or c was pressed'); }
- * }, {
- * key: "\t",
- * ctrl:true,
- * shift:true,
- * fn: function(){ alert('Control + shift + tab was pressed.'); }
- * }]
- * });
- *
- * Since 4.1.0, KeyMaps can bind to Components and process key-based events fired by Components.
- *
- * To bind to a Component, use the single parameter form of constructor:
- *
- * var map = new Ext.util.KeyMap({
- * target: myGridView,
- * eventName: 'itemkeydown',
- * processEvent: function(view, record, node, index, event) {
- *
- * // Load the event with the extra information needed by the mappings
- * event.view = view;
- * event.store = view.getStore();
- * event.record = record;
- * event.index = index;
- * return event;
- * },
- * binding: {
- * key: Ext.EventObject.DELETE,
- * fn: function(keyCode, e) {
- * e.store.remove(e.record);
- *
- * // Attempt to select the record that's now in its place
- * e.view.getSelectionModel().select(e,index);
- * e.view.el.focus();
- * }
- * }
- * });
- */
- Ext.define('Ext.util.KeyMap', {
- alternateClassName: 'Ext.KeyMap',
- /**
- * @cfg {Ext.Component/Ext.Element/HTMLElement/String} target
- * The object on which to listen for the event specified by the {@link #eventName} config option.
- */
- /**
- * @cfg {Object/Object[][]} binding
- * Either a single object describing a handling function for s specified key (or set of keys), or
- * an array of such objects.
- * @cfg {String/String[]} binding.key A single keycode or an array of keycodes to handle
- * @cfg {Boolean} binding.shift True to handle key only when shift is pressed, False to handle the
- * key only when shift is not pressed (defaults to undefined)
- * @cfg {Boolean} binding.ctrl True to handle key only when ctrl is pressed, False to handle the
- * key only when ctrl is not pressed (defaults to undefined)
- * @cfg {Boolean} binding.alt True to handle key only when alt is pressed, False to handle the key
- * only when alt is not pressed (defaults to undefined)
- * @cfg {Function} binding.handler The function to call when KeyMap finds the expected key combination
- * @cfg {Function} binding.fn Alias of handler (for backwards-compatibility)
- * @cfg {Object} binding.scope The scope of the callback function
- * @cfg {String} binding.defaultEventAction A default action to apply to the event. Possible values
- * are: stopEvent, stopPropagation, preventDefault. If no value is set no action is performed.
- */
- /**
- * @cfg {Object} [processEventScope=this]
- * The scope (`this` context) in which the {@link #processEvent} method is executed.
- */
- /**
- * @cfg {String} eventName
- * The event to listen for to pick up key events.
- */
- eventName: 'keydown',
- constructor: function(config) {
- var me = this;
- // Handle legacy arg list in which the first argument is the target.
- // TODO: Deprecate in V5
- if ((arguments.length !== 1) || (typeof config === 'string') || config.dom || config.tagName || config === document || config.isComponent) {
- me.legacyConstructor.apply(me, arguments);
- return;
- }
- Ext.apply(me, config);
- me.bindings = [];
- if (!me.target.isComponent) {
- me.target = Ext.get(me.target);
- }
- if (me.binding) {
- me.addBinding(me.binding);
- } else if (config.key) {
- me.addBinding(config);
- }
- me.enable();
- },
- /**
- * @private
- * Old constructor signature
- * @param {String/HTMLElement/Ext.Element/Ext.Component} el The element or its ID, or Component to bind to
- * @param {Object} binding The binding (see {@link #addBinding})
- * @param {String} [eventName="keydown"] The event to bind to
- */
- legacyConstructor: function(el, binding, eventName){
- var me = this;
- Ext.apply(me, {
- target: Ext.get(el),
- eventName: eventName || me.eventName,
- bindings: []
- });
- if (binding) {
- me.addBinding(binding);
- }
- me.enable();
- },
- /**
- * Add a new binding to this KeyMap.
- *
- * Usage:
- *
- * // Create a KeyMap
- * var map = new Ext.util.KeyMap(document, {
- * key: Ext.EventObject.ENTER,
- * fn: handleKey,
- * scope: this
- * });
- *
- * //Add a new binding to the existing KeyMap later
- * map.addBinding({
- * key: 'abc',
- * shift: true,
- * fn: handleKey,
- * scope: this
- * });
- *
- * @param {Object/Object[]} binding A single KeyMap config or an array of configs.
- * The following config object properties are supported:
- * @param {String/Array} binding.key A single keycode or an array of keycodes to handle.
- * @param {Boolean} binding.shift True to handle key only when shift is pressed,
- * False to handle the keyonly when shift is not pressed (defaults to undefined).
- * @param {Boolean} binding.ctrl True to handle key only when ctrl is pressed,
- * False to handle the key only when ctrl is not pressed (defaults to undefined).
- * @param {Boolean} binding.alt True to handle key only when alt is pressed,
- * False to handle the key only when alt is not pressed (defaults to undefined).
- * @param {Function} binding.handler The function to call when KeyMap finds the
- * expected key combination.
- * @param {Function} binding.fn Alias of handler (for backwards-compatibility).
- * @param {Object} binding.scope The scope of the callback function.
- * @param {String} binding.defaultEventAction A default action to apply to the event.
- * Possible values are: stopEvent, stopPropagation, preventDefault. If no value is
- * set no action is performed..
- */
- addBinding : function(binding){
- var keyCode = binding.key,
- processed = false,
- key,
- keys,
- keyString,
- i,
- len;
- if (Ext.isArray(binding)) {
- for (i = 0, len = binding.length; i < len; i++) {
- this.addBinding(binding[i]);
- }
- return;
- }
- if (Ext.isString(keyCode)) {
- keys = [];
- keyString = keyCode.toUpperCase();
- for (i = 0, len = keyString.length; i < len; ++i){
- keys.push(keyString.charCodeAt(i));
- }
- keyCode = keys;
- processed = true;
- }
- if (!Ext.isArray(keyCode)) {
- keyCode = [keyCode];
- }
- if (!processed) {
- for (i = 0, len = keyCode.length; i < len; ++i) {
- key = keyCode[i];
- if (Ext.isString(key)) {
- keyCode[i] = key.toUpperCase().charCodeAt(0);
- }
- }
- }
- this.bindings.push(Ext.apply({
- keyCode: keyCode
- }, binding));
- },
- /**
- * Process any keydown events on the element
- * @private
- * @param {Ext.EventObject} event
- */
- handleKeyDown: function(event) {
- var me = this;
- if (this.enabled) { //just in case
- var bindings = this.bindings,
- i = 0,
- len = bindings.length;
- // Process the event
- event = me.processEvent.apply(me||me.processEventScope, arguments);
- // If the processor does not return a keyEvent, we can't process it.
- // Allow them to return false to cancel processing of the event
- if (!event.getKey) {
- return event;
- }
- for(; i < len; ++i){
- this.processBinding(bindings[i], event);
- }
- }
- },
- /**
- * @cfg {Function} processEvent
- * An optional event processor function which accepts the argument list provided by the
- * {@link #eventName configured event} of the {@link #target}, and returns a keyEvent for processing by the KeyMap.
- *
- * This may be useful when the {@link #target} is a Component with s complex event signature. Extra information from
- * the event arguments may be injected into the event for use by the handler functions before returning it.
- */
- processEvent: function(event){
- return event;
- },
- /**
- * Process a particular binding and fire the handler if necessary.
- * @private
- * @param {Object} binding The binding information
- * @param {Ext.EventObject} event
- */
- processBinding: function(binding, event){
- if (this.checkModifiers(binding, event)) {
- var key = event.getKey(),
- handler = binding.fn || binding.handler,
- scope = binding.scope || this,
- keyCode = binding.keyCode,
- defaultEventAction = binding.defaultEventAction,
- i,
- len,
- keydownEvent = new Ext.EventObjectImpl(event);
- for (i = 0, len = keyCode.length; i < len; ++i) {
- if (key === keyCode[i]) {
- if (handler.call(scope, key, event) !== true && defaultEventAction) {
- keydownEvent[defaultEventAction]();
- }
- break;
- }
- }
- }
- },
- /**
- * Check if the modifiers on the event match those on the binding
- * @private
- * @param {Object} binding
- * @param {Ext.EventObject} event
- * @return {Boolean} True if the event matches the binding
- */
- checkModifiers: function(binding, e) {
- var keys = ['shift', 'ctrl', 'alt'],
- i = 0,
- len = keys.length,
- val, key;
- for (; i < len; ++i){
- key = keys[i];
- val = binding[key];
- if (!(val === undefined || (val === e[key + 'Key']))) {
- return false;
- }
- }
- return true;
- },
- /**
- * Shorthand for adding a single key listener.
- *
- * @param {Number/Number[]/Object} key Either the numeric key code, array of key codes or an object with the
- * following options: `{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}`
- * @param {Function} fn The function to call
- * @param {Object} [scope] The scope (`this` reference) in which the function is executed.
- * Defaults to the browser window.
- */
- on: function(key, fn, scope) {
- var keyCode, shift, ctrl, alt;
- if (Ext.isObject(key) && !Ext.isArray(key)) {
- keyCode = key.key;
- shift = key.shift;
- ctrl = key.ctrl;
- alt = key.alt;
- } else {
- keyCode = key;
- }
- this.addBinding({
- key: keyCode,
- shift: shift,
- ctrl: ctrl,
- alt: alt,
- fn: fn,
- scope: scope
- });
- },
- /**
- * Returns true if this KeyMap is enabled
- * @return {Boolean}
- */
- isEnabled : function() {
- return this.enabled;
- },
- /**
- * Enables this KeyMap
- */
- enable: function() {
- var me = this;
-
- if (!me.enabled) {
- me.target.on(me.eventName, me.handleKeyDown, me);
- me.enabled = true;
- }
- },
- /**
- * Disable this KeyMap
- */
- disable: function() {
- var me = this;
-
- if (me.enabled) {
- me.target.removeListener(me.eventName, me.handleKeyDown, me);
- me.enabled = false;
- }
- },
- /**
- * Convenience function for setting disabled/enabled by boolean.
- * @param {Boolean} disabled
- */
- setDisabled : function(disabled) {
- if (disabled) {
- this.disable();
- } else {
- this.enable();
- }
- },
- /**
- * Destroys the KeyMap instance and removes all handlers.
- * @param {Boolean} removeTarget True to also remove the {@link #target}
- */
- destroy: function(removeTarget) {
- var me = this;
- me.bindings = [];
- me.disable();
- if (removeTarget === true) {
- me.target.isComponent ? me.target.destroy() : me.target.remove();
- }
- delete me.target;
- }
- });
- /**
- * @class Ext.util.Memento
- * This class manages a set of captured properties from an object. These captured properties
- * can later be restored to an object.
- */
- Ext.define('Ext.util.Memento', function () {
- function captureOne (src, target, prop, prefix) {
- src[prefix ? prefix + prop : prop] = target[prop];
- }
- function removeOne (src, target, prop) {
- delete src[prop];
- }
- function restoreOne (src, target, prop, prefix) {
- var name = prefix ? prefix + prop : prop,
- value = src[name];
- if (value || src.hasOwnProperty(name)) {
- restoreValue(target, prop, value);
- }
- }
- function restoreValue (target, prop, value) {
- if (Ext.isDefined(value)) {
- target[prop] = value;
- } else {
- delete target[prop];
- }
- }
- function doMany (doOne, src, target, props, prefix) {
- if (src) {
- if (Ext.isArray(props)) {
- var p, pLen = props.length;
- for (p = 0; p < pLen; p++) {
- doOne(src, target, props[p], prefix);
- }
- } else {
- doOne(src, target, props, prefix);
- }
- }
- }
- return {
- /**
- * @property data
- * The collection of captured properties.
- * @private
- */
- data: null,
- /**
- * @property target
- * The default target object for capture/restore (passed to the constructor).
- */
- target: null,
- /**
- * Creates a new memento and optionally captures properties from the target object.
- * @param {Object} target The target from which to capture properties. If specified in the
- * constructor, this target becomes the default target for all other operations.
- * @param {String/String[]} props The property or array of properties to capture.
- */
- constructor: function (target, props) {
- if (target) {
- this.target = target;
- if (props) {
- this.capture(props);
- }
- }
- },
- /**
- * Captures the specified properties from the target object in this memento.
- * @param {String/String[]} props The property or array of properties to capture.
- * @param {Object} target The object from which to capture properties.
- */
- capture: function (props, target, prefix) {
- var me = this;
- doMany(captureOne, me.data || (me.data = {}), target || me.target, props, prefix);
- },
- /**
- * Removes the specified properties from this memento. These properties will not be
- * restored later without re-capturing their values.
- * @param {String/String[]} props The property or array of properties to remove.
- */
- remove: function (props) {
- doMany(removeOne, this.data, null, props);
- },
- /**
- * Restores the specified properties from this memento to the target object.
- * @param {String/String[]} props The property or array of properties to restore.
- * @param {Boolean} clear True to remove the restored properties from this memento or
- * false to keep them (default is true).
- * @param {Object} target The object to which to restore properties.
- */
- restore: function (props, clear, target, prefix) {
- doMany(restoreOne, this.data, target || this.target, props, prefix);
- if (clear !== false) {
- this.remove(props);
- }
- },
- /**
- * Restores all captured properties in this memento to the target object.
- * @param {Boolean} clear True to remove the restored properties from this memento or
- * false to keep them (default is true).
- * @param {Object} target The object to which to restore properties.
- */
- restoreAll: function (clear, target) {
- var me = this,
- t = target || this.target,
- data = me.data,
- prop;
- for (prop in data) {
- if (data.hasOwnProperty(prop)) {
- restoreValue(t, prop, data[prop]);
- }
- }
- if (clear !== false) {
- delete me.data;
- }
- }
- };
- }());
- /**
- * @class Ext.state.Provider
- * <p>Abstract base class for state provider implementations. The provider is responsible
- * for setting values and extracting values to/from the underlying storage source. The
- * storage source can vary and the details should be implemented in a subclass. For example
- * a provider could use a server side database or the browser localstorage where supported.</p>
- *
- * <p>This class provides methods for encoding and decoding <b>typed</b> variables including
- * dates and defines the Provider interface. By default these methods put the value and the
- * type information into a delimited string that can be stored. These should be overridden in
- * a subclass if you want to change the format of the encoded value and subsequent decoding.</p>
- */
- Ext.define('Ext.state.Provider', {
- mixins: {
- observable: 'Ext.util.Observable'
- },
-
- /**
- * @cfg {String} prefix A string to prefix to items stored in the underlying state store.
- * Defaults to <tt>'ext-'</tt>
- */
- prefix: 'ext-',
-
- constructor : function(config){
- config = config || {};
- var me = this;
- Ext.apply(me, config);
- /**
- * @event statechange
- * Fires when a state change occurs.
- * @param {Ext.state.Provider} this This state provider
- * @param {String} key The state key which was changed
- * @param {String} value The encoded value for the state
- */
- me.addEvents("statechange");
- me.state = {};
- me.mixins.observable.constructor.call(me);
- },
-
- /**
- * Returns the current value for a key
- * @param {String} name The key name
- * @param {Object} defaultValue A default value to return if the key's value is not found
- * @return {Object} The state data
- */
- get : function(name, defaultValue){
- return typeof this.state[name] == "undefined" ?
- defaultValue : this.state[name];
- },
- /**
- * Clears a value from the state
- * @param {String} name The key name
- */
- clear : function(name){
- var me = this;
- delete me.state[name];
- me.fireEvent("statechange", me, name, null);
- },
- /**
- * Sets the value for a key
- * @param {String} name The key name
- * @param {Object} value The value to set
- */
- set : function(name, value){
- var me = this;
- me.state[name] = value;
- me.fireEvent("statechange", me, name, value);
- },
- /**
- * Decodes a string previously encoded with {@link #encodeValue}.
- * @param {String} value The value to decode
- * @return {Object} The decoded value
- */
- decodeValue : function(value){
- // a -> Array
- // n -> Number
- // d -> Date
- // b -> Boolean
- // s -> String
- // o -> Object
- // -> Empty (null)
- var me = this,
- re = /^(a|n|d|b|s|o|e)\:(.*)$/,
- matches = re.exec(unescape(value)),
- all,
- type,
- value,
- keyValue,
- values,
- vLen,
- v;
-
- if(!matches || !matches[1]){
- return; // non state
- }
-
- type = matches[1];
- value = matches[2];
- switch (type) {
- case 'e':
- return null;
- case 'n':
- return parseFloat(value);
- case 'd':
- return new Date(Date.parse(value));
- case 'b':
- return (value == '1');
- case 'a':
- all = [];
- if(value != ''){
- values = value.split('^');
- vLen = values.length;
- for (v = 0; v < vLen; v++) {
- value = values[v];
- all.push(me.decodeValue(value));
- }
- }
- return all;
- case 'o':
- all = {};
- if(value != ''){
- values = value.split('^');
- vLen = values.length;
- for (v = 0; v < vLen; v++) {
- value = values[v];
- keyValue = value.split('=');
- all[keyValue[0]] = me.decodeValue(keyValue[1]);
- }
- }
- return all;
- default:
- return value;
- }
- },
- /**
- * Encodes a value including type information. Decode with {@link #decodeValue}.
- * @param {Object} value The value to encode
- * @return {String} The encoded value
- */
- encodeValue : function(value){
- var flat = '',
- i = 0,
- enc,
- len,
- key;
-
- if (value == null) {
- return 'e:1';
- } else if(typeof value == 'number') {
- enc = 'n:' + value;
- } else if(typeof value == 'boolean') {
- enc = 'b:' + (value ? '1' : '0');
- } else if(Ext.isDate(value)) {
- enc = 'd:' + value.toGMTString();
- } else if(Ext.isArray(value)) {
- for (len = value.length; i < len; i++) {
- flat += this.encodeValue(value[i]);
- if (i != len - 1) {
- flat += '^';
- }
- }
- enc = 'a:' + flat;
- } else if (typeof value == 'object') {
- for (key in value) {
- if (typeof value[key] != 'function' && value[key] !== undefined) {
- flat += key + '=' + this.encodeValue(value[key]) + '^';
- }
- }
- enc = 'o:' + flat.substring(0, flat.length-1);
- } else {
- enc = 's:' + value;
- }
- return escape(enc);
- }
- });
- /**
- * @author Ed Spencer
- *
- * Simple wrapper class that represents a set of records returned by a Proxy.
- */
- Ext.define('Ext.data.ResultSet', {
- /**
- * @cfg {Boolean} loaded
- * True if the records have already been loaded. This is only meaningful when dealing with
- * SQL-backed proxies.
- */
- loaded: true,
- /**
- * @cfg {Number} count
- * The number of records in this ResultSet. Note that total may differ from this number.
- */
- count: 0,
- /**
- * @cfg {Number} total
- * The total number of records reported by the data source. This ResultSet may form a subset of
- * those records (see {@link #count}).
- */
- total: 0,
- /**
- * @cfg {Boolean} success
- * True if the ResultSet loaded successfully, false if any errors were encountered.
- */
- success: false,
- /**
- * @cfg {Ext.data.Model[]} records (required)
- * The array of record instances.
- */
- /**
- * Creates the resultSet
- * @param {Object} [config] Config object.
- */
- constructor: function(config) {
- Ext.apply(this, config);
- /**
- * @property {Number} totalRecords
- * Copy of this.total.
- * @deprecated Will be removed in Ext JS 5.0. Use {@link #total} instead.
- */
- this.totalRecords = this.total;
- if (config.count === undefined) {
- this.count = this.records.length;
- }
- }
- });
- /**
- * @class Ext.fx.CubicBezier
- * @ignore
- */
- Ext.define('Ext.fx.CubicBezier', {
- /* Begin Definitions */
- singleton: true,
- /* End Definitions */
- cubicBezierAtTime: function(t, p1x, p1y, p2x, p2y, duration) {
- var cx = 3 * p1x,
- bx = 3 * (p2x - p1x) - cx,
- ax = 1 - cx - bx,
- cy = 3 * p1y,
- by = 3 * (p2y - p1y) - cy,
- ay = 1 - cy - by;
- function sampleCurveX(t) {
- return ((ax * t + bx) * t + cx) * t;
- }
- function solve(x, epsilon) {
- var t = solveCurveX(x, epsilon);
- return ((ay * t + by) * t + cy) * t;
- }
- function solveCurveX(x, epsilon) {
- var t0, t1, t2, x2, d2, i;
- for (t2 = x, i = 0; i < 8; i++) {
- x2 = sampleCurveX(t2) - x;
- if (Math.abs(x2) < epsilon) {
- return t2;
- }
- d2 = (3 * ax * t2 + 2 * bx) * t2 + cx;
- if (Math.abs(d2) < 1e-6) {
- break;
- }
- t2 = t2 - x2 / d2;
- }
- t0 = 0;
- t1 = 1;
- t2 = x;
- if (t2 < t0) {
- return t0;
- }
- if (t2 > t1) {
- return t1;
- }
- while (t0 < t1) {
- x2 = sampleCurveX(t2);
- if (Math.abs(x2 - x) < epsilon) {
- return t2;
- }
- if (x > x2) {
- t0 = t2;
- } else {
- t1 = t2;
- }
- t2 = (t1 - t0) / 2 + t0;
- }
- return t2;
- }
- return solve(t, 1 / (200 * duration));
- },
- cubicBezier: function(x1, y1, x2, y2) {
- var fn = function(pos) {
- return Ext.fx.CubicBezier.cubicBezierAtTime(pos, x1, y1, x2, y2, 1);
- };
- fn.toCSS3 = function() {
- return 'cubic-bezier(' + [x1, y1, x2, y2].join(',') + ')';
- };
- fn.reverse = function() {
- return Ext.fx.CubicBezier.cubicBezier(1 - x2, 1 - y2, 1 - x1, 1 - y1);
- };
- return fn;
- }
- });
- /**
- * A custom drag proxy implementation specific to {@link Ext.panel.Panel}s. This class
- * is primarily used internally for the Panel's drag drop implementation, and
- * should never need to be created directly.
- * @private
- */
- Ext.define('Ext.panel.Proxy', {
- alternateClassName: 'Ext.dd.PanelProxy',
-
- /**
- * @cfg {Boolean} [moveOnDrag=true]
- * True to move the panel to the dragged position when dropped
- */
- moveOnDrag: true,
- /**
- * Creates new panel proxy.
- * @param {Ext.panel.Panel} panel The {@link Ext.panel.Panel} to proxy for
- * @param {Object} [config] Config object
- */
- constructor: function(panel, config){
- var me = this;
-
- /**
- * @property panel
- * @type Ext.panel.Panel
- */
- me.panel = panel;
- me.id = me.panel.id +'-ddproxy';
- Ext.apply(me, config);
- },
- /**
- * @cfg {Boolean} insertProxy
- * True to insert a placeholder proxy element while dragging the panel, false to drag with no proxy.
- * Most Panels are not absolute positioned and therefore we need to reserve this space.
- */
- insertProxy: true,
- // private overrides
- setStatus: Ext.emptyFn,
- reset: Ext.emptyFn,
- update: Ext.emptyFn,
- stop: Ext.emptyFn,
- sync: Ext.emptyFn,
- /**
- * Gets the proxy's element
- * @return {Ext.Element} The proxy's element
- */
- getEl: function(){
- return this.ghost.el;
- },
- /**
- * Gets the proxy's ghost Panel
- * @return {Ext.panel.Panel} The proxy's ghost Panel
- */
- getGhost: function(){
- return this.ghost;
- },
- /**
- * Gets the proxy element. This is the element that represents where the
- * Panel was before we started the drag operation.
- * @return {Ext.Element} The proxy's element
- */
- getProxy: function(){
- return this.proxy;
- },
- /**
- * Hides the proxy
- */
- hide : function(){
- var me = this;
-
- if (me.ghost) {
- if (me.proxy) {
- me.proxy.remove();
- delete me.proxy;
- }
- // Unghost the Panel, do not move the Panel to where the ghost was
- me.panel.unghost(null, me.moveOnDrag);
- delete me.ghost;
- }
- },
- /**
- * Shows the proxy
- */
- show: function(){
- var me = this,
- panelSize;
-
- if (!me.ghost) {
- panelSize = me.panel.getSize();
- me.panel.el.setVisibilityMode(Ext.Element.DISPLAY);
- me.ghost = me.panel.ghost();
- if (me.insertProxy) {
- // bc Panels aren't absolute positioned we need to take up the space
- // of where the panel previously was
- me.proxy = me.panel.el.insertSibling({cls: Ext.baseCSSPrefix + 'panel-dd-spacer'});
- me.proxy.setSize(panelSize);
- }
- }
- },
- // private
- repair: function(xy, callback, scope) {
- this.hide();
- Ext.callback(callback, scope || this);
- },
- /**
- * Moves the proxy to a different position in the DOM. This is typically
- * called while dragging the Panel to keep the proxy sync'd to the Panel's
- * location.
- * @param {HTMLElement} parentNode The proxy's parent DOM node
- * @param {HTMLElement} [before] The sibling node before which the
- * proxy should be inserted. Defaults to the parent's last child if not
- * specified.
- */
- moveProxy : function(parentNode, before){
- if (this.proxy) {
- parentNode.insertBefore(this.proxy.dom, before);
- }
- }
- });
- /**
- * Represents an HTML fragment template. Templates may be {@link #compile precompiled} for greater performance.
- *
- * An instance of this class may be created by passing to the constructor either a single argument, or multiple
- * arguments:
- *
- * # Single argument: String/Array
- *
- * The single argument may be either a String or an Array:
- *
- * - String:
- *
- * var t = new Ext.Template("<div>Hello {0}.</div>");
- * t.{@link #append}('some-element', ['foo']);
- *
- * - Array:
- *
- * An Array will be combined with `join('')`.
- *
- * var t = new Ext.Template([
- * '<div name="{id}">',
- * '<span class="{cls}">{name:trim} {value:ellipsis(10)}</span>',
- * '</div>',
- * ]);
- * t.{@link #compile}();
- * t.{@link #append}('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
- *
- * # Multiple arguments: String, Object, Array, ...
- *
- * Multiple arguments will be combined with `join('')`.
- *
- * var t = new Ext.Template(
- * '<div name="{id}">',
- * '<span class="{cls}">{name} {value}</span>',
- * '</div>',
- * // a configuration object:
- * {
- * compiled: true, // {@link #compile} immediately
- * }
- * );
- *
- * # Notes
- *
- * - For a list of available format functions, see {@link Ext.util.Format}.
- * - `disableFormats` reduces `{@link #apply}` time when no formatting is required.
- */
- Ext.define('Ext.Template', {
- /* Begin Definitions */
- requires: ['Ext.dom.Helper', 'Ext.util.Format'],
- inheritableStatics: {
- /**
- * Creates a template from the passed element's value (_display:none_ textarea, preferred) or innerHTML.
- * @param {String/HTMLElement} el A DOM element or its id
- * @param {Object} config (optional) Config object
- * @return {Ext.Template} The created template
- * @static
- * @inheritable
- */
- from: function(el, config) {
- el = Ext.getDom(el);
- return new this(el.value || el.innerHTML, config || '');
- }
- },
- /* End Definitions */
- /**
- * Creates new template.
- *
- * @param {String...} html List of strings to be concatenated into template.
- * Alternatively an array of strings can be given, but then no config object may be passed.
- * @param {Object} config (optional) Config object
- */
- constructor: function(html) {
- var me = this,
- args = arguments,
- buffer = [],
- i = 0,
- length = args.length,
- value;
- me.initialConfig = {};
- if (length > 1) {
- for (; i < length; i++) {
- value = args[i];
- if (typeof value == 'object') {
- Ext.apply(me.initialConfig, value);
- Ext.apply(me, value);
- } else {
- buffer.push(value);
- }
- }
- html = buffer.join('');
- } else {
- if (Ext.isArray(html)) {
- buffer.push(html.join(''));
- } else {
- buffer.push(html);
- }
- }
- // @private
- me.html = buffer.join('');
- if (me.compiled) {
- me.compile();
- }
- },
- /**
- * @property {Boolean} isTemplate
- * `true` in this class to identify an objact as an instantiated Template, or subclass thereof.
- */
- isTemplate: true,
- /**
- * @cfg {Boolean} compiled
- * True to immediately compile the template. Defaults to false.
- */
- /**
- * @cfg {Boolean} disableFormats
- * True to disable format functions in the template. If the template doesn't contain
- * format functions, setting disableFormats to true will reduce apply time. Defaults to false.
- */
- disableFormats: false,
- re: /\{([\w\-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
- /**
- * Returns an HTML fragment of this template with the specified values applied.
- *
- * @param {Object/Array} values The template values. Can be an array if your params are numeric:
- *
- * var tpl = new Ext.Template('Name: {0}, Age: {1}');
- * tpl.apply(['John', 25]);
- *
- * or an object:
- *
- * var tpl = new Ext.Template('Name: {name}, Age: {age}');
- * tpl.apply({name: 'John', age: 25});
- *
- * @return {String} The HTML fragment
- */
- apply: function(values) {
- var me = this,
- useFormat = me.disableFormats !== true,
- fm = Ext.util.Format,
- tpl = me,
- ret;
- if (me.compiled) {
- return me.compiled(values).join('');
- }
- function fn(m, name, format, args) {
- if (format && useFormat) {
- if (args) {
- args = [values[name]].concat(Ext.functionFactory('return ['+ args +'];')());
- } else {
- args = [values[name]];
- }
- if (format.substr(0, 5) == "this.") {
- return tpl[format.substr(5)].apply(tpl, args);
- }
- else {
- return fm[format].apply(fm, args);
- }
- }
- else {
- return values[name] !== undefined ? values[name] : "";
- }
- }
- ret = me.html.replace(me.re, fn);
- return ret;
- },
- /**
- * Appends the result of this template to the provided output array.
- * @param {Object/Array} values The template values. See {@link #apply}.
- * @param {Array} out The array to which output is pushed.
- * @return {Array} The given out array.
- */
- applyOut: function(values, out) {
- var me = this;
- if (me.compiled) {
- out.push.apply(out, me.compiled(values));
- } else {
- out.push(me.apply(values));
- }
- return out;
- },
- /**
- * @method applyTemplate
- * @member Ext.Template
- * Alias for {@link #apply}.
- * @inheritdoc Ext.Template#apply
- */
- applyTemplate: function () {
- return this.apply.apply(this, arguments);
- },
- /**
- * Sets the HTML used as the template and optionally compiles it.
- * @param {String} html
- * @param {Boolean} compile (optional) True to compile the template.
- * @return {Ext.Template} this
- */
- set: function(html, compile) {
- var me = this;
- me.html = html;
- me.compiled = null;
- return compile ? me.compile() : me;
- },
- compileARe: /\\/g,
- compileBRe: /(\r\n|\n)/g,
- compileCRe: /'/g,
- /**
- * Compiles the template into an internal function, eliminating the RegEx overhead.
- * @return {Ext.Template} this
- */
- compile: function() {
- var me = this,
- fm = Ext.util.Format,
- useFormat = me.disableFormats !== true,
- body, bodyReturn;
- function fn(m, name, format, args) {
- if (format && useFormat) {
- args = args ? ',' + args: "";
- if (format.substr(0, 5) != "this.") {
- format = "fm." + format + '(';
- }
- else {
- format = 'this.' + format.substr(5) + '(';
- }
- }
- else {
- args = '';
- format = "(values['" + name + "'] == undefined ? '' : ";
- }
- return "'," + format + "values['" + name + "']" + args + ") ,'";
- }
- bodyReturn = me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn);
- body = "this.compiled = function(values){ return ['" + bodyReturn + "'];};";
- eval(body);
- return me;
- },
- /**
- * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
- *
- * @param {String/HTMLElement/Ext.Element} el The context element
- * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
- * @param {Boolean} returnElement (optional) true to return a Ext.Element.
- * @return {HTMLElement/Ext.Element} The new node or Element
- */
- insertFirst: function(el, values, returnElement) {
- return this.doInsert('afterBegin', el, values, returnElement);
- },
- /**
- * Applies the supplied values to the template and inserts the new node(s) before el.
- *
- * @param {String/HTMLElement/Ext.Element} el The context element
- * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
- * @param {Boolean} returnElement (optional) true to return a Ext.Element.
- * @return {HTMLElement/Ext.Element} The new node or Element
- */
- insertBefore: function(el, values, returnElement) {
- return this.doInsert('beforeBegin', el, values, returnElement);
- },
- /**
- * Applies the supplied values to the template and inserts the new node(s) after el.
- *
- * @param {String/HTMLElement/Ext.Element} el The context element
- * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
- * @param {Boolean} returnElement (optional) true to return a Ext.Element.
- * @return {HTMLElement/Ext.Element} The new node or Element
- */
- insertAfter: function(el, values, returnElement) {
- return this.doInsert('afterEnd', el, values, returnElement);
- },
- /**
- * Applies the supplied `values` to the template and appends the new node(s) to the specified `el`.
- *
- * For example usage see {@link Ext.Template Ext.Template class docs}.
- *
- * @param {String/HTMLElement/Ext.Element} el The context element
- * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
- * @param {Boolean} returnElement (optional) true to return an Ext.Element.
- * @return {HTMLElement/Ext.Element} The new node or Element
- */
- append: function(el, values, returnElement) {
- return this.doInsert('beforeEnd', el, values, returnElement);
- },
- doInsert: function(where, el, values, returnEl) {
- el = Ext.getDom(el);
- var newNode = Ext.DomHelper.insertHtml(where, el, this.apply(values));
- return returnEl ? Ext.get(newNode, true) : newNode;
- },
- /**
- * Applies the supplied values to the template and overwrites the content of el with the new node(s).
- *
- * @param {String/HTMLElement/Ext.Element} el The context element
- * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
- * @param {Boolean} returnElement (optional) true to return a Ext.Element.
- * @return {HTMLElement/Ext.Element} The new node or Element
- */
- overwrite: function(el, values, returnElement) {
- el = Ext.getDom(el);
- el.innerHTML = this.apply(values);
- return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
- }
- });
- /**
- * @class Ext.fx.Queue
- * Animation Queue mixin to handle chaining and queueing by target.
- * @private
- */
- Ext.define('Ext.fx.Queue', {
- requires: ['Ext.util.HashMap'],
- constructor: function() {
- this.targets = new Ext.util.HashMap();
- this.fxQueue = {};
- },
- // @private
- getFxDefaults: function(targetId) {
- var target = this.targets.get(targetId);
- if (target) {
- return target.fxDefaults;
- }
- return {};
- },
- // @private
- setFxDefaults: function(targetId, obj) {
- var target = this.targets.get(targetId);
- if (target) {
- target.fxDefaults = Ext.apply(target.fxDefaults || {}, obj);
- }
- },
- // @private
- stopAnimation: function(targetId) {
- var me = this,
- queue = me.getFxQueue(targetId),
- ln = queue.length;
- while (ln) {
- queue[ln - 1].end();
- ln--;
- }
- },
- /**
- * @private
- * Returns current animation object if the element has any effects actively running or queued, else returns false.
- */
- getActiveAnimation: function(targetId) {
- var queue = this.getFxQueue(targetId);
- return (queue && !!queue.length) ? queue[0] : false;
- },
- // @private
- hasFxBlock: function(targetId) {
- var queue = this.getFxQueue(targetId);
- return queue && queue[0] && queue[0].block;
- },
- // @private get fx queue for passed target, create if needed.
- getFxQueue: function(targetId) {
- if (!targetId) {
- return false;
- }
- var me = this,
- queue = me.fxQueue[targetId],
- target = me.targets.get(targetId);
- if (!target) {
- return false;
- }
- if (!queue) {
- me.fxQueue[targetId] = [];
- // GarbageCollector will need to clean up Elements since they aren't currently observable
- if (target.type != 'element') {
- target.target.on('destroy', function() {
- me.fxQueue[targetId] = [];
- });
- }
- }
- return me.fxQueue[targetId];
- },
- // @private
- queueFx: function(anim) {
- var me = this,
- target = anim.target,
- queue, ln;
- if (!target) {
- return;
- }
- queue = me.getFxQueue(target.getId());
- ln = queue.length;
- if (ln) {
- if (anim.concurrent) {
- anim.paused = false;
- }
- else {
- queue[ln - 1].on('afteranimate', function() {
- anim.paused = false;
- });
- }
- }
- else {
- anim.paused = false;
- }
- anim.on('afteranimate', function() {
- Ext.Array.remove(queue, anim);
- if (anim.remove) {
- if (target.type == 'element') {
- var el = Ext.get(target.id);
- if (el) {
- el.remove();
- }
- }
- }
- }, this);
- queue.push(anim);
- }
- });
- /**
- * This class parses the XTemplate syntax and calls abstract methods to process the parts.
- * @private
- */
- Ext.define('Ext.XTemplateParser', {
- constructor: function (config) {
- Ext.apply(this, config);
- },
- /**
- * @property {Number} level The 'for' loop context level. This is adjusted up by one
- * prior to calling {@link #doFor} and down by one after calling the corresponding
- * {@link #doEnd} that closes the loop. This will be 1 on the first {@link #doFor}
- * call.
- */
- /**
- * This method is called to process a piece of raw text from the tpl.
- * @param {String} text
- * @method doText
- */
- // doText: function (text)
- /**
- * This method is called to process expressions (like `{[expr]}`).
- * @param {String} expr The body of the expression (inside "{[" and "]}").
- * @method doExpr
- */
- // doExpr: function (expr)
- /**
- * This method is called to process simple tags (like `{tag}`).
- * @method doTag
- */
- // doTag: function (tag)
- /**
- * This method is called to process `<tpl else>`.
- * @method doElse
- */
- // doElse: function ()
- /**
- * This method is called to process `{% text %}`.
- * @param {String} text
- * @method doEval
- */
- // doEval: function (text)
- /**
- * This method is called to process `<tpl if="action">`. If there are other attributes,
- * these are passed in the actions object.
- * @param {String} action
- * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
- * @method doIf
- */
- // doIf: function (action, actions)
- /**
- * This method is called to process `<tpl elseif="action">`. If there are other attributes,
- * these are passed in the actions object.
- * @param {String} action
- * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
- * @method doElseIf
- */
- // doElseIf: function (action, actions)
- /**
- * This method is called to process `<tpl switch="action">`. If there are other attributes,
- * these are passed in the actions object.
- * @param {String} action
- * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
- * @method doSwitch
- */
- // doSwitch: function (action, actions)
- /**
- * This method is called to process `<tpl case="action">`. If there are other attributes,
- * these are passed in the actions object.
- * @param {String} action
- * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
- * @method doCase
- */
- // doCase: function (action, actions)
- /**
- * This method is called to process `<tpl default>`.
- * @method doDefault
- */
- // doDefault: function ()
- /**
- * This method is called to process `</tpl>`. It is given the action type that started
- * the tpl and the set of additional actions.
- * @param {String} type The type of action that is being ended.
- * @param {Object} actions The other actions keyed by the attribute name (such as 'exec').
- * @method doEnd
- */
- // doEnd: function (type, actions)
- /**
- * This method is called to process `<tpl for="action">`. If there are other attributes,
- * these are passed in the actions object.
- * @param {String} action
- * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
- * @method doFor
- */
- // doFor: function (action, actions)
- /**
- * This method is called to process `<tpl exec="action">`. If there are other attributes,
- * these are passed in the actions object.
- * @param {String} action
- * @param {Object} actions Other actions keyed by the attribute name.
- * @method doExec
- */
- // doExec: function (action, actions)
- /**
- * This method is called to process an empty `<tpl>`. This is unlikely to need to be
- * implemented, so a default (do nothing) version is provided.
- * @method
- */
- doTpl: Ext.emptyFn,
- parse: function (str) {
- var me = this,
- len = str.length,
- aliases = { elseif: 'elif' },
- topRe = me.topRe,
- actionsRe = me.actionsRe,
- index, stack, s, m, t, prev, frame, subMatch, begin, end, actions;
- me.level = 0;
- me.stack = stack = [];
- for (index = 0; index < len; index = end) {
- topRe.lastIndex = index;
- m = topRe.exec(str);
- if (!m) {
- me.doText(str.substring(index, len));
- break;
- }
- begin = m.index;
- end = topRe.lastIndex;
- if (index < begin) {
- me.doText(str.substring(index, begin));
- }
- if (m[1]) {
- end = str.indexOf('%}', begin+2);
- me.doEval(str.substring(begin+2, end));
- end += 2;
- } else if (m[2]) {
- end = str.indexOf(']}', begin+2);
- me.doExpr(str.substring(begin+2, end));
- end += 2;
- } else if (m[3]) { // if ('{' token)
- me.doTag(m[3]);
- } else if (m[4]) { // content of a <tpl xxxxxx> tag
- actions = null;
- while ((subMatch = actionsRe.exec(m[4])) !== null) {
- s = subMatch[2] || subMatch[3];
- if (s) {
- s = Ext.String.htmlDecode(s); // decode attr value
- t = subMatch[1];
- t = aliases[t] || t;
- actions = actions || {};
- prev = actions[t];
- if (typeof prev == 'string') {
- actions[t] = [prev, s];
- } else if (prev) {
- actions[t].push(s);
- } else {
- actions[t] = s;
- }
- }
- }
- if (!actions) {
- if (me.elseRe.test(m[4])) {
- me.doElse();
- } else if (me.defaultRe.test(m[4])) {
- me.doDefault();
- } else {
- me.doTpl();
- stack.push({ type: 'tpl' });
- }
- }
- else if (actions['if']) {
- me.doIf(actions['if'], actions)
- stack.push({ type: 'if' });
- }
- else if (actions['switch']) {
- me.doSwitch(actions['switch'], actions)
- stack.push({ type: 'switch' });
- }
- else if (actions['case']) {
- me.doCase(actions['case'], actions);
- }
- else if (actions['elif']) {
- me.doElseIf(actions['elif'], actions);
- }
- else if (actions['for']) {
- ++me.level;
- me.doFor(actions['for'], actions);
- stack.push({ type: 'for', actions: actions });
- }
- else if (actions.exec) {
- me.doExec(actions.exec, actions);
- stack.push({ type: 'exec', actions: actions });
- }
- /*
- else {
- // todo - error
- }
- /**/
- } else {
- frame = stack.pop();
- me.doEnd(frame.type, frame.actions);
- if (frame.type == 'for') {
- --me.level;
- }
- }
- }
- },
- // Internal regexes
-
- topRe: /(?:(\{\%)|(\{\[)|\{([^{}]*)\})|(?:<tpl([^>]*)\>)|(?:<\/tpl>)/g,
- actionsRe: /\s*(elif|elseif|if|for|exec|switch|case|eval)\s*\=\s*(?:(?:["]([^"]*)["])|(?:[']([^']*)[']))\s*/g,
- defaultRe: /^\s*default\s*$/,
- elseRe: /^\s*else\s*$/
- });
- /**
- * A class that manages a group of {@link Ext.Component#floating} Components and provides z-order management,
- * and Component activation behavior, including masking below the active (topmost) Component.
- *
- * {@link Ext.Component#floating Floating} Components which are rendered directly into the document (such as
- * {@link Ext.window.Window Window}s) which are {@link Ext.Component#method-show show}n are managed by a
- * {@link Ext.WindowManager global instance}.
- *
- * {@link Ext.Component#floating Floating} Components which are descendants of {@link Ext.Component#floating floating}
- * *Containers* (for example a {@link Ext.view.BoundList BoundList} within an {@link Ext.window.Window Window},
- * or a {@link Ext.menu.Menu Menu}), are managed by a ZIndexManager owned by that floating Container. Therefore
- * ComboBox dropdowns within Windows will have managed z-indices guaranteed to be correct, relative to the Window.
- */
- Ext.define('Ext.ZIndexManager', {
- alternateClassName: 'Ext.WindowGroup',
- statics: {
- zBase : 9000
- },
- constructor: function(container) {
- var me = this;
- me.list = {};
- me.zIndexStack = [];
- me.front = null;
- if (container) {
- // This is the ZIndexManager for an Ext.container.Container, base its zseed on the zIndex of the Container's element
- if (container.isContainer) {
- container.on('resize', me._onContainerResize, me);
- me.zseed = Ext.Number.from(me.rendered ? container.getEl().getStyle('zIndex') : undefined, me.getNextZSeed());
- // The containing element we will be dealing with (eg masking) is the content target
- me.targetEl = container.getTargetEl();
- me.container = container;
- }
- // This is the ZIndexManager for a DOM element
- else {
- Ext.EventManager.onWindowResize(me._onContainerResize, me);
- me.zseed = me.getNextZSeed();
- me.targetEl = Ext.get(container);
- }
- }
- // No container passed means we are the global WindowManager. Our target is the doc body.
- // DOM must be ready to collect that ref.
- else {
- Ext.EventManager.onWindowResize(me._onContainerResize, me);
- me.zseed = me.getNextZSeed();
- Ext.onDocumentReady(function() {
- me.targetEl = Ext.getBody();
- });
- }
- },
- getNextZSeed: function() {
- return (Ext.ZIndexManager.zBase += 10000);
- },
- setBase: function(baseZIndex) {
- this.zseed = baseZIndex;
- var result = this.assignZIndices();
- this._activateLast();
- return result;
- },
- // private
- assignZIndices: function() {
- var a = this.zIndexStack,
- len = a.length,
- i = 0,
- zIndex = this.zseed,
- comp;
- for (; i < len; i++) {
- comp = a[i];
- if (comp && !comp.hidden) {
- // Setting the zIndex of a Component returns the topmost zIndex consumed by
- // that Component.
- // If it's just a plain floating Component such as a BoundList, then the
- // return value is the passed value plus 10, ready for the next item.
- // If a floating *Container* has its zIndex set, it re-orders its managed
- // floating children, starting from that new base, and returns a value 10000 above
- // the highest zIndex which it allocates.
- zIndex = comp.setZIndex(zIndex);
- }
- }
- // Activate new topmost
- this._activateLast();
- return zIndex;
- },
- // private
- _setActiveChild: function(comp, oldFront) {
- var front = this.front;
- if (comp !== front) {
- if (front && !front.destroying) {
- front.setActive(false, comp);
- }
- this.front = comp;
- if (comp && comp != oldFront) {
- comp.setActive(true);
- if (comp.modal) {
- this._showModalMask(comp);
- }
- }
- }
- },
-
- onComponentHide: function(comp){
- comp.setActive(false);
- this._activateLast();
- },
- // private
- _activateLast: function() {
- var me = this,
- stack = me.zIndexStack,
- i = stack.length - 1,
- oldFront = me.front,
- comp;
- // There may be no visible floater to activate
- me.front = undefined;
- // Go down through the z-index stack.
- // Activate the next visible one down.
- // If that was modal, then we're done
- for (; i >= 0 && stack[i].hidden; --i);
- if ((comp = stack[i])) {
- me._setActiveChild(comp, oldFront);
- if (comp.modal) {
- return;
- }
- }
- // If the new top one was not modal, keep going down to find the next visible
- // modal one to shift the modal mask down under
- for (; i >= 0; --i) {
- comp = stack[i];
- // If we find a visible modal further down the zIndex stack, move the mask to just under it.
- if (comp.isVisible() && comp.modal) {
- me._showModalMask(comp);
- return;
- }
- }
- // No visible modal Component was found in the run down the stack.
- // So hide the modal mask
- me._hideModalMask();
- },
- _showModalMask: function(comp) {
- var me = this,
- zIndex = comp.el.getStyle('zIndex') - 4,
- maskTarget = comp.floatParent ? comp.floatParent.getTargetEl() : comp.container,
- viewSize = maskTarget.getBox();
- if (maskTarget.dom === document.body) {
- viewSize.height = Math.max(document.body.scrollHeight, Ext.dom.Element.getDocumentHeight());
- viewSize.width = Math.max(document.body.scrollWidth, viewSize.width);
- }
- if (!me.mask) {
- me.mask = Ext.getBody().createChild({
- cls: Ext.baseCSSPrefix + 'mask'
- });
- me.mask.setVisibilityMode(Ext.Element.DISPLAY);
- me.mask.on('click', me._onMaskClick, me);
- }
- me.mask.maskTarget = maskTarget;
- maskTarget.addCls(Ext.baseCSSPrefix + 'body-masked');
- me.mask.setBox(viewSize);
- me.mask.setStyle('zIndex', zIndex);
- me.mask.show();
- },
- _hideModalMask: function() {
- var mask = this.mask;
- if (mask && mask.isVisible()) {
- mask.maskTarget.removeCls(Ext.baseCSSPrefix + 'body-masked');
- mask.maskTarget = undefined;
- mask.hide();
- }
- },
- _onMaskClick: function() {
- if (this.front) {
- this.front.focus();
- }
- },
- _onContainerResize: function() {
- var mask = this.mask,
- maskTarget,
- viewSize;
- if (mask && mask.isVisible()) {
- // At the new container size, the mask might be *causing* the scrollbar, so to find the valid
- // client size to mask, we must temporarily unmask the parent node.
- mask.hide();
- maskTarget = mask.maskTarget;
- if (maskTarget.dom === document.body) {
- viewSize = {
- height: Math.max(document.body.scrollHeight, Ext.dom.Element.getDocumentHeight()),
- width: Math.max(document.body.scrollWidth, document.documentElement.clientWidth)
- }
- } else {
- viewSize = maskTarget.getViewSize(true);
- }
- mask.setSize(viewSize);
- mask.show();
- }
- },
- /**
- * Registers a floating {@link Ext.Component} with this ZIndexManager. This should not
- * need to be called under normal circumstances. Floating Components (such as Windows,
- * BoundLists and Menus) are automatically registered with a
- * {@link Ext.Component#zIndexManager zIndexManager} at render time.
- *
- * Where this may be useful is moving Windows between two ZIndexManagers. For example,
- * to bring the Ext.MessageBox dialog under the same manager as the Desktop's
- * ZIndexManager in the desktop sample app:
- *
- * MyDesktop.getDesktop().getManager().register(Ext.MessageBox);
- *
- * @param {Ext.Component} comp The Component to register.
- */
- register : function(comp) {
- var me = this;
-
- if (comp.zIndexManager) {
- comp.zIndexManager.unregister(comp);
- }
- comp.zIndexManager = me;
- me.list[comp.id] = comp;
- me.zIndexStack.push(comp);
- comp.on('hide', me.onComponentHide, me);
- },
- /**
- * Unregisters a {@link Ext.Component} from this ZIndexManager. This should not
- * need to be called. Components are automatically unregistered upon destruction.
- * See {@link #register}.
- * @param {Ext.Component} comp The Component to unregister.
- */
- unregister : function(comp) {
- var me = this,
- list = me.list;
-
- delete comp.zIndexManager;
- if (list && list[comp.id]) {
- delete list[comp.id];
- comp.un('hide', me.onComponentHide);
- Ext.Array.remove(me.zIndexStack, comp);
- // Destruction requires that the topmost visible floater be activated. Same as hiding.
- me._activateLast();
- }
- },
- /**
- * Gets a registered Component by id.
- * @param {String/Object} id The id of the Component or a {@link Ext.Component} instance
- * @return {Ext.Component}
- */
- get : function(id) {
- return id.isComponent ? id : this.list[id];
- },
- /**
- * Brings the specified Component to the front of any other active Components in this ZIndexManager.
- * @param {String/Object} comp The id of the Component or a {@link Ext.Component} instance
- * @return {Boolean} True if the dialog was brought to the front, else false
- * if it was already in front
- */
- bringToFront : function(comp) {
- var me = this,
- result = false;
-
- comp = me.get(comp);
- if (comp !== me.front) {
- Ext.Array.remove(me.zIndexStack, comp);
- me.zIndexStack.push(comp);
- me.assignZIndices();
- result = true;
- this.front = comp;
- }
- if (result && comp.modal) {
- me._showModalMask(comp);
- }
- return result;
- },
- /**
- * Sends the specified Component to the back of other active Components in this ZIndexManager.
- * @param {String/Object} comp The id of the Component or a {@link Ext.Component} instance
- * @return {Ext.Component} The Component
- */
- sendToBack : function(comp) {
- var me = this;
-
- comp = me.get(comp);
- Ext.Array.remove(me.zIndexStack, comp);
- me.zIndexStack.unshift(comp);
- me.assignZIndices();
- this._activateLast();
- return comp;
- },
- /**
- * Hides all Components managed by this ZIndexManager.
- */
- hideAll : function() {
- var list = this.list,
- item,
- id;
-
- for (id in list) {
- if (list.hasOwnProperty(id)) {
- item = list[id];
- if (item.isComponent && item.isVisible()) {
- item.hide();
- }
- }
- }
- },
- /**
- * @private
- * Temporarily hides all currently visible managed Components. This is for when
- * dragging a Window which may manage a set of floating descendants in its ZIndexManager;
- * they should all be hidden just for the duration of the drag.
- */
- hide: function() {
- var i = 0,
- stack = this.zIndexStack,
- len = stack.length,
- comp;
- this.tempHidden = [];
- for (; i < len; i++) {
- comp = stack[i];
- if (comp.isVisible()) {
- this.tempHidden.push(comp);
- comp.el.hide();
- }
- }
- },
- /**
- * @private
- * Restores temporarily hidden managed Components to visibility.
- */
- show: function() {
- var i = 0,
- tempHidden = this.tempHidden,
- len = tempHidden ? tempHidden.length : 0,
- comp;
- for (; i < len; i++) {
- comp = tempHidden[i];
- comp.el.show();
- comp.setPosition(comp.x, comp.y);
- }
- delete this.tempHidden;
- },
- /**
- * Gets the currently-active Component in this ZIndexManager.
- * @return {Ext.Component} The active Component
- */
- getActive : function() {
- return this.front;
- },
- /**
- * Returns zero or more Components in this ZIndexManager using the custom search function passed to this method.
- * The function should accept a single {@link Ext.Component} reference as its only argument and should
- * return true if the Component matches the search criteria, otherwise it should return false.
- * @param {Function} fn The search function
- * @param {Object} [scope] The scope (this reference) in which the function is executed.
- * Defaults to the Component being tested. That gets passed to the function if not specified.
- * @return {Array} An array of zero or more matching windows
- */
- getBy : function(fn, scope) {
- var r = [],
- i = 0,
- stack = this.zIndexStack,
- len = stack.length,
- comp;
- for (; i < len; i++) {
- comp = stack[i];
- if (fn.call(scope||comp, comp) !== false) {
- r.push(comp);
- }
- }
- return r;
- },
- /**
- * Executes the specified function once for every Component in this ZIndexManager, passing each
- * Component as the only parameter. Returning false from the function will stop the iteration.
- * @param {Function} fn The function to execute for each item
- * @param {Object} [scope] The scope (this reference) in which the function
- * is executed. Defaults to the current Component in the iteration.
- */
- each : function(fn, scope) {
- var list = this.list,
- id,
- comp;
-
- for (id in list) {
- if (list.hasOwnProperty(id)) {
- comp = list[id];
- if (comp.isComponent && fn.call(scope || comp, comp) === false) {
- return;
- }
- }
- }
- },
- /**
- * Executes the specified function once for every Component in this ZIndexManager, passing each
- * Component as the only parameter. Returning false from the function will stop the iteration.
- * The components are passed to the function starting at the bottom and proceeding to the top.
- * @param {Function} fn The function to execute for each item
- * @param {Object} scope (optional) The scope (this reference) in which the function
- * is executed. Defaults to the current Component in the iteration.
- */
- eachBottomUp: function (fn, scope) {
- var stack = this.zIndexStack,
- i = 0,
- len = stack.length,
- comp;
- for (; i < len; i++) {
- comp = stack[i];
- if (comp.isComponent && fn.call(scope || comp, comp) === false) {
- return;
- }
- }
- },
- /**
- * Executes the specified function once for every Component in this ZIndexManager, passing each
- * Component as the only parameter. Returning false from the function will stop the iteration.
- * The components are passed to the function starting at the top and proceeding to the bottom.
- * @param {Function} fn The function to execute for each item
- * @param {Object} [scope] The scope (this reference) in which the function
- * is executed. Defaults to the current Component in the iteration.
- */
- eachTopDown: function (fn, scope) {
- var stack = this.zIndexStack,
- i = stack.length,
- comp;
- for (; i-- > 0; ) {
- comp = stack[i];
- if (comp.isComponent && fn.call(scope || comp, comp) === false) {
- return;
- }
- }
- },
- destroy: function() {
- var me = this,
- list = me.list,
- comp;
- for (var id in list) {
- if (list.hasOwnProperty(id)) {
- comp = list[id];
- if (comp.isComponent) {
- comp.destroy();
- }
- }
- }
- delete me.zIndexStack;
- delete me.list;
- delete me.container;
- delete me.targetEl;
- }
- }, function() {
- /**
- * @class Ext.WindowManager
- * @extends Ext.ZIndexManager
- *
- * The default global floating Component group that is available automatically.
- *
- * This manages instances of floating Components which were rendered programatically without
- * being added to a {@link Ext.container.Container Container}, and for floating Components
- * which were added into non-floating Containers.
- *
- * *Floating* Containers create their own instance of ZIndexManager, and floating Components
- * added at any depth below there are managed by that ZIndexManager.
- *
- * @singleton
- */
- Ext.WindowManager = Ext.WindowMgr = new this();
- });
- /**
- * @class Ext.fx.target.Target
- This class specifies a generic target for an animation. It provides a wrapper around a
- series of different types of objects to allow for a generic animation API.
- A target can be a single object or a Composite object containing other objects that are
- to be animated. This class and it's subclasses are generally not created directly, the
- underlying animation will create the appropriate Ext.fx.target.Target object by passing
- the instance to be animated.
- The following types of objects can be animated:
- - {@link Ext.fx.target.Component Components}
- - {@link Ext.fx.target.Element Elements}
- - {@link Ext.fx.target.Sprite Sprites}
- * @markdown
- * @abstract
- */
- Ext.define('Ext.fx.target.Target', {
- isAnimTarget: true,
- /**
- * Creates new Target.
- * @param {Ext.Component/Ext.Element/Ext.draw.Sprite} target The object to be animated
- */
- constructor: function(target) {
- this.target = target;
- this.id = this.getId();
- },
-
- getId: function() {
- return this.target.id;
- }
- });
- /**
- * Represents an RGB color and provides helper functions get
- * color components in HSL color space.
- */
- Ext.define('Ext.draw.Color', {
- /* Begin Definitions */
- /* End Definitions */
- colorToHexRe: /(.*?)rgb\((\d+),\s*(\d+),\s*(\d+)\)/,
- rgbRe: /\s*rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)\s*/,
- hexRe: /\s*#([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)\s*/,
- /**
- * @cfg {Number} lightnessFactor
- *
- * The default factor to compute the lighter or darker color. Defaults to 0.2.
- */
- lightnessFactor: 0.2,
- /**
- * Creates new Color.
- * @param {Number} red Red component (0..255)
- * @param {Number} green Green component (0..255)
- * @param {Number} blue Blue component (0..255)
- */
- constructor : function(red, green, blue) {
- var me = this,
- clamp = Ext.Number.constrain;
- me.r = clamp(red, 0, 255);
- me.g = clamp(green, 0, 255);
- me.b = clamp(blue, 0, 255);
- },
- /**
- * Get the red component of the color, in the range 0..255.
- * @return {Number}
- */
- getRed: function() {
- return this.r;
- },
- /**
- * Get the green component of the color, in the range 0..255.
- * @return {Number}
- */
- getGreen: function() {
- return this.g;
- },
- /**
- * Get the blue component of the color, in the range 0..255.
- * @return {Number}
- */
- getBlue: function() {
- return this.b;
- },
- /**
- * Get the RGB values.
- * @return {Number[]}
- */
- getRGB: function() {
- var me = this;
- return [me.r, me.g, me.b];
- },
- /**
- * Get the equivalent HSL components of the color.
- * @return {Number[]}
- */
- getHSL: function() {
- var me = this,
- r = me.r / 255,
- g = me.g / 255,
- b = me.b / 255,
- max = Math.max(r, g, b),
- min = Math.min(r, g, b),
- delta = max - min,
- h,
- s = 0,
- l = 0.5 * (max + min);
- // min==max means achromatic (hue is undefined)
- if (min != max) {
- s = (l < 0.5) ? delta / (max + min) : delta / (2 - max - min);
- if (r == max) {
- h = 60 * (g - b) / delta;
- } else if (g == max) {
- h = 120 + 60 * (b - r) / delta;
- } else {
- h = 240 + 60 * (r - g) / delta;
- }
- if (h < 0) {
- h += 360;
- }
- if (h >= 360) {
- h -= 360;
- }
- }
- return [h, s, l];
- },
- /**
- * Return a new color that is lighter than this color.
- * @param {Number} factor Lighter factor (0..1), default to 0.2
- * @return Ext.draw.Color
- */
- getLighter: function(factor) {
- var hsl = this.getHSL();
- factor = factor || this.lightnessFactor;
- hsl[2] = Ext.Number.constrain(hsl[2] + factor, 0, 1);
- return this.fromHSL(hsl[0], hsl[1], hsl[2]);
- },
- /**
- * Return a new color that is darker than this color.
- * @param {Number} factor Darker factor (0..1), default to 0.2
- * @return Ext.draw.Color
- */
- getDarker: function(factor) {
- factor = factor || this.lightnessFactor;
- return this.getLighter(-factor);
- },
- /**
- * Return the color in the hex format, i.e. '#rrggbb'.
- * @return {String}
- */
- toString: function() {
- var me = this,
- round = Math.round,
- r = round(me.r).toString(16),
- g = round(me.g).toString(16),
- b = round(me.b).toString(16);
- r = (r.length == 1) ? '0' + r : r;
- g = (g.length == 1) ? '0' + g : g;
- b = (b.length == 1) ? '0' + b : b;
- return ['#', r, g, b].join('');
- },
- /**
- * Convert a color to hexadecimal format.
- *
- * **Note:** This method is both static and instance.
- *
- * @param {String/String[]} color The color value (i.e 'rgb(255, 255, 255)', 'color: #ffffff').
- * Can also be an Array, in this case the function handles the first member.
- * @returns {String} The color in hexadecimal format.
- * @static
- */
- toHex: function(color) {
- if (Ext.isArray(color)) {
- color = color[0];
- }
- if (!Ext.isString(color)) {
- return '';
- }
- if (color.substr(0, 1) === '#') {
- return color;
- }
- var digits = this.colorToHexRe.exec(color);
- if (Ext.isArray(digits)) {
- var red = parseInt(digits[2], 10),
- green = parseInt(digits[3], 10),
- blue = parseInt(digits[4], 10),
- rgb = blue | (green << 8) | (red << 16);
- return digits[1] + '#' + ("000000" + rgb.toString(16)).slice(-6);
- }
- else {
- return color;
- }
- },
- /**
- * Parse the string and create a new color.
- *
- * Supported formats: '#rrggbb', '#rgb', and 'rgb(r,g,b)'.
- *
- * If the string is not recognized, an undefined will be returned instead.
- *
- * **Note:** This method is both static and instance.
- *
- * @param {String} str Color in string.
- * @returns Ext.draw.Color
- * @static
- */
- fromString: function(str) {
- var values, r, g, b,
- parse = parseInt;
- if ((str.length == 4 || str.length == 7) && str.substr(0, 1) === '#') {
- values = str.match(this.hexRe);
- if (values) {
- r = parse(values[1], 16) >> 0;
- g = parse(values[2], 16) >> 0;
- b = parse(values[3], 16) >> 0;
- if (str.length == 4) {
- r += (r * 16);
- g += (g * 16);
- b += (b * 16);
- }
- }
- }
- else {
- values = str.match(this.rgbRe);
- if (values) {
- r = values[1];
- g = values[2];
- b = values[3];
- }
- }
- return (typeof r == 'undefined') ? undefined : new Ext.draw.Color(r, g, b);
- },
- /**
- * Returns the gray value (0 to 255) of the color.
- *
- * The gray value is calculated using the formula r*0.3 + g*0.59 + b*0.11.
- *
- * @returns {Number}
- */
- getGrayscale: function() {
- // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
- return this.r * 0.3 + this.g * 0.59 + this.b * 0.11;
- },
- /**
- * Create a new color based on the specified HSL values.
- *
- * **Note:** This method is both static and instance.
- *
- * @param {Number} h Hue component (0..359)
- * @param {Number} s Saturation component (0..1)
- * @param {Number} l Lightness component (0..1)
- * @returns Ext.draw.Color
- * @static
- */
- fromHSL: function(h, s, l) {
- var C, X, m, i, rgb = [],
- abs = Math.abs,
- floor = Math.floor;
- if (s == 0 || h == null) {
- // achromatic
- rgb = [l, l, l];
- }
- else {
- // http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL
- // C is the chroma
- // X is the second largest component
- // m is the lightness adjustment
- h /= 60;
- C = s * (1 - abs(2 * l - 1));
- X = C * (1 - abs(h - 2 * floor(h / 2) - 1));
- m = l - C / 2;
- switch (floor(h)) {
- case 0:
- rgb = [C, X, 0];
- break;
- case 1:
- rgb = [X, C, 0];
- break;
- case 2:
- rgb = [0, C, X];
- break;
- case 3:
- rgb = [0, X, C];
- break;
- case 4:
- rgb = [X, 0, C];
- break;
- case 5:
- rgb = [C, 0, X];
- break;
- }
- rgb = [rgb[0] + m, rgb[1] + m, rgb[2] + m];
- }
- return new Ext.draw.Color(rgb[0] * 255, rgb[1] * 255, rgb[2] * 255);
- }
- }, function() {
- var prototype = this.prototype;
- //These functions are both static and instance. TODO: find a more elegant way of copying them
- this.addStatics({
- fromHSL: function() {
- return prototype.fromHSL.apply(prototype, arguments);
- },
- fromString: function() {
- return prototype.fromString.apply(prototype, arguments);
- },
- toHex: function() {
- return prototype.toHex.apply(prototype, arguments);
- }
- });
- });
- /**
- * @private
- * Base class for Box Layout overflow handlers. These specialized classes are invoked when a Box Layout
- * (either an HBox or a VBox) has child items that are either too wide (for HBox) or too tall (for VBox)
- * for its container.
- */
- Ext.define('Ext.layout.container.boxOverflow.None', {
- alternateClassName: 'Ext.layout.boxOverflow.None',
-
- constructor: function(layout, config) {
- this.layout = layout;
- Ext.apply(this, config);
- },
- handleOverflow: Ext.emptyFn,
- clearOverflow: Ext.emptyFn,
- beginLayout: Ext.emptyFn,
- beginLayoutCycle: Ext.emptyFn,
- completeLayout: function (ownerContext) {
- var me = this,
- plan = ownerContext.state.boxPlan,
- overflow;
- if (plan && plan.tooNarrow) {
- overflow = me.handleOverflow(ownerContext);
- if (overflow) {
- if (overflow.reservedSpace) {
- me.layout.publishInnerCtSize(ownerContext, overflow.reservedSpace);
- }
- // TODO: If we need to use the code below then we will need to pass along
- // the new targetSize as state and use it calculate somehow...
- //
- //if (overflow.recalculate) {
- // ownerContext.invalidate({
- // state: {
- // overflow: overflow
- // }
- // });
- //}
- }
- } else {
- me.clearOverflow();
- }
- },
- onRemove: Ext.emptyFn,
- /**
- * @private
- * Normalizes an item reference, string id or numerical index into a reference to the item
- * @param {Ext.Component/String/Number} item The item reference, id or index
- * @return {Ext.Component} The item
- */
- getItem: function(item) {
- return this.layout.owner.getComponent(item);
- },
-
- getOwnerType: function(owner){
- var type = '';
-
- if (owner.is('toolbar')) {
- type = 'toolbar';
- } else if (owner.is('tabbar')) {
- type = 'tabbar';
- } else {
- type = owner.getXType();
- }
-
- return type;
- },
- getPrefixConfig: Ext.emptyFn,
- getSuffixConfig: Ext.emptyFn,
- getOverflowCls: function() {
- return '';
- }
- });
- /**
- * @class Ext.util.Offset
- * @ignore
- */
- Ext.define('Ext.util.Offset', {
- /* Begin Definitions */
- statics: {
- fromObject: function(obj) {
- return new this(obj.x, obj.y);
- }
- },
- /* End Definitions */
- constructor: function(x, y) {
- this.x = (x != null && !isNaN(x)) ? x : 0;
- this.y = (y != null && !isNaN(y)) ? y : 0;
- return this;
- },
- copy: function() {
- return new Ext.util.Offset(this.x, this.y);
- },
- copyFrom: function(p) {
- this.x = p.x;
- this.y = p.y;
- },
- toString: function() {
- return "Offset[" + this.x + "," + this.y + "]";
- },
- equals: function(offset) {
- if(!(offset instanceof this.statics())) {
- Ext.Error.raise('Offset must be an instance of Ext.util.Offset');
- }
- return (this.x == offset.x && this.y == offset.y);
- },
- round: function(to) {
- if (!isNaN(to)) {
- var factor = Math.pow(10, to);
- this.x = Math.round(this.x * factor) / factor;
- this.y = Math.round(this.y * factor) / factor;
- } else {
- this.x = Math.round(this.x);
- this.y = Math.round(this.y);
- }
- },
- isZero: function() {
- return this.x == 0 && this.y == 0;
- }
- });
- /**
- * A wrapper class which can be applied to any element. Fires a "click" event while the
- * mouse is pressed. The interval between firings may be specified in the config but
- * defaults to 20 milliseconds.
- *
- * Optionally, a CSS class may be applied to the element during the time it is pressed.
- */
- Ext.define('Ext.util.ClickRepeater', {
- extend: 'Ext.util.Observable',
- /**
- * Creates new ClickRepeater.
- * @param {String/HTMLElement/Ext.Element} el The element or its ID to listen on
- * @param {Object} [config] Config object.
- */
- constructor : function(el, config){
- var me = this;
- me.el = Ext.get(el);
- me.el.unselectable();
- Ext.apply(me, config);
- me.callParent();
- me.addEvents(
- /**
- * @event mousedown
- * Fires when the mouse button is depressed.
- * @param {Ext.util.ClickRepeater} this
- * @param {Ext.EventObject} e
- */
- "mousedown",
- /**
- * @event click
- * Fires on a specified interval during the time the element is pressed.
- * @param {Ext.util.ClickRepeater} this
- * @param {Ext.EventObject} e
- */
- "click",
- /**
- * @event mouseup
- * Fires when the mouse key is released.
- * @param {Ext.util.ClickRepeater} this
- * @param {Ext.EventObject} e
- */
- "mouseup"
- );
- if(!me.disabled){
- me.disabled = true;
- me.enable();
- }
- // allow inline handler
- if(me.handler){
- me.on("click", me.handler, me.scope || me);
- }
- },
- /**
- * @cfg {String/HTMLElement/Ext.Element} el
- * The element to act as a button.
- */
- /**
- * @cfg {String} pressedCls
- * A CSS class name to be applied to the element while pressed.
- */
- /**
- * @cfg {Boolean} accelerate
- * True if autorepeating should start slowly and accelerate.
- * "interval" and "delay" are ignored.
- */
- /**
- * @cfg {Number} interval
- * The interval between firings of the "click" event (in milliseconds).
- */
- interval : 20,
- /**
- * @cfg {Number} delay
- * The initial delay before the repeating event begins firing.
- * Similar to an autorepeat key delay.
- */
- delay: 250,
- /**
- * @cfg {Boolean} preventDefault
- * True to prevent the default click event
- */
- preventDefault : true,
- /**
- * @cfg {Boolean} stopDefault
- * True to stop the default click event
- */
- stopDefault : false,
- timer : 0,
- /**
- * Enables the repeater and allows events to fire.
- */
- enable: function(){
- if(this.disabled){
- this.el.on('mousedown', this.handleMouseDown, this);
- if (Ext.isIE){
- this.el.on('dblclick', this.handleDblClick, this);
- }
- if(this.preventDefault || this.stopDefault){
- this.el.on('click', this.eventOptions, this);
- }
- }
- this.disabled = false;
- },
- /**
- * Disables the repeater and stops events from firing.
- */
- disable: function(/* private */ force){
- if(force || !this.disabled){
- clearTimeout(this.timer);
- if(this.pressedCls){
- this.el.removeCls(this.pressedCls);
- }
- Ext.getDoc().un('mouseup', this.handleMouseUp, this);
- this.el.removeAllListeners();
- }
- this.disabled = true;
- },
- /**
- * Convenience function for setting disabled/enabled by boolean.
- * @param {Boolean} disabled
- */
- setDisabled: function(disabled){
- this[disabled ? 'disable' : 'enable']();
- },
- eventOptions: function(e){
- if(this.preventDefault){
- e.preventDefault();
- }
- if(this.stopDefault){
- e.stopEvent();
- }
- },
- // private
- destroy : function() {
- this.disable(true);
- Ext.destroy(this.el);
- this.clearListeners();
- },
- handleDblClick : function(e){
- clearTimeout(this.timer);
- this.el.blur();
- this.fireEvent("mousedown", this, e);
- this.fireEvent("click", this, e);
- },
- // private
- handleMouseDown : function(e){
- clearTimeout(this.timer);
- this.el.blur();
- if(this.pressedCls){
- this.el.addCls(this.pressedCls);
- }
- this.mousedownTime = new Date();
- Ext.getDoc().on("mouseup", this.handleMouseUp, this);
- this.el.on("mouseout", this.handleMouseOut, this);
- this.fireEvent("mousedown", this, e);
- this.fireEvent("click", this, e);
- // Do not honor delay or interval if acceleration wanted.
- if (this.accelerate) {
- this.delay = 400;
- }
- // Re-wrap the event object in a non-shared object, so it doesn't lose its context if
- // the global shared EventObject gets a new Event put into it before the timer fires.
- e = new Ext.EventObjectImpl(e);
- this.timer = Ext.defer(this.click, this.delay || this.interval, this, [e]);
- },
- // private
- click : function(e){
- this.fireEvent("click", this, e);
- this.timer = Ext.defer(this.click, this.accelerate ?
- this.easeOutExpo(Ext.Date.getElapsed(this.mousedownTime),
- 400,
- -390,
- 12000) :
- this.interval, this, [e]);
- },
- easeOutExpo : function (t, b, c, d) {
- return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
- },
- // private
- handleMouseOut : function(){
- clearTimeout(this.timer);
- if(this.pressedCls){
- this.el.removeCls(this.pressedCls);
- }
- this.el.on("mouseover", this.handleMouseReturn, this);
- },
- // private
- handleMouseReturn : function(){
- this.el.un("mouseover", this.handleMouseReturn, this);
- if(this.pressedCls){
- this.el.addCls(this.pressedCls);
- }
- this.click();
- },
- // private
- handleMouseUp : function(e){
- clearTimeout(this.timer);
- this.el.un("mouseover", this.handleMouseReturn, this);
- this.el.un("mouseout", this.handleMouseOut, this);
- Ext.getDoc().un("mouseup", this.handleMouseUp, this);
- if(this.pressedCls){
- this.el.removeCls(this.pressedCls);
- }
- this.fireEvent("mouseup", this, e);
- }
- });
- /**
- * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
- * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
- * should not contain any HTML, otherwise it may not be measured correctly.
- *
- * The measurement works by copying the relevant CSS styles that can affect the font related display,
- * then checking the size of an element that is auto-sized. Note that if the text is multi-lined, you must
- * provide a **fixed width** when doing the measurement.
- *
- * If multiple measurements are being done on the same element, you create a new instance to initialize
- * to avoid the overhead of copying the styles to the element repeatedly.
- */
- Ext.define('Ext.util.TextMetrics', {
- statics: {
- shared: null,
- /**
- * Measures the size of the specified text
- * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
- * that can affect the size of the rendered text
- * @param {String} text The text to measure
- * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
- * in order to accurately measure the text height
- * @return {Object} An object containing the text's size `{width: (width), height: (height)}`
- * @static
- */
- measure: function(el, text, fixedWidth){
- var me = this,
- shared = me.shared;
-
- if(!shared){
- shared = me.shared = new me(el, fixedWidth);
- }
- shared.bind(el);
- shared.setFixedWidth(fixedWidth || 'auto');
- return shared.getSize(text);
- },
-
- /**
- * Destroy the TextMetrics instance created by {@link #measure}.
- * @static
- */
- destroy: function(){
- var me = this;
- Ext.destroy(me.shared);
- me.shared = null;
- }
- },
-
- /**
- * Creates new TextMetrics.
- * @param {String/HTMLElement/Ext.Element} bindTo The element or its ID to bind to.
- * @param {Number} [fixedWidth] A fixed width to apply to the measuring element.
- */
- constructor: function(bindTo, fixedWidth){
- var measure = this.measure = Ext.getBody().createChild({
- cls: Ext.baseCSSPrefix + 'textmetrics'
- });
- this.el = Ext.get(bindTo);
-
- measure.position('absolute');
- measure.setLeftTop(-1000, -1000);
- measure.hide();
- if (fixedWidth) {
- measure.setWidth(fixedWidth);
- }
- },
-
- /**
- * Returns the size of the specified text based on the internal element's style and width properties
- * @param {String} text The text to measure
- * @return {Object} An object containing the text's size `{width: (width), height: (height)}`
- */
- getSize: function(text){
- var measure = this.measure,
- size;
-
- measure.update(text);
- size = measure.getSize();
- measure.update('');
- return size;
- },
-
- /**
- * Binds this TextMetrics instance to a new element
- * @param {String/HTMLElement/Ext.Element} el The element or its ID.
- */
- bind: function(el){
- var me = this;
-
- me.el = Ext.get(el);
- me.measure.setStyle(
- me.el.getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
- );
- },
-
- /**
- * Sets a fixed width on the internal measurement element. If the text will be multiline, you have
- * to set a fixed width in order to accurately measure the text height.
- * @param {Number} width The width to set on the element
- */
- setFixedWidth : function(width){
- this.measure.setWidth(width);
- },
-
- /**
- * Returns the measured width of the specified text
- * @param {String} text The text to measure
- * @return {Number} width The width in pixels
- */
- getWidth : function(text){
- this.measure.dom.style.width = 'auto';
- return this.getSize(text).width;
- },
-
- /**
- * Returns the measured height of the specified text
- * @param {String} text The text to measure
- * @return {Number} height The height in pixels
- */
- getHeight : function(text){
- return this.getSize(text).height;
- },
-
- /**
- * Destroy this instance
- */
- destroy: function(){
- var me = this;
- me.measure.remove();
- delete me.el;
- delete me.measure;
- }
- }, function(){
- Ext.Element.addMethods({
- /**
- * Returns the width in pixels of the passed text, or the width of the text in this Element.
- * @param {String} text The text to measure. Defaults to the innerHTML of the element.
- * @param {Number} [min] The minumum value to return.
- * @param {Number} [max] The maximum value to return.
- * @return {Number} The text width in pixels.
- * @member Ext.dom.Element
- */
- getTextWidth : function(text, min, max){
- return Ext.Number.constrain(Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width, min || 0, max || 1000000);
- }
- });
- });
- /**
- * @class Ext.app.Controller
- *
- * Controllers are the glue that binds an application together. All they really do is listen for events (usually from
- * views) and take some action. Here's how we might create a Controller to manage Users:
- *
- * Ext.define('MyApp.controller.Users', {
- * extend: 'Ext.app.Controller',
- *
- * init: function() {
- * console.log('Initialized Users! This happens before the Application launch function is called');
- * }
- * });
- *
- * The init function is a special method that is called when your application boots. It is called before the
- * {@link Ext.app.Application Application}'s launch function is executed so gives a hook point to run any code before
- * your Viewport is created.
- *
- * The init function is a great place to set up how your controller interacts with the view, and is usually used in
- * conjunction with another Controller function - {@link Ext.app.Controller#control control}. The control function
- * makes it easy to listen to events on your view classes and take some action with a handler function. Let's update
- * our Users controller to tell us when the panel is rendered:
- *
- * Ext.define('MyApp.controller.Users', {
- * extend: 'Ext.app.Controller',
- *
- * init: function() {
- * this.control({
- * 'viewport > panel': {
- * render: this.onPanelRendered
- * }
- * });
- * },
- *
- * onPanelRendered: function() {
- * console.log('The panel was rendered');
- * }
- * });
- *
- * We've updated the init function to use this.control to set up listeners on views in our application. The control
- * function uses the new ComponentQuery engine to quickly and easily get references to components on the page. If you
- * are not familiar with ComponentQuery yet, be sure to check out the {@link Ext.ComponentQuery documentation}. In brief though,
- * it allows us to pass a CSS-like selector that will find every matching component on the page.
- *
- * In our init function above we supplied 'viewport > panel', which translates to "find me every Panel that is a direct
- * child of a Viewport". We then supplied an object that maps event names (just 'render' in this case) to handler
- * functions. The overall effect is that whenever any component that matches our selector fires a 'render' event, our
- * onPanelRendered function is called.
- *
- * <u>Using refs</u>
- *
- * One of the most useful parts of Controllers is the new ref system. These use the new {@link Ext.ComponentQuery} to
- * make it really easy to get references to Views on your page. Let's look at an example of this now:
- *
- * Ext.define('MyApp.controller.Users', {
- * extend: 'Ext.app.Controller',
- *
- * refs: [
- * {
- * ref: 'list',
- * selector: 'grid'
- * }
- * ],
- *
- * init: function() {
- * this.control({
- * 'button': {
- * click: this.refreshGrid
- * }
- * });
- * },
- *
- * refreshGrid: function() {
- * this.getList().store.load();
- * }
- * });
- *
- * This example assumes the existence of a {@link Ext.grid.Panel Grid} on the page, which contains a single button to
- * refresh the Grid when clicked. In our refs array, we set up a reference to the grid. There are two parts to this -
- * the 'selector', which is a {@link Ext.ComponentQuery ComponentQuery} selector which finds any grid on the page and
- * assigns it to the reference 'list'.
- *
- * By giving the reference a name, we get a number of things for free. The first is the getList function that we use in
- * the refreshGrid method above. This is generated automatically by the Controller based on the name of our ref, which
- * was capitalized and prepended with get to go from 'list' to 'getList'.
- *
- * The way this works is that the first time getList is called by your code, the ComponentQuery selector is run and the
- * first component that matches the selector ('grid' in this case) will be returned. All future calls to getList will
- * use a cached reference to that grid. Usually it is advised to use a specific ComponentQuery selector that will only
- * match a single View in your application (in the case above our selector will match any grid on the page).
- *
- * Bringing it all together, our init function is called when the application boots, at which time we call this.control
- * to listen to any click on a {@link Ext.button.Button button} and call our refreshGrid function (again, this will
- * match any button on the page so we advise a more specific selector than just 'button', but have left it this way for
- * simplicity). When the button is clicked we use out getList function to refresh the grid.
- *
- * You can create any number of refs and control any number of components this way, simply adding more functions to
- * your Controller as you go. For an example of real-world usage of Controllers see the Feed Viewer example in the
- * examples/app/feed-viewer folder in the SDK download.
- *
- * <u>Generated getter methods</u>
- *
- * Refs aren't the only thing that generate convenient getter methods. Controllers often have to deal with Models and
- * Stores so the framework offers a couple of easy ways to get access to those too. Let's look at another example:
- *
- * Ext.define('MyApp.controller.Users', {
- * extend: 'Ext.app.Controller',
- *
- * models: ['User'],
- * stores: ['AllUsers', 'AdminUsers'],
- *
- * init: function() {
- * var User = this.getUserModel(),
- * allUsers = this.getAllUsersStore();
- *
- * var ed = new User({name: 'Ed'});
- * allUsers.add(ed);
- * }
- * });
- *
- * By specifying Models and Stores that the Controller cares about, it again dynamically loads them from the appropriate
- * locations (app/model/User.js, app/store/AllUsers.js and app/store/AdminUsers.js in this case) and creates getter
- * functions for them all. The example above will create a new User model instance and add it to the AllUsers Store.
- * Of course, you could do anything in this function but in this case we just did something simple to demonstrate the
- * functionality.
- *
- * <u>Further Reading</u>
- *
- * For more information about writing Ext JS 4 applications, please see the
- * [application architecture guide](#/guide/application_architecture). Also see the {@link Ext.app.Application} documentation.
- *
- * @docauthor Ed Spencer
- */
- Ext.define('Ext.app.Controller', {
- mixins: {
- observable: 'Ext.util.Observable'
- },
- /**
- * @cfg {String} id The id of this controller. You can use this id when dispatching.
- */
-
- /**
- * @cfg {String[]} models
- * Array of models to require from AppName.model namespace. For example:
- *
- * Ext.define("MyApp.controller.Foo", {
- * extend: "Ext.app.Controller",
- * models: ['User', 'Vehicle']
- * });
- *
- * This is equivalent of:
- *
- * Ext.define("MyApp.controller.Foo", {
- * extend: "Ext.app.Controller",
- * requires: ['MyApp.model.User', 'MyApp.model.Vehicle']
- * });
- *
- */
- /**
- * @cfg {String[]} views
- * Array of views to require from AppName.view namespace. For example:
- *
- * Ext.define("MyApp.controller.Foo", {
- * extend: "Ext.app.Controller",
- * views: ['List', 'Detail']
- * });
- *
- * This is equivalent of:
- *
- * Ext.define("MyApp.controller.Foo", {
- * extend: "Ext.app.Controller",
- * requires: ['MyApp.view.List', 'MyApp.view.Detail']
- * });
- *
- */
- /**
- * @cfg {String[]} stores
- * Array of stores to require from AppName.store namespace. For example:
- *
- * Ext.define("MyApp.controller.Foo", {
- * extend: "Ext.app.Controller",
- * stores: ['Users', 'Vehicles']
- * });
- *
- * This is equivalent of:
- *
- * Ext.define("MyApp.controller.Foo", {
- * extend: "Ext.app.Controller",
- * requires: ['MyApp.store.Users', 'MyApp.store.Vehicles']
- * });
- *
- */
- /**
- * @cfg {Object[]} refs
- * Array of configs to build up references to views on page. For example:
- *
- * Ext.define("MyApp.controller.Foo", {
- * extend: "Ext.app.Controller",
- * refs: [
- * {
- * ref: 'list',
- * selector: 'grid'
- * }
- * ],
- * });
- *
- * This will add method `getList` to the controller which will internally use
- * Ext.ComponentQuery to reference the grid component on page.
- */
- onClassExtended: function(cls, data, hooks) {
- var className = Ext.getClassName(cls),
- match = className.match(/^(.*)\.controller\./);
- if (match !== null) {
- var namespace = Ext.Loader.getPrefix(className) || match[1],
- onBeforeClassCreated = hooks.onBeforeCreated,
- requires = [],
- modules = ['model', 'view', 'store'],
- prefix;
- hooks.onBeforeCreated = function(cls, data) {
- var i, ln, module,
- items, j, subLn, item;
- for (i = 0,ln = modules.length; i < ln; i++) {
- module = modules[i];
- items = Ext.Array.from(data[module + 's']);
- for (j = 0,subLn = items.length; j < subLn; j++) {
- item = items[j];
- prefix = Ext.Loader.getPrefix(item);
- if (prefix === '' || prefix === item) {
- requires.push(namespace + '.' + module + '.' + item);
- }
- else {
- requires.push(item);
- }
- }
- }
- Ext.require(requires, Ext.Function.pass(onBeforeClassCreated, arguments, this));
- };
- }
- },
- /**
- * Creates new Controller.
- * @param {Object} config (optional) Config object.
- */
- constructor: function(config) {
- this.mixins.observable.constructor.call(this, config);
- Ext.apply(this, config || {});
- this.createGetters('model', this.models);
- this.createGetters('store', this.stores);
- this.createGetters('view', this.views);
- if (this.refs) {
- this.ref(this.refs);
- }
- },
- /**
- * A template method that is called when your application boots. It is called before the
- * {@link Ext.app.Application Application}'s launch function is executed so gives a hook point to run any code before
- * your Viewport is created.
- *
- * @param {Ext.app.Application} application
- * @template
- */
- init: function(application) {},
- /**
- * A template method like {@link #init}, but called after the viewport is created.
- * This is called after the {@link Ext.app.Application#launch launch} method of Application is executed.
- *
- * @param {Ext.app.Application} application
- * @template
- */
- onLaunch: function(application) {},
- createGetters: function(type, refs) {
- type = Ext.String.capitalize(type);
- var i = 0,
- length = (refs) ? refs.length : 0,
- fn, ref, parts, x, numparts;
- for (; i < length; i++) {
- fn = 'get';
- ref = refs[i];
- parts = ref.split('.');
- numParts = parts.length;
- // Handle namespaced class names. E.g. feed.Add becomes getFeedAddView etc.
- for (x = 0 ; x < numParts; x++) {
- fn += Ext.String.capitalize(parts[x]);
- }
- fn += type;
- if (!this[fn]) {
- this[fn] = Ext.Function.pass(this['get' + type], [ref], this);
- }
- // Execute it right away
- this[fn](ref);
- }
- },
- ref: function(refs) {
- refs = Ext.Array.from(refs);
-
- var me = this,
- i = 0,
- length = refs.length,
- info, ref, fn;
- for (; i < length; i++) {
- info = refs[i];
- ref = info.ref;
- fn = 'get' + Ext.String.capitalize(ref);
- if (!me[fn]) {
- me[fn] = Ext.Function.pass(me.getRef, [ref, info], me);
- }
- me.references = me.references || [];
- me.references.push(ref.toLowerCase());
- }
- },
- addRef: function(ref) {
- return this.ref([ref]);
- },
- getRef: function(ref, info, config) {
- this.refCache = this.refCache || {};
- info = info || {};
- config = config || {};
- Ext.apply(info, config);
- if (info.forceCreate) {
- return Ext.ComponentManager.create(info, 'component');
- }
- var me = this,
- cached = me.refCache[ref];
- if (!cached) {
- me.refCache[ref] = cached = Ext.ComponentQuery.query(info.selector)[0];
- if (!cached && info.autoCreate) {
- me.refCache[ref] = cached = Ext.ComponentManager.create(info, 'component');
- }
- if (cached) {
- cached.on('beforedestroy', function() {
- me.refCache[ref] = null;
- });
- }
- }
- return cached;
- },
- hasRef: function(ref) {
- return this.references && this.references.indexOf(ref.toLowerCase()) !== -1;
- },
- /**
- * Adds listeners to components selected via {@link Ext.ComponentQuery}. Accepts an
- * object containing component paths mapped to a hash of listener functions.
- *
- * In the following example the `updateUser` function is mapped to to the `click`
- * event on a button component, which is a child of the `useredit` component.
- *
- * Ext.define('AM.controller.Users', {
- * init: function() {
- * this.control({
- * 'useredit button[action=save]': {
- * click: this.updateUser
- * }
- * });
- * },
- *
- * updateUser: function(button) {
- * console.log('clicked the Save button');
- * }
- * });
- *
- * See {@link Ext.ComponentQuery} for more information on component selectors.
- *
- * @param {String/Object} selectors If a String, the second argument is used as the
- * listeners, otherwise an object of selectors -> listeners is assumed
- * @param {Object} listeners
- */
- control: function(selectors, listeners) {
- this.application.control(selectors, listeners, this);
- },
- /**
- * Returns instance of a {@link Ext.app.Controller controller} with the given name.
- * When controller doesn't exist yet, it's created.
- * @param {String} name
- * @return {Ext.app.Controller} a controller instance.
- */
- getController: function(name) {
- return this.application.getController(name);
- },
- /**
- * Returns instance of a {@link Ext.data.Store Store} with the given name.
- * When store doesn't exist yet, it's created.
- * @param {String} name
- * @return {Ext.data.Store} a store instance.
- */
- getStore: function(name) {
- return this.application.getStore(name);
- },
- /**
- * Returns a {@link Ext.data.Model Model} class with the given name.
- * A shorthand for using {@link Ext.ModelManager#getModel}.
- * @param {String} name
- * @return {Ext.data.Model} a model class.
- */
- getModel: function(model) {
- return this.application.getModel(model);
- },
- /**
- * Returns a View class with the given name. To create an instance of the view,
- * you can use it like it's used by Application to create the Viewport:
- *
- * this.getView('Viewport').create();
- *
- * @param {String} name
- * @return {Ext.Base} a view class.
- */
- getView: function(view) {
- return this.application.getView(view);
- }
- });
- /**
- * Base Manager class
- */
- Ext.define('Ext.AbstractManager', {
- /* Begin Definitions */
- requires: ['Ext.util.HashMap'],
- /* End Definitions */
- typeName: 'type',
- constructor: function(config) {
- Ext.apply(this, config || {});
- /**
- * @property {Ext.util.HashMap} all
- * Contains all of the items currently managed
- */
- this.all = new Ext.util.HashMap();
- this.types = {};
- },
- /**
- * Returns an item by id.
- * For additional details see {@link Ext.util.HashMap#get}.
- * @param {String} id The id of the item
- * @return {Object} The item, undefined if not found.
- */
- get : function(id) {
- return this.all.get(id);
- },
- /**
- * Registers an item to be managed
- * @param {Object} item The item to register
- */
- register: function(item) {
- var all = this.all,
- key = all.getKey(item);
-
- if (all.containsKey(key)) {
- Ext.Error.raise('Registering duplicate id "' + key + '" with this manager');
- }
- this.all.add(item);
- },
- /**
- * Unregisters an item by removing it from this manager
- * @param {Object} item The item to unregister
- */
- unregister: function(item) {
- this.all.remove(item);
- },
- /**
- * Registers a new item constructor, keyed by a type key.
- * @param {String} type The mnemonic string by which the class may be looked up.
- * @param {Function} cls The new instance class.
- */
- registerType : function(type, cls) {
- this.types[type] = cls;
- cls[this.typeName] = type;
- },
- /**
- * Checks if an item type is registered.
- * @param {String} type The mnemonic string by which the class may be looked up
- * @return {Boolean} Whether the type is registered.
- */
- isRegistered : function(type){
- return this.types[type] !== undefined;
- },
- /**
- * Creates and returns an instance of whatever this manager manages, based on the supplied type and
- * config object.
- * @param {Object} config The config object
- * @param {String} defaultType If no type is discovered in the config object, we fall back to this type
- * @return {Object} The instance of whatever this manager is managing
- */
- create: function(config, defaultType) {
- var type = config[this.typeName] || config.type || defaultType,
- Constructor = this.types[type];
- if (Constructor === undefined) {
- Ext.Error.raise("The '" + type + "' type has not been registered with this manager");
- }
- return new Constructor(config);
- },
- /**
- * Registers a function that will be called when an item with the specified id is added to the manager.
- * This will happen on instantiation.
- * @param {String} id The item id
- * @param {Function} fn The callback function. Called with a single parameter, the item.
- * @param {Object} scope The scope (this reference) in which the callback is executed.
- * Defaults to the item.
- */
- onAvailable : function(id, fn, scope){
- var all = this.all,
- item;
-
- if (all.containsKey(id)) {
- item = all.get(id);
- fn.call(scope || item, item);
- } else {
- all.on('add', function(map, key, item){
- if (key == id) {
- fn.call(scope || item, item);
- all.un('add', fn, scope);
- }
- });
- }
- },
-
- /**
- * Executes the specified function once for each item in the collection.
- * @param {Function} fn The function to execute.
- * @param {String} fn.key The key of the item
- * @param {Number} fn.value The value of the item
- * @param {Number} fn.length The total number of items in the collection
- * @param {Boolean} fn.return False to cease iteration.
- * @param {Object} scope The scope to execute in. Defaults to `this`.
- */
- each: function(fn, scope){
- this.all.each(fn, scope || this);
- },
-
- /**
- * Gets the number of items in the collection.
- * @return {Number} The number of items in the collection.
- */
- getCount: function(){
- return this.all.getCount();
- }
- });
- /**
- * @author Ed Spencer
- * @class Ext.ModelManager
- The ModelManager keeps track of all {@link Ext.data.Model} types defined in your application.
- __Creating Model Instances__
- Model instances can be created by using the {@link Ext#create Ext.create} method. Ext.create replaces
- the deprecated {@link #create Ext.ModelManager.create} method. It is also possible to create a model instance
- this by using the Model type directly. The following 3 snippets are equivalent:
- Ext.define('User', {
- extend: 'Ext.data.Model',
- fields: ['first', 'last']
- });
- // method 1, create using Ext.create (recommended)
- Ext.create('User', {
- first: 'Ed',
- last: 'Spencer'
- });
- // method 2, create through the manager (deprecated)
- Ext.ModelManager.create({
- first: 'Ed',
- last: 'Spencer'
- }, 'User');
- // method 3, create on the type directly
- new User({
- first: 'Ed',
- last: 'Spencer'
- });
- __Accessing Model Types__
- A reference to a Model type can be obtained by using the {@link #getModel} function. Since models types
- are normal classes, you can access the type directly. The following snippets are equivalent:
- Ext.define('User', {
- extend: 'Ext.data.Model',
- fields: ['first', 'last']
- });
- // method 1, access model type through the manager
- var UserType = Ext.ModelManager.getModel('User');
- // method 2, reference the type directly
- var UserType = User;
- * @markdown
- * @singleton
- */
- Ext.define('Ext.ModelManager', {
- extend: 'Ext.AbstractManager',
- alternateClassName: 'Ext.ModelMgr',
- requires: ['Ext.data.association.Association'],
-
- singleton: true,
- typeName: 'mtype',
- /**
- * Private stack of associations that must be created once their associated model has been defined
- * @property {Ext.data.association.Association[]} associationStack
- */
- associationStack: [],
- /**
- * Registers a model definition. All model plugins marked with isDefault: true are bootstrapped
- * immediately, as are any addition plugins defined in the model config.
- * @private
- */
- registerType: function(name, config) {
- var proto = config.prototype,
- model;
- if (proto && proto.isModel) {
- // registering an already defined model
- model = config;
- } else {
- // passing in a configuration
- if (!config.extend) {
- config.extend = 'Ext.data.Model';
- }
- model = Ext.define(name, config);
- }
- this.types[name] = model;
- return model;
- },
- /**
- * @private
- * Private callback called whenever a model has just been defined. This sets up any associations
- * that were waiting for the given model to be defined
- * @param {Function} model The model that was just created
- */
- onModelDefined: function(model) {
- var stack = this.associationStack,
- length = stack.length,
- create = [],
- association, i, created;
- for (i = 0; i < length; i++) {
- association = stack[i];
- if (association.associatedModel == model.modelName) {
- create.push(association);
- }
- }
- for (i = 0, length = create.length; i < length; i++) {
- created = create[i];
- this.types[created.ownerModel].prototype.associations.add(Ext.data.association.Association.create(created));
- Ext.Array.remove(stack, created);
- }
- },
- /**
- * Registers an association where one of the models defined doesn't exist yet.
- * The ModelManager will check when new models are registered if it can link them
- * together
- * @private
- * @param {Ext.data.association.Association} association The association
- */
- registerDeferredAssociation: function(association){
- this.associationStack.push(association);
- },
- /**
- * Returns the {@link Ext.data.Model} for a given model name
- * @param {String/Object} id The id of the model or the model instance.
- * @return {Ext.data.Model} a model class.
- */
- getModel: function(id) {
- var model = id;
- if (typeof model == 'string') {
- model = this.types[model];
- }
- return model;
- },
- /**
- * Creates a new instance of a Model using the given data.
- *
- * This method is deprecated. Use {@link Ext#create Ext.create} instead. For example:
- *
- * Ext.create('User', {
- * first: 'Ed',
- * last: 'Spencer'
- * });
- *
- * @param {Object} data Data to initialize the Model's fields with
- * @param {String} name The name of the model to create
- * @param {Number} id (Optional) unique id of the Model instance (see {@link Ext.data.Model})
- */
- create: function(config, name, id) {
- var con = typeof name == 'function' ? name : this.types[name || config.name];
- return new con(config, id);
- }
- }, function() {
- /**
- * Old way for creating Model classes. Instead use:
- *
- * Ext.define("MyModel", {
- * extend: "Ext.data.Model",
- * fields: []
- * });
- *
- * @param {String} name Name of the Model class.
- * @param {Object} config A configuration object for the Model you wish to create.
- * @return {Ext.data.Model} The newly registered Model
- * @member Ext
- * @deprecated 4.0.0 Use {@link Ext#define} instead.
- */
- Ext.regModel = function() {
- if (Ext.isDefined(Ext.global.console)) {
- Ext.global.console.warn('Ext.regModel has been deprecated. Models can now be created by extending Ext.data.Model: Ext.define("MyModel", {extend: "Ext.data.Model", fields: []});.');
- }
- return this.ModelManager.registerType.apply(this.ModelManager, arguments);
- };
- });
- /**
- * @class Ext.ComponentManager
- * <p>Provides a registry of all Components (instances of {@link Ext.Component} or any subclass
- * thereof) on a page so that they can be easily accessed by {@link Ext.Component component}
- * {@link Ext.Component#id id} (see {@link #get}, or the convenience method {@link Ext#getCmp Ext.getCmp}).</p>
- * <p>This object also provides a registry of available Component <i>classes</i>
- * indexed by a mnemonic code known as the Component's {@link Ext.Component#xtype xtype}.
- * The <code>xtype</code> provides a way to avoid instantiating child Components
- * when creating a full, nested config object for a complete Ext page.</p>
- * <p>A child Component may be specified simply as a <i>config object</i>
- * as long as the correct <code>{@link Ext.Component#xtype xtype}</code> is specified so that if and when the Component
- * needs rendering, the correct type can be looked up for lazy instantiation.</p>
- * <p>For a list of all available <code>{@link Ext.Component#xtype xtypes}</code>, see {@link Ext.Component}.</p>
- * @singleton
- */
- Ext.define('Ext.ComponentManager', {
- extend: 'Ext.AbstractManager',
- alternateClassName: 'Ext.ComponentMgr',
-
- singleton: true,
-
- typeName: 'xtype',
-
- /**
- * Creates a new Component from the specified config object using the
- * config object's xtype to determine the class to instantiate.
- * @param {Object} config A configuration object for the Component you wish to create.
- * @param {String} defaultType (optional) The xtype to use if the config object does not
- * contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
- * @return {Ext.Component} The newly instantiated Component.
- */
- create: function(component, defaultType){
- if (typeof component == 'string') {
- return Ext.widget(component);
- }
- if (component.isComponent) {
- return component;
- }
- return Ext.widget(component.xtype || defaultType, component);
- },
- registerType: function(type, cls) {
- this.types[type] = cls;
- cls[this.typeName] = type;
- cls.prototype[this.typeName] = type;
- }
- });
- /**
- * @class Ext.data.Types
- * <p>This is a static class containing the system-supplied data types which may be given to a {@link Ext.data.Field Field}.<p/>
- * <p>The properties in this class are used as type indicators in the {@link Ext.data.Field Field} class, so to
- * test whether a Field is of a certain type, compare the {@link Ext.data.Field#type type} property against properties
- * of this class.</p>
- * <p>Developers may add their own application-specific data types to this class. Definition names must be UPPERCASE.
- * each type definition must contain three properties:</p>
- * <div class="mdetail-params"><ul>
- * <li><code>convert</code> : <i>Function</i><div class="sub-desc">A function to convert raw data values from a data block into the data
- * to be stored in the Field. The function is passed the collowing parameters:
- * <div class="mdetail-params"><ul>
- * <li><b>v</b> : Mixed<div class="sub-desc">The data value as read by the Reader, if undefined will use
- * the configured <tt>{@link Ext.data.Field#defaultValue defaultValue}</tt>.</div></li>
- * <li><b>rec</b> : Mixed<div class="sub-desc">The data object containing the row as read by the Reader.
- * Depending on the Reader type, this could be an Array ({@link Ext.data.reader.Array ArrayReader}), an object
- * ({@link Ext.data.reader.Json JsonReader}), or an XML element.</div></li>
- * </ul></div></div></li>
- * <li><code>sortType</code> : <i>Function</i> <div class="sub-desc">A function to convert the stored data into comparable form, as defined by {@link Ext.data.SortTypes}.</div></li>
- * <li><code>type</code> : <i>String</i> <div class="sub-desc">A textual data type name.</div></li>
- * </ul></div>
- * <p>For example, to create a VELatLong field (See the Microsoft Bing Mapping API) containing the latitude/longitude value of a datapoint on a map from a JsonReader data block
- * which contained the properties <code>lat</code> and <code>long</code>, you would define a new data type like this:</p>
- *<pre><code>
- // Add a new Field data type which stores a VELatLong object in the Record.
- Ext.data.Types.VELATLONG = {
- convert: function(v, data) {
- return new VELatLong(data.lat, data.long);
- },
- sortType: function(v) {
- return v.Latitude; // When sorting, order by latitude
- },
- type: 'VELatLong'
- };
- </code></pre>
- * <p>Then, when declaring a Model, use: <pre><code>
- var types = Ext.data.Types; // allow shorthand type access
- Ext.define('Unit',
- extend: 'Ext.data.Model',
- fields: [
- { name: 'unitName', mapping: 'UnitName' },
- { name: 'curSpeed', mapping: 'CurSpeed', type: types.INT },
- { name: 'latitude', mapping: 'lat', type: types.FLOAT },
- { name: 'longitude', mapping: 'long', type: types.FLOAT },
- { name: 'position', type: types.VELATLONG }
- ]
- });
- </code></pre>
- * @singleton
- */
- Ext.define('Ext.data.Types', {
- singleton: true,
- requires: ['Ext.data.SortTypes']
- }, function() {
- var st = Ext.data.SortTypes;
- Ext.apply(Ext.data.Types, {
- /**
- * @property {RegExp} stripRe
- * A regular expression for stripping non-numeric characters from a numeric value. Defaults to <tt>/[\$,%]/g</tt>.
- * This should be overridden for localization.
- */
- stripRe: /[\$,%]/g,
- /**
- * @property {Object} AUTO
- * This data type means that no conversion is applied to the raw data before it is placed into a Record.
- */
- AUTO: {
- sortType: st.none,
- type: 'auto'
- },
- /**
- * @property {Object} STRING
- * This data type means that the raw data is converted into a String before it is placed into a Record.
- */
- STRING: {
- convert: function(v) {
- var defaultValue = this.useNull ? null : '';
- return (v === undefined || v === null) ? defaultValue : String(v);
- },
- sortType: st.asUCString,
- type: 'string'
- },
- /**
- * @property {Object} INT
- * This data type means that the raw data is converted into an integer before it is placed into a Record.
- * <p>The synonym <code>INTEGER</code> is equivalent.</p>
- */
- INT: {
- convert: function(v) {
- return v !== undefined && v !== null && v !== '' ?
- parseInt(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0);
- },
- sortType: st.none,
- type: 'int'
- },
- /**
- * @property {Object} FLOAT
- * This data type means that the raw data is converted into a number before it is placed into a Record.
- * <p>The synonym <code>NUMBER</code> is equivalent.</p>
- */
- FLOAT: {
- convert: function(v) {
- return v !== undefined && v !== null && v !== '' ?
- parseFloat(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0);
- },
- sortType: st.none,
- type: 'float'
- },
- /**
- * @property {Object} BOOL
- * <p>This data type means that the raw data is converted into a boolean before it is placed into
- * a Record. The string "true" and the number 1 are converted to boolean <code>true</code>.</p>
- * <p>The synonym <code>BOOLEAN</code> is equivalent.</p>
- */
- BOOL: {
- convert: function(v) {
- if (this.useNull && (v === undefined || v === null || v === '')) {
- return null;
- }
- return v === true || v === 'true' || v == 1;
- },
- sortType: st.none,
- type: 'bool'
- },
- /**
- * @property {Object} DATE
- * This data type means that the raw data is converted into a Date before it is placed into a Record.
- * The date format is specified in the constructor of the {@link Ext.data.Field} to which this type is
- * being applied.
- */
- DATE: {
- convert: function(v) {
- var df = this.dateFormat,
- parsed;
- if (!v) {
- return null;
- }
- if (Ext.isDate(v)) {
- return v;
- }
- if (df) {
- if (df == 'timestamp') {
- return new Date(v*1000);
- }
- if (df == 'time') {
- return new Date(parseInt(v, 10));
- }
- return Ext.Date.parse(v, df);
- }
- parsed = Date.parse(v);
- return parsed ? new Date(parsed) : null;
- },
- sortType: st.asDate,
- type: 'date'
- }
- });
- Ext.apply(Ext.data.Types, {
- /**
- * @property {Object} BOOLEAN
- * <p>This data type means that the raw data is converted into a boolean before it is placed into
- * a Record. The string "true" and the number 1 are converted to boolean <code>true</code>.</p>
- * <p>The synonym <code>BOOL</code> is equivalent.</p>
- */
- BOOLEAN: this.BOOL,
- /**
- * @property {Object} INTEGER
- * This data type means that the raw data is converted into an integer before it is placed into a Record.
- * <p>The synonym <code>INT</code> is equivalent.</p>
- */
- INTEGER: this.INT,
- /**
- * @property {Object} NUMBER
- * This data type means that the raw data is converted into a number before it is placed into a Record.
- * <p>The synonym <code>FLOAT</code> is equivalent.</p>
- */
- NUMBER: this.FLOAT
- });
- });
- /**
- * @author Ed Spencer
- *
- * Fields are used to define what a Model is. They aren't instantiated directly - instead, when we create a class that
- * extends {@link Ext.data.Model}, it will automatically create a Field instance for each field configured in a {@link
- * Ext.data.Model Model}. For example, we might set up a model like this:
- *
- * Ext.define('User', {
- * extend: 'Ext.data.Model',
- * fields: [
- * 'name', 'email',
- * {name: 'age', type: 'int'},
- * {name: 'gender', type: 'string', defaultValue: 'Unknown'}
- * ]
- * });
- *
- * Four fields will have been created for the User Model - name, email, age and gender. Note that we specified a couple
- * of different formats here; if we only pass in the string name of the field (as with name and email), the field is set
- * up with the 'auto' type. It's as if we'd done this instead:
- *
- * Ext.define('User', {
- * extend: 'Ext.data.Model',
- * fields: [
- * {name: 'name', type: 'auto'},
- * {name: 'email', type: 'auto'},
- * {name: 'age', type: 'int'},
- * {name: 'gender', type: 'string', defaultValue: 'Unknown'}
- * ]
- * });
- *
- * # Types and conversion
- *
- * The {@link #type} is important - it's used to automatically convert data passed to the field into the correct format.
- * In our example above, the name and email fields used the 'auto' type and will just accept anything that is passed
- * into them. The 'age' field had an 'int' type however, so if we passed 25.4 this would be rounded to 25.
- *
- * Sometimes a simple type isn't enough, or we want to perform some processing when we load a Field's data. We can do
- * this using a {@link #convert} function. Here, we're going to create a new field based on another:
- *
- * Ext.define('User', {
- * extend: 'Ext.data.Model',
- * fields: [
- * 'name', 'email',
- * {name: 'age', type: 'int'},
- * {name: 'gender', type: 'string', defaultValue: 'Unknown'},
- *
- * {
- * name: 'firstName',
- * convert: function(value, record) {
- * var fullName = record.get('name'),
- * splits = fullName.split(" "),
- * firstName = splits[0];
- *
- * return firstName;
- * }
- * }
- * ]
- * });
- *
- * Now when we create a new User, the firstName is populated automatically based on the name:
- *
- * var ed = Ext.create('User', {name: 'Ed Spencer'});
- *
- * console.log(ed.get('firstName')); //logs 'Ed', based on our convert function
- *
- * In fact, if we log out all of the data inside ed, we'll see this:
- *
- * console.log(ed.data);
- *
- * //outputs this:
- * {
- * age: 0,
- * email: "",
- * firstName: "Ed",
- * gender: "Unknown",
- * name: "Ed Spencer"
- * }
- *
- * The age field has been given a default of zero because we made it an int type. As an auto field, email has defaulted
- * to an empty string. When we registered the User model we set gender's {@link #defaultValue} to 'Unknown' so we see
- * that now. Let's correct that and satisfy ourselves that the types work as we expect:
- *
- * ed.set('gender', 'Male');
- * ed.get('gender'); //returns 'Male'
- *
- * ed.set('age', 25.4);
- * ed.get('age'); //returns 25 - we wanted an int, not a float, so no decimal places allowed
- */
- Ext.define('Ext.data.Field', {
- requires: ['Ext.data.Types', 'Ext.data.SortTypes'],
- alias: 'data.field',
-
- constructor : function(config) {
- if (Ext.isString(config)) {
- config = {name: config};
- }
- Ext.apply(this, config);
-
- var types = Ext.data.Types,
- st = this.sortType,
- t;
- if (this.type) {
- if (Ext.isString(this.type)) {
- this.type = types[this.type.toUpperCase()] || types.AUTO;
- }
- } else {
- this.type = types.AUTO;
- }
- // named sortTypes are supported, here we look them up
- if (Ext.isString(st)) {
- this.sortType = Ext.data.SortTypes[st];
- } else if(Ext.isEmpty(st)) {
- this.sortType = this.type.sortType;
- }
- if (!this.convert) {
- this.convert = this.type.convert;
- }
- },
-
- /**
- * @cfg {String} name
- *
- * The name by which the field is referenced within the Model. This is referenced by, for example, the `dataIndex`
- * property in column definition objects passed to {@link Ext.grid.property.HeaderContainer}.
- *
- * Note: In the simplest case, if no properties other than `name` are required, a field definition may consist of
- * just a String for the field name.
- */
-
- /**
- * @cfg {String/Object} type
- *
- * The data type for automatic conversion from received data to the *stored* value if
- * `{@link Ext.data.Field#convert convert}` has not been specified. This may be specified as a string value.
- * Possible values are
- *
- * - auto (Default, implies no conversion)
- * - string
- * - int
- * - float
- * - boolean
- * - date
- *
- * This may also be specified by referencing a member of the {@link Ext.data.Types} class.
- *
- * Developers may create their own application-specific data types by defining new members of the {@link
- * Ext.data.Types} class.
- */
-
- /**
- * @cfg {Function} convert
- *
- * A function which converts the value provided by the Reader into an object that will be stored in the Model.
- * It is passed the following parameters:
- *
- * - **v** : Mixed
- *
- * The data value as read by the Reader, if undefined will use the configured `{@link Ext.data.Field#defaultValue
- * defaultValue}`.
- *
- * - **rec** : Ext.data.Model
- *
- * The data object containing the Model as read so far by the Reader. Note that the Model may not be fully populated
- * at this point as the fields are read in the order that they are defined in your
- * {@link Ext.data.Model#cfg-fields fields} array.
- *
- * Example of convert functions:
- *
- * function fullName(v, record){
- * return record.name.last + ', ' + record.name.first;
- * }
- *
- * function location(v, record){
- * return !record.city ? '' : (record.city + ', ' + record.state);
- * }
- *
- * Ext.define('Dude', {
- * extend: 'Ext.data.Model',
- * fields: [
- * {name: 'fullname', convert: fullName},
- * {name: 'firstname', mapping: 'name.first'},
- * {name: 'lastname', mapping: 'name.last'},
- * {name: 'city', defaultValue: 'homeless'},
- * 'state',
- * {name: 'location', convert: location}
- * ]
- * });
- *
- * // create the data store
- * var store = Ext.create('Ext.data.Store', {
- * reader: {
- * type: 'json',
- * model: 'Dude',
- * idProperty: 'key',
- * root: 'daRoot',
- * totalProperty: 'total'
- * }
- * });
- *
- * var myData = [
- * { key: 1,
- * name: { first: 'Fat', last: 'Albert' }
- * // notice no city, state provided in data object
- * },
- * { key: 2,
- * name: { first: 'Barney', last: 'Rubble' },
- * city: 'Bedrock', state: 'Stoneridge'
- * },
- * { key: 3,
- * name: { first: 'Cliff', last: 'Claven' },
- * city: 'Boston', state: 'MA'
- * }
- * ];
- */
- /**
- * @cfg {String} dateFormat
- *
- * Used when converting received data into a Date when the {@link #type} is specified as `"date"`.
- *
- * A format string for the {@link Ext.Date#parse Ext.Date.parse} function, or "timestamp" if the value provided by
- * the Reader is a UNIX timestamp, or "time" if the value provided by the Reader is a javascript millisecond
- * timestamp. See {@link Ext.Date}.
- */
- dateFormat: null,
-
- /**
- * @cfg {Boolean} useNull
- *
- * Use when converting received data into a Number type (either int or float). If the value cannot be
- * parsed, null will be used if useNull is true, otherwise the value will be 0. Defaults to false.
- */
- useNull: false,
-
- /**
- * @cfg {Object} defaultValue
- *
- * The default value used **when a Model is being created by a {@link Ext.data.reader.Reader Reader}**
- * when the item referenced by the `{@link Ext.data.Field#mapping mapping}` does not exist in the data object
- * (i.e. undefined). Defaults to "".
- */
- defaultValue: "",
- /**
- * @cfg {String/Number} mapping
- *
- * (Optional) A path expression for use by the {@link Ext.data.reader.Reader} implementation that is creating the
- * {@link Ext.data.Model Model} to extract the Field value from the data object. If the path expression is the same
- * as the field name, the mapping may be omitted.
- *
- * The form of the mapping expression depends on the Reader being used.
- *
- * - {@link Ext.data.reader.Json}
- *
- * The mapping is a string containing the javascript expression to reference the data from an element of the data
- * item's {@link Ext.data.reader.Json#root root} Array. Defaults to the field name.
- *
- * - {@link Ext.data.reader.Xml}
- *
- * The mapping is an {@link Ext.DomQuery} path to the data item relative to the DOM element that represents the
- * {@link Ext.data.reader.Xml#record record}. Defaults to the field name.
- *
- * - {@link Ext.data.reader.Array}
- *
- * The mapping is a number indicating the Array index of the field's value. Defaults to the field specification's
- * Array position.
- *
- * If a more complex value extraction strategy is required, then configure the Field with a {@link #convert}
- * function. This is passed the whole row object, and may interrogate it in whatever way is necessary in order to
- * return the desired data.
- */
- mapping: null,
- /**
- * @cfg {Function} sortType
- *
- * A function which converts a Field's value to a comparable value in order to ensure correct sort ordering.
- * Predefined functions are provided in {@link Ext.data.SortTypes}. A custom sort example:
- *
- * // current sort after sort we want
- * // +-+------+ +-+------+
- * // |1|First | |1|First |
- * // |2|Last | |3|Second|
- * // |3|Second| |2|Last |
- * // +-+------+ +-+------+
- *
- * sortType: function(value) {
- * switch (value.toLowerCase()) // native toLowerCase():
- * {
- * case 'first': return 1;
- * case 'second': return 2;
- * default: return 3;
- * }
- * }
- */
- sortType : null,
- /**
- * @cfg {String} sortDir
- *
- * Initial direction to sort (`"ASC"` or `"DESC"`). Defaults to `"ASC"`.
- */
- sortDir : "ASC",
- /**
- * @cfg {Boolean} allowBlank
- * @private
- *
- * Used for validating a {@link Ext.data.Model model}. Defaults to true. An empty value here will cause
- * {@link Ext.data.Model}.{@link Ext.data.Model#isValid isValid} to evaluate to false.
- */
- allowBlank : true,
- /**
- * @cfg {Boolean} persist
- *
- * False to exclude this field from the {@link Ext.data.Model#modified} fields in a model. This will also exclude
- * the field from being written using a {@link Ext.data.writer.Writer}. This option is useful when model fields are
- * used to keep state on the client but do not need to be persisted to the server. Defaults to true.
- */
- persist: true
- });
- /**
- * @class Ext.Ajax
- * @singleton
- * @markdown
- A singleton instance of an {@link Ext.data.Connection}. This class
- is used to communicate with your server side code. It can be used as follows:
- Ext.Ajax.request({
- url: 'page.php',
- params: {
- id: 1
- },
- success: function(response){
- var text = response.responseText;
- // process server response here
- }
- });
- Default options for all requests can be set by changing a property on the Ext.Ajax class:
- Ext.Ajax.timeout = 60000; // 60 seconds
- Any options specified in the request method for the Ajax request will override any
- defaults set on the Ext.Ajax class. In the code sample below, the timeout for the
- request will be 60 seconds.
- Ext.Ajax.timeout = 120000; // 120 seconds
- Ext.Ajax.request({
- url: 'page.aspx',
- timeout: 60000
- });
- In general, this class will be used for all Ajax requests in your application.
- The main reason for creating a separate {@link Ext.data.Connection} is for a
- series of requests that share common settings that are different to all other
- requests in the application.
- */
- Ext.define('Ext.Ajax', {
- extend: 'Ext.data.Connection',
- singleton: true,
- /**
- * @cfg {Object} extraParams @hide
- */
- /**
- * @cfg {Object} defaultHeaders @hide
- */
- /**
- * @cfg {String} method (Optional) @hide
- */
- /**
- * @cfg {Number} timeout (Optional) @hide
- */
- /**
- * @cfg {Boolean} autoAbort (Optional) @hide
- */
- /**
- * @cfg {Boolean} disableCaching (Optional) @hide
- */
- /**
- * @property {Boolean} disableCaching
- * True to add a unique cache-buster param to GET requests. Defaults to true.
- */
- /**
- * @property {String} url
- * The default URL to be used for requests to the server.
- * If the server receives all requests through one URL, setting this once is easier than
- * entering it on every request.
- */
- /**
- * @property {Object} extraParams
- * An object containing properties which are used as extra parameters to each request made
- * by this object. Session information and other data that you need
- * to pass with each request are commonly put here.
- */
- /**
- * @property {Object} defaultHeaders
- * An object containing request headers which are added to each request made by this object.
- */
- /**
- * @property {String} method
- * The default HTTP method to be used for requests. Note that this is case-sensitive and
- * should be all caps (if not set but params are present will use
- * <tt>"POST"</tt>, otherwise will use <tt>"GET"</tt>.)
- */
- /**
- * @property {Number} timeout
- * The timeout in milliseconds to be used for requests. Defaults to 30000.
- */
- /**
- * @property {Boolean} autoAbort
- * Whether a new request should abort any pending requests.
- */
- autoAbort : false
- });
- /**
- * @class Ext.util.AbstractMixedCollection
- * @private
- */
- Ext.define('Ext.util.AbstractMixedCollection', {
- requires: ['Ext.util.Filter'],
- mixins: {
- observable: 'Ext.util.Observable'
- },
- /**
- * @private Mutation counter which is incremented upon add and remove.
- */
- generation: 0,
- constructor: function(allowFunctions, keyFn) {
- var me = this;
- me.items = [];
- me.map = {};
- me.keys = [];
- me.length = 0;
- /**
- * @event clear
- * Fires when the collection is cleared.
- */
- /**
- * @event add
- * Fires when an item is added to the collection.
- * @param {Number} index The index at which the item was added.
- * @param {Object} o The item added.
- * @param {String} key The key associated with the added item.
- */
- /**
- * @event replace
- * Fires when an item is replaced in the collection.
- * @param {String} key he key associated with the new added.
- * @param {Object} old The item being replaced.
- * @param {Object} new The new item.
- */
- /**
- * @event remove
- * Fires when an item is removed from the collection.
- * @param {Object} o The item being removed.
- * @param {String} key (optional) The key associated with the removed item.
- */
- me.allowFunctions = allowFunctions === true;
- if (keyFn) {
- me.getKey = keyFn;
- }
- me.mixins.observable.constructor.call(me);
- },
- /**
- * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
- * function should add function references to the collection. Defaults to
- * <tt>false</tt>.
- */
- allowFunctions : false,
- /**
- * Adds an item to the collection. Fires the {@link #event-add} event when complete.
- * @param {String} key <p>The key to associate with the item, or the new item.</p>
- * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
- * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
- * the MixedCollection will be able to <i>derive</i> the key for the new item.
- * In this case just pass the new item in this parameter.</p>
- * @param {Object} o The item to add.
- * @return {Object} The item added.
- */
- add : function(key, obj){
- var me = this,
- myObj = obj,
- myKey = key,
- old;
- if (arguments.length == 1) {
- myObj = myKey;
- myKey = me.getKey(myObj);
- }
- if (typeof myKey != 'undefined' && myKey !== null) {
- old = me.map[myKey];
- if (typeof old != 'undefined') {
- return me.replace(myKey, myObj);
- }
- me.map[myKey] = myObj;
- }
- me.generation++;
- me.length++;
- me.items.push(myObj);
- me.keys.push(myKey);
- if (me.hasListeners.add) {
- me.fireEvent('add', me.length - 1, myObj, myKey);
- }
- return myObj;
- },
- /**
- * MixedCollection has a generic way to fetch keys if you implement getKey. The default implementation
- * simply returns <b><code>item.id</code></b> but you can provide your own implementation
- * to return a different value as in the following examples:<pre><code>
- // normal way
- var mc = new Ext.util.MixedCollection();
- mc.add(someEl.dom.id, someEl);
- mc.add(otherEl.dom.id, otherEl);
- //and so on
- // using getKey
- var mc = new Ext.util.MixedCollection();
- mc.getKey = function(el){
- return el.dom.id;
- };
- mc.add(someEl);
- mc.add(otherEl);
- // or via the constructor
- var mc = new Ext.util.MixedCollection(false, function(el){
- return el.dom.id;
- });
- mc.add(someEl);
- mc.add(otherEl);
- * </code></pre>
- * @param {Object} item The item for which to find the key.
- * @return {Object} The key for the passed item.
- */
- getKey : function(o){
- return o.id;
- },
- /**
- * Replaces an item in the collection. Fires the {@link #event-replace} event when complete.
- * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
- * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
- * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
- * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
- * with one having the same key value, then just pass the replacement item in this parameter.</p>
- * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
- * with that key.
- * @return {Object} The new item.
- */
- replace : function(key, o){
- var me = this,
- old,
- index;
- if (arguments.length == 1) {
- o = arguments[0];
- key = me.getKey(o);
- }
- old = me.map[key];
- if (typeof key == 'undefined' || key === null || typeof old == 'undefined') {
- return me.add(key, o);
- }
- me.generation++;
- index = me.indexOfKey(key);
- me.items[index] = o;
- me.map[key] = o;
- if (me.hasListeners.replace) {
- me.fireEvent('replace', key, old, o);
- }
- return o;
- },
- /**
- * Adds all elements of an Array or an Object to the collection.
- * @param {Object/Array} objs An Object containing properties which will be added
- * to the collection, or an Array of values, each of which are added to the collection.
- * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
- * has been set to <tt>true</tt>.
- */
- addAll : function(objs){
- var me = this,
- i = 0,
- args,
- len,
- key;
- if (arguments.length > 1 || Ext.isArray(objs)) {
- args = arguments.length > 1 ? arguments : objs;
- for (len = args.length; i < len; i++) {
- me.add(args[i]);
- }
- } else {
- for (key in objs) {
- if (objs.hasOwnProperty(key)) {
- if (me.allowFunctions || typeof objs[key] != 'function') {
- me.add(key, objs[key]);
- }
- }
- }
- }
- },
- /**
- * Executes the specified function once for every item in the collection, passing the following arguments:
- * <div class="mdetail-params"><ul>
- * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
- * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
- * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
- * </ul></div>
- * The function should return a boolean value. Returning false from the function will stop the iteration.
- * @param {Function} fn The function to execute for each item.
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
- */
- each : function(fn, scope){
- var items = [].concat(this.items), // each safe for removal
- i = 0,
- len = items.length,
- item;
- for (; i < len; i++) {
- item = items[i];
- if (fn.call(scope || item, item, i, len) === false) {
- break;
- }
- }
- },
- /**
- * Executes the specified function once for every key in the collection, passing each
- * key, and its associated item as the first two parameters.
- * @param {Function} fn The function to execute for each item.
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
- */
- eachKey : function(fn, scope){
- var keys = this.keys,
- items = this.items,
- i = 0,
- len = keys.length;
- for (; i < len; i++) {
- fn.call(scope || window, keys[i], items[i], i, len);
- }
- },
- /**
- * Returns the first item in the collection which elicits a true return value from the
- * passed selection function.
- * @param {Function} fn The selection function to execute for each item.
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
- * @return {Object} The first item in the collection which returned true from the selection function, or null if none was found
- */
- findBy : function(fn, scope) {
- var keys = this.keys,
- items = this.items,
- i = 0,
- len = items.length;
- for (; i < len; i++) {
- if (fn.call(scope || window, items[i], keys[i])) {
- return items[i];
- }
- }
- return null;
- },
- find : function() {
- if (Ext.isDefined(Ext.global.console)) {
- Ext.global.console.warn('Ext.util.MixedCollection: find has been deprecated. Use findBy instead.');
- }
- return this.findBy.apply(this, arguments);
- },
- /**
- * Inserts an item at the specified index in the collection. Fires the {@link #event-add} event when complete.
- * @param {Number} index The index to insert the item at.
- * @param {String} key The key to associate with the new item, or the item itself.
- * @param {Object} o (optional) If the second parameter was a key, the new item.
- * @return {Object} The item inserted.
- */
- insert : function(index, key, obj){
- var me = this,
- myKey = key,
- myObj = obj;
- if (arguments.length == 2) {
- myObj = myKey;
- myKey = me.getKey(myObj);
- }
- if (me.containsKey(myKey)) {
- me.suspendEvents();
- me.removeAtKey(myKey);
- me.resumeEvents();
- }
- if (index >= me.length) {
- return me.add(myKey, myObj);
- }
- me.generation++;
- me.length++;
- Ext.Array.splice(me.items, index, 0, myObj);
- if (typeof myKey != 'undefined' && myKey !== null) {
- me.map[myKey] = myObj;
- }
- Ext.Array.splice(me.keys, index, 0, myKey);
- if (me.hasListeners.add) {
- me.fireEvent('add', index, myObj, myKey);
- }
- return myObj;
- },
- /**
- * Remove an item from the collection.
- * @param {Object} o The item to remove.
- * @return {Object} The item removed or false if no item was removed.
- */
- remove : function(o) {
- this.generation++;
- return this.removeAt(this.indexOf(o));
- },
- /**
- * Remove all items in the passed array from the collection.
- * @param {Array} items An array of items to be removed.
- * @return {Ext.util.MixedCollection} this object
- */
- removeAll : function(items) {
- items = [].concat(items);
- var i, iLen = items.length;
- for (i = 0; i < iLen; i++) {
- this.remove(items[i]);
- }
- return this;
- },
- /**
- * Remove an item from a specified index in the collection. Fires the {@link #event-remove} event when complete.
- * @param {Number} index The index within the collection of the item to remove.
- * @return {Object} The item removed or false if no item was removed.
- */
- removeAt : function(index) {
- var me = this,
- o,
- key;
- if (index < me.length && index >= 0) {
- me.length--;
- o = me.items[index];
- Ext.Array.erase(me.items, index, 1);
- key = me.keys[index];
- if (typeof key != 'undefined') {
- delete me.map[key];
- }
- Ext.Array.erase(me.keys, index, 1);
- if (me.hasListeners.remove) {
- me.fireEvent('remove', o, key);
- }
- me.generation++;
- return o;
- }
- return false;
- },
- /**
- * Removed an item associated with the passed key fom the collection.
- * @param {String} key The key of the item to remove.
- * @return {Object} The item removed or false if no item was removed.
- */
- removeAtKey : function(key){
- return this.removeAt(this.indexOfKey(key));
- },
- /**
- * Returns the number of items in the collection.
- * @return {Number} the number of items in the collection.
- */
- getCount : function(){
- return this.length;
- },
- /**
- * Returns index within the collection of the passed Object.
- * @param {Object} o The item to find the index of.
- * @return {Number} index of the item. Returns -1 if not found.
- */
- indexOf : function(o){
- return Ext.Array.indexOf(this.items, o);
- },
- /**
- * Returns index within the collection of the passed key.
- * @param {String} key The key to find the index of.
- * @return {Number} index of the key.
- */
- indexOfKey : function(key){
- return Ext.Array.indexOf(this.keys, key);
- },
- /**
- * Returns the item associated with the passed key OR index.
- * Key has priority over index. This is the equivalent
- * of calling {@link #getByKey} first, then if nothing matched calling {@link #getAt}.
- * @param {String/Number} key The key or index of the item.
- * @return {Object} If the item is found, returns the item. If the item was not found, returns <tt>undefined</tt>.
- * If an item was found, but is a Class, returns <tt>null</tt>.
- */
- get : function(key) {
- var me = this,
- mk = me.map[key],
- item = mk !== undefined ? mk : (typeof key == 'number') ? me.items[key] : undefined;
- return typeof item != 'function' || me.allowFunctions ? item : null; // for prototype!
- },
- /**
- * Returns the item at the specified index.
- * @param {Number} index The index of the item.
- * @return {Object} The item at the specified index.
- */
- getAt : function(index) {
- return this.items[index];
- },
- /**
- * Returns the item associated with the passed key.
- * @param {String/Number} key The key of the item.
- * @return {Object} The item associated with the passed key.
- */
- getByKey : function(key) {
- return this.map[key];
- },
- /**
- * Returns true if the collection contains the passed Object as an item.
- * @param {Object} o The Object to look for in the collection.
- * @return {Boolean} True if the collection contains the Object as an item.
- */
- contains : function(o){
- return typeof this.map[this.getKey(o)] != 'undefined';
- },
- /**
- * Returns true if the collection contains the passed Object as a key.
- * @param {String} key The key to look for in the collection.
- * @return {Boolean} True if the collection contains the Object as a key.
- */
- containsKey : function(key){
- return typeof this.map[key] != 'undefined';
- },
- /**
- * Removes all items from the collection. Fires the {@link #event-clear} event when complete.
- */
- clear : function(){
- var me = this;
- me.length = 0;
- me.items = [];
- me.keys = [];
- me.map = {};
- me.generation++;
- if (me.hasListeners.clear) {
- me.fireEvent('clear');
- }
- },
- /**
- * Returns the first item in the collection.
- * @return {Object} the first item in the collection..
- */
- first : function() {
- return this.items[0];
- },
- /**
- * Returns the last item in the collection.
- * @return {Object} the last item in the collection..
- */
- last : function() {
- return this.items[this.length - 1];
- },
- /**
- * Collects all of the values of the given property and returns their sum
- * @param {String} property The property to sum by
- * @param {String} [root] 'root' property to extract the first argument from. This is used mainly when
- * summing fields in records, where the fields are all stored inside the 'data' object
- * @param {Number} [start=0] The record index to start at
- * @param {Number} [end=-1] The record index to end at
- * @return {Number} The total
- */
- sum: function(property, root, start, end) {
- var values = this.extractValues(property, root),
- length = values.length,
- sum = 0,
- i;
- start = start || 0;
- end = (end || end === 0) ? end : length - 1;
- for (i = start; i <= end; i++) {
- sum += values[i];
- }
- return sum;
- },
- /**
- * Collects unique values of a particular property in this MixedCollection
- * @param {String} property The property to collect on
- * @param {String} root (optional) 'root' property to extract the first argument from. This is used mainly when
- * summing fields in records, where the fields are all stored inside the 'data' object
- * @param {Boolean} allowBlank (optional) Pass true to allow null, undefined or empty string values
- * @return {Array} The unique values
- */
- collect: function(property, root, allowNull) {
- var values = this.extractValues(property, root),
- length = values.length,
- hits = {},
- unique = [],
- value, strValue, i;
- for (i = 0; i < length; i++) {
- value = values[i];
- strValue = String(value);
- if ((allowNull || !Ext.isEmpty(value)) && !hits[strValue]) {
- hits[strValue] = true;
- unique.push(value);
- }
- }
- return unique;
- },
- /**
- * @private
- * Extracts all of the given property values from the items in the MC. Mainly used as a supporting method for
- * functions like sum and collect.
- * @param {String} property The property to extract
- * @param {String} root (optional) 'root' property to extract the first argument from. This is used mainly when
- * extracting field data from Model instances, where the fields are stored inside the 'data' object
- * @return {Array} The extracted values
- */
- extractValues: function(property, root) {
- var values = this.items;
- if (root) {
- values = Ext.Array.pluck(values, root);
- }
- return Ext.Array.pluck(values, property);
- },
- /**
- * Returns a range of items in this collection
- * @param {Number} startIndex (optional) The starting index. Defaults to 0.
- * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
- * @return {Array} An array of items
- */
- getRange : function(start, end){
- var me = this,
- items = me.items,
- range = [],
- i;
- if (items.length < 1) {
- return range;
- }
- start = start || 0;
- end = Math.min(typeof end == 'undefined' ? me.length - 1 : end, me.length - 1);
- if (start <= end) {
- for (i = start; i <= end; i++) {
- range[range.length] = items[i];
- }
- } else {
- for (i = start; i >= end; i--) {
- range[range.length] = items[i];
- }
- }
- return range;
- },
- /**
- * <p>Filters the objects in this collection by a set of {@link Ext.util.Filter Filter}s, or by a single
- * property/value pair with optional parameters for substring matching and case sensitivity. See
- * {@link Ext.util.Filter Filter} for an example of using Filter objects (preferred). Alternatively,
- * MixedCollection can be easily filtered by property like this:</p>
- <pre><code>
- //create a simple store with a few people defined
- var people = new Ext.util.MixedCollection();
- people.addAll([
- {id: 1, age: 25, name: 'Ed'},
- {id: 2, age: 24, name: 'Tommy'},
- {id: 3, age: 24, name: 'Arne'},
- {id: 4, age: 26, name: 'Aaron'}
- ]);
- //a new MixedCollection containing only the items where age == 24
- var middleAged = people.filter('age', 24);
- </code></pre>
- *
- *
- * @param {Ext.util.Filter[]/String} property A property on your objects, or an array of {@link Ext.util.Filter Filter} objects
- * @param {String/RegExp} value Either string that the property values
- * should start with or a RegExp to test against the property
- * @param {Boolean} [anyMatch=false] True to match any part of the string, not just the beginning
- * @param {Boolean} [caseSensitive=false] True for case sensitive comparison.
- * @return {Ext.util.MixedCollection} The new filtered collection
- */
- filter : function(property, value, anyMatch, caseSensitive) {
- var filters = [],
- filterFn;
- //support for the simple case of filtering by property/value
- if (Ext.isString(property)) {
- filters.push(new Ext.util.Filter({
- property : property,
- value : value,
- anyMatch : anyMatch,
- caseSensitive: caseSensitive
- }));
- } else if (Ext.isArray(property) || property instanceof Ext.util.Filter) {
- filters = filters.concat(property);
- }
- //at this point we have an array of zero or more Ext.util.Filter objects to filter with,
- //so here we construct a function that combines these filters by ANDing them together
- filterFn = function(record) {
- var isMatch = true,
- length = filters.length,
- i;
- for (i = 0; i < length; i++) {
- var filter = filters[i],
- fn = filter.filterFn,
- scope = filter.scope;
- isMatch = isMatch && fn.call(scope, record);
- }
- return isMatch;
- };
- return this.filterBy(filterFn);
- },
- /**
- * Filter by a function. Returns a <i>new</i> collection that has been filtered.
- * The passed function will be called with each object in the collection.
- * If the function returns true, the value is included otherwise it is filtered.
- * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
- * @return {Ext.util.MixedCollection} The new filtered collection
- */
- filterBy : function(fn, scope) {
- var me = this,
- newMC = new this.self(),
- keys = me.keys,
- items = me.items,
- length = items.length,
- i;
- newMC.getKey = me.getKey;
- for (i = 0; i < length; i++) {
- if (fn.call(scope || me, items[i], keys[i])) {
- newMC.add(keys[i], items[i]);
- }
- }
- return newMC;
- },
- /**
- * Finds the index of the first matching object in this collection by a specific property/value.
- * @param {String} property The name of a property on your objects.
- * @param {String/RegExp} value A string that the property values
- * should start with or a RegExp to test against the property.
- * @param {Number} [start=0] The index to start searching at.
- * @param {Boolean} [anyMatch=false] True to match any part of the string, not just the beginning.
- * @param {Boolean} [caseSensitive=false] True for case sensitive comparison.
- * @return {Number} The matched index or -1
- */
- findIndex : function(property, value, start, anyMatch, caseSensitive){
- if(Ext.isEmpty(value, false)){
- return -1;
- }
- value = this.createValueMatcher(value, anyMatch, caseSensitive);
- return this.findIndexBy(function(o){
- return o && value.test(o[property]);
- }, null, start);
- },
- /**
- * Find the index of the first matching object in this collection by a function.
- * If the function returns <i>true</i> it is considered a match.
- * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
- * @param {Object} [scope] The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
- * @param {Number} [start=0] The index to start searching at.
- * @return {Number} The matched index or -1
- */
- findIndexBy : function(fn, scope, start){
- var me = this,
- keys = me.keys,
- items = me.items,
- i = start || 0,
- len = items.length;
- for (; i < len; i++) {
- if (fn.call(scope || me, items[i], keys[i])) {
- return i;
- }
- }
- return -1;
- },
- /**
- * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering,
- * and by Ext.data.Store#filter
- * @private
- * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe
- * @param {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
- * @param {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
- * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
- */
- createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
- if (!value.exec) { // not a regex
- var er = Ext.String.escapeRegex;
- value = String(value);
- if (anyMatch === true) {
- value = er(value);
- } else {
- value = '^' + er(value);
- if (exactMatch === true) {
- value += '$';
- }
- }
- value = new RegExp(value, caseSensitive ? '' : 'i');
- }
- return value;
- },
- /**
- * Creates a shallow copy of this collection
- * @return {Ext.util.MixedCollection}
- */
- clone : function() {
- var me = this,
- copy = new this.self(),
- keys = me.keys,
- items = me.items,
- i = 0,
- len = items.length;
- for(; i < len; i++){
- copy.add(keys[i], items[i]);
- }
- copy.getKey = me.getKey;
- return copy;
- }
- });
- /**
- * @docauthor Tommy Maintz <tommy@sencha.com>
- *
- * A mixin which allows a data component to be sorted. This is used by e.g. {@link Ext.data.Store} and {@link Ext.data.TreeStore}.
- *
- * **NOTE**: This mixin is mainly for internal use and most users should not need to use it directly. It
- * is more likely you will want to use one of the component classes that import this mixin, such as
- * {@link Ext.data.Store} or {@link Ext.data.TreeStore}.
- */
- Ext.define("Ext.util.Sortable", {
- /**
- * @property {Boolean} isSortable
- * `true` in this class to identify an objact as an instantiated Sortable, or subclass thereof.
- */
- isSortable: true,
- /**
- * @property {String} defaultSortDirection
- * The default sort direction to use if one is not specified.
- */
- defaultSortDirection: "ASC",
- requires: [
- 'Ext.util.Sorter'
- ],
- /**
- * @property {String} sortRoot
- * The property in each item that contains the data to sort.
- */
- /**
- * Performs initialization of this mixin. Component classes using this mixin should call this method during their
- * own initialization.
- */
- initSortable: function() {
- var me = this,
- sorters = me.sorters;
- /**
- * @property {Ext.util.MixedCollection} sorters
- * The collection of {@link Ext.util.Sorter Sorters} currently applied to this Store
- */
- me.sorters = new Ext.util.AbstractMixedCollection(false, function(item) {
- return item.id || item.property;
- });
- if (sorters) {
- me.sorters.addAll(me.decodeSorters(sorters));
- }
- },
- /**
- * Sorts the data in the Store by one or more of its properties. Example usage:
- *
- * //sort by a single field
- * myStore.sort('myField', 'DESC');
- *
- * //sorting by multiple fields
- * myStore.sort([
- * {
- * property : 'age',
- * direction: 'ASC'
- * },
- * {
- * property : 'name',
- * direction: 'DESC'
- * }
- * ]);
- *
- * Internally, Store converts the passed arguments into an array of {@link Ext.util.Sorter} instances, and delegates
- * the actual sorting to its internal {@link Ext.util.MixedCollection}.
- *
- * When passing a single string argument to sort, Store maintains a ASC/DESC toggler per field, so this code:
- *
- * store.sort('myField');
- * store.sort('myField');
- *
- * Is equivalent to this code, because Store handles the toggling automatically:
- *
- * store.sort('myField', 'ASC');
- * store.sort('myField', 'DESC');
- *
- * @param {String/Ext.util.Sorter[]} sorters Either a string name of one of the fields in this Store's configured
- * {@link Ext.data.Model Model}, or an array of sorter configurations.
- * @param {String} direction The overall direction to sort the data by. Defaults to "ASC".
- * @return {Ext.util.Sorter[]}
- */
- sort: function(sorters, direction, where, doSort) {
- var me = this,
- sorter, sorterFn,
- newSorters;
- if (Ext.isArray(sorters)) {
- doSort = where;
- where = direction;
- newSorters = sorters;
- }
- else if (Ext.isObject(sorters)) {
- doSort = where;
- where = direction;
- newSorters = [sorters];
- }
- else if (Ext.isString(sorters)) {
- sorter = me.sorters.get(sorters);
- if (!sorter) {
- sorter = {
- property : sorters,
- direction: direction
- };
- newSorters = [sorter];
- }
- else if (direction === undefined) {
- sorter.toggle();
- }
- else {
- sorter.setDirection(direction);
- }
- }
- if (newSorters && newSorters.length) {
- newSorters = me.decodeSorters(newSorters);
- if (Ext.isString(where)) {
- if (where === 'prepend') {
- sorters = me.sorters.clone().items;
- me.sorters.clear();
- me.sorters.addAll(newSorters);
- me.sorters.addAll(sorters);
- }
- else {
- me.sorters.addAll(newSorters);
- }
- }
- else {
- me.sorters.clear();
- me.sorters.addAll(newSorters);
- }
- }
- if (doSort !== false) {
- me.onBeforeSort(newSorters);
-
- sorters = me.sorters.items;
- if (sorters.length) {
- // Sort using a generated sorter function which combines all of the Sorters passed
- me.doSort(me.generateComparator());
- }
- }
- return sorters;
- },
- /**
- * <p>Returns a comparator function which compares two items and returns -1, 0, or 1 depending
- * on the currently defined set of {@link #sorters}.</p>
- * <p>If there are no {@link #sorters} defined, it returns a function which returns <code>0</code> meaning that no sorting will occur.</p>
- */
- generateComparator: function() {
- return (this.sorters.items.length) ? (function(sorters) {
- return function(r1, r2) {
- var result = sorters[0].sort(r1, r2),
- length = sorters.length,
- i;
- // if we have more than one sorter, OR any additional sorter functions together
- for (i = 1; i < length; i++) {
- result = result || sorters[i].sort.call(this, r1, r2);
- }
- return result;
- };
- })(this.sorters.items) : function() {
- return 0;
- };
- },
- onBeforeSort: Ext.emptyFn,
- /**
- * @private
- * Normalizes an array of sorter objects, ensuring that they are all Ext.util.Sorter instances
- * @param {Object[]} sorters The sorters array
- * @return {Ext.util.Sorter[]} Array of Ext.util.Sorter objects
- */
- decodeSorters: function(sorters) {
- if (!Ext.isArray(sorters)) {
- if (sorters === undefined) {
- sorters = [];
- } else {
- sorters = [sorters];
- }
- }
- var length = sorters.length,
- Sorter = Ext.util.Sorter,
- fields = this.model ? this.model.prototype.fields : null,
- field,
- config, i;
- for (i = 0; i < length; i++) {
- config = sorters[i];
- if (!(config instanceof Sorter)) {
- if (Ext.isString(config)) {
- config = {
- property: config
- };
- }
- Ext.applyIf(config, {
- root : this.sortRoot,
- direction: "ASC"
- });
- //support for 3.x style sorters where a function can be defined as 'fn'
- if (config.fn) {
- config.sorterFn = config.fn;
- }
- //support a function to be passed as a sorter definition
- if (typeof config == 'function') {
- config = {
- sorterFn: config
- };
- }
- // ensure sortType gets pushed on if necessary
- if (fields && !config.transform) {
- field = fields.get(config.property);
- config.transform = field ? field.sortType : undefined;
- }
- sorters[i] = new Ext.util.Sorter(config);
- }
- }
- return sorters;
- },
- getSorters: function() {
- return this.sorters.items;
- }
- });
- /**
- * @class Ext.util.MixedCollection
- * <p>
- * Represents a collection of a set of key and value pairs. Each key in the MixedCollection
- * must be unique, the same key cannot exist twice. This collection is ordered, items in the
- * collection can be accessed by index or via the key. Newly added items are added to
- * the end of the collection. This class is similar to {@link Ext.util.HashMap} however it
- * is heavier and provides more functionality. Sample usage:
- * <pre><code>
- var coll = new Ext.util.MixedCollection();
- coll.add('key1', 'val1');
- coll.add('key2', 'val2');
- coll.add('key3', 'val3');
- console.log(coll.get('key1')); // prints 'val1'
- console.log(coll.indexOfKey('key3')); // prints 2
- * </code></pre>
- *
- * <p>
- * The MixedCollection also has support for sorting and filtering of the values in the collection.
- * <pre><code>
- var coll = new Ext.util.MixedCollection();
- coll.add('key1', 100);
- coll.add('key2', -100);
- coll.add('key3', 17);
- coll.add('key4', 0);
- var biggerThanZero = coll.filterBy(function(value){
- return value > 0;
- });
- console.log(biggerThanZero.getCount()); // prints 2
- * </code></pre>
- * </p>
- */
- Ext.define('Ext.util.MixedCollection', {
- extend: 'Ext.util.AbstractMixedCollection',
- mixins: {
- sortable: 'Ext.util.Sortable'
- },
- /**
- * Creates new MixedCollection.
- * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
- * function should add function references to the collection. Defaults to
- * <tt>false</tt>.
- * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
- * and return the key value for that item. This is used when available to look up the key on items that
- * were passed without an explicit key parameter to a MixedCollection method. Passing this parameter is
- * equivalent to providing an implementation for the {@link #getKey} method.
- */
- constructor: function() {
- var me = this;
- me.callParent(arguments);
- me.addEvents('sort');
- me.mixins.sortable.initSortable.call(me);
- },
- doSort: function(sorterFn) {
- this.sortBy(sorterFn);
- },
- /**
- * @private
- * Performs the actual sorting based on a direction and a sorting function. Internally,
- * this creates a temporary array of all items in the MixedCollection, sorts it and then writes
- * the sorted array data back into this.items and this.keys
- * @param {String} property Property to sort by ('key', 'value', or 'index')
- * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
- * @param {Function} fn (optional) Comparison function that defines the sort order.
- * Defaults to sorting by numeric value.
- */
- _sort : function(property, dir, fn){
- var me = this,
- i, len,
- dsc = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
- //this is a temporary array used to apply the sorting function
- c = [],
- keys = me.keys,
- items = me.items;
- //default to a simple sorter function if one is not provided
- fn = fn || function(a, b) {
- return a - b;
- };
- //copy all the items into a temporary array, which we will sort
- for(i = 0, len = items.length; i < len; i++){
- c[c.length] = {
- key : keys[i],
- value: items[i],
- index: i
- };
- }
- //sort the temporary array
- Ext.Array.sort(c, function(a, b){
- var v = fn(a[property], b[property]) * dsc;
- if(v === 0){
- v = (a.index < b.index ? -1 : 1);
- }
- return v;
- });
- //copy the temporary array back into the main this.items and this.keys objects
- for(i = 0, len = c.length; i < len; i++){
- items[i] = c[i].value;
- keys[i] = c[i].key;
- }
- me.fireEvent('sort', me);
- },
- /**
- * Sorts the collection by a single sorter function
- * @param {Function} sorterFn The function to sort by
- */
- sortBy: function(sorterFn) {
- var me = this,
- items = me.items,
- keys = me.keys,
- length = items.length,
- temp = [],
- i;
- //first we create a copy of the items array so that we can sort it
- for (i = 0; i < length; i++) {
- temp[i] = {
- key : keys[i],
- value: items[i],
- index: i
- };
- }
- Ext.Array.sort(temp, function(a, b) {
- var v = sorterFn(a.value, b.value);
- if (v === 0) {
- v = (a.index < b.index ? -1 : 1);
- }
- return v;
- });
- //copy the temporary array back into the main this.items and this.keys objects
- for (i = 0; i < length; i++) {
- items[i] = temp[i].value;
- keys[i] = temp[i].key;
- }
- me.fireEvent('sort', me, items, keys);
- },
- /**
- * Calculates the insertion index of the new item based upon the comparison function passed, or the current sort order.
- * @param {Object} newItem The new object to find the insertion position of.
- * @param {Function} [sorterFn] The function to sort by. This is the same as the sorting function
- * passed to {@link #sortBy}. It accepts 2 items from this MixedCollection, and returns -1 0, or 1
- * depending on the relative sort positions of the 2 compared items.
- *
- * If omitted, a function {@link #generateComparator generated} from the currently defined set of
- * {@link #sorters} will be used.
- *
- * @return {Number} The insertion point to add the new item into this MixedCollection at using {@link #insert}
- */
- findInsertionIndex: function(newItem, sorterFn) {
- var me = this,
- items = me.items,
- start = 0,
- end = items.length - 1,
- middle,
- comparison;
- if (!sorterFn) {
- sorterFn = me.generateComparator();
- }
- while (start <= end) {
- middle = (start + end) >> 1;
- comparison = sorterFn(newItem, items[middle]);
- if (comparison >= 0) {
- start = middle + 1;
- } else if (comparison < 0) {
- end = middle - 1;
- }
- }
- return start;
- },
- /**
- * Reorders each of the items based on a mapping from old index to new index. Internally this
- * just translates into a sort. The 'sort' event is fired whenever reordering has occured.
- * @param {Object} mapping Mapping from old item index to new item index
- */
- reorder: function(mapping) {
- var me = this,
- items = me.items,
- index = 0,
- length = items.length,
- order = [],
- remaining = [],
- oldIndex;
- me.suspendEvents();
- //object of {oldPosition: newPosition} reversed to {newPosition: oldPosition}
- for (oldIndex in mapping) {
- order[mapping[oldIndex]] = items[oldIndex];
- }
- for (index = 0; index < length; index++) {
- if (mapping[index] == undefined) {
- remaining.push(items[index]);
- }
- }
- for (index = 0; index < length; index++) {
- if (order[index] == undefined) {
- order[index] = remaining.shift();
- }
- }
- me.clear();
- me.addAll(order);
- me.resumeEvents();
- me.fireEvent('sort', me);
- },
- /**
- * Sorts this collection by <b>key</b>s.
- * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
- * @param {Function} fn (optional) Comparison function that defines the sort order.
- * Defaults to sorting by case insensitive string.
- */
- sortByKey : function(dir, fn){
- this._sort('key', dir, fn || function(a, b){
- var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
- return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
- });
- }
- });
- /**
- * @docauthor Evan Trimboli <evan@sencha.com>
- *
- * Contains a collection of all stores that are created that have an identifier. An identifier can be assigned by
- * setting the {@link Ext.data.AbstractStore#storeId storeId} property. When a store is in the StoreManager, it can be
- * referred to via it's identifier:
- *
- * Ext.create('Ext.data.Store', {
- * model: 'SomeModel',
- * storeId: 'myStore'
- * });
- *
- * var store = Ext.data.StoreManager.lookup('myStore');
- *
- * Also note that the {@link #lookup} method is aliased to {@link Ext#getStore} for convenience.
- *
- * If a store is registered with the StoreManager, you can also refer to the store by it's identifier when registering
- * it with any Component that consumes data from a store:
- *
- * Ext.create('Ext.data.Store', {
- * model: 'SomeModel',
- * storeId: 'myStore'
- * });
- *
- * Ext.create('Ext.view.View', {
- * store: 'myStore',
- * // other configuration here
- * });
- *
- */
- Ext.define('Ext.data.StoreManager', {
- extend: 'Ext.util.MixedCollection',
- alternateClassName: ['Ext.StoreMgr', 'Ext.data.StoreMgr', 'Ext.StoreManager'],
- singleton: true,
- uses: ['Ext.data.ArrayStore'],
-
- /**
- * @cfg {Object} listeners
- * @private
- */
- /**
- * Registers one or more Stores with the StoreManager. You do not normally need to register stores manually. Any
- * store initialized with a {@link Ext.data.Store#storeId} will be auto-registered.
- * @param {Ext.data.Store...} stores Any number of Store instances
- */
- register : function() {
- for (var i = 0, s; (s = arguments[i]); i++) {
- this.add(s);
- }
- },
- /**
- * Unregisters one or more Stores with the StoreManager
- * @param {String/Object...} stores Any number of Store instances or ID-s
- */
- unregister : function() {
- for (var i = 0, s; (s = arguments[i]); i++) {
- this.remove(this.lookup(s));
- }
- },
- /**
- * Gets a registered Store by id
- * @param {String/Object} store The id of the Store, or a Store instance, or a store configuration
- * @return {Ext.data.Store}
- */
- lookup : function(store) {
- // handle the case when we are given an array or an array of arrays.
- if (Ext.isArray(store)) {
- var fields = ['field1'],
- expand = !Ext.isArray(store[0]),
- data = store,
- i,
- len;
-
- if(expand){
- data = [];
- for (i = 0, len = store.length; i < len; ++i) {
- data.push([store[i]]);
- }
- } else {
- for(i = 2, len = store[0].length; i <= len; ++i){
- fields.push('field' + i);
- }
- }
- return new Ext.data.ArrayStore({
- data : data,
- fields: fields,
- autoDestroy: true,
- autoCreated: true,
- expanded: expand
- });
- }
-
- if (Ext.isString(store)) {
- // store id
- return this.get(store);
- } else {
- // store instance or store config
- return Ext.data.AbstractStore.create(store);
- }
- },
- // getKey implementation for MixedCollection
- getKey : function(o) {
- return o.storeId;
- }
- }, function() {
- /**
- * Creates a new store for the given id and config, then registers it with the {@link Ext.data.StoreManager Store Mananger}.
- * Sample usage:
- *
- * Ext.regStore('AllUsers', {
- * model: 'User'
- * });
- *
- * // the store can now easily be used throughout the application
- * new Ext.List({
- * store: 'AllUsers',
- * ... other config
- * });
- *
- * @param {String} id The id to set on the new store
- * @param {Object} config The store config
- * @member Ext
- * @method regStore
- */
- Ext.regStore = function(name, config) {
- var store;
- if (Ext.isObject(name)) {
- config = name;
- } else {
- config.storeId = name;
- }
- if (config instanceof Ext.data.Store) {
- store = config;
- } else {
- store = new Ext.data.Store(config);
- }
- return Ext.data.StoreManager.register(store);
- };
- /**
- * Shortcut to {@link Ext.data.StoreManager#lookup}.
- * @member Ext
- * @method getStore
- * @inheritdoc Ext.data.StoreManager#lookup
- */
- Ext.getStore = function(name) {
- return Ext.data.StoreManager.lookup(name);
- };
- });
- /**
- * @author Ed Spencer
- * @class Ext.data.Errors
- *
- * <p>Wraps a collection of validation error responses and provides convenient functions for
- * accessing and errors for specific fields.</p>
- *
- * <p>Usually this class does not need to be instantiated directly - instances are instead created
- * automatically when {@link Ext.data.Model#validate validate} on a model instance:</p>
- *
- <pre><code>
- //validate some existing model instance - in this case it returned 2 failures messages
- var errors = myModel.validate();
- errors.isValid(); //false
- errors.length; //2
- errors.getByField('name'); // [{field: 'name', message: 'must be present'}]
- errors.getByField('title'); // [{field: 'title', message: 'is too short'}]
- </code></pre>
- */
- Ext.define('Ext.data.Errors', {
- extend: 'Ext.util.MixedCollection',
- /**
- * Returns true if there are no errors in the collection
- * @return {Boolean}
- */
- isValid: function() {
- return this.length === 0;
- },
- /**
- * Returns all of the errors for the given field
- * @param {String} fieldName The field to get errors for
- * @return {Object[]} All errors for the given field
- */
- getByField: function(fieldName) {
- var errors = [],
- error, field, i;
- for (i = 0; i < this.length; i++) {
- error = this.items[i];
- if (error.field == fieldName) {
- errors.push(error);
- }
- }
- return errors;
- }
- });
- /**
- * @class Ext.data.writer.Json
- This class is used to write {@link Ext.data.Model} data to the server in a JSON format.
- The {@link #allowSingle} configuration can be set to false to force the records to always be
- encoded in an array, even if there is only a single record being sent.
- * @markdown
- */
- Ext.define('Ext.data.writer.Json', {
- extend: 'Ext.data.writer.Writer',
- alternateClassName: 'Ext.data.JsonWriter',
- alias: 'writer.json',
-
- /**
- * @cfg {String} root The key under which the records in this Writer will be placed. Defaults to <tt>undefined</tt>.
- * Example generated request, using root: 'records':
- <pre><code>
- {'records': [{name: 'my record'}, {name: 'another record'}]}
- </code></pre>
- */
- root: undefined,
-
- /**
- * @cfg {Boolean} encode True to use Ext.encode() on the data before sending. Defaults to <tt>false</tt>.
- * The encode option should only be set to true when a {@link #root} is defined, because the values will be
- * sent as part of the request parameters as opposed to a raw post. The root will be the name of the parameter
- * sent to the server.
- */
- encode: false,
-
- /**
- * @cfg {Boolean} allowSingle False to ensure that records are always wrapped in an array, even if there is only
- * one record being sent. When there is more than one record, they will always be encoded into an array.
- * Defaults to <tt>true</tt>. Example:
- * <pre><code>
- // with allowSingle: true
- "root": {
- "first": "Mark",
- "last": "Corrigan"
- }
- // with allowSingle: false
- "root": [{
- "first": "Mark",
- "last": "Corrigan"
- }]
- * </code></pre>
- */
- allowSingle: true,
-
- //inherit docs
- writeRecords: function(request, data) {
- var root = this.root;
-
- if (this.allowSingle && data.length == 1) {
- // convert to single object format
- data = data[0];
- }
-
- if (this.encode) {
- if (root) {
- // sending as a param, need to encode
- request.params[root] = Ext.encode(data);
- } else {
- Ext.Error.raise('Must specify a root when using encode');
- }
- } else {
- // send as jsonData
- request.jsonData = request.jsonData || {};
- if (root) {
- request.jsonData[root] = data;
- } else {
- request.jsonData = data;
- }
- }
- return request;
- }
- });
- /**
- * @class Ext.state.Manager
- * This is the global state manager. By default all components that are "state aware" check this class
- * for state information if you don't pass them a custom state provider. In order for this class
- * to be useful, it must be initialized with a provider when your application initializes. Example usage:
- <pre><code>
- // in your initialization function
- init : function(){
- Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
- var win = new Window(...);
- win.restoreState();
- }
- </code></pre>
- * This class passes on calls from components to the underlying {@link Ext.state.Provider} so that
- * there is a common interface that can be used without needing to refer to a specific provider instance
- * in every component.
- * @singleton
- * @docauthor Evan Trimboli <evan@sencha.com>
- */
- Ext.define('Ext.state.Manager', {
- singleton: true,
- requires: ['Ext.state.Provider'],
- constructor: function() {
- this.provider = new Ext.state.Provider();
- },
-
-
- /**
- * Configures the default state provider for your application
- * @param {Ext.state.Provider} stateProvider The state provider to set
- */
- setProvider : function(stateProvider){
- this.provider = stateProvider;
- },
- /**
- * Returns the current value for a key
- * @param {String} name The key name
- * @param {Object} defaultValue The default value to return if the key lookup does not match
- * @return {Object} The state data
- */
- get : function(key, defaultValue){
- return this.provider.get(key, defaultValue);
- },
- /**
- * Sets the value for a key
- * @param {String} name The key name
- * @param {Object} value The state data
- */
- set : function(key, value){
- this.provider.set(key, value);
- },
- /**
- * Clears a value from the state
- * @param {String} name The key name
- */
- clear : function(key){
- this.provider.clear(key);
- },
- /**
- * Gets the currently configured state provider
- * @return {Ext.state.Provider} The state provider
- */
- getProvider : function(){
- return this.provider;
- }
- });
- /**
- * @class Ext.state.Stateful
- * A mixin for being able to save the state of an object to an underlying
- * {@link Ext.state.Provider}.
- */
- Ext.define('Ext.state.Stateful', {
- /* Begin Definitions */
- mixins: {
- observable: 'Ext.util.Observable'
- },
- requires: ['Ext.state.Manager'],
- /* End Definitions */
- /**
- * @cfg {Boolean} stateful
- * <p>A flag which causes the object to attempt to restore the state of
- * internal properties from a saved state on startup. The object must have
- * a <code>{@link #stateId}</code> for state to be managed.
- * Auto-generated ids are not guaranteed to be stable across page loads and
- * cannot be relied upon to save and restore the same state for a object.<p>
- * <p>For state saving to work, the state manager's provider must have been
- * set to an implementation of {@link Ext.state.Provider} which overrides the
- * {@link Ext.state.Provider#set set} and {@link Ext.state.Provider#get get}
- * methods to save and recall name/value pairs. A built-in implementation,
- * {@link Ext.state.CookieProvider} is available.</p>
- * <p>To set the state provider for the current page:</p>
- * <pre><code>
- Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
- expires: new Date(new Date().getTime()+(1000*60*60*24*7)), //7 days from now
- }));
- * </code></pre>
- * <p>A stateful object attempts to save state when one of the events
- * listed in the <code>{@link #stateEvents}</code> configuration fires.</p>
- * <p>To save state, a stateful object first serializes its state by
- * calling <b><code>{@link #getState}</code></b>. By default, this function does
- * nothing. The developer must provide an implementation which returns an
- * object hash which represents the restorable state of the object.</p>
- * <p>The value yielded by getState is passed to {@link Ext.state.Manager#set}
- * which uses the configured {@link Ext.state.Provider} to save the object
- * keyed by the <code>{@link #stateId}</code>.</p>
- * <p>During construction, a stateful object attempts to <i>restore</i>
- * its state by calling {@link Ext.state.Manager#get} passing the
- * <code>{@link #stateId}</code></p>
- * <p>The resulting object is passed to <b><code>{@link #applyState}</code></b>.
- * The default implementation of <code>{@link #applyState}</code> simply copies
- * properties into the object, but a developer may override this to support
- * more behaviour.</p>
- * <p>You can perform extra processing on state save and restore by attaching
- * handlers to the {@link #beforestaterestore}, {@link #staterestore},
- * {@link #beforestatesave} and {@link #statesave} events.</p>
- */
- stateful: false,
- /**
- * @cfg {String} stateId
- * The unique id for this object to use for state management purposes.
- * <p>See {@link #stateful} for an explanation of saving and restoring state.</p>
- */
- /**
- * @cfg {String[]} stateEvents
- * <p>An array of events that, when fired, should trigger this object to
- * save its state. Defaults to none. <code>stateEvents</code> may be any type
- * of event supported by this object, including browser or custom events
- * (e.g., <tt>['click', 'customerchange']</tt>).</p>
- * <p>See <code>{@link #stateful}</code> for an explanation of saving and
- * restoring object state.</p>
- */
- /**
- * @cfg {Number} saveDelay
- * A buffer to be applied if many state events are fired within a short period.
- */
- saveDelay: 100,
- constructor: function(config) {
- var me = this;
- config = config || {};
- if (config.stateful !== undefined) {
- me.stateful = config.stateful;
- }
- if (config.saveDelay !== undefined) {
- me.saveDelay = config.saveDelay;
- }
- me.stateId = me.stateId || config.stateId;
- if (!me.stateEvents) {
- me.stateEvents = [];
- }
- if (config.stateEvents) {
- me.stateEvents.concat(config.stateEvents);
- }
- this.addEvents(
- /**
- * @event beforestaterestore
- * Fires before the state of the object is restored. Return false from an event handler to stop the restore.
- * @param {Ext.state.Stateful} this
- * @param {Object} state The hash of state values returned from the StateProvider. If this
- * event is not vetoed, then the state object is passed to <b><tt>applyState</tt></b>. By default,
- * that simply copies property values into this object. The method maybe overriden to
- * provide custom state restoration.
- */
- 'beforestaterestore',
- /**
- * @event staterestore
- * Fires after the state of the object is restored.
- * @param {Ext.state.Stateful} this
- * @param {Object} state The hash of state values returned from the StateProvider. This is passed
- * to <b><tt>applyState</tt></b>. By default, that simply copies property values into this
- * object. The method maybe overriden to provide custom state restoration.
- */
- 'staterestore',
- /**
- * @event beforestatesave
- * Fires before the state of the object is saved to the configured state provider. Return false to stop the save.
- * @param {Ext.state.Stateful} this
- * @param {Object} state The hash of state values. This is determined by calling
- * <b><tt>getState()</tt></b> on the object. This method must be provided by the
- * developer to return whetever representation of state is required, by default, Ext.state.Stateful
- * has a null implementation.
- */
- 'beforestatesave',
- /**
- * @event statesave
- * Fires after the state of the object is saved to the configured state provider.
- * @param {Ext.state.Stateful} this
- * @param {Object} state The hash of state values. This is determined by calling
- * <b><tt>getState()</tt></b> on the object. This method must be provided by the
- * developer to return whetever representation of state is required, by default, Ext.state.Stateful
- * has a null implementation.
- */
- 'statesave'
- );
- me.mixins.observable.constructor.call(me);
- if (me.stateful !== false) {
- me.addStateEvents(me.stateEvents);
- me.initState();
- }
- },
- /**
- * Add events that will trigger the state to be saved. If the first argument is an
- * array, each element of that array is the name of a state event. Otherwise, each
- * argument passed to this method is the name of a state event.
- *
- * @param {String/String[]} events The event name or an array of event names.
- */
- addStateEvents: function (events) {
- var me = this,
- i, event, stateEventsByName;
- if (me.stateful && me.getStateId()) {
- if (typeof events == 'string') {
- events = Array.prototype.slice.call(arguments, 0);
- }
- stateEventsByName = me.stateEventsByName || (me.stateEventsByName = {});
- for (i = events.length; i--; ) {
- event = events[i];
- if (!stateEventsByName[event]) {
- stateEventsByName[event] = 1;
- me.on(event, me.onStateChange, me);
- }
- }
- }
- },
- /**
- * This method is called when any of the {@link #stateEvents} are fired.
- * @private
- */
- onStateChange: function(){
- var me = this,
- delay = me.saveDelay,
- statics, runner;
- if (!me.stateful) {
- return;
- }
- if (delay) {
- if (!me.stateTask) {
- statics = Ext.state.Stateful;
- runner = statics.runner || (statics.runner = new Ext.util.TaskRunner());
- me.stateTask = runner.newTask({
- run: me.saveState,
- scope: me,
- interval: delay,
- repeat: 1
- });
- }
- me.stateTask.start();
- } else {
- me.saveState();
- }
- },
- /**
- * Saves the state of the object to the persistence store.
- */
- saveState: function() {
- var me = this,
- id = me.stateful && me.getStateId(),
- hasListeners = me.hasListeners,
- state;
- if (id) {
- state = me.getState() || {}; //pass along for custom interactions
- if (!hasListeners.beforestatesave || me.fireEvent('beforestatesave', me, state) !== false) {
- Ext.state.Manager.set(id, state);
- if (hasListeners.statesave) {
- me.fireEvent('statesave', me, state);
- }
- }
- }
- },
- /**
- * Gets the current state of the object. By default this function returns null,
- * it should be overridden in subclasses to implement methods for getting the state.
- * @return {Object} The current state
- */
- getState: function(){
- return null;
- },
- /**
- * Applies the state to the object. This should be overridden in subclasses to do
- * more complex state operations. By default it applies the state properties onto
- * the current object.
- * @param {Object} state The state
- */
- applyState: function(state) {
- if (state) {
- Ext.apply(this, state);
- }
- },
- /**
- * Gets the state id for this object.
- * @return {String} The 'stateId' or the implicit 'id' specified by component configuration.
- * @private
- */
- getStateId: function() {
- var me = this;
- return me.stateId || (me.autoGenId ? null : me.id);
- },
- /**
- * Initializes the state of the object upon construction.
- * @private
- */
- initState: function(){
- var me = this,
- id = me.stateful && me.getStateId(),
- hasListeners = me.hasListeners,
- state;
- if (id) {
- state = Ext.state.Manager.get(id);
- if (state) {
- state = Ext.apply({}, state);
- if (!hasListeners.beforestaterestore || me.fireEvent('beforestaterestore', me, state) !== false) {
- me.applyState(state);
- if (hasListeners.staterestore) {
- me.fireEvent('staterestore', me, state);
- }
- }
- }
- }
- },
- /**
- * Conditionally saves a single property from this object to the given state object.
- * The idea is to only save state which has changed from the initial state so that
- * current software settings do not override future software settings. Only those
- * values that are user-changed state should be saved.
- *
- * @param {String} propName The name of the property to save.
- * @param {Object} state The state object in to which to save the property.
- * @param {String} stateName (optional) The name to use for the property in state.
- * @return {Boolean} True if the property was saved, false if not.
- */
- savePropToState: function (propName, state, stateName) {
- var me = this,
- value = me[propName],
- config = me.initialConfig;
- if (me.hasOwnProperty(propName)) {
- if (!config || config[propName] !== value) {
- if (state) {
- state[stateName || propName] = value;
- }
- return true;
- }
- }
- return false;
- },
- /**
- * Gathers additional named properties of the instance and adds their current values
- * to the passed state object.
- * @param {String/String[]} propNames The name (or array of names) of the property to save.
- * @param {Object} state The state object in to which to save the property values.
- * @return {Object} state
- */
- savePropsToState: function (propNames, state) {
- var me = this,
- i, n;
- if (typeof propNames == 'string') {
- me.savePropToState(propNames, state);
- } else {
- for (i = 0, n = propNames.length; i < n; ++i) {
- me.savePropToState(propNames[i], state);
- }
- }
- return state;
- },
- /**
- * Destroys this stateful object.
- */
- destroy: function(){
- var me = this,
- task = me.stateTask;
- if (task) {
- task.destroy();
- me.stateTask = null;
- }
- me.clearListeners();
- }
- });
- /**
- * An abstract base class which provides shared methods for Components across the Sencha product line.
- *
- * Please refer to sub class's documentation
- * @private
- */
- Ext.define('Ext.AbstractComponent', {
- /* Begin Definitions */
- requires: [
- 'Ext.ComponentQuery',
- 'Ext.ComponentManager',
- 'Ext.util.ProtoElement'
- ],
- mixins: {
- observable: 'Ext.util.Observable',
- animate: 'Ext.util.Animate',
- elementCt: 'Ext.util.ElementContainer',
- renderable: 'Ext.util.Renderable',
- state: 'Ext.state.Stateful'
- },
- // The "uses" property specifies class which are used in an instantiated AbstractComponent.
- // They do *not* have to be loaded before this class may be defined - that is what "requires" is for.
- uses: [
- 'Ext.PluginManager',
- 'Ext.Element',
- 'Ext.DomHelper',
- 'Ext.XTemplate',
- 'Ext.ComponentQuery',
- 'Ext.ComponentLoader',
- 'Ext.EventManager',
- 'Ext.layout.Context',
- 'Ext.layout.Layout',
- 'Ext.layout.component.Auto',
- 'Ext.LoadMask',
- 'Ext.ZIndexManager'
- ],
- statics: {
- AUTO_ID: 1000,
- pendingLayouts: null,
- layoutSuspendCount: 0,
- cancelLayout: function(comp) {
- var context = this.runningLayoutContext || this.pendingLayouts;
- if (context) {
- context.cancelComponent(comp);
- }
- },
- flushLayouts: function () {
- var me = this,
- context = me.pendingLayouts;
- if (context && context.invalidQueue.length) {
- me.pendingLayouts = null;
- me.runningLayoutContext = context;
- context.hookMethods({
- runComplete: function () {
- // we need to release the layout queue before running any of the
- // finishedLayout calls because they call afterComponentLayout
- // which can re-enter by calling doLayout/doComponentLayout.
- me.runningLayoutContext = null;
- return this.callParent(); // not "me" here!
- }
- });
- context.run();
- }
- },
- resumeLayouts: function (flush) {
- if (this.layoutSuspendCount && ! --this.layoutSuspendCount) {
- if (flush) {
- this.flushLayouts();
- }
- }
- },
- suspendLayouts: function () {
- ++this.layoutSuspendCount;
- },
- updateLayout: function (comp, defer) {
- var me = this,
- running = me.runningLayoutContext,
- pending;
- if (running) {
- running.queueInvalidate(comp);
- } else {
- pending = me.pendingLayouts || (me.pendingLayouts = new Ext.layout.Context());
- pending.queueInvalidate(comp);
- if (!defer && !me.layoutSuspendCount && !comp.isLayoutSuspended()) {
- me.flushLayouts();
- }
- }
- }
- },
- /* End Definitions */
- /**
- * @property {Boolean} isComponent
- * `true` in this class to identify an objact as an instantiated Component, or subclass thereof.
- */
- isComponent: true,
- /**
- * @private
- */
- getAutoId: function() {
- this.autoGenId = true;
- return ++Ext.AbstractComponent.AUTO_ID;
- },
- deferLayouts: false,
- /**
- * @cfg {String} id
- * The **unique id of this component instance.**
- *
- * It should not be necessary to use this configuration except for singleton objects in your application. Components
- * created with an id may be accessed globally using {@link Ext#getCmp Ext.getCmp}.
- *
- * Instead of using assigned ids, use the {@link #itemId} config, and {@link Ext.ComponentQuery ComponentQuery}
- * which provides selector-based searching for Sencha Components analogous to DOM querying. The {@link
- * Ext.container.Container Container} class contains {@link Ext.container.Container#down shortcut methods} to query
- * its descendant Components by selector.
- *
- * Note that this id will also be used as the element id for the containing HTML element that is rendered to the
- * page for this component. This allows you to write id-based CSS rules to style the specific instance of this
- * component uniquely, and also to select sub-elements using this component's id as the parent.
- *
- * **Note**: to avoid complications imposed by a unique id also see `{@link #itemId}`.
- *
- * **Note**: to access the container of a Component see `{@link #ownerCt}`.
- *
- * Defaults to an {@link #getId auto-assigned id}.
- */
- /**
- * @property {Boolean} autoGenId
- * `true` indicates an id was auto-generated rather than provided by configuration.
- * @private
- */
- autoGenId: false,
- /**
- * @cfg {String} itemId
- * An itemId can be used as an alternative way to get a reference to a component when no object reference is
- * available. Instead of using an `{@link #id}` with {@link Ext}.{@link Ext#getCmp getCmp}, use `itemId` with
- * {@link Ext.container.Container}.{@link Ext.container.Container#getComponent getComponent} which will retrieve
- * `itemId`'s or {@link #id}'s. Since `itemId`'s are an index to the container's internal MixedCollection, the
- * `itemId` is scoped locally to the container -- avoiding potential conflicts with {@link Ext.ComponentManager}
- * which requires a **unique** `{@link #id}`.
- *
- * var c = new Ext.panel.Panel({ //
- * {@link Ext.Component#height height}: 300,
- * {@link #renderTo}: document.body,
- * {@link Ext.container.Container#layout layout}: 'auto',
- * {@link Ext.container.Container#cfg-items items}: [
- * {
- * itemId: 'p1',
- * {@link Ext.panel.Panel#title title}: 'Panel 1',
- * {@link Ext.Component#height height}: 150
- * },
- * {
- * itemId: 'p2',
- * {@link Ext.panel.Panel#title title}: 'Panel 2',
- * {@link Ext.Component#height height}: 150
- * }
- * ]
- * })
- * p1 = c.{@link Ext.container.Container#getComponent getComponent}('p1'); // not the same as {@link Ext#getCmp Ext.getCmp()}
- * p2 = p1.{@link #ownerCt}.{@link Ext.container.Container#getComponent getComponent}('p2'); // reference via a sibling
- *
- * Also see {@link #id}, `{@link Ext.container.Container#query}`, `{@link Ext.container.Container#down}` and
- * `{@link Ext.container.Container#child}`.
- *
- * **Note**: to access the container of an item see {@link #ownerCt}.
- */
- /**
- * @property {Ext.Container} ownerCt
- * This Component's owner {@link Ext.container.Container Container} (is set automatically
- * when this Component is added to a Container).
- *
- * **Note**: to access items within the Container see {@link #itemId}.
- * @readonly
- */
- /**
- * @cfg {String/Object} autoEl
- * A tag name or {@link Ext.DomHelper DomHelper} spec used to create the {@link #getEl Element} which will
- * encapsulate this Component.
- *
- * You do not normally need to specify this. For the base classes {@link Ext.Component} and
- * {@link Ext.container.Container}, this defaults to **'div'**. The more complex Sencha classes use a more
- * complex DOM structure specified by their own {@link #renderTpl}s.
- *
- * This is intended to allow the developer to create application-specific utility Components encapsulated by
- * different DOM elements. Example usage:
- *
- * {
- * xtype: 'component',
- * autoEl: {
- * tag: 'img',
- * src: 'http://www.example.com/example.jpg'
- * }
- * }, {
- * xtype: 'component',
- * autoEl: {
- * tag: 'blockquote',
- * html: 'autoEl is cool!'
- * }
- * }, {
- * xtype: 'container',
- * autoEl: 'ul',
- * cls: 'ux-unordered-list',
- * items: {
- * xtype: 'component',
- * autoEl: 'li',
- * html: 'First list item'
- * }
- * }
- */
- /**
- * @cfg {Ext.XTemplate/String/String[]} renderTpl
- * An {@link Ext.XTemplate XTemplate} used to create the internal structure inside this Component's encapsulating
- * {@link #getEl Element}.
- *
- * You do not normally need to specify this. For the base classes {@link Ext.Component} and
- * {@link Ext.container.Container}, this defaults to **`null`** which means that they will be initially rendered
- * with no internal structure; they render their {@link #getEl Element} empty. The more specialized ExtJS and Touch
- * classes which use a more complex DOM structure, provide their own template definitions.
- *
- * This is intended to allow the developer to create application-specific utility Components with customized
- * internal structure.
- *
- * Upon rendering, any created child elements may be automatically imported into object properties using the
- * {@link #renderSelectors} and {@link #childEls} options.
- * @protected
- */
- renderTpl: '{%this.renderContent(out,values)%}',
- /**
- * @cfg {Object} renderData
- *
- * The data used by {@link #renderTpl} in addition to the following property values of the component:
- *
- * - id
- * - ui
- * - uiCls
- * - baseCls
- * - componentCls
- * - frame
- *
- * See {@link #renderSelectors} and {@link #childEls} for usage examples.
- */
- /**
- * @cfg {Object} renderSelectors
- * An object containing properties specifying {@link Ext.DomQuery DomQuery} selectors which identify child elements
- * created by the render process.
- *
- * After the Component's internal structure is rendered according to the {@link #renderTpl}, this object is iterated through,
- * and the found Elements are added as properties to the Component using the `renderSelector` property name.
- *
- * For example, a Component which renderes a title and description into its element:
- *
- * Ext.create('Ext.Component', {
- * renderTo: Ext.getBody(),
- * renderTpl: [
- * '<h1 class="title">{title}</h1>',
- * '<p>{desc}</p>'
- * ],
- * renderData: {
- * title: "Error",
- * desc: "Something went wrong"
- * },
- * renderSelectors: {
- * titleEl: 'h1.title',
- * descEl: 'p'
- * },
- * listeners: {
- * afterrender: function(cmp){
- * // After rendering the component will have a titleEl and descEl properties
- * cmp.titleEl.setStyle({color: "red"});
- * }
- * }
- * });
- *
- * For a faster, but less flexible, alternative that achieves the same end result (properties for child elements on the
- * Component after render), see {@link #childEls} and {@link #addChildEls}.
- */
- /**
- * @cfg {Object[]} childEls
- * An array describing the child elements of the Component. Each member of the array
- * is an object with these properties:
- *
- * - `name` - The property name on the Component for the child element.
- * - `itemId` - The id to combine with the Component's id that is the id of the child element.
- * - `id` - The id of the child element.
- *
- * If the array member is a string, it is equivalent to `{ name: m, itemId: m }`.
- *
- * For example, a Component which renders a title and body text:
- *
- * Ext.create('Ext.Component', {
- * renderTo: Ext.getBody(),
- * renderTpl: [
- * '<h1 id="{id}-title">{title}</h1>',
- * '<p>{msg}</p>',
- * ],
- * renderData: {
- * title: "Error",
- * msg: "Something went wrong"
- * },
- * childEls: ["title"],
- * listeners: {
- * afterrender: function(cmp){
- * // After rendering the component will have a title property
- * cmp.title.setStyle({color: "red"});
- * }
- * }
- * });
- *
- * A more flexible, but somewhat slower, approach is {@link #renderSelectors}.
- */
- /**
- * @cfg {String/HTMLElement/Ext.Element} renderTo
- * Specify the id of the element, a DOM element or an existing Element that this component will be rendered into.
- *
- * **Notes:**
- *
- * Do *not* use this option if the Component is to be a child item of a {@link Ext.container.Container Container}.
- * It is the responsibility of the {@link Ext.container.Container Container}'s
- * {@link Ext.container.Container#layout layout manager} to render and manage its child items.
- *
- * When using this config, a call to render() is not required.
- *
- * See `{@link #render}` also.
- */
- /**
- * @cfg {Boolean} frame
- * Specify as `true` to have the Component inject framing elements within the Component at render time to provide a
- * graphical rounded frame around the Component content.
- *
- * This is only necessary when running on outdated, or non standard-compliant browsers such as Microsoft's Internet
- * Explorer prior to version 9 which do not support rounded corners natively.
- *
- * The extra space taken up by this framing is available from the read only property {@link #frameSize}.
- */
- /**
- * @property {Object} frameSize
- * @readonly
- * Indicates the width of any framing elements which were added within the encapsulating element
- * to provide graphical, rounded borders. See the {@link #frame} config.
- *
- * This is an object containing the frame width in pixels for all four sides of the Component containing the
- * following properties:
- *
- * @property {Number} frameSize.top The width of the top framing element in pixels.
- * @property {Number} frameSize.right The width of the right framing element in pixels.
- * @property {Number} frameSize.bottom The width of the bottom framing element in pixels.
- * @property {Number} frameSize.left The width of the left framing element in pixels.
- */
- /**
- * @cfg {String/Object} componentLayout
- * The sizing and positioning of a Component's internal Elements is the responsibility of the Component's layout
- * manager which sizes a Component's internal structure in response to the Component being sized.
- *
- * Generally, developers will not use this configuration as all provided Components which need their internal
- * elements sizing (Such as {@link Ext.form.field.Base input fields}) come with their own componentLayout managers.
- *
- * The {@link Ext.layout.container.Auto default layout manager} will be used on instances of the base Ext.Component
- * class which simply sizes the Component's encapsulating element to the height and width specified in the
- * {@link #setSize} method.
- */
- /**
- * @cfg {Ext.XTemplate/Ext.Template/String/String[]} tpl
- * An {@link Ext.Template}, {@link Ext.XTemplate} or an array of strings to form an Ext.XTemplate. Used in
- * conjunction with the `{@link #data}` and `{@link #tplWriteMode}` configurations.
- */
- /**
- * @cfg {Object} data
- * The initial set of data to apply to the `{@link #tpl}` to update the content area of the Component.
- */
- /**
- * @cfg {String} xtype
- * This property provides a shorter alternative to creating objects than using a full
- * class name. Using `xtype` is the most common way to define component instances,
- * especially in a container. For example, the items in a form containing text fields
- * could be created explicitly like so:
- *
- * items: [
- * Ext.create('Ext.form.field.Text', {
- * fieldLabel: 'Foo'
- * }),
- * Ext.create('Ext.form.field.Text', {
- * fieldLabel: 'Bar'
- * }),
- * Ext.create('Ext.form.field.Number', {
- * fieldLabel: 'Num'
- * })
- * ]
- *
- * But by using `xtype`, the above becomes:
- *
- * items: [
- * {
- * xtype: 'textfield',
- * fieldLabel: 'Foo'
- * },
- * {
- * xtype: 'textfield',
- * fieldLabel: 'Bar'
- * },
- * {
- * xtype: 'numberfield',
- * fieldLabel: 'Num'
- * }
- * ]
- *
- * When the `xtype` is common to many items, {@link Ext.container.AbstractContainer#defaultType}
- * is another way to specify the `xtype` for all items that don't have an explicit `xtype`:
- *
- * defaultType: 'textfield',
- * items: [
- * { fieldLabel: 'Foo' },
- * { fieldLabel: 'Bar' },
- * { fieldLabel: 'Num', xtype: 'numberfield' }
- * ]
- *
- * Each member of the `items` array is now just a "configuration object". These objects
- * are used to create and configure component instances. A configuration object can be
- * manually used to instantiate a component using {@link Ext#widget}:
- *
- * var text1 = Ext.create('Ext.form.field.Text', {
- * fieldLabel: 'Foo'
- * });
- *
- * // or alternatively:
- *
- * var text1 = Ext.widget({
- * xtype: 'textfield',
- * fieldLabel: 'Foo'
- * });
- *
- * This conversion of configuration objects into instantiated components is done when
- * a container is created as part of its {Ext.container.AbstractContainer#initComponent}
- * process. As part of the same process, the `items` array is converted from its raw
- * array form into a {@link Ext.util.MixedCollection} instance.
- *
- * You can define your own `xtype` on a custom {@link Ext.Component component} by specifying
- * the `xtype` property in {@link Ext#define}. For example:
- *
- * Ext.define('MyApp.PressMeButton', {
- * extend: 'Ext.button.Button',
- * xtype: 'pressmebutton',
- * text: 'Press Me'
- * });
- *
- * Care should be taken when naming an `xtype` in a custom component because there is
- * a single, shared scope for all xtypes. Third part components should consider using
- * a prefix to avoid collisions.
- *
- * Ext.define('Foo.form.CoolButton', {
- * extend: 'Ext.button.Button',
- * xtype: 'ux-coolbutton',
- * text: 'Cool!'
- * });
- */
- /**
- * @cfg {String} tplWriteMode
- * The Ext.(X)Template method to use when updating the content area of the Component.
- * See `{@link Ext.XTemplate#overwrite}` for information on default mode.
- */
- tplWriteMode: 'overwrite',
- /**
- * @cfg {String} [baseCls='x-component']
- * The base CSS class to apply to this components's element. This will also be prepended to elements within this
- * component like Panel's body will get a class x-panel-body. This means that if you create a subclass of Panel, and
- * you want it to get all the Panels styling for the element and the body, you leave the baseCls x-panel and use
- * componentCls to add specific styling for this component.
- */
- baseCls: Ext.baseCSSPrefix + 'component',
- /**
- * @cfg {String} componentCls
- * CSS Class to be added to a components root level element to give distinction to it via styling.
- */
- /**
- * @cfg {String} [cls='']
- * An optional extra CSS class that will be added to this component's Element. This can be useful
- * for adding customized styles to the component or any of its children using standard CSS rules.
- */
- /**
- * @cfg {String} [overCls='']
- * An optional extra CSS class that will be added to this component's Element when the mouse moves over the Element,
- * and removed when the mouse moves out. This can be useful for adding customized 'active' or 'hover' styles to the
- * component or any of its children using standard CSS rules.
- */
- /**
- * @cfg {String} [disabledCls='x-item-disabled']
- * CSS class to add when the Component is disabled. Defaults to 'x-item-disabled'.
- */
- disabledCls: Ext.baseCSSPrefix + 'item-disabled',
- /**
- * @cfg {String/String[]} ui
- * A set style for a component. Can be a string or an Array of multiple strings (UIs)
- */
- ui: 'default',
- /**
- * @cfg {String[]} uiCls
- * An array of of classNames which are currently applied to this component
- * @private
- */
- uiCls: [],
- /**
- * @cfg {String/Object} style
- * A custom style specification to be applied to this component's Element. Should be a valid argument to
- * {@link Ext.Element#applyStyles}.
- *
- * new Ext.panel.Panel({
- * title: 'Some Title',
- * renderTo: Ext.getBody(),
- * width: 400, height: 300,
- * layout: 'form',
- * items: [{
- * xtype: 'textarea',
- * style: {
- * width: '95%',
- * marginBottom: '10px'
- * }
- * },
- * new Ext.button.Button({
- * text: 'Send',
- * minWidth: '100',
- * style: {
- * marginBottom: '10px'
- * }
- * })
- * ]
- * });
- */
- /**
- * @cfg {Number} width
- * The width of this component in pixels.
- */
- /**
- * @cfg {Number} height
- * The height of this component in pixels.
- */
- /**
- * @cfg {Number/String} border
- * Specifies the border for this component. The border can be a single numeric value to apply to all sides or it can
- * be a CSS style specification for each style, for example: '10 5 3 10'.
- */
- /**
- * @cfg {Number/String} padding
- * Specifies the padding for this component. The padding can be a single numeric value to apply to all sides or it
- * can be a CSS style specification for each style, for example: '10 5 3 10'.
- */
- /**
- * @cfg {Number/String} margin
- * Specifies the margin for this component. The margin can be a single numeric value to apply to all sides or it can
- * be a CSS style specification for each style, for example: '10 5 3 10'.
- */
- /**
- * @cfg {Boolean} hidden
- * True to hide the component.
- */
- hidden: false,
- /**
- * @cfg {Boolean} disabled
- * True to disable the component.
- */
- disabled: false,
- /**
- * @cfg {Boolean} [draggable=false]
- * Allows the component to be dragged.
- */
- /**
- * @property {Boolean} draggable
- * Indicates whether or not the component can be dragged.
- * @readonly
- */
- draggable: false,
- /**
- * @cfg {Boolean} floating
- * Create the Component as a floating and use absolute positioning.
- *
- * 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
- * by the global {@link Ext.WindowManager WindowManager}.
- *
- * 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
- * ZIndexManager instance to manage its descendant floaters. If no floating ancestor can be found, the global WindowManager will be used.
- *
- * When a floating Component which has a ZindexManager managing descendant floaters is destroyed, those descendant floaters will also be destroyed.
- */
- floating: false,
- /**
- * @cfg {String} hideMode
- * A String which specifies how this Component's encapsulating DOM element will be hidden. Values may be:
- *
- * - `'display'` : The Component will be hidden using the `display: none` style.
- * - `'visibility'` : The Component will be hidden using the `visibility: hidden` style.
- * - `'offsets'` : The Component will be hidden by absolutely positioning it out of the visible area of the document.
- * This is useful when a hidden Component must maintain measurable dimensions. Hiding using `display` results in a
- * Component having zero dimensions.
- */
- hideMode: 'display',
- /**
- * @cfg {String} contentEl
- * Specify an existing HTML element, or the `id` of an existing HTML element to use as the content for this component.
- *
- * This config option is used to take an existing HTML element and place it in the layout element of a new component
- * (it simply moves the specified DOM element _after the Component is rendered_ to use as the content.
- *
- * **Notes:**
- *
- * The specified HTML element is appended to the layout element of the component _after any configured
- * {@link #html HTML} has been inserted_, and so the document will not contain this element at the time
- * the {@link #render} event is fired.
- *
- * The specified HTML element used will not participate in any **`{@link Ext.container.Container#layout layout}`**
- * scheme that the Component may use. It is just HTML. Layouts operate on child
- * **`{@link Ext.container.Container#cfg-items items}`**.
- *
- * Add either the `x-hidden` or the `x-hide-display` CSS class to prevent a brief flicker of the content before it
- * is rendered to the panel.
- */
- /**
- * @cfg {String/Object} [html='']
- * An HTML fragment, or a {@link Ext.DomHelper DomHelper} specification to use as the layout element content.
- * The HTML content is added after the component is rendered, so the document will not contain this HTML at the time
- * the {@link #render} event is fired. This content is inserted into the body _before_ any configured {@link #contentEl}
- * is appended.
- */
- /**
- * @cfg {Boolean} styleHtmlContent
- * True to automatically style the html inside the content target of this component (body for panels).
- */
- styleHtmlContent: false,
- /**
- * @cfg {String} [styleHtmlCls='x-html']
- * The class that is added to the content target when you set styleHtmlContent to true.
- */
- styleHtmlCls: Ext.baseCSSPrefix + 'html',
- /**
- * @cfg {Number} minHeight
- * The minimum value in pixels which this Component will set its height to.
- *
- * **Warning:** This will override any size management applied by layout managers.
- */
- /**
- * @cfg {Number} minWidth
- * The minimum value in pixels which this Component will set its width to.
- *
- * **Warning:** This will override any size management applied by layout managers.
- */
- /**
- * @cfg {Number} maxHeight
- * The maximum value in pixels which this Component will set its height to.
- *
- * **Warning:** This will override any size management applied by layout managers.
- */
- /**
- * @cfg {Number} maxWidth
- * The maximum value in pixels which this Component will set its width to.
- *
- * **Warning:** This will override any size management applied by layout managers.
- */
- /**
- * @cfg {Ext.ComponentLoader/Object} loader
- * A configuration object or an instance of a {@link Ext.ComponentLoader} to load remote content for this Component.
- */
- /**
- * @cfg {Boolean} autoShow
- * True to automatically show the component upon creation. This config option may only be used for
- * {@link #floating} components or components that use {@link #autoRender}. Defaults to false.
- */
- autoShow: false,
- /**
- * @cfg {Boolean/String/HTMLElement/Ext.Element} autoRender
- * This config is intended mainly for non-{@link #floating} Components which may or may not be shown. Instead of using
- * {@link #renderTo} in the configuration, and rendering upon construction, this allows a Component to render itself
- * upon first _{@link #method-show}_. If {@link #floating} is true, the value of this config is omited as if it is `true`.
- *
- * Specify as `true` to have this Component render to the document body upon first show.
- *
- * Specify as an element, or the ID of an element to have this Component render to a specific element upon first
- * show.
- */
- autoRender: false,
- // @private
- allowDomMove: true,
- /**
- * @cfg {Object/Object[]} plugins
- * An object or array of objects that will provide custom functionality for this component. The only requirement for
- * a valid plugin is that it contain an init method that accepts a reference of type Ext.Component. When a component
- * is created, if any plugins are available, the component will call the init method on each plugin, passing a
- * reference to itself. Each plugin can then call methods or respond to events on the component as needed to provide
- * its functionality.
- */
- /**
- * @property {Boolean} rendered
- * Indicates whether or not the component has been rendered.
- * @readonly
- */
- rendered: false,
- /**
- * @property {Number} componentLayoutCounter
- * @private
- * The number of component layout calls made on this object.
- */
- componentLayoutCounter: 0,
- /**
- * @cfg {Boolean/Number} [shrinkWrap=2]
- *
- * If this property is a number, it is interpreted as follows:
- *
- * - 0: Neither width nor height depend on content. This is equivalent to `false`.
- * - 1: Width depends on content (shrink wraps), but height does not.
- * - 2: Height depends on content (shrink wraps), but width does not. The default.
- * - 3: Both width and height depend on content (shrink wrap). This is equivalent to `true`.
- *
- * In CSS terms, shrink-wrap width is analogous to an inline-block element as opposed
- * to a block-level element. Some container layouts always shrink-wrap their children,
- * effectively ignoring this property (e.g., {@link Ext.layout.container.HBox},
- * {@link Ext.layout.container.VBox}, {@link Ext.layout.component.Dock}).
- */
- shrinkWrap: 2,
- weight: 0,
- /**
- * @property {Boolean} maskOnDisable
- * This is an internal flag that you use when creating custom components. By default this is set to true which means
- * that every component gets a mask when its disabled. Components like FieldContainer, FieldSet, Field, Button, Tab
- * override this property to false since they want to implement custom disable logic.
- */
- maskOnDisable: true,
- /**
- * @property {Boolean} [_isLayoutRoot=false]
- * Setting this property to `true` causes the {@link #isLayoutRoot} method to return
- * `true` and stop the search for the top-most component for a layout.
- * @protected
- */
- _isLayoutRoot: false,
- /**
- * Creates new Component.
- * @param {Object} config (optional) Config object.
- */
- constructor : function(config) {
- var me = this,
- i, len, xhooks;
- if (config) {
- Ext.apply(me, config);
- xhooks = me.xhooks;
- if (xhooks) {
- me.hookMethods(xhooks);
- delete me.xhooks;
- }
- } else {
- config = {};
- }
- me.initialConfig = config;
- me.mixins.elementCt.constructor.call(me);
- me.addEvents(
- /**
- * @event beforeactivate
- * Fires before a Component has been visually activated. Returning false from an event listener can prevent
- * the activate from occurring.
- * @param {Ext.Component} this
- */
- 'beforeactivate',
- /**
- * @event activate
- * Fires after a Component has been visually activated.
- * @param {Ext.Component} this
- */
- 'activate',
- /**
- * @event beforedeactivate
- * Fires before a Component has been visually deactivated. Returning false from an event listener can
- * prevent the deactivate from occurring.
- * @param {Ext.Component} this
- */
- 'beforedeactivate',
- /**
- * @event deactivate
- * Fires after a Component has been visually deactivated.
- * @param {Ext.Component} this
- */
- 'deactivate',
- /**
- * @event added
- * Fires after a Component had been added to a Container.
- * @param {Ext.Component} this
- * @param {Ext.container.Container} container Parent Container
- * @param {Number} pos position of Component
- */
- 'added',
- /**
- * @event disable
- * Fires after the component is disabled.
- * @param {Ext.Component} this
- */
- 'disable',
- /**
- * @event enable
- * Fires after the component is enabled.
- * @param {Ext.Component} this
- */
- 'enable',
- /**
- * @event beforeshow
- * Fires before the component is shown when calling the {@link #show} method. Return false from an event
- * handler to stop the show.
- * @param {Ext.Component} this
- */
- 'beforeshow',
- /**
- * @event show
- * Fires after the component is shown when calling the {@link #show} method.
- * @param {Ext.Component} this
- */
- 'show',
- /**
- * @event beforehide
- * Fires before the component is hidden when calling the {@link #hide} method. Return false from an event
- * handler to stop the hide.
- * @param {Ext.Component} this
- */
- 'beforehide',
- /**
- * @event hide
- * Fires after the component is hidden. Fires after the component is hidden when calling the {@link #hide}
- * method.
- * @param {Ext.Component} this
- */
- 'hide',
- /**
- * @event removed
- * Fires when a component is removed from an Ext.container.Container
- * @param {Ext.Component} this
- * @param {Ext.container.Container} ownerCt Container which holds the component
- */
- 'removed',
- /**
- * @event beforerender
- * Fires before the component is {@link #rendered}. Return false from an event handler to stop the
- * {@link #render}.
- * @param {Ext.Component} this
- */
- 'beforerender',
- /**
- * @event render
- * Fires after the component markup is {@link #rendered}.
- * @param {Ext.Component} this
- */
- 'render',
- /**
- * @event afterrender
- * Fires after the component rendering is finished.
- *
- * The afterrender event is fired after this Component has been {@link #rendered}, been postprocesed by any
- * afterRender method defined for the Component.
- * @param {Ext.Component} this
- */
- 'afterrender',
- /**
- * @event beforedestroy
- * Fires before the component is {@link #method-destroy}ed. Return false from an event handler to stop the
- * {@link #method-destroy}.
- * @param {Ext.Component} this
- */
- 'beforedestroy',
- /**
- * @event destroy
- * Fires after the component is {@link #method-destroy}ed.
- * @param {Ext.Component} this
- */
- 'destroy',
- /**
- * @event resize
- * Fires after the component is resized.
- * @param {Ext.Component} this
- * @param {Number} width The new width that was set
- * @param {Number} height The new height that was set
- * @param {Number} oldWidth The previous width
- * @param {Number} oldHeight The previous height
- */
- 'resize',
- /**
- * @event move
- * Fires after the component is moved.
- * @param {Ext.Component} this
- * @param {Number} x The new x position
- * @param {Number} y The new y position
- */
- 'move',
- /**
- * @event focus
- * Fires when this Component receives focus.
- * @param {Ext.Component} this
- * @param {Ext.EventObject} The focus event.
- */
- 'focus',
- /**
- * @event blur
- * Fires when this Component loses focus.
- * @param {Ext.Component} this
- * @param {Ext.EventObject} The blur event.
- */
- 'blur'
- );
- me.getId();
- me.setupProtoEl();
- // initComponent, beforeRender, or event handlers may have set the style or cls property since the protoEl was set up
- // so we must apply styles and classes here too.
- if (me.cls) {
- me.initialCls = me.cls;
- me.protoEl.addCls(me.cls);
- }
- if (me.style) {
- me.initialStyle = me.style;
- me.protoEl.setStyle(me.style);
- }
- me.mons = [];
- me.renderData = me.renderData || {};
- me.renderSelectors = me.renderSelectors || {};
- if (me.plugins) {
- me.plugins = [].concat(me.plugins);
- me.constructPlugins();
- }
- // Hash of event "hasListeners" flags.
- // For repeated events in time-critical code, the firing code should use
- // if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) { //code... }
- // Bubbling the events counts as one listener. initComponent may add listeners, so needs setting up now.
- me.hasListeners = {};
- me.initComponent();
- // ititComponent gets a chance to change the id property before registering
- Ext.ComponentManager.register(me);
- // Dont pass the config so that it is not applied to 'this' again
- me.mixins.observable.constructor.call(me);
- me.mixins.state.constructor.call(me, config);
- // Save state on resize.
- this.addStateEvents('resize');
- // Move this into Observable?
- if (me.plugins) {
- me.plugins = [].concat(me.plugins);
- for (i = 0, len = me.plugins.length; i < len; i++) {
- me.plugins[i] = me.initPlugin(me.plugins[i]);
- }
- }
- me.loader = me.getLoader();
- if (me.renderTo) {
- me.render(me.renderTo);
- // EXTJSIV-1935 - should be a way to do afterShow or something, but that
- // won't work. Likewise, rendering hidden and then showing (w/autoShow) has
- // implications to afterRender so we cannot do that.
- }
- if (me.autoShow) {
- me.show();
- }
- if (Ext.isDefined(me.disabledClass)) {
- if (Ext.isDefined(Ext.global.console)) {
- Ext.global.console.warn('Ext.Component: disabledClass has been deprecated. Please use disabledCls.');
- }
- me.disabledCls = me.disabledClass;
- delete me.disabledClass;
- }
- },
- initComponent: function () {
- // This is called again here to allow derived classes to add plugin configs to the
- // plugins array before calling down to this, the base initComponent.
- this.constructPlugins();
- // this will properly (ignore or) constrain the configured width/height to their
- // min/max values for consistency.
- this.setSize(this.width, this.height);
- },
- /**
- * The supplied default state gathering method for the AbstractComponent class.
- *
- * This method returns dimension settings such as `flex`, `anchor`, `width` and `height` along with `collapsed`
- * state.
- *
- * Subclasses which implement more complex state should call the superclass's implementation, and apply their state
- * to the result if this basic state is to be saved.
- *
- * Note that Component state will only be saved if the Component has a {@link #stateId} and there as a StateProvider
- * configured for the document.
- *
- * @return {Object}
- */
- getState: function() {
- var me = this,
- state = null,
- sizeModel = me.getSizeModel();
- if (sizeModel.width.configured) {
- state = me.addPropertyToState(state, 'width');
- }
- if (sizeModel.height.configured) {
- state = me.addPropertyToState(state, 'height');
- }
- return state;
- },
- /**
- * Save a property to the given state object if it is not its default or configured
- * value.
- *
- * @param {Object} state The state object
- * @param {String} propName The name of the property on this object to save.
- * @param {String} [value] The value of the state property (defaults to `this[propName]`).
- * @return {Boolean} The state object or a new object if state was null and the property
- * was saved.
- * @protected
- */
- addPropertyToState: function (state, propName, value) {
- var me = this,
- len = arguments.length;
- // If the property is inherited, it is a default and we don't want to save it to
- // the state, however if we explicitly specify a value, always save it
- if (len == 3 || me.hasOwnProperty(propName)) {
- if (len < 3) {
- value = me[propName];
- }
- // If the property has the same value as was initially configured, again, we
- // don't want to save it.
- if (value !== me.initialConfig[propName]) {
- (state || (state = {}))[propName] = value;
- }
- }
- return state;
- },
- show: Ext.emptyFn,
- animate: function(animObj) {
- var me = this,
- hasToWidth,
- hasToHeight,
- toHeight,
- toWidth,
- to,
- clearWidth,
- clearHeight;
- animObj = animObj || {};
- to = animObj.to || {};
- if (Ext.fx.Manager.hasFxBlock(me.id)) {
- return me;
- }
- hasToWidth = Ext.isDefined(to.width);
- if (hasToWidth) {
- toWidth = Ext.Number.constrain(to.width, me.minWidth, me.maxWidth);
- }
- hasToHeight = Ext.isDefined(to.height);
- if (hasToHeight) {
- toHeight = Ext.Number.constrain(to.height, me.minHeight, me.maxHeight);
- }
- // Special processing for animating Component dimensions.
- if (!animObj.dynamic && (hasToWidth || hasToHeight)) {
- var curWidth = (animObj.from ? animObj.from.width : undefined) || me.getWidth(),
- w = curWidth,
- curHeight = (animObj.from ? animObj.from.height : undefined) || me.getHeight(),
- h = curHeight,
- needsResize = false;
- if (hasToHeight && toHeight > curHeight) {
- h = toHeight;
- needsResize = true;
- }
- if (hasToWidth && toWidth > curWidth) {
- w = toWidth;
- needsResize = true;
- }
- // If any dimensions are being increased, we must resize the internal structure
- // of the Component, but then clip it by sizing its encapsulating element back to original dimensions.
- // The animation will then progressively reveal the larger content.
- if (needsResize) {
- clearWidth = !Ext.isNumber(me.width);
- clearHeight = !Ext.isNumber(me.height);
- me.setSize(w, h);
- me.el.setSize(curWidth, curHeight);
- if (clearWidth) {
- delete me.width;
- }
- if (clearHeight) {
- delete me.height;
- }
- }
- if (hasToWidth) {
- to.width = toWidth;
- }
- if (hasToHeight) {
- to.height = toHeight;
- }
- }
- return me.mixins.animate.animate.apply(me, arguments);
- },
- onHide: function() {
- this.updateLayout({ isRoot: false });
- },
- onShow : function() {
- this.updateLayout({ isRoot: false });
- },
- constructPlugin: function(plugin) {
- if (plugin.ptype && typeof plugin.init != 'function') {
- plugin.cmp = this;
- plugin = Ext.PluginManager.create(plugin);
- }
- else if (typeof plugin == 'string') {
- plugin = Ext.PluginManager.create({
- ptype: plugin,
- cmp: this
- });
- }
- return plugin;
- },
- /**
- * Ensures that the plugins array contains fully constructed plugin instances. This converts any configs into their
- * appropriate instances.
- */
- constructPlugins: function() {
- var me = this,
- plugins = me.plugins,
- i, len;
- if (plugins) {
- for (i = 0, len = plugins.length; i < len; i++) {
- // this just returns already-constructed plugin instances...
- plugins[i] = me.constructPlugin(plugins[i]);
- }
- }
- },
- // @private
- initPlugin : function(plugin) {
- plugin.init(this);
- return plugin;
- },
- /**
- * @private
- * Injected as an override by Ext.Aria.initialize
- */
- updateAria: Ext.emptyFn,
- /**
- * Called by Component#doAutoRender
- *
- * Register a Container configured `floating: true` with this Component's {@link Ext.ZIndexManager ZIndexManager}.
- *
- * Components added in ths way will not participate in any layout, but will be rendered
- * upon first show in the way that {@link Ext.window.Window Window}s are.
- */
- registerFloatingItem: function(cmp) {
- var me = this;
- if (!me.floatingItems) {
- me.floatingItems = new Ext.ZIndexManager(me);
- }
- me.floatingItems.register(cmp);
- },
- unregisterFloatingItem: function(cmp) {
- var me = this;
- if (me.floatingItems) {
- me.floatingItems.unregister(cmp);
- }
- },
- layoutSuspendCount: 0,
- suspendLayouts: function () {
- var me = this;
- if (!me.rendered) {
- return;
- }
- if (++me.layoutSuspendCount == 1) {
- me.suspendLayout = true;
- }
- },
- resumeLayouts: function (flushOptions) {
- var me = this;
- if (!me.rendered) {
- return;
- }
- if (! --me.layoutSuspendCount) {
- me.suspendLayout = false;
- if (flushOptions && !me.isLayoutSuspended()) {
- me.updateLayout(flushOptions);
- }
- }
- },
- setupProtoEl: function() {
- var me = this,
- cls = [ me.baseCls, me.getComponentLayout().targetCls ];
- if (Ext.isDefined(me.cmpCls)) {
- if (Ext.isDefined(Ext.global.console)) {
- Ext.global.console.warn('Ext.Component: cmpCls has been deprecated. Please use componentCls.');
- }
- me.componentCls = me.cmpCls;
- delete me.cmpCls;
- }
- if (me.componentCls) {
- cls.push(me.componentCls);
- } else {
- me.componentCls = me.baseCls;
- }
- me.protoEl = new Ext.util.ProtoElement({
- cls: cls.join(' ') // in case any of the parts have multiple classes
- });
- },
- /**
- * Sets the UI for the component. This will remove any existing UIs on the component. It will also loop through any
- * uiCls set on the component and rename them so they include the new UI
- * @param {String} ui The new UI for the component
- */
- setUI: function(ui) {
- var me = this,
- oldUICls = Ext.Array.clone(me.uiCls),
- newUICls = [],
- classes = [],
- cls,
- i;
- //loop through all exisiting uiCls and update the ui in them
- for (i = 0; i < oldUICls.length; i++) {
- cls = oldUICls[i];
- classes = classes.concat(me.removeClsWithUI(cls, true));
- newUICls.push(cls);
- }
- if (classes.length) {
- me.removeCls(classes);
- }
- //remove the UI from the element
- me.removeUIFromElement();
- //set the UI
- me.ui = ui;
- //add the new UI to the elemend
- me.addUIToElement();
- //loop through all exisiting uiCls and update the ui in them
- classes = [];
- for (i = 0; i < newUICls.length; i++) {
- cls = newUICls[i];
- classes = classes.concat(me.addClsWithUI(cls, true));
- }
- if (classes.length) {
- me.addCls(classes);
- }
- },
- /**
- * Adds