% Copyright 2007--today Alexander Grahn

% This material is subject to the LaTeX Project Public License. See
%   http://mirrors.ctan.org/macros/latex/base/lppl.txt
% for the details of that license.

% Package for creating portable, JavaScript driven animations from sets of
% graphics files or inline graphics (e. g. LaTeX-picture, PSTricks,
% pgf/TikZ, ...)

% Supports LaTeX->dvips->ps2pdf, (Xe)LaTeX->(x)dvipdfmx, LuaLaTeX,
% pdfLaTeX and LaTeX->dvisvgm workflows.

\NeedsTeXFormat{LaTeX2e}[2022-06-01]

\def\@anim@version{2023/06/18}
\ProvidesPackage{animate}
[\@anim@version\space PDF & SVG animations from files and inline graphics]

\RequirePackage{ifthen}
\RequirePackage{iftex}
\RequirePackage{ifdraft}
\RequirePackage{calc}

%driver options (the only package options we process immediately)
\newboolean{@anim@dvips}
\newboolean{@anim@dvipdfmx}
\newboolean{@anim@dvisvgm}
\newboolean{@anim@xetex}
\newboolean{@anim@export}%exporting animation frames

\ExplSyntaxOn
\DeclareKeys[anim@pkg]{
  pdftex.code:n = {},
  pdftex.value_forbidden:n = true,

  luatex.code:n = {},
  luatex.value_forbidden:n = true,

  xetex.code:n = {},
  xetex.value_forbidden:n = true,

  dvips.code:n = {},
  dvips.value_forbidden:n = true,

  dvipdfmx.code:n = {
    \PassOptionsToPackage{dvipdfmx}{pdfbase}
    \PassOptionsToPackage{dvipdfmx}{ocgbase}
    \PassOptionsToPackage{dvipdfmx}{graphics}
  },
  dvipdfmx.value_forbidden:n = true,

  dvisvgm.code:n = {
    \PassOptionsToPackage{dvisvgm}{pdfbase}
    \PassOptionsToPackage{dvisvgm}{graphics}
  },
  dvisvgm.value_forbidden:n = true,

  export.legacy_if_gset:n = @anim@export,
  export.default:n = true,
}
\ExplSyntaxOff
\DeclareUnknownKeyHandler[anim@pkg]{}

\ProcessKeyOptions[anim@pkg]
% remaining package options to be processed near end of this file

\RequirePackage{pdfbase}
\@ifpackagelater{pdfbase}{2022/08/04}{}{
  \PackageError{animate}{%
    Support package `pdfbase.sty' too old.%
  }{%
    Please install an up to date version of `pdfbase.sty'.\MessageBreak%
    Aborting.%
  }%
}

\ifpdf\else
  \setboolean{@anim@dvips}{true}% default dvi mode
\fi

\ExplSyntaxOn
\bool_if:NT\g_pbs_dvipdfmx_bool{
  \setboolean{@anim@dvipdfmx}{true}
  \setboolean{@anim@dvips}{false}
}
\bool_if:NT\g_pbs_dvisvgm_bool{
  \setboolean{@anim@dvisvgm}{true}
  \setboolean{@anim@dvips}{false}
}
\sys_if_engine_xetex:T{
  \bool_if:NT\g_pbs_dvipdfmx_bool{\setboolean{@anim@xetex}{true}}
  \setboolean{@anim@dvips}{false}
}
\ExplSyntaxOff

\RequirePackage{graphics}%\scalebox, \resizebox, \rotatebox
\RequirePackage{zref-abspage}

\newboolean{@anim@insideexport}
\newenvironment{anim@export}{%
  \global\@anim@insideexporttrue%
}{%
  \global\@anim@insideexportfalse%
}

% get number of pages in file given as #1 (file basename); #2 extension;
% store result into macro given as #3
\ifpdf %pdflatex/lualatex
  \def\@anim@getpagecount#1#2#3{%
    \pdfximage page 1 {#1.#2}\xdef#3{\the\pdflastximagepages}%
  }
\else
  \if@anim@xetex
    \def\@anim@getpagecount#1#2#3{%
      \xdef#3{\the\numexpr\XeTeXpdfpagecount "#1.#2"\relax}%
    }
  \else
    \if@anim@dvips
      \def\@anim@getpagecount#1#2#3{\gdef#3{1}}%
    \else %dvipdfmx, dvisvgm
      \def\@anim@getpagecount#1#2#3{{%
        \global\let#3\@undefined%
        \def\%##1: ##2:{%
          \expandafter\edef\csname ##1\endcsname{##2}%
          \global\let#3\Pages%
        }%
        \endlinechar`\:% `:' appended to every line read
        \IfFileExists{#1.xbb}{%
          %read from xbb file
          {\catcode`\:=12\catcode`\%=0\sbox0{\input{#1.xbb}}}%
          \ifdefined#3\else%
            \PackageError{animate}{%
              Cannot read number of pages from file\MessageBreak%
              -------------------------------------\MessageBreak%
              #1.xbb\MessageBreak%
              -------------------------------------\MessageBreak%
              This file seems to be invalid and should be deleted%
            }{}%
          \fi%
        }{%
          %read from command pipe (`extractbb')
          %firstly, check whether reading from pipe is allowed
          \immediate\closein\@inputcheck%
          \immediate\openin\@inputcheck="|extractbb -h"\relax%
          \ifeof\@inputcheck%
            \immediate\closein\@inputcheck%
            \PackageError{animate}{%
              Cannot determine number of pages in file\MessageBreak%
              ----------------------------------------\MessageBreak%
              #1.#2\MessageBreak%
              ----------------------------------------\MessageBreak%
              Try --shell-escape option (--enable-pipes in MiKTeX),%
              \MessageBreak or run\MessageBreak%
              \space\space extractbb #1.#2\MessageBreak%
              on the command line to provide a valid `xbb' file%
            }{}%
          \else%
            \immediate\closein\@inputcheck%
            {\catcode`\:=12\catcode`\%=0\sbox0{\@@input "|extractbb -O #1.#2"}}%
          \fi%
        }%
      }}
    \fi
  \fi
\fi

\if@anim@dvisvgm
  \setboolean{@anim@export}{false}
\fi

\if@anim@export
  \@ifclassloaded{standalone}{}{
    \PackageError{animate}{%
      Option `export' requires the `standalone' document class.\MessageBreak%
      Replace current document class with `standalone'%
    }{%
      Put the line\MessageBreak%
      `\protect\documentclass{standalone}'\MessageBreak%
      at the beginning of the document preamble.%
    }%
  }%
  \standaloneenv{anim@export}
\fi

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% low level PDF/SVG operations
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\ExplSyntaxOn
\let\@anim@literal\pbs_literal:nn

\def\@anim@xform#1#2#3#4#5{
  \pbs_pdfxform:nnnnn{#1}{#2}{#3}{#4}{#5}
  \xdef\@anim@lastxform{\pbs_pdflastxform:}
}

\let\@anim@refxform\pbs_pdfrefxform:n

\def\@anim@annot#1#2#3#4{
  \pbs_pdfannot:nnnn{#1}{#2}{#3}{#4}
}

\let\@anim@fpeval\fp_eval:n

%hashing object references of embedded files to avoid multiple inclusion
\let\@anim@pdfmdfivesum\file_mdfive_hash:n
\ExplSyntaxOff

\if@anim@dvisvgm
  \def\@anim@updatebbox#1#2#3{\special{dvisvgm:bbox #1 #2 #3 transform}}

  %approach similar to OCGs, that is, putting the frame content in an svg group,
  % <g id='...'>...</g>, whose visibility is then manipulated by JavaScript
  \def\@anim@newocg#1#2{\xdef\@anim@curocg{id='_#1.#2' class='ocg'}}
  \def\ocgbase@add@to@off@list#1{%
    \xdef\@anim@curocg{\@anim@curocg\space visibility='hidden'}%
  }
  \def\ocgbase@oc@bdc#1{\special{dvisvgm:raw <g #1>}}
  \def\ocgbase@oc@emc{\special{dvisvgm:raw </g>}}
