| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202 | <!DOCTYPE html><html><head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  <title>The source code</title>  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>  <style type="text/css">    .highlight { display: block; background-color: #ddd; }  </style>  <script type="text/javascript">    function highlight() {      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";    }  </script></head><body onload="prettyPrint(); highlight();">  <pre class="prettyprint lang-js"><span id='Ext-view-AbstractView'>/**</span> * @class Ext.view.AbstractView * This is an abstract superclass and should not be used directly. Please see {@link Ext.view.View}. * @private */Ext.define('Ext.view.AbstractView', {    extend: 'Ext.Component',    requires: [        'Ext.LoadMask',        'Ext.data.StoreManager',        'Ext.CompositeElementLite',        'Ext.DomQuery',        'Ext.selection.DataViewModel'    ],    mixins: {        bindable: 'Ext.util.Bindable'    },    inheritableStatics: {        getRecord: function(node) {            return this.getBoundView(node).getRecord(node);        },        getBoundView: function(node) {            return Ext.getCmp(node.boundView);        }    },<span id='Ext-view-AbstractView-cfg-tpl'>    /**</span>     * @cfg {String/String[]/Ext.XTemplate} tpl (required)     * The HTML fragment or an array of fragments that will make up the template used by this DataView.  This should     * be specified in the same format expected by the constructor of {@link Ext.XTemplate}.     */<span id='Ext-view-AbstractView-cfg-store'>    /**</span>     * @cfg {Ext.data.Store} store (required)     * The {@link Ext.data.Store} to bind this DataView to.     */<span id='Ext-view-AbstractView-cfg-deferInitialRefresh'>    /**</span>     * @cfg {Boolean} deferInitialRefresh     * <p>Defaults to <code>true</code> to defer the initial refresh of the view.</p>     * <p>This allows the View to execute its render and initial layout more quickly because the process will not be encumbered     * by the expensive update of the view structure.</p>     * <p><b>Important: </b>Be aware that this will mean that the View's item elements will not be available immediately upon render, so     * <i>selection</i> may not take place at render time. To access a View's item elements as soon as possible, use the {@link #viewready} event.     * Or set <code>deferInitialrefresh</code> to false, but this will be at the cost of slower rendering.</p>     */    deferInitialRefresh: true,<span id='Ext-view-AbstractView-cfg-itemSelector'>    /**</span>     * @cfg {String} itemSelector (required)     * <b>This is a required setting</b>. A simple CSS selector (e.g. <tt>div.some-class</tt> or     * <tt>span:first-child</tt>) that will be used to determine what nodes this DataView will be     * working with. The itemSelector is used to map DOM nodes to records. As such, there should     * only be one root level element that matches the selector for each record.     */<span id='Ext-view-AbstractView-cfg-itemCls'>    /**</span>     * @cfg {String} itemCls     * Specifies the class to be assigned to each element in the view when used in conjunction with the     * {@link #itemTpl} configuration.     */    itemCls: Ext.baseCSSPrefix + 'dataview-item',<span id='Ext-view-AbstractView-cfg-itemTpl'>    /**</span>     * @cfg {String/String[]/Ext.XTemplate} itemTpl     * The inner portion of the item template to be rendered. Follows an XTemplate     * structure and will be placed inside of a tpl.     */<span id='Ext-view-AbstractView-cfg-overItemCls'>    /**</span>     * @cfg {String} overItemCls     * A CSS class to apply to each item in the view on mouseover.     * Setting this will automatically set {@link #trackOver} to `true`.     */    //<locale><span id='Ext-view-AbstractView-cfg-loadingText'>    /**</span>     * @cfg {String} loadingText     * A string to display during data load operations.  If specified, this text will be     * displayed in a loading div and the view's contents will be cleared while loading, otherwise the view's     * contents will continue to display normally until the new data is loaded and the contents are replaced.     */    loadingText: 'Loading...',    //</locale><span id='Ext-view-AbstractView-cfg-loadMask'>    /**</span>     * @cfg {Boolean/Object} loadMask     * False to disable a load mask from displaying while the view is loading. This can also be a     * {@link Ext.LoadMask} configuration object.     */    loadMask: true,<span id='Ext-view-AbstractView-cfg-loadingCls'>    /**</span>     * @cfg {String} loadingCls     * The CSS class to apply to the loading message element. Defaults to Ext.LoadMask.prototype.msgCls "x-mask-loading".     */<span id='Ext-view-AbstractView-cfg-loadingUseMsg'>    /**</span>     * @cfg {Boolean} loadingUseMsg     * Whether or not to use the loading message.     * @private     */    loadingUseMsg: true,<span id='Ext-view-AbstractView-cfg-loadingHeight'>    /**</span>     * @cfg {Number} loadingHeight     * If specified, gives an explicit height for the data view when it is showing the {@link #loadingText},     * if that is specified. This is useful to prevent the view's height from collapsing to zero when the     * loading mask is applied and there are no other contents in the data view.     */<span id='Ext-view-AbstractView-cfg-selectedItemCls'>    /**</span>     * @cfg {String} selectedItemCls     * A CSS class to apply to each selected item in the view.     */    selectedItemCls: Ext.baseCSSPrefix + 'item-selected',    //<locale><span id='Ext-view-AbstractView-cfg-emptyText'>    /**</span>     * @cfg {String} emptyText     * The text to display in the view when there is no data to display.     * Note that when using local data the emptyText will not be displayed unless you set     * the {@link #deferEmptyText} option to false.     */    emptyText: "",    //</locale><span id='Ext-view-AbstractView-cfg-deferEmptyText'>    /**</span>     * @cfg {Boolean} deferEmptyText     * True to defer emptyText being applied until the store's first load.     */    deferEmptyText: true,<span id='Ext-view-AbstractView-cfg-trackOver'>    /**</span>     * @cfg {Boolean} trackOver     * When `true` the {@link #overItemCls} will be applied to rows when hovered over.     * This in return will also cause {@link Ext.view.View#highlightitem highlightitem} and     * {@link Ext.view.View#unhighlightitem unhighlightitem} events to be fired.     *     * Enabled automatically when the {@link #overItemCls} config is set.     */    trackOver: false,<span id='Ext-view-AbstractView-cfg-blockRefresh'>    /**</span>     * @cfg {Boolean} blockRefresh     * Set this to true to ignore refresh events on the bound store. This is useful if     * you wish to provide custom transition animations via a plugin     */    blockRefresh: false,<span id='Ext-view-AbstractView-cfg-disableSelection'>    /**</span>     * @cfg {Boolean} disableSelection     * True to disable selection within the DataView. This configuration will lock the selection model     * that the DataView uses.     */<span id='Ext-view-AbstractView-cfg-preserveScrollOnRefresh'>    /**</span>     * @cfg {Boolean} preserveScrollOnRefresh=false     * True to preserve scroll position across refresh operations.     */    preserveScrollOnRefresh: false,    //private    last: false,    triggerEvent: 'itemclick',    triggerCtEvent: 'containerclick',    addCmpEvents: function() {    },    // private    initComponent : function(){        var me = this,            isDef = Ext.isDefined,            itemTpl = me.itemTpl,            memberFn = {};        if (itemTpl) {            if (Ext.isArray(itemTpl)) {                // string array                itemTpl = itemTpl.join('');            } else if (Ext.isObject(itemTpl)) {                // tpl instance                memberFn = Ext.apply(memberFn, itemTpl.initialConfig);                itemTpl = itemTpl.html;            }            if (!me.itemSelector) {                me.itemSelector = '.' + me.itemCls;            }            itemTpl = Ext.String.format('<tpl for="."><div class="{0}">{1}</div></tpl>', me.itemCls, itemTpl);            me.tpl = new Ext.XTemplate(itemTpl, memberFn);        }        //<debug>        if (!isDef(me.tpl) || !isDef(me.itemSelector)) {            Ext.Error.raise({                sourceClass: 'Ext.view.View',                tpl: me.tpl,                itemSelector: me.itemSelector,                msg: "DataView requires both tpl and itemSelector configurations to be defined."            });        }        //</debug>        me.callParent();        if(Ext.isString(me.tpl) || Ext.isArray(me.tpl)){            me.tpl = new Ext.XTemplate(me.tpl);        }        //<debug>        // backwards compat alias for overClass/selectedClass        // TODO: Consider support for overCls generation Ext.Component config        if (isDef(me.overCls) || isDef(me.overClass)) {            if (Ext.isDefined(Ext.global.console)) {                Ext.global.console.warn('Ext.view.View: Using the deprecated overCls or overClass configuration. Use overItemCls instead.');            }            me.overItemCls = me.overCls || me.overClass;            delete me.overCls;            delete me.overClass;        }        if (me.overItemCls) {            me.trackOver = true;        }        if (isDef(me.selectedCls) || isDef(me.selectedClass)) {            if (Ext.isDefined(Ext.global.console)) {                Ext.global.console.warn('Ext.view.View: Using the deprecated selectedCls or selectedClass configuration. Use selectedItemCls instead.');            }            me.selectedItemCls = me.selectedCls || me.selectedClass;            delete me.selectedCls;            delete me.selectedClass;        }        //</debug>        me.addEvents(<span id='Ext-view-AbstractView-event-beforerefresh'>            /**</span>             * @event beforerefresh             * Fires before the view is refreshed             * @param {Ext.view.View} this The DataView object             */            'beforerefresh',<span id='Ext-view-AbstractView-event-refresh'>            /**</span>             * @event refresh             * Fires when the view is refreshed             * @param {Ext.view.View} this The DataView object             */            'refresh',<span id='Ext-view-AbstractView-event-viewready'>            /**</span>             * @event viewready             * Fires when the View's item elements representing Store items has been rendered. If the {@link #deferInitialRefresh} flag             * was set (and it is <code>true</code> by default), this will be <b>after</b> initial render, and no items will be available             * for selection until this event fires.             * @param {Ext.view.View} this             */            'viewready',<span id='Ext-view-AbstractView-event-itemupdate'>            /**</span>             * @event itemupdate             * Fires when the node associated with an individual record is updated             * @param {Ext.data.Model} record The model instance             * @param {Number} index The index of the record/node             * @param {HTMLElement} node The node that has just been updated             */            'itemupdate',<span id='Ext-view-AbstractView-event-itemadd'>            /**</span>             * @event itemadd             * Fires when the nodes associated with an recordset have been added to the underlying store             * @param {Ext.data.Model[]} records The model instance             * @param {Number} index The index at which the set of record/nodes starts             * @param {HTMLElement[]} node The node that has just been updated             */            'itemadd',<span id='Ext-view-AbstractView-event-itemremove'>            /**</span>             * @event itemremove             * Fires when the node associated with an individual record is removed             * @param {Ext.data.Model} record The model instance             * @param {Number} index The index of the record/node             */            'itemremove'        );        me.addCmpEvents();        // Look up the configured Store. If none configured, use the fieldless, empty Store defined in Ext.data.Store.        me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store');        me.bindStore(me.store, true);        me.all = new Ext.CompositeElementLite();        // We track the scroll position        me.scrollState = {            top: 0,            left: 0        };        me.on({            scroll: me.onViewScroll,            element: 'el',            scope: me        });    },    onRender: function() {        var me = this,            mask = me.loadMask,            cfg = {                msg: me.loadingText,                msgCls: me.loadingCls,                useMsg: me.loadingUseMsg,                // The store gets bound in initComponent, so while                // rendering let's push on the store                store: me.getMaskStore()            };        me.callParent(arguments);        if (mask) {            // either a config object             if (Ext.isObject(mask)) {                cfg = Ext.apply(cfg, mask);            }            // Attach the LoadMask to a *Component* so that it can be sensitive to resizing during long loads.            // If this DataView is floating, then mask this DataView.            // Otherwise, mask its owning Container (or this, if there *is* no owning Container).            // LoadMask captures the element upon render.            me.loadMask = new Ext.LoadMask(me, cfg);            me.loadMask.on({                scope: me,                beforeshow: me.onMaskBeforeShow,                hide: me.onMaskHide            });        }    },        finishRender: function(){        var me = this;        me.callParent(arguments);        // Kick off the refresh before layouts are resumed after the render         // completes, but after afterrender is fired on the view        if (!me.up('[collapsed],[hidden]')) {            me.doFirstRefresh(me.store);        }    },    onBoxReady: function() {        var me = this;        me.callParent(arguments);        // If the refresh was not kicked off on render due to a collapsed or hidden ancestor,        // kick it off as soon as we get layed out        if (!me.firstRefreshDone) {            me.doFirstRefresh(me.store);        }    },        getMaskStore: function(){        return this.store;        },        onMaskBeforeShow: function(){        var me = this,            loadingHeight = me.loadingHeight;        me.getSelectionModel().deselectAll();        me.all.clear();        if (loadingHeight && loadingHeight > me.getHeight()) {            me.hasLoadingHeight = true;            me.oldMinHeight = me.minHeight;            me.minHeight = loadingHeight;            me.updateLayout();        }    },    onMaskHide: function(){        var me = this;        if (!me.destroying && me.hasLoadingHeight) {            me.minHeight = me.oldMinHeight;            me.updateLayout();            delete me.hasLoadingHeight;        }    },    beforeRender: function() {        this.callParent(arguments);        this.getSelectionModel().beforeViewRender(this);    },    afterRender: function() {        this.callParent(arguments);        // Init the SelectionModel after any on('render') listeners have been added.        // Drag plugins create a DragDrop instance in a render listener, and that needs        // to see an itemmousedown event first.        this.getSelectionModel().bindComponent(this);    },<span id='Ext-view-AbstractView-method-getSelectionModel'>    /**</span>     * Gets the selection model for this view.     * @return {Ext.selection.Model} The selection model     */    getSelectionModel: function(){        var me = this,            mode = 'SINGLE';        if (!me.selModel) {            me.selModel = {};        }        if (me.simpleSelect) {            mode = 'SIMPLE';        } else if (me.multiSelect) {            mode = 'MULTI';        }        Ext.applyIf(me.selModel, {            allowDeselect: me.allowDeselect,            mode: mode        });        if (!me.selModel.events) {            me.selModel = new Ext.selection.DataViewModel(me.selModel);        }        if (!me.selModel.hasRelaySetup) {            me.relayEvents(me.selModel, [                'selectionchange', 'beforeselect', 'beforedeselect', 'select', 'deselect', 'focuschange'            ]);            me.selModel.hasRelaySetup = true;        }        // lock the selection model if user        // has disabled selection        if (me.disableSelection) {            me.selModel.locked = true;        }        return me.selModel;    },<span id='Ext-view-AbstractView-method-refresh'>    /**</span>     * Refreshes the view by reloading the data from the store and re-rendering the template.     */    refresh: function() {        var me = this,            targetEl,            targetParent,            oldDisplay,            nextSibling,            dom,            records;        if (!me.rendered || me.isDestroyed) {            return;        }        if (!me.hasListeners.beforerefresh || me.fireEvent('beforerefresh', me) !== false) {            targetEl = me.getTargetEl();            records = me.store.getRange();            dom = targetEl.dom;            // Updating is much quicker if done when the targetEl is detached from the document, and not displayed.            // But this resets the scroll position, so when preserving scroll position, this cannot be done.            if (!me.preserveScrollOnRefresh) {                targetParent = dom.parentNode;                oldDisplay = dom.style.display;                dom.style.display = 'none';                nextSibling = dom.nextSibling;                targetParent.removeChild(dom);            }            if (me.refreshCounter) {                me.clearViewEl();            } else {                me.fixedNodes = targetEl.dom.childNodes.length;                me.refreshCounter = 1;            }            // Always attempt to create the required markup after the fixedNodes.            // Usually, for an empty record set, this would be blank, but when the Template            // Creates markup outside of the record loop, this must still be honoured even if there are no            // records.            me.tpl.append(targetEl, me.collectData(records, 0));            // The emptyText is now appended to the View's element            // after any fixedNodes.            if (records.length < 1) {                if (!me.deferEmptyText || me.hasSkippedEmptyText) {                    Ext.core.DomHelper.insertHtml('beforeEnd', targetEl.dom, me.emptyText);                }                me.all.clear();            } else {                me.all.fill(Ext.query(me.getItemSelector(), targetEl.dom));                me.updateIndexes(0);            }            me.selModel.refresh();            me.hasSkippedEmptyText = true;            if (!me.preserveScrollOnRefresh) {                targetParent.insertBefore(dom, nextSibling);                dom.style.display = oldDisplay;            }            // Ensure layout system knows about new content size            this.refreshSize();            me.fireEvent('refresh', me);            // Upon first refresh, fire the viewready event.            // Reconfiguring the grid "renews" this event.            if (!me.viewReady) {                // Fire an event when deferred content becomes available.                // This supports grid Panel's deferRowRender capability                me.viewReady = true;                me.fireEvent('viewready', me);            }        }    },<span id='Ext-view-AbstractView-method-refreshSize'>    /**</span>     * @private     * Called by the framework when the view is refreshed, or when rows are added or deleted.     *      * These operations may cause the view's dimensions to change, and if the owning container     * is shrinkwrapping this view, then the layout must be updated to accommodate these new dimensions.     */    refreshSize: function() {        var sizeModel = this.getSizeModel();        if (sizeModel.height.shrinkWrap || sizeModel.width.shrinkWrap) {            this.updateLayout();        }    },    clearViewEl: function(){        // The purpose of this is to allow boilerplate HTML nodes to remain in place inside a View        // while the transient, templated data can be discarded and recreated.        // The first time through this code, we take a count of the number of existing child nodes.        // Subsequent refreshes then do not clear the entire element, but remove all nodes        // *after* the fixedNodes count.        // In particular, this is used in infinite grid scrolling: A very tall "stretcher" element is        // inserted into the View's element to create a scrollbar of the correct proportion.        var me = this,            el = me.getTargetEl();        if (me.fixedNodes) {            while (el.dom.childNodes[me.fixedNodes]) {                el.dom.removeChild(el.dom.childNodes[me.fixedNodes]);            }        } else {            el.update('');        }        me.refreshCounter++;    },    // Private template method to be overridden in subclasses.    onViewScroll: Ext.emptyFn,<span id='Ext-view-AbstractView-method-saveScrollState'>    /**</span>     * Saves the scrollState in a private variable. Must be used in conjunction with restoreScrollState.     * @private     */    saveScrollState: function() {        if (this.rendered) {            var dom = this.el.dom,                state = this.scrollState;            state.left = dom.scrollLeft;            state.top = dom.scrollTop;        }    },<span id='Ext-view-AbstractView-method-restoreScrollState'>    /**</span>     * Restores the scrollState.     * Must be used in conjunction with saveScrollState     * @private     */    restoreScrollState: function() {        if (this.rendered) {            var dom = this.el.dom,                 state = this.scrollState;            dom.scrollLeft = state.left;            dom.scrollTop = state.top;        }    },<span id='Ext-view-AbstractView-method-prepareData'>    /**</span>     * Function which can be overridden to provide custom formatting for each Record that is used by this     * DataView's {@link #tpl template} to render each node.     * @param {Object/Object[]} data The raw data object that was used to create the Record.     * @param {Number} recordIndex the index number of the Record being prepared for rendering.     * @param {Ext.data.Model} record The Record being prepared for rendering.     * @return {Array/Object} The formatted data in a format expected by the internal {@link #tpl template}'s overwrite() method.     * (either an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}))     */    prepareData: function(data, index, record) {        var associatedData, attr;        if (record) {            associatedData = record.getAssociatedData();            for (attr in associatedData) {                if (associatedData.hasOwnProperty(attr)) {                    data[attr] = associatedData[attr];                }            }        }        return data;    },<span id='Ext-view-AbstractView-method-collectData'>    /**</span>     * <p>Function which can be overridden which returns the data object passed to this     * DataView's {@link #tpl template} to render the whole DataView.</p>     * <p>This is usually an Array of data objects, each element of which is processed by an     * {@link Ext.XTemplate XTemplate} which uses <tt>'&lt;tpl for="."&gt;'</tt> to iterate over its supplied     * data object as an Array. However, <i>named</i> properties may be placed into the data object to     * provide non-repeating data such as headings, totals etc.</p>     * @param {Ext.data.Model[]} records An Array of {@link Ext.data.Model}s to be rendered into the DataView.     * @param {Number} startIndex the index number of the Record being prepared for rendering.     * @return {Object[]} An Array of data objects to be processed by a repeating XTemplate. May also     * contain <i>named</i> properties.     */    collectData : function(records, startIndex){        var data = [],            i = 0,            len = records.length,            record;        for (; i < len; i++) {            record = records[i];            data[i] = this.prepareData(record.data, startIndex + i, record);        }        return data;    },    // private    bufferRender : function(records, index){        var me = this,            div = me.renderBuffer || (me.renderBuffer = document.createElement('div'));        me.tpl.overwrite(div, me.collectData(records, index));        return Ext.query(me.getItemSelector(), div);    },    // private    onUpdate : function(ds, record){        var me = this,            index,            node;        if (me.viewReady) {            index = me.store.indexOf(record);            if (index > -1) {                node = me.bufferRender([record], index)[0];                // ensure the node actually exists in the DOM                if (me.getNode(record)) {                    me.all.replaceElement(index, node, true);                    me.updateIndexes(index, index);                    // Maintain selection after update                    // TODO: Move to approriate event handler.                    me.selModel.refresh();                    if (me.hasListeners.itemupdate) {                        me.fireEvent('itemupdate', record, index, node);                    }                    return node;                }            }        }    },    // private    onAdd : function(ds, records, index) {        var me = this,            nodes;        if (me.rendered) {            // If we are adding into an empty view, we must refresh in order that the *full tpl* is applied            // which might create boilerplate content *around* the record nodes.            if (me.all.getCount() === 0) {                me.refresh();                return;            }            nodes = me.bufferRender(records, index);            me.doAdd(nodes, records, index);            me.selModel.refresh();            me.updateIndexes(index);            // Ensure layout system knows about new content size            me.refreshSize();            if (me.hasListeners.itemadd) {                me.fireEvent('itemadd', records, index, nodes);            }        }    },    doAdd: function(nodes, records, index) {        var all = this.all,            count = all.getCount();        if (count === 0) {            this.clearViewEl();            this.getTargetEl().appendChild(nodes);        } else if (index < count) {            if (index === 0) {                all.item(index).insertSibling(nodes, 'before', true);            } else {                all.item(index - 1).insertSibling(nodes, 'after', true);            }        } else {            all.last().insertSibling(nodes, 'after', true);        }        Ext.Array.insert(all.elements, index, nodes);    },    // private    onRemove : function(ds, record, index) {        var me = this;        if (me.all.getCount()) {            if (me.store.getCount() === 0) {                // Refresh so emptyText can be applied if necessary                me.refresh();            } else {                // Just remove the element which corresponds to the removed record                // The tpl's full HTML will still be in place.                me.doRemove(record, index);                if (me.selModel.refreshOnRemove) {                    me.selModel.refresh();                }                me.updateIndexes(index);            }            // Ensure layout system knows about new content height            this.refreshSize();            if (me.hasListeners.itemremove) {                me.fireEvent('itemremove', record, index);            }        }    },    doRemove: function(record, index) {        this.all.removeElement(index, true);    },<span id='Ext-view-AbstractView-method-refreshNode'>    /**</span>     * Refreshes an individual node's data from the store.     * @param {Number} index The item's data index in the store     */    refreshNode : function(index){        this.onUpdate(this.store, this.store.getAt(index));    },    // private    updateIndexes : function(startIndex, endIndex) {        var ns = this.all.elements,            records = this.store.getRange(),            i;        startIndex = startIndex || 0;        endIndex = endIndex || ((endIndex === 0) ? 0 : (ns.length - 1));        for (i = startIndex; i <= endIndex; i++) {            ns[i].viewIndex = i;            ns[i].viewRecordId = records[i].internalId;            if (!ns[i].boundView) {                ns[i].boundView = this.id;            }        }    },<span id='Ext-view-AbstractView-method-getStore'>    /**</span>     * Returns the store associated with this DataView.     * @return {Ext.data.Store} The store     */    getStore : function() {        return this.store;    },<span id='Ext-view-AbstractView-method-bindStore'>    /**</span>     * Changes the data store bound to this view and refreshes it.     * @param {Ext.data.Store} store The store to bind to this view     */    bindStore : function(store, initial) {        var me = this;        me.mixins.bindable.bindStore.apply(me, arguments);        // Bind the store to our selection model unless it's the initial bind.        // Initial bind takes place afterRender        if (!initial) {            me.getSelectionModel().bindStore(me.store);        }        // If we have already achieved our first layout, refresh immediately.        // If we have bound to the Store before the first layout, then onBoxReady will        // call doFirstRefresh        if (me.componentLayoutCounter) {            me.doFirstRefresh(store);        }    },<span id='Ext-view-AbstractView-method-doFirstRefresh'>    /**</span>     * @private     * Perform the first refresh of the View from a newly bound store.     *      * This is called when this View has been sized for the first time.     */    doFirstRefresh: function(store) {        var me = this;        // Flag to prevent a second "first" refresh from onBoxReady        me.firstRefreshDone = true;        // 4.1.0: If we have a store, and the Store is *NOT* already loading (a refresh is on the way), then        // on first layout, refresh regardless of record count.        // Template may contain boilerplate HTML outside of record iteration loop.        // Also, emptyText is appended by the refresh method.        // We call refresh on a defer if this is the initial call, and we are configured to defer the initial refresh.        if (store && !store.loading) {            if (me.deferInitialRefresh) {                me.applyFirstRefresh();            } else {                me.refresh();            }        }    },        applyFirstRefresh: function(){        var me = this;        if (me.isDestroyed) {            return;        }                // In the case of an animated collapse/expand, the layout will        // be marked as though it's complete, yet the element itself may        // still be animating, which means we could trigger a layout while        // everything is not in the correct place. As such, wait until the        // animation has finished before kicking off the refresh. The problem        // occurs because both the refresh and the animation are running on        // a timer which makes it impossible to control the order of when        // the refresh is fired.        if (me.up('[isCollapsingOrExpanding]')) {            Ext.Function.defer(me.applyFirstRefresh, 100, me);        } else {            Ext.Function.defer(function () {                if (!me.isDestroyed) {                    me.refresh();                }            }, 1);        }    },    onUnbindStore: function(store) {        this.setMaskBind(null);    },    onBindStore: function(store) {        this.setMaskBind(store);    },    setMaskBind: function(store) {        var mask = this.loadMask;        if (mask && mask.bindStore) {            mask.bindStore(store);        }    },    getStoreListeners: function() {        var me = this;        return {            refresh: me.onDataRefresh,            add: me.onAdd,            remove: me.onRemove,            update: me.onUpdate,            clear: me.refresh        };    },<span id='Ext-view-AbstractView-method-onDataRefresh'>    /**</span>     * @private     * Calls this.refresh if this.blockRefresh is not true     */    onDataRefresh: function() {        var me = this,            // If we have an ancestor in a non-boxready state (collapsed or in-transition, or hidden), and we are still waiting            // for the first refresh, then block the refresh because that first visible, expanded layout will trigger the refresh            blockedByAncestor = !me.firstRefreshDone && (!me.rendered || me.up('[collapsed],[isCollapsingOrExpanding],[hidden]'));        // If are blocking *an initial refresh* because of an ancestor in a non-boxready state,        // then cancel any defer on the initial refresh that is going to happen on boxReady - that will be a data-driven refresh, NOT        // a render-time, delayable refresh. This is particularly important if the boxready occurs because of the "preflight" layout        // of an animated expand. If refresh is delayed it occur during the expand animation and cause errors.        if (blockedByAncestor) {            me.deferInitialRefresh = false;        } else if (me.blockRefresh !== true) {            me.firstRefreshDone = true;            me.refresh.apply(me, arguments);        }    },<span id='Ext-view-AbstractView-method-findItemByChild'>    /**</span>     * Returns the template node the passed child belongs to, or null if it doesn't belong to one.     * @param {HTMLElement} node     * @return {HTMLElement} The template node     */    findItemByChild: function(node){        return Ext.fly(node).findParent(this.getItemSelector(), this.getTargetEl());    },<span id='Ext-view-AbstractView-method-findTargetByEvent'>    /**</span>     * Returns the template node by the Ext.EventObject or null if it is not found.     * @param {Ext.EventObject} e     */    findTargetByEvent: function(e) {        return e.getTarget(this.getItemSelector(), this.getTargetEl());    },<span id='Ext-view-AbstractView-method-getSelectedNodes'>    /**</span>     * Gets the currently selected nodes.     * @return {HTMLElement[]} An array of HTMLElements     */    getSelectedNodes: function(){        var nodes   = [],            records = this.selModel.getSelection(),            ln = records.length,            i  = 0;        for (; i < ln; i++) {            nodes.push(this.getNode(records[i]));        }        return nodes;    },<span id='Ext-view-AbstractView-method-getRecords'>    /**</span>     * Gets an array of the records from an array of nodes     * @param {HTMLElement[]} nodes The nodes to evaluate     * @return {Ext.data.Model[]} records The {@link Ext.data.Model} objects     */    getRecords: function(nodes) {        var records = [],            i = 0,            len = nodes.length,            data = this.store.data;        for (; i < len; i++) {            records[records.length] = data.getByKey(nodes[i].viewRecordId);        }        return records;    },<span id='Ext-view-AbstractView-method-getRecord'>    /**</span>     * Gets a record from a node     * @param {Ext.Element/HTMLElement} node The node to evaluate     *     * @return {Ext.data.Model} record The {@link Ext.data.Model} object     */    getRecord: function(node){        return this.store.data.getByKey(Ext.getDom(node).viewRecordId);    },<span id='Ext-view-AbstractView-method-isSelected'>    /**</span>     * Returns true if the passed node is selected, else false.     * @param {HTMLElement/Number/Ext.data.Model} node The node, node index or record to check     * @return {Boolean} True if selected, else false     */    isSelected : function(node) {        // TODO: El/Idx/Record        var r = this.getRecord(node);        return this.selModel.isSelected(r);    },<span id='Ext-view-AbstractView-method-select'>    /**</span>     * Selects a record instance by record instance or index.     * @param {Ext.data.Model[]/Number} records An array of records or an index     * @param {Boolean} keepExisting     * @param {Boolean} suppressEvent Set to false to not fire a select event     * @deprecated 4.0 Use {@link Ext.selection.Model#select} instead.     */    select: function(records, keepExisting, suppressEvent) {        this.selModel.select(records, keepExisting, suppressEvent);    },<span id='Ext-view-AbstractView-method-deselect'>    /**</span>     * Deselects a record instance by record instance or index.     * @param {Ext.data.Model[]/Number} records An array of records or an index     * @param {Boolean} suppressEvent Set to false to not fire a deselect event     */    deselect: function(records, suppressEvent) {        this.selModel.deselect(records, suppressEvent);    },<span id='Ext-view-AbstractView-method-getNode'>    /**</span>     * Gets a template node.     * @param {HTMLElement/String/Number/Ext.data.Model} nodeInfo An HTMLElement template node, index of a template node,     * the id of a template node or the record associated with the node.     * @return {HTMLElement} The node or null if it wasn't found     */    getNode : function(nodeInfo) {        if ((!nodeInfo && nodeInfo !== 0) || !this.rendered) {            return null;        }        if (Ext.isString(nodeInfo)) {            return document.getElementById(nodeInfo);        }        if (Ext.isNumber(nodeInfo)) {            return this.all.elements[nodeInfo];        }        if (nodeInfo.isModel) {            return this.getNodeByRecord(nodeInfo);        }        return nodeInfo; // already an HTMLElement    },<span id='Ext-view-AbstractView-method-getNodeByRecord'>    /**</span>     * @private     */    getNodeByRecord: function(record) {        var ns = this.all.elements,            ln = ns.length,            i = 0;        for (; i < ln; i++) {            if (ns[i].viewRecordId === record.internalId) {                return ns[i];            }        }        return null;    },<span id='Ext-view-AbstractView-method-getNodes'>    /**</span>     * Gets a range nodes.     * @param {Number} start (optional) The index of the first node in the range     * @param {Number} end (optional) The index of the last node in the range     * @return {HTMLElement[]} An array of nodes     */    getNodes: function(start, end) {        var ns = this.all.elements;        if (end === undefined) {            end = ns.length;        } else {            end++;        }        return this.all.elements.slice(start||0, end);    },<span id='Ext-view-AbstractView-method-indexOf'>    /**</span>     * Finds the index of the passed node.     * @param {HTMLElement/String/Number/Ext.data.Model} nodeInfo An HTMLElement template node, index of a template node, the id of a template node     * or a record associated with a node.     * @return {Number} The index of the node or -1     */    indexOf: function(node) {        node = this.getNode(node);        if (!node && node !== 0) {            return -1;        }        if (Ext.isNumber(node.viewIndex)) {            return node.viewIndex;        }        return this.all.indexOf(node);    },    onDestroy : function() {        var me = this;        me.all.clear();        me.callParent();        me.bindStore(null);        me.selModel.destroy();    },    // invoked by the selection model to maintain visual UI cues    onItemSelect: function(record) {        var node = this.getNode(record);        if (node) {            Ext.fly(node).addCls(this.selectedItemCls);        }    },    // invoked by the selection model to maintain visual UI cues    onItemDeselect: function(record) {        var node = this.getNode(record);        if (node) {            Ext.fly(node).removeCls(this.selectedItemCls);        }    },    getItemSelector: function() {        return this.itemSelector;    }}, function() {    // all of this information is available directly    // from the SelectionModel itself, the only added methods    // to DataView regarding selection will perform some transformation/lookup    // between HTMLElement/Nodes to records and vice versa.    Ext.deprecate('extjs', '4.0', function() {        Ext.view.AbstractView.override({<span id='Ext-view-AbstractView-cfg-multiSelect'>            /**</span>             * @cfg {Boolean} [multiSelect=false]             * True to allow selection of more than one item at a time, false to allow selection of only a single item             * at a time or no selection at all, depending on the value of {@link #singleSelect}.             * @deprecated 4.1.1 Use {@link Ext.selection.Model#mode} 'MULTI' instead.             */<span id='Ext-view-AbstractView-cfg-singleSelect'>            /**</span>             * @cfg {Boolean} [singleSelect]             * Allows selection of exactly one item at a time. As this is the default selection mode anyway, this config             * is completely ignored.             * @removed 4.1.1 Use {@link Ext.selection.Model#mode} 'SINGLE' instead.             */<span id='Ext-view-AbstractView-cfg-simpleSelect'>            /**</span>             * @cfg {Boolean} [simpleSelect=false]             * True to enable multiselection by clicking on multiple items without requiring the user to hold Shift or Ctrl,             * false to force the user to hold Ctrl or Shift to select more than on item.             * @deprecated 4.1.1 Use {@link Ext.selection.Model#mode} 'SIMPLE' instead.             */<span id='Ext-view-AbstractView-method-getSelectionCount'>            /**</span>             * Gets the number of selected nodes.             * @return {Number} The node count             * @deprecated 4.0 Use {@link Ext.selection.Model#getCount} instead.             */            getSelectionCount : function(){                if (Ext.global.console) {                    Ext.global.console.warn("DataView: getSelectionCount will be removed, please interact with the Ext.selection.DataViewModel");                }                return this.selModel.getSelection().length;            },<span id='Ext-view-AbstractView-method-getSelectedRecords'>            /**</span>             * Gets an array of the selected records             * @return {Ext.data.Model[]} An array of {@link Ext.data.Model} objects             * @deprecated 4.0 Use {@link Ext.selection.Model#getSelection} instead.             */            getSelectedRecords : function(){                if (Ext.global.console) {                    Ext.global.console.warn("DataView: getSelectedRecords will be removed, please interact with the Ext.selection.DataViewModel");                }                return this.selModel.getSelection();            },            select: function(records, keepExisting, supressEvents) {                if (Ext.global.console) {                    Ext.global.console.warn("DataView: select will be removed, please access select through a DataView's SelectionModel, ie: view.getSelectionModel().select()");                }                var sm = this.getSelectionModel();                return sm.select.apply(sm, arguments);            },<span id='Ext-view-AbstractView-method-clearSelections'>            /**</span>             * Deselects all selected records.             * @deprecated 4.0 Use {@link Ext.selection.Model#deselectAll} instead.             */            clearSelections: function() {                if (Ext.global.console) {                    Ext.global.console.warn("DataView: clearSelections will be removed, please access deselectAll through DataView's SelectionModel, ie: view.getSelectionModel().deselectAll()");                }                var sm = this.getSelectionModel();                return sm.deselectAll();            }        });    });});</pre></body></html>
 |