| /* |
| * Copyright (C) 2018 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.AuditNavigationSidebarPanel = class AuditNavigationSidebarPanel extends WI.NavigationSidebarPanel |
| { |
| constructor() |
| { |
| super("audit", WI.UIString("Audits")); |
| } |
| |
| // Static |
| |
| static _createNavigationItemTitle() |
| { |
| return WI.UIString("Create", "Create @ Audit Tab Navigation Sidebar", "Title of button that creates a new audit."); |
| } |
| |
| // Public |
| |
| showDefaultContentView() |
| { |
| let contentView = new WI.ContentView; |
| |
| if (WI.auditManager.editing) { |
| let contentPlaceholder = WI.createMessageTextView(WI.UIString("Editing audits")); |
| contentView.element.appendChild(contentPlaceholder); |
| |
| let descriptionElement = contentPlaceholder.appendChild(document.createElement("div")); |
| descriptionElement.className = "description"; |
| descriptionElement.textContent = WI.UIString("Select an audit in the navigation sidebar to edit it."); |
| |
| let createAuditNavigationItem = new WI.ButtonNavigationItem("create-audit", WI.AuditNavigationSidebarPanel._createNavigationItemTitle(), "Images/Plus15.svg", 15, 15); |
| createAuditNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText; |
| createAuditNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleCreateButtonNavigationItemClicked, this); |
| |
| let createAuditHelpElement = WI.createNavigationItemHelp(WI.UIString("Press %s to create a new audit."), createAuditNavigationItem); |
| createAuditHelpElement.classList.add("create-audit"); |
| contentPlaceholder.appendChild(createAuditHelpElement); |
| |
| let stopEditingAuditsNavigationItem = new WI.ButtonNavigationItem("stop-editing-audits", WI.UIString("Done")); |
| stopEditingAuditsNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleEditButtonNavigationItemClicked, this); |
| |
| let stopEditingAuditsHelpElement = WI.createNavigationItemHelp(WI.UIString("Press %s to stop editing audits."), stopEditingAuditsNavigationItem); |
| stopEditingAuditsHelpElement.classList.add("stop-editing-audits"); |
| contentPlaceholder.appendChild(stopEditingAuditsHelpElement); |
| } else { |
| let hasEnabledAudit = WI.auditManager.tests.length && WI.auditManager.tests.some((test) => !test.disabled && test.supported); |
| |
| let contentPlaceholder = WI.createMessageTextView(WI.UIString("No audit selected")); |
| contentView.element.appendChild(contentPlaceholder); |
| |
| if (hasEnabledAudit) { |
| let descriptionElement = contentPlaceholder.appendChild(document.createElement("div")); |
| descriptionElement.className = "description"; |
| descriptionElement.textContent = WI.UIString("Select an audit in the navigation sidebar to view its results."); |
| } |
| |
| let importAuditNavigationItem = new WI.ButtonNavigationItem("import-audit", WI.UIString("Import"), "Images/Import.svg", 15, 15); |
| importAuditNavigationItem.title = WI.UIString("Import audit or result"); |
| importAuditNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText; |
| importAuditNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleImportButtonNavigationItemClicked, this); |
| |
| let importAuditHelpElement = WI.createNavigationItemHelp(WI.UIString("Press %s to import an audit or a result."), importAuditNavigationItem); |
| importAuditHelpElement.classList.add("import-audit"); |
| contentPlaceholder.appendChild(importAuditHelpElement); |
| |
| let startEditingAuditsNavigationItem = new WI.ButtonNavigationItem("start-editing-audits", WI.UIString("Edit")); |
| startEditingAuditsNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleEditButtonNavigationItemClicked, this); |
| |
| let startEditingAuditsHelpElement = WI.createNavigationItemHelp(hasEnabledAudit ? WI.UIString("Press %s to start editing audits.") : WI.UIString("Press %s to enable audits."), startEditingAuditsNavigationItem); |
| startEditingAuditsHelpElement.classList.add("start-editing-audits"); |
| contentPlaceholder.appendChild(startEditingAuditsHelpElement); |
| } |
| |
| let versionContainer = contentView.element.appendChild(document.createElement("div")); |
| versionContainer.classList.add("audit-version"); |
| |
| let version = WI.AuditTestBase.Version; |
| if (InspectorBackend.hasDomain("Audit")) |
| version = Math.min(version, InspectorBackend.getVersion("Audit")); |
| versionContainer.textContent = WI.UIString("Audit version: %s").format(version); |
| |
| versionContainer.appendChild(WI.auditManager.editing ? WI.ReferencePage.AuditTab.EditingAudits.createLinkElement() : WI.ReferencePage.AuditTab.createLinkElement()); |
| |
| this.contentBrowser.showContentView(contentView); |
| } |
| |
| // Popover delegate |
| |
| willDismissPopover(popover) |
| { |
| console.assert(popover instanceof WI.CreateAuditPopover, popover); |
| |
| let audit = popover.audit; |
| if (!audit) { |
| InspectorFrontendHost.beep(); |
| return; |
| } |
| |
| WI.auditManager.addTest(audit, {save: true}); |
| |
| WI.showRepresentedObject(audit); |
| } |
| |
| // Protected |
| |
| initialLayout() |
| { |
| super.initialLayout(); |
| |
| this.contentTreeOutline.allowsRepeatSelection = false; |
| |
| let controlsNavigationBar = new WI.NavigationBar; |
| |
| this._startStopButtonNavigationItem = new WI.ToggleButtonNavigationItem("start-stop-audit", WI.UIString("Start"), WI.UIString("Stop"), "Images/AuditStart.svg", "Images/AuditStop.svg", 13, 13); |
| this._startStopButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText; |
| this._startStopButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleStartStopButtonNavigationItemClicked, this); |
| controlsNavigationBar.addNavigationItem(this._startStopButtonNavigationItem); |
| |
| this._createButtonNavigationItem = new WI.ButtonNavigationItem("create-audit", WI.AuditNavigationSidebarPanel._createNavigationItemTitle(), "Images/Plus15.svg", 15, 15); |
| this._createButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText; |
| this._createButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleCreateButtonNavigationItemClicked, this); |
| controlsNavigationBar.addNavigationItem(this._createButtonNavigationItem); |
| |
| controlsNavigationBar.addNavigationItem(new WI.DividerNavigationItem); |
| |
| let importButtonNavigationItem = new WI.ButtonNavigationItem("import-audit", WI.UIString("Import"), "Images/Import.svg", 15, 15); |
| importButtonNavigationItem.title = WI.UIString("Import audit or result"); |
| importButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText; |
| importButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleImportButtonNavigationItemClicked, this); |
| controlsNavigationBar.addNavigationItem(importButtonNavigationItem); |
| |
| this.addSubview(controlsNavigationBar); |
| |
| this._editButtonNavigationItem = new WI.ActivateButtonNavigationItem("edit-audits", WI.UIString("Edit"), WI.UIString("Done")); |
| this._editButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleEditButtonNavigationItemClicked, this); |
| this.filterBar.addFilterNavigationItem(this._editButtonNavigationItem); |
| |
| for (let test of WI.auditManager.tests) |
| this._addTest(test); |
| |
| WI.auditManager.results.forEach((result, i) => { |
| this._addResult(result, i); |
| }); |
| |
| this._updateControlNavigationItems(); |
| this._updateEditNavigationItems(); |
| this._updateNoAuditsPlaceholder(); |
| |
| WI.AuditTestGroup.addEventListener(WI.AuditTestGroup.Event.TestRemoved, this._handleAuditTestRemoved, this); |
| |
| WI.auditManager.addEventListener(WI.AuditManager.Event.EditingChanged, this._handleAuditManagerEditingChanged, this); |
| WI.auditManager.addEventListener(WI.AuditManager.Event.RunningStateChanged, this._handleAuditManagerRunningStateChanged, this); |
| WI.auditManager.addEventListener(WI.AuditManager.Event.TestAdded, this._handleAuditTestAdded, this); |
| WI.auditManager.addEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditTestCompleted, this); |
| WI.auditManager.addEventListener(WI.AuditManager.Event.TestRemoved, this._handleAuditTestRemoved, this); |
| WI.auditManager.addEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditTestScheduled, this); |
| |
| this.contentTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this); |
| } |
| |
| closed() |
| { |
| super.closed(); |
| |
| if (this.didInitialLayout) { |
| WI.auditManager.removeEventListener(WI.AuditManager.Event.EditingChanged, this._handleAuditManagerEditingChanged, this); |
| WI.auditManager.removeEventListener(WI.AuditManager.Event.RunningStateChanged, this._handleAuditManagerRunningStateChanged, this); |
| WI.auditManager.removeEventListener(WI.AuditManager.Event.TestAdded, this._handleAuditTestAdded, this); |
| WI.auditManager.removeEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditTestCompleted, this); |
| WI.auditManager.removeEventListener(WI.AuditManager.Event.TestRemoved, this._handleAuditTestRemoved, this); |
| WI.auditManager.removeEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditTestScheduled, this); |
| } |
| } |
| |
| updateFilter() |
| { |
| super.updateFilter(); |
| |
| if (!this.hasActiveFilters) |
| this._updateNoAuditsPlaceholder(); |
| } |
| |
| hasCustomFilters() |
| { |
| return true; |
| } |
| |
| matchTreeElementAgainstCustomFilters(treeElement, flags) |
| { |
| if (WI.auditManager.editing) { |
| if (treeElement.representedObject instanceof WI.AuditTestResultBase || treeElement.hasAncestor(this._resultsFolderTreeElement) || treeElement === this._resultsFolderTreeElement) |
| return false; |
| } else { |
| if (treeElement.representedObject instanceof WI.AuditTestBase && (treeElement.representedObject.disabled || !treeElement.representedObject.supported)) |
| return false; |
| } |
| |
| return super.matchTreeElementAgainstCustomFilters(treeElement, flags); |
| } |
| |
| // Private |
| |
| _addTest(test) |
| { |
| let treeElement = new WI.AuditTreeElement(test); |
| |
| if (this._resultsFolderTreeElement) { |
| this.contentTreeOutline.insertChild(treeElement, this.contentTreeOutline.children.indexOf(this._resultsFolderTreeElement)); |
| this._resultsFolderTreeElement.hidden = !this._resultsFolderTreeElement.children.length || WI.auditManager.editing; |
| } else |
| this.contentTreeOutline.appendChild(treeElement); |
| } |
| |
| _addResult(result, index) |
| { |
| this.element.classList.add("has-results"); |
| |
| if (!this._resultsFolderTreeElement) { |
| this._resultsFolderTreeElement = new WI.FolderTreeElement(WI.UIString("Results")); |
| this.contentTreeOutline.appendChild(this._resultsFolderTreeElement); |
| } |
| |
| this._resultsFolderTreeElement.expand(); |
| |
| let resultFolderTreeElement = new WI.FolderTreeElement(WI.UIString("Run %d").format(index + 1)); |
| if (result instanceof WI.AuditTestResultBase) { |
| resultFolderTreeElement.subtitle = WI.UIString("Imported"); |
| result = [result]; |
| } |
| this._resultsFolderTreeElement.appendChild(resultFolderTreeElement); |
| |
| console.assert(this._resultsFolderTreeElement.children.length === WI.auditManager.results.length); |
| |
| for (let resultItem of result) |
| resultFolderTreeElement.appendChild(new WI.AuditTreeElement(resultItem)); |
| } |
| |
| _updateControlNavigationItems() |
| { |
| this._startStopButtonNavigationItem.toggled = WI.auditManager.runningState === WI.AuditManager.RunningState.Active || WI.auditManager.runningState === WI.AuditManager.RunningState.Stopping; |
| this._startStopButtonNavigationItem.enabled = WI.auditManager.tests.some((test) => !test.disabled && test.supported) && (WI.auditManager.runningState === WI.AuditManager.RunningState.Inactive || WI.auditManager.runningState === WI.AuditManager.RunningState.Active); |
| this._startStopButtonNavigationItem.hidden = WI.auditManager.editing; |
| |
| this._createButtonNavigationItem.hidden = !WI.auditManager.editing; |
| } |
| |
| _updateEditNavigationItems() |
| { |
| this._editButtonNavigationItem.label = WI.auditManager.editing ? this._editButtonNavigationItem.activatedToolTip : this._editButtonNavigationItem.defaultToolTip; |
| this._editButtonNavigationItem.activated = WI.auditManager.editing; |
| this._editButtonNavigationItem.enabled = WI.auditManager.editing || WI.auditManager.runningState === WI.AuditManager.RunningState.Inactive; |
| } |
| |
| _updateNoAuditsPlaceholder() |
| { |
| if (WI.auditManager.editing || WI.auditManager.tests.some((test) => !test.disabled && test.supported)) { |
| if (!this.hasActiveFilters) |
| this.hideEmptyContentPlaceholder(); |
| return; |
| } |
| |
| let contentPlaceholder = this.showEmptyContentPlaceholder(WI.UIString("No Enabled Audits")); |
| contentPlaceholder.classList.add("no-enabled-audits"); |
| |
| if (WI.auditManager.results.length) { |
| // Move the placeholder to be the first element in the content area, where it will |
| // be styled so that it doesn't obstruct the results elements. |
| this.contentView.element.insertBefore(contentPlaceholder, this.contentView.element.firstChild); |
| } |
| } |
| |
| _handleAuditManagerEditingChanged(event) |
| { |
| let previousSelectedTreeElement = this.contentTreeOutline.selectedTreeElement; |
| if (previousSelectedTreeElement) { |
| if (WI.auditManager.editing) { |
| if (!(previousSelectedTreeElement.representedObject instanceof WI.AuditTestBase)) |
| previousSelectedTreeElement.deselect(); |
| } else { |
| if (previousSelectedTreeElement.representedObject.disabled || !previousSelectedTreeElement.representedObject.supported) |
| previousSelectedTreeElement.deselect(); |
| } |
| } |
| |
| this.updateFilter(); |
| |
| if (!this.contentTreeOutline.selectedTreeElement) |
| this.showDefaultContentView(); |
| |
| this._updateControlNavigationItems(); |
| this._updateEditNavigationItems(); |
| this._updateNoAuditsPlaceholder(); |
| } |
| |
| _handleAuditManagerRunningStateChanged(event) |
| { |
| this._updateControlNavigationItems(); |
| this._updateEditNavigationItems(); |
| } |
| |
| _handleAuditTestAdded(event) |
| { |
| let {test} = event.data; |
| |
| this._addTest(test); |
| |
| this._updateControlNavigationItems(); |
| this._updateNoAuditsPlaceholder(); |
| } |
| |
| _handleAuditTestCompleted(event) |
| { |
| let {result, index} = event.data; |
| this._addResult(result, index); |
| |
| this._updateControlNavigationItems(); |
| this._updateEditNavigationItems(); |
| } |
| |
| _handleAuditTestRemoved(event) |
| { |
| console.assert(WI.auditManager.editing); |
| |
| let {test} = event.data; |
| |
| let treeElement = this.treeElementForRepresentedObject(test); |
| treeElement.parent.removeChild(treeElement); |
| |
| this._updateControlNavigationItems(); |
| } |
| |
| _handleAuditTestScheduled(event) |
| { |
| this._updateControlNavigationItems(); |
| this._updateEditNavigationItems(); |
| } |
| |
| _treeSelectionDidChange(event) |
| { |
| if (!this.selected) |
| return; |
| |
| let treeElement = this.contentTreeOutline.selectedTreeElement; |
| if (!treeElement || treeElement instanceof WI.FolderTreeElement) { |
| this.showDefaultContentView(); |
| return; |
| } |
| |
| let representedObject = treeElement.representedObject; |
| if (representedObject instanceof WI.AuditTestCase || representedObject instanceof WI.AuditTestGroup |
| || representedObject instanceof WI.AuditTestCaseResult || representedObject instanceof WI.AuditTestGroupResult) { |
| WI.showRepresentedObject(representedObject); |
| return; |
| } |
| |
| console.error("Unknown tree element", treeElement); |
| } |
| |
| _handleStartStopButtonNavigationItemClicked(event) |
| { |
| if (WI.auditManager.runningState === WI.AuditManager.RunningState.Inactive) |
| WI.auditManager.start(); |
| else if (WI.auditManager.runningState === WI.AuditManager.RunningState.Active) |
| WI.auditManager.stop(); |
| } |
| |
| _handleCreateButtonNavigationItemClicked(event) |
| { |
| console.assert(WI.auditManager.editing); |
| |
| let popover = new WI.CreateAuditPopover(this); |
| popover.show(event.target.element, [WI.RectEdge.MAX_Y, WI.RectEdge.MAX_X, WI.RectEdge.MIN_X]); |
| } |
| |
| _handleImportButtonNavigationItemClicked(event) |
| { |
| WI.FileUtilities.importJSON((result) => WI.auditManager.processJSON(result), {multiple: true}); |
| } |
| |
| _handleEditButtonNavigationItemClicked(event) |
| { |
| WI.auditManager.editing = !WI.auditManager.editing; |
| } |
| }; |