    /**
     * Adds a Load More button at the bottom of the list. When the user presses this button,
     * the next page of data will be loaded into the store and appended to the List.
     *
     * By specifying `{@link #autoPaging}: true`, an 'infinite scroll' effect can be achieved,
     * i.e., the next page of content will load automatically when the user scrolls to the
     * bottom of the list.
     *
     * ## Example
     *
     *     Ext.define('TweetList', {
     *         extend: 'Ext.List',
     *
     *         config: {
     *             store: Ext.create('TweetStore'),
     *
     *             plugins: [
     *                 {
     *                     xclass: 'Ext.plugin.ListPaging',
     *                     autoPaging: true
     *                 }
     *             ],
     *
     *             itemTpl: [
     *                 '<img src="{profile_image_url}" />',
     *                 '<div class="tweet">{text}</div>'
     *             ]
     *         }
     *     });
     *
     */
    Ext.define('Ext.plugin.ListPaging', {
        extend: 'Ext.Component',
        alias: 'plugin.listpaging',

        config: {
            /**
             * @cfg {Boolean} autoPaging
             * True to automatically load the next page when you scroll to the bottom of the list.
             */
            autoPaging: false,

            /**
             * @cfg {String} loadMoreText The text used as the label of the Load More button.
             */
            loadMoreText: 'Load More...',

            /**
             * @cfg {String} noMoreRecordsText The text used as the label of the Load More button when the Store's
             * {@link Ext.data.Store#totalCount totalCount} indicates that all of the records available on the server are
             * already loaded
             */
            noMoreRecordsText: 'No More Records',

            /**
             * @private
             * @cfg {String} loadTpl The template used to render the load more text
             */
            loadTpl: [
                '<div class="{cssPrefix}loading-spinner" style="font-size: 180%; margin: 10px auto;">',
                     '<span class="{cssPrefix}loading-top"></span>',
                     '<span class="{cssPrefix}loading-right"></span>',
                     '<span class="{cssPrefix}loading-bottom"></span>',
                     '<span class="{cssPrefix}loading-left"></span>',
                '</div>',
                '<div class="{cssPrefix}list-paging-msg">{message}</div>'
            ].join(''),

            /**
             *
             */
            loadMoreCmp: {
                xtype: 'component',
                baseCls: Ext.baseCSSPrefix + 'list-paging'
            },

            /**
             * @private
             * @cfg {Boolean} loadMoreCmpAdded Indicates whether or not the load more component has been added to the List
             * yet.
             */
            loadMoreCmpAdded: false,

            /**
             * @private
             * @cfg {String} loadingCls The CSS class that is added to the {@link #loadMoreCmp} while the Store is loading
             */
            loadingCls: Ext.baseCSSPrefix + 'loading',

            /**
             * @private
             * @cfg {Ext.List} list Local reference to the List this plugin is bound to
             */
            list: null,

            /**
             * @private
             * @cfg {Ext.scroll.Scroller} scroller Local reference to the List's Scroller
             */
            scroller: null,

            /**
             * @private
             * @cfg {Boolean} loading True if the plugin has initiated a Store load that has not yet completed
             */
            loading: false
        },

        /**
         * @private
         * Sets up all of the references the plugin needs
         */
        init: function(list) {
            var scroller = list.getScrollable().getScroller();

            this.setList(list);
            this.setScroller(scroller);

            list.getStore().on('load', this.onStoreLoad, this);

            // Disable main list load mask as we provide our own
            list.setLoadingText(null);

            if (this.getAutoPaging()) {
                scroller.on({
                    scrollend: this.onScrollEnd,
                    scope: this
                });
            }
        },

        /**
         * @private
         */
        applyLoadTpl: function(config) {
            return (Ext.isObject(config) && config.isTemplate) ? config : new Ext.XTemplate(config);
        },

        /**
         * @private
         */
        applyLoadMoreCmp: function(config) {
            config = Ext.merge(config, {
                html: this.getLoadTpl().apply({
                    cssPrefix: Ext.baseCSSPrefix,
                    message: this.getLoadMoreText()
                }),
                listeners: {
                    tap: {
                        fn: this.loadNextPage,
                        scope: this,
                        element: 'element'
                    }
                }
            });

            return Ext.factory(config, Ext.Component, this.getLoadMoreCmp());
        },

        /**
         * @private
         * If we're using autoPaging and detect that the user has scrolled to the bottom, kick off loading of the next page
         */
        onScrollEnd: function(scroller, position) {
            if (!this.getLoading() && position.y >= scroller.maxPosition.y) {
                if (!this.storeFullyLoaded()) {
                    this.loadNextPage();
                }
            }
        },

        /**
         * @private
         * Makes sure we add/remove the loading CSS class while the Store is loading
         */
        updateLoading: function(isLoading) {
            var loadMoreCmp = this.getLoadMoreCmp(),
                loadMoreCls = this.getLoadingCls();

            if (isLoading) {
                loadMoreCmp.addCls(loadMoreCls);
            } else {
                loadMoreCmp.removeCls(loadMoreCls);
            }
        },

        /**
         * @private
         */
        onStoreLoad: function(store) {
            var loadCmp  = this.addLoadMoreCmp(),
                template = this.getLoadTpl(),
                message  = this.storeFullyLoaded() ? this.getNoMoreRecordsText() : this.getLoadMoreText();

            this.setLoading(false);

            //restores scroll position after a Store load
            if (this.scrollY) {
                this.getScroller().scrollTo(null, this.scrollY);
                delete this.scrollY;
            }

            //if we've reached the end of the data set, switch to the noMoreRecordsText
            loadCmp.setHtml(template.apply({
                cssPrefix: Ext.baseCSSPrefix,
                message: message
            }));
        },

        /**
         * @private
         * Because the attached List's inner list element is rendered after our init function is called,
         * we need to dynamically add the loadMoreCmp later. This does this once and caches the result.
         */
        addLoadMoreCmp: function() {
            var list = this.getList(),
                cmp  = this.getLoadMoreCmp();

            if (!this.getLoadMoreCmpAdded()) {
                list.add(cmp);
                this.setLoadMoreCmpAdded(true);
            }

            return cmp;
        },

        /**
         * @private
         * Returns true if the Store is detected as being fully loaded, or the server did not return a total count, which
         * means we're in 'infinite' mode
         * @return {Boolean}
         */
        storeFullyLoaded: function() {
            var store = this.getList().getStore(),
                total = store.getTotalCount();

            return total !== null ? store.getTotalCount() <= (store.currentPage * store.getPageSize()) : false;
        },

        /**
         * @private
         */
        loadNextPage: function() {
            this.setLoading(true);

            //keep a cache of the current scroll position as we'll need to reset it after the List is
            //updated with new data
            this.scrollY = this.getScroller().position.y;

            this.getList().getStore().nextPage({ addRecords: true });
        }
    });