Bug 1067318 - edit as html live preview
MozReview-Commit-ID: 3cZiw8h8WM3
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -558,17 +558,17 @@ MarkupView.prototype = {
* React to new-node-front selection events.
* Highlights the node if needed, and make sure it is shown and selected in
* the view.
*/
_onNewSelection: function () {
let selection = this.inspector.selection;
if (this.htmlEditor) {
- this.htmlEditor.hide();
+ // this.htmlEditor.hide();
}
if (this._hoveredNode && this._hoveredNode !== selection.nodeFront) {
this.getContainer(this._hoveredNode).hovered = false;
this._hoveredNode = null;
}
if (!selection.isNode()) {
this.unmarkSelectedNode();
@@ -1283,21 +1283,27 @@ MarkupView.prototype = {
if (this.inspector.selection.nodeFront === parentContainer.node ||
(this.inspector.selection.nodeFront === removedNode && isHTMLTag)) {
let childContainers = parentContainer.getChildContainers();
if (childContainers && childContainers[childIndex]) {
this.markNodeAsSelected(childContainers[childIndex].node, reason);
if (childContainers[childIndex].hasChildren) {
this.expandNode(childContainers[childIndex].node);
}
+ clearTimeout(this.mutationTimer);
+ this.editedNode = childContainers[childIndex].node;
this.emit("reselectedonremoved");
}
}
};
+ this.mutationTimer = setTimeout(() => {
+ this.editedNode = removedNode;
+ }, 1000);
+
// Start listening for mutations until we find a childList change that has
// removedNode removed.
this.inspector.on("markupmutation", onMutations);
},
/**
* Make sure to stop listening for node removal markupmutations and not
* reselect the corresponding node when that happens.
@@ -1403,26 +1409,44 @@ MarkupView.prototype = {
* Open an editor in the UI to allow editing of a node's outerHTML.
*
* @param {NodeFront} node
* The NodeFront to edit.
*/
beginEditingOuterHTML: function (node) {
this.getNodeOuterHTML(node).then(oldValue => {
let container = this.getContainer(node);
+ this.editedNode = node;
if (!container) {
return;
}
// Load load and create HTML Editor as it is rarely used and fetch complex deps
if (!this.htmlEditor) {
let HTMLEditor = require("devtools/client/inspector/markup/views/html-editor");
this.htmlEditor = new HTMLEditor(this.doc);
}
+
+ let onHtmlEditorChange = (e, value) => {
+ if (!this.editedNode) {
+ // if the node is already being updated, don't do anything.
+ // unless we are finishing the edit, in which case we should probably wait and
+ // apply.
+ return;
+ }
+ let editedNode = this.editedNode;
+ this.editedNode = null;
+ this.updateNodeOuterHTML(editedNode, value, oldValue);
+ };
+
this.htmlEditor.show(container.tagLine, oldValue);
+ this.htmlEditor.on("change", onHtmlEditorChange);
+
this.htmlEditor.once("popuphidden", (e, commit, value) => {
+ this.htmlEditor.off("change", onHtmlEditorChange);
+
// Need to focus the <html> element instead of the frame / window
// in order to give keyboard focus back to doc (from editor).
this.doc.documentElement.focus();
if (commit) {
this.updateNodeOuterHTML(node, value, oldValue);
}
});
--- a/devtools/client/inspector/markup/views/html-editor.js
+++ b/devtools/client/inspector/markup/views/html-editor.js
@@ -30,16 +30,17 @@ function HTMLEditor(htmlDocument) {
this.container.style.display = "none";
this.editorInner = this.doc.createElement("div");
this.editorInner.className = "html-editor-inner";
this.container.appendChild(this.editorInner);
this.doc.body.appendChild(this.container);
this.hide = this.hide.bind(this);
this.refresh = this.refresh.bind(this);
+ this.onEditorChange = this.onEditorChange.bind(this);
EventEmitter.decorate(this);
this.doc.defaultView.addEventListener("resize",
this.refresh, true);
let config = {
mode: Editor.modes.html,
@@ -65,17 +66,17 @@ HTMLEditor.prototype = {
/**
* Need to refresh position by manually setting CSS values, so this will
* need to be called on resizes and other sizing changes.
*/
refresh: function () {
let element = this._attachedElement;
- if (element) {
+ if (element && element.offsetWidth) {
this.container.style.top = element.offsetTop + "px";
this.container.style.left = element.offsetLeft + "px";
this.container.style.width = element.offsetWidth + "px";
this.container.style.height = element.parentNode.offsetHeight + "px";
this.editor.refresh();
}
},
@@ -122,43 +123,49 @@ HTMLEditor.prototype = {
this._originalValue = text;
this.editor.setText(text);
this._attach(element);
this.container.style.display = "flex";
this._visible = true;
this.editor.refresh();
this.editor.focus();
+ this.editor.on("change", this.onEditorChange);
this.emit("popupshown");
},
/**
* Hide the editor, optionally committing the changes
*
* @param {Boolean} shouldCommit
* A change will be committed by default. If this param
* strictly equals false, no change will occur.
*/
hide: function (shouldCommit) {
if (!this._visible) {
return;
}
+ this.editor.off("change", this.onEditorChange);
this.container.style.display = "none";
this._detach();
let newValue = this.editor.getText();
let valueHasChanged = this._originalValue !== newValue;
let preventCommit = shouldCommit === false || !valueHasChanged;
this._originalValue = undefined;
this._visible = undefined;
this.emit("popuphidden", !preventCommit, newValue);
},
+ onEditorChange: function () {
+ this.emit("change", this.editor.getText());
+ },
+
/**
* Destroy this object and unbind all event handlers
*/
destroy: function () {
this.doc.defaultView.removeEventListener("resize",
this.refresh, true);
this.container.removeEventListener("click", this.hide);
this.editorInner.removeEventListener("click", stopPropagation);