Upgrade to ExtJS 3.0.3 - Released 10/11/2009
[extjs.git] / docs / source / debug.html
1 <html>
2 <head>
3   <title>The source code</title>
4     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
5     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
6 </head>
7 <body  onload="prettyPrint();">
8     <pre class="prettyprint lang-js">/*!
9  * Ext JS Library 3.0.3
10  * Copyright(c) 2006-2009 Ext JS, LLC
11  * licensing@extjs.com
12  * http://www.extjs.com/license
13  */
14 Ext.debug = {};
15
16 (function(){
17
18 var cp;
19
20 function createConsole(){
21
22     var scriptPanel = new Ext.debug.ScriptsPanel();
23     var logView = new Ext.debug.LogPanel();
24     var tree = new Ext.debug.DomTree();
25     var compInspector = new Ext.debug.ComponentInspector();
26     var compInfoPanel = new Ext.debug.ComponentInfoPanel();
27     var storeInspector = new Ext.debug.StoreInspector();
28     var objInspector = new Ext.debug.ObjectInspector();
29
30     var tabs = new Ext.TabPanel({
31         activeTab: 0,
32         border: false,
33         tabPosition: 'bottom',
34         items: [{
35             title: 'Debug Console',
36             layout:'border',
37             items: [logView, scriptPanel]
38         },{
39             title: 'HTML Inspector',
40             layout:'border',
41             items: [tree]
42         },{
43             title: 'Component Inspector',
44             layout: 'border',
45             items: [compInspector,compInfoPanel]
46         },{
47             title: 'Object Inspector',
48             layout: 'border',
49             items: [objInspector]
50         },{
51             title: 'Data Stores',
52             layout: 'border',
53             items: [storeInspector]
54         }]
55     });
56
57     cp = new Ext.Panel({
58         id: 'x-debug-browser',
59         title: 'Console',
60         collapsible: true,
61         animCollapse: false,
62         style: 'position:absolute;left:0;bottom:0;z-index:101',
63         height:200,
64         logView: logView,
65         layout: 'fit',
66
67         tools:[{
68             id: 'close',
69             handler: function(){
70                 cp.destroy();
71                 cp = null;
72                 Ext.EventManager.removeResizeListener(handleResize);
73             }
74         }],
75
76         items: tabs
77     });
78
79     cp.render(Ext.getBody());
80
81     cp.resizer = new Ext.Resizable(cp.el, {
82         minHeight:50,
83         handles: "n",
84         pinned: true,
85         transparent:true,
86         resizeElement : function(){
87             var box = this.proxy.getBox();
88             this.proxy.hide();
89             cp.setHeight(box.height);
90             return box;
91         }
92     });
93
94 //     function handleResize(){
95 //         cp.setWidth(Ext.getBody().getViewSize().width);
96 //     }
97 //     Ext.EventManager.onWindowResize(handleResize);
98 //
99 //     handleResize();
100
101     function handleResize(){
102         var b = Ext.getBody()
103         var size = b.getViewSize();
104         if(size.height < b.dom.scrollHeight) {
105             size.width -= 18;
106         }
107         cp.setWidth(size.width);
108     }
109     Ext.EventManager.onWindowResize(handleResize);
110     handleResize();
111 }
112
113
114 Ext.apply(Ext, {
115     log : function(){
116         if(!cp){
117             createConsole();
118         }
119         cp.logView.log.apply(cp.logView, arguments);
120     },
121
122     logf : function(format, arg1, arg2, etc){
123         Ext.log(String.format.apply(String, arguments));
124     },
125
126     dump : function(o){
127         if(typeof o == 'string' || typeof o == 'number' || typeof o == 'undefined' || Ext.isDate(o)){
128             Ext.log(o);
129         }else if(!o){
130             Ext.log("null");
131         }else if(typeof o != "object"){
132             Ext.log('Unknown return type');
133         }else if(Ext.isArray(o)){
134             Ext.log('['+o.join(',')+']');
135         }else{
136             var b = ["{\n"];
137             for(var key in o){
138                 var to = typeof o[key];
139                 if(to != "function" && to != "object"){
140                     b.push(String.format("  {0}: {1},\n", key, o[key]));
141                 }
142             }
143             var s = b.join("");
144             if(s.length > 3){
145                 s = s.substr(0, s.length-2);
146             }
147             Ext.log(s + "\n}");
148         }
149     },
150
151     _timers : {},
152
153     time : function(name){
154         name = name || "def";
155         Ext._timers[name] = new Date().getTime();
156     },
157
158     timeEnd : function(name, printResults){
159         var t = new Date().getTime();
160         name = name || "def";
161         var v = String.format("{0} ms", t-Ext._timers[name]);
162         Ext._timers[name] = new Date().getTime();
163         if(printResults !== false){
164             Ext.log('Timer ' + (name == "def" ? v : name + ": " + v));
165         }
166         return v;
167     }
168 });
169
170 })();
171
172
173 Ext.debug.ScriptsPanel = Ext.extend(Ext.Panel, {
174     id:'x-debug-scripts',
175     region: 'east',
176     minWidth: 200,
177     split: true,
178     width: 350,
179     border: false,
180     layout:'anchor',
181     style:'border-width:0 0 0 1px;',
182
183     initComponent : function(){
184
185         this.scriptField = new Ext.form.TextArea({
186             anchor: '100% -26',
187             style:'border-width:0;'
188         });
189
190         this.trapBox = new Ext.form.Checkbox({
191             id: 'console-trap',
192             boxLabel: 'Trap Errors',
193             checked: true
194         });
195
196         this.toolbar = new Ext.Toolbar([{
197                 text: 'Run',
198                 scope: this,
199                 handler: this.evalScript
200             },{
201                 text: 'Clear',
202                 scope: this,
203                 handler: this.clear
204             },
205             '->',
206             this.trapBox,
207             ' ', ' '
208         ]);
209
210         this.items = [this.toolbar, this.scriptField];
211
212         Ext.debug.ScriptsPanel.superclass.initComponent.call(this);
213     },
214
215     evalScript : function(){
216         var s = this.scriptField.getValue();
217         if(this.trapBox.getValue()){
218             try{
219                 var rt = eval(s);
220                 Ext.dump(rt === undefined? '(no return)' : rt);
221             }catch(e){
222                 Ext.log(e.message || e.descript);
223             }
224         }else{
225             var rt = eval(s);
226             Ext.dump(rt === undefined? '(no return)' : rt);
227         }
228     },
229
230     clear : function(){
231         this.scriptField.setValue('');
232         this.scriptField.focus();
233     }
234
235 });
236
237 Ext.debug.LogPanel = Ext.extend(Ext.Panel, {
238     autoScroll: true,
239     region: 'center',
240     border: false,
241     style:'border-width:0 1px 0 0',
242
243     log : function(){
244         var markup = [  '<div style="padding:5px !important;border-bottom:1px solid #ccc;">',
245                     Ext.util.Format.htmlEncode(Array.prototype.join.call(arguments, ', ')).replace(/\n/g, '<br/>').replace(/\s/g, '&#160;'),
246                     '</div>'].join('');
247
248         this.body.insertHtml('beforeend', markup);
249         this.body.scrollTo('top', 100000);
250     },
251
252     clear : function(){
253         this.body.update('');
254         this.body.dom.scrollTop = 0;
255     }
256 });
257
258 Ext.debug.DomTree = Ext.extend(Ext.tree.TreePanel, {
259     enableDD:false ,
260     lines:false,
261     rootVisible:false,
262     animate:false,
263     hlColor:'ffff9c',
264     autoScroll: true,
265     region:'center',
266     border:false,
267
268     initComponent : function(){
269
270
271         Ext.debug.DomTree.superclass.initComponent.call(this);
272
273         // tree related stuff
274         var styles = false, hnode;
275         var nonSpace = /^\s*$/;
276         var html = Ext.util.Format.htmlEncode;
277         var ellipsis = Ext.util.Format.ellipsis;
278         var styleRe = /\s?([a-z\-]*)\:([^;]*)(?:[;\s\n\r]*)/gi;
279
280         function findNode(n){
281             if(!n || n.nodeType != 1 || n == document.body || n == document){
282                 return false;
283             }
284             var pn = [n], p = n;
285             while((p = p.parentNode) && p.nodeType == 1 && p.tagName.toUpperCase() != 'HTML'){
286                 pn.unshift(p);
287             }
288             var cn = hnode;
289             for(var i = 0, len = pn.length; i < len; i++){
290                 cn.expand();
291                 cn = cn.findChild('htmlNode', pn[i]);
292                 if(!cn){ // in this dialog?
293                     return false;
294                 }
295             }
296             cn.select();
297             var a = cn.ui.anchor;
298             this.getTreeEl().dom.scrollTop = Math.max(0 ,a.offsetTop-10);
299             //treeEl.dom.scrollLeft = Math.max(0 ,a.offsetLeft-10); no likey
300             cn.highlight();
301             return true;
302         }
303
304         function nodeTitle(n){
305             var s = n.tagName;
306             if(n.id){
307                 s += '#'+n.id;
308             }else if(n.className){
309                 s += '.'+n.className;
310             }
311             return s;
312         }
313
314         /*
315         function onNodeSelect(t, n, last){
316             return;
317             if(last && last.unframe){
318                 last.unframe();
319             }
320             var props = {};
321             if(n && n.htmlNode){
322                 if(frameEl.pressed){
323                     n.frame();
324                 }
325                 if(inspecting){
326                     return;
327                 }
328                 addStyle.enable();
329                 reload.setDisabled(n.leaf);
330                 var dom = n.htmlNode;
331                 stylePanel.setTitle(nodeTitle(dom));
332                 if(styles && !showAll.pressed){
333                     var s = dom.style ? dom.style.cssText : '';
334                     if(s){
335                         var m;
336                         while ((m = styleRe.exec(s)) != null){
337                             props[m[1].toLowerCase()] = m[2];
338                         }
339                     }
340                 }else if(styles){
341                     var cl = Ext.debug.cssList;
342                     var s = dom.style, fly = Ext.fly(dom);
343                     if(s){
344                         for(var i = 0, len = cl.length; i<len; i++){
345                             var st = cl[i];
346                             var v = s[st] || fly.getStyle(st);
347                             if(v != undefined && v !== null && v !== ''){
348                                 props[st] = v;
349                             }
350                         }
351                     }
352                 }else{
353                     for(var a in dom){
354                         var v = dom[a];
355                         if((isNaN(a+10)) && v != undefined && v !== null && v !== '' && !(Ext.isGecko && a[0] == a[0].toUpperCase())){
356                             props[a] = v;
357                         }
358                     }
359                 }
360             }else{
361                 if(inspecting){
362                     return;
363                 }
364                 addStyle.disable();
365                 reload.disabled();
366             }
367             stylesGrid.setSource(props);
368             stylesGrid.treeNode = n;
369             stylesGrid.view.fitColumns();
370         }
371         */
372
373         this.loader = new Ext.tree.TreeLoader();
374         this.loader.load = function(n, cb){
375             var isBody = n.htmlNode == document.body;
376             var cn = n.htmlNode.childNodes;
377             for(var i = 0, c; c = cn[i]; i++){
378                 if(isBody && c.id == 'x-debug-browser'){
379                     continue;
380                 }
381                 if(c.nodeType == 1){
382                     n.appendChild(new Ext.debug.HtmlNode(c));
383                 }else if(c.nodeType == 3 && !nonSpace.test(c.nodeValue)){
384                     n.appendChild(new Ext.tree.TreeNode({
385                         text:'<em>' + ellipsis(html(String(c.nodeValue)), 35) + '</em>',
386                         cls: 'x-tree-noicon'
387                     }));
388                 }
389             }
390             cb();
391         };
392
393         //tree.getSelectionModel().on('selectionchange', onNodeSelect, null, {buffer:250});
394
395         this.root = this.setRootNode(new Ext.tree.TreeNode('Ext'));
396
397         hnode = this.root.appendChild(new Ext.debug.HtmlNode(
398                 document.getElementsByTagName('html')[0]
399         ));
400
401     }
402 });
403
404 Ext.debug.ComponentNodeUI = Ext.extend(Ext.tree.TreeNodeUI,{
405     onOver : function(e){
406         Ext.debug.ComponentNodeUI.superclass.onOver.call(this);
407         var cmp = this.node.attributes.component;
408         if (cmp.el && cmp.el.mask && cmp.id !='x-debug-browser') {
409             try { // Oddly bombs on some elements in IE, gets any we care about though
410                 cmp.el.mask();
411             } catch(e) {}
412         }
413     },
414
415     onOut : function(e){
416         Ext.debug.ComponentNodeUI.superclass.onOut.call(this);
417         var cmp = this.node.attributes.component;
418         if (cmp.el && cmp.el.unmask && cmp.id !='x-debug-browser') {
419             try {
420                 cmp.el.unmask();
421             } catch(e) {}
422         }
423     }
424 });
425
426 Ext.debug.ComponentInspector = Ext.extend(Ext.tree.TreePanel, {
427     enableDD:false ,
428     lines:false,
429     rootVisible:false,
430     animate:false,
431     hlColor:'ffff9c',
432     autoScroll: true,
433     region:'center',
434     border:false,
435
436     initComponent : function(){
437         this.loader = new Ext.tree.TreeLoader();
438         this.bbar = new Ext.Toolbar([{
439             text: 'Refresh',
440             handler: this.refresh,
441             scope: this
442         }]);
443         Ext.debug.ComponentInspector.superclass.initComponent.call(this);
444
445         this.root = this.setRootNode(new Ext.tree.TreeNode({
446             text: 'Ext Components',
447             component: Ext.ComponentMgr.all,
448             leaf: false
449         }));
450         this.parseRootNode();
451
452         this.on('click', this.onClick, this);
453     },
454
455     createNode: function(n,c) {
456         var leaf = (c.items && c.items.length > 0);
457         return n.appendChild(new Ext.tree.TreeNode({
458             text: c.id + (c.getXType() ? ' [ ' + c.getXType() + ' ]': '' ),
459             component: c,
460             uiProvider:Ext.debug.ComponentNodeUI,
461             leaf: !leaf
462         }));
463     },
464
465     parseChildItems: function(n) {
466         var cn = n.attributes.component.items;
467         if (cn) {
468             for (var i = 0;i < cn.length; i++) {
469                 var c = cn.get(i);
470                 if (c.id != this.id && c.id != this.bottomToolbar.id) {
471                     var newNode = this.createNode(n,c);
472                     if (!newNode.leaf) {
473                         this.parseChildItems(newNode)
474                     }
475                 }
476             }
477         }
478     },
479
480     parseRootNode: function() {
481         var n = this.root;
482         var cn = n.attributes.component.items;
483         for (var i = 0,c;c = cn[i];i++) {
484             if (c.id != this.id && c.id != this.bottomToolbar.id) {
485                 if (!c.ownerCt) {
486                     var newNode = this.createNode(n,c);
487                     if (!newNode.leaf) {
488                         this.parseChildItems(newNode);
489                     }
490                 }
491             }
492         }
493     },
494
495     onClick: function(node, e) {
496         var oi = Ext.getCmp('x-debug-objinspector');
497         oi.refreshNodes(node.attributes.component);
498         oi.ownerCt.show();
499     },
500
501     refresh: function() {
502         while (this.root.firstChild) {
503             this.root.removeChild(this.root.firstChild);
504         }
505         this.parseRootNode();
506         var ci = Ext.getCmp('x-debug-compinfo');
507         if (ci) {
508             ci.message('refreshed component tree - '+Ext.ComponentMgr.all.length)
509         }
510     }
511 });
512
513 Ext.debug.ComponentInfoPanel = Ext.extend(Ext.Panel,{
514     id:'x-debug-compinfo',
515     region: 'east',
516     minWidth: 200,
517     split: true,
518     width: 350,
519     border: false,
520     autoScroll: true,
521     layout:'anchor',
522     style:'border-width:0 0 0 1px;',
523
524     initComponent: function() {
525         this.watchBox = new Ext.form.Checkbox({
526             id: 'x-debug-watchcomp',
527             boxLabel: 'Watch ComponentMgr',
528             listeners: {
529                 check: function(cb, val) {
530                     if (val) {
531                         Ext.ComponentMgr.all.on('add', this.onAdd, this);
532                         Ext.ComponentMgr.all.on('remove', this.onRemove, this);
533                     } else {
534                         Ext.ComponentMgr.all.un('add', this.onAdd, this);
535                         Ext.ComponentMgr.all.un('remove', this.onRemove, this);
536                     }
537                 },
538                 scope: this
539             }
540         });
541
542         this.tbar = new Ext.Toolbar([{
543             text: 'Clear',
544             handler: this.clear,
545             scope: this
546         },'->',this.watchBox
547         ]);
548         Ext.debug.ComponentInfoPanel.superclass.initComponent.call(this);
549     },
550
551     onAdd: function(i, o, key) {
552         var markup = ['<div style="padding:5px !important;border-bottom:1px solid #ccc;">',
553                     'Added: '+o.id,
554                     '</div>'].join('');
555         this.insertMarkup(markup);
556     },
557
558     onRemove: function(o, key) {
559         var markup = ['<div style="padding:5px !important;border-bottom:1px solid #ccc;">',
560                     'Removed: '+o.id,
561                     '</div>'].join('');
562         this.insertMarkup(markup);
563     },
564
565     message: function(msg) {
566         var markup = ['<div style="padding:5px !important;border-bottom:1px solid #ccc;">',
567                     msg,
568                     '</div>'].join('');
569         this.insertMarkup(markup);
570     },
571     insertMarkup: function(markup) {
572         this.body.insertHtml('beforeend', markup);
573         this.body.scrollTo('top', 100000);
574     },
575     clear : function(){
576         this.body.update('');
577         this.body.dom.scrollTop = 0;
578     }
579 });
580
581 Ext.debug.ColumnNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
582     focus: Ext.emptyFn, // prevent odd scrolling behavior
583
584     renderElements : function(n, a, targetNode, bulkRender){
585         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
586
587         var t = n.getOwnerTree();
588         var cols = t.columns;
589         var bw = t.borderWidth;
590         var c = cols[0];
591
592         var buf = [
593              '<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf ', a.cls,'">',
594                 '<div class="x-tree-col" style="width:',c.width-bw,'px;">',
595                     '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
596                     '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow"/>',
597                     '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on"/>',
598                     '<a hidefocus="on" class="x-tree-node-anchor" href="',a.href ? a.href : "#",'" tabIndex="1" ',
599                     a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '>',
600                     '<span unselectable="on">', n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</span></a>",
601                 "</div>"];
602          for(var i = 1, len = cols.length; i < len; i++){
603              c = cols[i];
604
605              buf.push('<div class="x-tree-col ',(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
606                         '<div class="x-tree-col-text">',(c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</div>",
607                       "</div>");
608          }
609          buf.push(
610             '<div class="x-clear"></div></div>',
611             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
612             "</li>");
613
614         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
615             this.wrap = Ext.DomHelper.insertHtml("beforeBegin",
616                                 n.nextSibling.ui.getEl(), buf.join(""));
617         }else{
618             this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
619         }
620
621         this.elNode = this.wrap.childNodes[0];
622         this.ctNode = this.wrap.childNodes[1];
623         var cs = this.elNode.firstChild.childNodes;
624         this.indentNode = cs[0];
625         this.ecNode = cs[1];
626         this.iconNode = cs[2];
627         this.anchor = cs[3];
628         this.textNode = cs[3].firstChild;
629     }
630 });
631
632 Ext.debug.ObjectInspector = Ext.extend(Ext.tree.TreePanel, {
633     id: 'x-debug-objinspector',
634     enableDD:false ,
635     lines:false,
636     rootVisible:false,
637     animate:false,
638     hlColor:'ffff9c',
639     autoScroll: true,
640     region:'center',
641     border:false,
642     lines:false,
643     borderWidth: Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell
644     cls:'x-column-tree',
645
646     initComponent : function(){
647         this.showFunc = false;
648         this.toggleFunc = function() {
649             this.showFunc = !this.showFunc;
650             this.refreshNodes(this.currentObject);
651         }
652         this.bbar = new Ext.Toolbar([{
653             text: 'Show Functions',
654             enableToggle: true,
655             pressed: false,
656             handler: this.toggleFunc,
657             scope: this
658         }]);
659
660         Ext.apply(this,{
661             title: ' ',
662             loader: new Ext.tree.TreeLoader(),
663             columns:[{
664                 header:'Property',
665                 width: 300,
666                 dataIndex:'name'
667             },{
668                 header:'Value',
669                 width: 900,
670                 dataIndex:'value'
671             }]
672         });
673
674         Ext.debug.ObjectInspector.superclass.initComponent.call(this);
675
676         this.root = this.setRootNode(new Ext.tree.TreeNode({
677             text: 'Dummy Node',
678             leaf: false
679         }));
680
681         if (this.currentObject) {
682             this.parseNodes();
683         }
684     },
685
686     refreshNodes: function(newObj) {
687         this.currentObject = newObj;
688         var node = this.root;
689         while(node.firstChild){
690             node.removeChild(node.firstChild);
691         }
692         this.parseNodes();
693     },
694
695     parseNodes: function() {
696         for (var o in this.currentObject) {
697             if (!this.showFunc) {
698                 if (Ext.isFunction(this.currentObject[o])) {
699                     continue;
700                 }
701             }
702             this.createNode(o);
703         }
704     },
705
706     createNode: function(o) {
707         return this.root.appendChild(new Ext.tree.TreeNode({
708             name: o,
709             value: this.currentObject[o],
710             uiProvider:Ext.debug.ColumnNodeUI,
711             iconCls: 'x-debug-node',
712             leaf: true
713         }));
714     },
715
716     onRender : function(){
717         Ext.debug.ObjectInspector.superclass.onRender.apply(this, arguments);
718         this.headers = this.header.createChild({cls:'x-tree-headers'});
719
720         var cols = this.columns, c;
721         var totalWidth = 0;
722
723         for(var i = 0, len = cols.length; i < len; i++){
724              c = cols[i];
725              totalWidth += c.width;
726              this.headers.createChild({
727                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
728                  cn: {
729                      cls:'x-tree-hd-text',
730                      html: c.header
731                  },
732                  style:'width:'+(c.width-this.borderWidth)+'px;'
733              });
734         }
735         this.headers.createChild({cls:'x-clear'});
736         // prevent floats from wrapping when clipped
737         this.headers.setWidth(totalWidth);
738         this.innerCt.setWidth(totalWidth);
739     }
740 });
741
742
743 Ext.debug.StoreInspector = Ext.extend(Ext.tree.TreePanel, {
744     enableDD:false ,
745     lines:false,
746     rootVisible:false,
747     animate:false,
748     hlColor:'ffff9c',
749     autoScroll: true,
750     region:'center',
751     border:false,
752
753     initComponent: function() {
754         this.bbar = new Ext.Toolbar([{
755             text: 'Refresh',
756             handler: this.refresh,
757             scope: this
758         }]);
759         Ext.debug.StoreInspector.superclass.initComponent.call(this);
760
761         this.root = this.setRootNode(new Ext.tree.TreeNode({
762             text: 'Data Stores',
763             leaf: false
764         }));
765         this.on('click', this.onClick, this);
766
767         this.parseStores();
768     },
769
770     parseStores: function() {
771         var cn = Ext.StoreMgr.items;
772         for (var i = 0,c;c = cn[i];i++) {
773             this.root.appendChild({
774                 text: c.storeId + ' - ' + c.totalLength + ' records',
775                 component: c,
776                 leaf: true
777             });
778         }
779     },
780
781     onClick: function(node, e) {
782         var oi = Ext.getCmp('x-debug-objinspector');
783         oi.refreshNodes(node.attributes.component);
784         oi.ownerCt.show();
785     },
786
787     refresh: function() {
788         while (this.root.firstChild) {
789             this.root.removeChild(this.root.firstChild);
790         }
791         this.parseStores();
792     }
793 });
794
795 // highly unusual class declaration
796 Ext.debug.HtmlNode = function(){
797     var html = Ext.util.Format.htmlEncode;
798     var ellipsis = Ext.util.Format.ellipsis;
799     var nonSpace = /^\s*$/;
800
801     var attrs = [
802         {n: 'id', v: 'id'},
803         {n: 'className', v: 'class'},
804         {n: 'name', v: 'name'},
805         {n: 'type', v: 'type'},
806         {n: 'src', v: 'src'},
807         {n: 'href', v: 'href'}
808     ];
809
810     function hasChild(n){
811         for(var i = 0, c; c = n.childNodes[i]; i++){
812             if(c.nodeType == 1){
813                 return true;
814             }
815         }
816         return false;
817     }
818
819     function renderNode(n, leaf){
820         var tag = n.tagName.toLowerCase();
821         var s = '&lt;' + tag;
822         for(var i = 0, len = attrs.length; i < len; i++){
823             var a = attrs[i];
824             var v = n[a.n];
825             if(v && !nonSpace.test(v)){
826                 s += ' ' + a.v + '=&quot;<i>' + html(v) +'</i>&quot;';
827             }
828         }
829         var style = n.style ? n.style.cssText : '';
830         if(style){
831             s += ' style=&quot;<i>' + html(style.toLowerCase()) +'</i>&quot;';
832         }
833         if(leaf && n.childNodes.length > 0){
834             s+='&gt;<em>' + ellipsis(html(String(n.innerHTML)), 35) + '</em>&lt;/'+tag+'&gt;';
835         }else if(leaf){
836             s += ' /&gt;';
837         }else{
838             s += '&gt;';
839         }
840         return s;
841     }
842
843     var HtmlNode = function(n){
844         var leaf = !hasChild(n);
845         this.htmlNode = n;
846         this.tagName = n.tagName.toLowerCase();
847         var attr = {
848             text : renderNode(n, leaf),
849             leaf : leaf,
850             cls: 'x-tree-noicon'
851         };
852         HtmlNode.superclass.constructor.call(this, attr);
853         this.attributes.htmlNode = n; // for searching
854         if(!leaf){
855             this.on('expand', this.onExpand,  this);
856             this.on('collapse', this.onCollapse,  this);
857         }
858     };
859
860
861     Ext.extend(HtmlNode, Ext.tree.AsyncTreeNode, {
862         cls: 'x-tree-noicon',
863         preventHScroll: true,
864         refresh : function(highlight){
865             var leaf = !hasChild(this.htmlNode);
866             this.setText(renderNode(this.htmlNode, leaf));
867             if(highlight){
868                 Ext.fly(this.ui.textNode).highlight();
869             }
870         },
871
872         onExpand : function(){
873             if(!this.closeNode && this.parentNode){
874                 this.closeNode = this.parentNode.insertBefore(new Ext.tree.TreeNode({
875                     text:'&lt;/' + this.tagName + '&gt;',
876                     cls: 'x-tree-noicon'
877                 }), this.nextSibling);
878             }else if(this.closeNode){
879                 this.closeNode.ui.show();
880             }
881         },
882
883         onCollapse : function(){
884             if(this.closeNode){
885                 this.closeNode.ui.hide();
886             }
887         },
888
889         render : function(bulkRender){
890             HtmlNode.superclass.render.call(this, bulkRender);
891         },
892
893         highlightNode : function(){
894             //Ext.fly(this.htmlNode).highlight();
895         },
896
897         highlight : function(){
898             //Ext.fly(this.ui.textNode).highlight();
899         },
900
901         frame : function(){
902             this.htmlNode.style.border = '1px solid #0000ff';
903             //this.highlightNode();
904         },
905
906         unframe : function(){
907             //Ext.fly(this.htmlNode).removeClass('x-debug-frame');
908             this.htmlNode.style.border = '';
909         }
910     });
911
912     return HtmlNode;
913 }();</pre>
914 </body>
915 </html>