% Copyright 2007--2008 Alexander Grahn

% This material is subject to the LaTeX Project Public License. See
%   http://www.ctan.org/tex-archive/help/Catalogue/licenses.lppl.html
% 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, ...)

% makes use of OCGs (Optional Content Groups, aka PDF Layers)

% Supports LaTeX->dvips->ps2pdf or pdfLaTeX workflows.

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{animate}[2008/05/09]

%test for e-TeX
\ifx\eTeXversion\@undefined%
  \PackageError{animate}{%
    Missing support for e-TeX; abandoning...%
  }{%
    Use a TeX compiler that supports e-TeX and enable e-TeX %
    in the format.%
  }%
\fi%

\RequirePackage{keyval}
\RequirePackage{ifthen}
\RequirePackage{ifpdf}
\RequirePackage{ifdraft}
\RequirePackage{calc}

\DeclareOption*{}\ProcessOptions*\relax %allow anything as option
%(package options will be processed near end of this file)

\ifpdf
  %test for correct pdfTeX version
  \ifnum\pdftexversion<120
    \PackageError{animate}{%
      pdfTeX, version >= 1.20, required%
    }{%
      Install a newer version!%
    }%
  \fi
  %load MP to PDF converter
  \ifx\convertMPtoPDF\@undefined%
    \InputIfFileExists{supp-pdf}{}{}%
    \ifx\convertMPtoPDF\@undefined%
      \PackageWarningNoLine{animate}{%
        File `supp-pdf.tex', which is part of ConTeXt, is not%
        \MessageBreak%
        available. Therefore, MetaPost generated PS files%
        \MessageBreak%
        cannot be embedded using pdfTeX%
      }%
    \fi%
  \fi%
\else
  %define `?pdfmark' operator as in file hdvips.def from package `hyperref'
  \AtBeginDvi{%
    \ifx\pdfmark\@undefined%
      \special{!
        systemdict /pdfmark known%
        {%
          userdict /?pdfmark systemdict /exec get put%
        }{%
          userdict /?pdfmark systemdict /pop get put
          userdict /pdfmark systemdict /cleartomark get put%
        }%
        ifelse%
      }%
    \fi%
  }
\fi%

%\pdfmdfivesum is used for hashing object references of embedded files based
%on their MD5 digest (in order to avoid multiple inclusion); if it is not
%available, hashing will be based on the file name
\ifx\@undefined\pdfmdfivesum
  \def\pdfmdfivesum file #1{#1}
\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}

%missing package error message
\def\@anim@missing#1{%
  \PackageError{animate}{%
    Package `#1' has not been loaded yet%
  }{%
    Put the line\MessageBreak%
    `\protect\usepackage{#1}'\MessageBreak%
    to the preamble of your document!%
  }%
}

\newboolean{@anim@grxloaded}
\setboolean{@anim@grxloaded}{false}
\AtBeginDocument{%
  \@ifpackageloaded{graphicx}{%
    \setboolean{@anim@grxloaded}{true}%
  }{}%
}

%add AcroForm and OCProperties entries to Catalog
\ifpdf
  \newcount\@anim@@fields
  \pdfobj reserveobjnum
  \@anim@@fields=\pdflastobj
  \newcount\@anim@@ocgs
  \pdfobj reserveobjnum
  \@anim@@ocgs=\pdflastobj
  \newcount\@anim@@posterocgs
  \pdfobj reserveobjnum
  \@anim@@posterocgs=\pdflastobj
  \pdfcatalog{
    /AcroForm <<
      /Fields \the\@anim@@fields\space 0 R
    >>
    /OCProperties <<
      /OCGs \the\@anim@@ocgs\space 0 R
      /D <<
        /BaseState/OFF /ON \the\@anim@@posterocgs\space 0 R
%        /Order \the\@anim@@ocgs\space 0 R
      >>
    >>
  }
  \def\@anim@fields{}
  \def\@anim@ocgs{}
  \def\@anim@posterocgs{}
  \AtEndDocument{%
    \immediate\pdfobj useobjnum \@anim@@fields {%
      [\@anim@fields]%
    }%
    \immediate\pdfobj useobjnum \@anim@@ocgs {%
      [\@anim@ocgs]%
    }%
    \immediate\pdfobj useobjnum \@anim@@posterocgs {%
      [\@anim@posterocgs]%
    }%
  }%
\else
  % counts inserted interactive fields (widget annots)
  \newcount\@anim@fieldnum\@anim@fieldnum=0
  \special{ps:
    % array which takes refs to all widget annotations
    mark /_objdef {@anim@fields} /type/array /OBJ pdfmark
    % array which takes all ocgs (one per animation frame)
    mark /_objdef {@anim@ocgs} /type/array /OBJ pdfmark
    % array which takes ocgs shown by default (the poster frames)
    mark /_objdef {@anim@posterocgs} /type/array /OBJ pdfmark
    mark {Catalog} <<
      /AcroForm <<
        /Fields {@anim@fields}
      >>
      /OCProperties <<
        /OCGs {@anim@ocgs}
        /D <<
          /BaseState/OFF /ON {@anim@posterocgs}
%          /Order {@anim@ocgs}
        >>
      >>
    >> /PUT pdfmark
  }
\fi

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

%tests whether file is already known (and embedded), if so it returns identifier
%of xobj, otherwise returns `!'
\def\@anim@fileknown#1#2{% #1 file name, pdf: #2 page No. ps: #2 @anim@num
  %identify file by md5sum
  \ifcsname file:\pdfmdfivesum file {#1}.#2\endcsname%
    \@anim@getkeyval{file:\pdfmdfivesum file {#1}.#2}\else!\fi%
}
\def\@anim@makefileknown#1#2#3{%#1 file name, pdf: #2 page No. ps: #2 @anim@num,
  \@anim@newkey{file:\pdfmdfivesum file {#1}.#2}{#3}%   #3 Form XObj ID
}

\ifpdf
  %helper macro that typesets graphics file into savebox
  \def\@anim@filebox#1#2{% #1 filename, #2 page No. for multipage files
    \ifx\@anim@gropts\@empty%
      \def\@anim@mps{.mps}%
      \ifx\@anim@ext\@anim@mps%
        \global\setbox\@anim@box=\hbox{\convertMPtoPDF{#1}{1}{1}}%
      \else%
        \pdfximage page #2 {#1}%
        \global\setbox\@anim@box=\hbox{\pdfrefximage\pdflastximage}%
      \fi%
    \else%
      \edef\@anim@curfile{*[page=#2\@anim@gropts]{#1}}%
      \global\setbox\@anim@box=\hbox{%
        \expandafter\includegraphics\@anim@curfile}%
    \fi%
  }
\else
  \def\@anim@filebox#1#2{% #1 filename, #2 ignored
    \ifx\@anim@gropts\@empty%
      \edef\@anim@curfile{*{#1}}%
    \else%
      \edef\@anim@curfile{*[\@anim@gropts]{#1}}%
    \fi%
    \global\setbox\@anim@box=\hbox{\expandafter\includegraphics\@anim@curfile}%
  }
\fi

%create Form XObject from graphics file
\ifthenelse{\boolean{pdf}}{
  \def\@anim@ximage#1#2#3#4{%#1:@anim@num, #2:@anim@curframe@zb, #3:filename,
    %#4: page number of multipage pdf
    \ifthenelse{\equal{\@anim@fileknown{#3}{#4}}{!}}{% new file to be embedded
      \@anim@filebox{#3}{#4}% store file in a box
      \@anim@getsize{\@anim@box}%
      \ifnum#2=0
        \@anim@scale% scale animation widget
      \fi%
      \@anim@xinline{#1}{#2}{\@anim@box}{!}% delegate Form XObject creation
      \ifx\@anim@gropts\@empty\if@anim@multipdf\message{<#4>}\fi\fi%
      \@anim@makefileknown{#3}{#4}{\@anim@lastxform}% keep form xobject number
    }{% file known, re-use existing form xobject
      \ifnum#2=0
        \@anim@filebox{#3}{#4}% for size determination of first frame
        \@anim@getsize{\@anim@box}%
        \@anim@scale%
      \fi%
      \@anim@xinline{#1}{#2}{}{\@anim@fileknown{#3}{#4}}%
    }%
  }
}{
  \def\@anim@ximage#1#2#3#4{%#1:@anim@num, #2:@anim@curframe@zb, #3:filename,
    %#4: not used
    \ifthenelse{\equal{\@anim@fileknown{#3}{#1}}{!}}{%file is new to current
      \@anim@filebox{#3}{}% store file in a box                       animation
      \@anim@getsize{\@anim@box}%
      \ifnum#2=0
        \@anim@scale%
      \fi%
      \@anim@xinline{#1}{#2}{\@anim@box}{!}% delegate Form XObject creation
      \@anim@makefileknown{#3}{#1}{\@anim@lastxform}% keep form xobject ref
    }{%file already used in the current animation, re-use existing form xobject
      \ifnum#2=0
        \@anim@filebox{#3}{}% for size determination of first frame
        \@anim@getsize{\@anim@box}%
        \@anim@scale%
      \fi%
      \@anim@xinline{#1}{#2}{}{\@anim@fileknown{#3}{#1}}%
    }%
  }
}

\newboolean{@anim@timeline} %true if `timeline' option was set

