diff options
author | Norbert Preining <norbert@preining.info> | 2019-10-01 11:31:05 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-10-01 11:31:05 +0900 |
commit | 7550a40711358021c4e6028deb4aa9d11fc503e4 (patch) | |
tree | 8998204f93b3e6d64919a4be9a07b3e411def2b2 /texmf-dist/source/latex/bargraph-js | |
parent | 56cafb279fe43023ee711349757a0e9187c99923 (diff) |
add bargraph-js
Diffstat (limited to 'texmf-dist/source/latex/bargraph-js')
-rw-r--r-- | texmf-dist/source/latex/bargraph-js/bargraph-js.dtx | 1284 | ||||
-rw-r--r-- | texmf-dist/source/latex/bargraph-js/bargraph-js.ins | 59 |
2 files changed, 1343 insertions, 0 deletions
diff --git a/texmf-dist/source/latex/bargraph-js/bargraph-js.dtx b/texmf-dist/source/latex/bargraph-js/bargraph-js.dtx new file mode 100644 index 00000000..64f5b08a --- /dev/null +++ b/texmf-dist/source/latex/bargraph-js/bargraph-js.dtx @@ -0,0 +1,1284 @@ +%\iffalse +% makeindex -s gglo.ist -o bargraph-js.gls bargraph-js.glo +% makeindex -s gind.ist -o bargraph-js.ind bargraph-js.idx +%<*copyright> +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% bargraph-js.sty package, %% +%% Copyright (C) 2019 %% +%% dpstory@uakron.edu %% +%% %% +%% This program can redistributed and/or modified under %% +%% the terms of the LaTeX Project Public License %% +%% Distributed from CTAN archives in directory %% +%% macros/latex/base/lppl.txt; either version 1.2 of the %% +%% License, or (at your option) any later version. %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%</copyright> +%<package>\NeedsTeXFormat{LaTeX2e}[1997/12/01] +%<package>\ProvidesPackage{bargraph-js} +%<package> [2019/04/07 v0.8 bargraph-js: Create bar graphs using form fields and JavaScript] +%<*driver> +\documentclass{ltxdoc} +\usepackage{xcolor} +\usepackage[colorlinks,hyperindex=false,bookmarksopen=false]{hyperref} +\usepackage{bargraph-js} +%\pdfstringdefDisableCommands{\let\\\textbackslash} +\EnableCrossrefs +\CodelineIndex +\RecordChanges +\gdef\brpr#1{\texttt{\char123\relax#1\char125\relax}} +\let\darg\brpr +\let\env\texttt +\let\opt\texttt +\let\app\textsf +\let\pkg\textsf +\def\visispace{\symbol{32}} +\def\ameta#1{\ensuremath{\langle\textit{\texttt{#1}}\rangle}} +\def\meta#1{\textsl{\texttt{#1}}} +\def\SUB#1{\ensuremath{{}_{\mbox{\scriptsize\ttfamily#1}}}} +%\def\cs#1{\texttt{\bslash#1}} +\DeclareRobustCommand{\tmspace}[3]{% + \ifmmode\mskip#1#2\else\kern#1#3\fi\relax} +\renewcommand{\,}{\tmspace+\thinmuskip{.1667em}} +\let\thinspace\, +\renewcommand{\!}{\tmspace-\thinmuskip{.1667em}} +\let\negthinspace\! +\renewcommand{\:}{\tmspace+\medmuskip{.2222em}} +\let\medspace\: +\newcommand{\negmedspace}{\tmspace-\medmuskip{.2222em}} +\renewcommand{\;}{\tmspace+\thickmuskip{.2777em}} +\let\thickspace\; +\newcommand{\negthickspace}{\tmspace-\thickmuskip{.2777em}} +\makeatletter +\renewcommand{\paragraph} + {\@startsection{paragraph}{4}{0pt}{6pt}{-3pt} + {\normalfont\normalsize\bfseries}} +\renewenvironment{quote}[1][] + {\def\@rgi{#1}\ifx\@rgi\@empty + \let\rghtm\@empty\else\def\rghtm{\rightmargin\leftmargin}\fi + \list{}{\rghtm} %{\rightmargin\leftmargin}% + \item\relax} + {\endlist} +\makeatother +\InputIfFileExists{aebdocfmt.def}{\PackageInfo{bargraph-js}{Inputting aebdocfmt.def}} + {\def\IndexOpt{\DescribeMacro}\def\IndexKey{\DescribeMacro}\let\setupFullwidth\relax + \PackageInfo{bargraph-js}{aebdocfmt.def cannot be found}} +\begin{document} +\def\CMD#1{\textbackslash#1} + \GetFileInfo{bargraph-js.sty} + \title{\textsf{bargraph-js}: Create dynamic bar graphs using form fields and JavaScript} + \author{D. P. Story\\ + Email: \texttt{dpstory@acrotex.net}} + \date{processed \today} + \maketitle + \tableofcontents + \let\Email\texttt + \DocInput{bargraph-js.dtx} +\IfFileExists{\jobname.ind}{\newpage\setupFullwidth\par\PrintIndex}{\paragraph*{Index} The index goes here.\\Execute + \texttt{makeindex -s gind.ist -o bargraph-js.ind bargraph-js.idx}\\on the command line and recompile + \texttt{bargraph-js.dtx}.} +\IfFileExists{\jobname.gls}{\PrintChanges}{\paragraph*{Change History} The list of changes goes here.\\Execute + \texttt{makeindex -s gglo.ist -o bargraph-js.gls bargraph-js.glo}\\on the command line and recompile + \texttt{bargraph-js.dtx}.} +\end{document} +%</driver> +% \fi +% \MakeShortVerb{|} +% +% \InputIfFileExists{aebdonotindex.def}{\PackageInfo{web}{Inputting aebdonotindex.def}} +% {\PackageInfo{web}{cannot find aebdonotindex.def}} +% +% \begin{macrocode} +%<*package> +\RequirePackage{xkeyval} +% \end{macrocode} +% \section{Introduction} +% Through {\LaTeX} markup, a document author can create a PDF with form fields. +% \app{Adobe Acrobat Reader DC} (as well as the \app{Acrobat} application itself) allows you to change +% the properties of this field through the JavaScript engine; in particular, the dimensions +% of the bounding rectangles of the form fields can be changed. Changes to form fields can be saved as well. +% +% An {Acro\negthinspace\TeX} user (GBH) wanted to create a bar graph +% reflecting the responses of his students. This package is a generalization +% of that solution. +% +% A bar graph is based on qualitative (or categorical) data. The raw data +% is classified by some criterion and and a numerical value is assigned (often times the value is a count, +% but need not be). Totals for each category are +% recorded in the form of a bar graph. Graphs may be vertically or +% horizontally oriented. In this package, we address such problems as (1) +% creation and coloring of the bar graph; (2) scaling; and (3) labeling. +% Conceptually, more than one bar graph can be placed, one superimposed on +% top the other. +% \section{Options and requirements} +% We only have one option, \IndexOpt{dynamic}\opt{dynamic}, this brings in some JavaScript to support +% the creation of bar graphs with no explicit use of \cs{barfor} commands; this is +% used in creating histograms for discrete probability distributions. +% \begin{macrocode} +\DeclareOptionX{dynamic}{\def\bgjs@importDynamic + {\InputIfFileExists{dynam-js.def}{}{\PackageWarning{bargraph-js} + {The file dynam-js.def cannot be found}}}} +\let\bgjs@importDynamic\relax +\ProcessOptionsX\relax +\AtEndOfPackage{\bgjs@importDynamic} +\RequirePackage{xcolor} +\RequirePackage{eforms}[2019/05/24] +\edef\bgjs@restoreCats{\catcode`\noexpand\"=\the\catcode`\"\relax} +\@makeother\" +% \end{macrocode} +% An initial scale factor. The scale factor is dynamically reset, as needed. However, when a bar graph +% is cleared, \DescribeMacro\scaleFactorDef\cs{scaleFactorDef} is used. +% \begin{macrocode} +\def\scaleFactorDef#1{\def\sc@leF@ctorDef{#1}} +\scaleFactorDef{2} +% \end{macrocode} +% +% \section{The \texttt{bargraphenv} and \texttt{bargraph} environments} +% A bar graph is built within an \env{bargraphenv} environment that controls it. +% \begin{environment}{bargraphenv} +% A \env{bargraph} environment (defined later) is contained within the \env{bargraphenv} +% environment. The \env{bargraphenv} has a number of key-values to set. +% \paragraph*{Key-values for \env{bargraphenv}} \leavevmode\IndexKey{width}\texttt{width} is the width of the underlying +% \texttt{minipage}; similarly, \IndexKey{height}\texttt{height} is its height. +% \begin{macrocode} +\define@key{bgrphenv}{width}[\linewidth]{\setlength{\@tempdima}{#1}% + \ifxetex\else\addtolength{\@tempdima}{2bp}\fi + \edef\bgrphenv@width{\the\@tempdima}\@tempdima=.99626\@tempdima + \edef\bgrphenv@widthbp{\strip@pt\@tempdima}} +\define@key{bgrphenv}{height}[2in]{\setlength{\@tempdima}{#1}% + \edef\bgrphenv@height{\the\@tempdima}\@tempdima=.99626\@tempdima + \edef\bgrphenv@heightbp{\strip@pt\@tempdima}} +% \end{macrocode} +% A switch, a counter, and a token register used by this package. +% \begin{macrocode} +\newif\ifhorizontalbars \horizontalbarsfalse +\newcount\cntbars \cntbars=0 +\newtoks\bgtoks +% \end{macrocode} +% \leavevmode\IndexKey{o}\hskip-\marginparsep\texttt{=\ameta{{\upshape{horiz\string|vert}}}} +% When \texttt{o=vert}, the default, the bars of the bargraph +% increase/decrease vertically (height changes); while for +% \texttt{o=horiz}, they increase/decrease horizontally (width changes). +% \begin{macrocode} +\define@choicekey{bgrphenv}{o}[\val\nr]{horiz,vert}{% + \ifcase\nr\relax + \horizontalbarstrue\or + \horizontalbarsfalse\else + \horizontalbarsfalse\fi} +% \end{macrocode} +% \leavevmode\IndexKey{origin}\hskip-\marginparsep\texttt{=\ameta{\upshape0\string|.5}} Sets the origin of the bar graph. +% When \texttt{origin=0}, the baseline of the bars is the horizontal axis (\texttt{o=vert}) or the vertical +% axis (\texttt{o=horiz}); when \texttt{origin=.5}, the baseline of the bars is on a line positioned half-way +% up the vertical axis (\texttt{o=vert}) or half-way across the horizontal axis (\texttt{o=horiz}). +% \begin{macrocode} +\define@choicekey+{bgrphenv}{origin}{0,.5}[0]{\def\bgjs@origin{#1}} + {\PackageWarning{bargraph-js}{Bad choice for origin, + permissible values are 0 and .5.\MessageBreak + Will use a value of 0. Try again}} +\def\bgjs@origin{0} +% \end{macrocode} +% \leavevmode\IndexKey{showaxis}\hskip-\marginparsep\texttt{=\ameta{\upshape{true\string|false}}} +% If true, a horizontal or vertical axis is drawn, depending on the setting of the \texttt{o} key. +% \begin{macrocode} +\newif\if@bgshowaxis \@bgshowaxisfalse % dps23 \catcode`\%=14\relax +\define@boolkey{bgrphenv}[@bg]{showaxis}[true]{} +% \end{macrocode} +% The following \env{defineJS} environment defines \cs{hided@t@Fmt}. It declares +% some vital data for the current \env{bargraphenv} environment. In this environment +% we make exclamation point (\texttt!) the escape, and the comment character as \texttt{\%}. +% \begin{macrocode} +\begin{defineJS}[\catcode`\!=0\relax]{\hided@t@Fmt} +if (typeof dataForEnv=="undefined") + var dataForEnv=new Object; +dataForEnv["!p(1)"]=new Object; +dataForEnv["!p(1)"].width=!bgrphenv@widthbp; +dataForEnv["!p(1)"].height=!bgrphenv@heightbp; +dataForEnv["!p(1)"].horiz=!ifhorizontalbars(true)!else(false)!fi; +dataForEnv["!p(1)"].sf=!sc@leF@ctorDef; +dataForEnv["!p(1)"].bgs=[!bg@list]; +dataForEnv["!p(1)"].origin=!bgjs@origin; +var _nO=!bgjs@origin; +var r=event.target.rect; +if (dataForEnv["!p(1)"].horiz){ + var _w1=_nO*dataForEnv["!p(1)"].width; + dataForEnv["!p(1)"].maxDim=(_nO==0)?!% +!bgrphenv@widthbp:Math.min(_w1,!bgrphenv@widthbp-_w1); + dataForEnv["!p(1)"].baseline=r[0]+_w1; +} else { + var _h1=_nO*dataForEnv["!p(1)"].height; + dataForEnv["!p(1)"].maxDim=(_nO==0)?!% +!bgrphenv@heightbp:Math.min(_h1,!bgrphenv@heightbp-_h1); + dataForEnv["!p(1)"].baseline=r[3]+_h1; +} +dataForEnv["!p(1)"].values=new Object; +\end{defineJS} +% \end{macrocode} +% This text field holds various information about the \env{bargraphenv} named \texttt{\#1}. +% In this definition, we say |\cmd{\let\%\defjsLB}|; the format code is passed using +% \cs{hided@t@Fmt}, which is defined by a \env{defineJS} environment above. We also pass +% a parameter (|\bgrphenv@name=|\ameta{bgenv-name}) to \cs{hided@t@Fmt} through the \cs{bParams}/\cs{eParams} mechanism. +% \begin{macrocode} +\def\internalD@t@Hidden#1{% + \llap{\textField[\cmd{\bParams{#1}\eParams\let\%\defjsLB} + \autoCenter{n}\BC{}\BG{}\S{S}\textSize{0}\Ff{\FfReadOnly} + \AA{\AAFormat{\hided@t@Fmt}}]{internalData.#1}{2bp}{2bp}}} +% \end{macrocode} +% \DescribeEnv{bargraphenv}\hskip-\marginparsep\texttt{[\ameta{KV-pairs}]\darg{\ameta{env-name}}} +% The \env{bargraphenv} simply encloses the \env{bargraph} environment in a +% \env{minipage} of dimensions specified by the key-values. +% \begin{macrocode} +\def\bg@vs#1{\setlength\@tempdima{#1}\vskip\@tempdima} +\def\bg@hs#1{\setlength\@tempdima{#1}\hskip\@tempdima} +% \end{macrocode} +% \DescribeMacro\oBgEnvs is an internal command that will hold a comma-delimited list +% of all \env{bargraphenv}s in the document. +% \begin{macrocode} +\let\oBgEnvs\@gobble +\newif\ifisbgenv\isbgenvfalse +\let\barNum\ef@Zero +\def\bg@warning{\PackageWarningNoLine{bargraph-js} + {At least one more compile is required}\global\let\bg@warning\@empty} +\newenvironment{bargraphenv}[2][]{\offinterlineskip +% \end{macrocode} +% Within the \env{bargraphenv}, we define \DescribeMacro\vs\cs{vs} and \DescribeMacro\hs\cs{hs} to position objects +% vertically and horizontally. +% These are local definitions. Syntax: \cs{vs\darg{\ameta{dimen}}} and \cs{hs\darg{\ameta{dimen}}}. +% \begin{macrocode} + \let\vs\bg@vs\let\hs\bg@hs\global\isbgenvtrue + \@ifundefined{envname@#2}{\global\@namedef{envname@#2}{#2}} + {\PackageWarning{bargraph-js} + {The name '#2' has been used in an earlier\MessageBreak + bargraphenv environment, please choose another\MessageBreak + name for the bargraphenv environment that\MessageBreak + appears}}% + \g@addto@macro\oBgEnvs{,"#2":\dl@lBrace\dl@rBrace}% + \def\bgrphenv@name{#2}\setkeys{bgrphenv}{width,height,#1}% + \ifhorizontalbars + \@tempdima\bgrphenv@width\relax + \@tempdima=\bgjs@origin\@tempdima + \edef\bgjs@setorigin{\the\@tempdima}% + \else % vertical + \@tempdima\bgrphenv@height\relax + \@tempdima=\bgjs@origin\@tempdima + \edef\bgjs@setorigin{\the\@tempdima}% + \fi + \@tempdima=.99626\@tempdima + \edef\bgrphenv@setoriginbp{\strip@pt\@tempdima}% + \let\isdynamic\relax\let\bg@list\@gobble + \global\let\barNum\ef@Zero + \begin{minipage}[b][\bgrphenv@height][b]{\bgrphenv@width}\hfuzz4pt} + {\hfill\end{minipage}\leavevmode + \ifx\txtBgValues\relax\else + \@ifundefined{OBgEnvs}{\bg@warning}{\txtBgValues + \global\let\txtBgValues\relax}\fi + \internalD@t@Hidden{\bgrphenv@name}} +% \end{macrocode} +% \end{environment} +% \begin{environment}{bargraph}\hskip-\marginparsep\texttt{[\ameta{KV-pairs}]\darg{\ameta{bar-name}}} +% The description of an individual bar graph. This environment is contained +% within the \env{bargraphenv} environment. The content of the environment +% \emph{must consist exclusively} of the commands +% \cs{barfor\darg{\ameta{bar-name}}} and \cs{cmd\darg{\ameta{tex-cmds}}}. +% These pairs of tokens are collected and stacked up in their natural order. +% The \env{bargraph} environment has number of key-values to set +% +% \paragraph*{Key-values for \env{bargraph}} The environment has several key-value pairs.\medskip +% +% \noindent\IndexKey{nbars}\hskip-\marginparsep\texttt{=\ameta{pos-int}} +% \texttt{nbars} is the number of bars in this environment. +% \begin{macrocode} +\define@key{bargraph}{nbars}[0]{\gdef\nbars{#1}} +\def\nb@rsDef{0} +\def\nbars{0} +% \end{macrocode} +% \leavevmode\IndexKey{gap}\hskip-\marginparsep\texttt{=\ameta{pos-num}} +% \texttt{gap} is the gap between bars; it is a positive number (\emph{no dimensions}). +% It is interpreted as printer points (bp, big points). +% \begin{macrocode} +\define@key{bargraph}{gap}[0]{\gdef\bargap{#1bp}} +\def\b@rgapDef{0} +\def\bargap{0bp} % in bp units +% \end{macrocode} +% \leavevmode\IndexKey{bardimen}\hskip-\marginparsep\texttt{=\ameta{pos-num}} +% is the width/height of the bars (measured in bp points). When +% \texttt{o=vert}, then the value of \texttt{bardimen} is the width of each bar; +% when \texttt{o=horiz}, the value of \texttt{bardimen} is the height of each bar. +% The value \ameta{pos-num} is a dimensionless number. +% \begin{macrocode} +\define@key{bargraph}{bardimen}{\gdef\bardimen{#1bp}} +\def\b@rdimenDef{20} +\def\bardimen{20bp} % in bp units +% \end{macrocode} +% \cs{bgjs@seto} begins the process of gathering the command pairs +% \cs{barfor\darg{\ameta{name}}} and \cs{cmd\darg{\ameta{cmds}}}. +% \begin{macrocode} +\def\bgjs@seto{\cntbars\@ne + \ifx\barNum\ef@Zero\relax + \global\let\barNum\ef@One + \toks@={\ifhorizontalbars\else\ifxetex\else + \hskip2bp\fi\fi}\else\toks@={}\fi\bgjs@gettype} +% \end{macrocode} +% \cs{bgjs@gettype} continues \cs{bgjs@seto}, it determines if this is a dynamic \env{bargraph} environment or not +% \begin{macrocode} +\def\bgjs@gettype{\@ifnextchar\relax + {\bgjs@dynamic}{\bgjs@@getbartoks}} +\def\expbarfor#1\@nil{#1} +% \end{macrocode} +% The \cs{bgjs@dynamic} command continues \cs{bgjs@gettype} in the first case. It is the case where JavaScript does it +% all. We create only an initial bar, whose name is `\texttt{x\ameta{bar-name}.bgnoname@\ameta{env}}'. +% \begin{macrocode} +\def\bgjs@dynamic{\let\b@rforCommon\dyb@rforCommon + \edef\bgrph@name{x\bgrph@name}\leavevmode + \rlap{\barfor{bgnoname}}} +% \end{macrocode} +% The \cs{bgjs@@getbartoks} command continues \cs{bgjs@gettype} in the +% second case. We detect two types of tokens: +% \cs{barfor\darg{\ameta{arg}}} and \cs{cmd\darg{\ameta{arg}}}. If this +% former, we jump to \cs{bgjs@@getbarfor}, otherwise, we jump to +% \cs{bgjs@@getcmd}. +% \begin{macrocode} +\def\bgjs@@getbartoks{\@ifnextchar\barfor + {\bgjs@@getbarfor}{\@ifnextchar\cmd + {\bgjs@@getcmd}{\relax}}}% +% \end{macrocode} +% The \cs{bgjs@@getbarfor} command handles \cs{barfor} tokens, processing depends on +% \texttt{o=\ameta{\upshape{vert\string|horiz}}}. +% \begin{macrocode} +\def\hmrk{\hskip\bgjs@setorigin\relax} +\def\bgjs@@getbarfor\barfor#1{% + \ifhorizontalbars % o=horiz + \toks@=\expandafter{\the\toks@\leavevmode + \rlap{\hmrk\bgjs@adj\barfor{#1}}}% + \ifnum\cntbars=\nbars\relax + \toks@=\expandafter{\the\toks@\vcgBdry[0bp]}\else + \toks@=\expandafter{\the\toks@\vcgBdry[\bargap]}\fi + \else % o=vert + \toks@=\expandafter{\the\toks@ + \raisebox{\bgjs@setorigin+\bgjs@adj}{\barfor{#1}}}% + \ifnum\cntbars=\nbars\relax + \toks@=\expandafter{\the\toks@\cgBdry[0bp]}\else + \toks@=\expandafter{\the\toks@\cgBdry[\bargap]}\fi + \fi + \advance\cntbars\@ne + \ifnum\cntbars>\nbars + \def\bgjs@next{\@ifnextchar\cmd{\bgjs@@getbartoks} + {\let\\\expbarfor\the\toks@}}% + \else + \def\bgjs@next{\bgjs@@getbartoks}\fi + \unskip\bgjs@next} +% \end{macrocode} +% The \cs{bgjs@@getcmd} command handles \cs{barfor} tokens. Again, the processing depends on +% \texttt{o=\ameta{\upshape{vert\string|horiz}}}. +% \begin{macrocode} +\def\bgjs@@getcmd\cmd#1{% + \toks@=\expandafter{\the\toks@#1}% + \ifhorizontalbars % o=horiz + \ifnum\cntbars=\nbars\relax + \toks@=\expandafter{\the\toks@}\else + \toks@=\expandafter{\the\toks@}\fi + \else % o=vert + \ifnum\cntbars=\@ne + \toks@=\expandafter{\the\toks@}\else + \toks@=\expandafter{\the\toks@}\fi + \fi + \ifnum\cntbars>\nbars + \def\bgjs@next{\@ifnextchar\cmd{\bgjs@@getbartoks} + {\let\\\expbarfor\the\toks@}}\else + \def\bgjs@next{\bgjs@@getbartoks}\fi +\bgjs@next} +% \end{macrocode} +% Here we finally define the \DescribeEnv{bargraph}\env{bargraph} environment; it records its name, +% processes its key-values, and expands \cs{bgjs@seto}. +% \begin{macrocode} +\newenvironment{bargraph}[2][]{\def\bgrph@name{#2}% + \g@addto@macro\bg@list{,"#2"}\setkeys{bargraph}{nbars=\nb@rsDef, + bardimen=\b@rdimenDef,gap=\b@rgapDef,#1}% + \edef\bg@sign{\ifxetex-\else+\fi}% + \edef\bgjs@adj{\ifhorizontalbars + \ifxetex\else\hskip2bp\fi\else+\ifxetex0pt\else2bp\fi\fi}% + \if@bgshowaxis\ifhorizontalbars\else\leavevmode + \smash{\makebox[0pt][l]{\raisebox{\bgjs@setorigin\bg@sign.5pt}% + {\rule{\bgrphenv@width}{1pt}}}}\fi\fi + \bgjs@seto}{\if@bgshowaxis\ifhorizontalbars + \smash{\rlap{\setlength{\@tempdima} + {\bgjs@setorigin\bg@sign.5pt}% + \hskip\@tempdima\rule{1pt}{\bgrphenv@height}}}\fi\fi} +% \end{macrocode} +% \end{environment} +%\paragraph*{Saving the bar data.} We need to save the value of each bar so that +% when the user re-opens the document, the bar graphs will work as designed, +% including the optimization. We initialize this text field using \cs{OBgEnvs}. +% \begin{macrocode} +\def\txtBgValues{%\leavevmode + \llap{\textField[\S{S}\H{N}\BG{}\BC{}\F{\FHidden}% + \autoCenter{n}\DV{(\{\OBgEnvs\})}\V{(\{\OBgEnvs\})} + ]{bgValues}{2bp}{2bp}}\llap{\textField[\S{S}\H{N}\BG{}\BC{} + \F{\FHidden}\autoCenter{n}\DV{}\V{}]{barLabeling}{2bp}{2bp}}% +} +% \end{macrocode} +% The definition of \DescribeMacro\OBgEnvs\cs{OBgEnvs} is written the auxiliary +% file and is based in \DescribeMacro\oBgEnvs\cs{oBgEnvs}, which is an ongoing comma-delimited list of +% all names of \env{bargraphenvs} in the document. +% \begin{macrocode} +\gdef\bg@wrtBbValues{\immediate\write\@auxout{\string + \gdef\string\OBgEnvs{\oBgEnvs}}} +% \end{macrocode} +% At the end of the document, we issued \cs{wrtBgValues}, which defines the command +% \cs{OBgEnvs}. The command is a list of all \env{bargraphenv}s in the document and is used +% to build a JavaScript object that will hold the values of each bar in the document. +% \begin{macrocode} +\AtEndDocument{\ifisbgenv\bg@wrtBbValues\fi} +% \end{macrocode} +% \section{Designing the bars} +% The bars themselves are pushbuttons (created by \cs{pushButton} of \pkg{eforms}). The problem is to pass +% any appearance changes to the \env{bargraph} environment. We pass the bars in two ways: +% (1) key-values that are common to all bars; (2) key-values that are individual to a bar. +% \begin{macro}{\barforCommon}\leavevmode +% \hskip-\marginparsep\texttt{\,\darg{\ameta{KV-pairs}}} +% Pass \pkg{eforms} key-values that will be applied to all bars in this environment. +% \begin{macrocode} +\def\barforCommon#1{\def\b@rforCommon{#1}} +% \end{macrocode} +% Below are the default common key-value properties for a bar. +% \begin{macrocode} +\barforCommon{\S{S}\H{N}\BG{}\BC{}\F{\FHidden}\autoCenter{n}} +% \end{macrocode} +% \leavevmode\DescribeMacro{\dybarforCommon} +% \hskip-\marginparsep\texttt{\,\darg{\ameta{KV-pairs}}} +% The properties set by \cs{dybarforCommon} are the properties of the bars when the bars are dynamically generated. Bar +% are generated +% dynamically when there are no \cs{barfor} commands in the \env{bargraph} environment. +% \begin{macrocode} +\def\dybarforCommon#1{\def\dyb@rforCommon{#1}} +\dybarforCommon{\S{S}\H{N}\BG{}\BC{}\F{\FHidden}} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\presetsbarfor} +% \leavevmode +% \hskip-\marginparsep\texttt{\,\darg{\ameta{bar-name}}\darg{\ameta{name}}\darg{\ameta{KV-pairs}}} +% Pass customizing key-values to an individual bar. A bar (as defined by \cs{barfor}, defined below) +% takes a \ameta{name} argument, which is used to reference that bar. For example, to color the +% bar \ameta{name}, we say \cs{presetbarfor\darg{\ameta{bar-name}}\darg{\ameta{name}}\darg{\cs{BG\darg{red}}}}. +% \begin{macrocode} +\def\presetsbarfor#1#2#3{\protectedKeys{#1.#2@\bgrphenv@name}{#3}} +%\toks@={}\bgjs@gettwo#3\bgjs@stop\relax +% \expandafter\edef\csname #1.#2@\bgrphenv@name\endcsname{\the\toks@}} +%\def\bgjs@stop{\relax}\def\bgjs@relax{\relax} +% \end{macrocode} +% For this preset, we need to fully expand, while protecting the key-values. We store +% key-value pairs in \cs{toks@} but we place \cs{noexpand} front of the key; for example +% \cs{BG\darg{red}} is stored as \cs{noexpand\cs{BG}\darg{red}}. +% \begin{macrocode} +%\def\bgjs@gettwo#1#2{\ifx#1\bgjs@stop\else +% \toks@=\expandafter{\the\toks@\noexpand#1{#2}}\expandafter +% \bgjs@gettwo\fi} +\def\presetsb@rfor#1{\@nameuse{\bgrph@name.#1@\bgrphenv@name}} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\barfor}\hskip-\marginparsep\texttt{\,\darg{\ameta{name}}} +% Within the \env{bargraph} environment, place the named \cs{barfor} command. +% \begin{macrocode} +\def\barfor#1{\ifhorizontalbars\def\bgenv@angle{0}\else + \def\bgenv@angle{90}\fi + \@ifundefined{\bgrph@name.#1@\bgrphenv@name} + {\let\@presets\@gobble}{\let\@presets\presetsb@rfor}% + \pushButton[\presets{\b@rforCommon\R{\bgenv@angle}} +% \end{macrocode} +% We use \cs{epresets} key, new to \pkg{eforms} (2019/01/22). +% \begin{macrocode} + \epresets{\@presets{#1}} +% \end{macrocode} +% The field name of the \cs{pushButton} for the \cs{barfor} command is +% \begin{quote}\texttt{\ameta{bar-name}.\ameta{name}@\ameta{env-name}}\end{quote} +% \changes{v0.7}{2019/04/07}{Change field name of \string\cs{barfor}} +% We change the field name of \cs{barfor} to +% \begin{quote}\texttt{\ameta{env-name}@\ameta{bar-name}.\ameta{name}}\end{quote} +% In this way, the same \ameta{name} can be used in more than one environments. +% \begin{macrocode} + ]{\bgrphenv@name @\bgrph@name.#1}{\bardimen}{\bardimen}} +% \end{macrocode} +%\DescribeMacro\getBarName\leavevmode\hskip-\marginparsep +% \texttt{\darg{\ameta{env-name}}\darg{\ameta{bar-name}}\darg{\ameta{name}}} is the field name of the corresponding +% to the bar identified by its arguments. The second argument is assumed to be dynamic, not literal. If the second +% argument is a literal, then you can say \cs{getBarName\darg{\ameta{bar-name}}\darg{"myBar"}\darg{\ameta{name}}}; i.e., +% enclosed it in quotation marks. +% \begin{macrocode} +\def\getBarName(#1,#2,#3){#1+"@"+#2+"."+#3} +% \end{macrocode} +% \end{macro} +% \section{Inputting count data into a bar} +% \begin{macro}{\inputFor}\hskip-\marginparsep\texttt{\,\darg{\ameta{env}}\darg{\ameta{bar}}\darg{\ameta{name}}} +% Use \cs{inputFor} to create a text field to input count data. We need to set up +% a correspondence between \cs{inputFor} and a particular bar. The key-value options are passed to this +% field using \cs{presetinputfor}; its argument uses \ameta{bar} and \ameta{name}, which are used for labeling purposes +% (see the default definition of \cs{presetinptfor}). +% \begin{macrocode} +\def\inputFor#1#2#3{% env, bar, name + \textField[\cmd{\bParams{#1}{#2.#3}\eParams} + \presets{\priorpresetinputfor{#1}{#2.#3}} + \presets{\presetinputfor{#1}{#2.#3}} +% \end{macrocode} +% This package uses a lot of calculation events, we try to reduce the number of events processed by +% registering the current input field as an active graph. All the subsequent calculations are made only +% if the active graph (\texttt{updateBG.activegraph}) matches the \texttt{\ameta{env}.\ameta{bar}@\ameta{bgenv}}. +% \begin{macrocode} + \AAonfocus{updateBG.activegraph="#1@#2.#3";}]% +% \end{macrocode} +% The field name of this \cs{textField} shall be +% \begin{quote}\texttt{\ameta{env-name}.\ameta{bar-name}@\ameta{name}}\end{quote} +% \begin{macrocode} + {#1.#2@#3}} +% \end{macrocode} +% \end{macro} +% Below is the default definition of \DescribeMacro\presetinputfor\cs{presetinputfor}, it defines calculate, keystroke, +% format, +% and validate events. In this setup, we accept only natural numbers (or count data). We call the +% \pkg{bargraph-js}-defined JavaScript function \texttt{updateGG} (update bar graph). This command +% can be redefined. \cs{presetinputfor} takes one argument, which is internally pass as \texttt{\ameta{env-name}.\ameta{bar-name}}, +% as seen above in the definition of \cs{inputFor}. +% \begin{macrocode} +\def\priorpresetinputfor#1#2{\AddAAcalculate{% + updateBG.env=getEnvName(event.targetName);\r + updateBG(event.value,"#1@#2",\usebarlabel);\r} + \AAcalculate{;} +} +\newcommand{\presetinputfor}[2]{% + \AAkeystroke{AFNumber_Keystroke(0, 0, 0, 0, "", true);} + \AAformat{AFNumber_Format(0, 0, 0, 0, "", true);} + \AAvalidate{AFRange_Validate(true, 0, false, 0);} +} +% \end{macrocode} +% Manually re-scale all bar graphs in a particular bar graph environment, +% using both \DescribeMacro\displaysfFor\cs{displaysfFor}, which both displays +% the current scale factor and allows input for a new scale factor, and +% a push button \DescribeMacro\manualsfFor\cs{manualsfFor} to re-scale. For each +% of these commands, the first argument is optional arguments to pass to the +% form field, the second argument is the name of the \env{bargraphenv} to be re-scaled. +% \begin{macrocode} +\begin{defineJS}{\dsfForKeyStr} +if(event.willCommit) { + var v=event.value; + try { v=eval(v) } catch(e){}; + if(isNaN(v)) { + app.beep(0); + app.alert("Enter a nonnegative number") + event.rc=false; + } +} +\end{defineJS} +\newcommand{\displaysfFor}[2][]{% + \textField[\DV{2}\V{2}\TU{Current scale factor} + \AAkeystroke{\dsfForKeyStr} + \AAvalidate{AFRange_Validate(true, 0, false);} + #1]{txtRescale.#2}% +} +\begin{defineJS}[\catcode`\!=0\relax]{\msfForMU} +var f=this.getField("txtRescale.!p(1)"); +var v=eval(f.value); +if (v>0){ + dataForEnv["!p(1)"].sf=v; + rescaleBargraph("!p(1)"); +} +\end{defineJS} +\newcommand{\manualsfFor}[2][]{% + \pushButton[\cmd{\bParams{#2}\eParams} + \AAmouseup{\msfForMU}#1]{btnRescale.#2}% +} +% \end{macrocode} +% +%\subsection{A labeling scheme} +% Labeling by typesetting the label is possible, but we also provide labeling through +% Acrobat forms tool tips property. We have two approaches:\par\medskip\noindent +% \DescribeMacro\barLabelsTU\hskip-\marginparsep\texttt{\,\darg{\ameta{string}\string|\ameta{function}}} +% When the argument is a \ameta{string}, the label becomes +% `\texttt{\ameta{string}\,\ameta{env}.\ameta{bar},\,Value:\,value}', +% and with it is a \ameta{function}, the function must return a string representing the label. The argument of +% \cs{barLabelsTU} +% is used when the bar has a tool tip already defined. In this case, the form of the tool tip +% is a string which incorporates the token \texttt{@v@}; this token will be replaced by the value of the bar. +% \begin{macrocode} +\def\barLabelsTU#1{\def\usebarlabel{#1}\ifx\usebarlabel\@empty + \def\usebarlabel{""}\fi} +\barLabelsTU{""} +% \end{macrocode} +% \leavevmode\DescribeMacro\barLabelsNoTU +% \hskip-\marginparsep\texttt{\,\darg{\ameta{string}\string|\ameta{function}}} +% When the \cs{barFor} command has no associated tool tip (\cs{TU}), the value of \cs{barLabelsNoTU} is used. +% When the argument is a string the tokens \texttt{\#1} and \texttt{\#2} are used as placeholders for +% \texttt{\ameta{env}.\env{bar}} and \texttt{\ameta{value}}, respectively. When it is a \ameta{function}, +% the function returns a string based on its argument of \texttt{fld} (usually \texttt{\ameta{env-name}.\ameta{bar-name}}) and +% \texttt{v} (the \texttt{\ameta{value}}. +% \begin{macrocode} +\def\barLabelsNoTU#1{\def\@rgi{#1}\ifx\@empty + \edef\barLabelsNoTUJS{\barLabelsNoTUJSDef}\else + \def\barLabelsNoTUJS{#1}\fi} +\def\barLabelsNoTUJSDef{o.barname+": "+o.bar+", Value: "+o.value} +\expandafter\barLabelsNoTU\expandafter{\barLabelsNoTUJSDef} +\def\simpleBarLabels(#1,#2){"Bar for "+#1+", Value: "+#2;} +% \end{macrocode} +% \leavevmode\DescribeMacro\labelFld\hskip-\marginparsep +% \texttt{[\ameta{form-opts}]\darg{\ameta{caption}}\darg{\ameta{bg-name}.\ameta{bar-name}}\darg{\ameta{width}}\darg{\ameta{height}}}\\ +% The command creates a text field that is used to label horizontal or vertical bars. For vertical bars +% use the key \cs{R\darg{90}} in the \ameta{form-opts}. +% \begin{macrocode} +\newcommand\labelFld[4][]{\textField[\Ff{\FfReadOnly}\F{\FHidden} + \BC{}\BG{}#1\autoCenter{n}\DV{#2}\V{#2}]{#3@\bgrphenv@name}{#4}} +% \end{macrocode} +% \subsection{Saving and restoring critical data} +% \textbf{Restore critical data with an open action.} +% If the first page is opened, and \texttt{oBarLabeling} needs +% to be restored, we call \texttt{gbRestoreData()}. +% \begin{macrocode} +\begin{defineJS}[\catcode`\!=0\relax]{\bgOpenAction} +if(oBarLabeling.needsRestore) { + var bgTO=app.setTimeOut("bgRestoreData();!% +app.clearTimeOut(bgTO);!% +oBarLabeling.needsRestore=false",1000); +} +\end{defineJS} +\begingroup\@makeother\%\let\%\defjsLB +\thisPageAction{\JS{\bgOpenAction}}{} +\endgroup +% \end{macrocode} +% \textbf{Saving critical data.} We save the object \texttt{oBarLabeling} as a string to +% the hidden field \texttt{barLabeling}. +% \begin{macrocode} +\begin{willSave} +bgSaveData(); +\end{willSave} +% \end{macrocode} +% \section{Document JavaScript} +% JavaScript that supports the creation of bar graphs. +% \begin{macrocode} +\def\barDefColor{color.blue} +% \end{macrocode} +% \leavevmode\DescribeMacro\populateCommaData\hskip-\marginparsep\texttt{(env,bar,str[,validate])} is +% a convenience command to access the JavaScript function \texttt{populateCommaData}, which is defined in Section~\ref{s:labelcd} +% \begin{macrocode} +\newcommand{\populateCommaData}{% + populateCommaData.usebarlabel=\usebarlabel;\r + populateCommaData +} +\begin{insDLJS*}{bgjs} +\begin{newsegment}{AeB: bargraph-js} +/* + Document Level JavaScript + bargraph-js Package + D. P. Story copyright \the\year +*/ +\end{newsegment} +% \end{macrocode} +% \leavevmode\IndexJS{updateBG}\hskip-\marginparsep\texttt{(v,fld,o)} +% function is designed for bar graph with explicit \cs{barFor} commands +% inserted by the document author. +% \begin{macrocode} +\begin{newsegment}{Bar Graph of preset bars} +var _scaleFactorDef=\sc@leF@ctorDef; +var oBarLabeling = new Object; + oBarLabeling.needsRestore=true; +function updateBG (v,fld,o) { +% \end{macrocode} +% If \texttt{updateBG.activegraph} is different from \texttt{fld}, we exit, don't do any calculations. +% The form of \texttt{fld} is \texttt{\ameta{env-name}@\ameta{bar-name}.\ameta{name}}. +% \begin{macrocode} + if (typeof v=="undefined") return; + if (updateBG.activegraph!=fld) return; + var useDefLabeling=(typeof o=="string"); + var env=updateBG.env; + var w=dataForEnv[env].width; + var h=dataForEnv[env].height; + var baseline=dataForEnv[env].baseline + var isHoriz=dataForEnv[env].horiz; + var bRescaleNeeded=false; + var f=this.getField(fld); + var cachedValue=getBarValue(env,fld); + saveBarValue(env,fld,v); // dps13 + bRescaleNeeded=getNewScaleFactor(env,v); + var sf=dataForEnv[env].sf; + sf=(sf<0)?-sf:sf; + var r=f.rect; +% \end{macrocode} +% \textbf{Calculating the new rectangle.} +% \texttt{cachedValue} is the previous value of the bar, and \texttt{v} is the new +% value. There are several cases: horizontal versus vertical; \texttt{v<0} versus +% \texttt{v>=0}; and whether the rectangle is changing direction (neg to pos) or not. +% \begin{macrocode} + if(isHoriz) { // dps01 + if (v<0) { + if (cachedValue<0) // no change in sign + r[0]=r[2]+(v*sf); + else { // change from pos to neg + r[2]=r[0]; + r[0]=r[2]+(v*sf); + } + } else { + if (cachedValue<0) { // from neg to pos + r[0]=r[2]; + r[2]=r[0]+(v*sf); + } else // from pos to pos + r[2]=r[0]+(v*sf); + } + } else { // vertical + if (v<0) { // dps01 + if (cachedValue<0) { // no change in sign + r[3]=r[1]+(v*sf); + } else { // switching from pos to neg + r[1]=r[3]; + r[3]=r[1]+(v*sf); + } + } else { + if (cachedValue<0) // change in sign: neg to pos + r[3]=r[1]+(v*sf); + else // no change + r[1]=r[3]+(v*sf); + } + } + f.rect=r; +% \end{macrocode} +% \textbf{Applying a label to current bar.} If a field has an empty \cs{TU} property, +% we save this in \texttt{oBarLabeling}, if not already saved. If the field has an +% empty \cs{TU} field, we record this with a \texttt{-1}. +% \begin{macrocode} + if (typeof oBarLabeling[fld]=="undefined") + oBarLabeling[fld]=(f.userName!="")?f.userName:-1; +% \end{macrocode} +% If this field has no \cs{TU} property, we use the default, as expressed through +% \texttt{barLabelsDef}. +% \begin{macrocode} + if (oBarLabeling[fld]==-1)f.userName=barLabelsDef(fld,v); + else { +% \end{macrocode} +% If this field has a nonempty \cs{TU} property, we either use \texttt{simpleBarLabels}, +% passing a formatting string with it, or we pass the function name with is arguments. +% If the \texttt{o} argument is not passed, there is no change to the labeling. +% \begin{macrocode} + if (typeof o!="undefined") + f.userName=(useDefLabeling)?((o=="")?% +(simpleBarLabels(fld,v,oBarLabeling[fld])):% +%(simpleBarLabels(fld,v,f.userName)):% +(simpleBarLabels(fld,v,o))):o(fld,v); + } +% \end{macrocode} +% \textbf{Default fill color.} If the bar does not have an associated fill color, +% we apply the default color of \cs{barDefColor} +% \begin{macrocode} + f.fillColor=(color.equal(f.fillColor,color.transparent))?% +\barDefColor:f.fillColor; +% \end{macrocode} +% Make the bar visible (initially they are hidden) +% \begin{macrocode} + f.display=display.visible; + if(bRescaleNeeded) { +% \end{macrocode} +% If we must re-scale, we cannot allow the user to enter any more data +% until the re-scaling is finished; otherwise, data is occasionally lost. +% \begin{macrocode} + var g=this.getField(env); + if (g!=null)g.readonly=true; + app.setTimeOut("rescaleBargraph(\""+env+"\")",5); + } +} +% \end{macrocode} +% \leavevmode\IndexJS{barLabelsDef}\hskip-\marginparsep\texttt{(fld,v)} is the default function for +% labeling the bars using the tooltip property of a form field. +% inserted by the document author. +% \begin{macrocode} +function parseFld(fld,v) { + var pos=fld.indexOf("@"); + var env=fld.substring(0,pos); + fld=fld.substring(pos+1); + pos=fld.indexOf("."); + var barname=fld.substring(0,pos); + var bar=fld.substring(pos+1); + parseFld.env=env; + parseFld.barname=barname; + parseFld.bar=bar; + parseFld.value=v; +} +function barLabelsDef(fld,v){ + parseFld(fld,v); + var o=parseFld; + var strOrFnc=\barLabelsNoTUJS; + return ((typeof strOrFnc=="string")?strOrFnc:strOrFnc(fld,v)); +} +function simpleBarLabels(fld,v){ +% parseFld(fld,v); +% var o=parseFld; + if (arguments.length==2) +% return o.barname + ": "+o.bar+", Value: "+v; + return customBarLabels(fld,v); + else { + parseFld(fld,v); + var s=arguments[2]; + return replaceLblVars(s); + } +} +% \end{macrocode} +% \leavevmode\IndexJS{getNewScaleFactor}\hskip-\marginparsep\texttt{(env,v)} +% is a utility function for calculating the scale factor of the graph. +% \begin{macrocode} +function getNewScaleFactor(env,v) { + var w=dataForEnv[env].width; + var h=dataForEnv[env].height; + var maxDimen=dataForEnv[env].maxDim; + var nO=dataForEnv[env].origin; + var sf=dataForEnv[env].sf; + var isHoriz=dataForEnv[env].horiz; + v=(v<0)?-v:v; + if(isHoriz) { + maxDimen=(nO==0)?w:maxDimen; + if( sf*v > maxDimen ) { + sf=maxDimen/v; + dataForEnv[env].sf=sf; + return true; + } + } else { + maxDimen=(nO==0)?h:maxDimen; + if ( sf*v > maxDimen ) { + sf=maxDimen/v; + dataForEnv[env].sf=sf; + return true; + } + } + return false; +} +% \end{macrocode} +% This function converts the field name for a \texttt{\string\inputFor} field +% to the field name of corresponding bar. +% \begin{macrocode} +// <env>.<barname>@<name> --> <env>@<barbane>.<name> +function getBarName(name) { + var pos=name.indexOf("."); + var env=name.substring(0,pos); + name=name.substring(pos+1); + return env+"@"+name.replace(/@/,"."); +} +function getEnvName(name) { + var pos=name.indexOf("."); + var env=name.substring(0,pos); + return env; +} +% \end{macrocode} +% \leavevmode\IndexJS{rescaleBargraph}\hskip-\marginparsep\texttt{(env)} +% touches each field causing the calculation fields to activate. +% Here, \texttt{env} is the name of the \texttt{bargraphenv} that encloses +% the active bar graph. +% \begin{macrocode} +function rescaleBargraph(env) { + var oInputFor=this.getField(env); + this.delay=true; + // date input by the \\inputFor + if (oInputFor!=null) { + var a=oInputFor.getArray() + for (var i=0; i<a.length; i++) { +% \end{macrocode} +% If \texttt{updateBG.activegraph} is the same as \texttt{getBarName(a[i].name)}, +% we make the calculations. +% \begin{macrocode} + updateBG.activegraph=getBarName(a[i].name); + if (a[i].display!=display.hidden) { + this.calculate=false; + a[i].value=1*a[i].value + 1; + this.calculate=true; + a[i].value=1*a[i].value - 1; + } + } + } else { +% \end{macrocode} +% \texttt{env} does not exist, this means there are no input fields, what to do? +% \begin{macrocode} + var aBars=dataForEnv[env].bgs; + for (var i=0; i<aBars.length; i++) { + var bars=this.getField(env+"@"+aBars[i]); // dps07 + var widgets=bars.getArray(); + for (var j=0; j<widgets.length; j++) { + var name=widgets[j].name; + var Value=getBarValue(env,name); // dps13 + updateBG.env=env; + updateBG.activegraph=name; + updateBG(Value,updateBG.activegraph); + } + } + } + var f=this.getField("txtRescale."+env); + if (f!=null) f.value=Math.round(dataForEnv[env].sf*1000)/1000; + this.delay=false; + var g=this.getField(env); + if (g!=null)g.readonly=false; +% g.strokeColor=color.black; +} +% \end{macrocode} +% \leavevmode\IndexJS{optimizeScaling}\hskip-\marginparsep\texttt{(env)} +% calculates the height/length of each bar, selected the largest one, and rescales +% the bar graph so that the largest one not extends the entire height/width of the +% bar graph region. Here, \env{env} is the name of the \env{bargraphenv}, and +% \texttt{bars} is the name of the \env{bargraph} environment. If we ever get +% to where we are putting multiple \env{bargraph} environment within a +% \env{bargraphenv}, then \texttt{bars} might be an array. At the end of the function, +% \texttt{rescaleBargraph()} is called with a timed delay. +% \begin{macrocode} +function optimizeScaling(env) { + var w=dataForEnv[env].width; + var h=dataForEnv[env].height; + var sf=dataForEnv[env].sf; + var isHoriz=dataForEnv[env].horiz; + var aBGs=dataForEnv[env].bgs; + for (var i=0; i<aBGs.length; i++) { + var o=this.getField(env+"@"+aBGs[i]); // dps07 + var a=o.getArray(); + maxFldName=a[0].name; + if (isHoriz) { // if horizontal bars + var maxDim=0; maxFldName=""; + for (var i=0; i<a.length; i++) { + if (a[i].display==display.hidden) continue; + var thisDim=a[i].rect[2]-a[i].rect[0]; + thisDim=(thisDim<0)?-thisDim:thisDim; // dps01 + if ( thisDim > maxDim ) { + maxDim=thisDim; + maxFldName=a[i].name; + } + } + if (maxFldName=="") return; + // get name of input field env.bar@name + var v=getBarValue(env,maxFldName); + // now rescale all bars + v=(v<0)?-v:v; // dps01 + if (v !=0) { + sf=w/v; // if v !=0 + dataForEnv[env].sf=sf; + var g=this.getField(env); +% g.strokeColor=color.red; + if (g!=null)g.readonly=true; + app.setTimeOut("rescaleBargraph(\""+env+"\")",5); + } + } else { // if vertical bars + var maxDim=0; maxFldName=""; + for (var i=0; i< a.length; i++) { + if (a[i].display==display.hidden) continue; + var thisDim=a[i].rect[1]-a[i].rect[3]; + thisDim=(thisDim<0)?-thisDim:thisDim; // dps01 + if ( thisDim > maxDim ) { + maxDim=thisDim; + maxFldName=a[i].name; + } + } + if (maxFldName=="") return; + var v=getBarValue(env,maxFldName); // dps13 + if ( typeof v == "undefined" ) return; + // now rescale all bars + v=(v<0)?-v:v; // dps01 + if (v !=0) { + sf=h/v; + dataForEnv[env].sf=sf; + var g=this.getField(env); + if (g!=null)g.readonly=true; +% g.strokeColor=color.red; + app.setTimeOut("rescaleBargraph(\""+env+"\")",5); + } + } + } +} +% \end{macrocode} +% \leavevmode\IndexJS{resetBargraphs}\hskip-\marginparsep\texttt{(\ameta{various})} +% resets the bar graphs and the input fields. The unspecified argument +% is a list of names for \env{bargraphenv} and \env{bargraph} names. +% \begin{macrocode} +function resetBargraphs() { + this.calculate=false; + this.resetForm(arguments); + for (var n=0; n<arguments.length; n++){ + var f=this.getField("internalData."+arguments[n]); + if (f!=null) { + dataForEnv[arguments[n]].sf=_scaleFactorDef; + var env=arguments[n]; // dps01 + var isHoriz=dataForEnv[env].horiz; + aBGs=dataForEnv[arguments[n]].bgs; + // an environment name + for (var i=0; i<aBGs.length; i++) { + var o=this.getField(env+"@"+aBGs[i]); // dps07 + var a=o.getArray(); + o.display=display.hidden; + if (isHoriz) { + for(j=0;j<a.length;j++) { + var name=a[j].name; // dps01 + var value=getBarValue(env,name); // dps01 + if (value<0) { // dps01 + var r=a[j].rect; + r[0]=r[2]+20; + a[j].rect=r; + } + saveBarValue(arguments[n],a[j].name,"0"); + } + } else { + for(j=0;j<a.length;j++) { + var name=a[j].name; // dps01 + var value=getBarValue(env,name); // dps01 + if (value<0) { // dps01 + var r=a[j].rect; + r[3]=r[1]+20; + a[j].rect=r; + } + saveBarValue(arguments[n],a[j].name,"0"); + } + } + } + } + } + this.calculate=true; +} +// fld="<bgenv-name><bg-name>.<bar-name>", v=value of field +% \end{macrocode} +% \leavevmode\IndexJS{customBarLabels}\hskip-\marginparsep\texttt{(fld,v)} +% If you declare \cs{barLabelsTU\darg{customBarLabels}}, then any \cs{barFor} +% field that has a non-empty \cs{TU} key will be processed by this built-in function. +% An example from \texttt{bargraph-js-cnt-data.tex} declares +% \begin{quote}\small|\presetsbarfor{don}{\BG{red}\TU{AcroTeX revenues are \textdollar@v@}}|\end{quote} The function +% below, replaces \texttt{@v@} with the actual value of the bar data. +% \begin{macrocode} +function customBarLabels(fld,v){ + var f=this.getField(fld); + if ( oBarLabeling[fld] != -1 ) { + var s=oBarLabeling[fld]; + if (s.indexOf("@v@")==-1) + return barLabelsDef(fld,v); + else { + parseFld(fld,v); + return replaceLblVars(s); + } + } else return barLabelsDef(fld,v); +} +function replaceLblVars(s) { + var o=parseFld; + s=s.replace(/@env@/g,o.env); + s=s.replace(/@barname@/g,o.barname); + s=s.replace(/@bar@/g,o.bar); + s=s.replace(/@v@/g,o.value); + return s; +} +% \end{macrocode} +% \leavevmode\IndexJS{getBarValue}\hskip-\marginparsep\texttt{(env,name)} +% returns the value of the bar, whose field name is \texttt{name} and is part +% of the \texttt{env} \env{bargraphenv} environment. The value is retrieved +% from the hidden field \texttt{"bgVAlues"}, that holds an object of key-values. +% \begin{macrocode} +function getBarValue(env,name){ + var f=this.getField("bgValues"); + var o=eval(f.value); + var v=o[env][name]; + var retn=(typeof v=="undefined")?0:v; + return retn; +} +% \end{macrocode} +% \leavevmode\IndexJS{saveBarValue}\hskip-\marginparsep\texttt{(env,name,v)} +% saves the value of the bar. The value \texttt{v} is saved to the hidden +% field \texttt{"bgVAlues"}. +% \begin{macrocode} +function saveBarValue(env,name,v) { + var f=this.getField("bgValues"); + var o=eval(f.value); + o[env][name]=v; + f.value=o.toSource(); +} +\end{newsegment} +% \end{macrocode} +% \leavevmode\IndexJS{toggleFldVisibility}\hskip-\marginparsep\texttt{(name,bOk)} +% If \texttt{bOk} is true, display the field whose name is \texttt{name}; +% otherwise, hide the field. +% \begin{macrocode} +\begin{newsegment}{Toggle field visibility} +function toggleFldVisibility(name,bOk) { + var f=this.getField(name); + f.display=(bOk)?display.visible:display.hidden; +} +\end{newsegment} +% \end{macrocode} +% \subsection{Support for comma-delimited data}\label{s:labelcd} +% \leavevmode\IndexJS{populateCommData}\hskip-\marginparsep\texttt{(env,bar,v[,validate])} +% Populates the bars characterized by \texttt{env} and \texttt{bar}. The \texttt{v} argument +% is a string of comma-delimited numbers. When the \texttt{validate} argument is passed, it +% should be a JS function that validates each entry in the list; for example, passing +% \texttt{validateArrayNonNegNums} determines whether each entry is a nonnegative number. +% \begin{macrocode} +\begin{newsegment}{Comma-delimited Data} +function populateCommaData(env,bar,str) { + var value; + var f=this.getField(env+"@"+bar); + var g=f.getArray(); + var aValue=str.split(","); + var bOk=(arguments.length>3)?arguments[3](aValue):true; + if (bOk) { + for (var i=0; i<g.length; i++){ + updateBG.env=env; + updateBG.activegraph=g[i].name; + value=(typeof(aValue[i])=="undefined")?0:aValue[i]; + updateBG(value,updateBG.activegraph,% +populateCommaData.usebarlabel); + } + } else app.alert(arguments[3].msg); +} +\end{newsegment} +% \end{macrocode} +% Determines whether each entry in the array \texttt{aValue} +% is a non-negative number. Returns false if not. +% \begin{macrocode} +\begin{newsegment}{Validate input data for comma-delimited data} +validateArrayNonNegNums.msg="Enter only non-negative numbers"; +function validateArrayNonNegNums(aValue){ + var bOk=true; + for (var i=0; i<aValue.length; i++) { + var x=1*aValue[i]; + if(isNaN(x)) {bOk=false; break;} + else if (x<0) {bOk=false; break;} + } + return bOk +} +% \end{macrocode} +% Determines whether each entry in the array \texttt{aValue} +% is a number. Returns false if not. +% \begin{macrocode} +validateArrayOfNums.msg="Enter only numerical values"; +function validateArrayOfNums(aValue){ + var bOk=true; + for (var i=0; i<aValue.length; i++) { + var x=1*aValue[i]; + if(isNaN(x)) {bOk=false; break;} + } + return bOk +} +\end{newsegment} +% \end{macrocode} +% \subsection{Saving and restoring bar graph data} +% Save the contents of the \texttt{oBarLabeling} object as a string +% to the hidden text field \texttt{barLabeling}. +% \begin{macrocode} +\begin{newsegment}{Restore Data} +function bgSaveData() { + var f=this.getField("barLabeling"); + if(f!=null)f.value=oBarLabeling.toSource(); +} +% \end{macrocode} +% Restore \texttt{oBarLabeling} object from +% the hidden text field \texttt{barLabeling}. +% \begin{macrocode} +function bgRestoreData(){ + var f=this.getField("barLabeling"); + if(f!=null&&f.value!="")oBarLabeling=eval(f.value); +} +\end{newsegment} +\end{insDLJS*} +%</package> +%<*dynam> +% \end{macrocode} +% \subsection{Creating and displaying dynamic bar graph data}\leavevmode +% \IndexJS{displayDyBargraph}\unskip +% This function is designed for discrete probability distributions, +% it can build the probability mass function (pmf) and the cumulative +% distribution function (cdf). \texttt{displayDyBargraph} +% is a function that creates a (dynamic) bar graph, based on the arguments passed to it. +% The arguments are +%\begin{description} +% \item[\texttt{bar}] is the name \ameta{bgenv-name}\texttt@\ameta{bg-name} +% \item[\texttt{aPr}] is the array of probability distributions; \texttt{[k,pmfPr,cdfPr]} is the form of each entry +% in this array, where \texttt{k} is a value of the distribution, +% \texttt{pmfPr} is the probability mass at that value; and \texttt{cdfPr} is the cumulative +% mass up to and including the value \texttt{k}. +% \item[\texttt{bPmf}] is true if we are to calculate a probability mass function, +% and false if we calculate a cumulative distribution function. +% \item[\texttt{bOptimize}] is true if we optimize the graph (applies to Pmf only), +% otherwise, we use the current scale factor. +%\end{description} +% \begin{macrocode} +\begin{insDLJS}{dynam}{Display dynamic bar graphs} +%function displayDyBargraph(bar,aPr,bPmf,bOptimize) { // bar=env@bar-name +function displayDyBargraph(env,aPr,bPmf,bOptimize) { + if (typeof aPr[0]=="undefined") return; + var aBars=dataForEnv[env].bgs; + var bar=env+"@"+dataForEnv[env].bgs[0]; + var n=aPr.length; + var index=(bPmf)?1:2; + var bc=color.red; + var fc=color.blue; + var lbl=_labelDyBars; + if (arguments.length>4) { + var o=arguments[4]; + bc=(o.bc==undefined)?bc:o.bc; + fc=(o.fc==undefined)?fc:o.fc; + var lbl=(o.lbl==undefined)?_labelDyBars:o.lbl; + } else { + var f=this.getField(bar+".bar0"); + if ( f !=null ) { + bc=f.strokeColor; + fc=f.fillColor; + } + } +% var hbar="x"+bar; + var hbar=bar.replace(/@/,"@x"); // dps07 +%console.println("removing bar: "+bar); + this.removeField(bar); + var pos=hbar.indexOf("@"); + var env=hbar.substring(0,pos); +// get aux info, begin with +%console.println("hbar: "+ hbar); + var hb=this.getField(hbar); + var f=hb.getArray()[0]; + var pg=f.page; + var wd=dataForEnv[env].width; + var ht=dataForEnv[env].height; + var isHoriz=dataForEnv[env].horiz; + var frameH=ht; // frameH is one unit high + if (bPmf&&bOptimize) { + var maxValue=0; + for (var i=0; i<n; i++) + if (aPr[i][index]>maxValue) maxValue=aPr[i][index]; + } + var w=wd/(n+1); + var r=f.rect; + var defwidth=r[2]-r[0]; + if (defwidth*(n+1)>=wd) r[2]=r[0]+w; + else w=defwidth; + var gap=-1; // contiguous boundaries overlap + var sf=(bPmf&&bOptimize)?frameH/maxValue:frameH; + for (var i=0; i<n; i++){ + var g=this.addField(bar+".bar"+i,"button",pg,r); + // calculate height + var Rect=g.rect; + var v=aPr[i][index]; + this.delay=true; + Rect[1]=Rect[3]+(v*sf); + var pr=aPr[i][0]; + g.userName=lbl(pr,v,bPmf); +% g.userName=bPmf?("Pr(X="+pr+")="+v):("Pr(X<="+pr+")="+v); + g.display=display.visible; + g.rect=Rect; + r[0]+=(w+gap); + r[2]+=(w+gap); + this.delay=false; + } + if (!bPmf) { // cdf + var totalW=(n+1)*(w+gap); + var delta=wd-totalW; + r[2]+=delta; + g=this.addField(bar+".bar"+n,"button",pg,r); + var Rect=g.rect; + var v=1; + this.delay=true; + Rect[1]=Rect[3]+(v*sf); + g.display=display.visible; + g.rect=Rect; + } +// Now set color properties + g=this.getField(bar); + g.fillColor=fc; + g.strokeColor=bc; + this.delay=false; +} +function _labelDyBars(pr,v,bPmf) { + return bPmf?("Pr(X="+pr+")="+v):("Pr(X<="+pr+")="+v); +} +\end{insDLJS} +%</dynam> +%<*package> +\bgjs@restoreCats +%</package> +% \end{macrocode} +\endinput diff --git a/texmf-dist/source/latex/bargraph-js/bargraph-js.ins b/texmf-dist/source/latex/bargraph-js/bargraph-js.ins new file mode 100644 index 00000000..dd0a5084 --- /dev/null +++ b/texmf-dist/source/latex/bargraph-js/bargraph-js.ins @@ -0,0 +1,59 @@ +%% +%% This file will generate fast loadable files and documentation +%% driver files from the doc files in this package when run through +%% LaTeX or TeX. +%% +%% Copyright 2019 D. P. Story +%% ------------------------------------------- +%% +%% It may be distributed under the conditions of the LaTeX Project Public +%% License, either version 1.2 of this license or (at your option) any +%% later version. The latest version of this license is in +%% http://www.latex-project.org/lppl.txt +%% and version 1.2 or later is part of all distributions of LaTeX +%% version 1999/12/01 or later. +%% +%% --------------- start of docstrip commands ------------------ +%% +\def\filedate{2019/04/07} +\def\batchfile{bargraph-js.ins} + +\input docstrip +\ifx\generate\undefined + \Msg{**********************************************} + \Msg{*} + \Msg{* This installation requires docstrip} + \Msg{* version 2.4 or later.} + \Msg{*} + \Msg{* An older version of docstrip has been input} + \Msg{*} + \Msg{**********************************************} + \errhelp{Move or rename old docstrip.tex and get a newer one.} + \errmessage{Old docstrip in input path} + \batchmode + \csname @@end\endcsname\end +\fi + +\keepsilent +\askforoverwritefalse +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\preamble +\endpreamble + +\generate +{% + \file{bargraph-js.sty}{\from{bargraph-js.dtx}{copyright,package}} + \file{dynam-js.def}{\from{bargraph-js.dtx}{copyright,dynam}} +} + +\Msg{***************************************************************} +\Msg{*} +\Msg{* \space To finish the installation you have to copy the files } +\Msg{*} +\Msg{* \space *.sty, *.cfg and *.def into a directory searched by TeX} +\Msg{*} +\Msg{***************************************************************} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\endinput |