Communicating with a Web Viewer in FileMaker

Beezwax has always been active in the FileMaker community regarding advanced use of Web Viewers. Check out these earlier posts if you’d like to learn more:

  1. Native Web 2.0 Controls in FileMaker 12 Layouts
  2. Go, JavaScript Go!
  3. Enable WebKit debugger in FileMaker Web Viewer

 

When the Web Viewer was introduced in FileMaker v8.5, it was designed to display websites such as a Google Maps which included the address of the current record.  It accomplished this by inserting record data into the url as an HTTP GET request. However, it didn’t take long for developers to take advantage of html’s data URI syntax to insert html directly into the Web Address box.  This seems to be the most popular method currently to display a local web page inside of a web viewer.  The main advantage here is that you can include field references directly in the code as a way to pass data into a Web Viewer.

"data:text/html,<html><head><title>Data%20URL</title></head><body>Hello " & PERSON::NAME & "</body></html>"

You can think this of this method as analogous to a php server inserting data from a database before rendering the html to be delivered to the browser.  Similarly, when new data needs to be injected, the entire page is re-rendered.  However, we are moving towards a more  asynchronous world were only the data is transferred and the state of the web page is not reset.  This is generally done with ajax calls to a server, but in our web viewer, there is no server.

Treat the Web Address box as the url

If you use the web address box as intended, then you’ll gain the advantage of some html features such as GET parameters and hash values.  However, the Web Viewer will need to locate a resource, so where should we point it?

Export web files to the temporary directory

Using local files instead of inline html has some advantages such as faster load times, use of browser caching, and easier editing of the files.  It is also necessary in order to use the url features mentioned above.  Now that we’re loading static pages which don’t have inline field references, we’ll have to figure out a better way to get data into the Web Viewer.

Talking to the web viewer

As mentioned above, we can pass data to the Web Viewer by adding values to the url as get parameters at the time of page load.

file://…/S10/index.html?foo=bar&company=beezwax

You can grab the GET parameters from the url via the window.location.search object or you can use the URI.js javascript library to make things easier.  The drawback here is that you can only send data on page load.  Luckily the hash property of the url is not so limited.

onhashchange

When the value of of the hash changes, a javascript event called “hashchange” is triggered on the window.

file://…/S10/index.html#here_is_the_hash

You can change the value of this in the Web Viewer web address box from FileMaker, and the event will be triggered in the javascript of the Web Viewer!  You can then retrieve the hash value with window.location.hash.  A simple example of adding an event listener would be the following:

window.addEventListener("hashchange", function(){
    alert(window.location.hash);
});

This is perfect for passing smaller amounts of data to a Web Viewer, but it has a couple of problems. First, the data must be encoded to be url safe, which can cause a bit of a slowdown on large data.  Secondly is that Internet Explorer has a url character limit of 2,083 characters.  Hopefully, one day this will be expanded, but until then, we’ll have to work around the limit.  A file would have no length limit, and wouldn’t need to be url safe, so let’s use hashchange to alert the Web Viewer that it should go get a file of data that FileMaker just exported to the temp directory.  I’ll use jQuery in the example below for brevity.

$(window).on('hashchange', function(){ 
    $.get( "data.csv", function( data ) { 
        // parse data here 
        alert("data was retrieved."); 
    }); 
})

As the web has matured, we’ve grown more accustomed to asynchronous requests for data which return only the needed data while not reloading the entire page and losing the user’s state.  This is commonly known as ajax and has become a staple of modern web development.  However, ajax relies on a server  to return the data, and in our humble web viewer environment, there is no server.  You can kinda make ajax requests to local files, but this is problematic.

Cross-Origin Resource Sharing

For security reasons, ajax calls can only be made to the same domain as the page that requested it (i.e. the “origin” server).  This means that a page hosted on beezwax.net could not load data from filemaker.com through ajax unless filemaker.com explicitly allows it by configuring their server to set the response headers to allow it.  You may be wondering what this has to do with a Web Viewer because there’s no server, per se, and therefore no origin are involved.  Different browsers treat local files differently.  For example, Firefox allows ajax calls to local files, Chrome allows with with an option setting, Safari allows it with a developer preference change, and Internet Explorer doesn’t allow it at all.  Interestingly, a Web Viewer on OS X still allows it, but I suspect that this could change at any moment.

