4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
17 <body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-tree-View'>/**
19 </span> * Used as a view by {@link Ext.tree.Panel TreePanel}.
21 Ext.define('Ext.tree.View', {
22 extend: 'Ext.view.Table',
23 alias: 'widget.treeview',
25 loadingCls: Ext.baseCSSPrefix + 'grid-tree-loading',
26 expandedCls: Ext.baseCSSPrefix + 'grid-tree-node-expanded',
28 expanderSelector: '.' + Ext.baseCSSPrefix + 'tree-expander',
29 checkboxSelector: '.' + Ext.baseCSSPrefix + 'tree-checkbox',
30 expanderIconOverCls: Ext.baseCSSPrefix + 'tree-expander-over',
32 // Class to add to the node wrap element used to hold nodes when a parent is being
33 // collapsed or expanded. During the animation, UI interaction is forbidden by testing
34 // for an ancestor node with this class.
35 nodeAnimWrapCls: Ext.baseCSSPrefix + 'tree-animator-wrap',
39 <span id='Ext-tree-View-cfg-rootVisible'> /**
40 </span> * @cfg {Boolean} rootVisible
41 * False to hide the root node.
45 <span id='Ext-tree-View-cfg-animate'> /**
46 </span> * @cfg {Boolean} animate
47 * True to enable animated expand/collapse (defaults to the value of {@link Ext#enableFx Ext.enableFx})
51 collapseDuration: 250,
53 toggleOnDblClick: true,
55 initComponent: function() {
58 if (me.initialConfig.animate === undefined) {
59 me.animate = Ext.enableFx;
62 me.store = Ext.create('Ext.data.NodeStore', {
64 rootVisible: me.rootVisible,
66 beforeexpand: me.onBeforeExpand,
68 beforecollapse: me.onBeforeCollapse,
69 collapse: me.onCollapse,
75 me.setRootNode(me.node);
78 me.callParent(arguments);
81 processUIEvent: function(e) {
82 // If the clicked node is part of an animation, ignore the click.
83 // This is because during a collapse animation, the associated Records
84 // will already have been removed from the Store, and the event is not processable.
85 if (e.getTarget('.' + this.nodeAnimWrapCls, this.el)) {
88 return this.callParent(arguments);
92 this.store.removeAll();
95 setRootNode: function(node) {
97 me.store.setNode(node);
99 if (!me.rootVisible) {
104 onRender: function() {
108 me.callParent(arguments);
113 delegate: me.expanderSelector,
114 mouseover: me.onExpanderMouseOver,
115 mouseout: me.onExpanderMouseOut
119 delegate: me.checkboxSelector,
120 click: me.onCheckboxChange
124 onCheckboxChange: function(e, t) {
126 item = e.getTarget(me.getItemSelector(), me.getTargetEl());
129 me.onCheckChange(me.getRecord(item));
133 onCheckChange: function(record){
134 var checked = record.get('checked');
135 if (Ext.isBoolean(checked)) {
137 record.set('checked', checked);
138 this.fireEvent('checkchange', record, checked);
142 getChecked: function() {
144 this.node.cascadeBy(function(rec){
145 if (rec.get('checked')) {
152 isItemChecked: function(rec){
153 return rec.get('checked');
156 createAnimWrap: function(record, index) {
158 headerCt = this.panel.headerCt,
159 headers = headerCt.getGridColumns(),
160 i = 0, len = headers.length, item,
161 node = this.getNode(record),
164 for (; i < len; i++) {
166 thHtml += '<th style="width: ' + (item.hidden ? 0 : item.getDesiredWidth()) + 'px; height: 0px;"></th>';
169 nodeEl = Ext.get(node);
170 tmpEl = nodeEl.insertSibling({
173 '<td colspan="' + headerCt.getColumnCount() + '">',
174 '<div class="' + this.nodeAnimWrapCls + '">',
175 '<table class="' + Ext.baseCSSPrefix + 'grid-table" style="width: ' + headerCt.getFullWidth() + 'px;"><tbody>',
177 '</tbody></table>',
190 animateEl: tmpEl.down('div'),
191 targetEl: tmpEl.down('tbody')
195 getAnimWrap: function(parent) {
200 // We are checking to see which parent is having the animation wrap
202 if (parent.animWrap) {
203 return parent.animWrap;
205 parent = parent.parentNode;
210 doAdd: function(nodes, records, index) {
211 // If we are adding records which have a parent that is currently expanding
212 // lets add them to the animation wrap
215 parent = record.parentNode,
218 animWrap = me.getAnimWrap(parent),
219 targetEl, children, len;
221 if (!animWrap || !animWrap.expanding) {
223 return me.callParent(arguments);
226 // We need the parent that has the animWrap, not the nodes parent
227 parent = animWrap.record;
229 // If there is an anim wrap we do our special magic logic
230 targetEl = animWrap.targetEl;
231 children = targetEl.dom.childNodes;
233 // We subtract 1 from the childrens length because we have a tr in there with the th'es
234 len = children.length - 1;
236 // The relative index is the index in the full flat collection minus the index of the wraps parent
237 relativeIndex = index - me.indexOf(parent) - 1;
239 // If we are adding records to the wrap that have a higher relative index then there are currently children
240 // it means we have to append the nodes to the wrap
241 if (!len || relativeIndex >= len) {
242 targetEl.appendChild(nodes);
244 // If there are already more children then the relative index it means we are adding child nodes of
245 // some expanded node in the anim wrap. In this case we have to insert the nodes in the right location
247 // +1 because of the tr with th'es that is already there
248 Ext.fly(children[relativeIndex + 1]).insertSibling(nodes, 'before', true);
251 // We also have to update the CompositeElementLite collection of the DataView
252 Ext.Array.insert(a, index, nodes);
254 // If we were in an animation we need to now change the animation
255 // because the targetEl just got higher.
256 if (animWrap.isAnimating) {
261 beginBulkUpdate: function(){
262 this.bulkUpdate = true;
263 this.ownerCt.changingScrollbars = true;
266 endBulkUpdate: function(){
268 ownerCt = me.ownerCt;
270 me.bulkUpdate = false;
271 me.ownerCt.changingScrollbars = true;
275 onRemove : function(ds, record, index) {
277 bulk = me.bulkUpdate;
279 me.doRemove(record, index);
281 me.updateIndexes(index);
283 if (me.store.getCount() === 0){
287 me.fireEvent('itemremove', record, index);
291 doRemove: function(record, index) {
292 // If we are adding records which have a parent that is currently expanding
293 // lets add them to the animation wrap
295 parent = record.parentNode,
297 animWrap = me.getAnimWrap(record),
298 node = all.item(index).dom;
300 if (!animWrap || !animWrap.collapsing) {
302 return me.callParent(arguments);
305 animWrap.targetEl.appendChild(node);
306 all.removeElement(index);
309 onBeforeExpand: function(parent, records, index) {
313 if (!me.rendered || !me.animate) {
317 if (me.getNode(parent)) {
318 animWrap = me.getAnimWrap(parent);
320 animWrap = parent.animWrap = me.createAnimWrap(parent);
321 animWrap.animateEl.setHeight(0);
323 else if (animWrap.collapsing) {
324 // If we expand this node while it is still expanding then we
325 // have to remove the nodes from the animWrap.
326 animWrap.targetEl.select(me.itemSelector).remove();
328 animWrap.expanding = true;
329 animWrap.collapsing = false;
333 onExpand: function(parent) {
335 queue = me.animQueue,
342 if (me.singleExpand) {
343 me.ensureSingleExpand(parent);
346 animWrap = me.getAnimWrap(parent);
353 animateEl = animWrap.animateEl;
354 targetEl = animWrap.targetEl;
356 animateEl.stopAnimation();
357 // @TODO: we are setting it to 1 because quirks mode on IE seems to have issues with 0
359 animateEl.slideIn('t', {
360 duration: me.expandDuration,
363 lastframe: function() {
364 // Move all the nodes out of the anim wrap to their proper location
365 animWrap.el.insertSibling(targetEl.query(me.itemSelector), 'before');
366 animWrap.el.remove();
368 delete animWrap.record.animWrap;
374 animWrap.isAnimating = true;
377 resetScrollers: function(){
378 if (!this.bulkUpdate) {
379 var panel = this.panel;
381 panel.determineScrollbars();
382 panel.invalidateScroller();
386 onBeforeCollapse: function(parent, records, index) {
390 if (!me.rendered || !me.animate) {
394 if (me.getNode(parent)) {
395 animWrap = me.getAnimWrap(parent);
397 animWrap = parent.animWrap = me.createAnimWrap(parent, index);
399 else if (animWrap.expanding) {
400 // If we collapse this node while it is still expanding then we
401 // have to remove the nodes from the animWrap.
402 animWrap.targetEl.select(this.itemSelector).remove();
404 animWrap.expanding = false;
405 animWrap.collapsing = true;
409 onCollapse: function(parent) {
411 queue = me.animQueue,
413 animWrap = me.getAnimWrap(parent),
421 animateEl = animWrap.animateEl;
422 targetEl = animWrap.targetEl;
426 // @TODO: we are setting it to 1 because quirks mode on IE seems to have issues with 0
427 animateEl.stopAnimation();
428 animateEl.slideOut('t', {
429 duration: me.collapseDuration,
432 lastframe: function() {
433 animWrap.el.remove();
434 delete animWrap.record.animWrap;
440 animWrap.isAnimating = true;
443 <span id='Ext-tree-View-method-isAnimating'> /**
444 </span> * Checks if a node is currently undergoing animation
446 * @param {Ext.data.Model} node The node
447 * @return {Boolean} True if the node is animating
449 isAnimating: function(node) {
450 return !!this.animQueue[node.getId()];
453 collectData: function(records) {
454 var data = this.callParent(arguments),
460 for (; i < len; i++) {
463 if (record.get('qtip')) {
464 row.rowAttr = 'data-qtip="' + record.get('qtip') + '"';
465 if (record.get('qtitle')) {
466 row.rowAttr += ' ' + 'data-qtitle="' + record.get('qtitle') + '"';
469 if (record.isExpanded()) {
470 row.rowCls = (row.rowCls || '') + ' ' + this.expandedCls;
472 if (record.isLoading()) {
473 row.rowCls = (row.rowCls || '') + ' ' + this.loadingCls;
480 <span id='Ext-tree-View-method-expand'> /**
481 </span> * Expands a record that is loaded in the view.
482 * @param {Ext.data.Model} record The record to expand
483 * @param {Boolean} deep (optional) True to expand nodes all the way down the tree hierarchy.
484 * @param {Function} callback (optional) The function to run after the expand is completed
485 * @param {Object} scope (optional) The scope of the callback function.
487 expand: function(record, deep, callback, scope) {
488 return record.expand(deep, callback, scope);
491 <span id='Ext-tree-View-method-collapse'> /**
492 </span> * Collapses a record that is loaded in the view.
493 * @param {Ext.data.Model} record The record to collapse
494 * @param {Boolean} deep (optional) True to collapse nodes all the way up the tree hierarchy.
495 * @param {Function} callback (optional) The function to run after the collapse is completed
496 * @param {Object} scope (optional) The scope of the callback function.
498 collapse: function(record, deep, callback, scope) {
499 return record.collapse(deep, callback, scope);
502 <span id='Ext-tree-View-method-toggle'> /**
503 </span> * Toggles a record between expanded and collapsed.
504 * @param {Ext.data.Model} recordInstance
506 toggle: function(record) {
507 this[record.isExpanded() ? 'collapse' : 'expand'](record);
510 onItemDblClick: function(record, item, index) {
511 this.callParent(arguments);
512 if (this.toggleOnDblClick) {
517 onBeforeItemMouseDown: function(record, item, index, e) {
518 if (e.getTarget(this.expanderSelector, item)) {
521 return this.callParent(arguments);
524 onItemClick: function(record, item, index, e) {
525 if (e.getTarget(this.expanderSelector, item)) {
529 return this.callParent(arguments);
532 onExpanderMouseOver: function(e, t) {
533 e.getTarget(this.cellSelector, 10, true).addCls(this.expanderIconOverCls);
536 onExpanderMouseOut: function(e, t) {
537 e.getTarget(this.cellSelector, 10, true).removeCls(this.expanderIconOverCls);
540 <span id='Ext-tree-View-method-getTreeStore'> /**
541 </span> * Gets the base TreeStore from the bound TreePanel.
543 getTreeStore: function() {
544 return this.panel.store;
547 ensureSingleExpand: function(node) {
548 var parent = node.parentNode;
550 parent.eachChild(function(child) {
551 if (child !== node && child.isExpanded()) {