%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 / Hans Hagen \& Ton Otten}] %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 % 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. First some messages: \startmessages dutch library: javascript title: javascript 1: script set -- wordt geladen 2: onbekende preamble -- \stopmessages \startmessages english library: javascript title: javascript 1: loading script set -- 2: unknown preamble -- \stopmessages \startmessages german library: javascript title: javascript 1: Lade Scriptdatei -- 2: unbekannte Preamble -- \stopmessages \startmessages czech library: javascript title: javascript 1: nacita se soubor skriptu -- 2: neznama preambule -- \stopmessages \startmessages italian library: javascript title: javascript 1: caricamento dello script set -- 2: preambolo sconosciuto -- \stopmessages \startmessages norwegian library: javascript title: javascript 1: leser inn scriptsett -- 2: ukjent 'preamble' -- \stopmessages \startmessages romanian library: javascript title: javascript 1: se incarca scriptul -- 2: preambul necunoscut -- \stopmessages %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 \starttypen %D \naar{calculate total}[Sum()] %D \stoptypen %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 \starttypen %D \naar{calculate total}[Sum(1.5,2.3)] %D \stoptypen %D %D References are passed by using the \type{R{}} classifier. %D %D \starttypen %D \naar{calculate total}[Sum(1.5,2.3,R{overflow})] %D \stoptypen %D %D The last call calls the script \type{Sum} and passes the %D next set of variables: %D %D \starttypen %D JS_S_1="1.5"; %D JS_S_2="2.3"; %D JS_R_3="overflow"; %D JS_P_3=3; %D \stoptypen %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 \starttypen %D \naar{calculate total}[JS(Sum{V{1.5},V{2.3},R{overflow}})] %D \stoptypen %D %D does a verbose passing: %D %D \starttypen %D JS_V_1=1.5; %D JS_V_2=2.3; %D JS_R_3="overflow"; %D JS_P_3=3; %D \stoptypen % %D % %D Finally we have a counter that tells\JAVA\ how many % %D arguments were passed, % %D % %D \starttypen % %D JS_N % %D \stoptypen %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 \starttypen %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 \stoptypen %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 \starttypen %D \startJScode{name} %D name = 4 ; %D \stopJScode %D \stoptypen %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 \starttypen %D \startJScode{uses} uses {later} %D uses = 6 ; %D \stopJScode %D \stoptypen \long\gdef\startJScode#1 #2 {\doifelse{#2}{uses} {\def\next{\dostartJScodeA{#1}}} {\def\next{\dostartJScodeB{#1} #2}}% \next} \long\gdef\dostartJScodeA#1#2 #3\stopJScode% {\long\setgvalue{\r!java#1}{\do{#2}{#3}}} \long\gdef\dostartJScodeB#1#2\stopJScode% {\long\setgvalue{\r!java#1}{\do{}{#2}}} %D \macros %D {presetJScode} %D %D The code can be retrieved by saying %D %D \starttypen %D \presetJScode{SomeScript}{template} %D \stoptypen %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}. \def\setverbosecscharacter#1% {\edef#1{\string#1}} \def\setverbosecscharacters% temporary hack {\setverbosecscharacter |\setverbosecscharacter ~% \setverbosecscharacter\:\setverbosecscharacter\;% \setverbosecscharacter\+\setverbosecscharacter\-% \setverbosecscharacter\[\setverbosecscharacter\]% \setverbosecscharacter\.\setverbosecscharacter\\% \setverbosecscharacter\)\setverbosecscharacter\(% \setverbosecscharacter\0\setverbosecscharacter\1% \setverbosecscharacter\2\setverbosecscharacter\3% \setverbosecscharacter\4\setverbosecscharacter\5% \setverbosecscharacter\6\setverbosecscharacter\7% \setverbosecscharacter\8\setverbosecscharacter\9% \setverbosecscharacter\/\setverbosecscharacter\s} \newif\ifdirectJScode \def\presetJScode#1#2% #1=operation #2=arguments {\setverbosecscharacters \def\par{\delcharacter}% was: { } \scratchcounter=0 \global\let\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\docommando##1% {\doifundefinedelse{\r!java\r!java##1} {\showmessage{\m!javascript}{2}{##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]\docommando}% \getvalue{\r!java#1}} \def\dopresetJSvariables#1% {\advance\scratchcounter by 1 \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 \starttypen %D \startJSpreamble{oeps} %D oeps = 1 ; %D \stopJSpreamble %D \stoptypen %D %D which is the same as: %D %D \starttypen %D \startJSpreamble{now} used now %D now = 2 ; %D \stopJSpreamble %D \stoptypen %D %D while the next definition is only included when actually %D used. %D %D \starttypen %D \startJSpreamble{later} used later %D later = 3 ; %D \stopJSpreamble %D \stoptypen %D %D This command may be used more that once, but always before %D the first page is shipped out. \newif\ifoneJSpreamble \oneJSpreambletrue \let\allJSpreambles=\empty \newcounter\nofJSpreambles \newcounter\currentJSpreamble \long\def\startJSpreamble#1 #2 % {\bgroup % we need to restore the catcodes \popendofline % just in case it happens while reading lists \doifelse{#2}{used} {\def\next{\dostartJSpreamble#1 }} {\def\next{\dostartJSpreamble#1 now #2 }}% \next} \long\def\dostartJSpreamble#1 #2 #3\stopJSpreamble% {\processaction [#2] [ later=>\chardef\JSstatus=0, now=>\chardef\JSstatus=1, \s!default=>\chardef\JSstatus=2, \s!unknown=>\chardef\JSstatus=2]% \presetJSfunctions #3function ()\end \long\setgvalue{\r!java\r!java#1}{#3}% \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 \citeer {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} \def\getJSpreamble#1% {\getvalue{\r!java\r!java#1}} \def\presetJSpreamble% {\ifx\allJSpreambles\empty\else \bgroup \setverbosecscharacters \def\par{\delcharacter}% was: { } \global\let\JSpreamble=\empty \def\@@collectedJSpreamble{\r!java\r!java collected}% \letvalue{\@@collectedJSpreamble}=\empty \def\docommando##1% {\xdef\JScode{\getvalue{\r!java\r!java##1}}% \ifoneJSpreamble \@EA\setxvalue\@EA\@@collectedJSpreamble\@EA {\csname\@@collectedJSpreamble\endcsname\JScode}% \else \setxvalue{\r!java\r!java##1}% {\JScode}% \fi}% \processcommacommand[\allJSpreambles]\docommando \ifoneJSpreamble \gdef\allJSpreambles{collected}% \fi \global\let\presetJSpreamble=\relax \egroup \fi} \def\flushJSpreamble% {\iflocation\ifx\allJSpreambles\empty\else \ifcase\nofJSpreambles\else\ifnum\nofJSpreambles=\currentJSpreamble \bgroup \presetJSpreamble \expanded{\doflushJSpreamble{\allJSpreambles}}% \global\let\flushJSpreamble=\relax \global\let\allJSpreambles=\empty \egroup \fi\fi \fi\fi} \def\finalflushJSpreamble% kan direct in \everylastshipout, testen {\iflocation \flushJSpreamble \ifcase\currentJSpreamble\relax\else \savecurrentvalue\nofJSpreambles\currentJSpreamble \global\let\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=0 % \aftergroup counter \aftergroup\xdef \aftergroup#2% \aftergroup{% \expanded{\convertargument#1\noexpand\to\noexpand\JScode}% \expandafter\handletokens\JScode\with\dodoPSsanitizeJScode \aftergroup}% \endgroup \iftraceJScode \writestatus{JS trace}{#2}% \fi} %D I started with: %D %D \starttypen %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 \stoptypen %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=0 \chardef\JScomment=0 \newif\ifaddJSlinebreaks \def\flushJSisTEX% {\ifcase\JSisTEX \or \aftergroup T% \or \aftergroup T\aftergroup E% \or \aftergroup T\aftergroup E\aftergroup X% \fi \chardef\JSisTEX=0 } \def\doJSlinebreak% {\ifaddJSlinebreaks \aftergroup\string\aftergroup\n% \fi} \def\dodoPSsanitizeJScode#1% % input stack>500 & TEX check {\if#1/% \ifnum\JScomment=0 \chardef\JScomment=1 \else\ifnum\JScomment=1 \chardef\JScomment=2 \fi\fi \else \ifnum\JScomment=1 \aftergroup/% \chardef\JScomment=0 \fi \ifnum\JScomment=2 \if#1\delcharacter \chardef\JScomment=0 \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=1 \else\flushJSisTEX\aftergroup T\fi \else\if#1E% \ifnum\JSisTEX=1 \chardef\JSisTEX=2 \else\flushJSisTEX\aftergroup E\fi \else\if#1X% \ifnum\JSisTEX=2 \chardef\JSisTEX=3 \else\flushJSisTEX\aftergroup X\fi \else\if#1\normalspace \ifnum\JSisTEX=3 \chardef\JSisTEX=0 \else\flushJSisTEX\aftergroup#1\fi \else \flushJSisTEX\aftergroup\string\expandafter\aftergroup#1% \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. %\beginTEX \def\dododoPSsanitizeJScode% {\ifcase\JSisTEX\ifcase\JScomment \advance\scratchcounter by 1 \fi\fi \ifnum\scratchcounter=500 \expandafter\dodododoPSsanitizeJScode \fi} \def\dodododoPSsanitizeJScode% {\let\next={% \aftergroup}% \endgroup \begingroup \aftergroup\xdef \aftergroup\sanitizedJScode \aftergroup{% \aftergroup\sanitizedJScode \let\next=}} %\endTEX % % Why is \aftergroup not doing what I expect? % % \beginETEX \aftergroup % % \let\dododoPSsanitizeJScode\relax % % \endETEX %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 \dostartgoto \data {#2}% \start \dostartgotoJS {\number\buttonwidth}{\number\buttonheight} {\JScode}% \stop \dostopgotoJS \dostopgoto \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 \starttypen %D \useJSscripts[fld] %D \stoptypen %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} {\bgroup \setbox0=\hbox % forget spaces and worse {\setgvalue{\c!file\f!javascriptprefix#1}{}% \makeshortfilename[\f!javascriptprefix#1]% \showmessage{\m!javascript}{1}{#1}% \startreadingfile \readsysfile{\shortfilename}{}{}% \stopreadingfile}% \egroup}}} \def\douseJSscripts[#1][#2]% {\processcommalist[#1]\dodouseJSscripts \processcommalist[#2]\useJSpreamblenow} \def\useJSscripts% {\dodoubleempty\douseJSscripts} \protect \endinput