From 02954b53fc0b234d94a5d49ca6c11a8b0f1a1da7 Mon Sep 17 00:00:00 2001 From: Robert Flack Date: Tue, 11 Oct 2022 17:40:36 +0000 Subject: [PATCH 1/3] Replace proxied animations in getAnimations and reuse proxied CSS animations This makes document.getAnimations and Element.getAnimations correctly return proxied instances of the animation so as to behave as expected. Also by getting the proxied animations we can intelligently reuse the existing proxied CSS animation when only timing details have changed which implicitly fixes the performance issue of #84. --- dist/scroll-timeline.js | 2 +- dist/scroll-timeline.js.map | 2 +- src/index.js | 12 ++++++++++++ src/proxy-animation.js | 34 +++++++++++++++++++++++++++++++--- src/scroll-timeline-css.js | 32 ++++++++------------------------ 5 files changed, 53 insertions(+), 29 deletions(-) diff --git a/dist/scroll-timeline.js b/dist/scroll-timeline.js index 26c17b0a..a644100d 100644 --- a/dist/scroll-timeline.js +++ b/dist/scroll-timeline.js @@ -1,2 +1,2 @@ -!function(){function e(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,i=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[i++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}!function(){var e,n=new WeakMap;function r(e){for(var t,n=[],i=0;i0?CSS.percent(100*n/i):CSS.percent(100)}},{key:"__polyfill",get:function(){return!0}}]),e}();function g(e,t){for(var n=e.parentElement;null!=n;){if(t(n))return n;n=n.parentElement}}function y(e){switch(getComputedStyle(e).display){case"block":case"inline-block":case"list-item":case"table":case"table-caption":case"flow-root":case"flex":case"grid":return!0}return!1}function T(e){var t=getComputedStyle(e);return"none"!=t.transform||"none"!=t.perspective||"transform"==t.willChange||"perspective"==t.willChange||"none"!=t.filter||"filter"==t.willChange||"none"!=t.backdropFilter}function S(e){return"static"!=getComputedStyle(e).position||T(e)}function b(e){switch(getComputedStyle(e).position){case"static":case"relative":case"sticky":return g(e,y);case"absolute":return g(e,S);case"fixed":return g(e,T)}}function k(e){if(e){for(;e=b(e);)switch(getComputedStyle(e)["overflow-x"]){case"auto":case"scroll":case"hidden":return e==document.body&&"visible"==getComputedStyle(document.scrollingElement).overflow?document.scrollingElement:e}return document.scrollingElement}}function w(e,t){var n=s.get(e);return"inactive"===e.phase?null:e instanceof M?E(t,e.source,e.subject,n.orientation,n.inset):null}function E(e,t,n,i,r){for(var a=0,o=0,l=n,s=t.offsetParent;l&&l!=s;)o+=l.offsetLeft,a+=l.offsetTop,l=l.offsetParent;o-=t.offsetLeft+t.clientLeft,a-=t.offsetTop+t.clientTop;var u=getComputedStyle(t),c="horizontal-tb"==u.writingMode,m=void 0,f=void 0,h=void 0;"horizontal"==i||"inline"==i&&c||"block"==i&&!c?(m=n.clientWidth,f=o,("rtl"==u.direction||"vertical-rl"==u.writingMode)&&(f+=t.scrollWidth-t.clientWidth),h=t.clientWidth):(m=n.clientHeight,f=a,h=t.clientHeight);var d=function(e,t){var n={start:0,end:0};if(!e)return n;var i=e.split(" "),r=[];if(i.forEach(function(e){e.endsWith("%")?r.push(t/100*parseFloat(e)):e.endsWith("px")?r.push(parseFloat(e)):"auto"===e&&r.push(0)}),r.length>2)throw TypeError("Invalid inset");return 1==r.length?(n.start=r[0],n.end=r[0]):2==r.length&&(n.start=r[0],n.end=r[1]),n}(r,h),p=f-h+d.end,v=f+m-d.start,g=p+m,y=v-m,T=Math.min(g,y),S=Math.max(g,y),b=void 0,k=void 0;switch(e){case"cover":b=p,k=v;break;case"contain":b=T,k=S;break;case"enter":b=p,k=T;break;case"exit":b=S,k=v}return{start:b,end:k}}function x(e,t,n){return I(w(e,t),n,w(e,"cover"))}function I(e,t,n){return e&&n?(t.value/100*(e.end-e.start)+e.start-n.start)/(n.end-n.start):0}var M=function(e){function n(t){var n;return t.axis&&(t.orientation=t.axis),n=e.call(this,t)||this,s.get(a(n)).subject=t&&t.subject?t.subject:void 0,f(a(n)),c(a(n)),n}return i(n,e),t(n,[{key:"source",get:function(){return f(this),s.get(this).source},set:function(e){throw new Error("Cannot set the source of a view timeline")}},{key:"subject",get:function(){return s.get(this).subject}},{key:"axis",get:function(){return s.get(this).orientation}},{key:"currentTime",get:function(){var e=null,t=m(this.source,this.orientation);if(t==e)return e;var n=w(this,"cover");return n?CSS.percent((t-n.start)/(n.end-n.start)*100):e}}]),n}(v),P=window.Element.prototype.animate,R=window.Animation,C=["enter","exit","cover","contain"],N=function(){function e(){var e=this;this.state="pending",this.nativeResolve=this.nativeReject=null,this.promise=new Promise(function(t,n){e.nativeResolve=t,e.nativeReject=n})}var t=e.prototype;return t.resolve=function(e){this.state="resolved",this.nativeResolve(e)},t.reject=function(e){this.state="rejected",this.promise.catch(function(){}),this.nativeReject(e)},e}();function A(e){e.readyPromise=new N,requestAnimationFrame(function(){null!==e.timeline.currentTime&&B(e)})}function O(){return new DOMException("The user aborted a request","AbortError")}function j(e,t){if(null===t)return t;if("number"!=typeof t)throw new DOMException("Unexpected value: "+t+". Cannot convert to CssNumberish","InvalidStateError");var n=H(e);return CSS.percent(n?100*t/n:0)}function W(e,t){if(e.timeline){if(null===t)return t;if("percent"===t.unit){var n=H(e);return t.value*n/100}throw new DOMException("CSSNumericValue must be a percentage for progress based animations.","NotSupportedError")}if(null==t||"number"==typeof t)return t;var i=t.to("ms");if(convertTime)return i.value;throw new DOMException("CSSNumericValue must be either a number or a time value for time based animations.","InvalidStateError")}function L(e){if(e.finishedPromise&&"pending"==e.finishedPromise.state&&"finished"==e.proxy.playState){e.finishedPromise.resolve(e.proxy),e.animation.pause();var t=new CustomEvent("finish",{detail:{currentTime:e.proxy.currentTime,timelineTime:e.proxy.timeline.currentTime}});Object.defineProperty(t,"currentTime",{get:function(){return this.detail.currentTime}}),Object.defineProperty(t,"timelineTime",{get:function(){return this.detail.timelineTime}}),requestAnimationFrame(function(){queueMicrotask(function(){e.animation.dispatchEvent(t)})})}}function _(e){return null!==e.pendingPlaybackRate?e.pendingPlaybackRate:e.animation.playbackRate}function D(e){null!==e.pendingPlaybackRate&&(e.animation.playbackRate=e.pendingPlaybackRate,e.pendingPlaybackRate=null)}function V(e){if(!e.timeline)return null;var t=W(e,e.timeline.currentTime);if(null===t)return null;if(null===e.startTime)return null;var n=(t-e.startTime)*e.animation.playbackRate;return-0==n&&(n=0),n}function z(e,t){if(!e.timeline)return null;var n=W(e,e.timeline.currentTime);return null==n?null:n-t/e.animation.playbackRate}function U(e,t,n){if(e.timeline){var i=t?W(e,e.proxy.currentTime):V(e);if(i&&null!=e.startTime&&!e.proxy.pending){var r=_(e),a=H(e),o=e.previousCurrentTime;r>0&&i>=a?((null===o||o0)&&(o=0),e.holdTime=t?i:o):0!=r&&(t&&null!==e.holdTime&&(e.startTime=z(e,e.holdTime)),e.holdTime=null)}F(e),e.previousCurrentTime=W(e,e.proxy.currentTime),"finished"==e.proxy.playState?(e.finishedPromise||(e.finishedPromise=new N),"pending"==e.finishedPromise.state&&(n?L(e):Promise.resolve().then(function(){L(e)}))):(e.finishedPromise&&"resolved"==e.finishedPromise.state&&(e.finishedPromise=new N),"paused"!=e.animation.playState&&e.animation.pause())}}function H(e){var t=function(e){var t=e.proxy.effect.getTiming();return e.normalizedTiming||t}(e);return Math.max(0,t.delay+t.endDelay+t.iterations*t.duration)}function F(e){if(e.timeline)if(null!==e.startTime){var t=e.timeline.currentTime;if(null==t)return;K(e,(W(e,t)-e.startTime)*e.animation.playbackRate)}else null!==e.holdTime&&K(e,e.holdTime)}function K(e,t){var n=e.timeline,i=e.animation.playbackRate;e.animation.currentTime=t+(n.currentTime&&n.currentTime.value==(i<0?0:100)?i<0?.001:-.001:0)}function q(e,t){if(e.timeline){var n="paused"==e.proxy.playState&&e.proxy.pending,i=!1,r=null,a=W(e,e.proxy.currentTime);e.resetCurrentTimeOnResume&&(a=null,e.resetCurrentTimeOnResume=!1);var o=_(e),l=H(e);if(o>0&&t&&(null==a||a<0||a>=l))r=0;else if(o<0&&t&&(null==a||a<=0||a>l)){if(Infinity==l)return void e.animation.play();r=l}else 0==o&&null==a&&(r=0);null!=r&&(e.startTime=r,e.holdTime=null,D(e)),p(e.timeline,e.animation,Q.bind(e.proxy)),e.holdTime&&(e.startTime=null),e.pendingTask&&(e.pendingTask=null,i=!0),(null!==e.holdTime||null!==r||n||null!==e.pendingPlaybackRate)&&(e.readyPromise&&!i&&(e.readyPromise=null),F(e),e.readyPromise||A(e),e.pendingTask="play",U(e,!1,!1))}}function Q(e){var t=G.get(this);if(null!=e){t.pendingTask&&B(t);var n=this.playState;"running"!=n&&"finished"!=n||(K(t,(W(t,e)-W(t,this.startTime))*this.playbackRate),"finished"==n&&0!=_(t)&&(t.holdTime=null),U(t,!1,!1))}else"idle"!=t.animation.playState&&t.animation.cancel()}function B(e){"pause"==e.pendingTask?function(e){var t=W(e,e.timeline.currentTime);null!=e.startTime&&null==e.holdTime&&(e.holdTime=(t-e.startTime)*e.animation.playbackRate),D(e),e.startTime=null,e.readyPromise.resolve(e.proxy),U(e,!1,!1),F(e),e.pendingTask=null}(e):"play"==e.pendingTask&&function(e){var t=W(e,e.timeline.currentTime);if(null!=e.holdTime)D(e),0==e.animation.playbackRate?e.startTime=t:(e.startTime=t-e.holdTime/e.animation.playbackRate,e.holdTime=null);else if(null!==e.startTime&&null!==e.pendingPlaybackRate){var n=(t-e.startTime)*e.animation.playbackRate;D(e);var i=e.animation.playbackRate;0==i?(e.holdTime=null,e.startTime=t):e.startTime=t-n/i}e.readyPromise&&"pending"==e.readyPromise.state&&e.readyPromise.resolve(e.proxy),U(e,!1,!1),F(e),e.pendingTask=null}(e)}var G=new WeakMap,Y=function(){function e(e,t,n){void 0===n&&(n={});var i=e instanceof R?e:new R(e,a),r=t instanceof v,a=r?void 0:t;G.set(this,{animation:i,timeline:r?t:void 0,playState:r?"idle":null,readyPromise:null,finishedPromise:null,startTime:null,holdTime:null,previousCurrentTime:null,resetCurrentTimeOnResume:!1,pendingPlaybackRate:null,pendingTask:null,specifiedTiming:null,normalizedTiming:null,effect:null,timeRange:t instanceof ViewTimeline?Z(n):null,proxy:this})}var n=e.prototype;return n.finish=function(){var e=G.get(this);if(e.timeline){var t=_(e),n=H(e);if(0==t)throw new DOMException("Cannot finish Animation with a playbackRate of 0.","InvalidStateError");if(t>0&&Infinity==n)throw new DOMException("Cannot finish Animation with an infinite target effect end.","InvalidStateError");D(e);var i=t<0?0:n;this.currentTime=j(e,i);var r=W(e,e.timeline.currentTime);null===e.startTime&&null!==r&&(e.startTime=r-i/e.animation.playbackRate),"pause"==e.pendingTask&&null!==e.startTime&&(e.holdTime=null,e.pendingTask=null,e.readyPromise.resolve(this)),"play"==e.pendingTask&&null!==e.startTime&&(e.pendingTask=null,e.readyPromise.resolve(this)),U(e,!0,!0)}else e.animation.finish()},n.play=function(){var e=G.get(this);e.timeline?q(e,!0):e.animation.play()},n.pause=function(){var e=G.get(this);if(e.timeline){if("paused"!=this.playState){var t=null,n=e.animation.playbackRate,i=H(e);if(null===e.animation.currentTime)if(n>=0)t=0;else{if(Infinity==i)return void e.animation.pause();t=i}null!==t&&(e.startTime=t),"play"==e.pendingTask?e.pendingTask=null:e.readyPromise=null,e.readyPromise||A(e),e.pendingTask="pause"}}else e.animation.pause()},n.reverse=function(){var e=G.get(this),t=_(e),n=e.resetCurrentTimeOnResume?null:W(e,this.currentTime),i=Infinity==H(e),r=0!=t&&(t<0||n>0||!i);if(!e.timeline||!r)return r&&(e.pendingPlaybackRate=-_(e)),void e.animation.reverse();if("inactive"==e.timeline.phase)throw new DOMException("Cannot reverse an animation with no active timeline","InvalidStateError");this.updatePlaybackRate(-t),q(e,!0)},n.updatePlaybackRate=function(e){var t=G.get(this);if(t.pendingPlaybackRate=e,t.timeline){if(!t.readyPromise||"pending"!=t.readyPromise.state)switch(this.playState){case"idle":case"paused":D(t);break;case"finished":var n=W(t,t.timeline.currentTime),i=null!==n?(n-t.startTime)*t.animation.playbackRate:null;t.startTime=0==e?n:null!=n&&null!=i?(n-i)/e:null,D(t),U(t,!1,!1),F(t);break;default:q(t,!1)}}else t.animation.updatePlaybackRate(e)},n.persist=function(){G.get(this).animation.persist()},n.cancel=function(){var e=G.get(this);e.timeline?("idle"!=this.playState&&(function(e){e.pendingTask&&(e.pendingTask=null,D(e),e.readyPromise.reject(O()),A(e),e.readyPromise.resolve(e.proxy))}(e),e.finishedPromise&&"pending"==e.finishedPromise.state&&e.finishedPromise.reject(O()),e.finishedPromise=new N,e.animation.cancel()),e.startTime=null,e.holdTime=null,d(e.timeline,e.animation)):e.animation.cancel()},n.addEventListener=function(e,t,n){G.get(this).animation.addEventListener(e,t,n)},n.removeEventListener=function(e,t,n){G.get(this).animation.removeEventListener(e,t,n)},n.dispatchEvent=function(e){G.get(this).animation.dispatchEvent(e)},t(e,[{key:"effect",get:function(){var e=G.get(this);return e.timeline?(e.effect||(e.effect=function(e){var t=e.animation.effect,n=t.updateTiming,i={apply:function(n){t.getTiming();var i=n.apply(t);if(e.timeline){i.localTime=j(e,i.localTime),i.endTime=j(e,i.endTime),i.activeDuration=j(e,i.activeDuration);var r=H(e);i.duration=r?CSS.percent(100*(i.iterations?(r-i.delay-i.endDelay)/i.iterations:0)/r):CSS.percent(0),void 0===e.timeline.currentTime&&(i.localTime=null)}return i}},r={apply:function(i,r){var a=1e5;if(e.specifiedTiming)return e.specifiedTiming;e.specifiedTiming=i.apply(t);var o,l,s=Object.assign({},e.specifiedTiming),u=!1;return e.timeline instanceof ViewTimeline&&(o=function(e){if(!(e.timeline instanceof ViewTimeline))return 0;var t=e.timeRange.start;return x(e.timeline,t.name,t.offset)}(e),l=function(e){if(!(e.timeline instanceof ViewTimeline))return 0;var t=e.timeRange.end;return 1-x(e.timeline,t.name,t.offset)}(e),u=!0),(null===s.duration||"auto"===s.duration||u)&&e.timeline&&(u?(s.delay=o*a,s.endDelay=l*a):(s.delay=0,s.endDelay=0),s.duration=s.iterations?((s.iterations?a:0)-s.delay-s.endDelay)/s.iterations:0,n.apply(t,[s])),e.normalizedTiming=s,e.specifiedTiming}},a={apply:function(n,i,r){if(e.timeline){var a=r[0];if(Infinity===a.duration)throw TypeError("Effect duration cannot be Infinity when used with Scroll Timelines");if(Infinity===a.iterations)throw TypeError("Effect iterations cannot be Infinity when used with Scroll Timelines")}e.specifiedTiming&&n.apply(t,[e.specifiedTiming]),n.apply(t,r),e.specifiedTiming=null}},o=new Proxy(t,{get:function(e,n){var i=e[n];return"function"==typeof i?i.bind(t):i},set:function(e,t,n){return e[t]=n,!0}});return o.getComputedTiming=new Proxy(t.getComputedTiming,i),o.getTiming=new Proxy(t.getTiming,r),o.updateTiming=new Proxy(t.updateTiming,a),o}(e)),e.effect):e.animation.effect},set:function(e){G.get(this).animation.effect=e,details.effect=null}},{key:"timeline",get:function(){var e=G.get(this);return e.timeline||e.animation.timeline},set:function(e){var t=this.timeline;if(t!=e){var n=this.playState,i=this.currentTime,r=G.get(this),a=H(r),o=a>0?W(r,i)/a:0,l=t instanceof v,s=e instanceof v;r.resetCurrentTimeOnResume=!1;var u=this.pending;if(l&&d(r.timeline,r.animation),s){r.timeline=e,D(r);var c=r.animation.playbackRate>=0?0:H(r);switch(n){case"running":case"finished":r.startTime=c,p(r.timeline,r.animation,Q.bind(this));break;case"paused":r.resetCurrentTimeOnResume=!0,r.startTime=null,r.holdTime=W(r,CSS.percent(100*o));break;default:r.holdTime=null,r.startTime=null}return u&&(r.readyPromise&&"resolved"!=r.readyPromise.state||A(r),r.pendingTask="paused"==n?"pause":"play"),null!==r.startTime&&(r.holdTime=null),void U(r,!1,!1)}if(r.animation.timeline!=e)throw TypeError("Unsupported timeline: "+e);if(d(r.timeline,r.animation),r.timeline=null,l)switch(null!==i&&(r.animation.currentTime=o*H(r)),n){case"paused":r.animation.pause();break;case"running":case"finished":r.animation.play()}}}},{key:"startTime",get:function(){var e=G.get(this);return e.timeline?j(e,e.startTime):e.animation.startTime},set:function(e){var t=G.get(this);if(e=W(t,e),t.timeline){null==W(t,t.timeline.currentTime)&&null!=t.startTime&&(t.holdTime=null,F(t));var n=W(t,this.currentTime);D(t),t.startTime=e,t.resetCurrentTimeOnResume=!1,t.holdTime=null!==t.startTime&&0!=t.animation.playbackRate?null:n,t.pendingTask&&(t.pendingTask=null,t.readyPromise.resolve(this)),U(t,!0,!1),F(t)}else t.animation.startTime=e}},{key:"currentTime",get:function(){var e=G.get(this);return e.timeline?j(e,null!=e.holdTime?e.holdTime:V(e)):e.animation.currentTime},set:function(e){var t=G.get(this);if(e=W(t,e),t.timeline&&null!=e){var n=t.timeline.phase;null!==t.holdTime||null===t.startTime||"inactive"==n||0==t.animation.playbackRate?t.holdTime=e:t.startTime=z(t,e),t.resetCurrentTimeOnResume=!1,"inactive"==n&&(t.startTime=null),t.previousCurrentTime=null,"pause"==t.pendingTask&&(t.holdTime=e,D(t),t.startTime=null,t.pendingTask=null,t.readyPromise.resolve(this)),U(t,!0,!1)}else t.animation.currentTime=e}},{key:"playbackRate",get:function(){return G.get(this).animation.playbackRate},set:function(e){var t=G.get(this);if(t.timeline){t.pendingPlaybackRate=null;var n=this.currentTime;t.animation.playbackRate=e,null!==n&&(this.currentTime=n)}else t.animation.playbackRate=e}},{key:"playState",get:function(){var e=G.get(this);if(!e.timeline)return e.animation.playState;var t=W(e,this.currentTime);if(null===t&&null===e.startTime&&null==e.pendingTask)return"idle";if("pause"==e.pendingTask||null===e.startTime&&"play"!=e.pendingTask)return"paused";if(null!=t){if(e.animation.playbackRate>0&&t>=H(e))return"finished";if(e.animation.playbackRate<0&&t<=0)return"finished"}return"running"}},{key:"replaceState",get:function(){return G.get(this).animation.pending}},{key:"pending",get:function(){var e=G.get(this);return e.timeline?!!e.readyPromise&&"pending"==e.readyPromise.state:e.animation.pending}},{key:"id",get:function(){return G.get(this).animation.id}},{key:"onfinish",get:function(){return G.get(this).animation.onfinish},set:function(e){G.get(this).animation.onfinish=e}},{key:"oncancel",get:function(){return G.get(this).animation.oncancel},set:function(e){G.get(this).animation.oncancel=e}},{key:"onremove",get:function(){return G.get(this).animation.onremove},set:function(e){G.get(this).animation.onremove=e}},{key:"finished",get:function(){var e=G.get(this);return e.timeline?(e.finishedPromise||(e.finishedPromise=new N),e.finishedPromise.promise):e.animation.finished}},{key:"ready",get:function(){var e=G.get(this);return e.timeline?(e.readyPromise||(e.readyPromise=new N,e.readyPromise.resolve(this)),e.readyPromise.promise):e.animation.ready}}]),e}();function X(e,t){if(!e)return null;var n=e.split(" ");if(!C.includes(n[0])||2==n.length&&!n[1].endsWith("%"))throw TypeError("Invalid animation delay");var i=t;if(2==n.length){var r=parseFloat(n[1]);if(Number.isNaN(r))throw TypeError('"'+n[1]+'" is not a valid percentage for animation delay');i=CSS.percent(r)}return{name:n[0],offset:i}}function $(){return{name:"cover",offset:CSS.percent(0)}}function J(){return{name:"cover",offset:CSS.percent(100)}}function Z(e){var t=ee(e["animation-time-range"]);return e["animation-delay"]&&(t.start=X(e["animation-delay"],$().offset)),e["animation-end-delay"]&&(t.end=X(e["animation-end-delay"],J().offset)),t}function ee(e){var t={start:$(),end:J()};if(!e)return t;var n=e.split(" "),i=[],r=[];if(n.forEach(function(e){e.endsWith("%")?r.push(parseFloat(e)):i.push(e)}),i.length>2||r.length>2||1==r.length)throw TypeError("Invalid time range");return i.length&&(t.start.name=i[0],t.end.name=i.length>1?i[1]:i[0]),r.length>1&&(t.start.offset=CSS.percent(r[0]),t.end.offset=CSS.percent(r[1])),t}var te={IDENTIFIER:/[\w\\\@_-]+/g,WHITE_SPACE:/\s*/g,NUMBER:/^[0-9]+/,TIME:/^[0-9]+(s|ms)/,VIEW_TIMELINE:/view-timeline\s*:([^;}]+)/,VIEW_TIMELINE_NAME:/view-timeline-name\s*:([^;}]+)/,VIEW_TIMELINE_AXIS:/view-timeline-axis\s*:([^;}]+)/,ANIMATION_TIMELINE:/animation-timeline\s*:([^;}]+)/,ANIMATION_DELAY:/animation-delay\s*:([^;}]+)/,ANIMATION_END_DELAY:/animation-end-delay\s*:([^;}]+)/,ANIMATION_TIME_RANGE:/animation-time-range\s*:([^;}]+)/,ANIMATION_NAME:/animation-name\s*:([^;}]+)/,ANIMATION:/animation\s*:([^;}]+)/,SOURCE_ELEMENT:/selector\(#([^)]+)\)/},ne=["block","inline","vertical","horizontal"],ie=new(function(){function e(){this.cssRulesWithTimelineName=[],this.scrollTimelineOptions=new Map,this.subjectSelectorToViewTimeline=[],this.keyframeNamesSelectors=new Map}var t=e.prototype;return t.transpileStyleSheet=function(e,t,n){for(var i={sheetSrc:e,index:0,name:n};i.index=i.sheetSrc.length));)if(this.lookAhead("/*",i))for(;this.lookAhead("/*",i);)this.eatComment(i),this.eatWhitespace(i);else if(this.lookAhead("@scroll-timeline",i)){var r=this.parseScrollTimeline(i).scrollTimeline;t&&this.scrollTimelineOptions.set(r.name,r)}else{var a=this.parseQualifiedRule(i);if(!a)continue;t?this.parseKeyframesAndSaveNameMapping(a,i):this.handleScrollTimelineProps(a,i)}return i.sheetSrc},t.getAnimationTimelineOptions=function(e,t){for(var n=this.cssRulesWithTimelineName.length-1;n>=0;n--){var i=this.cssRulesWithTimelineName[n];if(t.matches(i.selector)&&(!i["animation-name"]||i["animation-name"]==e))return{"animation-timeline":i["animation-timeline"],"animation-delay":i["animation-delay"],"animation-end-delay":i["animation-end-delay"],"animation-time-range":i["animation-time-range"]}}return null},t.getSourceElement=function(e){var t=te.SOURCE_ELEMENT.exec(e);return t?document.getElementById(t[1]):"auto"===e?document.scrollingElement:null},t.getScrollTimelineOptions=function(e){var t=this.scrollTimelineOptions.get(e);if(null!=t&&t.source){var i=this.getSourceElement(t.source);return n({},i?{source:i}:{},"auto"!=t.orientation?{orientation:t.orientation}:{})}return null},t.findPreviousSiblingOrAncestorMatchingSelector=function(e,t){for(var n=e;n;){if(n.matches(t))return n;n=n.previousElementSibling||n.parentElement}return null},t.getViewTimelineOptions=function(e,t){for(var n=this.subjectSelectorToViewTimeline.length-1;n>=0;n--){var i=this.subjectSelectorToViewTimeline[n];if(i.name==e){var r=this.findPreviousSiblingOrAncestorMatchingSelector(t,i.selector);if(r)return{subject:r,axis:i.axis,inset:i.inset}}}return null},t.parseScrollTimeline=function(e){var t=e.index;this.assertString(e,"@scroll-timeline"),this.eatWhitespace(e);var n=this.parseIdentifier(e);this.eatWhitespace(e),this.assertString(e,"{"),this.eatWhitespace(e);for(var i={name:n,source:"auto",orientation:void 0};"}"!==this.peek(e);){var r=this.parseIdentifier(e);this.eatWhitespace(e),this.assertString(e,":"),this.eatWhitespace(e),i[r]=this.removeEnclosingDoubleQuotes(this.eatUntil(";",e)),this.assertString(e,";"),this.eatWhitespace(e)}this.assertString(e,"}");var a=e.index;return this.eatWhitespace(e),{scrollTimeline:i,startIndex:t,endIndex:a}},t.handleScrollTimelineProps=function(e,t){var n=this;if(!e.selector.includes("@keyframes")){var i=e.block.contents.includes("animation-name:"),r=e.block.contents.includes("animation-timeline:"),a=e.block.contents.includes("animation:");this.saveSubjectSelectorToViewTimeline(e);var o=[],l=[];r&&(o=this.extractMatches(e.block.contents,te.ANIMATION_TIMELINE)),i&&(l=this.extractMatches(e.block.contents,te.ANIMATION_NAME)),r&&i||a&&this.extractMatches(e.block.contents,te.ANIMATION).forEach(function(i){var a=n.extractAnimationName(i),s=n.extractTimelineName(i);a&&l.push(a),s&&(o.push(s),e.block.contents=e.block.contents.replace(s," ".repeat(s.length)),n.replacePart(e.block.startIndex,e.block.endIndex,e.block.contents,t)),(s||r)&&(n.hasDuration(i)||(e.block.contents=e.block.contents.replace("animation:","animation: 1s "),n.replacePart(e.block.startIndex,e.block.endIndex,e.block.contents,t)))}),this.saveRelationInList(e,o,l)}},t.saveSubjectSelectorToViewTimeline=function(e){var t=e.block.contents.includes("view-timeline:"),n=e.block.contents.includes("view-timeline-name:"),i=e.block.contents.includes("view-timeline-axis:");if(t||n){var r={selector:e.selector,name:"",axis:"block"};if(t){var a=this.extractMatches(e.block.contents,te.VIEW_TIMELINE,separator=" ");1==a.length?r.name=a[0]:2==a.length&&(ne.includes(a[0])?(r.axis=a[0],r.name=a[1]):(r.axis=a[1],r.name=a[0]))}if(n){var o=this.extractMatches(e.block.contents,te.VIEW_TIMELINE_NAME);r.name=o[0]}if(i){var l=this.extractMatches(e.block.contents,te.VIEW_TIMELINE_AXIS);ne.includes(l[0])&&(r.axis=l[0])}this.subjectSelectorToViewTimeline.push(r)}},t.hasDuration=function(e){return e.split(" ").filter(function(e){return te.TIME.exec(e)}).length>=1},t.saveRelationInList=function(e,t,i){var r=e.block.contents.includes("animation-delay:"),a=e.block.contents.includes("animation-end-delay:"),o=e.block.contents.includes("animation-time-range:"),l=[],s=[],u=[];r&&(l=this.extractMatches(e.block.contents,te.ANIMATION_DELAY)),a&&(s=this.extractMatches(e.block.contents,te.ANIMATION_END_DELAY)),o&&(u=this.extractMatches(e.block.contents,te.ANIMATION_TIME_RANGE));for(var c=Math.max(t.length,i.length,l.length,s.length,u.length),m=0;m0&&n.keyframeNamesSelectors.set(e,i)})}},t.replaceKeyframesAndGetMapping=function(e,t){function n(e){return C.some(function(t){return e.startsWith(t)})}var i=e.block.contents,r=function(e){for(var t=0,n=-1,i=[],r=0;r=t&&(i.index=e+n.length+(i.index-t))},t.eatComment=function(e){this.assertString(e,"/*"),this.eatUntil("*/",e,!0),this.assertString(e,"*/")},t.eatBlock=function(e){var t=e.index;this.assertString(e,"{");for(var n=1;0!=n;)this.lookAhead("/*",e)?this.eatComment(e):("{"===e.sheetSrc[e.index]?n++:"}"===e.sheetSrc[e.index]&&n--,this.advance(e));var i=e.index;return{startIndex:t,endIndex:i,contents:e.sheetSrc.slice(t,i)}},t.advance=function(e){if(e.index++,e.index>e.sheetSrc.length)throw this.parseError(e,"Advanced beyond the end")},t.eatUntil=function(e,t,n){void 0===n&&(n=!1);for(var i=t.index;!this.lookAhead(e,t);)this.advance(t);return n&&(t.sheetSrc=t.sheetSrc.slice(0,i)+" ".repeat(t.index-i)+t.sheetSrc.slice(t.index)),t.sheetSrc.slice(i,t.index)},t.parseSelector=function(e){var t=e.index;if(this.eatUntil("{",e),t===e.index)throw Error("Empty selector");return e.sheetSrc.slice(t,e.index)},t.eatWhitespace=function(e){te.WHITE_SPACE.lastIndex=e.index;var t=te.WHITE_SPACE.exec(e.sheetSrc);t&&(e.index+=t[0].length)},t.lookAhead=function(e,t){return t.sheetSrc.substr(t.index,e.length)==e},t.peek=function(e){return e.sheetSrc[e.index]},t.extractMatches=function(e,t,n){return void 0===n&&(n=","),t.exec(e)[1].trim().split(n).map(function(e){return e.trim()})},e}());function re(e,t,n,i,r,a){return I(E(e,t,n,i,r),a,E("cover",t,n,i,r))}if(function(){if(!CSS.supports("animation-timeline: works")){!function(){function e(e){if(0!==e.innerHTML.trim().length){var t=ie.transpileStyleSheet(e.innerHTML,!0);t=ie.transpileStyleSheet(t,!1),e.innerHTML=t}}new MutationObserver(function(t){for(var n,i=l(t);!(n=i()).done;)for(var r,a=l(n.value.addedNodes);!(r=a()).done;){var o=r.value;o instanceof HTMLStyleElement&&e(o)}}).observe(document.documentElement,{childList:!0,subtree:!0}),document.querySelectorAll("style").forEach(function(t){return e(t)}),document.querySelectorAll("link").forEach(function(e){})}();var e=new WeakMap;window.addEventListener("animationstart",function(t){t.target.getAnimations().filter(function(e){return e.animationName===t.animationName}).forEach(function(n){e.has(t.target)||e.set(t.target,new Map);var i=e.get(t.target);if(!i.has(n.animationName)){var r=function(e,t,n){var i=ie.getAnimationTimelineOptions(t,n),r=i["animation-timeline"];if(!r)return null;var a=ie.getScrollTimelineOptions(r)||ie.getViewTimelineOptions(r,n);return a?(a.subject&&function(e,t){var n=k(t.subject),i=t.axis||t.orientation,r=ie.keyframeNamesSelectors.get(e.animationName);if(r&&r.size){var a=[];e.effect.getKeyframes().forEach(function(e){var o=function(e,r){for(var a,o=null,s=l(e);!(a=s()).done;){var u=a.value,c=u[1];if(u[0]==100*r.offset){if("from"==c)o=0;else if("to"==c)o=100;else{var m=c.split(" ");o=1==m.length?parseFloat(m[0]):100*re(m[0],n,t.subject,i,t.inset,CSS.percent(parseFloat(m[1])))}break}}return o}(r,e);null!==o&&o>=0&&o<=100&&(e.offset=o/100,a.push(e))});var o=a.sort(function(e,t){return e.offsett.offset?1:0});e.effect.setKeyframes(o),ie.keyframeNamesSelectors.set(e.animationName,null)}}(e,a),{timeline:a.source?new v(a):new M(a),animOptions:i}):null}(n,n.animationName,t.target);i.set(n.animationName,r&&r.timeline&&n.timeline!=r.timeline?new Y(n,r.timeline,r.animOptions):null)}var a=i.get(n.animationName);null!==a&&(n.pause(),a.play())})})}}(),[].concat(document.styleSheets).filter(function(e){return null!==e.href}).length&&console.warn("Non-Inline StyleSheets detected: ScrollTimeline polyfill currently only supports inline styles within style tags"),!Reflect.defineProperty(window,"ScrollTimeline",{value:v}))throw Error("Error installing ScrollTimeline polyfill: could not attach ScrollTimeline to window");if(!Reflect.defineProperty(window,"ViewTimeline",{value:M}))throw Error("Error installing ViewTimeline polyfill: could not attach ViewTimeline to window");if(!Reflect.defineProperty(Element.prototype,"animate",{value:function(e,t){var n=t.timeline;n instanceof v&&delete t.timeline;var i=function(e,t){if(t in e){var n=e[t];return"number"!=typeof n?(delete e[t],n):null}},r=function(e,t){t&&(t.phase&&(e.name=t.phase),t.percent&&(e.offset=t.percent))},a=i(t,"delay"),o=i(t,"endDelay"),l=P.apply(this,[e,t]),s=new Y(l,n);if(n instanceof v){if(l.pause(),n instanceof ViewTimeline){var u=G.get(s);u.timeRange=ee(t.timeRange),r(u.timeRange.start,a),r(u.timeRange.end,o)}s.play()}return s}}))throw Error("Error installing ScrollTimeline polyfill: could not attach WAAPI's animate to DOM Element");if(!Reflect.defineProperty(window,"Animation",{value:Y}))throw Error("Error installing Animation constructor.")}(); +!function(){function e(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,i=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[i++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}!function(){var e,n=new WeakMap;function r(e){for(var t,n=[],i=0;i0?CSS.percent(100*n/i):CSS.percent(100)}},{key:"__polyfill",get:function(){return!0}}]),e}();function g(e,t){for(var n=e.parentElement;null!=n;){if(t(n))return n;n=n.parentElement}}function y(e){switch(getComputedStyle(e).display){case"block":case"inline-block":case"list-item":case"table":case"table-caption":case"flow-root":case"flex":case"grid":return!0}return!1}function T(e){var t=getComputedStyle(e);return"none"!=t.transform||"none"!=t.perspective||"transform"==t.willChange||"perspective"==t.willChange||"none"!=t.filter||"filter"==t.willChange||"none"!=t.backdropFilter}function S(e){return"static"!=getComputedStyle(e).position||T(e)}function b(e){switch(getComputedStyle(e).position){case"static":case"relative":case"sticky":return g(e,y);case"absolute":return g(e,S);case"fixed":return g(e,T)}}function k(e){if(e){for(;e=b(e);)switch(getComputedStyle(e)["overflow-x"]){case"auto":case"scroll":case"hidden":return e==document.body&&"visible"==getComputedStyle(document.scrollingElement).overflow?document.scrollingElement:e}return document.scrollingElement}}function w(e,t){var n=s.get(e);return"inactive"===e.phase?null:e instanceof P?E(t,e.source,e.subject,n.orientation,n.inset):null}function E(e,t,n,i,r){for(var a=0,o=0,l=n,s=t.offsetParent;l&&l!=s;)o+=l.offsetLeft,a+=l.offsetTop,l=l.offsetParent;o-=t.offsetLeft+t.clientLeft,a-=t.offsetTop+t.clientTop;var u=getComputedStyle(t),c="horizontal-tb"==u.writingMode,m=void 0,f=void 0,h=void 0;"horizontal"==i||"inline"==i&&c||"block"==i&&!c?(m=n.clientWidth,f=o,("rtl"==u.direction||"vertical-rl"==u.writingMode)&&(f+=t.scrollWidth-t.clientWidth),h=t.clientWidth):(m=n.clientHeight,f=a,h=t.clientHeight);var d=function(e,t){var n={start:0,end:0};if(!e)return n;var i=e.split(" "),r=[];if(i.forEach(function(e){e.endsWith("%")?r.push(t/100*parseFloat(e)):e.endsWith("px")?r.push(parseFloat(e)):"auto"===e&&r.push(0)}),r.length>2)throw TypeError("Invalid inset");return 1==r.length?(n.start=r[0],n.end=r[0]):2==r.length&&(n.start=r[0],n.end=r[1]),n}(r,h),p=f-h+d.end,v=f+m-d.start,g=p+m,y=v-m,T=Math.min(g,y),S=Math.max(g,y),b=void 0,k=void 0;switch(e){case"cover":b=p,k=v;break;case"contain":b=T,k=S;break;case"enter":b=p,k=T;break;case"exit":b=S,k=v}return{start:b,end:k}}function x(e,t,n){return I(w(e,t),n,w(e,"cover"))}function I(e,t,n){return e&&n?(t.value/100*(e.end-e.start)+e.start-n.start)/(n.end-n.start):0}var P=function(e){function n(t){var n;return t.axis&&(t.orientation=t.axis),n=e.call(this,t)||this,s.get(a(n)).subject=t&&t.subject?t.subject:void 0,f(a(n)),c(a(n)),n}return i(n,e),t(n,[{key:"source",get:function(){return f(this),s.get(this).source},set:function(e){throw new Error("Cannot set the source of a view timeline")}},{key:"subject",get:function(){return s.get(this).subject}},{key:"axis",get:function(){return s.get(this).orientation}},{key:"currentTime",get:function(){var e=null,t=m(this.source,this.orientation);if(t==e)return e;var n=w(this,"cover");return n?CSS.percent((t-n.start)/(n.end-n.start)*100):e}}]),n}(v),M=document.getAnimations,A=window.Element.prototype.getAnimations,R=window.Element.prototype.animate,C=window.Animation,N=["enter","exit","cover","contain"],O=function(){function e(){var e=this;this.state="pending",this.nativeResolve=this.nativeReject=null,this.promise=new Promise(function(t,n){e.nativeResolve=t,e.nativeReject=n})}var t=e.prototype;return t.resolve=function(e){this.state="resolved",this.nativeResolve(e)},t.reject=function(e){this.state="rejected",this.promise.catch(function(){}),this.nativeReject(e)},e}();function W(e){e.readyPromise=new O,requestAnimationFrame(function(){null!==e.timeline.currentTime&&Y(e)})}function j(){return new DOMException("The user aborted a request","AbortError")}function L(e,t){if(null===t)return t;if("number"!=typeof t)throw new DOMException("Unexpected value: "+t+". Cannot convert to CssNumberish","InvalidStateError");var n=K(e);return CSS.percent(n?100*t/n:0)}function _(e,t){if(e.timeline){if(null===t)return t;if("percent"===t.unit){var n=K(e);return t.value*n/100}throw new DOMException("CSSNumericValue must be a percentage for progress based animations.","NotSupportedError")}if(null==t||"number"==typeof t)return t;var i=t.to("ms");if(convertTime)return i.value;throw new DOMException("CSSNumericValue must be either a number or a time value for time based animations.","InvalidStateError")}function D(e){if(e.finishedPromise&&"pending"==e.finishedPromise.state&&"finished"==e.proxy.playState){e.finishedPromise.resolve(e.proxy),e.animation.pause();var t=new CustomEvent("finish",{detail:{currentTime:e.proxy.currentTime,timelineTime:e.proxy.timeline.currentTime}});Object.defineProperty(t,"currentTime",{get:function(){return this.detail.currentTime}}),Object.defineProperty(t,"timelineTime",{get:function(){return this.detail.timelineTime}}),requestAnimationFrame(function(){queueMicrotask(function(){e.animation.dispatchEvent(t)})})}}function V(e){return null!==e.pendingPlaybackRate?e.pendingPlaybackRate:e.animation.playbackRate}function z(e){null!==e.pendingPlaybackRate&&(e.animation.playbackRate=e.pendingPlaybackRate,e.pendingPlaybackRate=null)}function U(e){if(!e.timeline)return null;var t=_(e,e.timeline.currentTime);if(null===t)return null;if(null===e.startTime)return null;var n=(t-e.startTime)*e.animation.playbackRate;return-0==n&&(n=0),n}function H(e,t){if(!e.timeline)return null;var n=_(e,e.timeline.currentTime);return null==n?null:n-t/e.animation.playbackRate}function F(e,t,n){if(e.timeline){var i=t?_(e,e.proxy.currentTime):U(e);if(i&&null!=e.startTime&&!e.proxy.pending){var r=V(e),a=K(e),o=e.previousCurrentTime;r>0&&i>=a?((null===o||o0)&&(o=0),e.holdTime=t?i:o):0!=r&&(t&&null!==e.holdTime&&(e.startTime=H(e,e.holdTime)),e.holdTime=null)}q(e),e.previousCurrentTime=_(e,e.proxy.currentTime),"finished"==e.proxy.playState?(e.finishedPromise||(e.finishedPromise=new O),"pending"==e.finishedPromise.state&&(n?D(e):Promise.resolve().then(function(){D(e)}))):(e.finishedPromise&&"resolved"==e.finishedPromise.state&&(e.finishedPromise=new O),"paused"!=e.animation.playState&&e.animation.pause())}}function K(e){var t=function(e){var t=e.proxy.effect.getTiming();return e.normalizedTiming||t}(e);return Math.max(0,t.delay+t.endDelay+t.iterations*t.duration)}function q(e){if(e.timeline)if(null!==e.startTime){var t=e.timeline.currentTime;if(null==t)return;Q(e,(_(e,t)-e.startTime)*e.animation.playbackRate)}else null!==e.holdTime&&Q(e,e.holdTime)}function Q(e,t){var n=e.timeline,i=e.animation.playbackRate;e.animation.currentTime=t+(n.currentTime&&n.currentTime.value==(i<0?0:100)?i<0?.001:-.001:0)}function B(e,t){if(e.timeline){var n="paused"==e.proxy.playState&&e.proxy.pending,i=!1,r=null,a=_(e,e.proxy.currentTime);e.resetCurrentTimeOnResume&&(a=null,e.resetCurrentTimeOnResume=!1);var o=V(e),l=K(e);if(o>0&&t&&(null==a||a<0||a>=l))r=0;else if(o<0&&t&&(null==a||a<=0||a>l)){if(Infinity==l)return void e.animation.play();r=l}else 0==o&&null==a&&(r=0);null!=r&&(e.startTime=r,e.holdTime=null,z(e)),p(e.timeline,e.animation,G.bind(e.proxy)),e.holdTime&&(e.startTime=null),e.pendingTask&&(e.pendingTask=null,i=!0),(null!==e.holdTime||null!==r||n||null!==e.pendingPlaybackRate)&&(e.readyPromise&&!i&&(e.readyPromise=null),q(e),e.readyPromise||W(e),e.pendingTask="play",F(e,!1,!1))}}function G(e){var t=X.get(this);if(null!=e){t.pendingTask&&Y(t);var n=this.playState;"running"!=n&&"finished"!=n||(Q(t,(_(t,e)-_(t,this.startTime))*this.playbackRate),"finished"==n&&0!=V(t)&&(t.holdTime=null),F(t,!1,!1))}else"idle"!=t.animation.playState&&t.animation.cancel()}function Y(e){"pause"==e.pendingTask?function(e){var t=_(e,e.timeline.currentTime);null!=e.startTime&&null==e.holdTime&&(e.holdTime=(t-e.startTime)*e.animation.playbackRate),z(e),e.startTime=null,e.readyPromise.resolve(e.proxy),F(e,!1,!1),q(e),e.pendingTask=null}(e):"play"==e.pendingTask&&function(e){var t=_(e,e.timeline.currentTime);if(null!=e.holdTime)z(e),0==e.animation.playbackRate?e.startTime=t:(e.startTime=t-e.holdTime/e.animation.playbackRate,e.holdTime=null);else if(null!==e.startTime&&null!==e.pendingPlaybackRate){var n=(t-e.startTime)*e.animation.playbackRate;z(e);var i=e.animation.playbackRate;0==i?(e.holdTime=null,e.startTime=t):e.startTime=t-n/i}e.readyPromise&&"pending"==e.readyPromise.state&&e.readyPromise.resolve(e.proxy),F(e,!1,!1),q(e),e.pendingTask=null}(e)}var X=new WeakMap,$=new WeakMap,J=function(){function e(e,t,n){void 0===n&&(n={});var i=e instanceof C?e:new C(e,a),r=t instanceof v,a=r?void 0:t;$.set(i,this),X.set(this,{animation:i,timeline:r?t:void 0,playState:r?"idle":null,readyPromise:null,finishedPromise:null,startTime:null,holdTime:null,previousCurrentTime:null,resetCurrentTimeOnResume:!1,pendingPlaybackRate:null,pendingTask:null,specifiedTiming:null,normalizedTiming:null,effect:null,timeRange:t instanceof ViewTimeline?ne(n):null,proxy:this})}var n=e.prototype;return n.finish=function(){var e=X.get(this);if(e.timeline){var t=V(e),n=K(e);if(0==t)throw new DOMException("Cannot finish Animation with a playbackRate of 0.","InvalidStateError");if(t>0&&Infinity==n)throw new DOMException("Cannot finish Animation with an infinite target effect end.","InvalidStateError");z(e);var i=t<0?0:n;this.currentTime=L(e,i);var r=_(e,e.timeline.currentTime);null===e.startTime&&null!==r&&(e.startTime=r-i/e.animation.playbackRate),"pause"==e.pendingTask&&null!==e.startTime&&(e.holdTime=null,e.pendingTask=null,e.readyPromise.resolve(this)),"play"==e.pendingTask&&null!==e.startTime&&(e.pendingTask=null,e.readyPromise.resolve(this)),F(e,!0,!0)}else e.animation.finish()},n.play=function(){var e=X.get(this);e.timeline?B(e,!0):e.animation.play()},n.pause=function(){var e=X.get(this);if(e.timeline){if("paused"!=this.playState){var t=null,n=e.animation.playbackRate,i=K(e);if(null===e.animation.currentTime)if(n>=0)t=0;else{if(Infinity==i)return void e.animation.pause();t=i}null!==t&&(e.startTime=t),"play"==e.pendingTask?e.pendingTask=null:e.readyPromise=null,e.readyPromise||W(e),e.pendingTask="pause"}}else e.animation.pause()},n.reverse=function(){var e=X.get(this),t=V(e),n=e.resetCurrentTimeOnResume?null:_(e,this.currentTime),i=Infinity==K(e),r=0!=t&&(t<0||n>0||!i);if(!e.timeline||!r)return r&&(e.pendingPlaybackRate=-V(e)),void e.animation.reverse();if("inactive"==e.timeline.phase)throw new DOMException("Cannot reverse an animation with no active timeline","InvalidStateError");this.updatePlaybackRate(-t),B(e,!0)},n.updatePlaybackRate=function(e){var t=X.get(this);if(t.pendingPlaybackRate=e,t.timeline){if(!t.readyPromise||"pending"!=t.readyPromise.state)switch(this.playState){case"idle":case"paused":z(t);break;case"finished":var n=_(t,t.timeline.currentTime),i=null!==n?(n-t.startTime)*t.animation.playbackRate:null;t.startTime=0==e?n:null!=n&&null!=i?(n-i)/e:null,z(t),F(t,!1,!1),q(t);break;default:B(t,!1)}}else t.animation.updatePlaybackRate(e)},n.persist=function(){X.get(this).animation.persist()},n.cancel=function(){var e=X.get(this);e.timeline?("idle"!=this.playState&&(function(e){e.pendingTask&&(e.pendingTask=null,z(e),e.readyPromise.reject(j()),W(e),e.readyPromise.resolve(e.proxy))}(e),e.finishedPromise&&"pending"==e.finishedPromise.state&&e.finishedPromise.reject(j()),e.finishedPromise=new O,e.animation.cancel()),e.startTime=null,e.holdTime=null,d(e.timeline,e.animation)):e.animation.cancel()},n.addEventListener=function(e,t,n){X.get(this).animation.addEventListener(e,t,n)},n.removeEventListener=function(e,t,n){X.get(this).animation.removeEventListener(e,t,n)},n.dispatchEvent=function(e){X.get(this).animation.dispatchEvent(e)},t(e,[{key:"effect",get:function(){var e=X.get(this);return e.timeline?(e.effect||(e.effect=function(e){var t=e.animation.effect,n=t.updateTiming,i={apply:function(n){t.getTiming();var i=n.apply(t);if(e.timeline){i.localTime=L(e,i.localTime),i.endTime=L(e,i.endTime),i.activeDuration=L(e,i.activeDuration);var r=K(e);i.duration=r?CSS.percent(100*(i.iterations?(r-i.delay-i.endDelay)/i.iterations:0)/r):CSS.percent(0),void 0===e.timeline.currentTime&&(i.localTime=null)}return i}},r={apply:function(i,r){var a=1e5;if(e.specifiedTiming)return e.specifiedTiming;e.specifiedTiming=i.apply(t);var o,l,s=Object.assign({},e.specifiedTiming),u=!1;return e.timeline instanceof ViewTimeline&&(o=function(e){if(!(e.timeline instanceof ViewTimeline))return 0;var t=e.timeRange.start;return x(e.timeline,t.name,t.offset)}(e),l=function(e){if(!(e.timeline instanceof ViewTimeline))return 0;var t=e.timeRange.end;return 1-x(e.timeline,t.name,t.offset)}(e),u=!0),(null===s.duration||"auto"===s.duration||u)&&e.timeline&&(u?(s.delay=o*a,s.endDelay=l*a):(s.delay=0,s.endDelay=0),s.duration=s.iterations?((s.iterations?a:0)-s.delay-s.endDelay)/s.iterations:0,n.apply(t,[s])),e.normalizedTiming=s,e.specifiedTiming}},a={apply:function(n,i,r){if(e.timeline){var a=r[0];if(Infinity===a.duration)throw TypeError("Effect duration cannot be Infinity when used with Scroll Timelines");if(Infinity===a.iterations)throw TypeError("Effect iterations cannot be Infinity when used with Scroll Timelines")}e.specifiedTiming&&n.apply(t,[e.specifiedTiming]),n.apply(t,r),e.specifiedTiming=null}},o=new Proxy(t,{get:function(e,n){var i=e[n];return"function"==typeof i?i.bind(t):i},set:function(e,t,n){return e[t]=n,!0}});return o.getComputedTiming=new Proxy(t.getComputedTiming,i),o.getTiming=new Proxy(t.getTiming,r),o.updateTiming=new Proxy(t.updateTiming,a),o}(e)),e.effect):e.animation.effect},set:function(e){X.get(this).animation.effect=e,details.effect=null}},{key:"timeline",get:function(){var e=X.get(this);return e.timeline||e.animation.timeline},set:function(e){var t=this.timeline;if(t!=e){var n=this.playState,i=this.currentTime,r=X.get(this),a=K(r),o=a>0?_(r,i)/a:0,l=t instanceof v,s=e instanceof v;r.resetCurrentTimeOnResume=!1;var u=this.pending;if(l&&d(r.timeline,r.animation),s){r.timeline=e,z(r);var c=r.animation.playbackRate>=0?0:K(r);switch(n){case"running":case"finished":r.startTime=c,p(r.timeline,r.animation,G.bind(this));break;case"paused":r.resetCurrentTimeOnResume=!0,r.startTime=null,r.holdTime=_(r,CSS.percent(100*o));break;default:r.holdTime=null,r.startTime=null}return u&&(r.readyPromise&&"resolved"!=r.readyPromise.state||W(r),r.pendingTask="paused"==n?"pause":"play"),null!==r.startTime&&(r.holdTime=null),void F(r,!1,!1)}if(r.animation.timeline!=e)throw TypeError("Unsupported timeline: "+e);if(d(r.timeline,r.animation),r.timeline=null,l)switch(null!==i&&(r.animation.currentTime=o*K(r)),n){case"paused":r.animation.pause();break;case"running":case"finished":r.animation.play()}}}},{key:"startTime",get:function(){var e=X.get(this);return e.timeline?L(e,e.startTime):e.animation.startTime},set:function(e){var t=X.get(this);if(e=_(t,e),t.timeline){null==_(t,t.timeline.currentTime)&&null!=t.startTime&&(t.holdTime=null,q(t));var n=_(t,this.currentTime);z(t),t.startTime=e,t.resetCurrentTimeOnResume=!1,t.holdTime=null!==t.startTime&&0!=t.animation.playbackRate?null:n,t.pendingTask&&(t.pendingTask=null,t.readyPromise.resolve(this)),F(t,!0,!1),q(t)}else t.animation.startTime=e}},{key:"currentTime",get:function(){var e=X.get(this);return e.timeline?L(e,null!=e.holdTime?e.holdTime:U(e)):e.animation.currentTime},set:function(e){var t=X.get(this);if(e=_(t,e),t.timeline&&null!=e){var n=t.timeline.phase;null!==t.holdTime||null===t.startTime||"inactive"==n||0==t.animation.playbackRate?t.holdTime=e:t.startTime=H(t,e),t.resetCurrentTimeOnResume=!1,"inactive"==n&&(t.startTime=null),t.previousCurrentTime=null,"pause"==t.pendingTask&&(t.holdTime=e,z(t),t.startTime=null,t.pendingTask=null,t.readyPromise.resolve(this)),F(t,!0,!1)}else t.animation.currentTime=e}},{key:"playbackRate",get:function(){return X.get(this).animation.playbackRate},set:function(e){var t=X.get(this);if(t.timeline){t.pendingPlaybackRate=null;var n=this.currentTime;t.animation.playbackRate=e,null!==n&&(this.currentTime=n)}else t.animation.playbackRate=e}},{key:"playState",get:function(){var e=X.get(this);if(!e.timeline)return e.animation.playState;var t=_(e,this.currentTime);if(null===t&&null===e.startTime&&null==e.pendingTask)return"idle";if("pause"==e.pendingTask||null===e.startTime&&"play"!=e.pendingTask)return"paused";if(null!=t){if(e.animation.playbackRate>0&&t>=K(e))return"finished";if(e.animation.playbackRate<0&&t<=0)return"finished"}return"running"}},{key:"replaceState",get:function(){return X.get(this).animation.pending}},{key:"pending",get:function(){var e=X.get(this);return e.timeline?!!e.readyPromise&&"pending"==e.readyPromise.state:e.animation.pending}},{key:"id",get:function(){return X.get(this).animation.id}},{key:"onfinish",get:function(){return X.get(this).animation.onfinish},set:function(e){X.get(this).animation.onfinish=e}},{key:"oncancel",get:function(){return X.get(this).animation.oncancel},set:function(e){X.get(this).animation.oncancel=e}},{key:"onremove",get:function(){return X.get(this).animation.onremove},set:function(e){X.get(this).animation.onremove=e}},{key:"finished",get:function(){var e=X.get(this);return e.timeline?(e.finishedPromise||(e.finishedPromise=new O),e.finishedPromise.promise):e.animation.finished}},{key:"ready",get:function(){var e=X.get(this);return e.timeline?(e.readyPromise||(e.readyPromise=new O,e.readyPromise.resolve(this)),e.readyPromise.promise):e.animation.ready}}]),e}();function Z(e,t){if(!e)return null;var n=e.split(" ");if(!N.includes(n[0])||2==n.length&&!n[1].endsWith("%"))throw TypeError("Invalid animation delay");var i=t;if(2==n.length){var r=parseFloat(n[1]);if(Number.isNaN(r))throw TypeError('"'+n[1]+'" is not a valid percentage for animation delay');i=CSS.percent(r)}return{name:n[0],offset:i}}function ee(){return{name:"cover",offset:CSS.percent(0)}}function te(){return{name:"cover",offset:CSS.percent(100)}}function ne(e){var t=ie(e["animation-time-range"]);return e["animation-delay"]&&(t.start=Z(e["animation-delay"],ee().offset)),e["animation-end-delay"]&&(t.end=Z(e["animation-end-delay"],te().offset)),t}function ie(e){var t={start:ee(),end:te()};if(!e)return t;var n=e.split(" "),i=[],r=[];if(n.forEach(function(e){e.endsWith("%")?r.push(parseFloat(e)):i.push(e)}),i.length>2||r.length>2||1==r.length)throw TypeError("Invalid time range");return i.length&&(t.start.name=i[0],t.end.name=i.length>1?i[1]:i[0]),r.length>1&&(t.start.offset=CSS.percent(r[0]),t.end.offset=CSS.percent(r[1])),t}function re(e){for(var t=0;t=i.sheetSrc.length));)if(this.lookAhead("/*",i))for(;this.lookAhead("/*",i);)this.eatComment(i),this.eatWhitespace(i);else if(this.lookAhead("@scroll-timeline",i)){var r=this.parseScrollTimeline(i).scrollTimeline;t&&this.scrollTimelineOptions.set(r.name,r)}else{var a=this.parseQualifiedRule(i);if(!a)continue;t?this.parseKeyframesAndSaveNameMapping(a,i):this.handleScrollTimelineProps(a,i)}return i.sheetSrc},t.getAnimationTimelineOptions=function(e,t){for(var n=this.cssRulesWithTimelineName.length-1;n>=0;n--){var i=this.cssRulesWithTimelineName[n];if(t.matches(i.selector)&&(!i["animation-name"]||i["animation-name"]==e))return{"animation-timeline":i["animation-timeline"],"animation-delay":i["animation-delay"],"animation-end-delay":i["animation-end-delay"],"animation-time-range":i["animation-time-range"]}}return null},t.getSourceElement=function(e){var t=ae.SOURCE_ELEMENT.exec(e);return t?document.getElementById(t[1]):"auto"===e?document.scrollingElement:null},t.getScrollTimelineOptions=function(e){var t=this.scrollTimelineOptions.get(e);if(null!=t&&t.source){var i=this.getSourceElement(t.source);return n({},i?{source:i}:{},"auto"!=t.orientation?{orientation:t.orientation}:{})}return null},t.findPreviousSiblingOrAncestorMatchingSelector=function(e,t){for(var n=e;n;){if(n.matches(t))return n;n=n.previousElementSibling||n.parentElement}return null},t.getViewTimelineOptions=function(e,t){for(var n=this.subjectSelectorToViewTimeline.length-1;n>=0;n--){var i=this.subjectSelectorToViewTimeline[n];if(i.name==e){var r=this.findPreviousSiblingOrAncestorMatchingSelector(t,i.selector);if(r)return{subject:r,axis:i.axis,inset:i.inset}}}return null},t.parseScrollTimeline=function(e){var t=e.index;this.assertString(e,"@scroll-timeline"),this.eatWhitespace(e);var n=this.parseIdentifier(e);this.eatWhitespace(e),this.assertString(e,"{"),this.eatWhitespace(e);for(var i={name:n,source:"auto",orientation:void 0};"}"!==this.peek(e);){var r=this.parseIdentifier(e);this.eatWhitespace(e),this.assertString(e,":"),this.eatWhitespace(e),i[r]=this.removeEnclosingDoubleQuotes(this.eatUntil(";",e)),this.assertString(e,";"),this.eatWhitespace(e)}this.assertString(e,"}");var a=e.index;return this.eatWhitespace(e),{scrollTimeline:i,startIndex:t,endIndex:a}},t.handleScrollTimelineProps=function(e,t){var n=this;if(!e.selector.includes("@keyframes")){var i=e.block.contents.includes("animation-name:"),r=e.block.contents.includes("animation-timeline:"),a=e.block.contents.includes("animation:");this.saveSubjectSelectorToViewTimeline(e);var o=[],l=[];r&&(o=this.extractMatches(e.block.contents,ae.ANIMATION_TIMELINE)),i&&(l=this.extractMatches(e.block.contents,ae.ANIMATION_NAME)),r&&i||a&&this.extractMatches(e.block.contents,ae.ANIMATION).forEach(function(i){var a=n.extractAnimationName(i),s=n.extractTimelineName(i);a&&l.push(a),s&&(o.push(s),e.block.contents=e.block.contents.replace(s," ".repeat(s.length)),n.replacePart(e.block.startIndex,e.block.endIndex,e.block.contents,t)),(s||r)&&(n.hasDuration(i)||(e.block.contents=e.block.contents.replace("animation:","animation: 1s "),n.replacePart(e.block.startIndex,e.block.endIndex,e.block.contents,t)))}),this.saveRelationInList(e,o,l)}},t.saveSubjectSelectorToViewTimeline=function(e){var t=e.block.contents.includes("view-timeline:"),n=e.block.contents.includes("view-timeline-name:"),i=e.block.contents.includes("view-timeline-axis:");if(t||n){var r={selector:e.selector,name:"",axis:"block"};if(t){var a=this.extractMatches(e.block.contents,ae.VIEW_TIMELINE,separator=" ");1==a.length?r.name=a[0]:2==a.length&&(oe.includes(a[0])?(r.axis=a[0],r.name=a[1]):(r.axis=a[1],r.name=a[0]))}if(n){var o=this.extractMatches(e.block.contents,ae.VIEW_TIMELINE_NAME);r.name=o[0]}if(i){var l=this.extractMatches(e.block.contents,ae.VIEW_TIMELINE_AXIS);oe.includes(l[0])&&(r.axis=l[0])}this.subjectSelectorToViewTimeline.push(r)}},t.hasDuration=function(e){return e.split(" ").filter(function(e){return ae.TIME.exec(e)}).length>=1},t.saveRelationInList=function(e,t,i){var r=e.block.contents.includes("animation-delay:"),a=e.block.contents.includes("animation-end-delay:"),o=e.block.contents.includes("animation-time-range:"),l=[],s=[],u=[];r&&(l=this.extractMatches(e.block.contents,ae.ANIMATION_DELAY)),a&&(s=this.extractMatches(e.block.contents,ae.ANIMATION_END_DELAY)),o&&(u=this.extractMatches(e.block.contents,ae.ANIMATION_TIME_RANGE));for(var c=Math.max(t.length,i.length,l.length,s.length,u.length),m=0;m0&&n.keyframeNamesSelectors.set(e,i)})}},t.replaceKeyframesAndGetMapping=function(e,t){function n(e){return N.some(function(t){return e.startsWith(t)})}var i=e.block.contents,r=function(e){for(var t=0,n=-1,i=[],r=0;r=t&&(i.index=e+n.length+(i.index-t))},t.eatComment=function(e){this.assertString(e,"/*"),this.eatUntil("*/",e,!0),this.assertString(e,"*/")},t.eatBlock=function(e){var t=e.index;this.assertString(e,"{");for(var n=1;0!=n;)this.lookAhead("/*",e)?this.eatComment(e):("{"===e.sheetSrc[e.index]?n++:"}"===e.sheetSrc[e.index]&&n--,this.advance(e));var i=e.index;return{startIndex:t,endIndex:i,contents:e.sheetSrc.slice(t,i)}},t.advance=function(e){if(e.index++,e.index>e.sheetSrc.length)throw this.parseError(e,"Advanced beyond the end")},t.eatUntil=function(e,t,n){void 0===n&&(n=!1);for(var i=t.index;!this.lookAhead(e,t);)this.advance(t);return n&&(t.sheetSrc=t.sheetSrc.slice(0,i)+" ".repeat(t.index-i)+t.sheetSrc.slice(t.index)),t.sheetSrc.slice(i,t.index)},t.parseSelector=function(e){var t=e.index;if(this.eatUntil("{",e),t===e.index)throw Error("Empty selector");return e.sheetSrc.slice(t,e.index)},t.eatWhitespace=function(e){ae.WHITE_SPACE.lastIndex=e.index;var t=ae.WHITE_SPACE.exec(e.sheetSrc);t&&(e.index+=t[0].length)},t.lookAhead=function(e,t){return t.sheetSrc.substr(t.index,e.length)==e},t.peek=function(e){return e.sheetSrc[e.index]},t.extractMatches=function(e,t,n){return void 0===n&&(n=","),t.exec(e)[1].trim().split(n).map(function(e){return e.trim()})},e}());function se(e,t,n,i,r,a){return I(E(e,t,n,i,r),a,E("cover",t,n,i,r))}if(CSS.supports("animation-timeline: works")||(function(){function e(e){if(0!==e.innerHTML.trim().length){var t=le.transpileStyleSheet(e.innerHTML,!0);t=le.transpileStyleSheet(t,!1),e.innerHTML=t}}new MutationObserver(function(t){for(var n,i=l(t);!(n=i()).done;)for(var r,a=l(n.value.addedNodes);!(r=a()).done;){var o=r.value;o instanceof HTMLStyleElement&&e(o)}}).observe(document.documentElement,{childList:!0,subtree:!0}),document.querySelectorAll("style").forEach(function(t){return e(t)}),document.querySelectorAll("link").forEach(function(e){})}(),window.addEventListener("animationstart",function(e){e.target.getAnimations().filter(function(t){return t.animationName===e.animationName}).forEach(function(t){var n=function(e,t,n){var i=le.getAnimationTimelineOptions(t,n),r=i["animation-timeline"];if(!r)return null;var a=le.getScrollTimelineOptions(r)||le.getViewTimelineOptions(r,n);return a?(a.subject&&function(e,t){var n=k(t.subject),i=t.axis||t.orientation,r=le.keyframeNamesSelectors.get(e.animationName);if(r&&r.size){var a=[];e.effect.getKeyframes().forEach(function(e){var o=function(e,r){for(var a,o=null,s=l(e);!(a=s()).done;){var u=a.value,c=u[1];if(u[0]==100*r.offset){if("from"==c)o=0;else if("to"==c)o=100;else{var m=c.split(" ");o=1==m.length?parseFloat(m[0]):100*se(m[0],n,t.subject,i,t.inset,CSS.percent(parseFloat(m[1])))}break}}return o}(r,e);null!==o&&o>=0&&o<=100&&(e.offset=o/100,a.push(e))});var o=a.sort(function(e,t){return e.offsett.offset?1:0});e.effect.setKeyframes(o),le.keyframeNamesSelectors.set(e.animationName,null)}}(e,a),{timeline:a.source?new v(a):new P(a),animOptions:i}):null}(t,t.animationName,e.target);if(!n.timeline||t instanceof J)t.timeline=n.timeline;else{var i=new J(t,n.timeline,n.animOptions);t.pause(),i.play()}})})),[].concat(document.styleSheets).filter(function(e){return null!==e.href}).length&&console.warn("Non-Inline StyleSheets detected: ScrollTimeline polyfill currently only supports inline styles within style tags"),!Reflect.defineProperty(window,"ScrollTimeline",{value:v}))throw Error("Error installing ScrollTimeline polyfill: could not attach ScrollTimeline to window");if(!Reflect.defineProperty(window,"ViewTimeline",{value:P}))throw Error("Error installing ViewTimeline polyfill: could not attach ViewTimeline to window");if(!Reflect.defineProperty(Element.prototype,"animate",{value:function(e,t){var n=t.timeline;n instanceof v&&delete t.timeline;var i=function(e,t){if(t in e){var n=e[t];return"number"!=typeof n?(delete e[t],n):null}},r=function(e,t){t&&(t.phase&&(e.name=t.phase),t.percent&&(e.offset=t.percent))},a=i(t,"delay"),o=i(t,"endDelay"),l=R.apply(this,[e,t]),s=new J(l,n);if(n instanceof v){if(l.pause(),n instanceof ViewTimeline){var u=X.get(s);u.timeRange=ie(t.timeRange),r(u.timeRange.start,a),r(u.timeRange.end,o)}s.play()}return s}}))throw Error("Error installing ScrollTimeline polyfill: could not attach WAAPI's animate to DOM Element");if(!Reflect.defineProperty(window,"Animation",{value:J}))throw Error("Error installing Animation constructor.");if(!Reflect.defineProperty(Element.prototype,"getAnimations",{value:function(e){return re(A.apply(this,[e]))}}))throw Error("Error installing ScrollTimeline polyfill: could not attach WAAPI's getAnimations to DOM Element");if(!Reflect.defineProperty(document,"getAnimations",{value:function(e){return re(M.apply(this,[e]))}}))throw Error("Error installing ScrollTimeline polyfill: could not attach WAAPI's getAnimations to document")}(); //# sourceMappingURL=scroll-timeline.js.map diff --git a/dist/scroll-timeline.js.map b/dist/scroll-timeline.js.map index 19c72428..a1c3cf09 100644 --- a/dist/scroll-timeline.js.map +++ b/dist/scroll-timeline.js.map @@ -1 +1 @@ -{"version":3,"file":"scroll-timeline.js","sources":["../src/proxy-cssom.js","../src/scroll-timeline-base.js","../src/proxy-animation.js","../src/scroll-timeline-css-parser.js","../src/scroll-timeline-css.js","../src/index.js"],"sourcesContent":["// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nexport function installCSSOM() {\n // Object for storing details associated with an object which are to be kept\n // private. This approach allows the constructed objects to more closely\n // resemble their native counterparts when inspected.\n let privateDetails = new WeakMap();\n\n function displayUnit(unit) {\n switch(unit) {\n case 'percent':\n return '%';\n case 'number':\n return '';\n default:\n return unit.toLowerCase();\n }\n }\n\n function toCssUnitValue(v) {\n if (typeof v === 'number')\n return new CSSUnitValue(v, 'number');\n return v;\n }\n\n function toCssNumericArray(values) {\n const result = [];\n for (let i = 0; i < values.length; i++) {\n result[i] = toCssUnitValue(values[i]);\n }\n return result;\n }\n\n class MathOperation {\n constructor(values, operator, opt_name, opt_delimiter) {\n privateDetails.set(this, {\n values: toCssNumericArray(values),\n operator: operator,\n name: opt_name || operator,\n delimiter: opt_delimiter || ', '\n });\n }\n\n get operator() {\n return privateDetails.get(this).operator;\n }\n\n get values() {\n return privateDetails.get(this).values;\n }\n\n toString() {\n const details = privateDetails.get(this);\n return `${details.name}(${details.values.join(details.delimiter)})`;\n }\n }\n\n const cssOMTypes = {\n 'CSSUnitValue': class {\n constructor(value, unit) {\n privateDetails.set(this, {\n value: value,\n unit: unit\n });\n }\n\n get value() {\n return privateDetails.get(this).value;\n }\n\n set value(value) {\n privateDetails.get(this).value = value;\n }\n\n get unit() {\n return privateDetails.get(this).unit;\n }\n\n toString() {\n const details = privateDetails.get(this);\n return `${details.value}${displayUnit(details.unit)}`;\n }\n },\n\n 'CSSKeywordValue': class {\n constructor(value) {\n this.value = value;\n }\n\n toString() {\n return this.value.toString();\n }\n },\n\n 'CSSMathSum': class extends MathOperation {\n constructor(values) {\n super(arguments, 'sum', 'calc', ' + ');\n }\n },\n\n 'CSSMathProduct': class extends MathOperation {\n constructor(values) {\n super(arguments, 'product', 'calc', ' * ');\n }\n },\n\n 'CSSMathNegate': class extends MathOperation {\n constructor(values) {\n super([arguments[0]], 'negate', '-');\n }\n },\n\n 'CSSMathNegate': class extends MathOperation {\n constructor(values) {\n super([1, arguments[0]], 'invert', 'calc', ' / ');\n }\n },\n\n 'CSSMathMax': class extends MathOperation {\n constructor() {\n super(arguments, 'max');\n }\n },\n\n 'CSSMathMin': class extends MathOperation {\n constructor() {\n super(arguments, 'min');\n }\n }\n };\n\n if (!window.CSS) {\n if (!Reflect.defineProperty(window, 'CSS', { value: {} }))\n throw Error(`Error installing CSSOM support`);\n }\n\n if (!window.CSSUnitValue) {\n [\n 'number',\n 'percent',\n // Length units\n 'em',\n 'ex',\n 'px',\n 'cm',\n 'mm',\n 'in',\n 'pt',\n 'pc', // Picas\n 'Q', // Quarter millimeter\n 'vw',\n 'vh',\n 'vmin',\n 'vmax',\n 'rems',\n \"ch\",\n // Angle units\n 'deg',\n 'rad',\n 'grad',\n 'turn',\n // Time units\n 'ms',\n 's',\n 'Hz',\n 'kHz',\n // Resolution\n 'dppx',\n 'dpi',\n 'dpcm',\n // Other units\n \"fr\"\n ].forEach((name) => {\n const fn = (value) => {\n return new CSSUnitValue(value, name);\n };\n if (!Reflect.defineProperty(CSS, name, { value: fn }))\n throw Error(`Error installing CSS.${name}`);\n });\n }\n\n for (let type in cssOMTypes) {\n if (type in window)\n continue;\n if (!Reflect.defineProperty(window, type, { value: cssOMTypes[type] }))\n throw Error(`Error installing CSSOM support for ${type}`);\n }\n}\n","// Copyright 2019 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { parseLength } from \"./utils\";\n\nimport { installCSSOM } from \"./proxy-cssom.js\";\ninstallCSSOM();\n\nconst AUTO = new CSSKeywordValue(\"auto\");\n\nlet scrollTimelineOptions = new WeakMap();\nlet extensionScrollOffsetFunctions = [];\n\nfunction scrollEventSource(source) {\n if (source === document.scrollingElement) return document;\n return source;\n}\n\n/**\n * Updates the currentTime for all Web Animation instanced attached to a ScrollTimeline instance\n * @param scrollTimelineInstance {ScrollTimeline}\n */\nfunction updateInternal(scrollTimelineInstance) {\n validateSource(scrollTimelineInstance);\n const details = scrollTimelineOptions.get(scrollTimelineInstance);\n let animations = details.animations;\n if (animations.length === 0) return;\n let timelineTime = scrollTimelineInstance.currentTime;\n for (let i = 0; i < animations.length; i++) {\n animations[i].tickAnimation(timelineTime);\n }\n}\n\n/**\n * Calculates a scroll offset that corrects for writing modes, text direction\n * and a logical orientation.\n * @param scrollTimeline {ScrollTimeline}\n * @param orientation {String}\n * @returns {Number}\n */\nfunction directionAwareScrollOffset(source, orientation) {\n if (!source)\n return null;\n\n const style = getComputedStyle(source);\n // All writing modes are vertical except for horizontal-tb.\n // TODO: sideways-lr should flow bottom to top, but is currently unsupported\n // in Chrome.\n // http://drafts.csswg.org/css-writing-modes-4/#block-flow\n const horizontalWritingMode = style.writingMode == 'horizontal-tb';\n let currentScrollOffset = source.scrollTop;\n if (orientation == 'horizontal' ||\n (orientation == 'inline' && horizontalWritingMode) ||\n (orientation == 'block' && !horizontalWritingMode)) {\n // Negative values are reported for scrollLeft when the inline text\n // direction is right to left or for vertical text with a right to left\n // block flow. This is a consequence of shifting the scroll origin due to\n // changes in the overflow direction.\n // http://drafts.csswg.org/cssom-view/#overflow-directions.\n currentScrollOffset = Math.abs(source.scrollLeft);\n }\n return currentScrollOffset;\n}\n\n/**\n * Determines target effect end based on animation duration, iterations count and start and end delays\n * returned value should always be positive\n * @param options {Animation} animation\n * @returns {number}\n */\nexport function calculateTargetEffectEnd(animation) {\n return animation.effect.getComputedTiming().activeDuration;\n}\n\n/**\n * Calculates scroll offset based on orientation and source geometry\n * @param source {DOMElement}\n * @param orientation {String}\n * @returns {number}\n */\nexport function calculateMaxScrollOffset(source, orientation) {\n // Only one horizontal writing mode: horizontal-tb. All other writing modes\n // flow vertically.\n const horizontalWritingMode =\n getComputedStyle(source).writingMode == 'horizontal-tb';\n if (orientation === \"block\")\n orientation = horizontalWritingMode ? \"vertical\" : \"horizontal\";\n else if (orientation === \"inline\")\n orientation = horizontalWritingMode ? \"horizontal\" : \"vertical\";\n if (orientation === \"vertical\")\n return source.scrollHeight - source.clientHeight;\n else if (orientation === \"horizontal\")\n return source.scrollWidth - source.clientWidth;\n}\n\nfunction resolvePx(cssValue, resolvedLength) {\n if (cssValue instanceof CSSUnitValue) {\n if (cssValue.unit == \"percent\")\n return cssValue.value * resolvedLength / 100;\n else if (cssValue.unit == \"px\")\n return cssValue.value;\n else\n throw TypeError(\"Unhandled unit type \" + cssValue.unit);\n } else if (cssValue instanceof CSSMathSum) {\n let total = 0;\n for (let value of cssValue.values) {\n total += resolvePx(value, resolvedLength);\n }\n return total;\n }\n throw TypeError(\"Unsupported value type: \" + typeof(cssValue));\n}\n\n// Detects if the cached source is obsolete, and updates if required\n// to ensure the new source has a scroll listener.\nfunction validateSource(timeline) {\n if (!(timeline instanceof ViewTimeline))\n return;\n\n const node = timeline.subject;\n if (!node) {\n updateSource(timeline, null);\n return;\n }\n\n const display = getComputedStyle(node).display;\n if (display == 'none') {\n updateSource(timeline, null);\n return;\n }\n\n const source = getScrollParent(node);\n updateSource(timeline, source);\n}\n\nfunction updateSource(timeline, source) {\n const details = scrollTimelineOptions.get(timeline);\n const oldSource = details.source;\n const oldScrollListener = details.scrollListener;\n if (oldSource == source)\n return;\n\n if (oldSource && oldScrollListener) {\n scrollEventSource(oldSource).removeEventListener(\"scroll\",\n oldScrollListener);\n }\n scrollTimelineOptions.get(timeline).source = source;\n if (source) {\n const listener = () => {\n updateInternal(timeline);\n };\n scrollEventSource(source).addEventListener(\"scroll\", listener);\n details.scrollListener = listener;\n }\n}\n\n/**\n * Removes a Web Animation instance from ScrollTimeline\n * @param scrollTimeline {ScrollTimeline}\n * @param animation {Animation}\n * @param options {Object}\n */\nexport function removeAnimation(scrollTimeline, animation) {\n let animations = scrollTimelineOptions.get(scrollTimeline).animations;\n for (let i = 0; i < animations.length; i++) {\n if (animations[i].animation == animation) {\n animations.splice(i, 1);\n }\n }\n}\n\n/**\n * Attaches a Web Animation instance to ScrollTimeline.\n * @param scrollTimeline {ScrollTimeline}\n * @param animation {Animation}\n * @param tickAnimation {function(number)}\n */\nexport function addAnimation(scrollTimeline, animation, tickAnimation) {\n let animations = scrollTimelineOptions.get(scrollTimeline).animations;\n for (let i = 0; i < animations.length; i++) {\n if (animations[i].animation == animation)\n return;\n }\n\n animations.push({\n animation: animation,\n tickAnimation: tickAnimation\n });\n updateInternal(scrollTimeline);\n}\n\n// TODO: this is a private function used for unit testing add function\nexport function _getStlOptions(scrollTimeline) {\n return scrollTimelineOptions.get(scrollTimeline);\n}\n\nexport class ScrollTimeline {\n constructor(options) {\n scrollTimelineOptions.set(this, {\n source: null,\n orientation: \"block\",\n\n // View timeline\n subject: null,\n inset: (options ? options.inset : null),\n\n // Internal members\n animations: [],\n scrollListener: null\n });\n const source =\n options && options.source !== undefined ? options.source\n : document.scrollingElement;\n updateSource(this, source);\n this.orientation = (options && options.orientation) || \"block\";\n updateInternal(this);\n }\n\n set source(element) {\n updateSource(this, element);\n updateInternal(this);\n }\n\n get source() {\n return scrollTimelineOptions.get(this).source;\n }\n\n set orientation(orientation) {\n if (\n [\"block\", \"inline\", \"horizontal\", \"vertical\"].indexOf(orientation) === -1\n ) {\n throw TypeError(\"Invalid orientation\");\n }\n scrollTimelineOptions.get(this).orientation = orientation;\n updateInternal(this);\n }\n\n get orientation() {\n return scrollTimelineOptions.get(this).orientation;\n }\n\n get duration() {\n return CSS.percent(100);\n }\n\n get phase() {\n // Per https://drafts.csswg.org/scroll-animations-1/#phase-algorithm\n // Step 1\n const unresolved = null;\n // if source is null\n const container = this.source;\n if (!container) return \"inactive\";\n let scrollerStyle = getComputedStyle(container);\n\n // if source does not currently have a CSS layout box\n if (scrollerStyle.display == \"none\")\n return \"inactive\";\n\n // if source's layout box is not a scroll container\"\n if (container != document.scrollingElement &&\n (scrollerStyle.overflow == 'visible' ||\n scrollerStyle.overflow == \"clip\")) {\n return \"inactive\";\n }\n\n return \"active\"\n }\n\n get currentTime() {\n const unresolved = null;\n const container = this.source;\n if (!container) return unresolved;\n if (this.phase == 'inactive')\n return unresolved;\n\n const orientation = this.orientation;\n const scrollPos = directionAwareScrollOffset(container, orientation);\n const maxScrollPos = calculateMaxScrollOffset(container, orientation);\n\n return maxScrollPos > 0 ? CSS.percent(100 * scrollPos / maxScrollPos)\n : CSS.percent(100);\n }\n\n get __polyfill() {\n return true;\n }\n}\n\n// Methods for calculation of the containing block.\n// See https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block.\n\nfunction findClosestAncestor(element, matcher) {\n let candidate = element.parentElement;\n while(candidate != null) {\n if (matcher(candidate))\n return candidate;\n candidate = candidate.parentElement;\n }\n}\n\nfunction isBlockContainer(element) {\n const style = getComputedStyle(element);\n switch (style.display) {\n case 'block':\n case 'inline-block':\n case 'list-item':\n case 'table':\n case 'table-caption':\n case 'flow-root':\n case 'flex':\n case 'grid':\n return true;\n }\n\n return false;\n}\n\nfunction isFixedElementContainer(element) {\n const style = getComputedStyle(element);\n if (style.transform != 'none' || style.perspective != 'none')\n return true;\n\n if (style.willChange == 'transform' || style.willChange == 'perspective')\n return true;\n\n if (style.filter != 'none' || style.willChange == 'filter')\n return true;\n\n if (style.backdropFilter != 'none')\n return true;\n\n return false;\n}\n\nfunction isAbsoluteElementContainer(element) {\n const style = getComputedStyle(element);\n if (style.position != 'static')\n return true;\n\n return isFixedElementContainer(element);\n}\n\nfunction getContainingBlock(element) {\n switch (getComputedStyle(element).position) {\n case 'static':\n case 'relative':\n case 'sticky':\n return findClosestAncestor(element, isBlockContainer);\n\n case 'absolute':\n return findClosestAncestor(element, isAbsoluteElementContainer);\n\n case 'fixed':\n return findClosestAncestor(element, isFixedElementContainer);\n }\n}\n\nexport function getScrollParent(node) {\n if (!node)\n return undefined;\n\n while (node = getContainingBlock(node)) {\n const style = getComputedStyle(node);\n switch(style['overflow-x']) {\n case 'auto':\n case 'scroll':\n case 'hidden':\n // https://drafts.csswg.org/css-overflow-3/#overflow-propagation\n // The UA must apply the overflow from the root element to the viewport;\n // however, if the overflow is visible in both axis, then the overflow\n // of the first visible child body is applied instead.\n if (node == document.body &&\n getComputedStyle(document.scrollingElement).overflow == \"visible\")\n return document.scrollingElement;\n\n return node;\n }\n }\n return document.scrollingElement;\n}\n\n// ---- View timelines -----\n\n// Computes the scroll offsets corresponding to the [0, 100]% range for a\n// specific phase on a view timeline.\n// TODO: Track changes to determine when associated animations require their\n// timing to be renormalized.\nfunction range(timeline, phase) {\n const details = scrollTimelineOptions.get(timeline);\n\n const unresolved = null;\n if (timeline.phase === 'inactive')\n return unresolved;\n\n if (!(timeline instanceof ViewTimeline))\n return unresolved;\n\n // Compute the offset of the top-left corner of subject relative to\n // top-left corner of the container.\n const container = timeline.source;\n const target = timeline.subject;\n\n return calculateRange(phase, container, target, details.orientation, details.inset);\n}\n\nexport function calculateRange(phase, container, target, orientation, optionsInset) {\n let top = 0;\n let left = 0;\n let node = target;\n const ancestor = container.offsetParent;\n while (node && node != ancestor) {\n left += node.offsetLeft;\n top += node.offsetTop;\n node = node.offsetParent;\n }\n left -= container.offsetLeft + container.clientLeft;\n top -= container.offsetTop + container.clientTop;\n\n // Determine the view and container size based on the scroll direction.\n // The view position is the scroll position of the logical starting edge\n // of the view.\n const style = getComputedStyle(container);\n const horizontalWritingMode = style.writingMode == 'horizontal-tb';\n const rtl = style.direction == 'rtl' || style.writingMode == 'vertical-rl';\n let viewSize = undefined;\n let viewPos = undefined;\n let containerSize = undefined;\n if (orientation == 'horizontal' ||\n (orientation == 'inline' && horizontalWritingMode) ||\n (orientation == 'block' && !horizontalWritingMode)) {\n viewSize = target.clientWidth;\n viewPos = left;\n if (rtl)\n viewPos += container.scrollWidth - container.clientWidth;\n containerSize = container.clientWidth;\n } else {\n // TODO: support sideways-lr\n viewSize = target.clientHeight;\n viewPos = top;\n containerSize = container.clientHeight;\n }\n\n const inset = parseInset(optionsInset, containerSize);\n\n // Cover:\n // 0% progress represents the position at which the start border edge of the\n // element’s principal box coincides with the end edge of its view progress\n // visibility range.\n // 100% progress represents the position at which the end border edge of the\n // element’s principal box coincides with the start edge of its view progress\n // visibility range.\n const coverStartOffset = viewPos - containerSize + inset.end;\n const coverEndOffset = viewPos + viewSize - inset.start;\n\n // Contain:\n // The 0% progress represents the earlier of the following positions:\n // 1. The start border edge of the element’s principal box coincides with\n // the start edge of its view progress visibility range.\n // 2. The end border edge of the element’s principal box coincides with\n // the end edge of its view progress visibility range.\n // The 100% progress represents the greater of the following positions:\n // 1. The start border edge of the element’s principal box coincides with\n // the start edge of its view progress visibility range.\n // 2. The end border edge of the element’s principal box coincides with\n // the end edge of its view progress visibility range.\n const alignStartOffset = coverStartOffset + viewSize;\n const alignEndOffset = coverEndOffset - viewSize;\n const containStartOffset = Math.min(alignStartOffset, alignEndOffset);\n const containEndOffset = Math.max(alignStartOffset, alignEndOffset);\n\n // Enter and Exit bounds align with cover and contains bounds.\n\n let startOffset = undefined;\n let endOffset = undefined;\n\n switch(phase) {\n case 'cover':\n startOffset = coverStartOffset;\n endOffset = coverEndOffset;\n break;\n\n case 'contain':\n startOffset = containStartOffset;\n endOffset = containEndOffset;\n break;\n\n case 'enter':\n startOffset = coverStartOffset;\n endOffset = containStartOffset;\n break;\n\n case 'exit':\n startOffset = containEndOffset;\n endOffset = coverEndOffset;\n break;\n }\n\n return { start: startOffset, end: endOffset };\n}\n\nfunction parseInset(value, containerSize) {\n const inset = { start: 0, end: 0 };\n\n if(!value)\n return inset;\n\n const parts = value.split(' ');\n const insetParts = [];\n parts.forEach(part => {\n // TODO: Add support for relative lengths (e.g. em)\n if(part.endsWith(\"%\"))\n insetParts.push(containerSize / 100 * parseFloat(part));\n else if(part.endsWith(\"px\"))\n insetParts.push(parseFloat(part));\n else if(part === \"auto\")\n insetParts.push(0);\n });\n\n if (insetParts.length > 2) {\n throw TypeError(\"Invalid inset\");\n }\n\n if(insetParts.length == 1) {\n inset.start = insetParts[0];\n inset.end = insetParts[0];\n } else if(insetParts.length == 2) {\n inset.start = insetParts[0];\n inset.end = insetParts[1];\n }\n\n return inset;\n}\n\n// Calculate the fractional offset of a (phase, percent) pair relative to the\n// full cover range.\nexport function relativePosition(timeline, phase, percent) {\n const phaseRange = range(timeline, phase);\n const coverRange = range(timeline, 'cover');\n return calculateRelativePosition(phaseRange, percent, coverRange);\n}\n\nexport function calculateRelativePosition(phaseRange, percent, coverRange) {\n if (!phaseRange || !coverRange)\n return 0;\n\n const fraction = percent.value / 100;\n const offset =\n (phaseRange.end - phaseRange.start) * fraction + phaseRange.start;\n return (offset - coverRange.start) / (coverRange.end - coverRange.start);\n}\n\n// https://drafts.csswg.org/scroll-animations-1/rewrite#view-progress-timelines\nexport class ViewTimeline extends ScrollTimeline {\n // As specced, ViewTimeline has a subject and a source, but\n // ViewTimelineOptions only has source. Furthermore, there is a strict\n // relationship between subject and source (source is nearest scrollable\n // ancestor of subject).\n\n // Proceeding under the assumption that subject will be added to\n // ViewTimelineOptions. Inferring the source from the subject if not\n // explicitly set.\n constructor(options) {\n if (options.axis) {\n // Orientation called axis for a view timeline. Internally we can still\n // call this orientation, since the internal naming is not exposed.\n options.orientation = options.axis;\n }\n super(options);\n const details = scrollTimelineOptions.get(this);\n details.subject = options && options.subject ? options.subject : undefined;\n // TODO: Handle insets.\n\n validateSource(this);\n updateInternal(this);\n }\n\n get source() {\n validateSource(this);\n return scrollTimelineOptions.get(this).source;\n }\n\n set source(source) {\n throw new Error(\"Cannot set the source of a view timeline\");\n }\n\n get subject() {\n return scrollTimelineOptions.get(this).subject;\n }\n\n // The orientation is called \"axis\" for a view timeline.\n // Internally we still call it orientation.\n get axis() {\n return scrollTimelineOptions.get(this).orientation;\n }\n\n get currentTime() {\n const unresolved = null;\n const scrollPos = directionAwareScrollOffset(this.source, this.orientation);\n if (scrollPos == unresolved)\n return unresolved;\n\n const offsets = range(this, 'cover');\n if (!offsets)\n return unresolved;\n const progress =\n (scrollPos - offsets.start) / (offsets.end - offsets.start);\n\n return CSS.percent(100 * progress);\n }\n\n}\n","import {\n ScrollTimeline,\n addAnimation,\n removeAnimation,\n relativePosition\n} from \"./scroll-timeline-base\";\n\nconst nativeElementAnimate = window.Element.prototype.animate;\nconst nativeAnimation = window.Animation;\n\nexport const ANIMATION_DELAY_NAMES = ['enter', 'exit', 'cover', 'contain'];\n\nclass PromiseWrapper {\n constructor() {\n this.state = 'pending';\n this.nativeResolve = this.nativeReject = null;\n this.promise = new Promise((resolve, reject) => {\n this.nativeResolve = resolve;\n this.nativeReject = reject;\n });\n }\n resolve(value) {\n this.state = 'resolved';\n this.nativeResolve(value);\n }\n reject(reason) {\n this.state = 'rejected';\n // Do not report unhandled promise rejections.\n this.promise.catch(() => {});\n this.nativeReject(reason);\n }\n}\n\nfunction createReadyPromise(details) {\n details.readyPromise = new PromiseWrapper();\n // Trigger the pending task on the next animation frame.\n requestAnimationFrame(() => {\n const timelineTime = details.timeline.currentTime;\n if (timelineTime !== null)\n notifyReady(details);\n });\n}\n\nfunction createAbortError() {\n return new DOMException(\"The user aborted a request\", \"AbortError\");\n}\n\n// Converts a time from its internal representation to a percent. For a\n// monotonic timeline, time is reported as a double with implicit units of\n// milliseconds. For progress-based animations, times are reported as\n// percentages.\nfunction toCssNumberish(details, value) {\n if (value === null)\n return value;\n\n if (typeof value !== 'number') {\n throw new DOMException(\n `Unexpected value: ${value}. Cannot convert to CssNumberish`,\n \"InvalidStateError\");\n }\n\n const limit = effectEnd(details);\n const percent = limit ? 100 * value / limit : 0;\n return CSS.percent(percent);\n}\n\n// Covnerts a time to its internal representation. Progress-based animations\n// use times expressed as percentages. Each progress-based animation is backed\n// by a native animation with a document timeline in the polyfill. Thus, we\n// need to convert the timing from percent to milliseconds with implicit units.\nfunction fromCssNumberish(details, value) {\n if (!details.timeline) {\n // Document timeline\n if (value == null || typeof value === 'number')\n return value;\n\n const convertedTime = value.to('ms');\n if (convertTime)\n return convertedTime.value;\n\n throw new DOMException(\n \"CSSNumericValue must be either a number or a time value for \" +\n \"time based animations.\",\n \"InvalidStateError\");\n } else {\n // Scroll timeline.\n if (value === null)\n return value;\n\n if (value.unit === 'percent') {\n const duration = effectEnd(details);\n return value.value * duration / 100;\n }\n\n throw new DOMException(\n \"CSSNumericValue must be a percentage for progress based animations.\",\n \"NotSupportedError\");\n }\n}\n\nfunction normalizedTiming(details) {\n // Used normalized timing in the case of a progress-based animation or\n // specified timing with a document timeline. The normalizedTiming property\n // is initialized and cached when fetching the timing information.\n const timing = details.proxy.effect.getTiming();\n return details.normalizedTiming || timing;\n}\n\nfunction commitPendingPlay(details) {\n // https://drafts4.csswg.org/web-animations-2/#playing-an-animation-section\n // Refer to steps listed under \"Schedule a task to run ...\"\n\n const timelineTime = fromCssNumberish(details, details.timeline.currentTime);\n if (details.holdTime != null) {\n // A: If animation’s hold time is resolved,\n // A.1. Apply any pending playback rate on animation.\n // A.2. Let new start time be the result of evaluating:\n // ready time - hold time / playback rate for animation.\n // If the playback rate is zero, let new start time be simply ready\n // time.\n // A.3. Set the start time of animation to new start time.\n // A.4. If animation’s playback rate is not 0, make animation’s hold\n // time unresolved.\n applyPendingPlaybackRate(details);\n if (details.animation.playbackRate == 0) {\n details.startTime = timelineTime;\n } else {\n details.startTime\n = timelineTime -\n details.holdTime / details.animation.playbackRate;\n details.holdTime = null;\n }\n } else if (details.startTime !== null &&\n details.pendingPlaybackRate !== null) {\n // B: If animation’s start time is resolved and animation has a pending\n // playback rate,\n // B.1. Let current time to match be the result of evaluating:\n // (ready time - start time) × playback rate for animation.\n // B.2 Apply any pending playback rate on animation.\n // B.3 If animation’s playback rate is zero, let animation’s hold time\n // be current time to match.\n // B.4 Let new start time be the result of evaluating:\n // ready time - current time to match / playback rate\n // for animation.\n // If the playback rate is zero, let new start time be simply ready\n // time.\n // B.5 Set the start time of animation to new start time.\n const currentTimeToMatch =\n (timelineTime - details.startTime) * details.animation.playbackRate;\n applyPendingPlaybackRate(details);\n const playbackRate = details.animation.playbackRate;\n if (playbackRate == 0) {\n details.holdTime = null;\n details.startTime = timelineTime;\n } else {\n details.startTime = timelineTime - currentTimeToMatch / playbackRate;\n }\n }\n\n // 8.4 Resolve animation’s current ready promise with animation.\n if (details.readyPromise && details.readyPromise.state == 'pending')\n details.readyPromise.resolve(details.proxy);\n\n // 8.5 Run the procedure to update an animation’s finished state for\n // animation with the did seek flag set to false, and the\n // synchronously notify flag set to false.\n updateFinishedState(details, false, false);\n\n // Additional polyfill step to update the native animation's current time.\n syncCurrentTime(details);\n details.pendingTask = null;\n};\n\nfunction commitPendingPause(details) {\n // https://www.w3.org/TR/web-animations-1/#pausing-an-animation-section\n // Refer to steps listed under \"Schedule a task to run ...\"\n\n // 1. Let ready time be the time value of the timeline associated with\n // animation at the moment when the user agent completed processing\n // necessary to suspend playback of animation’s target effect.\n const readyTime = fromCssNumberish(details, details.timeline.currentTime);\n\n // 2. If animation’s start time is resolved and its hold time is not\n // resolved, let animation’s hold time be the result of evaluating\n // (ready time - start time) × playback rate.\n if (details.startTime != null && details.holdTime == null) {\n details.holdTime =\n (readyTime - details.startTime) * details.animation.playbackRate;\n }\n\n // 3. Apply any pending playback rate on animation.\n applyPendingPlaybackRate(details);\n\n // 4. Make animation’s start time unresolved.\n details.startTime = null;\n\n // 5. Resolve animation’s current ready promise with animation.\n details.readyPromise.resolve(details.proxy);\n\n // 6. Run the procedure to update an animation’s finished state for\n // animation with the did seek flag set to false, and the synchronously\n // notify flag set to false.\n updateFinishedState(details, false, false);\n\n // Additional polyfill step to update the native animation's current time.\n syncCurrentTime(details);\n details.pendingTask = null;\n};\n\nfunction commitFinishedNotification(details) {\n if (!details.finishedPromise || details.finishedPromise.state != 'pending')\n return;\n\n if (details.proxy.playState != 'finished')\n return;\n\n details.finishedPromise.resolve(details.proxy);\n\n details.animation.pause();\n\n // Event times are speced as doubles in web-animations-1.\n // Cannot dispatch a proxy to an event since the proxy is not a fully\n // transparent replacement. As a workaround, use a custom event and inject\n // the necessary getters.\n const finishedEvent =\n new CustomEvent('finish',\n { detail: {\n currentTime: details.proxy.currentTime,\n timelineTime: details.proxy.timeline.currentTime\n }});\n Object.defineProperty(finishedEvent, 'currentTime', {\n get: function() { return this.detail.currentTime; }\n });\n Object.defineProperty(finishedEvent, 'timelineTime', {\n get: function() { return this.detail.timelineTime; }\n });\n\n requestAnimationFrame(() => {\n queueMicrotask(() => {\n details.animation.dispatchEvent(finishedEvent);\n });\n });\n}\n\nfunction effectivePlaybackRate(details) {\n if (details.pendingPlaybackRate !== null)\n return details.pendingPlaybackRate;\n return details.animation.playbackRate;\n}\n\nfunction applyPendingPlaybackRate(details) {\n if (details.pendingPlaybackRate !== null) {\n details.animation.playbackRate = details.pendingPlaybackRate;\n details.pendingPlaybackRate = null;\n }\n}\n\nfunction calculateCurrentTime(details) {\n if (!details.timeline)\n return null;\n\n const timelineTime = fromCssNumberish(details, details.timeline.currentTime);\n if (timelineTime === null)\n return null;\n\n if (details.startTime === null)\n return null;\n\n let currentTime =\n (timelineTime - details.startTime) * details.animation.playbackRate;\n\n // Handle special case.\n if (currentTime == -0)\n currentTime = 0;\n\n return currentTime;\n}\n\nfunction calculateStartTime(details, currentTime) {\n if (!details.timeline)\n return null;\n\n const timelineTime = fromCssNumberish(details, details.timeline.currentTime);\n if (timelineTime == null)\n return null;\n\n return timelineTime - currentTime / details.animation.playbackRate;\n}\n\nfunction updateFinishedState(details, didSeek, synchronouslyNotify) {\n if (!details.timeline)\n return;\n\n // https://www.w3.org/TR/web-animations-1/#updating-the-finished-state\n // 1. Calculate the unconstrained current time. The dependency on did_seek is\n // required to accommodate timelines that may change direction. Without this\n // distinction, a once-finished animation would remain finished even when its\n // timeline progresses in the opposite direction.\n let unconstrainedCurrentTime =\n didSeek ? fromCssNumberish(details, details.proxy.currentTime)\n : calculateCurrentTime(details);\n\n // 2. Conditionally update the hold time.\n if (unconstrainedCurrentTime && details.startTime != null &&\n !details.proxy.pending) {\n // Can seek outside the bounds of the active effect. Set the hold time to\n // the unconstrained value of the current time in the event that this update\n // is the result of explicitly setting the current time and the new time\n // is out of bounds. An update due to a time tick should not snap the hold\n // value back to the boundary if previously set outside the normal effect\n // boundary. The value of previous current time is used to retain this\n // value.\n const playbackRate = effectivePlaybackRate(details);\n const upperBound = effectEnd(details);\n let boundary = details.previousCurrentTime;\n if (playbackRate > 0 && unconstrainedCurrentTime >= upperBound) {\n if (boundary === null || boundary < upperBound)\n boundary = upperBound;\n details.holdTime = didSeek ? unconstrainedCurrentTime : boundary;\n } else if (playbackRate < 0 && unconstrainedCurrentTime <= 0) {\n if (boundary == null || boundary > 0)\n boundary = 0;\n details.holdTime = didSeek ? unconstrainedCurrentTime : boundary;\n } else if (playbackRate != 0) {\n // Update start time and reset hold time.\n if (didSeek && details.holdTime !== null)\n details.startTime = calculateStartTime(details, details.holdTime);\n details.holdTime = null;\n }\n }\n\n // Additional step to ensure that the native animation has the same value for\n // current time as the proxy.\n syncCurrentTime(details);\n\n // 3. Set the previous current time.\n details.previousCurrentTime = fromCssNumberish(details,\n details.proxy.currentTime);\n\n // 4. Set the current finished state.\n const playState = details.proxy.playState;\n\n if (playState == 'finished') {\n if (!details.finishedPromise)\n details.finishedPromise = new PromiseWrapper();\n if (details.finishedPromise.state == 'pending') {\n // 5. Setup finished notification.\n if (synchronouslyNotify) {\n commitFinishedNotification(details);\n } else {\n Promise.resolve().then(() => {\n commitFinishedNotification(details);\n });\n }\n }\n } else {\n // 6. If not finished but the current finished promise is already resolved,\n // create a new promise.\n if (details.finishedPromise &&\n details.finishedPromise.state == 'resolved') {\n details.finishedPromise = new PromiseWrapper();\n }\n if (details.animation.playState != 'paused')\n details.animation.pause();\n }\n}\n\nfunction effectEnd(details) {\n // https://www.w3.org/TR/web-animations-1/#end-time\n const timing = normalizedTiming(details);\n const totalDuration =\n timing.delay + timing.endDelay + timing.iterations * timing.duration;\n\n return Math.max(0, totalDuration);\n}\n\nfunction hasActiveTimeline(details) {\n return !details.timeline || details.timeline.phase != 'inactive';\n}\n\nfunction syncCurrentTime(details) {\n if (!details.timeline)\n return;\n\n if (details.startTime !== null) {\n const timelineTime = details.timeline.currentTime;\n if (timelineTime == null)\n return;\n\n const timelineTimeMs = fromCssNumberish(details, timelineTime);\n\n setNativeCurrentTime(details,\n (timelineTimeMs - details.startTime) *\n details.animation.playbackRate);\n } else if (details.holdTime !== null) {\n setNativeCurrentTime(details, details.holdTime);\n }\n}\n\n// Sets the time of the underlying animation, nudging the time slightly if at\n// a scroll-timeline boundary to remain in the active phase.\nfunction setNativeCurrentTime(details, time) {\n const timeline = details.timeline;\n const playbackRate = details.animation.playbackRate;\n const atScrollTimelineBoundary =\n timeline.currentTime &&\n timeline.currentTime.value == (playbackRate < 0 ? 0 : 100);\n const delta =\n atScrollTimelineBoundary ? (playbackRate < 0 ? 0.001 : -0.001) : 0;\n\n details.animation.currentTime = time + delta;\n}\n\nfunction resetPendingTasks(details) {\n // https://www.w3.org/TR/web-animations-1/#reset-an-animations-pending-tasks\n\n // 1. If animation does not have a pending play task or a pending pause task,\n // abort this procedure.\n if (!details.pendingTask)\n return;\n\n // 2. If animation has a pending play task, cancel that task.\n // 3. If animation has a pending pause task, cancel that task.\n details.pendingTask = null;\n\n // 4. Apply any pending playback rate on animation.\n applyPendingPlaybackRate(details);\n\n // 5. Reject animation’s current ready promise with a DOMException named\n // \"AbortError\".\n details.readyPromise.reject(createAbortError());\n\n // 6. Let animation’s current ready promise be the result of creating a new\n // resolved Promise object.\n createReadyPromise(details);\n details.readyPromise.resolve(details.proxy);\n}\n\nfunction playInternal(details, autoRewind) {\n if (!details.timeline)\n return;\n\n // https://drafts.csswg.org/web-animations/#playing-an-animation-section.\n // 1. Let aborted pause be a boolean flag that is true if animation has a\n // pending pause task, and false otherwise.\n const abortedPause =\n details.proxy.playState == 'paused' && details.proxy.pending;\n\n // 2. Let has pending ready promise be a boolean flag that is initially\n // false.\n let hasPendingReadyPromise = false;\n\n // 3. Let seek time be a time value that is initially unresolved.\n let seekTime = null;\n\n // 4. Let has finite timeline be true if animation has an associated\n // timeline that is not monotonically increasing.\n // Note: this value will always true at this point in the polyfill.\n // Following steps are pruned based on the procedure for scroll\n // timelines.\n\n // 5. Perform the steps corresponding to the first matching condition from\n // the following, if any:\n //\n // 5a If animation’s effective playback rate > 0, the auto-rewind flag is\n // true and either animation’s:\n // current time is unresolved, or\n // current time < zero, or\n // current time >= target effect end,\n // 5a1. Set seek time to zero.\n //\n // 5b If animation’s effective playback rate < 0, the auto-rewind flag is\n // true and either animation’s:\n // current time is unresolved, or\n // current time ≤ zero, or\n // current time > target effect end,\n // 5b1. If associated effect end is positive infinity,\n // throw an \"InvalidStateError\" DOMException and abort these steps.\n // 5b2. Otherwise,\n // 5b2a Set seek time to animation's associated effect end.\n //\n // 5c If animation’s effective playback rate = 0 and animation’s current time\n // is unresolved,\n // 5c1. Set seek time to zero.\n let previousCurrentTime = fromCssNumberish(details,\n details.proxy.currentTime);\n\n // Resume of a paused animation after a timeline change snaps to the scroll\n // position.\n if (details.resetCurrentTimeOnResume) {\n previousCurrentTime = null;\n details.resetCurrentTimeOnResume = false;\n }\n\n const playbackRate = effectivePlaybackRate(details);\n const upperBound = effectEnd(details);\n if (playbackRate > 0 && autoRewind && (previousCurrentTime == null ||\n previousCurrentTime < 0 ||\n previousCurrentTime >= upperBound)) {\n seekTime = 0;\n } else if (playbackRate < 0 && autoRewind &&\n (previousCurrentTime == null || previousCurrentTime <= 0 ||\n previousCurrentTime > upperBound)) {\n if (upperBound == Infinity) {\n // Defer to native implementation to handle throwing the exception.\n details.animation.play();\n return;\n }\n seekTime = upperBound;\n } else if (playbackRate == 0 && previousCurrentTime == null) {\n seekTime = 0;\n }\n\n // 6. If seek time is resolved,\n // 6a1. Set animation's start time to seek time.\n // 6a2. Let animation's hold time be unresolved.\n // 6a3. Apply any pending playback rate on animation.\n if (seekTime != null) {\n details.startTime = seekTime;\n details.holdTime = null;\n applyPendingPlaybackRate(details);\n }\n\n // Additional step for the polyfill.\n addAnimation(details.timeline, details.animation,\n tickAnimation.bind(details.proxy));\n\n // 7. If animation's hold time is resolved, let its start time be\n // unresolved.\n if (details.holdTime) {\n details.startTime = null;\n }\n\n // 8. If animation has a pending play task or a pending pause task,\n // 8.1 Cancel that task.\n // 8.2 Set has pending ready promise to true.\n if (details.pendingTask) {\n details.pendingTask = null;\n hasPendingReadyPromise = true;\n }\n\n // 9. If the following three conditions are all satisfied:\n // animation’s hold time is unresolved, and\n // seek time is unresolved, and\n // aborted pause is false, and\n // animation does not have a pending playback rate,\n // abort this procedure.\n if (details.holdTime === null && seekTime === null &&\n !abortedPause && details.pendingPlaybackRate === null)\n return;\n\n // 10. If has pending ready promise is false, let animation’s current ready\n // promise be a new promise in the relevant Realm of animation.\n if (details.readyPromise && !hasPendingReadyPromise)\n details.readyPromise = null;\n\n // Additional polyfill step to ensure that the native animation has the\n // correct value for current time.\n syncCurrentTime(details);\n\n // 11. Schedule a task to run as soon as animation is ready.\n if (!details.readyPromise)\n createReadyPromise(details);\n details.pendingTask = 'play';\n\n // 12. Run the procedure to update an animation’s finished state for animation\n // with the did seek flag set to false, and the synchronously notify flag\n // set to false.\n updateFinishedState(details, /* seek */ false, /* synchronous */ false);\n}\n\nfunction tickAnimation(timelineTime) {\n const details = proxyAnimations.get(this);\n if (timelineTime == null) {\n // While the timeline is inactive, it's effect should not be applied.\n // To polyfill this behavior, we cancel the underlying animation.\n if (details.animation.playState != 'idle')\n details.animation.cancel();\n return;\n }\n\n if (details.pendingTask) {\n notifyReady(details);\n }\n\n const playState = this.playState;\n if (playState == 'running' || playState == 'finished') {\n const timelineTimeMs = fromCssNumberish(details, timelineTime);\n\n setNativeCurrentTime(\n details,\n (timelineTimeMs - fromCssNumberish(details, this.startTime)) *\n this.playbackRate);\n\n // Conditionally reset the hold time so that the finished state can be\n // properly recomputed.\n if (playState == 'finished' && effectivePlaybackRate(details) != 0)\n details.holdTime = null;\n updateFinishedState(details, false, false);\n }\n}\n\nfunction notifyReady(details) {\n if (details.pendingTask == 'pause') {\n commitPendingPause(details);\n } else if (details.pendingTask == 'play') {\n commitPendingPlay(details);\n }\n}\n\nfunction createProxyEffect(details) {\n const effect = details.animation.effect;\n const nativeUpdateTiming = effect.updateTiming;\n\n // Generic pass-through handler for any method or attribute that is not\n // explicitly overridden.\n const handler = {\n get: function(obj, prop) {\n const result = obj[prop];\n if (typeof result === 'function')\n return result.bind(effect);\n return result;\n },\n\n set: function(obj, prop, value) {\n obj[prop] = value;\n return true;\n }\n };\n // Override getComputedTiming to convert to percentages when using a\n // progress-based timeline.\n const getComputedTimingHandler = {\n apply: function(target) {\n // Ensure that the native animation is using normalized values.\n effect.getTiming();\n\n const timing = target.apply(effect);\n\n if (details.timeline) {\n const preConvertLocalTime = timing.localTime;\n timing.localTime = toCssNumberish(details, timing.localTime);\n timing.endTime = toCssNumberish(details, timing.endTime);\n timing.activeDuration =\n toCssNumberish(details, timing.activeDuration);\n const limit = effectEnd(details);\n const iteration_duration = timing.iterations ?\n (limit - timing.delay - timing.endDelay) / timing.iterations : 0;\n timing.duration = limit ?\n CSS.percent(100 * iteration_duration / limit) :\n CSS.percent(0);\n\n // Correct for inactive timeline.\n if (details.timeline.currentTime === undefined) {\n timing.localTime = null;\n }\n }\n return timing;\n }\n };\n // Override getTiming to normalize the timing. EffectEnd for the animation\n // align with the timeline duration.\n const getTimingHandler = {\n apply: function(target, thisArg) {\n // Arbitrary conversion of 100% to ms.\n const INTERNAL_DURATION_MS = 100000;\n\n if (details.specifiedTiming)\n return details.specifiedTiming;\n\n details.specifiedTiming = target.apply(effect);\n let timing = Object.assign({}, details.specifiedTiming);\n\n const timeline = details.timeline;\n let computedDelays = false;\n let startDelay;\n let endDelay;\n if (timeline instanceof ViewTimeline) {\n // Compute start and end delay to align with start and end times.\n // If times not specified use cover 0% to cover 100%.\n startDelay = fractionalStartDelay(details);\n endDelay = fractionalEndDelay(details);\n computedDelays = true;\n }\n\n let totalDuration;\n\n // Duration 'auto' case.\n if (timing.duration === null || timing.duration === 'auto' ||\n computedDelays) {\n if (details.timeline) {\n if (computedDelays) {\n timing.delay = startDelay * INTERNAL_DURATION_MS;\n timing.endDelay = endDelay * INTERNAL_DURATION_MS;\n } else {\n // TODO: start and end delay are specced as doubles and currently\n // ignored for a progress based animation. Support delay and endDelay\n // once CSSNumberish.\n timing.delay = 0;\n timing.endDelay = 0;\n }\n totalDuration = timing.iterations ? INTERNAL_DURATION_MS : 0;\n timing.duration = timing.iterations\n ? (totalDuration - timing.delay - timing.endDelay) /\n timing.iterations\n : 0;\n // Set the timing on the native animation to the normalized values\n // while preserving the specified timing.\n nativeUpdateTiming.apply(effect, [timing]);\n }\n }\n details.normalizedTiming = timing;\n return details.specifiedTiming;\n }\n };\n const updateTimingHandler = {\n apply: function(target, thisArg, argumentsList) {\n // Additional validation that is specific to scroll timelines.\n if (details.timeline) {\n const options = argumentsList[0];\n const duration = options.duration;\n if (duration === Infinity) {\n throw TypeError(\n \"Effect duration cannot be Infinity when used with Scroll \" +\n \"Timelines\");\n }\n const iterations = options.iterations;\n if (iterations === Infinity) {\n throw TypeError(\n \"Effect iterations cannot be Infinity when used with Scroll \" +\n \"Timelines\");\n }\n }\n\n // Apply updates on top of the original specified timing.\n if (details.specifiedTiming) {\n target.apply(effect, [details.specifiedTiming]);\n }\n target.apply(effect, argumentsList);\n // Force renormalization.\n details.specifiedTiming = null;\n }\n };\n const proxy = new Proxy(effect, handler);\n proxy.getComputedTiming = new Proxy(effect.getComputedTiming,\n getComputedTimingHandler);\n proxy.getTiming = new Proxy(effect.getTiming, getTimingHandler);\n proxy.updateTiming = new Proxy(effect.updateTiming, updateTimingHandler);\n return proxy;\n}\n\n// Computes the start delay as a fraction of the active cover range.\nfunction fractionalStartDelay(details) {\n if (!(details.timeline instanceof ViewTimeline))\n return 0;\n\n const startTime = details.timeRange.start;\n return relativePosition(details.timeline, startTime.name, startTime.offset);\n}\n\n// Computes the ends delay as a fraction of the active cover range.\nfunction fractionalEndDelay(details) {\n if (!(details.timeline instanceof ViewTimeline))\n return 0;\n\n const endTime = details.timeRange.end;\n return 1 - relativePosition(details.timeline, endTime.name, endTime.offset);\n}\n\n// Create an alternate Animation class which proxies API requests.\n// TODO: Create a full-fledged proxy so missing methods are automatically\n// fetched from Animation.\nlet proxyAnimations = new WeakMap();\n\nexport class ProxyAnimation {\n constructor(effect, timeline, animOptions={}) {\n const animation =\n (effect instanceof nativeAnimation) ?\n effect : new nativeAnimation(effect, animationTimeline);\n const isScrollAnimation = timeline instanceof ScrollTimeline;\n const animationTimeline = isScrollAnimation ? undefined : timeline;\n proxyAnimations.set(this, {\n animation: animation,\n timeline: isScrollAnimation ? timeline : undefined,\n playState: isScrollAnimation ? \"idle\" : null,\n readyPromise: null,\n finishedPromise: null,\n // Start and hold times are directly tracked in the proxy despite being\n // accessible via the animation so that direct manipulation of these\n // properties does not affect the play state of the underlying animation.\n // Note that any changes to these values require an update of current\n // time for the underlying animation to ensure that its hold time is set\n // to the correct position. These values are represented as floating point\n // numbers in milliseconds.\n startTime: null,\n holdTime: null,\n previousCurrentTime: null,\n // When changing the timeline on a paused animation, we defer updating the\n // start time until the animation resumes playing.\n resetCurrentTimeOnResume: false,\n // Calls to reverse and updatePlaybackRate set a pending rate that does\n // not immediately take effect. The value of this property is\n // inaccessible via the web animations API and therefore explicitly\n // tracked.\n pendingPlaybackRate: null,\n pendingTask: null,\n // Record the specified timing since it may be different than the timing\n // actually used for the animation. When fetching the timing, this value\n // will be returned, however, the native animation will use normalized\n // values.\n specifiedTiming: null,\n // The normalized timing has the corrected timing with the intrinsic\n // iteration duration resolved.\n normalizedTiming: null,\n // Effect proxy that performs the necessary time conversions when using a\n // progress-based timelines.\n effect: null,\n // Range when using a view-timeline. The default range is cover 0% to\n // 100%.\n timeRange: timeline instanceof ViewTimeline ? parseAnimationDelays(animOptions) : null,\n proxy: this\n });\n }\n\n // -----------------------------------------\n // Web animation API\n // -----------------------------------------\n\n get effect() {\n const details = proxyAnimations.get(this);\n if (!details.timeline)\n return details.animation.effect;\n\n // Proxy the effect to support timing conversions for progress based\n // animations.\n if (!details.effect)\n details.effect = createProxyEffect(details);\n\n return details.effect;\n }\n set effect(newEffect) {\n proxyAnimations.get(this).animation.effect = newEffect;\n // Reset proxy to force re-initialization the next time it is accessed.\n details.effect = null;\n }\n\n get timeline() {\n const details = proxyAnimations.get(this);\n // If we explicitly set a null timeline we will return the underlying\n // animation's timeline.\n return details.timeline || details.animation.timeline;\n }\n set timeline(newTimeline) {\n // https://drafts4.csswg.org/web-animations-2/#setting-the-timeline\n\n // 1. Let old timeline be the current timeline of animation, if any.\n // 2. If new timeline is the same object as old timeline, abort this\n // procedure.\n const oldTimeline = this.timeline;\n if (oldTimeline == newTimeline)\n return;\n\n // 3. Let previous play state be animation’s play state.\n const previousPlayState = this.playState;\n\n // 4. Let previous current time be the animation’s current time.\n const previousCurrentTime = this.currentTime;\n\n const details = proxyAnimations.get(this);\n const end = effectEnd(details);\n const progress =\n end > 0 ? fromCssNumberish(details, previousCurrentTime) / end : 0;\n\n // 5. Let from finite timeline be true if old timeline is not null and not\n // monotonically increasing.\n const fromScrollTimeline = (oldTimeline instanceof ScrollTimeline);\n\n // 6. Let to finite timeline be true if timeline is not null and not\n // monotonically increasing.\n const toScrollTimeline = (newTimeline instanceof ScrollTimeline);\n\n // 7. Let the timeline of animation be new timeline.\n // Cannot assume that the native implementation has mutable timeline\n // support. Deferring this step until we know that we are either\n // polyfilling, supporting natively, or throwing an error.\n\n // 8. Set the flag reset current time on resume to false.\n details.resetCurrentTimeOnResume = false;\n\n // Additional step required to track whether the animation was pending in\n // order to set up a new ready promise if needed.\n const pending = this.pending;\n\n if (fromScrollTimeline) {\n removeAnimation(details.timeline, details.animation);\n }\n\n // 9. Perform the steps corresponding to the first matching condition from\n // the following, if any:\n\n // If to finite timeline,\n if (toScrollTimeline) {\n // Deferred step 7.\n details.timeline = newTimeline;\n\n // 1. Apply any pending playback rate on animation\n applyPendingPlaybackRate(details);\n\n // 2. Let seek time be zero if playback rate >= 0, and animation’s\n // associated effect end otherwise.\n const seekTime =\n details.animation.playbackRate >= 0 ? 0 : effectEnd(details);\n\n // 3. Update the animation based on the first matching condition if any:\n switch (previousPlayState) {\n // If either of the following conditions are true:\n // * previous play state is running or,\n // * previous play state is finished\n // Set animation’s start time to seek time.\n case 'running':\n case 'finished':\n details.startTime = seekTime;\n // Additional polyfill step needed to associate the animation with\n // the scroll timeline.\n addAnimation(details.timeline, details.animation,\n tickAnimation.bind(this));\n break;\n\n // If previous play state is paused:\n // If previous current time is resolved:\n // * Set the flag reset current time on resume to true.\n // * Set start time to unresolved.\n // * Set hold time to previous current time.\n case 'paused':\n details.resetCurrentTimeOnResume = true;\n details.startTime = null;\n details.holdTime =\n fromCssNumberish(details, CSS.percent(100 * progress));\n break;\n\n // Oterwise\n default:\n details.holdTime = null;\n details.startTime = null;\n }\n\n // Additional steps required if the animation is pending as we need to\n // associate the pending promise with proxy animation.\n // Note: if the native promise already has an associated \"then\", we will\n // lose this association.\n if (pending) {\n if (!details.readyPromise ||\n details.readyPromise.state == 'resolved') {\n createReadyPromise(details);\n }\n if (previousPlayState == 'paused')\n details.pendingTask = 'pause';\n else\n details.pendingTask = 'play';\n }\n\n // Note that the following steps should apply when transitioning to\n // a monotonic timeline as well; however, we do not have a direct means\n // of applying the steps to the native animation.\n\n // 10. If the start time of animation is resolved, make animation’s hold\n // time unresolved. This step ensures that the finished play state of\n // animation is not “sticky” but is re-evaluated based on its updated\n // current time.\n if (details.startTime !== null)\n details.holdTime = null;\n\n // 11. Run the procedure to update an animation’s finished state for\n // animation with the did seek flag set to false, and the\n // synchronously notify flag set to false.\n updateFinishedState(details, false, false);\n return;\n }\n\n // To monotonic timeline.\n if (details.animation.timeline == newTimeline) {\n // Deferred step 7 from above. Clearing the proxy's timeline will\n // re-associate the proxy with the native animation.\n removeAnimation(details.timeline, details.animation);\n details.timeline = null;\n\n // If from finite timeline and previous current time is resolved,\n // Run the procedure to set the current time to previous current time.\n if (fromScrollTimeline) {\n if (previousCurrentTime !== null)\n details.animation.currentTime = progress * effectEnd(details);\n\n switch (previousPlayState) {\n case 'paused':\n details.animation.pause();\n break;\n\n case 'running':\n case 'finished':\n details.animation.play();\n }\n }\n } else {\n throw TypeError(\"Unsupported timeline: \" + newTimeline);\n }\n }\n\n get startTime() {\n const details = proxyAnimations.get(this);\n if (details.timeline)\n return toCssNumberish(details, details.startTime);\n\n return details.animation.startTime;\n }\n set startTime(value) {\n // https://drafts.csswg.org/web-animations/#setting-the-start-time-of-an-animation\n const details = proxyAnimations.get(this);\n value = fromCssNumberish(details, value);\n if (!details.timeline) {\n details.animation.startTime = value;\n return;\n }\n\n // 1. Let timeline time be the current time value of the timeline that\n // animation is associated with. If there is no timeline associated with\n // animation or the associated timeline is inactive, let the timeline\n // time be unresolved.\n const timelineTime = fromCssNumberish(details,\n details.timeline.currentTime);\n\n // 2. If timeline time is unresolved and new start time is resolved, make\n // animation’s hold time unresolved.\n if (timelineTime == null && details.startTime != null) {\n details.holdTime = null;\n // Clearing the hold time may have altered the value of current time.\n // Ensure that the underlying animations has the correct value.\n syncCurrentTime(details);\n }\n\n // 3. Let previous current time be animation’s current time.\n // Note: This is the current time after applying the changes from the\n // previous step which may cause the current time to become unresolved.\n const previousCurrentTime = fromCssNumberish(details, this.currentTime);\n\n // 4. Apply any pending playback rate on animation.\n applyPendingPlaybackRate(details);\n\n // 5. Set animation’s start time to new start time.\n details.startTime = value;\n\n // 6. Set the reset current time on resume flag to false.\n details.resetCurrentTimeOnResume = false;\n\n // 7. Update animation’s hold time based on the first matching condition\n // from the following,\n\n // If new start time is resolved,\n // If animation’s playback rate is not zero,\n // make animation’s hold time unresolved.\n\n // Otherwise (new start time is unresolved),\n // Set animation’s hold time to previous current time even if\n // previous current time is unresolved.\n\n if (details.startTime !== null && details.animation.playbackRate != 0)\n details.holdTime = null;\n else\n details.holdTime = previousCurrentTime;\n\n // 7. If animation has a pending play task or a pending pause task, cancel\n // that task and resolve animation’s current ready promise with\n // animation.\n if (details.pendingTask) {\n details.pendingTask = null;\n details.readyPromise.resolve(this);\n }\n\n // 8. Run the procedure to update an animation’s finished state for animation\n // with the did seek flag set to true, and the synchronously notify flag\n // set to false.\n updateFinishedState(details, true, false);\n\n // Ensure that currentTime is updated for the native animation.\n syncCurrentTime(details);\n }\n\n get currentTime() {\n const details = proxyAnimations.get(this);\n if (!details.timeline)\n return details.animation.currentTime;\n\n if (details.holdTime != null)\n return toCssNumberish(details, details.holdTime);\n\n return toCssNumberish(details, calculateCurrentTime(details));\n }\n set currentTime(value) {\n const details = proxyAnimations.get(this);\n value = fromCssNumberish(details, value);\n if (!details.timeline || value == null) {\n details.animation.currentTime = value;\n return;\n }\n\n // https://drafts.csswg.org/web-animations/#setting-the-current-time-of-an-animation\n const previouStartTime = details.startTime;\n const previousHoldTime = details.holdTime;\n const timelinePhase = details.timeline.phase;\n\n // Update either the hold time or the start time.\n if (details.holdTime !== null || details.startTime === null ||\n timelinePhase == 'inactive' || details.animation.playbackRate == 0) {\n // TODO: Support hold phase.\n details.holdTime = value;\n } else {\n details.startTime = calculateStartTime(details, value);\n }\n details.resetCurrentTimeOnResume = false;\n\n // Preserve invariant that we can only set a start time or a hold time in\n // the absence of an active timeline.\n if (timelinePhase == 'inactive')\n details.startTime = null;\n\n // Reset the previous current time.\n details.previousCurrentTime = null;\n\n // Synchronously resolve pending pause task.\n if (details.pendingTask == 'pause') {\n details.holdTime = value;\n applyPendingPlaybackRate(details);\n details.startTime = null;\n details.pendingTask = null;\n details.readyPromise.resolve(this);\n }\n\n // Update the finished state.\n updateFinishedState(details, true, false);\n }\n\n get playbackRate() {\n return proxyAnimations.get(this).animation.playbackRate;\n }\n set playbackRate(value) {\n const details = proxyAnimations.get(this);\n\n if (!details.timeline) {\n details.animation.playbackRate = value;\n return;\n }\n\n // 1. Clear any pending playback rate on animation.\n details.pendingPlaybackRate = null;\n\n // 2. Let previous time be the value of the current time of animation before\n // changing the playback rate.\n const previousCurrentTime = this.currentTime;\n\n // 3. Set the playback rate to new playback rate.\n details.animation.playbackRate = value;\n\n // 4. If previous time is resolved, set the current time of animation to\n // previous time\n if (previousCurrentTime !== null)\n this.currentTime = previousCurrentTime;\n }\n\n get playState() {\n const details = proxyAnimations.get(this);\n if (!details.timeline)\n return details.animation.playState;\n\n const currentTime = fromCssNumberish(details, this.currentTime);\n\n // 1. All of the following conditions are true:\n // * The current time of animation is unresolved, and\n // * the start time of animation is unresolved, and\n // * animation does not have either a pending play task or a pending pause\n // task,\n // then idle.\n if (currentTime === null && details.startTime === null &&\n details.pendingTask == null)\n return 'idle';\n\n // 2. Either of the following conditions are true:\n // * animation has a pending pause task, or\n // * both the start time of animation is unresolved and it does not have a\n // pending play task,\n // then paused.\n if (details.pendingTask == 'pause' ||\n (details.startTime === null && details.pendingTask != 'play'))\n return 'paused';\n\n // 3. For animation, current time is resolved and either of the following\n // conditions are true:\n // * animation’s effective playback rate > 0 and current time >= target\n // effect end; or\n // * animation’s effective playback rate < 0 and current time <= 0,\n // then finished.\n if (currentTime != null) {\n if (details.animation.playbackRate > 0 &&\n currentTime >= effectEnd(details))\n return 'finished';\n if (details.animation.playbackRate < 0 && currentTime <= 0)\n return 'finished';\n }\n\n // 4. Otherwise\n return 'running';\n }\n get replaceState() {\n // TODO: Fix me. Replace state is not a boolean.\n return proxyAnimations.get(this).animation.pending;\n }\n\n get pending() {\n const details = proxyAnimations.get(this);\n if (details.timeline) {\n return !!details.readyPromise &&\n details.readyPromise.state == 'pending';\n }\n\n return details.animation.pending;\n }\n\n finish() {\n const details = proxyAnimations.get(this);\n if (!details.timeline) {\n details.animation.finish();\n return;\n }\n\n // 1. If animation’s effective playback rate is zero, or if animation’s\n // effective playback rate > 0 and target effect end is infinity, throw\n // an InvalidStateError and abort these steps.\n const playbackRate = effectivePlaybackRate(details);\n const duration = effectEnd(details);\n if (playbackRate == 0) {\n throw new DOMException(\n \"Cannot finish Animation with a playbackRate of 0.\",\n \"InvalidStateError\");\n }\n if (playbackRate > 0 && duration == Infinity) {\n throw new DOMException(\n \"Cannot finish Animation with an infinite target effect end.\",\n \"InvalidStateError\");\n }\n\n // 2. Apply any pending playback rate to animation.\n applyPendingPlaybackRate(details);\n\n // 3. Set limit as follows:\n // If playback rate > 0,\n // Let limit be target effect end.\n // Otherwise,\n // Let limit be zero.\n const limit = playbackRate < 0 ? 0 : duration;\n\n // 4. Silently set the current time to limit.\n this.currentTime = toCssNumberish(details, limit);\n\n // 5. If animation’s start time is unresolved and animation has an\n // associated active timeline, let the start time be the result of\n // evaluating\n // timeline time - (limit / playback rate)\n // where timeline time is the current time value of the associated\n // timeline.\n const timelineTime = fromCssNumberish(details,\n details.timeline.currentTime);\n\n if (details.startTime === null && timelineTime !== null) {\n details.startTime =\n timelineTime - (limit / details.animation.playbackRate);\n }\n\n // 6. If there is a pending pause task and start time is resolved,\n // 6.1 Let the hold time be unresolved.\n // 6.2 Cancel the pending pause task.\n // 6.3 Resolve the current ready promise of animation with animation.\n if (details.pendingTask == 'pause' && details.startTime !== null) {\n details.holdTime = null;\n details.pendingTask = null;\n details.readyPromise.resolve(this);\n }\n\n // 7. If there is a pending play task and start time is resolved, cancel\n // that task and resolve the current ready promise of animation with\n // animation.\n if (details.pendingTask == 'play' && details.startTime !== null) {\n details.pendingTask = null;\n details.readyPromise.resolve(this);\n }\n\n // 8. Run the procedure to update an animation’s finished state for\n // animation with the did seek flag set to true, and the synchronously\n // notify flag set to true.\n updateFinishedState(details, true, true);\n }\n\n play() {\n const details = proxyAnimations.get(this);\n if (!details.timeline) {\n details.animation.play();\n return;\n }\n\n playInternal(details, /* autoRewind */ true);\n }\n\n pause() {\n const details = proxyAnimations.get(this);\n if (!details.timeline) {\n details.animation.pause();\n return;\n }\n\n // https://www.w3.org/TR/web-animations-1/#pausing-an-animation-section\n\n // 1. If animation has a pending pause task, abort these steps.\n // 2. If the play state of animation is paused, abort these steps.\n if (this.playState == \"paused\")\n return;\n\n // 3. Let seek time be a time value that is initially unresolved.\n // 4. Let has finite timeline be true if animation has an associated\n // timeline that is not monotonically increasing.\n // Note: always true if we have reached this point in the polyfill.\n // Pruning following steps to be specific to scroll timelines.\n let seekTime = null;\n\n // 5. If the animation’s current time is unresolved, perform the steps\n // according to the first matching condition from below:\n // 5a. If animation’s playback rate is ≥ 0,\n // Set seek time to zero.\n // 5b. Otherwise,\n // If associated effect end for animation is positive infinity,\n // throw an \"InvalidStateError\" DOMException and abort these\n // steps.\n // Otherwise,\n // Set seek time to animation's associated effect end.\n\n const playbackRate = details.animation.playbackRate;\n const duration = effectEnd(details);\n\n if (details.animation.currentTime === null) {\n if (playbackRate >= 0) {\n seekTime = 0;\n } else if (duration == Infinity) {\n // Let native implementation take care of throwing the exception.\n details.animation.pause();\n return;\n } else {\n seekTime = duration;\n }\n }\n\n // 6. If seek time is resolved,\n // If has finite timeline is true,\n // Set animation's start time to seek time.\n if (seekTime !== null)\n details.startTime = seekTime;\n\n // 7. Let has pending ready promise be a boolean flag that is initially\n // false.\n // 8. If animation has a pending play task, cancel that task and let has\n // pending ready promise be true.\n // 9. If has pending ready promise is false, set animation’s current ready\n // promise to a new promise in the relevant Realm of animation.\n if (details.pendingTask == 'play')\n details.pendingTask = null;\n else\n details.readyPromise = null;\n\n // 10. Schedule a task to be executed at the first possible moment after the\n // user agent has performed any processing necessary to suspend the\n // playback of animation’s target effect, if any.\n if (!details.readyPromise)\n createReadyPromise(details);\n details.pendingTask ='pause';\n }\n\n reverse() {\n const details = proxyAnimations.get(this);\n const playbackRate = effectivePlaybackRate(details);\n const previousCurrentTime =\n details.resetCurrentTimeOnResume ?\n null : fromCssNumberish(details, this.currentTime);\n const inifiniteDuration = effectEnd(details) == Infinity;\n\n // Let the native implementation handle throwing the exception in cases\n // where reversal is not possible. Error cases will not change the state\n // of the native animation.\n const reversable =\n (playbackRate != 0) &&\n (playbackRate < 0 || previousCurrentTime > 0 || !inifiniteDuration);\n if (!details.timeline || !reversable) {\n if (reversable)\n details.pendingPlaybackRate = -effectivePlaybackRate(details);\n details.animation.reverse();\n return;\n }\n\n if (details.timeline.phase == 'inactive') {\n throw new DOMException(\n \"Cannot reverse an animation with no active timeline\",\n \"InvalidStateError\");\n }\n\n this.updatePlaybackRate(-playbackRate);\n playInternal(details, /* autoRewind */ true);\n }\n\n updatePlaybackRate(rate) {\n const details = proxyAnimations.get(this);\n details.pendingPlaybackRate = rate;\n if (!details.timeline) {\n details.animation.updatePlaybackRate(rate);\n return;\n }\n\n // https://drafts.csswg.org/web-animations/#setting-the-playback-rate-of-an-animation\n\n // 1. Let previous play state be animation’s play state.\n // 2. Let animation’s pending playback rate be new playback rate.\n // Step 2 already performed as we need to record it even when using a\n // monotonic timeline.\n const previousPlayState = this.playState;\n\n // 3. Perform the steps corresponding to the first matching condition from\n // below:\n //\n // 3a If animation has a pending play task or a pending pause task,\n // Abort these steps.\n if (details.readyPromise && details.readyPromise.state == 'pending')\n return;\n\n switch(previousPlayState) {\n // 3b If previous play state is idle or paused,\n // Apply any pending playback rate on animation.\n case 'idle':\n case 'paused':\n applyPendingPlaybackRate(details);\n break;\n\n // 3c If previous play state is finished,\n // 3c.1 Let the unconstrained current time be the result of calculating\n // the current time of animation substituting an unresolved time\n // value for the hold time.\n // 3c.2 Let animation’s start time be the result of evaluating the\n // following expression:\n // timeline time - (unconstrained current time / pending playback rate)\n // Where timeline time is the current time value of the timeline\n // associated with animation.\n // 3c.3 If pending playback rate is zero, let animation’s start time be\n // timeline time.\n // 3c.4 Apply any pending playback rate on animation.\n // 3c.5 Run the procedure to update an animation’s finished state for\n // animation with the did seek flag set to false, and the\n // synchronously notify flag set to false.\n\n case 'finished':\n const timelineTime = fromCssNumberish(details,\n details.timeline.currentTime);\n const unconstrainedCurrentTime = timelineTime !== null ?\n (timelineTime - details.startTime) * details.animation.playbackRate\n : null;\n if (rate == 0) {\n details.startTime = timelineTime;\n } else {\n details.startTime =\n timelineTime != null && unconstrainedCurrentTime != null ?\n (timelineTime - unconstrainedCurrentTime) / rate : null;\n }\n applyPendingPlaybackRate(details);\n updateFinishedState(details, false, false);\n syncCurrentTime(details);\n break;\n\n // 3d Otherwise,\n // Run the procedure to play an animation for animation with the\n // auto-rewind flag set to false.\n default:\n playInternal(details, false);\n }\n }\n\n persist() {\n proxyAnimations.get(this).animation.persist();\n }\n\n get id() {\n return proxyAnimations.get(this).animation.id;\n }\n\n cancel() {\n const details = proxyAnimations.get(this);\n if (!details.timeline) {\n details.animation.cancel();\n return;\n }\n\n // https://www.w3.org/TR/web-animations-1/#canceling-an-animation-section\n // 1. If animation’s play state is not idle, perform the following steps:\n // 1.1 Run the procedure to reset an animation’s pending tasks on\n // animation.\n // 1.2 Reject the current finished promise with a DOMException named\n // \"AbortError\"\n // 1.3 Let current finished promise be a new (pending) Promise object.\n // 1.4+ Deferred to native implementation.\n // TODO: polyfill since timelineTime will be incorrect for the\n // cancel event. Also, should avoid sending a cancel event if\n // the native animation is canceled due to the scroll timeline\n // becoming inactive. This can likely be done by associating\n // the cancel event with the proxy and not the underlying\n // animation.\n if (this.playState != 'idle') {\n resetPendingTasks(details);\n if (details.finishedPromise &&\n details.finishedPromise.state == 'pending') {\n details.finishedPromise.reject(createAbortError());\n }\n details.finishedPromise = new PromiseWrapper();\n details.animation.cancel();\n }\n\n // 2. Make animation’s hold time unresolved.\n // 3. Make animation’s start time unresolved.\n details.startTime = null;\n details.holdTime = null;\n\n // Extra step in the polyfill the ensure the animation stops ticking.\n removeAnimation(details.timeline, details.animation);\n }\n\n get onfinish() {\n return proxyAnimations.get(this).animation.onfinish;\n }\n set onfinish(value) {\n proxyAnimations.get(this).animation.onfinish = value;\n }\n get oncancel() {\n return proxyAnimations.get(this).animation.oncancel;\n }\n set oncancel(value) {\n proxyAnimations.get(this).animation.oncancel = value;\n }\n get onremove() {\n return proxyAnimations.get(this).animation.onremove;\n }\n set onremove(value) {\n proxyAnimations.get(this).animation.onremove = value;\n }\n\n get finished() {\n const details = proxyAnimations.get(this);\n if (!details.timeline)\n return details.animation.finished;\n\n if (!details.finishedPromise) {\n details.finishedPromise = new PromiseWrapper();\n }\n return details.finishedPromise.promise;\n }\n\n get ready() {\n const details = proxyAnimations.get(this);\n if (!details.timeline)\n return details.animation.ready;\n\n if (!details.readyPromise) {\n details.readyPromise = new PromiseWrapper();\n details.readyPromise.resolve(this);\n }\n return details.readyPromise.promise;\n }\n\n // --------------------------------------------------\n // Event target API\n // --------------------------------------------------\n\n addEventListener(type, callback, options) {\n proxyAnimations.get(this).animation.addEventListener(type, callback,\n options);\n }\n\n removeEventListener(type, callback, options) {\n proxyAnimations.get(this).animation.removeEventListener(type, callback,\n options);\n }\n\n dispatchEvent(event) {\n proxyAnimations.get(this).animation.dispatchEvent(event);\n }\n};\n\n// animation-delay or animation-end-delay should be in the form of a name and an optional percentage\nfunction parseOneAnimationDelay(delay, defaultOffset) {\n if(!delay) return null;\n\n const parts = delay.split(' ');\n\n if(!ANIMATION_DELAY_NAMES.includes(parts[0]) ||\n (parts.length == 2 && !parts[1].endsWith('%')))\n throw TypeError(\"Invalid animation delay\");\n\n let offset = defaultOffset;\n if(parts.length == 2) {\n const percentage = parseFloat(parts[1]);\n if(Number.isNaN(percentage))\n throw TypeError(`\\\"${parts[1]}\\\" is not a valid percentage for animation delay`);\n\n offset = CSS.percent(percentage);\n }\n\n return { name: parts[0], offset: offset };\n}\n\nfunction defaultAnimationDelay() { return { name: 'cover', offset: CSS.percent(0) }; }\n\nfunction defaultAnimationEndDelay() { return { name: 'cover', offset: CSS.percent(100) }; }\n\nfunction parseAnimationDelays(animOptions) {\n const timeRange = parseTimeRange(animOptions['animation-time-range']);\n\n if(animOptions['animation-delay'])\n timeRange.start = parseOneAnimationDelay(animOptions['animation-delay'], defaultAnimationDelay().offset);\n\n if(animOptions['animation-end-delay'])\n timeRange.end = parseOneAnimationDelay(animOptions['animation-end-delay'], defaultAnimationEndDelay().offset);\n\n return timeRange;\n}\n\nfunction parseTimeRange(value) {\n const timeRange = {\n start: defaultAnimationDelay(),\n end: defaultAnimationEndDelay()\n };\n\n if (!value)\n return timeRange;\n\n // Format:\n // \n // --> 0% 100%\n // --> \n // \n // --> cover cover \n // TODO: Support all formatting options once ratified in the spec.\n const parts = value.split(' ');\n const names = [];\n const offsets = [];\n\n parts.forEach(part => {\n if (part.endsWith('%'))\n offsets.push(parseFloat(part));\n else\n names.push(part);\n });\n\n if (names.length > 2 || offsets.length > 2 || offsets.length == 1) {\n throw TypeError(\"Invalid time range\");\n }\n\n if (names.length) {\n timeRange.start.name = names[0];\n timeRange.end.name = names.length > 1 ? names[1] : names[0];\n }\n\n if (offsets.length > 1) {\n timeRange.start.offset = CSS.percent(offsets[0]);\n timeRange.end.offset = CSS.percent(offsets[1]);\n }\n\n return timeRange;\n}\n\nexport function animate(keyframes, options) {\n const timeline = options.timeline;\n\n if (timeline instanceof ScrollTimeline)\n delete options.timeline;\n\n const timelineOffset = (options, property) => {\n if (property in options) {\n const value = options[property];\n if (typeof value != 'number') {\n delete options[property];\n return value;\n }\n return null;\n }\n };\n\n const updateDelay = (timelineOffset, value) => {\n if (!value)\n return;\n\n // TODO(kevers): Update property names once ratified.\n // https://github.com/w3c/csswg-drafts/issues/7589\n if (value.phase)\n timelineOffset.name = value.phase;\n\n if (value.percent)\n timelineOffset.offset = value.percent;\n };\n\n const delayTimelineOffset = timelineOffset(options, 'delay');\n const endDelayTimelineOffset = timelineOffset(options, 'endDelay');\n\n const animation = nativeElementAnimate.apply(this, [keyframes, options]);\n const proxyAnimation = new ProxyAnimation(animation, timeline);\n\n if (timeline instanceof ScrollTimeline) {\n animation.pause();\n if (timeline instanceof ViewTimeline) {\n const details = proxyAnimations.get(proxyAnimation);\n details.timeRange = parseTimeRange(options.timeRange);\n updateDelay(details.timeRange.start, delayTimelineOffset);\n updateDelay(details.timeRange.end, endDelayTimelineOffset);\n }\n proxyAnimation.play();\n }\n\n return proxyAnimation;\n};\n","import { ANIMATION_DELAY_NAMES } from './proxy-animation';\n\n// This is also used in scroll-timeline-css.js\nexport const RegexMatcher = {\n IDENTIFIER: /[\\w\\\\\\@_-]+/g,\n WHITE_SPACE: /\\s*/g,\n NUMBER: /^[0-9]+/,\n TIME: /^[0-9]+(s|ms)/,\n VIEW_TIMELINE: /view-timeline\\s*:([^;}]+)/,\n VIEW_TIMELINE_NAME: /view-timeline-name\\s*:([^;}]+)/,\n VIEW_TIMELINE_AXIS: /view-timeline-axis\\s*:([^;}]+)/,\n ANIMATION_TIMELINE: /animation-timeline\\s*:([^;}]+)/,\n ANIMATION_DELAY: /animation-delay\\s*:([^;}]+)/,\n ANIMATION_END_DELAY: /animation-end-delay\\s*:([^;}]+)/,\n ANIMATION_TIME_RANGE: /animation-time-range\\s*:([^;}]+)/,\n ANIMATION_NAME: /animation-name\\s*:([^;}]+)/,\n ANIMATION: /animation\\s*:([^;}]+)/,\n SOURCE_ELEMENT: /selector\\(#([^)]+)\\)/,\n};\n\n// Used for ANIMATION_TIMELINE, ANIMATION_NAME and ANIMATION regex\nconst VALUES_CAPTURE_INDEX = 1;\n\nconst WHOLE_MATCH_INDEX = 0;\n\nconst ANIMATION_KEYWORDS = [\n 'normal', 'reverse', 'alternate', 'alternate-reverse',\n 'none', 'forwards', 'backwards', 'both',\n 'running', 'paused',\n 'ease', 'linear', 'ease-in', 'ease-out', 'ease-in-out'\n];\n\nconst VIEW_TIMELINE_AXIS_TYPES = ['block', 'inline', 'vertical', 'horizontal'];\n\n// 1 - Extracts @scroll-timeline and saves it in scrollTimelineOptions.\n// 2 - If we find any animation-timeline in any of the CSS Rules, \n// we will save objects in a list named cssRulesWithTimelineName\nexport class StyleParser {\n constructor() {\n this.cssRulesWithTimelineName = [];\n this.scrollTimelineOptions = new Map(); // save options by name\n this.subjectSelectorToViewTimeline = [];\n this.keyframeNamesSelectors = new Map();\n }\n\n // Inspired by\n // https://drafts.csswg.org/css-syntax/#parser-diagrams\n // https://github.com/GoogleChromeLabs/container-query-polyfill/blob/main/src/engine.ts\n // This function is called twice, in the first pass we are interested in saving\n // @scroll-timeline and @keyframe names, in the second pass\n // we will parse other rules\n transpileStyleSheet(sheetSrc, firstPass, srcUrl) {\n // AdhocParser\n const p = {\n sheetSrc: sheetSrc,\n index: 0,\n name: srcUrl,\n };\n\n while (p.index < p.sheetSrc.length) {\n this.eatWhitespace(p);\n if (p.index >= p.sheetSrc.length) break;\n if (this.lookAhead(\"/*\", p)) {\n while (this.lookAhead(\"/*\", p)) {\n this.eatComment(p);\n this.eatWhitespace(p);\n }\n continue;\n }\n\n if (this.lookAhead(\"@scroll-timeline\", p)) {\n const { scrollTimeline, startIndex, endIndex } = this.parseScrollTimeline(p);\n if (firstPass) this.scrollTimelineOptions.set(scrollTimeline.name, scrollTimeline);\n } else {\n const rule = this.parseQualifiedRule(p);\n if (!rule) continue;\n if (firstPass) {\n this.parseKeyframesAndSaveNameMapping(rule, p);\n } else {\n this.handleScrollTimelineProps(rule, p);\n }\n }\n }\n\n // If this sheet has no srcURL (like from a