/* * Copyright (C) 2013, 2015 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. */ WI.BreakpointActionView = class BreakpointActionView extends WI.Object { constructor(action, delegate, {omitFocus} = {}) { super(); console.assert(action); console.assert(delegate); this._action = action; this._delegate = delegate; this._element = document.createElement("div"); this._element.className = "breakpoint-action-block"; var header = this._element.appendChild(document.createElement("div")); header.className = "breakpoint-action-block-header"; var picker = header.appendChild(document.createElement("select")); picker.addEventListener("change", this._pickerChanged.bind(this)); for (var key in WI.BreakpointAction.Type) { var type = WI.BreakpointAction.Type[key]; var option = document.createElement("option"); option.textContent = WI.BreakpointActionView.displayStringForType(type); option.selected = this._action.type === type; option.value = type; picker.add(option); } let buttonContainerElement = header.appendChild(document.createElement("div")); buttonContainerElement.classList.add("breakpoint-action-button-container"); let appendActionButton = buttonContainerElement.appendChild(document.createElement("button")); appendActionButton.className = "breakpoint-action-append-button"; appendActionButton.addEventListener("click", this._appendActionButtonClicked.bind(this)); appendActionButton.title = WI.UIString("Add new breakpoint action after this action"); let removeActionButton = buttonContainerElement.appendChild(document.createElement("button")); removeActionButton.className = "breakpoint-action-remove-button"; removeActionButton.addEventListener("click", this._removeAction.bind(this)); removeActionButton.title = WI.UIString("Delete this breakpoint action"); this._bodyElement = this._element.appendChild(document.createElement("div")); this._bodyElement.className = "breakpoint-action-block-body"; this._updateBody(omitFocus); } // Static static displayStringForType(type) { switch (type) { case WI.BreakpointAction.Type.Log: return WI.UIString("Log Message"); case WI.BreakpointAction.Type.Evaluate: return WI.UIString("Evaluate JavaScript"); case WI.BreakpointAction.Type.Sound: return WI.UIString("Play Sound"); case WI.BreakpointAction.Type.Probe: return WI.UIString("Probe Expression"); default: console.assert(false); return ""; } } // Public get action() { return this._action; } get element() { return this._element; } // Private _pickerChanged(event) { this._action.type = event.target.value; this._updateBody(); this._delegate.breakpointActionViewResized(this); } _appendActionButtonClicked(event) { this._delegate.breakpointActionViewAppendActionView(this, new WI.BreakpointAction(this._action.type)); } _removeAction() { this._delegate.breakpointActionViewRemoveActionView(this); } _updateBody(omitFocus) { this._bodyElement.removeChildren(); let createOptionsElements = () => { let optionsElement = document.createElement("div"); let emulateUserGestureLabel = optionsElement.appendChild(document.createElement("label")); this._emulateUserGestureCheckbox = emulateUserGestureLabel.appendChild(document.createElement("input")); this._emulateUserGestureCheckbox.type = "checkbox"; this._emulateUserGestureCheckbox.checked = this._action.emulateUserGesture; this._emulateUserGestureCheckbox.addEventListener("change", this._handleEmulateUserGestureCheckboxChange.bind(this)); emulateUserGestureLabel.appendChild(document.createTextNode(WI.UIString("Emulate User Gesture", "Emulate User Gesture @ breakpoint action configuration", "Checkbox shown when configuring log/evaluate/probe breakpoint actions to cause it to be evaluated as though it was in response to user interaction."))); return optionsElement; }; switch (this._action.type) { case WI.BreakpointAction.Type.Log: this._bodyElement.hidden = false; var input = this._bodyElement.appendChild(document.createElement("input")); input.placeholder = WI.UIString("Message"); input.addEventListener("input", this._handleLogInputInput.bind(this)); input.value = this._action.data || ""; input.spellcheck = false; if (!omitFocus) setTimeout(function() { input.focus(); }, 0); var flexWrapper = this._bodyElement.appendChild(document.createElement("div")); flexWrapper.className = "flex"; if (WI.BreakpointAction.supportsEmulateUserAction()) flexWrapper.appendChild(createOptionsElements()); var descriptionElement = flexWrapper.appendChild(document.createElement("div")); descriptionElement.classList.add("description"); descriptionElement.setAttribute("dir", "ltr"); descriptionElement.textContent = WI.UIString("${expr} = expression"); break; case WI.BreakpointAction.Type.Evaluate: case WI.BreakpointAction.Type.Probe: this._bodyElement.hidden = false; var editorElement = this._bodyElement.appendChild(document.createElement("div")); editorElement.classList.add("breakpoint-action-eval-editor"); editorElement.classList.add(WI.SyntaxHighlightedStyleClassName); this._codeMirror = WI.CodeMirrorEditor.create(editorElement, { lineWrapping: true, mode: "text/javascript", matchBrackets: true, value: this._action.data || "", }); this._codeMirrorClientHeight = NaN; this._codeMirror.on("changes", this._handleJavaScriptCodeMirrorChanges.bind(this)); var completionController = new WI.CodeMirrorCompletionController(this._delegate.breakpointActionViewCodeMirrorCompletionControllerMode(this, this._codeMirror), this._codeMirror); completionController.addExtendedCompletionProvider("javascript", WI.javaScriptRuntimeCompletionProvider); if (WI.BreakpointAction.supportsEmulateUserAction()) this._bodyElement.appendChild(createOptionsElements()); // CodeMirror needs a refresh after the popover displays to layout otherwise it doesn't appear. setTimeout(() => { this._codeMirror.refresh(); if (!omitFocus) this._codeMirror.focus(); }, 0); break; case WI.BreakpointAction.Type.Sound: this._bodyElement.hidden = true; break; default: console.assert(false); this._bodyElement.hidden = true; break; } } _handleLogInputInput(event) { this._action.data = event.target.value; } _handleJavaScriptCodeMirrorChanges(codeMirror, changes) { // Throw away the expression if it's just whitespace. this._action.data = this._codeMirror.getValue().trim(); let {clientHeight} = this._codeMirror.getScrollInfo(); if (clientHeight !== this._codeMirrorClientHeight) { this._codeMirrorClientHeight = clientHeight; this._delegate.breakpointActionViewResized(this); } } _handleEmulateUserGestureCheckboxChange(event) { this._action.emulateUserGesture = this._emulateUserGestureCheckbox.checked; } };