~ubuntu-branches/ubuntu/karmic/firebug/karmic

« back to all changes in this revision

Viewing changes to content/firebug/dom.js

  • Committer: Bazaar Package Importer
  • Author(s): Jared Greenwald
  • Date: 2008-02-21 17:34:24 UTC
  • Revision ID: james.westby@ubuntu.com-20080221173424-illircvfpyvnp4uo
Tags: upstream-1.1.0~b11+svn317
ImportĀ upstreamĀ versionĀ 1.1.0~b11+svn317

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* See license.txt for terms of usage */
 
2
 
 
3
FBL.ns(function() { with (FBL) {
 
4
 
 
5
// ************************************************************************************************
 
6
// Constants
 
7
 
 
8
const jsdIStackFrame = CI("jsdIStackFrame");
 
9
 
 
10
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
11
 
 
12
const insertSliceSize = 18;
 
13
const insertInterval = 40;
 
14
 
 
15
const ignoreVars =
 
16
{
 
17
    "__firebug__": 1,
 
18
    "eval": 1,
 
19
 
 
20
    // We are forced to ignore Java-related variables, because
 
21
    // trying to access them causes browser freeze
 
22
    "java": 1,
 
23
    "sun": 1,
 
24
    "Packages": 1,
 
25
    "JavaArray": 1,
 
26
    "JavaMember": 1,
 
27
    "JavaObject": 1,
 
28
    "JavaClass": 1,
 
29
    "JavaPackage": 1
 
30
};
 
31
 
 
32
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
33
 
 
34
const RowTag =
 
35
    TR({class: "memberRow $member.open $member.type\\Row", $hasChildren: "$member.hasChildren",
 
36
        level: "$member.level"},
 
37
        TD({class: "memberLabelCell", style: "padding-left: $member.indent\\px"},
 
38
            DIV({class: "memberLabel $member.type\\Label"}, "$member.name")
 
39
        ),
 
40
        TD({class: "memberValueCell"},
 
41
            TAG("$member.tag", {object: "$member.value"})
 
42
        )
 
43
    );
 
44
 
 
45
const WatchRowTag =
 
46
    TR({class: "watchNewRow", level: 0},
 
47
        TD({class: "watchEditCell", colspan: 2},
 
48
            DIV({class: "watchEditBox"},
 
49
                $STR("NewWatch")
 
50
            )
 
51
        )
 
52
    );
 
53
 
 
54
const SizerRow =
 
55
    TR(
 
56
        TD({width: "30%"}),
 
57
        TD({width: "70%"})
 
58
    );
 
59
 
 
60
const DirTablePlate = domplate(Firebug.Rep,
 
61
{
 
62
    tag:
 
63
        TABLE({class: "domTable", cellpadding: 0, cellspacing: 0, onclick: "$onClick"},
 
64
            TBODY(
 
65
                SizerRow,
 
66
                FOR("member", "$object|memberIterator", RowTag)
 
67
            )
 
68
        ),
 
69
 
 
70
    watchTag:
 
71
        TABLE({class: "domTable", cellpadding: 0, cellspacing: 0,
 
72
               _toggles: "$toggles", _domPanel: "$domPanel", onclick: "$onClick"},
 
73
            TBODY(
 
74
                SizerRow,
 
75
                WatchRowTag
 
76
            )
 
77
        ),
 
78
 
 
79
    tableTag:
 
80
        TABLE({class: "domTable", cellpadding: 0, cellspacing: 0,
 
81
            _toggles: "$toggles", _domPanel: "$domPanel", onclick: "$onClick"},
 
82
            TBODY(
 
83
                SizerRow
 
84
            )
 
85
        ),
 
86
 
 
87
    rowTag:
 
88
        FOR("member", "$members", RowTag),
 
89
 
 
90
    memberIterator: function(object, level)
 
91
    {
 
92
        return getMembers(object, level);
 
93
    },
 
94
 
 
95
    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
96
 
 
97
    onClick: function(event)
 
98
    {
 
99
        if (!isLeftClick(event))
 
100
            return;
 
101
 
 
102
        var row = getAncestorByClass(event.target, "memberRow");
 
103
        var label = getAncestorByClass(event.target, "memberLabel");
 
104
        if (label && hasClass(row, "hasChildren"))
 
105
        {
 
106
            var row = label.parentNode.parentNode;
 
107
            this.toggleRow(row);
 
108
        }
 
109
        else
 
110
        {
 
111
            var object = Firebug.getRepObject(event.target);
 
112
            if (typeof(object) == "function")
 
113
            {
 
114
                FirebugChrome.select(object, "script");
 
115
                cancelEvent(event);
 
116
            }
 
117
            else if (event.detail == 2 && !object)
 
118
            {
 
119
                var panel = row.parentNode.parentNode.domPanel;
 
120
                if (panel)
 
121
                {
 
122
                    var rowValue = panel.getRowPropertyValue(row);
 
123
                    if (typeof(rowValue) == "boolean")
 
124
                        panel.setPropertyValue(row, !rowValue);
 
125
                    else
 
126
                        panel.editProperty(row);
 
127
 
 
128
                    cancelEvent(event);
 
129
                }
 
130
            }
 
131
        }
 
132
    },
 
133
 
 
134
    toggleRow: function(row)
 
135
    {
 
136
        var level = parseInt(row.getAttribute("level"));
 
137
        var toggles = row.parentNode.parentNode.toggles;
 
138
 
 
139
        if (hasClass(row, "opened"))
 
140
        {
 
141
            removeClass(row, "opened");
 
142
 
 
143
            if (toggles)
 
144
            {
 
145
                var path = getPath(row);
 
146
 
 
147
                // Remove the path from the toggle tree
 
148
                for (var i = 0; i < path.length; ++i)
 
149
                {
 
150
                    if (i == path.length-1)
 
151
                        delete toggles[path[i]];
 
152
                    else
 
153
                        toggles = toggles[path[i]];
 
154
                }
 
155
            }
 
156
 
 
157
            var rowTag = this.rowTag;
 
158
            var tbody = row.parentNode;
 
159
 
 
160
            setTimeout(function()
 
161
            {
 
162
                for (var firstRow = row.nextSibling; firstRow; firstRow = row.nextSibling)
 
163
                {
 
164
                    if (parseInt(firstRow.getAttribute("level")) <= level)
 
165
                        break;
 
166
 
 
167
                    tbody.removeChild(firstRow);
 
168
                }
 
169
            }, row.insertTimeout ? row.insertTimeout : 0);
 
170
        }
 
171
        else
 
172
        {
 
173
            setClass(row, "opened");
 
174
 
 
175
            if (toggles)
 
176
            {
 
177
                var path = getPath(row);
 
178
 
 
179
                // Mark the path in the toggle tree
 
180
                for (var i = 0; i < path.length; ++i)
 
181
                {
 
182
                    var name = path[i];
 
183
                    if ( toggles.hasOwnProperty(name) )
 
184
                        toggles = toggles[name];
 
185
                    else
 
186
                        toggles = toggles[name] = {};
 
187
                }
 
188
            }
 
189
 
 
190
            var value = row.lastChild.firstChild.repObject;
 
191
            var members = getMembers(value, level+1);
 
192
 
 
193
            var rowTag = this.rowTag;
 
194
            var lastRow = row;
 
195
 
 
196
            var delay = 0;
 
197
            while (members.length)
 
198
            {
 
199
                setTimeout(function(slice, isLast)
 
200
                {
 
201
                    if (lastRow.parentNode)
 
202
                        lastRow = rowTag.insertRows({members: slice}, lastRow)[1];
 
203
 
 
204
                    if (isLast)
 
205
                        delete row.insertTimeout;
 
206
                }, delay, members.splice(0, insertSliceSize), !members.length);
 
207
 
 
208
                delay += insertInterval;
 
209
            }
 
210
 
 
211
            row.insertTimeout = delay;
 
212
        }
 
213
    }
 
214
});
 
215
 
 
216
const ToolboxPlate = domplate(
 
217
{
 
218
    tag:
 
219
        DIV({class: "watchToolbox", _domPanel: "$domPanel", onclick: "$onClick"},
 
220
            IMG({class: "watchDeleteButton closeButton", src: "blank.gif"})
 
221
        ),
 
222
 
 
223
    onClick: function(event)
 
224
    {
 
225
        var toolbox = event.currentTarget;
 
226
        toolbox.domPanel.deleteWatch(toolbox.watchRow);
 
227
    }
 
228
});
 
229
 
 
230
// ************************************************************************************************
 
231
 
 
232
function DOMBasePanel() {}
 
233
 
 
234
DOMBasePanel.prototype = extend(Firebug.Panel,
 
235
{
 
236
    tag: DirTablePlate.tableTag,
 
237
 
 
238
    rebuild: function(update, scrollTop)
 
239
    {
 
240
        var members = getMembers(this.selection);
 
241
        expandMembers(members, this.toggles, 0, 0);
 
242
 
 
243
        this.showMembers(members, update, scrollTop);
 
244
    },
 
245
 
 
246
    showMembers: function(members, update, scrollTop)
 
247
    {
 
248
        // If we are still in the midst of inserting rows, cancel all pending
 
249
        // insertions here - this is a big speedup when stepping in the debugger
 
250
        if (this.timeouts)
 
251
        {
 
252
            for (var i = 0; i < this.timeouts.length; ++i)
 
253
                this.context.clearTimeout(this.timeouts[i]);
 
254
            delete this.timeouts;
 
255
        }
 
256
 
 
257
        if (!members.length)
 
258
            return this.showEmptyMembers();
 
259
 
 
260
        var panelNode = this.panelNode;
 
261
        var priorScrollTop = scrollTop == undefined ? panelNode.scrollTop : scrollTop;
 
262
 
 
263
        // If we are asked to "update" the current view, then build the new table
 
264
        // offscreen and swap it in when it's done
 
265
        var offscreen = update && panelNode.firstChild;
 
266
        var dest = offscreen ? this.document : panelNode;
 
267
 
 
268
        var table = this.tag.replace({domPanel: this, toggles: this.toggles}, dest);
 
269
        var tbody = table.lastChild;
 
270
        var rowTag = DirTablePlate.rowTag;
 
271
 
 
272
        // Insert the first slice immediately
 
273
        var slice = members.splice(0, insertSliceSize);
 
274
        rowTag.insertRows({members: slice}, tbody.lastChild);
 
275
 
 
276
        var timeouts = [];
 
277
 
 
278
        var delay = 0;
 
279
        while (members.length)
 
280
        {
 
281
            timeouts.push(this.context.setTimeout(function(slice)
 
282
            {
 
283
                rowTag.insertRows({members: slice}, tbody.lastChild);
 
284
 
 
285
                if ((panelNode.scrollHeight+panelNode.offsetHeight) >= priorScrollTop)
 
286
                    panelNode.scrollTop = priorScrollTop;
 
287
            }, delay, members.splice(0, insertSliceSize)));
 
288
 
 
289
            delay += insertInterval;
 
290
        }
 
291
 
 
292
        if (offscreen)
 
293
        {
 
294
            timeouts.push(this.context.setTimeout(function()
 
295
            {
 
296
                if (panelNode.firstChild)
 
297
                    panelNode.replaceChild(table, panelNode.firstChild);
 
298
                else
 
299
                    panelNode.appendChild(table);
 
300
 
 
301
                // Scroll back to where we were before
 
302
                panelNode.scrollTop = priorScrollTop;
 
303
            }, delay));
 
304
        }
 
305
        else
 
306
        {
 
307
            timeouts.push(this.context.setTimeout(function()
 
308
            {
 
309
                panelNode.scrollTop = scrollTop == undefined ? 0 : scrollTop;
 
310
            }, delay));
 
311
        }
 
312
 
 
313
        this.timeouts = [];
 
314
    },
 
315
 
 
316
    showEmptyMembers: function()
 
317
    {
 
318
        FirebugReps.Warning.tag.replace({object: "NoMembersWarning"}, this.panelNode);
 
319
    },
 
320
 
 
321
    findPathObject: function(object)
 
322
    {
 
323
        var pathIndex = -1;
 
324
        for (var i = 0; i < this.objectPath.length; ++i)
 
325
        {
 
326
            if (this.getPathObject(i) == object)
 
327
                return i;
 
328
        }
 
329
 
 
330
        return -1;
 
331
    },
 
332
 
 
333
    getPathObject: function(index)
 
334
    {
 
335
        var object = this.objectPath[index];
 
336
        if (object instanceof Property)
 
337
            return object.getObject();
 
338
        else
 
339
            return object;
 
340
    },
 
341
 
 
342
    getRowObject: function(row)
 
343
    {
 
344
        var object = getRowOwnerObject(row);
 
345
        return object ? object : this.selection;
 
346
    },
 
347
 
 
348
    getRowPropertyValue: function(row)
 
349
    {
 
350
        var object = this.getRowObject(row);
 
351
        if (object)
 
352
        {
 
353
            var propName = getRowName(row);
 
354
 
 
355
            if (object instanceof jsdIStackFrame)
 
356
                return Firebug.Debugger.evaluate(propName, this.context);
 
357
            else
 
358
                return object[propName];
 
359
        }
 
360
    },
 
361
 
 
362
    copyProperty: function(row)
 
363
    {
 
364
        var value = this.getRowPropertyValue(row);
 
365
        copyToClipboard(value);
 
366
    },
 
367
 
 
368
    editProperty: function(row, editValue)
 
369
    {
 
370
        if (hasClass(row, "watchNewRow"))
 
371
            Firebug.Editor.startEditing(row, "");
 
372
        else if (hasClass(row, "watchRow"))
 
373
            Firebug.Editor.startEditing(row, getRowName(row));
 
374
        else
 
375
        {
 
376
            var object = this.getRowObject(row);
 
377
            this.context.thisValue = object;
 
378
 
 
379
            if (!editValue)
 
380
            {
 
381
                var propValue = this.getRowPropertyValue(row);
 
382
 
 
383
                var type = typeof(propValue);
 
384
                if (type == "undefined" || type == "number" || type == "boolean")
 
385
                    editValue = propValue;
 
386
                else if (type == "string")
 
387
                    editValue = "\"" + escapeJS(propValue) + "\"";
 
388
                else if (propValue == null)
 
389
                    editValue = "null";
 
390
                else if (object instanceof Window || object instanceof jsdIStackFrame)
 
391
                    editValue = getRowName(row);
 
392
                else
 
393
                    editValue = "this." + getRowName(row);
 
394
            }
 
395
 
 
396
 
 
397
            Firebug.Editor.startEditing(row, editValue);
 
398
        }
 
399
    },
 
400
 
 
401
    deleteProperty: function(row)
 
402
    {
 
403
        if (hasClass(row, "watchRow"))
 
404
            this.deleteWatch(row);
 
405
        else
 
406
        {
 
407
            var object = getRowOwnerObject(row);
 
408
            if (!object)
 
409
                object = this.selection;
 
410
 
 
411
            if (object)
 
412
            {
 
413
                var name = getRowName(row);
 
414
                try
 
415
                {
 
416
                    delete object[name];
 
417
                }
 
418
                catch (exc)
 
419
                {
 
420
                    return;
 
421
                }
 
422
 
 
423
                this.rebuild(true);
 
424
                this.markChange();
 
425
            }
 
426
        }
 
427
    },
 
428
 
 
429
    setPropertyValue: function(row, value)
 
430
    {
 
431
        var name = getRowName(row);
 
432
        if (name == "this")
 
433
            return;
 
434
 
 
435
        var object = this.getRowObject(row);
 
436
        if (object && !(object instanceof jsdIStackFrame))
 
437
        {
 
438
            try
 
439
            {
 
440
                object[name] = Firebug.CommandLine.evaluate(value, this.context, null, object);
 
441
            }
 
442
            catch (exc)
 
443
            {
 
444
                try
 
445
                {
 
446
                    // If the value doesn't parse, then just store it as a string.  Some users will
 
447
                    // not realize they're supposed to enter a JavaScript expression and just type
 
448
                    // literal text
 
449
                    object[name] = value;
 
450
                }
 
451
                catch (exc)
 
452
                {
 
453
                    return;
 
454
                }
 
455
            }
 
456
        }
 
457
        else if (this.context.stopped)
 
458
        {
 
459
            try
 
460
            {
 
461
                Firebug.CommandLine.evaluate(name+"="+value, this.context);
 
462
            }
 
463
            catch (exc)
 
464
            {
 
465
                try
 
466
                {
 
467
                    // See catch block above...
 
468
                    object[name] = value;
 
469
                }
 
470
                catch (exc)
 
471
                {
 
472
                    return;
 
473
                }
 
474
            }
 
475
        }
 
476
 
 
477
        this.rebuild(true);
 
478
        this.markChange();
 
479
    },
 
480
 
 
481
    highlightRow: function(row)
 
482
    {
 
483
        if (this.highlightedRow)
 
484
            cancelClassTimed(this.highlightedRow, "jumpHighlight", this.context);
 
485
 
 
486
        this.highlightedRow = row;
 
487
 
 
488
        if (row)
 
489
            setClassTimed(row, "jumpHighlight", this.context);
 
490
    },
 
491
 
 
492
    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
493
    // extends Panel
 
494
 
 
495
    initialize: function()
 
496
    {
 
497
        this.objectPath = [];
 
498
        this.propertyPath = [];
 
499
        this.viewPath = [];
 
500
        this.pathIndex = -1;
 
501
        this.toggles = {};
 
502
 
 
503
        Firebug.Panel.initialize.apply(this, arguments);
 
504
    },
 
505
 
 
506
    destroy: function(state)
 
507
    {
 
508
        var view = this.viewPath[this.pathIndex];
 
509
        if (view && this.panelNode.scrollTop)
 
510
            view.scrollTop = this.panelNode.scrollTop;
 
511
 
 
512
        state.pathIndex = this.pathIndex;
 
513
        state.viewPath = this.viewPath;
 
514
        state.propertyPath = this.propertyPath;
 
515
        if (this.propertyPath.length > 0 && !this.propertyPath[1])
 
516
            state.firstSelection = persistObject(this.getPathObject(1), this.context);
 
517
 
 
518
        Firebug.Panel.destroy.apply(this, arguments);
 
519
    },
 
520
 
 
521
    show: function(state)
 
522
    {
 
523
        if (this.context.loaded && !this.selection)
 
524
        {
 
525
            if (!state)
 
526
            {
 
527
                this.select(null);
 
528
                return;
 
529
            }
 
530
 
 
531
            this.viewPath = state.viewPath;
 
532
            this.propertyPath = state.propertyPath;
 
533
 
 
534
            var selectObject = defaultObject = this.getDefaultSelection();
 
535
 
 
536
            if (state.firstSelection)
 
537
            {
 
538
                var restored = state.firstSelection(this.context);
 
539
                if (restored)
 
540
                {
 
541
                    selectObject = restored;
 
542
                    this.objectPath = [defaultObject, restored];
 
543
                }
 
544
                else
 
545
                    this.objectPath = [defaultObject];
 
546
            }
 
547
            else
 
548
                this.objectPath = [defaultObject];
 
549
 
 
550
            if (this.propertyPath.length > 1)
 
551
            {
 
552
                for (var i = 1; i < this.propertyPath.length; ++i)
 
553
                {
 
554
                    var name = this.propertyPath[i];
 
555
                    if (!name)
 
556
                        continue;
 
557
 
 
558
                    var object = selectObject;
 
559
                    try
 
560
                    {
 
561
                        selectObject = object[name];
 
562
                    }
 
563
                    catch (exc)
 
564
                    {
 
565
                        selectObject = null;
 
566
                    }
 
567
 
 
568
                    if (selectObject)
 
569
                    {
 
570
                        this.objectPath.push(new Property(object, name));
 
571
                    }
 
572
                    else
 
573
                    {
 
574
                        // If we can't access a property, just stop
 
575
                        this.viewPath.splice(i);
 
576
                        this.propertyPath.splice(i);
 
577
                        this.objectPath.splice(i);
 
578
                        selectObject = this.getPathObject(this.objectPath.length-1);
 
579
                        break;
 
580
                    }
 
581
                }
 
582
            }
 
583
 
 
584
            var selection = state.pathIndex <= this.objectPath.length-1
 
585
                ? this.getPathObject(state.pathIndex)
 
586
                : this.getPathObject(this.objectPath.length-1);
 
587
 
 
588
            this.select(selection);
 
589
        }
 
590
    },
 
591
 
 
592
    hide: function()
 
593
    {
 
594
        var view = this.viewPath[this.pathIndex];
 
595
        if (view && this.panelNode.scrollTop)
 
596
            view.scrollTop = this.panelNode.scrollTop;
 
597
    },
 
598
 
 
599
    supportsObject: function(object)
 
600
    {
 
601
        if (object == null)
 
602
            return 1000;
 
603
 
 
604
        if (typeof(object) == "undefined")
 
605
            return 1000;
 
606
        else if (object instanceof SourceLink)
 
607
            return 0;
 
608
        else
 
609
            return 1; // just agree to support everything but not agressively.
 
610
    },
 
611
 
 
612
    refresh: function()
 
613
    {
 
614
        this.rebuild(true);
 
615
    },
 
616
 
 
617
    updateSelection: function(object)
 
618
    {
 
619
        var previousIndex = this.pathIndex;
 
620
        var previousView = previousIndex == -1 ? null : this.viewPath[previousIndex];
 
621
 
 
622
        var newPath = this.pathToAppend;
 
623
        delete this.pathToAppend;
 
624
 
 
625
        var pathIndex = this.findPathObject(object);
 
626
        if (newPath || pathIndex == -1)
 
627
        {
 
628
            this.toggles = {};
 
629
 
 
630
            if (newPath)
 
631
            {
 
632
                // Remove everything after the point where we are inserting, so we
 
633
                // essentially replace it with the new path
 
634
                if (previousView)
 
635
                {
 
636
                    if (this.panelNode.scrollTop)
 
637
                        previousView.scrollTop = this.panelNode.scrollTop;
 
638
 
 
639
                    this.objectPath.splice(previousIndex+1);
 
640
                    this.propertyPath.splice(previousIndex+1);
 
641
                    this.viewPath.splice(previousIndex+1);
 
642
                }
 
643
 
 
644
                var value = this.getPathObject(previousIndex);
 
645
                for (var i = 0; i < newPath.length; ++i)
 
646
                {
 
647
                    var name = newPath[i];
 
648
                    var object = value;
 
649
                    value = value[name];
 
650
 
 
651
                    ++this.pathIndex;
 
652
                    this.objectPath.push(new Property(object, name));
 
653
                    this.propertyPath.push(name);
 
654
                    this.viewPath.push({toggles: this.toggles, scrollTop: 0});
 
655
                }
 
656
            }
 
657
            else
 
658
            {
 
659
                this.toggles = {};
 
660
 
 
661
                var win = this.context.window;
 
662
                if (object == win)
 
663
                {
 
664
                    this.pathIndex = 0;
 
665
                    this.objectPath = [win];
 
666
                    this.propertyPath = [null];
 
667
                    this.viewPath = [{toggles: this.toggles, scrollTop: 0}];
 
668
                }
 
669
                else
 
670
                {
 
671
                    this.pathIndex = 1;
 
672
                    this.objectPath = [win, object];
 
673
                    this.propertyPath = [null, null];
 
674
                    this.viewPath = [
 
675
                        {toggles: {}, scrollTop: 0},
 
676
                        {toggles: this.toggles, scrollTop: 0}
 
677
                    ];
 
678
                }
 
679
            }
 
680
 
 
681
            this.panelNode.scrollTop = 0;
 
682
            this.rebuild();
 
683
        }
 
684
        else
 
685
        {
 
686
            this.pathIndex = pathIndex;
 
687
 
 
688
            var view = this.viewPath[pathIndex];
 
689
            this.toggles = view.toggles;
 
690
 
 
691
            // Persist the current scroll location
 
692
            if (previousView && this.panelNode.scrollTop)
 
693
                previousView.scrollTop = this.panelNode.scrollTop;
 
694
 
 
695
            this.rebuild(false, view.scrollTop);
 
696
        }
 
697
 
 
698
    },
 
699
 
 
700
    getObjectPath: function(object)
 
701
    {
 
702
        return this.objectPath;
 
703
    },
 
704
 
 
705
    getDefaultSelection: function()
 
706
    {
 
707
        return this.context.window;
 
708
    },
 
709
 
 
710
    updateOption: function(name, value)
 
711
    {
 
712
        const optionMap = {showUserProps: 1, showUserFuncs: 1, showDOMProps: 1,
 
713
            showDOMFuncs: 1, showDOMConstants: 1};
 
714
        if (name in optionMap)
 
715
            this.rebuild(true);
 
716
    },
 
717
 
 
718
    getOptionsMenuItems: function()
 
719
    {
 
720
        return [
 
721
            optionMenu("ShowUserProps", "showUserProps"),
 
722
            optionMenu("ShowUserFuncs", "showUserFuncs"),
 
723
            optionMenu("ShowDOMProps", "showDOMProps"),
 
724
            optionMenu("ShowDOMFuncs", "showDOMFuncs"),
 
725
            optionMenu("ShowDOMConstants", "showDOMConstants"),
 
726
            "-",
 
727
            {label: "Refresh", command: bindFixed(this.rebuild, this, true) }
 
728
        ];
 
729
    },
 
730
 
 
731
    getContextMenuItems: function(object, target)
 
732
    {
 
733
        var row = getAncestorByClass(target, "memberRow");
 
734
 
 
735
        var items = [];
 
736
 
 
737
        if (row)
 
738
        {
 
739
            var rowName = getRowName(row);
 
740
            var rowObject = this.getRowObject(row);
 
741
            var rowValue = this.getRowPropertyValue(row);
 
742
 
 
743
            var isWatch = hasClass(row, "watchRow");
 
744
            var isStackFrame = rowObject instanceof jsdIStackFrame;
 
745
 
 
746
            if (typeof(rowValue) == "string" || typeof(rowValue) == "number")
 
747
            {
 
748
                // Functions already have a copy item in their context menu
 
749
                items.push(
 
750
                    "-",
 
751
                    {label: "CopyValue",
 
752
                        command: bindFixed(this.copyProperty, this, row) }
 
753
                );
 
754
            }
 
755
 
 
756
            items.push(
 
757
                "-",
 
758
                {label: isWatch ? "EditWatch" : (isStackFrame ? "EditVariable" : "EditProperty"),
 
759
                    command: bindFixed(this.editProperty, this, row) }
 
760
            );
 
761
 
 
762
            if (isWatch || (!isStackFrame && !isDOMMember(rowObject, rowName)))
 
763
            {
 
764
                items.push(
 
765
                    {label: isWatch ? "DeleteWatch" : "DeleteProperty",
 
766
                        command: bindFixed(this.deleteProperty, this, row) }
 
767
                );
 
768
            }
 
769
        }
 
770
 
 
771
        items.push(
 
772
            "-",
 
773
            {label: "Refresh", command: bindFixed(this.rebuild, this, true) }
 
774
        );
 
775
 
 
776
        return items;
 
777
    },
 
778
 
 
779
    getEditor: function(target, value)
 
780
    {
 
781
        if (!this.editor)
 
782
            this.editor = new DOMEditor(this.document);
 
783
 
 
784
        return this.editor;
 
785
    }
 
786
});
 
787
 
 
788
// ************************************************************************************************
 
789
 
 
790
var DOMMainPanel = Firebug.DOMPanel = function () {};
 
791
 
 
792
Firebug.DOMPanel.DirTable = DirTablePlate;
 
793
 
 
794
DOMMainPanel.prototype = extend(DOMBasePanel.prototype,
 
795
{
 
796
    selectRow: function(row, target)
 
797
    {
 
798
        if (!target)
 
799
            target = row.lastChild.firstChild;
 
800
 
 
801
        if (!target || !target.repObject)
 
802
            return;
 
803
 
 
804
        this.pathToAppend = getPath(row);
 
805
 
 
806
        // If the object is inside an array, look up its index
 
807
        var valueBox = row.lastChild.firstChild;
 
808
        if (hasClass(valueBox, "objectBox-array"))
 
809
        {
 
810
            var arrayIndex = FirebugReps.Arr.getItemIndex(target);
 
811
            this.pathToAppend.push(arrayIndex);
 
812
        }
 
813
 
 
814
        // Make sure we get a fresh status path for the object, since otherwise
 
815
        // it might find the object in the existing path and not refresh it
 
816
        this.context.chrome.clearStatusPath();
 
817
 
 
818
        this.select(target.repObject, true);
 
819
    },
 
820
 
 
821
    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
822
 
 
823
    onClick: function(event)
 
824
    {
 
825
        var repNode = Firebug.getRepNode(event.target);
 
826
        if (repNode)
 
827
        {
 
828
            var row = getAncestorByClass(event.target, "memberRow");
 
829
            if (row)
 
830
            {
 
831
                this.selectRow(row, repNode);
 
832
                cancelEvent(event);
 
833
            }
 
834
        }
 
835
    },
 
836
 
 
837
    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
838
    // extends Panel
 
839
 
 
840
    name: "dom",
 
841
    searchable: true,
 
842
    statusSeparator: ">",
 
843
 
 
844
    initialize: function()
 
845
    {
 
846
        this.onClick = bind(this.onClick, this);
 
847
 
 
848
        DOMBasePanel.prototype.initialize.apply(this, arguments);
 
849
    },
 
850
 
 
851
    initializeNode: function(oldPanelNode)
 
852
    {
 
853
        this.panelNode.addEventListener("click", this.onClick, false);
 
854
    },
 
855
 
 
856
    destroyNode: function()
 
857
    {
 
858
        this.panelNode.removeEventListener("click", this.onClick, false);
 
859
    },
 
860
 
 
861
    search: function(text, visit)
 
862
    {
 
863
        if (!text)
 
864
        {
 
865
            delete this.currentSearch;
 
866
            this.highlightRow(null);
 
867
            return false;
 
868
        }
 
869
 
 
870
        if (visit && this.currentSearch && this.currentSearch.currentNode)
 
871
        {
 
872
            Firebug.Search.clear(this.context);
 
873
            this.selectRow(this.currentSearch.currentNode);
 
874
            delete this.currentSearch;
 
875
            return true;
 
876
        }
 
877
        else
 
878
        {
 
879
            var row;
 
880
            if (this.currentSearch && text == this.currentSearch.text)
 
881
                row = this.currentSearch.findNext(true);
 
882
            else
 
883
            {
 
884
                function findRow(node) { return getAncestorByClass(node, "memberRow"); }
 
885
                this.currentSearch = new TextSearch(this.panelNode, findRow);
 
886
                row = this.currentSearch.find(text);
 
887
            }
 
888
 
 
889
            if (row)
 
890
            {
 
891
                if (visit)
 
892
                {
 
893
                    Firebug.Search.clear(this.context);
 
894
                    this.selectRow(row);
 
895
                    delete this.currentSearch;
 
896
                }
 
897
                else
 
898
                {
 
899
                    var sel = this.document.defaultView.getSelection();
 
900
                    sel.removeAllRanges();
 
901
                    sel.addRange(this.currentSearch.range);
 
902
 
 
903
                    scrollIntoCenterView(row, this.panelNode);
 
904
 
 
905
                    this.highlightRow(row);
 
906
                }
 
907
                return true;
 
908
            }
 
909
            else
 
910
                return false;
 
911
        }
 
912
    }
 
913
});
 
914
 
 
915
// ************************************************************************************************
 
916
 
 
917
function DOMSidePanel() {}
 
918
 
 
919
DOMSidePanel.prototype = extend(DOMBasePanel.prototype,
 
920
{
 
921
    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
922
    // extends Panel
 
923
 
 
924
    name: "domSide",
 
925
    parentPanel: "html"
 
926
});
 
927
 
 
928
// ************************************************************************************************
 
929
 
 
930
function WatchPanel() {}
 
931
 
 
932
WatchPanel.prototype = extend(DOMBasePanel.prototype,
 
933
{
 
934
    tag: DirTablePlate.watchTag,
 
935
 
 
936
    rebuild: function()
 
937
    {
 
938
        this.updateSelection(this.selection);
 
939
    },
 
940
 
 
941
    showEmptyMembers: function()
 
942
    {
 
943
        this.tag.replace({domPanel: this, toggles: {}}, this.panelNode);
 
944
    },
 
945
 
 
946
    addWatch: function(expression)
 
947
    {
 
948
        if (!this.watches)
 
949
            this.watches = [];
 
950
 
 
951
        this.watches.splice(0, 0, expression);
 
952
        this.rebuild(true);
 
953
    },
 
954
 
 
955
    removeWatch: function(expression)
 
956
    {
 
957
        if (!this.watches)
 
958
            return;
 
959
 
 
960
        var index = this.watches.indexOf(expression);
 
961
        if (index != -1)
 
962
            this.watches.splice(index, 1);
 
963
    },
 
964
 
 
965
    editNewWatch: function(value)
 
966
    {
 
967
        var watchNewRow = getElementByClass(this.panelNode, "watchNewRow");
 
968
        if (watchNewRow)
 
969
            this.editProperty(watchNewRow, value);
 
970
    },
 
971
 
 
972
    setWatchValue: function(row, value)
 
973
    {
 
974
        var rowIndex = getWatchRowIndex(row);
 
975
        this.watches[rowIndex] = value;
 
976
        this.rebuild(true);
 
977
    },
 
978
 
 
979
    deleteWatch: function(row)
 
980
    {
 
981
        var rowIndex = getWatchRowIndex(row);
 
982
        this.watches.splice(rowIndex, 1);
 
983
        this.rebuild(true);
 
984
 
 
985
        this.context.setTimeout(bindFixed(function()
 
986
        {
 
987
            this.showToolbox(null);
 
988
        }, this));
 
989
    },
 
990
 
 
991
    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
992
 
 
993
    showToolbox: function(row)
 
994
    {
 
995
        var toolbox = this.getToolbox();
 
996
        if (row)
 
997
        {
 
998
            if (hasClass(row, "editing"))
 
999
                return;
 
1000
 
 
1001
            toolbox.watchRow = row;
 
1002
 
 
1003
            var offset = getClientOffset(row);
 
1004
            toolbox.style.top = offset.y + "px";
 
1005
            this.panelNode.appendChild(toolbox);
 
1006
        }
 
1007
        else
 
1008
        {
 
1009
            delete toolbox.watchRow;
 
1010
            if (toolbox.parentNode)
 
1011
                toolbox.parentNode.removeChild(toolbox);
 
1012
        }
 
1013
    },
 
1014
 
 
1015
    getToolbox: function()
 
1016
    {
 
1017
        if (!this.toolbox)
 
1018
            this.toolbox = ToolboxPlate.tag.replace({domPanel: this}, this.document);
 
1019
 
 
1020
        return this.toolbox;
 
1021
    },
 
1022
 
 
1023
    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
1024
 
 
1025
    onMouseDown: function(event)
 
1026
    {
 
1027
        var watchNewRow = getAncestorByClass(event.target, "watchNewRow");
 
1028
        if (watchNewRow)
 
1029
        {
 
1030
            this.editProperty(watchNewRow);
 
1031
            cancelEvent(event);
 
1032
        }
 
1033
    },
 
1034
 
 
1035
    onMouseOver: function(event)
 
1036
    {
 
1037
        var watchRow = getAncestorByClass(event.target, "watchRow");
 
1038
        if (watchRow)
 
1039
            this.showToolbox(watchRow);
 
1040
    },
 
1041
 
 
1042
    onMouseOut: function(event)
 
1043
    {
 
1044
        if (isAncestor(event.relatedTarget, this.getToolbox()))
 
1045
            return;
 
1046
 
 
1047
        var watchRow = getAncestorByClass(event.relatedTarget, "watchRow");
 
1048
        if (!watchRow)
 
1049
            this.showToolbox(null);
 
1050
    },
 
1051
 
 
1052
    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
1053
    // extends Panel
 
1054
 
 
1055
    name: "watches",
 
1056
    order: 0,
 
1057
    parentPanel: "script",
 
1058
 
 
1059
    initialize: function()
 
1060
    {
 
1061
        this.onMouseDown = bind(this.onMouseDown, this);
 
1062
        this.onMouseOver = bind(this.onMouseOver, this);
 
1063
        this.onMouseOut = bind(this.onMouseOut, this);
 
1064
 
 
1065
        DOMBasePanel.prototype.initialize.apply(this, arguments);
 
1066
    },
 
1067
 
 
1068
    destroy: function(state)
 
1069
    {
 
1070
        state.watches = this.watches;
 
1071
 
 
1072
        Firebug.Panel.destroy.apply(this, arguments);
 
1073
    },
 
1074
 
 
1075
    show: function(state)
 
1076
    {
 
1077
        if (state)
 
1078
            this.watches = state.watches;
 
1079
    },
 
1080
 
 
1081
    initializeNode: function(oldPanelNode)
 
1082
    {
 
1083
        this.panelNode.addEventListener("mousedown", this.onMouseDown, false);
 
1084
        this.panelNode.addEventListener("mouseover", this.onMouseOver, false);
 
1085
        this.panelNode.addEventListener("mouseout", this.onMouseOut, false);
 
1086
    },
 
1087
 
 
1088
    destroyNode: function()
 
1089
    {
 
1090
        this.panelNode.removeEventListener("mousedown", this.onMouseDown, false);
 
1091
        this.panelNode.removeEventListener("mouseover", this.onMouseOver, false);
 
1092
        this.panelNode.removeEventListener("mouseout", this.onMouseOut, false);
 
1093
    },
 
1094
 
 
1095
    refresh: function()
 
1096
    {
 
1097
        this.rebuild(true);
 
1098
    },
 
1099
 
 
1100
    updateSelection: function(object)
 
1101
    {
 
1102
        var frame = this.context.currentFrame;
 
1103
 
 
1104
        var newFrame = frame && frame.script != this.lastScript;
 
1105
        if (newFrame)
 
1106
        {
 
1107
            this.toggles = {};
 
1108
            this.lastScript = frame.script;
 
1109
        }
 
1110
 
 
1111
        var members = [];
 
1112
 
 
1113
        if (this.watches)
 
1114
        {
 
1115
            for (var i = 0; i < this.watches.length; ++i)
 
1116
            {
 
1117
                var expr = this.watches[i];
 
1118
 
 
1119
                var value;
 
1120
                try
 
1121
                {
 
1122
                    value = Firebug.CommandLine.evaluate(expr, this.context);
 
1123
                }
 
1124
                catch (exc)
 
1125
                {
 
1126
                    if (exc instanceof this.context.window.Error)
 
1127
                        value = new ErrorCopy(exc.message);
 
1128
                    else
 
1129
                        value = new ErrorCopy(exc+"");
 
1130
                }
 
1131
 
 
1132
                addMember("watch", members, expr, value, 0);
 
1133
            }
 
1134
        }
 
1135
 
 
1136
        if (frame)
 
1137
        {
 
1138
            var thisVar = frame.thisValue.getWrappedValue();
 
1139
            addMember("user", members, "this", thisVar, 0);
 
1140
 
 
1141
            var listValue = {value: null}, lengthValue = {value: 0};
 
1142
            frame.scope.getProperties(listValue, lengthValue);
 
1143
 
 
1144
            var props = [], funcs = [];
 
1145
            for (var i = 0; i < lengthValue.value; ++i)
 
1146
            {
 
1147
                var prop = listValue.value[i];
 
1148
                var name = prop.name.getWrappedValue();
 
1149
                if (ignoreVars[name] == 1)
 
1150
                    continue;
 
1151
 
 
1152
                var value = prop.value.getWrappedValue();
 
1153
                if (typeof(value) == "function")
 
1154
                    addMember("userFunction", funcs, name, value, 0);
 
1155
                else
 
1156
                    addMember("user", props, name, value, 0);
 
1157
            }
 
1158
 
 
1159
            function sortName(a, b) { return a.name > b.name ? 1 : -1; }
 
1160
 
 
1161
            props.sort(sortName);
 
1162
            members.push.apply(members, props);
 
1163
 
 
1164
            funcs.sort(sortName);
 
1165
            members.push.apply(members, funcs);
 
1166
        }
 
1167
 
 
1168
        expandMembers(members, this.toggles, 0, 0);
 
1169
        this.showMembers(members, !newFrame);
 
1170
    }
 
1171
});
 
1172
 
 
1173
// ************************************************************************************************
 
1174
// Local Helpers
 
1175
 
 
1176
function DOMEditor(doc)
 
1177
{
 
1178
    this.box = this.tag.replace({}, doc, this);
 
1179
    this.input = this.box;
 
1180
 
 
1181
    this.tabNavigation = false;
 
1182
    this.tabCompletion = true;
 
1183
    this.completeAsYouType = false;
 
1184
    this.fixedWidth = true;
 
1185
 
 
1186
    this.autoCompleter = Firebug.CommandLine.autoCompleter;
 
1187
}
 
1188
 
 
1189
DOMEditor.prototype = domplate(Firebug.InlineEditor.prototype,
 
1190
{
 
1191
    tag: INPUT({class: "fixedWidthEditor", type: "text",
 
1192
                oninput: "$onInput", onkeypress: "$onKeyPress"}),
 
1193
 
 
1194
    endEditing: function(target, value, cancel)
 
1195
    {
 
1196
        // XXXjoe Kind of hackish - fix me
 
1197
        delete this.panel.context.thisValue;
 
1198
 
 
1199
        if (cancel || value == "")
 
1200
            return;
 
1201
 
 
1202
        var row = getAncestorByClass(target, "memberRow");
 
1203
        if (!row)
 
1204
            this.panel.addWatch(value);
 
1205
        else if (hasClass(row, "watchRow"))
 
1206
            this.panel.setWatchValue(row, value);
 
1207
        else
 
1208
            this.panel.setPropertyValue(row, value);
 
1209
    }
 
1210
});
 
1211
 
 
1212
// ************************************************************************************************
 
1213
// Local Helpers
 
1214
 
 
1215
function getMembers(object, level)
 
1216
{
 
1217
    if (!level)
 
1218
        level = 0;
 
1219
 
 
1220
    var ordinals = [], userProps = [], userClasses = [], userFuncs = [],
 
1221
        domProps = [], domFuncs = [], domConstants = [];
 
1222
 
 
1223
    try
 
1224
    {
 
1225
        var domMembers = getDOMMembers(object);
 
1226
 
 
1227
        for (var name in object)
 
1228
        {
 
1229
            if (ignoreVars[name] == 1)  // javascript.options.strict says ignoreVars is undefined.
 
1230
                continue;
 
1231
 
 
1232
            var val;
 
1233
            try
 
1234
            {
 
1235
                val = object[name];
 
1236
            }
 
1237
            catch (exc)
 
1238
            {
 
1239
                // Sometimes we get exceptions trying to access certain members
 
1240
            }
 
1241
 
 
1242
            var ordinal = parseInt(name);
 
1243
            if (ordinal || ordinal == 0)
 
1244
            {
 
1245
                addMember("ordinal", ordinals, name, val, level);
 
1246
            }
 
1247
            else if (typeof(val) == "function")
 
1248
            {
 
1249
                if (isClassFunction(val))
 
1250
                    addMember("userClass", userClasses, name, val, level);
 
1251
                else if (name in domMembers)
 
1252
                    addMember("domFunction", domFuncs, name, val, level, domMembers[name]);
 
1253
                else
 
1254
                    addMember("userFunction", userFuncs, name, val, level);
 
1255
            }
 
1256
            else
 
1257
            {
 
1258
                if (name in domMembers)
 
1259
                    addMember("dom", domProps, name, val, level, domMembers[name]);
 
1260
                else if (name in domConstantMap)
 
1261
                    addMember("dom", domConstants, name, val, level);
 
1262
                else
 
1263
                    addMember("user", userProps, name, val, level);
 
1264
            }
 
1265
        }
 
1266
    }
 
1267
    catch (exc)
 
1268
    {
 
1269
        // Sometimes we get exceptions just from trying to iterate the members
 
1270
        // of certain objects, like StorageList, but don't let that gum up the works
 
1271
        //throw exc;
 
1272
    }
 
1273
 
 
1274
    function sortName(a, b) { return a.name > b.name ? 1 : -1; }
 
1275
    function sortOrder(a, b) { return a.order > b.order ? 1 : -1; }
 
1276
 
 
1277
    var members = [];
 
1278
 
 
1279
    members.push.apply(members, ordinals);
 
1280
 
 
1281
    if (Firebug.showUserProps)
 
1282
    {
 
1283
        userProps.sort(sortName);
 
1284
        members.push.apply(members, userProps);
 
1285
    }
 
1286
 
 
1287
    if (Firebug.showUserFuncs)
 
1288
    {
 
1289
        userClasses.sort(sortName);
 
1290
        members.push.apply(members, userClasses);
 
1291
 
 
1292
        userFuncs.sort(sortName);
 
1293
        members.push.apply(members, userFuncs);
 
1294
    }
 
1295
 
 
1296
    if (Firebug.showDOMProps)
 
1297
    {
 
1298
        domProps.sort(sortOrder);
 
1299
        members.push.apply(members, domProps);
 
1300
    }
 
1301
 
 
1302
    if (Firebug.showDOMFuncs)
 
1303
    {
 
1304
        domFuncs.sort(sortName);
 
1305
        members.push.apply(members, domFuncs);
 
1306
    }
 
1307
 
 
1308
    if (Firebug.showDOMConstants)
 
1309
        members.push.apply(members, domConstants);
 
1310
 
 
1311
    return members;
 
1312
}
 
1313
 
 
1314
function expandMembers(members, toggles, offset, level)
 
1315
{
 
1316
    var expanded = 0;
 
1317
    for (var i = offset; i < members.length; ++i)
 
1318
    {
 
1319
        var member = members[i];
 
1320
        if (member.level > level)
 
1321
            break;
 
1322
 
 
1323
        if ( toggles.hasOwnProperty(member.name) )
 
1324
        {
 
1325
            member.open = "opened";  // member.level <= level && member.name in toggles.
 
1326
 
 
1327
            var newMembers = getMembers(member.value, level+1);  // sets newMembers.level to level+1
 
1328
 
 
1329
            var args = [i+1, 0];
 
1330
            args.push.apply(args, newMembers);
 
1331
            members.splice.apply(members, args);
 
1332
 
 
1333
            expanded += newMembers.length;
 
1334
            var memberToggles = toggles[member.name];
 
1335
            // if member.name == 'prototype', then toggles gets confused and we go into infinite loop XXXjjb
 
1336
            if (typeof(memberToggles) != 'object')
 
1337
                memberToggles = {};
 
1338
            i += newMembers.length + expandMembers(members, memberToggles, i+1, level+1);
 
1339
        }
 
1340
    }
 
1341
 
 
1342
    return expanded;
 
1343
}
 
1344
 
 
1345
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
1346
 
 
1347
function isClassFunction(fn)
 
1348
{
 
1349
    try
 
1350
    {
 
1351
        for (var name in fn.prototype)
 
1352
            return true;
 
1353
    } catch (exc) {}
 
1354
    return false;
 
1355
}
 
1356
 
 
1357
function hasProperties(ob)
 
1358
{
 
1359
    try
 
1360
    {
 
1361
        for (var name in ob)
 
1362
            return true;
 
1363
    } catch (exc) {}
 
1364
    return false;
 
1365
}
 
1366
 
 
1367
function addMember(type, props, name, value, level, order)
 
1368
{
 
1369
    var valueType = typeof(value);
 
1370
    var hasChildren = hasProperties(value) && !(value instanceof ErrorCopy) &&
 
1371
        (valueType == "function" || (valueType == "object" && value != null)
 
1372
        || (valueType == "string" && value.length > Firebug.stringCropLength));
 
1373
 
 
1374
    var rep = Firebug.getRep(value);
 
1375
    var tag = rep.shortTag ? rep.shortTag : rep.tag;
 
1376
 
 
1377
    props.push({
 
1378
        name: name,
 
1379
        value: value,
 
1380
        type: type,
 
1381
        rowClass: "memberRow-"+type,
 
1382
        open: "",
 
1383
        order: order,
 
1384
        level: level,
 
1385
        indent: level*16,
 
1386
        hasChildren: hasChildren,
 
1387
        tag: tag
 
1388
    });
 
1389
}
 
1390
 
 
1391
function getWatchRowIndex(row)
 
1392
{
 
1393
    var index = -1;
 
1394
    for (; row && hasClass(row, "watchRow"); row = row.previousSibling)
 
1395
        ++index;
 
1396
    return index;
 
1397
}
 
1398
 
 
1399
function getRowName(row)
 
1400
{
 
1401
    return row.firstChild.textContent;
 
1402
}
 
1403
 
 
1404
function getRowValue(row)
 
1405
{
 
1406
    return row.lastChild.firstChild.repObject;
 
1407
}
 
1408
 
 
1409
function getRowOwnerObject(row)
 
1410
{
 
1411
    var parentRow = getParentRow(row);
 
1412
    if (parentRow)
 
1413
        return getRowValue(parentRow);
 
1414
}
 
1415
 
 
1416
function getParentRow(row)
 
1417
{
 
1418
    var level = parseInt(row.getAttribute("level"))-1;
 
1419
    for (row = row.previousSibling; row; row = row.previousSibling)
 
1420
    {
 
1421
        if (parseInt(row.getAttribute("level")) == level)
 
1422
            return row;
 
1423
    }
 
1424
}
 
1425
 
 
1426
function getPath(row)
 
1427
{
 
1428
    var name = getRowName(row);
 
1429
    var path = [name];
 
1430
 
 
1431
    var level = parseInt(row.getAttribute("level"))-1;
 
1432
    for (row = row.previousSibling; row; row = row.previousSibling)
 
1433
    {
 
1434
        if (parseInt(row.getAttribute("level")) == level)
 
1435
        {
 
1436
            var name = getRowName(row);
 
1437
            path.splice(0, 0, name);
 
1438
 
 
1439
            --level;
 
1440
        }
 
1441
    }
 
1442
 
 
1443
    return path;
 
1444
}
 
1445
 
 
1446
// ************************************************************************************************
 
1447
 
 
1448
Firebug.registerPanel(DOMMainPanel);
 
1449
Firebug.registerPanel(DOMSidePanel);
 
1450
Firebug.registerPanel(WatchPanel);
 
1451
 
 
1452
// ************************************************************************************************
 
1453
 
 
1454
}});