3

I would like to know if it is possible to add an interactive app created with Javascript to a Latex document.

I would like to add some of the Javascripts that appear here.

Like that one:

enter image description here

EDIT

I can have the .js document, so I would like how to attach a .js to Latex.

Any idea? Any help?

  • Not wishing to dissuade other answers since your Question is GOOD. First off that page just like millions of others does not work for me, Why ? because it needs a mechanism that can be easily and is all too frequently exploited so my browser will not view it, Ok others will reply they can view it fine since that's a browser issue not a LaTeX PDF with JavaScript, However if you manage to add such code to your output PDF I will still not be able to view it Even if I open it in my insecure Acrobat Viewer which I don't normally use. Bottom line look for answers other than fixing on JavaScript – user170109 Mar 14 at 20:51
  • 1
    Yes, this one would be possible with PDF-Layers (OCGs), as can be created with package ocgx2, and JavaScript. Look at this example: tex.stackexchange.com/a/444956 Of course, a completely different logic would have to be implemented in JavaScript. – AlexG Mar 14 at 20:58
  • And what about if I have the .js document? – idriskameni Mar 14 at 21:27
  • 1
    @idriskameni you can copy the contents of the .js document into your LaTeX source, similar to the linked question. – Marijn Mar 14 at 21:52
  • Will the app apear if I just copy the code? – idriskameni Mar 14 at 23:25
2

This ports the card shuffling example to LaTeX and PDF, using PDF Layers (package ocgx2) and JavaScript. Acrobat Reader is required for display.

Update: Extended example with chart that plots King-diamond position versus riffle count.

Warning: It takes about 2h to compile the code twice using pdflatex due to the large number of OCGs (around 21k) that is created. Moreover, for pdflatex to run successfully, the main_memory must be increased in the format, or lualatex to be used instead. The (original) example without the chart compiles much faster (second code box).

enter image description here

\documentclass[a4paper,landscape]{article}

\usepackage{tikz,color,calc}
\usepackage{media9} %\mediabutton
\usepackage{xsavebox,ocgx2}

%suit symbols from https://tex.stackexchange.com/a/9643
\DeclareSymbolFont{extraup}{U}{zavm}{m}{n}
\DeclareMathSymbol{\varheart}{\mathalpha}{extraup}{86}
\DeclareMathSymbol{\vardiamond}{\mathalpha}{extraup}{87}

