/* * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ WebInspector.isBeingEdited = function(element) { while (element) { if (element.__editing) return true; element = element.parentNode; } return false; } WebInspector.markBeingEdited = function(element, value) { if (value) { if (element.__editing) return false; element.__editing = true; WebInspector.__editingCount = (WebInspector.__editingCount || 0) + 1; } else { if (!element.__editing) return false; delete element.__editing; --WebInspector.__editingCount; } return true; } WebInspector.isEditingAnyField = function() { return !!WebInspector.__editingCount; } WebInspector.isEventTargetAnEditableField = function(event) { const textInputTypes = {"text": true, "search": true, "tel": true, "url": true, "email": true, "password": true}; if (event.target instanceof HTMLInputElement) return event.target.type in textInputTypes; var codeMirrorEditorElement = event.target.enclosingNodeOrSelfWithClass("CodeMirror"); if (codeMirrorEditorElement && codeMirrorEditorElement.CodeMirror) return !codeMirrorEditorElement.CodeMirror.getOption("readOnly"); if (event.target instanceof HTMLTextAreaElement) return true; if (event.target.enclosingNodeOrSelfWithClass("text-prompt")) return true; return false; } WebInspector.EditingConfig = function(commitHandler, cancelHandler, context) { this.commitHandler = commitHandler; this.cancelHandler = cancelHandler; this.context = context; this.pasteHandler; this.multiline; this.customFinishHandler; this.spellcheck = false; } WebInspector.EditingConfig.prototype = { setPasteHandler: function(pasteHandler) { this.pasteHandler = pasteHandler; }, setMultiline: function(multiline) { this.multiline = multiline; }, setCustomFinishHandler: function(customFinishHandler) { this.customFinishHandler = customFinishHandler; } } WebInspector.startEditing = function(element, config) { if (!WebInspector.markBeingEdited(element, true)) return; config = config || new WebInspector.EditingConfig(function() {}, function() {}); var committedCallback = config.commitHandler; var cancelledCallback = config.cancelHandler; var pasteCallback = config.pasteHandler; var context = config.context; var oldText = getContent(element); var moveDirection = ""; element.classList.add("editing"); var oldSpellCheck = element.hasAttribute("spellcheck") ? element.spellcheck : undefined; element.spellcheck = config.spellcheck; if (config.multiline) element.classList.add("multiline"); var oldTabIndex = element.tabIndex; if (element.tabIndex < 0) element.tabIndex = 0; function blurEventListener() { editingCommitted.call(element); } function getContent(element) { if (element.tagName === "INPUT" && element.type === "text") return element.value; else return element.textContent; } function cleanUpAfterEditing() { WebInspector.markBeingEdited(element, false); this.classList.remove("editing"); this.scrollTop = 0; this.scrollLeft = 0; if (oldSpellCheck === undefined) element.removeAttribute("spellcheck"); else element.spellcheck = oldSpellCheck; if (oldTabIndex === -1) this.removeAttribute("tabindex"); else this.tabIndex = oldTabIndex; element.removeEventListener("blur", blurEventListener, false); element.removeEventListener("keydown", keyDownEventListener, true); if (pasteCallback) element.removeEventListener("paste", pasteEventListener, true); WebInspector.restoreFocusFromElement(element); } function editingCancelled() { if (this.tagName === "INPUT" && this.type === "text") this.value = oldText; else this.textContent = oldText; cleanUpAfterEditing.call(this); cancelledCallback(this, context); } function editingCommitted() { cleanUpAfterEditing.call(this); committedCallback(this, getContent(this), oldText, context, moveDirection); } function defaultFinishHandler(event) { var hasOnlyMetaModifierKey = event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey; if (isEnterKey(event) && (!config.multiline || hasOnlyMetaModifierKey)) return "commit"; else if (event.keyCode === WebInspector.KeyboardShortcut.Key.Escape.keyCode || event.keyIdentifier === "U+001B") return "cancel"; else if (event.keyIdentifier === "U+0009") // Tab key return "move-" + (event.shiftKey ? "backward" : "forward"); } function handleEditingResult(result, event) { if (result === "commit") { editingCommitted.call(element); event.preventDefault(); event.stopPropagation(); } else if (result === "cancel") { editingCancelled.call(element); event.preventDefault(); event.stopPropagation(); } 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, false); element.addEventListener("keydown", keyDownEventListener, true); if (pasteCallback) element.addEventListener("paste", pasteEventListener, true); element.focus(); return { cancel: editingCancelled.bind(element), commit: editingCommitted.bind(element) }; }