| /* |
| * Copyright (C) 2020 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.AnimationDetailsSidebarPanel = class AnimationDetailsSidebarPanel extends WI.DetailsSidebarPanel |
| { |
| constructor() |
| { |
| super("animation", WI.UIString("Animation")); |
| |
| this._animation = null; |
| |
| this._codeMirrorSectionMap = new Map; |
| } |
| |
| // Public |
| |
| inspect(objects) |
| { |
| if (!(objects instanceof Array)) |
| objects = [objects]; |
| |
| this.animation = objects.find((object) => object instanceof WI.Animation); |
| |
| return !!this.animation; |
| } |
| |
| get animation() |
| { |
| return this._animation; |
| } |
| |
| set animation(animation) |
| { |
| if (animation === this._animation) |
| return; |
| |
| if (this._animation) { |
| this._animation.removeEventListener(WI.Animation.Event.TargetChanged, this._handleAnimationTargetChanged, this); |
| this._animation.removeEventListener(WI.Animation.Event.EffectChanged, this._handleAnimationEffectChanged, this); |
| this._animation.removeEventListener(WI.Animation.Event.NameChanged, this._handleAnimationNameChanged, this); |
| } |
| |
| this._animation = animation || null; |
| |
| if (this._animation) { |
| this._animation.addEventListener(WI.Animation.Event.NameChanged, this._handleAnimationNameChanged, this); |
| this._animation.addEventListener(WI.Animation.Event.EffectChanged, this._handleAnimationEffectChanged, this); |
| this._animation.addEventListener(WI.Animation.Event.TargetChanged, this._handleAnimationTargetChanged, this); |
| } |
| |
| this.needsLayout(); |
| } |
| |
| // Protected |
| |
| initialLayout() |
| { |
| super.initialLayout(); |
| |
| this._idRow = new WI.DetailsSectionSimpleRow(WI.UIString("Identifier")); |
| this._typeRow = new WI.DetailsSectionSimpleRow(WI.UIString("Type")); |
| this._cssAnimationNameRow = new WI.DetailsSectionSimpleRow(WI.UIString("Name")); |
| this._cssTransitionPropertyRow = new WI.DetailsSectionSimpleRow(WI.UIString("Property")); |
| this._targetRow = new WI.DetailsSectionSimpleRow(WI.UIString("Target", "Web Animation Target Label", "Label for the current DOM node target of a web animation")); |
| |
| const identitySectionTitle = WI.UIString("Identity", "Web Animation Identity Title", "Section title for information about a web animation"); |
| let identitySection = new WI.DetailsSection("animation-identity", identitySectionTitle, [new WI.DetailsSectionGroup([this._idRow, this._typeRow, this._cssAnimationNameRow, this._cssTransitionPropertyRow, this._targetRow])]); |
| this.contentView.element.appendChild(identitySection.element); |
| |
| this._iterationCountRow = new WI.DetailsSectionSimpleRow(WI.UIString("Iterations", "Web Animation Iteration Count Label", "Label for the number of iterations of a web animation")); |
| this._iterationStartRow = new WI.DetailsSectionSimpleRow(WI.UIString("Start", "Web Animation Iteration Start Label", "Label for the number describing which iteration a web animation should start at")); |
| this._iterationDurationRow = new WI.DetailsSectionSimpleRow(WI.UIString("Duration", "Web Animation Iteration Duration Label", "Label for the time duration of each iteration of a web animation")); |
| let iterationsGroup = new WI.DetailsSectionGroup([this._iterationCountRow, this._iterationStartRow, this._iterationDurationRow]); |
| |
| this._startDelayRow = new WI.DetailsSectionSimpleRow(WI.UIString("Start Delay", "Web Animation Start Delay Label", "Label for the start delay time of a web animation ")); |
| this._endDelayRow = new WI.DetailsSectionSimpleRow(WI.UIString("End Delay", "Web Animation End Delay Label", "Label for the end delay time of a web animation ")); |
| this._timingFunctionRow = new WI.DetailsSectionSimpleRow(WI.UIString("Easing", "Web Animation Easing Label", "Label for the cubic-bezier timing function of a web animation")); |
| let timingGroup = new WI.DetailsSectionGroup([this._startDelayRow, this._endDelayRow, this._timingFunctionRow]); |
| |
| this._playbackDirectionRow = new WI.DetailsSectionSimpleRow(WI.UIString("Direction", "Web Animation Playback Direction Label", "Label for the playback direction of a web animation")); |
| this._fillModeRow = new WI.DetailsSectionSimpleRow(WI.UIString("Fill", "Web Animation Fill Mode Label", "Label for the fill mode of a web animation")); |
| let fillDirectionGroup = new WI.DetailsSectionGroup([this._playbackDirectionRow, this._fillModeRow]); |
| |
| const effectSectionTitle = WI.UIString("Effect", "Web Animation Effect Title", "Section title for information about the effect of a web animation"); |
| let effectSection = new WI.DetailsSection("animation-effect", effectSectionTitle, [iterationsGroup, timingGroup, fillDirectionGroup]); |
| this.contentView.element.appendChild(effectSection.element); |
| |
| this._keyframesGroup = new WI.DetailsSectionGroup; |
| |
| const keyframesSectionTitle = WI.UIString("Keyframes", "Web Animation Keyframes Title", "Section title for information about the keyframes of a web animation"); |
| let keyframesSection = new WI.DetailsSection("animation-keyframes", keyframesSectionTitle, [this._keyframesGroup]); |
| this.contentView.element.appendChild(keyframesSection.element); |
| |
| const selectable = false; |
| let backtraceTreeOutline = new WI.TreeOutline(selectable); |
| backtraceTreeOutline.disclosureButtons = false; |
| this._backtraceTreeController = new WI.CallFrameTreeController(backtraceTreeOutline); |
| |
| let backtraceRow = new WI.DetailsSectionRow; |
| backtraceRow.element.appendChild(backtraceTreeOutline.element); |
| |
| const backtraceSectionTitle = WI.UIString("Backtrace", "Web Animation Backtrace Title", "Section title for the JavaScript backtrace of the creation of a web animation"); |
| this._backtraceSection = new WI.DetailsSection("animation-backtrace", backtraceSectionTitle, [new WI.DetailsSectionGroup([backtraceRow])]); |
| this._backtraceSection.element.hidden = true; |
| this.contentView.element.appendChild(this._backtraceSection.element); |
| } |
| |
| layout() |
| { |
| super.layout(); |
| |
| if (!this._animation) |
| return; |
| |
| this._refreshIdentitySection(); |
| this._refreshEffectSection(); |
| this._refreshBacktraceSection(); |
| |
| for (let codeMirror of this._codeMirrorSectionMap.values()) |
| codeMirror.refresh(); |
| } |
| |
| shown() |
| { |
| super.shown(); |
| |
| for (let codeMirror of this._codeMirrorSectionMap.values()) |
| codeMirror.refresh(); |
| } |
| |
| // Private |
| |
| _refreshIdentitySection() |
| { |
| let animationType = this._animation.animationType; |
| let displayName = this._animation.displayName; |
| let cssAnimationName = this._animation.cssAnimationName; |
| let cssTransitionProperty = this._animation.cssTransitionProperty; |
| |
| switch (animationType) { |
| case WI.Animation.Type.WebAnimation: |
| this._idRow.value = this._animation.name; |
| break; |
| |
| case WI.Animation.Type.CSSAnimation: |
| this._idRow.value = cssAnimationName !== displayName ? displayName : null; |
| break; |
| |
| case WI.Animation.Type.CSSTransition: |
| this._idRow.value = cssTransitionProperty !== displayName ? displayName : null; |
| break; |
| } |
| |
| this._typeRow.value = WI.Animation.displayNameForAnimationType(animationType); |
| |
| this._cssAnimationNameRow.value = cssAnimationName; |
| this._cssTransitionPropertyRow.value = cssTransitionProperty; |
| |
| this._targetRow.value = null; |
| this._animation.requestEffectTarget((domNode) => { |
| this._targetRow.value = domNode ? WI.linkifyNodeReference(domNode) : null; |
| }); |
| } |
| |
| _refreshEffectSection() |
| { |
| for (let section of this._codeMirrorSectionMap.keys()) |
| section.removeEventListener(WI.DetailsSection.Event.CollapsedStateChanged, this._handleDetailsSectionCollapsedStateChanged, this); |
| this._codeMirrorSectionMap.clear(); |
| |
| const precision = 0; |
| const readOnly = true; |
| |
| this._iterationCountRow.value = !isNaN(this._animation.iterationCount) ? this._animation.iterationCount.toLocaleString() : null; |
| this._iterationStartRow.value = !isNaN(this._animation.iterationStart) ? this._animation.iterationStart.toLocaleString() : null; |
| this._iterationDurationRow.value = !isNaN(this._animation.iterationDuration) ? Number.secondsToString(this._animation.iterationDuration / 1000) : null; |
| |
| this._startDelayRow.value = this._animation.startDelay ? Number.secondsToString(this._animation.startDelay / 1000) : null; |
| this._endDelayRow.value = this._animation.endDelay ? Number.secondsToString(this._animation.endDelay / 1000) : null; |
| this._timingFunctionRow.value = this._animation.timingFunction ? this._animation.timingFunction.toString() : null; |
| |
| this._playbackDirectionRow.value = this._animation.playbackDirection ? WI.Animation.displayNameForPlaybackDirection(this._animation.playbackDirection) : null; |
| this._fillModeRow.value = this._animation.fillMode ? WI.Animation.displayNameForFillMode(this._animation.fillMode) : null; |
| |
| let keyframeSections = []; |
| for (let keyframe of this._animation.keyframes) { |
| let rows = []; |
| |
| let keyframeSection = new WI.DetailsSection("animation-keyframe-offset-" + keyframe.offset, Number.percentageString(keyframe.offset, precision)); |
| keyframeSection.addEventListener(WI.DetailsSection.Event.CollapsedStateChanged, this._handleDetailsSectionCollapsedStateChanged, this); |
| keyframeSections.push(keyframeSection); |
| |
| if (keyframe.easing) { |
| let subtitle = keyframeSection.headerElement.appendChild(document.createElement("span")); |
| subtitle.className = "subtitle"; |
| subtitle.textContent = ` ${emDash} ${keyframe.easing.toString()}`; |
| } |
| |
| if (keyframe.style) { |
| let codeMirrorElement = document.createElement("div"); |
| let codeMirror = WI.CodeMirrorEditor.create(codeMirrorElement, { |
| mode: "css", |
| readOnly: "nocursor", |
| lineWrapping: true, |
| }); |
| codeMirror.setValue(keyframe.style); |
| |
| const range = null; |
| function optionsForType(type) { |
| return { |
| allowedTokens: /\btag\b/, |
| callback(marker, valueObject, valueString) { |
| let swatch = new WI.InlineSwatch(type, valueObject, readOnly); |
| codeMirror.setUniqueBookmark(marker.range.startPosition().toCodeMirror(), swatch.element); |
| } |
| }; |
| } |
| createCodeMirrorColorTextMarkers(codeMirror, range, optionsForType(WI.InlineSwatch.Type.Color)); |
| createCodeMirrorGradientTextMarkers(codeMirror, range, optionsForType(WI.InlineSwatch.Type.Gradient)); |
| createCodeMirrorCubicBezierTextMarkers(codeMirror, range, optionsForType(WI.InlineSwatch.Type.Bezier)); |
| createCodeMirrorSpringTextMarkers(codeMirror, range, optionsForType(WI.InlineSwatch.Type.Spring)); |
| |
| let row = new WI.DetailsSectionRow; |
| row.element.classList.add("styles"); |
| row.element.appendChild(codeMirrorElement); |
| rows.push(row); |
| |
| this._codeMirrorSectionMap.set(keyframeSection, codeMirror); |
| } |
| |
| if (!rows.length) { |
| let emptyRow = new WI.DetailsSectionRow(WI.UIString("No Styles")); |
| emptyRow.showEmptyMessage(); |
| rows.push(emptyRow); |
| } |
| |
| keyframeSection.groups = [new WI.DetailsSectionGroup(rows)]; |
| } |
| if (!keyframeSections.length) { |
| let emptyRow = new WI.DetailsSectionRow(WI.UIString("No Keyframes")); |
| emptyRow.showEmptyMessage(); |
| keyframeSections.push(emptyRow); |
| } |
| this._keyframesGroup.rows = keyframeSections; |
| } |
| |
| _refreshBacktraceSection() |
| { |
| let callFrames = this._animation.backtrace; |
| this._backtraceTreeController.callFrames = callFrames; |
| this._backtraceSection.element.hidden = !callFrames.length; |
| } |
| |
| _handleAnimationNameChanged(event) |
| { |
| this._refreshIdentitySection(); |
| } |
| |
| _handleAnimationEffectChanged(event) |
| { |
| this._refreshEffectSection(); |
| } |
| |
| _handleAnimationTargetChanged(event) |
| { |
| this._refreshIdentitySection(); |
| } |
| |
| _handleDetailsSectionCollapsedStateChanged(event) |
| { |
| let codeMirror = this._codeMirrorSectionMap.get(event.target); |
| codeMirror.refresh(); |
| } |
| }; |