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