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