<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-grid-View'>/**
-</span> * @class Ext.grid.View
- * @extends Ext.view.Table
-
-The grid View class provides extra {@link Ext.grid.Panel} specific functionality to the
-{@link Ext.view.Table}. In general, this class is not instanced directly, instead a viewConfig
-option is passed to the grid:
-
- Ext.create('Ext.grid.Panel', {
- // other options
- viewConfig: {
- stripeRows: false
- }
- });
-
-__Drag Drop__
-Drag and drop functionality can be achieved in the grid by attaching a {@link Ext.grid.plugin.DragDrop} plugin
-when creating the view.
-
- Ext.create('Ext.grid.Panel', {
- // other options
- viewConfig: {
- plugins: {
- ddGroup: 'people-group',
- ptype: 'gridviewdragdrop',
- enableDrop: false
- }
- }
- });
-
- * @markdown
+ <pre class="prettyprint lang-js"><span id='Ext-tree-View'>/**
+</span> * Used as a view by {@link Ext.tree.Panel TreePanel}.
*/
-Ext.define('Ext.grid.View', {
+Ext.define('Ext.tree.View', {
extend: 'Ext.view.Table',
- alias: 'widget.gridview',
-
-<span id='Ext-grid-View-cfg-stripeRows'> /**
-</span> * @cfg {Boolean} stripeRows <tt>true</tt> to stripe the rows. Default is <tt>false</tt>.
- * <p>This causes the CSS class <tt><b>x-grid-row-alt</b></tt> to be added to alternate rows of
- * the grid. A default CSS rule is provided which sets a background color, but you can override this
- * with a rule which either overrides the <b>background-color</b> style using the '!important'
- * modifier, or which uses a CSS selector of higher specificity.</p>
+ 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',
+
+ // Class to add to the node wrap element used to hold nodes when a parent is being
+ // collapsed or expanded. During the animation, UI interaction is forbidden by testing
+ // for an ancestor node with this class.
+ nodeAnimWrapCls: Ext.baseCSSPrefix + 'tree-animator-wrap',
+
+ blockRefresh: true,
+
+<span id='Ext-tree-View-cfg-rootVisible'> /**
+</span> * @cfg {Boolean} rootVisible
+ * False to hide the root node.
*/
- stripeRows: true,
+ rootVisible: true,
+
+<span id='Ext-tree-View-cfg-animate'> /**
+</span> * @cfg {Boolean} animate
+ * True to enable animated expand/collapse (defaults to the value of {@link Ext#enableFx Ext.enableFx})
+ */
+
+ expandDuration: 250,
+ collapseDuration: 250,
- invalidateScrollerOnRefresh: true,
+ 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);
+ }
+ me.animQueue = {};
+ me.callParent(arguments);
+ },
+
+ processUIEvent: function(e) {
+ // If the clicked node is part of an animation, ignore the click.
+ // This is because during a collapse animation, the associated Records
+ // will already have been removed from the Store, and the event is not processable.
+ if (e.getTarget('.' + this.nodeAnimWrapCls, this.el)) {
+ return false;
+ }
+ return this.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();
+ }
+ },
-<span id='Ext-grid-View-method-scrollToTop'> /**
-</span> * Scroll the GridView to the top by scrolling the scroller.
- * @private
- */
- scrollToTop : function(){
- if (this.rendered) {
- var section = this.ownerCt,
- verticalScroller = section.verticalScroller;
-
- if (verticalScroller) {
- verticalScroller.scrollToTop();
+ onRender: function() {
+ var me = this,
+ el;
+
+ me.callParent(arguments);
+
+ el = me.el;
+ el.on({
+ scope: me,
+ delegate: me.expanderSelector,
+ mouseover: me.onExpanderMouseOver,
+ mouseout: me.onExpanderMouseOut
+ });
+ el.on({
+ scope: me,
+ delegate: me.checkboxSelector,
+ click: me.onCheckboxChange
+ });
+ },
+
+ onCheckboxChange: function(e, t) {
+ var me = this,
+ item = e.getTarget(me.getItemSelector(), me.getTargetEl());
+
+ if (item) {
+ me.onCheckChange(me.getRecord(item));
+ }
+ },
+
+ onCheckChange: function(record){
+ var checked = record.get('checked');
+ if (Ext.isBoolean(checked)) {
+ checked = !checked;
+ record.set('checked', checked);
+ this.fireEvent('checkchange', record, checked);
+ }
+ },
+
+ 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');
+ },
+
+ 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>';
}
+
+ nodeEl = Ext.get(node);
+ tmpEl = nodeEl.insertSibling({
+ tag: 'tr',
+ html: [
+ '<td colspan="' + headerCt.getColumnCount() + '">',
+ '<div class="' + this.nodeAnimWrapCls + '">',
+ '<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')
+ };
},
- // after adding a row stripe rows from then on
- onAdd: function(ds, records, index) {
- this.callParent(arguments);
- this.doStripeRows(index);
+ getAnimWrap: function(parent) {
+ if (!this.animate) {
+ return null;
+ }
+
+ // We are checking to see which parent is having the animation wrap
+ while (parent) {
+ if (parent.animWrap) {
+ return parent.animWrap;
+ }
+ parent = parent.parentNode;
+ }
+ return null;
+ },
+
+ 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
+ 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);
+ }
+
+ // 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);
+ }
+
+ // 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);
+ }
},
- // after removing a row stripe rows from then on
- onRemove: function(ds, records, index) {
- this.callParent(arguments);
- this.doStripeRows(index);
+ beginBulkUpdate: function(){
+ this.bulkUpdate = true;
+ this.ownerCt.changingScrollbars = true;
},
- onUpdate: function(ds, record, operation) {
- var index = ds.indexOf(record);
- this.callParent(arguments);
- this.doStripeRows(index, index);
+ endBulkUpdate: function(){
+ var me = this,
+ ownerCt = me.ownerCt;
+
+ me.bulkUpdate = false;
+ me.ownerCt.changingScrollbars = true;
+ me.resetScrollers();
+ },
+
+ onRemove : function(ds, record, index) {
+ var me = this,
+ bulk = me.bulkUpdate;
+
+ me.doRemove(record, index);
+ if (!bulk) {
+ me.updateIndexes(index);
+ }
+ if (me.store.getCount() === 0){
+ me.refresh();
+ }
+ if (!bulk) {
+ me.fireEvent('itemremove', record, index);
+ }
},
-<span id='Ext-grid-View-method-doStripeRows'> /**
-</span> * Stripe rows from a particular row index
- * @param {Number} startRow
- * @param {Number} endRow Optional argument specifying the last row to process. By default process up to the last row.
+ 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);
+ }
+
+ animWrap.targetEl.appendChild(node);
+ all.removeElement(index);
+ },
+
+ onBeforeExpand: function(parent, records, index) {
+ var me = this,
+ animWrap;
+
+ if (!me.rendered || !me.animate) {
+ return;
+ }
+
+ if (me.getNode(parent)) {
+ animWrap = me.getAnimWrap(parent);
+ if (!animWrap) {
+ animWrap = parent.animWrap = me.createAnimWrap(parent);
+ animWrap.animateEl.setHeight(0);
+ }
+ 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);
+ }
+
+ animWrap = me.getAnimWrap(parent);
+
+ if (!animWrap) {
+ me.resetScrollers();
+ return;
+ }
+
+ 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(){
+ if (!this.bulkUpdate) {
+ var panel = this.panel;
+
+ panel.determineScrollbars();
+ panel.invalidateScroller();
+ }
+ },
+
+ onBeforeCollapse: function(parent, records, index) {
+ var me = this,
+ animWrap;
+
+ if (!me.rendered || !me.animate) {
+ return;
+ }
+
+ 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;
+ }
+ },
+
+ onCollapse: function(parent) {
+ var me = this,
+ queue = me.animQueue,
+ id = parent.getId(),
+ animWrap = me.getAnimWrap(parent),
+ animateEl, targetEl;
+
+ if (!animWrap) {
+ me.resetScrollers();
+ return;
+ }
+
+ 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;
+ },
+
+<span id='Ext-tree-View-method-isAnimating'> /**
+</span> * Checks if a node is currently undergoing animation
* @private
+ * @param {Ext.data.Model} node The node
+ * @return {Boolean} True if the node is animating
*/
- doStripeRows: function(startRow, endRow) {
- // ensure stripeRows configuration is turned on
- if (this.stripeRows) {
- var rows = this.getNodes(startRow, endRow),
- rowsLn = rows.length,
- i = 0,
- row;
-
- for (; i < rowsLn; i++) {
- row = rows[i];
- // Remove prior applied row classes.
- row.className = row.className.replace(this.rowClsRe, ' ');
- startRow++;
- // Every odd row will get an additional cls
- if (startRow % 2 === 0) {
- row.className += (' ' + this.altRowCls);
+ 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;
+ }
}
+
+ return data;
+ },
+
+<span id='Ext-tree-View-method-expand'> /**
+</span> * Expands 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);
+ },
+
+<span id='Ext-tree-View-method-collapse'> /**
+</span> * Collapses 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.
+ */
+ collapse: function(record, deep, callback, scope) {
+ return record.collapse(deep, callback, scope);
+ },
+
+<span id='Ext-tree-View-method-toggle'> /**
+</span> * Toggles a record between expanded and collapsed.
+ * @param {Ext.data.Model} recordInstance
+ */
+ toggle: function(record) {
+ this[record.isExpanded() ? 'collapse' : 'expand'](record);
},
- refresh: function(firstPass) {
+ onItemDblClick: function(record, item, index) {
this.callParent(arguments);
- this.doStripeRows(0);
- // TODO: Remove gridpanel dependency
- var g = this.up('gridpanel');
- if (g && this.invalidateScrollerOnRefresh) {
- g.invalidateScroller();
+ 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;
+ }
+ 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);
+ },
+
+<span id='Ext-tree-View-method-getTreeStore'> /**
+</span> * 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();
+ }
+ });
}
}
-});
-</pre>
+});</pre>
</body>
</html>