%D \module %D [ file=java-ini, %D version=1998.01.30, %D title=\CONTEXT\ JavaScript Macros, %D subtitle=Initialization, %D author=Hans Hagen, %D date=\currentdate, %D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] %C %C This module is part of the \CONTEXT\ macro||package and is %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. \writestatus{loading}{ConTeXt JavaScript Macros / Initialization} % BUG: preamble zonder used/used en split % todo: lua sanitizer % JavaScript support is under development. In the near future % a slightly different model will be used. The JScode stuff % will probably become just auto function inclusion and the % JS_* things will disappear. First I have to find a way to % deal with global variables so the 'uses' thing will remain. % ook p{ref} % documentation should be corrected to JS( % Also, obeylines will be supported. \unprotect %D \JAVA\ support is not implemented as a generic support %D module. The main reason for this is that passing system %D variables to a \JAVASCRIPT\ is closely related to other core %D macros. %D \TEX\ is not the right tool to check the \JAVA\ code; the %D most we can do is reporting some passed variables: \newif\iftraceJScode \traceJScodefalse \let\traceJScode\traceJScodetrue %D A bit out of place, but not dangerous: \bgroup \catcode127=\@@letter \gdef\delcharacter{^^7f} \egroup %D The number of passed variables is minimalized by setting the %D next switch. \newif\ifminimalizeJScode \minimalizeJScodetrue %D \macros %D {JS*} %D %D Because \JAVASCRIPT's are activated by the user, for %D instance by activating on a button, their support is closely %D related to the referencing mechanism. Integration takes %D place by %D %D \starttyping %D \goto{calculate total}[Sum()] %D \stoptyping %D %D The \type{()} classify this as a script. If they are absent, %D the keyword is treated as a normal reference. %D %D One can pass arguments to such a script by saying: %D %D \starttyping %D \goto{calculate total}[Sum(1.5,2.3)] %D \stoptyping %D %D References are passed by using the \type{R{}} classifier. %D %D \starttyping %D \goto{calculate total}[Sum(1.5,2.3,R{overflow})] %D \stoptyping %D %D The last call calls the script \type{Sum} and passes the %D next set of variables: %D %D \starttyping %D JS_S_1="1.5"; %D JS_S_2="2.3"; %D JS_R_3="overflow"; %D JS_P_3=3; %D \stoptyping %D %D The first two parameters are just strings, the third one %D however is treated as a reference and results in passing the %D reference (if needed this references is prefixed) and the %D (real) page number. The alternative: %D %D \starttyping %D \goto{calculate total}[JS(Sum{V{1.5},V{2.3},R{overflow}})] %D \stoptyping %D %D does a verbose passing: %D %D \starttyping %D JS_V_1=1.5; %D JS_V_2=2.3; %D JS_R_3="overflow"; %D JS_P_3=3; %D \stoptyping %D We will also support direct function calls. In that case %D no intermediate variables are used. %D \macros %D {startJScode} %D %D A piece of \JAVASCRIPT\ code is defined by saying: %D %D \starttyping %D \startJScode{SomeScript} %D var Item=this.getField("item"); %D N=Item.getArray(); %D Total=this.getField("total"); %D Total.value=0; %D for (j=0; j0) && (JS_R_1!="")) %D { gotoNamedDest(JS_R_1) }; %D \stopJScode %D \stoptyping %D %D Such a piece of code is closely related to the interpreter %D used. Watch the last two lines, here the script adapts %D itself to the presence of a reference. %D %D While %D %D \starttyping %D \startJScode{name} %D name = 4 ; %D \stopJScode %D \stoptyping %D %D assumes uses no preamble or presumes that the preamble is %D always loaded, the next definition also tells \CONTEXT\ to %D actually include the preamble needed. %D %D \starttyping %D \startJScode{uses} uses {later} %D uses = 6 ; %D \stopJScode %D \stoptyping \long\def\startJScode#1 #2 {\doifelse{#2}{uses} {\dostartJScodeA{#1}} {\dostartJScodeB{#1} #2 }} \long\def\dostartJScodeA#1#2 #3\stopJScode {\long\setgvalue{\r!java#1}{\do{#2}{#3}}} \long\def\dostartJScodeB#1#2\stopJScode {\long\setgvalue{\r!java#1}{\do{}{#2}}} \let\stopJScode\relax %D \macros %D {presetJScode} %D %D The code can be retrieved by saying %D %D \starttyping %D \presetJScode{SomeScript}{template} %D \stoptyping %D %D Such a template is a comma separated list, where %D individual entries can optionally be transformed by %D \type{R{}} and \type{V{}}. %D %D After this call, the code is available in \type{\JScode}. \newif\ifdirectJScode \def\presetJScode#1#2% #1=operation #2=arguments {\setverbosecscharacters \def\par{\delcharacter}% was: { } \scratchcounter\zerocount \globallet\JScode\empty \def\do##1##2% {\doifelse{##2}{!}\directJScodetrue\directJScodefalse}% \getvalue{\r!java#1}% \edef\!!stringa{#2}% \ifx\!!stringa\empty \else \processcommacommand[\!!stringa]\dopresetJSvariables \fi \def\docommand##1% {\doifundefinedelse{\r!java\r!java##1} {\showmessage\m!javascript2{##1}} {\useJSpreamblenow{##1}}}% % {\doglobal\increment\currentJSpreamble % \doglobal\addtocommalist{##1}\allJSpreambles}}% \def\do##1##2% {\xdef\JScode{\ifdirectJScode#1(\JScode)\else\JScode##2\fi}% %\xdef\JScode{JS\string_N=\the\scratchcounter;\JScode}% \processcommalist[##1]\docommand}% \getvalue{\r!java#1}} \def\dopresetJSvariables#1% {\advance\scratchcounter \plusone \donefalse \dodopresetJSvariables#1\end}% \def\dodopresetJSvariables {\doifnextcharelse R\dodopresetJSrefvariables {\doifnextcharelse V\dodopresetJSvervariables {\doifnextcharelse S\dodopresetJSstrvariables \dodopresetJSrawvariables}}} \def\dodopresetJSrefvariables R#1\end {\doifreferencefoundelse{#1} {\donetrue \dododopresetJSvariables R{\referenceprefix#1}% \donefalse\dododopresetJSvariables P{\currentrealreference}} {\unknownreference{#1}}% \ifminimalizeJScode \else \donetrue\dododopresetJSvariables S{#1}% \fi} \def\dodopresetJSvervariables V#1\end {\donefalse\dododopresetJSvariables V{#1}% \ifminimalizeJScode \else \donetrue\dododopresetJSvariables S{#1}% \fi} \def\dodopresetJSstrvariables S#1\end {\donetrue\dododopresetJSvariables S{#1}} \def\dodopresetJSrawvariables #1\end {\donetrue\dododopresetJSvariables S{#1}} \def\JSprefix#1% {JS\string_#1\string_\the\scratchcounter} \def\dododopresetJSvariables#1#2% {\iftraceJScode \writestatus{JavaScript}{\JSprefix#1=#2} \xdef\JScode{\JScode console.println("\JSprefix#1=#2"); }% \fi \ifdirectJScode \xdef\JScode{\ifx\JScode\empty\else\JScode,\fi\ifdone"#2"\else#2\fi}% \else \xdef\JScode{\JScode\JSprefix#1=\ifdone"#2"\else#2\fi; }% \fi} %D \macros %D {startJSpreamble, flushJSpreamble} %D %D One can define insert \JAVASCRIPT\ code at the document level %D by using: %D %D \starttyping %D \startJSpreamble{oeps} %D oeps = 1 ; %D \stopJSpreamble %D \stoptyping %D %D which is the same as: %D %D \starttyping %D \startJSpreamble{now} used now %D now = 2 ; %D \stopJSpreamble %D \stoptyping %D %D while the next definition is only included when actually %D used. %D %D \starttyping %D \startJSpreamble{later} used later %D later = 3 ; %D \stopJSpreamble %D \stoptyping %D %D This command may be used more that once, but always before %D the first page is shipped out. \newif\ifoneJSpreamble \oneJSpreamblefalse \let\allJSpreambles\empty \newcounter\nofJSpreambles \newcounter\currentJSpreamble \long\def\startJSpreamble#1 #2 % {\bgroup % we need to restore the catcodes \restoreendofline % just in case it happens while reading lists \doifelse{#2}{used} {\dostartJSpreamble#1 } {\dostartJSpreamble#1 now #2 }} \long\def\dostartJSpreamble#1 #2 % {\processaction [#2] [ later=>\chardef\JSstatus\zerocount,% now=>\chardef\JSstatus\plusone ,% \s!default=>\chardef\JSstatus\plustwo ,% \s!unknown=>\chardef\JSstatus\plustwo ]% \ifaddJSlinebreaks \obeylines \let\obeyedline \normalpar \obeyspaces \let\obeyedspace\normalspace \fi \dodostartJSpreamble{#1}} \long\def\dodostartJSpreamble#1#2\stopJSpreamble {\presetJSfunctions #2function ()\end \long\setgvalue{\r!java\r!java#1}{#2}% \ifcase\JSstatus \else \useJSpreamblenow{#1}% \fi \egroup} %D \macros %D {setJSpreamble, addtoJSpreamble} %D %D In addition to the previous preamble definitions, we can %D set a preamble \quote {in||line} and add tokens to a %D preamble. \def\setJSpreamble#1#2% {\doifundefined{\r!java\r!java#1} {\setgvalue{\r!java\r!java#1}{#2;}% \doglobal\increment\currentJSpreamble \doglobal\addtocommalist{#1}\allJSpreambles}} \def\addtoJSpreamble#1#2% {\doifdefinedelse{\r!java\r!java#1} {\edef\!!stringa{\r!java\r!java#1}% \edef\!!stringb{\csname\!!stringa\endcsname}% \@EA\setgvalue\@EA\!!stringa\@EA{\!!stringb #2;}} {\setJSpreamble{#1}{#2}}} %D \macros %D {useJSpreamblenow} %D %D The next macro can be used to force inclusion of postponed %D \JAVASCRIPT\ preambles. \def\useJSpreamblenow#1% {\doglobal\increment\currentJSpreamble \doglobal\addtocommalist{#1}\allJSpreambles} %D Because we want to check for valid calls, we preload the %D functions. This means that we can call them directly as %D well as indirectly when defined by \type {\startJScode} etc. % \long\def\presetJSfunctions#1function #2(#3)% % {\doifelsenothing{#2} % {\long\def\presetJSfunctions##1\end{}} % {\stripspaces\from#2\to\ascii % \doifundefined{\r!java\ascii}{\setgvalue{\r!java\ascii}{\do{}{!}}}}% % \presetJSfunctions} \long\def\presetJSfunctions#1function#2(#3)% {\doifelse{#2}\space {\long\def\presetJSfunctions##1\end{}} {\stripspaces\from#2\to\ascii \doifundefined{\r!java\ascii}{\setgvalue{\r!java\ascii}{\do{}{!}}}}% \presetJSfunctions} \def\getJSpreamble#1% {\getvalue{\r!java\r!java#1}} \def\presetJSpreamble {\ifx\allJSpreambles\empty\else \bgroup \setverbosecscharacters \obeyspaces \let\obeyedspace\normalspace \def\par{\delcharacter}% was: { } \globallet\JSpreamble\empty \def\@@collectedJSpreamble{\r!java\r!java collected}% \letvalue{\@@collectedJSpreamble}=\empty \def\docommand##1% {\xdef\JScode{\getvalue{\r!java\r!java##1}}% \ifoneJSpreamble % \global\letcdcsname \@EA\setxvalue\@EA\@@collectedJSpreamble\@EA {\csname\@@collectedJSpreamble\endcsname\JScode}% \else \setxvalue{\r!java\r!java##1}{\JScode}% \fi}% \processcommacommand[\allJSpreambles]\docommand \ifoneJSpreamble \gdef\allJSpreambles{collected}% \fi \globallet\presetJSpreamble\relax \egroup \fi} \def\flushJSpreamble {\iflocation\ifx\allJSpreambles\empty\else \ifcase\nofJSpreambles\else\ifnum\nofJSpreambles=\currentJSpreamble \bgroup \presetJSpreamble \expanded{\doflushJSpreamble{\allJSpreambles}}% \globallet\flushJSpreamble\relax \globallet\allJSpreambles\empty \egroup \fi\fi \fi\fi} \def\finalflushJSpreamble {\iflocation \flushJSpreamble \ifcase\currentJSpreamble\relax\else \savecurrentvalue\nofJSpreambles\currentJSpreamble \globallet\currentJSpreamble\nofJSpreambles \fi \fi} \prependtoks \flushJSpreamble \to \everyshipout \prependtoks \finalflushJSpreamble \to \everylastshipout %D \macros %D {doPSsanitizeJScode} %D %D Before the code can be passed to the (\POSTSCRIPT\ or \PDF) %D output file, some precautions must be made concerning the %D use of \type{(} and~\type{)}. Here we use a beautiful %D \type{\aftergroup} trick I discovered in the \TABLE\ format. \def\doPSsanitizeJScode#1\to#2% {\begingroup \scratchcounter\zerocount % \aftergroup counter \aftergroup\xdef \aftergroup#2% \aftergroup{% \expanded{\defconvertedargument\noexpand\JScode{#1}}% \expandafter\handletokens\JScode\with\dodoPSsanitizeJScode \aftergroup}% \endgroup \iftraceJScode \writestatus{JS trace}{#2}% \fi} %D I started with: %D %D \starttyping %D \def\dodoPSsanitizeJScode#1% %D {\aftergroup\string %D \if#1(% %D \expandafter\aftergroup\csname#1\endcsname %D \else\if#1)% %D \expandafter\aftergroup\csname#1\endcsname %D \else\if#1;% %D \aftergroup;\aftergroup\string\expandafter\aftergroup\ %D \else %D \expandafter\aftergroup#1% %D \fi\fi\fi %D \advance\scratchcounter by 1 %D \ifnum\scratchcounter=500 %D \expandafter\dododoPSsanitizeJScode %D \fi} %D \stoptyping %D %D For pretty printing purposes, we need some way to signal %D \TEX\ macros. Therefore we introduce a special keyword %D \type{TEX}. When followed by a space, this keyword is %D ignored, that is, filtered from the stream. Now we have: \chardef\JSisTEX \zerocount \chardef\JScomment\zerocount \newif\ifaddJSlinebreaks \addJSlinebreakstrue \def\flushJSisTEX {\ifcase\JSisTEX \or \aftergroup T% \or \aftergroup T\aftergroup E% \or \aftergroup T\aftergroup E\aftergroup X% \fi \chardef\JSisTEX\zerocount} % \def\doJSlinebreak % {\ifaddJSlinebreaks % \aftergroup\string\aftergroup\n% % \fi} % % \def\dodoPSsanitizeJScode#1% % input stack>500 & TEX check % {\if#1/% % \ifnum\JScomment=0 % \chardef\JScomment\plusone % \else\ifnum\JScomment=1 % \chardef\JScomment\plustwo % \fi\fi % \else % \ifnum\JScomment=1 % \aftergroup/% % \chardef\JScomment\zerocount % \fi % \ifnum\JScomment=2 % \if#1\delcharacter % \chardef\JScomment\zerocount % \fi % \else % \if#1\delcharacter % \flushJSisTEX\doJSlinebreak % \else\if#1(% % \flushJSisTEX\aftergroup\string\expandafter\aftergroup\csname#1\endcsname % \else\if#1)% % \flushJSisTEX\aftergroup\string\expandafter\aftergroup\csname#1\endcsname % \else\if#1;% % \flushJSisTEX\aftergroup;\doJSlinebreak % \else\if#1T% % \ifnum\JSisTEX=0 \chardef\JSisTEX\plusone \else\flushJSisTEX\aftergroup T\fi % \else\if#1E% % \ifnum\JSisTEX=1 \chardef\JSisTEX\plustwo \else\flushJSisTEX\aftergroup E\fi % \else\if#1X% % \ifnum\JSisTEX=2 \chardef\JSisTEX\plusthree \else\flushJSisTEX\aftergroup X\fi % \else\if#1\normalspace % \ifnum\JSisTEX=3 \chardef\JSisTEX\zerocount \else\flushJSisTEX\aftergroup#1\fi % \else % \flushJSisTEX\aftergroup\string\expandafter\aftergroup#1% % \fi\fi\fi\fi\fi\fi\fi\fi % \fi % \fi % \dododoPSsanitizeJScode} % todo: "http:\\" -> simple. maar wel \" afvangen % % use new pdftex escape mechanism or make fully expandable version, not used that often btw \chardef\JSstring\zerocount \def\doJSlinebreak {\chardef\JScomment\zerocount \chardef\JSstring\zerocount \ifaddJSlinebreaks \aftergroup\string\aftergroup\n% \fi} \def\dodoPSsanitizeJScode#1% % input stack>500 & TEX check {\if#1/% \ifnum\JSstring=0 \ifnum\JScomment=0 \chardef\JScomment\plusone \else\ifnum\JScomment=1 \chardef\JScomment\plustwo \fi\fi \else \aftergroup/% \fi \else \ifnum\JScomment=1 \aftergroup/% \chardef\JScomment\zerocount \fi % is the delchar trick still needed? \ifnum\JScomment=2 \ifnum`#1=13 % brrr \doJSlinebreak \else\if#1\par \doJSlinebreak \else\if#1\delcharacter \doJSlinebreak \fi\fi\fi \else \ifnum`#1=13 % brrr \flushJSisTEX\doJSlinebreak \else\if#1\par \flushJSisTEX\doJSlinebreak \else\if#1\delcharacter \flushJSisTEX\doJSlinebreak \else\if#1(% \flushJSisTEX\aftergroup\string\expandafter\aftergroup\csname#1\endcsname \else\if#1)% \flushJSisTEX\aftergroup\string\expandafter\aftergroup\csname#1\endcsname %\else\if#1;% % \flushJSisTEX\aftergroup;\doJSlinebreak \else\if#1T% \ifnum\JSisTEX=0 \chardef\JSisTEX\plusone \else\flushJSisTEX\aftergroup T\fi \else\if#1E% \ifnum\JSisTEX=1 \chardef\JSisTEX\plustwo \else\flushJSisTEX\aftergroup E\fi \else\if#1X% \ifnum\JSisTEX=2 \chardef\JSisTEX\plusthree \else\flushJSisTEX\aftergroup X\fi \else\if#1\normalspace \ifnum\JSisTEX=3 \chardef\JSisTEX\zerocount \else\flushJSisTEX\aftergroup#1\fi \else % todo: "test\"test" \if#1"% \ifcase\JSstring \chardef\JSstring\plusone \else \chardef\JSstring\zerocount \fi \fi \flushJSisTEX\aftergroup\string\expandafter\aftergroup#1% \fi\fi\fi\fi\fi\fi\fi\fi\fi % \fi \fi \fi \dododoPSsanitizeJScode} %D Close reading learns that one line comments (\type{// ...}) %D are removed from the stream. This permits switching in %D pretty printing \JAVASCRIPT\ sources as well as saves %D some bytes. %D The magic 500 in the next hack prevents the input stack from %D overflowing when large scripts are sanitized. \def\dododoPSsanitizeJScode {\ifcase\JSisTEX\ifcase\JScomment \advance\scratchcounter \plusone \fi\fi \ifnum\scratchcounter=500 \expandafter\dodododoPSsanitizeJScode \fi} \def\dodododoPSsanitizeJScode {\let\next={% \aftergroup}% \endgroup \begingroup \aftergroup\xdef \aftergroup\sanitizedJScode \aftergroup{% \aftergroup\sanitizedJScode \let\next=}} %D The macro \type{\doPSsanitizeJScode} converts its argument %D into the macro \type{\sanitizedJScode}, thereby prefixing %D each \type{(} and \type{)} by a slash. %D Hooking this mechanism into the general \CONTEXT\ reference %D mechanism does not take much effort: \definespecialtest{JS}% {\doifdefinedelse{\r!java\currentreferenceoperation}} \definespeciallocation{JS}#1#2% {\iflocation \bgroup \bgroup \presetJScode \currentreferenceoperation \currentreferencearguments \egroup \dohandlegoto {#2}% {\dostartgotoJS\buttonwidth\buttonheight\JScode}% {\dostopgotoJS}% \egroup \else {#2}% \fi} %D \macros %D {useJSscripts} %D %D In due time, users will build their collections of scripts, %D which can be used (loaded) when applicable. Although not all %D public, we will provide some general purpose scripts, %D collected in files with names like \type{java-...}. One can %D load these scripts with \type{\useJSscripts}, like: %D %D \starttyping %D \useJSscripts[fld] %D \stoptyping %D %D The not so complicated implementation of this macro is: \def\dodouseJSscripts#1% {\doifelse{#1}\v!reset {\let\allJSpreambles\empty} {\doifundefined{\c!file\f!javascriptprefix#1} {\startnointerference \letgvalueempty{\c!file\f!javascriptprefix#1}% \makeshortfilename[\f!javascriptprefix#1]% \startreadingfile \readsysfile{\shortfilename.\mksuffix} {\showmessage\m!javascript1{#1}} {\readsysfile{\shortfilename.tex} {\showmessage\m!javascript1{#1}} \donothing}% \stopreadingfile \stopnointerference}}} \def\douseJSscripts[#1][#2]% {\processcommalist[#1]\dodouseJSscripts \processcommalist[#2]\useJSpreamblenow} \def\useJSscripts {\dodoubleempty\douseJSscripts} \protect \endinput