From 37a13dc082c0741946ba50e6fa75a4ef323f612e Mon Sep 17 00:00:00 2001 From: Bramus Date: Tue, 11 Oct 2022 14:28:11 +0200 Subject: [PATCH 1/5] Cache ProxyAnimation instances --- dist/scroll-timeline.js | 2 +- dist/scroll-timeline.js.map | 2 +- src/scroll-timeline-css.js | 21 +++++++++++++++------ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/dist/scroll-timeline.js b/dist/scroll-timeline.js index 89ccf7c4..38c4aedc 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"],A=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 N(e){e.readyPromise=new A,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 A),"pending"==e.finishedPromise.state&&(n?L(e):Promise.resolve().then(function(){L(e)}))):(e.finishedPromise&&"resolved"==e.finishedPromise.state&&(e.finishedPromise=new A),"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||N(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||N(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()),N(e),e.readyPromise.resolve(e.proxy))}(e),e.finishedPromise&&"pending"==e.finishedPromise.state&&e.finishedPromise.reject(O()),e.finishedPromise=new A,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||N(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 A),e.finishedPromise.promise):e.animation.finished}},{key:"ready",get:function(){var e=G.get(this);return e.timeline?(e.readyPromise||(e.readyPromise=new A,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(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){})}(),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=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}(t,t.animationName,e.target);if(n.timeline&&t.timeline!=n.timeline){var i=new Y(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: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 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"],A=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 N(e){e.readyPromise=new A,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 A),"pending"==e.finishedPromise.state&&(n?L(e):Promise.resolve().then(function(){L(e)}))):(e.finishedPromise&&"resolved"==e.finishedPromise.state&&(e.finishedPromise=new A),"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||N(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||N(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()),N(e),e.readyPromise.resolve(e.proxy))}(e),e.finishedPromise&&"pending"==e.finishedPromise.state&&e.finishedPromise.reject(O()),e.finishedPromise=new A,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||N(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 A),e.finishedPromise.promise):e.animation.finished}},{key:"ready",get:function(){var e=G.get(this);return e.timeline?(e.readyPromise||(e.readyPromise=new A,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(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){})}(),window.addEventListener("animationstart",function(e){e.target.getAnimations().filter(function(t){return t.animationName===e.animationName}).forEach(function(t,n){if(e.target.proxiedAnimations||(e.target.proxiedAnimations={}),!e.target.proxiedAnimations[t.animationName]){var i=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}(t,t.animationName,e.target);if(!i.timeline||t.timeline==i.timeline)return;e.target.proxiedAnimations[t.animationName]=new Y(t,i.timeline,i.animOptions)}t.pause(),e.target.proxiedAnimations[t.animationName].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.")}(); //# sourceMappingURL=scroll-timeline.js.map diff --git a/dist/scroll-timeline.js.map b/dist/scroll-timeline.js.map index b9b9900e..eec7dda6 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