To summarize where we are, we’ve been trying to find a way to store functions as
persistent globals, so that we can come back to them, from ANY future PDF form that our
reader downloads from our site, and use the code that’s stored there. It’s kind of like
being able to take a bathroom break during the Scholastic Aptitude Test, and having the
Cliffs Notes to Euclid’s Geometry already stored behind the toilet in the men’s room. We
want to be able to put whatever Cliffs Notes we want, behind whosoever’s toilet we want.
Recap
What we’ve figured out so far is that we can convert a function to a string with
JavaScript’s built-in toString() function. And we can convert strings to functions
with the built-in Function constructor. But in order to use the latter, we have to supply
the ‘guts’ of the function as a string, minus the declarative ‘function funcName(args)’
prefix. We developed a regex to clip that prefix off of function declarations yesterday.
But we also have another problem to deal with, namely the problem that toString()
introduces unwanted formatting into the strings it produces! Weirdly enough, it turns out
that this function inserts newlines and spaces willy-nilly, to format our code, whether
that’s what we wanted or not. The reason this is a problem for us is that when we put the
resulting string into a persistent global, and then that global gets read into memory again
later (at the start of another session), the newlines cause errors of the ‘unterminated
string literal’ kind.
The answer to the latter problem is another regex. All we have to do is pass the output
of toString() thru a regex that removes newlines. In JavaScript, we do:
str = str.replace(/n/g,'');
This regex-replace operation is applied globally (because of the lowercase ‘g’) to all
newlines. And since the replacement string is null, we end up deleting all newlines, plain
and simple. Problem solved.
The Magic Formula
So, in order to store a library function as a persistent global, here is what we have to
do:
- Convert the function to a string.
var str = myFunc().toString();
- Take the declarative front end off.
str = str.replace(/function [a-zA-Z_]+(S*)/g,'');
- Take the newlines out.
str = str.replace(/n/g,'');
- Make it a global.
global.f1 = str;
- Make it persistent.
global.setPersistent('f1',true);
To restore the function, we have to do this:
- Read the global string into a variable (optional).
var s = global.f1;
- Construct a new function.
var newFunc = new Function('a',s);
- Use the new function just like you would the old one.
x = newFunc( a );
The ‘a’ in the Function constructor is the argument that the old function took (if any).
Some functions have multiple args, some have none; obviously you’ll have to tailor this part
to the situation, although I hasten to add that as an exercise, you should be able to devise
a general solution (one set of code fitting all situations) using regexes, arrays, and/or
common sense. We’ve already complicated this discussion enough with regexes, so I won’t go
there any more just now. However, I do encourage you to play with the above recipes for
storing and reconstructing functions as persistent globals. Maybe you can find ways to
improve on my scheme through the use of arrays, concatenated strings (join/slice methods),
etc. In fact, I’m sure you can find many ways to improve on this scheme. All I was
trying to do is determine a way in which persistent storage of library functions
could be done. At all.
Checking to See If a Global Exists
Before I stop today, I want to give a tip for checking to see if a global already has
been defined or already ‘lives’ because of persistence. Sometimes in JavaScript it is not
easy to check a variable for validity. But in this case it turns out to be easy. You can
check a global this way:
if (global.myWhatever == 'undefined') { // doesn't exist }else
{ // exists}
An undefined variable, in PDF JavaScript, will evaluate to the string ‘undefined’. So
testing it is easy. Use this trick to test whether your library functions (above) are
present.
Another trick:
When you want to make a variable global, but not necessarily persistent, you do NOT have
to store it in the Global object explicitly. Just go up to the Tools:Forms:Document
JavaScripts menu command in Acrobat, and enter a global as you would a top-level function.
(Delete the function skeleton.) For example, enter gMyVariable = 0; and hit OK to
dismiss the dialog. (Don’t type a function.) From that point on, gMyVariable will be
globally available throughout your document, to any function that needs it. And, not only is
it available throguhout the current document… it is available to ALL open PDF documents
that have JavaScripts. As I said, it is a true global.