PageRenderTime 68ms CodeModel.GetById 19ms app.highlight 25ms RepoModel.GetById 1ms app.codeStats 2ms

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

https://bitbucket.org/srogerf/javascript
JavaScript | 14401 lines | 9947 code | 835 blank | 3619 comment | 856 complexity | 1a8790657b1626f7a21dd493d975bf3f MD5 | raw file

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

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

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