# Javascript application in latex

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:

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 '19 at 20:51
• 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 '19 at 20:58
• And what about if I have the .js document? – idriskameni Mar 14 '19 at 21:27
• @idriskameni you can copy the contents of the .js document into your LaTeX source, similar to the linked question. – Marijn Mar 14 '19 at 21:52
• It is probably not as simple as that, Javascript for PDF files is not the same as Javascript for web pages in terms of the interaction between the document and the code. But if you know where and how to put it you can copy parts of the code. I realize that the 'where and how to put it' is part of your question, you can try asking a more specific question here with an example document, or ask on Stack Overflow (here on TeX.SE there are not many people that know about JavaScript, because it is not used a lot for LaTeX and it has very limited viewer support). – Marijn Mar 15 '19 at 12:00

## 1 Answer

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).

\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:

\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 '19 at 16:32
• @idriskameni I have extended the example to add a chart similar to the HTML example. – AlexG Mar 22 '19 at 16:36