| /* | 
 | * 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. | 
 | */ | 
 |  | 
 | if (typeof __VRT_PLAYBACK_SPEED__ === 'undefined') { | 
 |     window.__VRT_PLAYBACK_SPEED__ = 1; | 
 | } | 
 | const nativeRaf = window.requestAnimationFrame; | 
 | const nativeSetTimeout = window.setTimeout; | 
 | const nativeSetInterval = window.setInterval; | 
 | const FIXED_FRAME_TIME = 16; | 
 | const MAX_FRAME_TIME = 80; | 
 | const TIMELINE_START = 1566458693300; | 
 |  | 
 | window.__VRT_TIMELINE_PAUSED__ = true; | 
 |  | 
 | let realFrameStartTime = 0; | 
 |  | 
 | /** Control timeline loop */ | 
 | let rafCbs = []; | 
 | let frameIdx = 0; | 
 | let timelineTime = 0; | 
 |  | 
 | function runFrame() { | 
 |     realFrameStartTime = NativeDate.now(); | 
 |     frameIdx++; | 
 |     timelineTime += FIXED_FRAME_TIME; | 
 |     const currentRafCbs = rafCbs; | 
 |     // Clear before calling the callbacks. raf may be registered in the callback | 
 |     rafCbs = []; | 
 |     currentRafCbs.forEach((cb) => { | 
 |         try { | 
 |             cb(); | 
 |         } | 
 |         catch (e) { | 
 |             // Catch error to avoid following tasks. | 
 |             __VRT_LOG_ERRORS__(e.toString()); | 
 |         } | 
 |     }); | 
 |     flushTimeoutHandlers(); | 
 |     flushIntervalHandlers(); | 
 | } | 
 | function timelineLoop() { | 
 |     if (!__VRT_TIMELINE_PAUSED__) { | 
 |         runFrame(); | 
 |     } | 
 |     nativeRaf(timelineLoop); | 
 | } | 
 | nativeRaf(timelineLoop); | 
 |  | 
 | window.requestAnimationFrame = function (cb) { | 
 |     rafCbs.push(cb); | 
 | }; | 
 |  | 
 | /** Mock setTimeout, setInterval */ | 
 | let timeoutHandlers = []; | 
 | let intervalHandlers = []; | 
 |  | 
 | let timeoutId = 1; | 
 | let intervalId = 1; | 
 |  | 
 | window.setTimeout = function (cb, timeout) { | 
 |     const elapsedFrame = Math.ceil(Math.max(timeout || 0, 1) / FIXED_FRAME_TIME); | 
 |     timeoutHandlers.push({ | 
 |         callback: cb, | 
 |         id: timeoutId, | 
 |         frame: frameIdx + elapsedFrame | 
 |     }); | 
 |  | 
 |     return timeoutId++; | 
 | } | 
 |  | 
 | window.clearTimeout = function (id) { | 
 |     const idx = timeoutHandlers.findIndex(handler => { | 
 |         return handler.id === id | 
 |     }); | 
 |     if (idx >= 0) { | 
 |         timeoutHandlers.splice(idx, 1); | 
 |     } | 
 | } | 
 |  | 
 | function flushTimeoutHandlers() { | 
 |     // Copy the array. In case setTimeout/clearTimeout is invoked in the callback. | 
 |     const savedTimeoutHandlers = timeoutHandlers.slice(); | 
 |     for (let i = 0; i < savedTimeoutHandlers.length; i++) { | 
 |         const handler = savedTimeoutHandlers[i]; | 
 |         if (handler.frame === frameIdx) { | 
 |             // Need find index again. In case setTimeout/clearTimeout is invoked in the callback. | 
 |             const idx = timeoutHandlers.indexOf(handler); | 
 |             timeoutHandlers.splice(idx, 1); | 
 |             try { | 
 |                 handler.callback(); | 
 |             } | 
 |             catch (e) { | 
 |                 // Catch error to avoid following tasks. | 
 |                 __VRT_LOG_ERRORS__(e.toString()); | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | window.setInterval = function (cb, interval) { | 
 |     const intervalFrame = Math.ceil(Math.max(interval || 0, 1) / FIXED_FRAME_TIME); | 
 |     intervalHandlers.push({ | 
 |         callback: cb, | 
 |         id: intervalId, | 
 |         intervalFrame, | 
 |         frame: frameIdx + intervalFrame | 
 |     }); | 
 |  | 
 |     return intervalId++; | 
 | } | 
 |  | 
 | window.clearInterval = function (id) { | 
 |     const idx = intervalHandlers.findIndex(handler => { | 
 |         return handler.id === id; | 
 |     }); | 
 |     if (idx >= 0) { | 
 |         intervalHandlers.splice(idx, 1); | 
 |     } | 
 | } | 
 |  | 
 | function flushIntervalHandlers() { | 
 |     // Copy the array. In case setInterval/clearInterval is invoked in the callback. | 
 |     const savedIntervalHandlers = intervalHandlers.slice(); | 
 |     for (let i = 0; i < savedIntervalHandlers.length; i++) { | 
 |         const handler = savedIntervalHandlers[i]; | 
 |         if (handler.frame === frameIdx) { | 
 |             try { | 
 |                 handler.callback(); | 
 |             } | 
 |             catch (e) { | 
 |                 // Catch error to avoid following tasks. | 
 |                 __VRT_LOG_ERRORS__(e.toString()); | 
 |             } | 
 |             handler.frame += handler.intervalFrame; | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | /** Mock Date */ | 
 |  | 
 | const NativeDate = window.Date; | 
 |  | 
 | const mockNow = function () { | 
 |     // // Use same time in one frame. | 
 |     // var realFrameTime = NativeDate.now(); | 
 |     // // Split frame. Add 8ms offset on the second half | 
 |     // // Avoid infinite loop when some logic determine whether to break the loop based on the execution time. | 
 |     // // For example https://github.com/apache/echarts/blob/737e23c0054e6b501ecc6f562920cffae953b5c6/src/core/echarts.ts#L537 | 
 |     // var frameDeltaTime = realFrameTime - realFrameStartTime; | 
 |     var frameDeltaTime = 0; | 
 |     // Use same time in one frame. | 
 |     return TIMELINE_START + (timelineTime + frameDeltaTime) * window.__VRT_PLAYBACK_SPEED__; | 
 | }; | 
 | function MockDate(...args) { | 
 |     if (!args.length) { | 
 |         return new NativeDate(mockNow()); | 
 |     } | 
 |     else { | 
 |         return new NativeDate(...args); | 
 |     } | 
 | } | 
 | MockDate.prototype = Object.create(NativeDate.prototype); | 
 | Object.setPrototypeOf(MockDate, NativeDate); | 
 | MockDate.now = mockNow; | 
 | window.Date = MockDate; | 
 |  | 
 | // TODO Do we need to mock performance? Or leave some API that can keep real. | 
 |  | 
 |  | 
 | export function start() { | 
 |     window.__VRT_TIMELINE_PAUSED__ = false; | 
 | } | 
 |  | 
 | export function pause() { | 
 |     window.__VRT_TIMELINE_PAUSED__ = true; | 
 | } | 
 |  | 
 | export function resume() { | 
 |     window.__VRT_TIMELINE_PAUSED__ = false; | 
 | } | 
 |  | 
 | export { nativeRaf, nativeSetInterval, nativeSetTimeout }; |