commit extjs-2.2.1
[extjs.git] / source / debug.js
1 /*\r
2  * Ext JS Library 2.2.1\r
3  * Copyright(c) 2006-2009, Ext JS, LLC.\r
4  * licensing@extjs.com\r
5  * \r
6  * http://extjs.com/license\r
7  */\r
8 \r
9 Ext.debug = {};\r
10 \r
11 (function(){\r
12 \r
13 var cp;\r
14 \r
15 function createConsole(){\r
16 \r
17     var scriptPanel = new Ext.debug.ScriptsPanel();\r
18     var logView = new Ext.debug.LogPanel();\r
19     var tree = new Ext.debug.DomTree();\r
20 \r
21     var tabs = new Ext.TabPanel({\r
22         activeTab: 0,\r
23         border: false,\r
24         tabPosition: 'bottom',\r
25         items: [{\r
26             title: 'Debug Console',\r
27             layout:'border',\r
28             items: [logView, scriptPanel]\r
29         },{\r
30             title: 'DOM Inspector',\r
31             layout:'border',\r
32             items: [tree]\r
33         }]\r
34     });\r
35 \r
36     cp = new Ext.Panel({\r
37         id: 'x-debug-browser',\r
38         title: 'Console',\r
39         collapsible: true,\r
40         animCollapse: false,\r
41         style: 'position:absolute;left:0;bottom:0;',\r
42         height:200,\r
43         logView: logView,\r
44         layout: 'fit',\r
45         \r
46         tools:[{\r
47             id: 'close',\r
48             handler: function(){\r
49                 cp.destroy();\r
50                 cp = null;\r
51                 Ext.EventManager.removeResizeListener(handleResize);\r
52             }\r
53         }],\r
54 \r
55         items: tabs\r
56     });\r
57 \r
58     cp.render(document.body);\r
59 \r
60     cp.resizer = new Ext.Resizable(cp.el, {\r
61         minHeight:50,\r
62         handles: "n",\r
63         pinned: true,\r
64         transparent:true,\r
65         resizeElement : function(){\r
66             var box = this.proxy.getBox();\r
67             this.proxy.hide();\r
68             cp.setHeight(box.height);\r
69             return box;\r
70         }\r
71     });\r
72 \r
73     function handleResize(){\r
74         cp.setWidth(Ext.getBody().getViewSize().width);\r
75     }\r
76     Ext.EventManager.onWindowResize(handleResize);\r
77 \r
78     handleResize();\r
79 }\r
80 \r
81 \r
82 Ext.apply(Ext, {\r
83     log : function(){\r
84         if(!cp){\r
85             createConsole();\r
86         }\r
87         cp.logView.log.apply(cp.logView, arguments);\r
88     },\r
89 \r
90     logf : function(format, arg1, arg2, etc){\r
91         Ext.log(String.format.apply(String, arguments));\r
92     },\r
93 \r
94     dump : function(o){\r
95         if(typeof o == 'string' || typeof o == 'number' || typeof o == 'undefined' || Ext.isDate(o)){\r
96             Ext.log(o);\r
97         }else if(!o){\r
98             Ext.log("null");\r
99         }else if(typeof o != "object"){\r
100             Ext.log('Unknown return type');\r
101         }else if(Ext.isArray(o)){\r
102             Ext.log('['+o.join(',')+']');\r
103         }else{\r
104             var b = ["{\n"];\r
105             for(var key in o){\r
106                 var to = typeof o[key];\r
107                 if(to != "function" && to != "object"){\r
108                     b.push(String.format("  {0}: {1},\n", key, o[key]));\r
109                 }\r
110             }\r
111             var s = b.join("");\r
112             if(s.length > 3){\r
113                 s = s.substr(0, s.length-2);\r
114             }\r
115             Ext.log(s + "\n}");\r
116         }\r
117     },\r
118 \r
119     _timers : {},\r
120 \r
121     time : function(name){\r
122         name = name || "def";\r
123         Ext._timers[name] = new Date().getTime();\r
124     },\r
125 \r
126     timeEnd : function(name, printResults){\r
127         var t = new Date().getTime();\r
128         name = name || "def";\r
129         var v = String.format("{0} ms", t-Ext._timers[name]);\r
130         Ext._timers[name] = new Date().getTime();\r
131         if(printResults !== false){\r
132             Ext.log('Timer ' + (name == "def" ? v : name + ": " + v));\r
133         }\r
134         return v;\r
135     }\r
136 });\r
137 \r
138 })();\r
139 \r
140 \r
141 Ext.debug.ScriptsPanel = Ext.extend(Ext.Panel, {\r
142     id:'x-debug-scripts',\r
143     region: 'east',\r
144     minWidth: 200,\r
145     split: true,\r
146     width: 350,\r
147     border: false,\r
148     layout:'anchor',\r
149     style:'border-width:0 0 0 1px;',\r
150 \r
151     initComponent : function(){\r
152 \r
153         this.scriptField = new Ext.form.TextArea({\r
154             anchor: '100% -26',\r
155             style:'border-width:0;'\r
156         });\r
157 \r
158         this.trapBox = new Ext.form.Checkbox({\r
159             id: 'console-trap',\r
160             boxLabel: 'Trap Errors',\r
161             checked: true\r
162         });\r
163 \r
164         this.toolbar = new Ext.Toolbar([{\r
165                 text: 'Run',\r
166                 scope: this,\r
167                 handler: this.evalScript\r
168             },{\r
169                 text: 'Clear',\r
170                 scope: this,\r
171                 handler: this.clear\r
172             },\r
173             '->',\r
174             this.trapBox,\r
175             ' ', ' '\r
176         ]);\r
177 \r
178         this.items = [this.toolbar, this.scriptField];\r
179 \r
180         Ext.debug.ScriptsPanel.superclass.initComponent.call(this);\r
181     },\r
182 \r
183     evalScript : function(){\r
184         var s = this.scriptField.getValue();\r
185         if(this.trapBox.getValue()){\r
186             try{\r
187                 var rt = eval(s);\r
188                 Ext.dump(rt === undefined? '(no return)' : rt);\r
189             }catch(e){\r
190                 Ext.log(e.message || e.descript);\r
191             }\r
192         }else{\r
193             var rt = eval(s);\r
194             Ext.dump(rt === undefined? '(no return)' : rt);\r
195         }\r
196     },\r
197 \r
198     clear : function(){\r
199         this.scriptField.setValue('');\r
200         this.scriptField.focus();\r
201     }\r
202 \r
203 });\r
204 \r
205 Ext.debug.LogPanel = Ext.extend(Ext.Panel, {\r
206     autoScroll: true,\r
207     region: 'center',\r
208     border: false,\r
209     style:'border-width:0 1px 0 0',\r
210 \r
211     log : function(){\r
212         var markup = [  '<div style="padding:5px !important;border-bottom:1px solid #ccc;">',\r
213                     Ext.util.Format.htmlEncode(Array.prototype.join.call(arguments, ', ')).replace(/\n/g, '<br />').replace(/\s/g, '&#160;'),\r
214                     '</div>'].join('');\r
215 \r
216         this.body.insertHtml('beforeend', markup);\r
217         this.body.scrollTo('top', 100000);\r
218     },\r
219 \r
220     clear : function(){\r
221         this.body.update('');\r
222         this.body.dom.scrollTop = 0;\r
223     }\r
224 });\r
225 \r
226 Ext.debug.DomTree = Ext.extend(Ext.tree.TreePanel, {\r
227     enableDD:false ,\r
228     lines:false,\r
229     rootVisible:false,\r
230     animate:false,\r
231     hlColor:'ffff9c',\r
232     autoScroll: true,\r
233     region:'center',\r
234     border:false,\r
235 \r
236     initComponent : function(){\r
237 \r
238 \r
239         Ext.debug.DomTree.superclass.initComponent.call(this);\r
240         \r
241         // tree related stuff\r
242         var styles = false, hnode;\r
243         var nonSpace = /^\s*$/;\r
244         var html = Ext.util.Format.htmlEncode;\r
245         var ellipsis = Ext.util.Format.ellipsis;\r
246         var styleRe = /\s?([a-z\-]*)\:([^;]*)(?:[;\s\n\r]*)/gi;\r
247 \r
248         function findNode(n){\r
249             if(!n || n.nodeType != 1 || n == document.body || n == document){\r
250                 return false;\r
251             }\r
252             var pn = [n], p = n;\r
253             while((p = p.parentNode) && p.nodeType == 1 && p.tagName.toUpperCase() != 'HTML'){\r
254                 pn.unshift(p);\r
255             }\r
256             var cn = hnode;\r
257             for(var i = 0, len = pn.length; i < len; i++){\r
258                 cn.expand();\r
259                 cn = cn.findChild('htmlNode', pn[i]);\r
260                 if(!cn){ // in this dialog?\r
261                     return false;\r
262                 }\r
263             }\r
264             cn.select();\r
265             var a = cn.ui.anchor;\r
266             treeEl.dom.scrollTop = Math.max(0 ,a.offsetTop-10);\r
267             //treeEl.dom.scrollLeft = Math.max(0 ,a.offsetLeft-10); no likey\r
268             cn.highlight();\r
269             return true;\r
270         }\r
271 \r
272         function nodeTitle(n){\r
273             var s = n.tagName;\r
274             if(n.id){\r
275                 s += '#'+n.id;\r
276             }else if(n.className){\r
277                 s += '.'+n.className;\r
278             }\r
279             return s;\r
280         }\r
281 \r
282         function onNodeSelect(t, n, last){\r
283             return;\r
284             if(last && last.unframe){\r
285                 last.unframe();\r
286             }\r
287             var props = {};\r
288             if(n && n.htmlNode){\r
289                 if(frameEl.pressed){\r
290                     n.frame();\r
291                 }\r
292                 if(inspecting){\r
293                     return;\r
294                 }\r
295                 addStyle.enable();\r
296                 reload.setDisabled(n.leaf);\r
297                 var dom = n.htmlNode;\r
298                 stylePanel.setTitle(nodeTitle(dom));\r
299                 if(styles && !showAll.pressed){\r
300                     var s = dom.style ? dom.style.cssText : '';\r
301                     if(s){\r
302                         var m;\r
303                         while ((m = styleRe.exec(s)) != null){\r
304                             props[m[1].toLowerCase()] = m[2];\r
305                         }\r
306                     }\r
307                 }else if(styles){\r
308                     var cl = Ext.debug.cssList;\r
309                     var s = dom.style, fly = Ext.fly(dom);\r
310                     if(s){\r
311                         for(var i = 0, len = cl.length; i<len; i++){\r
312                             var st = cl[i];\r
313                             var v = s[st] || fly.getStyle(st);\r
314                             if(v != undefined && v !== null && v !== ''){\r
315                                 props[st] = v;\r
316                             }\r
317                         }\r
318                     }\r
319                 }else{\r
320                     for(var a in dom){\r
321                         var v = dom[a];\r
322                         if((isNaN(a+10)) && v != undefined && v !== null && v !== '' && !(Ext.isGecko && a[0] == a[0].toUpperCase())){\r
323                             props[a] = v;\r
324                         }\r
325                     }\r
326                 }\r
327             }else{\r
328                 if(inspecting){\r
329                     return;\r
330                 }\r
331                 addStyle.disable();\r
332                 reload.disabled();\r
333             }\r
334             stylesGrid.setSource(props);\r
335             stylesGrid.treeNode = n;\r
336             stylesGrid.view.fitColumns();\r
337         }\r
338 \r
339         this.loader = new Ext.tree.TreeLoader();\r
340         this.loader.load = function(n, cb){\r
341             var isBody = n.htmlNode == document.body;\r
342             var cn = n.htmlNode.childNodes;\r
343             for(var i = 0, c; c = cn[i]; i++){\r
344                 if(isBody && c.id == 'x-debug-browser'){\r
345                     continue;\r
346                 }\r
347                 if(c.nodeType == 1){\r
348                     n.appendChild(new Ext.debug.HtmlNode(c));\r
349                 }else if(c.nodeType == 3 && !nonSpace.test(c.nodeValue)){\r
350                     n.appendChild(new Ext.tree.TreeNode({\r
351                         text:'<em>' + ellipsis(html(String(c.nodeValue)), 35) + '</em>',\r
352                         cls: 'x-tree-noicon'\r
353                     }));\r
354                 }\r
355             }\r
356             cb();\r
357         };\r
358 \r
359         //tree.getSelectionModel().on('selectionchange', onNodeSelect, null, {buffer:250});\r
360 \r
361         this.root = this.setRootNode(new Ext.tree.TreeNode('Ext'));\r
362 \r
363         hnode = this.root.appendChild(new Ext.debug.HtmlNode(\r
364                 document.getElementsByTagName('html')[0]\r
365         ));\r
366 \r
367     }\r
368 });\r
369 \r
370 \r
371 // highly unusual class declaration\r
372 Ext.debug.HtmlNode = function(){\r
373     var html = Ext.util.Format.htmlEncode;\r
374     var ellipsis = Ext.util.Format.ellipsis;\r
375     var nonSpace = /^\s*$/;\r
376 \r
377     var attrs = [\r
378         {n: 'id', v: 'id'},\r
379         {n: 'className', v: 'class'},\r
380         {n: 'name', v: 'name'},\r
381         {n: 'type', v: 'type'},\r
382         {n: 'src', v: 'src'},\r
383         {n: 'href', v: 'href'}\r
384     ];\r
385 \r
386     function hasChild(n){\r
387         for(var i = 0, c; c = n.childNodes[i]; i++){\r
388             if(c.nodeType == 1){\r
389                 return true;\r
390             }\r
391         }\r
392         return false;\r
393     }\r
394 \r
395     function renderNode(n, leaf){\r
396         var tag = n.tagName.toLowerCase();\r
397         var s = '&lt;' + tag;\r
398         for(var i = 0, len = attrs.length; i < len; i++){\r
399             var a = attrs[i];\r
400             var v = n[a.n];\r
401             if(v && !nonSpace.test(v)){\r
402                 s += ' ' + a.v + '=&quot;<i>' + html(v) +'</i>&quot;';\r
403             }\r
404         }\r
405         var style = n.style ? n.style.cssText : '';\r
406         if(style){\r
407             s += ' style=&quot;<i>' + html(style.toLowerCase()) +'</i>&quot;';\r
408         }\r
409         if(leaf && n.childNodes.length > 0){\r
410             s+='&gt;<em>' + ellipsis(html(String(n.innerHTML)), 35) + '</em>&lt;/'+tag+'&gt;';\r
411         }else if(leaf){\r
412             s += ' /&gt;';\r
413         }else{\r
414             s += '&gt;';\r
415         }\r
416         return s;\r
417     }\r
418 \r
419     var HtmlNode = function(n){\r
420         var leaf = !hasChild(n);\r
421         this.htmlNode = n;\r
422         this.tagName = n.tagName.toLowerCase();\r
423         var attr = {\r
424             text : renderNode(n, leaf),\r
425             leaf : leaf,\r
426             cls: 'x-tree-noicon'\r
427         };\r
428         HtmlNode.superclass.constructor.call(this, attr);\r
429         this.attributes.htmlNode = n; // for searching\r
430         if(!leaf){\r
431             this.on('expand', this.onExpand,  this);\r
432             this.on('collapse', this.onCollapse,  this);\r
433         }\r
434     };\r
435 \r
436 \r
437     Ext.extend(HtmlNode, Ext.tree.AsyncTreeNode, {\r
438         cls: 'x-tree-noicon',\r
439         preventHScroll: true,\r
440         refresh : function(highlight){\r
441             var leaf = !hasChild(this.htmlNode);\r
442             this.setText(renderNode(this.htmlNode, leaf));\r
443             if(highlight){\r
444                 Ext.fly(this.ui.textNode).highlight();\r
445             }\r
446         },\r
447 \r
448         onExpand : function(){\r
449             if(!this.closeNode && this.parentNode){\r
450                 this.closeNode = this.parentNode.insertBefore(new Ext.tree.TreeNode({\r
451                     text:'&lt;/' + this.tagName + '&gt;',\r
452                     cls: 'x-tree-noicon'\r
453                 }), this.nextSibling);\r
454             }else if(this.closeNode){\r
455                 this.closeNode.ui.show();\r
456             }\r
457         },\r
458 \r
459         onCollapse : function(){\r
460             if(this.closeNode){\r
461                 this.closeNode.ui.hide();\r
462             }\r
463         },\r
464 \r
465         render : function(bulkRender){\r
466             HtmlNode.superclass.render.call(this, bulkRender);\r
467         },\r
468 \r
469         highlightNode : function(){\r
470             //Ext.fly(this.htmlNode).highlight();\r
471         },\r
472 \r
473         highlight : function(){\r
474             //Ext.fly(this.ui.textNode).highlight();\r
475         },\r
476 \r
477         frame : function(){\r
478             this.htmlNode.style.border = '1px solid #0000ff';\r
479             //this.highlightNode();\r
480         },\r
481 \r
482         unframe : function(){\r
483             //Ext.fly(this.htmlNode).removeClass('x-debug-frame');\r
484             this.htmlNode.style.border = '';\r
485         }\r
486     });\r
487 \r
488     return HtmlNode;\r
489 }();\r
490 \r
491 \r