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