X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/6746dc89c47ed01b165cc1152533605f97eb8e8d..HEAD:/docs/source/View3.html diff --git a/docs/source/View3.html b/docs/source/View3.html index 91c04c5a..ae1c1f66 100644 --- a/docs/source/View3.html +++ b/docs/source/View3.html @@ -3,8 +3,8 @@ The source code - - + + @@ -15,490 +15,597 @@ -
/**
- * @class Ext.tree.View
- * @extends Ext.view.Table
+  
/**
+ * A mechanism for displaying data using custom layout templates and formatting.
+ *
+ * The View uses an {@link Ext.XTemplate} as its internal templating mechanism, and is bound to an
+ * {@link Ext.data.Store} so that as the data in the store changes the view is automatically updated
+ * to reflect the changes. The view also provides built-in behavior for many common events that can
+ * occur for its contained items including click, doubleclick, mouseover, mouseout, etc. as well as a
+ * built-in selection model. **In order to use these features, an {@link #itemSelector} config must
+ * be provided for the DataView to determine what nodes it will be working with.**
+ *
+ * The example below binds a View to a {@link Ext.data.Store} and renders it into an {@link Ext.panel.Panel}.
+ *
+ *     @example
+ *     Ext.define('Image', {
+ *         extend: 'Ext.data.Model',
+ *         fields: [
+ *             { name:'src', type:'string' },
+ *             { name:'caption', type:'string' }
+ *         ]
+ *     });
+ *
+ *     Ext.create('Ext.data.Store', {
+ *         id:'imagesStore',
+ *         model: 'Image',
+ *         data: [
+ *             { src:'http://www.sencha.com/img/20110215-feat-drawing.png', caption:'Drawing & Charts' },
+ *             { src:'http://www.sencha.com/img/20110215-feat-data.png', caption:'Advanced Data' },
+ *             { src:'http://www.sencha.com/img/20110215-feat-html5.png', caption:'Overhauled Theme' },
+ *             { src:'http://www.sencha.com/img/20110215-feat-perf.png', caption:'Performance Tuned' }
+ *         ]
+ *     });
+ *
+ *     var imageTpl = new Ext.XTemplate(
+ *         '<tpl for=".">',
+ *             '<div style="margin-bottom: 10px;" class="thumb-wrap">',
+ *               '<img src="{src}" />',
+ *               '<br/><span>{caption}</span>',
+ *             '</div>',
+ *         '</tpl>'
+ *     );
+ *
+ *     Ext.create('Ext.view.View', {
+ *         store: Ext.data.StoreManager.lookup('imagesStore'),
+ *         tpl: imageTpl,
+ *         itemSelector: 'div.thumb-wrap',
+ *         emptyText: 'No images available',
+ *         renderTo: Ext.getBody()
+ *     });
  */