%typesets playing card; args: value, suite, colour
\makeatletter
\def\mycard#1#2#3{%
  \fbox{\rule[-4ex]{0pt}{5ex}\makebox[3.5ex][l]{#1$\color{#3}\@nameuse{#2}$}}}
\makeatother

%initialisations (JavaScript) on page-open (/O <<...>>), reset on page-close
\usepackage{ifluatex}
\ifluatex\def\pdfpageattr{\pdfvariable pageattr}\fi
\begingroup
\edef\x{\endgroup
  \pdfpageattr{
    \the\pdfpageattr
    /AA <<
      /O << /S/JavaScript /JS (%
%        console.show();
        console.clear();
        %riffle top card
        var riffle=function(){
          for(var i=0;i<52;i++) oldOrder[i]=order[i];
          if(order[0]!=51){
            %remove current card from top
            var top=order.shift();
            %insert `top' at new random position
            order.splice(1+Math.random()*order.length, 0, top);
            %new King Diamond position
            KdiamondPos=order.indexOf(51);
            ++riffleCount;
            console.println(riffleCount+' '+KdiamondPos);
          }
        };
        var update=function(){
          %update OCG visibility
          for(tablePos=0;tablePos<52;tablePos++){
            try{card[tablePos][oldOrder[tablePos]].state=false;}catch(e){}
            try{card[tablePos][order[tablePos]].state=true;}catch(e){}
          }
          try{pix[riffleCount][KdiamondPos].state=true;}catch(e){}
        };
        var reset=function(){
          console.clear();
          try{app.clearInterval(myIntA);}catch(e){}
          try{app.clearInterval(myIntB);}catch(e){}
          for(var i=0;i<52;i++) {oldOrder[i]=order[i]; order[i]=i;}
          riffleCount=0; KdiamondPos=51;
          for(var j=0;j<52;j++){
            for(var i=0;i<350;i++){
              try{pix[i][j].state=false;}catch(e){}
            }
          }
          update(); 
        };
        %card[<table position>][<stack position>]
        var card=new Array(52);
        for(var i=0;i<52;i++){card[i]=new Array(52);}
        %pix[<riffle count>][<stack position>]
        var pix=new Array(350);
        for(var i=0;i<350;i++){pix[i]=new Array(52);}
        ocg=this.getOCGs(this.pageNum);
        for(var i in ocg){
          ocgName=ocg[i].name.split('-');
          if(ocgName[2]=='card')
            card[ocgName[0]][ocgName[1]]=ocg[i];
          else % 'pix'
            pix[ocgName[0]][ocgName[1]]=ocg[i];
        }%
        %initial order
        var order=new Array(52); for(var i=0;i<52;i++) order[i]=i;
        var oldOrder=new Array(52);
        var riffleCount=0, KdiamondPos=51;
      ) >>
      /C << /S/JavaScript /JS (reset();) >>
    >>
  }
}
\x

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}\parindent=0pt%
%
% build chart
\xsavebox{Pix}[0.317mm][c]{\tikz\fill (0,0) circle [radius=0.5mm];}
\parbox{\linewidth}{\offinterlineskip%
  $\uparrow$ K$\color{red}\vardiamond$ position\\[0.5ex]
  \makebox[111mm]{\hrulefill}\\
    \foreach \j in {0,...,51}{%
      \foreach \i in {0,...,349}{%
         \expandafter\ifnum\numexpr\j+\i\relax<51
           \phantom{\thePix}%
         \else%  
           \begin{ocg}{\i-\j-pix}{\i-\j-pix}{off}%
             \thePix%
           \end{ocg}%
         \fi% 
      }\\
    }%
  \makebox[111mm]{\hrulefill}\\[0.5ex]
  \makebox[111mm][r]{riffle count $\rightarrow$}%
  \begin{ocg}{0-51-pix}{0-51-pix}{on}\end{ocg}%
}\\[1ex]

%typeset playing cards into xsaveboxes
\foreach \suit/\suitcol in {%
  spadesuit/black,clubsuit/black,varheart/red,vardiamond/red}{%
  \fboxrule=0.5pt\fboxsep=3pt%
  \foreach \val in {A,2,3,...,10,J,Q,K}{%
    \xsavebox{\val-\suit}{\mycard{\val}{\suit}{\suitcol}}%
  }%
}%
\xsavebox{K-vardiamond}{%
  \fboxsep=1.5pt\fboxrule=2pt\mycard{K}{vardiamond}{red}}%
%
%create layered array of cards: 13x4x52=2704 OCGs!
\foreach \j in {0,...,3}{%
  \foreach \i in {0,...,12}{%
    \fboxrule=0pt\fboxsep=3.5pt%
    \makebox[\widthof{\xusebox{K-vardiamond}}][l]{%
      \foreach \suit/\jj in {spadesuit/0,clubsuit/1,varheart/2,vardiamond/3}{%
        \foreach \val/\ii in {%
          A/0,2/1,3/2,4/3,5/4,6/5,7/6,8/7,9/8,10/9,J/10,Q/11,K/12}{%
          \xdef\tablePos{\the\numexpr\j*13+\i\relax}%
          \xdef\stackPos{\the\numexpr\jj*13+\ii\relax}%
          \begin{ocg}{\tablePos-\stackPos-card}{\tablePos-\stackPos-card}{%
            \expandafter\ifnum\numexpr\tablePos-\stackPos\relax=0 on%
            \else off\fi%
          }%
            \makebox[0pt][l]{\xusebox{\val-\suit}}%
          \end{ocg}%
        }%
      }%
    }~%
  }\\%
}\\[1ex]
\mediabutton[
  jsaction={riffle();update();}
]{\fbox{\strut Riffle}}
\mediabutton[
  jsaction={
    var myIntA=app.setInterval(
      'riffle();update();if(riffleCount\%10==0||order[0]==51)
        app.clearInterval(myIntA);',100);
  }
]{\fbox{\strut Riffle$\times$10}}
\mediabutton[
  jsaction={
    var myIntB=app.setInterval(
      'riffle();update();if(order[0]==51) app.clearInterval(myIntB);',100);
  }
]{\fbox{\strut Auto Riffle}}
\mediabutton[
  jsaction={reset();}
]{\fbox{\strut Reset}}

\end{document}

Original example without chart:

enter image description here

\documentclass[a4paper,landscape]{article}

\usepackage{pgffor,color,calc}
\usepackage{media9} %\mediabutton
\usepackage{xsavebox,ocgx2}

%suit symbols from https://tex.stackexchange.com/a/9643
\DeclareSymbolFont{extraup}{U}{zavm}{m}{n}
\DeclareMathSymbol{\varheart}{\mathalpha}{extraup}{86}
\DeclareMathSymbol{\vardiamond}{\mathalpha}{extraup}{87}

%typesets playing card; args: value, suite, colour
\makeatletter
\def\mycard#1#2#3{%
  \fbox{\rule[-4ex]{0pt}{5ex}\makebox[3.5ex][l]{#1$\color{#3}\@nameuse{#2}$}}}
\makeatother

%initialisations (JavaScript) on page-open (/O <<...>>)
\usepackage{ifluatex}
\ifluatex\def\pdfpageattr{\pdfvariable pageattr}\fi
\begingroup
\edef\x{\endgroup
  \pdfpageattr{
    \the\pdfpageattr
    /AA << /O << /S/JavaScript /JS (%
      console.show();
      console.clear();
      %card[<table position>][<stack position>]
      var card=new Array(52);
      for(var i=0;i<52;i++){card[i]=new Array(52);}
      ocg=this.getOCGs(this.pageNum);
      for(var i in ocg){
        ocgName=ocg[i].name.split('-');
        card[ocgName[0]][ocgName[1]]=ocg[i];
      }%
      %initial order
      var order=new Array(52);
      var oldOrder=new Array(52);
      for(i=0;i<52;i++) order[i]=i;
      var riffleCount=0;
      %riffle top card
      var riffle=function(){
        for(i=0;i<52;i++) oldOrder[i]=order[i];
        if(order[0]!=51){
          %remove current card from top
          var top=order.shift();
          %insert `top' at new random position
          order.splice(1+Math.random()*order.length, 0, top);
          console.println(++riffleCount);
        }
      };
      var update=function(){
        %update OCG visibility
        for(tablePos=0;tablePos<52;tablePos++){
          card[tablePos][oldOrder[tablePos]].state=false;
          card[tablePos][order[tablePos]].state=true;
        }
      };
      var reset=function(){
        for(i=0;i<52;i++) {oldOrder[i]=order[i]; order[i]=i;}
        update(); riffleCount=0;
        console.clear();
      };
    ) >> >>
  }
}
\x

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}\parindent=0pt%
%
%typeset playing cards into xsaveboxes
\foreach \suit/\suitcol in {%
  spadesuit/black,clubsuit/black,varheart/red,vardiamond/red}{%
  \fboxrule=0.5pt\fboxsep=3pt%
  \foreach \val in {A,2,3,...,10,J,Q,K}{%
    \xsavebox{\val-\suit}{\mycard{\val}{\suit}{\suitcol}}%
  }%
}%
\xsavebox{K-vardiamond}{%
  \fboxsep=1.5pt\fboxrule=2pt\mycard{K}{vardiamond}{red}}%
%
%create layered array of cards: 13x4x52=2704 OCGs!
\foreach \j in {0,...,3}{%
  \foreach \i in {0,...,12}{%
    \fboxrule=0pt\fboxsep=3.5pt%
    \makebox[\widthof{\xusebox{K-vardiamond}}][l]{%
      \foreach \suit/\jj in {spadesuit/0,clubsuit/1,varheart/2,vardiamond/3}{%
        \foreach \val/\ii in {%
          A/0,2/1,3/2,4/3,5/4,6/5,7/6,8/7,9/8,10/9,J/10,Q/11,K/12}{%
          \xdef\tablePos{\the\numexpr\j*13+\i\relax}%
          \xdef\stackPos{\the\numexpr\jj*13+\ii\relax}%
          \begin{ocg}{\tablePos-\stackPos}{\tablePos-\stackPos}{%
            \expandafter\ifnum\numexpr\tablePos-\stackPos\relax=0 on%
            \else off\fi%
          }%
            \makebox[0pt][l]{\xusebox{\val-\suit}}%
          \end{ocg}%
        }%
      }%
    }~%
  }\\%
}\\[1ex]
\mediabutton[
  jsaction={riffle();update();}
]{\fbox{\strut Riffle}}
\mediabutton[
  jsaction={
    var myIntA=app.setInterval(
      'riffle();update();if(riffleCount\%10==0||order[0]==51)
        app.clearInterval(myIntA);',100);
  }
]{\fbox{\strut Riffle$\times$10}}
\mediabutton[
  jsaction={
    var myIntA=app.setInterval(
      'riffle();update();if(order[0]==51) app.clearInterval(myIntA);',100);
  }
]{\fbox{\strut Auto Riffle}}
\mediabutton[
  jsaction={reset();}
]{\fbox{\strut Reset}}

\end{document}
  • WOW, nice! And what about the other .js that there is in the website? Could you do the same? – idriskameni Mar 19 at 16:32
  • @idriskameni I have extended the example to add a chart similar to the HTML example. – AlexG Mar 22 at 16:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.