PageRenderTime 128ms CodeModel.GetById 36ms app.highlight 54ms RepoModel.GetById 2ms app.codeStats 4ms

/ext-4.0.7/pkgs/classes.js

https://bitbucket.org/srogerf/javascript
JavaScript | 14596 lines | 6692 code | 1488 blank | 6416 comment | 1428 complexity | a081e388c7e0f979768b033e444b0e49 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2
   3This file is part of Ext JS 4
   4
   5Copyright (c) 2011 Sencha Inc
   6
   7Contact:  http://www.sencha.com/contact
   8
   9GNU General Public License Usage
  10This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
  11
  12If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
  13
  14*/
  15/**
  16 * Base class that provides a common interface for publishing events. Subclasses are expected to to have a property
  17 * "events" with all the events defined, and, optionally, a property "listeners" with configured listeners defined.
  18 *
  19 * For example:
  20 *
  21 *     Ext.define('Employee', {
  22 *         extend: 'Ext.util.Observable',
  23 *         constructor: function(config){
  24 *             this.name = config.name;
  25 *             this.addEvents({
  26 *                 "fired" : true,
  27 *                 "quit" : true
  28 *             });
  29 *
  30 *             // Copy configured listeners into *this* object so that the base class's
  31 *             // constructor will add them.
  32 *             this.listeners = config.listeners;
  33 *
  34 *             // Call our superclass constructor to complete construction process.
  35 *             this.callParent(arguments)
  36 *         }
  37 *     });
  38 *
  39 * This could then be used like this:
  40 *
  41 *     var newEmployee = new Employee({
  42 *         name: employeeName,
  43 *         listeners: {
  44 *             quit: function() {
  45 *                 // By default, "this" will be the object that fired the event.
  46 *                 alert(this.name + " has quit!");
  47 *             }
  48 *         }
  49 *     });
  50 */
  51Ext.define('Ext.util.Observable', {
  52
  53    /* Begin Definitions */
  54
  55    requires: ['Ext.util.Event'],
  56
  57    statics: {
  58        /**
  59         * Removes **all** added captures from the Observable.
  60         *
  61         * @param {Ext.util.Observable} o The Observable to release
  62         * @static
  63         */
  64        releaseCapture: function(o) {
  65            o.fireEvent = this.prototype.fireEvent;
  66        },
  67
  68        /**
  69         * Starts capture on the specified Observable. All events will be passed to the supplied function with the event
  70         * name + standard signature of the event **before** the event is fired. If the supplied function returns false,
  71         * the event will not fire.
  72         *
  73         * @param {Ext.util.Observable} o The Observable to capture events from.
  74         * @param {Function} fn The function to call when an event is fired.
  75         * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to
  76         * the Observable firing the event.
  77         * @static
  78         */
  79        capture: function(o, fn, scope) {
  80            o.fireEvent = Ext.Function.createInterceptor(o.fireEvent, fn, scope);
  81        },
  82
  83        /**
  84         * Sets observability on the passed class constructor.
  85         *
  86         * This makes any event fired on any instance of the passed class also fire a single event through
  87         * the **class** allowing for central handling of events on many instances at once.
  88         *
  89         * Usage:
  90         *
  91         *     Ext.util.Observable.observe(Ext.data.Connection);
  92         *     Ext.data.Connection.on('beforerequest', function(con, options) {
  93         *         console.log('Ajax request made to ' + options.url);
  94         *     });
  95         *
  96         * @param {Function} c The class constructor to make observable.
  97         * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
  98         * @static
  99         */
 100        observe: function(cls, listeners) {
 101            if (cls) {
 102                if (!cls.isObservable) {
 103                    Ext.applyIf(cls, new this());
 104                    this.capture(cls.prototype, cls.fireEvent, cls);
 105                }
 106                if (Ext.isObject(listeners)) {
 107                    cls.on(listeners);
 108                }
 109                return cls;
 110            }
 111        }
 112    },
 113
 114    /* End Definitions */
 115
 116    /**
 117     * @cfg {Object} listeners
 118     *
 119     * A config object containing one or more event handlers to be added to this object during initialization. This
 120     * should be a valid listeners config object as specified in the {@link #addListener} example for attaching multiple
 121     * handlers at once.
 122     *
 123     * **DOM events from Ext JS {@link Ext.Component Components}**
 124     *
 125     * While _some_ Ext JS Component classes export selected DOM events (e.g. "click", "mouseover" etc), this is usually
 126     * only done when extra value can be added. For example the {@link Ext.view.View DataView}'s **`{@link
 127     * Ext.view.View#itemclick itemclick}`** event passing the node clicked on. To access DOM events directly from a
 128     * child element of a Component, we need to specify the `element` option to identify the Component property to add a
 129     * DOM listener to:
 130     *
 131     *     new Ext.panel.Panel({
 132     *         width: 400,
 133     *         height: 200,
 134     *         dockedItems: [{
 135     *             xtype: 'toolbar'
 136     *         }],
 137     *         listeners: {
 138     *             click: {
 139     *                 element: 'el', //bind to the underlying el property on the panel
 140     *                 fn: function(){ console.log('click el'); }
 141     *             },
 142     *             dblclick: {
 143     *                 element: 'body', //bind to the underlying body property on the panel
 144     *                 fn: function(){ console.log('dblclick body'); }
 145     *             }
 146     *         }
 147     *     });
 148     */
 149    // @private
 150    isObservable: true,
 151
 152    constructor: function(config) {
 153        var me = this;
 154
 155        Ext.apply(me, config);
 156        if (me.listeners) {
 157            me.on(me.listeners);
 158            delete me.listeners;
 159        }
 160        me.events = me.events || {};
 161
 162        if (me.bubbleEvents) {
 163            me.enableBubble(me.bubbleEvents);
 164        }
 165    },
 166
 167    // @private
 168    eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|vertical|horizontal|freezeEvent)$/,
 169
 170    /**
 171     * Adds listeners to any Observable object (or Ext.Element) which are automatically removed when this Component is
 172     * destroyed.
 173     *
 174     * @param {Ext.util.Observable/Ext.Element} item The item to which to add a listener/listeners.
 175     * @param {Object/String} ename The event name, or an object containing event name properties.
 176     * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function.
 177     * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference)
 178     * in which the handler function is executed.
 179     * @param {Object} opt (optional) If the `ename` parameter was an event name, this is the
 180     * {@link Ext.util.Observable#addListener addListener} options.
 181     */
 182    addManagedListener : function(item, ename, fn, scope, options) {
 183        var me = this,
 184            managedListeners = me.managedListeners = me.managedListeners || [],
 185            config;
 186
 187        if (typeof ename !== 'string') {
 188            options = ename;
 189            for (ename in options) {
 190                if (options.hasOwnProperty(ename)) {
 191                    config = options[ename];
 192                    if (!me.eventOptionsRe.test(ename)) {
 193                        me.addManagedListener(item, ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
 194                    }
 195                }
 196            }
 197        }
 198        else {
 199            managedListeners.push({
 200                item: item,
 201                ename: ename,
 202                fn: fn,
 203                scope: scope,
 204                options: options
 205            });
 206
 207            item.on(ename, fn, scope, options);
 208        }
 209    },
 210
 211    /**
 212     * Removes listeners that were added by the {@link #mon} method.
 213     *
 214     * @param {Ext.util.Observable/Ext.Element} item The item from which to remove a listener/listeners.
 215     * @param {Object/String} ename The event name, or an object containing event name properties.
 216     * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function.
 217     * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference)
 218     * in which the handler function is executed.
 219     */
 220    removeManagedListener : function(item, ename, fn, scope) {
 221        var me = this,
 222            options,
 223            config,
 224            managedListeners,
 225            length,
 226            i;
 227
 228        if (typeof ename !== 'string') {
 229            options = ename;
 230            for (ename in options) {
 231                if (options.hasOwnProperty(ename)) {
 232                    config = options[ename];
 233                    if (!me.eventOptionsRe.test(ename)) {
 234                        me.removeManagedListener(item, ename, config.fn || config, config.scope || options.scope);
 235                    }
 236                }
 237            }
 238        }
 239
 240        managedListeners = me.managedListeners ? me.managedListeners.slice() : [];
 241
 242        for (i = 0, length = managedListeners.length; i < length; i++) {
 243            me.removeManagedListenerItem(false, managedListeners[i], item, ename, fn, scope);
 244        }
 245    },
 246
 247    /**
 248     * Fires the specified event with the passed parameters (minus the event name, plus the `options` object passed
 249     * to {@link #addListener}).
 250     *
 251     * An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget}) by
 252     * calling {@link #enableBubble}.
 253     *
 254     * @param {String} eventName The name of the event to fire.
 255     * @param {Object...} args Variable number of parameters are passed to handlers.
 256     * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
 257     */
 258    fireEvent: function(eventName) {
 259        var name = eventName.toLowerCase(),
 260            events = this.events,
 261            event = events && events[name],
 262            bubbles = event && event.bubble;
 263
 264        return this.continueFireEvent(name, Ext.Array.slice(arguments, 1), bubbles);
 265    },
 266
 267    /**
 268     * Continue to fire event.
 269     * @private
 270     *
 271     * @param {String} eventName
 272     * @param {Array} args
 273     * @param {Boolean} bubbles
 274     */
 275    continueFireEvent: function(eventName, args, bubbles) {
 276        var target = this,
 277            queue, event,
 278            ret = true;
 279
 280        do {
 281            if (target.eventsSuspended === true) {
 282                if ((queue = target.eventQueue)) {
 283                    queue.push([eventName, args, bubbles]);
 284                }
 285                return ret;
 286            } else {
 287                event = target.events[eventName];
 288                // Continue bubbling if event exists and it is `true` or the handler didn't returns false and it
 289                // configure to bubble.
 290                if (event && event != true) {
 291                    if ((ret = event.fire.apply(event, args)) === false) {
 292                        break;
 293                    }
 294                }
 295            }
 296        } while (bubbles && (target = target.getBubbleParent()));
 297        return ret;
 298    },
 299
 300    /**
 301     * Gets the bubbling parent for an Observable
 302     * @private
 303     * @return {Ext.util.Observable} The bubble parent. null is returned if no bubble target exists
 304     */
 305    getBubbleParent: function(){
 306        var me = this, parent = me.getBubbleTarget && me.getBubbleTarget();
 307        if (parent && parent.isObservable) {
 308            return parent;
 309        }
 310        return null;
 311    },
 312
 313    /**
 314     * Appends an event handler to this object.
 315     *
 316     * @param {String} eventName The name of the event to listen for. May also be an object who's property names are
 317     * event names.
 318     * @param {Function} fn The method the event invokes.  Will be called with arguments given to
 319     * {@link #fireEvent} plus the `options` parameter described below.
 320     * @param {Object} [scope] The scope (`this` reference) in which the handler function is executed. **If
 321     * omitted, defaults to the object which fired the event.**
 322     * @param {Object} [options] An object containing handler configuration.
 323     *
 324     * **Note:** Unlike in ExtJS 3.x, the options object will also be passed as the last argument to every event handler.
 325     *
 326     * This object may contain any of the following properties:
 327     *
 328     * - **scope** : Object
 329     *
 330     *   The scope (`this` reference) in which the handler function is executed. **If omitted, defaults to the object
 331     *   which fired the event.**
 332     *
 333     * - **delay** : Number
 334     *
 335     *   The number of milliseconds to delay the invocation of the handler after the event fires.
 336     *
 337     * - **single** : Boolean
 338     *
 339     *   True to add a handler to handle just the next firing of the event, and then remove itself.
 340     *
 341     * - **buffer** : Number
 342     *
 343     *   Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed by the specified number of
 344     *   milliseconds. If the event fires again within that time, the original handler is _not_ invoked, but the new
 345     *   handler is scheduled in its place.
 346     *
 347     * - **target** : Observable
 348     *
 349     *   Only call the handler if the event was fired on the target Observable, _not_ if the event was bubbled up from a
 350     *   child Observable.
 351     *
 352     * - **element** : String
 353     *
 354     *   **This option is only valid for listeners bound to {@link Ext.Component Components}.** The name of a Component
 355     *   property which references an element to add a listener to.
 356     *
 357     *   This option is useful during Component construction to add DOM event listeners to elements of
 358     *   {@link Ext.Component Components} which will exist only after the Component is rendered.
 359     *   For example, to add a click listener to a Panel's body:
 360     *
 361     *       new Ext.panel.Panel({
 362     *           title: 'The title',
 363     *           listeners: {
 364     *               click: this.handlePanelClick,
 365     *               element: 'body'
 366     *           }
 367     *       });
 368     *
 369     * **Combining Options**
 370     *
 371     * Using the options argument, it is possible to combine different types of listeners:
 372     *
 373     * A delayed, one-time listener.
 374     *
 375     *     myPanel.on('hide', this.handleClick, this, {
 376     *         single: true,
 377     *         delay: 100
 378     *     });
 379     *
 380     * **Attaching multiple handlers in 1 call**
 381     *
 382     * The method also allows for a single argument to be passed which is a config object containing properties which
 383     * specify multiple events. For example:
 384     *
 385     *     myGridPanel.on({
 386     *         cellClick: this.onCellClick,
 387     *         mouseover: this.onMouseOver,
 388     *         mouseout: this.onMouseOut,
 389     *         scope: this // Important. Ensure "this" is correct during handler execution
 390     *     });
 391     *
 392     * One can also specify options for each event handler separately:
 393     *
 394     *     myGridPanel.on({
 395     *         cellClick: {fn: this.onCellClick, scope: this, single: true},
 396     *         mouseover: {fn: panel.onMouseOver, scope: panel}
 397     *     });
 398     *
 399     */
 400    addListener: function(ename, fn, scope, options) {
 401        var me = this,
 402            config,
 403            event;
 404
 405        if (typeof ename !== 'string') {
 406            options = ename;
 407            for (ename in options) {
 408                if (options.hasOwnProperty(ename)) {
 409                    config = options[ename];
 410                    if (!me.eventOptionsRe.test(ename)) {
 411                        me.addListener(ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
 412                    }
 413                }
 414            }
 415        }
 416        else {
 417            ename = ename.toLowerCase();
 418            me.events[ename] = me.events[ename] || true;
 419            event = me.events[ename] || true;
 420            if (Ext.isBoolean(event)) {
 421                me.events[ename] = event = new Ext.util.Event(me, ename);
 422            }
 423            event.addListener(fn, scope, Ext.isObject(options) ? options : {});
 424        }
 425    },
 426
 427    /**
 428     * Removes an event handler.
 429     *
 430     * @param {String} eventName The type of event the handler was associated with.
 431     * @param {Function} fn The handler to remove. **This must be a reference to the function passed into the
 432     * {@link #addListener} call.**
 433     * @param {Object} scope (optional) The scope originally specified for the handler. It must be the same as the
 434     * scope argument specified in the original call to {@link #addListener} or the listener will not be removed.
 435     */
 436    removeListener: function(ename, fn, scope) {
 437        var me = this,
 438            config,
 439            event,
 440            options;
 441
 442        if (typeof ename !== 'string') {
 443            options = ename;
 444            for (ename in options) {
 445                if (options.hasOwnProperty(ename)) {
 446                    config = options[ename];
 447                    if (!me.eventOptionsRe.test(ename)) {
 448                        me.removeListener(ename, config.fn || config, config.scope || options.scope);
 449                    }
 450                }
 451            }
 452        } else {
 453            ename = ename.toLowerCase();
 454            event = me.events[ename];
 455            if (event && event.isEvent) {
 456                event.removeListener(fn, scope);
 457            }
 458        }
 459    },
 460
 461    /**
 462     * Removes all listeners for this object including the managed listeners
 463     */
 464    clearListeners: function() {
 465        var events = this.events,
 466            event,
 467            key;
 468
 469        for (key in events) {
 470            if (events.hasOwnProperty(key)) {
 471                event = events[key];
 472                if (event.isEvent) {
 473                    event.clearListeners();
 474                }
 475            }
 476        }
 477
 478        this.clearManagedListeners();
 479    },
 480
 481    //<debug>
 482    purgeListeners : function() {
 483        if (Ext.global.console) {
 484            Ext.global.console.warn('Observable: purgeListeners has been deprecated. Please use clearListeners.');
 485        }
 486        return this.clearListeners.apply(this, arguments);
 487    },
 488    //</debug>
 489
 490    /**
 491     * Removes all managed listeners for this object.
 492     */
 493    clearManagedListeners : function() {
 494        var managedListeners = this.managedListeners || [],
 495            i = 0,
 496            len = managedListeners.length;
 497
 498        for (; i < len; i++) {
 499            this.removeManagedListenerItem(true, managedListeners[i]);
 500        }
 501
 502        this.managedListeners = [];
 503    },
 504
 505    /**
 506     * Remove a single managed listener item
 507     * @private
 508     * @param {Boolean} isClear True if this is being called during a clear
 509     * @param {Object} managedListener The managed listener item
 510     * See removeManagedListener for other args
 511     */
 512    removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
 513        if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
 514            managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope);
 515            if (!isClear) {
 516                Ext.Array.remove(this.managedListeners, managedListener);
 517            }
 518        }
 519    },
 520
 521    //<debug>
 522    purgeManagedListeners : function() {
 523        if (Ext.global.console) {
 524            Ext.global.console.warn('Observable: purgeManagedListeners has been deprecated. Please use clearManagedListeners.');
 525        }
 526        return this.clearManagedListeners.apply(this, arguments);
 527    },
 528    //</debug>
 529
 530    /**
 531     * Adds the specified events to the list of events which this Observable may fire.
 532     *
 533     * @param {Object/String} o Either an object with event names as properties with a value of `true` or the first
 534     * event name string if multiple event names are being passed as separate parameters. Usage:
 535     *
 536     *     this.addEvents({
 537     *         storeloaded: true,
 538     *         storecleared: true
 539     *     });
 540     *
 541     * @param {String...} more (optional) Additional event names if multiple event names are being passed as separate
 542     * parameters. Usage:
 543     *
 544     *     this.addEvents('storeloaded', 'storecleared');
 545     *
 546     */
 547    addEvents: function(o) {
 548        var me = this,
 549            args,
 550            len,
 551            i;
 552
 553            me.events = me.events || {};
 554        if (Ext.isString(o)) {
 555            args = arguments;
 556            i = args.length;
 557
 558            while (i--) {
 559                me.events[args[i]] = me.events[args[i]] || true;
 560            }
 561        } else {
 562            Ext.applyIf(me.events, o);
 563        }
 564    },
 565
 566    /**
 567     * Checks to see if this object has any listeners for a specified event
 568     *
 569     * @param {String} eventName The name of the event to check for
 570     * @return {Boolean} True if the event is being listened for, else false
 571     */
 572    hasListener: function(ename) {
 573        var event = this.events[ename.toLowerCase()];
 574        return event && event.isEvent === true && event.listeners.length > 0;
 575    },
 576
 577    /**
 578     * Suspends the firing of all events. (see {@link #resumeEvents})
 579     *
 580     * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
 581     * after the {@link #resumeEvents} call instead of discarding all suspended events.
 582     */
 583    suspendEvents: function(queueSuspended) {
 584        this.eventsSuspended = true;
 585        if (queueSuspended && !this.eventQueue) {
 586            this.eventQueue = [];
 587        }
 588    },
 589
 590    /**
 591     * Resumes firing events (see {@link #suspendEvents}).
 592     *
 593     * If events were suspended using the `queueSuspended` parameter, then all events fired
 594     * during event suspension will be sent to any listeners now.
 595     */
 596    resumeEvents: function() {
 597        var me = this,
 598            queued = me.eventQueue;
 599
 600        me.eventsSuspended = false;
 601        delete me.eventQueue;
 602
 603        if (queued) {
 604            Ext.each(queued, function(e) {
 605                me.continueFireEvent.apply(me, e);
 606            });
 607        }
 608    },
 609
 610    /**
 611     * Relays selected events from the specified Observable as if the events were fired by `this`.
 612     *
 613     * @param {Object} origin The Observable whose events this object is to relay.
 614     * @param {String[]} events Array of event names to relay.
 615     * @param {String} prefix
 616     */
 617    relayEvents : function(origin, events, prefix) {
 618        prefix = prefix || '';
 619        var me = this,
 620            len = events.length,
 621            i = 0,
 622            oldName,
 623            newName;
 624
 625        for (; i < len; i++) {
 626            oldName = events[i].substr(prefix.length);
 627            newName = prefix + oldName;
 628            me.events[newName] = me.events[newName] || true;
 629            origin.on(oldName, me.createRelayer(newName));
 630        }
 631    },
 632
 633    /**
 634     * @private
 635     * Creates an event handling function which refires the event from this object as the passed event name.
 636     * @param newName
 637     * @returns {Function}
 638     */
 639    createRelayer: function(newName){
 640        var me = this;
 641        return function(){
 642            return me.fireEvent.apply(me, [newName].concat(Array.prototype.slice.call(arguments, 0, -1)));
 643        };
 644    },
 645
 646    /**
 647     * Enables events fired by this Observable to bubble up an owner hierarchy by calling `this.getBubbleTarget()` if
 648     * present. There is no implementation in the Observable base class.
 649     *
 650     * This is commonly used by Ext.Components to bubble events to owner Containers.
 651     * See {@link Ext.Component#getBubbleTarget}. The default implementation in Ext.Component returns the
 652     * Component's immediate owner. But if a known target is required, this can be overridden to access the
 653     * required target more quickly.
 654     *
 655     * Example:
 656     *
 657     *     Ext.override(Ext.form.field.Base, {
 658     *         //  Add functionality to Field's initComponent to enable the change event to bubble
 659     *         initComponent : Ext.Function.createSequence(Ext.form.field.Base.prototype.initComponent, function() {
 660     *             this.enableBubble('change');
 661     *         }),
 662     *
 663     *         //  We know that we want Field's events to bubble directly to the FormPanel.
 664     *         getBubbleTarget : function() {
 665     *             if (!this.formPanel) {
 666     *                 this.formPanel = this.findParentByType('form');
 667     *             }
 668     *             return this.formPanel;
 669     *         }
 670     *     });
 671     *
 672     *     var myForm = new Ext.formPanel({
 673     *         title: 'User Details',
 674     *         items: [{
 675     *             ...
 676     *         }],
 677     *         listeners: {
 678     *             change: function() {
 679     *                 // Title goes red if form has been modified.
 680     *                 myForm.header.setStyle('color', 'red');
 681     *             }
 682     *         }
 683     *     });
 684     *
 685     * @param {String/String[]} events The event name to bubble, or an Array of event names.
 686     */
 687    enableBubble: function(events) {
 688        var me = this;
 689        if (!Ext.isEmpty(events)) {
 690            events = Ext.isArray(events) ? events: Ext.Array.toArray(arguments);
 691            Ext.each(events,
 692            function(ename) {
 693                ename = ename.toLowerCase();
 694                var ce = me.events[ename] || true;
 695                if (Ext.isBoolean(ce)) {
 696                    ce = new Ext.util.Event(me, ename);
 697                    me.events[ename] = ce;
 698                }
 699                ce.bubble = true;
 700            });
 701        }
 702    }
 703}, function() {
 704
 705    this.createAlias({
 706        /**
 707         * @method
 708         * Shorthand for {@link #addListener}.
 709         * @alias Ext.util.Observable#addListener
 710         */
 711        on: 'addListener',
 712        /**
 713         * @method
 714         * Shorthand for {@link #removeListener}.
 715         * @alias Ext.util.Observable#removeListener
 716         */
 717        un: 'removeListener',
 718        /**
 719         * @method
 720         * Shorthand for {@link #addManagedListener}.
 721         * @alias Ext.util.Observable#addManagedListener
 722         */
 723        mon: 'addManagedListener',
 724        /**
 725         * @method
 726         * Shorthand for {@link #removeManagedListener}.
 727         * @alias Ext.util.Observable#removeManagedListener
 728         */
 729        mun: 'removeManagedListener'
 730    });
 731
 732    //deprecated, will be removed in 5.0
 733    this.observeClass = this.observe;
 734
 735    Ext.apply(Ext.util.Observable.prototype, function(){
 736        // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
 737        // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
 738        // private
 739        function getMethodEvent(method){
 740            var e = (this.methodEvents = this.methodEvents || {})[method],
 741                returnValue,
 742                v,
 743                cancel,
 744                obj = this;
 745
 746            if (!e) {
 747                this.methodEvents[method] = e = {};
 748                e.originalFn = this[method];
 749                e.methodName = method;
 750                e.before = [];
 751                e.after = [];
 752
 753                var makeCall = function(fn, scope, args){
 754                    if((v = fn.apply(scope || obj, args)) !== undefined){
 755                        if (typeof v == 'object') {
 756                            if(v.returnValue !== undefined){
 757                                returnValue = v.returnValue;
 758                            }else{
 759                                returnValue = v;
 760                            }
 761                            cancel = !!v.cancel;
 762                        }
 763                        else
 764                            if (v === false) {
 765                                cancel = true;
 766                            }
 767                            else {
 768                                returnValue = v;
 769                            }
 770                    }
 771                };
 772
 773                this[method] = function(){
 774                    var args = Array.prototype.slice.call(arguments, 0),
 775                        b, i, len;
 776                    returnValue = v = undefined;
 777                    cancel = false;
 778
 779                    for(i = 0, len = e.before.length; i < len; i++){
 780                        b = e.before[i];
 781                        makeCall(b.fn, b.scope, args);
 782                        if (cancel) {
 783                            return returnValue;
 784                        }
 785                    }
 786
 787                    if((v = e.originalFn.apply(obj, args)) !== undefined){
 788                        returnValue = v;
 789                    }
 790
 791                    for(i = 0, len = e.after.length; i < len; i++){
 792                        b = e.after[i];
 793                        makeCall(b.fn, b.scope, args);
 794                        if (cancel) {
 795                            return returnValue;
 796                        }
 797                    }
 798                    return returnValue;
 799                };
 800            }
 801            return e;
 802        }
 803
 804        return {
 805            // these are considered experimental
 806            // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
 807            // adds an 'interceptor' called before the original method
 808            beforeMethod : function(method, fn, scope){
 809                getMethodEvent.call(this, method).before.push({
 810                    fn: fn,
 811                    scope: scope
 812                });
 813            },
 814
 815            // adds a 'sequence' called after the original method
 816            afterMethod : function(method, fn, scope){
 817                getMethodEvent.call(this, method).after.push({
 818                    fn: fn,
 819                    scope: scope
 820                });
 821            },
 822
 823            removeMethodListener: function(method, fn, scope){
 824                var e = this.getMethodEvent(method),
 825                    i, len;
 826                for(i = 0, len = e.before.length; i < len; i++){
 827                    if(e.before[i].fn == fn && e.before[i].scope == scope){
 828                        Ext.Array.erase(e.before, i, 1);
 829                        return;
 830                    }
 831                }
 832                for(i = 0, len = e.after.length; i < len; i++){
 833                    if(e.after[i].fn == fn && e.after[i].scope == scope){
 834                        Ext.Array.erase(e.after, i, 1);
 835                        return;
 836                    }
 837                }
 838            },
 839
 840            toggleEventLogging: function(toggle) {
 841                Ext.util.Observable[toggle ? 'capture' : 'releaseCapture'](this, function(en) {
 842                    if (Ext.isDefined(Ext.global.console)) {
 843                        Ext.global.console.log(en, arguments);
 844                    }
 845                });
 846            }
 847        };
 848    }());
 849});
 850
 851/**
 852 * @class Ext.util.Animate
 853 * This animation class is a mixin.
 854 * 
 855 * Ext.util.Animate provides an API for the creation of animated transitions of properties and styles.  
 856 * This class is used as a mixin and currently applied to {@link Ext.Element}, {@link Ext.CompositeElement}, 
 857 * {@link Ext.draw.Sprite}, {@link Ext.draw.CompositeSprite}, and {@link Ext.Component}.  Note that Components 
 858 * have a limited subset of what attributes can be animated such as top, left, x, y, height, width, and 
 859 * opacity (color, paddings, and margins can not be animated).
 860 * 
 861 * ## Animation Basics
 862 * 
 863 * All animations require three things - `easing`, `duration`, and `to` (the final end value for each property) 
 864 * you wish to animate. Easing and duration are defaulted values specified below.
 865 * Easing describes how the intermediate values used during a transition will be calculated. 
 866 * {@link Ext.fx.Anim#easing Easing} allows for a transition to change speed over its duration.
 867 * You may use the defaults for easing and duration, but you must always set a 
 868 * {@link Ext.fx.Anim#to to} property which is the end value for all animations.  
 869 * 
 870 * Popular element 'to' configurations are:
 871 * 
 872 *  - opacity
 873 *  - x
 874 *  - y
 875 *  - color
 876 *  - height
 877 *  - width 
 878 * 
 879 * Popular sprite 'to' configurations are:
 880 * 
 881 *  - translation
 882 *  - path
 883 *  - scale
 884 *  - stroke
 885 *  - rotation
 886 * 
 887 * The default duration for animations is 250 (which is a 1/4 of a second).  Duration is denoted in 
 888 * milliseconds.  Therefore 1 second is 1000, 1 minute would be 60000, and so on. The default easing curve 
 889 * used for all animations is 'ease'.  Popular easing functions are included and can be found in {@link Ext.fx.Anim#easing Easing}.
 890 * 
 891 * For example, a simple animation to fade out an element with a default easing and duration:
 892 * 
 893 *     var p1 = Ext.get('myElementId');
 894 * 
 895 *     p1.animate({
 896 *         to: {
 897 *             opacity: 0
 898 *         }
 899 *     });
 900 * 
 901 * To make this animation fade out in a tenth of a second:
 902 * 
 903 *     var p1 = Ext.get('myElementId');
 904 * 
 905 *     p1.animate({
 906 *        duration: 100,
 907 *         to: {
 908 *             opacity: 0
 909 *         }
 910 *     });
 911 * 
 912 * ## Animation Queues
 913 * 
 914 * By default all animations are added to a queue which allows for animation via a chain-style API.
 915 * For example, the following code will queue 4 animations which occur sequentially (one right after the other):
 916 * 
 917 *     p1.animate({
 918 *         to: {
 919 *             x: 500
 920 *         }
 921 *     }).animate({
 922 *         to: {
 923 *             y: 150
 924 *         }
 925 *     }).animate({
 926 *         to: {
 927 *             backgroundColor: '#f00'  //red
 928 *         }
 929 *     }).animate({
 930 *         to: {
 931 *             opacity: 0
 932 *         }
 933 *     });
 934 * 
 935 * You can change this behavior by calling the {@link Ext.util.Animate#syncFx syncFx} method and all 
 936 * subsequent animations for the specified target will be run concurrently (at the same time).
 937 * 
 938 *     p1.syncFx();  //this will make all animations run at the same time
 939 * 
 940 *     p1.animate({
 941 *         to: {
 942 *             x: 500
 943 *         }
 944 *     }).animate({
 945 *         to: {
 946 *             y: 150
 947 *         }
 948 *     }).animate({
 949 *         to: {
 950 *             backgroundColor: '#f00'  //red
 951 *         }
 952 *     }).animate({
 953 *         to: {
 954 *             opacity: 0
 955 *         }
 956 *     });
 957 * 
 958 * This works the same as:
 959 * 
 960 *     p1.animate({
 961 *         to: {
 962 *             x: 500,
 963 *             y: 150,
 964 *             backgroundColor: '#f00'  //red
 965 *             opacity: 0
 966 *         }
 967 *     });
 968 * 
 969 * The {@link Ext.util.Animate#stopAnimation stopAnimation} method can be used to stop any 
 970 * currently running animations and clear any queued animations. 
 971 * 
 972 * ## Animation Keyframes
 973 *
 974 * You can also set up complex animations with {@link Ext.fx.Anim#keyframes keyframes} which follow the 
 975 * CSS3 Animation configuration pattern. Note rotation, translation, and scaling can only be done for sprites. 
 976 * The previous example can be written with the following syntax:
 977 * 
 978 *     p1.animate({
 979 *         duration: 1000,  //one second total
 980 *         keyframes: {
 981 *             25: {     //from 0 to 250ms (25%)
 982 *                 x: 0
 983 *             },
 984 *             50: {   //from 250ms to 500ms (50%)
 985 *                 y: 0
 986 *             },
 987 *             75: {  //from 500ms to 750ms (75%)
 988 *                 backgroundColor: '#f00'  //red
 989 *             },
 990 *             100: {  //from 750ms to 1sec
 991 *                 opacity: 0
 992 *             }
 993 *         }
 994 *     });
 995 * 
 996 * ## Animation Events
 997 * 
 998 * Each animation you create has events for {@link Ext.fx.Anim#beforeanimate beforeanimate}, 
 999 * {@link Ext.fx.Anim#afteranimate afteranimate}, and {@link Ext.fx.Anim#lastframe lastframe}.  
1000 * Keyframed animations adds an additional {@link Ext.fx.Animator#keyframe keyframe} event which 
1001 * fires for each keyframe in your animation.
1002 * 
1003 * All animations support the {@link Ext.util.Observable#listeners listeners} configuration to attact functions to these events.
1004 *    
1005 *     startAnimate: function() {
1006 *         var p1 = Ext.get('myElementId');
1007 *         p1.animate({
1008 *            duration: 100,
1009 *             to: {
1010 *                 opacity: 0
1011 *             },
1012 *             listeners: {
1013 *                 beforeanimate:  function() {
1014 *                     // Execute my custom method before the animation
1015 *                     this.myBeforeAnimateFn();
1016 *                 },
1017 *                 afteranimate: function() {
1018 *                     // Execute my custom method after the animation
1019 *                     this.myAfterAnimateFn();
1020 *                 },
1021 *                 scope: this
1022 *         });
1023 *     },
1024 *     myBeforeAnimateFn: function() {
1025 *       // My custom logic
1026 *     },
1027 *     myAfterAnimateFn: function() {
1028 *       // My custom logic
1029 *     }
1030 * 
1031 * Due to the fact that animations run asynchronously, you can determine if an animation is currently 
1032 * running on any target by using the {@link Ext.util.Animate#getActiveAnimation getActiveAnimation} 
1033 * method.  This method will return false if there are no active animations or return the currently 
1034 * running {@link Ext.fx.Anim} instance.
1035 * 
1036 * In this example, we're going to wait for the current animation to finish, then stop any other 
1037 * queued animations before we fade our element's opacity to 0:
1038 * 
1039 *     var curAnim = p1.getActiveAnimation();
1040 *     if (curAnim) {
1041 *         curAnim.on('afteranimate', function() {
1042 *             p1.stopAnimation();
1043 *             p1.animate({
1044 *                 to: {
1045 *                     opacity: 0
1046 *                 }
1047 *             });
1048 *         });
1049 *     }
1050 * 
1051 * @docauthor Jamie Avins <jamie@sencha.com>
1052 */
1053Ext.define('Ext.util.Animate', {
1054
1055    uses: ['Ext.fx.Manager', 'Ext.fx.Anim'],
1056
1057    /**
1058     * <p>Perform custom animation on this object.<p>
1059     * <p>This method is applicable to both the {@link Ext.Component Component} class and the {@link Ext.Element Element} class.
1060     * It performs animated transitions of certain properties of this object over a specified timeline.</p>
1061     * <p>The sole parameter is an object which specifies start property values, end property values, and properties which
1062     * describe the timeline. Of the properties listed below, only <b><code>to</code></b> is mandatory.</p>
1063     * <p>Properties include<ul>
1064     * <li><code>from</code> <div class="sub-desc">An object which specifies start values for the properties being animated.
1065     * If not supplied, properties are animated from current settings. The actual properties which may be animated depend upon
1066     * ths object being animated. See the sections below on Element and Component animation.<div></li>
1067     * <li><code>to</code> <div class="sub-desc">An object which specifies end values for the properties being animated.</div></li>
1068     * <li><code>duration</code><div class="sub-desc">The duration <b>in milliseconds</b> for which the animation will run.</div></li>
1069     * <li><code>easing</code> <div class="sub-desc">A string value describing an easing type to modify the rate of change from the default linear to non-linear. Values may be one of:<code><ul>
1070     * <li>ease</li>
1071     * <li>easeIn</li>
1072     * <li>easeOut</li>
1073     * <li>easeInOut</li>
1074     * <li>backIn</li>
1075     * <li>backOut</li>
1076     * <li>elasticIn</li>
1077     * <li>elasticOut</li>
1078     * <li>bounceIn</li>
1079     * <li>bounceOut</li>
1080     * </ul></code></div></li>
1081     * <li><code>keyframes</code> <div class="sub-desc">This is an object which describes the state of animated properties at certain points along the timeline.
1082     * it is an object containing properties who's names are the percentage along the timeline being described and who's values specify the animation state at that point.</div></li>
1083     * <li><code>listeners</code> <div class="sub-desc">This is a standard {@link Ext.util.Observable#listeners listeners} configuration object which may be used
1084     * to inject behaviour at either the <code>beforeanimate</code> event or the <code>afteranimate</code> event.</div></li>
1085     * </ul></p>
1086     * <h3>Animating an {@link Ext.Element Element}</h3>
1087     * When animating an Element, the following properties may be specified in <code>from</code>, <code>to</code>, and <code>keyframe</code> objects:<ul>
1088     * <li><code>x</code> <div class="sub-desc">The page X position in pixels.</div></li>
1089     * <li><code>y</code> <div class="sub-desc">The page Y position in pixels</div></li>
1090     * <li><code>left</code> <div class="sub-desc">The element's CSS <code>left</code> value. Units must be supplied.</div></li>
1091     * <li><code>top</code> <div class="sub-desc">The element's CSS <code>top</code> value. Units must be supplied.</div></li>
1092     * <li><code>width</code> <div class="sub-desc">The element's CSS <code>width</code> value. Units must be supplied.</div></li>
1093     * <li><code>height</code> <div class="sub-desc">The element's CSS <code>height</code> value. Units must be supplied.</div></li>
1094     * <li><code>scrollLeft</code> <div class="sub-desc">The element's <code>scrollLeft</code> value.</div></li>
1095     * <li><code>scrollTop</code> <div class="sub-desc">The element's <code>scrollLeft</code> value.</div></li>
1096     * <li><code>opacity</code> <div class="sub-desc">The element's <code>opacity</code> value. This must be a value between <code>0</code> and <code>1</code>.</div></li>
1097     * </ul>
1098     * <p><b>Be aware than animating an Element which is being used by an Ext Component without in some way informing the Component about the changed element state
1099     * 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
1100     * directly animate certain properties of Components.</b></p>
1101     * <h3>Animating a {@link Ext.Component Component}</h3>
1102     * When animating an Element, the following properties may be specified in <code>from</code>, <code>to</code>, and <code>keyframe</code> objects:<ul>
1103     * <li><code>x</code> <div class="sub-desc">The Component's page X position in pixels.</div></li>
1104     * <li><code>y</code> <div class="sub-desc">The Component's page Y position in pixels</div></li>
1105     * <li><code>left</code> <div class="sub-desc">The Component's <code>left</code> value in pixels.</div></li>
1106     * <li><code>top</code> <div class="sub-desc">The Component's <code>top</code> value in pixels.</div></li>
1107     * <li><code>width</code> <div class="sub-desc">The Component's <code>width</code> value in pixels.</div></li>
1108     * <li><code>width</code> <div class="sub-desc">The Component's <code>width</code> value in pixels.</div></li>
1109     * <li><code>dynamic</code> <div class="sub-desc">Specify as true to update the Component's layout (if it is a Container) at every frame
1110     * of the animation. <i>Use sparingly as laying out on every intermediate size change is an expensive operation</i>.</div></li>
1111     * </ul>
1112     * <p>For example, to animate a Window to a new size, ensuring that its internal layout, and any shadow is correct:</p>
1113     * <pre><code>
1114myWindow = Ext.create('Ext.window.Window', {
1115    title: 'Test Component animation',
1116    width: 500,
1117    height: 300,
1118    layout: {
1119        type: 'hbox',
1120        align: 'stretch'
1121    },
1122    items: [{
1123        title: 'Left: 33%',
1124        margins: '5 0 5 5',
1125        flex: 1
1126    }, {
1127        title: 'Left: 66%',
1128        margins: '5 5 5 5',
1129        flex: 2
1130    }]
1131});
1132myWindow.show();
1133myWindow.header.el.on('click', function() {
1134    myWindow.animate({
1135        to: {
1136            width: (myWindow.getWidth() == 500) ? 700 : 500,
1137            height: (myWindow.getHeight() == 300) ? 400 : 300,
1138        }
1139    });
1140});
1141</code></pre>
1142     * <p>For performance reasons, by default, the internal layout is only updated when the Window reaches its final <code>"to"</code> size. If dynamic updating of the Window's child
1143     * Components is required, then configure the animation with <code>dynamic: true</code> and the two child items will maintain their proportions during the animation.</p>
1144     * @param {Object} config An object containing properties which describe the animation's start and end states, and the timeline of the animation.
1145     * @return {Object} this
1146     */
1147    animate: function(animObj) {
1148        var me = this;
1149        if (Ext.fx.Manager.hasFxBlock(me.id)) {
1150            return me;
1151        }
1152        Ext.fx.Manager.queueFx(Ext.create('Ext.fx.Anim', me.anim(animObj)));
1153        return this;
1154    },
1155
1156    // @private - process the passed fx configuration.
1157    anim: function(config) {
1158        if (!Ext.isObject(config)) {
1159            return (config) ? {} : false;
1160        }
1161
1162        var me = this;
1163
1164        if (config.stopAnimation) {
1165            me.stopAnimation();
1166        }
1167
1168        Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));
1169
1170        return Ext.apply({
1171            target: me,
1172            paused: true
1173        }, config);
1174    },
1175
1176    /**
1177     * @deprecated 4.0 Replaced by {@link #stopAnimation}
1178     * Stops any running effects and clears this object's internal effects queue if it contains
1179     * any additional effects that haven't started yet.
1180     * @return {Ext.Element} The Element
1181     * @method
1182     */
1183    stopFx: Ext.Function.alias(Ext.util.Animate, 'stopAnimation'),
1184
1185    /**
1186     * Stops any running effects and clears this object's internal effects queue if it contains
1187     * any additional effects that haven't started yet.
1188     * @return {Ext.Element} The Element
1189     */
1190    stopAnimation: function() {
1191        Ext.fx.Manager.stopAnimation(this.id);
1192        return this;
1193    },
1194
1195    /**
1196     * Ensures that all effects queued after syncFx is called on this object are
1197     * run concurrently.  This is the opposite of {@link #sequenceFx}.
1198     * @return {Object} this
1199     */
1200    syncFx: function() {
1201        Ext.fx.Manager.setFxDefaults(this.id, {
1202            concurrent: true
1203        });
1204        return this;
1205    },
1206
1207    /**
1208     * Ensures that all effects queued after sequenceFx is called on this object are
1209     * run in sequence.  This is the opposite of {@link #syncFx}.
1210     * @return {Object} this
1211     */
1212    sequenceFx: function() {
1213        Ext.fx.Manager.setFxDefaults(this.id, {
1214            concurrent: false
1215        });
1216        return this;
1217    },
1218
1219    /**
1220     * @deprecated 4.0 Replaced by {@link #getActiveAnimation}
1221     * @alias Ext.util.Animate#getActiveAnimation
1222     * @method
1223     */
1224    hasActiveFx: Ext.Function.alias(Ext.util.Animate, 'getActiveAnimation'),
1225
1226    /**
1227     * Returns the current animation if this object has any effects actively running or queued, else returns false.
1228     * @return {Ext.fx.Anim/Boolean} Anim if element has active effects, else false
1229     */
1230    getActiveAnimation: function() {
1231        return Ext.fx.Manager.getActiveAnimation(this.id);
1232    }
1233}, function(){
1234    // Apply Animate mixin manually until Element is defined in the proper 4.x way
1235    Ext.applyIf(Ext.Element.prototype, this.prototype);
1236    // We need to call this again so the animation methods get copied over to CE
1237    Ext.CompositeElementLite.importElementMethods();
1238});
1239/**
1240 * @class Ext.state.Provider
1241 * <p>Abstract base class for state provider implementations. The provider is responsible
1242 * for setting values  and extracting values to/from the underlying storage source. The 
1243 * storage source can vary and the details should be implemented in a subclass. For example
1244 * a provider could use a server side database or the browser localstorage where supported.</p>
1245 *
1246 * <p>This class provides methods for encoding and decoding <b>typed</b> variables including 
1247 * dates and defines the Provider interface. By default these methods put the value and the
1248 * type information into a delimited string that can be stored. These should be overridden in 
1249 * a subclass if you want to change the format of the encoded value and subsequent decoding.</p>
1250 */
1251Ext.define('Ext.state.Provider', {
1252    mixins: {
1253        observable: 'Ext.util.Observable'
1254    },
1255    
1256    /**
1257     * @cfg {String} prefix A string to prefix to items stored in the underlying state store. 
1258     * Defaults to <tt>'ext-'</tt>
1259     */
1260    prefix: 'ext-',
1261    
1262    constructor : function(config){
1263        config = config || {};
1264        var me = this;
1265        Ext.apply(me, config);
1266        /**
1267         * @event statechange
1268         * Fires when a state change occurs.
1269         * @param {Ext.state.Provider} this This state provider
1270         * @param {String} key The state key which was changed
1271         * @param {String} value The encoded value for the state
1272         */
1273        me.addEvents("statechange");
1274        me.state = {};
1275        me.mixins.observable.constructor.call(me);
1276    },
1277    
1278    /**
1279     * Returns the current value for a key
1280     * @param {String} name The key name
1281     * @param {Object} defaultValue A default value to return if the key's value is not found
1282     * @return {Object} The state data
1283     */
1284    get : function(name, defaultValue){
1285        return typeof this.state[name] == "undefined" ?
1286            defaultValue : this.state[name];
1287    },
1288
1289    /**
1290     * Clears a value from the state
1291     * @param {String} name The key name
1292     */
1293    clear : function(name){
1294        var me = this;
1295        delete me.state[name];
1296        me.fireEvent("statechange", me, name, null);
1297    },
1298
1299    /**
1300     * Sets the value for a key
1301     * @param {String} name The key name
1302     * @param {Object} value The value to set
1303     */
1304    set : function(name, value){
1305        var me = this;
1306        me.sta

Large files files are truncated, but you can click here to view the full file