-Ext.define('Ext.tree.View', {
-    extend: 'Ext.view.Table',
-    alias: 'widget.treeview',
-
-    loadingCls: Ext.baseCSSPrefix + 'grid-tree-loading',
-    expandedCls: Ext.baseCSSPrefix + 'grid-tree-node-expanded',
-
-    expanderSelector: '.' + Ext.baseCSSPrefix + 'tree-expander',
-    checkboxSelector: '.' + Ext.baseCSSPrefix + 'tree-checkbox',
-    expanderIconOverCls: Ext.baseCSSPrefix + 'tree-expander-over',
-
-    blockRefresh: true,
-
-    /** 
-     * @cfg {Boolean} rootVisible <tt>false</tt> to hide the root node (defaults to <tt>true</tt>)
-     */
-    rootVisible: true,
-
-    /** 
-     * @cfg {Boolean} animate <tt>true</tt> to enable animated expand/collapse (defaults to the value of {@link Ext#enableFx Ext.enableFx})
-     */
-
-    expandDuration: 250,
-    collapseDuration: 250,
-    
-    toggleOnDblClick: true,
-
-    initComponent: function() {
-        var me = this;
-        
-        if (me.initialConfig.animate === undefined) {
-            me.animate = Ext.enableFx;
-        }
-        
-        me.store = Ext.create('Ext.data.NodeStore', {
-            recursive: true,
-            rootVisible: me.rootVisible,
-            listeners: {
-                beforeexpand: me.onBeforeExpand,
-                expand: me.onExpand,
-                beforecollapse: me.onBeforeCollapse,
-                collapse: me.onCollapse,
-                scope: me
-            }
-        });
-        
-        if (me.node) {
-            me.setRootNode(me.node);
+Ext.define('Ext.view.View', {
+    extend: 'Ext.view.AbstractView',
+    alternateClassName: 'Ext.DataView',
+    alias: 'widget.dataview',
+
+    inheritableStatics: {
+        EventMap: {
+            mousedown: 'MouseDown',
+            mouseup: 'MouseUp',
+            click: 'Click',
+            dblclick: 'DblClick',
+            contextmenu: 'ContextMenu',
+            mouseover: 'MouseOver',
+            mouseout: 'MouseOut',
+            mouseenter: 'MouseEnter',
+            mouseleave: 'MouseLeave',
+            keydown: 'KeyDown',
+            focus: 'Focus'
         }
-        me.animQueue = {};
-        me.callParent(arguments);
-    },
-    
-    onClear: function(){
-        this.store.removeAll();    
     },
 
-    setRootNode: function(node) {
-        var me = this;        
-        me.store.setNode(node);
-        me.node = node;
-        if (!me.rootVisible) {
-            node.expand();
-        }
+    addCmpEvents: function() {
+        this.addEvents(
+            /**
+             * @event beforeitemmousedown
+             * Fires before the mousedown event on an item is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforeitemmousedown',
+            /**
+             * @event beforeitemmouseup
+             * Fires before the mouseup event on an item is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforeitemmouseup',
+            /**
+             * @event beforeitemmouseenter
+             * Fires before the mouseenter event on an item is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforeitemmouseenter',
+            /**
+             * @event beforeitemmouseleave
+             * Fires before the mouseleave event on an item is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforeitemmouseleave',
+            /**
+             * @event beforeitemclick
+             * Fires before the click event on an item is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforeitemclick',
+            /**
+             * @event beforeitemdblclick
+             * Fires before the dblclick event on an item is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforeitemdblclick',
+            /**
+             * @event beforeitemcontextmenu
+             * Fires before the contextmenu event on an item is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforeitemcontextmenu',
+            /**
+             * @event beforeitemkeydown
+             * Fires before the keydown event on an item is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object. Use {@link Ext.EventObject#getKey getKey()} to retrieve the key that was pressed.
+             */
+            'beforeitemkeydown',
+            /**
+             * @event itemmousedown
+             * Fires when there is a mouse down on an item
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'itemmousedown',
+            /**
+             * @event itemmouseup
+             * Fires when there is a mouse up on an item
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'itemmouseup',
+            /**
+             * @event itemmouseenter
+             * Fires when the mouse enters an item.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'itemmouseenter',
+            /**
+             * @event itemmouseleave
+             * Fires when the mouse leaves an item.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'itemmouseleave',
+            /**
+             * @event itemclick
+             * Fires when an item is clicked.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'itemclick',
+            /**
+             * @event itemdblclick
+             * Fires when an item is double clicked.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'itemdblclick',
+            /**
+             * @event itemcontextmenu
+             * Fires when an item is right clicked.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'itemcontextmenu',
+            /**
+             * @event itemkeydown
+             * Fires when a key is pressed while an item is currently selected.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object. Use {@link Ext.EventObject#getKey getKey()} to retrieve the key that was pressed.
+             */
+            'itemkeydown',
+            /**
+             * @event beforecontainermousedown
+             * Fires before the mousedown event on the container is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforecontainermousedown',
+            /**
+             * @event beforecontainermouseup
+             * Fires before the mouseup event on the container is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforecontainermouseup',
+            /**
+             * @event beforecontainermouseover
+             * Fires before the mouseover event on the container is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforecontainermouseover',
+            /**
+             * @event beforecontainermouseout
+             * Fires before the mouseout event on the container is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforecontainermouseout',
+            /**
+             * @event beforecontainerclick
+             * Fires before the click event on the container is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforecontainerclick',
+            /**
+             * @event beforecontainerdblclick
+             * Fires before the dblclick event on the container is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforecontainerdblclick',
+            /**
+             * @event beforecontainercontextmenu
+             * Fires before the contextmenu event on the container is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforecontainercontextmenu',
+            /**
+             * @event beforecontainerkeydown
+             * Fires before the keydown event on the container is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object. Use {@link Ext.EventObject#getKey getKey()} to retrieve the key that was pressed.
+             */
+            'beforecontainerkeydown',
+            /**
+             * @event containermouseup
+             * Fires when there is a mouse up on the container
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'containermouseup',
+            /**
+             * @event containermouseover
+             * Fires when you move the mouse over the container.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'containermouseover',
+            /**
+             * @event containermouseout
+             * Fires when you move the mouse out of the container.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'containermouseout',
+            /**
+             * @event containerclick
+             * Fires when the container is clicked.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'containerclick',
+            /**
+             * @event containerdblclick
+             * Fires when the container is double clicked.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'containerdblclick',
+            /**
+             * @event containercontextmenu
+             * Fires when the container is right clicked.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'containercontextmenu',
+            /**
+             * @event containerkeydown
+             * Fires when a key is pressed while the container is focused, and no item is currently selected.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object. Use {@link Ext.EventObject#getKey getKey()} to retrieve the key that was pressed.
+             */
+            'containerkeydown',
+
+            /**
+             * @event selectionchange
+             * Fires when the selected nodes change. Relayed event from the underlying selection model.
+             * @param {Ext.view.View} this
+             * @param {HTMLElement[]} selections Array of the selected nodes
+             */
+            'selectionchange',
+            /**
+             * @event beforeselect
+             * Fires before a selection is made. If any handlers return false, the selection is cancelled.
+             * @param {Ext.view.View} this
+             * @param {HTMLElement} node The node to be selected
+             * @param {HTMLElement[]} selections Array of currently selected nodes
+             */
+            'beforeselect'
+        );
     },
-    
-    onRender: function() {
+    // private
+    afterRender: function(){
         var me = this,
-            opts = {delegate: me.expanderSelector},
-            el;
+            listeners;
 
-        me.callParent(arguments);
+        me.callParent();
 
-        el = me.el;
-        el.on({
-            scope: me,
-            delegate: me.expanderSelector,
-            mouseover: me.onExpanderMouseOver,
-            mouseout: me.onExpanderMouseOut
-        });
-        el.on({
+        listeners = {
             scope: me,
-            delegate: me.checkboxSelector,
-            click: me.onCheckboxChange
-        });
-    },
+            /*
+             * We need to make copies of this since some of the events fired here will end up triggering
+             * a new event to be called and the shared event object will be mutated. In future we should
+             * investigate if there are any issues with creating a new event object for each event that
+             * is fired.
+             */
+            freezeEvent: true,
+            click: me.handleEvent,
+            mousedown: me.handleEvent,
+            mouseup: me.handleEvent,
+            dblclick: me.handleEvent,
+            contextmenu: me.handleEvent,
+            mouseover: me.handleEvent,
+            mouseout: me.handleEvent,
+            keydown: me.handleEvent
+        };
 
-    onCheckboxChange: function(e, t) {
-        var item = e.getTarget(this.getItemSelector(), this.getTargetEl()),
-            record, value;
-            
-        if (item) {
-            record = this.getRecord(item);
-            value = !record.get('checked');
-            record.set('checked', value);
-            this.fireEvent('checkchange', record, value);
-        }
-    },
+        me.mon(me.getTargetEl(), listeners);
 
-    getChecked: function() {
-        var checked = [];
-        this.node.cascadeBy(function(rec){
-            if (rec.get('checked')) {
-                checked.push(rec);
-            }
-        });
-        return checked;
-    },
-    
-    isItemChecked: function(rec){
-        return rec.get('checked');
+        if (me.store) {
+            me.bindStore(me.store, true);
+        }
     },
 
-    createAnimWrap: function(record, index) {
-        var thHtml = '',
-            headerCt = this.panel.headerCt,
-            headers = headerCt.getGridColumns(),
-            i = 0, len = headers.length, item,
-            node = this.getNode(record),
-            tmpEl, nodeEl;
-
-        for (; i < len; i++) {
-            item = headers[i];
-            thHtml += '<th style="width: ' + (item.hidden ? 0 : item.getDesiredWidth()) + 'px; height: 0px;"></th>';
+    handleEvent: function(e) {
+        if (this.processUIEvent(e) !== false) {
+            this.processSpecialEvent(e);
         }
-
-        nodeEl = Ext.get(node);        
-        tmpEl = nodeEl.insertSibling({
-            tag: 'tr',
-            html: [
-                '<td colspan="' + headerCt.getColumnCount() + '">',
-                    '<div class="' + Ext.baseCSSPrefix + 'tree-animator-wrap' + '">',
-                        '<table class="' + Ext.baseCSSPrefix + 'grid-table" style="width: ' + headerCt.getFullWidth() + 'px;"><tbody>',
-                            thHtml,
-                        '</tbody></table>',
-                    '</div>',
-                '</td>'
-            ].join('')
-        }, 'after');
-
-        return {
-            record: record,
-            node: node,
-            el: tmpEl,
-            expanding: false,
-            collapsing: false,
-            animating: false,
-            animateEl: tmpEl.down('div'),
-            targetEl: tmpEl.down('tbody')
-        };
     },
 
-    getAnimWrap: function(parent) {
-        if (!this.animate) {
-            return null;
-        }
+    // Private template method
+    processItemEvent: Ext.emptyFn,
+    processContainerEvent: Ext.emptyFn,
+    processSpecialEvent: Ext.emptyFn,
 
-        // We are checking to see which parent is having the animation wrap
-        while (parent) {
-            if (parent.animWrap) {
-                return parent.animWrap;
-            }
-            parent = parent.parentNode;
+    /*
+     * Returns true if this mouseover/out event is still over the overItem.
+     */
+    stillOverItem: function (event, overItem) {
+        var nowOver;
+
+        // There is this weird bug when you hover over the border of a cell it is saying
+        // the target is the table.
+        // BrowserBug: IE6 & 7. If me.mouseOverItem has been removed and is no longer
+        // in the DOM then accessing .offsetParent will throw an "Unspecified error." exception.
+        // typeof'ng and checking to make sure the offsetParent is an object will NOT throw
+        // this hard exception.
+        if (overItem && typeof(overItem.offsetParent) === "object") {
+            // mouseout : relatedTarget == nowOver, target == wasOver
+            // mouseover: relatedTarget == wasOver, target == nowOver
+            nowOver = (event.type == 'mouseout') ? event.getRelatedTarget() : event.getTarget();
+            return Ext.fly(overItem).contains(nowOver);
         }
-        return null;
+
+        return false;
     },
 
-    doAdd: function(nodes, records, index) {
-        // If we are adding records which have a parent that is currently expanding
-        // lets add them to the animation wrap
+    processUIEvent: function(e) {
         var me = this,
-            record = records[0],
-            parent = record.parentNode,
-            a = me.all.elements,
-            relativeIndex = 0,
-            animWrap = me.getAnimWrap(parent),
-            targetEl, children, len;
-
-        if (!animWrap || !animWrap.expanding) {
-            me.resetScrollers();
-            return me.callParent(arguments);
-        }
+            item = e.getTarget(me.getItemSelector(), me.getTargetEl()),
+            map = this.statics().EventMap,
+            index, record,
+            type = e.type,
+            overItem = me.mouseOverItem,
+            newType;
+
+        if (!item) {
+            if (type == 'mouseover' && me.stillOverItem(e, overItem)) {
+                item = overItem;
+            }
 
-        // We need the parent that has the animWrap, not the nodes parent
-        parent = animWrap.record;
-        
-        // If there is an anim wrap we do our special magic logic
-        targetEl = animWrap.targetEl;
-        children = targetEl.dom.childNodes;
-        
-        // We subtract 1 from the childrens length because we have a tr in there with the th'es
-        len = children.length - 1;
-        
-        // The relative index is the index in the full flat collection minus the index of the wraps parent
-        relativeIndex = index - me.indexOf(parent) - 1;
-        
-        // If we are adding records to the wrap that have a higher relative index then there are currently children
-        // it means we have to append the nodes to the wrap
-        if (!len || relativeIndex >= len) {
-            targetEl.appendChild(nodes);
-        }
-        // If there are already more children then the relative index it means we are adding child nodes of
-        // some expanded node in the anim wrap. In this case we have to insert the nodes in the right location
-        else {
-            // +1 because of the tr with th'es that is already there
-            Ext.fly(children[relativeIndex + 1]).insertSibling(nodes, 'before', true);
+            // Try to get the selected item to handle the keydown event, otherwise we'll just fire a container keydown event
+            if (type == 'keydown') {
+                record = me.getSelectionModel().getLastSelected();
+                if (record) {
+                    item = me.getNode(record);
+                }
+            }
         }
 
-        // We also have to update the CompositeElementLite collection of the DataView
-        Ext.Array.insert(a, index, nodes);
-        
-        // If we were in an animation we need to now change the animation
-        // because the targetEl just got higher.
-        if (animWrap.isAnimating) {
-            me.onExpand(parent);
-        }
-    },
-    
-    doRemove: function(record, index) {
-        // If we are adding records which have a parent that is currently expanding
-        // lets add them to the animation wrap
-        var me = this,
-            parent = record.parentNode,
-            all = me.all,
-            animWrap = me.getAnimWrap(record),
-            node = all.item(index).dom;
-
-        if (!animWrap || !animWrap.collapsing) {
-            me.resetScrollers();
-            return me.callParent(arguments);
-        }
+        if (item) {
+            index = me.indexOf(item);
+            if (!record) {
+                record = me.getRecord(item);
+            }
 
-        animWrap.targetEl.appendChild(node);
-        all.removeElement(index);
-    },
+            if (me.processItemEvent(record, item, index, e) === false) {
+                return false;
+            }
 
-    onBeforeExpand: function(parent, records, index) {
-        var me = this,
-            animWrap;
-            
-        if (!me.rendered || !me.animate) {
-            return;
-        }
+            newType = me.isNewItemEvent(item, e);
+            if (newType === false) {
+                return false;
+            }
 
-        if (me.getNode(parent)) {
-            animWrap = me.getAnimWrap(parent);
-            if (!animWrap) {
-                animWrap = parent.animWrap = me.createAnimWrap(parent);
-                animWrap.animateEl.setHeight(0);
+            if (
+                (me['onBeforeItem' + map[newType]](record, item, index, e) === false) ||
+                (me.fireEvent('beforeitem' + newType, me, record, item, index, e) === false) ||
+                (me['onItem' + map[newType]](record, item, index, e) === false)
+            ) {
+                return false;
             }
-            else if (animWrap.collapsing) {
-                // If we expand this node while it is still expanding then we
-                // have to remove the nodes from the animWrap.
-                animWrap.targetEl.select(me.itemSelector).remove();
-            } 
-            animWrap.expanding = true;
-            animWrap.collapsing = false;
-        }
-    },
 
-    onExpand: function(parent) {
-        var me = this,
-            queue = me.animQueue,
-            id = parent.getId(),
-            animWrap,
-            animateEl, 
-            targetEl,
-            queueItem;        
-        
-        if (me.singleExpand) {
-            me.ensureSingleExpand(parent);
+            me.fireEvent('item' + newType, me, record, item, index, e);
         }
-        
-        animWrap = me.getAnimWrap(parent);
+        else {
+            if (
+                (me.processContainerEvent(e) === false) ||
+                (me['onBeforeContainer' + map[type]](e) === false) ||
+                (me.fireEvent('beforecontainer' + type, me, e) === false) ||
+                (me['onContainer' + map[type]](e) === false)
+            ) {
+                return false;
+            }
 
-        if (!animWrap) {
-            me.resetScrollers();
-            return;
+            me.fireEvent('container' + type, me, e);
         }
-        
-        animateEl = animWrap.animateEl;
-        targetEl = animWrap.targetEl;
-
-        animateEl.stopAnimation();
-        // @TODO: we are setting it to 1 because quirks mode on IE seems to have issues with 0
-        queue[id] = true;
-        animateEl.slideIn('t', {
-            duration: me.expandDuration,
-            listeners: {
-                scope: me,
-                lastframe: function() {
-                    // Move all the nodes out of the anim wrap to their proper location
-                    animWrap.el.insertSibling(targetEl.query(me.itemSelector), 'before');
-                    animWrap.el.remove();
-                    me.resetScrollers();
-                    delete animWrap.record.animWrap;
-                    delete queue[id];
-                }
-            }
-        });
-        
-        animWrap.isAnimating = true;
-    },
-    
-    resetScrollers: function(){
-        var panel = this.panel;
-        
-        panel.determineScrollbars();
-        panel.invalidateScroller();
+
+        return true;
     },
 
-    onBeforeCollapse: function(parent, records, index) {
+    isNewItemEvent: function (item, e) {
         var me = this,
-            animWrap;
-            
-        if (!me.rendered || !me.animate) {
-            return;
-        }
+            overItem = me.mouseOverItem,
+            type = e.type;
 
-        if (me.getNode(parent)) {
-            animWrap = me.getAnimWrap(parent);
-            if (!animWrap) {
-                animWrap = parent.animWrap = me.createAnimWrap(parent, index);
-            }
-            else if (animWrap.expanding) {
-                // If we collapse this node while it is still expanding then we
-                // have to remove the nodes from the animWrap.
-                animWrap.targetEl.select(this.itemSelector).remove();
-            }
-            animWrap.expanding = false;
-            animWrap.collapsing = true;
+        switch (type) {
+            case 'mouseover':
+                if (item === overItem) {
+                    return false;
+                }
+                me.mouseOverItem = item;
+                return 'mouseenter';
+
+            case 'mouseout':
+                // If the currently mouseovered item contains the mouseover target, it's *NOT* a mouseleave
+                if (me.stillOverItem(e, overItem)) {
+                    return false;
+                }
+                me.mouseOverItem = null;
+                return 'mouseleave';
         }
+        return type;
     },
-    
-    onCollapse: function(parent) {
-        var me = this,
-            queue = me.animQueue,
-            id = parent.getId(),
-            animWrap = me.getAnimWrap(parent),
-            animateEl, targetEl;
-
-        if (!animWrap) {
-            me.resetScrollers();
-            return;
+
+    // private
+    onItemMouseEnter: function(record, item, index, e) {
+        if (this.trackOver) {
+            this.highlightItem(item);
         }
-        
-        animateEl = animWrap.animateEl;
-        targetEl = animWrap.targetEl;
-
-        queue[id] = true;
-        
-        // @TODO: we are setting it to 1 because quirks mode on IE seems to have issues with 0
-        animateEl.stopAnimation();
-        animateEl.slideOut('t', {
-            duration: me.collapseDuration,
-            listeners: {
-                scope: me,
-                lastframe: function() {
-                    animWrap.el.remove();
-                    delete animWrap.record.animWrap;
-                    me.resetScrollers();
-                    delete queue[id];
-                }             
-            }
-        });
-        animWrap.isAnimating = true;
     },
-    
-    /**
-     * Checks if a node is currently undergoing animation
-     * @private
-     * @param {Ext.data.Model} node The node
-     * @return {Boolean} True if the node is animating
-     */
-    isAnimating: function(node) {
-        return !!this.animQueue[node.getId()];    
-    },
-    
-    collectData: function(records) {
-        var data = this.callParent(arguments),
-            rows = data.rows,
-            len = rows.length,
-            i = 0,
-            row, record;
-            
-        for (; i < len; i++) {
-            row = rows[i];
-            record = records[i];
-            if (record.get('qtip')) {
-                row.rowAttr = 'data-qtip="' + record.get('qtip') + '"';
-                if (record.get('qtitle')) {
-                    row.rowAttr += ' ' + 'data-qtitle="' + record.get('qtitle') + '"';
-                }
-            }
-            if (record.isExpanded()) {
-                row.rowCls = (row.rowCls || '') + ' ' + this.expandedCls;
-            }
-            if (record.isLoading()) {
-                row.rowCls = (row.rowCls || '') + ' ' + this.loadingCls;
-            }
+
+    // private
+    onItemMouseLeave : function(record, item, index, e) {
+        if (this.trackOver) {
+            this.clearHighlight();
         }
-        
-        return data;
-    },
-    
-    /**
-     * Expand a record that is loaded in the view.
-     * @param {Ext.data.Model} record The record to expand
-     * @param {Boolean} deep (optional) True to expand nodes all the way down the tree hierarchy.
-     * @param {Function} callback (optional) The function to run after the expand is completed
-     * @param {Object} scope (optional) The scope of the callback function.
-     */
-    expand: function(record, deep, callback, scope) {
-        return record.expand(deep, callback, scope);
     },
-    
-    /**
-     * Collapse a record that is loaded in the view.
-     * @param {Ext.data.Model} record The record to collapse
-     * @param {Boolean} deep (optional) True to collapse nodes all the way up the tree hierarchy.
-     * @param {Function} callback (optional) The function to run after the collapse is completed
-     * @param {Object} scope (optional) The scope of the callback function.
+
+    // @private, template methods
+    onItemMouseDown: Ext.emptyFn,
+    onItemMouseUp: Ext.emptyFn,
+    onItemFocus: Ext.emptyFn,
+    onItemClick: Ext.emptyFn,
+    onItemDblClick: Ext.emptyFn,
+    onItemContextMenu: Ext.emptyFn,
+    onItemKeyDown: Ext.emptyFn,
+    onBeforeItemMouseDown: Ext.emptyFn,
+    onBeforeItemMouseUp: Ext.emptyFn,
+    onBeforeItemFocus: Ext.emptyFn,
+    onBeforeItemMouseEnter: Ext.emptyFn,
+    onBeforeItemMouseLeave: Ext.emptyFn,
+    onBeforeItemClick: Ext.emptyFn,
+    onBeforeItemDblClick: Ext.emptyFn,
+    onBeforeItemContextMenu: Ext.emptyFn,
+    onBeforeItemKeyDown: Ext.emptyFn,
+
+    // @private, template methods
+    onContainerMouseDown: Ext.emptyFn,
+    onContainerMouseUp: Ext.emptyFn,
+    onContainerMouseOver: Ext.emptyFn,
+    onContainerMouseOut: Ext.emptyFn,
+    onContainerClick: Ext.emptyFn,
+    onContainerDblClick: Ext.emptyFn,
+    onContainerContextMenu: Ext.emptyFn,
+    onContainerKeyDown: Ext.emptyFn,
+    onBeforeContainerMouseDown: Ext.emptyFn,
+    onBeforeContainerMouseUp: Ext.emptyFn,
+    onBeforeContainerMouseOver: Ext.emptyFn,
+    onBeforeContainerMouseOut: Ext.emptyFn,
+    onBeforeContainerClick: Ext.emptyFn,
+    onBeforeContainerDblClick: Ext.emptyFn,
+    onBeforeContainerContextMenu: Ext.emptyFn,
+    onBeforeContainerKeyDown: Ext.emptyFn,
+
+    /**
+     * Highlights a given item in the DataView. This is called by the mouseover handler if {@link #overItemCls}
+     * and {@link #trackOver} are configured, but can also be called manually by other code, for instance to
+     * handle stepping through the list via keyboard navigation.
+     * @param {HTMLElement} item The item to highlight
      */
-    collapse: function(record, deep, callback, scope) {
-        return record.collapse(deep, callback, scope);
+    highlightItem: function(item) {
+        var me = this;
+        me.clearHighlight();
+        me.highlightedItem = item;
+        Ext.fly(item).addCls(me.overItemCls);
     },
-    
-    /**
-     * Toggle a record between expanded and collapsed.
-     * @param {Ext.data.Record} recordInstance
+
+    /**
+     * Un-highlights the currently highlighted item, if any.
      */
-    toggle: function(record) {
-        this[record.isExpanded() ? 'collapse' : 'expand'](record);
-    },
-    
-    onItemDblClick: function(record, item, index) {
-        this.callParent(arguments);
-        if (this.toggleOnDblClick) {
-            this.toggle(record);
-        }
-    },
-    
-    onBeforeItemMouseDown: function(record, item, index, e) {
-        if (e.getTarget(this.expanderSelector, item)) {
-            return false;
-        }
-        return this.callParent(arguments);
-    },
-    
-    onItemClick: function(record, item, index, e) {
-        if (e.getTarget(this.expanderSelector, item)) {
-            this.toggle(record);
-            return false;
+    clearHighlight: function() {
+        var me = this,
+            highlighted = me.highlightedItem;
+
+        if (highlighted) {
+            Ext.fly(highlighted).removeCls(me.overItemCls);
+            delete me.highlightedItem;
         }
-        return this.callParent(arguments);
-    },
-    
-    onExpanderMouseOver: function(e, t) {
-        e.getTarget(this.cellSelector, 10, true).addCls(this.expanderIconOverCls);
-    },
-    
-    onExpanderMouseOut: function(e, t) {
-        e.getTarget(this.cellSelector, 10, true).removeCls(this.expanderIconOverCls);
     },
-    
-    /**
-     * Gets the base TreeStore from the bound TreePanel.
-     */
-    getTreeStore: function() {
-        return this.panel.store;
-    },    
-    
-    ensureSingleExpand: function(node) {
-        var parent = node.parentNode;
-        if (parent) {
-            parent.eachChild(function(child) {
-                if (child !== node && child.isExpanded()) {
-                    child.collapse();
-                }
-            });
+
+    refresh: function() {
+        var me = this;
+        me.clearHighlight();
+        me.callParent(arguments);
+        if (!me.isFixedHeight()) {
+            me.doComponentLayout();
         }
     }
 });