/ext-4.1.0_b3/docs/extjs/examples/kitchensink/all-classes.js
https://bitbucket.org/srogerf/javascript · JavaScript · 28581 lines · 24127 code · 835 blank · 3619 comment · 863 complexity · 1a8790657b1626f7a21dd493d975bf3f MD5 · raw file
- /*
- 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 a cls to the uiCls array, which will also call {@link #addUIClsToElement} and adds to all elements of this
- * component.
- * @param {String/String[]} classes A string or an array of strings to add to the uiCls
- * @param {Object} skip (Boolean) skip True to skip adding it to the class and do it later (via the return)
- */
- addClsWithUI: function(classes, skip) {
- var me = this,
- clsArray = [],
- length,
- i = 0,
- cls;
- if (typeof classes === "string") {
- classes = (classes.indexOf(' ') < 0) ? [classes] : Ext.String.splitWords(classes);
- }
- length = classes.length;
- me.uiCls = Ext.Array.clone(me.uiCls);
- for (; i < length; i++) {
- cls = classes[i];
- if (cls && !me.hasUICls(cls)) {
- me.uiCls.push(cls);
- clsArray = clsArray.concat(me.addUIClsToElement(cls));
- }
- }
- if (skip !== true) {
- me.addCls(clsArray);
- }
- return clsArray;
- },
- /**
- * Removes a cls to the uiCls array, which will also call {@link #removeUIClsFromElement} and removes it from all
- * elements of this component.
- * @param {String/String[]} cls A string or an array of strings to remove to the uiCls
- */
- removeClsWithUI: function(classes, skip) {
- var me = this,
- clsArray = [],
- i = 0,
- length, cls;
- if (typeof classes === "string") {
- classes = (classes.indexOf(' ') < 0) ? [classes] : Ext.String.splitWords(classes);
- }
- length = classes.length;
- for (i = 0; i < length; i++) {
- cls = classes[i];
- if (cls && me.hasUICls(cls)) {
- me.uiCls = Ext.Array.remove(me.uiCls, cls);
- clsArray = clsArray.concat(me.removeUIClsFromElement(cls));
- }
- }
- if (skip !== true) {
- me.removeCls(clsArray);
- }
- return clsArray;
- },
- /**
- * Checks if there is currently a specified uiCls
- * @param {String} cls The cls to check
- */
- hasUICls: function(cls) {
- var me = this,
- uiCls = me.uiCls || [];
- return Ext.Array.contains(uiCls, cls);
- },
- frameElementsArray: ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'],
- /**
- * Method which adds a specified UI + uiCls to the components element. Can be overridden to remove the UI from more
- * than just the components element.
- * @param {String} ui The UI to remove from the element
- */
- addUIClsToElement: function(cls) {
- var me = this,
- baseClsUi = me.baseCls + '-' + me.ui + '-' + cls,
- result = [Ext.baseCSSPrefix + cls, me.baseCls + '-' + cls, baseClsUi],
- frameElementCls = me.frameElementCls;
- if (me.frame && !Ext.supports.CSS3BorderRadius) {
- // define each element of the frame
- var frameElementsArray = me.frameElementsArray,
- frameElementsLength = frameElementsArray.length,
- i = 0,
- el, frameElement, c;
- // loop through each of them, and if they are defined add the ui
- for (; i < frameElementsLength; i++) {
- frameElement = frameElementsArray[i];
- el = me['frame' + frameElement.toUpperCase()];
- c = baseClsUi + '-' + frameElement;
- if (el && el.dom) {
- el.addCls(c);
- } else if (Ext.Array.indexOf(frameElementCls[frameElement], c) == -1) {
- frameElementCls[frameElement].push(c);
- }
- }
- }
- me.frameElementCls = frameElementCls;
- return result;
- },
- /**
- * Method which removes a specified UI + uiCls from the components element. The cls which is added to the element
- * will be: `this.baseCls + '-' + ui`
- * @param {String} ui The UI to add to the element
- */
- removeUIClsFromElement: function(cls) {
- var me = this,
- baseClsUi = me.baseCls + '-' + me.ui + '-' + cls,
- result = [Ext.baseCSSPrefix + cls, me.baseCls + '-' + cls, baseClsUi],
- frameElementCls = me.frameElementCls;
- if (me.frame && !Ext.supports.CSS3BorderRadius) {
- // define each element of the frame
- var frameElementsArray = me.frameElementsArray,
- frameElementsLength = frameElementsArray.length,
- i = 0,
- el, frameElement, c;
- // loop through each of them, and if they are defined add the ui
- for (; i < frameElementsLength; i++) {
- frameElement = frameElementsArray[i];
- el = me['frame' + frameElement.toUpperCase()];
- c = baseClsUi + '-' + frameElement;
- if (el && el.dom) {
- el.addCls(c);
- } else {
- Ext.Array.remove(frameElementCls[frameElement], c);
- }
- }
- }
- me.frameElementCls = frameElementCls;
- return result;
- },
- /**
- * Method which adds a specified UI to the components element.
- * @private
- */
- addUIToElement: function() {
- var me = this,
- baseClsUI = me.baseCls + '-' + me.ui,
- frameElementCls = me.frameElementCls;
- me.addCls(baseClsUI);
- if (me.frame && !Ext.supports.CSS3BorderRadius) {
- // define each element of the frame
- var frameElementsArray = me.frameElementsArray,
- frameElementsLength = frameElementsArray.length,
- i = 0,
- el, frameElement, c;
- // loop through each of them, and if they are defined add the ui
- for (; i < frameElementsLength; i++) {
- frameElement = frameElementsArray[i];
- el = me['frame' + frameElement.toUpperCase()];
- c = baseClsUI + '-' + frameElement;
- if (el) {
- el.addCls(c);
- } else {
- if (!Ext.Array.contains(frameElementCls[frameElement], c)) {
- frameElementCls[frameElement].push(c);
- }
- }
- }
- }
- },
- /**
- * Method which removes a specified UI from the components element.
- * @private
- */
- removeUIFromElement: function() {
- var me = this,
- baseClsUI = me.baseCls + '-' + me.ui,
- frameElementCls = me.frameElementCls;
- me.removeCls(baseClsUI);
- if (me.frame && !Ext.supports.CSS3BorderRadius) {
- // define each element of the frame
- var frameElementsArray = me.frameElementsArray,
- frameElementsLength = frameElementsArray.length,
- i = 0,
- el, frameElement, c;
- for (; i < frameElementsLength; i++) {
- frameElement = frameElementsArray[i];
- el = me['frame' + frameElement.toUpperCase()];
- c = baseClsUI + '-' + frameElement;
- if (el) {
- el.removeCls(c);
- } else {
- Ext.Array.remove(frameElementCls[frameElement], c);
- }
- }
- }
- },
- /**
- * @private
- */
- getTpl: function(name) {
- return Ext.XTemplate.getTpl(this, name);
- },
- /**
- * Converts style definitions to String.
- * @return {String} A CSS style string with style, padding, margin and border.
- * @private
- */
- initStyles: function(targetEl) {
- var me = this,
- Element = Ext.Element,
- padding = me.padding,
- margin = me.margin,
- x = me.x,
- y = me.y,
- width, height;
- // Convert the padding, margin and border properties from a space seperated string
- // into a proper style string
- if (padding !== undefined) {
- targetEl.setStyle('padding', Element.unitizeBox((padding === true) ? 5 : padding));
- }
- if (margin !== undefined) {
- targetEl.setStyle('margin', Element.unitizeBox((margin === true) ? 5 : margin));
- }
- if (me.border !== undefined) {
- me.setBorder(me.border, targetEl);
- }
- // 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.cls != me.initialCls) {
- targetEl.addCls(me.cls);
- delete me.cls;
- delete me.initialCls;
- }
- if (me.style && me.style != me.initialStyle) {
- targetEl.setStyle(me.style);
- delete me.style;
- delete me.initialStyle;
- }
- if (x !== undefined) {
- targetEl.setStyle('left', x + 'px');
- }
- if (y !== undefined) {
- targetEl.setStyle('top', y + 'px');
- }
- // 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;
- // framed components need their width/height to apply to the frame, which is
- // best handled in layout at present
- if (typeof width == 'number') {
- targetEl.setStyle('width', width + 'px');
- }
- if (typeof height == 'number') {
- targetEl.setStyle('height', height + 'px');
- }
- }
- },
- // @private
- initEvents : function() {
- var me = this,
- afterRenderEvents = me.afterRenderEvents,
- el,
- property,
- fn = function(listeners){
- me.mon(el, listeners);
- };
- if (afterRenderEvents) {
- for (property in afterRenderEvents) {
- if (afterRenderEvents.hasOwnProperty(property)) {
- el = me[property];
- if (el && el.on) {
- Ext.each(afterRenderEvents[property], fn);
- }
- }
- }
- }
- // This will add focus/blur listeners to the getFocusEl() element if that is naturally focusable.
- // If *not* naturally focusable, then the FocusManager must be enabled to get it to listen for focus so that
- // the FocusManager can track and highlight focus.
- me.addFocusListener();
- },
- /**
- * @private
- * <p>Sets up the focus listener on this Component's {@link #getFocusEl focusEl} if it has one.</p>
- * <p>Form Components which must implicitly participate in tabbing order usually have a naturally focusable
- * element as their {@link #getFocusEl focusEl}, and it is the DOM event of that recieving focus which drives
- * the Component's onFocus handling, and the DOM event of it being blurred which drives the onBlur handling.</p>
- * <p>If the {@link #getFocusEl focusEl} is <b>not</b> naturally focusable, then the listeners are only added
- * if the {@link Ext.FocusManager FocusManager} is enabled.</p>
- */
- addFocusListener: function() {
- var me = this,
- focusEl = me.getFocusEl(),
- needsTabIndex;
- // All Containers may be focusable, not only "form" type elements, but also
- // Panels, Toolbars, Windows etc.
- // Usually, the <DIV> element they will return as their focusEl will not be able to recieve focus
- // However, if the FocusManager is invoked, its non-default navigation handlers (invoked when
- // tabbing/arrowing off of certain Components) may explicitly focus a Panel or Container or FieldSet etc.
- // Add listeners to the focus and blur events on the focus element
- // If this Component returns a focusEl, we might need to add a focus listener to it.
- if (focusEl) {
- // getFocusEl might return a Component if a Container wishes to delegate focus to a descendant.
- // Window can do this via its defaultFocus configuration which can reference a Button.
- if (focusEl.isComponent) {
- return focusEl.addFocusListener();
- }
- // If the focusEl is naturally focusable, then we always need a focus listener to drive the Component's
- // onFocus handling.
- // If *not* naturally focusable, then we only need the focus listener if the FocusManager is enabled.
- needsTabIndex = focusEl.needsTabIndex();
- if (!me.focusListenerAdded && (!needsTabIndex || Ext.FocusManager.enabled)) {
- if (needsTabIndex) {
- focusEl.dom.tabIndex = -1;
- }
- focusEl.on({
- focus: me.onFocus,
- blur: me.onBlur,
- scope: me
- });
- me.focusListenerAdded = true;
- }
- }
- },
- /**
- * @private
- * <p>Returns the focus holder element associated with this Component. At the Component base class level, this function returns <code>undefined</code>.</p>
- * <p>Subclasses which use embedded focusable elements (such as Window, Field and Button) should override this for use by the {@link #focus} method.</p>
- * <p>Containers which need to participate in the {@link Ext.FocusManager FocusManager}'s navigation and Container focusing scheme also
- * need to return a focusEl, although focus is only listened for in this case if the {@link Ext.FocusManager FocusManager} is {@link Ext.FocusManager#method-enable enable}d.</p>
- * @returns {undefined} <code>undefined</code> because raw Components cannot by default hold focus.
- */
- getFocusEl: Ext.emptyFn,
- isFocusable: function(c) {
- var me = this,
- focusEl;
- if ((me.focusable !== false) && (focusEl = me.getFocusEl()) && me.rendered && !me.destroying && !me.isDestroyed && !me.disabled && me.isVisible(true)) {
- // getFocusEl might return a Component if a Container wishes to delegate focus to a descendant.
- // Window can do this via its defaultFocus configuration which can reference a Button.
- if (focusEl.isComponent) {
- return focusEl.isFocusable();
- }
- return focusEl && focusEl.dom && focusEl.isVisible();
- }
- },
- // private
- preFocus: Ext.emptyFn,
- // private
- onFocus: function(e) {
- var me = this,
- focusCls = me.focusCls,
- focusEl = me.getFocusEl();
- if (!me.disabled) {
- me.preFocus(e);
- if (focusCls && focusEl) {
- focusEl.addCls(me.addClsWithUI(focusCls, true));
- }
- if (!me.hasFocus) {
- me.hasFocus = true;
- me.fireEvent('focus', me, e);
- }
- }
- },
- // private
- beforeBlur : Ext.emptyFn,
- // private
- onBlur : function(e) {
- var me = this,
- focusCls = me.focusCls,
- focusEl = me.getFocusEl();
- if (me.destroying) {
- return;
- }
- me.beforeBlur(e);
- if (focusCls && focusEl) {
- focusEl.removeCls(me.removeClsWithUI(focusCls, true));
- }
- if (me.validateOnBlur) {
- me.validate();
- }
- me.hasFocus = false;
- me.fireEvent('blur', me, e);
- me.postBlur(e);
- },
- // private
- postBlur : Ext.emptyFn,
- /**
- * Tests whether this Component matches the selector string.
- * @param {String} selector The selector string to test against.
- * @return {Boolean} True if this Component matches the selector.
- */
- is: function(selector) {
- return Ext.ComponentQuery.is(this, selector);
- },
- /**
- * Walks up the `ownerCt` axis looking for an ancestor Container which matches the passed simple selector.
- *
- * Example:
- *
- * var owningTabPanel = grid.up('tabpanel');
- *
- * @param {String} [selector] The simple selector to test.
- * @return {Ext.container.Container} The matching ancestor Container (or `undefined` if no match was found).
- */
- up: function(selector) {
- // Use bubble target to navigate upwards so that Components can implement their own hierarchy.
- // For example Menus implement getBubbleTarget because they have a parentMenu or ownerButton as an
- // upward link depending upon how they are owned and triggered.
- var result = this.getBubbleTarget();
- if (selector) {
- for (; result; result = result.getBubbleTarget()) {
- if (Ext.ComponentQuery.is(result, selector)) {
- return result;
- }
- }
- }
- return result;
- },
- /**
- * Returns the next sibling of this Component.
- *
- * Optionally selects the next sibling which matches the passed {@link Ext.ComponentQuery ComponentQuery} selector.
- *
- * May also be refered to as **`next()`**
- *
- * Note that this is limited to siblings, and if no siblings of the item match, `null` is returned. Contrast with
- * {@link #nextNode}
- * @param {String} [selector] A {@link Ext.ComponentQuery ComponentQuery} selector to filter the following items.
- * @return {Ext.Component} The next sibling (or the next sibling which matches the selector).
- * Returns null if there is no matching sibling.
- */
- nextSibling: function(selector) {
- var o = this.ownerCt, it, last, idx, c;
- if (o) {
- it = o.items;
- idx = it.indexOf(this) + 1;
- if (idx) {
- if (selector) {
- for (last = it.getCount(); idx < last; idx++) {
- if ((c = it.getAt(idx)).is(selector)) {
- return c;
- }
- }
- } else {
- if (idx < it.getCount()) {
- return it.getAt(idx);
- }
- }
- }
- }
- return null;
- },
- /**
- * Returns the previous sibling of this Component.
- *
- * Optionally selects the previous sibling which matches the passed {@link Ext.ComponentQuery ComponentQuery}
- * selector.
- *
- * May also be refered to as **`prev()`**
- *
- * Note that this is limited to siblings, and if no siblings of the item match, `null` is returned. Contrast with
- * {@link #previousNode}
- * @param {String} [selector] A {@link Ext.ComponentQuery ComponentQuery} selector to filter the preceding items.
- * @return {Ext.Component} The previous sibling (or the previous sibling which matches the selector).
- * Returns null if there is no matching sibling.
- */
- previousSibling: function(selector) {
- var o = this.ownerCt, it, idx, c;
- if (o) {
- it = o.items;
- idx = it.indexOf(this);
- if (idx != -1) {
- if (selector) {
- for (--idx; idx >= 0; idx--) {
- if ((c = it.getAt(idx)).is(selector)) {
- return c;
- }
- }
- } else {
- if (idx) {
- return it.getAt(--idx);
- }
- }
- }
- }
- return null;
- },
- /**
- * Returns the previous node in the Component tree in tree traversal order.
- *
- * Note that this is not limited to siblings, and if invoked upon a node with no matching siblings, will walk the
- * tree in reverse order to attempt to find a match. Contrast with {@link #previousSibling}.
- * @param {String} [selector] A {@link Ext.ComponentQuery ComponentQuery} selector to filter the preceding nodes.
- * @return {Ext.Component} The previous node (or the previous node which matches the selector).
- * Returns null if there is no matching node.
- */
- previousNode: function(selector, includeSelf) {
- var node = this,
- result,
- it, len, i;
- // If asked to include self, test me
- if (includeSelf && node.is(selector)) {
- return node;
- }
- result = this.prev(selector);
- if (result) {
- return result;
- }
- if (node.ownerCt) {
- for (it = node.ownerCt.items.items, i = Ext.Array.indexOf(it, node) - 1; i > -1; i--) {
- if (it[i].query) {
- result = it[i].query(selector);
- result = result[result.length - 1];
- if (result) {
- return result;
- }
- }
- }
- return node.ownerCt.previousNode(selector, true);
- }
- },
- /**
- * Returns the next node in the Component tree in tree traversal order.
- *
- * Note that this is not limited to siblings, and if invoked upon a node with no matching siblings, will walk the
- * tree to attempt to find a match. Contrast with {@link #nextSibling}.
- * @param {String} [selector] A {@link Ext.ComponentQuery ComponentQuery} selector to filter the following nodes.
- * @return {Ext.Component} The next node (or the next node which matches the selector).
- * Returns null if there is no matching node.
- */
- nextNode: function(selector, includeSelf) {
- var node = this,
- result,
- it, len, i;
- // If asked to include self, test me
- if (includeSelf && node.is(selector)) {
- return node;
- }
- result = this.next(selector);
- if (result) {
- return result;
- }
- if (node.ownerCt) {
- for (it = node.ownerCt.items, i = it.indexOf(node) + 1, it = it.items, len = it.length; i < len; i++) {
- if (it[i].down) {
- result = it[i].down(selector);
- if (result) {
- return result;
- }
- }
- }
- return node.ownerCt.nextNode(selector);
- }
- },
- /**
- * Retrieves the id of this component. Will autogenerate an id if one has not already been set.
- * @return {String}
- */
- getId : function() {
- return this.id || (this.id = 'ext-comp-' + (this.getAutoId()));
- },
- getItemId : function() {
- return this.itemId || this.id;
- },
- /**
- * Retrieves the top level element representing this component.
- * @return {Ext.dom.Element}
- */
- getEl : function() {
- return this.el;
- },
- /**
- * This is used to determine where to insert the 'html', 'contentEl' and 'items' in this component.
- * @private
- */
- getTargetEl: function() {
- return this.frameBody || this.el;
- },
- /**
- * @private
- * Returns the CSS style object which will set the Component's scroll styles. This must be applied
- * to the {@link #getTargetEl target element}.
- */
- getOverflowStyle: function() {
- var me = this,
- result = null;
- if (typeof me.autoScroll == 'boolean') {
- result = {
- overflow: me.autoScroll ? 'auto' : ''
- };
- } else if (me.overflowX !== undefined || me.overflowY !== undefined) {
- result = {
- 'overflow-x': (me.overflowX||''),
- 'overflow-y': (me.overflowY||'')
- };
- }
- // The scrollable container element must be non-statically positioned or IE6/7 will make
- // positioned children stay in place rather than scrolling with the rest of the content
- if (result && (Ext.isIE6 || Ext.isIE7)) {
- result.position = 'relative';
- }
- return result;
- },
- /**
- * Tests whether or not this Component is of a specific xtype. This can test whether this Component is descended
- * from the xtype (default) or whether it is directly of the xtype specified (shallow = true).
- *
- * **If using your own subclasses, be aware that a Component must register its own xtype to participate in
- * determination of inherited xtypes.**
- *
- * For a list of all available xtypes, see the {@link Ext.Component} header.
- *
- * Example usage:
- *
- * var t = new Ext.form.field.Text();
- * var isText = t.isXType('textfield'); // true
- * var isBoxSubclass = t.isXType('field'); // true, descended from Ext.form.field.Base
- * var isBoxInstance = t.isXType('field', true); // false, not a direct Ext.form.field.Base instance
- *
- * @param {String} xtype The xtype to check for this Component
- * @param {Boolean} [shallow=false] True to check whether this Component is directly of the specified xtype, false to
- * check whether this Component is descended from the xtype.
- * @return {Boolean} True if this component descends from the specified xtype, false otherwise.
- */
- isXType: function(xtype, shallow) {
- if (shallow) {
- return this.xtype === xtype;
- }
- else {
- return this.xtypesMap[xtype];
- }
- },
- /**
- * Returns this Component's xtype hierarchy as a slash-delimited string. For a list of all available xtypes, see the
- * {@link Ext.Component} header.
- *
- * **If using your own subclasses, be aware that a Component must register its own xtype to participate in
- * determination of inherited xtypes.**
- *
- * Example usage:
- *
- * var t = new Ext.form.field.Text();
- * alert(t.getXTypes()); // alerts 'component/field/textfield'
- *
- * @return {String} The xtype hierarchy string
- */
- getXTypes: function() {
- var self = this.self,
- xtypes, parentPrototype, parentXtypes;
- if (!self.xtypes) {
- xtypes = [];
- parentPrototype = this;
- while (parentPrototype) {
- parentXtypes = parentPrototype.xtypes;
- if (parentXtypes !== undefined) {
- xtypes.unshift.apply(xtypes, parentXtypes);
- }
- parentPrototype = parentPrototype.superclass;
- }
- self.xtypeChain = xtypes;
- self.xtypes = xtypes.join('/');
- }
- return self.xtypes;
- },
- /**
- * Update the content area of a component.
- * @param {String/Object} htmlOrData If this component has been configured with a template via the tpl config then
- * it will use this argument as data to populate the template. If this component was not configured with a template,
- * the components content area will be updated via Ext.Element update
- * @param {Boolean} [loadScripts=false] Only legitimate when using the html configuration.
- * @param {Function} [callback] Only legitimate when using the html configuration. Callback to execute when
- * scripts have finished loading
- */
- update : function(htmlOrData, loadScripts, cb) {
- var me = this;
- if (me.tpl && !Ext.isString(htmlOrData)) {
- me.data = htmlOrData;
- if (me.rendered) {
- me.tpl[me.tplWriteMode](me.getTargetEl(), htmlOrData || {});
- }
- } else {
- me.html = Ext.isObject(htmlOrData) ? Ext.DomHelper.markup(htmlOrData) : htmlOrData;
- if (me.rendered) {
- me.getTargetEl().update(me.html, loadScripts, cb);
- }
- }
- if (me.rendered) {
- me.updateLayout();
- }
- },
- /**
- * Convenience function to hide or show this component by boolean.
- * @param {Boolean} visible True to show, false to hide
- * @return {Ext.Component} this
- */
- setVisible : function(visible) {
- return this[visible ? 'show': 'hide']();
- },
- /**
- * Returns true if this component is visible.
- *
- * @param {Boolean} [deep=false] Pass `true` to interrogate the visibility status of all parent Containers to
- * determine whether this Component is truly visible to the user.
- *
- * Generally, to determine whether a Component is hidden, the no argument form is needed. For example when creating
- * dynamically laid out UIs in a hidden Container before showing them.
- *
- * @return {Boolean} True if this component is visible, false otherwise.
- */
- isVisible: function(deep) {
- var me = this,
- child = me,
- visible = me.rendered && !me.hidden,
- ancestor = me.ownerCt;
- // Clear hiddenOwnerCt property
- me.hiddenAncestor = false;
- if (me.destroyed) {
- return false;
- }
- if (deep && visible && ancestor) {
- while (ancestor) {
- // If any ancestor is hidden, then this is hidden.
- // If an ancestor Panel (only Panels have a collapse method) is collapsed,
- // then its layoutTarget (body) is hidden, so this is hidden unless its within a
- // docked item; they are still visible when collapsed (Unless they themseves are hidden)
- if (ancestor.hidden || (ancestor.collapsed &&
- !(ancestor.getDockedItems && Ext.Array.contains(ancestor.getDockedItems(), child)))) {
- // Store hiddenOwnerCt property if needed
- me.hiddenAncestor = ancestor;
- visible = false;
- break;
- }
- child = ancestor;
- ancestor = ancestor.ownerCt;
- }
- }
- return visible;
- },
- onBoxReady: function(){
- var me = this;
- if (me.disableOnBoxReady) {
- me.onDisable();
- } else if (me.enableOnBoxReady) {
- me.onEnable();
- }
- },
- /**
- * Enable the component
- * @param {Boolean} [silent=false] Passing true will supress the 'enable' event from being fired.
- */
- enable: function(silent) {
- var me = this;
- delete me.disableOnBoxReady;
- me.removeCls(me.disabledCls);
- if (me.rendered) {
- me.onEnable();
- } else {
- me.enableOnBoxReady = true;
- }
- me.disabled = false;
- delete me.resetDisable;
- if (silent !== true) {
- me.fireEvent('enable', me);
- }
- return me;
- },
- /**
- * Disable the component.
- * @param {Boolean} [silent=false] Passing true will supress the 'disable' event from being fired.
- */
- disable: function(silent) {
- var me = this;
- delete me.enableOnBoxReady;
- me.addCls(me.disabledCls);
- if (me.rendered) {
- me.onDisable();
- } else {
- me.disableOnBoxReady = true;
- }
- me.disabled = true;
- if (silent !== true) {
- delete me.resetDisable;
- me.fireEvent('disable', me);
- }
- return me;
- },
- /**
- * Allows addition of behavior to the enable operation.
- * After calling the superclass’s onEnable, the Component will be enabled.
- *
- * @template
- * @protected
- */
- onEnable: function() {
- if (this.maskOnDisable) {
- this.el.dom.disabled = false;
- this.unmask();
- }
- },
- /**
- * Allows addition of behavior to the disable operation.
- * After calling the superclass’s onDisable, the Component will be disabled.
- *
- * @template
- * @protected
- */
- onDisable : function() {
- if (this.maskOnDisable) {
- this.el.dom.disabled = true;
- this.mask();
- }
- },
- mask: function() {
- var box = this.lastBox,
- target = this.getMaskTarget(),
- args = [];
- // Pass it the height of our element if we know it.
- if (box) {
- args[2] = box.height;
- }
- target.mask.apply(target, args);
- },
- unmask: function() {
- this.getMaskTarget().unmask();
- },
- getMaskTarget: function(){
- return this.el
- },
- /**
- * Method to determine whether this Component is currently disabled.
- * @return {Boolean} the disabled state of this Component.
- */
- isDisabled : function() {
- return this.disabled;
- },
- /**
- * Enable or disable the component.
- * @param {Boolean} disabled True to disable.
- */
- setDisabled : function(disabled) {
- return this[disabled ? 'disable': 'enable']();
- },
- /**
- * Method to determine whether this Component is currently set to hidden.
- * @return {Boolean} the hidden state of this Component.
- */
- isHidden : function() {
- return this.hidden;
- },
- /**
- * Adds a CSS class to the top level element representing this component.
- * @param {String/String[]} cls The CSS class name to add
- * @return {Ext.Component} Returns the Component to allow method chaining.
- */
- addCls : function(cls) {
- var me = this,
- el = me.rendered ? me.el : me.protoEl;
- el.addCls.apply(el, arguments);
- return me;
- },
- /**
- * @inheritdoc Ext.AbstractComponent#addCls
- * @deprecated 4.1 Use {@link #addCls} instead.
- */
- addClass : function() {
- return this.addCls.apply(this, arguments);
- },
- /**
- * Checks if the specified CSS class exists on this element's DOM node.
- * @param {String} className The CSS class to check for
- * @return {Boolean} True if the class exists, else false
- * @method
- */
- hasCls: function (cls) {
- var me = this,
- el = me.rendered ? me.el : me.protoEl;
- return el.hasCls.apply(el, arguments);
- },
- /**
- * Removes a CSS class from the top level element representing this component.
- * @param {String/String[]} cls The CSS class name to remove
- * @returns {Ext.Component} Returns the Component to allow method chaining.
- */
- removeCls : function(cls) {
- var me = this,
- el = me.rendered ? me.el : me.protoEl;
- el.removeCls.apply(el, arguments);
- return me;
- },
- removeClass : function() {
- if (Ext.isDefined(Ext.global.console)) {
- Ext.global.console.warn('Ext.Component: removeClass has been deprecated. Please use removeCls.');
- }
- return this.removeCls.apply(this, arguments);
- },
- addOverCls: function() {
- var me = this;
- if (!me.disabled) {
- me.el.addCls(me.overCls);
- }
- },
- removeOverCls: function() {
- this.el.removeCls(this.overCls);
- },
- addListener : function(element, listeners, scope, options) {
- var me = this,
- fn,
- option;
- if (Ext.isString(element) && (Ext.isObject(listeners) || options && options.element)) {
- if (options.element) {
- fn = listeners;
- listeners = {};
- listeners[element] = fn;
- element = options.element;
- if (scope) {
- listeners.scope = scope;
- }
- for (option in options) {
- if (options.hasOwnProperty(option)) {
- if (me.eventOptionsRe.test(option)) {
- listeners[option] = options[option];
- }
- }
- }
- }
- // At this point we have a variable called element,
- // and a listeners object that can be passed to on
- if (me[element] && me[element].on) {
- me.mon(me[element], listeners);
- } else {
- me.afterRenderEvents = me.afterRenderEvents || {};
- if (!me.afterRenderEvents[element]) {
- me.afterRenderEvents[element] = [];
- }
- me.afterRenderEvents[element].push(listeners);
- }
- }
- return me.mixins.observable.addListener.apply(me, arguments);
- },
- // inherit docs
- removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
- var me = this,
- element = managedListener.options ? managedListener.options.element : null;
- if (element) {
- element = me[element];
- if (element && element.un) {
- if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
- element.un(managedListener.ename, managedListener.fn, managedListener.scope);
- if (!isClear) {
- Ext.Array.remove(me.managedListeners, managedListener);
- }
- }
- }
- } else {
- return me.mixins.observable.removeManagedListenerItem.apply(me, arguments);
- }
- },
- /**
- * Provides the link for Observable's fireEvent method to bubble up the ownership hierarchy.
- * @return {Ext.container.Container} the Container which owns this Component.
- */
- getBubbleTarget : function() {
- return this.ownerCt;
- },
- /**
- * Method to determine whether this Component is floating.
- * @return {Boolean} the floating state of this component.
- */
- isFloating : function() {
- return this.floating;
- },
- /**
- * Method to determine whether this Component is draggable.
- * @return {Boolean} the draggable state of this component.
- */
- isDraggable : function() {
- return !!this.draggable;
- },
- /**
- * Method to determine whether this Component is droppable.
- * @return {Boolean} the droppable state of this component.
- */
- isDroppable : function() {
- return !!this.droppable;
- },
- /**
- * Method to manage awareness of when components are added to their
- * respective Container, firing an #added event. References are
- * established at add time rather than at render time.
- *
- * Allows addition of behavior when a Component is added to a
- * Container. At this stage, the Component is in the parent
- * Container's collection of child items. After calling the
- * superclass's onAdded, the ownerCt reference will be present,
- * and if configured with a ref, the refOwner will be set.
- *
- * @param {Ext.container.Container} container Container which holds the component
- * @param {Number} pos Position at which the component was added
- *
- * @template
- * @protected
- */
- onAdded : function(container, pos) {
- var me = this;
- me.ownerCt = container;
- if (me.hasListeners.added) {
- me.fireEvent('added', me, container, pos);
- }
- },
- /**
- * Method to manage awareness of when components are removed from their
- * respective Container, firing a #removed event. References are properly
- * cleaned up after removing a component from its owning container.
- *
- * Allows addition of behavior when a Component is removed from
- * its parent Container. At this stage, the Component has been
- * removed from its parent Container's collection of child items,
- * but has not been destroyed (It will be destroyed if the parent
- * Container's autoDestroy is true, or if the remove call was
- * passed a truthy second parameter). After calling the
- * superclass's onRemoved, the ownerCt and the refOwner will not
- * be present.
- * @param {Boolean} destroying Will be passed as true if the Container performing the remove operation will delete this
- * Component upon remove.
- *
- * @template
- * @protected
- */
- onRemoved : function(destroying) {
- var me = this;
- if (me.hasListeners.removed) {
- me.fireEvent('removed', me, me.ownerCt);
- }
- delete me.ownerCt;
- },
- /**
- * Invoked before the Component is destroyed.
- *
- * @method
- * @template
- * @protected
- */
- beforeDestroy : Ext.emptyFn,
- /**
- * Allows addition of behavior to the resize operation.
- *
- * Called when Ext.resizer.Resizer#drag event is fired.
- *
- * @method
- * @template
- * @protected
- */
- onResize : Ext.emptyFn,
- /**
- * Sets the width and height of this Component. This method fires the {@link #resize} event. This method can accept
- * either width and height as separate arguments, or you can pass a size object like `{width:10, height:20}`.
- *
- * @param {Number/String/Object} width The new width to set. This may be one of:
- *
- * - A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).
- * - A String used to set the CSS width style.
- * - A size object in the format `{width: widthValue, height: heightValue}`.
- * - `undefined` to leave the width unchanged.
- *
- * @param {Number/String} height The new height to set (not required if a size object is passed as the first arg).
- * This may be one of:
- *
- * - A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).
- * - A String used to set the CSS height style. Animation may **not** be used.
- * - `undefined` to leave the height unchanged.
- *
- * @return {Ext.Component} this
- */
- setSize : function(width, height) {
- var me = this;
- // support for standard size objects
- if (width && typeof width == 'object') {
- height = width.height;
- width = width.width;
- }
- // Constrain within configured maxima
- if (typeof width == 'number') {
- me.width = Ext.Number.constrain(width, me.minWidth, me.maxWidth);
- } else if (width === null) {
- delete me.width;
- }
-
- if (typeof height == 'number') {
- me.height = Ext.Number.constrain(height, me.minHeight, me.maxHeight);
- } else if (height === null) {
- delete me.height;
- }
- // If not rendered, all we need to is set the properties.
- // The initial layout will set the size
- if (me.rendered && me.isVisible()) {
- // If we are changing size, then we are not the root.
- me.updateLayout({
- isRoot: false
- });
- }
- return me;
- },
- /**
- * Determines whether this Component is the root of a layout. This returns `true` if
- * this component can run its layout without assistance from or impact on its owner.
- * If this component cannot run its layout given these restrictions, `false` is returned
- * and its owner will be considered as the next candidate for the layout root.
- *
- * Setting the {@link #_isLayoutRoot} property to `true` causes this method to always
- * return `true`. This may be useful when updating a layout of a Container which shrink
- * wraps content, and you know that it will not change size, and so can safely be the
- * topmost participant in the layout run.
- * @protected
- */
- isLayoutRoot: function() {
- var me = this,
- ownerLayout = me.ownerLayout;
- // Return true if we have been explicitly flagged as the layout root, or if we are floating.
- // Sometimes floating Components get an ownerCt ref injected into them which is *not* a true ownerCt, merely
- // an upward link for reference purposes. For example a grid column menu is linked to the
- // owning header via an ownerCt reference.
- if (!ownerLayout || me._isLayoutRoot || me.floating) {
- return true;
- }
- return ownerLayout.isItemLayoutRoot(me);
- },
- /**
- * Returns true if layout is suspended for this component. This can come from direct
- * suspension of this component's layout activity ({@link #suspendLayouts}) or if one
- * of this component's containers is suspended.
- *
- * @return {Boolean} True layout of this component is suspended.
- */
- isLayoutSuspended: function () {
- var comp = this,
- ownerLayout;
- while (comp) {
- if (comp.layoutSuspendCount || comp.suspendLayout) {
- return true;
- }
- ownerLayout = comp.ownerLayout;
- if (!ownerLayout) {
- break;
- }
- // TODO - what about suspending a Layout instance?
- // this works better than ownerCt since ownerLayout means "is managed by" in
- // the proper sense... some floating components have ownerCt but won't have an
- // ownerLayout
- comp = ownerLayout.owner;
- }
- return false;
- },
- /**
- * Updates this component's layout. If this update effects this components {@link #ownerCt},
- * that component's `updateLayout` method will be called to perform the layout instead.
- * Otherwise, just this component (and its child items) will layout.
- *
- * @param {Object} options An object with layout options.
- * @param {Boolean} options.defer True if this layout should be deferred.
- * @param {Boolean} options.isRoot True if this layout should be the root of the layout.
- */
- updateLayout: function (options) {
- var me = this,
- defer,
- isRoot = options && options.isRoot;
- if (!me.rendered || me.layoutSuspendCount || me.suspendLayout) {
- return;
- }
- if (me.hidden) {
- Ext.AbstractComponent.cancelLayout(me);
- } else if (typeof isRoot != 'boolean') {
- isRoot = me.isLayoutRoot();
- }
- // if we aren't the root, see if our ownerLayout will handle it...
- if (isRoot || !me.ownerLayout || !me.ownerLayout.onContentChange(me)) {
- // either we are the root or our ownerLayout doesn't care
- if (!me.isLayoutSuspended()) {
- // we aren't suspended (knew that), but neither is any of our ownerCt's...
- defer = (options && options.hasOwnProperty('defer')) ? options.defer : me.deferLayouts;
- Ext.AbstractComponent.updateLayout(me, defer);
- }
- }
- },
- /**
- * Returns an object that describes how this component's width and height is managed. These
- * objects are shared and should not be modified.
- *
- * @return {Object} The size model for this component.
- * @return {Object} return.width The width aspect of this component's size model.
- * @return {Boolean} return.width.auto True if width is either natural or shrinkWrap (not fixed).
- * @return {Boolean} return.width.calculated True if width is calculated by a layout.
- * @return {Boolean} return.width.configured True if width is specified on this component.
- * @return {Boolean} return.width.fixed True if width is either calculated or configured.
- * @return {Boolean} return.width.natural True if width is determined by CSS and does not depend on content.
- * @return {Boolean} return.width.shrinkWrap True if width is determined by content.
- * @return {Object} return.height The height aspect of this component's size model.
- * @return {Boolean} return.height.auto True if height is either natural or shrinkWrap (not fixed).
- * @return {Boolean} return.height.calculated True if height is calculated by a layout.
- * @return {Boolean} return.height.configured True if height is specified on this component.
- * @return {Boolean} return.height.fixed True if height is either calculated or configured.
- * @return {Boolean} return.height.natural True if height is determined by CSS and does not depend on content.
- * @return {Boolean} return.height.shrinkWrap True if height is determined by content.
- */
- getSizeModel: function (ownerCtSizeModel) {
- var me = this,
- Layout = Ext.layout.Layout.prototype,
- models = Layout.sizeModels,
- heightModel, ownerLayout, policy, shrinkWrap, widthModel;
- if (typeof me.width == 'number') {
- widthModel = models.configured;
- }
- if (typeof me.height == 'number') {
- heightModel = models.configured;
- }
- if (!widthModel || !heightModel) {
- if (me.floating) {
- policy = Layout.autoSizePolicy;
- shrinkWrap = 3;
- } else {
- if (!(ownerLayout = me.ownerLayout)) {
- policy = Layout.autoSizePolicy;
- shrinkWrap = me.shrinkWrap;
- } else {
- policy = ownerLayout.getItemSizePolicy(me);
- shrinkWrap = ownerLayout.isItemShrinkWrap(me);
- }
- shrinkWrap = (shrinkWrap === true) ? 3 : (shrinkWrap || 0); // false->0, true->3
- if (shrinkWrap !== 3) {
- if (!ownerCtSizeModel) {
- ownerCtSizeModel = me.ownerCt && me.ownerCt.getSizeModel();
- }
- if (ownerCtSizeModel) {
- shrinkWrap |= (ownerCtSizeModel.width.shrinkWrap ? 1 : 0) | (ownerCtSizeModel.height.shrinkWrap ? 2 : 0);
- }
- }
- }
- if (!widthModel) {
- if (!policy.setsWidth) {
- widthModel = (shrinkWrap & 1) ? models.shrinkWrap : models.natural;
- } else if (policy.readsWidth) {
- widthModel = (shrinkWrap & 1) ? models.calculatedFromShrinkWrap :
- models.calculatedFromNatural;
- } else {
- widthModel = models.calculated;
- }
- }
- if (!heightModel) {
- if (!policy.setsHeight) {
- heightModel = (shrinkWrap & 2) ? models.shrinkWrap : models.natural;
- } else if (policy.readsHeight) {
- heightModel = (shrinkWrap & 2) ? models.calculatedFromShrinkWrap :
- models.calculatedFromNatural;
- } else {
- heightModel = models.calculated;
- }
- }
- }
- return {
- width: widthModel,
- height: heightModel
- };
- },
- isDescendant: function(ancestor) {
- if (ancestor.isContainer) {
- for (var c = this.ownerCt; c; c = c.ownerCt) {
- if (c === ancestor) {
- return true;
- }
- }
- }
- return false;
- },
- /**
- * This method needs to be called whenever you change something on this component that requires the Component's
- * layout to be recalculated.
- * @return {Ext.container.Container} this
- */
- doComponentLayout : function() {
- this.updateLayout();
- return this;
- },
- /**
- * Forces this component to redo its componentLayout.
- * @deprecated 4.1.0 Use {@link #updateLayout} instead.
- */
- forceComponentLayout: function () {
- this.updateLayout();
- },
- // @private
- setComponentLayout : function(layout) {
- var currentLayout = this.componentLayout;
- if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
- currentLayout.setOwner(null);
- }
- this.componentLayout = layout;
- layout.setOwner(this);
- },
- getComponentLayout : function() {
- var me = this;
- if (!me.componentLayout || !me.componentLayout.isLayout) {
- me.setComponentLayout(Ext.layout.Layout.create(me.componentLayout, 'autocomponent'));
- }
- return me.componentLayout;
- },
- /**
- * Called by the layout system after the Component has been layed out.
- *
- * @param {Number} width The width that was set
- * @param {Number} height The height that was set
- * @param {Number} oldWidth The old width. <code>undefined</code> if this was the initial layout.
- * @param {Number} oldHeight The old height. <code>undefined</code> if this was the initial layout.
- *
- * @template
- * @protected
- */
- afterComponentLayout: function(width, height, oldWidth, oldHeight) {
- var me = this;
- if (++me.componentLayoutCounter === 1) {
- me.afterFirstLayout();
- }
- if (me.hasListeners.resize && (width !== oldWidth || height !== oldHeight)) {
- me.fireEvent('resize', me, width, height, oldWidth, oldHeight);
- }
- },
- /**
- * Occurs before componentLayout is run. Returning false from this method will prevent the componentLayout from
- * being executed.
- *
- * @param {Number} adjWidth The box-adjusted width that was set
- * @param {Number} adjHeight The box-adjusted height that was set
- *
- * @template
- * @protected
- */
- beforeComponentLayout: function(width, height) {
- return true;
- },
- /**
- * Sets the left and top of the component. To set the page XY position instead, use {@link #setPagePosition}. This
- * method fires the {@link #move} event.
- * @param {Number} left The new left
- * @param {Number} top The new top
- * @param {Boolean/Object} [animate] If true, the Component is _animated_ into its new position. You may also pass an
- * animation configuration.
- * @return {Ext.Component} this
- */
- setPosition : function(x, y, animate) {
- var me = this,
- pos = me.beforeSetPosition.apply(me, arguments);
- if (pos && me.rendered) {
- // Convert position WRT RTL
- pos = me.convertPosition(pos);
- if (animate) {
- me.stopAnimation();
- me.animate(Ext.apply({
- duration: 1000,
- listeners: {
- afteranimate: Ext.Function.bind(me.afterSetPosition, me, [pos.left, pos.top])
- },
- to: pos
- }, animate));
- } else {
- // Must use Element's methods to set element position because, if it is a Layer (floater), it may need to sync a shadow
- // We must also only set the properties which are defined because Element.setLeftTop autos any undefined coordinates
- if (pos.left !== undefined && pos.top !== undefined) {
- me.el.setLeftTop(pos.left, pos.top);
- } else if (pos.left !== undefined) {
- me.el.setLeft(pos.left);
- } else if (pos.top !==undefined) {
- me.el.setTop(pos.top);
- }
- me.afterSetPosition(pos.left, pos.top);
- }
- }
- return me;
- },
- /**
- * @private Template method called before a Component is positioned.
- */
- beforeSetPosition: function (x, y, animate) {
- var pos, x0;
- // decode the position arguments:
- if (!x || Ext.isNumber(x)) {
- pos = { x: x, y : y, anim: animate };
- } else if (Ext.isNumber(x0 = x[0])) { // an array of [x, y]
- pos = { x : x0, y : x[1], anim: y };
- } else {
- pos = { x: x.x, y: x.y, anim: y }; // already an object w/ x & y properties
- }
- pos.hasX = Ext.isNumber(pos.x);
- pos.hasY = Ext.isNumber(pos.y);
- // store the position as specified:
- this.x = pos.x;
- this.y = pos.y;
- return (pos.hasX || pos.hasY) ? pos : null;
- },
- /**
- * Template method called after a Component has been positioned.
- *
- * @param {Number} x
- * @param {Number} y
- *
- * @template
- * @protected
- */
- afterSetPosition: function(x, y) {
- var me = this;
- me.onPosition(x, y);
- if (me.hasListeners.move) {
- me.fireEvent('move', me, x, y);
- }
- },
- /**
- * This method converts an "{x: x, y: y}" object to a "{left: x+'px', top: y+'px'}" object.
- * The returned object contains the styles to set to effect the position. This is
- * overridden in RTL mode to be "{right: x, top: y}".
- * @private
- */
- convertPosition: function (pos, withUnits) {
- var ret = {},
- El = Ext.Element;
- if (pos.hasX) {
- ret.left = withUnits ? El.addUnits(pos.x) : pos.x;
- }
- if (pos.hasY) {
- ret.top = withUnits ? El.addUnits(pos.y) : pos.y;
- }
- return ret;
- },
- /**
- * Called after the component is moved, this method is empty by default but can be implemented by any
- * subclass that needs to perform custom logic after a move occurs.
- *
- * @param {Number} x The new x position
- * @param {Number} y The new y position
- *
- * @template
- * @protected
- */
- onPosition: Ext.emptyFn,
- /**
- * Sets the width of the component. This method fires the {@link #resize} event.
- *
- * @param {Number} width The new width to setThis may be one of:
- *
- * - A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).
- * - A String used to set the CSS width style.
- *
- * @return {Ext.Component} this
- */
- setWidth : function(width) {
- return this.setSize(width);
- },
- /**
- * Sets the height of the component. This method fires the {@link #resize} event.
- *
- * @param {Number} height The new height to set. This may be one of:
- *
- * - A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).
- * - A String used to set the CSS height style.
- * - _undefined_ to leave the height unchanged.
- *
- * @return {Ext.Component} this
- */
- setHeight : function(height) {
- return this.setSize(undefined, height);
- },
- /**
- * Gets the current size of the component's underlying element.
- * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
- */
- getSize : function() {
- return this.el.getSize();
- },
- /**
- * Gets the current width of the component's underlying element.
- * @return {Number}
- */
- getWidth : function() {
- return this.el.getWidth();
- },
- /**
- * Gets the current height of the component's underlying element.
- * @return {Number}
- */
- getHeight : function() {
- return this.el.getHeight();
- },
- /**
- * Gets the {@link Ext.ComponentLoader} for this Component.
- * @return {Ext.ComponentLoader} The loader instance, null if it doesn't exist.
- */
- getLoader: function(){
- var me = this,
- autoLoad = me.autoLoad ? (Ext.isObject(me.autoLoad) ? me.autoLoad : {url: me.autoLoad}) : null,
- loader = me.loader || autoLoad;
- if (loader) {
- if (!loader.isLoader) {
- me.loader = new Ext.ComponentLoader(Ext.apply({
- target: me,
- autoLoad: autoLoad
- }, loader));
- } else {
- loader.setTarget(me);
- }
- return me.loader;
- }
- return null;
- },
- /**
- * Sets the dock position of this component in its parent panel. Note that this only has effect if this item is part
- * of the dockedItems collection of a parent that has a DockLayout (note that any Panel has a DockLayout by default)
- * @param {Object} dock The dock position.
- * @param {Boolean} [layoutParent=false] True to re-layout parent.
- * @return {Ext.Component} this
- */
- setDocked : function(dock, layoutParent) {
- var me = this;
- me.dock = dock;
- if (layoutParent && me.ownerCt && me.rendered) {
- me.ownerCt.updateLayout();
- }
- return me;
- },
- /**
- *
- * @param {String/Number} border The border, see {@link #border}. If a falsey value is passed
- * the border will be removed.
- */
- setBorder: function(border, /* private */ targetEl) {
- var me = this,
- initial = !!targetEl;
- if (me.rendered || initial) {
- if (!initial) {
- targetEl = me.el;
- }
- if (!border) {
- border = 0;
- } else {
- border = Ext.Element.unitizeBox((border === true) ? 1 : border);
- }
- targetEl.setStyle('border-width', border);
- if (!initial) {
- me.updateLayout();
- }
- }
- me.border = border;
- },
- onDestroy : function() {
- var me = this;
- if (me.monitorResize && Ext.EventManager.resizeEvent) {
- Ext.EventManager.resizeEvent.removeListener(me.setSize, me);
- }
- // Destroying the floatingItems ZIndexManager will also destroy descendant floating Components
- Ext.destroy(
- me.componentLayout,
- me.loadMask,
- me.floatingItems
- );
- },
- /**
- * Destroys the Component.
- */
- destroy : function() {
- var me = this,
- selectors = me.renderSelectors,
- selector,
- el;
- if (!me.isDestroyed) {
- if (!me.hasListeners.beforedestroy || me.fireEvent('beforedestroy', me) !== false) {
- me.destroying = true;
- me.beforeDestroy();
- if (me.floating) {
- delete me.floatParent;
- // A zIndexManager is stamped into a *floating* Component when it is added to a Container.
- // If it has no zIndexManager at render time, it is assigned to the global Ext.WindowManager instance.
- if (me.zIndexManager) {
- me.zIndexManager.unregister(me);
- }
- } else if (me.ownerCt && me.ownerCt.remove) {
- me.ownerCt.remove(me, false);
- }
- me.onDestroy();
- // Attempt to destroy all plugins
- Ext.destroy(me.plugins);
- if (me.hasListeners.destroy) {
- me.fireEvent('destroy', me);
- }
- Ext.ComponentManager.unregister(me);
- me.mixins.state.destroy.call(me);
- me.clearListeners();
- // make sure we clean up the element references after removing all events
- if (me.rendered) {
- // In case we are queued for a layout.
- Ext.AbstractComponent.cancelLayout(me);
- if (!me.preserveElOnDestroy) {
- me.el.remove();
- }
- me.mixins.elementCt.destroy.call(me); // removes childEls
- if (selectors) {
- for (selector in selectors) {
- if (selectors.hasOwnProperty(selector)) {
- el = me[selector];
- if (el) { // in case any other code may have already removed it
- delete me[selector];
- el.remove();
- }
- }
- }
- }
- delete me.el;
- delete me.frameBody;
- delete me.rendered;
- }
- me.destroying = false;
- me.isDestroyed = true;
- }
- }
- },
- /**
- * Retrieves a plugin by its pluginId which has been bound to this component.
- * @param {Object} pluginId
- * @return {Ext.AbstractPlugin} plugin instance.
- */
- getPlugin: function(pluginId) {
- var i = 0,
- plugins = this.plugins,
- ln = plugins.length;
- for (; i < ln; i++) {
- if (plugins[i].pluginId === pluginId) {
- return plugins[i];
- }
- }
- },
- /**
- * Determines whether this component is the descendant of a particular container.
- * @param {Ext.Container} container
- * @return {Boolean} True if it is.
- */
- isDescendantOf: function(container) {
- return !!this.findParentBy(function(p){
- return p === container;
- });
- }
- }, function() {
- var abstractComponent = this;
- abstractComponent.createAlias({
- on: 'addListener',
- prev: 'previousSibling',
- next: 'nextSibling'
- });
- Ext.resumeLayouts = function (flush) {
- abstractComponent.resumeLayouts(flush);
- };
- Ext.suspendLayouts = function () {
- abstractComponent.suspendLayouts();
- };
- /**
- *
- * Utility wrapper that suspends layouts of all components for the duration of a given function.
- * @param {Function} fn The function to execute.
- * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
- */
- Ext.batchLayouts = function(fn, scope) {
- abstractComponent.suspendLayouts();
- // Invoke the function
- fn.call(scope);
- abstractComponent.resumeLayouts(true);
- };
- });
- /**
- * Base class for all Ext components. All subclasses of Component may participate in the automated Ext component
- * lifecycle of creation, rendering and destruction which is provided by the {@link Ext.container.Container Container}
- * class. Components may be added to a Container through the {@link Ext.container.Container#cfg-items items} config option
- * at the time the Container is created, or they may be added dynamically via the
- * {@link Ext.container.Container#method-add add} method.
- *
- * The Component base class has built-in support for basic hide/show and enable/disable and size control behavior.
- *
- * All Components are registered with the {@link Ext.ComponentManager} on construction so that they can be referenced at
- * any time via {@link Ext#getCmp Ext.getCmp}, passing the {@link #id}.
- *
- * All user-developed visual widgets that are required to participate in automated lifecycle and size management should
- * subclass Component.
- *
- * See the Creating new UI controls chapter in [Component Guide][1] for details on how and to either extend
- * or augment Ext JS base classes to create custom Components.
- *
- * Every component has a specific xtype, which is its Ext-specific type name, along with methods for checking the xtype
- * like {@link #getXType} and {@link #isXType}. See the [Component Guide][1] for more information on xtypes and the
- * Component hierarchy.
- *
- * This is the list of all valid xtypes:
- *
- * xtype Class
- * ------------- ------------------
- * button {@link Ext.button.Button}
- * buttongroup {@link Ext.container.ButtonGroup}
- * colorpalette {@link Ext.picker.Color}
- * component {@link Ext.Component}
- * container {@link Ext.container.Container}
- * cycle {@link Ext.button.Cycle}
- * dataview {@link Ext.view.View}
- * datepicker {@link Ext.picker.Date}
- * editor {@link Ext.Editor}
- * editorgrid {@link Ext.grid.plugin.Editing}
- * grid {@link Ext.grid.Panel}
- * multislider {@link Ext.slider.Multi}
- * panel {@link Ext.panel.Panel}
- * progressbar {@link Ext.ProgressBar}
- * slider {@link Ext.slider.Single}
- * splitbutton {@link Ext.button.Split}
- * tabpanel {@link Ext.tab.Panel}
- * treepanel {@link Ext.tree.Panel}
- * viewport {@link Ext.container.Viewport}
- * window {@link Ext.window.Window}
- *
- * Toolbar components
- * ---------------------------------------
- * pagingtoolbar {@link Ext.toolbar.Paging}
- * toolbar {@link Ext.toolbar.Toolbar}
- * tbfill {@link Ext.toolbar.Fill}
- * tbitem {@link Ext.toolbar.Item}
- * tbseparator {@link Ext.toolbar.Separator}
- * tbspacer {@link Ext.toolbar.Spacer}
- * tbtext {@link Ext.toolbar.TextItem}
- *
- * Menu components
- * ---------------------------------------
- * menu {@link Ext.menu.Menu}
- * menucheckitem {@link Ext.menu.CheckItem}
- * menuitem {@link Ext.menu.Item}
- * menuseparator {@link Ext.menu.Separator}
- * menutextitem {@link Ext.menu.Item}
- *
- * Form components
- * ---------------------------------------
- * form {@link Ext.form.Panel}
- * checkbox {@link Ext.form.field.Checkbox}
- * combo {@link Ext.form.field.ComboBox}
- * datefield {@link Ext.form.field.Date}
- * displayfield {@link Ext.form.field.Display}
- * field {@link Ext.form.field.Base}
- * fieldset {@link Ext.form.FieldSet}
- * hidden {@link Ext.form.field.Hidden}
- * htmleditor {@link Ext.form.field.HtmlEditor}
- * label {@link Ext.form.Label}
- * numberfield {@link Ext.form.field.Number}
- * radio {@link Ext.form.field.Radio}
- * radiogroup {@link Ext.form.RadioGroup}
- * textarea {@link Ext.form.field.TextArea}
- * textfield {@link Ext.form.field.Text}
- * timefield {@link Ext.form.field.Time}
- * trigger {@link Ext.form.field.Trigger}
- *
- * Chart components
- * ---------------------------------------
- * chart {@link Ext.chart.Chart}
- * barchart {@link Ext.chart.series.Bar}
- * columnchart {@link Ext.chart.series.Column}
- * linechart {@link Ext.chart.series.Line}
- * piechart {@link Ext.chart.series.Pie}
- *
- * It should not usually be necessary to instantiate a Component because there are provided subclasses which implement
- * specialized Component use cases which cover most application needs. However it is possible to instantiate a base
- * Component, and it will be renderable, or will particpate in layouts as the child item of a Container:
- *
- * @example
- * Ext.create('Ext.Component', {
- * html: 'Hello world!',
- * width: 300,
- * height: 200,
- * padding: 20,
- * style: {
- * color: '#FFFFFF',
- * backgroundColor:'#000000'
- * },
- * renderTo: Ext.getBody()
- * });
- *
- * The Component above creates its encapsulating `div` upon render, and use the configured HTML as content. More complex
- * internal structure may be created using the {@link #renderTpl} configuration, although to display database-derived
- * mass data, it is recommended that an ExtJS data-backed Component such as a {@link Ext.view.View View}, or {@link
- * Ext.grid.Panel GridPanel}, or {@link Ext.tree.Panel TreePanel} be used.
- *
- * [2]: #!/guide/components
- */
- Ext.define('Ext.Component', {
- /* Begin Definitions */
- alias: ['widget.component', 'widget.box'],
- extend: 'Ext.AbstractComponent',
- requires: [
- 'Ext.util.DelayedTask'
- ],
- uses: [
- 'Ext.Layer',
- 'Ext.resizer.Resizer',
- 'Ext.util.ComponentDragger'
- ],
- mixins: {
- floating: 'Ext.util.Floating'
- },
- statics: {
- // Collapse/expand directions
- DIRECTION_TOP: 'top',
- DIRECTION_RIGHT: 'right',
- DIRECTION_BOTTOM: 'bottom',
- DIRECTION_LEFT: 'left',
- VERTICAL_DIRECTION_Re: /^(?:top|bottom)$/,
- // RegExp whih specifies characters in an xtype which must be translated to '-' when generating auto IDs.
- // This includes dot, comma and whitespace
- INVALID_ID_CHARS_Re: /[\.,\s]/g
- },
- /* End Definitions */
- /**
- * @cfg {Boolean/Object} resizable
- * Specify as `true` to apply a {@link Ext.resizer.Resizer Resizer} to this Component after rendering.
- *
- * May also be specified as a config object to be passed to the constructor of {@link Ext.resizer.Resizer Resizer}
- * to override any defaults. By default the Component passes its minimum and maximum size, and uses
- * `{@link Ext.resizer.Resizer#dynamic}: false`
- */
- /**
- * @cfg {String} resizeHandles
- * A valid {@link Ext.resizer.Resizer} handles config string. Only applies when resizable = true.
- */
- resizeHandles: 'all',
- /**
- * @cfg {Boolean} [autoScroll=false]
- * `true` to use overflow:'auto' on the components layout element and show scroll bars automatically when necessary,
- * `false` to clip any overflowing content.
- * This should not be combined with {@link #overflowX} or {@link #overflowY}.
- */
- /**
- * @cfg {String} overflowX
- * Possible values are:
- * * `'auto'` to enable automatic horizontal scrollbar (overflow-x: 'auto').
- * * `'scroll'` to always enable horizontal scrollbar (overflow-x: 'scroll').
- * The default is overflow-x: 'hidden'. This should not be combined with {@link #autoScroll}.
- */
- /**
- * @cfg {String} overflowY
- * Possible values are:
- * * `'auto'` to enable automatic vertical scrollbar (overflow-y: 'auto').
- * * `'scroll'` to always enable vertical scrollbar (overflow-y: 'scroll').
- * The default is overflow-y: 'hidden'. This should not be combined with {@link #autoScroll}.
- */
- /**
- * @cfg {Boolean} floating
- * Specify as true to float the Component outside of the document flow using CSS absolute positioning.
- *
- * Components such as {@link Ext.window.Window Window}s and {@link Ext.menu.Menu Menu}s are floating by default.
- *
- * Floating Components that are programatically {@link Ext.Component#render rendered} will register themselves with
- * the global {@link Ext.WindowManager ZIndexManager}
- *
- * ### Floating Components as child items of a Container
- *
- * A floating Component may be used as a child item of a Container. This just allows the floating Component to seek
- * a ZIndexManager by examining the ownerCt chain.
- *
- * When configured as floating, Components acquire, at render time, a {@link Ext.ZIndexManager ZIndexManager} which
- * manages a stack of related floating Components. The ZIndexManager brings a single floating Component to the top
- * of its stack when the Component's {@link #toFront} method is called.
- *
- * The ZIndexManager is found by traversing up the {@link #ownerCt} chain to find an ancestor which itself is
- * floating. This is so that descendant floating Components of floating _Containers_ (Such as a ComboBox dropdown
- * within a Window) can have its zIndex managed relative to any siblings, but always **above** that floating
- * ancestor Container.
- *
- * If no floating ancestor is found, a floating Component registers itself with the default {@link Ext.WindowManager
- * ZIndexManager}.
- *
- * Floating components _do not participate in the Container's layout_. Because of this, they are not rendered until
- * you explicitly {@link #method-show} them.
- *
- * After rendering, the ownerCt reference is deleted, and the {@link #floatParent} property is set to the found
- * floating ancestor Container. If no floating ancestor Container was found the {@link #floatParent} property will
- * not be set.
- */
- floating: false,
- /**
- * @cfg {Boolean} toFrontOnShow
- * True to automatically call {@link #toFront} when the {@link #method-show} method is called on an already visible,
- * floating component.
- */
- toFrontOnShow: true,
- /**
- * @property {Ext.ZIndexManager} zIndexManager
- * Only present for {@link #floating} Components after they have been rendered.
- *
- * A reference to the ZIndexManager which is managing this Component's z-index.
- *
- * The {@link Ext.ZIndexManager ZIndexManager} maintains a stack of floating Component z-indices, and also provides
- * a single modal mask which is insert just beneath the topmost visible modal floating Component.
- *
- * Floating Components may be {@link #toFront brought to the front} or {@link #toBack sent to the back} of the
- * z-index stack.
- *
- * This defaults to the global {@link Ext.WindowManager ZIndexManager} for floating Components that are
- * programatically {@link Ext.Component#render rendered}.
- *
- * For {@link #floating} Components which are added to a Container, the ZIndexManager is acquired from the first
- * ancestor Container found which is floating. If no floating ancestor is found, the global {@link Ext.WindowManager ZIndexManager} is
- * used.
- *
- * See {@link #floating} and {@link #zIndexParent}
- * @readonly
- */
- /**
- * @property {Ext.Container} floatParent
- * Only present for {@link #floating} Components which were inserted as child items of Containers.
- *
- * Floating Components that are programatically {@link Ext.Component#render rendered} will not have a `floatParent`
- * property.
- *
- * For {@link #floating} Components which are child items of a Container, the floatParent will be the owning Container.
- *
- * For example, the dropdown {@link Ext.view.BoundList BoundList} of a ComboBox which is in a Window will have the
- * Window as its `floatParent`
- *
- * See {@link #floating} and {@link #zIndexManager}
- * @readonly
- */
- /**
- * @property {Ext.Container} zIndexParent
- * Only present for {@link #floating} Components which were inserted as child items of Containers, and which have a floating
- * Container in their containment ancestry.
- *
- * For {@link #floating} Components which are child items of a Container, the zIndexParent will be a floating
- * ancestor Container which is responsible for the base z-index value of all its floating descendants. It provides
- * a {@link Ext.ZIndexManager ZIndexManager} which provides z-indexing services for all its descendant floating
- * Components.
- *
- * Floating Components that are programatically {@link Ext.Component#render rendered} will not have a `zIndexParent`
- * property.
- *
- * For example, the dropdown {@link Ext.view.BoundList BoundList} of a ComboBox which is in a Window will have the
- * Window as its `zIndexParent`, and will always show above that Window, wherever the Window is placed in the z-index stack.
- *
- * See {@link #floating} and {@link #zIndexManager}
- * @readonly
- */
- /**
- * @cfg {Boolean/Object} [draggable=false]
- * Specify as true to make a {@link #floating} Component draggable using the Component's encapsulating element as
- * the drag handle.
- *
- * This may also be specified as a config object for the {@link Ext.util.ComponentDragger ComponentDragger} which is
- * instantiated to perform dragging.
- *
- * For example to create a Component which may only be dragged around using a certain internal element as the drag
- * handle, use the delegate option:
- *
- * new Ext.Component({
- * constrain: true,
- * floating: true,
- * style: {
- * backgroundColor: '#fff',
- * border: '1px solid black'
- * },
- * html: '<h1 style="cursor:move">The title</h1><p>The content</p>',
- * draggable: {
- * delegate: 'h1'
- * }
- * }).show();
- */
- hideMode: 'display',
- // Deprecate 5.0
- hideParent: false,
- bubbleEvents: [],
- actionMode: 'el',
- monPropRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
- //renderTpl: new Ext.XTemplate(
- // '<div id="{id}" class="{baseCls} {cls} {cmpCls}<tpl if="typeof ui !== \'undefined\'"> {uiBase}-{ui}</tpl>"<tpl if="typeof style !== \'undefined\'"> style="{style}"</tpl>></div>', {
- // compiled: true,
- // disableFormats: true
- // }
- //),
- /**
- * Creates new Component.
- * @param {Ext.Element/String/Object} config The configuration options may be specified as either:
- *
- * - **an element** : it is set as the internal element and its id used as the component id
- * - **a string** : it is assumed to be the id of an existing element and is used as the component id
- * - **anything else** : it is assumed to be a standard config object and is applied to the component
- */
- constructor: function(config) {
- var me = this;
- config = config || {};
- if (config.initialConfig) {
- // Being initialized from an Ext.Action instance...
- if (config.isAction) {
- me.baseAction = config;
- }
- config = config.initialConfig;
- // component cloning / action set up
- }
- else if (config.tagName || config.dom || Ext.isString(config)) {
- // element object
- config = {
- applyTo: config,
- id: config.id || config
- };
- }
- me.callParent([config]);
- // If we were configured from an instance of Ext.Action, (or configured with a baseAction option),
- // register this Component as one of its items
- if (me.baseAction){
- me.baseAction.addComponent(me);
- }
- },
- /**
- * The initComponent template method is an important initialization step for a Component. It is intended to be
- * implemented by each subclass of Ext.Component to provide any needed constructor logic. The
- * initComponent method of the class being created is called first, with each initComponent method
- * up the hierarchy to Ext.Component being called thereafter. This makes it easy to implement and,
- * if needed, override the constructor logic of the Component at any step in the hierarchy.
- *
- * The initComponent method **must** contain a call to {@link Ext.Base#callParent callParent} in order
- * to ensure that the parent class' initComponent method is also called.
- *
- * All config options passed to the constructor are applied to `this` before initComponent is called,
- * so you can simply access them with `this.someOption`.
- *
- * The following example demonstrates using a dynamic string for the text of a button at the time of
- * instantiation of the class.
- *
- * Ext.define('DynamicButtonText', {
- * extend: 'Ext.button.Button',
- *
- * initComponent: function() {
- * this.text = new Date();
- * this.renderTo = Ext.getBody();
- * this.callParent();
- * }
- * });
- *
- * Ext.onReady(function() {
- * Ext.create('DynamicButtonText');
- * });
- *
- * @template
- * @protected
- */
- initComponent: function() {
- var me = this;
- me.callParent();
- if (me.listeners) {
- me.on(me.listeners);
- me.listeners = null; //change the value to remove any on prototype
- }
- me.enableBubble(me.bubbleEvents);
- me.mons = [];
- },
- // private
- afterRender: function() {
- var me = this;
- me.callParent();
- if (!(me.x && me.y) && (me.pageX || me.pageY)) {
- me.setPagePosition(me.pageX, me.pageY);
- }
- if (me.resizable) {
- me.initResizable(me.resizable);
- }
- if (me.draggable) {
- me.initDraggable();
- }
- },
- /**
- * Sets the overflow on the content element of the component.
- * @param {Boolean} scroll True to allow the Component to auto scroll.
- * @return {Ext.Component} this
- */
- setAutoScroll : function(scroll) {
- var me = this,
- layout,
- targetEl;
- me.autoScroll = !!scroll;
- // Scrolling styles must be applied to the element into which content is rendered.
- // This means the layout's target if we are using a layout.
- if (me.rendered) {
- targetEl = (layout = me.getLayout && me.getLayout()) ? layout.getRenderTarget() : me.getTargetEl();
- targetEl.setStyle(me.getOverflowStyle());
- }
- return me;
- },
- /**
- * Sets the overflow x/y on the content element of the component. The x/y overflow
- * values can be any valid CSS overflow (e.g., 'auto' or 'scroll'). By default, the
- * value is 'hidden'. Passing null for one of the values will erase the inline style.
- * Passing `undefined` will preserve the current value.
- *
- * @param {String} overflowX The overflow-x value.
- * @param {String} overflowY The overflow-y value.
- * @return {Ext.Component} this
- */
- setOverflowXY: function(overflowX, overflowY) {
- var me = this,
- layout,
- targetEl,
- argCount = arguments.length;
- if (argCount) {
- me.overflowX = overflowX || '';
- if (argCount > 1) {
- me.overflowY = overflowY || '';
- }
- }
- // Scrolling styles must be applied to the element into which content is rendered.
- // This means the layout's target if we are using a layout.
- if (me.rendered) {
- targetEl = (layout = me.getLayout && me.getLayout()) ? layout.getRenderTarget() : me.getTargetEl();
- targetEl.setStyle(me.getOverflowStyle());
- }
- return me;
- },
- beforeRender: function () {
- var me = this,
- floating = me.floating,
- cls;
- if (floating) {
- me.addCls(Ext.baseCSSPrefix + 'layer');
- cls = floating.cls;
- if (cls) {
- me.addCls(cls);
- }
- }
- return me.callParent();
- },
- // private
- makeFloating : function (dom) {
- this.mixins.floating.constructor.call(this, dom);
- },
- wrapPrimaryEl: function (dom) {
- if (this.floating) {
- this.makeFloating(dom);
- } else {
- this.callParent(arguments);
- }
- },
- initResizable: function(resizable) {
- var me = this;
- resizable = Ext.apply({
- target: me,
- dynamic: false,
- constrainTo: me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : null),
- handles: me.resizeHandles
- }, resizable);
- resizable.target = me;
- me.resizer = new Ext.resizer.Resizer(resizable);
- },
- getDragEl: function() {
- return this.el;
- },
- initDraggable: function() {
- var me = this,
- ddConfig = Ext.applyIf({
- el: me.getDragEl(),
- constrainTo: me.constrain ? (me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : me.el.getScopeParent())) : undefined
- }, me.draggable);
- // Add extra configs if Component is specified to be constrained
- if (me.constrain || me.constrainDelegate) {
- ddConfig.constrain = me.constrain;
- ddConfig.constrainDelegate = me.constrainDelegate;
- }
- me.dd = new Ext.util.ComponentDragger(me, ddConfig);
- },
- /**
- * Scrolls this Component's {@link #getTargetEl target element} by the passed delta values, optionally animating.
- *
- * All of the following are equivalent:
- *
- * comp.scrollBy(10, 10, true);
- * comp.scrollBy([10, 10], true);
- * comp.scrollBy({ x: 10, y: 10 }, true);
- *
- * @param {Number/Number[]/Object} deltaX Either the x delta, an Array specifying x and y deltas or
- * an object with "x" and "y" properties.
- * @param {Number/Boolean/Object} deltaY Either the y delta, or an animate flag or config object.
- * @param {Boolean/Object} animate Animate flag/config object if the delta values were passed separately.
- */
- scrollBy: function(deltaX, deltaY, animate) {
- var el;
- if ((el = this.getTargetEl()) && el.dom) {
- el.scrollBy.apply(el, arguments);
- }
- },
- /**
- * This method allows you to show or hide a LoadMask on top of this component.
- *
- * @param {Boolean/Object/String} load True to show the default LoadMask, a config object that will be passed to the
- * LoadMask constructor, or a message String to show. False to hide the current LoadMask.
- * @param {Boolean} [targetEl=false] True to mask the targetEl of this Component instead of the `this.el`. For example,
- * setting this to true on a Panel will cause only the body to be masked.
- * @return {Ext.LoadMask} The LoadMask instance that has just been shown.
- */
- setLoading : function(load, targetEl) {
- var me = this,
- config;
- if (me.rendered) {
- Ext.destroy(me.loadMask);
- me.loadMask = null;
- if (load !== false && !me.collapsed) {
- if (Ext.isObject(load)) {
- config = Ext.apply({}, load);
- } else if (Ext.isString(load)) {
- config = {msg: load};
- } else {
- config = {};
- }
- if (targetEl) {
- Ext.applyIf(config, {
- useTargetEl: true
- });
- }
- me.loadMask = new Ext.LoadMask(me, config);
- me.loadMask.show();
- }
- }
- return me.loadMask;
- },
- beforeSetPosition: function () {
- var me = this,
- pos = me.callParent(arguments), // pass all args on for signature decoding
- adj;
- if (pos) {
- adj = me.adjustPosition(pos.x, pos.y);
- pos.x = adj.x;
- pos.y = adj.y;
- }
- return pos || null;
- },
- afterSetPosition: function(ax, ay) {
- this.onPosition(ax, ay);
- this.fireEvent('move', this, ax, ay);
- },
- /**
- * Displays component at specific xy position.
- * A floating component (like a menu) is positioned relative to its ownerCt if any.
- * Useful for popping up a context menu:
- *
- * listeners: {
- * itemcontextmenu: function(view, record, item, index, event, options) {
- * Ext.create('Ext.menu.Menu', {
- * width: 100,
- * height: 100,
- * margin: '0 0 10 0',
- * items: [{
- * text: 'regular item 1'
- * },{
- * text: 'regular item 2'
- * },{
- * text: 'regular item 3'
- * }]
- * }).showAt(event.getXY());
- * }
- * }
- *
- * @param {Number} x The new x position
- * @param {Number} y The new y position
- * @param {Boolean/Object} [animate] True to animate the Component into its new position. You may also pass an
- * animation configuration.
- */
- showAt: function(x, y, animate) {
- var me = this;
- if (!me.rendered && (me.autoRender || me.floating)) {
- me.doAutoRender();
- }
- if (me.floating) {
- me.setPosition(x, y, animate);
- } else {
- me.setPagePosition(x, y, animate);
- }
- me.show();
- },
- /**
- * Sets the page XY position of the component. To set the left and top instead, use {@link #setPosition}.
- * This method fires the {@link #move} event.
- * @param {Number} x The new x position
- * @param {Number} y The new y position
- * @param {Boolean/Object} [animate] True to animate the Component into its new position. You may also pass an
- * animation configuration.
- * @return {Ext.Component} this
- */
- setPagePosition: function(x, y, animate) {
- var me = this,
- p,
- floatParentBox;
- if (Ext.isArray(x)) {
- y = x[1];
- x = x[0];
- }
- me.pageX = x;
- me.pageY = y;
- if (me.floating) {
- // Floating Components which are registered with a Container have to have their x and y properties made relative
- if (me.isContainedFloater()) {
- floatParentBox = me.floatParent.getTargetEl().getViewRegion();
- if (Ext.isNumber(x) && Ext.isNumber(floatParentBox.left)) {
- x -= floatParentBox.left;
- }
- if (Ext.isNumber(y) && Ext.isNumber(floatParentBox.top)) {
- y -= floatParentBox.top;
- }
- }
- me.setPosition(x, y, animate);
- } else {
- p = me.el.translatePoints(x, y);
- me.setPosition(p.left, p.top, animate);
- }
- return me;
- },
- // Utility method to determine if a Component is floating, and has an owning Container whose coordinate system
- // it must be positioned in when using setPosition.
- isContainedFloater: function() {
- return (this.floating && this.floatParent);
- },
- /**
- * Gets the current box measurements of the component's underlying element.
- * @param {Boolean} [local=false] If true the element's left and top are returned instead of page XY.
- * @return {Object} box An object in the format {x, y, width, height}
- */
- getBox : function(local){
- var pos = local ? this.getPosition(local) : this.el.getXY(),
- size = this.getSize();
- size.x = pos[0];
- size.y = pos[1];
- return size;
- },
- /**
- * Sets the current box measurements of the component's underlying element.
- * @param {Object} box An object in the format {x, y, width, height}
- * @return {Ext.Component} this
- */
- updateBox : function(box){
- this.setSize(box.width, box.height);
- this.setPagePosition(box.x, box.y);
- return this;
- },
- // Include margins
- getOuterSize: function() {
- var el = this.el;
- return {
- width: el.getWidth() + el.getMargin('lr'),
- height: el.getHeight() + el.getMargin('tb')
- };
- },
- // private
- adjustPosition: function(x, y) {
- var me = this,
- floatParentBox;
- // Floating Components being positioned in their ownerCt have to be made absolute.
- if (me.isContainedFloater()) {
- floatParentBox = me.floatParent.getTargetEl().getViewRegion();
- x += floatParentBox.left;
- y += floatParentBox.top;
- }
- return {
- x: x,
- y: y
- };
- },
- /**
- * Gets the current XY position of the component's underlying element.
- * @param {Boolean} [local=false] If true the element's left and top are returned instead of page XY.
- * @return {Number[]} The XY position of the element (e.g., [100, 200])
- */
- getPosition: function(local) {
- var me = this,
- el = me.el,
- xy,
- isContainedFloater = me.isContainedFloater(),
- floatParentBox;
- // Floating Components which were just rendered with no ownerCt return local position.
- if ((local === true) || !isContainedFloater) {
- return [el.getLeft(true), el.getTop(true)];
- }
- // Use our previously set x and y properties if possible.
- if (me.x !== undefined && me.y !== undefined) {
- xy = [me.x, me.y];
- } else {
- xy = me.el.getXY();
- // Floating Components in an ownerCt have to have their positions made relative
- if (isContainedFloater) {
- floatParentBox = me.floatParent.getTargetEl().getViewRegion();
- xy[0] -= floatParentBox.left;
- xy[1] -= floatParentBox.top;
- }
- }
- return xy;
- },
- getId: function() {
- var me = this,
- xtype;
- if (!me.id) {
- xtype = me.getXType();
- if (xtype) {
- xtype = xtype.replace(Ext.Component.INVALID_ID_CHARS_Re, '-');
- } else {
- xtype = Ext.name.toLowerCase() + '-comp';
- }
- me.id = xtype + '-' + me.getAutoId();
- }
- return me.id;
- },
- /**
- * Shows this Component, rendering it first if {@link #autoRender} or {@link #floating} are `true`.
- *
- * After being shown, a {@link #floating} Component (such as a {@link Ext.window.Window}), is activated it and
- * brought to the front of its {@link #zIndexManager z-index stack}.
- *
- * @param {String/Ext.Element} [animateTarget=null] **only valid for {@link #floating} Components such as {@link
- * Ext.window.Window Window}s or {@link Ext.tip.ToolTip ToolTip}s, or regular Components which have been configured
- * with `floating: true`.** The target from which the Component should animate from while opening.
- * @param {Function} [callback] A callback function to call after the Component is displayed.
- * Only necessary if animation was specified.
- * @param {Object} [scope] The scope (`this` reference) in which the callback is executed.
- * Defaults to this Component.
- * @return {Ext.Component} this
- */
- show: function(animateTarget, cb, scope) {
- var me = this;
- if (me.rendered && me.isVisible()) {
- if (me.toFrontOnShow && me.floating) {
- me.toFront();
- }
- } else if (me.fireEvent('beforeshow', me) !== false) {
- me.hidden = false;
- // Render on first show if there is an autoRender config, or if this is a floater (Window, Menu, BoundList etc).
- if (!me.rendered && (me.autoRender || me.floating)) {
- me.doAutoRender();
- }
- if (me.rendered) {
- me.beforeShow();
- me.onShow.apply(me, arguments);
- me.afterShow.apply(me, arguments);
- }
- }
- return me;
- },
- /**
- * Invoked before the Component is shown.
- *
- * @method
- * @template
- * @protected
- */
- beforeShow: Ext.emptyFn,
- /**
- * Allows addition of behavior to the show operation. After
- * calling the superclass's onShow, the Component will be visible.
- *
- * Override in subclasses where more complex behaviour is needed.
- *
- * Gets passed the same parameters as #show.
- *
- * @param {String/Ext.Element} [animateTarget]
- * @param {Function} [callback]
- * @param {Object} [scope]
- *
- * @template
- * @protected
- */
- onShow: function() {
- var me = this;
- me.el.show();
- me.callParent(arguments);
- if (me.floating && me.constrain) {
- me.doConstrain();
- }
- },
- /**
- * Invoked after the Component is shown (after #onShow is called).
- *
- * Gets passed the same parameters as #show.
- *
- * @param {String/Ext.Element} [animateTarget]
- * @param {Function} [callback]
- * @param {Object} [scope]
- *
- * @template
- * @protected
- */
- afterShow: function(animateTarget, cb, scope) {
- var me = this,
- fromBox,
- toBox,
- ghostPanel;
- // Default to configured animate target if none passed
- animateTarget = animateTarget || me.animateTarget;
- // Need to be able to ghost the Component
- if (!me.ghost) {
- animateTarget = null;
- }
- // If we're animating, kick of an animation of the ghost from the target to the *Element* current box
- if (animateTarget) {
- animateTarget = animateTarget.el ? animateTarget.el : Ext.get(animateTarget);
- toBox = me.el.getBox();
- fromBox = animateTarget.getBox();
- me.el.addCls(Ext.baseCSSPrefix + 'hide-offsets');
- ghostPanel = me.ghost();
- ghostPanel.el.stopAnimation();
- // Shunting it offscreen immediately, *before* the Animation class grabs it ensure no flicker.
- ghostPanel.el.setX(-10000);
- ghostPanel.el.animate({
- from: fromBox,
- to: toBox,
- listeners: {
- afteranimate: function() {
- delete ghostPanel.componentLayout.lastComponentSize;
- me.unghost();
- me.el.removeCls(Ext.baseCSSPrefix + 'hide-offsets');
- me.onShowComplete(cb, scope);
- }
- }
- });
- }
- else {
- me.onShowComplete(cb, scope);
- }
- },
- /**
- * Invoked after the #afterShow method is complete.
- *
- * Gets passed the same `callback` and `scope` parameters that #afterShow received.
- *
- * @param {Function} [callback]
- * @param {Object} [scope]
- *
- * @template
- * @protected
- */
- onShowComplete: function(cb, scope) {
- var me = this;
- if (me.floating) {
- me.toFront();
- me.onFloatShow();
- }
- Ext.callback(cb, scope || me);
- me.fireEvent('show', me);
- delete me.hiddenByLayout;
- },
- /**
- * Hides this Component, setting it to invisible using the configured {@link #hideMode}.
- * @param {String/Ext.Element/Ext.Component} [animateTarget=null] **only valid for {@link #floating} Components
- * such as {@link Ext.window.Window Window}s or {@link Ext.tip.ToolTip ToolTip}s, or regular Components which have
- * been configured with `floating: true`.**. The target to which the Component should animate while hiding.
- * @param {Function} [callback] A callback function to call after the Component is hidden.
- * @param {Object} [scope] The scope (`this` reference) in which the callback is executed.
- * Defaults to this Component.
- * @return {Ext.Component} this
- */
- hide: function() {
- var me = this;
- // Clear the flag which is set if a floatParent was hidden while this is visible.
- // If a hide operation was subsequently called, that pending show must be hidden.
- me.showOnParentShow = false;
- if (!(me.rendered && !me.isVisible()) && me.fireEvent('beforehide', me) !== false) {
- me.hidden = true;
- if (me.rendered) {
- me.onHide.apply(me, arguments);
- }
- }
- return me;
- },
- /**
- * Possibly animates down to a target element.
- *
- * Allows addition of behavior to the hide operation. After
- * calling the superclass’s onHide, the Component will be hidden.
- *
- * Gets passed the same parameters as #hide.
- *
- * @param {String/Ext.Element/Ext.Component} [animateTarget]
- * @param {Function} [callback]
- * @param {Object} [scope]
- *
- * @template
- * @protected
- */
- onHide: function(animateTarget, cb, scope) {
- var me = this,
- ghostPanel,
- toBox;
- // Default to configured animate target if none passed
- animateTarget = animateTarget || me.animateTarget;
- // Need to be able to ghost the Component
- if (!me.ghost) {
- animateTarget = null;
- }
- // If we're animating, kick off an animation of the ghost down to the target
- if (animateTarget) {
- animateTarget = animateTarget.el ? animateTarget.el : Ext.get(animateTarget);
- ghostPanel = me.ghost();
- ghostPanel.el.stopAnimation();
- toBox = animateTarget.getBox();
- toBox.width += 'px';
- toBox.height += 'px';
- ghostPanel.el.animate({
- to: toBox,
- listeners: {
- afteranimate: function() {
- delete ghostPanel.componentLayout.lastComponentSize;
- ghostPanel.el.hide();
- me.afterHide(cb, scope);
- }
- }
- });
- }
- me.el.hide();
- if (!animateTarget) {
- me.afterHide(cb, scope);
- }
- },
- /**
- * Invoked after the Component has been hidden.
- *
- * Gets passed the same `callback` and `scope` parameters that #onHide received.
- *
- * @param {Function} [callback]
- * @param {Object} [scope]
- *
- * @template
- * @protected
- */
- afterHide: function(cb, scope) {
- var me = this;
- delete me.hiddenByLayout;
- // we are the back-end method of onHide at this level, but our call to our parent
- // may need to be async... so callParent won't quite work here...
- Ext.AbstractComponent.prototype.onHide.call(this);
- Ext.callback(cb, scope || me);
- me.fireEvent('hide', me);
- },
- /**
- * Allows addition of behavior to the destroy operation.
- * After calling the superclass’s onDestroy, the Component will be destroyed.
- *
- * @template
- * @protected
- */
- onDestroy: function() {
- var me = this;
- // Ensure that any ancillary components are destroyed.
- if (me.rendered) {
- Ext.destroy(
- me.proxy,
- me.proxyWrap,
- me.resizer
- );
- // Different from AbstractComponent
- if (me.actionMode == 'container' || me.removeMode == 'container') {
- me.container.remove();
- }
- }
- delete me.focusTask;
- me.callParent();
- },
- deleteMembers: function() {
- var args = arguments,
- len = args.length,
- i = 0;
- for (; i < len; ++i) {
- delete this[args[i]];
- }
- },
- /**
- * Try to focus this component.
- * @param {Boolean} [selectText] If applicable, true to also select the text in this component
- * @param {Boolean/Number} [delay] Delay the focus this number of milliseconds (true for 10 milliseconds).
- * @return {Ext.Component} The focused Component. Usually <code>this</code> Component. Some Containers may
- * delegate focus to a descendant Component ({@link Ext.window.Window Window}s can do this through their
- * {@link Ext.window.Window#defaultFocus defaultFocus} config option.
- */
- focus: function(selectText, delay) {
- var me = this,
- focusEl,
- focusElDom;
- if (me.rendered && !me.isDestroyed && me.isVisible(true) && (focusEl = me.getFocusEl())) {
- // getFocusEl might return a Component if a Container wishes to delegate focus to a descendant.
- // Window can do this via its defaultFocus configuration which can reference a Button.
- if (focusEl.isComponent) {
- return focusEl.focus(selectText, delay);
- }
- // If delay is wanted, queue a call to this function.
- if (delay) {
- if (!me.focusTask) {
- me.focusTask = new Ext.util.DelayedTask(me.focus);
- }
- me.focusTask.delay(Ext.isNumber(delay) ? delay : 10, null, me, [selectText, false]);
- return me;
- }
- // If it was an Element with a dom property
- if ((focusElDom = focusEl.dom)) {
- // Not a natural focus holding element, add a tab index to make it programatically focusable.
- if (focusEl.needsTabIndex()) {
- focusElDom.tabIndex = -1;
- }
- // Focus the element.
- // The focusEl has a DOM focus listener on it which invokes the Component's onFocus method
- // to perform Component-specific focus processing
- focusEl.focus();
- if (selectText === true) {
- focusElDom.select();
- }
- }
- // Focusing a floating Component brings it to the front of its stack.
- // this is performed by its zIndexManager. Pass preventFocus true to avoid recursion.
- if (me.floating) {
- me.toFront(true);
- }
- }
- return me;
- },
- // private
- blur: function() {
- var focusEl;
- if (this.rendered && (focusEl = this.getFocusEl())) {
- focusEl.blur();
- }
- return this;
- },
- getEl: function() {
- return this.el;
- },
- // Deprecate 5.0
- getResizeEl: function() {
- return this.el;
- },
- // Deprecate 5.0
- getPositionEl: function() {
- return this.el;
- },
- // Deprecate 5.0
- getActionEl: function() {
- return this.el;
- },
- // Deprecate 5.0
- getVisibilityEl: function() {
- return this.el;
- },
- // Deprecate 5.0
- onResize: Ext.emptyFn,
- // private
- getBubbleTarget: function() {
- return this.ownerCt;
- },
- // private
- getContentTarget: function() {
- return this.el;
- },
- /**
- * Clone the current component using the original config values passed into this instance by default.
- * @param {Object} overrides A new config containing any properties to override in the cloned version.
- * An id property can be passed on this object, otherwise one will be generated to avoid duplicates.
- * @return {Ext.Component} clone The cloned copy of this component
- */
- cloneConfig: function(overrides) {
- overrides = overrides || {};
- var id = overrides.id || Ext.id(),
- cfg = Ext.applyIf(overrides, this.initialConfig),
- self;
- cfg.id = id;
- self = Ext.getClass(this);
- // prevent dup id
- return new self(cfg);
- },
- /**
- * Gets the xtype for this component as registered with {@link Ext.ComponentManager}. For a list of all available
- * xtypes, see the {@link Ext.Component} header. Example usage:
- *
- * var t = new Ext.form.field.Text();
- * alert(t.getXType()); // alerts 'textfield'
- *
- * @return {String} The xtype
- */
- getXType: function() {
- return this.self.xtype;
- },
- /**
- * Find a container above this component at any level by a custom function. If the passed function returns true, the
- * container will be returned.
- * @param {Function} fn The custom function to call with the arguments (container, this component).
- * @return {Ext.container.Container} The first Container for which the custom function returns true
- */
- findParentBy: function(fn) {
- var p;
- // Iterate up the ownerCt chain until there's no ownerCt, or we find an ancestor which matches using the selector function.
- for (p = this.ownerCt; p && !fn(p, this); p = p.ownerCt) {
- // do nothing
- }
- return p || null;
- },
- /**
- * Find a container above this component at any level by xtype or class
- *
- * See also the {@link Ext.Component#up up} method.
- *
- * @param {String/Ext.Class} xtype The xtype string for a component, or the class of the component directly
- * @return {Ext.container.Container} The first Container which matches the given xtype or class
- */
- findParentByType: function(xtype) {
- return Ext.isFunction(xtype) ?
- this.findParentBy(function(p) {
- return p.constructor === xtype;
- })
- :
- this.up(xtype);
- },
- /**
- * Bubbles up the component/container heirarchy, calling the specified function with each component. The scope
- * (*this*) of function call will be the scope provided or the current component. The arguments to the function will
- * be the args provided or the current component. If the function returns false at any point, the bubble is stopped.
- *
- * @param {Function} fn The function to call
- * @param {Object} [scope] The scope of the function. Defaults to current node.
- * @param {Array} [args] The args to call the function with. Defaults to passing the current component.
- * @return {Ext.Component} this
- */
- bubble: function(fn, scope, args) {
- var p = this;
- while (p) {
- if (fn.apply(scope || p, args || [p]) === false) {
- break;
- }
- p = p.ownerCt;
- }
- return this;
- },
- getProxy: function() {
- var me = this,
- target;
- if (!me.proxy) {
- target = Ext.getBody();
- if (Ext.scopeResetCSS) {
- me.proxyWrap = target = Ext.getBody().createChild({
- cls: Ext.resetCls
- });
- }
- me.proxy = me.el.createProxy(Ext.baseCSSPrefix + 'proxy-el', target, true);
- }
- return me.proxy;
- }
- });
- /**
- * @class Ext.app.EventBus
- * @private
- */
- Ext.define('Ext.app.EventBus', {
- requires: [
- 'Ext.util.Event',
- 'Ext.Component'
- ],
- mixins: {
- observable: 'Ext.util.Observable'
- },
- constructor: function() {
- this.mixins.observable.constructor.call(this);
- this.bus = {};
- var me = this;
- Ext.override(Ext.Component, {
- fireEvent: function(ev) {
- if (Ext.util.Observable.prototype.fireEvent.apply(this, arguments) !== false) {
- return me.dispatch.call(me, ev, this, arguments);
- }
- return false;
- }
- });
- },
- dispatch: function(ev, target, args) {
- var bus = this.bus,
- selectors = bus[ev],
- selector, controllers, id, events, event, i, ln;
- if (selectors) {
- // Loop over all the selectors that are bound to this event
- for (selector in selectors) {
- // Check if the target matches the selector
- if (selectors.hasOwnProperty(selector) && target.is(selector)) {
- // Loop over all the controllers that are bound to this selector
- controllers = selectors[selector];
- for (id in controllers) {
- if (controllers.hasOwnProperty(id)) {
- // Loop over all the events that are bound to this selector on this controller
- events = controllers[id];
- for (i = 0, ln = events.length; i < ln; i++) {
- event = events[i];
- // Fire the event!
- if (event.fire.apply(event, Array.prototype.slice.call(args, 1)) === false) {
- return false;
- }
- }
- }
- }
- }
- }
- }
- return true;
- },
- control: function(selectors, listeners, controller) {
- var bus = this.bus,
- selector, options, listener, scope, event, listenerList, ev;
- if (Ext.isString(selectors)) {
- selector = selectors;
- selectors = {};
- selectors[selector] = listeners;
- this.control(selectors, null, controller);
- return;
- }
- for (selector in selectors) {
- if (selectors.hasOwnProperty(selector)) {
- listenerList = selectors[selector] || {};
- //inner loop
- for (ev in listenerList) {
- if (listenerList.hasOwnProperty(ev)) {
- options = {};
- listener = listenerList[ev];
- scope = controller;
- event = new Ext.util.Event(controller, ev);
- // Normalize the listener
- if (Ext.isObject(listener)) {
- options = listener;
- listener = options.fn;
- scope = options.scope || controller;
- delete options.fn;
- delete options.scope;
- }
- event.addListener(listener, scope, options);
- // Create the bus tree if it is not there yet
- bus[ev] = bus[ev] || {};
- bus[ev][selector] = bus[ev][selector] || {};
- bus[ev][selector][controller.id] = bus[ev][selector][controller.id] || [];
- // Push our listener in our bus
- bus[ev][selector][controller.id].push(event);
- }
- } //end inner loop
- }
- } //end outer loop
- }
- });
- /**
- * @author Ed Spencer
- *
- * Readers are used to interpret data to be loaded into a {@link Ext.data.Model Model} instance or a {@link
- * Ext.data.Store Store} - often in response to an AJAX request. In general there is usually no need to create
- * a Reader instance directly, since a Reader is almost always used together with a {@link Ext.data.proxy.Proxy Proxy},
- * and is configured using the Proxy's {@link Ext.data.proxy.Proxy#cfg-reader reader} configuration property:
- *
- * Ext.create('Ext.data.Store', {
- * model: 'User',
- * proxy: {
- * type: 'ajax',
- * url : 'users.json',
- * reader: {
- * type: 'json',
- * root: 'users'
- * }
- * },
- * });
- *
- * The above reader is configured to consume a JSON string that looks something like this:
- *
- * {
- * "success": true,
- * "users": [
- * { "name": "User 1" },
- * { "name": "User 2" }
- * ]
- * }
- *
- *
- * # Loading Nested Data
- *
- * Readers have the ability to automatically load deeply-nested data objects based on the {@link Ext.data.association.Association
- * associations} configured on each Model. Below is an example demonstrating the flexibility of these associations in a
- * fictional CRM system which manages a User, their Orders, OrderItems and Products. First we'll define the models:
- *
- * Ext.define("User", {
- * extend: 'Ext.data.Model',
- * fields: [
- * 'id', 'name'
- * ],
- *
- * hasMany: {model: 'Order', name: 'orders'},
- *
- * proxy: {
- * type: 'rest',
- * url : 'users.json',
- * reader: {
- * type: 'json',
- * root: 'users'
- * }
- * }
- * });
- *
- * Ext.define("Order", {
- * extend: 'Ext.data.Model',
- * fields: [
- * 'id', 'total'
- * ],
- *
- * hasMany : {model: 'OrderItem', name: 'orderItems', associationKey: 'order_items'},
- * belongsTo: 'User'
- * });
- *
- * Ext.define("OrderItem", {
- * extend: 'Ext.data.Model',
- * fields: [
- * 'id', 'price', 'quantity', 'order_id', 'product_id'
- * ],
- *
- * belongsTo: ['Order', {model: 'Product', associationKey: 'product'}]
- * });
- *
- * Ext.define("Product", {
- * extend: 'Ext.data.Model',
- * fields: [
- * 'id', 'name'
- * ],
- *
- * hasMany: 'OrderItem'
- * });
- *
- * This may be a lot to take in - basically a User has many Orders, each of which is composed of several OrderItems.
- * Finally, each OrderItem has a single Product. This allows us to consume data like this:
- *
- * {
- * "users": [
- * {
- * "id": 123,
- * "name": "Ed",
- * "orders": [
- * {
- * "id": 50,
- * "total": 100,
- * "order_items": [
- * {
- * "id" : 20,
- * "price" : 40,
- * "quantity": 2,
- * "product" : {
- * "id": 1000,
- * "name": "MacBook Pro"
- * }
- * },
- * {
- * "id" : 21,
- * "price" : 20,
- * "quantity": 3,
- * "product" : {
- * "id": 1001,
- * "name": "iPhone"
- * }
- * }
- * ]
- * }
- * ]
- * }
- * ]
- * }
- *
- * The JSON response is deeply nested - it returns all Users (in this case just 1 for simplicity's sake), all of the
- * Orders for each User (again just 1 in this case), all of the OrderItems for each Order (2 order items in this case),
- * and finally the Product associated with each OrderItem. Now we can read the data and use it as follows:
- *
- * var store = Ext.create('Ext.data.Store', {
- * model: "User"
- * });
- *
- * store.load({
- * callback: function() {
- * //the user that was loaded
- * var user = store.first();
- *
- * console.log("Orders for " + user.get('name') + ":")
- *
- * //iterate over the Orders for each User
- * user.orders().each(function(order) {
- * console.log("Order ID: " + order.getId() + ", which contains items:");
- *
- * //iterate over the OrderItems for each Order
- * order.orderItems().each(function(orderItem) {
- * //we know that the Product data is already loaded, so we can use the synchronous getProduct
- * //usually, we would use the asynchronous version (see {@link Ext.data.association.BelongsTo})
- * var product = orderItem.getProduct();
- *
- * console.log(orderItem.get('quantity') + ' orders of ' + product.get('name'));
- * });
- * });
- * }
- * });
- *
- * Running the code above results in the following:
- *
- * Orders for Ed:
- * Order ID: 50, which contains items:
- * 2 orders of MacBook Pro
- * 3 orders of iPhone
- */
- Ext.define('Ext.data.reader.Reader', {
- requires: ['Ext.data.ResultSet'],
- alternateClassName: ['Ext.data.Reader', 'Ext.data.DataReader'],
-
- /**
- * @cfg {String} idProperty
- * Name of the property within a row object that contains a record identifier value. Defaults to The id of the
- * model. If an idProperty is explicitly specified it will override that of the one specified on the model
- */
- /**
- * @cfg {String} totalProperty
- * Name of the property from which to retrieve the total number of records in the dataset. This is only needed if
- * the whole dataset is not passed in one go, but is being paged from the remote server. Defaults to total.
- */
- totalProperty: 'total',
- /**
- * @cfg {String} successProperty
- * Name of the property from which to retrieve the success attribute. Defaults to success. See
- * {@link Ext.data.proxy.Server}.{@link Ext.data.proxy.Server#exception exception} for additional information.
- */
- successProperty: 'success',
- /**
- * @cfg {String} root
- * The name of the property which contains the Array of row objects. For JSON reader it's dot-separated list
- * of property names. For XML reader it's a CSS selector. For array reader it's not applicable.
- *
- * By default the natural root of the data will be used. The root Json array, the root XML element, or the array.
- *
- * The data packet value for this property should be an empty array to clear the data or show no data.
- */
- root: '',
-
- /**
- * @cfg {String} messageProperty
- * The name of the property which contains a response message. This property is optional.
- */
-
- /**
- * @cfg {Boolean} implicitIncludes
- * True to automatically parse models nested within other models in a response object. See the
- * Ext.data.reader.Reader intro docs for full explanation. Defaults to true.
- */
- implicitIncludes: true,
-
- /**
- * @property {Object} metaData
- * The raw meta data that was most recently read, if any. Meta data can include existing
- * Reader config options like {@link #idProperty}, {@link #totalProperty}, etc. that get
- * automatically applied to the Reader, and those can still be accessed directly from the Reader
- * if needed. However, meta data is also often used to pass other custom data to be processed
- * by application code. For example, it is common when reconfiguring the data model of a grid to
- * also pass a corresponding column model config to be applied to the grid. Any such data will
- * not get applied to the Reader directly (it just gets passed through and is ignored by Ext).
- * This metaData property gives you access to all meta data that was passed, including any such
- * custom data ignored by the reader.
- *
- * This is a read-only property, and it will get replaced each time a new meta data object is
- * passed to the reader. Note that typically you would handle proxy's
- * {@link Ext.data.proxy.Proxy#metachange metachange} event which passes this exact same meta
- * object to listeners. However this property is available if it's more convenient to access it
- * via the reader directly in certain cases.
- * @readonly
- */
-
- /*
- * @property {Boolean} isReader
- * `true` in this class to identify an objact as an instantiated Reader, or subclass thereof.
- */
- isReader: true,
-
- /**
- * Creates new Reader.
- * @param {Object} config (optional) Config object.
- */
- constructor: function(config) {
- var me = this;
-
- Ext.apply(me, config || {});
- me.fieldCount = 0;
- me.model = Ext.ModelManager.getModel(config.model);
- if (me.model) {
- me.buildExtractors();
- }
- },
- /**
- * Sets a new model for the reader.
- * @private
- * @param {Object} model The model to set.
- * @param {Boolean} setOnProxy True to also set on the Proxy, if one is configured
- */
- setModel: function(model, setOnProxy) {
- var me = this;
-
- me.model = Ext.ModelManager.getModel(model);
- me.buildExtractors(true);
-
- if (setOnProxy && me.proxy) {
- me.proxy.setModel(me.model, true);
- }
- },
- /**
- * Reads the given response object. This method normalizes the different types of response object that may be passed
- * to it, before handing off the reading of records to the {@link #readRecords} function.
- * @param {Object} response The response object. This may be either an XMLHttpRequest object or a plain JS object
- * @return {Ext.data.ResultSet} The parsed ResultSet object
- */
- read: function(response) {
- var data = response;
-
- if (response && response.responseText) {
- data = this.getResponseData(response);
- }
-
- if (data) {
- return this.readRecords(data);
- } else {
- return this.nullResultSet;
- }
- },
- /**
- * Abstracts common functionality used by all Reader subclasses. Each subclass is expected to call this function
- * before running its own logic and returning the Ext.data.ResultSet instance. For most Readers additional
- * processing should not be needed.
- * @param {Object} data The raw data object
- * @return {Ext.data.ResultSet} A ResultSet object
- */
- readRecords: function(data) {
- var me = this;
-
- /*
- * We check here whether the number of fields has changed since the last read.
- * This works around an issue when a Model is used for both a Tree and another
- * source, because the tree decorates the model with extra fields and it causes
- * issues because the readers aren't notified.
- */
- if (me.fieldCount !== me.getFields().length) {
- me.buildExtractors(true);
- }
-
- /**
- * @property {Object} rawData
- * The raw data object that was last passed to readRecords. Stored for further processing if needed
- */
- me.rawData = data;
- data = me.getData(data);
-
- var success = true,
- recordCount = 0,
- records = [],
- root, total, value, message;
-
- if (me.successProperty) {
- value = me.getSuccess(data);
- if (value === false || value === 'false') {
- success = false;
- }
- }
-
- if (me.messageProperty) {
- message = me.getMessage(data);
- }
-
- // Only try and extract other data if call was successful
- if (success) {
- // If we pass an array as the data, we dont use getRoot on the data.
- // Instead the root equals to the data.
- root = Ext.isArray(data) ? data : me.getRoot(data);
-
- if (root) {
- total = root.length;
- }
- if (me.totalProperty) {
- value = parseInt(me.getTotal(data), 10);
- if (!isNaN(value)) {
- total = value;
- }
- }
- if (root) {
- records = me.extractData(root);
- recordCount = records.length;
- }
- }
- return new Ext.data.ResultSet({
- total : total || recordCount,
- count : recordCount,
- records: records,
- success: success,
- message: message
- });
- },
- /**
- * Returns extracted, type-cast rows of data.
- * @param {Object[]/Object} root from server response
- * @private
- */
- extractData : function(root) {
- var me = this,
- records = [],
- Model = me.model,
- length = root.length,
- convertedValues, node, record, i;
-
- if (!root.length && Ext.isObject(root)) {
- root = [root];
- length = 1;
- }
- for (i = 0; i < length; i++) {
- node = root[i];
- // Create a record with an empty data object.
- // Populate that data object by extracting and converting field values from raw data
- record = new Model(undefined, me.getId(node), node, convertedValues = {});
- // If the server did not include an id in the response data, the Model constructor will mark the record as phantom.
- // We need to set phantom to false here because records created from a server response using a reader by definition are not phantom records.
- record.phantom = false;
- // Use generated function to extract all fields at once
- me.convertRecordData(convertedValues, node, record);
- records.push(record);
-
- if (me.implicitIncludes) {
- me.readAssociated(record, node);
- }
- }
- return records;
- },
-
- /**
- * @private
- * Loads a record's associations from the data object. This prepopulates hasMany and belongsTo associations
- * on the record provided.
- * @param {Ext.data.Model} record The record to load associations for
- * @param {Object} data The data object
- * @return {String} Return value description
- */
- readAssociated: function(record, data) {
- var associations = record.associations.items,
- i = 0,
- length = associations.length,
- association, associationData, proxy, reader;
-
- for (; i < length; i++) {
- association = associations[i];
- associationData = this.getAssociatedDataRoot(data, association.associationKey || association.name);
-
- if (associationData) {
- reader = association.getReader();
- if (!reader) {
- proxy = association.associatedModel.proxy;
- // if the associated model has a Reader already, use that, otherwise attempt to create a sensible one
- if (proxy) {
- reader = proxy.getReader();
- } else {
- reader = new this.constructor({
- model: association.associatedName
- });
- }
- }
- association.read(record, reader, associationData);
- }
- }
- },
-
- /**
- * @private
- * Used internally by {@link #readAssociated}. Given a data object (which could be json, xml etc) for a specific
- * record, this should return the relevant part of that data for the given association name. This is only really
- * needed to support the XML Reader, which has to do a query to get the associated data object
- * @param {Object} data The raw data object
- * @param {String} associationName The name of the association to get data for (uses associationKey if present)
- * @return {Object} The root
- */
- getAssociatedDataRoot: function(data, associationName) {
- return data[associationName];
- },
-
- getFields: function() {
- return this.model.prototype.fields.items;
- },
- /**
- * @private
- * By default this function just returns what is passed to it. It can be overridden in a subclass
- * to return something else. See XmlReader for an example.
- * @param {Object} data The data object
- * @return {Object} The normalized data object
- */
- getData: function(data) {
- return data;
- },
- /**
- * @private
- * This will usually need to be implemented in a subclass. Given a generic data object (the type depends on the type
- * of data we are reading), this function should return the object as configured by the Reader's 'root' meta data config.
- * See XmlReader's getRoot implementation for an example. By default the same data object will simply be returned.
- * @param {Object} data The data object
- * @return {Object} The same data object
- */
- getRoot: function(data) {
- return data;
- },
- /**
- * Takes a raw response object (as passed to this.read) and returns the useful data segment of it. This must be
- * implemented by each subclass
- * @param {Object} response The responce object
- * @return {Object} The useful data from the response
- */
- getResponseData: function(response) {
- Ext.Error.raise("getResponseData must be implemented in the Ext.data.reader.Reader subclass");
- },
- /**
- * @private
- * Reconfigures the meta data tied to this Reader
- */
- onMetaChange : function(meta) {
- var fields = meta.fields,
- me = this,
- newModel;
-
- // save off the raw meta data
- me.metaData = meta;
-
- // set any reader-specific configs from meta if available
- me.root = meta.root || me.root;
- me.idProperty = meta.idProperty || me.idProperty;
- me.totalProperty = meta.totalProperty || me.totalProperty;
- me.successProperty = meta.successProperty || me.successProperty;
- me.messageProperty = meta.messageProperty || me.messageProperty;
-
- if (fields) {
- if (me.model) {
- me.model.setFields(fields);
- me.setModel(me.model, true);
- }
- else {
- newModel = Ext.define("Ext.data.reader.Json-Model" + Ext.id(), {
- extend: 'Ext.data.Model',
- fields: fields
- });
- if (me.idProperty) {
- // We only do this if the reader actually has a custom idProperty set,
- // otherwise let the model use its own default value. It is valid for
- // the reader idProperty to be undefined, in which case it will use the
- // model's idProperty (in getIdProperty()).
- newModel.idProperty = me.idProperty;
- }
- me.setModel(newModel, true);
- }
- }
- else {
- me.buildExtractors(true);
- }
- },
-
- /**
- * Get the idProperty to use for extracting data
- * @private
- * @return {String} The id property
- */
- getIdProperty: function(){
- return this.idProperty || this.model.prototype.idProperty;
- },
- /**
- * @private
- * This builds optimized functions for retrieving record data and meta data from an object.
- * Subclasses may need to implement their own getRoot function.
- * @param {Boolean} [force=false] True to automatically remove existing extractor functions first
- */
- buildExtractors: function(force) {
- var me = this,
- idProp = me.getIdProperty(),
- totalProp = me.totalProperty,
- successProp = me.successProperty,
- messageProp = me.messageProperty,
- accessor,
- idField,
- map;
-
- if (force === true) {
- delete me.convertRecordData;
- }
-
- if (me.convertRecordData) {
- return;
- }
- //build the extractors for all the meta data
- if (totalProp) {
- me.getTotal = me.createAccessor(totalProp);
- }
- if (successProp) {
- me.getSuccess = me.createAccessor(successProp);
- }
- if (messageProp) {
- me.getMessage = me.createAccessor(messageProp);
- }
- if (idProp) {
- idField = me.model.prototype.fields.get(idProp);
- if (idField) {
- map = idField.mapping;
- idProp = (map !== undefined && map !== null) ? map : idProp;
- }
- accessor = me.createAccessor(idProp);
- me.getId = function(record) {
- var id = accessor.call(me, record);
- return (id === undefined || id === '') ? null : id;
- };
- } else {
- me.getId = function() {
- return null;
- };
- }
- me.convertRecordData = me.buildRecordDataExtractor();
- },
- /**
- * @private
- * Return a function which will read a raw row object in the format this Reader accepts, and populates
- * a record's data object with converted data values.
- *
- * The returned function must be passed the following parameters:
- *
- * - dest A record's empty data object into which the new field value properties are injected.
- * - source A raw row data object of whatever type this Reader consumes
- * - record The record which is being populated.
- *
- */
- buildRecordDataExtractor: function() {
- var me = this,
- modelProto = me.model.prototype,
- clientIdProp = modelProto.clientIdProperty,
- fields = modelProto.fields.items,
- numFields = fields.length,
- fieldVarName = [],
- prefix = '__field',
- varName,
- i = 0,
- field,
- code = [
- 'var me = this,\n',
- ' fields = me.model.prototype.fields,\n',
- ' value,\n',
- ' internalId'
- ];
- for (; i < numFields; i++) {
- field = fields[i];
- fieldVarName[i] = '__field' + i;
- code.push(',\n ', fieldVarName[i], ' = fields.get("', field.name, '")');
- }
- code.push(';\n\n return function(dest, source, record) {\n');
- for (i = 0; i < numFields; i++) {
- field = fields[i];
- varName = fieldVarName[i];
- // createFieldAccessExpression must be implemented in subclasses to extract data from the source object in the correct way.
- code.push(' dest["' + field.name + '"]', ' = ', me.createFieldAccessExpression(field, varName, 'source'), ';\n');
- }
- // set the client id as the internalId of the record.
- // clientId handles the case where a client side record did not previously exist on the server,
- // so the server is passing back a client id that can be used to pair the server side record up with the client record
- if (clientIdProp) {
- code.push(' if (internalId = ' + me.createFieldAccessExpression({mapping: clientIdProp}, null, 'source') + ') {\n');
- code.push(' record.internalId = internalId;\n }\n');
- }
- code.push(' };');
- // Here we are creating a new Function and invoking it immediately in the scope of this Reader
- // It declares several vars capturing the configured context of this Reader, and returns a function
- // which, when passed a record data object, a raw data row in the format this Reader is configured to read,
- // and the record which is being created, will populate the record's data object from the raw row data.
- return Ext.functionFactory(code.join('')).call(me);
- },
- destroyReader: function() {
- var me = this;
- delete me.proxy;
- delete me.model;
- delete me.convertRecordData;
- delete me.getId;
- delete me.getTotal;
- delete me.getSuccess;
- delete me.getMessage;
- }
- }, function() {
- Ext.apply(this.prototype, {
- // Private. Empty ResultSet to return when response is falsy (null|undefined|empty string)
- nullResultSet: new Ext.data.ResultSet({
- total : 0,
- count : 0,
- records: [],
- success: true
- })
- });
- });
- /**
- * @author Ed Spencer
- * @class Ext.data.reader.Json
- *
- * <p>The JSON Reader is used by a Proxy to read a server response that is sent back in JSON format. This usually
- * happens as a result of loading a Store - for example we might create something like this:</p>
- *
- <pre><code>
- Ext.define('User', {
- extend: 'Ext.data.Model',
- fields: ['id', 'name', 'email']
- });
- var store = Ext.create('Ext.data.Store', {
- model: 'User',
- proxy: {
- type: 'ajax',
- url : 'users.json',
- reader: {
- type: 'json'
- }
- }
- });
- </code></pre>
- *
- * <p>The example above creates a 'User' model. Models are explained in the {@link Ext.data.Model Model} docs if you're
- * not already familiar with them.</p>
- *
- * <p>We created the simplest type of JSON Reader possible by simply telling our {@link Ext.data.Store Store}'s
- * {@link Ext.data.proxy.Proxy Proxy} that we want a JSON Reader. The Store automatically passes the configured model to the
- * Store, so it is as if we passed this instead:
- *
- <pre><code>
- reader: {
- type : 'json',
- model: 'User'
- }
- </code></pre>
- *
- * <p>The reader we set up is ready to read data from our server - at the moment it will accept a response like this:</p>
- *
- <pre><code>
- [
- {
- "id": 1,
- "name": "Ed Spencer",
- "email": "ed@sencha.com"
- },
- {
- "id": 2,
- "name": "Abe Elias",
- "email": "abe@sencha.com"
- }
- ]
- </code></pre>
- *
- * <p><u>Reading other JSON formats</u></p>
- *
- * <p>If you already have your JSON format defined and it doesn't look quite like what we have above, you can usually
- * pass JsonReader a couple of configuration options to make it parse your format. For example, we can use the
- * {@link #root} configuration to parse data that comes back like this:</p>
- *
- <pre><code>
- {
- "users": [
- {
- "id": 1,
- "name": "Ed Spencer",
- "email": "ed@sencha.com"
- },
- {
- "id": 2,
- "name": "Abe Elias",
- "email": "abe@sencha.com"
- }
- ]
- }
- </code></pre>
- *
- * <p>To parse this we just pass in a {@link #root} configuration that matches the 'users' above:</p>
- *
- <pre><code>
- reader: {
- type: 'json',
- root: 'users'
- }
- </code></pre>
- *
- * <p>Sometimes the JSON structure is even more complicated. Document databases like CouchDB often provide metadata
- * around each record inside a nested structure like this:</p>
- *
- <pre><code>
- {
- "total": 122,
- "offset": 0,
- "users": [
- {
- "id": "ed-spencer-1",
- "value": 1,
- "user": {
- "id": 1,
- "name": "Ed Spencer",
- "email": "ed@sencha.com"
- }
- }
- ]
- }
- </code></pre>
- *
- * <p>In the case above the record data is nested an additional level inside the "users" array as each "user" item has
- * additional metadata surrounding it ('id' and 'value' in this case). To parse data out of each "user" item in the
- * JSON above we need to specify the {@link #record} configuration like this:</p>
- *
- <pre><code>
- reader: {
- type : 'json',
- root : 'users',
- record: 'user'
- }
- </code></pre>
- *
- * <p><u>Response MetaData</u></p>
- *
- * The server can return metadata in its response, in addition to the record data, that describe attributes
- * of the data set itself or are used to reconfigure the Reader. To pass metadata in the response you simply
- * add a `metaData` attribute to the root of the response data. The metaData attribute can contain anything,
- * but supports a specific set of properties that are handled by the Reader if they are present:
- *
- * - {@link #root}: the property name of the root response node containing the record data
- * - {@link #idProperty}: property name for the primary key field of the data
- * - {@link #totalProperty}: property name for the total number of records in the data
- * - {@link #successProperty}: property name for the success status of the response
- * - {@link #messageProperty}: property name for an optional response message
- * - {@link Ext.data.Model#cfg-fields fields}: Config used to reconfigure the Model's fields before converting the
- * response data into records
- *
- * An initial Reader configuration containing all of these properties might look like this ("fields" would be
- * included in the Model definition, not shown):
- reader: {
- type : 'json',
- root : 'root',
- idProperty : 'id',
- totalProperty : 'total',
- successProperty: 'success',
- messageProperty: 'message'
- }
- If you were to pass a response object containing attributes different from those initially defined above, you could
- use the `metaData` attribute to reconifgure the Reader on the fly. For example:
- {
- "count": 1,
- "ok": true,
- "msg": "Users found",
- "users": [{
- "userId": 123,
- "name": "Ed Spencer",
- "email": "ed@sencha.com"
- }],
- "metaData": {
- "root": "users",
- "idProperty": 'userId',
- "totalProperty": 'count',
- "successProperty": 'ok',
- "messageProperty": 'msg'
- }
- }
- * You can also place any other arbitrary data you need into the `metaData` attribute which will be ignored by the Reader,
- * but will be accessible via the Reader's {@link #metaData} property (which is also passed to listeners via the Proxy's
- * {@link Ext.data.proxy.Proxy#metachange metachange} event (also relayed by the {@link Ext.data.AbstractStore#metachange
- * store}). Application code can then process the passed metadata in any way it chooses.
- *
- * A simple example for how this can be used would be customizing the fields for a Model that is bound to a grid. By passing
- * the `fields` property the Model will be automatically updated by the Reader internally, but that change will not be
- * reflected automatically in the grid unless you also update the column configuration. You could do this manually, or you
- * could simply pass a standard grid {@link Ext.panel.Table#columns column} config object as part of the `metaData` attribute
- * and then pass that along to the grid. Here's a very simple example for how that could be accomplished:
- // response format:
- {
- ...
- "metaData": {
- "fields": [
- { "name": "userId", "type": "int" },
- { "name": "name", "type": "string" },
- { "name": "birthday", "type": "date", "dateFormat": "Y-j-m" },
- ],
- "columns": [
- { "text": "User ID", "dataIndex": "userId", "width": 40 },
- { "text": "User Name", "dataIndex": "name", "flex": 1 },
- { "text": "Birthday", "dataIndex": "birthday", "flex": 1, "format": 'Y-j-m', "xtype": "datecolumn" }
- ]
- }
- }
- The Reader will automatically read the meta fields config and rebuild the Model based on the new fields, but to handle
- the new column configuration you would need to handle the metadata within the application code. This is done simply enough
- by handling the metachange event on either the store or the proxy, e.g.:
- var store = Ext.create('Ext.data.Store', {
- ...
- listeners: {
- 'metachange': function(store, meta) {
- myGrid.reconfigure(store, meta.columns);
- }
- }
- });
- */
- Ext.define('Ext.data.reader.Json', {
- extend: 'Ext.data.reader.Reader',
- alternateClassName: 'Ext.data.JsonReader',
- alias : 'reader.json',
- root: '',
- /**
- * @cfg {String} record The optional location within the JSON response that the record data itself can be found at.
- * See the JsonReader intro docs for more details. This is not often needed.
- */
- /**
- * @cfg {Boolean} useSimpleAccessors True to ensure that field names/mappings are treated as literals when
- * reading values. Defalts to <tt>false</tt>.
- * For example, by default, using the mapping "foo.bar.baz" will try and read a property foo from the root, then a property bar
- * from foo, then a property baz from bar. Setting the simple accessors to true will read the property with the name
- * "foo.bar.baz" direct from the root object.
- */
- useSimpleAccessors: false,
- /**
- * Reads a JSON object and returns a ResultSet. Uses the internal getTotal and getSuccess extractors to
- * retrieve meta data from the response, and extractData to turn the JSON data into model instances.
- * @param {Object} data The raw JSON data
- * @return {Ext.data.ResultSet} A ResultSet containing model instances and meta data about the results
- */
- readRecords: function(data) {
- //this has to be before the call to super because we use the meta data in the superclass readRecords
- if (data.metaData) {
- this.onMetaChange(data.metaData);
- }
- /**
- * @deprecated will be removed in Ext JS 5.0. This is just a copy of this.rawData - use that instead
- * @property {Object} jsonData
- */
- this.jsonData = data;
- return this.callParent([data]);
- },
- //inherit docs
- getResponseData: function(response) {
- var data;
- try {
- data = Ext.decode(response.responseText);
- }
- catch (ex) {
- Ext.Error.raise({
- response: response,
- json: response.responseText,
- parseError: ex,
- msg: 'Unable to parse the JSON returned by the server: ' + ex.toString()
- });
- }
- if (!data) {
- Ext.Error.raise('JSON object not found');
- }
- return data;
- },
- //inherit docs
- buildExtractors : function() {
- var me = this;
- me.callParent(arguments);
- if (me.root) {
- me.getRoot = me.createAccessor(me.root);
- } else {
- me.getRoot = function(root) {
- return root;
- };
- }
- },
- /**
- * @private
- * We're just preparing the data for the superclass by pulling out the record objects we want. If a {@link #record}
- * was specified we have to pull those out of the larger JSON object, which is most of what this function is doing
- * @param {Object} root The JSON root node
- * @return {Ext.data.Model[]} The records
- */
- extractData: function(root) {
- var recordName = this.record,
- data = [],
- length, i;
- if (recordName) {
- length = root.length;
-
- if (!length && Ext.isObject(root)) {
- length = 1;
- root = [root];
- }
- for (i = 0; i < length; i++) {
- data[i] = root[i][recordName];
- }
- } else {
- data = root;
- }
- return this.callParent([data]);
- },
- /**
- * @private
- * Returns an accessor function for the given property string. Gives support for properties such as the following:
- * 'someProperty'
- * 'some.property'
- * 'some["property"]'
- * This is used by buildExtractors to create optimized extractor functions when casting raw data into model instances.
- */
- createAccessor: function() {
- var re = /[\[\.]/;
- return function(expr) {
- if (Ext.isEmpty(expr)) {
- return Ext.emptyFn;
- }
- if (Ext.isFunction(expr)) {
- return expr;
- }
- if (this.useSimpleAccessors !== true) {
- var i = String(expr).search(re);
- if (i >= 0) {
- return Ext.functionFactory('obj', 'return obj' + (i > 0 ? '.' : '') + expr);
- }
- }
- return function(obj) {
- return obj[expr];
- };
- };
- }(),
- /**
- * @private
- * Returns an accessor expression for the passed Field. Gives support for properties such as the following:
- * 'someProperty'
- * 'some.property'
- * 'some["property"]'
- * This is used by buildExtractors to create optimized on extractor function which converts raw data into model instances.
- */
- createFieldAccessExpression: function() {
- var re = /[\[\.]/;
- return function(field, fieldVarName, dataName) {
- var me = this,
- hasMap = (field.mapping !== null),
- map = hasMap ? field.mapping : field.name,
- result,
- operatorSearch;
- if (typeof map === 'function') {
- result = fieldVarName + '.mapping(' + dataName + ', this)';
- } else if (this.useSimpleAccessors === true || ((operatorSearch = String(map).search(re)) < 0)) {
- if (!hasMap || isNaN(map)) {
- // If we don't provide a mapping, we may have a field name that is numeric
- map = '"' + map + '"';
- }
- result = dataName + "[" + map + "]";
- } else {
- result = dataName + (operatorSearch > 0 ? '.' : '') + map;
- }
- if (field.defaultValue !== undefined) {
- result = '(' + result + ' === undefined) ? ' + fieldVarName + '.defaultValue : ' + result;
- }
- if (field.convert) {
- result = fieldVarName + '.convert(' + result + ', record)';
- }
- return result;
- };
- }()
- });
- /**
- * @author Ed Spencer
- *
- * Proxies are used by {@link Ext.data.Store Stores} to handle the loading and saving of {@link Ext.data.Model Model}
- * data. Usually developers will not need to create or interact with proxies directly.
- *
- * # Types of Proxy
- *
- * There are two main types of Proxy - {@link Ext.data.proxy.Client Client} and {@link Ext.data.proxy.Server Server}.
- * The Client proxies save their data locally and include the following subclasses:
- *
- * - {@link Ext.data.proxy.LocalStorage LocalStorageProxy} - saves its data to localStorage if the browser supports it
- * - {@link Ext.data.proxy.SessionStorage SessionStorageProxy} - saves its data to sessionStorage if the browsers supports it
- * - {@link Ext.data.proxy.Memory MemoryProxy} - holds data in memory only, any data is lost when the page is refreshed
- *
- * The Server proxies save their data by sending requests to some remote server. These proxies include:
- *
- * - {@link Ext.data.proxy.Ajax Ajax} - sends requests to a server on the same domain
- * - {@link Ext.data.proxy.JsonP JsonP} - uses JSON-P to send requests to a server on a different domain
- * - {@link Ext.data.proxy.Direct Direct} - uses {@link Ext.direct.Manager} to send requests
- *
- * Proxies operate on the principle that all operations performed are either Create, Read, Update or Delete. These four
- * operations are mapped to the methods {@link #create}, {@link #read}, {@link #update} and {@link #destroy}
- * respectively. Each Proxy subclass implements these functions.
- *
- * The CRUD methods each expect an {@link Ext.data.Operation Operation} object as the sole argument. The Operation
- * encapsulates information about the action the Store wishes to perform, the {@link Ext.data.Model model} instances
- * that are to be modified, etc. See the {@link Ext.data.Operation Operation} documentation for more details. Each CRUD
- * method also accepts a callback function to be called asynchronously on completion.
- *
- * Proxies also support batching of Operations via a {@link Ext.data.Batch batch} object, invoked by the {@link #batch}
- * method.
- */
- Ext.define('Ext.data.proxy.Proxy', {
- alias: 'proxy.proxy',
- alternateClassName: ['Ext.data.DataProxy', 'Ext.data.Proxy'],
- requires: [
- 'Ext.data.reader.Json',
- 'Ext.data.writer.Json'
- ],
- uses: [
- 'Ext.data.Batch',
- 'Ext.data.Operation',
- 'Ext.data.Model'
- ],
- mixins: {
- observable: 'Ext.util.Observable'
- },
- /**
- * @cfg {String} batchOrder
- * Comma-separated ordering 'create', 'update' and 'destroy' actions when batching. Override this to set a different
- * order for the batched CRUD actions to be executed in. Defaults to 'create,update,destroy'.
- */
- batchOrder: 'create,update,destroy',
- /**
- * @cfg {Boolean} batchActions
- * True to batch actions of a particular type when synchronizing the store. Defaults to true.
- */
- batchActions: true,
- /**
- * @cfg {String} defaultReaderType
- * The default registered reader type. Defaults to 'json'.
- * @private
- */
- defaultReaderType: 'json',
- /**
- * @cfg {String} defaultWriterType
- * The default registered writer type. Defaults to 'json'.
- * @private
- */
- defaultWriterType: 'json',
- /**
- * @cfg {String/Ext.data.Model} model
- * The name of the Model to tie to this Proxy. Can be either the string name of the Model, or a reference to the
- * Model constructor. Required.
- */
- /**
- * @cfg {Object/String/Ext.data.reader.Reader} reader
- * The Ext.data.reader.Reader to use to decode the server's response or data read from client. This can either be a
- * Reader instance, a config object or just a valid Reader type name (e.g. 'json', 'xml').
- */
- /**
- * @cfg {Object/String/Ext.data.writer.Writer} writer
- * The Ext.data.writer.Writer to use to encode any request sent to the server or saved to client. This can either be
- * a Writer instance, a config object or just a valid Writer type name (e.g. 'json', 'xml').
- */
- /**
- * @property {Boolean} isProxy
- * `true` in this class to identify an objact as an instantiated Proxy, or subclass thereof.
- */
- isProxy: true,
- /**
- * Creates the Proxy
- * @param {Object} config (optional) Config object.
- */
- constructor: function(config) {
- config = config || {};
- if (config.model === undefined) {
- delete config.model;
- }
- Ext.apply(this, config);
- this.mixins.observable.constructor.call(this);
- if (this.model !== undefined && !(this.model instanceof Ext.data.Model)) {
- this.setModel(this.model);
- }
- /**
- * @event metachange
- * Fires when this proxy's reader provides new metadata. Metadata usually consists
- * of new field definitions, but can include any configuration data required by an
- * application, and can be processed as needed in the event handler.
- * This event is currently only fired for JsonReaders. Note that this event is also
- * propagated by {@link Ext.data.Store}, which is typically where it would be handled.
- * @param {Ext.data.proxy.Proxy} this
- * @param {Object} meta The JSON metadata
- */
- },
- /**
- * Sets the model associated with this proxy. This will only usually be called by a Store
- *
- * @param {String/Ext.data.Model} model The new model. Can be either the model name string,
- * or a reference to the model's constructor
- * @param {Boolean} setOnStore Sets the new model on the associated Store, if one is present
- */
- setModel: function(model, setOnStore) {
- this.model = Ext.ModelManager.getModel(model);
- var reader = this.reader,
- writer = this.writer;
- this.setReader(reader);
- this.setWriter(writer);
- if (setOnStore && this.store) {
- this.store.setModel(this.model);
- }
- },
- /**
- * Returns the model attached to this Proxy
- * @return {Ext.data.Model} The model
- */
- getModel: function() {
- return this.model;
- },
- /**
- * Sets the Proxy's Reader by string, config object or Reader instance
- *
- * @param {String/Object/Ext.data.reader.Reader} reader The new Reader, which can be either a type string,
- * a configuration object or an Ext.data.reader.Reader instance
- * @return {Ext.data.reader.Reader} The attached Reader object
- */
- setReader: function(reader) {
- var me = this;
- if (reader === undefined || typeof reader == 'string') {
- reader = {
- type: reader
- };
- }
- if (reader.isReader) {
- reader.setModel(me.model);
- } else {
- Ext.applyIf(reader, {
- proxy: me,
- model: me.model,
- type : me.defaultReaderType
- });
- reader = Ext.createByAlias('reader.' + reader.type, reader);
- }
- if (reader.onMetaChange) {
- reader.onMetaChange = Ext.Function.createSequence(reader.onMetaChange, this.onMetaChange, this);
- }
- me.reader = reader;
- return me.reader;
- },
- /**
- * Returns the reader currently attached to this proxy instance
- * @return {Ext.data.reader.Reader} The Reader instance
- */
- getReader: function() {
- return this.reader;
- },
- /**
- * @private
- * Called each time the reader's onMetaChange is called so that the proxy can fire the metachange event
- */
- onMetaChange: function(meta) {
- this.fireEvent('metachange', this, meta);
- },
- /**
- * Sets the Proxy's Writer by string, config object or Writer instance
- *
- * @param {String/Object/Ext.data.writer.Writer} writer The new Writer, which can be either a type string,
- * a configuration object or an Ext.data.writer.Writer instance
- * @return {Ext.data.writer.Writer} The attached Writer object
- */
- setWriter: function(writer) {
- if (writer === undefined || typeof writer == 'string') {
- writer = {
- type: writer
- };
- }
- if (!(writer instanceof Ext.data.writer.Writer)) {
- Ext.applyIf(writer, {
- model: this.model,
- type : this.defaultWriterType
- });
- writer = Ext.createByAlias('writer.' + writer.type, writer);
- }
- this.writer = writer;
- return this.writer;
- },
- /**
- * Returns the writer currently attached to this proxy instance
- * @return {Ext.data.writer.Writer} The Writer instance
- */
- getWriter: function() {
- return this.writer;
- },
- /**
- * Performs the given create operation.
- * @param {Ext.data.Operation} operation The Operation to perform
- * @param {Function} callback Callback function to be called when the Operation has completed (whether
- * successful or not)
- * @param {Object} scope Scope to execute the callback function in
- * @method
- */
- create: Ext.emptyFn,
- /**
- * Performs the given read operation.
- * @param {Ext.data.Operation} operation The Operation to perform
- * @param {Function} callback Callback function to be called when the Operation has completed (whether
- * successful or not)
- * @param {Object} scope Scope to execute the callback function in
- * @method
- */
- read: Ext.emptyFn,
- /**
- * Performs the given update operation.
- * @param {Ext.data.Operation} operation The Operation to perform
- * @param {Function} callback Callback function to be called when the Operation has completed (whether
- * successful or not)
- * @param {Object} scope Scope to execute the callback function in
- * @method
- */
- update: Ext.emptyFn,
- /**
- * Performs the given destroy operation.
- * @param {Ext.data.Operation} operation The Operation to perform
- * @param {Function} callback Callback function to be called when the Operation has completed (whether
- * successful or not)
- * @param {Object} scope Scope to execute the callback function in
- * @method
- */
- destroy: Ext.emptyFn,
- /**
- * Performs a batch of {@link Ext.data.Operation Operations}, in the order specified by {@link #batchOrder}. Used
- * internally by {@link Ext.data.Store}'s {@link Ext.data.Store#sync sync} method. Example usage:
- *
- * myProxy.batch({
- * create : [myModel1, myModel2],
- * update : [myModel3],
- * destroy: [myModel4, myModel5]
- * });
- *
- * Where the myModel* above are {@link Ext.data.Model Model} instances - in this case 1 and 2 are new instances and
- * have not been saved before, 3 has been saved previously but needs to be updated, and 4 and 5 have already been
- * saved but should now be destroyed.
- *
- * Note that the previous version of this method took 2 arguments (operations and listeners). While this is still
- * supported for now, the current signature is now a single `options` argument that can contain both operations and
- * listeners, in addition to other options. The multi-argument signature will likely be deprecated in a future release.
- *
- * @param {Object} options Object containing one or more properties supported by the batch method:
- *
- * @param {Object} options.operations Object containing the Model instances to act upon, keyed by action name
- *
- * @param {Object} [options.listeners] Event listeners object passed straight through to the Batch -
- * see {@link Ext.data.Batch} for details
- *
- * @param {Ext.data.Batch/Object} [options.batch] A {@link Ext.data.Batch} object (or batch config to apply
- * to the created batch). If unspecified a default batch will be auto-created.
- *
- * @param {Function} [options.callback] The function to be called upon completion of processing the batch.
- * The callback is called regardless of success or failure and is passed the following parameters:
- * @param {Ext.data.Batch} options.callback.batch The {@link Ext.data.Batch batch} that was processed,
- * containing all operations in their current state after processing
- * @param {Object} options.callback.options The options argument that was originally passed into batch
- *
- * @param {Function} [options.success] The function to be called upon successful completion of the batch. The
- * success function is called only if no exceptions were reported in any operations. If one or more exceptions
- * occurred then the `failure` function will be called instead. The success function is called
- * with the following parameters:
- * @param {Ext.data.Batch} options.success.batch The {@link Ext.data.Batch batch} that was processed,
- * containing all operations in their current state after processing
- * @param {Object} options.success.options The options argument that was originally passed into batch
- *
- * @param {Function} [options.failure] The function to be called upon unsuccessful completion of the batch. The
- * failure function is called when one or more operations returns an exception during processing (even if some
- * operations were also successful). In this case you can check the batch's {@link Ext.data.Batch#exceptions
- * exceptions} array to see exactly which operations had exceptions. The failure function is called with the
- * following parameters:
- * @param {Ext.data.Batch} options.failure.batch The {@link Ext.data.Batch batch} that was processed,
- * containing all operations in their current state after processing
- * @param {Object} options.failure.options The options argument that was originally passed into batch
- *
- * @param {Object} [options.scope] The scope in which to execute any callbacks (i.e. the `this` object inside
- * the callback, success and/or failure functions). Defaults to the proxy.
- *
- * @return {Ext.data.Batch} The newly created Batch
- */
- batch: function(options, /* deprecated */listeners) {
- var me = this,
- useBatch = me.batchActions,
- batch,
- records;
- if (options.operations === undefined) {
- // the old-style (operations, listeners) signature was called
- // so convert to the single options argument syntax
- options = {
- operations: options,
- listeners: listeners
- }
- }
- if (options.batch) {
- if (Ext.isDefined(options.batch.runOperation)) {
- batch = Ext.applyIf(options.batch, {
- proxy: me,
- listeners: {}
- });
- }
- } else {
- options.batch = {
- proxy: me,
- listeners: options.listeners || {}
- };
- }
- if (!batch) {
- batch = new Ext.data.Batch(options.batch);
- }
- batch.on('complete', Ext.bind(me.onBatchComplete, me, [options], 0));
- var actions = me.batchOrder.split(','),
- aLen = actions.length,
- action, a, r, rLen, record;
- for (a = 0; a < aLen; a++) {
- action = actions[a];
- records = options.operations[action];
- if (records) {
- if (useBatch) {
- batch.add(new Ext.data.Operation({
- action : action,
- records : records
- }));
- } else {
- rLen = records.length;
- for (r = 0; r < rLen; r++) {
- record = records[r];
- batch.add(new Ext.data.Operation({
- action : action,
- records : [record]
- }));
- }
- }
- }
- }
- batch.start();
- return batch;
- },
- /**
- * @private
- * The internal callback that the proxy uses to call any specified user callbacks after completion of a batch
- */
- onBatchComplete: function(batchOptions, batch) {
- var scope = batchOptions.scope || this;
- if (batch.hasException) {
- if (Ext.isFunction(batchOptions.failure)) {
- Ext.callback(batchOptions.failure, scope, [batch, batchOptions]);
- }
- } else if (Ext.isFunction(batchOptions.success)) {
- Ext.callback(batchOptions.success, scope, [batch, batchOptions]);
- }
- if (Ext.isFunction(batchOptions.callback)) {
- Ext.callback(batchOptions.callback, scope, [batch, batchOptions]);
- }
- }
- }, function() {
- //backwards compatibility
- Ext.data.DataProxy = this;
- });
- /**
- * @author Ed Spencer
- *
- * ServerProxy is a superclass of {@link Ext.data.proxy.JsonP JsonPProxy} and {@link Ext.data.proxy.Ajax AjaxProxy}, and
- * would not usually be used directly.
- *
- * ServerProxy should ideally be named HttpProxy as it is a superclass for all HTTP proxies - for Ext JS 4.x it has been
- * called ServerProxy to enable any 3.x applications that reference the HttpProxy to continue to work (HttpProxy is now
- * an alias of AjaxProxy).
- * @private
- */
- Ext.define('Ext.data.proxy.Server', {
- extend: 'Ext.data.proxy.Proxy',
- alias : 'proxy.server',
- alternateClassName: 'Ext.data.ServerProxy',
- uses : ['Ext.data.Request'],
- /**
- * @cfg {String} url
- * The URL from which to request the data object.
- */
- /**
- * @cfg {String} pageParam
- * The name of the 'page' parameter to send in a request. Defaults to 'page'. Set this to undefined if you don't
- * want to send a page parameter.
- */
- pageParam: 'page',
- /**
- * @cfg {String} startParam
- * The name of the 'start' parameter to send in a request. Defaults to 'start'. Set this to undefined if you don't
- * want to send a start parameter.
- */
- startParam: 'start',
- /**
- * @cfg {String} limitParam
- * The name of the 'limit' parameter to send in a request. Defaults to 'limit'. Set this to undefined if you don't
- * want to send a limit parameter.
- */
- limitParam: 'limit',
- /**
- * @cfg {String} groupParam
- * The name of the 'group' parameter to send in a request. Defaults to 'group'. Set this to undefined if you don't
- * want to send a group parameter.
- */
- groupParam: 'group',
- /**
- * @cfg {String} sortParam
- * The name of the 'sort' parameter to send in a request. Defaults to 'sort'. Set this to undefined if you don't
- * want to send a sort parameter.
- */
- sortParam: 'sort',
- /**
- * @cfg {String} filterParam
- * The name of the 'filter' parameter to send in a request. Defaults to 'filter'. Set this to undefined if you don't
- * want to send a filter parameter.
- */
- filterParam: 'filter',
- /**
- * @cfg {String} directionParam
- * The name of the direction parameter to send in a request. **This is only used when simpleSortMode is set to
- * true.** Defaults to 'dir'.
- */
- directionParam: 'dir',
- /**
- * @cfg {Boolean} simpleSortMode
- * Enabling simpleSortMode in conjunction with remoteSort will only send one sort property and a direction when a
- * remote sort is requested. The directionParam and sortParam will be sent with the property name and either 'ASC'
- * or 'DESC'.
- */
- simpleSortMode: false,
- /**
- * @cfg {Boolean} noCache
- * Disable caching by adding a unique parameter name to the request. Set to false to allow caching. Defaults to true.
- */
- noCache : true,
- /**
- * @cfg {String} cacheString
- * The name of the cache param added to the url when using noCache. Defaults to "_dc".
- */
- cacheString: "_dc",
- /**
- * @cfg {Number} timeout
- * The number of milliseconds to wait for a response. Defaults to 30000 milliseconds (30 seconds).
- */
- timeout : 30000,
- /**
- * @cfg {Object} api
- * Specific urls to call on CRUD action methods "create", "read", "update" and "destroy". Defaults to:
- *
- * api: {
- * create : undefined,
- * read : undefined,
- * update : undefined,
- * destroy : undefined
- * }
- *
- * The url is built based upon the action being executed [create|read|update|destroy] using the commensurate
- * {@link #api} property, or if undefined default to the configured
- * {@link Ext.data.Store}.{@link Ext.data.proxy.Server#url url}.
- *
- * For example:
- *
- * api: {
- * create : '/controller/new',
- * read : '/controller/load',
- * update : '/controller/update',
- * destroy : '/controller/destroy_action'
- * }
- *
- * If the specific URL for a given CRUD action is undefined, the CRUD action request will be directed to the
- * configured {@link Ext.data.proxy.Server#url url}.
- */
- constructor: function(config) {
- var me = this;
- config = config || {};
- /**
- * @event exception
- * Fires when the server returns an exception
- * @param {Ext.data.proxy.Proxy} this
- * @param {Object} response The response from the AJAX request
- * @param {Ext.data.Operation} operation The operation that triggered request
- */
- me.callParent([config]);
- /**
- * @cfg {Object} extraParams
- * Extra parameters that will be included on every request. Individual requests with params of the same name
- * will override these params when they are in conflict.
- */
- me.extraParams = config.extraParams || {};
- me.api = Ext.apply({}, config.api || me.api);
-
- //backwards compatibility, will be deprecated in 5.0
- me.nocache = me.noCache;
- },
- //in a ServerProxy all four CRUD operations are executed in the same manner, so we delegate to doRequest in each case
- create: function() {
- return this.doRequest.apply(this, arguments);
- },
- read: function() {
- return this.doRequest.apply(this, arguments);
- },
- update: function() {
- return this.doRequest.apply(this, arguments);
- },
- destroy: function() {
- return this.doRequest.apply(this, arguments);
- },
- /**
- * Sets a value in the underlying {@link #extraParams}.
- * @param {String} name The key for the new value
- * @param {Object} value The value
- */
- setExtraParam: function(name, value) {
- this.extraParams[name] = value;
- },
- /**
- * Creates and returns an Ext.data.Request object based on the options passed by the {@link Ext.data.Store Store}
- * that this Proxy is attached to.
- * @param {Ext.data.Operation} operation The {@link Ext.data.Operation Operation} object to execute
- * @return {Ext.data.Request} The request object
- */
- buildRequest: function(operation) {
- var me = this,
- params = Ext.applyIf(operation.params || {}, me.extraParams || {}),
- request;
- //copy any sorters, filters etc into the params so they can be sent over the wire
- params = Ext.applyIf(params, me.getParams(operation));
- if (operation.id && !params.id) {
- params.id = operation.id;
- }
- request = new Ext.data.Request({
- params : params,
- action : operation.action,
- records : operation.records,
- operation: operation,
- url : operation.url,
- // this is needed by JsonSimlet in order to properly construct responses for
- // requests from this proxy
- proxy: me
- });
- request.url = me.buildUrl(request);
- /*
- * Save the request on the Operation. Operations don't usually care about Request and Response data, but in the
- * ServerProxy and any of its subclasses we add both request and response as they may be useful for further processing
- */
- operation.request = request;
- return request;
- },
- // Should this be documented as protected method?
- processResponse: function(success, operation, request, response, callback, scope) {
- var me = this,
- reader,
- result;
- if (success === true) {
- reader = me.getReader();
- result = reader.read(me.extractResponseData(response));
- if (result.success !== false) {
- //see comment in buildRequest for why we include the response object here
- Ext.apply(operation, {
- response: response,
- resultSet: result
- });
- operation.commitRecords(result.records);
- operation.setCompleted();
- operation.setSuccessful();
- } else {
- operation.setException(result.message);
- me.fireEvent('exception', this, response, operation);
- }
- } else {
- me.setException(operation, response);
- me.fireEvent('exception', this, response, operation);
- }
- //this callback is the one that was passed to the 'read' or 'write' function above
- if (typeof callback == 'function') {
- callback.call(scope || me, operation);
- }
- me.afterRequest(request, success);
- },
- /**
- * Sets up an exception on the operation
- * @private
- * @param {Ext.data.Operation} operation The operation
- * @param {Object} response The response
- */
- setException: function(operation, response) {
- operation.setException({
- status: response.status,
- statusText: response.statusText
- });
- },
- /**
- * Template method to allow subclasses to specify how to get the response for the reader.
- * @template
- * @private
- * @param {Object} response The server response
- * @return {Object} The response data to be used by the reader
- */
- extractResponseData: function(response) {
- return response;
- },
- /**
- * Encode any values being sent to the server. Can be overridden in subclasses.
- * @private
- * @param {Array} An array of sorters/filters.
- * @return {Object} The encoded value
- */
- applyEncoding: function(value) {
- return Ext.encode(value);
- },
- /**
- * Encodes the array of {@link Ext.util.Sorter} objects into a string to be sent in the request url. By default,
- * this simply JSON-encodes the sorter data
- * @param {Ext.util.Sorter[]} sorters The array of {@link Ext.util.Sorter Sorter} objects
- * @return {String} The encoded sorters
- */
- encodeSorters: function(sorters) {
- var min = [],
- length = sorters.length,
- i = 0;
- for (; i < length; i++) {
- min[i] = {
- property : sorters[i].property,
- direction: sorters[i].direction
- };
- }
- return this.applyEncoding(min);
- },
- /**
- * Encodes the array of {@link Ext.util.Filter} objects into a string to be sent in the request url. By default,
- * this simply JSON-encodes the filter data
- * @param {Ext.util.Filter[]} filters The array of {@link Ext.util.Filter Filter} objects
- * @return {String} The encoded filters
- */
- encodeFilters: function(filters) {
- var min = [],
- length = filters.length,
- i = 0;
- for (; i < length; i++) {
- min[i] = {
- property: filters[i].property,
- value : filters[i].value
- };
- }
- return this.applyEncoding(min);
- },
- /**
- * @private
- * Copy any sorters, filters etc into the params so they can be sent over the wire
- */
- getParams: function(operation) {
- var me = this,
- params = {},
- isDef = Ext.isDefined,
- groupers = operation.groupers,
- sorters = operation.sorters,
- filters = operation.filters,
- page = operation.page,
- start = operation.start,
- limit = operation.limit,
- simpleSortMode = me.simpleSortMode,
- pageParam = me.pageParam,
- startParam = me.startParam,
- limitParam = me.limitParam,
- groupParam = me.groupParam,
- sortParam = me.sortParam,
- filterParam = me.filterParam,
- directionParam = me.directionParam;
- if (pageParam && isDef(page)) {
- params[pageParam] = page;
- }
- if (startParam && isDef(start)) {
- params[startParam] = start;
- }
- if (limitParam && isDef(limit)) {
- params[limitParam] = limit;
- }
- if (groupParam && groupers && groupers.length > 0) {
- // Grouper is a subclass of sorter, so we can just use the sorter method
- params[groupParam] = me.encodeSorters(groupers);
- }
- if (sortParam && sorters && sorters.length > 0) {
- if (simpleSortMode) {
- params[sortParam] = sorters[0].property;
- params[directionParam] = sorters[0].direction;
- } else {
- params[sortParam] = me.encodeSorters(sorters);
- }
- }
- if (filterParam && filters && filters.length > 0) {
- params[filterParam] = me.encodeFilters(filters);
- }
- return params;
- },
- /**
- * Generates a url based on a given Ext.data.Request object. By default, ServerProxy's buildUrl will add the
- * cache-buster param to the end of the url. Subclasses may need to perform additional modifications to the url.
- * @param {Ext.data.Request} request The request object
- * @return {String} The url
- */
- buildUrl: function(request) {
- var me = this,
- url = me.getUrl(request);
- if (!url) {
- Ext.Error.raise("You are using a ServerProxy but have not supplied it with a url.");
- }
- if (me.noCache) {
- url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.cacheString, Ext.Date.now()));
- }
- return url;
- },
- /**
- * Get the url for the request taking into account the order of priority,
- * - The request
- * - The api
- * - The url
- * @private
- * @param {Ext.data.Request} request The request
- * @return {String} The url
- */
- getUrl: function(request) {
- return request.url || this.api[request.action] || this.url;
- },
- /**
- * In ServerProxy subclasses, the {@link #create}, {@link #read}, {@link #update} and {@link #destroy} methods all
- * pass through to doRequest. Each ServerProxy subclass must implement the doRequest method - see {@link
- * Ext.data.proxy.JsonP} and {@link Ext.data.proxy.Ajax} for examples. This method carries the same signature as
- * each of the methods that delegate to it.
- *
- * @param {Ext.data.Operation} operation The Ext.data.Operation object
- * @param {Function} callback The callback function to call when the Operation has completed
- * @param {Object} scope The scope in which to execute the callback
- */
- doRequest: function(operation, callback, scope) {
- Ext.Error.raise("The doRequest function has not been implemented on your Ext.data.proxy.Server subclass. See src/data/ServerProxy.js for details");
- },
- /**
- * Optional callback function which can be used to clean up after a request has been completed.
- * @param {Ext.data.Request} request The Request object
- * @param {Boolean} success True if the request was successful
- * @method
- */
- afterRequest: Ext.emptyFn,
- onDestroy: function() {
- Ext.destroy(this.reader, this.writer);
- }
- });
- /**
- * @author Ed Spencer
- *
- * AjaxProxy is one of the most widely-used ways of getting data into your application. It uses AJAX requests to load
- * data from the server, usually to be placed into a {@link Ext.data.Store Store}. Let's take a look at a typical setup.
- * Here we're going to set up a Store that has an AjaxProxy. To prepare, we'll also set up a {@link Ext.data.Model
- * Model}:
- *
- * Ext.define('User', {
- * extend: 'Ext.data.Model',
- * fields: ['id', 'name', 'email']
- * });
- *
- * //The Store contains the AjaxProxy as an inline configuration
- * var store = Ext.create('Ext.data.Store', {
- * model: 'User',
- * proxy: {
- * type: 'ajax',
- * url : 'users.json'
- * }
- * });
- *
- * store.load();
- *
- * Our example is going to load user data into a Store, so we start off by defining a {@link Ext.data.Model Model} with
- * the fields that we expect the server to return. Next we set up the Store itself, along with a
- * {@link Ext.data.Store#proxy proxy} configuration. This configuration was automatically turned into an
- * Ext.data.proxy.Ajax instance, with the url we specified being passed into AjaxProxy's constructor.
- * It's as if we'd done this:
- *
- * new Ext.data.proxy.Ajax({
- * url: 'users.json',
- * model: 'User',
- * reader: 'json'
- * });
- *
- * A couple of extra configurations appeared here - {@link #model} and {@link #reader}. These are set by default when we
- * create the proxy via the Store - the Store already knows about the Model, and Proxy's default {@link
- * Ext.data.reader.Reader Reader} is {@link Ext.data.reader.Json JsonReader}.
- *
- * Now when we call store.load(), the AjaxProxy springs into action, making a request to the url we configured
- * ('users.json' in this case). As we're performing a read, it sends a GET request to that url (see
- * {@link #actionMethods} to customize this - by default any kind of read will be sent as a GET request and any kind of write
- * will be sent as a POST request).
- *
- * # Limitations
- *
- * AjaxProxy cannot be used to retrieve data from other domains. If your application is running on http://domainA.com it
- * cannot load data from http://domainB.com because browsers have a built-in security policy that prohibits domains
- * talking to each other via AJAX.
- *
- * If you need to read data from another domain and can't set up a proxy server (some software that runs on your own
- * domain's web server and transparently forwards requests to http://domainB.com, making it look like they actually came
- * from http://domainA.com), you can use {@link Ext.data.proxy.JsonP} and a technique known as JSON-P (JSON with
- * Padding), which can help you get around the problem so long as the server on http://domainB.com is set up to support
- * JSON-P responses. See {@link Ext.data.proxy.JsonP JsonPProxy}'s introduction docs for more details.
- *
- * # Readers and Writers
- *
- * AjaxProxy can be configured to use any type of {@link Ext.data.reader.Reader Reader} to decode the server's response.
- * If no Reader is supplied, AjaxProxy will default to using a {@link Ext.data.reader.Json JsonReader}. Reader
- * configuration can be passed in as a simple object, which the Proxy automatically turns into a {@link
- * Ext.data.reader.Reader Reader} instance:
- *
- * var proxy = new Ext.data.proxy.Ajax({
- * model: 'User',
- * reader: {
- * type: 'xml',
- * root: 'users'
- * }
- * });
- *
- * proxy.getReader(); //returns an {@link Ext.data.reader.Xml XmlReader} instance based on the config we supplied
- *
- * # Url generation
- *
- * AjaxProxy automatically inserts any sorting, filtering, paging and grouping options into the url it generates for
- * each request. These are controlled with the following configuration options:
- *
- * - {@link #pageParam} - controls how the page number is sent to the server (see also {@link #startParam} and {@link #limitParam})
- * - {@link #sortParam} - controls how sort information is sent to the server
- * - {@link #groupParam} - controls how grouping information is sent to the server
- * - {@link #filterParam} - controls how filter information is sent to the server
- *
- * Each request sent by AjaxProxy is described by an {@link Ext.data.Operation Operation}. To see how we can customize
- * the generated urls, let's say we're loading the Proxy with the following Operation:
- *
- * var operation = new Ext.data.Operation({
- * action: 'read',
- * page : 2
- * });
- *
- * Now we'll issue the request for this Operation by calling {@link #read}:
- *
- * var proxy = new Ext.data.proxy.Ajax({
- * url: '/users'
- * });
- *
- * proxy.read(operation); //GET /users?page=2
- *
- * Easy enough - the Proxy just copied the page property from the Operation. We can customize how this page data is sent
- * to the server:
- *
- * var proxy = new Ext.data.proxy.Ajax({
- * url: '/users',
- * pageParam: 'pageNumber'
- * });
- *
- * proxy.read(operation); //GET /users?pageNumber=2
- *
- * Alternatively, our Operation could have been configured to send start and limit parameters instead of page:
- *
- * var operation = new Ext.data.Operation({
- * action: 'read',
- * start : 50,
- * limit : 25
- * });
- *
- * var proxy = new Ext.data.proxy.Ajax({
- * url: '/users'
- * });
- *
- * proxy.read(operation); //GET /users?start=50&limit;=25
- *
- * Again we can customize this url:
- *
- * var proxy = new Ext.data.proxy.Ajax({
- * url: '/users',
- * startParam: 'startIndex',
- * limitParam: 'limitIndex'
- * });
- *
- * proxy.read(operation); //GET /users?startIndex=50&limitIndex;=25
- *
- * AjaxProxy will also send sort and filter information to the server. Let's take a look at how this looks with a more
- * expressive Operation object:
- *
- * var operation = new Ext.data.Operation({
- * action: 'read',
- * sorters: [
- * new Ext.util.Sorter({
- * property : 'name',
- * direction: 'ASC'
- * }),
- * new Ext.util.Sorter({
- * property : 'age',
- * direction: 'DESC'
- * })
- * ],
- * filters: [
- * new Ext.util.Filter({
- * property: 'eyeColor',
- * value : 'brown'
- * })
- * ]
- * });
- *
- * This is the type of object that is generated internally when loading a {@link Ext.data.Store Store} with sorters and
- * filters defined. By default the AjaxProxy will JSON encode the sorters and filters, resulting in something like this
- * (note that the url is escaped before sending the request, but is left unescaped here for clarity):
- *
- * var proxy = new Ext.data.proxy.Ajax({
- * url: '/users'
- * });
- *
- * proxy.read(operation); //GET /users?sort=[{"property":"name","direction":"ASC"},{"property":"age","direction":"DESC"}]&filter;=[{"property":"eyeColor","value":"brown"}]
- *
- * We can again customize how this is created by supplying a few configuration options. Let's say our server is set up
- * to receive sorting information is a format like "sortBy=name#ASC,age#DESC". We can configure AjaxProxy to provide
- * that format like this:
- *
- * var proxy = new Ext.data.proxy.Ajax({
- * url: '/users',
- * sortParam: 'sortBy',
- * filterParam: 'filterBy',
- *
- * //our custom implementation of sorter encoding - turns our sorters into "name#ASC,age#DESC"
- * encodeSorters: function(sorters) {
- * var length = sorters.length,
- * sortStrs = [],
- * sorter, i;
- *
- * for (i = 0; i < length; i++) {
- * sorter = sorters[i];
- *
- * sortStrs[i] = sorter.property + '#' + sorter.direction
- * }
- *
- * return sortStrs.join(",");
- * }
- * });
- *
- * proxy.read(operation); //GET /users?sortBy=name#ASC,age#DESC&filterBy;=[{"property":"eyeColor","value":"brown"}]
- *
- * We can also provide a custom {@link #encodeFilters} function to encode our filters.
- *
- * @constructor
- * Note that if this HttpProxy is being used by a {@link Ext.data.Store Store}, then the Store's call to
- * {@link Ext.data.Store#method-load load} will override any specified callback and params options. In this case, use the
- * {@link Ext.data.Store Store}'s events to modify parameters, or react to loading events.
- *
- * @param {Object} config (optional) Config object.
- * If an options parameter is passed, the singleton {@link Ext.Ajax} object will be used to make the request.
- */
- Ext.define('Ext.data.proxy.Ajax', {
- requires: ['Ext.util.MixedCollection', 'Ext.Ajax'],
- extend: 'Ext.data.proxy.Server',
- alias: 'proxy.ajax',
- alternateClassName: ['Ext.data.HttpProxy', 'Ext.data.AjaxProxy'],
-
- /**
- * @property {Object} actionMethods
- * Mapping of action name to HTTP request method. In the basic AjaxProxy these are set to 'GET' for 'read' actions
- * and 'POST' for 'create', 'update' and 'destroy' actions. The {@link Ext.data.proxy.Rest} maps these to the
- * correct RESTful methods.
- */
- actionMethods: {
- create : 'POST',
- read : 'GET',
- update : 'POST',
- destroy: 'POST'
- },
-
- /**
- * @cfg {Object} headers
- * Any headers to add to the Ajax request. Defaults to undefined.
- */
-
- /**
- * @ignore
- */
- doRequest: function(operation, callback, scope) {
- var writer = this.getWriter(),
- request = this.buildRequest(operation, callback, scope);
-
- if (operation.allowWrite()) {
- request = writer.write(request);
- }
-
- Ext.apply(request, {
- headers : this.headers,
- timeout : this.timeout,
- scope : this,
- callback : this.createRequestCallback(request, operation, callback, scope),
- method : this.getMethod(request),
- disableCaching: false // explicitly set it to false, ServerProxy handles caching
- });
-
- Ext.Ajax.request(request);
-
- return request;
- },
-
- /**
- * Returns the HTTP method name for a given request. By default this returns based on a lookup on
- * {@link #actionMethods}.
- * @param {Ext.data.Request} request The request object
- * @return {String} The HTTP method to use (should be one of 'GET', 'POST', 'PUT' or 'DELETE')
- */
- getMethod: function(request) {
- return this.actionMethods[request.action];
- },
-
- /**
- * @private
- * TODO: This is currently identical to the JsonPProxy version except for the return function's signature. There is a lot
- * of code duplication inside the returned function so we need to find a way to DRY this up.
- * @param {Ext.data.Request} request The Request object
- * @param {Ext.data.Operation} operation The Operation being executed
- * @param {Function} callback The callback function to be called when the request completes. This is usually the callback
- * passed to doRequest
- * @param {Object} scope The scope in which to execute the callback function
- * @return {Function} The callback function
- */
- createRequestCallback: function(request, operation, callback, scope) {
- var me = this;
-
- return function(options, success, response) {
- me.processResponse(success, operation, request, response, callback, scope);
- };
- }
- }, function() {
- //backwards compatibility, remove in Ext JS 5.0
- Ext.data.HttpProxy = this;
- });
- /**
- * @author Ed Spencer
- *
- * A Model represents some object that your application manages. For example, one might define a Model for Users,
- * Products, Cars, or any other real-world object that we want to model in the system. Models are registered via the
- * {@link Ext.ModelManager model manager}, and are used by {@link Ext.data.Store stores}, which are in turn used by many
- * of the data-bound components in Ext.
- *
- * Models are defined as a set of fields and any arbitrary methods and properties relevant to the model. For example:
- *
- * Ext.define('User', {
- * extend: 'Ext.data.Model',
- * fields: [
- * {name: 'name', type: 'string'},
- * {name: 'age', type: 'int'},
- * {name: 'phone', type: 'string'},
- * {name: 'alive', type: 'boolean', defaultValue: true}
- * ],
- *
- * changeName: function() {
- * var oldName = this.get('name'),
- * newName = oldName + " The Barbarian";
- *
- * this.set('name', newName);
- * }
- * });
- *
- * The fields array is turned into a {@link Ext.util.MixedCollection MixedCollection} automatically by the {@link
- * Ext.ModelManager ModelManager}, and all other functions and properties are copied to the new Model's prototype.
- *
- * Now we can create instances of our User model and call any model logic we defined:
- *
- * var user = Ext.create('User', {
- * name : 'Conan',
- * age : 24,
- * phone: '555-555-5555'
- * });
- *
- * user.changeName();
- * user.get('name'); //returns "Conan The Barbarian"
- *
- * # Validations
- *
- * Models have built-in support for validations, which are executed against the validator functions in {@link
- * Ext.data.validations} ({@link Ext.data.validations see all validation functions}). Validations are easy to add to
- * models:
- *
- * Ext.define('User', {
- * extend: 'Ext.data.Model',
- * fields: [
- * {name: 'name', type: 'string'},
- * {name: 'age', type: 'int'},
- * {name: 'phone', type: 'string'},
- * {name: 'gender', type: 'string'},
- * {name: 'username', type: 'string'},
- * {name: 'alive', type: 'boolean', defaultValue: true}
- * ],
- *
- * validations: [
- * {type: 'presence', field: 'age'},
- * {type: 'length', field: 'name', min: 2},
- * {type: 'inclusion', field: 'gender', list: ['Male', 'Female']},
- * {type: 'exclusion', field: 'username', list: ['Admin', 'Operator']},
- * {type: 'format', field: 'username', matcher: /([a-z]+)[0-9]{2,3}/}
- * ]
- * });
- *
- * The validations can be run by simply calling the {@link #validate} function, which returns a {@link Ext.data.Errors}
- * object:
- *
- * var instance = Ext.create('User', {
- * name: 'Ed',
- * gender: 'Male',
- * username: 'edspencer'
- * });
- *
- * var errors = instance.validate();
- *
- * # Associations
- *
- * Models can have associations with other Models via {@link Ext.data.association.HasOne},
- * {@link Ext.data.association.BelongsTo belongsTo} and {@link Ext.data.association.HasMany hasMany} associations.
- * For example, let's say we're writing a blog administration application which deals with Users, Posts and Comments.
- * We can express the relationships between these models like this:
- *
- * Ext.define('Post', {
- * extend: 'Ext.data.Model',
- * fields: ['id', 'user_id'],
- *
- * belongsTo: 'User',
- * hasMany : {model: 'Comment', name: 'comments'}
- * });
- *
- * Ext.define('Comment', {
- * extend: 'Ext.data.Model',
- * fields: ['id', 'user_id', 'post_id'],
- *
- * belongsTo: 'Post'
- * });
- *
- * Ext.define('User', {
- * extend: 'Ext.data.Model',
- * fields: ['id'],
- *
- * hasMany: [
- * 'Post',
- * {model: 'Comment', name: 'comments'}
- * ]
- * });
- *
- * See the docs for {@link Ext.data.association.HasOne}, {@link Ext.data.association.BelongsTo} and
- * {@link Ext.data.association.HasMany} for details on the usage and configuration of associations.
- * Note that associations can also be specified like this:
- *
- * Ext.define('User', {
- * extend: 'Ext.data.Model',
- * fields: ['id'],
- *
- * associations: [
- * {type: 'hasMany', model: 'Post', name: 'posts'},
- * {type: 'hasMany', model: 'Comment', name: 'comments'}
- * ]
- * });
- *
- * # Using a Proxy
- *
- * Models are great for representing types of data and relationships, but sooner or later we're going to want to load or
- * save that data somewhere. All loading and saving of data is handled via a {@link Ext.data.proxy.Proxy Proxy}, which
- * can be set directly on the Model:
- *
- * Ext.define('User', {
- * extend: 'Ext.data.Model',
- * fields: ['id', 'name', 'email'],
- *
- * proxy: {
- * type: 'rest',
- * url : '/users'
- * }
- * });
- *
- * Here we've set up a {@link Ext.data.proxy.Rest Rest Proxy}, which knows how to load and save data to and from a
- * RESTful backend. Let's see how this works:
- *
- * var user = Ext.create('User', {name: 'Ed Spencer', email: 'ed@sencha.com'});
- *
- * user.save(); //POST /users
- *
- * Calling {@link #save} on the new Model instance tells the configured RestProxy that we wish to persist this Model's
- * data onto our server. RestProxy figures out that this Model hasn't been saved before because it doesn't have an id,
- * and performs the appropriate action - in this case issuing a POST request to the url we configured (/users). We
- * configure any Proxy on any Model and always follow this API - see {@link Ext.data.proxy.Proxy} for a full list.
- *
- * Loading data via the Proxy is equally easy:
- *
- * //get a reference to the User model class
- * var User = Ext.ModelManager.getModel('User');
- *
- * //Uses the configured RestProxy to make a GET request to /users/123
- * User.load(123, {
- * success: function(user) {
- * console.log(user.getId()); //logs 123
- * }
- * });
- *
- * Models can also be updated and destroyed easily:
- *
- * //the user Model we loaded in the last snippet:
- * user.set('name', 'Edward Spencer');
- *
- * //tells the Proxy to save the Model. In this case it will perform a PUT request to /users/123 as this Model already has an id
- * user.save({
- * success: function() {
- * console.log('The User was updated');
- * }
- * });
- *
- * //tells the Proxy to destroy the Model. Performs a DELETE request to /users/123
- * user.destroy({
- * success: function() {
- * console.log('The User was destroyed!');
- * }
- * });
- *
- * # Usage in Stores
- *
- * It is very common to want to load a set of Model instances to be displayed and manipulated in the UI. We do this by
- * creating a {@link Ext.data.Store Store}:
- *
- * var store = Ext.create('Ext.data.Store', {
- * model: 'User'
- * });
- *
- * //uses the Proxy we set up on Model to load the Store data
- * store.load();
- *
- * A Store is just a collection of Model instances - usually loaded from a server somewhere. Store can also maintain a
- * set of added, updated and removed Model instances to be synchronized with the server via the Proxy. See the {@link
- * Ext.data.Store Store docs} for more information on Stores.
- *
- * @constructor
- * Creates new Model instance.
- * @param {Object} data An object containing keys corresponding to this model's fields, and their associated values
- */
- Ext.define('Ext.data.Model', {
- alternateClassName: 'Ext.data.Record',
- mixins: {
- observable: 'Ext.util.Observable'
- },
- requires: [
- 'Ext.ModelManager',
- 'Ext.data.IdGenerator',
- 'Ext.data.Field',
- 'Ext.data.Errors',
- 'Ext.data.Operation',
- 'Ext.data.validations',
- 'Ext.data.proxy.Ajax',
- 'Ext.util.MixedCollection'
- ],
- sortConvertFields: function(f1, f2) {
- var f1SpecialConvert = f1.type && f1.convert !== f1.type.convert,
- f2SpecialConvert = f2.type && f2.convert !== f2.type.convert;
- if (f1SpecialConvert && !f2SpecialConvert) {
- return 1;
- }
- if (!f1SpecialConvert && f2SpecialConvert) {
- return -1;
- }
- return 0;
- },
- itemNameFn: function(item) {
- return item.name;
- },
- onClassExtended: function(cls, data, hooks) {
- var onBeforeClassCreated = hooks.onBeforeCreated;
- hooks.onBeforeCreated = function(cls, data) {
- var me = this,
- name = Ext.getClassName(cls),
- prototype = cls.prototype,
- superCls = cls.prototype.superclass,
- validations = data.validations || [],
- fields = data.fields || [],
- associations = data.associations || [],
- belongsTo = data.belongsTo,
- hasMany = data.hasMany,
- hasOne = data.hasOne,
- addAssociations = function(items, type) {
- var i = 0,
- len,
- item;
- if (items) {
- items = Ext.Array.from(items);
- for (len = items.length; i < len; ++i) {
- item = items[i];
- if (!Ext.isObject(item)) {
- item = {model: item};
- }
- item.type = type;
- associations.push(item);
- }
- }
- },
- idgen = data.idgen,
- fieldsMixedCollection = new Ext.util.MixedCollection(false, prototype.itemNameFn),
- associationsMixedCollection = new Ext.util.MixedCollection(false, prototype.itemNameFn),
- superValidations = superCls.validations,
- superFields = superCls.fields,
- superAssociations = superCls.associations,
- association, i, ln,
- dependencies = [],
- idProperty = data.idProperty || cls.prototype.idProperty,
- fieldConvertSortFn = Ext.Function.bind(
- fieldsMixedCollection.sortBy,
- fieldsMixedCollection,
- [prototype.sortConvertFields], false);
- // Save modelName on class and its prototype
- cls.modelName = name;
- prototype.modelName = name;
- // Merge the validations of the superclass and the new subclass
- if (superValidations) {
- validations = superValidations.concat(validations);
- }
- data.validations = validations;
- // Merge the fields of the superclass and the new subclass
- if (superFields) {
- fields = superFields.items.concat(fields);
- }
- fieldsMixedCollection.on({
- add: fieldConvertSortFn,
- replace: fieldConvertSortFn
- });
- for (i = 0, ln = fields.length; i < ln; ++i) {
- fieldsMixedCollection.add(new Ext.data.Field(fields[i]));
- }
- if (!fieldsMixedCollection.get(idProperty)) {
- fieldsMixedCollection.add(new Ext.data.Field(idProperty));
- }
- data.fields = fieldsMixedCollection;
- if (idgen) {
- data.idgen = Ext.data.IdGenerator.get(idgen);
- }
- //associations can be specified in the more convenient format (e.g. not inside an 'associations' array).
- //we support that here
- addAssociations(data.belongsTo, 'belongsTo');
- delete data.belongsTo;
- addAssociations(data.hasMany, 'hasMany');
- delete data.hasMany;
- addAssociations(data.hasOne, 'hasOne');
- delete data.hasOne;
- if (superAssociations) {
- associations = superAssociations.items.concat(associations);
- }
- for (i = 0, ln = associations.length; i < ln; ++i) {
- dependencies.push('association.' + associations[i].type.toLowerCase());
- }
- if (data.proxy) {
- if (typeof data.proxy === 'string') {
- dependencies.push('proxy.' + data.proxy);
- }
- else if (typeof data.proxy.type === 'string') {
- dependencies.push('proxy.' + data.proxy.type);
- }
- }
- Ext.require(dependencies, function() {
- Ext.ModelManager.registerType(name, cls);
- for (i = 0, ln = associations.length; i < ln; ++i) {
- association = associations[i];
- Ext.apply(association, {
- ownerModel: name,
- associatedModel: association.model
- });
- if (Ext.ModelManager.getModel(association.model) === undefined) {
- Ext.ModelManager.registerDeferredAssociation(association);
- } else {
- associationsMixedCollection.add(Ext.data.association.Association.create(association));
- }
- }
- data.associations = associationsMixedCollection;
- onBeforeClassCreated.call(me, cls, data, hooks);
- cls.setProxy(cls.prototype.proxy || cls.prototype.defaultProxyType);
- // Fire the onModelDefined template method on ModelManager
- Ext.ModelManager.onModelDefined(cls);
- });
- };
- },
- inheritableStatics: {
- /**
- * Sets the Proxy to use for this model. Accepts any options that can be accepted by
- * {@link Ext#createByAlias Ext.createByAlias}.
- * @param {String/Object/Ext.data.proxy.Proxy} proxy The proxy
- * @return {Ext.data.proxy.Proxy}
- * @static
- * @inheritable
- */
- setProxy: function(proxy) {
- //make sure we have an Ext.data.proxy.Proxy object
- if (!proxy.isProxy) {
- if (typeof proxy == "string") {
- proxy = {
- type: proxy
- };
- }
- proxy = Ext.createByAlias("proxy." + proxy.type, proxy);
- }
- proxy.setModel(this);
- this.proxy = this.prototype.proxy = proxy;
- return proxy;
- },
- /**
- * Returns the configured Proxy for this Model
- * @return {Ext.data.proxy.Proxy} The proxy
- * @static
- * @inheritable
- */
- getProxy: function() {
- return this.proxy;
- },
- /**
- * Apply a new set of field definitions to the existing model. This will replace any existing
- * fields, including fields inherited from superclasses. Mainly for reconfiguring the
- * model based on changes in meta data (called from Reader's onMetaChange method).
- * @static
- * @inheritable
- */
- setFields: function(fields) {
- var me = this,
- prototypeFields = me.prototype.fields,
- len = fields.length,
- i = 0;
- if (prototypeFields) {
- prototypeFields.clear();
- }
- else {
- prototypeFields = me.prototype.fields = new Ext.util.MixedCollection(false, function(field) {
- return field.name;
- });
- }
- for (; i < len; i++) {
- prototypeFields.add(new Ext.data.Field(fields[i]));
- }
- me.fields = prototypeFields;
- return prototypeFields;
- },
- getFields: function() {
- return this.fields;
- },
- /**
- * Asynchronously loads a model instance by id. Sample usage:
- *
- * Ext.define('MyApp.User', {
- * extend: 'Ext.data.Model',
- * fields: [
- * {name: 'id', type: 'int'},
- * {name: 'name', type: 'string'}
- * ]
- * });
- *
- * MyApp.User.load(10, {
- * scope: this,
- * failure: function(record, operation) {
- * //do something if the load failed
- * },
- * success: function(record, operation) {
- * //do something if the load succeeded
- * },
- * callback: function(record, operation) {
- * //do something whether the load succeeded or failed
- * }
- * });
- *
- * @param {Number/String} id The id of the model to load
- * @param {Object} config (optional) config object containing success, failure and callback functions, plus
- * optional scope
- * @static
- * @inheritable
- */
- load: function(id, config) {
- config = Ext.apply({}, config);
- config = Ext.applyIf(config, {
- action: 'read',
- id : id
- });
- var operation = new Ext.data.Operation(config),
- scope = config.scope || this,
- record = null,
- callback;
- callback = function(operation) {
- if (operation.wasSuccessful()) {
- record = operation.getRecords()[0];
- Ext.callback(config.success, scope, [record, operation]);
- } else {
- Ext.callback(config.failure, scope, [record, operation]);
- }
- Ext.callback(config.callback, scope, [record, operation]);
- };
- this.proxy.read(operation, callback, this);
- }
- },
- statics: {
- PREFIX : 'ext-record',
- AUTO_ID: 1,
- EDIT : 'edit',
- REJECT : 'reject',
- COMMIT : 'commit',
- /**
- * Generates a sequential id. This method is typically called when a record is {@link Ext#create
- * create}d and {@link #constructor no id has been specified}. The id will automatically be assigned to the
- * record. The returned id takes the form: {PREFIX}-{AUTO_ID}.
- *
- * - **PREFIX** : String - Ext.data.Model.PREFIX (defaults to 'ext-record')
- * - **AUTO_ID** : String - Ext.data.Model.AUTO_ID (defaults to 1 initially)
- *
- * @param {Ext.data.Model} rec The record being created. The record does not exist, it's a {@link #phantom}.
- * @return {String} auto-generated string id, `"ext-record-i++"`;
- * @static
- */
- id: function(rec) {
- var id = [this.PREFIX, '-', this.AUTO_ID++].join('');
- rec.phantom = true;
- rec.internalId = id;
- return id;
- }
- },
- /**
- * @cfg {String/Object} idgen
- * The id generator to use for this model. The default id generator does not generate
- * values for the {@link #idProperty}.
- *
- * This can be overridden at the model level to provide a custom generator for a model.
- * The simplest form of this would be:
- *
- * Ext.define('MyApp.data.MyModel', {
- * extend: 'Ext.data.Model',
- * requires: ['Ext.data.SequentialIdGenerator'],
- * idgen: 'sequential',
- * ...
- * });
- *
- * The above would generate {@link Ext.data.SequentialIdGenerator sequential} id's such
- * as 1, 2, 3 etc..
- *
- * Another useful id generator is {@link Ext.data.UuidGenerator}:
- *
- * Ext.define('MyApp.data.MyModel', {
- * extend: 'Ext.data.Model',
- * requires: ['Ext.data.UuidGenerator'],
- * idgen: 'uuid',
- * ...
- * });
- *
- * An id generation can also be further configured:
- *
- * Ext.define('MyApp.data.MyModel', {
- * extend: 'Ext.data.Model',
- * idgen: {
- * type: 'sequential',
- * seed: 1000,
- * prefix: 'ID_'
- * }
- * });
- *
- * The above would generate id's such as ID_1000, ID_1001, ID_1002 etc..
- *
- * If multiple models share an id space, a single generator can be shared:
- *
- * Ext.define('MyApp.data.MyModelX', {
- * extend: 'Ext.data.Model',
- * idgen: {
- * type: 'sequential',
- * id: 'xy'
- * }
- * });
- *
- * Ext.define('MyApp.data.MyModelY', {
- * extend: 'Ext.data.Model',
- * idgen: {
- * type: 'sequential',
- * id: 'xy'
- * }
- * });
- *
- * For more complex, shared id generators, a custom generator is the best approach.
- * See {@link Ext.data.IdGenerator} for details on creating custom id generators.
- *
- * @markdown
- */
- idgen: {
- isGenerator: true,
- type: 'default',
- generate: function () {
- return null;
- },
- getRecId: function (rec) {
- return rec.modelName + '-' + rec.internalId;
- }
- },
- /**
- * @property {Boolean} editing
- * Internal flag used to track whether or not the model instance is currently being edited.
- * @readonly
- */
- editing : false,
- /**
- * @property {Boolean} dirty
- * True if this Record has been modified.
- * @readonly
- */
- dirty : false,
- /**
- * @cfg {String} persistenceProperty
- * The name of the property on this Persistable object that its data is saved to. Defaults to 'data'
- * (i.e: all persistable data resides in `this.data`.)
- */
- persistenceProperty: 'data',
- evented: false,
- /**
- * @property {Boolean} isModel
- * `true` in this class to identify an objact as an instantiated Model, or subclass thereof.
- */
- isModel: true,
- /**
- * @property {Boolean} phantom
- * True when the record does not yet exist in a server-side database (see {@link #setDirty}).
- * Any record which has a real database pk set as its id property is NOT a phantom -- it's real.
- */
- phantom : false,
- /**
- * @cfg {String} idProperty
- * The name of the field treated as this Model's unique id. Defaults to 'id'.
- */
- idProperty: 'id',
- /**
- * @cfg {String} [clientIdProperty='clientId']
- * The name of a property that is used for submitting this Model's unique client-side identifier
- * to the server when multiple phantom records are saved as part of the same {@link Ext.data.Operation Operation}.
- * In such a case, the server response should include the client id for each record
- * so that the server response data can be used to update the client-side records if necessary.
- * This property cannot have the same name as any of this Model's fields.
- */
- clientIdProperty: 'clientId',
- /**
- * @cfg {String} defaultProxyType
- * The string type of the default Model Proxy. Defaults to 'ajax'.
- */
- defaultProxyType: 'ajax',
- // Fields config and property
- /**
- * @cfg {Object[]/String[]} fields
- * The fields for this model. This is an Array of **{@link Ext.data.Field Field}** definition objects. A Field
- * definition may simply be the *name* of the Field, but a Field encapsulates {@link Ext.data.Field#type data type},
- * {@link Ext.data.Field#convert custom conversion} of raw data, and a {@link Ext.data.Field#mapping mapping}
- * property to specify by name of index, how to extract a field's value from a raw data object, so it is best practice
- * to specify a full set of {@link Ext.data.Field Field} config objects.
- */
- /**
- * @property {Ext.util.MixedCollection} fields
- * A {@link Ext.util.MixedCollection Collection} of the fields defined for this Model (including fields defined in superclasses)
- *
- * This is a collection of {@link Ext.data.Field} instances, each of which encapsulates information that the field was configured with.
- * By default, you can specify a field as simply a String, representing the *name* of the field, but a Field encapsulates
- * {@link Ext.data.Field#type data type}, {@link Ext.data.Field#convert custom conversion} of raw data, and a {@link Ext.data.Field#mapping mapping}
- * property to specify by name of index, how to extract a field's value from a raw data object.
- */
- /**
- * @cfg {Object[]} validations
- * An array of {@link Ext.data.validations validations} for this model.
- */
- // Associations configs and properties
- /**
- * @cfg {Object[]} associations
- * An array of {@link Ext.data.Association associations} for this model.
- */
- /**
- * @cfg {String/Object/String[]/Object[]} hasMany
- * One or more {@link Ext.data.HasManyAssociation HasMany associations} for this model.
- */
- /**
- * @cfg {String/Object/String[]/Object[]} belongsTo
- * One or more {@link Ext.data.BelongsToAssociation BelongsTo associations} for this model.
- */
- /**
- * @cfg {String/Object/Ext.data.proxy.Proxy} proxy
- * The {@link Ext.data.proxy.Proxy proxy} to use for this model.
- */
- /**
- * @event idchanged
- * Fired when this model's id changes
- * @param {Ext.data.Model} this
- * @param {Number/String} oldId The old id
- * @param {Number/String} newId The new id
- */
- // id, raw and convertedData not documented intentionally, meant to be used internally.
- constructor: function(data, id, raw, convertedData) {
- data = data || {};
- var me = this,
- fields,
- length,
- field,
- name,
- value,
- newId,
- persistenceProperty,
- i;
- /**
- * @property {Number/String} internalId
- * An internal unique ID for each Model instance, used to identify Models that don't have an ID yet
- * @private
- */
- me.internalId = (id || id === 0) ? id : Ext.data.Model.id(me);
- /**
- * @property {Object} raw The raw data used to create this model if created via a reader.
- */
- me.raw = raw;
- if (!me.data) {
- me.data = {};
- }
- /**
- * @property {Object} modified Key: value pairs of all fields whose values have changed
- */
- me.modified = {};
- // Deal with spelling error in previous releases
- if (me.persistanceProperty) {
- if (Ext.isDefined(Ext.global.console)) {
- Ext.global.console.warn('Ext.data.Model: persistanceProperty has been deprecated. Use persistenceProperty instead.');
- }
- me.persistenceProperty = me.persistanceProperty;
- }
- me[me.persistenceProperty] = convertedData || {};
- me.mixins.observable.constructor.call(me);
- if (!convertedData) {
- //add default field values if present
- fields = me.fields.items;
- length = fields.length;
- i = 0;
- persistenceProperty = me[me.persistenceProperty];
- if (Ext.isArray(data)) {
- for (; i < length; i++) {
- field = fields[i];
- name = field.name;
- value = data[i];
- if (value === undefined) {
- value = field.defaultValue;
- }
- // Have to map array data so the values get assigned to the named fields
- // rather than getting set as the field names with undefined values.
- if (field.convert) {
- value = field.convert(value, me);
- }
- persistenceProperty[name] = value ;
- }
- } else {
- for (; i < length; i++) {
- field = fields[i];
- name = field.name;
- value = data[name];
- if (value === undefined) {
- value = field.defaultValue;
- }
- if (field.convert) {
- value = field.convert(value, me);
- }
- persistenceProperty[name] = value ;
- }
- }
- }
- /**
- * @property {Array} stores
- * An array of {@link Ext.data.AbstractStore} objects that this record is bound to.
- */
- me.stores = [];
- if (me.getId()) {
- me.phantom = false;
- } else if (me.phantom) {
- newId = me.idgen.generate();
- if (newId !== null) {
- me.setId(newId);
- }
- }
- // clear any dirty/modified since we're initializing
- me.dirty = false;
- me.modified = {};
- if (typeof me.init == 'function') {
- me.init();
- }
- me.id = me.idgen.getRecId(me);
- },
- /**
- * Returns the value of the given field
- * @param {String} fieldName The field to fetch the value for
- * @return {Object} The value
- */
- get: function(field) {
- return this[this.persistenceProperty][field];
- },
- /**
- * Sets the given field to the given value, marks the instance as dirty
- * @param {String/Object} fieldName The field to set, or an object containing key/value pairs
- * @param {Object} value The value to set
- */
- set: function(fieldName, value) {
- var me = this,
- fields = me.fields,
- modified = me.modified,
- modifiedFieldNames = [],
- field, key, i, currentValue, notEditing, count, length;
- /*
- * If we're passed an object, iterate over that object.
- */
- if (arguments.length == 1 && Ext.isObject(fieldName)) {
- notEditing = !me.editing;
- count = 0;
- fields = me.fields.items;
- length = fields.length;
- for (i = 0; i < length; i++) {
- field = fields[i].name;
- if (fieldName.hasOwnProperty(field)) {
- if (!count && notEditing) {
- me.beginEdit();
- }
- ++count;
- me.set(field, fieldName[field]);
- }
- }
- if (notEditing && count) {
- me.endEdit(false, modifiedFieldNames);
- }
- } else {
- fields = me.fields;
- if (fields) {
- field = fields.get(fieldName);
- if (field && field.convert) {
- value = field.convert(value, me);
- }
- }
- currentValue = me.get(fieldName);
- me[me.persistenceProperty][fieldName] = value;
- if (field && field.persist && !me.isEqual(currentValue, value)) {
- if (me.isModified(fieldName)) {
- if (me.isEqual(modified[fieldName], value)) {
- // the original value in me.modified equals the new value, so the
- // field is no longer modified
- delete modified[fieldName];
- // we might have removed the last modified field, so check to see if
- // there are any modified fields remaining and correct me.dirty:
- me.dirty = false;
- for (key in modified) {
- if (modified.hasOwnProperty(key)){
- me.dirty = true;
- break;
- }
- }
- }
- } else {
- me.dirty = true;
- modified[fieldName] = currentValue;
- }
- }
- if(fieldName === me.idProperty && currentValue !== value) {
- me.fireEvent('idchanged', me, currentValue, value);
- }
- if (!me.editing) {
- me.afterEdit([fieldName]);
- }
- }
- },
- /**
- * Checks if two values are equal, taking into account certain
- * special factors, for example dates.
- * @private
- * @param {Object} a The first value
- * @param {Object} b The second value
- * @return {Boolean} True if the values are equal
- */
- isEqual: function(a, b){
- if (Ext.isDate(a) && Ext.isDate(b)) {
- return Ext.Date.isEqual(a, b);
- }
- return a === b;
- },
- /**
- * Begins an edit. While in edit mode, no events (e.g.. the `update` event) are relayed to the containing store.
- * When an edit has begun, it must be followed by either {@link #endEdit} or {@link #cancelEdit}.
- */
- beginEdit : function(){
- var me = this;
- if (!me.editing) {
- me.editing = true;
- me.dirtySave = me.dirty;
- me.dataSave = Ext.apply({}, me[me.persistenceProperty]);
- me.modifiedSave = Ext.apply({}, me.modified);
- }
- },
- /**
- * Cancels all changes made in the current edit operation.
- */
- cancelEdit : function(){
- var me = this;
- if (me.editing) {
- me.editing = false;
- // reset the modified state, nothing changed since the edit began
- me.modified = me.modifiedSave;
- me[me.persistenceProperty] = me.dataSave;
- me.dirty = me.dirtySave;
- delete me.modifiedSave;
- delete me.dataSave;
- delete me.dirtySave;
- }
- },
- /**
- * Ends an edit. If any data was modified, the containing store is notified (ie, the store's `update` event will
- * fire).
- * @param {Boolean} silent True to not notify the store of the change
- * @param {String[]} modifiedFieldNames Array of field names changed during edit.
- */
- endEdit : function(silent, modifiedFieldNames){
- var me = this,
- changed;
- if (me.editing) {
- me.editing = false;
- if(!modifiedFieldNames) {
- modifiedFieldNames = me.getModifiedFieldNames();
- }
- changed = me.dirty || modifiedFieldNames.length > 0;
- delete me.modifiedSave;
- delete me.dataSave;
- delete me.dirtySave;
- if (changed && silent !== true) {
- me.afterEdit(modifiedFieldNames);
- }
- }
- },
- /**
- * Gets the names of all the fields that were modified during an edit
- * @private
- * @return {String[]} An array of modified field names
- */
- getModifiedFieldNames: function(){
- var me = this,
- saved = me.dataSave,
- data = me[me.persistenceProperty],
- modified = [],
- key;
- for (key in data) {
- if (data.hasOwnProperty(key)) {
- if (!me.isEqual(data[key], saved[key])) {
- modified.push(key);
- }
- }
- }
- return modified;
- },
- /**
- * Gets a hash of only the fields that have been modified since this Model was created or commited.
- * @return {Object}
- */
- getChanges : function(){
- var modified = this.modified,
- changes = {},
- field;
- for (field in modified) {
- if (modified.hasOwnProperty(field)){
- changes[field] = this.get(field);
- }
- }
- return changes;
- },
- /**
- * Returns true if the passed field name has been `{@link #modified}` since the load or last commit.
- * @param {String} fieldName {@link Ext.data.Field#name}
- * @return {Boolean}
- */
- isModified : function(fieldName) {
- return this.modified.hasOwnProperty(fieldName);
- },
- /**
- * Marks this **Record** as `{@link #dirty}`. This method is used interally when adding `{@link #phantom}` records
- * to a {@link Ext.data.proxy.Server#writer writer enabled store}.
- *
- * Marking a record `{@link #dirty}` causes the phantom to be returned by {@link Ext.data.Store#getUpdatedRecords}
- * where it will have a create action composed for it during {@link Ext.data.Model#save model save} operations.
- */
- setDirty : function() {
- var me = this,
- fields = me.fields.items,
- fLen = fields.length,
- field, name, f;
- me.dirty = true;
- for (f = 0; f < fLen; f++) {
- field = fields[f];
- if (field.persist) {
- name = field.name;
- me.modified[name] = me.get(name);
- }
- }
- },
- markDirty : function() {
- if (Ext.isDefined(Ext.global.console)) {
- Ext.global.console.warn('Ext.data.Model: markDirty has been deprecated. Use setDirty instead.');
- }
- return this.setDirty.apply(this, arguments);
- },
- /**
- * Usually called by the {@link Ext.data.Store} to which this model instance has been {@link #join joined}. Rejects
- * all changes made to the model instance since either creation, or the last commit operation. Modified fields are
- * reverted to their original values.
- *
- * Developers should subscribe to the {@link Ext.data.Store#update} event to have their code notified of reject
- * operations.
- *
- * @param {Boolean} silent (optional) True to skip notification of the owning store of the change.
- * Defaults to false.
- */
- reject : function(silent) {
- var me = this,
- modified = me.modified,
- field;
- for (field in modified) {
- if (modified.hasOwnProperty(field)) {
- if (typeof modified[field] != "function") {
- me[me.persistenceProperty][field] = modified[field];
- }
- }
- }
- me.dirty = false;
- me.editing = false;
- me.modified = {};
- if (silent !== true) {
- me.afterReject();
- }
- },
- /**
- * Usually called by the {@link Ext.data.Store} which owns the model instance. Commits all changes made to the
- * instance since either creation or the last commit operation.
- *
- * Developers should subscribe to the {@link Ext.data.Store#update} event to have their code notified of commit
- * operations.
- *
- * @param {Boolean} silent (optional) True to skip notification of the owning store of the change.
- * Defaults to false.
- */
- commit : function(silent) {
- var me = this;
- me.phantom = me.dirty = me.editing = false;
- me.modified = {};
- if (silent !== true) {
- me.afterCommit();
- }
- },
- /**
- * Creates a copy (clone) of this Model instance.
- *
- * @param {String} [id] A new id, defaults to the id of the instance being copied.
- * See `{@link Ext.data.Model#id id}`. To generate a phantom instance with a new id use:
- *
- * var rec = record.copy(); // clone the record
- * Ext.data.Model.id(rec); // automatically generate a unique sequential id
- *
- * @return {Ext.data.Model}
- */
- copy : function(newId) {
- var me = this;
- return new me.self(Ext.apply({}, me[me.persistenceProperty]), newId);
- },
- /**
- * Sets the Proxy to use for this model. Accepts any options that can be accepted by
- * {@link Ext#createByAlias Ext.createByAlias}.
- *
- * @param {String/Object/Ext.data.proxy.Proxy} proxy The proxy
- * @return {Ext.data.proxy.Proxy}
- */
- setProxy: function(proxy) {
- //make sure we have an Ext.data.proxy.Proxy object
- if (!proxy.isProxy) {
- if (typeof proxy === "string") {
- proxy = {
- type: proxy
- };
- }
- proxy = Ext.createByAlias("proxy." + proxy.type, proxy);
- }
- proxy.setModel(this.self);
- this.proxy = proxy;
- return proxy;
- },
- /**
- * Returns the configured Proxy for this Model.
- * @return {Ext.data.proxy.Proxy} The proxy
- */
- getProxy: function() {
- return this.proxy;
- },
- /**
- * Validates the current data against all of its configured {@link #validations}.
- * @return {Ext.data.Errors} The errors object
- */
- validate: function() {
- var errors = new Ext.data.Errors(),
- validations = this.validations,
- validators = Ext.data.validations,
- length, validation, field, valid, type, i;
- if (validations) {
- length = validations.length;
- for (i = 0; i < length; i++) {
- validation = validations[i];
- field = validation.field || validation.name;
- type = validation.type;
- valid = validators[type](validation, this.get(field));
- if (!valid) {
- errors.add({
- field : field,
- message: validation.message || validators[type + 'Message']
- });
- }
- }
- }
- return errors;
- },
- /**
- * Checks if the model is valid. See {@link #validate}.
- * @return {Boolean} True if the model is valid.
- */
- isValid: function(){
- return this.validate().isValid();
- },
- /**
- * Saves the model instance using the configured proxy.
- * @param {Object} options Options to pass to the proxy. Config object for {@link Ext.data.Operation}.
- * @return {Ext.data.Model} The Model instance
- */
- save: function(options) {
- options = Ext.apply({}, options);
- var me = this,
- action = me.phantom ? 'create' : 'update',
- scope = options.scope || me,
- stores = me.stores,
- i = 0,
- storeCount,
- store,
- args,
- operation,
- callback;
- Ext.apply(options, {
- records: [me],
- action : action
- });
- operation = new Ext.data.Operation(options);
- callback = function(operation) {
- args = [me, operation];
- if (operation.wasSuccessful()) {
- for(storeCount = stores.length; i < storeCount; i++) {
- store = stores[i];
- store.fireEvent('write', store, operation);
- store.fireEvent('datachanged', store);
- // Not firing refresh here, since it's a single record
- }
- Ext.callback(options.success, scope, args);
- } else {
- Ext.callback(options.failure, scope, args);
- }
- Ext.callback(options.callback, scope, args);
- };
- me.getProxy()[action](operation, callback, me);
- return me;
- },
- /**
- * Destroys the model using the configured proxy.
- * @param {Object} options Options to pass to the proxy. Config object for {@link Ext.data.Operation}.
- * @return {Ext.data.Model} The Model instance
- */
- destroy: function(options){
- options = Ext.apply({}, options);
- var me = this,
- scope = options.scope || me,
- stores = me.stores,
- i = 0,
- storeCount,
- store,
- args,
- operation,
- callback;
- Ext.apply(options, {
- records: [me],
- action : 'destroy'
- });
- operation = new Ext.data.Operation(options);
- callback = function(operation) {
- args = [me, operation];
- if (operation.wasSuccessful()) {
- for(storeCount = stores.length; i < storeCount; i++) {
- store = stores[i];
- store.fireEvent('write', store, operation);
- store.fireEvent('datachanged', store);
- // Not firing refresh here, since it's a single record
- }
- me.clearListeners();
- Ext.callback(options.success, scope, args);
- } else {
- Ext.callback(options.failure, scope, args);
- }
- Ext.callback(options.callback, scope, args);
- };
- me.getProxy().destroy(operation, callback, me);
- return me;
- },
- /**
- * Returns the unique ID allocated to this model instance as defined by {@link #idProperty}.
- * @return {Number/String} The id
- */
- getId: function() {
- return this.get(this.idProperty);
- },
- /**
- * @private
- */
- getObservableId: function() {
- return this.id;
- },
- /**
- * Sets the model instance's id field to the given id.
- * @param {Number/String} id The new id
- */
- setId: function(id) {
- this.set(this.idProperty, id);
- this.phantom = !(id || id === 0);
- },
- /**
- * Tells this model instance that it has been added to a store.
- * @param {Ext.data.Store} store The store to which this model has been added.
- */
- join : function(store) {
- Ext.Array.include(this.stores, store);
- },
- /**
- * Tells this model instance that it has been removed from the store.
- * @param {Ext.data.Store} store The store from which this model has been removed.
- */
- unjoin: function(store) {
- Ext.Array.remove(this.stores, store);
- },
- /**
- * @private
- * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
- * afterEdit method is called
- * @param {String[]} modifiedFieldNames Array of field names changed during edit.
- */
- afterEdit : function(modifiedFieldNames) {
- this.callStore('afterEdit', modifiedFieldNames);
- },
- /**
- * @private
- * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
- * afterReject method is called
- */
- afterReject : function() {
- this.callStore("afterReject");
- },
- /**
- * @private
- * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
- * afterCommit method is called
- */
- afterCommit: function() {
- this.callStore('afterCommit');
- },
- /**
- * @private
- * Helper function used by afterEdit, afterReject and afterCommit. Calls the given method on the
- * {@link Ext.data.Store store} that this instance has {@link #join joined}, if any. The store function
- * will always be called with the model instance as its single argument. If this model is joined to
- * a Ext.data.NodeStore, then this method calls the given method on the NodeStore and the associated Ext.data.TreeStore
- * @param {String} fn The function to call on the store
- */
- callStore: function(fn) {
- var args = Ext.Array.clone(arguments),
- stores = this.stores,
- i = 0,
- len = stores.length,
- store, treeStore;
- args[0] = this;
- for (; i < len; ++i) {
- store = stores[i];
- if (store && typeof store[fn] == "function") {
- store[fn].apply(store, args);
- }
- // if the record is bound to a NodeStore call the TreeStore's method as well
- treeStore = store.treeStore;
- if (treeStore && typeof treeStore[fn] == "function") {
- treeStore[fn].apply(treeStore, args);
- }
- }
- },
- /**
- * Gets all values for each field in this model and returns an object
- * containing the current data.
- * @param {Boolean} includeAssociated True to also include associated data. Defaults to false.
- * @return {Object} An object hash containing all the values in this model
- */
- getData: function(includeAssociated){
- var me = this,
- fields = me.fields.items,
- fLen = fields.length,
- data = {},
- name, f;
- for (f = 0; f < fLen; f++) {
- name = fields[f].name;
- data[name] = me.get(name);
- }
- if (includeAssociated === true) {
- Ext.apply(data, me.getAssociatedData());
- }
- return data;
- },
- /**
- * Gets all of the data from this Models *loaded* associations. It does this recursively - for example if we have a
- * User which hasMany Orders, and each Order hasMany OrderItems, it will return an object like this:
- *
- * {
- * orders: [
- * {
- * id: 123,
- * status: 'shipped',
- * orderItems: [
- * ...
- * ]
- * }
- * ]
- * }
- *
- * @return {Object} The nested data set for the Model's loaded associations
- */
- getAssociatedData: function(){
- return this.prepareAssociatedData(this, [], null);
- },
- /**
- * @private
- * This complex-looking method takes a given Model instance and returns an object containing all data from
- * all of that Model's *loaded* associations. See {@link #getAssociatedData}
- * @param {Ext.data.Model} record The Model instance
- * @param {String[]} ids PRIVATE. The set of Model instance internalIds that have already been loaded
- * @param {String} associationType (optional) The name of the type of association to limit to.
- * @return {Object} The nested data set for the Model's loaded associations
- */
- prepareAssociatedData: function(record, ids, associationType) {
- //we keep track of all of the internalIds of the models that we have loaded so far in here
- var associations = record.associations.items,
- associationCount = associations.length,
- associationData = {},
- associatedStore, associatedRecords, associatedRecord,
- associatedRecordCount, association, id, i, j, type, allow;
- for (i = 0; i < associationCount; i++) {
- association = associations[i];
- type = association.type;
- allow = true;
- if (associationType) {
- allow = type == associationType;
- }
- if (allow && type == 'hasMany') {
- //this is the hasMany store filled with the associated data
- associatedStore = record[association.storeName];
- //we will use this to contain each associated record's data
- associationData[association.name] = [];
- //if it's loaded, put it into the association data
- if (associatedStore && associatedStore.getCount() > 0) {
- associatedRecords = associatedStore.data.items;
- associatedRecordCount = associatedRecords.length;
- //now we're finally iterating over the records in the association. We do this recursively
- for (j = 0; j < associatedRecordCount; j++) {
- associatedRecord = associatedRecords[j];
- // Use the id, since it is prefixed with the model name, guaranteed to be unique
- id = associatedRecord.id;
- //when we load the associations for a specific model instance we add it to the set of loaded ids so that
- //we don't load it twice. If we don't do this, we can fall into endless recursive loading failures.
- if (Ext.Array.indexOf(ids, id) == -1) {
- ids.push(id);
- associationData[association.name][j] = associatedRecord.getData();
- Ext.apply(associationData[association.name][j], this.prepareAssociatedData(associatedRecord, ids, type));
- }
- }
- }
- } else if (allow && (type == 'belongsTo' || type == 'hasOne')) {
- associatedRecord = record[association.instanceName];
- if (associatedRecord !== undefined) {
- id = associatedRecord.id;
- if (Ext.Array.indexOf(ids, id) === -1) {
- ids.push(id);
- associationData[association.name] = associatedRecord.getData();
- Ext.apply(associationData[association.name], this.prepareAssociatedData(associatedRecord, ids, type));
- }
- }
- }
- }
- return associationData;
- }
- });
- /**
- *
- */
- Ext.define('Ext.container.DockingContainer', {
- /* Begin Definitions */
- requires: ['Ext.util.MixedCollection', 'Ext.Element' ],
- /* End Definitions */
- isDockingContainer: true,
- /**
- * @cfg {Object} defaultDockWeights
- * This object holds the default weights applied to dockedItems that have no weight. These start with a
- * weight of 1, to allow negative weights to insert before top items and are odd numbers
- * so that even weights can be used to get between different dock orders.
- *
- * To make default docking order match border layout, do this:
- *
- * Ext.panel.AbstractPanel.prototype.defaultDockWeights = { top: 1, bottom: 3, left: 5, right: 7 };
- *
- * Changing these defaults as above or individually on this object will effect all Panels.
- * To change the defaults on a single panel, you should replace the entire object:
- *
- * initComponent: function () {
- * // NOTE: Don't change members of defaultDockWeights since the object is shared.
- * this.defaultDockWeights = { top: 1, bottom: 3, left: 5, right: 7 };
- *
- * this.callParent();
- * }
- *
- * To change only one of the default values, you do this:
- *
- * initComponent: function () {
- * // NOTE: Don't change members of defaultDockWeights since the object is shared.
- * this.defaultDockWeights = Ext.applyIf({ top: 10 }, this.defaultDockWeights);
- *
- * this.callParent();
- * }
- */
- defaultDockWeights: {
- top: { render: 1, visual: 1 },
- left: { render: 3, visual: 5 },
- right: { render: 5, visual: 7 },
- bottom: { render: 7, visual: 3 }
- },
- // @private
- // Values to decide which side of the body element docked items must go
- // This overides any weight. A left/top will *always* sort before a right/bottom
- // regardless of any weight value. Weights sort at either side of the "body" dividing point.
- dockOrder: {
- top: -1,
- left: -1,
- right: 1,
- bottom: 1
- },
- /**
- * Adds docked item(s) to the container.
- * @param {Object/Object[]} component The Component or array of components to add. The components
- * must include a 'dock' parameter on each component to indicate where it should be docked ('top', 'right',
- * 'bottom', 'left').
- * @param {Number} pos (optional) The index at which the Component will be added
- */
- addDocked : function(items, pos) {
- var me = this,
- i = 0,
- item, length;
- items = me.prepareItems(items);
- length = items.length;
- for (; i < length; i++) {
- item = items[i];
- item.dock = item.dock || 'top';
- // Allow older browsers to target docked items to style without borders
- if (me.border === false) {
- // item.cls = item.cls || '' + ' ' + me.baseCls + '-noborder-docked-' + item.dock;
- }
- if (pos !== undefined) {
- me.dockedItems.insert(pos + i, item);
- } else {
- me.dockedItems.add(item);
- }
- if (item.onAdded !== Ext.emptyFn) {
- item.onAdded(me, i);
- }
- if (me.onDockedAdd !== Ext.emptyFn) {
- me.onDockedAdd(item);
- }
- }
- if (me.rendered && !me.suspendLayout) {
- me.updateLayout();
- }
- return items;
- },
- destroyDockedItems: function(){
- var dockedItems = this.dockedItems,
- c;
- if (dockedItems) {
- while ((c = dockedItems.first())) {
- this.removeDocked(c, true);
- }
- }
- },
- doRenderDockedItems: function (out, renderData, after) {
- // Careful! This method is bolted on to the frameTpl and renderTpl so all we get for
- // context is the renderData! The "this" pointer is either the frameTpl or the
- // renderTpl instance!
- // Due to framing, we will be called in two different ways: in the frameTpl or in
- // the renderTpl. The frameTpl version enters via doRenderFramingDockedItems which
- // sets "$skipDockedItems" on the renderTpl's renderData.
- //
- var me = renderData.$comp,
- layout = me.componentLayout;
- if (layout.getDockedItems && !renderData.$skipDockedItems) {
- var items = layout.getDockedItems('render', !after),
- tree = items && layout.getItemsRenderTree(items);
- if (tree) {
- Ext.DomHelper.generateMarkup(tree, out);
- }
- }
- },
- /**
- * Finds a docked component by id, itemId or position. Also see {@link #getDockedItems}
- * @param {String/Number} comp The id, itemId or position of the docked component (see {@link #getComponent} for details)
- * @return {Ext.Component} The docked component (if found)
- */
- getDockedComponent: function(comp) {
- if (Ext.isObject(comp)) {
- comp = comp.getItemId();
- }
- return this.dockedItems.get(comp);
- },
- /**
- * Retrieves an array of all currently docked Components.
- *
- * For example to find a toolbar that has been docked at top:
- *
- * panel.getDockedItems('toolbar[dock="top"]');
- *
- * @param {String} selector A {@link Ext.ComponentQuery ComponentQuery} selector string to filter the returned items.
- * @param {Boolean} beforeBody An optional flag to limit the set of items to only those
- * before the body (true) or after the body (false). All components are returned by
- * default.
- * @return {Ext.Component[]} The array of docked components meeting the specified criteria.
- */
- getDockedItems : function(selector, beforeBody) {
- var dockedItems = this.getComponentLayout().getDockedItems('render', beforeBody);
- if (selector && dockedItems.length) {
- dockedItems = Ext.ComponentQuery.query(selector, dockedItems);
- }
- return dockedItems;
- },
- getDockingRefItems: function(deep, containerItems) {
- // deep fetches the docked items and their descendants using '*' and then '* *'
- var selector = deep && '*,* *',
- // start with only the top/left docked items (and maybe their children)
- dockedItems = this.getDockedItems(selector, true),
- items;
- // push container items (and maybe their children) after top/left docked items:
- dockedItems.push.apply(dockedItems, containerItems);
- // push right/bottom docked items (and maybe their children) after container items:
- items = this.getDockedItems(selector, false);
- dockedItems.push.apply(dockedItems, items);
- return dockedItems;
- },
- initDockingItems: function() {
- var me = this,
- items = me.dockedItems;
- me.dockedItems = new Ext.util.AbstractMixedCollection(false, me.getComponentId);
- if (items) {
- me.addDocked(items);
- }
- },
- /**
- * Inserts docked item(s) to the panel at the indicated position.
- * @param {Number} pos The index at which the Component will be inserted
- * @param {Object/Object[]} component. The Component or array of components to add. The components
- * must include a 'dock' paramater on each component to indicate where it should be docked ('top', 'right',
- * 'bottom', 'left').
- */
- insertDocked : function(pos, items) {
- this.addDocked(items, pos);
- },
- // Placeholder empty functions
- /**
- * Invoked after a docked item is added to the Panel.
- * @param {Ext.Component} component
- * @template
- * @protected
- */
- onDockedAdd : Ext.emptyFn,
- /**
- * Invoked after a docked item is removed from the Panel.
- * @param {Ext.Component} component
- * @template
- * @protected
- */
- onDockedRemove : Ext.emptyFn,
- /**
- * Removes the docked item from the panel.
- * @param {Ext.Component} item. The Component to remove.
- * @param {Boolean} autoDestroy (optional) Destroy the component after removal.
- */
- removeDocked : function(item, autoDestroy) {
- var me = this,
- layout,
- hasLayout;
- if (!me.dockedItems.contains(item)) {
- return item;
- }
- layout = me.componentLayout;
- hasLayout = layout && me.rendered;
- if (hasLayout) {
- layout.onRemove(item);
- }
- me.dockedItems.remove(item);
- item.onRemoved();
- me.onDockedRemove(item);
- if (autoDestroy === true || (autoDestroy !== false && me.autoDestroy)) {
- item.destroy();
- } else if (hasLayout) {
- // not destroying, make any layout related removals
- layout.afterRemove(item);
- }
- if (!me.destroying && !me.suspendLayout) {
- me.updateLayout();
- }
- return item;
- },
- setupDockingRenderTpl: function (renderTpl) {
- renderTpl.renderDockedItems = this.doRenderDockedItems;
- }
- });
- /**
- * @class Ext.fx.Easing
- *
- * This class contains a series of function definitions used to modify values during an animation.
- * They describe how the intermediate values used during a transition will be calculated. It allows for a transition to change
- * speed over its duration. The following options are available:
- *
- * - linear The default easing type
- * - backIn
- * - backOut
- * - bounceIn
- * - bounceOut
- * - ease
- * - easeIn
- * - easeOut
- * - easeInOut
- * - elasticIn
- * - elasticOut
- * - cubic-bezier(x1, y1, x2, y2)
- *
- * Note that cubic-bezier will create a custom easing curve following the CSS3 [transition-timing-function][0]
- * specification. The four values specify points P1 and P2 of the curve as (x1, y1, x2, y2). All values must
- * be in the range [0, 1] or the definition is invalid.
- *
- * [0]: http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag
- *
- * @singleton
- */
- Ext.ns('Ext.fx');
- Ext.require('Ext.fx.CubicBezier', function() {
- var math = Math,
- pi = math.PI,
- pow = math.pow,
- sin = math.sin,
- sqrt = math.sqrt,
- abs = math.abs,
- backInSeed = 1.70158;
- Ext.fx.Easing = {
- // ease: Ext.fx.CubicBezier.cubicBezier(0.25, 0.1, 0.25, 1),
- // linear: Ext.fx.CubicBezier.cubicBezier(0, 0, 1, 1),
- // 'ease-in': Ext.fx.CubicBezier.cubicBezier(0.42, 0, 1, 1),
- // 'ease-out': Ext.fx.CubicBezier.cubicBezier(0, 0.58, 1, 1),
- // 'ease-in-out': Ext.fx.CubicBezier.cubicBezier(0.42, 0, 0.58, 1),
- // 'easeIn': Ext.fx.CubicBezier.cubicBezier(0.42, 0, 1, 1),
- // 'easeOut': Ext.fx.CubicBezier.cubicBezier(0, 0.58, 1, 1),
- // 'easeInOut': Ext.fx.CubicBezier.cubicBezier(0.42, 0, 0.58, 1)
- };
- Ext.apply(Ext.fx.Easing, {
- linear: function(n) {
- return n;
- },
- ease: function(n) {
- var q = 0.07813 - n / 2,
- alpha = -0.25,
- Q = sqrt(0.0066 + q * q),
- x = Q - q,
- X = pow(abs(x), 1/3) * (x < 0 ? -1 : 1),
- y = -Q - q,
- Y = pow(abs(y), 1/3) * (y < 0 ? -1 : 1),
- t = X + Y + 0.25;
- return pow(1 - t, 2) * 3 * t * 0.1 + (1 - t) * 3 * t * t + t * t * t;
- },
- easeIn: function (n) {
- return pow(n, 1.7);
- },
- easeOut: function (n) {
- return pow(n, 0.48);
- },
- easeInOut: function(n) {
- var q = 0.48 - n / 1.04,
- Q = sqrt(0.1734 + q * q),
- x = Q - q,
- X = pow(abs(x), 1/3) * (x < 0 ? -1 : 1),
- y = -Q - q,
- Y = pow(abs(y), 1/3) * (y < 0 ? -1 : 1),
- t = X + Y + 0.5;
- return (1 - t) * 3 * t * t + t * t * t;
- },
- backIn: function (n) {
- return n * n * ((backInSeed + 1) * n - backInSeed);
- },
- backOut: function (n) {
- n = n - 1;
- return n * n * ((backInSeed + 1) * n + backInSeed) + 1;
- },
- elasticIn: function (n) {
- if (n === 0 || n === 1) {
- return n;
- }
- var p = 0.3,
- s = p / 4;
- return pow(2, -10 * n) * sin((n - s) * (2 * pi) / p) + 1;
- },
- elasticOut: function (n) {
- return 1 - Ext.fx.Easing.elasticIn(1 - n);
- },
- bounceIn: function (n) {
- return 1 - Ext.fx.Easing.bounceOut(1 - n);
- },
- bounceOut: function (n) {
- var s = 7.5625,
- p = 2.75,
- l;
- if (n < (1 / p)) {
- l = s * n * n;
- } else {
- if (n < (2 / p)) {
- n -= (1.5 / p);
- l = s * n * n + 0.75;
- } else {
- if (n < (2.5 / p)) {
- n -= (2.25 / p);
- l = s * n * n + 0.9375;
- } else {
- n -= (2.625 / p);
- l = s * n * n + 0.984375;
- }
- }
- }
- return l;
- }
- });
- Ext.apply(Ext.fx.Easing, {
- 'back-in': Ext.fx.Easing.backIn,
- 'back-out': Ext.fx.Easing.backOut,
- 'ease-in': Ext.fx.Easing.easeIn,
- 'ease-out': Ext.fx.Easing.easeOut,
- 'elastic-in': Ext.fx.Easing.elasticIn,
- 'elastic-out': Ext.fx.Easing.elasticIn,
- 'bounce-in': Ext.fx.Easing.bounceIn,
- 'bounce-out': Ext.fx.Easing.bounceOut,
- 'ease-in-out': Ext.fx.Easing.easeInOut
- });
- });
- /**
- * This class compiles the XTemplate syntax into a function object. The function is used
- * like so:
- *
- * function (out, values, parent, xindex, xcount) {
- * // out is the output array to store results
- * // values, parent, xindex and xcount have their historical meaning
- * }
- *
- * @markdown
- * @private
- */
- Ext.define('Ext.XTemplateCompiler', {
- extend: 'Ext.XTemplateParser',
- // Chrome really likes "new Function" to realize the code block (as in it is
- // 2x-3x faster to call it than using eval), but Firefox chokes on it badly.
- // IE and Opera are also fine with the "new Function" technique.
- useEval: Ext.isGecko,
- // See http://jsperf.com/nige-array-append for quickest way to append to an array of unknown length
- // (Due to arbitrary code execution inside a template, we cannot easily track the length in var)
- // On IE6 and 7 myArray[myArray.length]='foo' is better. On other browsers myArray.push('foo') is better.
- useIndex: Ext.isIE6 || Ext.isIE7,
- useFormat: true,
-
- propNameRe: /^[\w\d\$]*$/,
- compile: function (tpl) {
- var me = this,
- code = me.generate(tpl);
- // When using "new Function", we have to pass our "Ext" variable to it in order to
- // support sandboxing. If we did not, the generated function would use the global
- // "Ext", not the "Ext" from our sandbox (scope chain).
- //
- return me.useEval ? me.evalTpl(code) : (new Function('Ext', code))(Ext);
- },
- generate: function (tpl) {
- var me = this;
- me.body = [
- 'var c0=values, a0 = Ext.isArray(c0), p0=parent, n0=xcount, i0=xindex, v;\n'
- ];
- me.funcs = [
- // note: Ext here is properly sandboxed
- 'var fm=Ext.util.Format;'
- ];
- me.switches = [];
- me.parse(tpl);
- me.funcs.push(
- (me.useEval ? '$=' : 'return') + ' function (' + me.fnArgs + ') {',
- me.body.join(''),
- '}'
- );
- var code = me.funcs.join('\n');
- return code;
- },
- //-----------------------------------
- // XTemplateParser callouts
- doText: function (text) {
- var me = this,
- out = me.body;
- text = text.replace(me.aposRe, "\\'").replace(me.newLineRe, '\\n');
- if (me.useIndex) {
- out.push('out[out.length]=\'', text, '\'\n');
- } else {
- out.push('out.push(\'', text, '\')\n');
- }
- },
- doExpr: function (expr) {
- var out = this.body;
- out.push('if ((v=' + expr + ')!==undefined) out');
- if (this.useIndex) {
- out.push('[out.length]=String(v)\n');
- } else {
- out.push('.push(String(v))\n');
- }
- },
- doTag: function (tag) {
- this.doExpr(this.parseTag(tag));
- },
- doElse: function () {
- this.body.push('} else {\n');
- },
- doEval: function (text) {
- this.body.push(text, '\n');
- },
- doIf: function (action, actions) {
- var me = this;
- // If it's just a propName, use it directly in the if
- if (me.propNameRe.test(action)) {
- me.body.push('if (', me.parseTag(action), ') {\n');
- }
- // Otherwise, it must be an expression, and needs to be returned from an fn which uses with(values)
- else {
- me.body.push('if (', me.addFn(action), me.callFn, ') {\n');
- }
- if (actions.exec) {
- me.doExec(actions.exec);
- }
- },
- doElseIf: function (action, actions) {
- var me = this;
- // If it's just a propName, use it directly in the else if
- if (me.propNameRe.test(action)) {
- me.body.push('} else if (', me.parseTag(action), ') {\n');
- }
- // Otherwise, it must be an expression, and needs to be returned from an fn which uses with(values)
- else {
- me.body.push('} else if (', me.addFn(action), me.callFn, ') {\n');
- }
- if (actions.exec) {
- me.doExec(actions.exec);
- }
- },
- doSwitch: function (action) {
- var me = this;
- // If it's just a propName, use it directly in the switch
- if (me.propNameRe.test(action)) {
- me.body.push('switch (', me.parseTag(action), ') {\n');
- }
- // Otherwise, it must be an expression, and needs to be returned from an fn which uses with(values)
- else {
- me.body.push('switch (', me.addFn(action), me.callFn, ') {\n');
- }
- me.switches.push(0);
- },
- doCase: function (action) {
- var me = this,
- cases = Ext.isArray(action) ? action : [action],
- n = me.switches.length - 1,
- match, i;
- if (me.switches[n]) {
- me.body.push('break;\n');
- } else {
- me.switches[n]++;
- }
- for (i = 0, n = cases.length; i < n; ++i) {
- match = me.intRe.exec(cases[i]);
- cases[i] = match ? match[1] : ("'" + cases[i].replace(me.aposRe,"\\'") + "'");
- }
- me.body.push('case ', cases.join(': case '), ':\n');
- },
- doDefault: function () {
- var me = this,
- n = me.switches.length - 1;
- if (me.switches[n]) {
- me.body.push('break;\n');
- } else {
- me.switches[n]++;
- }
- me.body.push('default:\n');
- },
- doEnd: function (type, actions) {
- var me = this,
- L = me.level-1;
- if (type == 'for') {
- /*
- To exit a for loop we must restore the outer loop's context. The code looks
- like this (which goes with that produced by doFor:
- for (...) { // the part generated by doFor
- ... // the body of the for loop
- // ... any tpl for exec statement goes here...
- }
- parent = p1;
- values = r2;
- xcount = n1;
- xindex = i1
- */
- if (actions.exec) {
- me.doExec(actions.exec);
- }
- me.body.push('}\n');
- me.body.push('parent=p',L,';values=r',L+1,';xcount=n',L,';xindex=i',L,'\n');
- } else if (type == 'if' || type == 'switch') {
- me.body.push('}\n');
- }
- },
- doFor: function (action, actions) {
- var me = this,
- s = me.addFn(action),
- L = me.level,
- up = L-1;
- /*
- We are trying to produce a block of code that looks like below. We use the nesting
- level to uniquely name the control variables.
- var c2 = f5.call(this, out, values, parent, xindex, xcount),
- // c2 is the context object for the for loop
- a2 = Ext.isArray(c2),
- // a2 is the isArray result for the context
- p2 = c1,
- // p2 is the parent context (of the outer for loop)
- r2 = values
- // r2 is the values object to
- parent = a1 ? c1[i1] : p2 // set parent
- // i2 is the loop index and n2 is the number (xcount) of this for loop
- for (var i2 = 0, n2 = a2 ? c2.length : (c2 ? 1 : 0), xcount = n2; i2 < n2; ++i2) {
- values = a2 ? c2[i2] : c2 // adjust special vars to inner scope
- xindex = i2 + 1 // xindex is 1-based
- The body of the loop is whatever comes between the tpl and /tpl statements (which
- is handled by doEnd).
- */
- me.body.push('var c',L,'=',s,me.callFn,', a',L,'=Ext.isArray(c',L,'), p',L,'=c',up,',r',L,'=values\n',
- 'parent=a',up,'?c',up,'[i',up,']:p',L,'\n',
- 'for (var i',L,'=0,n',L,'=a',L,'?c',L,'.length:(c',L,'?1:0), xcount=n',L,';i',L,'<n'+L+';++i',L,'){\n',
- 'values=a',L,'?c',L,'[i',L,']:c',L,'\n',
- 'xindex=i',L,'+1\n');
- },
- doExec: function (action, actions) {
- var me = this,
- name = 'f' + me.funcs.length;
- me.funcs.push('function ' + name + '(' + me.fnArgs + ') {',
- ' try { with(values) {',
- ' ' + action,
- ' }} catch(e) {}',
- '}');
- me.body.push(name + me.callFn + '\n');
- },
- //-----------------------------------
- // Internal
- addFn: function (body) {
- var me = this,
- name = 'f' + me.funcs.length;
- if (body === '.') {
- me.funcs.push('function ' + name + '(' + me.fnArgs + ') {',
- ' return values',
- '}');
- } else if (body === '..') {
- me.funcs.push('function ' + name + '(' + me.fnArgs + ') {',
- ' return parent',
- '}');
- } else {
- me.funcs.push('function ' + name + '(' + me.fnArgs + ') {',
- ' try { with(values) {',
- ' return(' + body + ')',
- ' }} catch(e) {}',
- '}');
- }
- return name;
- },
- parseTag: function (tag) {
- var m = this.tagRe.exec(tag),
- name = m[1],
- format = m[2],
- args = m[3],
- math = m[4],
- v;
- // name = "." - Just use the values object.
- if (name == '.') {
- // filter to not include arrays/objects/nulls
- v = 'Ext.Array.indexOf(["string", "number", "boolean"], typeof values) > -1 || Ext.isDate(values) ? values : ""';
- }
- // name = "#" - Use the xindex
- else if (name == '#') {
- v = 'xindex';
- }
- else if (name.substr(0, 7) == "parent.") {
- v = name;
- }
- // compound Javascript property name (e.g., "foo.bar")
- else if (isNaN(name) && name.indexOf('-') == -1 && name.indexOf('.') != -1) {
- v = "values." + name;
- }
- // number or a '-' in it or a single word (maybe a keyword): use array notation
- // (http://jsperf.com/string-property-access/4)
- else {
- v = "values['" + name + "']";
- }
- if (math) {
- v = '(' + v + math + ')';
- }
- if (format && this.useFormat) {
- args = args ? ',' + args : "";
- if (format.substr(0, 5) != "this.") {
- format = "fm." + format + '(';
- } else {
- format += '(';
- }
- } else {
- return v;
- }
- return format + v + args + ')';
- },
- // @private
- evalTpl: function ($) {
- // We have to use eval to realize the code block and capture the inner func we also
- // don't want a deep scope chain. We only do this in Firefox and it is also unhappy
- // with eval containing a return statement, so instead we assign to "$" and return
- // that. Because we use "eval", we are automatically sandboxed properly.
- eval($);
- return $;
- },
- newLineRe: /\r\n|\r|\n/g,
- aposRe: /[']/g,
- intRe: /^\s*(\d+)\s*$/,
- tagRe: /([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\/]\s?[\d\.\+\-\*\/\(\)]+)?/
- }, function () {
- var proto = this.prototype;
- proto.fnArgs = 'out,values,parent,xindex,xcount';
- proto.callFn = '.call(this,' + proto.fnArgs + ')';
- });
- /**
- * A template class that supports advanced functionality like:
- *
- * - Autofilling arrays using templates and sub-templates
- * - Conditional processing with basic comparison operators
- * - Basic math function support
- * - Execute arbitrary inline code with special built-in template variables
- * - Custom member functions
- * - Many special tags and built-in operators that aren't defined as part of the API, but are supported in the templates that can be created
- *
- * XTemplate provides the templating mechanism built into {@link Ext.view.View}.
- *
- * The {@link Ext.Template} describes the acceptable parameters to pass to the constructor. The following examples
- * demonstrate all of the supported features.
- *
- * # Sample Data
- *
- * This is the data object used for reference in each code example:
- *
- * var data = {
- * name: 'Don Griffin',
- * title: 'Senior Technomage',
- * company: 'Sencha Inc.',
- * drinks: ['Coffee', 'Water', 'More Coffee'],
- * kids: [
- * { name: 'Aubrey', age: 17 },
- * { name: 'Joshua', age: 13 },
- * { name: 'Cale', age: 10 },
- * { name: 'Nikol', age: 5 },
- * { name: 'Solomon', age: 0 }
- * ]
- * };
- *
- * # Auto filling of arrays
- *
- * The **tpl** tag and the **for** operator are used to process the provided data object:
- *
- * - If the value specified in for is an array, it will auto-fill, repeating the template block inside the tpl
- * tag for each item in the array.
- * - If for="." is specified, the data object provided is examined.
- * - While processing an array, the special variable {#} will provide the current array index + 1 (starts at 1, not 0).
- *
- * Examples:
- *
- * <tpl for=".">...</tpl> // loop through array at root node
- * <tpl for="foo">...</tpl> // loop through array at foo node
- * <tpl for="foo.bar">...</tpl> // loop through array at foo.bar node
- *
- * Using the sample data above:
- *
- * var tpl = new Ext.XTemplate(
- * '<p>Kids: ',
- * '<tpl for=".">', // process the data.kids node
- * '<p>{#}. {name}</p>', // use current array index to autonumber
- * '</tpl></p>'
- * );
- * tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
- *
- * An example illustrating how the **for** property can be leveraged to access specified members of the provided data
- * object to populate the template:
- *
- * var tpl = new Ext.XTemplate(
- * '<p>Name: {name}</p>',
- * '<p>Title: {title}</p>',
- * '<p>Company: {company}</p>',
- * '<p>Kids: ',
- * '<tpl for="kids">', // interrogate the kids property within the data
- * '<p>{name}</p>',
- * '</tpl></p>'
- * );
- * tpl.overwrite(panel.body, data); // pass the root node of the data object
- *
- * Flat arrays that contain values (and not objects) can be auto-rendered using the special **`{.}`** variable inside a
- * loop. This variable will represent the value of the array at the current index:
- *
- * var tpl = new Ext.XTemplate(
- * '<p>{name}\'s favorite beverages:</p>',
- * '<tpl for="drinks">',
- * '<div> - {.}</div>',
- * '</tpl>'
- * );
- * tpl.overwrite(panel.body, data);
- *
- * When processing a sub-template, for example while looping through a child array, you can access the parent object's
- * members via the **parent** object:
- *
- * var tpl = new Ext.XTemplate(
- * '<p>Name: {name}</p>',
- * '<p>Kids: ',
- * '<tpl for="kids">',
- * '<tpl if="age > 1">',
- * '<p>{name}</p>',
- * '<p>Dad: {parent.name}</p>',
- * '</tpl>',
- * '</tpl></p>'
- * );
- * tpl.overwrite(panel.body, data);
- *
- * # Conditional processing with basic comparison operators
- *
- * The **tpl** tag and the **if** operator are used to provide conditional checks for deciding whether or not to render
- * specific parts of the template.
- *
- * Using the sample data above:
- *
- * var tpl = new Ext.XTemplate(
- * '<p>Name: {name}</p>',
- * '<p>Kids: ',
- * '<tpl for="kids">',
- * '<tpl if="age > 1">',
- * '<p>{name}</p>',
- * '</tpl>',
- * '</tpl></p>'
- * );
- * tpl.overwrite(panel.body, data);
- *
- * More advanced conditionals are also supported:
- *
- * var tpl = new Ext.XTemplate(
- * '<p>Name: {name}</p>',
- * '<p>Kids: ',
- * '<tpl for="kids">',
- * '<p>{name} is a ',
- * '<tpl if="age >= 13">',
- * '<p>teenager</p>',
- * '<tpl elseif="age >= 2">',
- * '<p>kid</p>',
- * '<tpl else">',
- * '<p>baby</p>',
- * '</tpl>',
- * '</tpl></p>'
- * );
- *
- * var tpl = new Ext.XTemplate(
- * '<p>Name: {name}</p>',
- * '<p>Kids: ',
- * '<tpl for="kids">',
- * '<p>{name} is a ',
- * '<tpl switch="name">',
- * '<tpl case="Aubrey" case="Nikol">',
- * '<p>girl</p>',
- * '<tpl default">',
- * '<p>boy</p>',
- * '</tpl>',
- * '</tpl></p>'
- * );
- *
- * A `break` is implied between each case and default, however, multiple cases can be listed
- * in a single <tpl> tag.
- *
- * # Using double quotes
- *
- * Examples:
- *
- * var tpl = new Ext.XTemplate(
- * "<tpl if='age > 1 && age < 10'>Child</tpl>",
- * "<tpl if='age >= 10 && age < 18'>Teenager</tpl>",
- * "<tpl if='this.isGirl(name)'>...</tpl>",
- * '<tpl if="id == \'download\'">...</tpl>',
- * "<tpl if='needsIcon'><img src='{icon}' class='{iconCls}'/></tpl>",
- * "<tpl if='name == \"Don\"'>Hello</tpl>"
- * );
- *
- * # Basic math support
- *
- * The following basic math operators may be applied directly on numeric data values:
- *
- * + - * /
- *
- * For example:
- *
- * var tpl = new Ext.XTemplate(
- * '<p>Name: {name}</p>',
- * '<p>Kids: ',
- * '<tpl for="kids">',
- * '<tpl if="age > 1">', // <-- Note that the > is encoded
- * '<p>{#}: {name}</p>', // <-- Auto-number each item
- * '<p>In 5 Years: {age+5}</p>', // <-- Basic math
- * '<p>Dad: {parent.name}</p>',
- * '</tpl>',
- * '</tpl></p>'
- * );
- * tpl.overwrite(panel.body, data);
- *
- * # Execute arbitrary inline code with special built-in template variables
- *
- * Anything between `{[ ... ]}` is considered code to be executed in the scope of the template.
- * The expression is evaluated and the result is included in the generated result. There are
- * some special variables available in that code:
- *
- * - **out**: The output array into which the template is being appended (using `push` to later
- * `join`).
- * - **values**: The values in the current scope. If you are using scope changing sub-templates,
- * you can change what values is.
- * - **parent**: The scope (values) of the ancestor template.
- * - **xindex**: If you are in a looping template, the index of the loop you are in (1-based).
- * - **xcount**: If you are in a looping template, the total length of the array you are looping.
- *
- * This example demonstrates basic row striping using an inline code block and the xindex variable:
- *
- * var tpl = new Ext.XTemplate(
- * '<p>Name: {name}</p>',
- * '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
- * '<p>Kids: ',
- * '<tpl for="kids">',
- * '<div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
- * '{name}',
- * '</div>',
- * '</tpl></p>'
- * );
- *
- * Any code contained in "verbatim" blocks (using "{% ... %}") will be inserted directly in
- * the generated code for the template. These blocks are not included in the output. This
- * can be used for simple things like break/continue in a loop, or control structures or
- * method calls (when they don't produce output). The `this` references the template instance.
- *
- * var tpl = new Ext.XTemplate(
- * '<p>Name: {name}</p>',
- * '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
- * '<p>Kids: ',
- * '<tpl for="kids">',
- * '{% if (xindex % 2 === 0) continue; %}',
- * '{name}',
- * '{% if (xindex > 100) break; %}',
- * '</div>',
- * '</tpl></p>'
- * );
- *
- * # Template member functions
- *
- * One or more member functions can be specified in a configuration object passed into the XTemplate constructor for
- * more complex processing:
- *
- * var tpl = new Ext.XTemplate(
- * '<p>Name: {name}</p>',
- * '<p>Kids: ',
- * '<tpl for="kids">',
- * '<tpl if="this.isGirl(name)">',
- * '<p>Girl: {name} - {age}</p>',
- * '<tpl else>',
- * '<p>Boy: {name} - {age}</p>',
- * '</tpl>',
- * '<tpl if="this.isBaby(age)">',
- * '<p>{name} is a baby!</p>',
- * '</tpl>',
- * '</tpl></p>',
- * {
- * // XTemplate configuration:
- * disableFormats: true,
- * // member functions:
- * isGirl: function(name){
- * return name == 'Sara Grace';
- * },
- * isBaby: function(age){
- * return age < 1;
- * }
- * }
- * );
- * tpl.overwrite(panel.body, data);
- */
- Ext.define('Ext.XTemplate', {
- extend: 'Ext.Template',
- requires: 'Ext.XTemplateCompiler',
- /**
- * @cfg {Boolean} compiled
- * Only applies to {@link Ext.Template}, XTemplates are compiled automatically on the
- * first call to {@link #apply} or {@link #applyOut}.
- */
- apply: function(values) {
- return this.applyOut(values, []).join('');
- },
- applyOut: function(values, out) {
- var me = this,
- compiler;
- if (!me.fn) {
- compiler = new Ext.XTemplateCompiler({
- useFormat: me.disableFormats !== true
- });
- me.fn = compiler.compile(me.html);
- }
- try {
- me.fn.call(me, out, values, {}, 1, 1);
- } catch (e) {
- Ext.log('Error: ' + e.message);
- }
- return out;
- },
- /**
- * Does nothing. XTemplates are compiled automatically, so this function simply returns this.
- * @return {Ext.XTemplate} this
- */
- compile: function() {
- return this;
- },
- statics: {
- /**
- * Gets an `XTemplate` from an object (an instance of an {@link Ext#define}'d class).
- * Many times, templates are configured high in the class hierarchy and are to be
- * shared by all classes that derive from that base. To further complicate matters,
- * these templates are seldom actual instances but are rather configurations. For
- * example:
- *
- * Ext.define('MyApp.Class', {
- * someTpl: [
- * 'tpl text here'
- * ]
- * });
- *
- * The goal being to share that template definition with all instances and even
- * instances of derived classes, until `someTpl` is overridden. This method will
- * "upgrade" these configurations to be real `XTemplate` instances *in place* (to
- * avoid creating one instance per object).
- *
- * @param {Object} instance The object from which to get the `XTemplate` (must be
- * an instance of an {@link Ext#define}'d class).
- * @param {String} name The name of the property by which to get the `XTemplate`.
- * @return {Ext.XTemplate} The `XTemplate` instance or null if not found.
- * @protected
- */
- getTpl: function (instance, name) {
- var tpl = instance[name], // go for it! 99% of the time we will get it!
- proto;
- if (tpl && !tpl.isTemplate) { // tpl is just a configuration (not an instance)
- // create the template instance from the configuration:
- tpl = Ext.ClassManager.dynInstantiate('Ext.XTemplate', tpl);
- // and replace the reference with the new instance:
- if (instance.hasOwnProperty(name)) { // the tpl is on the instance
- instance[name] = tpl;
- } else { // must be somewhere in the prototype chain
- for (proto = instance.self.prototype; proto; proto = proto.superclass) {
- if (proto.hasOwnProperty(name)) {
- proto[name] = tpl;
- break;
- }
- }
- }
- }
- // else !tpl (no such tpl) or the tpl is an instance already... either way, tpl
- // is ready to return
- return tpl || null;
- }
- }
- });
- /**
- * A non-rendering placeholder item which instructs the Toolbar's Layout to begin using
- * the right-justified button container.
- *
- * @example
- * Ext.create('Ext.panel.Panel', {
- * title: 'Toolbar Fill Example',
- * width: 300,
- * height: 200,
- * tbar : [
- * 'Item 1',
- * { xtype: 'tbfill' },
- * 'Item 2'
- * ],
- * renderTo: Ext.getBody()
- * });
- */
- Ext.define('Ext.toolbar.Fill', {
- extend: 'Ext.Component',
- alias: 'widget.tbfill',
- alternateClassName: 'Ext.Toolbar.Fill',
- /**
- * @property {Boolean} isFill
- * `true` in this class to identify an objact as an instantiated Fill, or subclass thereof.
- */
- isFill : true,
- flex: 1
- });
- /**
- * @class Ext.fx.target.Element
- *
- * This class represents a animation target for an {@link Ext.Element}. In general this class will not be
- * created directly, the {@link Ext.Element} will be passed to the animation and
- * and the appropriate target will be created.
- */
- Ext.define('Ext.fx.target.Element', {
- /* Begin Definitions */
-
- extend: 'Ext.fx.target.Target',
-
- /* End Definitions */
- type: 'element',
- getElVal: function(el, attr, val) {
- if (val == undefined) {
- if (attr === 'x') {
- val = el.getX();
- }
- else if (attr === 'y') {
- val = el.getY();
- }
- else if (attr === 'scrollTop') {
- val = el.getScroll().top;
- }
- else if (attr === 'scrollLeft') {
- val = el.getScroll().left;
- }
- else if (attr === 'height') {
- val = el.getHeight();
- }
- else if (attr === 'width') {
- val = el.getWidth();
- }
- else {
- val = el.getStyle(attr);
- }
- }
- return val;
- },
- getAttr: function(attr, val) {
- var el = this.target;
- return [[ el, this.getElVal(el, attr, val)]];
- },
- setAttr: function(targetData) {
- var target = this.target,
- ln = targetData.length,
- attrs, attr, o, i, j, ln2, element, value;
- for (i = 0; i < ln; i++) {
- attrs = targetData[i].attrs;
- for (attr in attrs) {
- if (attrs.hasOwnProperty(attr)) {
- ln2 = attrs[attr].length;
- for (j = 0; j < ln2; j++) {
- o = attrs[attr][j];
- element = o[0];
- value = o[1];
- if (attr === 'x') {
- element.setX(value);
- } else if (attr === 'y') {
- element.setY(value);
- } else if (attr === 'scrollTop') {
- element.scrollTo('top', value);
- } else if (attr === 'scrollLeft') {
- element.scrollTo('left',value);
- } else if (attr === 'width') {
- element.setWidth(value);
- } else if (attr === 'height') {
- element.setHeight(value);
- } else {
- element.setStyle(attr, value);
- }
- }
- }
- }
- }
- }
- });
- /**
- * @class Ext.fx.target.ElementCSS
- *
- * This class represents a animation target for an {@link Ext.Element} that supports CSS
- * based animation. In general this class will not be created directly, the {@link Ext.Element}
- * will be passed to the animation and the appropriate target will be created.
- */
- Ext.define('Ext.fx.target.ElementCSS', {
- /* Begin Definitions */
- extend: 'Ext.fx.target.Element',
- /* End Definitions */
- setAttr: function(targetData, isFirstFrame) {
- var cssArr = {
- attrs: [],
- duration: [],
- easing: []
- },
- ln = targetData.length,
- attributes,
- attrs,
- attr,
- easing,
- duration,
- o,
- i,
- j,
- ln2;
- for (i = 0; i < ln; i++) {
- attrs = targetData[i];
- duration = attrs.duration;
- easing = attrs.easing;
- attrs = attrs.attrs;
- for (attr in attrs) {
- if (Ext.Array.indexOf(cssArr.attrs, attr) == -1) {
- cssArr.attrs.push(attr.replace(/[A-Z]/g, function(v) {
- return '-' + v.toLowerCase();
- }));
- cssArr.duration.push(duration + 'ms');
- cssArr.easing.push(easing);
- }
- }
- }
- attributes = cssArr.attrs.join(',');
- duration = cssArr.duration.join(',');
- easing = cssArr.easing.join(', ');
- for (i = 0; i < ln; i++) {
- attrs = targetData[i].attrs;
- for (attr in attrs) {
- ln2 = attrs[attr].length;
- for (j = 0; j < ln2; j++) {
- o = attrs[attr][j];
- o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionProperty', isFirstFrame ? '' : attributes);
- o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionDuration', isFirstFrame ? '' : duration);
- o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionTimingFunction', isFirstFrame ? '' : easing);
- o[0].setStyle(attr, o[1]);
- // Must trigger reflow to make this get used as the start point for the transition that follows
- if (isFirstFrame) {
- o = o[0].dom.offsetWidth;
- }
- else {
- // Remove transition properties when completed.
- o[0].on(Ext.supports.CSS3TransitionEnd, function() {
- this.setStyle(Ext.supports.CSS3Prefix + 'TransitionProperty', null);
- this.setStyle(Ext.supports.CSS3Prefix + 'TransitionDuration', null);
- this.setStyle(Ext.supports.CSS3Prefix + 'TransitionTimingFunction', null);
- }, o[0], { single: true });
- }
- }
- }
- }
- }
- });
- /**
- * @class Ext.fx.target.CompositeElement
- *
- * This class represents a animation target for a {@link Ext.CompositeElement}. It allows
- * each {@link Ext.Element} in the group to be animated as a whole. In general this class will not be
- * created directly, the {@link Ext.CompositeElement} will be passed to the animation and
- * and the appropriate target will be created.
- */
- Ext.define('Ext.fx.target.CompositeElement', {
- /* Begin Definitions */
- extend: 'Ext.fx.target.Element',
- /* End Definitions */
- /**
- * @property {Boolean} isComposite
- * `true` in this class to identify an objact as an instantiated CompositeElement, or subclass thereof.
- */
- isComposite: true,
-
- constructor: function(target) {
- target.id = target.id || Ext.id(null, 'ext-composite-');
- this.callParent([target]);
- },
- getAttr: function(attr, val) {
- var out = [],
- elements = this.target.elements,
- length = elements.length,
- i;
- for (i = 0; i < length; i++) {
- var el = elements[i];
- if (el) {
- el = this.target.getElement(el);
- out.push([el, this.getElVal(el, attr, val)]);
- }
- }
- return out;
- }
- });
- /**
- * @class Ext.fx.target.CompositeElementCSS
- *
- * This class represents a animation target for a {@link Ext.CompositeElement}, where the
- * constituent elements support CSS based animation. It allows each {@link Ext.Element} in
- * the group to be animated as a whole. In general this class will not be created directly,
- * the {@link Ext.CompositeElement} will be passed to the animation and the appropriate target
- * will be created.
- */
- Ext.define('Ext.fx.target.CompositeElementCSS', {
- /* Begin Definitions */
- extend: 'Ext.fx.target.CompositeElement',
- requires: ['Ext.fx.target.ElementCSS'],
- /* End Definitions */
- setAttr: function() {
- return Ext.fx.target.ElementCSS.prototype.setAttr.apply(this, arguments);
- }
- });
- /**
- * @class Ext.fx.target.Sprite
- This class represents a animation target for a {@link Ext.draw.Sprite}. In general this class will not be
- created directly, the {@link Ext.draw.Sprite} will be passed to the animation and
- and the appropriate target will be created.
- * @markdown
- */
- Ext.define('Ext.fx.target.Sprite', {
- /* Begin Definitions */
- extend: 'Ext.fx.target.Target',
- /* End Definitions */
- type: 'draw',
- getFromPrim: function(sprite, attr) {
- var o;
- if (attr == 'translate') {
- o = {
- x: sprite.attr.translation.x || 0,
- y: sprite.attr.translation.y || 0
- };
- }
- else if (attr == 'rotate') {
- o = {
- degrees: sprite.attr.rotation.degrees || 0,
- x: sprite.attr.rotation.x,
- y: sprite.attr.rotation.y
- };
- }
- else {
- o = sprite.attr[attr];
- }
- return o;
- },
- getAttr: function(attr, val) {
- return [[this.target, val != undefined ? val : this.getFromPrim(this.target, attr)]];
- },
- setAttr: function(targetData) {
- var ln = targetData.length,
- spriteArr = [],
- attrs, attr, attrArr, attPtr, spritePtr, idx, value, i, j, x, y, ln2;
- for (i = 0; i < ln; i++) {
- attrs = targetData[i].attrs;
- for (attr in attrs) {
- attrArr = attrs[attr];
- ln2 = attrArr.length;
- for (j = 0; j < ln2; j++) {
- spritePtr = attrArr[j][0];
- attPtr = attrArr[j][1];
- if (attr === 'translate') {
- value = {
- x: attPtr.x,
- y: attPtr.y
- };
- }
- else if (attr === 'rotate') {
- x = attPtr.x;
- if (isNaN(x)) {
- x = null;
- }
- y = attPtr.y;
- if (isNaN(y)) {
- y = null;
- }
- value = {
- degrees: attPtr.degrees,
- x: x,
- y: y
- };
- }
- else if (attr === 'width' || attr === 'height' || attr === 'x' || attr === 'y') {
- value = parseFloat(attPtr);
- }
- else {
- value = attPtr;
- }
- idx = Ext.Array.indexOf(spriteArr, spritePtr);
- if (idx == -1) {
- spriteArr.push([spritePtr, {}]);
- idx = spriteArr.length - 1;
- }
- spriteArr[idx][1][attr] = value;
- }
- }
- }
- ln = spriteArr.length;
- for (i = 0; i < ln; i++) {
- spritePtr = spriteArr[i];
- spritePtr[0].setAttributes(spritePtr[1]);
- }
- this.target.redraw();
- }
- });
- /**
- * @class Ext.fx.target.CompositeSprite
- This class represents a animation target for a {@link Ext.draw.CompositeSprite}. It allows
- each {@link Ext.draw.Sprite} in the group to be animated as a whole. In general this class will not be
- created directly, the {@link Ext.draw.CompositeSprite} will be passed to the animation and
- and the appropriate target will be created.
- * @markdown
- */
- Ext.define('Ext.fx.target.CompositeSprite', {
- /* Begin Definitions */
- extend: 'Ext.fx.target.Sprite',
- /* End Definitions */
- getAttr: function(attr, val) {
- var out = [],
- sprites = [].concat(this.target.items),
- length = sprites.length,
- i;
- for (i = 0; i < length; i++) {
- var sprite = sprites[i];
- out.push([sprite, val != undefined ? val : this.getFromPrim(sprite, attr)]);
- }
- return out;
- }
- });
- /**
- * @class Ext.fx.target.Component
- *
- * This class represents a animation target for a {@link Ext.Component}. In general this class will not be
- * created directly, the {@link Ext.Component} will be passed to the animation and
- * and the appropriate target will be created.
- */
- Ext.define('Ext.fx.target.Component', {
- /* Begin Definitions */
-
- extend: 'Ext.fx.target.Target',
-
- /* End Definitions */
- type: 'component',
- // Methods to call to retrieve unspecified "from" values from a target Component
- getPropMethod: {
- top: function() {
- return this.getPosition(true)[1];
- },
- left: function() {
- return this.getPosition(true)[0];
- },
- x: function() {
- return this.getPosition()[0];
- },
- y: function() {
- return this.getPosition()[1];
- },
- height: function() {
- return this.getHeight();
- },
- width: function() {
- return this.getWidth();
- },
- opacity: function() {
- return this.el.getStyle('opacity');
- }
- },
- compMethod: {
- top: 'setPosition',
- left: 'setPosition',
- x: 'setPagePosition',
- y: 'setPagePosition',
- height: 'setSize',
- width: 'setSize',
- opacity: 'setOpacity'
- },
- // Read the named attribute from the target Component. Use the defined getter for the attribute
- getAttr: function(attr, val) {
- return [[this.target, val !== undefined ? val : this.getPropMethod[attr].call(this.target)]];
- },
- setAttr: function(targetData, isFirstFrame, isLastFrame) {
- var me = this,
- target = me.target,
- ln = targetData.length,
- attrs, attr, o, i, j, meth, targets, left, top, w, h;
- for (i = 0; i < ln; i++) {
- attrs = targetData[i].attrs;
- for (attr in attrs) {
- targets = attrs[attr].length;
- meth = {
- setPosition: {},
- setPagePosition: {},
- setSize: {},
- setOpacity: {}
- };
- for (j = 0; j < targets; j++) {
- o = attrs[attr][j];
- // We REALLY want a single function call, so push these down to merge them: eg
- // meth.setPagePosition.target = <targetComponent>
- // meth.setPagePosition['x'] = 100
- // meth.setPagePosition['y'] = 100
- meth[me.compMethod[attr]].target = o[0];
- meth[me.compMethod[attr]][attr] = o[1];
- }
- if (meth.setPosition.target) {
- o = meth.setPosition;
- left = (o.left === undefined) ? undefined : parseFloat(o.left);
- top = (o.top === undefined) ? undefined : parseFloat(o.top);
- o.target.setPosition(left, top);
- }
- if (meth.setPagePosition.target) {
- o = meth.setPagePosition;
- o.target.setPagePosition(o.x, o.y);
- }
- if (meth.setSize.target) {
- o = meth.setSize;
- // Dimensions not being animated MUST NOT be autosized. They must remain at current value.
- w = (o.width === undefined) ? o.target.getWidth() : parseFloat(o.width);
- h = (o.height === undefined) ? o.target.getHeight() : parseFloat(o.height);
- // Only set the size of the Component on the last frame, or if the animation was
- // configured with dynamic: true.
- // In other cases, we just set the target element size.
- // This will result in either clipping if animating a reduction in size, or the revealing of
- // the inner elements of the Component if animating an increase in size.
- // Component's animate function initially resizes to the larger size before resizing the
- // outer element to clip the contents.
- if (isLastFrame || me.dynamic) {
- o.target.setSize(w, h);
- } else {
- o.target.el.setSize(w, h);
- }
- }
- if (meth.setOpacity.target) {
- o = meth.setOpacity;
- o.target.el.setStyle('opacity', o.opacity);
- }
- }
- }
- }
- });
- /**
- * @class Ext.fx.Manager
- * Animation Manager which keeps track of all current animations and manages them on a frame by frame basis.
- * @private
- * @singleton
- */
- Ext.define('Ext.fx.Manager', {
- /* Begin Definitions */
- singleton: true,
- requires: ['Ext.util.MixedCollection',
- 'Ext.fx.target.Element',
- 'Ext.fx.target.ElementCSS',
- 'Ext.fx.target.CompositeElement',
- 'Ext.fx.target.CompositeElementCSS',
- 'Ext.fx.target.Sprite',
- 'Ext.fx.target.CompositeSprite',
- 'Ext.fx.target.Component'],
- mixins: {
- queue: 'Ext.fx.Queue'
- },
- /* End Definitions */
- constructor: function() {
- this.items = new Ext.util.MixedCollection();
- this.mixins.queue.constructor.call(this);
- // this.requestAnimFrame = (function() {
- // var raf = window.requestAnimationFrame ||
- // window.webkitRequestAnimationFrame ||
- // window.mozRequestAnimationFrame ||
- // window.oRequestAnimationFrame ||
- // window.msRequestAnimationFrame;
- // if (raf) {
- // return function(callback, element) {
- // raf(callback);
- // };
- // }
- // else {
- // return function(callback, element) {
- // window.setTimeout(callback, Ext.fx.Manager.interval);
- // };
- // }
- // })();
- },
- /**
- * @cfg {Number} interval Default interval in miliseconds to calculate each frame. Defaults to 16ms (~60fps)
- */
- interval: 16,
- /**
- * @cfg {Boolean} forceJS Force the use of JavaScript-based animation instead of CSS3 animation, even when CSS3
- * animation is supported by the browser. This defaults to true currently, as CSS3 animation support is still
- * considered experimental at this time, and if used should be thouroughly tested across all targeted browsers.
- * @protected
- */
- forceJS: true,
- // @private Target factory
- createTarget: function(target) {
- var me = this,
- useCSS3 = !me.forceJS && Ext.supports.Transitions,
- targetObj;
- me.useCSS3 = useCSS3;
- if (target) {
- // dom element, string or fly
- if (target.tagName || Ext.isString(target) || target.isFly) {
- target = Ext.get(target);
- targetObj = new Ext.fx.target['Element' + (useCSS3 ? 'CSS' : '')](target);
- }
- // Element
- else if (target.dom) {
- targetObj = new Ext.fx.target['Element' + (useCSS3 ? 'CSS' : '')](target);
- }
- // Element Composite
- else if (target.isComposite) {
- targetObj = new Ext.fx.target['CompositeElement' + (useCSS3 ? 'CSS' : '')](target);
- }
- // Draw Sprite
- else if (target.isSprite) {
- targetObj = new Ext.fx.target.Sprite(target);
- }
- // Draw Sprite Composite
- else if (target.isCompositeSprite) {
- targetObj = new Ext.fx.target.CompositeSprite(target);
- }
- // Component
- else if (target.isComponent) {
- targetObj = new Ext.fx.target.Component(target);
- }
- else if (target.isAnimTarget) {
- return target;
- }
- else {
- return null;
- }
- me.targets.add(targetObj);
- return targetObj;
- }
- else {
- return null;
- }
- },
- /**
- * Add an Anim to the manager. This is done automatically when an Anim instance is created.
- * @param {Ext.fx.Anim} anim
- */
- addAnim: function(anim) {
- var items = this.items,
- task = this.task
- // Make sure we use the anim's id, not the anim target's id here. The anim id will be unique on
- // each call to addAnim. `anim.target` is the DOM element being targeted, and since multiple animations
- // can target a single DOM node concurrently, the target id cannot be assumned to be unique.
- items.add(anim.id, anim);
- //Ext.log('+ added anim ', anim.id, ', target: ', anim.target.getId(), ', duration: ', anim.duration);
- // Start the timer if not already running
- if (!task && items.length) {
- task = this.task = {
- run: this.runner,
- interval: this.interval,
- scope: this
- };
- //Ext.log('--->> Starting task');
- Ext.TaskManager.start(task);
- }
- },
- /**
- * Remove an Anim from the manager. This is done automatically when an Anim ends.
- * @param {Ext.fx.Anim} anim
- */
- removeAnim: function(anim) {
- var me = this,
- items = me.items,
- task = me.task;
-
- items.removeAtKey(anim.id);
- //Ext.log(' X removed anim ', anim.id, ', target: ', anim.target.getId(), ', frames: ', anim.frameCount, ', item count: ', items.length);
-
- // Stop the timer if there are no more managed Anims
- if (task && !items.length) {
- //Ext.log('[]--- Stopping task');
- Ext.TaskManager.stop(task);
- delete me.task;
- }
- },
- /**
- * @private
- * Runner function being called each frame
- */
- runner: function() {
- var me = this,
- items = me.items.getRange(),
- i = 0,
- len = items.length,
- anim;
- //Ext.log(' executing anim runner task with ', len, ' items');
- me.targetArr = {};
- // Single timestamp for all animations this interval
- me.timestamp = new Date();
-
- // Loop to start any new animations first before looping to
- // execute running animations (which will also include all animations
- // started in this loop). This is a subtle difference from simply
- // iterating in one loop and starting then running each animation,
- // but separating the loops is necessary to ensure that all new animations
- // actually kick off prior to existing ones regardless of array order.
- // Otherwise in edge cases when there is excess latency in overall
- // performance, allowing existing animations to run before new ones can
- // lead to dropped frames and subtle race conditions when they are
- // interdependent, which is often the case with certain Element fx.
- for (; i < len; i++) {
- anim = items[i];
-
- if (anim.isReady()) {
- //Ext.log(' starting anim ', anim.id, ', target: ', anim.target.id);
- me.startAnim(anim);
- }
- }
-
- for (i = 0; i < len; i++) {
- anim = items[i];
-
- if (anim.isRunning()) {
- //Ext.log(' running anim ', anim.target.id);
- me.runAnim(anim);
- } else if (!me.useCSS3) {
- // When using CSS3 transitions the animations get paused since they are not
- // needed once the transition is handed over to the browser, so we can
- // ignore this case. However if we are doing JS animations and something is
- // paused here it's possibly unintentional.
- //Ext.log(' (i) anim ', anim.id, ' is active but not running...');
- }
- }
- // Apply all the pending changes to their targets
- me.applyPendingAttrs();
- },
- /**
- * @private
- * Start the individual animation (initialization)
- */
- startAnim: function(anim) {
- anim.start(this.timestamp);
- },
- /**
- * @private
- * Run the individual animation for this frame
- */
- runAnim: function(anim) {
- if (!anim) {
- return;
- }
- var me = this,
- targetId = anim.target.getId(),
- useCSS3 = me.useCSS3 && anim.target.type == 'element',
- elapsedTime = me.timestamp - anim.startTime,
- lastFrame = (elapsedTime >= anim.duration),
- target, o;
- target = this.collectTargetData(anim, elapsedTime, useCSS3, lastFrame);
-
- // For CSS3 animation, we need to immediately set the first frame's attributes without any transition
- // to get a good initial state, then add the transition properties and set the final attributes.
- if (useCSS3) {
- //Ext.log(' (i) using CSS3 transitions');
-
- // Flush the collected attributes, without transition
- anim.target.setAttr(target.anims[anim.id].attributes, true);
- // Add the end frame data
- me.collectTargetData(anim, anim.duration, useCSS3, lastFrame);
- // Pause the animation so runAnim doesn't keep getting called
- anim.paused = true;
- target = anim.target.target;
- // We only want to attach an event on the last element in a composite
- if (anim.target.isComposite) {
- target = anim.target.target.last();
- }
- // Listen for the transitionend event
- o = {};
- o[Ext.supports.CSS3TransitionEnd] = anim.lastFrame;
- o.scope = anim;
- o.single = true;
- target.on(o);
- }
- },
- /**
- * @private
- * Collect target attributes for the given Anim object at the given timestamp
- * @param {Ext.fx.Anim} anim The Anim instance
- * @param {Number} timestamp Time after the anim's start time
- * @param {Boolean} [useCSS3=false] True if using CSS3-based animation, else false
- * @param {Boolean} [isLastFrame=false] True if this is the last frame of animation to be run, else false
- * @return {Object} The animation target wrapper object containing the passed animation along with the
- * new attributes to set on the target's element in the next animation frame.
- */
- collectTargetData: function(anim, elapsedTime, useCSS3, isLastFrame) {
- var targetId = anim.target.getId(),
- target = this.targetArr[targetId];
-
- if (!target) {
- // Create a thin wrapper around the target so that we can create a link between the
- // target element and its associated animations. This is important later when applying
- // attributes to the target so that each animation can be independently run with its own
- // duration and stopped at any point without affecting other animations for the same target.
- target = this.targetArr[targetId] = {
- id: targetId,
- el: anim.target,
- anims: {}
- }
- }
- // This is a wrapper for the animation so that we can also save state along with it,
- // including the current elapsed time and lastFrame status. Even though this method only
- // adds a single anim object per call, each target element could have multiple animations
- // associated with it, which is why the anim is added to the target's `anims` hash by id.
- target.anims[anim.id] = {
- id: anim.id,
- anim: anim,
- elapsed: elapsedTime,
- isLastFrame: isLastFrame,
- // This is the object that gets applied to the target element below in applyPendingAttrs():
- attributes: [{
- duration: anim.duration,
- easing: (useCSS3 && anim.reverse) ? anim.easingFn.reverse().toCSS3() : anim.easing,
- // This is where the magic happens. The anim calculates what its new attributes should
- // be based on the current frame and returns those as a hash of values.
- attrs: anim.runAnim(elapsedTime)
- }]
- };
-
- return target;
- },
-
- /**
- * @private
- * Apply all pending attribute changes to their targets
- */
- applyPendingAttrs: function() {
- var targetArr = this.targetArr,
- target, targetId, animWrap, anim, animId;
-
- // Loop through each target
- for (targetId in targetArr) {
- if (targetArr.hasOwnProperty(targetId)) {
- target = targetArr[targetId];
-
- // Each target could have multiple associated animations, so iterate those
- for (animId in target.anims) {
- if (target.anims.hasOwnProperty(animId)) {
- animWrap = target.anims[animId],
- anim = animWrap.anim;
-
- // If the animation has valid attributes, set them on the target
- if (animWrap.attributes && anim.isRunning()) {
- //Ext.log(' > applying attributes for anim ', animWrap.id, ', target: ', target.id, ', elapsed: ', animWrap.elapsed);
- target.el.setAttr(animWrap.attributes, false, animWrap.isLastFrame);
-
- // If this particular anim is at the last frame end it
- if (animWrap.isLastFrame) {
- //Ext.log(' running last frame for ', animWrap.id, ', target: ', targetId);
- anim.lastFrame();
- }
- }
- }
- }
- }
- }
- }
- });
- /**
- * @class Ext.fx.Animator
- *
- * This class is used to run keyframe based animations, which follows the CSS3 based animation structure.
- * Keyframe animations differ from typical from/to animations in that they offer the ability to specify values
- * at various points throughout the animation.
- *
- * ## Using Keyframes
- *
- * The {@link #keyframes} option is the most important part of specifying an animation when using this
- * class. A key frame is a point in a particular animation. We represent this as a percentage of the
- * total animation duration. At each key frame, we can specify the target values at that time. Note that
- * you *must* specify the values at 0% and 100%, the start and ending values. There is also a {@link #keyframe}
- * event that fires after each key frame is reached.
- *
- * ## Example
- *
- * In the example below, we modify the values of the element at each fifth throughout the animation.
- *
- * @example
- * Ext.create('Ext.fx.Animator', {
- * target: Ext.getBody().createChild({
- * style: {
- * width: '100px',
- * height: '100px',
- * 'background-color': 'red'
- * }
- * }),
- * duration: 10000, // 10 seconds
- * keyframes: {
- * 0: {
- * opacity: 1,
- * backgroundColor: 'FF0000'
- * },
- * 20: {
- * x: 30,
- * opacity: 0.5
- * },
- * 40: {
- * x: 130,
- * backgroundColor: '0000FF'
- * },
- * 60: {
- * y: 80,
- * opacity: 0.3
- * },
- * 80: {
- * width: 200,
- * y: 200
- * },
- * 100: {
- * opacity: 1,
- * backgroundColor: '00FF00'
- * }
- * }
- * });
- */
- Ext.define('Ext.fx.Animator', {
- /* Begin Definitions */
- mixins: {
- observable: 'Ext.util.Observable'
- },
- requires: ['Ext.fx.Manager'],
- /* End Definitions */
- /**
- * @property {Boolean} isAnimator
- * `true` in this class to identify an objact as an instantiated Animator, or subclass thereof.
- */
- isAnimator: true,
- /**
- * @cfg {Number} duration
- * Time in milliseconds for the animation to last. Defaults to 250.
- */
- duration: 250,
- /**
- * @cfg {Number} delay
- * Time to delay before starting the animation. Defaults to 0.
- */
- delay: 0,
- /* private used to track a delayed starting time */
- delayStart: 0,
- /**
- * @cfg {Boolean} dynamic
- * Currently only for Component Animation: Only set a component's outer element size bypassing layouts. Set to true to do full layouts for every frame of the animation. Defaults to false.
- */
- dynamic: false,
- /**
- * @cfg {String} easing
- *
- * This describes how the intermediate values used during a transition will be calculated. It allows for a transition to change
- * speed over its duration.
- *
- * - backIn
- * - backOut
- * - bounceIn
- * - bounceOut
- * - ease
- * - easeIn
- * - easeOut
- * - easeInOut
- * - elasticIn
- * - elasticOut
- * - cubic-bezier(x1, y1, x2, y2)
- *
- * Note that cubic-bezier will create a custom easing curve following the CSS3 [transition-timing-function][0]
- * specification. The four values specify points P1 and P2 of the curve as (x1, y1, x2, y2). All values must
- * be in the range [0, 1] or the definition is invalid.
- *
- * [0]: http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag
- */
- easing: 'ease',
- /**
- * Flag to determine if the animation has started
- * @property running
- * @type Boolean
- */
- running: false,
- /**
- * Flag to determine if the animation is paused. Only set this to true if you need to
- * keep the Anim instance around to be unpaused later; otherwise call {@link #end}.
- * @property paused
- * @type Boolean
- */
- paused: false,
- /**
- * @private
- */
- damper: 1,
- /**
- * @cfg {Number} iterations
- * Number of times to execute the animation. Defaults to 1.
- */
- iterations: 1,
- /**
- * Current iteration the animation is running.
- * @property currentIteration
- * @type Number
- */
- currentIteration: 0,
- /**
- * Current keyframe step of the animation.
- * @property keyframeStep
- * @type Number
- */
- keyframeStep: 0,
- /**
- * @private
- */
- animKeyFramesRE: /^(from|to|\d+%?)$/,
- /**
- * @cfg {Ext.fx.target.Target} target
- * The Ext.fx.target to apply the animation to. If not specified during initialization, this can be passed to the applyAnimator
- * method to apply the same animation to many targets.
- */
- /**
- * @cfg {Object} keyframes
- * Animation keyframes follow the CSS3 Animation configuration pattern. 'from' is always considered '0%' and 'to'
- * is considered '100%'.<b>Every keyframe declaration must have a keyframe rule for 0% and 100%, possibly defined using
- * "from" or "to"</b>. A keyframe declaration without these keyframe selectors is invalid and will not be available for
- * animation. The keyframe declaration for a keyframe rule consists of properties and values. Properties that are unable to
- * be animated are ignored in these rules, with the exception of 'easing' which can be changed at each keyframe. For example:
- <pre><code>
- keyframes : {
- '0%': {
- left: 100
- },
- '40%': {
- left: 150
- },
- '60%': {
- left: 75
- },
- '100%': {
- left: 100
- }
- }
- </code></pre>
- */
- constructor: function(config) {
- var me = this;
- config = Ext.apply(me, config || {});
- me.config = config;
- me.id = Ext.id(null, 'ext-animator-');
- me.addEvents(
- /**
- * @event beforeanimate
- * Fires before the animation starts. A handler can return false to cancel the animation.
- * @param {Ext.fx.Animator} this
- */
- 'beforeanimate',
- /**
- * @event keyframe
- * Fires at each keyframe.
- * @param {Ext.fx.Animator} this
- * @param {Number} keyframe step number
- */
- 'keyframe',
- /**
- * @event afteranimate
- * Fires when the animation is complete.
- * @param {Ext.fx.Animator} this
- * @param {Date} startTime
- */
- 'afteranimate'
- );
- me.mixins.observable.constructor.call(me, config);
- me.timeline = [];
- me.createTimeline(me.keyframes);
- if (me.target) {
- me.applyAnimator(me.target);
- Ext.fx.Manager.addAnim(me);
- }
- },
- /**
- * @private
- */
- sorter: function (a, b) {
- return a.pct - b.pct;
- },
- /**
- * @private
- * Takes the given keyframe configuration object and converts it into an ordered array with the passed attributes per keyframe
- * or applying the 'to' configuration to all keyframes. Also calculates the proper animation duration per keyframe.
- */
- createTimeline: function(keyframes) {
- var me = this,
- attrs = [],
- to = me.to || {},
- duration = me.duration,
- prevMs, ms, i, ln, pct, anim, nextAnim, attr;
- for (pct in keyframes) {
- if (keyframes.hasOwnProperty(pct) && me.animKeyFramesRE.test(pct)) {
- attr = {attrs: Ext.apply(keyframes[pct], to)};
- // CSS3 spec allow for from/to to be specified.
- if (pct == "from") {
- pct = 0;
- }
- else if (pct == "to") {
- pct = 100;
- }
- // convert % values into integers
- attr.pct = parseInt(pct, 10);
- attrs.push(attr);
- }
- }
- // Sort by pct property
- Ext.Array.sort(attrs, me.sorter);
- // Only an end
- //if (attrs[0].pct) {
- // attrs.unshift({pct: 0, attrs: element.attrs});
- //}
- ln = attrs.length;
- for (i = 0; i < ln; i++) {
- prevMs = (attrs[i - 1]) ? duration * (attrs[i - 1].pct / 100) : 0;
- ms = duration * (attrs[i].pct / 100);
- me.timeline.push({
- duration: ms - prevMs,
- attrs: attrs[i].attrs
- });
- }
- },
- /**
- * Applies animation to the Ext.fx.target
- * @private
- * @param target
- * @type String/Object
- */
- applyAnimator: function(target) {
- var me = this,
- anims = [],
- timeline = me.timeline,
- reverse = me.reverse,
- ln = timeline.length,
- anim, easing, damper, initial, attrs, lastAttrs, i;
- if (me.fireEvent('beforeanimate', me) !== false) {
- for (i = 0; i < ln; i++) {
- anim = timeline[i];
- attrs = anim.attrs;
- easing = attrs.easing || me.easing;
- damper = attrs.damper || me.damper;
- delete attrs.easing;
- delete attrs.damper;
- anim = new Ext.fx.Anim({
- target: target,
- easing: easing,
- damper: damper,
- duration: anim.duration,
- paused: true,
- to: attrs
- });
- anims.push(anim);
- }
- me.animations = anims;
- me.target = anim.target;
- for (i = 0; i < ln - 1; i++) {
- anim = anims[i];
- anim.nextAnim = anims[i + 1];
- anim.on('afteranimate', function() {
- this.nextAnim.paused = false;
- });
- anim.on('afteranimate', function() {
- this.fireEvent('keyframe', this, ++this.keyframeStep);
- }, me);
- }
- anims[ln - 1].on('afteranimate', function() {
- this.lastFrame();
- }, me);
- }
- },
- /**
- * @private
- * Fires beforeanimate and sets the running flag.
- */
- start: function(startTime) {
- var me = this,
- delay = me.delay,
- delayStart = me.delayStart,
- delayDelta;
- if (delay) {
- if (!delayStart) {
- me.delayStart = startTime;
- return;
- }
- else {
- delayDelta = startTime - delayStart;
- if (delayDelta < delay) {
- return;
- }
- else {
- // Compensate for frame delay;
- startTime = new Date(delayStart.getTime() + delay);
- }
- }
- }
- if (me.fireEvent('beforeanimate', me) !== false) {
- me.startTime = startTime;
- me.running = true;
- me.animations[me.keyframeStep].paused = false;
- }
- },
- /**
- * @private
- * Perform lastFrame cleanup and handle iterations
- * @returns a hash of the new attributes.
- */
- lastFrame: function() {
- var me = this,
- iter = me.iterations,
- iterCount = me.currentIteration;
- iterCount++;
- if (iterCount < iter) {
- me.startTime = new Date();
- me.currentIteration = iterCount;
- me.keyframeStep = 0;
- me.applyAnimator(me.target);
- me.animations[me.keyframeStep].paused = false;
- }
- else {
- me.currentIteration = 0;
- me.end();
- }
- },
- /**
- * Fire afteranimate event and end the animation. Usually called automatically when the
- * animation reaches its final frame, but can also be called manually to pre-emptively
- * stop and destroy the running animation.
- */
- end: function() {
- var me = this;
- me.fireEvent('afteranimate', me, me.startTime, new Date() - me.startTime);
- },
-
- isReady: function() {
- return this.paused === false && this.running === false && this.iterations > 0;
- },
-
- isRunning: function() {
- // Explicitly return false, we don't want to be run continuously by the manager
- return false;
- }
- });
- /**
- * @class Ext.draw.Draw
- * Base Drawing class. Provides base drawing functions.
- * @private
- */
- Ext.define('Ext.draw.Draw', {
- /* Begin Definitions */
- singleton: true,
- requires: ['Ext.draw.Color'],
- /* End Definitions */
- pathToStringRE: /,?([achlmqrstvxz]),?/gi,
- pathCommandRE: /([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,
- pathValuesRE: /(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,
- stopsRE: /^(\d+%?)$/,
- radian: Math.PI / 180,
- availableAnimAttrs: {
- along: "along",
- blur: null,
- "clip-rect": "csv",
- cx: null,
- cy: null,
- fill: "color",
- "fill-opacity": null,
- "font-size": null,
- height: null,
- opacity: null,
- path: "path",
- r: null,
- rotation: "csv",
- rx: null,
- ry: null,
- scale: "csv",
- stroke: "color",
- "stroke-opacity": null,
- "stroke-width": null,
- translation: "csv",
- width: null,
- x: null,
- y: null
- },
- is: function(o, type) {
- type = String(type).toLowerCase();
- return (type == "object" && o === Object(o)) ||
- (type == "undefined" && typeof o == type) ||
- (type == "null" && o === null) ||
- (type == "array" && Array.isArray && Array.isArray(o)) ||
- (Object.prototype.toString.call(o).toLowerCase().slice(8, -1)) == type;
- },
- ellipsePath: function(sprite) {
- var attr = sprite.attr;
- return Ext.String.format("M{0},{1}A{2},{3},0,1,1,{0},{4}A{2},{3},0,1,1,{0},{1}z", attr.x, attr.y - attr.ry, attr.rx, attr.ry, attr.y + attr.ry);
- },
- rectPath: function(sprite) {
- var attr = sprite.attr;
- if (attr.radius) {
- return Ext.String.format("M{0},{1}l{2},0a{3},{3},0,0,1,{3},{3}l0,{5}a{3},{3},0,0,1,{4},{3}l{6},0a{3},{3},0,0,1,{4},{4}l0,{7}a{3},{3},0,0,1,{3},{4}z", attr.x + attr.radius, attr.y, attr.width - attr.radius * 2, attr.radius, -attr.radius, attr.height - attr.radius * 2, attr.radius * 2 - attr.width, attr.radius * 2 - attr.height);
- }
- else {
- return Ext.String.format("M{0},{1}l{2},0,0,{3},{4},0z", attr.x, attr.y, attr.width, attr.height, -attr.width);
- }
- },
- // To be deprecated, converts itself (an arrayPath) to a proper SVG path string
- path2string: function () {
- return this.join(",").replace(Ext.draw.Draw.pathToStringRE, "$1");
- },
- // Convert the passed arrayPath to a proper SVG path string (d attribute)
- pathToString: function(arrayPath) {
- return arrayPath.join(",").replace(Ext.draw.Draw.pathToStringRE, "$1");
- },
- parsePathString: function (pathString) {
- if (!pathString) {
- return null;
- }
- var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0},
- data = [],
- me = this;
- if (me.is(pathString, "array") && me.is(pathString[0], "array")) { // rough assumption
- data = me.pathClone(pathString);
- }
- if (!data.length) {
- String(pathString).replace(me.pathCommandRE, function (a, b, c) {
- var params = [],
- name = b.toLowerCase();
- c.replace(me.pathValuesRE, function (a, b) {
- b && params.push(+b);
- });
- if (name == "m" && params.length > 2) {
- data.push([b].concat(Ext.Array.splice(params, 0, 2)));
- name = "l";
- b = (b == "m") ? "l" : "L";
- }
- while (params.length >= paramCounts[name]) {
- data.push([b].concat(Ext.Array.splice(params, 0, paramCounts[name])));
- if (!paramCounts[name]) {
- break;
- }
- }
- });
- }
- data.toString = me.path2string;
- return data;
- },
- mapPath: function (path, matrix) {
- if (!matrix) {
- return path;
- }
- var x, y, i, ii, j, jj, pathi;
- path = this.path2curve(path);
- for (i = 0, ii = path.length; i < ii; i++) {
- pathi = path[i];
- for (j = 1, jj = pathi.length; j < jj-1; j += 2) {
- x = matrix.x(pathi[j], pathi[j + 1]);
- y = matrix.y(pathi[j], pathi[j + 1]);
- pathi[j] = x;
- pathi[j + 1] = y;
- }
- }
- return path;
- },
- pathClone: function(pathArray) {
- var res = [],
- j, jj, i, ii;
- if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { // rough assumption
- pathArray = this.parsePathString(pathArray);
- }
- for (i = 0, ii = pathArray.length; i < ii; i++) {
- res[i] = [];
- for (j = 0, jj = pathArray[i].length; j < jj; j++) {
- res[i][j] = pathArray[i][j];
- }
- }
- res.toString = this.path2string;
- return res;
- },
- pathToAbsolute: function (pathArray) {
- if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { // rough assumption
- pathArray = this.parsePathString(pathArray);
- }
- var res = [],
- x = 0,
- y = 0,
- mx = 0,
- my = 0,
- i = 0,
- ln = pathArray.length,
- r, pathSegment, j, ln2;
- // MoveTo initial x/y position
- if (ln && pathArray[0][0] == "M") {
- x = +pathArray[0][1];
- y = +pathArray[0][2];
- mx = x;
- my = y;
- i++;
- res[0] = ["M", x, y];
- }
- for (; i < ln; i++) {
- r = res[i] = [];
- pathSegment = pathArray[i];
- if (pathSegment[0] != pathSegment[0].toUpperCase()) {
- r[0] = pathSegment[0].toUpperCase();
- switch (r[0]) {
- // Elliptical Arc
- case "A":
- r[1] = pathSegment[1];
- r[2] = pathSegment[2];
- r[3] = pathSegment[3];
- r[4] = pathSegment[4];
- r[5] = pathSegment[5];
- r[6] = +(pathSegment[6] + x);
- r[7] = +(pathSegment[7] + y);
- break;
- // Vertical LineTo
- case "V":
- r[1] = +pathSegment[1] + y;
- break;
- // Horizontal LineTo
- case "H":
- r[1] = +pathSegment[1] + x;
- break;
- case "M":
- // MoveTo
- mx = +pathSegment[1] + x;
- my = +pathSegment[2] + y;
- default:
- j = 1;
- ln2 = pathSegment.length;
- for (; j < ln2; j++) {
- r[j] = +pathSegment[j] + ((j % 2) ? x : y);
- }
- }
- }
- else {
- j = 0;
- ln2 = pathSegment.length;
- for (; j < ln2; j++) {
- res[i][j] = pathSegment[j];
- }
- }
- switch (r[0]) {
- // ClosePath
- case "Z":
- x = mx;
- y = my;
- break;
- // Horizontal LineTo
- case "H":
- x = r[1];
- break;
- // Vertical LineTo
- case "V":
- y = r[1];
- break;
- // MoveTo
- case "M":
- pathSegment = res[i];
- ln2 = pathSegment.length;
- mx = pathSegment[ln2 - 2];
- my = pathSegment[ln2 - 1];
- default:
- pathSegment = res[i];
- ln2 = pathSegment.length;
- x = pathSegment[ln2 - 2];
- y = pathSegment[ln2 - 1];
- }
- }
- res.toString = this.path2string;
- return res;
- },
- // TO BE DEPRECATED
- pathToRelative: function (pathArray) {
- if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) {
- pathArray = this.parsePathString(pathArray);
- }
- var res = [],
- x = 0,
- y = 0,
- mx = 0,
- my = 0,
- start = 0;
- if (pathArray[0][0] == "M") {
- x = pathArray[0][1];
- y = pathArray[0][2];
- mx = x;
- my = y;
- start++;
- res.push(["M", x, y]);
- }
- for (var i = start, ii = pathArray.length; i < ii; i++) {
- var r = res[i] = [],
- pa = pathArray[i];
- if (pa[0] != pa[0].toLowerCase()) {
- r[0] = pa[0].toLowerCase();
- switch (r[0]) {
- case "a":
- r[1] = pa[1];
- r[2] = pa[2];
- r[3] = pa[3];
- r[4] = pa[4];
- r[5] = pa[5];
- r[6] = +(pa[6] - x).toFixed(3);
- r[7] = +(pa[7] - y).toFixed(3);
- break;
- case "v":
- r[1] = +(pa[1] - y).toFixed(3);
- break;
- case "m":
- mx = pa[1];
- my = pa[2];
- default:
- for (var j = 1, jj = pa.length; j < jj; j++) {
- r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
- }
- }
- } else {
- r = res[i] = [];
- if (pa[0] == "m") {
- mx = pa[1] + x;
- my = pa[2] + y;
- }
- for (var k = 0, kk = pa.length; k < kk; k++) {
- res[i][k] = pa[k];
- }
- }
- var len = res[i].length;
- switch (res[i][0]) {
- case "z":
- x = mx;
- y = my;
- break;
- case "h":
- x += +res[i][len - 1];
- break;
- case "v":
- y += +res[i][len - 1];
- break;
- default:
- x += +res[i][len - 2];
- y += +res[i][len - 1];
- }
- }
- res.toString = this.path2string;
- return res;
- },
- // Returns a path converted to a set of curveto commands
- path2curve: function (path) {
- var me = this,
- points = me.pathToAbsolute(path),
- ln = points.length,
- attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
- i, seg, segLn, point;
-
- for (i = 0; i < ln; i++) {
- points[i] = me.command2curve(points[i], attrs);
- if (points[i].length > 7) {
- points[i].shift();
- point = points[i];
- while (point.length) {
- Ext.Array.splice(points, i++, 0, ["C"].concat(Ext.Array.splice(point, 0, 6)));
- }
- Ext.Array.erase(points, i, 1);
- ln = points.length;
- i--;
- }
- seg = points[i];
- segLn = seg.length;
- attrs.x = seg[segLn - 2];
- attrs.y = seg[segLn - 1];
- attrs.bx = parseFloat(seg[segLn - 4]) || attrs.x;
- attrs.by = parseFloat(seg[segLn - 3]) || attrs.y;
- }
- return points;
- },
-
- interpolatePaths: function (path, path2) {
- var me = this,
- p = me.pathToAbsolute(path),
- p2 = me.pathToAbsolute(path2),
- attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
- attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
- fixArc = function (pp, i) {
- if (pp[i].length > 7) {
- pp[i].shift();
- var pi = pp[i];
- while (pi.length) {
- Ext.Array.splice(pp, i++, 0, ["C"].concat(Ext.Array.splice(pi, 0, 6)));
- }
- Ext.Array.erase(pp, i, 1);
- ii = Math.max(p.length, p2.length || 0);
- }
- },
- fixM = function (path1, path2, a1, a2, i) {
- if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
- Ext.Array.splice(path2, i, 0, ["M", a2.x, a2.y]);
- a1.bx = 0;
- a1.by = 0;
- a1.x = path1[i][1];
- a1.y = path1[i][2];
- ii = Math.max(p.length, p2.length || 0);
- }
- };
- for (var i = 0, ii = Math.max(p.length, p2.length || 0); i < ii; i++) {
- p[i] = me.command2curve(p[i], attrs);
- fixArc(p, i);
- (p2[i] = me.command2curve(p2[i], attrs2));
- fixArc(p2, i);
- fixM(p, p2, attrs, attrs2, i);
- fixM(p2, p, attrs2, attrs, i);
- var seg = p[i],
- seg2 = p2[i],
- seglen = seg.length,
- seg2len = seg2.length;
- attrs.x = seg[seglen - 2];
- attrs.y = seg[seglen - 1];
- attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x;
- attrs.by = parseFloat(seg[seglen - 3]) || attrs.y;
- attrs2.bx = (parseFloat(seg2[seg2len - 4]) || attrs2.x);
- attrs2.by = (parseFloat(seg2[seg2len - 3]) || attrs2.y);
- attrs2.x = seg2[seg2len - 2];
- attrs2.y = seg2[seg2len - 1];
- }
- return [p, p2];
- },
-
- //Returns any path command as a curveto command based on the attrs passed
- command2curve: function (pathCommand, d) {
- var me = this;
- if (!pathCommand) {
- return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
- }
- if (pathCommand[0] != "T" && pathCommand[0] != "Q") {
- d.qx = d.qy = null;
- }
- switch (pathCommand[0]) {
- case "M":
- d.X = pathCommand[1];
- d.Y = pathCommand[2];
- break;
- case "A":
- pathCommand = ["C"].concat(me.arc2curve.apply(me, [d.x, d.y].concat(pathCommand.slice(1))));
- break;
- case "S":
- pathCommand = ["C", d.x + (d.x - (d.bx || d.x)), d.y + (d.y - (d.by || d.y))].concat(pathCommand.slice(1));
- break;
- case "T":
- d.qx = d.x + (d.x - (d.qx || d.x));
- d.qy = d.y + (d.y - (d.qy || d.y));
- pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, d.qx, d.qy, pathCommand[1], pathCommand[2]));
- break;
- case "Q":
- d.qx = pathCommand[1];
- d.qy = pathCommand[2];
- pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[3], pathCommand[4]));
- break;
- case "L":
- pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[1], pathCommand[2]);
- break;
- case "H":
- pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], d.y, pathCommand[1], d.y);
- break;
- case "V":
- pathCommand = ["C"].concat(d.x, d.y, d.x, pathCommand[1], d.x, pathCommand[1]);
- break;
- case "Z":
- pathCommand = ["C"].concat(d.x, d.y, d.X, d.Y, d.X, d.Y);
- break;
- }
- return pathCommand;
- },
- quadratic2curve: function (x1, y1, ax, ay, x2, y2) {
- var _13 = 1 / 3,
- _23 = 2 / 3;
- return [
- _13 * x1 + _23 * ax,
- _13 * y1 + _23 * ay,
- _13 * x2 + _23 * ax,
- _13 * y2 + _23 * ay,
- x2,
- y2
- ];
- },
-
- rotate: function (x, y, rad) {
- var cos = Math.cos(rad),
- sin = Math.sin(rad),
- X = x * cos - y * sin,
- Y = x * sin + y * cos;
- return {x: X, y: Y};
- },
- arc2curve: function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
- // for more information of where this Math came from visit:
- // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
- var me = this,
- PI = Math.PI,
- radian = me.radian,
- _120 = PI * 120 / 180,
- rad = radian * (+angle || 0),
- res = [],
- math = Math,
- mcos = math.cos,
- msin = math.sin,
- msqrt = math.sqrt,
- mabs = math.abs,
- masin = math.asin,
- xy, cos, sin, x, y, h, rx2, ry2, k, cx, cy, f1, f2, df, c1, s1, c2, s2,
- t, hx, hy, m1, m2, m3, m4, newres, i, ln, f2old, x2old, y2old;
- if (!recursive) {
- xy = me.rotate(x1, y1, -rad);
- x1 = xy.x;
- y1 = xy.y;
- xy = me.rotate(x2, y2, -rad);
- x2 = xy.x;
- y2 = xy.y;
- cos = mcos(radian * angle);
- sin = msin(radian * angle);
- x = (x1 - x2) / 2;
- y = (y1 - y2) / 2;
- h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
- if (h > 1) {
- h = msqrt(h);
- rx = h * rx;
- ry = h * ry;
- }
- rx2 = rx * rx;
- ry2 = ry * ry;
- k = (large_arc_flag == sweep_flag ? -1 : 1) *
- msqrt(mabs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x)));
- cx = k * rx * y / ry + (x1 + x2) / 2;
- cy = k * -ry * x / rx + (y1 + y2) / 2;
- f1 = masin(((y1 - cy) / ry).toFixed(7));
- f2 = masin(((y2 - cy) / ry).toFixed(7));
- f1 = x1 < cx ? PI - f1 : f1;
- f2 = x2 < cx ? PI - f2 : f2;
- if (f1 < 0) {
- f1 = PI * 2 + f1;
- }
- if (f2 < 0) {
- f2 = PI * 2 + f2;
- }
- if (sweep_flag && f1 > f2) {
- f1 = f1 - PI * 2;
- }
- if (!sweep_flag && f2 > f1) {
- f2 = f2 - PI * 2;
- }
- }
- else {
- f1 = recursive[0];
- f2 = recursive[1];
- cx = recursive[2];
- cy = recursive[3];
- }
- df = f2 - f1;
- if (mabs(df) > _120) {
- f2old = f2;
- x2old = x2;
- y2old = y2;
- f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
- x2 = cx + rx * mcos(f2);
- y2 = cy + ry * msin(f2);
- res = me.arc2curve(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
- }
- df = f2 - f1;
- c1 = mcos(f1);
- s1 = msin(f1);
- c2 = mcos(f2);
- s2 = msin(f2);
- t = math.tan(df / 4);
- hx = 4 / 3 * rx * t;
- hy = 4 / 3 * ry * t;
- m1 = [x1, y1];
- m2 = [x1 + hx * s1, y1 - hy * c1];
- m3 = [x2 + hx * s2, y2 - hy * c2];
- m4 = [x2, y2];
- m2[0] = 2 * m1[0] - m2[0];
- m2[1] = 2 * m1[1] - m2[1];
- if (recursive) {
- return [m2, m3, m4].concat(res);
- }
- else {
- res = [m2, m3, m4].concat(res).join().split(",");
- newres = [];
- ln = res.length;
- for (i = 0; i < ln; i++) {
- newres[i] = i % 2 ? me.rotate(res[i - 1], res[i], rad).y : me.rotate(res[i], res[i + 1], rad).x;
- }
- return newres;
- }
- },
- // TO BE DEPRECATED
- rotateAndTranslatePath: function (sprite) {
- var alpha = sprite.rotation.degrees,
- cx = sprite.rotation.x,
- cy = sprite.rotation.y,
- dx = sprite.translation.x,
- dy = sprite.translation.y,
- path,
- i,
- p,
- xy,
- j,
- res = [];
- if (!alpha && !dx && !dy) {
- return this.pathToAbsolute(sprite.attr.path);
- }
- dx = dx || 0;
- dy = dy || 0;
- path = this.pathToAbsolute(sprite.attr.path);
- for (i = path.length; i--;) {
- p = res[i] = path[i].slice();
- if (p[0] == "A") {
- xy = this.rotatePoint(p[6], p[7], alpha, cx, cy);
- p[6] = xy.x + dx;
- p[7] = xy.y + dy;
- } else {
- j = 1;
- while (p[j + 1] != null) {
- xy = this.rotatePoint(p[j], p[j + 1], alpha, cx, cy);
- p[j] = xy.x + dx;
- p[j + 1] = xy.y + dy;
- j += 2;
- }
- }
- }
- return res;
- },
- // TO BE DEPRECATED
- rotatePoint: function (x, y, alpha, cx, cy) {
- if (!alpha) {
- return {
- x: x,
- y: y
- };
- }
- cx = cx || 0;
- cy = cy || 0;
- x = x - cx;
- y = y - cy;
- alpha = alpha * this.radian;
- var cos = Math.cos(alpha),
- sin = Math.sin(alpha);
- return {
- x: x * cos - y * sin + cx,
- y: x * sin + y * cos + cy
- };
- },
- pathDimensions: function (path) {
- if (!path || !(path + "")) {
- return {x: 0, y: 0, width: 0, height: 0};
- }
- path = this.path2curve(path);
- var x = 0,
- y = 0,
- X = [],
- Y = [],
- i = 0,
- ln = path.length,
- p, xmin, ymin, dim;
- for (; i < ln; i++) {
- p = path[i];
- if (p[0] == "M") {
- x = p[1];
- y = p[2];
- X.push(x);
- Y.push(y);
- }
- else {
- dim = this.curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
- X = X.concat(dim.min.x, dim.max.x);
- Y = Y.concat(dim.min.y, dim.max.y);
- x = p[5];
- y = p[6];
- }
- }
- xmin = Math.min.apply(0, X);
- ymin = Math.min.apply(0, Y);
- return {
- x: xmin,
- y: ymin,
- path: path,
- width: Math.max.apply(0, X) - xmin,
- height: Math.max.apply(0, Y) - ymin
- };
- },
- intersectInside: function(path, cp1, cp2) {
- return (cp2[0] - cp1[0]) * (path[1] - cp1[1]) > (cp2[1] - cp1[1]) * (path[0] - cp1[0]);
- },
- intersectIntersection: function(s, e, cp1, cp2) {
- var p = [],
- dcx = cp1[0] - cp2[0],
- dcy = cp1[1] - cp2[1],
- dpx = s[0] - e[0],
- dpy = s[1] - e[1],
- n1 = cp1[0] * cp2[1] - cp1[1] * cp2[0],
- n2 = s[0] * e[1] - s[1] * e[0],
- n3 = 1 / (dcx * dpy - dcy * dpx);
- p[0] = (n1 * dpx - n2 * dcx) * n3;
- p[1] = (n1 * dpy - n2 * dcy) * n3;
- return p;
- },
- intersect: function(subjectPolygon, clipPolygon) {
- var me = this,
- i = 0,
- ln = clipPolygon.length,
- cp1 = clipPolygon[ln - 1],
- outputList = subjectPolygon,
- cp2, s, e, point, ln2, inputList, j;
- for (; i < ln; ++i) {
- cp2 = clipPolygon[i];
- inputList = outputList;
- outputList = [];
- s = inputList[inputList.length - 1];
- j = 0;
- ln2 = inputList.length;
- for (; j < ln2; j++) {
- e = inputList[j];
- if (me.intersectInside(e, cp1, cp2)) {
- if (!me.intersectInside(s, cp1, cp2)) {
- outputList.push(me.intersectIntersection(s, e, cp1, cp2));
- }
- outputList.push(e);
- }
- else if (me.intersectInside(s, cp1, cp2)) {
- outputList.push(me.intersectIntersection(s, e, cp1, cp2));
- }
- s = e;
- }
- cp1 = cp2;
- }
- return outputList;
- },
-
- bezier : function (a, b, c, d, x) {
- if (x === 0) {
- return a;
- }
- else if (x === 1) {
- return d;
- }
- var du = 1 - x,
- d3 = du * du * du,
- r = x / du;
- return d3 * (a + r * (3 * b + r * (3 * c + d * r)));
- },
-
- bezierDim : function (a, b, c, d) {
- var points = [], r;
- // The min and max happens on boundary or b' == 0
- if (a + 3 * c == d + 3 * b) {
- r = a - b;
- r /= 2 * (a - b - b + c);
- if ( r < 1 && r > 0) {
- points.push(r);
- }
- } else {
- // b'(x) / -3 = (a-3b+3c-d)x^2+ (-2a+4b-2c)x + (a-b)
- // delta = -4 (-b^2+a c+b c-c^2-a d+b d)
- var A = a - 3 * b + 3 * c - d,
- top = 2 * (a - b - b + c),
- C = a - b,
- delta = top * top - 4 * A * C,
- bottom = A + A,
- s;
- if (delta === 0) {
- r = top / bottom;
- if (r < 1 && r > 0) {
- points.push(r);
- }
- } else if (delta > 0) {
- s = Math.sqrt(delta);
- r = (s + top) / bottom;
-
- if (r < 1 && r > 0) {
- points.push(r);
- }
-
- r = (top - s) / bottom;
-
- if (r < 1 && r > 0) {
- points.push(r);
- }
- }
- }
- var min = Math.min(a, d), max = Math.max(a, d);
- for (var i = 0; i < points.length; i++) {
- min = Math.min(min, this.bezier(a, b, c, d, points[i]));
- max = Math.max(max, this.bezier(a, b, c, d, points[i]));
- }
- return [min, max];
- },
-
- curveDim: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
- var x = this.bezierDim(p1x, c1x, c2x, p2x),
- y = this.bezierDim(p1y, c1y, c2y, p2y);
- return {
- min: {
- x: x[0],
- y: y[0]
- },
- max: {
- x: x[1],
- y: y[1]
- }
- };
- },
- /**
- * @private
- *
- * Calculates bezier curve control anchor points for a particular point in a path, with a
- * smoothing curve applied. The smoothness of the curve is controlled by the 'value' parameter.
- * Note that this algorithm assumes that the line being smoothed is normalized going from left
- * to right; it makes special adjustments assuming this orientation.
- *
- * @param {Number} prevX X coordinate of the previous point in the path
- * @param {Number} prevY Y coordinate of the previous point in the path
- * @param {Number} curX X coordinate of the current point in the path
- * @param {Number} curY Y coordinate of the current point in the path
- * @param {Number} nextX X coordinate of the next point in the path
- * @param {Number} nextY Y coordinate of the next point in the path
- * @param {Number} value A value to control the smoothness of the curve; this is used to
- * divide the distance between points, so a value of 2 corresponds to
- * half the distance between points (a very smooth line) while higher values
- * result in less smooth curves. Defaults to 4.
- * @return {Object} Object containing x1, y1, x2, y2 bezier control anchor points; x1 and y1
- * are the control point for the curve toward the previous path point, and
- * x2 and y2 are the control point for the curve toward the next path point.
- */
- getAnchors: function (prevX, prevY, curX, curY, nextX, nextY, value) {
- value = value || 4;
- var M = Math,
- PI = M.PI,
- halfPI = PI / 2,
- abs = M.abs,
- sin = M.sin,
- cos = M.cos,
- atan = M.atan,
- control1Length, control2Length, control1Angle, control2Angle,
- control1X, control1Y, control2X, control2Y, alpha;
- // Find the length of each control anchor line, by dividing the horizontal distance
- // between points by the value parameter.
- control1Length = (curX - prevX) / value;
- control2Length = (nextX - curX) / value;
- // Determine the angle of each control anchor line. If the middle point is a vertical
- // turnaround then we force it to a flat horizontal angle to prevent the curve from
- // dipping above or below the middle point. Otherwise we use an angle that points
- // toward the previous/next target point.
- if ((curY >= prevY && curY >= nextY) || (curY <= prevY && curY <= nextY)) {
- control1Angle = control2Angle = halfPI;
- } else {
- control1Angle = atan((curX - prevX) / abs(curY - prevY));
- if (prevY < curY) {
- control1Angle = PI - control1Angle;
- }
- control2Angle = atan((nextX - curX) / abs(curY - nextY));
- if (nextY < curY) {
- control2Angle = PI - control2Angle;
- }
- }
- // Adjust the calculated angles so they point away from each other on the same line
- alpha = halfPI - ((control1Angle + control2Angle) % (PI * 2)) / 2;
- if (alpha > halfPI) {
- alpha -= PI;
- }
- control1Angle += alpha;
- control2Angle += alpha;
- // Find the control anchor points from the angles and length
- control1X = curX - control1Length * sin(control1Angle);
- control1Y = curY + control1Length * cos(control1Angle);
- control2X = curX + control2Length * sin(control2Angle);
- control2Y = curY + control2Length * cos(control2Angle);
- // One last adjustment, make sure that no control anchor point extends vertically past
- // its target prev/next point, as that results in curves dipping above or below and
- // bending back strangely. If we find this happening we keep the control angle but
- // reduce the length of the control line so it stays within bounds.
- if ((curY > prevY && control1Y < prevY) || (curY < prevY && control1Y > prevY)) {
- control1X += abs(prevY - control1Y) * (control1X - curX) / (control1Y - curY);
- control1Y = prevY;
- }
- if ((curY > nextY && control2Y < nextY) || (curY < nextY && control2Y > nextY)) {
- control2X -= abs(nextY - control2Y) * (control2X - curX) / (control2Y - curY);
- control2Y = nextY;
- }
-
- return {
- x1: control1X,
- y1: control1Y,
- x2: control2X,
- y2: control2Y
- };
- },
- /* Smoothing function for a path. Converts a path into cubic beziers. Value defines the divider of the distance between points.
- * Defaults to a value of 4.
- */
- smooth: function (originalPath, value) {
- var path = this.path2curve(originalPath),
- newp = [path[0]],
- x = path[0][1],
- y = path[0][2],
- j,
- points,
- i = 1,
- ii = path.length,
- beg = 1,
- mx = x,
- my = y,
- cx = 0,
- cy = 0;
- for (; i < ii; i++) {
- var pathi = path[i],
- pathil = pathi.length,
- pathim = path[i - 1],
- pathiml = pathim.length,
- pathip = path[i + 1],
- pathipl = pathip && pathip.length;
- if (pathi[0] == "M") {
- mx = pathi[1];
- my = pathi[2];
- j = i + 1;
- while (path[j][0] != "C") {
- j++;
- }
- cx = path[j][5];
- cy = path[j][6];
- newp.push(["M", mx, my]);
- beg = newp.length;
- x = mx;
- y = my;
- continue;
- }
- if (pathi[pathil - 2] == mx && pathi[pathil - 1] == my && (!pathip || pathip[0] == "M")) {
- var begl = newp[beg].length;
- points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], mx, my, newp[beg][begl - 2], newp[beg][begl - 1], value);
- newp[beg][1] = points.x2;
- newp[beg][2] = points.y2;
- }
- else if (!pathip || pathip[0] == "M") {
- points = {
- x1: pathi[pathil - 2],
- y1: pathi[pathil - 1]
- };
- } else {
- points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], pathi[pathil - 2], pathi[pathil - 1], pathip[pathipl - 2], pathip[pathipl - 1], value);
- }
- newp.push(["C", x, y, points.x1, points.y1, pathi[pathil - 2], pathi[pathil - 1]]);
- x = points.x2;
- y = points.y2;
- }
- return newp;
- },
- findDotAtSegment: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
- var t1 = 1 - t;
- return {
- x: Math.pow(t1, 3) * p1x + Math.pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + Math.pow(t, 3) * p2x,
- y: Math.pow(t1, 3) * p1y + Math.pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + Math.pow(t, 3) * p2y
- };
- },
- snapEnds: function (from, to, stepsMax, prettyNumbers) {
- if (Ext.isDate(from)) {
- return this.snapEndsByDate(from, to, stepsMax);
- }
- var step = (to - from) / stepsMax,
- level = Math.floor(Math.log(step) / Math.LN10) + 1,
- m = Math.pow(10, level),
- cur,
- modulo = Math.round((step % m) * Math.pow(10, 2 - level)),
- interval = [[0, 15], [20, 4], [30, 2], [40, 4], [50, 9], [60, 4], [70, 2], [80, 4], [100, 15]],
- stepCount = 0,
- value,
- weight,
- i,
- topValue,
- topWeight = 1e9,
- ln = interval.length;
- cur = from = Math.floor(from / m) * m;
-
- if(prettyNumbers){
- for (i = 0; i < ln; i++) {
- value = interval[i][0];
- weight = (value - modulo) < 0 ? 1e6 : (value - modulo) / interval[i][1];
- if (weight < topWeight) {
- topValue = value;
- topWeight = weight;
- }
- }
- step = Math.floor(step * Math.pow(10, -level)) * Math.pow(10, level) + topValue * Math.pow(10, level - 2);
- while (cur < to) {
- cur += step;
- stepCount++;
- }
- to = +cur.toFixed(10);
- }else{
- stepCount = stepsMax;
- }
-
- return {
- from: from,
- to: to,
- power: level,
- step: step,
- steps: stepCount
- };
- },
- /**
- * snapEndsByDate is a utility method to deduce an appropriate tick configuration for the data set of given
- * feature. Refer to {@link #snapEnds}.
- *
- * @param {Date} from The minimum value in the data
- * @param {Date} to The maximum value in the data
- * @param {Number} stepsMax The maximum number of ticks
- * @param {Boolean} lockEnds If true, the 'from' and 'to' parameters will be used as fixed end values
- * and will not be adjusted
- * @return {Object} The calculated step and ends info; properties are:
- * - from: The result start value, which may be lower than the original start value
- * - to: The result end value, which may be higher than the original end value
- * - step: The value size of each step
- * - steps: The number of steps. NOTE: the steps may not divide the from/to range perfectly evenly;
- * there may be a smaller distance between the last step and the end value than between prior
- * steps, particularly when the `endsLocked` param is true. Therefore it is best to not use
- * the `steps` result when finding the axis tick points, instead use the `step`, `to`, and
- * `from` to find the correct point for each tick.
- */
- snapEndsByDate: function (from, to, stepsMax, lockEnds) {
- var selectedStep = false,
- scales = [
- [Ext.Date.MILLI, [1, 2, 3, 5, 10, 20, 30, 50, 100, 200, 300, 500]],
- [Ext.Date.SECOND, [1, 2, 3, 5, 10, 15, 30]],
- [Ext.Date.MINUTE, [1, 2, 3, 5, 10, 20, 30]],
- [Ext.Date.HOUR, [1, 2, 3, 4, 6, 12]],
- [Ext.Date.DAY, [1, 2, 3, 7, 14]],
- [Ext.Date.MONTH, [1, 2, 3, 4, 6]]
- ],
- sLen = scales.length,
- stop = false,
- scale, j, yearDiff, s;
- // Find the most desirable scale
- for (s = 0; s < sLen; s++) {
- scale = scales[s];
- if (!stop) {
- for (j = 0; j < scale[1].length; j++) {
- if (to < Ext.Date.add(from, scale[0], scale[1][j] * stepsMax)) {
- selectedStep = [scale[0], scale[1][j]];
- stop = true;
- break;
- }
- }
- }
- }
- if (!selectedStep) {
- yearDiff = this.snapEnds(from.getFullYear(), to.getFullYear() + 1, stepsMax, lockEnds);
- selectedStep = [Date.YEAR, Math.round(yearDiff.step)];
- }
- return this.snapEndsByDateAndStep(from, to, selectedStep, lockEnds);
- },
- /**
- * snapEndsByDateAndStep is a utility method to deduce an appropriate tick configuration for the data set of given
- * feature and specific step size.
- * @param {Date} from The minimum value in the data
- * @param {Date} to The maximum value in the data
- * @param {Array} step An array with two components: The first is the unit of the step (day, month, year, etc). The second one is the number of units for the step (1, 2, etc.).
- * @param {Boolean} lockEnds If true, the 'from' and 'to' parameters will be used as fixed end values
- * and will not be adjusted
- */
- snapEndsByDateAndStep: function(from, to, step, lockEnds) {
- var fromStat = [from.getFullYear(), from.getMonth(), from.getDate(),
- from.getHours(), from.getMinutes(), from.getSeconds(), from.getMilliseconds()],
- steps = 0, testFrom, testTo;
- if (lockEnds) {
- testFrom = from;
- } else {
- switch (step[0]) {
- case Ext.Date.MILLI:
- testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3],
- fromStat[4], fromStat[5], Math.floor(fromStat[6] / step[1]) * step[1]);
- break;
- case Ext.Date.SECOND:
- testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3],
- fromStat[4], Math.floor(fromStat[5] / step[1]) * step[1], 0);
- break;
- case Ext.Date.MINUTE:
- testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3],
- Math.floor(fromStat[4] / step[1]) * step[1], 0, 0);
- break;
- case Ext.Date.HOUR:
- testFrom = new Date(fromStat[0], fromStat[1], fromStat[2],
- Math.floor(fromStat[3] / step[1]) * step[1], 0, 0, 0);
- break;
- case Ext.Date.DAY:
- testFrom = new Date(fromStat[0], fromStat[1],
- Math.floor(fromStat[2] - 1 / step[1]) * step[1] + 1, 0, 0, 0, 0);
- break;
- case Ext.Date.MONTH:
- testFrom = new Date(fromStat[0], Math.floor(fromStat[1] / step[1]) * step[1], 1, 0, 0, 0, 0);
- break;
- default: // Ext.Date.YEAR
- testFrom = new Date(Math.floor(fromStat[0] / step[1]) * step[1], 0, 1, 0, 0, 0, 0);
- break;
- }
- }
- testTo = testFrom;
- // TODO(zhangbei) : We can do it better somehow...
- while (testTo < to) {
- testTo = Ext.Date.add(testTo, step[0], step[1]);
- steps++;
- }
- if (lockEnds) {
- testTo = to;
- }
- return {
- from : +testFrom,
- to : +testTo,
- step : (testTo - testFrom) / steps,
- steps : steps
- };
- },
- sorter: function (a, b) {
- return a.offset - b.offset;
- },
- rad: function(degrees) {
- return degrees % 360 * Math.PI / 180;
- },
- degrees: function(radian) {
- return radian * 180 / Math.PI % 360;
- },
- withinBox: function(x, y, bbox) {
- bbox = bbox || {};
- return (x >= bbox.x && x <= (bbox.x + bbox.width) && y >= bbox.y && y <= (bbox.y + bbox.height));
- },
- parseGradient: function(gradient) {
- var me = this,
- type = gradient.type || 'linear',
- angle = gradient.angle || 0,
- radian = me.radian,
- stops = gradient.stops,
- stopsArr = [],
- stop,
- vector,
- max,
- stopObj;
- if (type == 'linear') {
- vector = [0, 0, Math.cos(angle * radian), Math.sin(angle * radian)];
- max = 1 / (Math.max(Math.abs(vector[2]), Math.abs(vector[3])) || 1);
- vector[2] *= max;
- vector[3] *= max;
- if (vector[2] < 0) {
- vector[0] = -vector[2];
- vector[2] = 0;
- }
- if (vector[3] < 0) {
- vector[1] = -vector[3];
- vector[3] = 0;
- }
- }
- for (stop in stops) {
- if (stops.hasOwnProperty(stop) && me.stopsRE.test(stop)) {
- stopObj = {
- offset: parseInt(stop, 10),
- color: Ext.draw.Color.toHex(stops[stop].color) || '#ffffff',
- opacity: stops[stop].opacity || 1
- };
- stopsArr.push(stopObj);
- }
- }
- // Sort by pct property
- Ext.Array.sort(stopsArr, me.sorter);
- if (type == 'linear') {
- return {
- id: gradient.id,
- type: type,
- vector: vector,
- stops: stopsArr
- };
- }
- else {
- return {
- id: gradient.id,
- type: type,
- centerX: gradient.centerX,
- centerY: gradient.centerY,
- focalX: gradient.focalX,
- focalY: gradient.focalY,
- radius: gradient.radius,
- vector: vector,
- stops: stopsArr
- };
- }
- }
- });
- /**
- * @class Ext.fx.PropertyHandler
- * @ignore
- */
- Ext.define('Ext.fx.PropertyHandler', {
- /* Begin Definitions */
- requires: ['Ext.draw.Draw'],
- statics: {
- defaultHandler: {
- pixelDefaultsRE: /width|height|top$|bottom$|left$|right$/i,
- unitRE: /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/,
- scrollRE: /^scroll/i,
- computeDelta: function(from, end, damper, initial, attr) {
- damper = (typeof damper == 'number') ? damper : 1;
- var unitRE = this.unitRE,
- match = unitRE.exec(from),
- start, units;
- if (match) {
- from = match[1];
- units = match[2];
- if (!this.scrollRE.test(attr) && !units && this.pixelDefaultsRE.test(attr)) {
- units = 'px';
- }
- }
- from = +from || 0;
- match = unitRE.exec(end);
- if (match) {
- end = match[1];
- units = match[2] || units;
- }
- end = +end || 0;
- start = (initial != null) ? initial : from;
- return {
- from: from,
- delta: (end - start) * damper,
- units: units
- };
- },
- get: function(from, end, damper, initialFrom, attr) {
- var ln = from.length,
- out = [],
- i, initial, res, j, len;
- for (i = 0; i < ln; i++) {
- if (initialFrom) {
- initial = initialFrom[i][1].from;
- }
- if (Ext.isArray(from[i][1]) && Ext.isArray(end)) {
- res = [];
- j = 0;
- len = from[i][1].length;
- for (; j < len; j++) {
- res.push(this.computeDelta(from[i][1][j], end[j], damper, initial, attr));
- }
- out.push([from[i][0], res]);
- }
- else {
- out.push([from[i][0], this.computeDelta(from[i][1], end, damper, initial, attr)]);
- }
- }
- return out;
- },
- set: function(values, easing) {
- var ln = values.length,
- out = [],
- i, val, res, len, j;
- for (i = 0; i < ln; i++) {
- val = values[i][1];
- if (Ext.isArray(val)) {
- res = [];
- j = 0;
- len = val.length;
- for (; j < len; j++) {
- res.push(val[j].from + (val[j].delta * easing) + (val[j].units || 0));
- }
- out.push([values[i][0], res]);
- } else {
- out.push([values[i][0], val.from + (val.delta * easing) + (val.units || 0)]);
- }
- }
- return out;
- }
- },
- color: {
- rgbRE: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
- hexRE: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
- hex3RE: /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i,
- parseColor : function(color, damper) {
- damper = (typeof damper == 'number') ? damper : 1;
- var out = false,
- reList = [this.hexRE, this.rgbRE, this.hex3RE],
- length = reList.length,
- match, base, re, i;
- for (i = 0; i < length; i++) {
- re = reList[i];
- base = (i % 2 === 0) ? 16 : 10;
- match = re.exec(color);
- if (match && match.length === 4) {
- if (i === 2) {
- match[1] += match[1];
- match[2] += match[2];
- match[3] += match[3];
- }
- out = {
- red: parseInt(match[1], base),
- green: parseInt(match[2], base),
- blue: parseInt(match[3], base)
- };
- break;
- }
- }
- return out || color;
- },
- computeDelta: function(from, end, damper, initial) {
- from = this.parseColor(from);
- end = this.parseColor(end, damper);
- var start = initial ? initial : from,
- tfrom = typeof start,
- tend = typeof end;
- //Extra check for when the color string is not recognized.
- if (tfrom == 'string' || tfrom == 'undefined'
- || tend == 'string' || tend == 'undefined') {
- return end || start;
- }
- return {
- from: from,
- delta: {
- red: Math.round((end.red - start.red) * damper),
- green: Math.round((end.green - start.green) * damper),
- blue: Math.round((end.blue - start.blue) * damper)
- }
- };
- },
- get: function(start, end, damper, initialFrom) {
- var ln = start.length,
- out = [],
- i, initial;
- for (i = 0; i < ln; i++) {
- if (initialFrom) {
- initial = initialFrom[i][1].from;
- }
- out.push([start[i][0], this.computeDelta(start[i][1], end, damper, initial)]);
- }
- return out;
- },
- set: function(values, easing) {
- var ln = values.length,
- out = [],
- i, val, parsedString, from, delta;
- for (i = 0; i < ln; i++) {
- val = values[i][1];
- if (val) {
- from = val.from;
- delta = val.delta;
- //multiple checks to reformat the color if it can't recognized by computeDelta.
- val = (typeof val == 'object' && 'red' in val)?
- 'rgb(' + val.red + ', ' + val.green + ', ' + val.blue + ')' : val;
- val = (typeof val == 'object' && val.length)? val[0] : val;
- if (typeof val == 'undefined') {
- return [];
- }
- parsedString = typeof val == 'string'? val :
- 'rgb(' + [
- (from.red + Math.round(delta.red * easing)) % 256,
- (from.green + Math.round(delta.green * easing)) % 256,
- (from.blue + Math.round(delta.blue * easing)) % 256
- ].join(',') + ')';
- out.push([
- values[i][0],
- parsedString
- ]);
- }
- }
- return out;
- }
- },
- object: {
- interpolate: function(prop, damper) {
- damper = (typeof damper == 'number') ? damper : 1;
- var out = {},
- p;
- for(p in prop) {
- out[p] = parseInt(prop[p], 10) * damper;
- }
- return out;
- },
- computeDelta: function(from, end, damper, initial) {
- from = this.interpolate(from);
- end = this.interpolate(end, damper);
- var start = initial ? initial : from,
- delta = {},
- p;
- for(p in end) {
- delta[p] = end[p] - start[p];
- }
- return {
- from: from,
- delta: delta
- };
- },
- get: function(start, end, damper, initialFrom) {
- var ln = start.length,
- out = [],
- i, initial;
- for (i = 0; i < ln; i++) {
- if (initialFrom) {
- initial = initialFrom[i][1].from;
- }
- out.push([start[i][0], this.computeDelta(start[i][1], end, damper, initial)]);
- }
- return out;
- },
- set: function(values, easing) {
- var ln = values.length,
- out = [],
- outObject = {},
- i, from, delta, val, p;
- for (i = 0; i < ln; i++) {
- val = values[i][1];
- from = val.from;
- delta = val.delta;
- for (p in from) {
- outObject[p] = Math.round(from[p] + delta[p] * easing);
- }
- out.push([
- values[i][0],
- outObject
- ]);
- }
- return out;
- }
- },
- path: {
- computeDelta: function(from, end, damper, initial) {
- damper = (typeof damper == 'number') ? damper : 1;
- var start;
- from = +from || 0;
- end = +end || 0;
- start = (initial != null) ? initial : from;
- return {
- from: from,
- delta: (end - start) * damper
- };
- },
- forcePath: function(path) {
- if (!Ext.isArray(path) && !Ext.isArray(path[0])) {
- path = Ext.draw.Draw.parsePathString(path);
- }
- return path;
- },
- get: function(start, end, damper, initialFrom) {
- var endPath = this.forcePath(end),
- out = [],
- startLn = start.length,
- startPathLn, pointsLn, i, deltaPath, initial, j, k, path, startPath;
- for (i = 0; i < startLn; i++) {
- startPath = this.forcePath(start[i][1]);
- deltaPath = Ext.draw.Draw.interpolatePaths(startPath, endPath);
- startPath = deltaPath[0];
- endPath = deltaPath[1];
- startPathLn = startPath.length;
- path = [];
- for (j = 0; j < startPathLn; j++) {
- deltaPath = [startPath[j][0]];
- pointsLn = startPath[j].length;
- for (k = 1; k < pointsLn; k++) {
- initial = initialFrom && initialFrom[0][1][j][k].from;
- deltaPath.push(this.computeDelta(startPath[j][k], endPath[j][k], damper, initial));
- }
- path.push(deltaPath);
- }
- out.push([start[i][0], path]);
- }
- return out;
- },
- set: function(values, easing) {
- var ln = values.length,
- out = [],
- i, j, k, newPath, calcPath, deltaPath, deltaPathLn, pointsLn;
- for (i = 0; i < ln; i++) {
- deltaPath = values[i][1];
- newPath = [];
- deltaPathLn = deltaPath.length;
- for (j = 0; j < deltaPathLn; j++) {
- calcPath = [deltaPath[j][0]];
- pointsLn = deltaPath[j].length;
- for (k = 1; k < pointsLn; k++) {
- calcPath.push(deltaPath[j][k].from + deltaPath[j][k].delta * easing);
- }
- newPath.push(calcPath.join(','));
- }
- out.push([values[i][0], newPath.join(',')]);
- }
- return out;
- }
- }
- /* End Definitions */
- }
- }, function() {
- var props = [
- 'outlineColor',
- 'backgroundColor',
- 'borderColor',
- 'borderTopColor',
- 'borderRightColor',
- 'borderBottomColor',
- 'borderLeftColor',
- 'fill',
- 'stroke'
- ],
- length = props.length,
- i = 0,
- prop;
- for (; i<length; i++) {
- prop = props[i];
- this[prop] = this.color;
- }
- });
- /**
- * @class Ext.fx.Anim
- *
- * This class manages animation for a specific {@link #target}. The animation allows
- * animation of various properties on the target, such as size, position, color and others.
- *
- * ## Starting Conditions
- * The starting conditions for the animation are provided by the {@link #from} configuration.
- * Any/all of the properties in the {@link #from} configuration can be specified. If a particular
- * property is not defined, the starting value for that property will be read directly from the target.
- *
- * ## End Conditions
- * The ending conditions for the animation are provided by the {@link #to} configuration. These mark
- * the final values once the animations has finished. The values in the {@link #from} can mirror
- * those in the {@link #to} configuration to provide a starting point.
- *
- * ## Other Options
- * - {@link #duration}: Specifies the time period of the animation.
- * - {@link #easing}: Specifies the easing of the animation.
- * - {@link #iterations}: Allows the animation to repeat a number of times.
- * - {@link #alternate}: Used in conjunction with {@link #iterations}, reverses the direction every second iteration.
- *
- * ## Example Code
- *
- * @example
- * var myComponent = Ext.create('Ext.Component', {
- * renderTo: document.body,
- * width: 200,
- * height: 200,
- * style: 'border: 1px solid red;'
- * });
- *
- * Ext.create('Ext.fx.Anim', {
- * target: myComponent,
- * duration: 1000,
- * from: {
- * width: 400 //starting width 400
- * },
- * to: {
- * width: 300, //end width 300
- * height: 300 // end width 300
- * }
- * });
- */
- Ext.define('Ext.fx.Anim', {
- /* Begin Definitions */
- mixins: {
- observable: 'Ext.util.Observable'
- },
- requires: ['Ext.fx.Manager', 'Ext.fx.Animator', 'Ext.fx.Easing', 'Ext.fx.CubicBezier', 'Ext.fx.PropertyHandler'],
- /* End Definitions */
- /**
- * @property {Boolean} isAnimation
- * `true` in this class to identify an objact as an instantiated Anim, or subclass thereof.
- */
- isAnimation: true,
- /**
- * @cfg {Function} callback
- * A function to be run after the animation has completed.
- */
- /**
- * @cfg {Function} scope
- * The scope that the {@link #callback} function will be called with
- */
- /**
- * @cfg {Number} duration
- * Time in milliseconds for a single animation to last. Defaults to 250. If the {@link #iterations} property is
- * specified, then each animate will take the same duration for each iteration.
- */
- duration: 250,
- /**
- * @cfg {Number} delay
- * Time to delay before starting the animation. Defaults to 0.
- */
- delay: 0,
- /* private used to track a delayed starting time */
- delayStart: 0,
- /**
- * @cfg {Boolean} dynamic
- * Currently only for Component Animation: Only set a component's outer element size bypassing layouts. Set to true to do full layouts for every frame of the animation. Defaults to false.
- */
- dynamic: false,
- /**
- * @cfg {String} easing
- This describes how the intermediate values used during a transition will be calculated. It allows for a transition to change
- speed over its duration.
- -backIn
- -backOut
- -bounceIn
- -bounceOut
- -ease
- -easeIn
- -easeOut
- -easeInOut
- -elasticIn
- -elasticOut
- -cubic-bezier(x1, y1, x2, y2)
- Note that cubic-bezier will create a custom easing curve following the CSS3 [transition-timing-function][0]
- specification. The four values specify points P1 and P2 of the curve as (x1, y1, x2, y2). All values must
- be in the range [0, 1] or the definition is invalid.
- [0]: http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag
- * @markdown
- */
- easing: 'ease',
- /**
- * @cfg {Object} keyframes
- * Animation keyframes follow the CSS3 Animation configuration pattern. 'from' is always considered '0%' and 'to'
- * is considered '100%'.<b>Every keyframe declaration must have a keyframe rule for 0% and 100%, possibly defined using
- * "from" or "to"</b>. A keyframe declaration without these keyframe selectors is invalid and will not be available for
- * animation. The keyframe declaration for a keyframe rule consists of properties and values. Properties that are unable to
- * be animated are ignored in these rules, with the exception of 'easing' which can be changed at each keyframe. For example:
- <pre><code>
- keyframes : {
- '0%': {
- left: 100
- },
- '40%': {
- left: 150
- },
- '60%': {
- left: 75
- },
- '100%': {
- left: 100
- }
- }
- </code></pre>
- */
- /**
- * @private
- */
- damper: 1,
- /**
- * @private
- */
- bezierRE: /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,
- /**
- * Run the animation from the end to the beginning
- * Defaults to false.
- * @cfg {Boolean} reverse
- */
- reverse: false,
- /**
- * Flag to determine if the animation has started
- * @property running
- * @type Boolean
- */
- running: false,
- /**
- * Flag to determine if the animation is paused. Only set this to true if you need to
- * keep the Anim instance around to be unpaused later; otherwise call {@link #end}.
- * @property paused
- * @type Boolean
- */
- paused: false,
- /**
- * Number of times to execute the animation. Defaults to 1.
- * @cfg {Number} iterations
- */
- iterations: 1,
- /**
- * Used in conjunction with iterations to reverse the animation each time an iteration completes.
- * @cfg {Boolean} alternate
- * Defaults to false.
- */
- alternate: false,
- /**
- * Current iteration the animation is running.
- * @property currentIteration
- * @type Number
- */
- currentIteration: 0,
- /**
- * Starting time of the animation.
- * @property startTime
- * @type Date
- */
- startTime: 0,
- /**
- * Contains a cache of the interpolators to be used.
- * @private
- * @property propHandlers
- * @type Object
- */
- /**
- * @cfg {String/Object} target
- * The {@link Ext.fx.target.Target} to apply the animation to. This should only be specified when creating an Ext.fx.Anim directly.
- * The target does not need to be a {@link Ext.fx.target.Target} instance, it can be the underlying object. For example, you can
- * pass a Component, Element or Sprite as the target and the Anim will create the appropriate {@link Ext.fx.target.Target} object
- * automatically.
- */
- /**
- * @cfg {Object} from
- * An object containing property/value pairs for the beginning of the animation. If not specified, the current state of the
- * Ext.fx.target will be used. For example:
- <pre><code>
- from : {
- opacity: 0, // Transparent
- color: '#ffffff', // White
- left: 0
- }
- </code></pre>
- */
- /**
- * @cfg {Object} to
- * An object containing property/value pairs for the end of the animation. For example:
- <pre><code>
- to : {
- opacity: 1, // Opaque
- color: '#00ff00', // Green
- left: 500
- }
- </code></pre>
- */
-
- // private
- frameCount: 0,
- // @private
- constructor: function(config) {
- var me = this,
- curve;
-
- config = config || {};
- // If keyframes are passed, they really want an Animator instead.
- if (config.keyframes) {
- return new Ext.fx.Animator(config);
- }
- Ext.apply(me, config);
- if (me.from === undefined) {
- me.from = {};
- }
- me.propHandlers = {};
- me.config = config;
- me.target = Ext.fx.Manager.createTarget(me.target);
- me.easingFn = Ext.fx.Easing[me.easing];
- me.target.dynamic = me.dynamic;
- // If not a pre-defined curve, try a cubic-bezier
- if (!me.easingFn) {
- me.easingFn = String(me.easing).match(me.bezierRE);
- if (me.easingFn && me.easingFn.length == 5) {
- curve = me.easingFn;
- me.easingFn = Ext.fx.CubicBezier.cubicBezier(+curve[1], +curve[2], +curve[3], +curve[4]);
- }
- }
- me.id = Ext.id(null, 'ext-anim-');
- me.addEvents(
- /**
- * @event beforeanimate
- * Fires before the animation starts. A handler can return false to cancel the animation.
- * @param {Ext.fx.Anim} this
- */
- 'beforeanimate',
- /**
- * @event afteranimate
- * Fires when the animation is complete.
- * @param {Ext.fx.Anim} this
- * @param {Date} startTime
- */
- 'afteranimate',
- /**
- * @event lastframe
- * Fires when the animation's last frame has been set.
- * @param {Ext.fx.Anim} this
- * @param {Date} startTime
- */
- 'lastframe'
- );
- me.mixins.observable.constructor.call(me);
- Ext.fx.Manager.addAnim(me);
- },
- /**
- * @private
- * Helper to the target
- */
- setAttr: function(attr, value) {
- return Ext.fx.Manager.items.get(this.id).setAttr(this.target, attr, value);
- },
- /**
- * @private
- * Set up the initial currentAttrs hash.
- */
- initAttrs: function() {
- var me = this,
- from = me.from,
- to = me.to,
- initialFrom = me.initialFrom || {},
- out = {},
- start, end, propHandler, attr;
- for (attr in to) {
- if (to.hasOwnProperty(attr)) {
- start = me.target.getAttr(attr, from[attr]);
- end = to[attr];
- // Use default (numeric) property handler
- if (!Ext.fx.PropertyHandler[attr]) {
- if (Ext.isObject(end)) {
- propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler.object;
- } else {
- propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler.defaultHandler;
- }
- }
- // Use custom handler
- else {
- propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler[attr];
- }
- out[attr] = propHandler.get(start, end, me.damper, initialFrom[attr], attr);
- }
- }
- me.currentAttrs = out;
- },
- /**
- * @private
- * Fires beforeanimate and sets the running flag.
- */
- start: function(startTime) {
- var me = this,
- delay = me.delay,
- delayStart = me.delayStart,
- delayDelta;
-
- if (delay) {
- if (!delayStart) {
- me.delayStart = startTime;
- return;
- }
- else {
- delayDelta = startTime - delayStart;
- if (delayDelta < delay) {
- return;
- }
- else {
- // Compensate for frame delay;
- startTime = new Date(delayStart.getTime() + delay);
- }
- }
- }
- if (me.fireEvent('beforeanimate', me) !== false) {
- me.startTime = startTime;
- if (!me.paused && !me.currentAttrs) {
- me.initAttrs();
- }
- me.running = true;
- me.frameCount = 0;
- }
- },
- /**
- * @private
- * Calculate attribute value at the passed timestamp.
- * @returns a hash of the new attributes.
- */
- runAnim: function(elapsedTime) {
- var me = this,
- attrs = me.currentAttrs,
- duration = me.duration,
- easingFn = me.easingFn,
- propHandlers = me.propHandlers,
- ret = {},
- easing, values, attr, lastFrame;
- if (elapsedTime >= duration) {
- elapsedTime = duration;
- lastFrame = true;
- }
- if (me.reverse) {
- elapsedTime = duration - elapsedTime;
- }
- for (attr in attrs) {
- if (attrs.hasOwnProperty(attr)) {
- values = attrs[attr];
- easing = lastFrame ? 1 : easingFn(elapsedTime / duration);
- ret[attr] = propHandlers[attr].set(values, easing);
- }
- }
- me.frameCount++;
-
- return ret;
- },
- /**
- * @private
- * Perform lastFrame cleanup and handle iterations
- * @returns a hash of the new attributes.
- */
- lastFrame: function() {
- var me = this,
- iter = me.iterations,
- iterCount = me.currentIteration;
- iterCount++;
- if (iterCount < iter) {
- if (me.alternate) {
- me.reverse = !me.reverse;
- }
- me.startTime = new Date();
- me.currentIteration = iterCount;
- // Turn off paused for CSS3 Transitions
- me.paused = false;
- }
- else {
- me.currentIteration = 0;
- me.end();
- me.fireEvent('lastframe', me, me.startTime);
- }
- },
- /**
- * Fire afteranimate event and end the animation. Usually called automatically when the
- * animation reaches its final frame, but can also be called manually to pre-emptively
- * stop and destroy the running animation.
- */
- end: function() {
- var me = this;
- me.startTime = 0;
- me.paused = false;
- me.running = false;
- Ext.fx.Manager.removeAnim(me);
- me.fireEvent('afteranimate', me, me.startTime);
- Ext.callback(me.callback, me.scope, [me, me.startTime]);
- },
-
- isReady: function() {
- return this.paused === false && this.running === false && this.iterations > 0;
- },
-
- isRunning: function() {
- return this.paused === false && this.running === true && this.isAnimator !== true;
- }
- });
- // Set flag to indicate that Fx is available. Class might not be available immediately.
- Ext.enableFx = true;
- /**
- * A specialized floating Component that supports a drop status icon, {@link Ext.Layer} styles
- * and auto-repair. This is the default drag proxy used by all Ext.dd components.
- */
- Ext.define('Ext.dd.StatusProxy', {
- extend: 'Ext.Component',
- animRepair: false,
- childEls: [
- 'ghost'
- ],
- renderTpl: [
- '<div class="' + Ext.baseCSSPrefix + 'dd-drop-icon"></div>' +
- '<div id="{id}-ghost" class="' + Ext.baseCSSPrefix + 'dd-drag-ghost"></div>'
- ],
- /**
- * Creates new StatusProxy.
- * @param {Object} [config] Config object.
- */
- constructor: function(config) {
- var me = this;
- config = config || {};
- Ext.apply(me, {
- hideMode: 'visibility',
- hidden: true,
- floating: true,
- id: me.id || Ext.id(),
- cls: Ext.baseCSSPrefix + 'dd-drag-proxy ' + this.dropNotAllowed,
- shadow: config.shadow || false,
- renderTo: Ext.getDetachedBody()
- });
- me.callParent(arguments);
- this.dropStatus = this.dropNotAllowed;
- },
- /**
- * @cfg {String} dropAllowed
- * The CSS class to apply to the status element when drop is allowed.
- */
- dropAllowed : Ext.baseCSSPrefix + 'dd-drop-ok',
- /**
- * @cfg {String} dropNotAllowed
- * The CSS class to apply to the status element when drop is not allowed.
- */
- dropNotAllowed : Ext.baseCSSPrefix + 'dd-drop-nodrop',
- /**
- * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
- * over the current target element.
- * @param {String} cssClass The css class for the new drop status indicator image
- */
- setStatus : function(cssClass){
- cssClass = cssClass || this.dropNotAllowed;
- if (this.dropStatus != cssClass) {
- this.el.replaceCls(this.dropStatus, cssClass);
- this.dropStatus = cssClass;
- }
- },
- /**
- * Resets the status indicator to the default dropNotAllowed value
- * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
- */
- reset : function(clearGhost){
- var me = this,
- clsPrefix = Ext.baseCSSPrefix + 'dd-drag-proxy ';
- me.el.replaceCls(clsPrefix + me.dropAllowed, clsPrefix + me.dropNotAllowed);
- me.dropStatus = me.dropNotAllowed;
- if (clearGhost) {
- me.ghost.update('');
- }
- },
- /**
- * Updates the contents of the ghost element
- * @param {String/HTMLElement} html The html that will replace the current innerHTML of the ghost element, or a
- * DOM node to append as the child of the ghost element (in which case the innerHTML will be cleared first).
- */
- update : function(html){
- if (typeof html == "string") {
- this.ghost.update(html);
- } else {
- this.ghost.update("");
- html.style.margin = "0";
- this.ghost.dom.appendChild(html);
- }
- var el = this.ghost.dom.firstChild;
- if (el) {
- Ext.fly(el).setStyle('float', 'none');
- }
- },
- /**
- * Returns the ghost element
- * @return {Ext.Element} el
- */
- getGhost : function(){
- return this.ghost;
- },
- /**
- * Hides the proxy
- * @param {Boolean} clear True to reset the status and clear the ghost contents,
- * false to preserve them
- */
- hide : function(clear) {
- this.callParent();
- if (clear) {
- this.reset(true);
- }
- },
- /**
- * Stops the repair animation if it's currently running
- */
- stop : function(){
- if (this.anim && this.anim.isAnimated && this.anim.isAnimated()) {
- this.anim.stop();
- }
- },
- /**
- * Force the Layer to sync its shadow and shim positions to the element
- */
- sync : function(){
- this.el.sync();
- },
- /**
- * Causes the proxy to return to its position of origin via an animation.
- * Should be called after an invalid drop operation by the item being dragged.
- * @param {Number[]} xy The XY position of the element ([x, y])
- * @param {Function} callback The function to call after the repair is complete.
- * @param {Object} scope The scope (`this` reference) in which the callback function is executed.
- * Defaults to the browser window.
- */
- repair : function(xy, callback, scope) {
- var me = this;
- me.callback = callback;
- me.scope = scope;
- if (xy && me.animRepair !== false) {
- me.el.addCls(Ext.baseCSSPrefix + 'dd-drag-repair');
- me.el.hideUnders(true);
- me.anim = me.el.animate({
- duration: me.repairDuration || 500,
- easing: 'ease-out',
- to: {
- x: xy[0],
- y: xy[1]
- },
- stopAnimation: true,
- callback: me.afterRepair,
- scope: me
- });
- } else {
- me.afterRepair();
- }
- },
- // private
- afterRepair : function() {
- var me = this;
-
- me.hide(true);
- me.el.removeCls(Ext.baseCSSPrefix + 'dd-drag-repair');
- if (typeof me.callback == "function") {
- me.callback.call(me.scope || me);
- }
- delete me.callback;
- delete me.scope;
- }
- });
- /**
- * Base Layout class - extended by ComponentLayout and ContainerLayout
- */
- Ext.define('Ext.layout.Layout', {
- requires: [
- 'Ext.XTemplate'
- ],
- uses: [ 'Ext.layout.Context' ],
- /**
- * @property {Boolean} isLayout
- * `true` in this class to identify an objact as an instantiated Layout, or subclass thereof.
- */
- isLayout: true,
- initialized: false,
- running: false,
- autoSizePolicy: {
- setsWidth: 0,
- setsHeight: 0
- },
- sizeModels: {
- calculated: {
- name: 'calculated',
- auto: false,
- calculated: true,
- configured: false,
- fixed: true,
- natural: false,
- shrinkWrap: false
- },
- calculatedFromNatural: {
- name: 'calculatedFromNatural',
- auto: true,
- calculated: true,
- configured: false,
- fixed: true,
- natural: true,
- shrinkWrap: false
- },
- calculatedFromShrinkWrap: {
- name: 'calculatedFromShrinkWrap',
- auto: true,
- calculated: true,
- configured: false,
- fixed: true,
- natural: false,
- shrinkWrap: true
- },
- configured: {
- name: 'configured',
- auto: false,
- calculated: false,
- configured: true,
- fixed: true,
- natural: false,
- shrinkWrap: false
- },
- natural: {
- name: 'natural',
- auto: true,
- calculated: false,
- configured: false,
- fixed: false,
- natural: true,
- shrinkWrap: false
- },
- shrinkWrap: {
- name: 'shrinkWrap',
- auto: true,
- calculated: false,
- configured: false,
- fixed: false,
- natural: false,
- shrinkWrap: true
- }
- },
- statics: {
- layoutsByType: {},
- create: function(layout, defaultType) {
- var ClassManager = Ext.ClassManager,
- layoutsByType = this.layoutsByType,
- alias, className, config, layoutClass, type, load;
- if (!layout || typeof layout === 'string') {
- type = layout || defaultType;
- config = {};
- } else if (layout.isLayout) {
- return layout;
- } else {
- config = layout;
- type = layout.type || defaultType;
- }
- if (!(layoutClass = layoutsByType[type])) {
- alias = 'layout.' + type;
- className = ClassManager.getNameByAlias(alias);
- // this is needed to support demand loading of the class
- if (!className) {
- load = true;
- }
-
- layoutClass = ClassManager.get(className);
- if (load || !layoutClass) {
- return ClassManager.instantiateByAlias(alias, config || {});
- }
- layoutsByType[type] = layoutClass;
- }
- return new layoutClass(config);
- }
- },
- constructor : function(config) {
- var me = this;
- me.id = Ext.id(null, me.type + '-');
- Ext.apply(me, config);
- me.layoutCount = 0;
- },
- /**
- * @property {Boolean} done Used only during a layout run, this value indicates that a
- * layout has finished its calculations. This flag is set to true prior to the call to
- * {@link #calculate} and should be set to false if this layout has more work to do.
- */
- /**
- * Called before any calculation cycles to prepare for layout.
- *
- * This is a write phase and DOM reads should be strictly avoided when overridding
- * this method.
- *
- * @param {Ext.layout.ContextItem} ownerContext The context item for the layout's owner
- * component.
- * @method beginLayout
- */
- beginLayout: Ext.emptyFn,
- /**
- * Called before any calculation cycles to reset DOM values and prepare for calculation.
- *
- * This is a write phase and DOM reads should be strictly avoided when overridding
- * this method.
- *
- * @param {Ext.layout.ContextItem} ownerContext The context item for the layout's owner
- * component.
- * @method beginLayoutCycle
- */
- beginLayoutCycle: Ext.emptyFn,
- /**
- * Called to perform the calculations for this layout. This method will be called at
- * least once and may be called repeatedly if the {@link #done} property is cleared
- * before return to indicate that this layout is not yet done. The {@link #done} property
- * is always set to `true` before entering this method.
- *
- * This is a read phase and DOM writes should be strictly avoided in derived classes.
- * Instead, DOM writes need to be written to {@link Ext.layout.ContextItem} objects to
- * be flushed at the next opportunity.
- *
- * @param {Ext.layout.ContextItem} ownerContext The context item for the layout's owner
- * component.
- * @method calculate
- * @abstract
- */
- /**
- * This method (if implemented) is called at the end of the cycle in which this layout
- * completes (by not setting {@link #done} to `false` in {@link #calculate}). It is
- * possible for the layout to complete and yet become invalid before the end of the cycle,
- * in which case, this method will not be called. It is also possible for this method to
- * be called and then later the layout becomes invalidated. This will result in
- * {@link #calculate} being called again, followed by another call to this method.
- *
- * This is a read phase and DOM writes should be strictly avoided in derived classes.
- * Instead, DOM writes need to be written to {@link Ext.layout.ContextItem} objects to
- * be flushed at the next opportunity.
- *
- * This method need not be implemented by derived classes and, in fact, should only be
- * implemented when needed.
- *
- * @param {Ext.layout.ContextItem} ownerContext The context item for the layout's owner
- * component.
- * @method completeLayout
- */
- /**
- * This method (if implemented) is called after all layouts have completed. In most
- * ways this is similar to {@link #completeLayout}. This call can cause this (or any
- * layout) to be become invalid (see {@link Ext.layout.Context#invalidate}), but this
- * is best avoided. This method is intended to be where final reads are made and so it
- * is best to avoidinvalidating layouts at this point whenever possible. Even so, this
- * method can be used to perform final checks that may require all other layouts to be
- * complete and then invalidate some results.
- *
- * This is a read phase and DOM writes should be strictly avoided in derived classes.
- * Instead, DOM writes need to be written to {@link Ext.layout.ContextItem} objects to
- * be flushed at the next opportunity.
- *
- * This method need not be implemented by derived classes and, in fact, should only be
- * implemented when needed.
- *
- * @param {Ext.layout.ContextItem} ownerContext The context item for the layout's owner
- * component.
- * @method finalizeLayout
- */
- /**
- * This method is called after all layouts are complete and their calculations flushed
- * to the DOM. No further layouts will be run and this method is only called once per
- * layout run. The base component layout caches {@link #lastComponentSize}.
- *
- * This is a write phase and DOM reads should be avoided if possible when overridding
- * this method.
- *
- * This method need not be implemented by derived classes and, in fact, should only be
- * implemented when needed.
- *
- * @param {Ext.layout.ContextItem} ownerContext The context item for the layout's owner
- * component.
- */
- finishedLayout: function () {
- this.ownerContext = null;
- },
-
- /**
- * This method (if implemented) is called after all layouts are finished, and all have
- * a `lastComponentSize` cached. No further layouts will be run and this method is only
- * called once per layout run. It is the bookend to {@link #beginLayout}.
- *
- * This is a write phase and DOM reads should be avoided if possible when overridding
- * this method. This is the catch-all tail method to a layout and so the rules are more
- * relaxed. Even so, for performance reasons, it is best to avoid reading the DOM. If
- * a read is necessary, consider implementing a {@link #finalizeLayout} method to do the
- * required reads.
- *
- * This method need not be implemented by derived classes and, in fact, should only be
- * implemented when needed.
- *
- * @param {Ext.layout.ContextItem} ownerContext The context item for the layout's owner
- * component.
- * @method notifyOwner
- */
-
- redoLayout: Ext.emptyFn,
- undoLayout: Ext.emptyFn,
- getAnimatePolicy: function() {
- return this.animatePolicy;
- },
- /**
- * Returns an object describing how this layout manages the size of the given component.
- * This method must be implemented by any layout that manages components.
- *
- * @param {Ext.Component} item
- *
- * @return {Object} An object describing the sizing done by the layout for this item or
- * null if the layout mimics the size policy of its ownerCt (e.g., 'fit' and 'card').
- * @return {Boolean} return.readsWidth True if the natural/auto width of this component
- * is used by the ownerLayout.
- * @return {Boolean} return.readsHeight True if the natural/auto height of this component
- * is used by the ownerLayout.
- * @return {Boolean} return.setsWidth True if the ownerLayout set this component's width.
- * @return {Boolean} return.setsHeight True if the ownerLayout set this component's height.
- *
- * @protected
- */
- getItemSizePolicy: function (item) {
- return this.autoSizePolicy;
- },
- isItemBoxParent: function (itemContext) {
- return false;
- },
- isItemLayoutRoot: function (item) {
- var sizeModel = item.getSizeModel(),
- width = sizeModel.width,
- height = sizeModel.height;
- // If this component has never had a layout and some of its dimensions are set by
- // its ownerLayout, we cannot be the layoutRoot...
- if (!item.componentLayout.lastComponentSize && (width.calculated || height.calculated)) {
- return false;
- }
- // otherwise an ownerCt whose size is not effected by its content is a root
- return !width.shrinkWrap && !height.shrinkWrap;
- },
- isItemShrinkWrap: function (item) {
- return item.shrinkWrap;
- },
- isRunning: function () {
- return !!this.ownerContext;
- },
- //-----------------------------------------------------
- /*
- * Clears any styles which must be cleared before layout can take place.
- * Only DOM WRITES must be performed at this stage.
- *
- * An entry for the owner's element ID must be created in the layoutContext containing
- * a reference to the target which must be sized/positioned/styled by the layout at
- * the flush stage:
- *
- * {
- * target: me.owner
- * }
- *
- * Component layouts should iterate through managed Elements,
- * pushing an entry for each element:
- *
- * {
- * target: childElement
- * }
- */
- //-----------------------------------------------------
- getItemsRenderTree: function (items, renderCfgs) {
- var length = items.length,
- i, item, itemConfig, result;
- if (length) {
- result = [];
- for (i = 0; i < length; ++i) {
- item = items[i];
- // If we are being asked to move an already rendered Component, we must not recalculate its renderTree
- // and rerun its render process. The Layout's isValidParent check will ensure that the DOM is moved into place.
- if (!item.rendered) {
- // If we've already calculated the item's element config, don't calculate it again.
- // This may happen if the rendering process mutates the owning Container's items
- // collection, and Ext.layout.Container#getRenderTree runs through the collection again.
- // Note that the config may be null if a beforerender listener vetoed the operation, so
- // we must compare to undefined.
- if (renderCfgs && (renderCfgs[item.id] !== undefined)) {
- itemConfig = renderCfgs[item.id];
- } else {
- // Perform layout preprocessing in the bulk render path
- this.configureItem(item);
- itemConfig = item.getRenderTree();
- if (renderCfgs) {
- renderCfgs[item.id] = itemConfig;
- }
- }
- // itemConfig mey be null if a beforerender listener vetoed the operation.
- if (itemConfig) {
- result.push(itemConfig);
- }
- }
- }
- }
- return result;
- },
- finishRender: Ext.emptyFn,
- finishRenderItems: function (target, items) {
- var length = items.length,
- i, item;
- for (i = 0; i < length; i++) {
- item = items[i];
- // Only postprocess items which are being rendered. deferredRender may mean that only one has been rendered.
- if (item.rendering) {
- // Tell the item at which index in the Container it is
- item.finishRender(i);
- this.afterRenderItem(item);
- }
- }
- },
- renderChildren: function () {
- var me = this,
- items = me.getLayoutItems(),
- target = me.getRenderTarget();
- me.renderItems(items, target);
- },
- /**
- * Iterates over all passed items, ensuring they are rendered. If the items are already rendered,
- * also determines if the items are in the proper place in the dom.
- * @protected
- */
- renderItems : function(items, target) {
- var me = this,
- ln = items.length,
- i = 0,
- item;
- if (ln) {
- Ext.suspendLayouts();
- for (; i < ln; i++) {
- item = items[i];
- if (item && !item.rendered) {
- me.renderItem(item, target, i);
- } else if (!me.isValidParent(item, target, i)) {
- me.moveItem(item, target, i);
- } else {
- // still need to configure the item, it may have moved in the container.
- me.configureItem(item);
- }
- }
- Ext.resumeLayouts(true);
- }
- },
- /**
- * Validates item is in the proper place in the dom.
- * @protected
- */
- isValidParent : function(item, target, position) {
- var itemDom = item.el ? item.el.dom : Ext.getDom(item),
- targetDom = (target && target.dom) || target;
- // Test DOM nodes for equality using "===" : http://jsperf.com/dom-equality-test
- if (itemDom && targetDom) {
- if (typeof position == 'number') {
- return itemDom === targetDom.childNodes[position];
- }
- return itemDom.parentNode === targetDom;
- }
- return false;
- },
- /**
- * Called before an item is rendered to allow the layout to configure the item.
- * @param {Ext.Component} item The item to be configured
- * @protected
- */
- configureItem: function(item) {
- item.ownerLayout = this;
- },
- /**
- * Renders the given Component into the target Element.
- * @param {Ext.Component} item The Component to render
- * @param {Ext.dom.Element} target The target Element
- * @param {Number} position The position within the target to render the item to
- * @private
- */
- renderItem : function(item, target, position) {
- if (!item.rendered) {
- this.configureItem(item);
- item.render(target, position);
- this.afterRenderItem(item);
- }
- },
- /**
- * Moves Component to the provided target instead.
- * @private
- */
- moveItem : function(item, target, position) {
- target = target.dom || target;
- if (typeof position == 'number') {
- position = target.childNodes[position];
- }
- target.insertBefore(item.el.dom, position || null);
- item.container = Ext.get(target);
- this.configureItem(item);
- },
- /**
- * This method is called when a child item changes in some way. By default this calls
- * {@link Ext.AbstractComponent#updateLayout} on this layout's owner.
- *
- * @param {Ext.Component} child The child item that has changed.
- * @return {Boolean} True if this layout has handled the content change.
- */
- onContentChange: function () {
- this.owner.updateLayout();
- return true;
- },
- /**
- * A one-time initialization method called just before rendering.
- * @protected
- */
- initLayout : function() {
- this.initialized = true;
- },
- // @private Sets the layout owner
- setOwner : function(owner) {
- this.owner = owner;
- },
- /**
- * Returns the set of items to layout (empty by default).
- * @protected
- */
- getLayoutItems : function() {
- return [];
- },
- // Placeholder empty functions for subclasses to extend
- afterRenderItem: Ext.emptyFn,
- onAdd : Ext.emptyFn,
- onRemove : Ext.emptyFn,
- onDestroy : Ext.emptyFn,
- /**
- * Removes layout's itemCls and owning Container's itemCls.
- * Clears the managed dimensinos flags
- * @protected
- */
- afterRemove : function(item) {
- var me = this,
- el = item.el,
- owner = me.owner,
- removeClasses;
- if (item.rendered) {
- removeClasses = [].concat(me.itemCls || []);
- if (owner.itemCls) {
- removeClasses = Ext.Array.push(removeClasses, owner.itemCls);
- }
- if (removeClasses.length) {
- el.removeCls(removeClasses);
- }
- }
- delete item.ownerLayout;
- },
- /**
- * Destroys this layout. This method removes a `targetCls` from the `target`
- * element and calls `onDestroy`.
- *
- * A derived class can override either this method or `onDestroy` but in all
- * cases must call the base class versions of these methods to allow the base class to
- * perform its cleanup.
- *
- * This method (or `onDestroy`) are overridden by subclasses most often to purge
- * event handlers or remove unmanged DOM nodes.
- *
- * @protected
- */
- destroy : function() {
- var me = this;
- if (me.targetCls) {
- var target = me.getTarget();
- if (target) {
- target.removeCls(me.targetCls);
- }
- }
- me.onDestroy();
- },
- sortWeightedItems: function (items, reverseProp) {
- for (var i = 0, length = items.length; i < length; ++i) {
- items[i].$i = i;
- }
- Ext.Array.sort(items, function (item1, item2) {
- var ret = item2.weight - item1.weight;
- if (!ret) {
- ret = item1.$i - item2.$i;
- if (item1[reverseProp]) {
- ret = -ret;
- }
- }
- return ret;
- });
- for (i = 0; i < length; ++i) {
- delete items[i].$i;
- }
- }
- });
- /**
- * This class is intended to be extended or created via the {@link Ext.Component#componentLayout layout}
- * configuration property. See {@link Ext.Component#componentLayout} for additional details.
- * @private
- */
- Ext.define('Ext.layout.component.Component', {
- /* Begin Definitions */
- extend: 'Ext.layout.Layout',
- /* End Definitions */
- type: 'component',
- isComponentLayout: true,
- nullBox: {},
- usesContentHeight: true,
- usesContentWidth: true,
- usesHeight: true,
- usesWidth: true,
- beginLayoutCycle: function (ownerContext, firstCycle) {
- var me = this,
- owner = me.owner,
- ownerCtContext = ownerContext.ownerCtContext,
- heightModel = ownerContext.heightModel,
- widthModel = ownerContext.widthModel,
- body = owner.el.dom === document.body,
- lastBox = owner.lastBox || me.nullBox,
- lastSize = owner.el.lastBox || me.nullBox,
- dirty, ownerLayout, v;
- me.callParent(arguments);
- if (firstCycle) {
- if (me.usesContentWidth) {
- ++ownerContext.consumersContentWidth;
- }
- if (me.usesContentHeight) {
- ++ownerContext.consumersContentHeight;
- }
- if (me.usesWidth) {
- ++ownerContext.consumersWidth;
- }
- if (me.usesHeight) {
- ++ownerContext.consumersHeight;
- }
- if (ownerCtContext && !ownerCtContext.hasRawContent) {
- ownerLayout = owner.ownerLayout;
- if (ownerLayout.usesWidth) {
- ++ownerContext.consumersWidth;
- }
- if (ownerLayout.usesHeight) {
- ++ownerContext.consumersHeight;
- }
- }
- }
- // we want to publish configured dimensions as early as possible and since this is
- // a write phase...
- if (widthModel.configured) {
- // If the owner.el is the body, owner.width is not dirty (we don't want to write
- // it to the body el). For other el's, the width may already be correct in the
- // DOM (e.g., it is rendered in the markup initially). If the width is not
- // correct in the DOM, this is only going to be the case on the first cycle.
- dirty = !body && firstCycle && owner.width !== lastSize.width;
-
- ownerContext.setWidth(owner.width, dirty);
- } else if (ownerContext.isTopLevel && widthModel.calculated) {
- v = lastBox.width;
- ownerContext.setWidth(v, /*dirty=*/v != lastSize.width);
- }
- if (heightModel.configured) {
- dirty = !body && firstCycle && owner.height !== lastSize.height;
- ownerContext.setHeight(owner.height, dirty);
- } else if (ownerContext.isTopLevel && heightModel.calculated) {
- v = lastBox.height;
- ownerContext.setHeight(v, v != lastSize.height);
- }
- },
- finishedLayout: function(ownerContext) {
- var me = this,
- elementChildren = ownerContext.children,
- owner = me.owner,
- len, i, elContext, lastBox, props, v;
- // NOTE: In the code below we cannot use getProp because that will generate a layout dependency
- // Set lastBox on managed child Elements.
- // So that ContextItem.constructor can snag the lastBox for use by its undo method.
- if (elementChildren) {
- len = elementChildren.length;
- for (i = 0; i < len; i++) {
- elContext = elementChildren[i];
- elContext.el.lastBox = elContext.props;
- }
- }
- // Cache the size from which we are changing so that notifyOwner can notify the owningComponent with all essential information
- ownerContext.previousSize = me.lastComponentSize;
- // Cache the currently layed out size
- me.lastComponentSize = owner.el.lastBox = props = ownerContext.props;
- // lastBox is a copy of the defined props to allow save/restore of these (panel
- // collapse needs this)
- owner.lastBox = lastBox = {};
- v = props.x;
- if (v !== undefined) {
- lastBox.x = v;
- }
- v = props.y;
- if (v !== undefined) {
- lastBox.y = v;
- }
- v = props.width;
- if (v !== undefined) {
- lastBox.width = v;
- }
- v = props.height;
- if (v !== undefined) {
- lastBox.height = v;
- }
- me.callParent(arguments);
- },
-
- notifyOwner: function(ownerContext) {
- var me = this,
- currentSize = me.lastComponentSize,
- prevSize = ownerContext.previousSize,
- args = [currentSize.width, currentSize.height];
- if (prevSize) {
- args.push(prevSize.width, prevSize.height);
- }
- // Call afterComponentLayout passing new size, and only passing old size if there *was* an old size.
- me.owner.afterComponentLayout.apply(me.owner, args);
- },
- /**
- * Returns the owner component's resize element.
- * @return {Ext.Element}
- */
- getTarget : function() {
- return this.owner.el;
- },
- /**
- * Returns the element into which rendering must take place. Defaults to the owner Component's encapsulating element.
- *
- * May be overridden in Component layout managers which implement an inner element.
- * @return {Ext.Element}
- */
- getRenderTarget : function() {
- return this.owner.el;
- },
- cacheTargetInfo: function(ownerContext) {
- var me = this,
- targetInfo = me.targetInfo,
- target;
- if (!targetInfo) {
- target = ownerContext.getEl('getTarget', me);
- me.targetInfo = targetInfo = {
- padding: target.getPaddingInfo(),
- border: target.getBorderInfo()
- };
- }
- return targetInfo;
- },
- measureAutoDimensions: function (ownerContext, dimensions) {
- // Subtle But Important:
- //
- // We don't want to call getProp/hasProp et.al. unless we in fact need that value
- // for our results! If we call it and don't need it, the layout manager will think
- // we depend on it and will schedule us again should it change.
- var me = this,
- owner = me.owner,
- heightModel = ownerContext.heightModel,
- widthModel = ownerContext.widthModel,
- boxParent = ownerContext.boxParent,
- isBoxParent = ownerContext.isBoxParent,
- props = ownerContext.props,
- isContainer,
- ret = {
- gotWidth: false,
- gotHeight: false,
- isContainer: (isContainer = !ownerContext.hasRawContent)
- },
- hv = dimensions || 3,
- zeroWidth, zeroHeight,
- needed = 0,
- got = 0,
- ready, size;
- // Note: this method is called *a lot*, so we have to be careful not to waste any
- // time or make useless calls or, especially, read the DOM when we can avoid it.
- //---------------------------------------------------------------------
- // Width
- if (widthModel.shrinkWrap && ownerContext.consumersContentWidth) {
- ++needed;
- zeroWidth = !(hv & 1);
- if (isContainer) {
- // as a componentLayout for a container, we rely on the container layout to
- // produce contentWidth...
- if (zeroWidth) {
- ret.contentWidth = 0;
- ret.gotWidth = true;
- ++got;
- } else if ((ret.contentWidth = ownerContext.getProp('contentWidth')) !== undefined) {
- ret.gotWidth = true;
- ++got;
- }
- } else {
- size = props.contentWidth;
- if (typeof size == 'number') { // if (already determined)
- ret.contentWidth = size;
- ret.gotWidth = true;
- ++got;
- } else {
- if (zeroWidth) {
- ready = true;
- } else if (!ownerContext.hasDomProp('containerChildrenDone')) {
- ready = false;
- } else if (isBoxParent || !boxParent || boxParent.widthModel.shrinkWrap) {
- // if we have no boxParent, we are ready, but a shrinkWrap boxParent
- // artificially provides width early in the measurement process so
- // we are ready to go in that case as well...
- ready = true;
- } else {
- // lastly, we have a boxParent that will be given a width, so we
- // can wait for that width to be set in order to properly measure
- // whatever is inside...
- ready = boxParent.hasDomProp('width');
- }
- if (ready) {
- if (!isNaN(ret.contentWidth = zeroWidth ? 0 : me.measureContentWidth(ownerContext))) {
- ownerContext.setContentWidth(ret.contentWidth, true);
- ret.gotWidth = true;
- ++got;
- }
- }
- }
- }
- } else if (widthModel.natural && ownerContext.consumersWidth) {
- ++needed;
- size = props.width;
- // zeroWidth does not apply
- if (typeof size == 'number') { // if (already determined)
- ret.width = size;
- ret.gotWidth = true;
- ++got;
- } else {
- if (isBoxParent || !boxParent) {
- ready = true;
- } else {
- // lastly, we have a boxParent that will be given a width, so we
- // can wait for that width to be set in order to properly measure
- // whatever is inside...
- ready = boxParent.hasDomProp('width');
- }
- if (ready) {
- if (!isNaN(ret.width = me.measureOwnerWidth(ownerContext))) {
- ownerContext.setWidth(ret.width, false);
- ret.gotWidth = true;
- ++got;
- }
- }
- }
- }
- //---------------------------------------------------------------------
- // Height
- if (heightModel.shrinkWrap && ownerContext.consumersContentHeight) {
- ++needed;
- zeroHeight = !(hv & 2);
- if (isContainer) {
- // don't ask unless we need to know...
- if (zeroHeight) {
- ret.contentHeight = 0;
- ret.gotHeight = true;
- ++got;
- } else if ((ret.contentHeight = ownerContext.getProp('contentHeight')) !== undefined) {
- ret.gotHeight = true;
- ++got;
- }
- } else {
- size = props.contentHeight;
- if (typeof size == 'number') { // if (already determined)
- ret.contentHeight = size;
- ret.gotHeight = true;
- ++got;
- } else {
- if (zeroHeight) {
- ready = true;
- } else if (!ownerContext.hasDomProp('containerChildrenDone')) {
- ready = false;
- } else if (owner.noWrap) {
- ready = true;
- } else if (!widthModel.shrinkWrap) {
- // fixed width, so we need the width to determine the height...
- ready = (ownerContext.bodyContext || ownerContext).hasDomProp('width');// && (!ownerContext.bodyContext || ownerContext.bodyContext.hasDomProp('width'));
- } else if (isBoxParent || !boxParent || boxParent.widthModel.shrinkWrap) {
- // if we have no boxParent, we are ready, but an autoWidth boxParent
- // artificially provides width early in the measurement process so
- // we are ready to go in that case as well...
- ready = true;
- } else {
- // lastly, we have a boxParent that will be given a width, so we
- // can wait for that width to be set in order to properly measure
- // whatever is inside...
- ready = boxParent.hasDomProp('width');
- }
- if (ready) {
- if (!isNaN(ret.contentHeight = zeroHeight ? 0 : me.measureContentHeight(ownerContext))) {
- ownerContext.setContentHeight(ret.contentHeight, true);
- ret.gotHeight = true;
- ++got;
- }
- }
- }
- }
- } else if (heightModel.natural && ownerContext.consumersHeight) {
- ++needed;
- size = props.height;
- // zeroHeight does not apply
- if (typeof size == 'number') { // if (already determined)
- ret.height = size;
- ret.gotHeight = true;
- ++got;
- } else {
- if (isBoxParent || !boxParent) {
- ready = true;
- } else {
- // lastly, we have a boxParent that will be given a width, so we
- // can wait for that width to be set in order to properly measure
- // whatever is inside...
- ready = boxParent.hasDomProp('width');
- }
- if (ready) {
- if (!isNaN(ret.height = me.measureOwnerHeight(ownerContext))) {
- ownerContext.setHeight(ret.height, false);
- ret.gotHeight = true;
- ++got;
- }
- }
- }
- }
- if (boxParent) {
- ownerContext.onBoxMeasured();
- }
- ret.gotAll = got == needed;
- // see if we can avoid calling this method by storing something on ownerContext.
- return ret;
- },
- measureContentWidth: function (ownerContext) {
- // contentWidth includes padding, but not border, framing or margins
- return ownerContext.el.getWidth() - ownerContext.getFrameInfo().width;
- },
- measureContentHeight: function (ownerContext) {
- // contentHeight includes padding, but not border, framing or margins
- return ownerContext.el.getHeight() - ownerContext.getFrameInfo().height;
- },
- measureOwnerHeight: function (ownerContext) {
- return ownerContext.el.getHeight();
- },
- measureOwnerWidth: function (ownerContext) {
- return ownerContext.el.getWidth();
- }
- });
- /**
- * This ComponentLayout handles docking for Panels. It takes care of panels that are
- * part of a ContainerLayout that sets this Panel's size and Panels that are part of
- * an AutoContainerLayout in which this panel get his height based of the CSS or
- * or its content.
- * @private
- */
- Ext.define('Ext.layout.component.Dock', {
- /* Begin Definitions */
- extend: 'Ext.layout.component.Component',
- alias: 'layout.dock',
- alternateClassName: 'Ext.layout.component.AbstractDock',
- /* End Definitions */
- type: 'dock',
- initializedBorders: -1,
- horizontalCollapsePolicy: { width: true },
- verticalCollapsePolicy: { height: true },
- finishRender: function () {
- var me = this,
- target, items;
- me.callParent();
- target = me.getRenderTarget();
- items = me.getDockedItems();
- me.finishRenderItems(target, items);
- },
- isItemBoxParent: function (itemContext) {
- return true;
- },
- isItemShrinkWrap: function (item) {
- return true;
- },
- dockOpposites: {
- top: 'bottom',
- right: 'left',
- bottom: 'top',
- left: 'right'
- },
- handleItemBorders: function() {
- var me = this,
- owner = me.owner,
- borders, docked,
- oldBorders = me.borders,
- opposites = me.dockOpposites,
- currentGeneration = owner.dockedItems.generation,
- i, ln, item, dock, side,
- collapsed = me.collapsed;
- if (me.initializedBorders == currentGeneration || (owner.border && !owner.manageBodyBorders)) {
- return;
- }
- me.initializedBorders = currentGeneration;
- // Borders have to be calculated using expanded docked item collection.
- me.collapsed = false;
- docked = me.getLayoutItems();
- me.collapsed = collapsed;
- borders = { top: [], right: [], bottom: [], left: [] };
- for (i = 0, ln = docked.length; i < ln; i++) {
- item = docked[i];
- dock = item.dock;
- if (item.ignoreBorderManagement) {
- continue;
- }
- if (!borders[dock].satisfied) {
- borders[dock].push(item);
- borders[dock].satisfied = true;
- }
- if (!borders.top.satisfied && opposites[dock] !== 'top') {
- borders.top.push(item);
- }
- if (!borders.right.satisfied && opposites[dock] !== 'right') {
- borders.right.push(item);
- }
- if (!borders.bottom.satisfied && opposites[dock] !== 'bottom') {
- borders.bottom.push(item);
- }
- if (!borders.left.satisfied && opposites[dock] !== 'left') {
- borders.left.push(item);
- }
- }
- if (oldBorders) {
- for (side in oldBorders) {
- if (oldBorders.hasOwnProperty(side)) {
- ln = oldBorders[side].length;
- if (!owner.manageBodyBorders) {
- for (i = 0; i < ln; i++) {
- oldBorders[side][i].removeCls(Ext.baseCSSPrefix + 'docked-noborder-' + side);
- }
- if (!oldBorders[side].satisfied && !owner.bodyBorder) {
- owner.removeBodyCls(Ext.baseCSSPrefix + 'docked-noborder-' + side);
- }
- }
- else if (oldBorders[side].satisfied) {
- owner.setBodyStyle('border-' + side + '-width', '');
- }
- }
- }
- }
- for (side in borders) {
- if (borders.hasOwnProperty(side)) {
- ln = borders[side].length;
- if (!owner.manageBodyBorders) {
- for (i = 0; i < ln; i++) {
- borders[side][i].addCls(Ext.baseCSSPrefix + 'docked-noborder-' + side);
- }
- if ((!borders[side].satisfied && !owner.bodyBorder) || owner.bodyBorder === false) {
- owner.addBodyCls(Ext.baseCSSPrefix + 'docked-noborder-' + side);
- }
- }
- else if (borders[side].satisfied) {
- owner.setBodyStyle('border-' + side + '-width', '1px');
- }
- }
- }
- me.borders = borders;
- },
- beginLayout: function(ownerContext) {
- var me = this,
- owner = me.owner,
- docked = me.getLayoutItems(),
- layoutContext = ownerContext.context,
- dockedItemCount = docked.length,
- collapsedVert = false,
- collapsedHorz = false,
- dockedItems, i, item, itemContext, offsets,
- collapsed;
- me.callParent(arguments);
- me.handleItemBorders();
- // Cache the children as ContextItems (like a Container). Also setup to handle
- // collapsed state:
- collapsed = owner.getCollapsed();
- if (Ext.isDefined(me.lastCollapsedState) && (collapsed !== me.lastCollapsedState)) {
- // If we are collapsing...
- if (me.owner.collapsed) {
- ownerContext.isCollapsingOrExpanding = 1;
- // Add the collapsed class now, so that collapsed CSS rules are applied before measurements are taken by the layout.
- owner.addClsWithUI(owner.collapsedCls);
- } else {
- ownerContext.isCollapsingOrExpanding = 2;
- // Remove the collapsed class now, before layout calculations are done.
- owner.removeClsWithUI(owner.collapsedCls);
- ownerContext.lastCollapsedState = me.lastCollapsedState;
- }
- }
- me.lastCollapsedState = collapsed;
- ownerContext.dockedItems = dockedItems = [];
- for (i = 0; i < dockedItemCount; i++) {
- item = docked[i];
- itemContext = layoutContext.getCmp(item);
- itemContext.dockedAt = { x: 0, y: 0 };
- itemContext.offsets = offsets = Ext.Element.parseBox(item.offsets || {});
- offsets.width = offsets.left + offsets.right;
- offsets.height = offsets.top + offsets.bottom;
- dockedItems.push(itemContext);
- }
- if (owner.collapsed) {
- if (owner.collapsedVertical()) {
- collapsedVert = true;
- ownerContext.measureDimensions = 1;
- } else {
- collapsedHorz = true;
- ownerContext.measureDimensions = 2;
- }
- }
- ownerContext.collapsedVert = collapsedVert;
- ownerContext.collapsedHorz = collapsedHorz;
- ownerContext.bodyContext = ownerContext.getEl('body');
- },
- beginLayoutCycle: function(ownerContext) {
- var me = this,
- docked = ownerContext.dockedItems,
- len = docked.length,
- owner = me.owner,
- frameBody = owner.frameBody,
- i, item, dock;
- me.callParent(arguments);
- // If we are collapsed, we want to auto-layout using the placeholder/expander
- // instead of the normal items/dockedItems. This must be done here since we could
- // be in a box layout w/stretchmax which sets the width/heightModel to allow it to
- // control the size.
- if (ownerContext.collapsedVert) {
- ownerContext.heightModel = me.sizeModels.shrinkWrap;
- } else if (ownerContext.collapsedHorz) {
- ownerContext.widthModel = me.sizeModels.shrinkWrap
- }
- if (ownerContext.widthModel.auto) {
- if (ownerContext.widthModel.shrinkWrap) {
- owner.el.setWidth(null);
- }
- owner.body.setWidth(null);
- if (frameBody) {
- frameBody.setWidth(null);
- }
- }
- if (ownerContext.heightModel.auto) {
- owner.body.setHeight(null);
- //owner.el.setHeight(null); Disable this for now
- if (frameBody) {
- frameBody.setHeight(null);
- }
- }
- // Each time we begin (2nd+ would be due to invalidate) we need to publish the
- // known contentWidth/Height if we are collapsed:
- if (ownerContext.collapsedVert) {
- ownerContext.setContentHeight(0);
- } else if (ownerContext.collapsedHorz) {
- ownerContext.setContentWidth(0);
- }
- // dock: 'right' items, when a panel gets narrower get "squished". Moving them to
- // left:0px avoids this!
- for (i = 0; i < len; i++) {
- item = docked[i].target;
- dock = item.dock;
- if (dock == 'right') {
- item.el.setLeft(0);
- } else if (dock != 'left') {
- continue;
- }
- // TODO - clear width/height?
- }
- },
- calculate: function (ownerContext) {
- var me = this,
- measure = me.measureAutoDimensions(ownerContext, ownerContext.measureDimensions),
- state = ownerContext.state,
- horzDone = state.horzDone,
- vertDone = state.vertDone,
- bodyContext = ownerContext.bodyContext,
- horz, vert, forward, backward;
- // make sure we can use these value w/o calling methods to get them
- ownerContext.borderInfo || ownerContext.getBorderInfo();
- ownerContext.paddingInfo || ownerContext.getPaddingInfo();
- ownerContext.framingInfo || ownerContext.getFraming();
- bodyContext.borderInfo || bodyContext.getBorderInfo();
- bodyContext.paddingInfo || bodyContext.getPaddingInfo();
- // Start the axes so they are ready to proceed inwards (fixed-size) or outwards
- // (shrinkWrap) and stash key property names as well:
- horz = !horzDone &&
- me.createAxis(ownerContext, measure.contentWidth, ownerContext.widthModel,
- 'left', 'right', 'x', 'width', 'Width', ownerContext.collapsedHorz);
- vert = !vertDone &&
- me.createAxis(ownerContext, measure.contentHeight, ownerContext.heightModel,
- 'top', 'bottom', 'y', 'height', 'Height', ownerContext.collapsedVert);
- // We iterate forward and backward over the dockedItems at the same time based on
- // whether an axis is shrinkWrap or fixed-size. For a fixed-size axis, the outer box
- // axis is allocated to docked items in forward order and is reduced accordingly.
- // To handle a shrinkWrap axis, the box starts at the inner (body) size and is used to
- // size docked items in backwards order. This is because the last docked item shares
- // an edge with the body. The item size is used to adjust the shrinkWrap axis outwards
- // until the first docked item (at the outermost edge) is processed. This backwards
- // order ensures that docked items never get an incorrect size for any dimension.
- for (forward = 0, backward = ownerContext.dockedItems.length; backward--; ++forward) {
- if (horz) {
- me.dockChild(ownerContext, horz, backward, forward);
- }
- if (vert) {
- me.dockChild(ownerContext, vert, backward, forward);
- }
- }
- if (horz && me.finishAxis(ownerContext, horz)) {
- state.horzDone = horzDone = horz;
- }
- if (vert && me.finishAxis(ownerContext, vert)) {
- state.vertDone = vertDone = vert;
- }
- // Once all items are docked, the final size of the outer panel or inner body can
- // be determined. If we can determine both width and height, we are done.
- if (horzDone && vertDone && me.finishConstraints(ownerContext, horzDone, vertDone)) {
- // Size information is published as we dock items but position is hard to do
- // that way (while avoiding published multiple times) so we publish all the
- // positions at the end.
- me.finishPositions(ownerContext, horzDone, vertDone);
- } else {
- me.done = false;
- }
- },
- /**
- * Creates an axis object given the particulars.
- * @private
- */
- createAxis: function (ownerContext, contentSize, sizeModel, dockBegin, dockEnd, posProp,
- sizeProp, sizePropCap, collapsedAxis) {
- var begin = 0,
- owner = this.owner,
- maxSize = owner['max' + sizePropCap],
- minSize = owner['min' + sizePropCap] || 0,
- hasMaxSize = maxSize != null, // exactly the same as "maxSize !== null && maxSize !== undefined"
- constrainedSize = ownerContext.state['constrained' + sizePropCap],
- isConstrainedSize = constrainedSize != null,
- setSize = 'set' + sizePropCap,
- border, bodyContext, frameSize, padding, end;
- if (sizeModel.shrinkWrap && !isConstrainedSize) {
- // End position before adding docks around the content is content size plus the body borders in this axis.
- // If collapsed in this axis, the body borders will not be shown.
- if (collapsedAxis) {
- end = 0;
- } else {
- bodyContext = ownerContext.bodyContext;
- end = contentSize + bodyContext.borderInfo[sizeProp];
- }
- } else {
- border = ownerContext.borderInfo;
- frameSize = ownerContext.framingInfo;
- padding = ownerContext.paddingInfo;
- if (isConstrainedSize) {
- end = constrainedSize;
- sizeModel = this.sizeModels.calculated; // behave as if calculated
- ownerContext[setSize](constrainedSize);
- } else {
- end = ownerContext.getProp(sizeProp);
- }
- end -= border[dockEnd] + padding[dockEnd] + frameSize[dockEnd];
- begin = border[dockBegin] + padding[dockBegin] + frameSize[dockBegin];
- }
- return {
- shrinkWrap: sizeModel.shrinkWrap,
- sizeModel: sizeModel,
- // An axis tracks start and end+1 px positions. eg 0 to 10 for 10px high
- begin: begin,
- end: end,
- collapsed: collapsedAxis,
- horizontal: posProp == 'x',
- ignoreFrameBegin: false,
- ignoreFrameEnd: false,
- initialSize: end - begin,
- hasMinMaxConstraints: (minSize || hasMaxSize) && sizeModel.shrinkWrap,
- isConstrainedSize: isConstrainedSize,
- minSize: minSize,
- maxSize: hasMaxSize ? maxSize : 1e9,
- dockBegin: dockBegin, // 'left' or 'top'
- dockEnd: dockEnd, // 'right' or 'end'
- posProp: posProp, // 'x' or 'y'
- sizeProp: sizeProp, // 'width' or 'height'
- sizePropCap: sizePropCap, // 'Width' or 'Height'
- setSize: setSize
- };
- },
- /**
- * Docks a child item on the specified axis. This boils down to determining if the item
- * is docked at the "beginning" of the axis ("left" if horizontal, "top" if vertical),
- * the "end" of the axis ("right" if horizontal, "bottom" if vertical) or stretches
- * along the axis ("top" or "bottom" if horizontal, "left" or "right" if vertical). It
- * also has to differentiate between fixed and shrinkWrap sized dimensions.
- * @private
- */
- dockChild: function (ownerContext, axis, backward, forward) {
- var me = this,
- itemContext = ownerContext.dockedItems[axis.shrinkWrap ? backward : forward],
- item = itemContext.target,
- dock = item.dock, // left/top/right/bottom
- pos;
- if(item.ignoreParentFrame && ownerContext.isCollapsingOrExpanding) {
- // collapsed window header margins may differ from expanded window header margins
- // so we need to make sure the old cached values are not used in axis calculations
- itemContext.clearMarginCache();
- }
- if (dock == axis.dockBegin) {
- if (axis.shrinkWrap) {
- pos = me.dockOutwardBegin(ownerContext, itemContext, item, axis);
- } else {
- pos = me.dockInwardBegin(ownerContext, itemContext, item, axis);
- }
- } else if (dock == axis.dockEnd) {
- if (axis.shrinkWrap) {
- pos = me.dockOutwardEnd(ownerContext, itemContext, item, axis);
- } else {
- pos = me.dockInwardEnd(ownerContext, itemContext, item, axis);
- }
- } else {
- pos = me.dockStretch(ownerContext, itemContext, item, axis);
- }
- itemContext.dockedAt[axis.posProp] = pos;
- },
- /**
- * Docks an item on a fixed-size axis at the "beginning". The "beginning" of the horizontal
- * axis is "left" and the vertical is "top". For a fixed-size axis, the size works from
- * the outer element (the panel) towards the body.
- * @private
- */
- dockInwardBegin: function (ownerContext, itemContext, item, axis) {
- var pos = axis.begin,
- sizeProp = axis.sizeProp,
- dock;
- if (item.ignoreParentFrame) {
- dock = item.dock;
- pos -= ownerContext.borderInfo[dock] + ownerContext.paddingInfo[dock] +
- ownerContext.framingInfo[dock];
- }
- if (!item.overlay) {
- axis.begin += itemContext.getProp(sizeProp) + itemContext.getMarginInfo()[sizeProp];
- }
- return pos;
- },
- /**
- * Docks an item on a fixed-size axis at the "end". The "end" of the horizontal axis is
- * "right" and the vertical is "bottom".
- * @private
- */
- dockInwardEnd: function (ownerContext, itemContext, item, axis) {
- var sizeProp = axis.sizeProp,
- size = itemContext.getProp(sizeProp) + itemContext.getMarginInfo()[sizeProp],
- pos = axis.end - size;
- if (!item.overlay) {
- axis.end = pos;
- }
- if (item.ignoreParentFrame) {
- pos += ownerContext.borderInfo[item.dock] + ownerContext.paddingInfo[item.dock] +
- ownerContext.framingInfo[item.dock];
- }
- return pos;
- },
- /**
- * Docks an item on a shrinkWrap axis at the "beginning". The "beginning" of the horizontal
- * axis is "left" and the vertical is "top". For a shrinkWrap axis, the size works from
- * the body outward to the outermost element (the panel).
- *
- * During the docking process, coordinates are allowed to be negative. We start with the
- * body at (0,0) so items docked "top" or "left" will simply be assigned negative x/y. In
- * the {@link #finishPositions} method these are corrected and framing is added. This way
- * the correction is applied as a simple translation of delta x/y on all coordinates to
- * bring the origin back to (0,0).
- * @private
- */
- dockOutwardBegin: function (ownerContext, itemContext, item, axis) {
- var pos = axis.begin,
- sizeProp = axis.sizeProp,
- dock, size;
- if (axis.collapsed) {
- axis.ignoreFrameBegin = axis.ignoreFrameEnd = true;
- } else if (item.ignoreParentFrame) {
- dock = item.dock;
- pos -= ownerContext.borderInfo[dock] + ownerContext.paddingInfo[dock] +
- ownerContext.framingInfo[dock];
- axis.ignoreFrameBegin = true;
- }
- if (!item.overlay) {
- size = itemContext.getProp(sizeProp) + itemContext.getMarginInfo()[sizeProp];
- pos -= size;
- axis.begin = pos;
- }
- return pos;
- },
- /**
- * Docks an item on a shrinkWrap axis at the "end". The "end" of the horizontal axis is
- * "right" and the vertical is "bottom".
- * @private
- */
- dockOutwardEnd: function (ownerContext, itemContext, item, axis) {
- var pos = axis.end,
- sizeProp = axis.sizeProp,
- dock, size;
- size = itemContext.getProp(sizeProp) + itemContext.getMarginInfo()[sizeProp];
- if (axis.collapsed) {
- axis.ignoreFrameBegin = axis.ignoreFrameEnd = true;
- } else if (item.ignoreParentFrame) {
- dock = item.dock;
- pos += ownerContext.borderInfo[dock] + ownerContext.paddingInfo[dock] +
- ownerContext.framingInfo[dock];
- axis.ignoreFrameEnd = true;
- }
- if (!item.overlay) {
- axis.end = pos + size;
- }
- return pos;
- },
- /**
- * Docks an item that might stretch across an axis. This is done for dock "top" and
- * "bottom" items on the horizontal axis and dock "left" and "right" on the vertical.
- * @private
- */
- dockStretch: function (ownerContext, itemContext, item, axis) {
- var dock = item.dock, // left/top/right/bottom (also used to index padding/border)
- sizeProp = axis.sizeProp, // 'width' or 'height'
- horizontal = dock == 'top' || dock == 'bottom',
- offsets = itemContext.offsets,
- border = ownerContext.borderInfo,
- padding = ownerContext.paddingInfo,
- endProp = horizontal ? 'right' : 'bottom',
- startProp = horizontal ? 'left' : 'top',
- pos = axis.begin + offsets[startProp],
- margin, size, framing;
- if (item.stretch !== false) {
- size = axis.end - pos - offsets[endProp];
- if (item.ignoreParentFrame) {
- framing = ownerContext.framingInfo;
- pos -= border[startProp] + padding[startProp] + framing[startProp];
- size += border[sizeProp] + padding[sizeProp] + framing[sizeProp];
- }
- margin = itemContext.getMarginInfo();
- size -= margin[sizeProp];
- itemContext[axis.setSize](size);
- }
- return pos;
- },
- /**
- * Finishes the calculation of an axis by determining its size. In non-shrink-wrap
- * cases, this is also where we set the body size.
- * @private
- */
- finishAxis: function (ownerContext, axis) {
- var size = axis.end - axis.begin,
- setSizeMethod = axis.setSize,
- beginName = axis.dockBegin, // left or top
- endName = axis.dockEnd, // right or bottom
- border = ownerContext.borderInfo,
- padding = ownerContext.paddingInfo,
- framing = ownerContext.framingInfo,
- frameSize = padding[beginName] + border[beginName] + framing[beginName],
- bodyContext = ownerContext.bodyContext;
- if (axis.shrinkWrap) {
- // Since items docked left/top on a shrinkWrap axis go into negative coordinates,
- // we apply a delta to all coordinates to adjust their relative origin back to
- // (0,0).
- axis.delta = -axis.begin; // either 0 or a positive number
- bodyContext[setSizeMethod](axis.initialSize);
- if (axis.ignoreFrameBegin) {
- axis.delta -= border[beginName];
- bodyContext.setProp(axis.posProp, -axis.begin - frameSize);
- } else {
- size += frameSize;
- axis.delta += padding[beginName] + framing[beginName];
- bodyContext.setProp(axis.posProp, -axis.begin);
- }
- if (!axis.ignoreFrameEnd) {
- size += padding[endName] + border[endName] + framing[endName];
- }
- axis.size = size; // we have to wait for min/maxWidth/Height processing
- } else {
- // For a fixed-size axis, we started at the outer box and already have the
- // proper origin... almost... except for the owner's border.
- axis.delta = -border[axis.dockBegin]; // 'left' or 'top'
- // Body size is remaining space between ends of Axis.
- bodyContext[setSizeMethod](size);
- bodyContext.setProp(axis.posProp, axis.begin - frameSize);
- }
- return !isNaN(size);
- },
- /**
- * Finishes processing of each axis by applying the min/max size constraints.
- * @private
- */
- finishConstraints: function (ownerContext, horz, vert) {
- var horzTooSmall = horz.size < horz.minSize,
- horzTooBig = horz.size > horz.maxSize,
- vertTooSmall = vert.size < vert.minSize,
- vertTooBig = vert.size > vert.maxSize,
- state = ownerContext.state,
- ret = true,
- configured = this.sizeModels.configured;
- // Analysis of the potential constraint feedback given the possibilities for the
- // various constraints:
- //
- // #1: h < min, v > max : (Expand width, Shrink height)
- // In general, making the panel wider could possibly cause the content to
- // be shorter thereby eliminating the need to reduce the height, but we
- // just measured the content width given effectively infinite space in
- // which to expand. This means it is very unlikey (if not impossible) for
- // the height to change given more width, so no special concerns.
- //
- // #2: h < min, v < min : (Expand width, Expand height)
- // Making panel bigger in both directions has no concerns. Again, making
- // the panel wider could only reduce height, so the need to expand the
- // height would remain.
- //
- // #3: h > max, v > max : (Shrink width, Shrink height)
- // Making the panel narrower cannot cause the maxHeight violation to go
- // away, so no special concerns.
- //
- // #4: h > max, v < min : (Shrink width, Expand height)
- // Finally an interesting case! Shrinking the width can cause the height
- // to increase. We cannot know if it will increase enough to avoid the
- // minHeight violation, but if we apply the minHeight constraint, we will
- // not be able to tell that we should not have done so. Which means, in
- // this case, we must only apply the maxWidth constraint, allowing the
- // layout to rerun and perhaps apply the minHeight constraint next time.
- // NOTE: if we are already applying a constraint on a given axis, that axis will
- // *not* be in shrinkWrap mode.
- if (horz.shrinkWrap && horzTooBig && vert.shrinkWrap && vertTooSmall) { // if (#4)
- state.constrainedWidth = horz.maxSize;
- ownerContext.widthModel = configured; // via maxWidth config
- ret = false;
- } else {
- if (horz.shrinkWrap) {
- if (horzTooBig) {
- state.constrainedWidth = horz.maxSize;
- ownerContext.widthModel = configured;
- ret = false;
- } else if (horzTooSmall) {
- state.constrainedWidth = horz.minSize;
- ownerContext.widthModel = configured;
- ret = false;
- }
- }
- if (vert.shrinkWrap) {
- if (vertTooBig) {
- state.constrainedHeight = vert.maxSize;
- ownerContext.heightModel = configured;
- ret = false;
- } else if (vertTooSmall) {
- state.constrainedHeight = vert.minSize;
- ownerContext.heightModel = configured;
- ret = false;
- }
- }
- }
- if (ret) {
- if (horz.shrinkWrap) {
- ownerContext.setWidth(horz.size);
- }
- if (vert.shrinkWrap) {
- ownerContext.setHeight(vert.size);
- }
- } else {
- ownerContext.invalidate({
- state: {
- constrainedWidth: state.constrainedWidth,
- constrainedHeight: state.constrainedHeight
- }
- });
- }
- return ret;
- },
- /**
- * Finishes the calculation by setting positions on the body and all of the items.
- * @private
- */
- finishPositions: function (ownerContext, horz, vert) {
- var dockedItems = ownerContext.dockedItems,
- length = dockedItems.length,
- deltaX = horz.delta,
- deltaY = vert.delta,
- index, itemContext;
- for (index = 0; index < length; ++index) {
- itemContext = dockedItems[index];
- itemContext.setProp('x', deltaX + itemContext.dockedAt.x);
- itemContext.setProp('y', deltaY + itemContext.dockedAt.y);
- }
- },
- finishedLayout: function(ownerContext) {
- var me = this,
- target = ownerContext.target;
- me.callParent(arguments);
- if (!ownerContext.animatePolicy) {
- if (ownerContext.isCollapsingOrExpanding === 1) {
- target.afterCollapse(false);
- } else if (ownerContext.isCollapsingOrExpanding === 2) {
- target.afterExpand(false);
- }
- }
- },
- getAnimatePolicy: function(ownerContext) {
- var me = this,
- lastCollapsedState, policy;
- if (ownerContext.isCollapsingOrExpanding == 1) {
- lastCollapsedState = me.lastCollapsedState;
- } else if (ownerContext.isCollapsingOrExpanding == 2) {
- lastCollapsedState = ownerContext.lastCollapsedState;
- }
- if (lastCollapsedState == 'left' || lastCollapsedState == 'right') {
- policy = me.horizontalCollapsePolicy;
- } else if (lastCollapsedState == 'top' || lastCollapsedState == 'bottom') {
- policy = me.verticalCollapsePolicy;
- }
- return policy;
- },
- /**
- * Retrieve an ordered and/or filtered array of all docked Components.
- * @param {String} [order='render'] The desired ordering of the items ('render' or 'visual').
- * @param {Boolean} [beforeBody] An optional flag to limit the set of items to only those
- * before the body (true) or after the body (false). All components are returned by
- * default.
- * @return {Ext.Component[]} An array of components.
- * @protected
- */
- getDockedItems: function(order, beforeBody) {
- var me = this,
- all = me.owner.dockedItems.items,
- sort = all && all.length && order !== false,
- renderOrder,
- dock, dockedItems, i, isBefore, length;
- if (beforeBody == null) {
- dockedItems = sort ? all.slice() : all;
- } else {
- dockedItems = [];
- for (i = 0, length = all.length; i < length; ++i) {
- dock = all[i].dock;
- isBefore = (dock == 'top' || dock == 'left');
- if (beforeBody ? isBefore : !isBefore) {
- dockedItems.push(all[i]);
- }
- }
- sort = sort && dockedItems.length;
- }
- if (sort) {
- renderOrder = (order = order || 'render') == 'render';
- Ext.Array.sort(dockedItems, function(a, b) {
- var aw,
- bw;
- // If the two items are on opposite sides of the body, they must not be sorted by any weight value:
- // For rendering purposes, left/top *always* sorts before right/bottom
- if (renderOrder && ((aw = me.owner.dockOrder[a.dock]) !== (bw = me.owner.dockOrder[b.dock]))) {
- // The two dockOrder values cancel out when two items are on opposite sides.
- if (!(aw + bw)) {
- return aw - bw;
- }
- }
- aw = me.getItemWeight(a, order);
- bw = me.getItemWeight(b, order);
- if ((aw !== undefined) && (bw !== undefined)) {
- return aw - bw;
- }
- return 0;
- });
- }
- return dockedItems || [];
- },
- getItemWeight: function (item, order) {
- var weight = item.weight || this.owner.defaultDockWeights[item.dock];
- return weight[order] || weight;
- },
- /**
- * @protected
- * Returns an array containing all the **visible** docked items inside this layout's owner Panel
- * @return {Array} An array containing all the **visible** docked items of the Panel
- */
- getLayoutItems : function() {
- var me = this,
- items,
- itemCount,
- item,
- i,
- result;
- if (me.owner.collapsed) {
- result = me.owner.getCollapsedDockedItems();
- } else {
- items = me.getDockedItems('visual');
- itemCount = items.length;
- result = [];
- for (i = 0; i < itemCount; i++) {
- item = items[i];
- if (!item.hidden) {
- result.push(item);
- }
- }
- }
- return result;
- },
- // Content size includes padding but not borders, so subtract them off
- measureContentWidth: function (ownerContext) {
- var bodyContext = ownerContext.bodyContext;
- return bodyContext.el.getWidth() - bodyContext.getBorderInfo().width;
- },
- measureContentHeight: function (ownerContext) {
- var bodyContext = ownerContext.bodyContext;
- return bodyContext.el.getHeight() - bodyContext.getBorderInfo().height;
- },
-
- redoLayout: function(ownerContext) {
- var me = this,
- owner = me.owner;
-
- // If we are collapsing...
- if (ownerContext.isCollapsingOrExpanding == 1) {
- if (owner.reExpander) {
- owner.reExpander.el.show();
- }
- // Add the collapsed class now, so that collapsed CSS rules are applied before measurements are taken by the layout.
- owner.addClsWithUI(owner.collapsedCls);
- ownerContext.redo(true);
- } else if (ownerContext.isCollapsingOrExpanding == 2) {
- // Remove the collapsed class now, before layout calculations are done.
- owner.removeClsWithUI(owner.collapsedCls);
- ownerContext.bodyContext.redo();
- }
- },
- // @private override inherited.
- // We need to render in the correct order, top/left before bottom/right
- renderChildren: function() {
- var me = this,
- items = me.getDockedItems(),
- target = me.getRenderTarget();
- me.renderItems(items, target);
- },
- /**
- * @protected
- * Render the top and left docked items before any existing DOM nodes in our render target,
- * and then render the right and bottom docked items after. This is important, for such things
- * as tab stops and ARIA readers, that the DOM nodes are in a meaningful order.
- * Our collection of docked items will already be ordered via Panel.getDockedItems().
- */
- renderItems: function(items, target) {
- var me = this,
- dockedItemCount = items.length,
- itemIndex = 0,
- correctPosition = 0,
- item,
- staticNodeCount = 0,
- targetNodes = me.getRenderTarget().dom.childNodes,
- targetChildCount = targetNodes.length,
- i, j, targetChildNode, item;
- // Calculate the number of DOM nodes in our target that are not our docked items
- for (i = 0, j = 0; i < targetChildCount; i++) {
- targetChildNode = targetNodes[i];
- if (Ext.fly(targetChildNode).hasCls('x-resizable-handle')) {
- break;
- }
- for (j = 0; j < dockedItemCount; j++) {
- item = items[j];
- if (item.rendered && item.el.dom === targetChildNode) {
- break;
- }
- }
- // Walked off the end of the docked items without matching the found child node;
- // Then it's a static node.
- if (j === dockedItemCount) {
- staticNodeCount++;
- }
- }
- // Now we go through our docked items and render/move them
- for (; itemIndex < dockedItemCount; itemIndex++, correctPosition++) {
- item = items[itemIndex];
- // If we're now at the first right/bottom docked item, we jump over the body element.
- //
- // TODO: This is affected if users provide custom weight values to their
- // docked items, which puts it out of (t,l,r,b) order. Avoiding a second
- // sort operation here, for now, in the name of performance. getDockedItems()
- // needs the sort operation not just for this layout-time rendering, but
- // also for getRefItems() to return a logical ordering (FocusManager, CQ, et al).
- if (itemIndex === correctPosition && (item.dock === 'right' || item.dock === 'bottom')) {
- correctPosition += staticNodeCount;
- }
- // Same logic as Layout.renderItems()
- if (item && !item.rendered) {
- me.renderItem(item, target, correctPosition);
- }
- else if (!me.isValidParent(item, target, correctPosition)) {
- me.moveItem(item, target, correctPosition);
- }
- }
- },
- undoLayout: function(ownerContext) {
- var me = this,
- owner = me.owner;
-
- // If we are collapsing...
- if (ownerContext.isCollapsingOrExpanding == 1) {
- // We do not want to see the re-expander header until the final collapse is complete
- if (owner.reExpander) {
- owner.reExpander.el.hide();
- }
- // Add the collapsed class now, so that collapsed CSS rules are applied before measurements are taken by the layout.
- owner.removeClsWithUI(owner.collapsedCls);
- ownerContext.undo(true);
- } else if (ownerContext.isCollapsingOrExpanding == 2) {
- // Remove the collapsed class now, before layout calculations are done.
- owner.addClsWithUI(owner.collapsedCls);
- ownerContext.bodyContext.undo();
- }
- },
- sizePolicy: {
- nostretch: {
- setsWidth: 0,
- setsHeight: 0
- },
- stretchH: {
- setsWidth: 1,
- setsHeight: 0
- },
- stretchV: {
- setsWidth: 0,
- setsHeight: 1
- },
- // Circular dependency with partial auto-sized panels:
- //
- // If we have an autoHeight docked item being stretched horizontally (top/bottom),
- // that stretching will determine its width and its width must be set before its
- // autoHeight can be determined. If that item is docked in an autoWidth panel, the
- // body will need its height set before it can determine its width, but the height
- // of the docked item is needed to subtract from the panel height in order to set
- // the body height.
- //
- // This same pattern occurs with autoHeight panels with autoWidth docked items on
- // left or right. If the panel is fully auto or fully fixed, these problems don't
- // come up because there is no dependency between the dimensions.
- //
- // Cutting the Gordian Knot: In these cases, we have to allow something to measure
- // itself without full context. This is OK as long as the managed dimension doesn't
- // effect the auto-dimension, which is often the case for things like toolbars. The
- // managed dimension only effects overflow handlers and such and does not change the
- // auto-dimension. To encourage the item to measure itself without waiting for the
- // managed dimension, we have to tell it that the layout will also be reading that
- // dimension. This is similar to how stretchmax works.
- autoStretchH: {
- readsWidth: 1,
- setsWidth: 1,
- setsHeight: 0
- },
- autoStretchV: {
- readsHeight: 1,
- setsWidth: 0,
- setsHeight: 1
- }
- },
- getItemSizePolicy: function (item) {
- var policy = this.sizePolicy,
- dock, vertical;
- if (item.stretch === false) {
- return policy.nostretch;
- }
- dock = item.dock;
- vertical = (dock == 'left' || dock == 'right');
- /*
- owner = this.owner;
- autoWidth = !owner.isFixedWidth();
- autoHeight = !owner.isFixedHeight();
- if (autoWidth !== autoHeight) { // if (partial auto)
- // see above...
- if (vertical) {
- if (autoHeight) {
- return policy.autoStretchV;
- }
- } else if (autoWidth) {
- return policy.autoStretchH;
- }
- }*/
- if (vertical) {
- return policy.stretchV;
- }
- return policy.stretchH;
- },
- /**
- * @protected
- * We are overriding the Ext.layout.Layout configureItem method to also add a class that
- * indicates the position of the docked item. We use the itemCls (x-docked) as a prefix.
- * An example of a class added to a dock: right item is x-docked-right
- * @param {Ext.Component} item The item we are configuring
- */
- configureItem : function(item, pos) {
- this.callParent(arguments);
- item.addCls(Ext.baseCSSPrefix + 'docked');
- item.addClsWithUI('docked-' + item.dock);
- },
- afterRemove : function(item) {
- this.callParent(arguments);
- if (this.itemCls) {
- item.el.removeCls(this.itemCls + '-' + item.dock);
- }
- var dom = item.el.dom;
- if (!item.destroying && dom) {
- dom.parentNode.removeChild(dom);
- }
- this.childrenChanged = true;
- }
- });
- /**
- * This class represents a rectangular region in X,Y space, and performs geometric
- * transformations or tests upon the region.
- *
- * This class may be used to compare the document regions occupied by elements.
- */
- Ext.define('Ext.util.Region', {
- /* Begin Definitions */
- requires: ['Ext.util.Offset'],
- statics: {
- /**
- * @static
- * Retrieves an Ext.util.Region for a particular element.
- * @param {String/HTMLElement/Ext.Element} el An element ID, htmlElement or Ext.Element representing an element in the document.
- * @returns {Ext.util.Region} region
- */
- getRegion: function(el) {
- return Ext.fly(el).getPageBox(true);
- },
- /**
- * @static
- * Creates a Region from a "box" Object which contains four numeric properties `top`, `right`, `bottom` and `left`.
- * @param {Object} o An object with `top`, `right`, `bottom` and `left` properties.
- * @return {Ext.util.Region} region The Region constructed based on the passed object
- */
- from: function(o) {
- return new this(o.top, o.right, o.bottom, o.left);
- }
- },
- /* End Definitions */
- /**
- * Creates a region from the bounding sides.
- * @param {Number} top Top The topmost pixel of the Region.
- * @param {Number} right Right The rightmost pixel of the Region.
- * @param {Number} bottom Bottom The bottom pixel of the Region.
- * @param {Number} left Left The leftmost pixel of the Region.
- */
- constructor : function(t, r, b, l) {
- var me = this;
- me.y = me.top = me[1] = t;
- me.right = r;
- me.bottom = b;
- me.x = me.left = me[0] = l;
- },
- /**
- * Checks if this region completely contains the region that is passed in.
- * @param {Ext.util.Region} region
- * @return {Boolean}
- */
- contains : function(region) {
- var me = this;
- return (region.x >= me.x &&
- region.right <= me.right &&
- region.y >= me.y &&
- region.bottom <= me.bottom);
- },
- /**
- * Checks if this region intersects the region passed in.
- * @param {Ext.util.Region} region
- * @return {Ext.util.Region/Boolean} Returns the intersected region or false if there is no intersection.
- */
- intersect : function(region) {
- var me = this,
- t = Math.max(me.y, region.y),
- r = Math.min(me.right, region.right),
- b = Math.min(me.bottom, region.bottom),
- l = Math.max(me.x, region.x);
- if (b > t && r > l) {
- return new this.self(t, r, b, l);
- }
- else {
- return false;
- }
- },
- /**
- * Returns the smallest region that contains the current AND targetRegion.
- * @param {Ext.util.Region} region
- * @return {Ext.util.Region} a new region
- */
- union : function(region) {
- var me = this,
- t = Math.min(me.y, region.y),
- r = Math.max(me.right, region.right),
- b = Math.max(me.bottom, region.bottom),
- l = Math.min(me.x, region.x);
- return new this.self(t, r, b, l);
- },
- /**
- * Modifies the current region to be constrained to the targetRegion.
- * @param {Ext.util.Region} targetRegion
- * @return {Ext.util.Region} this
- */
- constrainTo : function(r) {
- var me = this,
- constrain = Ext.Number.constrain;
- me.top = me.y = constrain(me.top, r.y, r.bottom);
- me.bottom = constrain(me.bottom, r.y, r.bottom);
- me.left = me.x = constrain(me.left, r.x, r.right);
- me.right = constrain(me.right, r.x, r.right);
- return me;
- },
- /**
- * Modifies the current region to be adjusted by offsets.
- * @param {Number} top top offset
- * @param {Number} right right offset
- * @param {Number} bottom bottom offset
- * @param {Number} left left offset
- * @return {Ext.util.Region} this
- */
- adjust : function(t, r, b, l) {
- var me = this;
- me.top = me.y += t;
- me.left = me.x += l;
- me.right += r;
- me.bottom += b;
- return me;
- },
- /**
- * Get the offset amount of a point outside the region
- * @param {String} [axis]
- * @param {Ext.util.Point} [p] the point
- * @return {Ext.util.Offset}
- */
- getOutOfBoundOffset: function(axis, p) {
- if (!Ext.isObject(axis)) {
- if (axis == 'x') {
- return this.getOutOfBoundOffsetX(p);
- } else {
- return this.getOutOfBoundOffsetY(p);
- }
- } else {
- p = axis;
- var d = new Ext.util.Offset();
- d.x = this.getOutOfBoundOffsetX(p.x);
- d.y = this.getOutOfBoundOffsetY(p.y);
- return d;
- }
- },
- /**
- * Get the offset amount on the x-axis
- * @param {Number} p the offset
- * @return {Number}
- */
- getOutOfBoundOffsetX: function(p) {
- if (p <= this.x) {
- return this.x - p;
- } else if (p >= this.right) {
- return this.right - p;
- }
- return 0;
- },
- /**
- * Get the offset amount on the y-axis
- * @param {Number} p the offset
- * @return {Number}
- */
- getOutOfBoundOffsetY: function(p) {
- if (p <= this.y) {
- return this.y - p;
- } else if (p >= this.bottom) {
- return this.bottom - p;
- }
- return 0;
- },
- /**
- * Check whether the point / offset is out of bound
- * @param {String} [axis]
- * @param {Ext.util.Point/Number} [p] the point / offset
- * @return {Boolean}
- */
- isOutOfBound: function(axis, p) {
- if (!Ext.isObject(axis)) {
- if (axis == 'x') {
- return this.isOutOfBoundX(p);
- } else {
- return this.isOutOfBoundY(p);
- }
- } else {
- p = axis;
- return (this.isOutOfBoundX(p.x) || this.isOutOfBoundY(p.y));
- }
- },
- /**
- * Check whether the offset is out of bound in the x-axis
- * @param {Number} p the offset
- * @return {Boolean}
- */
- isOutOfBoundX: function(p) {
- return (p < this.x || p > this.right);
- },
- /**
- * Check whether the offset is out of bound in the y-axis
- * @param {Number} p the offset
- * @return {Boolean}
- */
- isOutOfBoundY: function(p) {
- return (p < this.y || p > this.bottom);
- },
- /**
- * Restrict a point within the region by a certain factor.
- * @param {String} [axis]
- * @param {Ext.util.Point/Ext.util.Offset/Object} [p]
- * @param {Number} [factor]
- * @return {Ext.util.Point/Ext.util.Offset/Object/Number}
- * @private
- */
- restrict: function(axis, p, factor) {
- if (Ext.isObject(axis)) {
- var newP;
- factor = p;
- p = axis;
- if (p.copy) {
- newP = p.copy();
- }
- else {
- newP = {
- x: p.x,
- y: p.y
- };
- }
- newP.x = this.restrictX(p.x, factor);
- newP.y = this.restrictY(p.y, factor);
- return newP;
- } else {
- if (axis == 'x') {
- return this.restrictX(p, factor);
- } else {
- return this.restrictY(p, factor);
- }
- }
- },
- /**
- * Restrict an offset within the region by a certain factor, on the x-axis
- * @param {Number} p
- * @param {Number} [factor=1] The factor.
- * @return {Number}
- * @private
- */
- restrictX : function(p, factor) {
- if (!factor) {
- factor = 1;
- }
- if (p <= this.x) {
- p -= (p - this.x) * factor;
- }
- else if (p >= this.right) {
- p -= (p - this.right) * factor;
- }
- return p;
- },
- /**
- * Restrict an offset within the region by a certain factor, on the y-axis
- * @param {Number} p
- * @param {Number} [factor] The factor, defaults to 1
- * @return {Number}
- * @private
- */
- restrictY : function(p, factor) {
- if (!factor) {
- factor = 1;
- }
- if (p <= this.y) {
- p -= (p - this.y) * factor;
- }
- else if (p >= this.bottom) {
- p -= (p - this.bottom) * factor;
- }
- return p;
- },
- /**
- * Get the width / height of this region
- * @return {Object} an object with width and height properties
- * @private
- */
- getSize: function() {
- return {
- width: this.right - this.x,
- height: this.bottom - this.y
- };
- },
- /**
- * Create a copy of this Region.
- * @return {Ext.util.Region}
- */
- copy: function() {
- return new this.self(this.y, this.right, this.bottom, this.x);
- },
- /**
- * Copy the values of another Region to this Region
- * @param {Ext.util.Region} p The region to copy from.
- * @return {Ext.util.Region} This Region
- */
- copyFrom: function(p) {
- var me = this;
- me.top = me.y = me[1] = p.y;
- me.right = p.right;
- me.bottom = p.bottom;
- me.left = me.x = me[0] = p.x;
- return this;
- },
- /*
- * Dump this to an eye-friendly string, great for debugging
- * @return {String}
- */
- toString: function() {
- return "Region[" + this.top + "," + this.right + "," + this.bottom + "," + this.left + "]";
- },
- /**
- * Translate this region by the given offset amount
- * @param {Ext.util.Offset/Object} x Object containing the `x` and `y` properties.
- * Or the x value is using the two argument form.
- * @param {Number} y The y value unless using an Offset object.
- * @return {Ext.util.Region} this This Region
- */
- translateBy: function(x, y) {
- if (arguments.length == 1) {
- y = x.y;
- x = x.x;
- }
- var me = this;
- me.top = me.y += y;
- me.right += x;
- me.bottom += y;
- me.left = me.x += x;
- return me;
- },
- /**
- * Round all the properties of this region
- * @return {Ext.util.Region} this This Region
- */
- round: function() {
- var me = this;
- me.top = me.y = Math.round(me.y);
- me.right = Math.round(me.right);
- me.bottom = Math.round(me.bottom);
- me.left = me.x = Math.round(me.x);
- return me;
- },
- /**
- * Check whether this region is equivalent to the given region
- * @param {Ext.util.Region} region The region to compare with
- * @return {Boolean}
- */
- equals: function(region) {
- return (this.top == region.top && this.right == region.right && this.bottom == region.bottom && this.left == region.left);
- }
- });
- /*
- * This is a derivative of the similarly named class in the YUI Library.
- * The original license:
- * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
- * Code licensed under the BSD License:
- * http://developer.yahoo.net/yui/license.txt
- */
- /**
- * DragDropManager is a singleton that tracks the element interaction for
- * all DragDrop items in the window. Generally, you will not call
- * this class directly, but it does have helper methods that could
- * be useful in your DragDrop implementations.
- */
- Ext.define('Ext.dd.DragDropManager', {
- singleton: true,
- requires: ['Ext.util.Region'],
- uses: ['Ext.tip.QuickTipManager'],
- // shorter ClassName, to save bytes and use internally
- alternateClassName: ['Ext.dd.DragDropMgr', 'Ext.dd.DDM'],
- /**
- * @property {String[]} ids
- * Two dimensional Array of registered DragDrop objects. The first
- * dimension is the DragDrop item group, the second the DragDrop
- * object.
- * @private
- */
- ids: {},
- /**
- * @property {String[]} handleIds
- * Array of element ids defined as drag handles. Used to determine
- * if the element that generated the mousedown event is actually the
- * handle and not the html element itself.
- * @private
- */
- handleIds: {},