<script> and JSONP

Loading a javascript file does not run into the same security concerns as a data file.  Think about how for example jQuery is loaded from a cdn with no regard to cross-origin issues.  Because the data is loaded as javascript, it is executed rather than loaded as data.  The loaded file must have a variable assignment or function call in order to pass the data to the scope of the web viewer.  This is the idea behind JSONP and a couple of examples are below.

First, a script tag is added to the dom:

var script_block = document.createElement('script');
script_block.type = 'text/javascript';
script_block.src = 'data.js';
document.head.appendChild(script_block);

The contents of data.js could be:

receiveData("I am the data that has been exported by Filemaker to a file in the temporary directory");

When the script tag is added to the dom, the data.js file is loaded and executed, meaning the function “receiveData” is called with the data as an argument.  “receiveData” would be a function that you would write to handle the data.  The idea of wrapping data in a function call or variable assignment in order to get around CORS restrictions is called JSONP.  The P in this instance stands for “padding,” meaning that the data is padded with a function call.

Talking to Filemaker

The fmp:// url scheme has been widely discussed, so there’s no need to go into it too deeply here, but it’s worth a quick recap.

For fmp:// url,

  • the use is to call a particular script in a particular database
  • format is  fmp://account:password@netaddress/databasename?script=scriptname&param=scriptparameter&$var1=value&$var2=value
  • account, password, param, & variables are optional
  • “$” works as the netaddress for local files
  • support for local files was added in FileMaker 13.0v2

In order to call the script from javascript, you could do either of the following:

window.location.url = "fmp://netaddress/database?script=scriptname"
var body = document.getElementsByTagName('body')[0];
var a = document.createElement('a');
a.href = 'fmp://netaddress/database?script=scriptname';
a.style.display = 'none';
body.appendChild(a);
a.click();
a.parentNode.removeChild(a);

Although the second example is more complicated, I’ve had problems with the first one causing FileMaker to respond unexpectedly.

A serious limitation of sending data from a Web Viewer to FileMaker is the same IE url character limit that we mentioned above.  Seedcode has a “hacktackular” workaround involving setting the Windows clipboard via javascript, but they also caution that “using the clip board is not ideal,” plus there’s the risk this may fail in a future version of IE or Spartan.  I don’t have any other workaround other than just avoiding that type of data transfer or not relying on Windows.

Chronology

  1. Export web contents as files to the temporary directory.
  2. Load the location of the index file into your Web Viewer’s web address box.
  3. When you have data you want to send to the Web Viewer, first export a jsonp data file to the temporary directory.  The data should be wrapped in a callback function.
  4. Alert the Web Viewer that there is new data available by changing the hash of the url in the Web Viewer (I use a utc timestamp with a prefix so the hash is always something different).
  5. Add an event listener for the “hashchange” window event with a handler function.
  6. The handler function will then inject a script tag into the dom with the source set to the data file.
  7. The callback function in the data file is called with the data sent as an argument.

Gotchas

  • If a Web Viewer calls an fmp:// url while a script is busy, the call is ignored
  • localStorage and web sql not available in the Web Viewer
  • hash cannot begin with a number
  • data is exported as a file to temp directory, which could be a security concern for some

FMAjax

Because of the intricacies of everything involved in the communication between FileMaker and the Web Viewer, I’ve written a javascript library and accompanying FileMaker module to wrap everything up neatly for you.  Check back here later for an article dedicated to using the library.

UPDATE:  See post on FMAjax here

4 thoughts on “Communicating with a Web Viewer in FileMaker

  1. Don’t use the “Set Web Viewer” script step. That will reload the viewer. Instead, use a field or global variable to contain the url, and reference that in the web viewer’s web address field. You can then update the field or global variable at the web viewer will see the change.

  2. Great post, Ryan. One thing that might be helpful to clarify:

    When you update the web viewer’s hash, what FileMaker function are you using to ensure that the web viewer doesn’t actually reload?

Leave a Reply