browser/devtools/markupview/html-editor.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* vim:set ts=2 sw=2 sts=2 et tw=80:
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5 "use strict";
michael@0 6
michael@0 7 const {Cu} = require("chrome");
michael@0 8 const Editor = require("devtools/sourceeditor/editor");
michael@0 9 Cu.import("resource://gre/modules/Services.jsm");
michael@0 10 Cu.import("resource://gre/modules/devtools/event-emitter.js");
michael@0 11
michael@0 12 exports.HTMLEditor = HTMLEditor;
michael@0 13
michael@0 14 function ctrl(k) {
michael@0 15 return (Services.appinfo.OS == "Darwin" ? "Cmd-" : "Ctrl-") + k;
michael@0 16 }
michael@0 17 function stopPropagation(e) {
michael@0 18 e.stopPropagation();
michael@0 19 }
michael@0 20 /**
michael@0 21 * A wrapper around the Editor component, that allows editing of HTML.
michael@0 22 *
michael@0 23 * The main functionality this provides around the Editor is the ability
michael@0 24 * to show/hide/position an editor inplace. It only appends once to the
michael@0 25 * body, and uses CSS to position the editor. The reason it is done this
michael@0 26 * way is that the editor is loaded in an iframe, and calling appendChild
michael@0 27 * causes it to reload.
michael@0 28 *
michael@0 29 * Meant to be embedded inside of an HTML page, as in markup-view.xhtml.
michael@0 30 *
michael@0 31 * @param HTMLDocument htmlDocument
michael@0 32 * The document to attach the editor to. Will also use this
michael@0 33 * document as a basis for listening resize events.
michael@0 34 */
michael@0 35 function HTMLEditor(htmlDocument)
michael@0 36 {
michael@0 37 this.doc = htmlDocument;
michael@0 38 this.container = this.doc.createElement("div");
michael@0 39 this.container.className = "html-editor theme-body";
michael@0 40 this.container.style.display = "none";
michael@0 41 this.editorInner = this.doc.createElement("div");
michael@0 42 this.editorInner.className = "html-editor-inner";
michael@0 43 this.container.appendChild(this.editorInner);
michael@0 44
michael@0 45 this.doc.body.appendChild(this.container);
michael@0 46 this.hide = this.hide.bind(this);
michael@0 47 this.refresh = this.refresh.bind(this);
michael@0 48
michael@0 49 EventEmitter.decorate(this);
michael@0 50
michael@0 51 this.doc.defaultView.addEventListener("resize",
michael@0 52 this.refresh, true);
michael@0 53
michael@0 54 let config = {
michael@0 55 mode: Editor.modes.html,
michael@0 56 lineWrapping: true,
michael@0 57 styleActiveLine: false,
michael@0 58 extraKeys: {},
michael@0 59 theme: "mozilla markup-view"
michael@0 60 };
michael@0 61
michael@0 62 config.extraKeys[ctrl("Enter")] = this.hide;
michael@0 63 config.extraKeys["F2"] = this.hide;
michael@0 64 config.extraKeys["Esc"] = this.hide.bind(this, false);
michael@0 65
michael@0 66 this.container.addEventListener("click", this.hide, false);
michael@0 67 this.editorInner.addEventListener("click", stopPropagation, false);
michael@0 68 this.editor = new Editor(config);
michael@0 69
michael@0 70 let iframe = this.editorInner.ownerDocument.createElement("iframe");
michael@0 71 this.editor.appendTo(this.editorInner, iframe).then(() => {
michael@0 72 this.hide(false);
michael@0 73 }).then(null, (err) => console.log(err.message));
michael@0 74 }
michael@0 75
michael@0 76 HTMLEditor.prototype = {
michael@0 77
michael@0 78 /**
michael@0 79 * Need to refresh position by manually setting CSS values, so this will
michael@0 80 * need to be called on resizes and other sizing changes.
michael@0 81 */
michael@0 82 refresh: function() {
michael@0 83 let element = this._attachedElement;
michael@0 84
michael@0 85 if (element) {
michael@0 86 this.container.style.top = element.offsetTop + "px";
michael@0 87 this.container.style.left = element.offsetLeft + "px";
michael@0 88 this.container.style.width = element.offsetWidth + "px";
michael@0 89 this.container.style.height = element.parentNode.offsetHeight + "px";
michael@0 90 this.editor.refresh();
michael@0 91 }
michael@0 92 },
michael@0 93
michael@0 94 /**
michael@0 95 * Anchor the editor to a particular element.
michael@0 96 *
michael@0 97 * @param DOMNode element
michael@0 98 * The element that the editor will be anchored to.
michael@0 99 * Should belong to the HTMLDocument passed into the constructor.
michael@0 100 */
michael@0 101 _attach: function(element)
michael@0 102 {
michael@0 103 this._detach();
michael@0 104 this._attachedElement = element;
michael@0 105 element.classList.add("html-editor-container");
michael@0 106 this.refresh();
michael@0 107 },
michael@0 108
michael@0 109 /**
michael@0 110 * Unanchor the editor from an element.
michael@0 111 */
michael@0 112 _detach: function()
michael@0 113 {
michael@0 114 if (this._attachedElement) {
michael@0 115 this._attachedElement.classList.remove("html-editor-container");
michael@0 116 this._attachedElement = undefined;
michael@0 117 }
michael@0 118 },
michael@0 119
michael@0 120 /**
michael@0 121 * Anchor the editor to a particular element, and show the editor.
michael@0 122 *
michael@0 123 * @param DOMNode element
michael@0 124 * The element that the editor will be anchored to.
michael@0 125 * Should belong to the HTMLDocument passed into the constructor.
michael@0 126 * @param string text
michael@0 127 * Value to set the contents of the editor to
michael@0 128 * @param function cb
michael@0 129 * The function to call when hiding
michael@0 130 */
michael@0 131 show: function(element, text)
michael@0 132 {
michael@0 133 if (this._visible) {
michael@0 134 return;
michael@0 135 }
michael@0 136
michael@0 137 this._originalValue = text;
michael@0 138 this.editor.setText(text);
michael@0 139 this._attach(element);
michael@0 140 this.container.style.display = "flex";
michael@0 141 this._visible = true;
michael@0 142
michael@0 143 this.editor.refresh();
michael@0 144 this.editor.focus();
michael@0 145
michael@0 146 this.emit("popupshown");
michael@0 147 },
michael@0 148
michael@0 149 /**
michael@0 150 * Hide the editor, optionally committing the changes
michael@0 151 *
michael@0 152 * @param bool shouldCommit
michael@0 153 * A change will be committed by default. If this param
michael@0 154 * strictly equals false, no change will occur.
michael@0 155 */
michael@0 156 hide: function(shouldCommit)
michael@0 157 {
michael@0 158 if (!this._visible) {
michael@0 159 return;
michael@0 160 }
michael@0 161
michael@0 162 this.container.style.display = "none";
michael@0 163 this._detach();
michael@0 164
michael@0 165 let newValue = this.editor.getText();
michael@0 166 let valueHasChanged = this._originalValue !== newValue;
michael@0 167 let preventCommit = shouldCommit === false || !valueHasChanged;
michael@0 168 this._originalValue = undefined;
michael@0 169 this._visible = undefined;
michael@0 170 this.emit("popuphidden", !preventCommit, newValue);
michael@0 171 },
michael@0 172
michael@0 173 /**
michael@0 174 * Destroy this object and unbind all event handlers
michael@0 175 */
michael@0 176 destroy: function()
michael@0 177 {
michael@0 178 this.doc.defaultView.removeEventListener("resize",
michael@0 179 this.refresh, true);
michael@0 180 this.container.removeEventListener("click", this.hide, false);
michael@0 181 this.editorInner.removeEventListener("click", stopPropagation, false);
michael@0 182
michael@0 183 this.hide(false);
michael@0 184 this.container.parentNode.removeChild(this.container);
michael@0 185 }
michael@0 186 };

mercurial