2
2
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3
3
* Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
4
* Copyright (C) 2009 Joseph Pecoraro
5
6
* Redistribution and use in source and binary forms, with or without
6
7
* modification, are permitted provided that the following conditions
30
31
WebInspector.ElementsTreeOutline = function() {
31
32
this.element = document.createElement("ol");
32
33
this.element.addEventListener("mousedown", this._onmousedown.bind(this), false);
33
this.element.addEventListener("dblclick", this._ondblclick.bind(this), false);
34
34
this.element.addEventListener("mousemove", this._onmousemove.bind(this), false);
35
35
this.element.addEventListener("mouseout", this._onmouseout.bind(this), false);
78
78
// and the select() call would change the focusedDOMNode and reenter this setter. So to
79
79
// avoid calling focusedNodeChanged() twice, first check if _focusedDOMNode is the same
80
80
// node as the one passed in.
81
if (objectsAreSame(this._focusedDOMNode, x)) {
81
if (this._focusedDOMNode === x) {
82
82
this.focusedNodeChanged();
84
84
if (x && !this.suppressSelectHighlight) {
85
InspectorController.highlightDOMNode(x);
85
InspectorController.highlightDOMNode(x.id);
87
87
if ("_restorePreviousHighlightNodeTimeout" in this)
88
88
clearTimeout(this._restorePreviousHighlightNodeTimeout);
138
138
focusedNodeChanged: function(forceUpdate) {},
140
findTreeElement: function(node, isAncestor, getParent, equal)
140
findTreeElement: function(node)
142
if (typeof isAncestor === "undefined")
143
isAncestor = isAncestorIncludingParentFrames;
144
if (typeof getParent === "undefined")
145
getParent = parentNodeOrFrameElement;
146
if (typeof equal === "undefined")
147
equal = objectsAreSame;
149
var treeElement = TreeOutline.prototype.findTreeElement.call(this, node, isAncestor, getParent, equal);
142
var treeElement = TreeOutline.prototype.findTreeElement.call(this, node, isAncestorNode, parentNode);
150
143
if (!treeElement && node.nodeType === Node.TEXT_NODE) {
151
144
// The text node might have been inlined if it was short, so try to find the parent element.
152
treeElement = TreeOutline.prototype.findTreeElement.call(this, node.parentNode, isAncestor, getParent, equal);
145
treeElement = TreeOutline.prototype.findTreeElement.call(this, node.parentNode, isAncestorNode, parentNode);
155
148
return treeElement;
195
_ondblclick: function(event)
197
var element = this._treeElementFromEvent(event);
199
if (!element || !element.ondblclick)
202
element.ondblclick(element, event);
205
188
_onmousedown: function(event)
207
190
var element = this._treeElementFromEvent(event);
231
214
_onmouseout: function(event)
233
216
var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
234
if (nodeUnderMouse.isDescendant(this.element))
217
if (nodeUnderMouse && nodeUnderMouse.isDescendant(this.element))
237
220
if (this._previousHoveredElement) {
248
231
WebInspector.ElementsTreeElement = function(node)
250
var hasChildren = node.contentDocument || (Preferences.ignoreWhitespace ? (firstChildSkippingWhitespace.call(node) ? true : false) : node.hasChildNodes());
233
var hasChildren = Preferences.ignoreWhitespace ? (firstChildSkippingWhitespace.call(node) ? true : false) : node.hasChildNodes();
251
234
var titleInfo = nodeTitleInfo.call(node, hasChildren, WebInspector.linkifyURL);
253
236
if (titleInfo.hasChildren)
256
239
// The title will be updated in onattach.
257
240
TreeElement.call(this, "", node, titleInfo.hasChildren);
242
if (this.representedObject.nodeType == Node.ELEMENT_NODE)
243
this._canAddAttributes = true;
260
246
WebInspector.ElementsTreeElement.prototype = {
296
282
this.listItemElement.addStyleClass("hovered");
298
284
this.listItemElement.removeStyleClass("hovered");
285
if (this._canAddAttributes)
286
this.toggleNewAttributeButton();
290
toggleNewAttributeButton: function()
292
function removeWhenEditing(event)
294
if (this._addAttributeElement && this._addAttributeElement.parentNode)
295
this._addAttributeElement.parentNode.removeChild(this._addAttributeElement);
296
delete this._addAttributeElement;
299
if (!this._addAttributeElement && this._hovered && !this._editing) {
300
var span = document.createElement("span");
301
span.className = "add-attribute";
302
span.textContent = "\u2026";
303
span.addEventListener("dblclick", removeWhenEditing.bind(this), false);
304
this._addAttributeElement = span;
306
var tag = this.listItemElement.getElementsByClassName("webkit-html-tag")[0];
307
this._insertInLastAttributePosition(tag, span);
308
} else if (!this._hovered && this._addAttributeElement) {
309
if (this._addAttributeElement.parentNode)
310
this._addAttributeElement.parentNode.removeChild(this._addAttributeElement);
311
delete this._addAttributeElement;
302
315
updateSelection: function()
304
317
var listItemElement = this.listItemElement;
357
370
this.updateChildren();
360
373
updateChildren: function(fullRefresh)
375
WebInspector.domAgent.getChildNodesAsync(this.representedObject, this._updateChildren.bind(this, fullRefresh));
378
_updateChildren: function(fullRefresh)
362
380
if (fullRefresh) {
363
381
var selectedTreeElement = this.treeOutline.selectedTreeElement;
364
382
if (selectedTreeElement && selectedTreeElement.hasAncestor(this))
375
393
var child = (Preferences.ignoreWhitespace ? firstChildSkippingWhitespace.call(node) : node.firstChild);
377
395
var currentTreeElement = treeElement.children[treeChildIndex];
378
if (!currentTreeElement || !objectsAreSame(currentTreeElement.representedObject, child)) {
396
if (!currentTreeElement || currentTreeElement.representedObject !== child) {
379
397
// Find any existing element that is later in the children list.
380
398
var existingTreeElement = null;
381
399
for (var i = (treeChildIndex + 1); i < treeElement.children.length; ++i) {
382
if (objectsAreSame(treeElement.children[i].representedObject, child)) {
400
if (treeElement.children[i].representedObject === child) {
383
401
existingTreeElement = treeElement.children[i];
414
432
var currentNode = currentChild.representedObject;
415
433
var currentParentNode = currentNode.parentNode;
417
if (objectsAreSame(currentParentNode, this.representedObject))
419
if (this.representedObject.contentDocument && objectsAreSame(currentParentNode, this.representedObject.contentDocument))
435
if (currentParentNode === this.representedObject)
422
438
var selectedTreeElement = this.treeOutline.selectedTreeElement;
426
442
this.removeChildAtIndex(i);
428
if (this.treeOutline.panel && currentNode.contentDocument)
429
this.treeOutline.panel.unregisterMutationEventListeners(currentNode.contentDocument.defaultView);
432
if (this.representedObject.contentDocument)
433
updateChildrenOfNode(this.representedObject.contentDocument);
434
445
updateChildrenOfNode(this.representedObject);
436
447
var lastChild = this.children[this.children.length - 1];
483
491
if (this._editing)
486
if (this._startEditing(event))
494
if (this._startEditing(event, treeElement))
489
497
if (this.treeOutline.panel) {
490
this.treeOutline.rootDOMNode = this.parent.representedObject;
498
this.treeOutline.rootDOMNode = this.representedObject.parentNode;
491
499
this.treeOutline.focusedDOMNode = this.representedObject;
498
_startEditing: function(event)
506
_insertInLastAttributePosition: function(tag, node)
508
if (tag.getElementsByClassName("webkit-html-attribute").length > 0)
509
tag.insertBefore(node, tag.lastChild);
511
var nodeName = tag.textContent.match(/^<(.*?)>$/)[1];
512
tag.textContent = '';
513
tag.appendChild(document.createTextNode('<'+nodeName));
514
tag.appendChild(node);
515
tag.appendChild(document.createTextNode('>'));
519
_startEditing: function(event, treeElement)
500
521
if (this.treeOutline.focusedDOMNode != this.representedObject)
510
531
var attribute = event.target.enclosingNodeOrSelfWithClass("webkit-html-attribute");
512
return this._startEditingAttribute(attribute, event);
533
return this._startEditingAttribute(attribute, event.target);
535
var newAttribute = event.target.enclosingNodeOrSelfWithClass("add-attribute");
537
return this._addNewAttribute(treeElement.listItemElement);
517
_startEditingAttribute: function(attribute, event)
542
_addNewAttribute: function(listItemElement)
544
var attr = document.createElement("span");
545
attr.className = "webkit-html-attribute";
546
attr.style.marginLeft = "2px"; // overrides the .editing margin rule
547
attr.style.marginRight = "2px"; // overrides the .editing margin rule
548
var name = document.createElement("span");
549
name.className = "webkit-html-attribute-name new-attribute";
550
name.textContent = " ";
551
var value = document.createElement("span");
552
value.className = "webkit-html-attribute-value";
553
attr.appendChild(name);
554
attr.appendChild(value);
556
var tag = listItemElement.getElementsByClassName("webkit-html-tag")[0];
557
this._insertInLastAttributePosition(tag, attr);
558
return this._startEditingAttribute(attr, attr);
561
_triggerEditAttribute: function(attributeName)
563
var attributeElements = this.listItemElement.getElementsByClassName("webkit-html-attribute-name");
564
for (var i = 0, len = attributeElements.length; i < len; ++i) {
565
if (attributeElements[i].textContent === attributeName) {
566
for (var elem = attributeElements[i].nextSibling; elem; elem = elem.nextSibling) {
567
if (elem.nodeType !== Node.ELEMENT_NODE)
570
if (elem.hasStyleClass("webkit-html-attribute-value"))
571
return this._startEditingAttribute(attributeElements[i].parentNode, elem);
577
_startEditingAttribute: function(attribute, elementForSelection)
519
579
if (WebInspector.isBeingEdited(attribute))
545
605
this._editing = true;
547
607
WebInspector.startEditing(attribute, this._attributeEditingCommitted.bind(this), this._editingCancelled.bind(this), attributeName);
548
window.getSelection().setBaseAndExtent(event.target, 0, event.target, 1);
608
window.getSelection().setBaseAndExtent(elementForSelection, 0, elementForSelection, 1);
566
_attributeEditingCommitted: function(element, newText, oldText, attributeName)
626
_attributeEditingCommitted: function(element, newText, oldText, attributeName, moveDirection)
568
628
delete this._editing;
630
// Before we do anything, determine where we should move
631
// next based on the current element's settings
636
var attributes = this.representedObject.attributes;
637
for (var i = 0, len = attributes.length; i < len; ++i) {
638
if (attributes[i].name === attributeName) {
640
if (moveDirection === "backward" && i > 0)
641
moveToAttribute = attributes[i - 1].name;
642
else if (moveDirection === "forward" && i < attributes.length - 1)
643
moveToAttribute = attributes[i + 1].name;
644
else if (moveDirection === "forward" && i === attributes.length - 1)
649
if (!found && moveDirection === "backward")
650
moveToAttribute = attributes[attributes.length - 1].name;
651
else if (!found && moveDirection === "forward" && !/^\s*$/.test(newText))
655
function moveToNextAttributeIfNeeded() {
657
this._triggerEditAttribute(moveToAttribute);
658
else if (newAttribute)
659
this._addNewAttribute(this.listItemElement);
570
662
var parseContainerElement = document.createElement("span");
571
663
parseContainerElement.innerHTML = "<span " + newText + "></span>";
572
664
var parseElement = parseContainerElement.firstChild;
573
if (!parseElement || !parseElement.hasAttributes()) {
574
editingCancelled(element, context);
667
this._editingCancelled(element, attributeName);
668
moveToNextAttributeIfNeeded.call(this);
672
if (!parseElement.hasAttributes()) {
673
this.representedObject.removeAttribute(attributeName);
675
moveToNextAttributeIfNeeded.call(this);
579
680
for (var i = 0; i < parseElement.attributes.length; ++i) {
580
681
var attr = parseElement.attributes[i];
581
682
foundOriginalAttribute = foundOriginalAttribute || attr.name === attributeName;
582
InspectorController.inspectedWindow().Element.prototype.setAttribute.call(this.representedObject, attr.name, attr.value);
684
this.representedObject.setAttribute(attr.name, attr.value);
685
} catch(e) {} // ignore invalid attribute (innerHTML doesn't throw errors, but this can)
585
688
if (!foundOriginalAttribute)
586
InspectorController.inspectedWindow().Element.prototype.removeAttribute.call(this.representedObject, attributeName);
689
this.representedObject.removeAttribute(attributeName);
588
691
this._updateTitle();
590
693
this.treeOutline.focusedNodeChanged(true);
695
moveToNextAttributeIfNeeded.call(this);
593
698
_textNodeEditingCommitted: function(element, newText)