\else
  \ExplSyntaxOn
  \def\@anim@dictobj#1{
    \pbs_pdfobj:nnn{}{dict}{#1}
    \xdef\@anim@lastobj{\pbs_pdflastobj:}
  }

  \def\@anim@arrayobj#1{
    \pbs_pdfobj:nnn{}{array}{#1}
    \xdef\@anim@lastobj{\pbs_pdflastobj:}
  }

  \def\@anim@streamobj#1#2{
    \pbs_pdfobj:nnn{}{stream}{{#1}{#2}}
    \xdef\@anim@lastobj{\pbs_pdflastobj:}
  }

  \def\@anim@widget#1#2#3#4{
    \pbs_pdfannot:nnnn{#1}{#2}{#3}{#4}
    \pbs_appendtofields:n{\pbs_pdflastann:}
  }
  \ExplSyntaxOff

  \RequirePackage{ocgbase} %OCG generating and configuration macros
  \def\@anim@newocg#1#2{%#1:@anim@num, #2:@anim@curframe@zb
    \ocgbase@new@ocg{#1.#2}{}{1}%
    \xdef\@anim@curocg{\ocgbase@last@ocg}%
  }
\fi
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%creating and using global definitions
\def\@anim@newkey#1#2{{\expandafter\xdef\csname#1\endcsname{#2}}}
\def\@anim@getkeyval#1{\ifcsname#1\endcsname\csname#1\endcsname\fi}

\def\@animate@rerunwarn{%
  \ifcsname @anim@rerunwarned\endcsname\else%
    \gdef\@anim@rerunwarned{}%
      \PackageWarningNoLine{animate}{%
      @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\MessageBreak
      @ Rerun to get internal references right! @\MessageBreak
      @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}%
  \fi%
}

%macro for writing global defs to external *.aux file
\def\@anim@keytoaux#1#2{%
  \immediate\write\@mainaux{\string\@anim@newkey{#1}{#2}}%
  \ifthenelse{\equal{\@anim@getkeyval{#1}}{#2}}{}{%
    \AtEndDocument{\@animate@rerunwarn}}%
}

\AtBeginDocument{%
  \immediate\write\@mainaux{\string\providecommand\string\@anim@newkey[2]{}}%
}

%missing package error message
\newcommand{\@anim@missing}[2][]{%
  \ifthenelse{\equal{#1}{}}{%
    \def\@anim@pkgopt{}%
  }{%
    \def\@anim@pkgopt{[#1]}%
  }%
  \PackageError{animate}{%
    Package `#2' required.
    Put the line\MessageBreak%
    `\protect\usepackage\@anim@pkgopt{#2}'\MessageBreak%
    to the preamble of your document%
  }{}%
}

\newboolean{@anim@grxloaded}
\setboolean{@anim@grxloaded}{false}
\AtBeginDocument{%
  \@ifpackageloaded{graphicx}{\setboolean{@anim@grxloaded}{true}}{}%
  \gdef\@anim@lscape{}%
  \gdef\@anim@@lscape{:ls}%
  \ifdefined\landscape%
    \g@addto@macro{\landscape}{\gdef\@anim@lscape{:ls}}%
    \g@addto@macro{\endlandscape}{\gdef\@anim@lscape{}}%
  \fi%
}

% true if any of `autoplay' or `autoresume' options is set
\newboolean{@anim@autoplayorresume}
% true if any of `controls' or `palindrome' or `label' options is set
\newboolean{@anim@timeline} %true if `timeline' option is set
\newboolean{@anim@multipage} % multipage document?
\newboolean{@anim@nomouse} % animation widget to react on mouse events?

\newbox\@anim@box %stores animation frames
\newbox\@anim@measbox % for measuring purposes
\newdimen\@anim@tmpdima %length registers for occasional use
\newdimen\@anim@tmpdimb

\def\@anim@firstofthree#1#2#3{#1}
\def\@anim@secndofthree#1#2#3{#2}
\def\@anim@thirdofthree#1#2#3{#3}
%macros for recalling saved nat. dimensions
\def\@anim@xformnatwd#1{\expandafter\@anim@firstofthree#1}
\def\@anim@xformnatht#1{\expandafter\@anim@secndofthree#1}
\def\@anim@xformnatdp#1{\expandafter\@anim@thirdofthree#1}

%helper macro that typesets graphics file into savebox
\if@anim@dvips
  \def\@anim@filebox#1#2#3#4{% dvips: no multi-page support
    \edef\@anim@curfile{[#3]{#1}}%
    \setbox#4=\hbox{%
      \expandafter\includegraphics\@anim@curfile}%
  }
\else
  %pdfTeX, LuaTeX, dvipdfmx, xetex, dvisvgm
  \def\@anim@filebox#1#2#3#4{%
    \edef\@anim@curfile{[#3,page=#2]{#1}}%
    \setbox#4=\hbox{%
      \expandafter\includegraphics\@anim@curfile}%
  }
\fi

\def\@anim@checkboxsize#1#2{% #1: box number, #2: file
  \ifdim\wd#1=\z@%
    \PackageError{animate}{%
      Graphics to be used for first frame%
      \MessageBreak must not have zero width%
    }{%
      Check content of file #2%
    }%
  \fi%
  \ifdim\ht#1=\z@\ifdim\dp#1=\z@%
    \PackageError{animate}{%
      Graphics to be used for first frame%
      \MessageBreak must not have zero height%
    }{%
      Check content of file #2%
    }%
  \fi\fi%
}

%create Form XObject from graphics file
\def\@anim@ximage#1#2#3#4#5{%#1:@anim@num, #2:@anim@curframe@zb, #3:filename,
  %         #4: page number of multipage file, #5 graphicx inclusion options
  % fingerprint "<file chksum>.<page>.<graphicx opts>.<current document page>"
  % of external graphics to prevent re-embedding
  \edef\@anim@fingerprint{%
    \@anim@pdfmdfivesum{#3}.#4.#5%
    \if@anim@dvisvgm.\@anim@abspage\fi%
  }%
  \edef\@anim@curxform{\@anim@getkeyval{xform:\@anim@fingerprint}}%
  \ifthenelse{\equal{\@anim@curxform}{}}{%
    % new file
    \@anim@filebox{#3}{#4}{#5}{\@anim@box}% store file in a box
    \edef\@anim@natdims{{\the\wd\@anim@box}{\the\ht\@anim@box}{\the\dp\@anim@box}}%
    \@anim@xinline{#1}{#2}{\@anim@box}{newfile}% Form XObject creation
    \@anim@newkey{natdims:\@anim@lastxform}{\@anim@natdims}%
    \@anim@newkey{scaleddims:\@anim@lastxform}{% scaled dimensions
      {\@anim@animwidth}{\@anim@animheight}{\@anim@animdepth}%
    }%
    \@anim@newkey{xform:\@anim@fingerprint}{\@anim@lastxform}%
  }{% file known, re-using existing xform
    \edef\@anim@curndims{\@anim@getkeyval{scaleddims:\@anim@curxform}}%
    \setbox\@anim@box=\hbox to \@anim@xformnatwd{\@anim@curndims}{%
      \vrule width \z@
        height \@anim@xformnatht{\@anim@curndims}
        depth \@anim@xformnatdp{\@anim@curndims}%
      \@anim@refxform{\@anim@curxform}%
      \hss%
    }%
    \@anim@xinline{#1}{#2}{\@anim@box}{\@anim@curxform}%
  }%
}

%creates Form XObject from box contents
\def\@anim@xinline#1#2#3#4{% #1 anim num, #2 frame num, #3 box num, #4 status (
  \edef\@anim@curwd{\the\wd#3}%        `newfile', xform ref of known file or `inline')
  \edef\@anim@curht{\the\ht#3}%
  \edef\@anim@curdp{\the\dp#3}%
  % measure 0th frame, to determine final animation widget dims
  \ifnum#2=\z@\relax%
    \ifthenelse{\equal{#4}{newfile}\OR\equal{#4}{inline}}{%
      \setbox\@anim@measbox=\hbox to \wd#3{%
        \vrule width \z@ height \ht#3 depth \dp#3\hss}%
    }{% known file
        \edef\@anim@curndims{\@anim@getkeyval{natdims:#4}}%
        \setbox\@anim@measbox=\hbox to \@anim@xformnatwd{\@anim@curndims}{%
          \vrule width \z@
            height \@anim@xformnatht{\@anim@curndims}
            depth \@anim@xformnatdp{\@anim@curndims}%
          \hss%
        }%
    }%
    \@anim@scale{\@anim@measbox}%
    \if@anim@dvisvgm% for clipping purposes
      \@anim@keytoaux{a#1.wdbp}{\strip@pt\dimexpr0.996264\dimexpr\@anim@animwidth}%
      \@anim@keytoaux{a#1.htbp}{\strip@pt\dimexpr0.996264\dimexpr\@anim@animheight}%
      \@anim@keytoaux{a#1.thtbp}{\strip@pt\dimexpr0.996264\dimexpr\@anim@animtotalheight}%
    \fi%
  \fi%
  \def\@anim@needresize{0}%
  % test if content dimensions differ from those of the final widget
  \ifdim\@anim@curwd=\@anim@animwidth\else\def\@anim@needresize{1}\fi%
  \ifdim\@anim@curht=\@anim@animheight\else\def\@anim@needresize{1}\fi%
  \ifdim\@anim@curdp=\@anim@animdepth\else\def\@anim@needresize{1}\fi%
  % resize content to final dimensions, if necessary
  \ifnum\@anim@needresize>\z@\relax%
    \setbox#3=\hbox{\resizebox{\@anim@animwidth}{\@anim@animheight}{\box#3}}%
  \fi%
  \ifthenelse{\equal{#4}{newfile}\OR\equal{#4}{inline}}{%
    \@anim@xform{1}{1}{}{}{#3}%
    %keep a record of XObject number
    \@anim@newkey{img@#2}{\@anim@lastxform}%
  }{%
    %known file
    \ifnum\@anim@needresize>\z@%
      \@anim@xform{0}{1}{}{}{#3}%
      \@anim@newkey{img@#2}{\@anim@lastxform}%
    \else%
      \@anim@newkey{img@#2}{#4}%
    \fi%
  }%
  \message{<a#1,fr#2>}%
}

\def\@anim@refxformlist#1,#2\@nil{%
  \ifthenelse{\equal{#1}{}}{}{%
    \if@anim@dvips%
      \@anim@refxform{{#1}}%
    \else%
      \@anim@refxform{#1}%
    \fi%
  }%
  \ifthenelse{\equal{#2}{}}{}{\@anim@refxformlist#2\@nil}%
}

% this counts the length of a clist <item a>,<item b>,<item b>,..., (note the
% trailing comma)
\def\@anim@countxformlist#1{%
  \expandafter\@anim@@countxformlist\expandafter0\expandafter:#1,\@nil%
}
\def\@anim@@countxformlist#1:#2,#3\@nil{%
  \ifx\relax#2\relax%
    \the\numexpr#1%
  \else%
    \ifx\relax#3\relax%
      \the\numexpr#1%
    \else%
      \expandafter\@anim@@countxformlist\the\numexpr #1+1:#3\@nil%
    \fi%
  \fi%
}

\def\@anim@zaptrailingcommafromxref#1,\@nil{\if@anim@dvips{#1}\else#1\fi}%

% inserts one animation frame into the output acc. to \ifcase\@anim@method
% as a non-interactive Widget annotation (0 or 1) or as an xform (2) that is
% referenced in the page content (and tagged as OCG or for multipage PDF export)
% args: #1 animation num, #2 frame number, #3 comma-separated list of xform
% refs (transparencies)
\def\@anim@makeframe#1#2#3{%
  \edef\@anim@arg{#3}%
  \setboolean{@anim@singleref}{false}\def\@anim@framexform{}%
  % count number of xforms in the frame content; a single xform can be referenced
  % at once, while a transparency list (multiple, overlayed xforms) must be boxed
  % and nested in an additional xform
  \ifnum\@anim@countxformlist{\@anim@arg}=\@ne%
    \setboolean{@anim@singleref}{true}%
    \edef\@anim@framexform{\expandafter\@anim@zaptrailingcommafromxref\@anim@arg\@nil}%
  \fi%
  % however, this isn't possible on landscape pages with \@anim@method==0|1
  % because frame content needs to be rotated by 90 deg counterclockwise
  \ifnum\@anim@method>\@ne\else\ifx\@anim@lscape\@anim@@lscape%
    \setboolean{@anim@singleref}{false}\def\@anim@framexform{}%
  \fi\fi%
  % ... nor if the animation is to be exported to multipage PDF
  \if@anim@export%
    \setboolean{@anim@singleref}{false}\def\@anim@framexform{}%
  \fi%
  % ... also, for method=ocg, we prefer packing content in an xform, as it allows
  % higher frame rates
  \ifnum\@anim@method=\tw@\if@anim@dvisvgm\else%
    \setboolean{@anim@singleref}{false}\def\@anim@framexform{}%
  \fi\fi%
  \if@anim@singleref\else%
    % put frame content in a box if necessary
    \setbox\@anim@box=\hbox to \@anim@animwidth{%
      \vrule width \z@ height \@anim@animheight depth \@anim@animdepth%
      \expandafter\@anim@refxformlist\@anim@arg,\@nil%
      \hss%
    }%
    % rotate content on lscape pages
    \ifnum\@anim@method>\@ne\else\ifx\@anim@lscape\@anim@@lscape%
        \setbox\@anim@box=\hbox{\rotatebox{90}{\box\@anim@box}}%
    \fi\fi%
  \fi%
  \ifcase\@anim@method% icon based
    \if@anim@singleref\else%
      \@anim@xform{0}{1}{}{}{\@anim@box}%
      \let\@anim@framexform\@anim@lastxform%
    \fi%
    %initial visibility
    \ifnum\@anim@poster>\@anim@mtwo\relax% insert poster frame
      \ifnum#2=\@anim@poster\relax%
        \let\@anim@posterap\@anim@framexform%
      \else%
        \ifnum\@anim@poster=\@anim@mone\relax% use last frame as poster
          \let\@anim@posterap\@anim@framexform%
        \fi%
      \fi%
    \fi%
    %insert (invisible) widget with current frame as appearance
    \@anim@widget{\@anim@animwidth}{\@anim@animheight}{\@anim@animdepth}{%
      /Subtype/Widget%
      /F 2%
      /FT/Btn/Ff 65537%
      /BS <</W 0>>%
      /AP <</N \@anim@framexform>>%
      /MK <</TP 1/I \@anim@framexform%
      /IF<</S/A/FB true>>>>%
      /T (#1.#2)%
    }%
  \or% widget based
    %initial visibility
    \def\@anim@annotflag{/F 2}% default: hidden
    \ifnum\@anim@poster>\@anim@mtwo\relax% insert poster frame
      \ifnum#2=\@anim@poster\relax%
        \def\@anim@annotflag{/F 4}% not hidden + print (4)
      \else%
        \ifnum\@anim@poster=\@anim@mone\relax% use last frame as poster
          \ifthenelse{%
            \NOT\equal{\@anim@getkeyval{a#1.poster}}{}\AND%
            #2=\@anim@getkeyval{a#1.poster}%
          }{%
            \def\@anim@annotflag{/F 4}%
          }{}%
        \fi%
      \fi%
    \fi%
    %frame insertion
    \if@anim@singleref\else%
      \@anim@xform{0}{1}{}{}{\@anim@box}%
      \let\@anim@framexform\@anim@lastxform%
    \fi%
    \@anim@widget{\@anim@animwidth}{\@anim@animheight}{\@anim@animdepth}{%
      /Subtype/Widget%
      \@anim@annotflag%
      /FT/Btn/Ff 65537%
      /BS <</W 0>>%
      /AP <</N \@anim@framexform>>%
      /MK <</TP 1/I \@anim@framexform%
      /IF<</S/A/FB true>>>>%
      /T (#1.#2)%
    }%
  \or% ocg-based (abused for dvisvgm backend as well as for
    % multipage PDF export)
    \if@anim@export%
      \if@anim@insideexport\else\anim@export\fi%
      \box\@anim@box%
      \endanim@export%
    \else%
      \@anim@newocg{#1}{#2}%
      %initial visibility
      \ifnum\@anim@poster=\@anim@mone\relax% use last frame as poster
        \ifthenelse{%
          \NOT\equal{\@anim@getkeyval{a#1.poster}}{}\AND%
          #2=\@anim@getkeyval{a#1.poster}%
        }{}{%
          \ocgbase@add@to@off@list{\@anim@curocg}%
        }%
      \else%
        \ifnum#2=\@anim@poster\relax\else%
          \ocgbase@add@to@off@list{\@anim@curocg}%
        \fi%
      \fi%
      %frame insertion
      \if@anim@dvisvgm%
        \if@anim@singleref\else%
          \@anim@xform{0}{1}{}{}{\@anim@box}%
          \let\@anim@framexform\@anim@lastxform%
        \fi%
        % `marked content' method for dvisvgm
        \ocgbase@oc@bdc{\@anim@curocg}%
        \@anim@refxform{\@anim@framexform}%
        \ocgbase@oc@emc%
      \else%
        % /OC method for PDF output, higher frame rates than
        % with marked content (BDC/EMC)
        \@anim@xform{0}{1}{}{/OC \@anim@curocg}{\@anim@box}%
        \@anim@refxform{\@anim@lastxform}%
      \fi%
    \fi%
  \fi%
}
\newboolean{@anim@singleref}

%create XObjects of all button faces
\ifnum\if@anim@dvips 1\else\if@anim@dvisvgm 1\else0\fi\fi=1
  %dvips .OR. dvisvgm
  %stroking commands
  \def\@anim@btnend{%
    0.5 setlinewidth
    1 setlinecap
    1 setlinejoin
    6.5 1 moveto
    1 1 1 6.5 2 arct
    1 6.5 lineto
    1 14 6.5 14 2 arct
    6.5 14 lineto
    14 14 14 6.5 2 arct
    14 6.5 lineto
    14 1 6.5 1 2 arct
    closepath
    \ifx\empty\@anim@bg\empty\else
      gsave \@anim@bg\space bgfill grestore
    \fi
    \@anim@fg\space
    stroke
    %
    1 setlinewidth
    4.5 4.7 moveto
    8.6 7.5 lineto
    4.5 10.3 lineto
    stroke
    0 setlinejoin
    10.0 4.7 moveto
    10.0 10.3 lineto
    stroke
  }
  \def\@anim@btnstep{%
    0.5 setlinewidth
    1 setlinecap
    1 setlinejoin
    6.5 1 moveto
    1 1 1 6.5 2 arct
    1 6.5 lineto
    1 14 6.5 14 2 arct
    6.5 14 lineto
    14 14 14 6.5 2 arct
    14 6.5 lineto
    14 1 6.5 1 2 arct
    closepath
    \ifx\empty\@anim@bg\empty\else
      gsave \@anim@bg\space bgfill grestore
    \fi
    \@anim@fg\space
    stroke
    %
    1 setlinewidth
    5.5 4.7 moveto
    9.6 7.5 lineto
    5.5 10.3 lineto
    stroke
  }
  \def\@anim@btnplay{%
    0.5 setlinewidth
    1 setlinecap
    1 setlinejoin
    0 1 moveto
    14 1 14 6.5 2 arct
    14 6.5 lineto
    14 14 1 14 2 arct
    0 14 lineto
    \ifx\empty\@anim@bg\empty\else
      gsave closepath \@anim@bg\space bgfill grestore
    \fi
    \@anim@fg\space
    stroke
    0.1 setlinewidth
    0 14 moveto
    0 1 lineto
    stroke
    %
    1 setlinewidth
    5 4 moveto
    5 11 lineto
    10 7.5 lineto
    closepath
    stroke
  }
  \def\@anim@btnpause{%
    0.5 setlinewidth
    1 setlinecap
    1 setlinejoin
    0 1 moveto
    14 1 14 6.5 2 arct
    14 6.5 lineto
    14 14 1 14 2 arct
    0 14 lineto
    \ifx\empty\@anim@bg\empty\else
      gsave closepath \@anim@bg\space bgfill grestore
    \fi
    \@anim@fg\space
    stroke
    %
    0 setlinecap
    2 setlinewidth
    2.2 4 moveto
    2.2 11 lineto
    stroke
  }
  \def\@anim@btnminus{%
    0.5 setlinewidth
    1 setlinecap
    1 setlinejoin
    6.5 1 moveto
    1 1 1 6.5 2 arct
    1 6.5 lineto
    1 14 6.5 14 2 arct
    6.5 14 lineto
    14 14 14 6.5 2 arct
    14 6.5 lineto
    14 1 6.5 1 2 arct
    closepath
    \ifx\empty\@anim@bg\empty\else
      gsave \@anim@bg\space bgfill grestore
    \fi
    \@anim@fg\space
    stroke
    %
    1.0 setlinewidth
    0 setlinecap
    4.7 7.5 moveto
    10.3 7.5 lineto
    stroke
  }
  \def\@anim@btnplus{%
    0.5 setlinewidth
    1 setlinecap
    1 setlinejoin
    6.5 1 moveto
    1 1 1 6.5 2 arct
    1 6.5 lineto
    1 14 6.5 14 2 arct
    6.5 14 lineto
    14 14 14 6.5 2 arct
    14 6.5 lineto
    14 1 6.5 1 2 arct
    closepath
    \ifx\empty\@anim@bg\empty\else
      gsave \@anim@bg\space bgfill grestore
    \fi
    \@anim@fg\space
    stroke
    %
    1.0 setlinewidth
    0 setlinecap
    4.7 7.5 moveto
    10.3 7.5 lineto
    7.5 4.7  moveto
    7.5 10.3 lineto
    stroke
  }
  \def\@anim@btnresetbg{% Reset-button background for dvisvgm
    6.5 1 moveto
    1 1 1 6.5 2 arct
    1 6.5 lineto
    1 14 6.5 14 2 arct
    6.5 14 lineto
    14 14 14 6.5 2 arct
    14 6.5 lineto
    14 1 6.5 1 2 arct
    closepath
    \@anim@bg\space fill
  }
  \def\@anim@btnreset{%
    0.5 setlinewidth
    1 setlinecap
    1 setlinejoin
    6.5 1 moveto
    1 1 1 6.5 2 arct
    1 6.5 lineto
    1 14 6.5 14 2 arct
    6.5 14 lineto
    14 14 14 6.5 2 arct
    14 6.5 lineto
    14 1 6.5 1 2 arct
    closepath
    \ifx\empty\@anim@bg\empty\else\if@anim@dvisvgm\else
      gsave \@anim@bg\space bgfill grestore
    \fi\fi
    \@anim@fg\space
    stroke
    %
    1 setlinewidth
    7.5 4.7 moveto
    7.5 10.3 lineto
    stroke
    0 setlinejoin
    3.5 5 moveto
    7 7.5 lineto
    3.5 10 lineto
    closepath
    fill
    11.5 5 moveto
    8 7.5 lineto
    11.5 10 lineto
    closepath
    fill
    1 setlinewidth
    2 setlinecap
    3 7.5 moveto
    3.5 7.5 lineto
    12 7.5 moveto
    11.5 7.5 lineto
    stroke
  }
  \def\@anim@makebutton#1#2{% #1: name ; #2:current colour, alpha, ...
    \edef\@anim@arg{#1}%
    \ifx\@anim@EndLeft\@anim@arg%
      \@anim@xbutton{EndLeft}{%
        \if@anim@dvisvgm\else\ifx\@anim@lscape\@anim@@lscape
          [0 1 -1 0 15 0] concat
        \fi\fi%
        [-1 0 0 1 15 0] concat
        \@anim@btnend}{#2}%
    \fi%
    \ifx\@anim@EndRight\@anim@arg%
      \@anim@xbutton{EndRight}{%
        \if@anim@dvisvgm\else\ifx\@anim@lscape\@anim@@lscape
          [0 1 -1 0 15 0] concat
        \fi\fi%
        \@anim@btnend}{#2}%
    \fi%
    \ifx\@anim@Minus\@anim@arg%
      \@anim@xbutton{Minus}{%
        \if@anim@dvisvgm\else\ifx\@anim@lscape\@anim@@lscape
          [0 1 -1 0 15 0] concat
        \fi\fi%
        \@anim@btnminus}{#2}%
    \fi%
    \ifx\@anim@PauseLeft\@anim@arg%
      \@anim@xbutton{PauseLeft}{%
        \if@anim@dvisvgm\else\ifx\@anim@lscape\@anim@@lscape
          [0 1 -1 0 15 0] concat
        \fi\fi%
        [-1 0 0 1 15 0] concat
        \@anim@btnpause}{#2}%
    \fi%
    \ifx\@anim@PauseRight\@anim@arg%
      \@anim@xbutton{PauseRight}{%
        \if@anim@dvisvgm\else\ifx\@anim@lscape\@anim@@lscape
          [0 1 -1 0 15 0] concat
        \fi\fi%
        \@anim@btnpause}{#2}%
    \fi%
    \ifx\@anim@PlayLeft\@anim@arg%
      \@anim@xbutton{PlayLeft}{%
        \if@anim@dvisvgm\else\ifx\@anim@lscape\@anim@@lscape
          [0 1 -1 0 15 0] concat
        \fi\fi%
        [-1 0 0 1 15 0] concat
        \@anim@btnplay}{#2}%
    \fi%
    \ifx\@anim@PlayRight\@anim@arg%
      \@anim@xbutton{PlayRight}{%
        \if@anim@dvisvgm\else\ifx\@anim@lscape\@anim@@lscape
          [0 1 -1 0 15 0] concat
        \fi\fi%
        \@anim@btnplay}{#2}%
    \fi%
    \ifx\@anim@Plus\@anim@arg%
      \@anim@xbutton{Plus}{\@anim@btnplus}{#2}%
    \fi%
    \ifx\@anim@Reset\@anim@arg%
      \if@anim@dvisvgm% Reset btn has filled foreground parts. In dvisvgm, for
        % filling and setting transparency correctly, BG and FG are treated
        % separately.
        \ifcsname btn#1:#2\endcsname\else%
          \ifx\empty\@anim@bg\empty\else%
            \@anim@xbutton{ResetBG}{\@anim@btnresetbg}{#2}%
          \fi%
          \begingroup%
            \def\@anim@alpha{}%
            \@anim@xbutton{ResetFG}{\@anim@btnreset}{#2}%
          \endgroup%
          \ifx\empty\@anim@bg\empty\else%
            \setbox\@anim@box=\hbox to \@anim@btnsize {%
              \vbox to \@anim@btnsize {\vss%
                \@anim@refxform{\@anim@getkeyval{btnResetBG:#2}}%
                \@anim@refxform{\@anim@lastxform}%
              }\hss%
            }%
            \@anim@xform{0}{1}{}{}{\@anim@box}%
          \fi%
          \@anim@newkey{btn#1:#2}{\@anim@lastxform}%
        \fi%
      \else%
        \@anim@xbutton{Reset}{%
          \ifx\@anim@lscape\@anim@@lscape [0 1 -1 0 15 0] concat \fi%
          \@anim@btnreset}{#2}%
      \fi%
    \fi%
    \ifx\@anim@StepLeft\@anim@arg%
      \@anim@xbutton{StepLeft}{%
        \if@anim@dvisvgm\else\ifx\@anim@lscape\@anim@@lscape
          [0 1 -1 0 15 0] concat
        \fi\fi%
        [-1 0 0 1 15 0] concat
        \@anim@btnstep}{#2}%
    \fi%
    \ifx\@anim@StepRight\@anim@arg%
      \@anim@xbutton{StepRight}{%
        \if@anim@dvisvgm\else\ifx\@anim@lscape\@anim@@lscape
          [0 1 -1 0 15 0] concat
        \fi\fi%
        \@anim@btnstep}{#2}%
    \fi%
  }
  \def\@anim@xbutton#1#2#3{% #1: name; #2: stroking commands,
    % only create if button face doesn't exist yet
    \ifcsname btn#1:#3\endcsname\else% #3: current colour+alpha+ ... combination
      \setbox\@anim@box=\hbox to 15bp {\vbox to 15bp {\vss%
        \@anim@literal{}{%
          save
          \ifx\empty\@anim@alpha\empty%
            /bgfill {fill} bind def
          \else\if@anim@dvisvgm
            /bgfill {fill} bind def
          \else%
            /bgfill {%
              /.setfillconstantalpha where {%
                pop gsave \@anim@alpha\space .setfillconstantalpha fill
                1 .setfillconstantalpha newpath fill grestore newpath%
              }{%
                /.setopacityalpha where {%
                  pop gsave \@anim@alpha\space .setopacityalpha fill
                  1 .setopacityalpha newpath fill grestore newpath%
                }{%
                  %Distiller
                  gsave
                  mark /ca \@anim@alpha\space/SetTransparency pdfmark fill
                  mark /ca 1.0 /SetTransparency pdfmark newpath fill
                  grestore newpath%
                } ifelse%
              } ifelse%
            } bind def
          \fi\fi%
          #2
          restore%
        }%
      }\hss}%
      \if@anim@dvisvgm%
        \setbox\@anim@box=\hbox{%
          \resizebox*{!}{\@anim@btnsize}{\box\@anim@box}}%
      \fi%
      %distill box into XObject
      \@anim@xform{0}{1}{}{%
        \ifx\empty\@anim@alpha\empty\else\if@anim@dvisvgm%
            fill-opacity='\@anim@alpha'%
        \fi\fi%
      }{\@anim@box}%
      \@anim@newkey{btn#1:#3}{\@anim@lastxform}%
    \fi%
  }
\else %pdftex/dvipdfmx/xetex
  %stroking commands
  \def\@anim@btnend{%
    5 w
    1 J
    1 j
    \@anim@fg\space
    65 10 m
    30 10 l
    18.957 10 10 18.957 10 30 c
    10 120 l
    10 131.043 18.957 140 30 140 c
    120 140 l
    131.043 140 140 131.043 140 120 c
    140 30 l
    140 18.957 131.043 10 120 10 c
    \ifx\empty\@anim@bg\empty s
    \else\@anim@bg\space b \fi
    %
    10 w
    45 47 m
    86 75 l
    45 103 l
    S
    0 j
    100 47 m
    100 103 l
    S%
  }
  \def\@anim@btnstep{%
    5 w
    1 J
    1 j
    \@anim@fg\space
    65 10 m
    30 10 l
    18.957 10 10 18.957 10 30 c
    10 120 l
    10 131.043 18.957 140 30 140 c
    120 140 l
    131.043 140 140 131.043 140 120 c
    140 30 l
    140 18.957 131.043 10 120 10 c
    \ifx\empty\@anim@bg\empty s
    \else\@anim@bg\space b \fi
    %
    10 w
    55 47 m
    96 75 l
    55 103 l
    S%
  }
  \def\@anim@btnplay{%
    5 w
    1 J
    1 j
    \@anim@fg\space
    0 10 m
    120 10 l
    131.043 10 140 18.957 140 30 c
    140 120 l
    140 131.043 131.043 140 120 140 c
    0 140 l
    \ifx\empty\@anim@bg\empty S
    \else\@anim@bg\space B \fi
    1 w
    0 140 m
    0 10 l
    S
    %
    10 w
    50 40 m
    50 110 l
    100 75 l
    s%
  }
  \def\@anim@btnpause{%
    5 w
    1 J
    1 j
    \@anim@fg\space
    0 10 m
    120 10 l
    131.043 10 140 18.957 140 30 c
    140 120 l
    140 131.043 131.043 140 120 140 c
    0 140 l
    \ifx\empty\@anim@bg\empty S
    \else\@anim@bg\space B \fi
    %
    0 J
    20 w
    22 40 m
    22 110 l
    S%
  }
  \def\@anim@btnminus{%
    5 w
    1 J
    1 j
    \@anim@fg\space
    65 10 m
    30 10 l
    18.957 10 10 18.957 10 30 c
    10 120 l
    10 131.043 18.957 140 30 140 c
    120 140 l
    131.043 140 140 131.043 140 120 c
    140 30 l
    140 18.957 131.043 10 120 10 c
    \ifx\empty\@anim@bg\empty s
    \else\@anim@bg\space b \fi
    %
    10 w
    0 J
    47 75 m
    103 75 l
    S%
  }
  \def\@anim@btnplus{%
    5 w
    1 J
    1 j
    \@anim@fg\space
    65 10 m
    30 10 l
    18.957 10 10 18.957 10 30 c
    10 120 l
    10 131.043 18.957 140 30 140 c
    120 140 l
    131.043 140 140 131.043 140 120 c
    140 30 l
    140 18.957 131.043 10 120 10 c
    \ifx\empty\@anim@bg\empty s
    \else\@anim@bg\space b \fi
    %
    10 w
    0 J
    47 75 m
    103 75 l
    75 47 m
    75 103 l
    S%
  }
  \def\@anim@btnreset{%
    5 w
    1 J
    1 j
    \@anim@fg\space
    65 10 m
    30 10 l
    18.957 10 10 18.957 10 30 c
    10 120 l
    10 131.043 18.957 140 30 140 c
    120 140 l
    131.043 140 140 131.043 140 120 c
    140 30 l
    140 18.957 131.043 10 120 10 c
    \ifx\empty\@anim@bg\empty s
    \else\@anim@bg\space b \fi
    %
    10 w
    75 47 m
    75 103 l
    S
    \@anim@@@fg\space
    \ifx\empty\@anim@alpha\empty\else/R2 gs \fi
    35 50 m
    70 75 l
    35 100 l
    f
    115 50 m
    80 75 l
    115 100 l
    f
    2 J
    0 j
    30 75 m
    35 75 l
    120 75 m
    115 75 l
    S%
  }
  \def\@anim@makebutton#1#2{% #1: name ; #2: current colour, alpha, ...
    \edef\@anim@arg{#1}%
    \ifx\@anim@EndLeft\@anim@arg%
      \@anim@xbutton{EndLeft}{%
        q -0.1 0 0 0.1 15 0 cm
        \ifx\@anim@lscape\@anim@@lscape 0 -1 -1 0 150 150 cm \fi%
        \@anim@btnend\space Q%
      }{#2}%
    \fi%
    \ifx\@anim@EndRight\@anim@arg%
      \@anim@xbutton{EndRight}{%
        q 0.1 0 0 0.1 0 0 cm
        \ifx\@anim@lscape\@anim@@lscape 0 1 -1 0 150 0 cm \fi%
        \@anim@btnend\space Q%
      }{#2}%
    \fi%
    \ifx\@anim@Minus\@anim@arg%
      \@anim@xbutton{Minus}{%
        q 0.1 0 0 0.1 0 0 cm
        \ifx\@anim@lscape\@anim@@lscape 0 1 -1 0 150 0 cm \fi%
        \@anim@btnminus\space Q%
      }{#2}%
    \fi%
    \ifx\@anim@PauseLeft\@anim@arg%
      \@anim@xbutton{PauseLeft}{%
        q -0.1 0 0 0.1 15 0 cm
        \ifx\@anim@lscape\@anim@@lscape 0 -1 -1 0 150 150 cm \fi%
        \@anim@btnpause\space Q%
      }{#2}%
    \fi%
    \ifx\@anim@PauseRight\@anim@arg%
      \@anim@xbutton{PauseRight}{%
        q 0.1 0 0 0.1 0 0 cm
        \ifx\@anim@lscape\@anim@@lscape 0 1 -1 0 150 0 cm \fi%
        \@anim@btnpause\space Q%
      }{#2}%
    \fi%
    \ifx\@anim@PlayLeft\@anim@arg%
      \@anim@xbutton{PlayLeft}{%
        q -0.1 0 0 0.1 15 0 cm
        \ifx\@anim@lscape\@anim@@lscape 0 -1 -1 0 150 150 cm \fi%
        \@anim@btnplay\space Q%
      }{#2}%
    \fi%
    \ifx\@anim@PlayRight\@anim@arg%
      \@anim@xbutton{PlayRight}{%
        q 0.1 0 0 0.1 0 0 cm
        \ifx\@anim@lscape\@anim@@lscape 0 1 -1 0 150 0 cm \fi%
        \@anim@btnplay\space Q%
      }{#2}%
    \fi%
    \ifx\@anim@Plus\@anim@arg%
      \@anim@xbutton{Plus}{%
        q 0.1 0 0 0.1 0 0 cm
        \@anim@btnplus\space Q%
      }{#2}%
    \fi%
    \ifx\@anim@Reset\@anim@arg%
      \@anim@xbutton{Reset}{%
        q 0.1 0 0 0.1 0 0 cm
        \ifx\@anim@lscape\@anim@@lscape 0 1 -1 0 150 0 cm \fi%
        \@anim@btnreset\space Q%
      }{#2}%
    \fi%
    \ifx\@anim@StepLeft\@anim@arg%
      \@anim@xbutton{StepLeft}{%
        q -0.1 0 0 0.1 15 0 cm
        \ifx\@anim@lscape\@anim@@lscape 0 -1 -1 0 150 150 cm \fi%
        \@anim@btnstep\space Q%
      }{#2}%
    \fi%
    \ifx\@anim@StepRight\@anim@arg%
      \@anim@xbutton{StepRight}{%
        q 0.1 0 0 0.1 0 0 cm
        \ifx\@anim@lscape\@anim@@lscape 0 1 -1 0 150 0 cm \fi%
        \@anim@btnstep\space Q%
      }{#2}%
    \fi%
  }
  %XObject creation
  \def\@anim@xbutton#1#2#3{% #1 name, #2 stroking commands, #3 @anim@num
    \ifcsname btn#1:#3\endcsname\else% only create if button face doesn't% exist yet
      \edef\@anim@arg{#1}%
      \@anim@streamobj{%
        /Type/XObject/Subtype/Form/BBox [0 0 15 15]%
        \ifx\empty\@anim@alpha\empty\else%
          /Resources <<%
            /ExtGState <<%
              /R1 <</Type/ExtGState/ca \@anim@alpha>>%
              \ifx\@anim@Reset\@anim@arg%
                /R2 <</Type/ExtGState/ca 1.0>>%
              \fi%
            >>%
          >>%
        \fi%
      }{\ifx\empty\@anim@alpha\empty\else/R1 gs\fi\space #2}%
      \@anim@newkey{btn#1:#3}{\@anim@lastobj}%
    \fi%
  }
\fi

%determines file type of the sequence
\def\zap@finalspace#1 \@nil{\unquote@name{#1}}
\ifpdf
  \def\@anim@getpath#1#2{% #2: empty | user provided file extension
    \ifx\@empty#2\@empty%
      \gdef\@anim@ext{.pdf}% we start with `pdf'
      \IfFileExists{#1\@anim@ext}{}{%
      \gdef\@anim@ext{.mps}%
      \IfFileExists{#1\@anim@ext}{}{%
      \gdef\@anim@ext{.png}%
      \IfFileExists{#1\@anim@ext}{}{%
      \gdef\@anim@ext{.jpg}%
      \IfFileExists{#1\@anim@ext}{}{%
      \gdef\@anim@ext{.jpeg}%
      \IfFileExists{#1\@anim@ext}{}{%
      \gdef\@anim@ext{.jp2}%
      \IfFileExists{#1\@anim@ext}{}{%
      \gdef\@anim@ext{.j2k}%
      \IfFileExists{#1\@anim@ext}{}{%
      \gdef\@anim@ext{.jpx}%
      \IfFileExists{#1\@anim@ext}{}{%
        \PackageError{animate}{%
          None of the files\MessageBreak%
          `#1.pdf',\MessageBreak%
          `#1.mps',\MessageBreak%
          `#1.png',\MessageBreak%
          `#1.jpg',\MessageBreak%
          `#1.jpeg',\MessageBreak%
          `#1.jp2',\MessageBreak%
          `#1.j2k' or\MessageBreak%
          `#1.jpx',\MessageBreak%
          could be found.\MessageBreak%
          Wrong file type? Mis-spelled file name?%
        }{}%
      }}}}}}}}%
    \else%
      \gdef\@anim@ext{.#2}%
      \IfFileExists{#1\@anim@ext}{}{%
        \PackageError{animate}{%
          File `#1.#2' could not be found.\MessageBreak%
          Wrong file type? Mis-spelled file name?%
        }{}%
      }%
    \fi%
    \xdef\@anim@pathtofile{\expandafter\zap@finalspace\@filef@und\@nil}%
  }
\else
  \if@anim@dvipdfmx %XeLaTeX, dvipdfmx
    \def\@anim@getpath#1#2{%
      \ifx\@empty#2\@empty%
        \gdef\@anim@ext{.pdf}% we start with `pdf'
        \IfFileExists{#1\@anim@ext}{}{%
        \gdef\@anim@ext{.mps}%
        \IfFileExists{#1\@anim@ext}{}{%
        \gdef\@anim@ext{.eps}%
        \IfFileExists{#1\@anim@ext}{}{%
        \gdef\@anim@ext{.ps}%
        \IfFileExists{#1\@anim@ext}{}{%
        \gdef\@anim@ext{.png}%
        \IfFileExists{#1\@anim@ext}{}{%
        \gdef\@anim@ext{.jpg}%
        \IfFileExists{#1\@anim@ext}{}{%
        \gdef\@anim@ext{.jpeg}%
        \IfFileExists{#1\@anim@ext}{}{%
        \gdef\@anim@ext{.bmp}%
        \IfFileExists{#1\@anim@ext}{}{%
          \PackageError{animate}{%
            None of the files\MessageBreak%
            `#1.pdf',\MessageBreak%
            `#1.mps',\MessageBreak%
            `#1.eps',\MessageBreak%
            `#1.ps',\MessageBreak%
            `#1.png',\MessageBreak%
            `#1.jpg',\MessageBreak%
            `#1.jpeg' or\MessageBreak%
            `#1.bmp'\MessageBreak%
            could be found.\MessageBreak%
            Wrong file type? Mis-spelled file name?%
          }{}%
        }}}}}}}}%
      \else%
        \gdef\@anim@ext{.#2}%
        \IfFileExists{#1\@anim@ext}{}{%
          \PackageError{animate}{%
            File `#1.#2' could not be found.\MessageBreak%
            Wrong file type? Mis-spelled file name?%
          }{}%
        }%
      \fi%
      \xdef\@anim@pathtofile{\expandafter\zap@finalspace\@filef@und\@nil}%
    }
  \else
    \if@anim@dvisvgm
      \def\@anim@getpath#1#2{%
        \ifx\@empty#2\@empty%
          \gdef\@anim@ext{.pdf}% we start with `pdf'
          \IfFileExists{#1\@anim@ext}{}{%
          \gdef\@anim@ext{.eps}%
          \IfFileExists{#1\@anim@ext}{}{%
          \gdef\@anim@ext{.ps}%
          \IfFileExists{#1\@anim@ext}{}{%
          \gdef\@anim@ext{.mps}%
          \IfFileExists{#1\@anim@ext}{}{%
          \gdef\@anim@ext{.svg}%
          \IfFileExists{#1\@anim@ext}{}{%
          \gdef\@anim@ext{.png}%
          \IfFileExists{#1\@anim@ext}{}{%
          \gdef\@anim@ext{.jpg}%
          \IfFileExists{#1\@anim@ext}{}{%
          \gdef\@anim@ext{.jpeg}%
          \IfFileExists{#1\@anim@ext}{}{%
            \PackageError{animate}{%
              None of the files\MessageBreak%
              `#1.pdf',\MessageBreak%
              `#1.eps',\MessageBreak%
              `#1.ps',\MessageBreak%
              `#1.mps',\MessageBreak%
              `#1.svg',\MessageBreak%
              `#1.png',\MessageBreak%
              `#1.jpg',\MessageBreak%
              `#1.jpeg' or\MessageBreak%
              could be found.\MessageBreak%
              Wrong file type? Mis-spelled file name?%
            }{}%
          }}}}}}}}%
        \else%
          \gdef\@anim@ext{.#2}%
          \IfFileExists{#1\@anim@ext}{}{%
            \PackageError{animate}{%
              File `#1.#2' could not be found.\MessageBreak%
              Wrong file type? Mis-spelled file name?%
            }{}%
          }%
        \fi%
        \xdef\@anim@pathtofile{\expandafter\zap@finalspace\@filef@und\@nil}%
      }
    \else %dvips
      \def\@anim@getpath#1#2{%
        \ifx\@empty#2\@empty%
          \gdef\@anim@ext{.eps}% we start with `eps'
          \IfFileExists{#1\@anim@ext}{}{%
          \gdef\@anim@ext{.mps}%
          \IfFileExists{#1\@anim@ext}{}{%
          \gdef\@anim@ext{.ps}%
          \IfFileExists{#1\@anim@ext}{}{%
            \PackageError{animate}{%
              None of the files\MessageBreak%
              `#1.eps',\MessageBreak%
              `#1.mps' or\MessageBreak%
              `#1.ps'\MessageBreak%
              could be found.\MessageBreak%
              Wrong file type? Mis-spelled file name?%
            }{}%
          }}}%
        \else%
          \gdef\@anim@ext{.#2}%
          \IfFileExists{#1\@anim@ext}{}{%
            \PackageError{animate}{%
              File `#1.#2' could not be found.\MessageBreak%
              Wrong file type? Mis-spelled file name?%
            }{}%
          }%
        \fi%
        \xdef\@anim@pathtofile{\expandafter\zap@finalspace\@filef@und\@nil}%
      }
    \fi
  \fi
\fi

%counts embedded animations
\newcount\@anim@num\@anim@num=\z@%

%current frame
\newcount\@anim@curframe
\newcount\@anim@curframe@zb %zero based

%draftbox
\def\@anim@draftbox{%
  \begingroup%
  \vtop{% draw boxes
    \offinterlineskip%
    \hbox{\raisebox{-\@anim@animdepth}{%
      \frame{%
        \rule{\z@}{\@anim@animtotalheight}\hskip\@anim@animwidth%
      }%
    }}%
    \if@anim@controls%
      \setlength{\@anim@tmpdima}{\@anim@btnsize}%
      \setlength{\@anim@tmpdimb}{\z@}%
      \vskip 0.1\@anim@tmpdima%
      \hbox to \@anim@animwidth {%
        \if@anim@ctrlleft\hspace{\@anim@ctrlsindent}\fi%
        \ifnum\if@anim@ctrlright\@ne\else%
          \if@anim@ctrlcentre\@ne\else\z@\fi\fi=\@ne\hss\fi%
        \setboolean{@anim@controls@firstgrp}{false}%
        \if@anim@controls@stop%
          \setboolean{@anim@controls@firstgrp}{true}%
          \addtolength{\@anim@tmpdimb}{2\@anim@tmpdima}%
        \fi%
        \if@anim@controls@step%
          \setboolean{@anim@controls@firstgrp}{true}%
          \addtolength{\@anim@tmpdimb}{2\@anim@tmpdima}%
        \fi%
        \if@anim@controls@play%
          \setboolean{@anim@controls@firstgrp}{true}%
          \addtolength{\@anim@tmpdimb}{2\@anim@tmpdima}%
        \fi%
        \if@anim@controls@firstgrp%
          \frame{\phantom{\rule{\@anim@tmpdimb}{\@anim@tmpdima}}}%
        \fi%
        \if@anim@controls@speed%
          \if@anim@controls@firstgrp\hskip 0.3\@anim@tmpdima\fi%
          \frame{\phantom{\rule{3\@anim@tmpdima}{\@anim@tmpdima}}}%
        \fi%
        \ifnum\if@anim@ctrlleft\@ne\else%
          \if@anim@ctrlcentre\@ne\else\z@\fi\fi=\@ne\hss\fi%
        \if@anim@ctrlright\hspace{\@anim@ctrlsindent}\fi%
      }%
    \fi%
  }%
  \endgroup%
}

%detects multipage PDF and corrects user supplied page range
\def\@anim@checkmultipage#1#2{% no multi-page support in dvips
  \setboolean{@anim@multipage}{false}}%
\if@anim@dvips\else
  \def\@anim@checkmultipage#1#2{% #1: file base name, #2 user provided file ext
    \ifx\@empty#2\@empty%
      \IfFileExists{#1.pdf}{%
        \setboolean{@anim@multipage}{true}%
        \gdef\@anim@ext{.pdf}%
      }{}%
    \else%
      \IfFileExists{#1.#2}{%
        \setboolean{@anim@multipage}{true}%
        \gdef\@anim@ext{.#2}%
      }{}%
    \fi%
    \if@anim@multipage%
      \xdef\@anim@pathtofile{\expandafter\zap@finalspace\@filef@und\@nil}%
      \filename@parse{\@anim@pathtofile}%
      \@anim@getpagecount{\filename@area\filename@base}{\filename@ext}{%
        \@anim@lastpage%
      }%
      \ifdefined\@anim@lastpage\else% double check multipage support
        \setboolean{@anim@multipage}{false}%
      \fi%
    \fi%
    \if@anim@multipage%
      %we need it zero-based
      \edef\@anim@lastpage{\the\numexpr\@anim@lastpage-\@ne\relax}%
      \ifx\@anim@first\@empty%
        \gdef\@anim@first{0}%
      \else%
        \ifnum\@anim@first<\z@\relax\gdef\@anim@first{0}\fi%
        \ifnum\@anim@first>\@anim@lastpage\relax%
          \global\let\@anim@first\@anim@lastpage%
        \fi%
      \fi%
      \ifx\@anim@last\@empty%
        \global\let\@anim@last\@anim@lastpage%
      \else%
        \ifnum\@anim@last<\z@\relax\gdef\@anim@last{0}\fi%
        \ifnum\@anim@last>\@anim@lastpage\relax%
          \global\let\@anim@last\@anim@lastpage%
        \fi%
      \fi%
      \@anim@curframe=\@anim@first%
      \advance\@anim@curframe by \@ne%
      \xdef\@anim@first{\the\@anim@curframe}%
      \@anim@curframe=\@anim@last%
      \advance\@anim@curframe by \@ne%
      \xdef\@anim@last{\the\@anim@curframe}%
    \fi%
  }%
\fi%

%environment for setting LTR typesetting direction
\def\@anim@beginLTR{%
  \ifdefined\textdir% LuaTeX
    \begingroup%
    \edef\@anim@curTxtDir{\the\textdir}%
    \textdir TLT\relax%
  \else%              e-TeX based engines
    \ifnum\TeXXeTstate>\z@\beginL\fi%
  \fi%
}
\def\@anim@endLTR{%
  \ifdefined\textdir%
    \endgroup%
  \else%
    \ifnum\TeXXeTstate>\z@\endL\fi%
  \fi%
}

%user command for embedding animation sequence
% #1: options
% #2: frame rate (fps)
% #3: basename of graphics file sequence (without frame number and extension)
% #4: first frame (integer)
% #5: last frame (integer)
\newcommand{\animategraphics}[5][]{%
  \@anim@endsanitize%
  \if@anim@export\else\leavevmode\fi%
  \if@anim@grxloaded\else%
    \if@anim@dvipdfmx\if@anim@xetex%
      \@anim@missing{graphicx}\else%
      \@anim@missing[dvipdfmx]{graphicx}\fi%
    \else%
      \@anim@missing{graphicx}%
    \fi%
  \fi%
  \@anim@reset% to default settings
  \if@anim@dvisvgm%
    \special{dvisvgm:raw
      <!-- animation by LaTeX-package `animate',
        https://ctan.org/tex-archive/macros/latex/contrib/animate,
        v. \@anim@version\space-->%
    }%
  \fi%
  \begingroup%
  \@anim@beginLTR%
  \ifcsname Ginput@path\endcsname% make use of graphic[xs] search path
    \let\input@path\Ginput@path%
  \fi%
  \SetKeys[anim@user]{#1}%
  %store current abs. page num in macro \@anim@abspage
  \if@anim@dvisvgm\if@anim@draft\else%
    \zref@labelbyprops{anim@abspage\the\@anim@num}{abspage}%
    \zref@def@extractdefault{\@anim@abspage}{%
      anim@abspage\the\@anim@num}{abspage}{-1}%
  \fi\fi%
  \ifx\empty\@anim@bg\empty\xdef\@anim@alpha{}\fi%
  \xdef\@anim@btnsize{\the\dimexpr\@anim@btnsize\relax}%
  %correct wrong option combination; totalheight overrides height
  \ifnum\@anim@resizeflags=3\relax% height+totalheight->totalheight
    \global\@anim@resizeflags=\@ne%
  \fi%
  \ifnum\@anim@resizeflags=7\relax% height+totalheight+width->totalheight+width
    \global\@anim@resizeflags=5%
  \fi%
  \ifx\@anim@bb\@empty\else\xdef\@anim@gropts{\@anim@gropts%
    \ifx\@anim@gropts\@empty\else,\fi\@anim@bb}\fi%
  \ifx\@anim@viewport\@empty\else\xdef\@anim@gropts{\@anim@gropts%
    \ifx\@anim@gropts\@empty\else,\fi\@anim@viewport}\fi%
  \ifx\@anim@trim\@empty\else\xdef\@anim@gropts{\@anim@gropts%
    \ifx\@anim@gropts\@empty\else,\fi\@anim@trim}\fi%
  \ifx\@anim@angle\@empty\else\xdef\@anim@gropts{\@anim@gropts%
    \ifx\@anim@gropts\@empty\else,\fi\@anim@angle}\fi%
  \if@anim@hiresbb\xdef\@anim@gropts{\@anim@gropts%
    \ifx\@anim@gropts\@empty\else,\fi hiresbb}\fi%
  \if@anim@interpolate\xdef\@anim@gropts{\@anim@gropts%
    \ifx\@anim@gropts\@empty\else,\fi interpolate}\fi%
  \ifx\@anim@pagebox\@empty\else\xdef\@anim@gropts{\@anim@gropts%
    \ifx\@anim@gropts\@empty\else,\fi\@anim@pagebox}\fi%
  \ifthenelse{\boolean{@anim@autoplay}\OR\boolean{@anim@autoresume}}{%
    \setboolean{@anim@autoplayorresume}{true}%
  }{}%
  \ifthenelse{\NOT\boolean{@anim@controls}\AND\boolean{@anim@step}}{%
    \setboolean{@anim@loop}{true}%
  }{}%
  \if@anim@step%
    %for stepping animations, ignore any `controls' settings except
    \setboolean{@anim@controls@play}{false}%        `step' and `stop'
    \setboolean{@anim@controls@speed}{false}%
    \setboolean{@anim@controls}{false}%
    \if@anim@controls@stop\setboolean{@anim@controls}{true}\fi%
    \if@anim@controls@step\setboolean{@anim@controls}{true}\fi%
  \fi%
  \edef\@anim@base{#3}%
  \edef\@anim@first{#4}%
  \edef\@anim@last{#5}%
  \@anim@checkmultipage{\@anim@base}{\@anim@ftype}%test for multipage file
  \if@anim@multipage\else% cope with wrong user input
    \ifthenelse{\equal{#4}{}\OR\equal{#5}{}}{%
      \PackageError{animate}{%
        Missing frame number in \protect\animategraphics\space command%
      }{}%
    }{}%
    \ifthenelse{\@anim@first<\z@\OR\@anim@last<\z@}{%
      \PackageError{animate}{%
        Negative frame numbers not allowed%
      }{}%
    }{}%
  \fi%
  \def\@anim@relop{>}%
  \let\@anim@numtemplate\@anim@first%
  \ifnum\@anim@first>\@anim@last\relax%
    \edef\@anim@every{-\@anim@every}%
    \def\@anim@relop{<}%
    \let\@anim@numtemplate\@anim@last%
  \fi%
  \global\@anim@curframe=\@anim@first%
  \global\@anim@curframe@zb=\z@%
  \if@anim@export%
    \def\@anim@method{\tw@}%
    \setboolean{@anim@controls}{false}%
    \setboolean{@anim@draft}{false}%
  \fi%
  \if@anim@dvisvgm%
    \def\@anim@method{\tw@}% using ocg-like method for dvisvgm
  \fi%
  \if@anim@draft%
    \if@anim@multipage%
      %store file in a box
      \@anim@filebox{\@anim@pathtofile}{\@anim@first}{\@anim@gropts}{\@anim@box}%
    \else%
      %get file name extension
      \@anim@getpath{\@anim@base\@anim@first}{\@anim@ftype}%
      \@anim@filebox{\@anim@pathtofile}{1}{\@anim@gropts}{\@anim@box}%
    \fi%
    \@anim@scale{\@anim@box}%
    %draw draftbox according to dimensions of the first frame
    \@anim@draftbox%
  \else%
    \xdef\@anim@nfps{#2\space}% current frame rate
    \xdef\@anim@nfps{\expandafter\@anim@zap@space\@anim@nfps\@empty}%
    \ifdim\@anim@nfps pt<\z@%
      \PackageError{animate}{%
        Negative frame rate `\@anim@nfps' is not allowed%
      }{}%
    \fi%
    \global\let\@anim@fps\@anim@nfps%
    %
    \if@anim@export%
      \anim@export%
    \fi%
    %embed content (graphics sequence)
    \loop\ifnum\@anim@curframe\@anim@relop\@anim@last\relax\else%
      \if@anim@multipage%
        %embed graphics
        \@anim@ximage{\the\@anim@num}{\the\@anim@curframe@zb}{%
          \@anim@pathtofile}{\the\@anim@curframe}{\@anim@gropts}%
      \else%
        %get path to current file end its extension
        \@anim@getpath{%
          \@anim@base\@anim@pad{\@anim@numtemplate}{\the\@anim@curframe}%
        }{\@anim@ftype}%
        %embed graphics
        \@anim@ximage{\the\@anim@num}{\the\@anim@curframe@zb}{%
          \@anim@pathtofile}{1}{\@anim@gropts}%
      \fi%
      \global\advance\@anim@curframe by \@anim@every%
      \global\advance\@anim@curframe@zb by \@ne%
    \repeat%
    \xdef\@anim@frames{\the\@anim@curframe@zb}% total number
    \global\@anim@tmpcnt=\@anim@frames%
    \global\advance\@anim@tmpcnt by -\@ne%
    \xdef\@anim@maxframe{\the\@anim@tmpcnt}% highest frame index
    %make animation frames from embedded content ...
    \if@anim@dvisvgm\ifcsname a\the\@anim@num.htbp\endcsname%
      \@anim@beginsvgclip{\the\@anim@num}%
    \fi\fi%
    \if@anim@timeline% ... by building timeline (optional timeline file)
      \@anim@buildtmln{\the\@anim@num}%
    \else% ... or by direct insertion
      \global\@anim@curframe@zb=\z@%
      \whiledo{\@anim@curframe@zb<\@anim@frames}{%
        \@anim@makeframe{\the\@anim@num}{\the\@anim@curframe@zb}{%
          \@anim@getkeyval{img@\the\@anim@curframe@zb},%
        }%
        \global\advance\@anim@curframe@zb by \@ne%
      }%
    \fi%
    \if@anim@dvisvgm\ifcsname a\the\@anim@num.htbp\endcsname%
      \@anim@endsvgclip%
    \fi\fi%
    % if last frame used as poster, write frame num to aux file
    \ifnum\@anim@poster=\@anim@mone\relax%
      \@anim@keytoaux{a\the\@anim@num.poster}{\@anim@maxframe}%
    \fi%
    %insert animation widget & controls
    \if@anim@export\else%
      % insert <script> part
      \if@anim@dvisvgm\@anim@script{\the\@anim@num}\fi%
      \edef\@anim@btnattrs{%
        \@anim@bgcolour:\@anim@fgcolour:\@anim@alpha%
        \if@anim@dvisvgm:\@anim@btnsize:\@anim@abspage\fi%
        \@anim@lscape%
      }%
      \@anim@insertwidgets{\the\@anim@num}{\@anim@btnattrs}%
    \fi%
  \fi%
  \@anim@endLTR%
  \endgroup%
  \global\advance\@anim@num by \@ne%
}%
%adjust catcode of `:' character within \animategraphics
\let\@anim@animategraphics\animategraphics
\def\animategraphics{\@anim@sanitizeColon\@anim@animategraphics}

%inserts animation and control button widgets
\newboolean{@anim@controls@firstgrp}
\def\@anim@insertwidgets#1#2{%#1:@anim@num, #2 btn colour, alpha, ...
  \vtop{%
    \offinterlineskip%
    \hbox{\@anim@animwidget{#1}}%
    \if@anim@controls%
      \setlength{\@anim@tmpdima}{\@anim@btnsize}%
      \vskip 0.1\@anim@tmpdima%
      \hbox to \@anim@animwidth {%
        \if@anim@ctrlleft\hspace{\@anim@ctrlsindent}\fi%
        \ifnum\if@anim@ctrlright\@ne\else%
          \if@anim@ctrlcentre\@ne\else\z@\fi\fi=\@ne\hss\fi%
        \def\@anim@tooltip{}%
        \setboolean{@anim@controls@firstgrp}{false}%
        \if@anim@controls@stop%
          \setboolean{@anim@controls@firstgrp}{true}%
          \@anim@makebutton{EndLeft}{#2}%
          \@anim@buttonwidget{#1}{EndLeft}{EndLeft:#2}%
        \fi%
        \if@anim@controls@step%
          \setboolean{@anim@controls@firstgrp}{true}%
          \@anim@makebutton{StepLeft}{#2}%
          \@anim@buttonwidget{#1}{StepLeft}{StepLeft:#2}%
        \fi%
        \if@anim@controls@play%
          \setboolean{@anim@controls@firstgrp}{true}%
          \@anim@makebutton{PauseLeft}{#2}%
          \@anim@buttonwidget{#1}{PauseLeft}{PauseLeft:#2}%
          \@anim@makebutton{PlayLeft}{#2}%
          \@anim@buttonwidget{#1}{PlayLeft}{PlayLeft:#2}%
          \@anim@buttonwidget{#1}{PlayPauseLeft}{}%
          \@anim@makebutton{PauseRight}{#2}%
          \@anim@buttonwidget{#1}{PauseRight}{PauseRight:#2}%
          \@anim@makebutton{PlayRight}{#2}%
          \@anim@buttonwidget{#1}{PlayRight}{PlayRight:#2}%
          \@anim@buttonwidget{#1}{PlayPauseRight}{}%
        \fi%
        \if@anim@controls@step%
          \setboolean{@anim@controls@firstgrp}{true}%
          \@anim@makebutton{StepRight}{#2}%
          \@anim@buttonwidget{#1}{StepRight}{StepRight:#2}%
        \fi%
        \if@anim@controls@stop%
          \setboolean{@anim@controls@firstgrp}{true}%
          \@anim@makebutton{EndRight}{#2}%
          \@anim@buttonwidget{#1}{EndRight}{EndRight:#2}%
        \fi%
        \if@anim@controls@speed%
          \if@anim@controls@firstgrp\hskip 0.3\@anim@tmpdima\fi%
          \def\@anim@tooltip{/TU (slower)}%
          \@anim@makebutton{Minus}{#2}%
          \@anim@buttonwidget{#1}{Minus}{Minus:#2}%
          \def\@anim@tooltip{/TU (default speed)}%
          \@anim@makebutton{Reset}{#2}%
          \@anim@buttonwidget{#1}{Reset}{Reset:#2}%
          \def\@anim@tooltip{/TU (faster)}%
          \@anim@makebutton{Plus}{#2}%
          \@anim@buttonwidget{#1}{Plus}{Plus:#2}%
        \fi%
        \ifnum\if@anim@ctrlleft\@ne\else%
          \if@anim@ctrlcentre\@ne\else\z@\fi\fi=\@ne\hss\fi%
        \if@anim@ctrlright\hspace{\@anim@ctrlsindent}\fi%
      }%
    \fi%
  }%
}

\newcount\@anim@resizeflags% resizing flags according to options given.

%set animation widget dimensions
\def\@anim@scale#1{% #1: box number
  \begingroup%
    %natural dimensions \width, \height, \depth, \totalheight
    \def\width{\wd#1}%
    \def\height{\ht#1}%
    \def\depth{\dp#1}%
    \let\totalheight\@anim@tmpdimb%
    \setlength{\totalheight}{\dimexpr\height+\depth\relax}%
    %evaluate resizing options
    \xdef\@anim@animwidth{\the\dimexpr\@anim@widtharg\relax}%
    \xdef\@anim@animheight{\the\dimexpr\@anim@heightarg\relax}%
    \xdef\@anim@animtotalheight{\the\dimexpr\@anim@totheightarg\relax}%
  \endgroup%
  %now resize
  \ifcase\@anim@resizeflags% bit 2^2=width, 2^1=height, 2^0=totalhight given
  \or% 1
    \setbox#1=\hbox{\resizebox*{!}{\@anim@animtotalheight}{\box#1}}%
  \or% 2
    \setbox#1=\hbox{\resizebox{!}{\@anim@animheight}{\box#1}}%
  \or\or% 4
    \setbox#1=\hbox{\resizebox{\@anim@animwidth}{!}{\box#1}}%
  \or% 5
    \if@anim@iso%
      \setlength{\@anim@tmpdima}{%
        \totalheightof{\usebox#1}*\ratio{\@anim@animwidth}{\wd#1}}%
      \ifdim\@anim@tmpdima<\z@\@anim@tmpdima=-\@anim@tmpdima\fi%
      \setlength{\@anim@tmpdimb}{\@anim@animtotalheight}%
      \ifdim\@anim@tmpdimb<\z@\@anim@tmpdimb=-\@anim@tmpdimb\fi%
      \ifdim\@anim@tmpdima<\@anim@tmpdimb%
        \setbox#1=\hbox{\resizebox*{\@anim@animwidth}{!}{\box#1}}%
      \else%
        \setbox#1=\hbox{%
          \resizebox*{!}{\@anim@animtotalheight}{\box#1}}%
      \fi%
    \else%
      \setbox#1=\hbox{%
        \resizebox*{\@anim@animwidth}{\@anim@totheightarg}{\box#1}}%
    \fi%
  \or% 6
    \if@anim@iso%
      \setlength{\@anim@tmpdima}{\ht#1*\ratio{\@anim@animwidth}{\wd#1}}%
      \ifdim\@anim@tmpdima<\z@\@anim@tmpdima=-\@anim@tmpdima\fi%
      \setlength{\@anim@tmpdimb}{\@anim@animheight}%
      \ifdim\@anim@tmpdimb<\z@\@anim@tmpdimb=-\@anim@tmpdimb\fi%
      \ifdim\@anim@tmpdima<\@anim@tmpdimb%
        \setbox#1=\hbox{\resizebox{\@anim@animwidth}{!}{\box#1}}%
      \else%
        \setbox#1=\hbox{\resizebox{!}{\@anim@animheight}{\box#1}}%
      \fi%
    \else%
      \setbox#1=\hbox{%
        \resizebox{\@anim@animwidth}{\@anim@animheight}{\box#1}}%
    \fi%
  \fi%
  %apply scale
  \ifdim\@anim@boxscale pt=\p@\else%
    \setbox#1=\hbox{\scalebox{\@anim@boxscale}{\box#1}}%
  \fi%
  %dimensions after resizing
  \xdef\@anim@animwidth{\the\wd#1}%
  \xdef\@anim@animheight{\the\ht#1}%
  \xdef\@anim@animdepth{\the\dp#1}%
  \xdef\@anim@animtotalheight{\the\dimexpr\ht#1+\dp#1\relax}%
}

%interactive Widget annotation serving as animation frame container / that is
%overlayed on the non-interactive frame Widgets or on the page content with
%frame OCGs
\if@anim@dvisvgm
  \def\@anim@animwidget#1{%
    \ifnum\@anim@poster>\@anim@maxframe\relax%
      \PackageError{animate}{%
        Poster frame `\@anim@poster' is out-of-bounds\MessageBreak%
        on input line \the\inputlineno.\MessageBreak%
        Try option `poster=last'%
      }{}%
    \fi%
    \@anim@annot{\@anim@animwidth}{\@anim@animheight}{\@anim@animdepth}{%
      id='anm#1'
      \if@anim@nomouse\else
        cursor='pointer'
      \fi%
    }%
    \@anim@updatebbox{\@anim@animwidth}{\@anim@animheight}{\@anim@animdepth}%
    \hbox to \@anim@animwidth {%
      \vrule width \z@ height \@anim@animheight depth \@anim@animdepth%
      \hss%
    }%
  }
\else
  \def\@anim@animwidget#1{%
    \@anim@pojscript{#1}%
    \@anim@pcjscript{#1}%
    \def\@anim@annotflag{}%
    \ifnum\@anim@poster>\@anim@maxframe\relax%
      \PackageError{animate}{%
        Poster frame `\@anim@poster' is out-of-bounds\MessageBreak%
        on input line \the\inputlineno.\MessageBreak%
        Try option `poster=last'%
      }{}%
    \fi%
    \ifnum\@anim@method=\z@\relax%
      \def\@anim@annotflag{/F 4}% 4=allow printing
      \ifnum\@anim@poster=\@anim@mtwo\relax%
        \ifdefined\@anim@apdummy\else% empty appearance widget
          \setbox\@anim@box=\hbox{\phantom{x}}%
          \@anim@xform{0}{1}{}{}{\@anim@box}%
          \global\let\@anim@apdummy\@anim@lastxform%
          \@anim@widget{1ex}{1ex}{\z@}{%
            /Subtype/Widget%
            /F 2%
            /FT/Btn/Ff 65537%
            /BS <</W 0>>%
            /AP <</N \@anim@lastxform>>%
            /MK <</TP 1/I \@anim@lastxform/IF<</S/A/FB true>>>>%
            /T (0000)%
          }%
        \fi%
        \def\@anim@apmk{\@anim@apdummy}%
      \else%
        \def\@anim@apmk{\@anim@posterap}%
      \fi%
    \else%
      \ifdefined\@anim@apdummy\else% empty appearance dummy
        \setbox\@anim@box=\hbox{\phantom{x}}%
        \@anim@xform{0}{1}{}{}{\@anim@box}%
        \global\let\@anim@apdummy\@anim@lastxform%
      \fi%
      \def\@anim@apmk{\@anim@apdummy}%
    \fi%
    %this is for Foxit compatibility:
    %  Widget annots don't seem to acknowledge /P* events
    %  a non-interactive /Screen annotation for initializing the animation
    %  upon PageOpen/PageVisible events and for clean-up upon PageClose/
    %  PageInvisible events
    \@anim@annot{1ex}{1ex}{\z@}{%
      /Subtype/Screen/F 2%
      /AA <<%
        /PV <</S/JavaScript/JS \@anim@pojscriptobj>>% PageVisible
        /PO <</S/JavaScript/JS \@anim@pojscriptobj>>% PageOpen
        /PI <</S/JavaScript/JS \@anim@pcjscriptobj>>% PageInvisible
        /PC <</S/JavaScript/JS \@anim@pcjscriptobj>>% PageClose
      >>%
    }%
    \@anim@widget{\@anim@animwidth}{\@anim@animheight}{\@anim@animdepth}{%
      /Subtype/Widget%
      /FT/Btn/Ff \if@anim@nomouse 65537\else 65536\fi%
      \@anim@annotflag%
      /BS <</W 0>>%
      /H/N%
      /T (anm#1)%
      \@anim@alttext% %/Contents (...)
      /AA <<%
        /PV <</S/JavaScript/JS \@anim@pojscriptobj>>% PageVisible
        /PO <</S/JavaScript/JS \@anim@pojscriptobj>>% PageOpen
        /PI <</S/JavaScript/JS \@anim@pcjscriptobj>>% PageInvisible
        /PC <</S/JavaScript/JS \@anim@pcjscriptobj>>% PageClose
        \if@anim@nomouse\else%
          /D <</S/JavaScript/JS (\@anim@wdgtDownJS{#1})>>% mouse-down
          /U <</S/JavaScript/JS (\@anim@wdgtUpJS{#1})>>% mouse-up
        \fi%
      >>%
      /AP <</N \@anim@apmk>>%
      /MK <</TP 1/I \@anim@apmk/IF<</S/A/FB true>>>>%
    }%
    \hbox to \@anim@animwidth {%
      \vrule width \z@ height \@anim@animheight depth \@anim@animdepth%
      \hss%
    }%
  }
\fi
%creates control button widget
\newboolean{@anim@js}
\newboolean{@anim@mk}
\if@anim@dvisvgm
  \def\@anim@buttonwidget#1#2#3{% #1:@anim@num, #2:action,
                                % #3:button face and colour combination
    \def\@anim@arg{#2}\@anim@jstrue%
    \ifx\@anim@PlayLeft\@anim@arg%
      \@anim@jsfalse%
      \@anim@newocg{#1}{PlayLeft}%
    \fi%
    \ifx\@anim@PauseLeft\@anim@arg%
      \@anim@jsfalse%
      \@anim@newocg{#1}{PauseLeft}%
      \ocgbase@add@to@off@list{\@anim@curocg}%
    \fi%
    \ifx\@anim@PlayRight\@anim@arg%
      \@anim@jsfalse%
      \@anim@newocg{#1}{PlayRight}%
    \fi%
    \ifx\@anim@PauseRight\@anim@arg%
      \@anim@jsfalse%
      \@anim@newocg{#1}{PauseRight}%
      \ocgbase@add@to@off@list{\@anim@curocg}%
    \fi%
    % insert button face
    \if@anim@js%
      \ifx\@empty#3\@empty\else% PlayPauseLeft/PlayPauseRight don't have btn
        \@anim@refxform{\@anim@getkeyval{btn#3}}%                    % faces
      \fi%
    \else% PlayLeft, PauseLeft, PlayRight, PauseRight
      \ocgbase@oc@bdc{\@anim@curocg}%
      \@anim@refxform{\@anim@getkeyval{btn#3}}%
      \ocgbase@oc@emc%
    \fi%
    % insert widget
    \if@anim@js%
      \@anim@annot{\@anim@btnsize}{\@anim@btnsize}{\z@}{%
        cursor='pointer' onmousedown='%
          event.preventDefault();event.stopPropagation();%
          \@anim@btnJS{#1}{\@anim@arg}%
        '%
      }%
      \@anim@updatebbox{\@anim@btnsize}{\@anim@btnsize}{0pt}%
      \hbox to \@anim@btnsize {%
        \vrule width \z@ height \@anim@btnsize depth \z@%
        \hss%
      }%
    \fi%
  }
\else
  \def\@anim@buttonwidget#1#2#3{%#1:@anim@num, #2:action,
                                %#3:button face and colour+alpha+... combination
    \edef\@anim@arg{#2}\@anim@jstrue\@anim@mktrue%
    \def\@anim@annotflag{/F 0}% visible, but don't print
    \ifx\@anim@PlayLeft\@anim@arg\@anim@jsfalse\fi%       %hidden, don't print |
    \ifx\@anim@PauseLeft\@anim@arg\@anim@jsfalse\def\@anim@annotflag{/F 2}\fi%_|
    \ifx\@anim@PlayRight\@anim@arg\@anim@jsfalse\fi%
    \ifx\@anim@PauseRight\@anim@arg\@anim@jsfalse\def\@anim@annotflag{/F 2}\fi%
    \ifx\@anim@PlayPauseLeft\@anim@arg\@anim@mkfalse\fi%
    \ifx\@anim@PlayPauseRight\@anim@arg\@anim@mkfalse\fi%
    \if@anim@mk\else%
      \ifdefined\@anim@apdummy\else% empty appearance dummy
        \setbox\@anim@box=\hbox{\phantom{x}}%
        \@anim@xform{0}{1}{}{}{\@anim@box}%
        \global\let\@anim@apdummy\@anim@lastxform%
      \fi%
    \fi%
    \@anim@widget{\@anim@btnsize}{\@anim@btnsize}{\z@}{%
      /Subtype/Widget%
      \@anim@annotflag%
      /FT/Btn/Ff \if@anim@js 65536\else 65537\fi%
      /H\if@anim@js/I\else/N\fi%
      /BS <</W 0>>%
      \if@anim@mk%
        /AP <</N \@anim@getkeyval{btn#3}>>%
        /MK <</TP 1/I \@anim@getkeyval{btn#3}/IF<</S/A/FB true>>>>%
      \else%
        /AP <</N \@anim@apdummy>>%
        /MK <</TP 1/I \@anim@apdummy/IF<</S/A/FB true>>>>%
      \fi%
      /T (#1.#2)%
      \@anim@tooltip%
      \if@anim@js%
        /AA <</D <</S/JavaScript/JS (\@anim@btnJS{#1}{\@anim@arg})>>>>%
      \fi%
    }%
    \if@anim@js%
      \hbox to \@anim@btnsize {%
        \vrule width \z@ height \@anim@btnsize depth \z@%
        \hss%
      }%
    \fi%
  }
\fi

\newboolean{@anim@pauseframes} % true if \newframe* is being used
\newboolean{@anim@chfps} % true if \newframe or \newframe* is being used
                         % with optional `new frame rate' argument
\newboolean{@anim@usrjs} % true if user JavaScript provided by user

\newcount\@anim@skipfram % counter for skipped frames

%user environment for animating inline graphics
% #1: options
% #2: frame rate (fps)
\newenvironment{animateinline}[2][]{%
  \@anim@endsanitize%
  \if@anim@export\else\leavevmode\fi%
  \@anim@reset% to default settings
  \if@anim@dvisvgm%
    \special{dvisvgm:raw
      <!-- animation by LaTeX-package `animate',
        https://ctan.org/tex-archive/macros/latex/contrib/animate,
        v. \@anim@version\space-->%
    }%
  \fi%
  \begingroup%
  \@anim@beginLTR%
  \ifdefined\NoHyper\NoHyper\fi% silently suppress any hyperref specials
  \ifcsname Ginput@path\endcsname% make use of graphic[xs] search path
    \let\input@path\Ginput@path%
  \fi%
  \SetKeys[anim@user]{#1}%
  %store current abs. page num in macro \@anim@abspage
  \if@anim@dvisvgm\if@anim@draft\else%
    \zref@labelbyprops{anim@abspage\the\@anim@num}{abspage}%
    \zref@def@extractdefault{\@anim@abspage}{%
      anim@abspage\the\@anim@num}{abspage}{-1}%
  \fi\fi%
  \ifx\empty\@anim@bg\empty\xdef\@anim@alpha{}\fi%
  \xdef\@anim@btnsize{\the\dimexpr\@anim@btnsize\relax}%
  %correct wrong option combination; totalheight overrides height
  \ifnum\@anim@resizeflags=3\relax% height+totalheight->totalheight
    \global\@anim@resizeflags=\@ne%
  \fi%
  \ifnum\@anim@resizeflags=7\relax% height+totalheight+width->totalheight+width
    \global\@anim@resizeflags=5%
  \fi%
  \ifthenelse{\boolean{@anim@autoplay}\OR\boolean{@anim@autoresume}}{%
    \setboolean{@anim@autoplayorresume}{true}%
  }{}%
  \ifthenelse{\NOT\boolean{@anim@controls}\AND\boolean{@anim@step}}{%
    \setboolean{@anim@loop}{true}%
  }{}%
  \if@anim@step%
    %for stepping animations, ignore any `controls' settings except
    \setboolean{@anim@controls@play}{false}%        `step' and `stop'
    \setboolean{@anim@controls@speed}{false}%
    \setboolean{@anim@controls}{false}%
    \if@anim@controls@stop\setboolean{@anim@controls}{true}\fi%
    \if@anim@controls@step\setboolean{@anim@controls}{true}\fi%
  \fi%
  \global\@anim@curframe@zb=\z@%
  \global\@anim@skipfram=\z@%
  \if@anim@export%
    \def\@anim@method{\tw@}%
    \setboolean{@anim@controls}{false}%
    \setboolean{@anim@draft}{false}%
  \fi%
  \if@anim@dvisvgm%
    \def\@anim@method{\tw@}%
  \fi%
  \if@anim@draft\else%
    \if@anim@step\else%
      \xdef\@anim@nfps{-1}%
      \@anim@processfpsarg{#2}%
    \fi%
    \if@anim@export%
      \anim@export%
    \fi%
  \fi%
  \let\newframe\@anim@@newframe%
  \let\multiframe\@anim@multiframe%
  \@anim@beginframe%
  \ignorespaces%
}{%
  \unskip%
  \@anim@endframe{\the\@anim@num}{\the\@anim@curframe@zb}%
  \global\advance\@anim@curframe@zb by \@ne%
  \if@anim@draft\else%
    \xdef\@anim@frames{\the\@anim@curframe@zb}% total number
    \global\@anim@tmpcnt=\@anim@frames%
    \global\advance\@anim@tmpcnt by -\@ne%
    \xdef\@anim@maxframe{\the\@anim@tmpcnt}% highest frame index
    %make animation frames from embedded content ...
    \if@anim@dvisvgm\ifcsname a\the\@anim@num.htbp\endcsname%
      \@anim@beginsvgclip{\the\@anim@num}%
    \fi\fi%
    \if@anim@timeline% ... by building timeline (optional timeline file)
      \@anim@buildtmln{\the\@anim@num}%
    \else% ... or by direct insertion
      \global\@anim@curframe@zb=\z@%
      \whiledo{\@anim@curframe@zb<\@anim@frames}{%
        \@anim@makeframe{\the\@anim@num}{\the\@anim@curframe@zb}{%
          \@anim@getkeyval{img@\the\@anim@curframe@zb},%
        }%
        \global\advance\@anim@curframe@zb by \@ne%
      }%
    \fi%
    \if@anim@dvisvgm\ifcsname a\the\@anim@num.htbp\endcsname%
      \@anim@endsvgclip%
    \fi\fi%
    %insert animation widget & controls
    \if@anim@export\else%
      % insert <script> part
      \if@anim@dvisvgm\@anim@script{\the\@anim@num}\fi%
      \edef\@anim@btnattrs{%
        \@anim@bgcolour:\@anim@fgcolour:\@anim@alpha%
        \if@anim@dvisvgm:\@anim@btnsize:\@anim@abspage\fi%
        \@anim@lscape%
      }%
      \@anim@insertwidgets{\the\@anim@num}{\@anim@btnattrs}%
    \fi%
    % if last frame used as poster, write frame num to aux file
    \ifnum\@anim@poster=\@anim@mone\relax%
      \@anim@keytoaux{a\the\@anim@num.poster}{\@anim@maxframe}%
    \fi%
  \fi%
  \ifdefined\endNoHyper\endNoHyper\fi%
  \@anim@endLTR%
  \endgroup%
  \global\advance\@anim@num by \@ne%
}
\let\@anim@animateinline\animateinline
\def\animateinline{\@anim@sanitizeColon\@anim@animateinline}

% clip animation to final dimensions (dvisvgm)
\def\@anim@beginsvgclip#1{%
  \special{dvisvgm:raw
    <svg overflow="hidden"
      x="{?x}" y="{?(y-\@anim@getkeyval{a#1.htbp})}"
      width="\@anim@getkeyval{a#1.wdbp}" height="\@anim@getkeyval{a#1.thtbp}"
      viewBox="%
        {?x} {?(y-\@anim@getkeyval{a#1.htbp})}
        \@anim@getkeyval{a#1.wdbp} \@anim@getkeyval{a#1.thtbp}%
      ">%
  }%
}%
\def\@anim@endsvgclip{%
  \special{dvisvgm:raw </svg>}%
}

%usercommand for use within `animateinline' environment;
%terminates the current frame and starts a new one
\def\@anim@@newframe{% aliased to \newframe inside `animateinline'
  \unskip%
  \@anim@endframe{\the\@anim@num}{\the\@anim@curframe@zb}%
  \@ifstar\@anim@newframestar\@anim@newframe%
}
\def\@anim@processfpsarg#1{%
  %process frame rate argument
  \global\let\@anim@pfps\@anim@nfps%
  \xdef\@anim@nfps{#1\space}%
  \xdef\@anim@nfps{\expandafter\@anim@zap@space\@anim@nfps\@empty}%
  \ifx\@anim@nfps\@empty% no change of frame rate
    \global\let\@anim@nfps\@anim@pfps%
  \fi%
  \ifnum\@anim@curframe@zb=\z@\relax%
    \global\let\@anim@fps\@anim@nfps% default fps
    \global\let\@anim@nfpsat\@anim@nfps%
  \else%
    \xdef\@anim@nfpsat{\@anim@nfpsat,\@anim@nfps}%
  \fi%
  \ifdim\@anim@nfps pt=\@anim@pfps pt\else%
    \ifnum\@anim@curframe@zb=\z@\relax\else%
      \setboolean{@anim@chfps}{true}% change fps
    \fi%
  \fi%
  \ifdim\@anim@nfps pt<\z@%
    \PackageError{animate}{%
      Negative frame rate `\@anim@nfps' is not allowed%
    }{}%
  \fi%
}%
\newcommand{\@anim@newframe}[1][]{% #1: new frame rate
  \ifnum\@anim@skipfram=\z@\relax%
    \global\advance\@anim@curframe@zb by \@ne%
    \if@anim@draft\else%
      \if@anim@step\else%
        \if@anim@timeline\else%
          %process frame rate argument
          \@anim@processfpsarg{#1}%
        \fi%
      \fi%
    \fi%
  \fi%
  \@anim@beginframe%
  \ignorespaces%
}
\newcommand{\@anim@newframestar}[1][]{% starred variant for pausing animation
  \ifnum\@anim@skipfram=\z@\relax%            #1: new frame rate
    \if@anim@draft\else%
      \if@anim@step\else%
        \if@anim@timeline\else%
          %build JavaScript commands to fill the `pauseAt' array
          \xdef\@anim@pauseat{%
            \@anim@pauseat%
            a\the\@anim@num_pauseAt[\the\@anim@curframe@zb]=1;%
          }%
          \setboolean{@anim@pauseframes}{true}%
        \fi%
      \fi%
    \fi%
    \global\advance\@anim@curframe@zb by \@ne%
    \if@anim@draft\else%
      \if@anim@step\else%
        \if@anim@timeline\else%
          %process frame rate argument
          \@anim@processfpsarg{#1}%
        \fi%
      \fi%
    \fi%
  \fi%
  \@anim@beginframe%
  \ignorespaces%
}

%starts new frame
\def\@anim@beginframe{%
  \begingroup%
  \lrbox{\@anim@box}% store graphics in a box
  \ifdefined\@anim@curTxtDir\textdir\@anim@curTxtDir\fi%
  \@anim@begin%
}

%terminates current frame
\def\@anim@endframe#1#2{%
  \@anim@end%
  \endlrbox%
  \endgroup%
  \ifnum\@anim@skipfram=\z@\relax%
    \ifnum#2=\z@\relax%
      \ifdim\wd\@anim@box=\z@%
        \PackageError{animate}{%
          Content of first frame must not have zero width%
        }{%
          Possible reason: \protect\begin{animateinline}{...}
          immediately followed by \protect\newframe%
        }%
      \fi%
      \ifdim\ht\@anim@box=\z@\ifdim\dp\@anim@box=\z@%
          \PackageError{animate}{%
            Content of first frame must not have zero height%
          }{}%
      \fi\fi%
      %draw draftbox according to dimensions of the first frame
      \if@anim@draft%
        \@anim@scale{\@anim@box}%
        \@anim@draftbox%
      \fi%
    \else% remaining frames
      %deal with zero width or totalheight boxes
      \ifdim\wd\@anim@box=\z@\setbox\@anim@box=\hbox{\phantom{X}}\fi%
      \ifdim\ht\@anim@box=\z@\ifdim\dp\@anim@box=\z@%
        \setbox\@anim@box=\hbox{\phantom{X}}%
      \fi\fi%
    \fi%
    %now create Form XObject of box contents
    \if@anim@draft\else%
      \@anim@xinline{#1}{#2}{\@anim@box}{inline}%
    \fi%
  \fi%
  \global\advance\@anim@skipfram by \@ne%
  \ifnum\@anim@skipfram=\@anim@every\relax%
    \global\@anim@skipfram=\z@%
  \fi%
}

%user command for building loops around parameterized frame content, somewhat
%like \multido (package multido) with embedded \newframe
\newcount\@anim@mulframecnt %takes current loop No. of \multiframe
\newboolean{@anim@insidemulti} % for checking whether we are inside \multiframe
\setboolean{@anim@insidemulti}{false}
\newcommand{\@anim@multiframe}[3]{%
  % aliased to \multiframe inside `animateinline'
  % #1: #repetitions, #2: vars, #3: frame content
  \unskip%
  \if@anim@insidemulti%
    \PackageError{animate}{\protect\multiframe\space cannot be nested}{}%
  \fi%
  \setboolean{@anim@insidemulti}{true}%
  %reset macro for updating variables
  \gdef\@anim@updatevars{}%
  %reset loop counter
  \global\@anim@mulframecnt=\z@%
  %remove spaces from variable declaration list (2nd arg)
  \xdef\@anim@vardecls{#2\space}%
  \xdef\@anim@vardecls{\expandafter\@anim@zap@space\@anim@vardecls\@empty}%
  \ifnum#1<\z@\relax%
    \PackageError{animate}{%
      \protect\multiframe: first argument must be greater than zero%
    }{}%
  \fi%
  %parse list of variable declarations and build macro for updating them
  \expandafter\@anim@parsevars\@anim@vardecls,\@nil%
  \whiledo{\@anim@mulframecnt<#1}{%
    \ifnum\@anim@mulframecnt>\z@\relax%
      \@anim@@newframe%
      \@anim@updatevars% update variables
    \fi%
    \global\advance\@anim@mulframecnt by \@ne%
    \if@anim@draft%
      \global\@anim@mulframecnt=#1%
    \else%
      \def\multiframebreak{\global\@anim@mulframecnt=#1}%
    \fi%
    \ignorespaces%
    #3% execute loop body
    \unskip%
  }%
  \setboolean{@anim@insidemulti}{false}%
  \ignorespaces%
}

%splits list of variable declarations
\def\@anim@parsevars#1,#2\@nil{%
  \ifthenelse{\equal{#1}{}}{}{\@anim@parsedecl#1\@nil=+\@nil\relax}%
  \ifthenelse{\equal{#2}{}}{}{\@anim@parsevars#2\@nil}%
}
%parser for single variable declaration
\def\@anim@parsedecl#1=#2+#3\@nil#4\relax{%
  \ifx\@empty#4\@empty%
    \PackageError{animate}{\protect\multiframe: bad variable declaration}{}%
  \fi%
  \ifthenelse{\equal{#1}{}\OR\equal{#2}{}\OR\equal{#3}{}}{%
    \PackageError{animate}{\protect\multiframe: bad variable declaration}{}%
  }{}%
  \edef\@anim@vartype{\@anim@getvartype#1\@nil}%
  \ifthenelse{\equal{\@anim@vartype}{d}\OR\equal{\@anim@vartype}{D}}{%
    % dimensions, prefix d, D
    %initialise variable
    \setlength{\@anim@tmpdima}{#2}%
    \expandafter\edef\csname#1\endcsname{\number\@anim@tmpdima sp}%
    %global copy that saves current variable value between loops
    \expandafter\xdef\csname#1@old\endcsname{\number\@anim@tmpdima sp}%
    \setlength{\@anim@tmpdimb}{#3}%
    %global copy of increment
    \expandafter\xdef\csname#1@inc\endcsname{\number\@anim@tmpdimb sp}%
    %append script for updating variable to \@anim@updatevars macro
    \g@addto@macro\@anim@updatevars{%
      %new value
      \setlength{\@anim@tmpdima}{\csname#1@old\endcsname}%
      \addtolength{\@anim@tmpdima}{\csname#1@inc\endcsname}%
      \expandafter\edef\csname#1\endcsname{\number\@anim@tmpdima sp}%
      %global copy
      \expandafter\xdef\csname#1@old\endcsname{\number\@anim@tmpdima sp}%
    }%
  }{%
    \ifthenelse{%
      \equal{\@anim@vartype}{n}\OR\equal{\@anim@vartype}{N}%
      \OR\equal{\@anim@vartype}{r}\OR\equal{\@anim@vartype}{R}%
    }{% real numbers, prefix n, N, r, R
      %initialise variable
      \expandafter\edef\csname#1\endcsname{\@anim@fpeval{#2}}%
      %global copy that saves current variable value between loops
      \expandafter\xdef\csname#1@old\endcsname{\@anim@fpeval{#2}}%
      %append script for updating variable to \@anim@updatevars macro
      \g@addto@macro\@anim@updatevars{%
        %new value
        \expandafter\edef\csname#1\endcsname{%
          \@anim@fpeval{#3+\csname#1@old\endcsname}%
        }%
        %global copy
        \expandafter\xdef\csname#1@old\endcsname{\csname#1\endcsname}%
      }%
    }{%
      \ifthenelse{\equal{\@anim@vartype}{i}\OR\equal{\@anim@vartype}{I}}{%
        % integers, prefix i, I
        %initialise variable
        \setcounter{@anim@ltxcnt}{#2}%
        \expandafter\edef\csname#1\endcsname{\the@anim@ltxcnt}%
        %global copy that saves current variable value between loops
        \expandafter\xdef\csname#1@old\endcsname{\the@anim@ltxcnt}%
        \setcounter{@anim@ltxcnt}{#3}%
        %global copy of increment
        \expandafter\xdef\csname#1@inc\endcsname{\the@anim@ltxcnt}%
        %append script for updating variable to \@anim@updatevars macro
        \g@addto@macro\@anim@updatevars{%
          %new value
          \expandafter\global\expandafter\@anim@tmpcnt\csname#1@old\endcsname%
          \global\advance\@anim@tmpcnt by \csname#1@inc\endcsname%
          \expandafter\edef\csname#1\endcsname{\the\@anim@tmpcnt}%
          %global copy
          \expandafter\xdef\csname#1@old\endcsname{\the\@anim@tmpcnt}%
        }%
      }{%
        \PackageError{animate}{%
          \protect\multiframe: wrong name prefix `\@anim@vartype' in%
          \MessageBreak variable `#1'.\MessageBreak%
          Use any of d, D (dimensions), i, I (integer numbers)\MessageBreak%
          or n, N, r, R (real numbers) as the first letter to\MessageBreak%
          specify the variable type%
        }{}%
      }%
    }%
  }%
}
%get initial letter from variable name (the variable type, as with \multido)
\def\@anim@getvartype#1#2\@nil{#1}%

%prints zero padded integers
% #1: arbitrary integer number as template specifying the
%     width, e. g. `987654' for a width of 6 digits
% #2: the number to be formatted
\def\@anim@pad#1#2{%
  \@anim@@pad{\@anim@template{0}{#1}}{#2}%
}
%low level macros used by \@anim@pad
\def\@anim@@pad#1#2{% #1: string of zeros specifying width, #2 number
  \ifnum1#2<1#1
    \@anim@@pad{#1}{0#2}%
  \else%
    #2%
  \fi%
}%
\def\@anim@template#1#2{% create template (string of zeros) from given num
  \ifnum10#1>1#2
    #1%
  \else%
    \@anim@template{0#1}{#2}%
  \fi%
}%

\begingroup\catcode`\_=13\let_\space
\xdef\@anim@nusewarning{%
  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\MessageBreak%
  @_There_are_unused_animation_transparencies._@\MessageBreak%
  @_See_the_transcript_file____________________@\MessageBreak%
  @___``\jobname.log''\MessageBreak%
  @_for_additional_information!________________@\MessageBreak%
  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%
}
\endgroup

%macros for sanitizing characters
\def\@anim@sanitize#1#2\@nil{%
  \catcode`#1=12%
  \ifx\@empty#2\@empty\else%
    \@anim@sanitize#2\@nil%
  \fi%
}
\def\@anim@sanitizeJS{% sanitize JS code while being read from timeline file
  \begingroup%
  \@anim@sanitize.:;?!/"'*+,->=<$\&@][)(^_`|~\#\@nil%
  \def\\{\string\\}%
  \def\{{\string\{}%
  \def\}{\string\}}%
  \def\%{\string\%}%
}
\def\@anim@sanitizeColon{% sanitize colon (`:') while reading the option list,
  \begingroup%             in particular the `buttonfg' and `buttonbg' options
  \@anim@sanitize:\@nil%  arguments
}
\let\@anim@endsanitize\endgroup

%building timeline from timeline file
\newread\@anim@@tmlnfile
\newboolean{@anim@eof}
\newcount\@anim@tmpcnt %scratch counter for different uses
\newcounter{@anim@ltxcnt} %scratch LaTeX counter for different uses
\newcount\@anim@curlayer %takes the number of the current layer being processed
\newcount\@anim@lineno %current input line No.
\newcount\@anim@curfield %current field of input line processed

\def\@anim@buildtmln#1{% #1:@anim@num
  \global\@anim@curframe@zb=\z@%
  %read timeline file a first time to get number of lines (= number of frames)
  \openin\@anim@@tmlnfile=\@anim@tmlnfile%
  \@anim@sanitizeJS%
  \endlinechar=-1% suppress trailing space at input line end
  \global\read\@anim@@tmlnfile to \@anim@inputline%
  \@anim@endsanitize%
  \edef\@anim@inputline{\@anim@inputline\space}%
  %remove all spaces from input line
  \edef\@anim@inputline{\expandafter\@anim@zap@space\@anim@inputline\@empty}%
  \ifeof\@anim@@tmlnfile\setboolean{@anim@eof}{true}\else%
    \setboolean{@anim@eof}{false}\fi%
  \whiledo{\NOT\boolean{@anim@eof}}{%
    \ifthenelse{\equal{\@anim@inputline}{}}{}{%
      \global\advance\@anim@curframe@zb by \@ne%
    }%
    \@anim@sanitizeJS%
    \endlinechar=-1% suppress trailing space at input line end
    \global\read\@anim@@tmlnfile to \@anim@inputline%
    \@anim@endsanitize%
    \edef\@anim@inputline{\@anim@inputline\space}%
    \edef\@anim@inputline{\expandafter\@anim@zap@space\@anim@inputline\@empty}%
    \ifeof\@anim@@tmlnfile\setboolean{@anim@eof}{true}\else%
      \setboolean{@anim@eof}{false}\fi%
  }%
  \closein\@anim@@tmlnfile%
  \global\let\@anim@transp\@anim@frames% total number of transparencies
  \xdef\@anim@frames{\the\@anim@curframe@zb}%total number of actual frames
  \global\@anim@tmpcnt=\@anim@frames%
  \global\advance\@anim@tmpcnt by -\@ne%
  \xdef\@anim@maxframe{\the\@anim@tmpcnt}% highest frame index
  \global\@anim@curframe@zb=\z@%
  \gdef\@anim@maxlayer{0}% highest layer index
  %reopen timeline file and build timeline
  \openin\@anim@@tmlnfile=\@anim@tmlnfile%
  \global\@anim@lineno=\z@%
  \@anim@sanitizeJS%
  \endlinechar=-1% suppress trailing space at input line end
  \global\read\@anim@@tmlnfile to \@anim@inputline%
  \@anim@endsanitize%
  \edef\@anim@inputlinenospc{\@anim@inputline\space}%
  \edef\@anim@inputlinenospc{%
    \expandafter\@anim@zap@space\@anim@inputlinenospc\@empty}%
  \ifeof\@anim@@tmlnfile\setboolean{@anim@eof}{true}\else%
    \setboolean{@anim@eof}{false}\fi%
  \message{<building timeline a#1 from file `\@anim@tmlnfile':}%
  \whiledo{\NOT\boolean{@anim@eof}}{%
    \global\advance\@anim@lineno by \@ne%
    %initialise content and transparency lists for current frame
    \def\@anim@frmcontent{}%
    \def\@anim@trlst{}%
    \ifthenelse{\equal{\@anim@inputlinenospc}{}}{}{%
      %process input line
      \global\@anim@curfield=\z@%
      \expandafter\@anim@parseline\@anim@inputline:\@nil%
      %combine the layers of the current frame
      \global\@anim@curlayer=\z@%
      \loop\ifnum\@anim@curlayer>\@anim@maxlayer\relax\else%
        \edef\@anim@frmcontent{%
          \@anim@frmcontent%
          \@anim@getkeyval{%
            layercontent@\the\@anim@curframe@zb.\the\@anim@curlayer}%
        }%
        \edef\@anim@trlst{%
          \@anim@trlst%
          \@anim@getkeyval{%
            layertrlst@\the\@anim@curframe@zb.\the\@anim@curlayer}%
        }%
        \@anim@newkey{%
          layercontent@\the\@anim@curframe@zb.\the\@anim@curlayer}{}%
        \@anim@newkey{%
          layertrlst@\the\@anim@curframe@zb.\the\@anim@curlayer}{}%
        \global\advance\@anim@curlayer by \@ne%
      \repeat%
      %detect multiple inclusion of the same transp. in the current frame
      \xdef\@anim@@trlst{\the\@anim@curframe@zb.\@anim@trlst}%
      \expandafter\@anim@findmult\@anim@@trlst,\@nil%
      %insert frame Widget
      \@anim@makeframe{#1}{\the\@anim@curframe@zb}{\@anim@frmcontent}%
      %clean-up
      \expandafter\@anim@resetmult\@anim@@trlst,\@nil%
      \message{.}%
      \global\advance\@anim@curframe@zb by \@ne%
    }%
    \@anim@sanitizeJS%
    \endlinechar=-1% suppress trailing space at input line end
    \global\read\@anim@@tmlnfile to \@anim@inputline%
    \@anim@endsanitize%
    \edef\@anim@inputlinenospc{\@anim@inputline\space}%
    \edef\@anim@inputlinenospc{%
      \expandafter\@anim@zap@space\@anim@inputlinenospc\@empty}%
    \ifeof\@anim@@tmlnfile\setboolean{@anim@eof}{true}\else%
      \setboolean{@anim@eof}{false}%
    \fi%
  }%
  \message{\the\@anim@curframe@zb\space frames built}%
  \closein\@anim@@tmlnfile%
  %check for unused transparencies
  \global\@anim@tmpcnt=\z@%
  \loop\ifnum\@anim@transp>\@anim@tmpcnt\relax%
    \ifcsname u@tr\the\@anim@tmpcnt\endcsname%
      {\expandafter\global\expandafter\let\csname
         u@tr\the\@anim@tmpcnt\endcsname\@undefined}%
    \else%
      \PackageWarning{animate}{%
        Transparency \the\@anim@tmpcnt\space has never been used\MessageBreak%
        in the current animation.\MessageBreak%
        Timeline ``\@anim@tmlnfile'',\MessageBreak\jobname.tex%
      }%
      \ifcsname @anim@nusewarned\endcsname\else%
        \AtEndDocument{%
          \PackageWarningNoLine{animate}{\@anim@nusewarning}%
        }%
        \gdef\@anim@nusewarned{}%
      \fi%
    \fi%
    \global\advance\@anim@tmpcnt by \@ne%
  \repeat%
  \message{>}%
}

%reads one line of timeline file and splits into its colon separated parts
\def\@anim@parseline#1:#2\@nil{%
  \ifcase\@anim@curfield\relax%
    %first field: a star (pause frame)
    \if@anim@step\else%
      \edef\@anim@tmp{#1\space}%
      \edef\@anim@tmp{\expandafter\@anim@zap@space\@anim@tmp\@empty}%
      \ifthenelse{\equal{\@anim@tmp}{*}}{%
        \xdef\@anim@pauseat{%
          \@anim@pauseat%
          a\the\@anim@num_pauseAt[\the\@anim@curframe@zb]=1;%
        }%
        \setboolean{@anim@pauseframes}{true}%
      }{}%
    \fi%
    \global\advance\@anim@curfield by \@ne%
    \@anim@parseline#2\@nil%
  \or%
    \if@anim@step\else%
      %second field: new frame rate
      \@anim@processfpsarg{#1}%
    \fi%
    \global\advance\@anim@curfield by \@ne%
    \@anim@parseline#2\@nil%
  \or%
    %parse the third field (list of layer specifications)
    \global\@anim@curlayer=\z@%
    \edef\@anim@tmp{#1\space}%
    \edef\@anim@tmp{\expandafter\@anim@zap@space\@anim@tmp\@empty}%
    \expandafter\@anim@parsethird\@anim@tmp;\@nil%
    \edef\@anim@tmp{#2\space}%
    \edef\@anim@tmp{\expandafter\@anim@zap@space\@anim@tmp\@empty}%
    %fourth field: JavaScript provided by the user
    \ifx\@anim@tmp\empty\else\@anim@parseforth#2\@nil\fi%
  \fi%
}

%parses frame contents specification (semicolon separated list of layer
%specifications)
\def\@anim@parsethird#1;#2\@nil{%
  \ifthenelse{\equal{#1}{}}{}{\@anim@parselayer#1,\@nil}%process one layer spec
  \ifthenelse{\equal{#2}{}}{}{%
    \global\advance\@anim@curlayer by \@ne%
    \ifnum\@anim@curlayer>\@anim@maxlayer\relax%
      \xdef\@anim@maxlayer{\the\@anim@curlayer}%
    \fi%
    \@anim@parsethird#2\@nil%
  }%
}

%process JavaScript field
\def\@anim@parseforth#1:\@nil{%
  \edef\@anim@tmp{#1\space}%
  \edef\@anim@tmp{\expandafter\@anim@zap@space\@anim@tmp\@empty}%
  \ifx\@anim@tmp\@empty\else%
    \xdef\@anim@usrjsat{%
      \@anim@usrjsat%
      a\the\@anim@num_usrJS[\the\@anim@curframe@zb]=function(){#1};%
    }%
    \setboolean{@anim@usrjs}{true}%
  \fi%
}

%parses contents of one layer specification (comma separated list of transp.
%specs)
\def\@anim@parselayer#1,#2\@nil{%
  \ifthenelse{\equal{#1}{}}{}{\@anim@process#1x\@nil}%process one transp spec
  \ifthenelse{\equal{#2}{}}{}{\@anim@parselayer#2\@nil}%
}

%process transparency spec, such as `123' or `456x78' or `9x0' or `c' (clear
%transparency stack)
\def\@anim@process#1x#2\@nil{%
  %determine number of repetitions of current transparency
  \ifthenelse{\equal{#2}{}}{%
    \gdef\@anim@repeats{1}%
  }{%
    \@anim@getrepetitions#2\@nil%
  }%
  \ifnum\@anim@repeats=\z@\relax% =0 means: repeat until end of timeline
    \let\@anim@repeatuntil\@anim@frames%
  \else%
    \global\@anim@tmpcnt=\@anim@curframe@zb%
    \global\advance\@anim@tmpcnt by \@anim@repeats%
    \ifnum\@anim@frames<\@anim@tmpcnt\relax%
      \let\@anim@repeatuntil\@anim@frames%
    \else%
      \edef\@anim@repeatuntil{\the\@anim@tmpcnt}%
    \fi%
  \fi%
  %test whether transparency stack of the current layer is to be cleared
  \ifthenelse{\equal{#1}{c}\OR\equal{#1}{C}}{%
    \global\@anim@tmpcnt=\@anim@curframe@zb%
    \loop\ifnum\@anim@frames>\@anim@tmpcnt\relax%
      %clear layer content
      \@anim@newkey{layercontent@\the\@anim@tmpcnt.\the\@anim@curlayer}{}%
      %clear list of transparencies used in the current layer
      \@anim@newkey{layertrlst@\the\@anim@tmpcnt.\the\@anim@curlayer}{}%
      \global\advance\@anim@tmpcnt by \@ne%
    \repeat%
  }{%
    %build contents of animation layer according to transparency specifications
    \ifnum\@anim@transp<#1\relax\else%   ignore non-existing
      \ifnum\@anim@transp=#1\relax\else% transparencies
        \global\@anim@tmpcnt=\@anim@curframe@zb%
        \loop\ifnum\@anim@repeatuntil>\@anim@tmpcnt\relax%
          %layer content
          \edef\@anim@tmpvar{\@anim@getkeyval{%
            layercontent@\the\@anim@tmpcnt.\the\@anim@curlayer}}%
          \@anim@newkey{layercontent@\the\@anim@tmpcnt.\the\@anim@curlayer}{%
            \@anim@tmpvar\@anim@getkeyval{img@#1},%
          }%
          %append transp. to list of transparencies used in the current layer
          \edef\@anim@tmpvar{\@anim@getkeyval{%
            layertrlst@\the\@anim@tmpcnt.\the\@anim@curlayer}}%
          \@anim@newkey{layertrlst@\the\@anim@tmpcnt.\the\@anim@curlayer}{%
            \@anim@tmpvar#1,}%
          \global\advance\@anim@tmpcnt by \@ne%
        \repeat%
      \fi%
    \fi%
  }%
}
%get number of repetitions of frame
\def\@anim@getrepetitions#1x\@nil{%
  \gdef\@anim@repeats{#1}%
}

\begingroup\catcode`\_=13\let_\space
\xdef\@anim@multwarning{%
  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\MessageBreak%
  @_In_certain_animation_frames,_transparencies_were_@\MessageBreak%
  @_included_multiple_times._This_may_considerably___@\MessageBreak%
  @_slow_down_animation_speed._Revise_the_timeline___@\MessageBreak%
  @_file_of_the_corresponding_animation!_____________@\MessageBreak%
  @__________________________________________________@\MessageBreak%
  @_See_the_transcript_file__________________________@\MessageBreak%
  @___``\jobname.log''\MessageBreak%
  @_for_additional_information_about_which_anima-____@\MessageBreak%
  @_tion(s)_are_affected!____________________________@\MessageBreak%
  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%
}%
\endgroup

%detects multiple inclusion of the same transparency
\def\@anim@findmult#1.#2,#3\@nil{%
  \ifthenelse{\equal{#2}{}}{}{%
    \@anim@newkey{u@tr#2}{}% mark current transp. as `used'
    \ifcsname m@tr#2\endcsname%
      \PackageWarning{animate}{%
        Transparency #2 more than once included in frame #1.%
        \MessageBreak%
        Timeline ``\@anim@tmlnfile'' on input line \the\@anim@lineno,%
        \MessageBreak\jobname.tex%
      }%
      \ifcsname @anim@multwarned\endcsname\else%
        \AtEndDocument{%
          \PackageWarningNoLine{animate}{\@anim@multwarning}%
        }%
        \gdef\@anim@multwarned{}%
      \fi%
    \else%
      {\expandafter\xdef\csname m@tr#2\endcsname{}}%
    \fi%
  }%
  \ifthenelse{\equal{#3}{}}{}{%
    \@anim@findmult#1.#3\@nil%
  }%
}
%reset transparency list
\def\@anim@resetmult#1.#2,#3\@nil{%
  \ifthenelse{\equal{#2}{}}{}{%
    {\expandafter\global\expandafter\let\csname m@tr#2\endcsname\@undefined}%
  }%
  \ifthenelse{\equal{#3}{}}{}{%
    \@anim@resetmult#1.#3\@nil%
  }%
}

\def\@anim@zap@space#1 #2{%
  #1%
  \ifx#2\@empty\else\expandafter\@anim@zap@space\fi
  #2%
}

%command options
\newboolean{@anim@iso}
\newboolean{@anim@hiresbb}
\newboolean{@anim@interpolate}
\newboolean{@anim@controls}
\newboolean{@anim@controls@play}
\newboolean{@anim@controls@step}
\newboolean{@anim@controls@stop}
\newboolean{@anim@controls@speed}
\newboolean{@anim@ctrlleft}
\newboolean{@anim@ctrlright}
\newboolean{@anim@ctrlcentre}
\newboolean{@anim@loop}
\newboolean{@anim@autoplay}
\newboolean{@anim@autoresume}
\newboolean{@anim@autopause}
\newboolean{@anim@palindrome}
\newboolean{@anim@step}
\newboolean{@anim@meas}
\setboolean{@anim@nomouse}{false}
\newboolean{@anim@draft}
\def\@anim@mone{-1}\def\@anim@mtwo{-2}
\def\@anim@user@controls@all{%
  \setboolean{@anim@controls}{true}%
  \setboolean{@anim@controls@play}{true}%
  \setboolean{@anim@controls@step}{true}%
  \setboolean{@anim@controls@stop}{true}%
  \setboolean{@anim@controls@speed}{true}%
}
\def\@anim@user@controls@none{%
  \setboolean{@anim@controls}{false}%
  \setboolean{@anim@controls@play}{false}%
  \setboolean{@anim@controls@step}{false}%
  \setboolean{@anim@controls@stop}{false}%
  \setboolean{@anim@controls@speed}{false}%
}
\def\@anim@parse@ctrlsalignarg#1#2#3+#4\@nil{%
  \ifthenelse{\equal{#3}{}}{}{\def#1{#3}}%
  \ifthenelse{\equal{#4}{}}{#2=\z@}{\@anim@getalignindent#2#4\@nil}%
}
\def\@anim@getalignindent#1#2+\@nil{#1=#2}
% helper macro to get number of colour components
\def\@anim@colours#1{\@anim@@colours#1:\@nil}
\def\@anim@@colours#1:#2\@nil{% helper macro to get number of colour components
  \xdef\@anim@colour{\@anim@colour\space #1}% and to replace : by ` ' in the arg
  \ifthenelse{\equal{#2}{}}{}{%
    \global\advance\@anim@tmpcnt by \@ne%
    \@anim@@colours#2\@nil%
  }%
}

\ExplSyntaxOn
\DeclareKeys[anim@user]{
  label.code:n = {
    \gdef\@anim@label{#1}
    \ifcsname @anim@#1\endcsname
      \PackageWarning{animate}{Label~#1~multiply~defined}
      \ifdefined\@anim@multlabel\else
        \gdef\@anim@multlabel{}
        \AtEndDocument{
          \PackageWarningNoLine{animate}{
          @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\MessageBreak
          @~There~are~multiply-defined~labels!~@\MessageBreak
          @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}
        }
      \fi
    \fi
    \expandafter\gdef\csname  @anim@#1\endcsname{}
  },
  label.value_required:n = true,

  width.code:n = {
    \gdef\@anim@widtharg{#1}
    \ifdefined\@anim@usrwd\else
      \global\advance\@anim@resizeflags by 4
      \def\@anim@usrwd{}
    \fi
  },
  width.value_required:n = true,

  height.code:n = {
    \gdef\@anim@heightarg{#1}
    \ifdefined\@anim@usrht\else
      \global\advance\@anim@resizeflags by 2
      \def\@anim@usrht{}
    \fi
  },
  height.value_required:n = true,

  totalheight.code:n = {
    \gdef\@anim@totheightarg{#1}
    \ifdefined\@anim@usrtht\else
      \global\advance\@anim@resizeflags by \@ne
      \def\@anim@usrtht{}
    \fi
  },
  totalheight.value_required:n = true,

  keepaspectratio.legacy_if_gset:n = @anim@iso,
  keepaspectratio.default:n = true,

  bb.code:n = {\xdef\@anim@bb{bb=#1}},
  bb.value_required:n = true,

  viewport.code:n = {\xdef\@anim@viewport{viewport=#1}},
  viewport.value_required:n = true,

  trim.code:n = {\xdef\@anim@trim{trim=#1}},
  trim.value_required:n = true,

  angle.code:n = {\xdef\@anim@angle{angle=#1}},
  angle.value_required:n = true,

  pagebox.code:n = {\xdef\@anim@pagebox{pagebox=#1}},
  pagebox.value_required:n = true,

  clip.code:n = {},      % no-op, as embedded graphics are always
  clip.default:n = true, % clipped to widget box

  hiresbb.legacy_if_gset:n = @anim@hiresbb,
  hiresbb.default:n = true,

  interpolate.legacy_if_gset:n = @anim@interpolate,
  interpolate.default:n = true,

  scale.tl_gset_x:N = \@anim@boxscale,
  scale.value_required:n = true,

  buttonsize.tl_gset:N = \@anim@btnsize,
  buttonsize.value_required:n = true,

  buttonbg.code:n = {
    \global\@anim@tmpcnt=\@ne
    \gdef\@anim@colour{}
    \@anim@colours{#1}
    \global\let\@anim@bgcolour\@anim@colour
    \ifnum\if@anim@dvips 1\else\if@anim@dvisvgm 1\else0\fi\fi=1
      \ifnum\@anim@tmpcnt=\@ne\relax
        \xdef\@anim@bg{\@anim@colour\space setgray}
      \else
        \ifnum\@anim@tmpcnt=3\relax
          \xdef\@anim@bg{\@anim@colour\space setrgbcolor}
        \else
          \ifnum\@anim@tmpcnt=4\relax
            \xdef\@anim@bg{\@anim@colour\space setcmykcolor}
          \fi
        \fi
      \fi
    \else% pdftex and dvipdfmx
      \ifnum\@anim@tmpcnt=\@ne\relax
        \xdef\@anim@bg{\@anim@colour\space g}
      \else
        \ifnum\@anim@tmpcnt=3\relax
          \xdef\@anim@bg{\@anim@colour\space rg}
        \else
          \ifnum\@anim@tmpcnt=4\relax
            \xdef\@anim@bg{\@anim@colour\space k}
          \fi
        \fi
      \fi
    \fi
  },
  buttonbg.value_required:n = true,

  buttonfg.code:n = {
    \global\@anim@tmpcnt=\@ne
    \gdef\@anim@colour{}
    \@anim@colours{#1}
    \global\let\@anim@fgcolour\@anim@colour
    \ifnum\if@anim@dvips 1\else\if@anim@dvisvgm 1\else0\fi\fi=1
      \ifnum\@anim@tmpcnt=\@ne\relax
        \xdef\@anim@fg{\@anim@colour\space setgray}
      \else
        \ifnum\@anim@tmpcnt=3\relax
          \xdef\@anim@fg{\@anim@colour\space setrgbcolor}
        \else
          \ifnum\@anim@tmpcnt=4\relax
            \xdef\@anim@fg{\@anim@colour\space setcmykcolor}
          \fi
        \fi
      \fi
    \else% pdftex and dvipdfmx
      \ifnum\@anim@tmpcnt=\@ne\relax
        \xdef\@anim@fg{\@anim@colour\space G}
        \xdef\@anim@@@fg{\@anim@colour\space g}
      \else
        \ifnum\@anim@tmpcnt=3\relax
          \xdef\@anim@fg{\@anim@colour\space RG}
          \xdef\@anim@@@fg{\@anim@colour\space rg}
        \else
          \ifnum\@anim@tmpcnt=4\relax
            \xdef\@anim@fg{\@anim@colour\space K}
            \xdef\@anim@@@fg{\@anim@colour\space k}
          \fi
        \fi
      \fi
    \fi
  },
  buttonfg.value_required:n = true,

  buttonalpha.tl_gset_x:N = \@anim@alpha,
  buttonalpha.value_required:n = true,

  alttext.code:n = {
    \ifthenelse{\equal{#1}{none}}{
      \gdef\@anim@alttext{}
    }{
      \xdef\@anim@alttext{/Contents~(#1)}
    }
  },
  alttext.value_required:n = true,

  controls.multichoice:,
  controls / all.code:n = {\@anim@user@controls@all},
  controls / true.code:n = {\@anim@user@controls@all},
  controls / on.code:n = {\@anim@user@controls@all},
  controls / none.code:n = {\@anim@user@controls@none},
  controls / false.code:n = {\@anim@user@controls@none},
  controls / off.code:n = {\@anim@user@controls@none},
  controls / play.code:n = {
    \setboolean{@anim@controls}{true}
    \setboolean{@anim@controls@play}{true}
  },
  controls / step.code:n = {
    \setboolean{@anim@controls}{true}
    \setboolean{@anim@controls@step}{true}
  },
  controls / stop.code:n = {
    \setboolean{@anim@controls}{true}
    \setboolean{@anim@controls@stop}{true}
  },
  controls / speed.code:n = {
    \setboolean{@anim@controls}{true}
    \setboolean{@anim@controls@speed}{true}
  },
  controls.default:n = all,

  controlsaligned.code:n = {
    \@anim@parse@ctrlsalignarg\@anim@tmpvar\@anim@tmpdima#1+\@nil
    \ifthenelse{\equal{\@anim@tmpvar}{left}}{
      \setboolean{@anim@ctrlleft}{true}
      \setboolean{@anim@ctrlright}{false}
      \setboolean{@anim@ctrlcentre}{false}
      \xdef\@anim@ctrlsindent{\the\@anim@tmpdima}
    }{
      \ifthenelse{\equal{\@anim@tmpvar}{right}}{
        \setboolean{@anim@ctrlleft}{false}
        \setboolean{@anim@ctrlright}{true}
        \setboolean{@anim@ctrlcentre}{false}
        \xdef\@anim@ctrlsindent{\the\@anim@tmpdima}
      }{
        \ifthenelse{\equal{\@anim@tmpvar}{center}}{
          \setboolean{@anim@ctrlleft}{false}
          \setboolean{@anim@ctrlright}{false}
          \setboolean{@anim@ctrlcentre}{true}
          \xdef\@anim@ctrlsindent{\z@}
        }{}
      }
    }
  },
  controlsaligned.value_required:n = true,

  loop.legacy_if_gset:n = @anim@loop,
  loop.default:n = true,

  autoplay.code:n = {
    \setboolean{@anim@autoplay}{#1}
    \if@anim@autoplay\setboolean{@anim@autoresume}{false}\fi
  },
  autoplay.default:n = true,

  autoresume.code:n = {
    \setboolean{@anim@autoresume}{#1}
    \if@anim@autoresume\setboolean{@anim@autoplay}{false}\fi
  },
  autoresume.default:n = true,

  autopause.legacy_if_gset:n = @anim@autopause,
  autopause.default:n = true,

  palindrome.legacy_if_gset:n = @anim@palindrome,
  palindrome.default:n = true,

  poster.choice:, % <num> | first | last | none
  poster / first.code:n = {\gdef\@anim@poster{0}},
  poster / last.code:n = {\gdef\@anim@poster{-1}},
  poster / none.code:n = {\gdef\@anim@poster{-2}},
  poster / unknown.code:n = {% frame <num>
    \ifnum#1<\z@\relax
      \PackageError{animate}{
        Poster~frame~`#1'~is~out-of-bounds~on\MessageBreak
        input~line~\the\inputlineno.\MessageBreak
        Try~one~of~`poster=first'~or~`poster=none'
      }{}
    \fi
    \gdef\@anim@poster{#1}
  },
  poster.default:n = first,

  step.legacy_if_gset:n = @anim@step,
  step.default:n = true,

  draft.legacy_if_gset:n = @anim@draft,
  draft.default:n = true,

  final.code:n = {
    \ifthenelse{\equal{#1}{true}}{
      \setboolean{@anim@draft}{false}
    }{
      \setboolean{@anim@draft}{true}
    }
  },
  final.default:n = true,

  nomouse.legacy_if_gset:n = @anim@nomouse,
  nomouse.default:n = true,

  timeline.code:n = {
    \IfFileExists{#1}{
      \xdef\@anim@tmlnfile{\expandafter\zap@finalspace\@filef@und\@nil}
      \setboolean{@anim@timeline}{true}
    }{
      \PackageError{animate}{timeline~file~`#1'~cannot~be~opened~for~reading
      }{
        Make~sure~file~`#1'~exists~and~is~readable!
      }
    }
  },
  timeline.value_required:n = true,

  begin.tl_gset:N = \@anim@begin,
  begin.value_required:n = true,

  end.tl_gset:N = \@anim@end,
  end.value_required:n = true,

  every.code:n = {
    \ifnum#1<\@ne\relax\gdef\@anim@every{1}\else\gdef\@anim@every{#1}\fi
  },
  every.value_required:n = true,

  measure.legacy_if_gset:n = @anim@meas,
  measure.default:n = true,

  method.choices:nn = {icon,widget,ocg} {
    \ifthenelse{\equal{#1}{icon}}{
      \def\@anim@method{\z@}
    }{
      \ifthenelse{\equal{#1}{widget}}{
        \def\@anim@method{\@ne}
      }{
        \ifthenelse{\equal{#1}{ocg}}{
          \def\@anim@method{\tw@}
        }{}
      }
    }
  },
  method.value_required:n = true,

  type.tl_gset_x:N = \@anim@ftype,
  type.value_required:n = true,
}
\ExplSyntaxOff

%macro to reset macros and booleans
\def\@anim@reset{%
  \gdef\@anim@label{}%
  \global\let\@anim@boxscale\@anim@@boxscale%
  \global\let\if@anim@iso=\if@anim@@iso%
  \global\let\@anim@bb\@anim@@bb%
  \global\let\@anim@viewport\@anim@@viewport%
  \global\let\@anim@trim\@anim@@trim%
  \global\let\@anim@angle\@anim@@angle%
  \global\let\if@anim@hiresbb=\if@anim@@hiresbb%
  \global\let\if@anim@interpolate=\if@anim@@interpolate%
  \global\let\@anim@pagebox\@anim@@pagebox%
  \global\@anim@resizeflags\@anim@@resizeflags%
  \global\let\@anim@widtharg\@anim@@widtharg%
  \global\let\@anim@heightarg\@anim@@heightarg%
  \global\let\@anim@totheightarg\@anim@@totheightarg%
  \let\@anim@usrwd\@anim@pkgwd%
  \let\@anim@usrht\@anim@pkght%
  \let\@anim@usrtht\@anim@pkgtht%
  \let\@anim@alttext\@anim@@alttext%
  \global\let\@anim@bg\@anim@@bg%
  \global\let\@anim@fg\@anim@@fg%
  \global\let\@anim@@@fg\@anim@@@@fg%
  \global\let\@anim@bgcolour\@anim@@bgcolour% default button colours
  \global\let\@anim@fgcolour\@anim@@fgcolour%
  \global\let\@anim@alpha\@anim@@alpha%
  \global\let\@anim@btnsize\@anim@@btnsize%
  \global\let\@anim@poster\@anim@@poster%
  \global\let\if@anim@controls=\if@anim@@controls%
  \global\let\if@anim@controls@play=\if@anim@@controls@play%
  \global\let\if@anim@controls@step=\if@anim@@controls@step%
  \global\let\if@anim@controls@stop=\if@anim@@controls@stop%
  \global\let\if@anim@controls@speed=\if@anim@@controls@speed%
  \global\let\if@anim@ctrlleft=\if@anim@@ctrlleft%
  \global\let\if@anim@ctrlright=\if@anim@@ctrlright%
  \global\let\if@anim@ctrlcentre=\if@anim@@ctrlcentre%
  \global\let\@anim@ctrlsindent\@anim@@ctrlsindent%
  \global\let\if@anim@loop=\if@anim@@loop%
  \global\let\if@anim@autoplay=\if@anim@@autoplay%
  \global\let\if@anim@autoresume=\if@anim@@autoresume%
  \global\let\if@anim@autopause=\if@anim@@autopause%
  \global\let\if@anim@palindrome=\if@anim@@palindrome%
  \global\let\if@anim@step=\if@anim@@step%
  \global\let\if@anim@draft=\if@anim@@draft%
  \global\let\@anim@method\@anim@@method%
  \global\let\if@anim@nomouse=\if@anim@@nomouse%
  \gdef\@anim@properties{}%
  \setboolean{@anim@pauseframes}{false}%
  \gdef\@anim@pauseat{}%
  \setboolean{@anim@chfps}{false}%
  \setboolean{@anim@usrjs}{false}%
  \gdef\@anim@nfpsat{}%
  \gdef\@anim@pfpsat{}%
  \gdef\@anim@usrjsat{}%
  \gdef\@anim@tmlnfile{}%
  \setboolean{@anim@timeline}{false}%
  \gdef\@anim@begin{}%
  \gdef\@anim@end{}%
  \gdef\@anim@every{1}% frames to be included
  \gdef\@anim@gropts{}%
  \setboolean{@anim@multipage}{false}%
  \setboolean{@anim@meas}{false}%
  \setboolean{@anim@autoplayorresume}{false}%
  \global\let\@anim@ftype\@anim@@ftype%
}

%package options
\newcount\@anim@@resizeflags
\def\@anim@@widtharg{\width}
\def\@anim@@heightarg{\height}
\def\@anim@@totheightarg{\totalheight}
\newboolean{@anim@@iso}
\def\@anim@@bb{}
\def\@anim@@viewport{}
\def\@anim@@trim{}
\def\@anim@@angle{}
\def\@anim@@pagebox{}
\newboolean{@anim@@hiresbb}
\newboolean{@anim@@interpolate}
\def\@anim@@boxscale{1}
\def\@anim@@btnsize{1.44em}
\def\@anim@@bg{}
\def\@anim@@bgcolour{}
\def\@anim@@fgcolour{}
\def\@anim@@alpha{}
\edef\@anim@@alttext{/Contents (animation by animate, v. \@anim@version)}
\newboolean{@anim@@controls}
\newboolean{@anim@@controls@play}
\newboolean{@anim@@controls@step}
\newboolean{@anim@@controls@stop}
\newboolean{@anim@@controls@speed}
\def\@anim@pkg@controls@all{%
  \setboolean{@anim@@controls}{true}%
  \setboolean{@anim@@controls@play}{true}%
  \setboolean{@anim@@controls@step}{true}%
  \setboolean{@anim@@controls@stop}{true}%
  \setboolean{@anim@@controls@speed}{true}%
}
\def\@anim@pkg@controls@none{%
  \setboolean{@anim@@controls}{false}%
  \setboolean{@anim@@controls@play}{false}%
  \setboolean{@anim@@controls@step}{false}%
  \setboolean{@anim@@controls@stop}{false}%
  \setboolean{@anim@@controls@speed}{false}%
}
\newboolean{@anim@@ctrlleft}
\newboolean{@anim@@ctrlright}
\newboolean{@anim@@ctrlcentre}
\setboolean{@anim@@ctrlcentre}{true}
\def\@anim@@ctrlsindent{\z@}
\newboolean{@anim@@loop}
\newboolean{@anim@@autoplay}
\newboolean{@anim@@autoresume}
\newboolean{@anim@@autopause}
\newboolean{@anim@@palindrome}
\gdef\@anim@@poster{0}%
\newboolean{@anim@@step}
\newboolean{@anim@@draft}
\ifdraft{%globally set by document class
  \setboolean{@anim@@draft}{true}%
}{%
  \setboolean{@anim@@draft}{false}%
}
\newboolean{@anim@@nomouse}
\def\@anim@@method{\z@}%default animation method is `icon'
\def\@anim@@ftype{}% file type (extension) of graphics

\ExplSyntaxOn
\DeclareKeys[anim@pkg]{
  width.code:n = {
    \gdef\@anim@@widtharg{#1}
    \ifdefined\@anim@pkgwd\else
      \advance\@anim@@resizeflags by 4
      \def\@anim@pkgwd{}%
    \fi%
  },
  width.value_required:n = true,

  height.code:n = {
    \gdef\@anim@@heightarg{#1}
    \ifdefined\@anim@pkght\else
      \advance\@anim@@resizeflags by 2
      \def\@anim@pkght{}
    \fi
  },
  height.value_required:n = true,

  totalheight.code:n = {
    \gdef\@anim@@totheightarg{#1}
    \ifdefined\@anim@pkgtht\else
      \advance\@anim@@resizeflags by \@ne
      \def\@anim@pkgtht{}
    \fi
  },
  totalheight.value_required:n = true,

  keepaspectratio.legacy_if_gset:n = @anim@@iso,
  keepaspectratio.default:n = true,

  bb.code:n = {\xdef\@anim@@bb{bb=#1}},
  bb.value_required:n = true,

  viewport.code:n = {\xdef\@anim@@viewport{viewport=#1}},
  viewport.value_required:n = true,

  trim.code:n = {\xdef\@anim@@trim{trim=#1}},
  trim.value_required:n = true,

  angle.code:n = {\xdef\@anim@@angle{angle=#1}},
  angle.value_required:n = true,

  pagebox.code:n = {\xdef\@anim@@pagebox{pagebox=#1}},
  pagebox.value_required:n = true,

  clip.code:n = {},      % no-op, as embedded graphics are always
  clip.default:n = true, % clipped to widget box

  hiresbb.legacy_if_gset:n = @anim@@hiresbb,
  hiresbb.default:n = true,

  interpolate.legacy_if_gset:n = @anim@@interpolate,
  interpolate.default:n = true,

  scale.tl_gset_x:N = \@anim@@boxscale,
  scale.value_required:n = true,

  buttonsize.tl_gset:N = \@anim@@btnsize,
  buttonsize.value_required:n = true,

  buttonbg.code:n = {
    \global\@anim@tmpcnt=\@ne
    \gdef\@anim@colour{}
    \@anim@colours{#1}
    \global\let\@anim@@bgcolour\@anim@colour
    \ifnum\if@anim@dvips 1\else\if@anim@dvisvgm 1\else0\fi\fi=1
      \ifnum\@anim@tmpcnt=\@ne\relax
        \xdef\@anim@@bg{\@anim@colour\space setgray}
      \else
        \ifnum\@anim@tmpcnt=3\relax
          \xdef\@anim@@bg{\@anim@colour\space setrgbcolor}
        \else
          \ifnum\@anim@tmpcnt=4\relax
            \xdef\@anim@@bg{\@anim@colour\space setcmykcolor}
          \fi
        \fi
      \fi
    \else% pdftex and dvipdfmx
      \ifnum\@anim@tmpcnt=\@ne\relax
        \xdef\@anim@@bg{\@anim@colour\space g}
      \else
        \ifnum\@anim@tmpcnt=3\relax
          \xdef\@anim@@bg{\@anim@colour\space rg}
        \else
          \ifnum\@anim@tmpcnt=4\relax
            \xdef\@anim@@bg{\@anim@colour\space k}
          \fi
        \fi
      \fi
    \fi
  },
  buttonbg.value_required:n = true,

  buttonfg.code:n = {
    \global\@anim@tmpcnt=\@ne
    \gdef\@anim@colour{}
    \@anim@colours{#1}
    \global\let\@anim@@fgcolour\@anim@colour
    \ifnum\if@anim@dvips 1\else\if@anim@dvisvgm 1\else0\fi\fi=1
      \ifnum\@anim@tmpcnt=\@ne\relax
        \xdef\@anim@@fg{\@anim@colour\space setgray}
      \else
        \ifnum\@anim@tmpcnt=3\relax
          \xdef\@anim@@fg{\@anim@colour\space setrgbcolor}
        \else
          \ifnum\@anim@tmpcnt=4\relax
            \xdef\@anim@@fg{\@anim@colour\space setcmykcolor}
          \fi
        \fi
      \fi
    \else% pdftex and dvipdfmx
      \ifnum\@anim@tmpcnt=\@ne\relax
        \xdef\@anim@@fg{\@anim@colour\space G}
        \xdef\@anim@@@@fg{\@anim@colour\space g}
      \else
        \ifnum\@anim@tmpcnt=3\relax
          \xdef\@anim@@fg{\@anim@colour\space RG}
          \xdef\@anim@@@@fg{\@anim@colour\space rg}
        \else
          \ifnum\@anim@tmpcnt=4\relax
            \xdef\@anim@@fg{\@anim@colour\space K}
            \xdef\@anim@@@@fg{\@anim@colour\space k}
          \fi
        \fi
      \fi
    \fi
  },
  buttonfg.value_required:n = true,

  buttonalpha.tl_gset_x:N = \@anim@@alpha,
  buttonalpha.value_required:n = true,

  alttext.code:n = {
    \ifthenelse{\equal{#1}{none}}{
      \gdef\@anim@@alttext{}
    }{
      \xdef\@anim@@alttext{/Contents~(#1)}
    }
  },
  alttext.value_required:n = true,

  controls.multichoice:,
  controls / all.code:n = {\@anim@pkg@controls@all},
  controls / true.code:n = {\@anim@pkg@controls@all},
  controls / on.code:n = {\@anim@pkg@controls@all},
  controls / none.code:n = {\@anim@pkg@controls@none},
  controls / false.code:n = {\@anim@pkg@controls@none},
  controls / off.code:n = {\@anim@pkg@controls@none},
  controls / play.code:n = {
    \setboolean{@anim@@controls}{true}
    \setboolean{@anim@@controls@play}{true}
  },
  controls / step.code:n = {
    \setboolean{@anim@@controls}{true}
    \setboolean{@anim@@controls@step}{true}
  },
  controls / stop.code:n = {
    \setboolean{@anim@@controls}{true}
    \setboolean{@anim@@controls@stop}{true}
  },
  controls / speed.code:n = {
    \setboolean{@anim@@controls}{true}
    \setboolean{@anim@@controls@speed}{true}
  },
  controls.default:n = all,

  controlsaligned.code:n = {
    \@anim@parse@ctrlsalignarg\@anim@tmpvar\@anim@tmpdima#1+\@nil
    \setboolean{@anim@@ctrlcentre}{false}
    \ifthenelse{\equal{\@anim@tmpvar}{left}}{
      \setboolean{@anim@@ctrlleft}{true}
      \edef\@anim@@ctrlsindent{\the\@anim@tmpdima}
    }{
      \ifthenelse{\equal{\@anim@tmpvar}{right}}{
        \setboolean{@anim@@ctrlright}{true}
        \edef\@anim@@ctrlsindent{\the\@anim@tmpdima}
      }{
        \setboolean{@anim@@ctrlcentre}{true}
        \xdef\@anim@@ctrlsindent{\z@}
      }
    }
  },
  controlsaligned.value_required:n = true,

  loop.legacy_if_gset:n = @anim@@loop,
  loop.default:n = true,

  autoplay.code:n = {
    \setboolean{@anim@@autoplay}{#1}
    \if@anim@@autoplay\setboolean{@anim@@autoresume}{false}\fi
  },
  autoplay.default:n = true,

  autoresume.code:n = {
    \setboolean{@anim@@autoresume}{#1}
    \if@anim@@autoresume\setboolean{@anim@@autoplay}{false}\fi
  },
  autoresume.default:n = true,

  autopause.legacy_if_gset:n = @anim@@autopause,
  autopause.default:n = true,

  palindrome.legacy_if_gset:n = @anim@@palindrome,
  palindrome.default:n = true,

  poster.choice:, % <num> | first | last | none
  poster / first.code:n = {\gdef\@anim@@poster{0}},
  poster / last.code:n = {\gdef\@anim@@poster{-1}},
  poster / none.code:n = {\gdef\@anim@@poster{-2}},
  poster / unknown.code:n = {% frame <num>
    \ifnum#1<\z@\relax
      \PackageError{animate}{
        Poster~frame~`#1'~is~out-of-bounds~on\MessageBreak
        input~line~\the\inputlineno.\MessageBreak
        Try~one~of~`poster=first'~or~`poster=none'
      }{}
    \fi
    \gdef\@anim@@poster{#1}
  },
  poster.default:n = first,

  step.legacy_if_gset:n = @anim@@step,
  step.default:n = true,

  draft.code:n = {
    \setboolean{@anim@@draft}{#1}
    \if@anim@export\setboolean{@anim@@draft}{false}\fi
  },
  draft.default:n = true,

  final.code:n = {
    \ifthenelse{\equal{#1}{true}}{
      \setboolean{@anim@@draft}{false}
    }{%
      \setboolean{@anim@@draft}{true}
    }%
  },
  final.default:n = true,

  nomouse.legacy_if_gset:n = @anim@@nomouse,
  nomouse.default:n = true,

  method.choices:nn = {icon,widget,ocg} {
    \ifthenelse{\equal{#1}{icon}}{
      \def\@anim@@method{\z@}
    }{
      \ifthenelse{\equal{#1}{widget}}{
        \def\@anim@@method{\@ne}
      }{
        \ifthenelse{\equal{#1}{ocg}}{
          \def\@anim@@method{\tw@}
        }{}% ignoring wrong input
      }
    }
  },
  method.value_required:n = true,

  type.tl_gset_x:N = \@anim@@ftype,
  type.value_required:n = true,
}
\ExplSyntaxOff

\ifnum\if@anim@dvips 1\else\if@anim@dvisvgm 1\else 0\fi\fi=1 %
  %dvips .OR. dvisvgm
  \def\@anim@@fg{0 setgray}\def\@anim@@@@fg{}
\else% pdftex and dvipdfmx
  \def\@anim@@fg{0 G}\def\@anim@@@@fg{0 g}
\fi

%process package options
\ProcessKeyOptions[anim@pkg]

%%%%%%%%%%%%%%%%%%%%%%%%
% JavaScript part
%%%%%%%%%%%%%%%%%%%%%%%%

%some JS engine specifics
% PDF readers
\def\@anim@consoleshow{console.show();}
\def\@anim@app{app.}
\def\@anim@setTimeout{setTimeOut}
\def\@anim@log{println}
\def\@anim@shift{shift}
\if@anim@dvisvgm
  % web browsers
  \def\@anim@consoleshow{}
  \def\@anim@app{}
  \def\@anim@setTimeout{setTimeout}
  \def\@anim@log{log}
  \def\@anim@shift{shiftKey}
\fi

\begingroup

% `$' --> newline in svg output
\if@anim@dvisvgm
  %insert newline special
  \catcode`\$=\active\gdef${{?nl}}
\else
  \catcode`$=\catcode`\%
\fi

%JS variable declarations
\gdef\@anim@@jsvardecls#1{% #1: @anim@num
  var a#1_idx;$% animation frame index
  \ifnum\@anim@method>\z@%
    var a#1_on;$% add. index used by Widget and OCG methods
  \fi%
  %takes references to frame ocgs/Field objects belonging to the current
  %animation
  var a#1_fr;$%
  \ifnum\@anim@method=\z@ % icon based
    var a#1_wid;$% reference to interactive widget
    \ifnum\@anim@poster=\@anim@mtwo
      var blnk;$%
    \fi%
  \fi%
  \if@anim@controls@play%
    var a#1_btnPauseLeft,a#1_btnPlayLeft,a#1_btnPauseRight,a#1_btnPlayRight;$%
  \fi%
  var a#1_playsRight,a#1_isPaused\if@anim@step\else,a#1_playing\fi;$%
  \if@anim@step\else
    var a#1_int% timer id (takes return val of setIntervall())
    \ifnum\@anim@poster>\z@% custom poster (frame _between_, i. e.
      ,a#1_posterOn%         not including, first and last)
    \fi;$%
    % function pointers
    var a#1_pause,a#1_playRight,a#1_playLeft,a#1_playBwd,a#1_playFwd;$%
    \if@anim@pauseframes
      %this array takes the frame numbers at which to pause playback
      var a#1_pauseAt;$%
    \fi%
    \if@anim@chfps
      %arrays that take frame numbers (array index) and fps values
      var a#1_nFpsAt;$%
    \fi
    var a#1_fps,a#1_spd;$% frames per second (FPS), speed scaling factor
    var a#1_setFps;$% function pointer for setting FPS
    \if@anim@meas % speed measurement
      var spc=String.fromCharCode(32);$%
      var a#1_frcnt,% frame counter
          a#1_msStart,% takes start time (in millisecs)
          a#1_msEnd;$% takes end time (in millisecs)
      var a#1_startMeas,a#1_stopMeas;$% function pointers
    \fi%
  \fi%
  \if@anim@usrjs
    % array of function pointers to timeline JS actions, indexed by frame number
    var a#1_usrJS,a#1_curjs;$%
  \fi
  var a#1_seekFrame,a#1_gotoNext,a#1_gotoPrev;$%
  var a#1_stopFirst,a#1_stopLast;$%
  \if@anim@controls@play
    var a#1_playPauseBwd,a#1_playPauseFwd;$%
  \fi%
  \if@anim@controls@step
    var a#1_stepBwd,a#1_stepFwd;$%
  \fi%
  \if@anim@controls@speed
    var a#1_spdIncr,a#1_spdDecr,a#1_spdReset;$%
  \fi%
  \ifx\empty\@anim@label\empty\else
    var anim;$%
  \fi%
}

%JS initialisation
\gdef\@anim@@jsinit#1{% #1: @anim@num
  \if@anim@dvisvgm\else var curdoc=this;\fi
  if(!a#1_fr){$%
    a#1_fr=new Array();$%
    \ifnum\@anim@method>\z@%
      a#1_on=0;$%
    \fi%
    \if@anim@step\else%
      \ifnum\@anim@poster>\z@% poster frame _between_ (i. e. non including) first
        a#1_posterOn=true;% and last
      \fi%
    \fi%
    \ifcase\@anim@method %icon based
      a#1_wid=this.getField('anm#1');$%reference to interactive widget
      for(var i=0;i<=\@anim@maxframe;i++){$%
        a#1_fr[i]=this.getField('#1.'+i).buttonGetIcon();$%
      }$%
      \ifnum\@anim@poster=\@anim@mtwo
        if(!blnk){$% blank poster (one for all animations)
          blnk=this.getField('0000').buttonGetIcon();$%
        }$%
      \fi%
    \or %widget based
      %get array of animation frames
      for(var i=0;i<=\@anim@maxframe;i++){$%
        a#1_fr[i]=this.getField('#1.'+i);$%
      }$%
    \or %ocg based
      %get array of ocgs of current page
      \if@anim@dvisvgm
        var ocg=document.getElementsByClassName('ocg');$%
      \else
        var ocg=this.getOCGs(this.pageNum);$%
      \fi
      for(var i=0;i<ocg.length;i++){$%
        \if@anim@dvisvgm % slice(1) --> remove leading underscore
          var ocgName=ocg[i].getAttribute('id').slice(1).split('.');$%
        \else
          var ocgName=ocg[i].name.split('.');$%
        \fi
        if(ocgName[0]==#1){$%
          %get array of animation frames
          a#1_fr[ocgName[1]]=ocg[i];$%
          %set basic frame state
          \if@anim@dvisvgm\else
            a#1_fr[ocgName[1]].state=ocg[i].initState;$%
          \fi%
        }$%
      }$%
    \fi%
    \if@anim@controls@play%
      \if@anim@dvisvgm
        a#1_btnPauseLeft=document.getElementById('_#1.PauseLeft');$%
        a#1_btnPlayLeft=document.getElementById('_#1.PlayLeft');$%
        a#1_btnPauseRight=document.getElementById('_#1.PauseRight');$%
        a#1_btnPlayRight=document.getElementById('_#1.PlayRight');$%
      \else
        a#1_btnPauseLeft=this.getField('#1.PauseLeft');$%
        a#1_btnPlayLeft=this.getField('#1.PlayLeft');$%
        a#1_btnPauseRight=this.getField('#1.PauseRight');$%
        a#1_btnPlayRight=this.getField('#1.PlayRight');$%
      \fi%
    \fi%
    %playing state and direction
    \if@anim@step%
      \if@anim@palindrome
        a#1_playsRight=true;$%
      \fi%
    \else
      a#1_playsRight=true;$%
    \fi
    a#1_isPaused=false;$%
    \if@anim@step\else
      a#1_playing=false;$%
      \if@anim@pauseframes
        a#1_pauseAt=new Array();$% frame numbers at which to pause playback
        \@anim@pauseat%
      \fi%
      \if@anim@chfps
        %arrays that take frame numbers (array index) and fps values
        a#1_nFpsAt=new Array(\@anim@nfpsat);$%
      \fi
      \ifdim\@anim@fps pt=\z@%
        a#1_fps=1e-6;$%
      \else
        a#1_fps=\@anim@fps;$%
      \fi
      a#1_spd=1;$%
      a#1_setFps=function(f){$%
        a#1_fps=(f==0?1e-6:f);$%
        if(a#1_playing){$%
          if(a#1_playsRight){a#1_playRight();}else{a#1_playLeft();}$%
        }$%
      };$%
    \fi%
    \if@anim@usrjs
      %array to take frame numbers (array index) and user provided JavaScript
      a#1_usrJS=new Array();$%
      \@anim@usrjsat% populate array
    \fi%
    %actions
    a#1_seekFrame=function(f){$%
      \ifnum\@anim@poster=\@anim@mtwo
        if(f<-1){$%
          \ifcase\@anim@method %icon based
            a#1_wid.buttonSetIcon(blnk);$%
            curdoc.dirty=false;$%
          \or %widget based
            a#1_on=0;$%
            a#1_fr[0].display=display.hidden;$%
            curdoc.dirty=false;$%
          \or %ocg based
            a#1_on=0;$%
            \if@anim@dvisvgm
              a#1_fr[0].setAttribute('visibility','hidden');$%
            \else
              a#1_fr[0].state=false;$%
            \fi%
          \fi
          a#1_idx=-1;$%
        }$%
      \fi
      if(f>\@anim@maxframe||f<0){return -1;}$%
      a#1_idx=f;$%
      \if@anim@usrjs
        if(typeof a#1_usrJS[f]!=='undefined'){$%
          a#1_curjs=a#1_usrJS[f];$%
          var ret=\@anim@app\@anim@setTimeout(% execute JS in "parallel"
            'try{a#1_curjs();}catch(e){console.\@anim@log(e);}',1%
          );$%
        }$%
      \fi%
      \ifcase\@anim@method %icon based
        a#1_wid.buttonSetIcon(a#1_fr[f]);$%
        curdoc.dirty=false;$%
      \or %widget based
        a#1_fr[a#1_on].display=display.hidden;$%
        a#1_fr[f].display=display.visible;$%
        a#1_on=f;$%
        curdoc.dirty=false;$%
      \or%ocg based
        \if@anim@dvisvgm
          a#1_fr[a#1_on].setAttribute('visibility','hidden');$%
          a#1_fr[f].setAttribute('visibility','visible');$%
        \else
          a#1_fr[a#1_on].state=false;$%
          a#1_fr[f].state=true;$%
        \fi
        a#1_on=f;$%
      \fi%
      \if@anim@step\else%
        \if@anim@meas
          a#1_frcnt++;$%
        \fi%
        \if@anim@pauseframes
          if(a#1_playing&&a#1_pauseAt[f]){a#1_pause();}$%
        \fi%
        \if@anim@chfps
          if(a#1_nFpsAt[f]!=a#1_fps){a#1_setFps(a#1_nFpsAt[f]);}$%
        \fi%
      \fi%
      return 0;$%
    };$%
    a#1_stopFirst=function(){%
      \if@anim@step
        a#1_isPaused=false;% stop
      \else
        a#1_pause(true);% stop
      \fi
      a#1_seekFrame(0);%
    };$%
    a#1_stopLast=function(){%
      \if@anim@step
        a#1_isPaused=false;% stop
      \else
        a#1_pause(true);% stop
      \fi
      a#1_seekFrame(\@anim@maxframe);%
    };$%
    a#1_gotoNext=function(){$%
      if(a#1_seekFrame(a#1_idx+1)<0){$%
        \if@anim@step\else\if@anim@meas
          a#1_stopMeas();$%
        \fi\fi%
        \if@anim@palindrome%
          \if@anim@step
            a#1_stopLast();$%
            a#1_playsRight=false;$%
          \else
            if(a#1_playing){%
              a#1_seekFrame(a#1_idx-1);%
              a#1_playLeft();%
            }$%
          \fi%
        \else%
          \if@anim@step
            a#1_stopLast();$%
          \else%
            \if@anim@loop
              if(a#1_playing){%
                a#1_seekFrame(0);%
                a#1_setFps(\@anim@fps);%
              }else{a#1_stopLast();}$%
            \else
              a#1_stopLast();$%
            \fi%
          \fi%
        \fi
      }$%
    };$%
    a#1_gotoPrev=function(){$%
      if(a#1_seekFrame(a#1_idx-1)<0){$%
        \if@anim@step\else\if@anim@meas
          a#1_stopMeas();$%
        \fi\fi%
        \if@anim@palindrome%
          \if@anim@step
            a#1_stopFirst();$%
            a#1_playsRight=true;$%
          \else
            if(a#1_playing){%
              a#1_seekFrame(a#1_idx+1);%
              a#1_playRight();%
            }$%
          \fi%
        \else%
          \if@anim@step
            a#1_stopFirst();$%
          \else%
            \if@anim@loop
              if(a#1_playing){%
                a#1_seekFrame(\@anim@maxframe);%
              }else{a#1_stopFirst();}$%
            \else
              a#1_stopFirst();$%
            \fi%
          \fi%
        \fi
      }$%
    };$%
    \if@anim@step\else
      a#1_pause=function(stop){$%
        try{\@anim@app clearInterval(a#1_int);}catch(e){}$%
        a#1_playing=false;$%
        a#1_isPaused=!stop;$%
        \if@anim@controls@play
          \if@anim@dvisvgm
            a#1_btnPauseLeft.setAttribute('visibility','hidden');$%
            a#1_btnPlayLeft.setAttribute('visibility','visible');$%
            a#1_btnPauseRight.setAttribute('visibility','hidden');$%
            a#1_btnPlayRight.setAttribute('visibility','visible');$%
          \else
            a#1_btnPauseLeft.display=display.hidden;$%
            a#1_btnPlayLeft.display=display.visible;$%
            a#1_btnPauseRight.display=display.hidden;$%
            a#1_btnPlayRight.display=display.visible;$%
            curdoc.dirty=false;$%
          \fi%
        \fi%
      };$%
      a#1_playRight=function(){$%
        \ifnum\@anim@poster>\z@%
          if(a#1_posterOn&&!a#1_isPaused){%
            a#1_seekFrame(0);%
            a#1_posterOn=false;%
          }$%
        \fi%
        \if@anim@meas a#1_startMeas();\fi
        var tmp_int;$%
        try{tmp_int=\@anim@app setInterval('a#1_gotoNext()',%
          1000/a#1_fps/a#1_spd);}catch(e){}$%
        try{\@anim@app clearInterval(a#1_int);}catch(e){}$%
        a#1_int=tmp_int;$%
        a#1_playsRight=true;$%
        a#1_playing=true;$%
        a#1_isPaused=false;$%
        \if@anim@controls@play%
          \if@anim@dvisvgm
            a#1_btnPauseLeft.setAttribute('visibility','visible');$%
            a#1_btnPlayLeft.setAttribute('visibility','hidden');$%
            a#1_btnPauseRight.setAttribute('visibility','visible');$%
            a#1_btnPlayRight.setAttribute('visibility','hidden');$%
          \else
            a#1_btnPauseLeft.display=display.visible;$%
            a#1_btnPlayLeft.display=display.hidden;$%
            a#1_btnPauseRight.display=display.visible;$%
            a#1_btnPlayRight.display=display.hidden;$%
            curdoc.dirty=false;$%
          \fi%
        \fi%
      };$%
      a#1_playLeft=function(){$%
        \ifnum\@anim@poster>\z@%
          if(a#1_posterOn&&!a#1_isPaused){%
            a#1_seekFrame(\@anim@maxframe);%
            a#1_posterOn=false;%
          }$%
        \fi%
        \if@anim@meas a#1_startMeas();\fi
        var tmp_int;$%
        try{tmp_int=\@anim@app setInterval('a#1_gotoPrev()',%
          1000/a#1_fps/a#1_spd);}catch(e){}$%
        try{\@anim@app clearInterval(a#1_int);}catch(e){}$%
        a#1_int=tmp_int;$%
        a#1_playsRight=false;$%
        a#1_playing=true;$%
        a#1_isPaused=false;$%
        \if@anim@controls@play%
          \if@anim@dvisvgm
            a#1_btnPauseLeft.setAttribute('visibility','visible');$%
            a#1_btnPlayLeft.setAttribute('visibility','hidden');$%
            a#1_btnPauseRight.setAttribute('visibility','visible');$%
            a#1_btnPlayRight.setAttribute('visibility','hidden');$%
          \else
            a#1_btnPauseLeft.display=display.visible;$%
            a#1_btnPlayLeft.display=display.hidden;$%
            a#1_btnPauseRight.display=display.visible;$%
            a#1_btnPlayRight.display=display.hidden;$%
            curdoc.dirty=false;$%
          \fi%
        \fi%
      };$%
      \if@anim@controls@play
        a#1_playPauseBwd=function(){%
          if(a#1_playing){a#1_pause();}else{a#1_playBwd();}%
        };$%
        a#1_playPauseFwd=function(){%
          if(a#1_playing){a#1_pause();}else{a#1_playFwd();}%
        };$%
      \fi%
      \if@anim@controls@speed
        a#1_spdIncr=function(){% speed up animation
          a#1_spd*=1.1;%
          a#1_setFps(a#1_fps);%
        };$%
        a#1_spdDecr=function(){% slow down animation
          a#1_spd/=1.1;%
          a#1_setFps(a#1_fps);%
        };$%
        a#1_spdReset=function(){% reset to default speed
          a#1_spd=1;%
          a#1_setFps(a#1_fps);%
        };$%
      \fi%
      \if@anim@meas
        a#1_startMeas=function(){%
          a#1_frcnt=1;% reset frame counter
          a#1_msStart=(new Date()).getTime();%
        };$%
        a#1_stopMeas=function(){$%
          a#1_msEnd=(new Date()).getTime();$%
          \@anim@consoleshow console.\@anim@log(%
            'av.'+spc+'frame'+spc+'rate:'+spc+%
            1e3*a#1_frcnt/(a#1_msEnd-a#1_msStart)+spc+'fps'%
          );$%
        };$%
      \fi%
    \fi%
    \if@anim@step
      a#1_stepBwd=function(){%
        a#1_seekFrame(a#1_idx-1);%
        a#1_isPaused=true;%
      };$%
      a#1_stepFwd=function(){%
        a#1_seekFrame(a#1_idx+1);%
        a#1_isPaused=true;%
      };$%
    \else
      a#1_stepBwd=function(){if(!a#1_playing){%
        a#1_seekFrame(a#1_idx-1);%
        a#1_isPaused=true;%
      }};$%
      a#1_stepFwd=function(){if(!a#1_playing){%
        a#1_seekFrame(a#1_idx+1);%
        a#1_isPaused=true;%
      }};$%
      a#1_playBwd=function(){%
        if(a#1_idx==0){%
          a#1_stopLast();%
          \if@anim@pauseframes if(a#1_pauseAt[a#1_idx]){a#1_pause();return;}\fi%
        }%
        a#1_playLeft();%
      };$%
      a#1_playFwd=function(){%
        if(a#1_idx==\@anim@maxframe){%
          a#1_stopFirst();%
          \if@anim@pauseframes if(a#1_pauseAt[a#1_idx]){a#1_pause();return;}\fi%
        }%
        a#1_playRight();%
      };$%
    \fi%
    %animation API for labelled anims
    \ifx\empty\@anim@label\empty\else
      if(!anim){anim=new Array();}$%
      if(typeof anim['\@anim@label']==='undefined'){$%
        anim['\@anim@label']={$%
          \if@anim@step\else
            playBwd:a#1_playBwd,$%
            playFwd:a#1_playFwd,$%
            pause:a#1_pause,$%
            get isPlaying(){return a#1_playing;},$%
            get fps(){return a#1_fps;},$%
            set fps(f){$%
              if(f<0||isNaN(f)){%
                throw new RangeError('Frame_rate_not_in_allowed_range');%
              }else{a#1_setFps(f);}%
            },$%
            get speed(){return a#1_spd;},$%
            set speed(s){$%
              if(s<=0||isNaN(s)){$%
                throw new RangeError('Speed_factor_not_in_allowed_range');$%
              }else{$%
                a#1_spd=s;$%
                a#1_setFps(a#1_fps);$%
              }$%
            },$%
            get dt(){return 1000/a#1_fps/a#1_spd;},$%
            get playsFwd(){return a#1_playsRight;},$%
          \fi
          get numFrames(){return \@anim@frames;},$%
          get frameNum(){return a#1_idx;},$%
          set frameNum(ff){$%
            var f=Math.floor(ff);$%
            if(f<0||f>\@anim@maxframe||isNaN(f)){%
              throw new RangeError('Frame_number_not_in_allowed_range');%
            }$%
            \if@anim@step
              if(a#1_seekFrame(f)>-1)a#1_isPaused=true;$%
            \else
              if(a#1_playing){$%
                a#1_pause();a#1_seekFrame(f);$%
                if(a#1_playsRight){a#1_playRight();}else{a#1_playLeft();}$%
              }else{if(a#1_seekFrame(f)>-1)a#1_isPaused=true;}$%
            \fi%
          },$%
          stopFirst:a#1_stopFirst,$%
          stopLast:a#1_stopLast$%
        };$%
      }$%
    \fi%
    %poster=none
    \ifnum\@anim@poster=\@anim@mtwo
      a#1_seekFrame(-2);$%
    \fi%
    %poster=<num>|first
    \ifnum\@anim@poster>\@anim@mone
      a#1_seekFrame(\@anim@poster);$%
    \fi%
    %poster=last
    \ifnum\@anim@poster=\@anim@mone
      a#1_seekFrame(\@anim@maxframe);$%
    \fi%
  }%
}

%page-open/visible code
\gdef\@anim@@pojscript#1{% #1: @anim@num
  \if@anim@step\else%
    \if@anim@autoplayorresume%
      \if@anim@autoresume
        if(a#1_isPaused){$%
      \fi
        if(a#1_playsRight){a#1_playFwd();}else{a#1_playBwd();}$%
      \if@anim@autoresume%
        }$%
      \fi%
    \fi%
  \fi%
}

%animation PDF widget's PageOpen/PageVisible code as PDF stream object
\gdef\@anim@pojscript#1{% @anim@num
  \@anim@streamobj{}{%
%    \@anim@consoleshow%
%    console.clear();%
    app.focusRect=false;%
    \@anim@@jsvardecls{#1}%
    \@anim@@jsinit{#1}%
    \@anim@@pojscript{#1}%
  }%
  \global\let\@anim@pojscriptobj\@anim@lastobj%
}

%page-close/invisible code
\gdef\@anim@@pcjscript#1{% #1: @anim@num
  \if@anim@step%
    \if@anim@palindrome
      a#1_playsRight=true;$%
    \fi%
  \else
    if(a#1_playing){%
      \if@anim@autopause
        a#1_pause();%
      \else
        a#1_pause(true);% stop
      \fi%
    }$%
    if(!a#1_isPaused)a#1_playsRight=true;$%
  \fi
  %re-display poster frame
  \ifnum\@anim@poster=\@anim@mtwo %poster=none
    if(!a#1_isPaused&&a#1_idx!=-2)a#1_seekFrame(-2);$% not paused == stopped
  \fi%
  \ifnum\@anim@poster>\@anim@mone %poster=<num>|first
    \ifnum\numexpr%
      \ifnum\@anim@poster>\z@\@ne\else\z@\fi% custom poster
      *\if@anim@step\z@\else\@ne\fi\relax>\z@%
      if(!a#1_isPaused){$%
        if(a#1_idx!=\@anim@poster)a#1_seekFrame(\@anim@poster);$%
        a#1_posterOn=true;$%
      }$%
    \else
      if(!a#1_isPaused&&a#1_idx!=\@anim@poster)a#1_seekFrame(\@anim@poster);$%
    \fi%
  \fi%
  \ifnum\@anim@poster=\@anim@mone %poster=last
    if(!a#1_isPaused&&a#1_idx!=\@anim@maxframe)a#1_seekFrame(\@anim@maxframe);$%
  \fi%
}

%animation PDF widget's PageClose and PageInvisible code as PDF stream object
\gdef\@anim@pcjscript#1{%
  \@anim@streamobj{}{\@anim@@pcjscript{#1}}%
  \global\let\@anim@pcjscriptobj\@anim@lastobj%
}

%other trigger events in the anim widget's AA dictionary
\gdef\@anim@wdgtDownJS#1{%
  \if@anim@step\else % pause on MouseDown
    try{if(a#1_playing){a#1_pause();}}catch(e){}$%
  \fi%
}%
\gdef\@anim@wdgtUpJS#1{%
  try{$%
    \if@anim@step%
      \if@anim@palindrome
        if(event.\@anim@shift)a#1_playsRight=!a#1_playsRight;$%
        if(a#1_idx==\@anim@maxframe){%
          a#1_playsRight=false;%
        }else if(a#1_idx==0){%
          a#1_playsRight=true;%
        }$%
        if(a#1_playsRight){%
          a#1_gotoNext();%
        }else{%
          a#1_gotoPrev();%
        }$%
      \else
        if(event.\@anim@shift){%
          if(a#1_idx==0){%
            a#1_stopLast();%
          }else{%
            a#1_gotoPrev();%
          }$%
        }else{%
          if(a#1_idx==\@anim@maxframe){%
            a#1_stopFirst();%
          }else{%
            a#1_gotoNext();%
          }$%
        }%
      \fi
      a#1_isPaused=true;$%
    \else
      if(event.\@anim@shift)a#1_playsRight=!a#1_playsRight;$%
      \if@anim@palindrome
        if(a#1_idx==0)a#1_playsRight=true;$%
        if(a#1_idx==\@anim@maxframe)a#1_playsRight=false;$%
        if(a#1_playsRight){a#1_playRight();}else{a#1_playLeft();}%
      \else
        var doplay=true;$%
        if(a#1_idx==\@anim@maxframe&&a#1_playsRight){%
          a#1_stopFirst();%
          \if@anim@pauseframes
            if(a#1_pauseAt[a#1_idx]){a#1_pause();doplay=false;}%
          \fi%
        }$%
        if(a#1_idx==0&&!a#1_playsRight){%
          a#1_stopLast();%
          \if@anim@pauseframes
            if(a#1_pauseAt[a#1_idx]){a#1_pause();doplay=false;}%
          \fi%
        }$%
        if(doplay){%
          if(a#1_playsRight){a#1_playRight();}else{a#1_playLeft();}%
        }$%
      \fi
    \fi%
  }catch(e){}$%
}%

\gdef\@anim@EndLeft{EndLeft}%
\gdef\@anim@StepLeft{StepLeft}%
\gdef\@anim@PlayPauseLeft{PlayPauseLeft}%
\gdef\@anim@PlayLeft{PlayLeft}%
\gdef\@anim@PauseLeft{PauseLeft}%
\gdef\@anim@PlayPauseRight{PlayPauseRight}%
\gdef\@anim@PlayRight{PlayRight}%
\gdef\@anim@PauseRight{PauseRight}%
\gdef\@anim@StepRight{StepRight}%
\gdef\@anim@EndRight{EndRight}%
\gdef\@anim@Minus{Minus}%
\gdef\@anim@Reset{Reset}%
\gdef\@anim@Plus{Plus}%

%actions for control buttons
\gdef\@anim@btnJS#1#2{%
  \ifx#2\@anim@EndLeft
    a#1_stopFirst();%
  \else%
  \ifx#2\@anim@StepLeft
    a#1_stepBwd();%
  \else%
  \ifx#2\@anim@PlayPauseLeft
    a#1_playPauseBwd();%
  \else%
  \ifx#2\@anim@PlayPauseRight
    a#1_playPauseFwd();%
  \else%
  \ifx#2\@anim@StepRight
    a#1_stepFwd();%
  \else%
  \ifx#2\@anim@EndRight
    a#1_stopLast();%
  \else%
  \ifx#2\@anim@Minus
    a#1_spdDecr();%
  \else%
  \ifx#2\@anim@Reset
    a#1_spdReset();%
  \else%
  \ifx#2\@anim@Plus
    a#1_spdIncr();%
  \fi\fi\fi\fi\fi\fi\fi\fi\fi%
}

\if@anim@dvisvgm
  \gdef\@anim@script#1{% #1: anim num
    \special{dvisvgm:raw
      $<defs>$%
      <script type="text/javascript">$%
      <![CDATA[$%
        \@anim@@jsvardecls{#1}% variables that are supposed to be global
        (function(){$% IIFE (self-executing anon. function) to limit scope
          function onload(){$%
            \@anim@@jsinit{#1}$%
            \if@anim@nomouse\else
              document.getElementById('anm#1').addEventListener('mouseup',%
                onMouseUpWdgt);$%
              document.getElementById('anm#1').addEventListener('mousedown',%
                onMouseDownWdgt);$%
              document.getElementById('anm#1').addEventListener('touchend',%
                onTouchEndWdgt);$%
            \fi%
            onvisible();$% in case of `autoplay' option
          }$%
          function onvisible(){$%
            \@anim@@pojscript{#1}%
          }$%
          function onhidden(){$%
            \@anim@@pcjscript{#1}%
          }$%
          function onVisibilityChange(){$%
            if(document.hidden){%
              try{onhidden();}catch(e){}%
            }else{%
              try{onvisible();}catch(e){}%
            }$%
          }$%
          document.addEventListener('visibilitychange',onVisibilityChange);$%
          window.addEventListener('load',onload);$%
          \if@anim@nomouse\else
            function onWidgetUpAction(event){$%
              \@anim@wdgtUpJS{#1}%
            }$%
            function onTouchEndWdgt(event){$%
              event.preventDefault();event.stopPropagation();$%
              \if@anim@step\else
                if(a#1_playing){try{a#1_pause();}catch(e){}}else{$%
              \fi%
                \@anim@wdgtUpJS{#1}%
              \if@anim@step\else%
                }$%
              \fi%
            }$%
            function onMouseDownWdgt(event){$%
              event.preventDefault();event.stopPropagation();$%
              \@anim@wdgtDownJS{#1}%
            }$%
            function onMouseUpWdgt(event){$%
              event.preventDefault();event.stopPropagation();$%
              onWidgetUpAction(event);$%
            }$%
          \fi%
        })();$% end of IIFE
      ]]>$%
      </script>$%
      </defs>%
    }%
  }
\fi

\endgroup
\endinput
