Go, JavaScript Go!

Posted by Brian 06/12/2012 at 11:28PM

Introduction

You gotta love FileMaker Go. Go’s ability to effortlessly create data-driven mobile apps and extend existing desktop data applications to mobile users is transformative. But sooner or later, almost all of us run up against one of Go’s core limitations: Its lack of native plugins.

Most of us understand this isn’t an oversight, but rather a basic constraint imposed by the architecture of Apple’s iOS. Still, the inability to extend Go’s native toolbox through plugins often limits the capabilities and expressiveness of Go solutions. Many developers and users have probably daydreamed of how much better their mobile solutions might be if only they could find some way of extending Go with plugin functionality. If only… if only…

In FileMaker Go 12, we can now do exactly this.

Giving New Life To Your Mobile Database

This article describes how you can create virtual plugins for FileMaker Go 12 using JavaScript and FileMaker 12’s new fmp:// protocol in your Go solutions — a technique we’ll call “JavaScript Go”. Using this new technique, you’ll be able to leverage the vast power of JavaScript — as well as popular JavaScript libraries like jQuery, zepto.js, and underscore.js — to add power, functionality, and excitement to your mobile solutions.

As usual, a complete demo file is available at the end of this post. Thanks to WorldCloud for use of their FileMaker hosting services, we're able to offer a live, hosted demo version of this database at: fmp://shared-hosting–2.filemaker12.com/javascriptGO

OK. Let’s get started…

A Quick Review. Or, "How To Create Your Very Own Monster in 4 Easy Steps"

The underlying techniques we’ll be using here repurpose several steps described in my recent blog post Native Web 2.0 Controls in FileMaker 12 Layouts. I explained how FileMaker 12’s new fmp:// protocol lets us integrate native web controls and design patterns as full citizens of our Pro and Go visual interfaces. In our current post, we’ll flip this technology on its head. This time, we'll use the same fmp:// protocol to create JavaScript engines that silently spring to life to add plugin-like power and capabilities to our hosted or stand-alone Go solutions any time we want. (Note that although we’ll focus almost exclusively on FileMaker Go, this technique works equally well on any hosted or shared Pro 12 solution).

Let’s quickly recap the steps explained previously, this time following in the footsteps of that immortal classic, “Young Frankenstein”.

Step 1: Find a Body

As the movies teach us, a budding mad scientist building a creature from scratch must start with a body. When the young Dr. Frankenstein needed a body for his creature, he was forced to unearth a cold, hulking corpse in the dead of night. We FileMaker Go developers have it much easier: We’ll just place a named Web Viewer on our Go layouts, staying nice and cozy as we do so.

Step 2: Select a Brain (and Make it a Good One)

The movies also make clear that once we have our body, we must find it a brain. This step trips up so many mad scientists; time and again their well-meaning sidekicks bring back the worst specimen in the entire Brain Repository. We won’t make that mistake: We’ll return from our lab with speedy, lean, industry-standard JavaScript, ready to insert that into our Web Viewer.

Step 3: Give Your Creature Life

Once we have a Body and have inserted our Brain into it, we face the age-old question of how to bring them both to life. To animate his new creation, Young Frankenstein had to risk life and limb, high atop a questionably designed gothic castle on a dark and stormy night. We’ll be able to bring our JavaScript virtual plugin to life simply by injecting custom JavaScript and live FileMaker parameters into our Web Viewer whenever we want it to live. If only Dr. Frederick Frankenstein had known of FileMaker 12…

Step 4: Teach Your Creature to Speak

Finally, our now-living creature must be able to communicate to us. Here’s where FileMaker 12’s new fmp:// protocol really helps us out: In earlier versions of Go, Web Viewers trying to converse with native FileMaker scripts made awkward grunting noises, just like so many matinee creatures. Igor, Frau Blücher and company needed an entire movie to resolve this tricky conundrum, ultimately resorting to a dangerous brain transference procedure to enable their monster to communicate clearly. In our case, FileMaker 12’s new fmp:// protocol makes it easy: Our Web Viewers will speak directly and eloquently to our native FileMaker scripts right out of the box, with no need for expensive brain surgeries our HMO’s likely won’t cover. Huzzah!

And Now to the Laboratory…

Let’s roll up our sleeves and look at the details of how we can build a JavaScript Go creature of our own:

jsGO Web Viewer

We’ll start by simply placing a Web Viewer on our layout, which will act as the “body” for our JavaScript “brains”. Let’s call it “jsGO.view”. I’ve found a couple of gotchas — the Web Viewer can’t be hidden on an inactive tab or beyond the visible area of a layout, and Go performance is best if each layout gets its own copy of “jsGO.view”.

