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