<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>The source code</title>
- <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
- <script type="text/javascript" src="../prettify/prettify.js"></script>
+ <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>
</head>
<body onload="prettyPrint(); highlight();">
- <pre class="prettyprint lang-js"><span id='Ext-container-Container'>/**
-</span> * @class Ext.container.Container
- * @extends Ext.container.AbstractContainer
- * <p>Base class for any {@link Ext.Component} that may contain other Components. Containers handle the
- * basic behavior of containing items, namely adding, inserting and removing items.</p>
+ <pre class="prettyprint lang-js"><span id='Ext-grid-header-Container'>/**
+</span> * @class Ext.grid.header.Container
+ * @extends Ext.container.Container
*
- * <p>The most commonly used Container classes are {@link Ext.panel.Panel}, {@link Ext.window.Window} and {@link Ext.tab.Panel}.
- * If you do not need the capabilities offered by the aforementioned classes you can create a lightweight
- * Container to be encapsulated by an HTML element to your specifications by using the
- * <code><b>{@link Ext.Component#autoEl autoEl}</b></code> config option.</p>
- *
- * {@img Ext.Container/Ext.Container.png Ext.Container component}
- * <p>The code below illustrates how to explicitly create a Container:<pre><code>
-// explicitly create a Container
-Ext.create('Ext.container.Container', {
- layout: {
- type: 'hbox'
- },
- width: 400,
- renderTo: Ext.getBody(),
- border: 1,
- style: {borderColor:'#000000', borderStyle:'solid', borderWidth:'1px'},
- defaults: {
- labelWidth: 80,
- // implicitly create Container by specifying xtype
- xtype: 'datefield',
- flex: 1,
- style: {
- padding: '10px'
- }
- },
- items: [{
- xtype: 'datefield',
- name: 'startDate',
- fieldLabel: 'Start date'
- },{
- xtype: 'datefield',
- name: 'endDate',
- fieldLabel: 'End date'
- }]
-});
-</code></pre></p>
- *
- * <p><u><b>Layout</b></u></p>
- * <p>Container classes delegate the rendering of child Components to a layout
- * manager class which must be configured into the Container using the
- * <code><b>{@link #layout}</b></code> configuration property.</p>
- * <p>When either specifying child <code>{@link #items}</code> of a Container,
- * or dynamically {@link #add adding} Components to a Container, remember to
- * consider how you wish the Container to arrange those child elements, and
- * whether those child elements need to be sized using one of Ext's built-in
- * <b><code>{@link #layout}</code></b> schemes. By default, Containers use the
- * {@link Ext.layout.container.Auto Auto} scheme which only
- * renders child components, appending them one after the other inside the
- * Container, and <b>does not apply any sizing</b> at all.</p>
- * <p>A common mistake is when a developer neglects to specify a
- * <b><code>{@link #layout}</code></b> (e.g. widgets like GridPanels or
- * TreePanels are added to Containers for which no <code><b>{@link #layout}</b></code>
- * has been specified). If a Container is left to use the default
- * {Ext.layout.container.Auto Auto} scheme, none of its
- * child components will be resized, or changed in any way when the Container
- * is resized.</p>
- * <p>Certain layout managers allow dynamic addition of child components.
- * Those that do include {@link Ext.layout.container.Card},
- * {@link Ext.layout.container.Anchor}, {@link Ext.layout.container.VBox}, {@link Ext.layout.container.HBox}, and
- * {@link Ext.layout.container.Table}. For example:<pre><code>
-// Create the GridPanel.
-var myNewGrid = new Ext.grid.Panel({
- store: myStore,
- headers: myHeaders,
- title: 'Results', // the title becomes the title of the tab
-});
-
-myTabPanel.add(myNewGrid); // {@link Ext.tab.Panel} implicitly uses {@link Ext.layout.container.Card Card}
-myTabPanel.{@link Ext.tab.Panel#setActiveTab setActiveTab}(myNewGrid);
- * </code></pre></p>
- * <p>The example above adds a newly created GridPanel to a TabPanel. Note that
- * a TabPanel uses {@link Ext.layout.container.Card} as its layout manager which
- * means all its child items are sized to {@link Ext.layout.container.Fit fit}
- * exactly into its client area.
- * <p><b><u>Overnesting is a common problem</u></b>.
- * An example of overnesting occurs when a GridPanel is added to a TabPanel
- * by wrapping the GridPanel <i>inside</i> a wrapping Panel (that has no
- * <code><b>{@link #layout}</b></code> specified) and then add that wrapping Panel
- * to the TabPanel. The point to realize is that a GridPanel <b>is</b> a
- * Component which can be added directly to a Container. If the wrapping Panel
- * has no <code><b>{@link #layout}</b></code> configuration, then the overnested
- * GridPanel will not be sized as expected.<p>
- *
- * <p><u><b>Adding via remote configuration</b></u></p>
- *
- * <p>A server side script can be used to add Components which are generated dynamically on the server.
- * An example of adding a GridPanel to a TabPanel where the GridPanel is generated by the server
- * based on certain parameters:
- * </p><pre><code>
-// execute an Ajax request to invoke server side script:
-Ext.Ajax.request({
- url: 'gen-invoice-grid.php',
- // send additional parameters to instruct server script
- params: {
- startDate: Ext.getCmp('start-date').getValue(),
- endDate: Ext.getCmp('end-date').getValue()
- },
- // process the response object to add it to the TabPanel:
- success: function(xhr) {
- var newComponent = eval(xhr.responseText); // see discussion below
- myTabPanel.add(newComponent); // add the component to the TabPanel
- myTabPanel.setActiveTab(newComponent);
- },
- failure: function() {
- Ext.Msg.alert("Grid create failed", "Server communication failure");
- }
-});
-</code></pre>
- * <p>The server script needs to return a JSON representation of a configuration object, which, when decoded
- * will return a config object with an {@link Ext.Component#xtype xtype}. The server might return the following
- * JSON:</p><pre><code>
-{
- "xtype": 'grid',
- "title": 'Invoice Report',
- "store": {
- "model": 'Invoice',
- "proxy": {
- "type": 'ajax',
- "url": 'get-invoice-data.php',
- "reader": {
- "type": 'json'
- "record": 'transaction',
- "idProperty": 'id',
- "totalRecords": 'total'
- })
- },
- "autoLoad": {
- "params": {
- "startDate": '01/01/2008',
- "endDate": '01/31/2008'
- }
- }
- },
- "headers": [
- {"header": "Customer", "width": 250, "dataIndex": 'customer', "sortable": true},
- {"header": "Invoice Number", "width": 120, "dataIndex": 'invNo', "sortable": true},
- {"header": "Invoice Date", "width": 100, "dataIndex": 'date', "renderer": Ext.util.Format.dateRenderer('M d, y'), "sortable": true},
- {"header": "Value", "width": 120, "dataIndex": 'value', "renderer": 'usMoney', "sortable": true}
- ]
-}
-</code></pre>
- * <p>When the above code fragment is passed through the <code>eval</code> function in the success handler
- * of the Ajax request, the result will be a config object which, when added to a Container, will cause instantiation
- * of a GridPanel. <b>Be sure that the Container is configured with a layout which sizes and positions the child items to your requirements.</b></p>
- * <p>Note: since the code above is <i>generated</i> by a server script, the <code>autoLoad</code> params for
- * the Store, the user's preferred date format, the metadata to allow generation of the Model layout, and the ColumnModel
- * can all be generated into the code since these are all known on the server.</p>
+ * Container which holds headers and is docked at the top or bottom of a TablePanel.
+ * The HeaderContainer drives resizing/moving/hiding of columns within the TableView.
+ * As headers are hidden, moved or resized the headercontainer is responsible for
+ * triggering changes within the view.
*/
-Ext.define('Ext.container.Container', {
- extend: 'Ext.container.AbstractContainer',
- alias: 'widget.container',
- alternateClassName: 'Ext.Container',
-
-<span id='Ext-container-Container-method-getChildByElement'> /**
-</span> * Return the immediate child Component in which the passed element is located.
- * @param el The element to test.
- * @return {Component} The child item which contains the passed element.
- */
- getChildByElement: function(el) {
- var item,
- itemEl,
+Ext.define('Ext.grid.header.Container', {
+ extend: 'Ext.container.Container',
+ uses: [
+ 'Ext.grid.ColumnLayout',
+ 'Ext.grid.column.Column',
+ 'Ext.menu.Menu',
+ 'Ext.menu.CheckItem',
+ 'Ext.menu.Separator',
+ 'Ext.grid.plugin.HeaderResizer',
+ 'Ext.grid.plugin.HeaderReorderer'
+ ],
+ border: true,
+
+ alias: 'widget.headercontainer',
+
+ baseCls: Ext.baseCSSPrefix + 'grid-header-ct',
+ dock: 'top',
+
+<span id='Ext-grid-header-Container-cfg-weight'> /**
+</span> * @cfg {Number} weight
+ * HeaderContainer overrides the default weight of 0 for all docked items to 100.
+ * This is so that it has more priority over things like toolbars.
+ */
+ weight: 100,
+ defaultType: 'gridcolumn',
+<span id='Ext-grid-header-Container-cfg-defaultWidth'> /**
+</span> * @cfg {Number} defaultWidth
+ * Width of the header if no width or flex is specified. Defaults to 100.
+ */
+ defaultWidth: 100,
+
+
+ sortAscText: 'Sort Ascending',
+ sortDescText: 'Sort Descending',
+ sortClearText: 'Clear Sort',
+ columnsText: 'Columns',
+
+ lastHeaderCls: Ext.baseCSSPrefix + 'column-header-last',
+ firstHeaderCls: Ext.baseCSSPrefix + 'column-header-first',
+ headerOpenCls: Ext.baseCSSPrefix + 'column-header-open',
+
+ // private; will probably be removed by 4.0
+ triStateSort: false,
+
+ ddLock: false,
+
+ dragging: false,
+
+<span id='Ext-grid-header-Container-property-isGroupHeader'> /**
+</span> * <code>true</code> if this HeaderContainer is in fact a group header which contains sub headers.
+ * @type Boolean
+ * @property isGroupHeader
+ */
+
+<span id='Ext-grid-header-Container-cfg-sortable'> /**
+</span> * @cfg {Boolean} sortable
+ * Provides the default sortable state for all Headers within this HeaderContainer.
+ * Also turns on or off the menus in the HeaderContainer. Note that the menu is
+ * shared across every header and therefore turning it off will remove the menu
+ * items for every header.
+ */
+ sortable: true,
+
+ initComponent: function() {
+ var me = this;
+
+ me.headerCounter = 0;
+ me.plugins = me.plugins || [];
+
+ // TODO: Pass in configurations to turn on/off dynamic
+ // resizing and disable resizing all together
+
+ // Only set up a Resizer and Reorderer for the topmost HeaderContainer.
+ // Nested Group Headers are themselves HeaderContainers
+ if (!me.isHeader) {
+ me.resizer = Ext.create('Ext.grid.plugin.HeaderResizer');
+ me.reorderer = Ext.create('Ext.grid.plugin.HeaderReorderer');
+ if (!me.enableColumnResize) {
+ me.resizer.disable();
+ }
+ if (!me.enableColumnMove) {
+ me.reorderer.disable();
+ }
+ me.plugins.push(me.reorderer, me.resizer);
+ }
+
+ // Base headers do not need a box layout
+ if (me.isHeader && !me.items) {
+ me.layout = 'auto';
+ }
+ // HeaderContainer and Group header needs a gridcolumn layout.
+ else {
+ me.layout = {
+ type: 'gridcolumn',
+ availableSpaceOffset: me.availableSpaceOffset,
+ align: 'stretchmax',
+ resetStretch: true
+ };
+ }
+ me.defaults = me.defaults || {};
+ Ext.applyIf(me.defaults, {
+ width: me.defaultWidth,
+ triStateSort: me.triStateSort,
+ sortable: me.sortable
+ });
+ me.callParent();
+ me.addEvents(
+<span id='Ext-grid-header-Container-event-columnresize'> /**
+</span> * @event columnresize
+ * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
+ * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
+ * @param {Number} width
+ */
+ 'columnresize',
+
+<span id='Ext-grid-header-Container-event-headerclick'> /**
+</span> * @event headerclick
+ * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
+ * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
+ * @param {Ext.EventObject} e
+ * @param {HTMLElement} t
+ */
+ 'headerclick',
+
+<span id='Ext-grid-header-Container-event-headertriggerclick'> /**
+</span> * @event headertriggerclick
+ * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
+ * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
+ * @param {Ext.EventObject} e
+ * @param {HTMLElement} t
+ */
+ 'headertriggerclick',
+
+<span id='Ext-grid-header-Container-event-columnmove'> /**
+</span> * @event columnmove
+ * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
+ * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
+ * @param {Number} fromIdx
+ * @param {Number} toIdx
+ */
+ 'columnmove',
+<span id='Ext-grid-header-Container-event-columnhide'> /**
+</span> * @event columnhide
+ * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
+ * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
+ */
+ 'columnhide',
+<span id='Ext-grid-header-Container-event-columnshow'> /**
+</span> * @event columnshow
+ * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
+ * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
+ */
+ 'columnshow',
+<span id='Ext-grid-header-Container-event-sortchange'> /**
+</span> * @event sortchange
+ * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
+ * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
+ * @param {String} direction
+ */
+ 'sortchange',
+<span id='Ext-grid-header-Container-event-menucreate'> /**
+</span> * @event menucreate
+ * Fired immediately after the column header menu is created.
+ * @param {Ext.grid.header.Container} ct This instance
+ * @param {Ext.menu.Menu} menu The Menu that was created
+ */
+ 'menucreate'
+ );
+ },
+
+ onDestroy: function() {
+ Ext.destroy(this.resizer, this.reorderer);
+ this.callParent();
+ },
+
+ applyDefaults: function(config){
+ /*
+ * Ensure header.Container defaults don't get applied to a RowNumberer
+ * if an xtype is supplied. This isn't an ideal solution however it's
+ * much more likely that a RowNumberer with no options will be created,
+ * wanting to use the defaults specified on the class as opposed to
+ * those setup on the Container.
+ */
+ if (config && !config.isComponent && config.xtype == 'rownumberer') {
+ return config;
+ }
+ return this.callParent([config]);
+ },
+
+ applyColumnsState: function(columns) {
+ if (!columns || !columns.length) {
+ return;
+ }
+
+ var me = this,
+ i = 0,
+ index,
+ col;
+
+ Ext.each(columns, function (columnState) {
+ col = me.down('gridcolumn[headerId=' + columnState.id + ']');
+ if (col) {
+ index = me.items.indexOf(col);
+ if (i !== index) {
+ me.moveHeader(index, i);
+ }
+
+ if (col.applyColumnState) {
+ col.applyColumnState(columnState);
+ }
+ ++i;
+ }
+ });
+ },
+
+ getColumnsState: function () {
+ var me = this,
+ columns = [],
+ state;
+
+ me.items.each(function (col) {
+ state = col.getColumnState && col.getColumnState();
+ if (state) {
+ columns.push(state);
+ }
+ });
+
+ return columns;
+ },
+
+ // Invalidate column cache on add
+ // We cannot refresh the View on every add because this method is called
+ // when the HeaderDropZone moves Headers around, that will also refresh the view
+ onAdd: function(c) {
+ var me = this;
+ if (!c.headerId) {
+ c.headerId = c.initialConfig.id || ('h' + (++me.headerCounter));
+ }
+ //<debug warn>
+ if (Ext.global.console && Ext.global.console.warn) {
+ if (!me._usedIDs) me._usedIDs = {};
+ if (me._usedIDs[c.headerId]) {
+ Ext.global.console.warn(this.$className, 'attempted to reuse an existing id', c.headerId);
+ }
+ me._usedIDs[c.headerId] = true;
+ }
+ //</debug>
+ me.callParent(arguments);
+ me.purgeCache();
+ },
+
+ // Invalidate column cache on remove
+ // We cannot refresh the View on every remove because this method is called
+ // when the HeaderDropZone moves Headers around, that will also refresh the view
+ onRemove: function(c) {
+ var me = this;
+ me.callParent(arguments);
+ me.purgeCache();
+ },
+
+ afterRender: function() {
+ this.callParent();
+ var store = this.up('[store]').store,
+ sorters = store.sorters,
+ first = sorters.first(),
+ hd;
+
+ if (first) {
+ hd = this.down('gridcolumn[dataIndex=' + first.property +']');
+ if (hd) {
+ hd.setSortState(first.direction, false, true);
+ }
+ }
+ },
+
+ afterLayout: function() {
+ if (!this.isHeader) {
+ var me = this,
+ topHeaders = me.query('>gridcolumn:not([hidden])'),
+ viewEl,
+ firstHeaderEl,
+ lastHeaderEl;
+
+ me.callParent(arguments);
+
+ if (topHeaders.length) {
+ firstHeaderEl = topHeaders[0].el;
+ if (firstHeaderEl !== me.pastFirstHeaderEl) {
+ if (me.pastFirstHeaderEl) {
+ me.pastFirstHeaderEl.removeCls(me.firstHeaderCls);
+ }
+ firstHeaderEl.addCls(me.firstHeaderCls);
+ me.pastFirstHeaderEl = firstHeaderEl;
+ }
+
+ lastHeaderEl = topHeaders[topHeaders.length - 1].el;
+ if (lastHeaderEl !== me.pastLastHeaderEl) {
+ if (me.pastLastHeaderEl) {
+ me.pastLastHeaderEl.removeCls(me.lastHeaderCls);
+ }
+ lastHeaderEl.addCls(me.lastHeaderCls);
+ me.pastLastHeaderEl = lastHeaderEl;
+ }
+ }
+ }
+
+ },
+
+ onHeaderShow: function(header, preventLayout) {
+ // Pass up to the GridSection
+ var me = this,
+ gridSection = me.ownerCt,
+ menu = me.getMenu(),
+ topItems, topItemsVisible,
+ colCheckItem,
+ itemToEnable,
+ len, i;
+
+ if (menu) {
+
+ colCheckItem = menu.down('menucheckitem[headerId=' + header.id + ']');
+ if (colCheckItem) {
+ colCheckItem.setChecked(true, true);
+ }
+
+ // There's more than one header visible, and we've disabled some checked items... re-enable them
+ topItems = menu.query('#columnItem>menucheckitem[checked]');
+ topItemsVisible = topItems.length;
+ if ((me.getVisibleGridColumns().length > 1) && me.disabledMenuItems && me.disabledMenuItems.length) {
+ if (topItemsVisible == 1) {
+ Ext.Array.remove(me.disabledMenuItems, topItems[0]);
+ }
+ for (i = 0, len = me.disabledMenuItems.length; i < len; i++) {
+ itemToEnable = me.disabledMenuItems[i];
+ if (!itemToEnable.isDestroyed) {
+ itemToEnable[itemToEnable.menu ? 'enableCheckChange' : 'enable']();
+ }
+ }
+ if (topItemsVisible == 1) {
+ me.disabledMenuItems = topItems;
+ } else {
+ me.disabledMenuItems = [];
+ }
+ }
+ }
+
+ // Only update the grid UI when we are notified about base level Header shows;
+ // Group header shows just cause a layout of the HeaderContainer
+ if (!header.isGroupHeader) {
+ if (me.view) {
+ me.view.onHeaderShow(me, header, true);
+ }
+ if (gridSection) {
+ gridSection.onHeaderShow(me, header);
+ }
+ }
+ me.fireEvent('columnshow', me, header);
+
+ // The header's own hide suppresses cascading layouts, so lay the headers out now
+ if (preventLayout !== true) {
+ me.doLayout();
+ }
+ },
+
+ doComponentLayout: function(){
+ var me = this;
+ if (me.view && me.view.saveScrollState) {
+ me.view.saveScrollState();
+ }
+ me.callParent(arguments);
+ if (me.view && me.view.restoreScrollState) {
+ me.view.restoreScrollState();
+ }
+ },
+
+ onHeaderHide: function(header, suppressLayout) {
+ // Pass up to the GridSection
+ var me = this,
+ gridSection = me.ownerCt,
+ menu = me.getMenu(),
+ colCheckItem;
+
+ if (menu) {
+
+ // If the header was hidden programmatically, sync the Menu state
+ colCheckItem = menu.down('menucheckitem[headerId=' + header.id + ']');
+ if (colCheckItem) {
+ colCheckItem.setChecked(false, true);
+ }
+ me.setDisabledItems();
+ }
+
+ // Only update the UI when we are notified about base level Header hides;
+ if (!header.isGroupHeader) {
+ if (me.view) {
+ me.view.onHeaderHide(me, header, true);
+ }
+ if (gridSection) {
+ gridSection.onHeaderHide(me, header);
+ }
+
+ // The header's own hide suppresses cascading layouts, so lay the headers out now
+ if (!suppressLayout) {
+ me.doLayout();
+ }
+ }
+ me.fireEvent('columnhide', me, header);
+ },
+
+ setDisabledItems: function(){
+ var me = this,
+ menu = me.getMenu(),
+ i = 0,
+ len,
+ itemsToDisable,
+ itemToDisable;
+
+ // Find what to disable. If only one top level item remaining checked, we have to disable stuff.
+ itemsToDisable = menu.query('#columnItem>menucheckitem[checked]');
+ if ((itemsToDisable.length === 1)) {
+ if (!me.disabledMenuItems) {
+ me.disabledMenuItems = [];
+ }
+
+ // If down to only one column visible, also disable any descendant checkitems
+ if ((me.getVisibleGridColumns().length === 1) && itemsToDisable[0].menu) {
+ itemsToDisable = itemsToDisable.concat(itemsToDisable[0].menu.query('menucheckitem[checked]'));
+ }
+
+ len = itemsToDisable.length;
+ // Disable any further unchecking at any level.
+ for (i = 0; i < len; i++) {
+ itemToDisable = itemsToDisable[i];
+ if (!Ext.Array.contains(me.disabledMenuItems, itemToDisable)) {
+
+ // If we only want to disable check change: it might be a disabled item, so enable it prior to
+ // setting its correct disablement level.
+ itemToDisable.disabled = false;
+ itemToDisable[itemToDisable.menu ? 'disableCheckChange' : 'disable']();
+ me.disabledMenuItems.push(itemToDisable);
+ }
+ }
+ }
+ },
+
+<span id='Ext-grid-header-Container-method-tempLock'> /**
+</span> * Temporarily lock the headerCt. This makes it so that clicking on headers
+ * don't trigger actions like sorting or opening of the header menu. This is
+ * done because extraneous events may be fired on the headers after interacting
+ * with a drag drop operation.
+ * @private
+ */
+ tempLock: function() {
+ this.ddLock = true;
+ Ext.Function.defer(function() {
+ this.ddLock = false;
+ }, 200, this);
+ },
+
+ onHeaderResize: function(header, w, suppressFocus) {
+ this.tempLock();
+ if (this.view && this.view.rendered) {
+ this.view.onHeaderResize(header, w, suppressFocus);
+ }
+ },
+
+ onHeaderClick: function(header, e, t) {
+ this.fireEvent("headerclick", this, header, e, t);
+ },
+
+ onHeaderTriggerClick: function(header, e, t) {
+ // generate and cache menu, provide ability to cancel/etc
+ if (this.fireEvent("headertriggerclick", this, header, e, t) !== false) {
+ this.showMenuBy(t, header);
+ }
+ },
+
+ showMenuBy: function(t, header) {
+ var menu = this.getMenu(),
+ ascItem = menu.down('#ascItem'),
+ descItem = menu.down('#descItem'),
+ sortableMth;
+
+ menu.activeHeader = menu.ownerCt = header;
+ menu.setFloatParent(header);
+ // TODO: remove coupling to Header's titleContainer el
+ header.titleContainer.addCls(this.headerOpenCls);
+
+ // enable or disable asc & desc menu items based on header being sortable
+ sortableMth = header.sortable ? 'enable' : 'disable';
+ if (ascItem) {
+ ascItem[sortableMth]();
+ }
+ if (descItem) {
+ descItem[sortableMth]();
+ }
+ menu.showBy(t);
+ },
+
+ // remove the trigger open class when the menu is hidden
+ onMenuDeactivate: function() {
+ var menu = this.getMenu();
+ // TODO: remove coupling to Header's titleContainer el
+ menu.activeHeader.titleContainer.removeCls(this.headerOpenCls);
+ },
+
+ moveHeader: function(fromIdx, toIdx) {
+
+ // An automatically expiring lock
+ this.tempLock();
+ this.onHeaderMoved(this.move(fromIdx, toIdx), fromIdx, toIdx);
+ },
+
+ purgeCache: function() {
+ var me = this;
+ // Delete column cache - column order has changed.
+ delete me.gridDataColumns;
+ delete me.hideableColumns;
+
+ // Menu changes when columns are moved. It will be recreated.
+ if (me.menu) {
+ me.menu.destroy();
+ delete me.menu;
+ }
+ },
+
+ onHeaderMoved: function(header, fromIdx, toIdx) {
+ var me = this,
+ gridSection = me.ownerCt;
+
+ if (gridSection && gridSection.onHeaderMove) {
+ gridSection.onHeaderMove(me, header, fromIdx, toIdx);
+ }
+ me.fireEvent("columnmove", me, header, fromIdx, toIdx);
+ },
+
+<span id='Ext-grid-header-Container-method-getMenu'> /**
+</span> * Gets the menu (and will create it if it doesn't already exist)
+ * @private
+ */
+ getMenu: function() {
+ var me = this;
+
+ if (!me.menu) {
+ me.menu = Ext.create('Ext.menu.Menu', {
+ hideOnParentHide: false, // Persists when owning ColumnHeader is hidden
+ items: me.getMenuItems(),
+ listeners: {
+ deactivate: me.onMenuDeactivate,
+ scope: me
+ }
+ });
+ me.setDisabledItems();
+ me.fireEvent('menucreate', me, me.menu);
+ }
+ return me.menu;
+ },
+
+<span id='Ext-grid-header-Container-method-getMenuItems'> /**
+</span> * Returns an array of menu items to be placed into the shared menu
+ * across all headers in this header container.
+ * @returns {Array} menuItems
+ */
+ getMenuItems: function() {
+ var me = this,
+ menuItems = [],
+ hideableColumns = me.enableColumnHide ? me.getColumnMenu(me) : null;
+
+ if (me.sortable) {
+ menuItems = [{
+ itemId: 'ascItem',
+ text: me.sortAscText,
+ cls: Ext.baseCSSPrefix + 'hmenu-sort-asc',
+ handler: me.onSortAscClick,
+ scope: me
+ },{
+ itemId: 'descItem',
+ text: me.sortDescText,
+ cls: Ext.baseCSSPrefix + 'hmenu-sort-desc',
+ handler: me.onSortDescClick,
+ scope: me
+ }];
+ }
+ if (hideableColumns && hideableColumns.length) {
+ menuItems.push('-', {
+ itemId: 'columnItem',
+ text: me.columnsText,
+ cls: Ext.baseCSSPrefix + 'cols-icon',
+ menu: hideableColumns
+ });
+ }
+ return menuItems;
+ },
+
+ // sort asc when clicking on item in menu
+ onSortAscClick: function() {
+ var menu = this.getMenu(),
+ activeHeader = menu.activeHeader;
+
+ activeHeader.setSortState('ASC');
+ },
+
+ // sort desc when clicking on item in menu
+ onSortDescClick: function() {
+ var menu = this.getMenu(),
+ activeHeader = menu.activeHeader;
+
+ activeHeader.setSortState('DESC');
+ },
+
+<span id='Ext-grid-header-Container-method-getColumnMenu'> /**
+</span> * Returns an array of menu CheckItems corresponding to all immediate children of the passed Container which have been configured as hideable.
+ */
+ getColumnMenu: function(headerContainer) {
+ var menuItems = [],
i = 0,
- it = this.items.items,
- ln = it.length;
+ item,
+ items = headerContainer.query('>gridcolumn[hideable]'),
+ itemsLn = items.length,
+ menuItem;
- el = Ext.getDom(el);
- for (; i < ln; i++) {
- item = it[i];
- itemEl = item.getEl();
- if ((itemEl.dom === el) || itemEl.contains(el)) {
- return item;
+ for (; i < itemsLn; i++) {
+ item = items[i];
+ menuItem = Ext.create('Ext.menu.CheckItem', {
+ text: item.text,
+ checked: !item.hidden,
+ hideOnClick: false,
+ headerId: item.id,
+ menu: item.isGroupHeader ? this.getColumnMenu(item) : undefined,
+ checkHandler: this.onColumnCheckChange,
+ scope: this
+ });
+ if (itemsLn === 1) {
+ menuItem.disabled = true;
}
+ menuItems.push(menuItem);
+
+ // If the header is ever destroyed - for instance by dragging out the last remaining sub header,
+ // then the associated menu item must also be destroyed.
+ item.on({
+ destroy: Ext.Function.bind(menuItem.destroy, menuItem)
+ });
+ }
+ return menuItems;
+ },
+
+ onColumnCheckChange: function(checkItem, checked) {
+ var header = Ext.getCmp(checkItem.headerId);
+ header[checked ? 'show' : 'hide']();
+ },
+
+<span id='Ext-grid-header-Container-method-getColumnsForTpl'> /**
+</span> * Get the columns used for generating a template via TableChunker.
+ * Returns an array of all columns and their
+ * - dataIndex
+ * - align
+ * - width
+ * - id
+ * - columnId - used to create an identifying CSS class
+ * - cls The tdCls configuration from the Column object
+ * @private
+ */
+ getColumnsForTpl: function(flushCache) {
+ var cols = [],
+ headers = this.getGridColumns(flushCache),
+ headersLn = headers.length,
+ i = 0,
+ header,
+ width;
+
+ for (; i < headersLn; i++) {
+ header = headers[i];
+
+ if (header.hidden || header.up('headercontainer[hidden=true]')) {
+ width = 0;
+ } else {
+ width = header.getDesiredWidth();
+ // IE6 and IE7 bug.
+ // Setting the width of the first TD does not work - ends up with a 1 pixel discrepancy.
+ // We need to increment the passed with in this case.
+ if ((i === 0) && (Ext.isIE6 || Ext.isIE7)) {
+ width += 1;
+ }
+ }
+ cols.push({
+ dataIndex: header.dataIndex,
+ align: header.align,
+ width: width,
+ id: header.id,
+ cls: header.tdCls,
+ columnId: header.getItemId()
+ });
+ }
+ return cols;
+ },
+
+<span id='Ext-grid-header-Container-method-getColumnCount'> /**
+</span> * Returns the number of <b>grid columns</b> descended from this HeaderContainer.
+ * Group Columns are HeaderContainers. All grid columns are returned, including hidden ones.
+ */
+ getColumnCount: function() {
+ return this.getGridColumns().length;
+ },
+
+<span id='Ext-grid-header-Container-method-getFullWidth'> /**
+</span> * Gets the full width of all columns that are visible.
+ */
+ getFullWidth: function(flushCache) {
+ var fullWidth = 0,
+ headers = this.getVisibleGridColumns(flushCache),
+ headersLn = headers.length,
+ i = 0;
+
+ for (; i < headersLn; i++) {
+ if (!isNaN(headers[i].width)) {
+ // use headers getDesiredWidth if its there
+ if (headers[i].getDesiredWidth) {
+ fullWidth += headers[i].getDesiredWidth();
+ // if injected a diff cmp use getWidth
+ } else {
+ fullWidth += headers[i].getWidth();
+ }
+ }
+ }
+ return fullWidth;
+ },
+
+ // invoked internally by a header when not using triStateSorting
+ clearOtherSortStates: function(activeHeader) {
+ var headers = this.getGridColumns(),
+ headersLn = headers.length,
+ i = 0,
+ oldSortState;
+
+ for (; i < headersLn; i++) {
+ if (headers[i] !== activeHeader) {
+ oldSortState = headers[i].sortState;
+ // unset the sortstate and dont recurse
+ headers[i].setSortState(null, true);
+ //if (!silent && oldSortState !== null) {
+ // this.fireEvent('sortchange', this, headers[i], null);
+ //}
+ }
+ }
+ },
+
+<span id='Ext-grid-header-Container-method-getVisibleGridColumns'> /**
+</span> * Returns an array of the <b>visible</b> columns in the grid. This goes down to the lowest column header
+ * level, and does not return <i>grouped</i> headers which contain sub headers.
+ * @param {Boolean} refreshCache If omitted, the cached set of columns will be returned. Pass true to refresh the cache.
+ * @returns {Array}
+ */
+ getVisibleGridColumns: function(refreshCache) {
+ return Ext.ComponentQuery.query(':not([hidden])', this.getGridColumns(refreshCache));
+ },
+
+<span id='Ext-grid-header-Container-method-getGridColumns'> /**
+</span> * Returns an array of all columns which map to Store fields. This goes down to the lowest column header
+ * level, and does not return <i>grouped</i> headers which contain sub headers.
+ * @param {Boolean} refreshCache If omitted, the cached set of columns will be returned. Pass true to refresh the cache.
+ * @returns {Array}
+ */
+ getGridColumns: function(refreshCache) {
+ var me = this,
+ result = refreshCache ? null : me.gridDataColumns;
+
+ // Not already got the column cache, so collect the base columns
+ if (!result) {
+ me.gridDataColumns = result = [];
+ me.cascade(function(c) {
+ if ((c !== me) && !c.isGroupHeader) {
+ result.push(c);
+ }
+ });
+ }
+
+ return result;
+ },
+
+<span id='Ext-grid-header-Container-method-getHideableColumns'> /**
+</span> * @private
+ * For use by column headers in determining whether there are any hideable columns when deciding whether or not
+ * the header menu should be disabled.
+ */
+ getHideableColumns: function(refreshCache) {
+ var me = this,
+ result = refreshCache ? null : me.hideableColumns;
+
+ if (!result) {
+ result = me.hideableColumns = me.query('[hideable]');
+ }
+ return result;
+ },
+
+<span id='Ext-grid-header-Container-method-getHeaderIndex'> /**
+</span> * Get the index of a leaf level header regardless of what the nesting
+ * structure is.
+ */
+ getHeaderIndex: function(header) {
+ var columns = this.getGridColumns();
+ return Ext.Array.indexOf(columns, header);
+ },
+
+<span id='Ext-grid-header-Container-method-getHeaderAtIndex'> /**
+</span> * Get a leaf level header by index regardless of what the nesting
+ * structure is.
+ */
+ getHeaderAtIndex: function(index) {
+ var columns = this.getGridColumns();
+ return columns[index];
+ },
+
+<span id='Ext-grid-header-Container-method-prepareData'> /**
+</span> * Maps the record data to base it on the header id's.
+ * This correlates to the markup/template generated by
+ * TableChunker.
+ */
+ prepareData: function(data, rowIdx, record, view, panel) {
+ var obj = {},
+ headers = this.gridDataColumns || this.getGridColumns(),
+ headersLn = headers.length,
+ colIdx = 0,
+ header,
+ headerId,
+ renderer,
+ value,
+ metaData,
+ store = panel.store;
+
+ for (; colIdx < headersLn; colIdx++) {
+ metaData = {
+ tdCls: '',
+ style: ''
+ };
+ header = headers[colIdx];
+ headerId = header.id;
+ renderer = header.renderer;
+ value = data[header.dataIndex];
+
+ // When specifying a renderer as a string, it always resolves
+ // to Ext.util.Format
+ if (typeof renderer === "string") {
+ header.renderer = renderer = Ext.util.Format[renderer];
+ }
+
+ if (typeof renderer === "function") {
+ value = renderer.call(
+ header.scope || this.ownerCt,
+ value,
+ // metadata per cell passing an obj by reference so that
+ // it can be manipulated inside the renderer
+ metaData,
+ record,
+ rowIdx,
+ colIdx,
+ store,
+ view
+ );
+ }
+
+ // <debug>
+ if (metaData.css) {
+ // This warning attribute is used by the compat layer
+ obj.cssWarning = true;
+ metaData.tdCls = metaData.css;
+ delete metaData.css;
+ }
+ // </debug>
+
+ obj[headerId+'-modified'] = record.isModified(header.dataIndex) ? Ext.baseCSSPrefix + 'grid-dirty-cell' : '';
+ obj[headerId+'-tdCls'] = metaData.tdCls;
+ obj[headerId+'-tdAttr'] = metaData.tdAttr;
+ obj[headerId+'-style'] = metaData.style;
+ if (value === undefined || value === null || value === '') {
+ value = '&#160;';
+ }
+ obj[headerId] = value;
+ }
+ return obj;
+ },
+
+ expandToFit: function(header) {
+ if (this.view) {
+ this.view.expandToFit(header);
}
- return null;
}
});
</pre>