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.
8 WebInspector
.InplaceEditor = function()
13 * @typedef {{cancel: function(), commit: function(), setWidth: function(number)}}
15 WebInspector
.InplaceEditor
.Controller
;
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
);
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
);
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
);
46 return Promise
.reject(new Error("Editing is already in progress"));
51 WebInspector
.InplaceEditor
.prototype = {
55 editorContent: function(editingContext
) {
56 var element
= editingContext
.element
;
57 if (element
.tagName
=== "INPUT" && element
.type
=== "text")
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)
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");
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
;
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))
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
= "";
124 function consumeCopy(e
)
129 this.setUpEditor(editingContext
);
131 editingContext
.oldText
= isMultiline
? config
.initialValue
: this.editorContent(editingContext
);
136 function blurEventListener(e
) {
137 if (config
.blurHandler
&& !config
.blurHandler(element
, e
))
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);
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
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
))
183 else if (event
.keyCode
=== WebInspector
.KeyboardShortcut
.Keys
.Esc
.code
|| event
.keyIdentifier
=== "U+001B")
185 else if (!isMultiline
&& event
.keyIdentifier
=== "U+0009") // Tab key
186 return "move-" + (event
.shiftKey
? "backward" : "forward");
190 function handleEditingResult(result
, event
)
192 if (result
=== "commit") {
193 editingCommitted
.call(element
);
195 } else if (result
=== "cancel") {
196 editingCancelled
.call(element
);
198 } else if (result
&& result
.startsWith("move-")) {
199 moveDirection
= result
.substring(5);
200 if (event
.keyIdentifier
!== "U+0009")
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);
228 element
.addEventListener("paste", pasteEventListener
, true);
231 cancel
: editingCancelled
.bind(element
),
232 commit
: editingCommitted
.bind(element
),
233 setWidth: function() {}
235 this.augmentEditingHandle(editingContext
, handle
);
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
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}
261 * @type {boolean|undefined}
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
;
290 this.lineWrapping
= lineWrapping
;
291 this.smartIndent
= smartIndent
;
295 * @param {function(!Event):string} postKeydownFinishHandler
297 setPostKeydownFinishHandler: function(postKeydownFinishHandler
)
299 this.postKeydownFinishHandler
= postKeydownFinishHandler
;