Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / docs / source / View2.html
1 <!DOCTYPE html><html><head><title>Sencha Documentation Project</title><link rel="stylesheet" href="../reset.css" type="text/css"><link rel="stylesheet" href="../prettify.css" type="text/css"><link rel="stylesheet" href="../prettify_sa.css" type="text/css"><script type="text/javascript" src="../prettify.js"></script></head><body onload="prettyPrint()"><pre class="prettyprint"><pre><span id='Ext-tree.View'>/**
2 </span> * @class Ext.tree.View
3  * @extends Ext.view.Table
4  */
5 Ext.define('Ext.tree.View', {
6     extend: 'Ext.view.Table',
7     alias: 'widget.treeview',
8
9     loadingCls: Ext.baseCSSPrefix + 'grid-tree-loading',
10     expandedCls: Ext.baseCSSPrefix + 'grid-tree-node-expanded',
11
12     expanderSelector: '.' + Ext.baseCSSPrefix + 'tree-expander',
13     checkboxSelector: '.' + Ext.baseCSSPrefix + 'tree-checkbox',
14     expanderIconOverCls: Ext.baseCSSPrefix + 'tree-expander-over',
15
16     blockRefresh: true,
17
18 <span id='Ext-tree.View-cfg-rootVisible'>    /** 
19 </span>     * @cfg {Boolean} rootVisible &lt;tt&gt;false&lt;/tt&gt; to hide the root node (defaults to &lt;tt&gt;true&lt;/tt&gt;)
20      */
21     rootVisible: true,
22
23 <span id='Ext-tree.View-cfg-animate'>    /** 
24 </span>     * @cfg {Boolean} animate &lt;tt&gt;true&lt;/tt&gt; to enable animated expand/collapse (defaults to the value of {@link Ext#enableFx Ext.enableFx})
25      */
26
27     expandDuration: 250,
28     collapseDuration: 250,
29     
30     toggleOnDblClick: true,
31
32     initComponent: function() {
33         var me = this;
34         
35         if (me.initialConfig.animate === undefined) {
36             me.animate = Ext.enableFx;
37         }
38         
39         me.store = Ext.create('Ext.data.NodeStore', {
40             recursive: true,
41             rootVisible: me.rootVisible,
42             listeners: {
43                 beforeexpand: me.onBeforeExpand,
44                 expand: me.onExpand,
45                 beforecollapse: me.onBeforeCollapse,
46                 collapse: me.onCollapse,
47                 scope: me
48             }
49         });
50         
51         if (me.node) {
52             me.setRootNode(me.node);
53         }
54         me.animQueue = {};
55         me.callParent(arguments);
56     },
57     
58     onClear: function(){
59         this.store.removeAll();    
60     },
61
62     setRootNode: function(node) {
63         var me = this;        
64         me.store.setNode(node);
65         me.node = node;
66         if (!me.rootVisible) {
67             node.expand();
68         }
69     },
70     
71     onRender: function() {
72         var me = this,
73             opts = {delegate: me.expanderSelector},
74             el;
75
76         me.callParent(arguments);
77
78         el = me.el;
79         el.on({
80             scope: me,
81             delegate: me.expanderSelector,
82             mouseover: me.onExpanderMouseOver,
83             mouseout: me.onExpanderMouseOut
84         });
85         el.on({
86             scope: me,
87             delegate: me.checkboxSelector,
88             click: me.onCheckboxChange
89         });
90     },
91
92     onCheckboxChange: function(e, t) {
93         var item = e.getTarget(this.getItemSelector(), this.getTargetEl()),
94             record, value;
95             
96         if (item) {
97             record = this.getRecord(item);
98             value = !record.get('checked');
99             record.set('checked', value);
100             this.fireEvent('checkchange', record, value);
101         }
102     },
103
104     getChecked: function() {
105         var checked = [];
106         this.node.cascadeBy(function(rec){
107             if (rec.get('checked')) {
108                 checked.push(rec);
109             }
110         });
111         return checked;
112     },
113     
114     isItemChecked: function(rec){
115         return rec.get('checked');
116     },
117
118     createAnimWrap: function(record, index) {
119         var thHtml = '',
120             headerCt = this.panel.headerCt,
121             headers = headerCt.getGridColumns(),
122             i = 0, len = headers.length, item,
123             node = this.getNode(record),
124             tmpEl, nodeEl;
125
126         for (; i &lt; len; i++) {
127             item = headers[i];
128             thHtml += '&lt;th style=&quot;width: ' + (item.hidden ? 0 : item.getDesiredWidth()) + 'px; height: 0px;&quot;&gt;&lt;/th&gt;';
129         }
130
131         nodeEl = Ext.get(node);        
132         tmpEl = nodeEl.insertSibling({
133             tag: 'tr',
134             html: [
135                 '&lt;td colspan=&quot;' + headerCt.getColumnCount() + '&quot;&gt;',
136                     '&lt;div class=&quot;' + Ext.baseCSSPrefix + 'tree-animator-wrap' + '&quot;&gt;',
137                         '&lt;table class=&quot;' + Ext.baseCSSPrefix + 'grid-table&quot; style=&quot;width: ' + headerCt.getFullWidth() + 'px;&quot;&gt;&lt;tbody&gt;',
138                             thHtml,
139                         '&lt;/tbody&gt;&lt;/table&gt;',
140                     '&lt;/div&gt;',
141                 '&lt;/td&gt;'
142             ].join('')
143         }, 'after');
144
145         return {
146             record: record,
147             node: node,
148             el: tmpEl,
149             expanding: false,
150             collapsing: false,
151             animating: false,
152             animateEl: tmpEl.down('div'),
153             targetEl: tmpEl.down('tbody')
154         };
155     },
156
157     getAnimWrap: function(parent) {
158         if (!this.animate) {
159             return null;
160         }
161
162         // We are checking to see which parent is having the animation wrap
163         while (parent) {
164             if (parent.animWrap) {
165                 return parent.animWrap;
166             }
167             parent = parent.parentNode;
168         }
169         return null;
170     },
171
172     doAdd: function(nodes, records, index) {
173         // If we are adding records which have a parent that is currently expanding
174         // lets add them to the animation wrap
175         var me = this,
176             record = records[0],
177             parent = record.parentNode,
178             a = me.all.elements,
179             relativeIndex = 0,
180             animWrap = me.getAnimWrap(parent),
181             targetEl, children, len;
182
183         if (!animWrap || !animWrap.expanding) {
184             me.resetScrollers();
185             return me.callParent(arguments);
186         }
187
188         // We need the parent that has the animWrap, not the nodes parent
189         parent = animWrap.record;
190         
191         // If there is an anim wrap we do our special magic logic
192         targetEl = animWrap.targetEl;
193         children = targetEl.dom.childNodes;
194         
195         // We subtract 1 from the childrens length because we have a tr in there with the th'es
196         len = children.length - 1;
197         
198         // The relative index is the index in the full flat collection minus the index of the wraps parent
199         relativeIndex = index - me.indexOf(parent) - 1;
200         
201         // If we are adding records to the wrap that have a higher relative index then there are currently children
202         // it means we have to append the nodes to the wrap
203         if (!len || relativeIndex &gt;= len) {
204             targetEl.appendChild(nodes);
205         }
206         // If there are already more children then the relative index it means we are adding child nodes of
207         // some expanded node in the anim wrap. In this case we have to insert the nodes in the right location
208         else {
209             // +1 because of the tr with th'es that is already there
210             Ext.fly(children[relativeIndex + 1]).insertSibling(nodes, 'before', true);
211         }
212         
213         // We also have to update the CompositeElementLite collection of the DataView
214         if (index &lt; a.length) {
215             a.splice.apply(a, [index, 0].concat(nodes));
216         }
217         else {            
218             a.push.apply(a, nodes);
219         }
220         
221         // If we were in an animation we need to now change the animation
222         // because the targetEl just got higher.
223         if (animWrap.isAnimating) {
224             me.onExpand(parent);
225         }
226     },
227     
228     doRemove: function(record, index) {
229         // If we are adding records which have a parent that is currently expanding
230         // lets add them to the animation wrap
231         var me = this,
232             parent = record.parentNode,
233             all = me.all,
234             animWrap = me.getAnimWrap(record),
235             node = all.item(index).dom;
236
237         if (!animWrap || !animWrap.collapsing) {
238             me.resetScrollers();
239             return me.callParent(arguments);
240         }
241
242         animWrap.targetEl.appendChild(node);
243         all.removeElement(index);
244     },
245
246     onBeforeExpand: function(parent, records, index) {
247         var me = this,
248             animWrap;
249             
250         if (!me.animate) {
251             return;
252         }
253
254         if (me.getNode(parent)) {
255             animWrap = me.getAnimWrap(parent);
256             if (!animWrap) {
257                 animWrap = parent.animWrap = me.createAnimWrap(parent);
258                 animWrap.animateEl.setHeight(0);
259             }
260             else if (animWrap.collapsing) {
261                 // If we expand this node while it is still expanding then we
262                 // have to remove the nodes from the animWrap.
263                 animWrap.targetEl.select(me.itemSelector).remove();
264             } 
265             animWrap.expanding = true;
266             animWrap.collapsing = false;
267         }
268     },
269
270     onExpand: function(parent) {
271         var me = this,
272             queue = me.animQueue,
273             id = parent.getId(),
274             animWrap,
275             animateEl, 
276             targetEl,
277             queueItem;        
278         
279         if (me.singleExpand) {
280             me.ensureSingleExpand(parent);
281         }
282         
283         animWrap = me.getAnimWrap(parent);
284
285         if (!animWrap) {
286             me.resetScrollers();
287             return;
288         }
289         
290         animateEl = animWrap.animateEl;
291         targetEl = animWrap.targetEl;
292
293         animateEl.stopAnimation();
294         // @TODO: we are setting it to 1 because quirks mode on IE seems to have issues with 0
295         queue[id] = true;
296         animateEl.slideIn('t', {
297             duration: me.expandDuration,
298             listeners: {
299                 scope: me,
300                 lastframe: function() {
301                     // Move all the nodes out of the anim wrap to their proper location
302                     animWrap.el.insertSibling(targetEl.query(me.itemSelector), 'before');
303                     animWrap.el.remove();
304                     me.resetScrollers();
305                     delete animWrap.record.animWrap;
306                     delete queue[id];
307                 }
308             }
309         });
310         
311         animWrap.isAnimating = true;
312     },
313     
314     resetScrollers: function(){
315         var panel = this.panel;
316         
317         panel.determineScrollbars();
318         panel.invalidateScroller();
319     },
320
321     onBeforeCollapse: function(parent, records, index) {
322         var me = this,
323             animWrap;
324             
325         if (!me.animate) {
326             return;
327         }
328
329         if (me.getNode(parent)) {
330             animWrap = me.getAnimWrap(parent);
331             if (!animWrap) {
332                 animWrap = parent.animWrap = me.createAnimWrap(parent, index);
333             }
334             else if (animWrap.expanding) {
335                 // If we collapse this node while it is still expanding then we
336                 // have to remove the nodes from the animWrap.
337                 animWrap.targetEl.select(this.itemSelector).remove();
338             }
339             animWrap.expanding = false;
340             animWrap.collapsing = true;
341         }
342     },
343     
344     onCollapse: function(parent) {
345         var me = this,
346             queue = me.animQueue,
347             id = parent.getId(),
348             animWrap = me.getAnimWrap(parent),
349             animateEl, targetEl;
350
351         if (!animWrap) {
352             me.resetScrollers();
353             return;
354         }
355         
356         animateEl = animWrap.animateEl;
357         targetEl = animWrap.targetEl;
358
359         queue[id] = true;
360         
361         // @TODO: we are setting it to 1 because quirks mode on IE seems to have issues with 0
362         animateEl.stopAnimation();
363         animateEl.slideOut('t', {
364             duration: me.collapseDuration,
365             listeners: {
366                 scope: me,
367                 lastframe: function() {
368                     animWrap.el.remove();
369                     delete animWrap.record.animWrap;
370                     me.resetScrollers();
371                     delete queue[id];
372                 }             
373             }
374         });
375         animWrap.isAnimating = true;
376     },
377     
378 <span id='Ext-tree.View-method-isAnimating'>    /**
379 </span>     * Checks if a node is currently undergoing animation
380      * @private
381      * @param {Ext.data.Model} node The node
382      * @return {Boolean} True if the node is animating
383      */
384     isAnimating: function(node) {
385         return !!this.animQueue[node.getId()];    
386     },
387     
388     collectData: function(records) {
389         var data = this.callParent(arguments),
390             rows = data.rows,
391             len = rows.length,
392             i = 0,
393             row, record;
394             
395         for (; i &lt; len; i++) {
396             row = rows[i];
397             record = records[i];
398             if (record.get('qtip')) {
399                 row.rowAttr = 'data-qtip=&quot;' + record.get('qtip') + '&quot;';
400                 if (record.get('qtitle')) {
401                     row.rowAttr += ' ' + 'data-qtitle=&quot;' + record.get('qtitle') + '&quot;';
402                 }
403             }
404             if (record.isExpanded()) {
405                 row.rowCls = (row.rowCls || '') + ' ' + this.expandedCls;
406             }
407             if (record.isLoading()) {
408                 row.rowCls = (row.rowCls || '') + ' ' + this.loadingCls;
409             }
410         }
411         
412         return data;
413     },
414     
415 <span id='Ext-tree.View-method-expand'>    /**
416 </span>     * Expand a record that is loaded in the view.
417      * @param {Ext.data.Model} record The record to expand
418      * @param {Boolean} deep (optional) True to expand nodes all the way down the tree hierarchy.
419      * @param {Function} callback (optional) The function to run after the expand is completed
420      * @param {Object} scope (optional) The scope of the callback function.
421      */
422     expand: function(record, deep, callback, scope) {
423         return record.expand(deep, callback, scope);
424     },
425     
426 <span id='Ext-tree.View-method-collapse'>    /**
427 </span>     * Collapse a record that is loaded in the view.
428      * @param {Ext.data.Model} record The record to collapse
429      * @param {Boolean} deep (optional) True to collapse nodes all the way up the tree hierarchy.
430      * @param {Function} callback (optional) The function to run after the collapse is completed
431      * @param {Object} scope (optional) The scope of the callback function.
432      */
433     collapse: function(record, deep, callback, scope) {
434         return record.collapse(deep, callback, scope);
435     },
436     
437 <span id='Ext-tree.View-method-toggle'>    /**
438 </span>     * Toggle a record between expanded and collapsed.
439      * @param {Ext.data.Record} recordInstance
440      */
441     toggle: function(record) {
442         this[record.isExpanded() ? 'collapse' : 'expand'](record);
443     },
444     
445     onItemDblClick: function(record, item, index) {
446         this.callParent(arguments);
447         if (this.toggleOnDblClick) {
448             this.toggle(record);
449         }
450     },
451     
452     onBeforeItemMouseDown: function(record, item, index, e) {
453         if (e.getTarget(this.expanderSelector, item)) {
454             return false;
455         }
456         return this.callParent(arguments);
457     },
458     
459     onItemClick: function(record, item, index, e) {
460         if (e.getTarget(this.expanderSelector, item)) {
461             this.toggle(record);
462             return false;
463         }
464         return this.callParent(arguments);
465     },
466     
467     onExpanderMouseOver: function(e, t) {
468         e.getTarget(this.cellSelector, 10, true).addCls(this.expanderIconOverCls);
469     },
470     
471     onExpanderMouseOut: function(e, t) {
472         e.getTarget(this.cellSelector, 10, true).removeCls(this.expanderIconOverCls);
473     },
474     
475 <span id='Ext-tree.View-method-getTreeStore'>    /**
476 </span>     * Gets the base TreeStore from the bound TreePanel.
477      */
478     getTreeStore: function() {
479         return this.panel.store;
480     },    
481     
482     ensureSingleExpand: function(node) {
483         var parent = node.parentNode;
484         if (parent) {
485             parent.eachChild(function(child) {
486                 if (child !== node &amp;&amp; child.isExpanded()) {
487                     child.collapse();
488                 }
489             });
490         }
491     }
492 });</pre></pre></body></html>