Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(395)

Side by Side Diff: Source/devtools/front_end/elements/ElementsTreeOutline.js

Issue 397303002: DevTools: [Elements] Implement shortcut-based node cut-copy-pasting (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Make DOMAgent.copyTo hidden Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. 2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com> 3 * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
4 * Copyright (C) 2009 Joseph Pecoraro 4 * Copyright (C) 2009 Joseph Pecoraro
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
8 * are met: 8 * are met:
9 * 9 *
10 * 1. Redistributions of source code must retain the above copyright 10 * 1. Redistributions of source code must retain the above copyright
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
64 this._eventSupport = new WebInspector.Object(); 64 this._eventSupport = new WebInspector.Object();
65 65
66 this._visible = false; 66 this._visible = false;
67 67
68 this.element.addEventListener("contextmenu", this._contextMenuEventFired.bin d(this), true); 68 this.element.addEventListener("contextmenu", this._contextMenuEventFired.bin d(this), true);
69 this._contextMenuCallback = contextMenuCallback; 69 this._contextMenuCallback = contextMenuCallback;
70 this._setPseudoClassCallback = setPseudoClassCallback; 70 this._setPseudoClassCallback = setPseudoClassCallback;
71 this._createNodeDecorators(); 71 this._createNodeDecorators();
72 } 72 }
73 73
74 /** @typedef {{node: !WebInspector.DOMNode, isCut: boolean}} */
75 WebInspector.ElementsTreeOutline.ClipboardData;
76
74 /** 77 /**
75 * @enum {string} 78 * @enum {string}
76 */ 79 */
77 WebInspector.ElementsTreeOutline.Events = { 80 WebInspector.ElementsTreeOutline.Events = {
78 SelectedNodeChanged: "SelectedNodeChanged", 81 SelectedNodeChanged: "SelectedNodeChanged",
79 ElementsTreeUpdated: "ElementsTreeUpdated" 82 ElementsTreeUpdated: "ElementsTreeUpdated"
80 } 83 }
81 84
82 /** 85 /**
83 * @const 86 * @const
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
137 wireToDOMModel: function() 140 wireToDOMModel: function()
138 { 141 {
139 this._elementsTreeUpdater = new WebInspector.ElementsTreeUpdater(this._t arget.domModel, this); 142 this._elementsTreeUpdater = new WebInspector.ElementsTreeUpdater(this._t arget.domModel, this);
140 }, 143 },
141 144
142 unwireFromDOMModel: function() 145 unwireFromDOMModel: function()
143 { 146 {
144 if (this._elementsTreeUpdater) 147 if (this._elementsTreeUpdater)
145 this._elementsTreeUpdater.dispose(); 148 this._elementsTreeUpdater.dispose();
146 }, 149 },
150
151 /**
152 * @param {?WebInspector.ElementsTreeOutline.ClipboardData} data
153 */
154 _setClipboardData: function(data)
155 {
156 if (this._clipboardNodeData) {
157 var treeElement = this.findTreeElement(this._clipboardNodeData.node) ;
158 if (treeElement)
159 treeElement.setInClipboard(false);
160 delete this._clipboardNodeData;
161 }
162
163 if (data) {
164 var treeElement = this.findTreeElement(data.node);
165 if (treeElement)
166 treeElement.setInClipboard(true);
167 this._clipboardNodeData = data;
168 }
169 },
170
171 _cutNode: function()
172 {
173 this.handleCopyOrCutEvent(true);
174 },
175
176 _copyNode: function()
177 {
178 this.handleCopyOrCutEvent(false);
179 },
180
181 _pasteNode: function()
182 {
183 this.handlePasteEvent();
184 },
185
186 /**
187 * @param {boolean} isCut
188 * @param {!Event=} event
189 */
190 handleCopyOrCutEvent: function(isCut, event)
191 {
192 this._setClipboardData(null);
193 var currentFocusElement = WebInspector.currentFocusElement();
194 if (currentFocusElement && WebInspector.isBeingEdited(currentFocusElemen t))
195 return;
196
197 // Don't prevent the normal copy if the user has a selection.
198 if (!window.getSelection().isCollapsed)
199 return;
200
201 var selectedNode = this.selectedDOMNode();
202 if (!selectedNode)
203 return;
204
205 if (event) {
206 event.clipboardData.clearData();
207 event.preventDefault();
208 }
209 selectedNode.copyNode();
210 this._setClipboardData({node: selectedNode, isCut: isCut});
211 },
212
213 /**
214 * @return {boolean}
215 */
216 _canPaste: function()
217 {
218 if (!this._clipboardNodeData)
219 return false;
220
221 // Don't prevent the normal copy if the user has a selection.
222 if (!window.getSelection().isCollapsed)
223 return false;
224
225 var currentFocusElement = WebInspector.currentFocusElement();
226 if (currentFocusElement && WebInspector.isBeingEdited(currentFocusElemen t))
227 return false;
228
229 var targetNode = this.selectedDOMNode();
230 if (!targetNode)
231 return false;
232
233 var node = this._clipboardNodeData.node;
234 if (this._clipboardNodeData.isCut && (node === targetNode || node.isAnce stor(targetNode)))
aandrey 2014/07/25 13:32:54 node.isSelfOrAncestor(targetNode)
apavlov 2014/07/25 15:04:47 This works for ordinary DOM Node's, not our WI.DOM
235 return false;
236
237 if (targetNode.target() !== node.target())
238 return false;
239 return true;
240 },
241
242 /**
243 * @param {!Event=} event
244 */
245 handlePasteEvent: function(event)
246 {
247 if (!this._canPaste())
248 return;
249
250 if (this._clipboardNodeData.isCut) {
251 this._clipboardNodeData.node.moveTo(this.selectedDOMNode(), null, ex pandCallback.bind(this));
252 this._setClipboardData(null);
253 } else {
254 this._clipboardNodeData.node.copyTo(this.selectedDOMNode(), null, ex pandCallback.bind(this));
255 }
256
257 if (event)
258 event.preventDefault();
259
260 /**
261 * @param {?Protocol.Error} error
262 * @param {!DOMAgent.NodeId} nodeId
263 * @this {WebInspector.ElementsTreeOutline}
264 */
265 function expandCallback(error, nodeId)
266 {
267 if (error)
268 return;
269 var pastedNode = this._domModel.nodeForId(nodeId);
270 if (!pastedNode)
271 return;
272 this.selectDOMNode(pastedNode);
273 }
274 },
275
147 /** 276 /**
148 * @param {boolean} visible 277 * @param {boolean} visible
149 */ 278 */
150 setVisible: function(visible) 279 setVisible: function(visible)
151 { 280 {
152 this._visible = visible; 281 this._visible = visible;
153 if (!this._visible) 282 if (!this._visible)
154 return; 283 return;
155 284
156 this._updateModifiedNodes(); 285 this._updateModifiedNodes();
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after
343 472
344 /** 473 /**
345 * @param {?WebInspector.DOMNode} node 474 * @param {?WebInspector.DOMNode} node
346 * @param {boolean} omitFocus 475 * @param {boolean} omitFocus
347 */ 476 */
348 _revealAndSelectNode: function(node, omitFocus) 477 _revealAndSelectNode: function(node, omitFocus)
349 { 478 {
350 if (this._suppressRevealAndSelect) 479 if (this._suppressRevealAndSelect)
351 return; 480 return;
352 481
482 this._updateModifiedNodes();
483
353 if (!this._includeRootDOMNode && node === this.rootDOMNode && this.rootD OMNode) 484 if (!this._includeRootDOMNode && node === this.rootDOMNode && this.rootD OMNode)
354 node = this.rootDOMNode.firstChild; 485 node = this.rootDOMNode.firstChild;
355 if (!node) 486 if (!node)
356 return; 487 return;
357 var treeElement = this.createTreeElementFor(node); 488 var treeElement = this.createTreeElementFor(node);
358 if (!treeElement) 489 if (!treeElement)
359 return; 490 return;
360 491
361 treeElement.revealAndSelect(omitFocus); 492 treeElement.revealAndSelect(omitFocus);
362 }, 493 },
(...skipping 550 matching lines...) Expand 10 before | Expand all | Expand 10 after
913 // Preserve the semantic of node by following the order of updates for h ide and show. 1044 // Preserve the semantic of node by following the order of updates for h ide and show.
914 if (show) { 1045 if (show) {
915 for (var i = 0, size = this._highlightResult.length; i < size; ++i) 1046 for (var i = 0, size = this._highlightResult.length; i < size; ++i)
916 updateEntryShow(this._highlightResult[i]); 1047 updateEntryShow(this._highlightResult[i]);
917 } else { 1048 } else {
918 for (var i = (this._highlightResult.length - 1); i >= 0; --i) 1049 for (var i = (this._highlightResult.length - 1); i >= 0; --i)
919 updateEntryHide(this._highlightResult[i]); 1050 updateEntryHide(this._highlightResult[i]);
920 } 1051 }
921 }, 1052 },
922 1053
1054 /**
1055 * @param {boolean} inClipboard
1056 */
1057 setInClipboard: function(inClipboard)
1058 {
1059 if (this._inClipboard === inClipboard)
1060 return;
1061 this._inClipboard = inClipboard;
1062 this.listItemElement.classList.toggle("in-clipboard", inClipboard);
1063 },
1064
923 get hovered() 1065 get hovered()
924 { 1066 {
925 return this._hovered; 1067 return this._hovered;
926 }, 1068 },
927 1069
928 set hovered(x) 1070 set hovered(x)
929 { 1071 {
930 if (this._hovered === x) 1072 if (this._hovered === x)
931 return; 1073 return;
932 1074
(...skipping 513 matching lines...) Expand 10 before | Expand all | Expand 10 after
1446 }, 1588 },
1447 1589
1448 _populateNodeContextMenu: function(contextMenu) 1590 _populateNodeContextMenu: function(contextMenu)
1449 { 1591 {
1450 // Add free-form node-related actions. 1592 // Add free-form node-related actions.
1451 var openTagElement = this.treeOutline.getCachedTreeElement(this.represen tedObject) || this; 1593 var openTagElement = this.treeOutline.getCachedTreeElement(this.represen tedObject) || this;
1452 var isEditable = this.hasEditableNode(); 1594 var isEditable = this.hasEditableNode();
1453 if (isEditable && !this._editing) 1595 if (isEditable && !this._editing)
1454 contextMenu.appendItem(WebInspector.UIString("Edit as HTML"), openTa gElement._editAsHTML.bind(openTagElement)); 1596 contextMenu.appendItem(WebInspector.UIString("Edit as HTML"), openTa gElement._editAsHTML.bind(openTagElement));
1455 var isShadowRoot = this.representedObject.isShadowRoot(); 1597 var isShadowRoot = this.representedObject.isShadowRoot();
1456 if (!isShadowRoot)
1457 contextMenu.appendItem(WebInspector.UIString("Copy as HTML"), this._ copyHTML.bind(this));
1458 1598
1459 // Place it here so that all "Copy"-ing items stick together. 1599 // Place it here so that all "Copy"-ing items stick together.
1460 if (this.representedObject.nodeType() === Node.ELEMENT_NODE) 1600 if (this.representedObject.nodeType() === Node.ELEMENT_NODE)
1461 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCa seMenuTitles() ? "Copy CSS path" : "Copy CSS Path"), this._copyCSSPath.bind(this )); 1601 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCa seMenuTitles() ? "Copy CSS path" : "Copy CSS Path"), this._copyCSSPath.bind(this ));
1462 if (!isShadowRoot) 1602 if (!isShadowRoot)
1463 contextMenu.appendItem(WebInspector.UIString("Copy XPath"), this._co pyXPath.bind(this)); 1603 contextMenu.appendItem(WebInspector.UIString("Copy XPath"), this._co pyXPath.bind(this));
1604 if (!isShadowRoot) {
1605 var treeOutline = this.treeOutline;
1606 contextMenu.appendItem(WebInspector.UIString("Copy"), treeOutline._c opyNode.bind(treeOutline));
aandrey 2014/07/25 13:32:54 This callbacks should always work regardless of th
apavlov 2014/07/25 15:04:47 Done
1607 contextMenu.appendItem(WebInspector.UIString("Cut"), treeOutline._cu tNode.bind(treeOutline));
1608 if (treeOutline._canPaste())
1609 contextMenu.appendItem(WebInspector.UIString("Paste"), treeOutli ne._pasteNode.bind(treeOutline));
aandrey 2014/07/25 13:32:54 maybe always append this option, but make disabled
apavlov 2014/07/25 15:04:47 Done.
1610 }
1611
1464 if (isEditable) 1612 if (isEditable)
1465 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCa seMenuTitles() ? "Delete node" : "Delete Node"), this.remove.bind(this)); 1613 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCa seMenuTitles() ? "Delete node" : "Delete Node"), this.remove.bind(this));
1466 }, 1614 },
1467 1615
1468 _startEditing: function() 1616 _startEditing: function()
1469 { 1617 {
1470 if (this.treeOutline.selectedDOMNode() !== this._node) 1618 if (this.treeOutline.selectedDOMNode() !== this._node)
1471 return; 1619 return;
1472 1620
1473 var listItem = this._listItemNode; 1621 var listItem = this._listItemNode;
(...skipping 866 matching lines...) Expand 10 before | Expand all | Expand 10 after
2340 */ 2488 */
2341 function commitChange(initialValue, value) 2489 function commitChange(initialValue, value)
2342 { 2490 {
2343 if (initialValue !== value) 2491 if (initialValue !== value)
2344 node.setOuterHTML(value, selectNode); 2492 node.setOuterHTML(value, selectNode);
2345 } 2493 }
2346 2494
2347 node.getOuterHTML(this._startEditingAsHTML.bind(this, commitChange)); 2495 node.getOuterHTML(this._startEditingAsHTML.bind(this, commitChange));
2348 }, 2496 },
2349 2497
2350 _copyHTML: function()
2351 {
2352 this._node.copyNode();
2353 },
2354
2355 _copyCSSPath: function() 2498 _copyCSSPath: function()
2356 { 2499 {
2357 InspectorFrontendHost.copyText(WebInspector.DOMPresentationUtils.cssPath (this._node, true)); 2500 InspectorFrontendHost.copyText(WebInspector.DOMPresentationUtils.cssPath (this._node, true));
2358 }, 2501 },
2359 2502
2360 _copyXPath: function() 2503 _copyXPath: function()
2361 { 2504 {
2362 InspectorFrontendHost.copyText(WebInspector.DOMPresentationUtils.xPath(t his._node, true)); 2505 InspectorFrontendHost.copyText(WebInspector.DOMPresentationUtils.xPath(t his._node, true));
2363 }, 2506 },
2364 2507
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after
2628 2771
2629 this._treeOutline._fireElementsTreeUpdated(nodes); 2772 this._treeOutline._fireElementsTreeUpdated(nodes);
2630 }, 2773 },
2631 2774
2632 _reset: function() 2775 _reset: function()
2633 { 2776 {
2634 this._treeOutline.rootDOMNode = null; 2777 this._treeOutline.rootDOMNode = null;
2635 this._treeOutline.selectDOMNode(null, false); 2778 this._treeOutline.selectDOMNode(null, false);
2636 this._domModel.hideDOMNodeHighlight(); 2779 this._domModel.hideDOMNodeHighlight();
2637 this._recentlyModifiedNodes.clear(); 2780 this._recentlyModifiedNodes.clear();
2781 delete this._treeOutline._clipboardNodeData;
2638 } 2782 }
2639 } 2783 }
2640 2784
2641 /** 2785 /**
2642 * @constructor 2786 * @constructor
2643 * @param {boolean} isUpdated 2787 * @param {boolean} isUpdated
2644 * @param {!WebInspector.DOMNode=} parent 2788 * @param {!WebInspector.DOMNode=} parent
2645 */ 2789 */
2646 WebInspector.ElementsTreeUpdater.UpdateEntry = function(isUpdated, parent) 2790 WebInspector.ElementsTreeUpdater.UpdateEntry = function(isUpdated, parent)
2647 { 2791 {
(...skipping 23 matching lines...) Expand all
2671 var treeOutline = new WebInspector.ElementsTreeOutline(node.target(), fa lse, false); 2815 var treeOutline = new WebInspector.ElementsTreeOutline(node.target(), fa lse, false);
2672 treeOutline.rootDOMNode = node; 2816 treeOutline.rootDOMNode = node;
2673 treeOutline.element.classList.add("outline-disclosure"); 2817 treeOutline.element.classList.add("outline-disclosure");
2674 if (!treeOutline.children[0].hasChildren) 2818 if (!treeOutline.children[0].hasChildren)
2675 treeOutline.element.classList.add("single-node"); 2819 treeOutline.element.classList.add("single-node");
2676 treeOutline.setVisible(true); 2820 treeOutline.setVisible(true);
2677 treeOutline.element.treeElementForTest = treeOutline.children[0]; 2821 treeOutline.element.treeElementForTest = treeOutline.children[0];
2678 return treeOutline.element; 2822 return treeOutline.element;
2679 } 2823 }
2680 } 2824 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698