/*
This file is part of Ext JS 4.2
Copyright (c) 2011-2013 Sencha Inc
Contact: http://www.sencha.com/contact
GNU General Public License Usage
This file may be used under the terms of the GNU General Public License version 3.0 as
published by the Free Software Foundation and appearing in the file LICENSE included in the
packaging of this file.
Please review the following information to ensure the GNU General Public License version 3.0
requirements will be met: http://www.gnu.org/copyleft/gpl.html.
If you are unsure which license is appropriate for your use, please contact the sales department
at http://www.sencha.com/contact.
Build date: 2013-05-16 14:36:50 (f9be68accb407158ba2b1be2c226a6ce1f649314)
*/
/**
* 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 View 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.view.View', {
extend: 'Ext.view.AbstractView',
alternateClassName: 'Ext.DataView',
alias: 'widget.dataview',
// private delay to buffer row highlighting/unhighlighting on mouse move.
// This is ignored if the public mouseOverOutBuffer remains a non-zero value
deferHighlight: Ext.isIE7m ? 100 : 0,
/**
* @cfg {Number} [mouseOverOutBuffer=20]
* The number of milliseconds to buffer mouseover and mouseout event handling on view items.
*
* Configure this as `false` to process mouseover and mouseout events immediately.
*/
mouseOverOutBuffer: 20,
inputTagRe: /^textarea$|^input$/i,
inheritableStatics: {
EventMap: {
mousedown: 'MouseDown',
mouseup: 'MouseUp',
click: 'Click',
dblclick: 'DblClick',
contextmenu: 'ContextMenu',
mouseover: 'MouseOver',
mouseout: 'MouseOut',
mouseenter: 'MouseEnter',
mouseleave: 'MouseLeave',
keydown: 'KeyDown',
focus: 'Focus'
}
},
initComponent: function() {
var me = this;
me.callParent();
//
if (me.mouseOverOutBuffer) {
me.handleMouseOverOrOut =
Ext.Function.createBuffered(me.handleMouseOverOrOut, me.mouseOverOutBuffer, me);
me.lastMouseOverOutEvent = new Ext.EventObjectImpl();
}
// Not buffering mouse over/out handling - buffer item highlighting.
else if (me.deferHighlight){
me.setHighlightedItem =
Ext.Function.createBuffered(me.setHighlightedItem, me.deferHighlight, me);
}
},
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
* @inheritdoc Ext.selection.DataViewModel#selectionchange
*/
'selectionchange',
/**
* @event
* @inheritdoc Ext.selection.DataViewModel#beforeselect
*/
'beforeselect',
/**
* @event
* @inheritdoc Ext.selection.DataViewModel#beforedeselect
*/
'beforedeselect',
/**
* @event
* @inheritdoc Ext.selection.DataViewModel#select
*/
'select',
/**
* @event
* @inheritdoc Ext.selection.DataViewModel#deselect
*/
'deselect',
/**
* @event
* @inheritdoc Ext.selection.DataViewModel#focuschange
*/
'focuschange',
/**
* @event highlightitem
* Fires when a node is highlighted using keyboard navigation, or mouseover.
* @param {Ext.view.View} view This View Component.
* @param {Ext.Element} node The highlighted node.
*/
'highlightitem',
/**
* @event unhighlightitem
* Fires when a node is unhighlighted using keyboard navigation, or mouseout.
* @param {Ext.view.View} view This View Component.
* @param {Ext.Element} node The previously highlighted node.
*/
'unhighlightitem'
);
},
getFocusEl: function() {
return this.getTargetEl();
},
// @private
afterRender: function(){
var me = this,
onMouseOverOut = me.mouseOverOutBuffer ? me.onMouseOverOut : me.handleMouseOverOrOut;
me.callParent();
me.mon(me.getTargetEl(), {
scope: me,
/*
* 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,
keydown: me.handleEvent,
mouseover: onMouseOverOut,
mouseout: onMouseOverOut
});
},
onMouseOverOut: function(e) {
var me = this;
// Determining if we are entering or leaving view items is deferred until
// mouse move churn settles down.
me.lastMouseOverOutEvent.setEvent(e.browserEvent, true);
me.handleMouseOverOrOut(me.lastMouseOverOutEvent);
},
handleMouseOverOrOut: function(e) {
var me = this,
isMouseout = e.type === 'mouseout',
method = isMouseout ? e.getRelatedTarget : e.getTarget,
nowOverItem = method.call(e, me.itemSelector) || method.call(e, me.dataRowSelector);
// If the mouse event of whatever type tells use that we are no longer over the current mouseOverItem...
if (!me.mouseOverItem || nowOverItem !== me.mouseOverItem) {
// First fire mouseleave for the item we just left
if (me.mouseOverItem) {
e.item = me.mouseOverItem;
e.newType = 'mouseleave';
me.handleEvent(e);
}
// If we are over an item, fire the mouseenter
me.mouseOverItem = nowOverItem;
if (me.mouseOverItem) {
e.item = me.mouseOverItem;
e.newType = 'mouseenter';
me.handleEvent(e);
}
}
},
handleEvent: function(e) {
var me = this,
key = e.type == 'keydown' && e.getKey();
if (me.processUIEvent(e) !== false) {
me.processSpecialEvent(e);
}
// After all listeners have processed the event, then unless the user is typing into an input field,
// prevent browser's default action on SPACE which is to focus the event's target element.
// Focusing causes the browser to attempt to scroll the element into view.
if (key === e.SPACE) {
if (!me.inputTagRe.test(e.getTarget().tagName)) {
e.stopEvent();
}
}
},
// Private template method
processItemEvent: Ext.emptyFn,
processContainerEvent: Ext.emptyFn,
processSpecialEvent: Ext.emptyFn,
processUIEvent: function(e) {
// If the target event has been removed from the body (data update causing view DOM to be updated),
// do not process. isAncestor uses native methods to check.
if (!Ext.getBody().isAncestor(e.target)) {
return;
}
var me = this,
item = e.getTarget(me.getItemSelector(), me.getTargetEl()),
map = this.statics().EventMap,
index, record,
type = e.type,
newType = e.type,
sm;
// If the event is a mouseover/mouseout event converted to a mouseenter/mouseleave,
// use that event type and ensure that the item is correct.
if (e.newType) {
newType = e.newType;
item = e.item;
}
// For keydown events, try to get either the last focused item or the selected item.
// If we have not focused an item, we'll just fire a container keydown event.
if (!item && type == 'keydown') {
sm = me.getSelectionModel();
record = sm.lastFocused || sm.getLastSelected();
if (record) {
item = me.getNode(record, true);
}
}
if (item) {
if (!record) {
record = me.getRecord(item);
}
index = me.indexInStore ? me.indexInStore(record) : me.indexOf(item);
// It is possible for an event to arrive for which there is no record... this
// can happen with dblclick where the clicks are on removal actions (think a
// grid w/"delete row" action column)
if (!record || me.processItemEvent(record, item, index, e) === false) {
return false;
}
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;
}
me.fireEvent('item' + newType, me, record, item, index, e);
}
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;
}
me.fireEvent('container' + type, me, e);
}
return true;
},
// @private
onItemMouseEnter: function(record, item, index, e) {
if (this.trackOver) {
this.highlightItem(item);
}
},
// @private
onItemMouseLeave : function(record, item, index, e) {
if (this.trackOver) {
this.clearHighlight();
}
},
// 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,
// @private
setHighlightedItem: function(item){
var me = this,
highlighted = me.highlightedItem,
overItemCls = me.overItemCls,
beforeOverItemCls = me.beforeOverItemCls,
previous;
if (highlighted != item){
if (highlighted) {
Ext.fly(highlighted).removeCls(overItemCls);
previous = highlighted.previousSibling;
if (beforeOverItemCls && previous) {
Ext.fly(previous).removeCls(beforeOverItemCls);
}
me.fireEvent('unhighlightitem', me, highlighted);
}
me.highlightedItem = item;
if (item) {
Ext.fly(item).addCls(me.overItemCls);
previous = item.previousSibling;
if (beforeOverItemCls && previous) {
Ext.fly(previous).addCls(beforeOverItemCls);
}
me.fireEvent('highlightitem', me, item);
}
}
},
/**
* Highlights a given item in the View. 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
*/
highlightItem: function(item) {
this.setHighlightedItem(item);
},
/**
* Un-highlights the currently highlighted item, if any.
*/
clearHighlight: function() {
this.setHighlightedItem(undefined);
},
onUpdate: function(store, record){
var me = this,
node,
newNode,
highlighted;
if (me.viewReady) {
node = me.getNode(record);
newNode = me.callParent(arguments);
highlighted = me.highlightedItem;
if (highlighted && highlighted === node) {
delete me.highlightedItem;
if (newNode) {
me.highlightItem(newNode);
}
}
}
},
refresh: function() {
this.clearHighlight();
this.callParent(arguments);
},
/**
* Focuses a node in the view.
* @param {Ext.data.Model} rec The record associated to the node that is to be focused.
*/
focusNode: function(rec){
var me = this,
node = me.getNode(rec, true),
el = me.el,
adjustmentY = 0,
adjustmentX = 0,
elRegion = el.getRegion(),
nodeRegion;
// Viewable region must not include scrollbars, so use
// DOM client dimensions
elRegion.bottom = elRegion.top + el.dom.clientHeight;
elRegion.right = elRegion.left + el.dom.clientWidth;
if (node) {
nodeRegion = Ext.fly(node).getRegion();
// node is above
if (nodeRegion.top < elRegion.top) {
adjustmentY = nodeRegion.top - elRegion.top;
// node is below
} else if (nodeRegion.bottom > elRegion.bottom) {
adjustmentY = nodeRegion.bottom - elRegion.bottom;
}
// node is left
if (nodeRegion.left < elRegion.left) {
adjustmentX = nodeRegion.left - elRegion.left;
// node is right
} else if (nodeRegion.right > elRegion.right) {
adjustmentX = nodeRegion.right - elRegion.right;
}
if (adjustmentX || adjustmentY) {
me.scrollBy(adjustmentX, adjustmentY, false);
}
el.focus();
}
}
});
|