diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/devtools/front_end/ui/InplaceEditor.js')
-rw-r--r-- | chromium/third_party/WebKit/Source/devtools/front_end/ui/InplaceEditor.js | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/ui/InplaceEditor.js b/chromium/third_party/WebKit/Source/devtools/front_end/ui/InplaceEditor.js new file mode 100644 index 00000000000..d0e1c363e1c --- /dev/null +++ b/chromium/third_party/WebKit/Source/devtools/front_end/ui/InplaceEditor.js @@ -0,0 +1,260 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @constructor + */ +WebInspector.InplaceEditor = function() +{ +}; + +/** + * @param {!Element} element + * @param {!WebInspector.InplaceEditor.Config=} config + * @return {?{cancel: function(), commit: function(), setWidth: function(number)}} + */ +WebInspector.InplaceEditor.startEditing = function(element, config) +{ + if (config.multiline) + return WebInspector.moduleManager.instance(WebInspector.InplaceEditor).startEditing(element, config); + + if (!WebInspector.InplaceEditor._defaultInstance) + WebInspector.InplaceEditor._defaultInstance = new WebInspector.InplaceEditor(); + return WebInspector.InplaceEditor._defaultInstance.startEditing(element, config); +} + +WebInspector.InplaceEditor.prototype = { + /** + * @return {string} + */ + editorContent: function(editingContext) { + var element = editingContext.element; + if (element.tagName === "INPUT" && element.type === "text") + return element.value; + + return element.textContent; + }, + + setUpEditor: function(editingContext) + { + var element = editingContext.element; + element.classList.add("editing"); + + var oldTabIndex = element.getAttribute("tabIndex"); + if (typeof oldTabIndex !== "number" || oldTabIndex < 0) + element.tabIndex = 0; + WebInspector.setCurrentFocusElement(element); + editingContext.oldTabIndex = oldTabIndex; + }, + + closeEditor: function(editingContext) + { + var element = editingContext.element; + element.classList.remove("editing"); + + if (typeof editingContext.oldTabIndex !== "number") + element.removeAttribute("tabIndex"); + else + element.tabIndex = editingContext.oldTabIndex; + element.scrollTop = 0; + element.scrollLeft = 0; + }, + + cancelEditing: function(editingContext) + { + var element = editingContext.element; + if (element.tagName === "INPUT" && element.type === "text") + element.value = editingContext.oldText; + else + element.textContent = editingContext.oldText; + }, + + augmentEditingHandle: function(editingContext, handle) + { + }, + + /** + * @param {!Element} element + * @param {!WebInspector.InplaceEditor.Config=} config + * @return {?{cancel: function(), commit: function()}} + */ + startEditing: function(element, config) + { + if (!WebInspector.markBeingEdited(element, true)) + return null; + + config = config || new WebInspector.InplaceEditor.Config(function() {}, function() {}); + var editingContext = { element: element, config: config }; + var committedCallback = config.commitHandler; + var cancelledCallback = config.cancelHandler; + var pasteCallback = config.pasteHandler; + var context = config.context; + var isMultiline = config.multiline || false; + var moveDirection = ""; + var self = this; + + /** + * @param {?Event} e + */ + function consumeCopy(e) + { + e.consume(); + } + + this.setUpEditor(editingContext); + + editingContext.oldText = isMultiline ? config.initialValue : this.editorContent(editingContext); + + /** + * @param {?Event=} e + */ + function blurEventListener(e) { + if (!isMultiline || !e || !e.relatedTarget || !e.relatedTarget.isSelfOrDescendant(element)) + editingCommitted.call(element); + } + + function cleanUpAfterEditing() + { + WebInspector.markBeingEdited(element, false); + + element.removeEventListener("blur", blurEventListener, isMultiline); + element.removeEventListener("keydown", keyDownEventListener, true); + if (pasteCallback) + element.removeEventListener("paste", pasteEventListener, true); + + WebInspector.restoreFocusFromElement(element); + self.closeEditor(editingContext); + } + + /** @this {Element} */ + function editingCancelled() + { + self.cancelEditing(editingContext); + cleanUpAfterEditing(); + cancelledCallback(this, context); + } + + /** @this {Element} */ + function editingCommitted() + { + cleanUpAfterEditing(); + + committedCallback(this, self.editorContent(editingContext), editingContext.oldText, context, moveDirection); + } + + function defaultFinishHandler(event) + { + var isMetaOrCtrl = WebInspector.isMac() ? + event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey : + event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey; + if (isEnterKey(event) && (event.isMetaOrCtrlForTest || !isMultiline || isMetaOrCtrl)) + return "commit"; + else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code || event.keyIdentifier === "U+001B") + return "cancel"; + else if (!isMultiline && event.keyIdentifier === "U+0009") // Tab key + return "move-" + (event.shiftKey ? "backward" : "forward"); + } + + function handleEditingResult(result, event) + { + if (result === "commit") { + editingCommitted.call(element); + event.consume(true); + } else if (result === "cancel") { + editingCancelled.call(element); + event.consume(true); + } else if (result && result.startsWith("move-")) { + moveDirection = result.substring(5); + if (event.keyIdentifier !== "U+0009") + blurEventListener(); + } + } + + function pasteEventListener(event) + { + var result = pasteCallback(event); + handleEditingResult(result, event); + } + + function keyDownEventListener(event) + { + var handler = config.customFinishHandler || defaultFinishHandler; + var result = handler(event); + handleEditingResult(result, event); + } + + element.addEventListener("blur", blurEventListener, isMultiline); + element.addEventListener("keydown", keyDownEventListener, true); + if (pasteCallback) + element.addEventListener("paste", pasteEventListener, true); + + var handle = { + cancel: editingCancelled.bind(element), + commit: editingCommitted.bind(element) + }; + this.augmentEditingHandle(editingContext, handle); + return handle; + } +} + +/** + * @constructor + * @param {function(!Element,string,string,T,string)} commitHandler + * @param {function(!Element,T)} cancelHandler + * @param {T=} context + * @template T + */ +WebInspector.InplaceEditor.Config = function(commitHandler, cancelHandler, context) +{ + this.commitHandler = commitHandler; + this.cancelHandler = cancelHandler + this.context = context; + + /** + * Handles the "paste" event, return values are the same as those for customFinishHandler + * @type {function(!Element)|undefined} + */ + this.pasteHandler; + + /** + * Whether the edited element is multiline + * @type {boolean|undefined} + */ + this.multiline; + + /** + * Custom finish handler for the editing session (invoked on keydown) + * @type {function(!Element,*)|undefined} + */ + this.customFinishHandler; +} + +WebInspector.InplaceEditor.Config.prototype = { + setPasteHandler: function(pasteHandler) + { + this.pasteHandler = pasteHandler; + }, + + /** + * @param {string} initialValue + * @param {!Object} mode + * @param {string} theme + * @param {boolean=} lineWrapping + * @param {boolean=} smartIndent + */ + setMultilineOptions: function(initialValue, mode, theme, lineWrapping, smartIndent) + { + this.multiline = true; + this.initialValue = initialValue; + this.mode = mode; + this.theme = theme; + this.lineWrapping = lineWrapping; + this.smartIndent = smartIndent; + }, + + setCustomFinishHandler: function(customFinishHandler) + { + this.customFinishHandler = customFinishHandler; + } +} |