Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / ui / InplaceEditor.js
blob9bb9cb48a4cdee2c313c9c0694d0803f7fac8786
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 /**
6 * @constructor
7 */
8 WebInspector.InplaceEditor = function()
12 /**
13 * @typedef {{cancel: function(), commit: function(), setWidth: function(number)}}
15 WebInspector.InplaceEditor.Controller;
17 /**
18 * @param {!Element} element
19 * @param {!WebInspector.InplaceEditor.Config=} config
20 * @return {?WebInspector.InplaceEditor.Controller}
22 WebInspector.InplaceEditor.startEditing = function(element, config)
24 if (!WebInspector.InplaceEditor._defaultInstance)
25 WebInspector.InplaceEditor._defaultInstance = new WebInspector.InplaceEditor();
26 return WebInspector.InplaceEditor._defaultInstance.startEditing(element, config);
29 /**
30 * @param {!Element} element
31 * @param {!WebInspector.InplaceEditor.Config=} config
32 * @return {!Promise.<!WebInspector.InplaceEditor.Controller>}
34 WebInspector.InplaceEditor.startMultilineEditing = function(element, config)
36 return self.runtime.instancePromise(WebInspector.InplaceEditor).then(startEditing);
38 /**
39 * @param {!Object} inplaceEditor
40 * @return {!WebInspector.InplaceEditor.Controller|!Promise.<!WebInspector.InplaceEditor.Controller>}
42 function startEditing(inplaceEditor)
44 var controller = /** @type {!WebInspector.InplaceEditor} */ (inplaceEditor).startEditing(element, config);
45 if (!controller)
46 return Promise.reject(new Error("Editing is already in progress"));
47 return controller;
51 WebInspector.InplaceEditor.prototype = {
52 /**
53 * @return {string}
55 editorContent: function(editingContext) {
56 var element = editingContext.element;
57 if (element.tagName === "INPUT" && element.type === "text")
58 return element.value;
60 return element.textContent;
63 setUpEditor: function(editingContext)
65 var element = editingContext.element;
66 element.classList.add("editing");
68 var oldTabIndex = element.getAttribute("tabIndex");
69 if (typeof oldTabIndex !== "number" || oldTabIndex < 0)
70 element.tabIndex = 0;
71 WebInspector.setCurrentFocusElement(element);
72 editingContext.oldTabIndex = oldTabIndex;
75 closeEditor: function(editingContext)
77 var element = editingContext.element;
78 element.classList.remove("editing");
80 if (typeof editingContext.oldTabIndex !== "number")
81 element.removeAttribute("tabIndex");
82 else
83 element.tabIndex = editingContext.oldTabIndex;
84 element.scrollTop = 0;
85 element.scrollLeft = 0;
88 cancelEditing: function(editingContext)
90 var element = editingContext.element;
91 if (element.tagName === "INPUT" && element.type === "text")
92 element.value = editingContext.oldText;
93 else
94 element.textContent = editingContext.oldText;
97 augmentEditingHandle: function(editingContext, handle)
102 * @param {!Element} element
103 * @param {!WebInspector.InplaceEditor.Config=} config
104 * @return {?WebInspector.InplaceEditor.Controller}
106 startEditing: function(element, config)
108 if (!WebInspector.markBeingEdited(element, true))
109 return null;
111 config = config || new WebInspector.InplaceEditor.Config(function() {}, function() {});
112 var editingContext = { element: element, config: config };
113 var committedCallback = config.commitHandler;
114 var cancelledCallback = config.cancelHandler;
115 var pasteCallback = config.pasteHandler;
116 var context = config.context;
117 var isMultiline = config.multiline || false;
118 var moveDirection = "";
119 var self = this;
122 * @param {!Event} e
124 function consumeCopy(e)
126 e.consume();
129 this.setUpEditor(editingContext);
131 editingContext.oldText = isMultiline ? config.initialValue : this.editorContent(editingContext);
134 * @param {!Event=} e
136 function blurEventListener(e) {
137 if (config.blurHandler && !config.blurHandler(element, e))
138 return;
139 if (!isMultiline || !e || !e.relatedTarget || !e.relatedTarget.isSelfOrDescendant(element))
140 editingCommitted.call(element);
143 function cleanUpAfterEditing()
145 WebInspector.markBeingEdited(element, false);
147 element.removeEventListener("blur", blurEventListener, isMultiline);
148 element.removeEventListener("keydown", keyDownEventListener, true);
149 if (pasteCallback)
150 element.removeEventListener("paste", pasteEventListener, true);
152 WebInspector.restoreFocusFromElement(element);
153 self.closeEditor(editingContext);
156 /** @this {Element} */
157 function editingCancelled()
159 self.cancelEditing(editingContext);
160 cleanUpAfterEditing();
161 cancelledCallback(this, context);
164 /** @this {Element} */
165 function editingCommitted()
167 cleanUpAfterEditing();
169 committedCallback(this, self.editorContent(editingContext), editingContext.oldText, context, moveDirection);
173 * @param {!Event} event
174 * @return {string}
176 function defaultFinishHandler(event)
178 var isMetaOrCtrl = WebInspector.isMac() ?
179 event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey :
180 event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey;
181 if (isEnterKey(event) && (event.isMetaOrCtrlForTest || !isMultiline || isMetaOrCtrl))
182 return "commit";
183 else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code || event.keyIdentifier === "U+001B")
184 return "cancel";
185 else if (!isMultiline && event.keyIdentifier === "U+0009") // Tab key
186 return "move-" + (event.shiftKey ? "backward" : "forward");
187 return "";
190 function handleEditingResult(result, event)
192 if (result === "commit") {
193 editingCommitted.call(element);
194 event.consume(true);
195 } else if (result === "cancel") {
196 editingCancelled.call(element);
197 event.consume(true);
198 } else if (result && result.startsWith("move-")) {
199 moveDirection = result.substring(5);
200 if (event.keyIdentifier !== "U+0009")
201 blurEventListener();
206 * @param {!Event} event
208 function pasteEventListener(event)
210 var result = pasteCallback(event);
211 handleEditingResult(result, event);
215 * @param {!Event} event
217 function keyDownEventListener(event)
219 var result = defaultFinishHandler(event);
220 if (!result && config.postKeydownFinishHandler)
221 result = config.postKeydownFinishHandler(event);
222 handleEditingResult(result, event);
225 element.addEventListener("blur", blurEventListener, isMultiline);
226 element.addEventListener("keydown", keyDownEventListener, true);
227 if (pasteCallback)
228 element.addEventListener("paste", pasteEventListener, true);
230 var handle = {
231 cancel: editingCancelled.bind(element),
232 commit: editingCommitted.bind(element),
233 setWidth: function() {}
235 this.augmentEditingHandle(editingContext, handle);
236 return handle;
241 * @constructor
242 * @param {function(!Element,string,string,T,string)} commitHandler
243 * @param {function(!Element,T)} cancelHandler
244 * @param {T=} context
245 * @param {function(!Element,!Event):boolean=} blurHandler
246 * @template T
248 WebInspector.InplaceEditor.Config = function(commitHandler, cancelHandler, context, blurHandler)
250 this.commitHandler = commitHandler;
251 this.cancelHandler = cancelHandler;
252 this.context = context;
253 this.blurHandler = blurHandler;
256 * @type {function(!Event):string|undefined}
258 this.pasteHandler;
261 * @type {boolean|undefined}
263 this.multiline;
266 * @type {function(!Event):string|undefined}
268 this.postKeydownFinishHandler;
271 WebInspector.InplaceEditor.Config.prototype = {
272 setPasteHandler: function(pasteHandler)
274 this.pasteHandler = pasteHandler;
278 * @param {string} initialValue
279 * @param {!Object} mode
280 * @param {string} theme
281 * @param {boolean=} lineWrapping
282 * @param {boolean=} smartIndent
284 setMultilineOptions: function(initialValue, mode, theme, lineWrapping, smartIndent)
286 this.multiline = true;
287 this.initialValue = initialValue;
288 this.mode = mode;
289 this.theme = theme;
290 this.lineWrapping = lineWrapping;
291 this.smartIndent = smartIndent;
295 * @param {function(!Event):string} postKeydownFinishHandler
297 setPostKeydownFinishHandler: function(postKeydownFinishHandler)
299 this.postKeydownFinishHandler = postKeydownFinishHandler;