Upgrade to ExtJS 4.0.1 - Released 05/18/2011
[extjs.git] / docs / source / View2.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         if (index &lt; a.length) {
232             a.splice.apply(a, [index, 0].concat(nodes));
233         }
234         else {            
235             a.push.apply(a, nodes);
236         }
237         
238         // If we were in an animation we need to now change the animation
239         // because the targetEl just got higher.
240         if (animWrap.isAnimating) {
241             me.onExpand(parent);
242         }
243     },
244     
245     doRemove: function(record, index) {
246         // If we are adding records which have a parent that is currently expanding
247         // lets add them to the animation wrap
248         var me = this,
249             parent = record.parentNode,
250             all = me.all,
251             animWrap = me.getAnimWrap(record),
252             node = all.item(index).dom;
253
254         if (!animWrap || !animWrap.collapsing) {
255             me.resetScrollers();
256             return me.callParent(arguments);
257         }
258
259         animWrap.targetEl.appendChild(node);
260         all.removeElement(index);
261     },
262
263     onBeforeExpand: function(parent, records, index) {
264         var me = this,
265             animWrap;
266             
267         if (!me.rendered || !me.animate) {
268             return;
269         }
270
271         if (me.getNode(parent)) {
272             animWrap = me.getAnimWrap(parent);
273             if (!animWrap) {
274                 animWrap = parent.animWrap = me.createAnimWrap(parent);
275                 animWrap.animateEl.setHeight(0);
276             }
277             else if (animWrap.collapsing) {
278                 // If we expand this node while it is still expanding then we
279                 // have to remove the nodes from the animWrap.
280                 animWrap.targetEl.select(me.itemSelector).remove();
281             } 
282             animWrap.expanding = true;
283             animWrap.collapsing = false;
284         }
285     },
286
287     onExpand: function(parent) {
288         var me = this,
289             queue = me.animQueue,
290             id = parent.getId(),
291             animWrap,
292             animateEl, 
293             targetEl,
294             queueItem;        
295         
296         if (me.singleExpand) {
297             me.ensureSingleExpand(parent);
298         }
299         
300         animWrap = me.getAnimWrap(parent);
301
302         if (!animWrap) {
303             me.resetScrollers();
304             return;
305         }
306         
307         animateEl = animWrap.animateEl;
308         targetEl = animWrap.targetEl;
309
310         animateEl.stopAnimation();
311         // @TODO: we are setting it to 1 because quirks mode on IE seems to have issues with 0
312         queue[id] = true;
313         animateEl.slideIn('t', {
314             duration: me.expandDuration,
315             listeners: {
316                 scope: me,
317                 lastframe: function() {
318                     // Move all the nodes out of the anim wrap to their proper location
319                     animWrap.el.insertSibling(targetEl.query(me.itemSelector), 'before');
320                     animWrap.el.remove();
321                     me.resetScrollers();
322                     delete animWrap.record.animWrap;
323                     delete queue[id];
324                 }
325             }
326         });
327         
328         animWrap.isAnimating = true;
329     },
330     
331     resetScrollers: function(){
332         var panel = this.panel;
333         
334         panel.determineScrollbars();
335         panel.invalidateScroller();
336     },
337
338     onBeforeCollapse: function(parent, records, index) {
339         var me = this,
340             animWrap;
341             
342         if (!me.rendered || !me.animate) {
343             return;
344         }
345
346         if (me.getNode(parent)) {
347             animWrap = me.getAnimWrap(parent);
348             if (!animWrap) {
349                 animWrap = parent.animWrap = me.createAnimWrap(parent, index);
350             }
351             else if (animWrap.expanding) {
352                 // If we collapse this node while it is still expanding then we
353                 // have to remove the nodes from the animWrap.
354                 animWrap.targetEl.select(this.itemSelector).remove();
355             }
356             animWrap.expanding = false;
357             animWrap.collapsing = true;
358         }
359     },
360     
361     onCollapse: function(parent) {
362         var me = this,
363             queue = me.animQueue,
364             id = parent.getId(),
365             animWrap = me.getAnimWrap(parent),
366             animateEl, targetEl;
367
368         if (!animWrap) {
369             me.resetScrollers();
370             return;
371         }
372         
373         animateEl = animWrap.animateEl;
374         targetEl = animWrap.targetEl;
375
376         queue[id] = true;
377         
378         // @TODO: we are setting it to 1 because quirks mode on IE seems to have issues with 0
379         animateEl.stopAnimation();
380         animateEl.slideOut('t', {
381             duration: me.collapseDuration,
382             listeners: {
383                 scope: me,
384                 lastframe: function() {
385                     animWrap.el.remove();
386                     delete animWrap.record.animWrap;
387                     me.resetScrollers();
388                     delete queue[id];
389                 }             
390             }
391         });
392         animWrap.isAnimating = true;
393     },
394     
395 <span id='Ext-tree-View-method-isAnimating'>    /**
396 </span>     * Checks if a node is currently undergoing animation
397      * @private
398      * @param {Ext.data.Model} node The node
399      * @return {Boolean} True if the node is animating
400      */
401     isAnimating: function(node) {
402         return !!this.animQueue[node.getId()];    
403     },
404     
405     collectData: function(records) {
406         var data = this.callParent(arguments),
407             rows = data.rows,
408             len = rows.length,
409             i = 0,
410             row, record;
411             
412         for (; i &lt; len; i++) {
413             row = rows[i];
414             record = records[i];
415             if (record.get('qtip')) {
416                 row.rowAttr = 'data-qtip=&quot;' + record.get('qtip') + '&quot;';
417                 if (record.get('qtitle')) {
418                     row.rowAttr += ' ' + 'data-qtitle=&quot;' + record.get('qtitle') + '&quot;';
419                 }
420             }
421             if (record.isExpanded()) {
422                 row.rowCls = (row.rowCls || '') + ' ' + this.expandedCls;
423             }
424             if (record.isLoading()) {
425                 row.rowCls = (row.rowCls || '') + ' ' + this.loadingCls;
426             }
427         }
428         
429         return data;
430     },
431     
432 <span id='Ext-tree-View-method-expand'>    /**
433 </span>     * Expand a record that is loaded in the view.
434      * @param {Ext.data.Model} record The record to expand
435      * @param {Boolean} deep (optional) True to expand nodes all the way down the tree hierarchy.
436      * @param {Function} callback (optional) The function to run after the expand is completed
437      * @param {Object} scope (optional) The scope of the callback function.
438      */
439     expand: function(record, deep, callback, scope) {
440         return record.expand(deep, callback, scope);
441     },
442     
443 <span id='Ext-tree-View-method-collapse'>    /**
444 </span>     * Collapse a record that is loaded in the view.
445      * @param {Ext.data.Model} record The record to collapse
446      * @param {Boolean} deep (optional) True to collapse nodes all the way up the tree hierarchy.
447      * @param {Function} callback (optional) The function to run after the collapse is completed
448      * @param {Object} scope (optional) The scope of the callback function.
449      */
450     collapse: function(record, deep, callback, scope) {
451         return record.collapse(deep, callback, scope);
452     },
453     
454 <span id='Ext-tree-View-method-toggle'>    /**
455 </span>     * Toggle a record between expanded and collapsed.
456      * @param {Ext.data.Record} recordInstance
457      */
458     toggle: function(record) {
459         this[record.isExpanded() ? 'collapse' : 'expand'](record);
460     },
461     
462     onItemDblClick: function(record, item, index) {
463         this.callParent(arguments);
464         if (this.toggleOnDblClick) {
465             this.toggle(record);
466         }
467     },
468     
469     onBeforeItemMouseDown: function(record, item, index, e) {
470         if (e.getTarget(this.expanderSelector, item)) {
471             return false;
472         }
473         return this.callParent(arguments);
474     },
475     
476     onItemClick: function(record, item, index, e) {
477         if (e.getTarget(this.expanderSelector, item)) {
478             this.toggle(record);
479             return false;
480         }
481         return this.callParent(arguments);
482     },
483     
484     onExpanderMouseOver: function(e, t) {
485         e.getTarget(this.cellSelector, 10, true).addCls(this.expanderIconOverCls);
486     },
487     
488     onExpanderMouseOut: function(e, t) {
489         e.getTarget(this.cellSelector, 10, true).removeCls(this.expanderIconOverCls);
490     },
491     
492 <span id='Ext-tree-View-method-getTreeStore'>    /**
493 </span>     * Gets the base TreeStore from the bound TreePanel.
494      */
495     getTreeStore: function() {
496         return this.panel.store;
497     },    
498     
499     ensureSingleExpand: function(node) {
500         var parent = node.parentNode;
501         if (parent) {
502             parent.eachChild(function(child) {
503                 if (child !== node &amp;&amp; child.isExpanded()) {
504                     child.collapse();
505                 }
506             });
507         }
508     }
509 });</pre>
510 </body>
511 </html>