|  | /* | 
|  | * Licensed to the Apache Software Foundation (ASF) under one | 
|  | * or more contributor license agreements.  See the NOTICE file | 
|  | * distributed with this work for additional information | 
|  | * regarding copyright ownership.  The ASF licenses this file | 
|  | * to you under the Apache License, Version 2.0 (the | 
|  | * "License"); you may not use this file except in compliance | 
|  | * with the License.  You may obtain a copy of the License at | 
|  | * | 
|  | *   http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, | 
|  | * software distributed under the License is distributed on an | 
|  | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
|  | * KIND, either express or implied.  See the License for the | 
|  | * specific language governing permissions and limitations | 
|  | * under the License. | 
|  | */ | 
|  |  | 
|  | import seedrandom from 'seedrandom'; | 
|  | import { ActionPlayback } from './ActionPlayback'; | 
|  | import * as timeline from './timeline'; | 
|  |  | 
|  | let myRandom = new seedrandom('echarts-random'); | 
|  | // Random for echarts code. | 
|  | // In case different echarts version called random different times. | 
|  | // It will cause following random number all wrong. | 
|  | let myRandom2 = new seedrandom('echarts-random-inner'); | 
|  | // Fixed random generator | 
|  | Math.random = function () { | 
|  | const val = myRandom(); | 
|  | return val; | 
|  | }; | 
|  |  | 
|  | window.__random__inner__ = function () { | 
|  | const val = myRandom2(); | 
|  | return val; | 
|  | }; | 
|  |  | 
|  | let vstStarted = false; | 
|  |  | 
|  | window.__VRT_START__ = function () { | 
|  | if (vstStarted) { | 
|  | return; | 
|  | } | 
|  | vstStarted = true; | 
|  | timeline.start(); | 
|  |  | 
|  | // TODO not support reload without simpleRequire | 
|  | if (window.__VRT_RUN_CONTEXT__) { | 
|  | // Restore from previous run | 
|  | setTimeout(async () => { | 
|  | await __VRT_RUN_ACTIONS__( | 
|  | window.__VRT_RUN_CONTEXT__.actions, | 
|  | window.__VRT_RUN_CONTEXT__.currentActionIndex, | 
|  | window.__VRT_RUN_CONTEXT__.currentActionContext | 
|  | ) | 
|  | }, 1000); | 
|  | } | 
|  | else { | 
|  | // Screenshot after 1000ms (200ms if 5x speed), wait the animation to be finished | 
|  | setTimeout(async () => { | 
|  | // Pause timeline until run actions. | 
|  | timeline.pause(); | 
|  | await __VRT_FULL_SCREENSHOT__(); | 
|  | }, 1000); | 
|  | } | 
|  | } | 
|  |  | 
|  | function saveRunningContext(actions, actionIndex, playback) { | 
|  | localStorage.setItem('vstRunContext', JSON.stringify({ | 
|  | actions: actions, | 
|  | currentActionIndex: actionIndex, | 
|  | currentActionContext: playback.getContext() | 
|  | })); | 
|  | } | 
|  |  | 
|  | window.__VRT_RUN_ACTIONS__ = async function (actions, restoredActionIndex, restoredActionContext) { | 
|  | // Actions can only bu runned once. | 
|  | timeline.resume(); | 
|  |  | 
|  | const actionPlayback = new ActionPlayback(); | 
|  |  | 
|  | const nativeLocation = window.location; | 
|  | let currentActionIndex = 0; | 
|  |  | 
|  | // Some test cases change the params through reload(). | 
|  | // We need to save the running info and keep running after reload. | 
|  | // window.location seems can't be redefined anymore. So we can only provide helper functions. | 
|  | window.__VRT_RELOAD__ = function () { | 
|  | // Mark reload triggered and let ActionPlayback stop. | 
|  | window.__VRT_RELOAD_TRIGGERED__ = true; | 
|  | saveRunningContext(actions, currentActionIndex, actionPlayback); | 
|  | timeline.nativeSetTimeout(() => { | 
|  | // CDPSession pay be disconnected if reload immediately. | 
|  | nativeLocation.reload(); | 
|  | }, 100); | 
|  | } | 
|  |  | 
|  | for (const [index, action] of actions.entries()) { | 
|  | currentActionIndex = index; | 
|  | if (index < restoredActionIndex) { | 
|  | continue; | 
|  | } | 
|  | window.scrollTo(action.scrollX, action.scrollY); | 
|  | await actionPlayback.runAction(action, index === restoredActionIndex ? restoredActionContext : null); | 
|  |  | 
|  | } | 
|  | actionPlayback.stop(); | 
|  |  | 
|  | __VRT_FINISH_ACTIONS__(); | 
|  | } | 
|  |  | 
|  |  | 
|  | window.addEventListener('DOMContentLoaded', () => { | 
|  | let style = document.createElement('style'); | 
|  | // Disable all css animation since it will cause screenshot inconsistent. | 
|  | // PENDING Only tooltip? div[_echarts_instance_]>div | 
|  | style.innerHTML = ` | 
|  | * { | 
|  | transition-delay: 0s !important; | 
|  | transition-duration: 0s !important; | 
|  | animation-delay: -0.0001s !important; | 
|  | animation-duration: 0s !important; | 
|  | } | 
|  | `; | 
|  | document.head.appendChild(style); | 
|  |  | 
|  |  | 
|  | // Prevent triggered mouseout event when mouse move out of window. | 
|  | // DON"T know why, but it happens occasionally and cause hover state/ tooltip wrong before screenshot. | 
|  |  | 
|  |  | 
|  | document.body.addEventListener('mouseout', e => { | 
|  | if (!e.relatedTarget) { | 
|  | e.stopPropagation(); | 
|  | e.stopImmediatePropagation(); | 
|  | } | 
|  | }, true); | 
|  |  | 
|  | // Draw mouse | 
|  | const box = document.createElement('puppeteer-mouse-pointer'); | 
|  | const styleElement = document.createElement('style'); | 
|  | styleElement.innerHTML = ` | 
|  | puppeteer-mouse-pointer { | 
|  | pointer-events: none; | 
|  | position: absolute; | 
|  | top: 0; | 
|  | z-index: 10000; | 
|  | left: 0; | 
|  | width: 20px; | 
|  | height: 20px; | 
|  | background: rgba(0,0,0,.4); | 
|  | border: 1px solid white; | 
|  | border-radius: 10px; | 
|  | margin: -10px 0 0 -10px; | 
|  | padding: 0; | 
|  | transition: background .2s, border-radius .2s, border-color .2s; | 
|  | } | 
|  | puppeteer-mouse-pointer.button-1 { | 
|  | transition: none; | 
|  | background: rgba(0,0,0,0.9); | 
|  | } | 
|  | puppeteer-mouse-pointer.button-2 { | 
|  | transition: none; | 
|  | border-color: rgba(0,0,255,0.9); | 
|  | } | 
|  | puppeteer-mouse-pointer.button-3 { | 
|  | transition: none; | 
|  | border-radius: 4px; | 
|  | } | 
|  | puppeteer-mouse-pointer.button-4 { | 
|  | transition: none; | 
|  | border-color: rgba(255,0,0,0.9); | 
|  | } | 
|  | puppeteer-mouse-pointer.button-5 { | 
|  | transition: none; | 
|  | border-color: rgba(0,255,0,0.9); | 
|  | } | 
|  | `; | 
|  | document.head.appendChild(styleElement); | 
|  | document.body.appendChild(box); | 
|  | document.addEventListener('mousemove', event => { | 
|  | box.style.left = event.pageX + 'px'; | 
|  | box.style.top = event.pageY + 'px'; | 
|  | updateButtons(event.buttons); | 
|  | }, true); | 
|  | document.addEventListener('mousedown', event => { | 
|  | updateButtons(event.buttons); | 
|  | box.classList.add('button-' + event.which); | 
|  | }, true); | 
|  | document.addEventListener('mouseup', event => { | 
|  | updateButtons(event.buttons); | 
|  | box.classList.remove('button-' + event.which); | 
|  | }, true); | 
|  | function updateButtons(buttons) { | 
|  | for (let i = 0; i < 5; i++) { | 
|  | box.classList.toggle('button-' + i, buttons & (1 << i)); | 
|  | } | 
|  | } | 
|  | }); |