Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / examples / ux / treegrid / TreeGridSorter.js
1 /*!
2  * Ext JS Library 3.3.1
3  * Copyright(c) 2006-2010 Sencha Inc.
4  * licensing@sencha.com
5  * http://www.sencha.com/license
6  */
7 Ext.ns('Ext.ux.tree');
8
9 /**
10  * @class Ext.ux.tree.TreeGridSorter
11  * @extends Ext.tree.TreeSorter
12  * Provides sorting of nodes in a {@link Ext.ux.tree.TreeGrid}.  The TreeGridSorter automatically monitors events on the
13  * associated TreeGrid that might affect the tree's sort order (beforechildrenrendered, append, insert and textchange).
14  * Example usage:<br />
15  * <pre><code>
16  new Ext.ux.tree.TreeGridSorter(myTreeGrid, {
17      folderSort: true,
18      dir: "desc",
19      sortType: function(node) {
20          // sort by a custom, typed attribute:
21          return parseInt(node.id, 10);
22      }
23  });
24  </code></pre>
25  * @constructor
26  * @param {TreeGrid} tree
27  * @param {Object} config
28  */
29 Ext.ux.tree.TreeGridSorter = Ext.extend(Ext.tree.TreeSorter, {
30     /**
31      * @cfg {Array} sortClasses The CSS classes applied to a header when it is sorted. (defaults to <tt>['sort-asc', 'sort-desc']</tt>)
32      */
33     sortClasses : ['sort-asc', 'sort-desc'],
34     /**
35      * @cfg {String} sortAscText The text displayed in the 'Sort Ascending' menu item (defaults to <tt>'Sort Ascending'</tt>)
36      */
37     sortAscText : 'Sort Ascending',
38     /**
39      * @cfg {String} sortDescText The text displayed in the 'Sort Descending' menu item (defaults to <tt>'Sort Descending'</tt>)
40      */
41     sortDescText : 'Sort Descending',
42
43     constructor : function(tree, config) {
44         if(!Ext.isObject(config)) {
45             config = {
46                 property: tree.columns[0].dataIndex || 'text',
47                 folderSort: true
48             }
49         }
50
51         Ext.ux.tree.TreeGridSorter.superclass.constructor.apply(this, arguments);
52
53         this.tree = tree;
54         tree.on('headerclick', this.onHeaderClick, this);
55         tree.ddAppendOnly = true;
56
57         var me = this;
58         this.defaultSortFn = function(n1, n2){
59
60             var desc = me.dir && me.dir.toLowerCase() == 'desc',
61                 prop = me.property || 'text',
62                 sortType = me.sortType,
63                 caseSensitive = me.caseSensitive === true,
64                 leafAttr = me.leafAttr || 'leaf',
65                 attr1 = n1.attributes,
66                 attr2 = n2.attributes;
67
68             if(me.folderSort){
69                 if(attr1[leafAttr] && !attr2[leafAttr]){
70                     return 1;
71                 }
72                 if(!attr1[leafAttr] && attr2[leafAttr]){
73                     return -1;
74                 }
75             }
76             var prop1 = attr1[prop],
77                 prop2 = attr2[prop],
78                 v1 = sortType ? sortType(prop1) : (caseSensitive ? prop1 : prop1.toUpperCase());
79                 v2 = sortType ? sortType(prop2) : (caseSensitive ? prop2 : prop2.toUpperCase());
80                 
81             if(v1 < v2){
82                 return desc ? +1 : -1;
83             }else if(v1 > v2){
84                 return desc ? -1 : +1;
85             }else{
86                 return 0;
87             }
88         };
89
90         tree.on('afterrender', this.onAfterTreeRender, this, {single: true});
91         tree.on('headermenuclick', this.onHeaderMenuClick, this);
92     },
93
94     onAfterTreeRender : function() {
95         if(this.tree.hmenu){
96             this.tree.hmenu.insert(0,
97                 {itemId:'asc', text: this.sortAscText, cls: 'xg-hmenu-sort-asc'},
98                 {itemId:'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'}
99             );
100         }
101         this.updateSortIcon(0, 'asc');
102     },
103
104     onHeaderMenuClick : function(c, id, index) {
105         if(id === 'asc' || id === 'desc') {
106             this.onHeaderClick(c, null, index);
107             return false;
108         }
109     },
110
111     onHeaderClick : function(c, el, i) {
112         if(c && !this.tree.headersDisabled){
113             var me = this;
114
115             me.property = c.dataIndex;
116             me.dir = c.dir = (c.dir === 'desc' ? 'asc' : 'desc');
117             me.sortType = c.sortType;
118             me.caseSensitive === Ext.isBoolean(c.caseSensitive) ? c.caseSensitive : this.caseSensitive;
119             me.sortFn = c.sortFn || this.defaultSortFn;
120
121             this.tree.root.cascade(function(n) {
122                 if(!n.isLeaf()) {
123                     me.updateSort(me.tree, n);
124                 }
125             });
126
127             this.updateSortIcon(i, c.dir);
128         }
129     },
130
131     // private
132     updateSortIcon : function(col, dir){
133         var sc = this.sortClasses,
134             hds = this.tree.innerHd.select('td').removeClass(sc);
135         hds.item(col).addClass(sc[dir == 'desc' ? 1 : 0]);
136     }
137 });