%creates Form XObject from box contents
\ifpdf%
  %#1: @anim@num, #2:@anim@curframe@zb, #3: empty (`') or box number,
  %#4: `!' or form xobj identifier
  \def\@anim@xinline#1#2#3#4{%
    \ifthenelse{\equal{#4}{!}}{%
      %put graphics into XObject
      \global\setbox\@anim@box=\hbox{%
        \raisebox{-\dp#3}[\@anim@frameheight\p@][\@anim@framedepth\p@]{\box#3}%
      }%
      \immediate\pdfxform\@anim@box%
      \xdef\@anim@lastxform{\the\pdflastxform}%
      %keep a record of XObject number
      \@anim@newkey{img@#2}{\@anim@lastxform\space 0 R}%
    }{%
      \@anim@newkey{img@#2}{#4\space 0 R}%
    }%
    \if@anim@timeline\else%
      \@anim@newkey{frmxobject@#2}{/im#2\space\@anim@getkeyval{img@#2}}%
      \@anim@newkey{frmcontent@#2}{ q /im#2\space Do Q}%
      \@anim@makeframe{#1}{#2}%
    \fi%
  }
\else
  \def\@anim@xinline#1#2#3#4{%
    \ifthenelse{\equal{#4}{!}}{%
      \begingroup%
        \setlength{\unitlength}{1pt}%
        %mark BBox of the graphics
        \begin{picture}(0,0)%
        \put(0,-\@anim@framedepth){%
          \special{ps:
            currentpoint /frame@lly exch def /frame@llx exch def
          }%
        }%
        \put(\@anim@framewidth,\@anim@frameheight){%
          \special{ps:
            currentpoint /frame@ury exch def /frame@urx exch def
          }%
        }%
        \end{picture}%
      \endgroup%
      %distill graphics into XObject
      \special{ps:
        gsave
        mark
          /_objdef {@anim@frm@#1@#2}
          /BBox [frame@llx frame@lly frame@urx frame@ury]
        /BP pdfmark
        {%
          isls{%landscape mode (powerdot, geometry /w landscape option)
            /frame@dxdy {%
              frame@urx frame@llx sub abs
              frame@ury frame@lly sub abs div%
            } bind def
            /frame@dydx {1 frame@dxdy div} bind def
            /frame@cx {frame@urx frame@llx add 2 div} bind def
            /frame@cy {frame@ury frame@lly add 2 div} bind def
            % graphics needs to be rescaled for some reason ...
            [frame@dxdy 0 0 frame@dydx 1 frame@dxdy sub frame@cx mul
              1 frame@dydx sub frame@cy mul] concat
            %... rotated by 270 degrees ...
            [0 -1 1 0 frame@cx frame@cy sub frame@cx frame@cy add] concat
            %... and flipped around vertical axis
            [-1 0 0 1 frame@llx frame@urx add 0] concat%
          }{% flip around horizontal axis in portrait mode
            [1 0 0 -1 0 frame@lly frame@ury add] concat%
          }ifelse
        }?pdfmark
      }%
      \begin{picture}(0,0)%
        \put(0,0){\box#3}%
      \end{picture}%
      \special{ps:
        mark /EP pdfmark
        grestore
      }%
      \xdef\@anim@lastxform{@anim@frm@#1@#2}%
      \@anim@newkey{img@#2}{{@anim@frm@#1@#2}}%
    }{%
      \@anim@newkey{img@#2}{{#4}}%
    }%
    \if@anim@timeline\else%
      \@anim@newkey{frmxobject@#2}{/im#2\space\@anim@getkeyval{img@#2}}%
      \@anim@newkey{frmcontent@#2}{ q /im#2\space Do Q}%
      \@anim@makeframe{#1}{#2}%
    \fi%
  }
\fi

%creates OCG
\ifpdf
  \def\@anim@newocg#1#2{%#1:@anim@num, #2:@anim@curframe@zb
    \immediate\pdfobj{<</Type/OCG /Name (#1.#2)>>}% new ocg
    \xdef\@anim@curocg{\the\pdflastobj\space 0 R}%
    %append to ocg array
    \xdef\@anim@ocgs{\@anim@ocgs\space\@anim@curocg}%
    %store ref to ocg if it's a poster
    \ifnum#2=0
      \def\@anim@posterframe{first}%
    \else%
      \def\@anim@posterframe{last}%
    \fi%
    \ifx\@anim@poster\@anim@posterframe%
      \xdef\@anim@posterocg{\@anim@curocg}%
    \fi%
  }
\else
  \def\@anim@newocg#1#2{%#1:@anim@num, #2:@anim@curframe@zb
    \special{ps:
      mark /_objdef {@anim@ocg@#1@#2} /type/dict /OBJ pdfmark %new ocg
      mark {@anim@ocg@#1@#2} <<
        /Type/OCG /Name (#1.#2)
      >> /PUT pdfmark
      mark {@anim@ocgs} {@anim@ocg@#1@#2} /APPEND pdfmark
    }%
    \ifnum#2=0
      \def\@anim@posterframe{first}%
    \else%
      \def\@anim@posterframe{last}%
    \fi%
    \ifx\@anim@poster\@anim@posterframe%
      \xdef\@anim@posterocg{@anim@ocg@#1@#2}%
    \fi%
  }
\fi

%non-interactive Widget annotation, representing one frame of the animation
\ifpdf
  \def\@anim@makeframe#1#2{% #1:@anim@num, #2:@anim@curframe@zb
    %container XObject for all transparencies that compose the frame
    \setlength{\unitlength}{1pt}%
    \global\setbox\@anim@box=\hbox{%
      \pdfliteral{q}%
      \begin{picture}(\@anim@framewidth,\@anim@frametotalheight)%
      \end{picture}%
      \pdfliteral{Q\@anim@getkeyval{frmcontent@#2}}%
    }%
    \immediate\pdfxform resources {%
      /XObject <<\@anim@getkeyval{frmxobject@#2}>>}\@anim@box%
    %
    %frame widget, using the container XObject as its Appearance,
    %associated with the just created OCG
    \@anim@newocg{#1}{#2}% create new opt. content group (OCG)
    \pdfannot
      width \@anim@animwidth\p@ height \@anim@animheight\p@
      depth \@anim@animdepth\p@
    {%
      /Subtype/Widget%
      /OC \@anim@curocg
      /F 4 % allow printing
      /FT/Btn/Ff 65537% non-interactive push button
      /BS <</W 0>>%
      /AP <</N \the\pdflastxform\space 0 R>>%
      /T (anm#1fr#2)%
    }%
  }
\else
  \def\@anim@makeframe#1#2{%
    %container XObject
    \special{ps:
      mark  /_objdef {@anim@anmfrm@#1@#2} /type/stream /OBJ pdfmark
      mark {@anim@anmfrm@#1@#2} (\@anim@getkeyval{frmcontent@#2}) /PUT
        pdfmark
      mark {@anim@anmfrm@#1@#2} <<
        /Type/XObject/Subtype/Form/FormType 1
        /BBox [frame@llx frame@lly frame@urx frame@ury]
        /Resources <</XObject <<\@anim@getkeyval{frmxobject@#2}>>>>
      >> /PUT pdfmark
    }%
    %
    %frame widget
    \setlength{\unitlength}{1pt}%
    \begin{picture}(0,0)% mark annotation rectangle
      \put(0,-\@anim@animdepth){%
      \special{ps:
        currentpoint /wid@lly exch def /wid@llx exch def
      }%
    }%
    \put(\@anim@animwidth,\@anim@animheight){%
      \special{ps:
        currentpoint /wid@ury exch def /wid@urx exch def
      }%
    }%
    \end{picture}%
    \@anim@newocg{#1}{#2}%
    \special{ps:
      mark
        /_objdef {annot@\the\@anim@fieldnum}%
        /Rect [wid@llx wid@lly wid@urx wid@ury]%
        /Subtype/Widget%
        /OC {@anim@ocg@#1@#2}
        /F 4
        /FT/Btn/Ff 65537
        /BS <</W 0>>%
        /AP <</N {@anim@anmfrm@#1@#2}>>%
        /T (anm#1fr#2)%
      /ANN pdfmark
    }%
    \global\advance\@anim@fieldnum by \@ne%
  }
\fi

%create container XObjects for play & pause button faces
\ifpdf
  \def\@anim@makeppcontainer#1#2#3{% #1: @anim@num, #2: Right or Left,
    % #3: index of existing button face colour combination
    \immediate\pdfobj{<</Type/OCG /Name (#1.Play#2)>>}%
    \edef\@anim@playocg{\the\pdflastobj\space 0 R}%
    \immediate\pdfobj{<< /Type/OCG /Name (#1.Pause#2)>>}%
    \edef\@anim@pauseocg{\the\pdflastobj\space 0 R}%
    %append to ocg array
    \xdef\@anim@ocgs{\@anim@ocgs\space\@anim@playocg\space\@anim@pauseocg}%
    %although not really necessary, append play button ocg to posterocg array
    \xdef\@anim@posterocgs{\@anim@posterocgs\space\@anim@playocg}%
    %new container XObject
    \immediate\pdfobj stream attr{%
      /Type/XObject/Subtype/Form/BBox [0 0 15 15]%
      /Resources <<%
        /Properties <</oc0 \@anim@playocg /oc1 \@anim@pauseocg>>%
        /XObject <<%
          /im0 \@anim@getkeyval{btnPlay#2:#3}
          /im1 \@anim@getkeyval{btnPause#2:#3}%
        >>%
      >>%
    }{%
      /OC/oc0 BDC q /im0 Do Q EMC /OC/oc1 BDC q /im1 Do Q EMC%
    }%
    \@anim@newkey{btn#1.PlayPause#2}{\the\pdflastobj\space 0 R}%
  }
\else
  \def\@anim@makeppcontainer#1#2#3{%
    \special{ps:
      %OCGs
      mark /_objdef {#1.Play#2} /type/dict /OBJ pdfmark
      mark {#1.Play#2} <<
        /Type/OCG /Name (#1.Play#2)
      >> /PUT pdfmark
      mark /_objdef {#1.Pause#2} /type/dict /OBJ pdfmark
      mark {#1.Pause#2} <<
        /Type/OCG /Name (#1.Pause#2)
      >> /PUT pdfmark
      %append to ocg array
      mark {@anim@ocgs} {#1.Play#2} /APPEND pdfmark
      mark {@anim@ocgs} {#1.Pause#2} /APPEND pdfmark
      %append play button ocg to posterocg array
      mark {@anim@posterocgs} {#1.Play#2} /APPEND pdfmark
      %new container XObject
      mark  /_objdef {btn#1.PlayPause#2} /type/stream /OBJ pdfmark
      mark {btn#1.PlayPause#2} (%
        /OC/oc0 BDC q /im0 Do Q EMC /OC/oc1 BDC q /im1 Do Q EMC%
      ) /PUT pdfmark
      mark {btn#1.PlayPause#2} <<
        /Type/XObject/Subtype/Form/FormType 1
        /BBox [0 0 15 15]
        /Resources <<%
          /Properties <</oc0 {#1.Play#2} /oc1 {#1.Pause#2}>>%
          /XObject <<%
            /im0 {btnPlay#2:#3}
            /im1 {btnPause#2:#3}%
          >>%
        >>%
      >> /PUT pdfmark
    }%
  }
\fi

%create XObjects of all button faces
\ifpdf
  %stroking commands
  \def\@anim@btnend{%
    \@anim@bg\space
    2.5 w
    1 J
    1 j
    \@anim@fg\space
    10 10 130 130 re
    S
    10 w
    45 40 m
    95 75 l
    45 110 l
    S
    0 j
    105 44 m
    105 106 l
    S
  }
  \def\@anim@btnstep{%
    \@anim@bg\space
    2.5 w
    1 J
    1 j
    \@anim@fg\space
    10 10 130 130 re
    S
    10 w
    50 40 m
    100 75 l
    50 110 l
    S
  }
  \def\@anim@btnplay{%
    \@anim@bg\space
    2.5 w
    1 J
    1 j
    \@anim@fg\space
    10 10 130 130 re
    S
    10 w
    50 40 m
    50 110 l
    100 75 l
    h
    S
  }
  \def\@anim@btnpause{%
    \@anim@bg\space
    2.5 w
    1 j
    \@anim@fg\space
    0 10 m
    140 10 l
    140 140 l
    0 140 l
    S
    20 w
    22 40 m
    22 110 l
    S
  }
  \def\@anim@btnminus{%
    \@anim@bg\space
    2.5 w
    1 J
    1 j
    \@anim@fg\space
    10 10 130 130 re
    S
    10 w
    0 J
    40 75 m
    110 75 l
    S
  }
  \def\@anim@btnplus{%
    \@anim@bg\space
    2.5 w
    1 J
    1 j
    \@anim@fg\space
    10 10 130 130 re
    S
    10 w
    0 J
    40 75 m
    110 75 l
    75 40 m
    75 110 l
    S
  }
  \def\@anim@btnreset{%
    \@anim@bg\space
    2.5 w
    1 J
    1 j
    \@anim@fg\space
    10 10 130 130 re
    S
    10 w
    75 40 m
    75 110 l
    S
    \@anim@@@fg\space
    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@makebuttons#1{%
    \@anim@xbutton{EndLeft}{%
      q -0.1 0 0 0.1 15 0 cm
      \@anim@btnend\space Q%
    }{#1}%
    \@anim@xbutton{EndRight}{%
      q 0.1 0 0 0.1 0 0 cm
      \@anim@btnend\space Q%
    }{#1}%
    \@anim@xbutton{Minus}{%
      q 0.1 0 0 0.1 0 0 cm
      \@anim@btnminus\space Q%
    }{#1}%
    \@anim@xbutton{PauseLeft}{%
      q -0.1 0 0 0.1 15 0 cm
      \@anim@btnpause\space Q%
    }{#1}%
    \@anim@xbutton{PauseRight}{%
      q 0.1 0 0 0.1 0 0 cm
      \@anim@btnpause\space Q%
    }{#1}%
    \@anim@xbutton{PlayLeft}{%
      q -0.1 0 0 0.1 15 0 cm
      \@anim@btnplay\space Q%
    }{#1}%
    \@anim@xbutton{PlayRight}{%
      q 0.1 0 0 0.1 0 0 cm
      \@anim@btnplay\space Q%
    }{#1}%
    \@anim@xbutton{Plus}{%
      q 0.1 0 0 0.1 0 0 cm
      \@anim@btnplus\space Q%
    }{#1}%
    \@anim@xbutton{Reset}{%
      q 0.1 0 0 0.1 0 0 cm
      \@anim@btnreset\space Q%
    }{#1}%
    \@anim@xbutton{StepLeft}{%
      q -0.1 0 0 0.1 15 0 cm
      \@anim@btnstep\space Q%
    }{#1}%
    \@anim@xbutton{StepRight}{%
      q 0.1 0 0 0.1 0 0 cm
      \@anim@btnstep\space Q%
    }{#1}%
  }
  %XObject creation
  \def\@anim@xbutton#1#2#3{% #1 name, #2 stroking commands, #3 @anim@num
    \immediate\pdfobj stream attr{%
      /Type/XObject/Subtype/Form/BBox [0 0 15 15]%
    }{#2}%
    \@anim@newkey{btn#1:#3}{\the\pdflastobj\space 0 R}%
  }
\else
  \def\@anim@btnend{%
    \@anim@bg\space
    \@anim@fg\space
    0.25 setlinewidth
    1 setlinecap
    1 setlinejoin
    1 1 moveto
    14 1 lineto
    14 14 lineto
    1 14 lineto
    closepath
    stroke
    1 setlinewidth
    4.5 4 moveto
    9.5 7.5 lineto
    4.5 11 lineto
    stroke
    0 setlinejoin
    10.5 4.4 moveto
    10.5 10.6 lineto
    stroke
  }
  \def\@anim@btnstep{%
    \@anim@bg\space
    \@anim@fg\space
    0.25 setlinewidth
    1 setlinecap
    1 setlinejoin
    1 1 moveto
    14 1 lineto
    14 14 lineto
    1 14 lineto
    closepath
    stroke
    1 setlinewidth
    5 4 moveto
    10 7.5 lineto
    5 11 lineto
    stroke
  }
  \def\@anim@btnplay{%
    \@anim@bg\space
    \@anim@fg\space
    0.25 setlinewidth
    1 setlinecap
    1 setlinejoin
    1 1 moveto
    14 1 lineto
    14 14 lineto
    1 14 lineto
    closepath
    stroke
    1 setlinewidth
    5 4 moveto
    5 11 lineto
    10 7.5 lineto
    closepath
    stroke
  }
  \def\@anim@btnpause{%
    \@anim@bg\space
    \@anim@fg\space
    0.25 setlinewidth
    0 setlinecap
    1 setlinejoin
    0 1 moveto
    14 1 lineto
    14 14 lineto
    0 14 lineto
    stroke
    2 setlinewidth
    2.2 4 moveto
    2.2 11 lineto
    stroke
  }
  \def\@anim@btnminus{%
    \@anim@bg\space
    \@anim@fg\space
    0.25 setlinewidth
    1 setlinecap
    1 setlinejoin
    1 1 moveto
    14 1 lineto
    14 14 lineto
    1 14 lineto
    closepath
    stroke
    1.0 setlinewidth
    0 setlinecap
    4 7.5 moveto
    11 7.5 lineto
    stroke
  }
  \def\@anim@btnplus{%
    \@anim@bg\space
    \@anim@fg\space
    0.25 setlinewidth
    1 setlinecap
    1 setlinejoin
    1 1 moveto
    14 1 lineto
    14 14 lineto
    1 14 lineto
    closepath
    stroke
    1.0 setlinewidth
    0 setlinecap
    4 7.5 moveto
    11 7.5 lineto
    7.5 4  moveto
    7.5 11 lineto
    stroke
  }
  \def\@anim@btnreset{%
    \@anim@bg\space
    \@anim@fg\space
    0.25 setlinewidth
    1 setlinecap
    1 setlinejoin
    1 1 moveto
    14 1 lineto
    14 14 lineto
    1 14 lineto
    closepath
    stroke
    1 setlinewidth
    7.5 4 moveto
    7.5 11 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@makebuttons#1{% #1: @anim@num
    \@anim@xbutton{EndLeft}{%
      [-1 0 0 1 15 0] concat
      \@anim@btnend%
    }{#1}%
    \@anim@xbutton{EndRight}{%
      \@anim@btnend%
    }{#1}%
    \@anim@xbutton{Minus}{%
      \@anim@btnminus%
    }{#1}%
    \@anim@xbutton{PauseLeft}{%
      [-1 0 0 1 15 0] concat
      \@anim@btnpause%
    }{#1}%
    \@anim@xbutton{PauseRight}{%
      \@anim@btnpause%
    }{#1}%
    \@anim@xbutton{PlayLeft}{%
      [-1 0 0 1 15 0] concat
      \@anim@btnplay%
    }{#1}%
    \@anim@xbutton{PlayRight}{%
      \@anim@btnplay%
    }{#1}%
    \@anim@xbutton{Plus}{%
      \@anim@btnplus%
    }{#1}%
    \@anim@xbutton{Reset}{%
      \@anim@btnreset%
    }{#1}%
    \@anim@xbutton{StepLeft}{%
      [-1 0 0 1 15 0] concat
      \@anim@btnstep%
    }{#1}%
    \@anim@xbutton{StepRight}{%
      \@anim@btnstep%
    }{#1}%
  }
  \def\@anim@xbutton#1#2#3{% #1: name; #2: stroking commands, #3 @anim@num
    \special{ps:
      gsave [1 0 0 1 0 0] setmatrix
      mark
        /_objdef {btn#1:#3}
        /BBox [0 0 15 15]
      /BP pdfmark
      {%
        %landscape mode (powerdot, geometry /w landscape option)
        isls{%
          [0 -1 1 0 0 15] concat % rotate by 270 degrees
          [-1 0 0 1 15 0] concat%  flip around vertical axis
        }if
        #2
      }?pdfmark mark /EP pdfmark
      grestore
    }%
  }
\fi

%determines file type of the sequence
\ifpdf
  \def\@anim@getext#1{%
    \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{.jbig2}%
    \IfFileExists{#1\@anim@ext}{}{%
    \gdef\@anim@ext{.jb2}%
    \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}{%
        Neither of the files\MessageBreak%
        `#1.pdf',\MessageBreak%
        `#1.mps',\MessageBreak%
        `#1.png',\MessageBreak%
        `#1.jpg',\MessageBreak%
        `#1.jpeg',\MessageBreak%
        `#1.jbig2',\MessageBreak%
        `#1.jb2',\MessageBreak%
        `#1.jp2',\MessageBreak%
        `#1.j2k', or\MessageBreak%
        `#1.jpx',\MessageBreak%
        could be found.\MessageBreak%
        Wrong file type? Mis-spelled file name?%
      }{}%
    }}}}}}}}}}%
  }%
\else
  \def\@anim@getext#1{%
    \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}{%
        Neither 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?%
      }{}%
    }}}%
  }%
\fi

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

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

%draftbox from savebox
\def\@anim@draftbox#1{% #1: box number
  %get size
  \setlength{\unitlength}{1pt}%
  \vtop{% draw boxes
    \offinterlineskip%
    \hbox{%
      \raisebox{-\@anim@animdepth\p@}{%
        \begin{picture}(\@anim@animwidth,\@anim@animtotalheight)%
          \put(0,0){\framebox(\@anim@animwidth,\@anim@animtotalheight){}}%
        \end{picture}%
      }%
    }%
    \if@anim@controls%
      \setlength{\@anim@tmpdima}{\@anim@btnsize}%
      \vskip 0.1\@anim@tmpdima%
      \hbox to \@anim@animwidth\p@ {%
        \hss%
        \setlength{\@anim@tmpdimb}{2\@anim@tmpdima}%
        \begin{picture}(\strip@pt\@anim@tmpdimb,\strip@pt\@anim@tmpdima)%
          \put(0,0){\line(1,0){\strip@pt\@anim@tmpdimb}}%
          \put(0,\strip@pt\@anim@tmpdima){%
            \line(1,0){\strip@pt\@anim@tmpdimb}%
          }%
          \put(0,0){\line(0,1){\strip@pt\@anim@tmpdima}}%
        \end{picture}%
        \if@anim@step\else%
          \begin{picture}(\strip@pt\@anim@tmpdimb,\strip@pt\@anim@tmpdima)%
            \put(0,0){\line(1,0){\strip@pt\@anim@tmpdimb}}%
            \put(0,\strip@pt\@anim@tmpdima){%
              \line(1,0){\strip@pt\@anim@tmpdimb}%
            }%
          \end{picture}%
        \fi%
        \begin{picture}(\strip@pt\@anim@tmpdimb,\strip@pt\@anim@tmpdima)%
          \put(0,0){\line(1,0){\strip@pt\@anim@tmpdimb}}%
          \put(0,\strip@pt\@anim@tmpdima){%
            \line(1,0){\strip@pt\@anim@tmpdimb}%
          }%
          \put(\strip@pt\@anim@tmpdimb,0){%
            \line(0,1){\strip@pt\@anim@tmpdima}%
          }%
        \end{picture}%
        \if@anim@step\else%
          \hskip 0.3\@anim@tmpdima%
          \setlength{\@anim@tmpdimb}{3\@anim@tmpdima}%
          \begin{picture}(\strip@pt\@anim@tmpdimb,\strip@pt\@anim@tmpdima)%
            \put(0,0){\line(0,1){\strip@pt\@anim@tmpdima}}%
            \put(0,0){\line(1,0){\strip@pt\@anim@tmpdimb}}%
            \put(0,\strip@pt\@anim@tmpdima){%
              \line(1,0){\strip@pt\@anim@tmpdimb}%
            }%
            \put(\strip@pt\@anim@tmpdimb,0){%
              \line(0,1){\strip@pt\@anim@tmpdima}%
            }%
          \end{picture}%
        \fi%
        \hss%
      }%
    \fi%
  }%
}

\newboolean{@anim@multipdf} % multipage PDF?

\ifpdf
  %detects multipage PDF and corrects user supplied page range
  \def\@anim@checkmultipdf#1{% #1: file base name
    \IfFileExists{#1.pdf}{%
      \gdef\@anim@ext{.pdf}%
      \@anim@filebox{#1.pdf}{1}%
      \ifx\@anim@first\@empty%
        \@anim@curframe=\@ne%
      \else%
        \ifnum\@anim@first<0 \gdef\@anim@first{0}\fi%
        \@anim@curframe=\@anim@first%
        \advance\@anim@curframe by \@ne% because pdfximage pages are 1-based
        \ifnum\@anim@curframe>\pdflastximagepages
          \@anim@curframe=\pdflastximagepages% correct wrong user input
        \fi%
      \fi%
      \xdef\@anim@first{\the\@anim@curframe}%
      \ifx\@anim@last\@empty%
        \@anim@curframe=\pdflastximagepages%
      \else%
        \ifnum\@anim@last<0 \gdef\@anim@last{0}\fi%
        \@anim@curframe=\@anim@last%
        \advance\@anim@curframe by \@ne%
        \ifnum\@anim@curframe>\pdflastximagepages
          \@anim@curframe=\pdflastximagepages%
        \fi%
      \fi%
      \ifnum\@anim@first>\@anim@curframe
        \xdef\@anim@last{\@anim@first}%
        \xdef\@anim@first{\the\@anim@curframe}%
      \else%
        \xdef\@anim@last{\the\@anim@curframe}%
      \fi%
      \setboolean{@anim@multipdf}{true}%
    }{}%
  }
\else
  \def\@anim@checkmultipdf#1{}% no-op in dvi mode
\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][]{%
  \leavevmode%
  \begingroup%
  \ifpdf\else%
    \ifx\includegraphics\@undefined\@anim@missing{graphics}\fi%
  \fi%
  \@anim@reset% to default settings
  \setkeys{anim@user}{#1}%
  \def\@anim@base{#3}%
  \def\@anim@first{#4}%
  \def\@anim@last{#5}%
  %
  \@anim@checkmultipdf{\@anim@base}%test for multipage pdf
  \@anim@curframe=\@anim@first%
  \global\@anim@curframe@zb=0%
  %
  \if@anim@draft%
    \if@anim@multipdf%
      %store file in a box
      \@anim@filebox{\@anim@base.pdf}{\@anim@first}%
    \else%
      %get file name extension
      \@anim@getext{\@anim@base\@anim@first}%
      \@anim@filebox{\@anim@base\@anim@first\@anim@ext}{1}%
    \fi%
    %draw draftbox according to dimensions of the first frame
    \@anim@draftbox{\@anim@box}%
  \else%
    \if@anim@controls%
      %create button faces only once in the current colour combination
      \ifcsname
        btncol:\@anim@bgcolour:\@anim@fgcolour\endcsname\else%
        \@anim@makebuttons{\the\@anim@num}%
        \@anim@newkey{btncol:\@anim@bgcolour:\@anim@fgcolour}{\the\@anim@num}%
      \fi%
      \if@anim@step\else%
        %combine Pause & Play button faces into one XObject
        \@anim@makeppcontainer{\the\@anim@num}{Right}{%
          \@anim@getkeyval{btncol:\@anim@bgcolour:\@anim@fgcolour}}%
        \@anim@makeppcontainer{\the\@anim@num}{Left}{%
          \@anim@getkeyval{btncol:\@anim@bgcolour:\@anim@fgcolour}}%
      \fi%
    \fi%
    \xdef\@anim@fps{#2}% current frame rate
    \xdef\@anim@nfps{#2}%
    %
    %read all frames of the sequence
    \loop\ifnum\@anim@curframe>\@anim@last\else%
      \if@anim@multipdf%
        %embed graphics
        \@anim@ximage{\the\@anim@num}{\the\@anim@curframe@zb}{\@anim@base.pdf}{%
          \the\@anim@curframe}%
      \else%
        %get file name extension of current file
        \@anim@getext{%
          \@anim@base\@anim@pad{\@anim@first}{\the\@anim@curframe}%
        }%
        %embed graphics
        \@anim@ximage{\the\@anim@num}{\the\@anim@curframe@zb}{%
          \@anim@base\@anim@pad{\@anim@first}{\the\@anim@curframe}\@anim@ext%
        }{1}%
      \fi%
      \advance\@anim@curframe by \@anim@every%
      \global\advance\@anim@curframe@zb by \@ne%
    \repeat%
    \xdef\@anim@frames{\the\@anim@curframe@zb}% total number
    %build timeline from optional timeline file
    \if@anim@timeline\@anim@buildtmln{\the\@anim@num}\fi%
    %insert animation widget & controls
    \@anim@insertwidgets{\the\@anim@num}{%
      \@anim@getkeyval{btncol:\@anim@bgcolour:\@anim@fgcolour}}%
  \fi%
  \global\advance\@anim@num by \@ne%
  \endgroup%
}%

%inserts animation and control button widgets
\def\@anim@insertwidgets#1#2{%#1:@anim@num, #2 existing btn colour combination
  \vtop{%
    \offinterlineskip%
    \hbox{\@anim@animwidget{#1}}%
    \if@anim@controls%
      \setlength{\@anim@tmpdima}{\@anim@btnsize}%
      \vskip 0.1\@anim@tmpdima%
      \hbox to \@anim@animwidth\p@ {%
        \hss%
        \@anim@buttonwidget{#1}{EndLeft}{EndLeft:#2}%
        \@anim@buttonwidget{#1}{StepLeft}{StepLeft:#2}%
        \if@anim@step\else%
          \@anim@buttonwidget{#1}{PlayPauseLeft}{#1.PlayPauseLeft}%
          \@anim@buttonwidget{#1}{PlayPauseRight}{#1.PlayPauseRight}%
        \fi%
        \@anim@buttonwidget{#1}{StepRight}{StepRight:#2}%
        \@anim@buttonwidget{#1}{EndRight}{EndRight:#2}%
        \if@anim@step\else%
          \hskip 0.3\@anim@tmpdima%
          \@anim@buttonwidget{#1}{Minus}{Minus:#2}%
          \@anim@buttonwidget{#1}{Reset}{Reset:#2}%
          \@anim@buttonwidget{#1}{Plus}{Plus:#2}%
        \fi%
        \hss%
      }%
    \fi%
  }%
}

%measures natural dimensions of its box argument
\def\@anim@getsize#1{%
  \xdef\@anim@framewidth{\strip@pt\wd#1}%
  \xdef\@anim@frameheight{\strip@pt\ht#1}%
  \xdef\@anim@framedepth{\strip@pt\dp#1}%
  \setlength{\@anim@tmpdima}{\ht#1}%
  \addtolength{\@anim@tmpdima}{\dp#1}%
  \xdef\@anim@frametotalheight{\strip@pt\@anim@tmpdima}%
}

%calculates widget dimensions from natural ones, taking resizing options
%into account
\def\@anim@scale{%
  %initial widget dimensions
  \xdef\@anim@animwidth{\@anim@framewidth}%
  \xdef\@anim@animheight{\@anim@frameheight}%
  \xdef\@anim@animdepth{\@anim@framedepth}%
  \xdef\@anim@animtotalheight{\@anim@frametotalheight}%
  %rescale height & depth
  \ifthenelse{%
    \NOT\equal{\@anim@boxheight}{}\AND%
    \NOT\equal{\@anim@boxdepth}{}%
  }{%
    \xdef\@anim@animheight{\@anim@boxheight}%
    \xdef\@anim@animdepth{\@anim@boxdepth}%
  }{%
    \ifthenelse{\NOT\equal{\@anim@boxheight}{}}{%
      \xdef\@anim@animheight{\@anim@boxheight}%
    }{%
      \ifthenelse{\NOT\equal{\@anim@boxdepth}{}}{%
        \xdef\@anim@animdepth{\@anim@boxdepth}%
      }{%if neither height nor depth are given but width is, rescale
        %ht & dp to keep aspect ratio
        \ifthenelse{\NOT\equal{\@anim@boxwidth}{}}{%
          \setlength{\@anim@tmpdima}{%
            \@anim@animheight\p@%
            *\ratio{\@anim@boxwidth\p@}{\@anim@animwidth\p@}%
          }\xdef\@anim@animheight{\strip@pt\@anim@tmpdima}%
          \setlength{\@anim@tmpdima}{%
            \@anim@animdepth\p@%
            *\ratio{\@anim@boxwidth\p@}{\@anim@animwidth\p@}%
          }\xdef\@anim@animdepth{\strip@pt\@anim@tmpdima}%
        }{}%
      }%
    }%
  }%
  \ifthenelse{\NOT\equal{\@anim@boxwidth}{}}{%rescale width
    \ifthenelse{% depth missing
      \NOT\equal{\@anim@boxheight}{}\AND%
      \equal{\@anim@boxdepth}{}%
    }{%
      \setlength{\@anim@tmpdima}{%
        \@anim@animtotalheight\p@%
          *\ratio{\@anim@boxwidth\p@}{\@anim@animwidth\p@}%
      }%
      \setlength{\@anim@tmpdimb}{\@anim@tmpdima-\@anim@boxheight\p@}%
      \xdef\@anim@animdepth{\strip@pt\@anim@tmpdimb}%
    }{%
      \ifthenelse{% height missing
        \equal{\@anim@boxheight}{}\AND%
        \NOT\equal{\@anim@boxdepth}{}%
      }{%
        \setlength{\@anim@tmpdima}{%
          \@anim@animtotalheight\p@%
            *\ratio{\@anim@boxwidth\p@}{\@anim@animwidth\p@}%
        }%
        \setlength{\@anim@tmpdimb}{\@anim@tmpdima-\@anim@boxdepth\p@}%
        \xdef\@anim@animheight{\strip@pt\@anim@tmpdimb}%
      }{}%
    }%
    \xdef\@anim@animwidth{\@anim@boxwidth}%
  }{%
    %if width is not given, but either height or depth are, scale
    %width to keep aspect ratio
    \ifthenelse{%
      \NOT\equal{\@anim@boxheight}{}\OR%
      \NOT\equal{\@anim@boxdepth}{}%
    }{%
      \setlength{\@anim@tmpdima}{%
        \@anim@animwidth\p@%
          *\ratio{\@anim@animheight\p@+\@anim@animdepth\p@}{%
            \@anim@animtotalheight\p@}%
      }%
      \xdef\@anim@animwidth{\strip@pt\@anim@tmpdima}%
    }{}%
  }%
  %apply scaling factor
  \setlength{\@anim@tmpdima}{\@anim@animwidth\p@}%
  \setlength{\@anim@tmpdima}{\@anim@boxscale\@anim@tmpdima}%
  \xdef\@anim@animwidth{\strip@pt\@anim@tmpdima}%
  \setlength{\@anim@tmpdima}{\@anim@animheight\p@}%
  \setlength{\@anim@tmpdima}{\@anim@boxscale\@anim@tmpdima}%
  \xdef\@anim@animheight{\strip@pt\@anim@tmpdima}%
  \setlength{\@anim@tmpdima}{\@anim@animdepth\p@}%
  \setlength{\@anim@tmpdima}{\@anim@boxscale\@anim@tmpdima}%
  \xdef\@anim@animdepth{\strip@pt\@anim@tmpdima}%
  \setlength{\@anim@tmpdima}{\@anim@animheight\p@}%
  \addtolength{\@anim@tmpdima}{\@anim@animdepth\p@}%
  \xdef\@anim@animtotalheight{\strip@pt\@anim@tmpdima}%
}

%interactive Widget annotation that is overlayed on the non-interactive
%frame Windgets
\ifpdf
  \def\@anim@animwidget#1{%
    %create JavaScript objects
    \@anim@pojscript{#1}% to be executed on PO event
    \@anim@otherjscript{#1}% on other events in the AA dict
    %add poster to posterocg array
    \def\@anim@posterframe{none}%
    \ifx\@anim@poster\@anim@posterframe\else%
      \xdef\@anim@posterocgs{\@anim@posterocgs\space\@anim@posterocg}%
    \fi%
    \pdfannot
      width \@anim@animwidth\p@ height \@anim@animheight\p@
      depth \@anim@animdepth\p@
    {%
      /Subtype/Widget%
      /FT/Btn/Ff 65536% interactive push button
      /BS <</W 0>>%
      /H/N%
      /T (anm#1)%
      /AA <<% bind JavaScripts to trigger events
        /PO <</S/JavaScript/JS \@anim@pojscriptobj>>%
        \@anim@otherjscriptkey%
      >>%
    }%
    %append widget reference to Fields array of the AcroForm dict
    \xdef\@anim@fields{\@anim@fields\space\the\pdflastannot\space 0 R}%
    \hbox to \@anim@animwidth\p@ {%
      \vrule width 0pt height \@anim@animheight\p@ depth \@anim@animdepth\p@%
      \hss%
    }%
  }%
\else
  \def\@anim@animwidget#1{%
    \setlength{\unitlength}{1pt}%
    %mark annotation rectangle
    \begin{picture}(0,0)%
      \put(0,-\@anim@animdepth){%
      \special{ps:
        currentpoint /wid@lly exch def /wid@llx exch def
      }%
    }%
    \put(\@anim@animwidth,\@anim@animheight){%
      \special{ps:
        currentpoint /wid@ury exch def /wid@urx exch def
      }%
    }%
    \end{picture}%
    \@anim@pojscript{#1}%
    \@anim@otherjscript{#1}%
    \def\@anim@posterframe{none}%
    \ifx\@anim@poster\@anim@posterframe\else%
      \special{ps:
        mark {@anim@posterocgs} {\@anim@posterocg} /APPEND pdfmark
      }%
    \fi
    \special{ps:
      mark
        /_objdef {annot@\the\@anim@fieldnum}%
        /Rect [wid@llx wid@lly wid@urx wid@ury]%
        /Subtype/Widget%
        /FT/Btn/Ff 65536%
        /BS <</W 0>>%
        /H/N%
        /T (anm#1)%
        /AA <<%
          /PO <</S/JavaScript/JS {a#1@pojscript}>>%
          \@anim@otherjscriptkey%
        >>%
      /ANN pdfmark
      mark {@anim@fields} {annot@\the\@anim@fieldnum} /APPEND pdfmark
    }%
    \global\advance\@anim@fieldnum by \@ne%
    \hbox to \@anim@animwidth\p@ {%
      \vrule width 0pt height \@anim@animheight\p@ depth \@anim@animdepth\p@%
      \hss%
    }%
  }
\fi

%creates control button widget
\ifpdf
  \def\@anim@buttonwidget#1#2#3{%#1:@anim@num, #2:action, #3:button face XObject
    \@anim@upjscript{#1}{#2}% JavaScript actions for ButtonUp events
    %button size corresponds to current font size
    \setlength{\@anim@tmpdima}{\@anim@btnsize}%
    \hbox to \@anim@tmpdima {%
      \pdfannot
        width \@anim@tmpdima
        height \@anim@tmpdima
        depth 0pt
      {%
        /Subtype/Widget%
        /FT/Btn/Ff 65536% push button
        /BS <</W 0>>%
        /H/I%
        /AP <</N \@anim@getkeyval{btn#3}>>%
        /T (btn@#1@#2)%
        /A <</S/JavaScript/JS (\@anim@upjscriptstring)>>%
      }%
      \xdef\@anim@fields{\@anim@fields\space\the\pdflastannot\space 0 R}%
      \vrule width 0pt height \@anim@tmpdima depth 0pt%
      \hss%
    }%
  }%
\else
  \def\@anim@buttonwidget#1#2#3{%
    \@anim@upjscript{#1}{#2}%
    \setlength{\@anim@tmpdima}{\@anim@btnsize}%
    \setlength{\unitlength}{1pt}%
    \begin{picture}(0,0)%
      \put(0,0){%
      \special{ps:
        currentpoint /wid@lly exch def /wid@llx exch def
      }%
    }%
    \put(\strip@pt\@anim@tmpdima,\strip@pt\@anim@tmpdima){%
      \special{ps:
        currentpoint /wid@ury exch def /wid@urx exch def
      }%
    }%
    \end{picture}%
    \special{ps:
      mark
        /Rect [wid@llx wid@lly wid@urx wid@ury]
        /Subtype/Widget
        /FT/Btn/Ff 65536
        /BS <</W 0>>
        /H/I
        /AP <</N {btn#3}>>%
        /T (btn@#1@#2)%
        /A <</S/JavaScript/JS (\@anim@upjscriptstring)>>%
      /ANN pdfmark
      mark {@anim@fields} {annot@\the\@anim@fieldnum} /APPEND pdfmark
    }%
    \global\advance\@anim@fieldnum by \@ne%
    \hbox to \the\@anim@tmpdima {%
      \vrule width 0pt height \the\@anim@tmpdima\space depth 0pt%
        \hss%
    }%
  }
\fi

\newboolean{@anim@inside} % for checking whether we are inside the
\setboolean{@anim@inside}{false} %  `animateinline' environment
\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

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

%user environment for animating inline graphics
% #1: options
% #2: frame rate (fps)
\newenvironment{animateinline}[2][]{%
  \leavevmode%
  \begingroup%
  \setboolean{@anim@inside}{true}%
  \@anim@reset% to default settings
  \setkeys{anim@user}{#1}%
  \if@anim@draft\else%
    %create button faces if necessary
    \if@anim@controls%
      \ifcsname
        btncol:\@anim@bgcolour:\@anim@fgcolour\endcsname\else%
        \@anim@makebuttons{\the\@anim@num}%
        \@anim@newkey{btncol:\@anim@bgcolour:\@anim@fgcolour}{\the\@anim@num}%
      \fi%
      \if@anim@step\else%
        \@anim@makeppcontainer{\the\@anim@num}{Right}{%
          \@anim@getkeyval{btncol:\@anim@bgcolour:\@anim@fgcolour}}%
        \@anim@makeppcontainer{\the\@anim@num}{Left}{%
          \@anim@getkeyval{btncol:\@anim@bgcolour:\@anim@fgcolour}}%
      \fi%
    \fi%
    \xdef\@anim@fps{#2}%
    \xdef\@anim@nfps{#2}% current frame rate (for use in \newframe)
  \fi%
  \global\@anim@curframe@zb=0%
  \global\@anim@skipfram=0%
  \@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
    %build timeline from optional timeline file
    \if@anim@timeline\@anim@buildtmln{\the\@anim@num}\fi%
    %insert animation widget & controls
    \@anim@insertwidgets{\the\@anim@num}{%
      \@anim@getkeyval{btncol:\@anim@bgcolour:\@anim@fgcolour}}%
  \fi%
  \global\advance\@anim@num by \@ne%
  \setboolean{@anim@inside}{false}%
  \endgroup%
}

%usercommand for use within `animateinline' environment;
%terminates the current frame and starts a new one
\def\newframe{%
  \unskip%
  \if@anim@inside\else%
    \PackageError{animate}{%
      \protect\newframe\space cannot be used outside `animateinline'%
      \MessageBreak environment%
    }{}%
  \fi%
  \@anim@endframe{\the\@anim@num}{\the\@anim@curframe@zb}%
  \@ifstar\@anim@newframestar\@anim@newframe%
}
\newcommand{\@anim@newframe}[1][]{% #1: new frame rate
  \ifnum\@anim@skipfram=0
    \if@anim@draft\else%
      \if@anim@step\else%
        \if@anim@timeline\else%
          \edef\@anim@pfps{\@anim@nfps}%
          \edef\@anim@nfps{#1}%
          \ifx\@anim@nfps\@empty%
            \edef\@anim@nfps{\@anim@pfps}%
          \else%
            \ifnum\@anim@curframe@zb=0
              \xdef\@anim@fps{#1}%
            \fi%
            %build JavaScript commands to fill the `nFpsAt' & `pFpsAt' arrays
            \xdef\@anim@nfpsat{%
              \@anim@nfpsat%
              a\the\@anim@num.nFpsAt[\the\@anim@curframe@zb]=Math.abs(#1);%
            }%
            \xdef\@anim@pfpsat{%
              \@anim@pfpsat%
              a\the\@anim@num.pFpsAt[\the\@anim@curframe@zb]=%
                Math.abs(\@anim@pfps);%
            }%
            \setboolean{@anim@chfps}{true}% change fps
          \fi%
        \fi%
      \fi%
    \fi%
    \global\advance\@anim@curframe@zb by \@ne%
  \fi%
  \@anim@beginframe%
  \ignorespaces%
}
\newcommand{\@anim@newframestar}[1][]{% starred variant for pausing animation
  \ifnum\@anim@skipfram=0 %             #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}%
          \xdef\@anim@pfps{\@anim@nfps}%
          \xdef\@anim@nfps{#1}%
          \ifx\@anim@nfps\@empty%
            \xdef\@anim@nfps{\@anim@pfps}%
          \else%
            \ifnum\@anim@curframe@zb=0
              \xdef\@anim@fps{#1}%
            \fi%
            %build JavaScript commands to fill the `nFpsAt' & `pFpsAt' arrays
            \xdef\@anim@nfpsat{%
              \@anim@nfpsat%
              a\the\@anim@num.nFpsAt[\the\@anim@curframe@zb]=Math.abs(#1);%
            }%
            \xdef\@anim@pfpsat{%
              \@anim@pfpsat%
              a\the\@anim@num.pFpsAt[\the\@anim@curframe@zb]=%
                Math.abs(\@anim@pfps);%
            }%
            \setboolean{@anim@chfps}{true}% change fps
          \fi%
        \fi%
      \fi%
    \fi%
    \global\advance\@anim@curframe@zb by \@ne%
  \fi%
  \@anim@beginframe%
  \ignorespaces%
}

%starts new frame
\def\@anim@beginframe{%
  \begin{lrbox}{\@anim@box}% store graphics in a box
  \the\@anim@begin%
}

%terminates current frame
\def\@anim@endframe#1#2{%
  \the\@anim@end%
  \end{lrbox}%
  \ifnum\@anim@skipfram=0
    \@anim@getsize{\@anim@box}%determine size of graphics
    \ifnum #2=0
      \ifdim\wd\@anim@box=0pt%
        \PackageError{animate}{%
          Contents of first frame must not have zero width%
        }{%
          Possible reason: \protect\begin{animateinline}{...}
          immediately followed by \protect\newframe%
        }%
      \fi%
      \ifdim\ht\@anim@box=0pt%
        \ifdim\dp\@anim@box=0pt%
          \PackageError{animate}{%
            Contents of first frame must not have zero height%
          }{}%
        \fi%
      \fi%
      \@anim@scale%
      %draw draftbox according to dimensions of the first frame
      \if@anim@draft\@anim@draftbox{\@anim@box}\fi%
    \fi%
    %now create Form XObject of box contents
    \if@anim@draft\else%
      \@anim@xinline{#1}{#2}{\@anim@box}{!}\message{<a#1,fr#2>}%
    \fi%
  \fi%
  \global\advance\@anim@skipfram by \@ne%
  \ifnum\@anim@skipfram=\@anim@every
    \global\@anim@skipfram=0%
  \fi%
}

%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 (stringed zeros) from given num
  \ifnum10#1<1#2
    \@anim@template{0#1}{#2}%
  \else%
    \ifnum10#1=1#2
      \@anim@template{0#1}{#2}%
    \else%
      #1%
    \fi%
  \fi%
}%

%building timeline from timeline file
\newread\@anim@@tmlnfile
\newboolean{@anim@eof}
\newcount\@anim@tmpcnt %scratch counter for different uses
\newcount\@anim@curlayer %takes the number of the current layer being processed

\def\@anim@buildtmln#1{% #1:@anim@num
  \endlinechar=-1% suppress trailing space at input line end
  \global\@anim@curframe@zb=0%
  %read timeline file a first time to get number of lines (= number of frames)
  \openin\@anim@@tmlnfile=\@anim@tmlnfile%
  \read\@anim@@tmlnfile to \@anim@inputline%
  \edef\@anim@inputline{\@anim@inputline\space}%
  %remove all spaces from input line
  \edef\@anim@inputline{\expandafter\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}{}}{}{%
      %initialise contents, resource & transparency lists for each frame
      \@anim@newkey{trlst@\the\@anim@curframe@zb}{}%
      \@anim@newkey{frmxobject@\the\@anim@curframe@zb}{}%
      \@anim@newkey{frmcontent@\the\@anim@curframe@zb}{}%
      \global\advance\@anim@curframe@zb by \@ne%
    }%
    \read\@anim@@tmlnfile to \@anim@inputline%
    \edef\@anim@inputline{\@anim@inputline\space}%
    \edef\@anim@inputline{\expandafter\zap@space\@anim@inputline\@empty}%
    \ifeof\@anim@@tmlnfile\setboolean{@anim@eof}{true}\else%
      \setboolean{@anim@eof}{false}\fi%
  }%
  \closein\@anim@@tmlnfile%
  \xdef\@anim@transp{\@anim@frames}% total number of transparencies
  \xdef\@anim@frames{\the\@anim@curframe@zb}%total number of actual frames
  \global\@anim@curframe@zb=0%
  \gdef\@anim@maxlayer{0}% highest layer index
  %reopen timeline file and build timeline
  \openin\@anim@@tmlnfile=\@anim@tmlnfile%
  \read\@anim@@tmlnfile to \@anim@inputline%
  \edef\@anim@inputline{\@anim@inputline\space}%
  \edef\@anim@inputline{\expandafter\zap@space\@anim@inputline\@empty}%
  \ifeof\@anim@@tmlnfile\setboolean{@anim@eof}{true}\else%
    \setboolean{@anim@eof}{false}\fi%
  \message{<building timeline a#1:}%
  \whiledo{\NOT\boolean{@anim@eof}}{%
    \ifthenelse{\equal{\@anim@inputline}{}}{}{%
      %process input line
      \expandafter\@anim@parseline\@anim@inputline\@nil%
      %combine the layers of the current frame
      \global\@anim@curlayer=0%
      \loop\ifnum\@anim@curlayer>\@anim@maxlayer\else%
        \@anim@newkey{frmcontent@\the\@anim@curframe@zb}{%
          \@anim@getkeyval{frmcontent@\the\@anim@curframe@zb}%
          \@anim@getkeyval{%
            layercontent@\the\@anim@curframe@zb.\the\@anim@curlayer}%
        }%
        \@anim@newkey{%
          layercontent@\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
      \edef\@anim@trlst{%
        \the\@anim@curframe@zb.\@anim@getkeyval{trlst@\the\@anim@curframe@zb}}%
      \expandafter\@anim@findmult\@anim@trlst,:\@nil%
      %insert frame Widget
      \@anim@makeframe{#1}{\the\@anim@curframe@zb}%
      %clean-up
      \expandafter\@anim@resetmult\@anim@trlst,:\@nil%
      \@anim@newkey{trlst@\the\@anim@curframe@zb}{}%
      \@anim@newkey{frmxobject@\the\@anim@curframe@zb}{}%
      \@anim@newkey{frmcontent@\the\@anim@curframe@zb}{}%
      \global\advance\@anim@curframe@zb by \@ne%
    }%
    \read\@anim@@tmlnfile to \@anim@inputline%
    \edef\@anim@inputline{\@anim@inputline\space}%
    \edef\@anim@inputline{\expandafter\zap@space\@anim@inputline\@empty}%
    \ifeof\@anim@@tmlnfile\setboolean{@anim@eof}{true}\else%
      \setboolean{@anim@eof}{false}\fi%
    \message{.}%
  }%
  \closein\@anim@@tmlnfile%
  %check for unused transparencies
  \@anim@tmpcnt=0%
  \loop\ifnum\@anim@transp>\@anim@tmpcnt
    \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%
        File \jobname.tex%
      }%
      \ifx\@anim@nusewarned\@undefined%
        \AtEndDocument{%
          \PackageWarningNoLine{animate}{%
            @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\MessageBreak%
            @ There were unused animation transparencies.\space See\space\space%
            \space @\MessageBreak%
            @ the transcript file\space\space\space\space\space\space\space%
            \space\space\space\space\space\space\space\space\space\space\space%
            \space\space\space\space\space\space\space\space\space\space\space%
            \space\space @\MessageBreak%
            @ ``\jobname.log''\MessageBreak%
            @ for additional information!\space\space\space\space\space%
            \space\space\space\space\space\space\space\space\space\space%
            \space\space\space\space\space\space\space\space @\MessageBreak%
            @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%
          }%
        }%
        \gdef\@anim@nusewarned{}%
      \fi%
    \fi%
    \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:#3\@nil{%
  \if@anim@step\else%
    \ifthenelse{\equal{#1}{*}}{%first field is a star (pause frame)
      \xdef\@anim@pauseat{%
        \@anim@pauseat%
        a\the\@anim@num.pauseAt[\the\@anim@curframe@zb]=1;%
      }%
      \setboolean{@anim@pauseframes}{true}%
    }{}%
    \xdef\@anim@pfps{\@anim@nfps}%
    \xdef\@anim@nfps{#2}%
    %treat the second field (new frame rate)
    \ifx\@anim@nfps\@empty% no change of frame rate
      \xdef\@anim@nfps{\@anim@pfps}%
    \else%
      \ifnum\@anim@curframe@zb=0
        \xdef\@anim@fps{#2}%
      \fi%
      %build JavaScript commands to fill the `nFpsAt' & `pFpsAt' arrays
      \xdef\@anim@nfpsat{%
        \@anim@nfpsat%
        a\the\@anim@num.nFpsAt[\the\@anim@curframe@zb]=Math.abs(#2);%
      }%
      \xdef\@anim@pfpsat{%
        \@anim@pfpsat%
        a\the\@anim@num.pFpsAt[\the\@anim@curframe@zb]=Math.abs(\@anim@pfps);%
      }%
      \setboolean{@anim@chfps}{true}% change fps
    \fi%
  \fi%
  %parse the third field (list of layer specifications)
  \global\@anim@curlayer=0
  \@anim@parsethird#3;:\@nil%
}

%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
      \xdef\@anim@maxlayer{\the\@anim@curlayer}%
    \fi%
    \@anim@parsethird#2\@nil%
  }%
}

%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
\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=0 %0= means: repeat until end of timeline
    \edef\@anim@repeatuntil{\@anim@frames}%
  \else%
    \@anim@tmpcnt=\@anim@curframe@zb%
    \advance\@anim@tmpcnt by \@anim@repeats%
    \ifnum\@anim@frames<\@anim@tmpcnt
      \edef\@anim@repeatuntil{\@anim@frames}%
    \else%
      \edef\@anim@repeatuntil{\the\@anim@tmpcnt}%
    \fi%
  \fi%
  %build contents of animation layer and update frame xobject resource list
  %according to transparency specifications
  \ifnum\@anim@transp<#1\else%   ignore non-existing
    \ifnum\@anim@transp=#1\else% transparencies
      \@anim@newkey{u@tr#1}{}% mark current transp. as used within the animation
      \@anim@tmpcnt=\@anim@curframe@zb%
      \loop\ifnum\@anim@repeatuntil>\@anim@tmpcnt
        %ressource list
        \@anim@newkey{frmxobject@\the\@anim@tmpcnt}{%
          \@anim@getkeyval{frmxobject@\the\@anim@tmpcnt}\space%
          /im#1\space\@anim@getkeyval{img@#1}%
        }%
        %layer content
        \edef\@anim@tmplayercont{\@anim@getkeyval{%
          layercontent@\the\@anim@tmpcnt.\the\@anim@curlayer}}%
        \@anim@newkey{layercontent@\the\@anim@tmpcnt.\the\@anim@curlayer}{%
          \@anim@tmplayercont\space q /im#1\space Do Q%
        }%
        %append transparency to list of transparencies used in the current frame
        \@anim@newkey{trlst@\the\@anim@tmpcnt}{%
          \@anim@getkeyval{trlst@\the\@anim@tmpcnt}#1,}%
        \advance\@anim@tmpcnt by \@ne%
      \repeat%
    \fi%
  \fi%
}

%detects multiple inclusion of the same transparency
\def\@anim@findmult#1.#2,#3\@nil{%
  \ifthenelse{\equal{#2}{}}{}{%
    \ifcsname m@tr#2\endcsname%
      \PackageWarning{animate}{%
        Transparency #2 multiply included in frame #1.%
        \MessageBreak%
        File \jobname.tex%
      }%
      \ifx\@anim@multwarned\@undefined%
        \AtEndDocument{%
          \PackageWarningNoLine{animate}{%
            @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%
            \MessageBreak%
            @ In certain animation frames, transparencies were%
            \space\space @\MessageBreak%
            @ multiply included. This may considerably slow\space%
            \space\space\space\space @\MessageBreak%
            @ down animation speed. Revise the timeline file of\space @%
            \MessageBreak%
            @ the corresponding animation! See the transcript\space\space%
            \space @\MessageBreak%
            @ file ``\jobname.log''\MessageBreak%
            @ for additional information!\space\space\space\space\space%
            \space\space\space\space\space\space\space\space\space\space%
            \space\space\space\space\space\space\space\space @\MessageBreak%
            @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%
          }%
        }%
        \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%
  }%
}

%get number of repetitions of frame
\def\@anim@getrepetitions#1x:\@nil{%
  \gdef\@anim@repeats{#1}%
}

%command options
%setting the widget size
\define@key{anim@user}{width}{%
  \setlength{\@anim@tmpdima}{#1}%
  \xdef\@anim@boxwidth{\strip@pt\@anim@tmpdima}%
}
\define@key{anim@user}{height}{%
  \setlength{\@anim@tmpdima}{#1}%
  \xdef\@anim@boxheight{\strip@pt\@anim@tmpdima}%
}
\define@key{anim@user}{depth}{%
  \setlength{\@anim@tmpdima}{#1}%
  \xdef\@anim@boxdepth{\strip@pt\@anim@tmpdima}%
}
\define@key{anim@user}{scale}{%
  \gdef\@anim@boxscale{#1}%
}
\define@key{anim@user}{buttonsize}{%
  \gdef\@anim@btnsize{#1}%
}
\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%
  }%
}
\ifpdf %background frame of buttons
  \def\@anim@@@bg{%
    0 j
    0 0 150 150 re
    f%
  }
\else
  \def\@anim@@@bg{%
    0 setlinejoin
    0 0 moveto
    15 0 lineto
    15 15 lineto
    0 15 lineto
    closepath
    fill%
  }
\fi
\define@key{anim@user}{buttonbg}{%background colour of buttons
  \global\@anim@tmpcnt=\@ne%
  \gdef\@anim@colour{}%
  \@anim@colours#1:!\@nil%
  \xdef\@anim@bgcolour{\@anim@colour}%
  \ifpdf%
    \ifnum\@anim@tmpcnt=\@ne
      \xdef\@anim@bg{\@anim@colour\space g\space\@anim@@@bg}%
    \else%
      \ifnum\@anim@tmpcnt=3
        \xdef\@anim@bg{\@anim@colour\space rg\space\@anim@@@bg}%
      \else%
        \ifnum\@anim@tmpcnt=4
          \xdef\@anim@bg{\@anim@colour\space k\space\@anim@@@bg}%
        \fi%
      \fi%
    \fi%
  \else%
    \ifnum\@anim@tmpcnt=\@ne
      \xdef\@anim@bg{\@anim@colour\space setgray\space\@anim@@@bg}%
    \else%
      \ifnum\@anim@tmpcnt=3
        \xdef\@anim@bg{\@anim@colour\space setrgbcolor\space\@anim@@@bg}%
      \else%
        \ifnum\@anim@tmpcnt=4
          \xdef\@anim@bg{\@anim@colour\space setcmykcolor\space\@anim@@@bg}%
        \fi%
      \fi%
    \fi%
  \fi%
}
\define@key{anim@user}{buttonfg}{%stroking colour of buttons
  \global\@anim@tmpcnt=\@ne%
  \gdef\@anim@colour{}%
  \@anim@colours#1:!\@nil%
  \xdef\@anim@fgcolour{\@anim@colour}%
  \ifpdf%
    \ifthenelse{\the\@anim@tmpcnt=\@ne}{%
      \xdef\@anim@fg{\@anim@colour\space G}%
      \xdef\@anim@@@fg{\@anim@colour\space g}%
    }{%
      \ifthenelse{\the\@anim@tmpcnt=3}{%
        \xdef\@anim@fg{\@anim@colour\space RG}%
        \xdef\@anim@@@fg{\@anim@colour\space rg}%
      }{%
        \ifthenelse{\the\@anim@tmpcnt=4}{%
          \xdef\@anim@fg{\@anim@colour\space K}%
          \xdef\@anim@@@fg{\@anim@colour\space k}%
        }{}%
      }%
    }%
  \else%
    \ifthenelse{\the\@anim@tmpcnt=\@ne}{%
      \xdef\@anim@fg{\@anim@colour\space setgray}%
    }{%
      \ifthenelse{\the\@anim@tmpcnt=3}{%
        \xdef\@anim@fg{\@anim@colour\space setrgbcolor}%
      }{%
        \ifthenelse{\the\@anim@tmpcnt=4}{%
          \xdef\@anim@fg{\@anim@colour\space setcmykcolor}%
        }{}%
      }%
    }%
  \fi%
}
\newboolean{@anim@controls}
\setboolean{@anim@controls}{false}
\define@key{anim@user}{controls}[true]{%
  \setboolean{@anim@controls}{#1}%
}
\newboolean{@anim@loop}
\setboolean{@anim@loop}{false}
\define@key{anim@user}{loop}[true]{%
  \setboolean{@anim@loop}{#1}%
}
\newboolean{@anim@autoplay}
\setboolean{@anim@autoplay}{false}
\define@key{anim@user}{autoplay}[true]{%
  \setboolean{@anim@autoplay}{#1}%
  \if@anim@autoplay\setboolean{@anim@autoresume}{false}\fi%
}
\newboolean{@anim@autoresume}
\setboolean{@anim@autoresume}{false}
\define@key{anim@user}{autoresume}[true]{%
  \setboolean{@anim@autoresume}{#1}%
  \if@anim@autoresume\setboolean{@anim@autoplay}{false}\fi%
}
\newboolean{@anim@autopause}
\setboolean{@anim@autopause}{false}
\define@key{anim@user}{autopause}[true]{%
  \setboolean{@anim@autopause}{#1}%
}
\newboolean{@anim@palindrome}
\setboolean{@anim@palindrome}{false}
\define@key{anim@user}{palindrome}[true]{%
  \setboolean{@anim@palindrome}{#1}%
}
%default frame to be shown if animation is not activated
\define@key{anim@user}{poster}[first]{% | none | last
  \ifthenelse{% correct wrong input to `none'
    \equal{#1}{first}\OR%
    \equal{#1}{last}%
  }{%
    \gdef\@anim@poster{#1}%
  }{%
    \gdef\@anim@poster{none}%
  }%
}
\newboolean{@anim@step}
\setboolean{@anim@step}{false}
\define@key{anim@user}{step}[true]{%
  \setboolean{@anim@step}{#1}%
}
\newboolean{@anim@draft}
\setboolean{@anim@draft}{false}
\define@key{anim@user}{draft}[true]{%
  \setboolean{@anim@draft}{#1}%
}
\define@key{anim@user}{final}[true]{%
  \ifthenelse{\equal{#1}{true}}{%
    \setboolean{@anim@draft}{false}%
  }{%
    \setboolean{@anim@draft}{true}%
  }%
}
\define@key{anim@user}{timeline}{%
  \IfFileExists{#1}{%
    \gdef\@anim@tmlnfile{#1}%
    \setboolean{@anim@timeline}{true}%
  }{%
    \PackageError{animate}{timeline file `#1' cannot be opened for reading%
    }{%
      Make sure file `#1' exists and is readable!%
    }%
  }%
}
\newtoks\@anim@begin
\define@key{anim@user}{begin}{%
  \@anim@begin{#1}%
}
\newtoks\@anim@end
\define@key{anim@user}{end}{%
  \@anim@end{#1}%
}
\define@key{anim@user}{every}{% embed every #1 frames
  \ifnum#1<\@ne\gdef\@anim@every{1}\else\gdef\@anim@every{#1}\fi%
}
\define@key{anim@user}{bb}{%
  \if@anim@grxloaded\xdef\@anim@gropts{\@anim@gropts, bb=#1}%
  \else\@anim@missing{graphicx}\fi%
}
\define@key{anim@user}{viewport}{%
  \if@anim@grxloaded\xdef\@anim@gropts{\@anim@gropts, viewport=#1}%
  \else\@anim@missing{graphicx}\fi%
}
\define@key{anim@user}{trim}{%
  \if@anim@grxloaded\xdef\@anim@gropts{\@anim@gropts, trim=#1}%
  \else\@anim@missing{graphicx}\fi%
}
\newboolean{@anim@meas}
\define@key{anim@user}{measure}[true]{%
  \setboolean{@anim@meas}{#1}%
}

%macro to reset macros and booleans
\def\@anim@reset{%
  \gdef\@anim@boxscale{1.0}%
  \gdef\@anim@boxdepth{}%
  \gdef\@anim@boxheight{}%
  \gdef\@anim@boxwidth{}%
  \xdef\@anim@bg{\@anim@@bg}%
  \xdef\@anim@fg{\@anim@@fg}%
  \ifpdf\xdef\@anim@@@fg{\@anim@@@@fg}\fi%
  \xdef\@anim@bgcolour{\@anim@@bgcolour}% default button colours
  \xdef\@anim@fgcolour{\@anim@@fgcolour}%
  \xdef\@anim@btnsize{\@anim@@btnsize}%
  \xdef\@anim@poster{\@anim@@poster}%
  \global\let\if@anim@controls=\if@anim@@controls%
  \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%
  \gdef\@anim@posterocg{}%
  \gdef\@anim@properties{}%
  \setboolean{@anim@pauseframes}{false}%
  \gdef\@anim@pauseat{}%
  \setboolean{@anim@chfps}{false}%
  \gdef\@anim@nfpsat{}%
  \gdef\@anim@pfpsat{}%
  \gdef\@anim@tmlnfile{}%
  \setboolean{@anim@timeline}{false}%
  \global\@anim@begin={}%
  \global\@anim@end={}%
  \gdef\@anim@every{1}% frames to be included
  \gdef\@anim@gropts{}%
  \setboolean{@anim@multipdf}{false}%
  \setboolean{@anim@meas}{false}%
}

%package options
\def\@anim@@bg{}
\def\@anim@@bgcolour{}
\define@key{anim@pkg}{buttonbg}{%background colour of buttons
  \global\@anim@tmpcnt=\@ne%
  \gdef\@anim@colour{}%
  \@anim@colours#1:!\@nil%
  \xdef\@anim@@bgcolour{\@anim@colour}%
  \ifpdf%
    \ifnum\@anim@tmpcnt=\@ne
      \xdef\@anim@@bg{\@anim@colour\space g\space\@anim@@@bg}%
    \else%
      \ifnum\@anim@tmpcnt=3
        \xdef\@anim@@bg{\@anim@colour\space rg\space\@anim@@@bg}%
      \else%
        \ifnum\@anim@tmpcnt=4
          \xdef\@anim@@bg{\@anim@colour\space k\space\@anim@@@bg}%
        \fi%
      \fi%
    \fi%
  \else%
    \ifnum\@anim@tmpcnt=\@ne
      \xdef\@anim@@bg{\@anim@colour\space setgray\space\@anim@@@bg}%
    \else%
      \ifnum\@anim@tmpcnt=3
        \xdef\@anim@@bg{\@anim@colour\space setrgbcolor\space\@anim@@@bg}%
      \else%
        \ifnum\@anim@tmpcnt=4
          \xdef\@anim@@bg{\@anim@colour\space setcmykcolor\space\@anim@@@bg}%
        \fi%
      \fi%
    \fi%
  \fi%
}
\ifpdf\def\@anim@@fg{0 G}\def\@anim@@@@fg{0 g}\else\def\@anim@@fg{0 setgray}\fi
\def\@anim@@fgcolour{}
\define@key{anim@pkg}{buttonfg}{%stroking colour of buttons
  \global\@anim@tmpcnt=\@ne%
  \gdef\@anim@colour{}%
  \@anim@colours#1:!\@nil%
  \xdef\@anim@@fgcolour{\@anim@colour}%
  \ifpdf%
    \ifthenelse{\the\@anim@tmpcnt=\@ne}{%
      \xdef\@anim@@fg{\@anim@colour\space G}%
      \xdef\@anim@@@@fg{\@anim@colour\space g}%
    }{%
      \ifthenelse{\the\@anim@tmpcnt=3}{%
        \xdef\@anim@@fg{\@anim@colour\space RG}%
        \xdef\@anim@@@@fg{\@anim@colour\space rg}%
      }{%
        \ifthenelse{\the\@anim@tmpcnt=4}{%
          \xdef\@anim@@fg{\@anim@colour\space K}%
          \xdef\@anim@@@@fg{\@anim@colour\space k}%
        }{}%
      }%
    }%
  \else%
    \ifthenelse{\the\@anim@tmpcnt=\@ne}{%
      \xdef\@anim@@fg{\@anim@colour\space setgray}%
    }{%
      \ifthenelse{\the\@anim@tmpcnt=3}{%
        \xdef\@anim@@fg{\@anim@colour\space setrgbcolor}%
      }{%
        \ifthenelse{\the\@anim@tmpcnt=4}{%
          \xdef\@anim@@fg{\@anim@colour\space setcmykcolor}%
        }{}%
      }%
    }%
  \fi%
}
\def\@anim@@btnsize{1.44em}%
\define@key{anim@pkg}{buttonsize}{%
  \gdef\@anim@@btnsize{#1}%
}
\newboolean{@anim@@controls}
\setboolean{@anim@@controls}{false}%
\define@key{anim@pkg}{controls}[true]{%
  \setboolean{@anim@@controls}{#1}%
}
\newboolean{@anim@@loop}
\setboolean{@anim@@loop}{false}%
\define@key{anim@pkg}{loop}[true]{%
  \setboolean{@anim@@loop}{#1}%
}
\newboolean{@anim@@autoplay}
\setboolean{@anim@@autoplay}{false}
\define@key{anim@pkg}{autoplay}[true]{%
  \setboolean{@anim@@autoplay}{#1}%
  \if@anim@@autoplay\setboolean{@anim@@autoresume}{false}\fi%
}
\newboolean{@anim@@autoresume}
\setboolean{@anim@@autoresume}{false}
\define@key{anim@pkg}{autoresume}[true]{%
  \setboolean{@anim@@autoresume}{#1}%
  \if@anim@@autoresume\setboolean{@anim@@autoplay}{false}\fi%
}
\newboolean{@anim@@autopause}
\setboolean{@anim@@autopause}{false}
\define@key{anim@pkg}{autopause}[true]{%
  \setboolean{@anim@@autopause}{#1}%
}
\newboolean{@anim@@palindrome}
\setboolean{@anim@@palindrome}{false}
\define@key{anim@pkg}{palindrome}[true]{%
  \setboolean{@anim@@palindrome}{#1}%
}
\gdef\@anim@@poster{first}%
\define@key{anim@pkg}{poster}[first]{%
  \ifthenelse{%
    \equal{#1}{first}\OR%
    \equal{#1}{last}%
  }{%
    \gdef\@anim@@poster{#1}%
  }{%
    \gdef\@anim@@poster{none}%
  }%
}
\newboolean{@anim@@step}
\setboolean{@anim@@step}{false}
\define@key{anim@pkg}{step}[true]{%
  \setboolean{@anim@@step}{#1}%
}
\newboolean{@anim@@draft}
\ifdraft{%globally set by document class
  \setboolean{@anim@@draft}{true}%
}{%
  \setboolean{@anim@@draft}{false}%
}%
\define@key{anim@pkg}{draft}[true]{%
  \setboolean{@anim@@draft}{#1}%
}
\define@key{anim@pkg}{final}[true]{%
  \ifthenelse{\equal{#1}{true}}{%
    \setboolean{@anim@@draft}{false}%
  }{%
    \setboolean{@anim@@draft}{true}%
  }%
}

%process package options
\def\@anim@setpkgkeys#1{\setkeys{anim@pkg}{#1}}
\edef\@anim@pkgopts{{\@ptionlist{\@currname.\@currext}}}
\expandafter\@anim@setpkgkeys\@anim@pkgopts

%JavaScript objects
%animation widget's PageOpen event
\ifpdf
  \def\@anim@pojscript#1{%
    \immediate\pdfobj stream {\@anim@@pojscript{#1}}%
    \xdef\@anim@pojscriptobj{\the\pdflastobj\space 0 R}%
  }
\else
  \def\@anim@pojscript#1{%
    \special{ps:
      mark /_objdef {a#1@pojscript} /type/stream /OBJ pdfmark
      mark {a#1@pojscript} (\@anim@@pojscript{#1}) /PUT pdfmark
    }%
  }
\fi
\def\@anim@posterlast{last}% helper macro
\def\@anim@@pojscript#1{% #1: @anim@num
%  console.show();%
%  console.clear();%
  if(typeof(a#1)=='undefined'){% initialize animation
    a#1=new Object();%
    %takes references to frame ocgs belonging to the current animation
    a#1.fr=new Array();%
    %takes references to control button face ocgs
    \if@anim@controls a#1.btn=new Array();\fi
    %get array of ocgs of current page
    var ocg=this.getOCGs(this.pageNum);%
    for(var i=0;i<ocg.length;i++){%
      var ocgName=ocg[i].name.split('.');%
      if(ocgName[0]==#1){%
        \if@anim@controls
          if(ocgName[1].charAt(0)=='P'){% PlayLeft, PauseLeft, ...
            a#1.btn[ocgName[1]]=ocg[i];%
            %set basic button state
            a#1.btn[ocgName[1]].state=ocg[i].initState;%
          }else{% frame ocg
        \fi
            a#1.fr[ocgName[1]]=ocg[i];%
            %set basic frame state
            a#1.fr[ocgName[1]].state=ocg[i].initState;%
        \if@anim@controls%
          }%
        \fi%
      }%
    }%
    \ifx\@anim@poster\@anim@posterlast % set default frame
      %holds index of the frame to be displayed
      a#1.idx=\@anim@frames-1;%
    \else % first & none
      a#1.idx=0;%
    \fi%
    %playing state and direction
    \if@anim@step%
      \if@anim@palindrome
        a#1.playsRight=true;%
      \fi%
    \else
      a#1.isPlaying=false;%
      a#1.playsRight=true;%
      a#1.isPaused=false;%
      \if@anim@pauseframes
        %this array takes the frame numbers at which to pause playback
        a#1.pauseAt=new Array();%
        \@anim@pauseat%
      \fi%
      \if@anim@chfps
        %arrays that take frame numbers (array index) and fps values
        a#1.nFpsAt=new Array();%
        \@anim@nfpsat%
        a#1.pFpsAt=new Array();%
        \@anim@pfpsat%
        a#1.fpsDeflt=Math.abs(\@anim@fps);%
      \fi
      a#1.dtDeflt=1000/(1e-6+Math.abs(\@anim@fps));%
      a#1.dt=a#1.dtDeflt;%
    \fi%
    %actions
    a#1.actnEndLeft=function(){%
      \if@anim@step\else
        a#1.actnPause();%
        a#1.isPaused=false;% because it has been explicitly stopped
      \fi
      a#1.fr[a#1.idx].state=false;%
      a#1.idx=0;%
      a#1.fr[0].state=true;%
      \if@anim@chfps
        a#1.fpsDeflt=Math.abs(\@anim@fps);%
        a#1.dtDeflt=1000/(1e-6+a#1.fpsDeflt);%
        a#1.dt=a#1.dtDeflt;%
      \fi%
    };%
    a#1.actnEndRight=function(){%
      \if@anim@step\else
        a#1.actnPause();%
        a#1.isPaused=false;%
      \fi
      a#1.fr[a#1.idx].state=false;%
      a#1.idx=\@anim@frames-1;%
      a#1.fr[a#1.idx].state=true;%
      \if@anim@chfps %frame rate from the last fps change (\@anim@nfps)
        a#1.fpsDeflt=Math.abs(\@anim@nfps);%
        a#1.dtDeflt=1000/(1e-6+a#1.fpsDeflt);%
        a#1.dt=a#1.dtDeflt;%
      \fi%
    };%
    a#1.actnNext=function(){%
      a#1.fr[a#1.idx].state=false;%
      try{%
        a#1.fr[++a#1.idx].state=true;%
        \if@anim@step\else\if@anim@meas a#1.frcnt++;\fi\fi%
      }catch(e){%
        --a#1.idx;%
        \if@anim@palindrome%
          \if@anim@step
            a#1.actnEndRight();%
            a#1.playsRight=false;%
          \else
            a#1.fr[a#1.idx].state=true;%
            \if@anim@meas if(a#1.isPlaying){a#1.stopMeas();}\fi
            if(a#1.isPlaying){a#1.isPaused=true;a#1.actnPlayLeft();}%
          \fi%
        \else%
          \if@anim@step
            a#1.actnEndRight();%
          \else%
            \if@anim@loop
              if(a#1.isPlaying){%
                \if@anim@chfps
                  a#1.fpsDeflt=Math.abs(\@anim@fps);%
                  a#1.dtDeflt=1000/(1e-6+a#1.fpsDeflt);%
                  a#1.dt=a#1.dtDeflt;%
                \fi
                a#1.actnPlayRight();%
              }else{a#1.actnEndRight();}%
            \else
              a#1.actnEndRight();%
            \fi%
          \fi%
        \fi%
      }%
      \if@anim@step\else%
        \if@anim@chfps
          if(%
            a#1.nFpsAt[a#1.idx]>=0&&%
            a#1.nFpsAt[a#1.idx]!=a#1.fpsDeflt%
          ){%
            a#1.fpsDeflt=a#1.nFpsAt[a#1.idx];%
            a#1.dtDeflt=1000/(1e-6+a#1.fpsDeflt);%
            a#1.dt=a#1.dtDeflt;%
            if(a#1.isPlaying){a#1.isPaused=true;a#1.actnPlayRight();}%
          }%
        \fi%
        \if@anim@pauseframes
          if(a#1.isPlaying&&a#1.pauseAt[a#1.idx]){a#1.actnPause();}%
        \fi%
      \fi%
    };%
    a#1.actnPrev=function(){%
      a#1.fr[a#1.idx].state=false;%
      try{%
        a#1.fr[--a#1.idx].state=true;%
        \if@anim@step\else\if@anim@meas a#1.frcnt++;\fi\fi%
      }catch(e){%
        ++a#1.idx;%
        \if@anim@palindrome%
          \if@anim@step
            a#1.actnEndLeft();%
            a#1.playsRight=true;%
          \else
            a#1.fr[a#1.idx].state=true;%
            \if@anim@meas if(a#1.isPlaying){a#1.stopMeas();}\fi
            if(a#1.isPlaying){a#1.isPaused=true;a#1.actnPlayRight();}%
          \fi%
        \else%
          \if@anim@step
            a#1.actnEndLeft();%
          \else%
            \if@anim@loop
              if(a#1.isPlaying){%
                \if@anim@chfps
                  a#1.fpsDeflt=Math.abs(\@anim@nfps);%
                  a#1.dtDeflt=1000/(1e-6+a#1.fpsDeflt);%
                  a#1.dt=a#1.dtDeflt;%
                \fi
                a#1.actnPlayLeft();%
              }else{a#1.actnEndLeft();}%
            \else
              a#1.actnEndLeft();%
            \fi%
          \fi%
        \fi%
      }%
      \if@anim@step\else%
        \if@anim@chfps
          if(%
            a#1.pFpsAt[a#1.idx]>=0&&%
            a#1.pFpsAt[a#1.idx]!=a#1.fpsDeflt%
          ){%
            a#1.fpsDeflt=a#1.pFpsAt[a#1.idx];%
            a#1.dtDeflt=1000/(1e-6+a#1.fpsDeflt);%
            a#1.dt=a#1.dtDeflt;%
            if(a#1.isPlaying){a#1.isPaused=true;a#1.actnPlayLeft();}%
          }%
        \fi%
        \if@anim@pauseframes
          if(a#1.isPlaying&&a#1.pauseAt[a#1.idx]){a#1.actnPause();}%
        \fi%
      \fi%
    };%
    \if@anim@step\else
      a#1.actnPause=function(){%
        \if@anim@meas if(a#1.isPlaying){a#1.stopMeas();}\fi
        try{app.clearInterval(a#1_int);}catch(e){}%
        a#1.isPlaying=false;%
        a#1.isPaused=true;%
        \if@anim@controls
          a#1.btn['PauseLeft'].state=false;%
          a#1.btn['PlayLeft'].state=true;%
          a#1.btn['PauseRight'].state=false;%
          a#1.btn['PlayRight'].state=true;%
        \fi%
      };%
      a#1.actnPlayRight=function(){%
        try{app.clearInterval(a#1_int);}catch(e){}%
        if(!a#1.isPaused){a#1.actnEndLeft();}%
        a#1.playsRight=true;%
        a#1.isPlaying=true;%
        a#1.isPaused=false;%
        \if@anim@controls
          a#1.btn['PauseLeft'].state=true;%
          a#1.btn['PlayLeft'].state=false;%
          a#1.btn['PauseRight'].state=true;%
          a#1.btn['PlayRight'].state=false;%
        \fi%
        \if@anim@meas a#1.startMeas();\fi
        a#1_int=app.setInterval('a#1.actnNext()',a#1.dt);%
      };%
      a#1.actnPlayLeft=function(){%
        try{app.clearInterval(a#1_int);}catch(e){}%
        if(!a#1.isPaused){a#1.actnEndRight();}%
        a#1.playsRight=false;%
        a#1.isPlaying=true;%
        a#1.isPaused=false;%
        \if@anim@controls
          a#1.btn['PauseLeft'].state=true;%
          a#1.btn['PlayLeft'].state=false;%
          a#1.btn['PauseRight'].state=true;%
          a#1.btn['PlayRight'].state=false;%
        \fi%
        \if@anim@meas a#1.startMeas();\fi
        a#1_int=app.setInterval('a#1.actnPrev()',a#1.dt);%
      };%
      \if@anim@controls
        a#1.actnIncr=function(){% speed up animation
          try{app.clearInterval(a#1_int);}catch(e){}%
          a#1.dt/=1.2;%
          if(a#1.isPlaying){%
            if(a#1.playsRight){%
              a#1_int=app.setInterval('a#1.actnNext()',a#1.dt);%
            }else{%
              a#1_int=app.setInterval('a#1.actnPrev()',a#1.dt);%
            }%
          }%
        };%
        a#1.actnDecr=function(){% slow down animation
          try{app.clearInterval(a#1_int);}catch(e){}%
          a#1.dt*=1.2;%
          if(a#1.isPlaying){%
            if(a#1.playsRight){%
              a#1_int=app.setInterval('a#1.actnNext()',a#1.dt);%
            }else{%
              a#1_int=app.setInterval('a#1.actnPrev()',a#1.dt);%
            }%
          }%
        };%
        a#1.actnReset=function(){% reset to default speed
          a#1.dt=a#1.dtDeflt;%
          try{app.clearInterval(a#1_int);}catch(e){}%
          if(a#1.isPlaying){%
            if(a#1.playsRight){%
              a#1_int=app.setInterval('a#1.actnNext()',a#1.dt);%
            }else{%
              a#1_int=app.setInterval('a#1.actnPrev()',a#1.dt);%
            }%
          }%
        };%
      \fi%
    \fi%
    \if@anim@step\else%
      \if@anim@meas
        var spc=String.fromCharCode(32);%
        a#1.frcnt=0;% frame counter for speed measurements
        a#1.msStart=0;% takes start time (in millisecs)
        a#1.msEnd=0;% takes end time (in millisecs)
        a#1.startMeas=function(){%
          a#1.frcnt=0;% reset frame counter
          a#1.msStart=(new Date()).getTime();%
        };%
        a#1.stopMeas=function(){%
          a#1.msEnd=(new Date()).getTime();%
          app.alert({%
            cMsg:'av.'+spc+'frame'+spc+'rate:'+spc+%
                 1e3*a#1.frcnt/(a#1.msEnd-a#1.msStart)+spc+'fps',%
            nIcon:3%
          });
        };%
      \fi%
    \fi%
  }%
  \if@anim@step\else%
    \if@anim@autoplay
      if(a#1.playsRight){%
        a#1.actnPlayRight();%
      }else{%
        a#1.actnPlayLeft();%
      }%
    \fi%
    \if@anim@autoresume
      if(a#1.isPaused){%
        if(a#1.playsRight){a#1.actnPlayRight();}else{a#1.actnPlayLeft();}%
      }%
    \fi%
  \fi%
}
%other trigger events in the anim widget's AA dictionary
\def\@anim@otherjscript#1{%
  \def\@anim@posterframe{last}%
  \xdef\@anim@otherjscriptkey{%
    /PC <</S/JavaScript/JS (% PageClose
      \if@anim@autopause%
        \if@anim@step\else
          if(a#1.isPlaying||a#1.isPaused){a#1.actnPause();}%
          else{%
        \fi%
      \fi%
      \if@anim@step\else%
        \if@anim@controls
            a#1.playsRight=true;%
        \else%
          \if@anim@palindrome
            a#1.playsRight=true;%
          \fi%
        \fi%
      \fi%
      \ifx\@anim@poster\@anim@posterframe
            a#1.actnEndRight();%
      \else
            a#1.actnEndLeft();%
      \fi%
      \if@anim@autopause%
        \if@anim@step\else
          }
        \fi%
      \fi%
    )>>%
    \if@anim@step\else%
      /D <</S/JavaScript/JS (% pause on MouseDown
        %disable focus rectangle
        app.focusRect=false;%
        if(a#1.isPlaying){a#1.actnPause();}%
      )>>%
    \fi%
    /U <</S/JavaScript/JS (% play/resume on MouseUp
      \if@anim@step%
        \if@anim@palindrome
          if(a#1.playsRight){%
            a#1.actnNext();%
          }else{%
            a#1.actnPrev();%
          }%
        \else
          if(a#1.idx==\@anim@frames-1){%
            a#1.actnEndLeft();%
          }else{%
            a#1.actnNext();%
          }%
        \fi%
      \else
        if(a#1.playsRight){%
          a#1.actnPlayRight();%
        }else{%
          a#1.actnPlayLeft();%
        }%
      \fi%
    )>>%
  }%
}
%actions for control buttons
\def\@anim@upjscript#1#2{%
  \def\@anim@action{#2}%
  \def\@anim@EndLeft{EndLeft}%
  \def\@anim@StepLeft{StepLeft}%
  \def\@anim@PlayPauseLeft{PlayPauseLeft}%
  \def\@anim@PlayPauseRight{PlayPauseRight}%
  \def\@anim@StepRight{StepRight}%
  \def\@anim@EndRight{EndRight}%
  \def\@anim@Minus{Minus}%
  \def\@anim@Reset{Reset}%
  \def\@anim@Plus{Plus}%
  \xdef\@anim@upjscriptstring{%
    app.focusRect=false;%
    \ifx\@anim@action\@anim@EndLeft
      a#1.actnEndLeft();%
    \else%
    \ifx\@anim@action\@anim@StepLeft%
      \if@anim@step
        a#1.actnPrev();%
      \else
        if(!a#1.isPlaying){a#1.isPaused=true;a#1.actnPrev();}%
      \fi%
    \else%
    \ifx\@anim@action\@anim@PlayPauseLeft
      if(a#1.isPlaying){a#1.actnPause();}%
      else{a#1.actnPlayLeft();}%
    \else%
    \ifx\@anim@action\@anim@PlayPauseRight
      if(a#1.isPlaying){a#1.actnPause();}%
      else{a#1.actnPlayRight();}%
    \else%
    \ifx\@anim@action\@anim@StepRight%
      \if@anim@step
        a#1.actnNext();%
      \else
        if(!a#1.isPlaying){a#1.isPaused=true;a#1.actnNext();}%
      \fi%
    \else%
    \ifx\@anim@action\@anim@EndRight
      a#1.actnEndRight();%
    \else%
    \ifx\@anim@action\@anim@Minus
      a#1.actnDecr();%
    \else%
    \ifx\@anim@action\@anim@Reset
      a#1.actnReset();%
    \else%
    \ifx\@anim@action\@anim@Plus
      a#1.actnIncr();%
    \fi\fi\fi\fi\fi\fi\fi\fi\fi%
  }%
}