jsGO Custom Functions

JavaScript Go uses a small set of lightweight custom functions: “jsGO.fmpWrap” provides a context-aware wrapper for the new fmp:// protocol, so whether we’re running a local or hosted Go solution, or a hosted or shared Pro solution, our JavaScript will be able to communicate with our FileMaker scripts. The custom functions “jsGO.scriptWrap” and “jsGO.webWrap” help us easily wrap JavaScript snippets in valid script and HTML5 wrappers; Last, a set of hash custom functions allow us to pass named parameters to our core scripts (see the next section for more info). All of these custom functions are fully generalized, so we can just paste them into any solution then get on with our work.

jsGO Scripts

Our technique uses 2 scripts: The first, “jsGO.listener” will simply listen for results our JavaScript returns to our solution via the fmp:// protocol, then place these results in a global variable, $$jsGO.results.

Our second script, “jsGO” is the script that will do the heavy lifting, and the script that will be the public face of javascriptGO. We’ll invoke our jsGO script whenever we call our virtual plugin, much as we’d normally call a standard plugin in Pro.  Let’s take a closer look at how it works:

Our jsGO script will need to be able to accept many types of data, and varying numbers of parameters, depending on the JavaScript functionality desired. To handle this gracefully, we’ll choose the widely-used method of passing named parameters in a hash format. We’ll require each call to jsGO to pass a “function” parameter telling our script what we’re asking of it; beyond this, we’ll accept any number of additional parameters needed. This will let our script accept variable types and numbers of parameters, then parse each parameter passed to local variables we can integrate into our JavaScript.

After we’ve parsed our parameters, we’ll use a simple if / elseif structure to locate the function requested. If a null or unknown function is encountered, we’ll return an error (via $$_jsGO.error) and exit. Within our if / elseif structure, once we’ve located the desired function, we’ll define the custom JavaScript that will perform that task. In the simplest cases (like requesting time in milliseconds, getting random numbers, etc.) we’ll only need static JavaScript. More typically, we’ll inject live FileMaker data passed via our parameters directly into our JavaScript, so that our virtual plugin will be able to act on live solution data. Whatever task we’re performing, our JavaScript will include a return function enabling it to speak to our listener script with its calculated results.

Now comes the fun part: We’ll wrap our JavaScript snippets inside script and HTML5 wrappers and insert this into the “jsGO.view” Web Viewer on our active layout, bringing our creation to life. Once we’ve done this, we’ll pause and wait briefly for our Web Viewer to do its work and communicate its results back to our jsGO.listener script. As soon as we have these results, our script will resume. 

Last, we may perform optional cleanup if, for example, our $$jsGO.results need to be reformatted a bit or have characters un/escaped. And then, jsGo exits, allowing the calling script or object to process our JavaScript Go results and continue its work. Here’s the main body of our "jsGO" script:

# Parse Parameters

 

