--- /dev/null
+<html>\r
+<head>\r
+ <title>The source code</title>\r
+ <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />\r
+ <script type="text/javascript" src="../resources/prettify/prettify.js"></script>\r
+</head>\r
+<body onload="prettyPrint();">\r
+ <pre class="prettyprint lang-js">// custom menu item to contain Ext trees\r
+Ext.menu.TreeItem = Ext.extend(Ext.menu.Adapter, {\r
+ constructor : function(config){\r
+ Ext.menu.TreeItem.superclass.constructor.call(this, config.tree, config);\r
+ this.tree = this.component;\r
+ this.addEvents('selectionchange');\r
+\r
+ this.tree.on("render", function(tree){\r
+ tree.body.swallowEvent(['click','keydown', 'keypress', 'keyup']);\r
+ });\r
+\r
+ this.tree.getSelectionModel().on("selectionchange", this.onSelect, this);\r
+ },\r
+\r
+ onSelect : function(tree, sel){\r
+ this.fireEvent("select", this, sel, tree);\r
+ }\r
+});\r
+\r
+\r
+// custom menu containing a single tree\r
+Ext.menu.TreeMenu = Ext.extend(Ext.menu.Menu, {\r
+ cls:'x-tree-menu',\r
+ keyNav: true,\r
+ hideOnClick:false,\r
+ plain: true,\r
+\r
+ constructor : function(config){\r
+ Ext.menu.TreeMenu.superclass.constructor.call(this, config);\r
+ this.treeItem = new Ext.menu.TreeItem(config);\r
+ this.add(this.treeItem);\r
+\r
+ this.tree = this.treeItem.tree;\r
+ this.tree.on('click', this.onNodeClick, this);\r
+ this.relayEvents(this.treeItem, ["selectionchange"]);\r
+ },\r
+\r
+ // private\r
+ beforeDestroy : function() {\r
+ this.tree.destroy();\r
+ },\r
+ \r
+ onNodeClick : function(node, e){\r
+ if(!node.attributes.isFolder){\r
+ this.treeItem.handleClick(e);\r
+ }\r
+ }\r
+});\r
+\r
+\r
+// custom form field for displaying a tree, similar to select or combo\r
+Ext.ux.TreeSelector = Ext.extend(Ext.form.TriggerField, {\r
+ initComponent : function(){\r
+ Ext.ux.TreeSelector.superclass.initComponent.call(this);\r
+ this.addEvents('selectionchange');\r
+\r
+ this.tree.getSelectionModel().on('selectionchange', this.onSelection, this);\r
+ this.tree.on({\r
+ 'expandnode': this.sync,\r
+ 'collapsenode' : this.sync,\r
+ 'append' : this.sync,\r
+ 'remove' : this.sync,\r
+ 'insert' : this.sync,\r
+ scope: this\r
+ });\r
+ this.on('focus', this.onTriggerClick, this);\r
+ },\r
+\r
+ sync : function(){\r
+ if(this.menu && this.menu.isVisible()){\r
+ if(this.tree.body.getHeight() > this.maxHeight){\r
+ this.tree.body.setHeight(this.maxHeight);\r
+ this.restricted = true;\r
+ }else if(this.restricted && this.tree.body.dom.firstChild.offsetHeight < this.maxHeight){\r
+ this.tree.body.setHeight('');\r
+ this.restricted = false;\r
+ }\r
+ this.menu.el.sync();\r
+ }\r
+ },\r
+\r
+ onSelection : function(tree, node){\r
+ if(!node){\r
+ this.setRawValue('');\r
+ }else{\r
+ this.setRawValue(node.text);\r
+ }\r
+ },\r
+\r
+ initEvents : function(){\r
+ Ext.ux.TreeSelector.superclass.initEvents.call(this);\r
+ this.el.on('mousedown', this.onTriggerClick, this);\r
+ this.el.on("keydown", this.onKeyDown, this);\r
+ },\r
+\r
+ onKeyDown : function(e){\r
+ if(e.getKey() == e.DOWN){\r
+ this.onTriggerClick();\r
+ }\r
+ },\r
+\r
+ validateBlur : function(){\r
+ return !this.menu || !this.menu.isVisible();\r
+ },\r
+\r
+ getValue : function(){\r
+ var sm = this.tree.getSelectionModel();\r
+ var s = sm.getSelectedNode();\r
+ return s ? s.id : '';\r
+ },\r
+\r
+ setValue : function(id){\r
+ var n = this.tree.getNodeById(id);\r
+ if(n){\r
+ n.select();\r
+ }else{\r
+ this.tree.getSelectionModel().clearSelections();\r
+ }\r
+ },\r
+\r
+ // private\r
+ onDestroy : function(){\r
+ if(this.menu) {\r
+ this.menu.destroy();\r
+ }\r
+ if(this.wrap){\r
+ this.wrap.remove();\r
+ }\r
+ Ext.ux.TreeSelector.superclass.onDestroy.call(this);\r
+ },\r
+\r
+ // private\r
+ menuListeners : {\r
+ show : function(){ // retain focus styling\r
+ this.onFocus();\r
+ },\r
+ hide : function(){\r
+ this.focus.defer(10, this);\r
+ var ml = this.menuListeners;\r
+ this.menu.un("show", ml.show, this);\r
+ this.menu.un("hide", ml.hide, this);\r
+ }\r
+ },\r
+\r
+ onTriggerClick : function(){\r
+ if(this.disabled){\r
+ return;\r
+ }\r
+ this.menu.on(Ext.apply({}, this.menuListeners, {\r
+ scope:this\r
+ }));\r
+\r
+ this.menu.show(this.el, "tl-bl?");\r
+ this.sync();\r
+ var sm = this.tree.getSelectionModel();\r
+ var selected = sm.getSelectedNode();\r
+ if(selected){\r
+ selected.ensureVisible();\r
+ sm.activate.defer(250, sm, [selected]);\r
+ }\r
+ },\r
+\r
+ beforeBlur : function(){\r
+ //\r
+ },\r
+\r
+ onRender : function(){\r
+ Ext.ux.TreeSelector.superclass.onRender.apply(this, arguments);\r
+ this.menu = new Ext.menu.TreeMenu(Ext.apply(this.menuConfig || {}, {tree: this.tree}));\r
+ this.menu.render();\r
+\r
+ this.tree.body.addClass('x-tree-selector');\r
+ },\r
+\r
+ readOnly: true\r
+});\r
+\r
+/*\r
+ * Custom tree keyboard navigation that supports node navigation without selection\r
+ */\r
+Ext.tree.ActivationModel = Ext.extend(Ext.tree.DefaultSelectionModel, {\r
+ select : function(node){\r
+ return this.activate(Ext.tree.ActivationModel.superclass.select.call(this, node));\r
+ },\r
+ \r
+ activate : function(node){\r
+ if(!node){\r
+ return;\r
+ }\r
+ if(this.activated != node) {\r
+ if(this.activated){\r
+ this.activated.ui.removeClass('x-tree-activated');\r
+ }\r
+ this.activated = node;\r
+ node.ui.addClass('x-tree-activated');\r
+ }\r
+ node.ui.focus();\r
+ return node; \r
+ },\r
+ \r
+ activatePrevious : function(){\r
+ var s = this.activated;\r
+ if(!s){\r
+ return null;\r
+ }\r
+ var ps = s.previousSibling;\r
+ if(ps){\r
+ if(!ps.isExpanded() || ps.childNodes.length < 1){\r
+ return this.activate(ps);\r
+ } else{\r
+ var lc = ps.lastChild;\r
+ while(lc && lc.isExpanded() && lc.childNodes.length > 0){\r
+ lc = lc.lastChild;\r
+ }\r
+ return this.activate(lc);\r
+ }\r
+ } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){\r
+ return this.activate(s.parentNode);\r
+ }\r
+ return null;\r
+ },\r
+\r
+ activateNext : function(){\r
+ var s = this.activated;\r
+ if(!s){\r
+ return null;\r
+ }\r
+ if(s.firstChild && s.isExpanded()){\r
+ return this.activate(s.firstChild);\r
+ }else if(s.nextSibling){\r
+ return this.activate(s.nextSibling);\r
+ }else if(s.parentNode){\r
+ var newS = null;\r
+ s.parentNode.bubble(function(){\r
+ if(this.nextSibling){\r
+ newS = this.getOwnerTree().selModel.activate(this.nextSibling);\r
+ return false;\r
+ }\r
+ });\r
+ return newS;\r
+ }\r
+ return null;\r
+ },\r
+\r
+ onKeyDown : function(e){\r
+ var s = this.activated;\r
+ // undesirable, but required\r
+ var sm = this;\r
+ if(!s){\r
+ return;\r
+ }\r
+ var k = e.getKey();\r
+ switch(k){\r
+ case e.DOWN:\r
+ e.stopEvent();\r
+ this.activateNext();\r
+ break;\r
+ case e.UP:\r
+ e.stopEvent();\r
+ this.activatePrevious();\r
+ break;\r
+ case e.RIGHT:\r
+ e.preventDefault();\r
+ if(s.hasChildNodes()){\r
+ if(!s.isExpanded()){\r
+ s.expand();\r
+ }else if(s.firstChild){\r
+ this.activate(s.firstChild, e);\r
+ }\r
+ }\r
+ break;\r
+ case e.LEFT:\r
+ e.preventDefault();\r
+ if(s.hasChildNodes() && s.isExpanded()){\r
+ s.collapse();\r
+ }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){\r
+ this.activate(s.parentNode, e);\r
+ }\r
+ break;\r
+ };\r
+ }\r
+});</pre> \r
+</body>\r
+</html>
\ No newline at end of file