provide installation instructions
[extjs.git] / air / samples / tasks / js / TreeSelector.js
1 /*\r
2  * Ext JS Library 0.30\r
3  * Copyright(c) 2006-2009, Ext JS, LLC.\r
4  * licensing@extjs.com\r
5  * \r
6  * http://extjs.com/license\r
7  */\r
8 \r
9 // custom menu item to contain Ext trees\r
10 Ext.menu.TreeItem = Ext.extend(Ext.menu.Adapter, {\r
11         constructor : function(config){\r
12         Ext.menu.TreeItem.superclass.constructor.call(this, config.tree, config);\r
13         this.tree = this.component;\r
14         this.addEvents('selectionchange');\r
15 \r
16         this.tree.on("render", function(tree){\r
17             tree.body.swallowEvent(['click','keydown', 'keypress', 'keyup']);\r
18         });\r
19 \r
20         this.tree.getSelectionModel().on("selectionchange", this.onSelect, this);\r
21     },\r
22 \r
23     onSelect : function(tree, sel){\r
24         this.fireEvent("select", this, sel, tree);\r
25     }\r
26 });\r
27 \r
28 \r
29 // custom menu containing a single tree\r
30 Ext.menu.TreeMenu = Ext.extend(Ext.menu.Menu, {\r
31     cls:'x-tree-menu',\r
32         keyNav: true,\r
33         hideOnClick:false,\r
34     plain: true,\r
35 \r
36     constructor : function(config){\r
37         Ext.menu.TreeMenu.superclass.constructor.call(this, config);\r
38         this.treeItem = new Ext.menu.TreeItem(config);\r
39         this.add(this.treeItem);\r
40 \r
41         this.tree = this.treeItem.tree;\r
42         this.tree.on('click', this.onNodeClick, this);\r
43         this.relayEvents(this.treeItem, ["selectionchange"]);\r
44     },\r
45 \r
46     // private\r
47     beforeDestroy : function() {\r
48         this.tree.destroy();\r
49     },\r
50         \r
51         onNodeClick : function(node, e){\r
52                 if(!node.attributes.isFolder){\r
53                         this.treeItem.handleClick(e);\r
54                 }\r
55         }\r
56 });\r
57 \r
58 \r
59 // custom form field for displaying a tree, similar to select or combo\r
60 Ext.ux.TreeSelector = Ext.extend(Ext.form.TriggerField, {\r
61         initComponent : function(){\r
62                 Ext.ux.TreeSelector.superclass.initComponent.call(this);\r
63                 this.addEvents('selectionchange');\r
64 \r
65                 this.tree.getSelectionModel().on('selectionchange', this.onSelection, this);\r
66                 this.tree.on({\r
67                         'expandnode': this.sync,\r
68                         'collapsenode' : this.sync,\r
69                         'append' : this.sync,\r
70                         'remove' : this.sync,\r
71                         'insert' : this.sync,\r
72                         scope: this\r
73                 });\r
74                 this.on('focus', this.onTriggerClick, this);\r
75     },\r
76 \r
77         sync : function(){\r
78                 if(this.menu && this.menu.isVisible()){\r
79                         if(this.tree.body.getHeight() > this.maxHeight){\r
80                                 this.tree.body.setHeight(this.maxHeight);\r
81                                 this.restricted = true;\r
82                         }else if(this.restricted && this.tree.body.dom.firstChild.offsetHeight < this.maxHeight){\r
83                                 this.tree.body.setHeight('');\r
84                                 this.restricted = false;\r
85                         }\r
86                         this.menu.el.sync();\r
87                 }\r
88         },\r
89 \r
90         onSelection : function(tree, node){\r
91                 if(!node){\r
92                         this.setRawValue('');\r
93                 }else{\r
94                         this.setRawValue(node.text);\r
95                 }\r
96         },\r
97 \r
98         initEvents : function(){\r
99                 Ext.ux.TreeSelector.superclass.initEvents.call(this);\r
100                 this.el.on('mousedown', this.onTriggerClick, this);\r
101                 this.el.on("keydown", this.onKeyDown,  this);\r
102         },\r
103 \r
104         onKeyDown : function(e){\r
105                 if(e.getKey() == e.DOWN){\r
106                         this.onTriggerClick();\r
107                 }\r
108         },\r
109 \r
110     validateBlur : function(){\r
111         return !this.menu || !this.menu.isVisible();\r
112     },\r
113 \r
114     getValue : function(){\r
115                 var sm = this.tree.getSelectionModel();\r
116                 var s = sm.getSelectedNode();\r
117         return s ? s.id : '';\r
118     },\r
119 \r
120     setValue : function(id){\r
121                 var n = this.tree.getNodeById(id);\r
122                 if(n){\r
123                         n.select();\r
124                 }else{\r
125                         this.tree.getSelectionModel().clearSelections();\r
126                 }\r
127     },\r
128 \r
129     // private\r
130     onDestroy : function(){\r
131         if(this.menu) {\r
132             this.menu.destroy();\r
133         }\r
134         if(this.wrap){\r
135             this.wrap.remove();\r
136         }\r
137         Ext.ux.TreeSelector.superclass.onDestroy.call(this);\r
138     },\r
139 \r
140         // private\r
141     menuListeners : {\r
142         show : function(){ // retain focus styling\r
143             this.onFocus();\r
144         },\r
145         hide : function(){\r
146             this.focus.defer(10, this);\r
147             var ml = this.menuListeners;\r
148             this.menu.un("show", ml.show,  this);\r
149             this.menu.un("hide", ml.hide,  this);\r
150         }\r
151     },\r
152 \r
153     onTriggerClick : function(){\r
154                 if(this.disabled){\r
155             return;\r
156         }\r
157         this.menu.on(Ext.apply({}, this.menuListeners, {\r
158             scope:this\r
159         }));\r
160 \r
161         this.menu.show(this.el, "tl-bl?");\r
162                 this.sync();\r
163                 var sm = this.tree.getSelectionModel();\r
164                 var selected = sm.getSelectedNode();\r
165                 if(selected){\r
166                         selected.ensureVisible();\r
167                         sm.activate.defer(250, sm, [selected]);\r
168                 }\r
169     },\r
170 \r
171     beforeBlur : function(){\r
172         //\r
173     },\r
174 \r
175         onRender : function(){\r
176                 Ext.ux.TreeSelector.superclass.onRender.apply(this, arguments);\r
177                 this.menu = new Ext.menu.TreeMenu(Ext.apply(this.menuConfig || {}, {tree: this.tree}));\r
178                 this.menu.render();\r
179 \r
180                 this.tree.body.addClass('x-tree-selector');\r
181         },\r
182 \r
183         readOnly: true\r
184 });\r
185 \r
186 /*\r
187  * Custom tree keyboard navigation that supports node navigation without selection\r
188  */\r
189 Ext.tree.ActivationModel = Ext.extend(Ext.tree.DefaultSelectionModel, {\r
190         select : function(node){\r
191         return this.activate(Ext.tree.ActivationModel.superclass.select.call(this, node));\r
192     },\r
193     \r
194     activate : function(node){\r
195                 if(!node){\r
196                         return;\r
197                 }\r
198                 if(this.activated != node) {\r
199                         if(this.activated){\r
200                                 this.activated.ui.removeClass('x-tree-activated');\r
201                         }\r
202                         this.activated = node;\r
203                         node.ui.addClass('x-tree-activated');\r
204                 }\r
205                 node.ui.focus();\r
206                 return node;    \r
207         },\r
208         \r
209         activatePrevious : function(){\r
210         var s = this.activated;\r
211         if(!s){\r
212             return null;\r
213         }\r
214         var ps = s.previousSibling;\r
215         if(ps){\r
216             if(!ps.isExpanded() || ps.childNodes.length < 1){\r
217                 return this.activate(ps);\r
218             } else{\r
219                 var lc = ps.lastChild;\r
220                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){\r
221                     lc = lc.lastChild;\r
222                 }\r
223                 return this.activate(lc);\r
224             }\r
225         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){\r
226             return this.activate(s.parentNode);\r
227         }\r
228         return null;\r
229     },\r
230 \r
231     activateNext : function(){\r
232         var s = this.activated;\r
233         if(!s){\r
234             return null;\r
235         }\r
236         if(s.firstChild && s.isExpanded()){\r
237              return this.activate(s.firstChild);\r
238          }else if(s.nextSibling){\r
239              return this.activate(s.nextSibling);\r
240          }else if(s.parentNode){\r
241             var newS = null;\r
242             s.parentNode.bubble(function(){\r
243                 if(this.nextSibling){\r
244                     newS = this.getOwnerTree().selModel.activate(this.nextSibling);\r
245                     return false;\r
246                 }\r
247             });\r
248             return newS;\r
249          }\r
250         return null;\r
251     },\r
252 \r
253     onKeyDown : function(e){\r
254         var s = this.activated;\r
255         // undesirable, but required\r
256         var sm = this;\r
257         if(!s){\r
258             return;\r
259         }\r
260         var k = e.getKey();\r
261         switch(k){\r
262              case e.DOWN:\r
263                  e.stopEvent();\r
264                  this.activateNext();\r
265              break;\r
266              case e.UP:\r
267                  e.stopEvent();\r
268                  this.activatePrevious();\r
269              break;\r
270              case e.RIGHT:\r
271                  e.preventDefault();\r
272                  if(s.hasChildNodes()){\r
273                      if(!s.isExpanded()){\r
274                          s.expand();\r
275                      }else if(s.firstChild){\r
276                          this.activate(s.firstChild, e);\r
277                      }\r
278                  }\r
279              break;\r
280              case e.LEFT:\r
281                  e.preventDefault();\r
282                  if(s.hasChildNodes() && s.isExpanded()){\r
283                      s.collapse();\r
284                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){\r
285                      this.activate(s.parentNode, e);\r
286                  }\r
287              break;\r
288         };\r
289     }\r
290 });