Local HTML and JavaScript on the iPhone

Way back in the iPhone 1.x days, I made a Metra train schedule optimized for the iPhone and made it available here. Because the schedule was just a small set of static files, I thought it would be more appropriate to save them locally on the iPhone. But the iPhone 1.x had no mechanism for doing that, so the files sat on the server. Earlier this week, I used Files, one of the many transfer-and-view apps for the iPhone, to move the schedule onto my iPhone itself. In doing so, I learned that the iPhone is much more open to scripting than I’d thought.

Let’s start with the train schedule. It comprises 5 files: an HTML file for weekdays, an HTML file for Saturday, an HTML file for Sunday, a CSS file to style the tables, and a PNG for the iPhone icon. On the server, these all sit in the same directory, called metra, and are linked to one another through relative addresses. For example, each of the HTML files has a line like this in its <head>

<link rel="stylesheet" type="text/css" media="all" href="metra.css" /> 

which loads the external CSS stylesheet. After making a copy on my iMac’s hard drive, I used Files to copy the metra directory and its contents to my iPhone.

I then opened the metra directory and tapped the mf.html file to view the weekday schedule.

I confess I was somewhat surprised to see the schedule displayed just as it was when accessed from the server through Mobile Safari. I thought the viewer in Files wouldn’t be able to “find” the external stylesheet. The links to the Saturday and Sunday schedule pages worked, too.

Basically, everything worked the way it would if the local files were on my iMac’s hard disk.

This is great for static HTML, I thought, but what about JavaScript? It turns out JavaScript works, too. I downloaded the Realtime JavaScript Evaluator, simplified it, put the JavaScript into an external file, added some iPhone-specific lines, and transferred the results to my iPhone. It worked too!

The HTML is in a file called js-evaluator.html

 1:  <?xml version="1.0" encoding="UTF-8" ?>
 2:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 3:  <html>
 4:  <head>
 5:    <title>JavaScript Evaluator</title>
 6:    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 7:    <meta name="viewport" content="width = device-width" />
 8:    <style type="text/css">
 9:      h1 {
10:        font-family: Helvetica;
11:        font-size: 24px;
12:      }
13:      textarea {
14:        font-family: Courier;
15:        font-size: 14px;
16:        width: 100%;
17:        height: 100px;
18:      }
19:    </style>
20:    <script language="JavaScript" src="evaluate.js"></script>
22:      <!-- This file and the JavaScript associated with it were adapted from the
23:      Realtime JavaScript Evaluator at
24:      http://www.teria.com/~koseki/memo/javascript/realtime_eval.html -->
25:  </head>
27:  <body>
28:  <h1>JavaScript Evaluator</h1>
30:  <form>
31:    <textarea id="code" rows="10" cols="30" wrap="virtual">Math.sin(Math.PI/4)</textarea>
32:    <br />
33:    <input type="button" value="Eval" onClick="evalOnce();">
34:    <br />
35:    <textarea id="out" rows="10" cols="30" wrap="virtual"></textarea>
36:  </form>
38:  <p>Enter some JavaScript in the top box and tap the Eval button to see the result in the lower box.</p>
40:  </body>
41:  </html>

and the JavaScript is in evaluate.js

 1:  // This file and the HTML associated with it were adapted from the
 2:  // Realtime JavaScript Evaluator at
 3:  // http://www.teria.com/~koseki/memo/javascript/realtime_eval.html
 5:  var buffer = "";
 7:  function evalOnce() {
 8:      try {
 9:          var code = document.getElementById("code").value;
10:          var result = eval(code);
11:          buffer += result;
12:          flush();
13:      } catch (ex) {
14:          buffer = ex;
15:          flush();
16:      }
17:  }
19:  function clear()  {
20:      var out = document.getElementById("out");
21:      out.value = "";
22:  }
24:  function print(str)  {
25:      buffer += str + "\n";
26:  }
28:  function flush()  {
29:      var out = document.getElementById("out");
30:      out.value = buffer;
31:      buffer = "";
32:  }

So now I can write and run little bits of JavaScript on my iPhone. Even better, complicated JavaScript programs, written by real programmers, should also run. And external JavaScript libraries will work as long as the libraries are stored on the iPhone, and the libraries are linked via relative addressing.

So jQuery and Prototype and other JS libraries used to make complicated web apps could also be used to make complicated local apps.

To test a much more complicated system—albeit without any external libraries—I downloaded the TiddlyWiki home page and installed it on my iPhone. Although the layout was clumsy because it wasn’t designed with the iPhone in mind, it worked perfectly. No, I couldn’t make permanent additions to it, but the iPhone could handle all the other JavaScript and CSS tricks that TiddlyWiki uses.

One downside is that the user interface that Olive Tree has chosen for Files gets in the way of interactive HTML pages. The semi-transparent navigation controls at the top and bottom of the screen fade away, like the controls in the DVD Player application. A tap on the screen brings them back.

The no-controls look great for viewing PDFs and other non-interactive documents, because it gives you the whole screen for content. Unfortunately, interactive documents rely on screen taps, so the controls keep appearing and disappearing, blocking not only your view but your ability to tap on links and other page elements near the top and bottom.

So, although I love Files for viewing PDFs, I’m in the market for another transfer-and-view utility for working with HTML files. If you know of one that has permanent controls, shoot me an email and tell me about it.

In summary, I see two uses for this desktop-like behavior:

  1. Long, complicated hypertext document collections stored locally on the iPhone. I can imagine students who put their class notes in, say, VoodooPad, exporting the notes as HTML and putting them on their iPhone for studying on the go.
  2. Simpler HTML documents that contain useful JavaScripts. It wouldn’t be hard to roll your own unit conversion page, for example, with only the units that you actually use in your work. Or any number of special purpose calculators that fit the work you do. No need to learn Objective C.

I’m sure some will look at this and say that writing apps in JavaScript is a step backward. After all, you could do that with iPhone 1.x. It was, in fact, the only way to write apps for 1.x and the iPhone was considered somewhat crippled because of that. But the difference between then and now is that these “web” apps are local to your phone (or iPod Touch) and don’t require a connection to the Internet. While I know constant connection to the ’net is coming, it isn’t here yet. Local files and scripts are still really useful.