Set Variable [ $_value; Value:#P ( "value" ) ]

Set Variable [ $_regex; Value:#P ( "regex" ) ]

 

      . . . (omitting similar steps for brevity) . . .


#     â–¼ Single-Pass Loop â–¼

Loop

 

# Set Custom JavaScript by Function

 

If [ IsEmpty ( $_function ) ]

Set Variable [ $_error; Value:10 ]

Set Variable [ $_error_message; Value:"Required parameter 'function' not received." ]

Exit Loop If [ True ]

Else If [ $_function = "number.toHex" ]

Set Variable [ $_javascript; Value:Let ( the_script = "

var num = " & Int ( $_param_1 ) & " ;

var result = num.toString(16);

self.location = " & sQuote ( jsGO.fmpWrap ( "jsGO.listener" ; "" & "" ) ) & "+result; ";

jsGo.scriptWrap ( the_script ) )]

 

      . . . (omitting additional function definitions for brevity) . . .

 

Else If [ $_function = "regex.strip" ]

Set Variable [ $_javascript; Value:Let ( [

text=Substitute($_param_1; ["'";"\'"];["\"";"\\\""];["¶";"\n"];["=";"–"]); pattern = $_param_2 ;

the_script = "

var str=" & sQuote ( text ) & " ;

var patt = " & pattern & " ;

var resultStr = str.replace(patt,'');

self.location = " & sQuote ( jsGO.fmpWrap ( "jsGO.listener" ; "" & "" ) ) & " + resultStr ; "

];

jsGo.scriptWrap ( the_script ) )]

Else

Set Variable [ $_error; Value:10 ]

Set Variable [ $_error_message; Value:"Unknown 'function' parameter received." ] Exit Loop If [ True ]

End If

 

# Set web viewer and wait for response

 

Set Variable [ $_start_timestamp; Value:Get ( CurrentTimeStamp ) ]

Set Web Viewer [ Object Name: "jsGO.view"; URL: "data:text/html," & jsGo.webWrap ( $_javascript ) ]

Loop

Exit Loop If [ $$_jsGO.results ≠ "" or Get ( CurrentTimeStamp ) ≥ $_start_timestamp + $_timeout_seconds ]

Pause/Resume Script [ Duration (seconds): $_loop_interval ]

End Loop

 

# Reset web viewer and handle any errors

 

Set Web Viewer [ Object Name: "jsGO.view"; URL: "data:text/html," & jsGo.webWrap ( "" ) ]

If [ IsEmpty ( $$_jsGO.results ) ]

Set Variable [ $_error; Value:20 ]

Set Variable [ $_error_message; Value:"No results were returned from the web viewer." ]

Exit Loop If [ True ]

End If

 

# Do any Cleanup if needed

 

If [ $_function = "" ]

Else If [ $_function = "values.unique" ]

Set Variable [ $$_jsGO.results; Value:Substitute ( $$_jsGO.results ; "," ; ¶ ) ]

 

      . . . (omitting similar steps for brevity) . . .

 

Else If [ $_function = "number.toHex" ]

Set Variable [ $$_jsGO.results; Value:Upper ( $$_jsGO.results ) ]

End If

 

Exit Loop If [ True ] End Loop

#       â–² Single-Pass Loop â–²

Is It Friendly?

Any useful plugin must be easy to integrate into our solutions and simple to use. We understand that any FileMaker plugin will be complex under the hood, but we also expect this complexity to be hidden within the plugin. Our technique meets this test nicely.

Adding JavaScript Go to any solution is very simple, because it’s designed to be quickly bolted in to any solution: We simply add jsGO Web Viewers to the solution’s layouts, paste in our custom functions, paste in our scripts, and… that’s it. Just that easily, we can now call a JavaScript Go function from any script or layout object in this solution as simply as we’d call a normal FileMaker plugin. The most noticeable difference is that instead of making a true function call (as with a true plugin), we instead run our jsGO script. Otherwise, the concepts — and the simplicity — are virtually identical. For example, here’s a sample call to jsGO:

Perform Script [

“jsGO”; Parameter: List (

# ( "function" ; "regex.match" ) ;

# ( "value" ; [filemaker_value] ) ;

# ( "regex" ; [regex_pattern] )

)

]

Putting On The Ritz

We're finished in our laboratory. But the movies make clear we're not truly finished until we've displayed our creation before a community of skeptical scientists. So let’s bring JavaScript Go out onto the stage, cue the band, and see what he can actually do for us:

Out of the box, JavaScript Go knows some nifty steps. JavaScript string and array operations offer us a great deal of power, and number, date, and time methods bring some nice extensions to our native FileMaker toolbox. These native JavaScript functions give us new capabilities, and in some cases offer more precision (getting time in milliseconds, more accurate random numbers, etc). Not a bad start…

The band takes the tune up a half step, and JavaScript Go kicks it up a notch, showing off it’s ability to do RegEx — the powerful pattern matching tool used by many modern programming environments, including JavaScript. Want to find all phone numbers — regardless of formatting — anywhere in a string? Or all html or xml tags — even unexpected ones? Or maybe you’d like to find all zip codes in your text, but only if they’re followed by a 4 number suffix? And then you’d like to replace ones meeting certain criteria with other strings of your choosing? We can do any of these complex operations in only a single call to our virtual plugin.

As the band notches it up another half step, JavaScript Go now shows off a bit of what it can do using JavaScript libraries like jQueryzepto.js, and underscore.js. We can store these directly within our mobile solutions, maximizing performance and ensuring these libraries will work offline if needed. Using these powerful libraries, we can extend JavaScript’s native toolset even further to let us do things like industry-standard RSA encryption (locally on the device, without any round trips to a server), AJAX operations, and much more heavy lifting than we can review in the current post. (But… shameless teaser… stay tuned for future posts).

So far, everything’s going great. But what’s this? Someone from the audience runs up and pulls the plug on our internet connection, setting our iOS device to Airplane Mode and leaving JavaScript Go without a network connection of any kind. The crowd gasps! Now the band unexpectedly takes it up a full step and a half — to D# minor! — just as a klieg light shatters, causing a small fire to break out onstage. Smelling imminent failure, the previously friendly crowd of scientists reaches in unison for a hidden supply of tomatoes (apparently stored beneath their seats). Will our JavaScript creature degenerate into an incoherent lump of technology, perhaps grabbing Fay Wray and making an ill-advised attempt to scale the Empire State Building in the process?

Not at all. JavaScript Go smiles to itself, then does the seemingly impossible, continuing to perform flawlessly without missing a beat — even without a network connection! Except for functions explicitly built to query live data sources, everything still works perfectly, including our extended libraries, because we’ve cached them locally in our solution. Our solutions hum along seamlessly, our potential crisis is averted, and those rotten tomatoes disappear into non-existence as our audience sees how robust and versatile our virtual plugin is. The ghost of Fay Wray (who never belonged in our movie anyway) breathes a sigh of relief from its final resting place.

Conclusion

JavaScript Go offers FileMaker Go 12 developers the opportunity to move beyond the box of Go’s stock functionality. We can now create mobile solutions that are richer, more powerful, and more full-featured than previously possible. At the same time, we gain access to JavaScript’s world-wide development community, and a vast library of JavaScript functions and libraries. And as with many of the most powerful FileMaker Pro plugins, JavaScript Go is endlessly extensible, ultimately being limited only by our imaginations. It will be exciting to see how our development community puts these exciting new capabilities of FileMaker Go 12 to use.

Enjoy!
-Brian

Follow Brian on Twitter

.  .  .  .  .  .  .  .  .  .  .  .  .

Demo File

Our demo file now comes in 2 flavors: You can access a hosted version of  file at: fmp://shared-hosting–2.filemaker12.com/javascriptGO. Note, however, that this hosted file doesn't offer admin access to scripts and custom functions. If you'd like to tinker under the hood or load this file locally, you can download an unlocked copy of the demo file: javascriptGO.fmp12

Comments

Leave a comment

  1. Gravatar
    johnjackson@pobox.com 14 days later:

    I tried the demo(s): it works fine at the hosted site, but the downloaded version just returns errors…

    Error 20: “No results were returned from the web viewer…”

  2. Gravatar
    Brian Schick 14 days later:

    John,

    It sounds like you’re probably running the demo locally in FileMaker Pro 12, which isn’t currently supported by the fmp:// protocol that powers this technique. At this time, Pro solutions must be hosted (on a FileMaker server) or shared peer-to-peer (where a mac/pc other than yours has first opened the file). This limitation exists only on Pro; Go solutions can be either local (installed directly in Go 12 on the iOS device) or shared/hosted.

  3. Gravatar
    John Jackson Jackson 14 days later:

    You’re right – that’s what I tried. Yes, it works fine locally on my iPad. It wasn’t clear from article that it was a Go-only feature…now, if we only had this on the desktop (along with PDF generation in the runtime)…

  4. Gravatar
    Brian Schick 14 days later:

    @John: Glad to hear it. And remember, this technique works great on the desktop except when the file is local on your Mac or PC.

    By the way, the inability to use the fmp:// protocol on local files in Pro 12 may be a temporary glitch. We think there’s reason to hope that FileMaker may fix this limitation in an incremental release to FileMaker 12. If and when this happens, I double pinky promise that we’ll let our readers know as soon as we hear the good news ;-).

  5. Gravatar
    William 14 days later:

    What about “offline” solutions. Does this work when you’re not connected?

  6. Gravatar
    Brian Schick 14 days later:

    @William: Yes. JavaScriptGo caches all assets locally, so if you use jsGo functions on a locally stored Go 12 solution, everything should work without problems even if you go into Airplane mode. The only exception to this would of course be any functions designed to pull data from live network or web sources, like, say, AJAX functions.

    BTW, you can confirm this in the demo file. Just load it onto your iOS device, pop your device into airplane mode, and give it a go…

  7. Gravatar
    William 14 days later:

    Thanks @Brian. Works great.

    Do I need to worry about FileMaker Go and the Web Viewer flushing the JavaScript library out of the cache?

  8. Gravatar
    Brian Schick 15 days later:

    William, I’m assuming you’re asking if cached JavaScript assets will affect memory resources available in Go? (If not, let me know)…

    The short answer: No worries. JavaScript Go immediately clears the web viewer once it’s returned its response, keeping everything nice and lean.

    The slightly longer answer: There is one aspect of JavaScript caching we might reconsider. For the demo, I cached 3 extended JS libraries in global fields - mostly for simplicity and conceptual clarity. In a production solution, I might opt to put those JavaScript assets into 3 custom functions, so that we weren’t keeping them stored in memory.

    In that case, we’d end up with custom functions like “jsLibrary.jQuery” that we’d just insert into our JavaScript snippets in the jsGo script. This would be a very easy switch - we’d just need to escape out every double quote in each of the JavaScript libraries before setting up your custom function. I believe you could also just replace all double quotes with single quotes, but you’d want to test this to confirm (and please let me know if you do ;-)

Leave a comment