Chapter 13

Packaging Apps as Bookmarks: Bookmarklets and Data URLs

WHAT YOU WILL LEARN IN THIS CHAPTER:

  • Discovering what bookmarklets are
  • Creating offline apps using bookmarklets
  • Encoding your images and source code

Because IOS web apps function inside the Safari environment, there are two seemingly obvious restrictions for the web developer: You must live with the built-in capabilities of the Safari on IOS browser; and you need a constant Wi-Fi (or, for iPhone/iPad, 3G) connection in order to run any application.

As you will discover in Chapter 15, you can use HTML5 offline storage capabilities to get around the dependency on a live connection. However, there are also two lesser-known technologies — bookmarklets and data URLs — that can provide similar results. These technologies have actually been around for years, but they have tended to exist on the periphery of mainstream web development. Developers are now reexamining these two technologies to maximize the potential of the IOS web application platform.

Bookmarklets (short for bookmark applets) are mini JavaScript “applets” that can be stored as a bookmark inside Safari. A data URL is a technique for storing an entire web page or application (pages, styles, images, data, and scripts) inside a single URL, which can then be saved as a bookmark inside Safari. This application-in-a-bookmark can then be accessed in offline mode. You probably won’t want to package heavy-duty enterprise-level apps in a bookmarklet, but for smaller apps, it could be something to consider.

WORKING WITH BOOKMARKLETS

A bookmarklet is JavaScript stored as a URL and saved as a bookmark in the browser. It is typically used as a one-click applet that performs a very specific task or performs an action on the current web page. A bookmarklet uses the javascript: protocol followed by script code. For instance, here’s the simplest of examples:

javascript:alert('iPhone')

Because the scripting code for a bookmarklet is housed inside a URL, the script must be condensed into one long string of code. Therefore, to enter multiple statements, separate each line with a semicolon:

javascript:alert('Bookmarklet 1'),alert('Bookmarklet 2')

In this case, I added spaces inside each of the strings. You can either substitute %20 for a blank space or let Safari do the conversion for you.

If the script returns a value, be sure to enclose it inside of void() to ensure that the JavaScript code runs as expected. For example, the following WikiSearch bookmarklet displays a JavaScript prompt dialog box (see Figure 13-1), and then calls a Wikipedia search URL using the user’s value as the search term:

javascript:t=prompt('Search Wikipedia:',getSelection());
  if(t)void(location.href=
  'http://en.wikipedia.org/w/wiki.phtml?search='+escape(t))

Here’s a second example that provides a front-end onto Google’s define service:

javascript:d=
  prompt('Define:',getSelection());
  if(d)void(location.href='http://www.google.com/search?q=define:'+
  escape(d))

Adding a Bookmarklet to Safari on IOS

Bookmarklets are normally added in a standard browser through a drag-and-drop action. However, because that user input is not available on your IOS device, one way to add to your bookmarklet is through the following process:

1. On your main computer, create your bookmarklet script and test it by pasting it into the Address box of Safari.

2. When the functionality works as expected, drag the javascript: URL from your Address box onto your Bookmarks bar in Safari. If you are going to have a set of bookmarklets, you may wish to create a special Bookmarklets folder to store these scripts. Or, if your bookmarklet is contained within the href of an a link, drag the link onto the Bookmarks bar instead.

3. Sync the bookmarks of your IOS device and main computer through iTunes or iCloud.

4. Access the bookmarklet in the Bookmarks inside Safari.

Although this process is convenient enough for you while you are developing your bookmarklet app, you’ll probably want to provide a solution directly on the device itself if you intend to distribute the app to others. Here’s one way to do so:

1. Create a web page and place your bookmarklet code in a Textarea element on the page, making it easy to select and copy to a device’s clipboard.

2. Add a new bookmark by tapping the + button.

3. Go to the bookmark you just saved in Safari’s bookmarks list and click Edit.

4. Remove the default URL that is shown in the Bookmark dialog box.

5. Paste the JavaScript bookmarklet code into the URL box.

6. Edit the name as desired.

7. Click Done to complete.

Exploring How Bookmarklets Can Be Used

Although you can use bookmarklets for these sorts of general purposes, their real usefulness to IOS web app developers is turning JavaScript into a macro language for Safari to extend the functionality of the browser. For example, Safari always opens normal links in the existing window, replacing the existing page. Richard Herrera from doctyper.com wrote a bookmarklet that transforms the links of a page and forces them to open in a new tab. Here is the script, which is tricky to read because it is contained within a one-line, encoded URL:

javascript:(function(){var%20a=document.getElementsByTagName('a'),for(var%20i=0,
j=a.length;i%3Cj;i++){a[i].setAttribute('target','_blank'),var%20img=document.
createElement('img'),img.setAttribute('class',%20'new-window'),img.setAttribute
('src','data:image/gif;base64,'+'R0lGODlhEAAMALMLAL66tBISEjExMdTQyBoaGjs7OyUlJ
WZmZgAAAMzMzP///////wAAAAAAAAAAAAAA'+'ACH5BAEAAAsALAAAAAAQAAwAAAQ/cMlZqr2Tps13
yVJBjOT4gYairqohCTDMsu4iHHgwr7UA/LqdopZS'+'DBBIpGG5lBQH0GgtU9xNJ9XZ1cnsNicRADs
='),img.setAttribute('style','width:16px!important;height:12px!important;
border:none!important;'),a[i].appendChild(img);}})();

When executed, the script adds a target="_blank" attribute to all links on the page and adds a small “new window” icon after the link (see Figure 13-2).

An IOS user can then use this self-contained “applet” on any page in which he wishes to transform the links. Notice that the icon image used in the script is encoded inside of a data URL, so that the script is not dependent on any external files.

Although the entire script needs to be condensed into a single string of commands, Safari is actually smart enough to convert the hard breaks for you when a multiline script is pasted into the URL box. Just make sure each statement is separated by a semicolon. Therefore, the following code, which is much easier to work with and debug, would still execute properly when pasted directly into the URL box:

image
javascript:(
 function(){
        var a=document.getElementsByTagName('a'),
        for(var i=0,j=a.length;i%3Cj;i++) {
               a[i].setAttribute('target','_blank'),
               var img=document.createElement('img'),
               img.setAttribute('class','new-window'),
 img.setAttribute('src','data:image/gif;base64,'+'R0lGODlhEAAMALMLAL66tBISEjExMdTQ
yBoaGjs7OyUlJWZmZgAAAMzMzP///////wAAAAAAAAAAAAAA'+'ACH5BAEAAAsALAAAAAAQAAwAAAQ/cMl
Zqr2Tps13yVJBjOT4gYairqohCTDMsu4iHHgwr7UA/LqdopZS'+'DBBIpGG5lBQH0GgtU9xNJ9XZ1cnsNi
cRADs='),
               img.setAttribute('style','width:16px!important;
               height:12px!important;
               border:none!important;'),
               a[i].appendChild(img);
        }
 })();

Code snippet BookmarkletSample.js

Bookmarklets can be handy developer tools to assist in testing and debugging on IOS. For example, the following bookmarklet, based on a script created at iPhoneWebDev.com, gives you View Source functionality (see Figure 13-3) on the device itself:

image
javascript:
var sourceWindow = window.open("about:blank");
var newDoc = sourceWindow.document;
newDoc.open();
newDoc.write(
"<html><head><title>Source of " + document.location.href +
"</title><meta name="viewport" id="viewport" content="initial-scale=1.0;" +
"user-scalable=0;maximum-scale=0.6667;width=480"/><script>function do_onload()"
+ "{setTimeout(function(){window.scrollTo(0,1);},100);}if(navigator.userAgent.
indexOf" + "("iPhone")!=-1)window.onload=do_onload;</script></head><body>
</body></html>");
newDoc.close();
var pre = newDoc.body.appendChild(newDoc.createElement("pre"));
pre.appendChild(newDoc.createTextNode(document.documentElement.innerHTML));

Code snippet ViewSource.js

STORING AN APPLICATION IN A DATA URL

In addition to JavaScript functionality, you can also store a web page or even a complete application inside a bookmark. The data: protocol enables you to encode an entire page’s content — HTML, CSS, JavaScript, and images — inside a single URL. To be clear, data URLs store the actual contents of the page itself, not just a simple link to a remote page. You can save this data URL as a bookmark. When users access this bookmark in Safari, they can interact with the page whether or not they have Internet access. The implications are significant — you can use data URLs to package certain types of web apps and get around the live Internet connection requirement.

Constraints and Issues with Using Data URLs

Although the potential of data URLs is exciting for the developer, make sure you keep the following constraints and issues in mind before working with them:

  • You can store client-side technologies — such as HTML, CSS, JavaScript, and XML — inside a data URL. However, you cannot package PHP, MySQL, or any server-side applications in a bookmark.
  • Any web application that requires server access for data or application functionality needs to have a way to pack and go: (1) Use client-side JavaScript for application functionality, and (2) package up a snapshot of the data and put it in a form accessible from a client script. However, in most cases in which you need to use cached data, you should use HTML5 offline storage instead (see Chapter 15).
  • The Web application must be entirely self-contained. Therefore, every external resource the application needs, such as images, style sheets, and .js libraries, must be encoded inside the main HTML file.
  • External resources that are referenced multiple times cannot be cached. Therefore, each separate reference must be encoded and embedded in the file.
  • Images must be encoded as Base64, though the conversion increases their size by approximately 33 percent. (Base64 is a process of encoding binary data so it can be represented with normal character set characters. Encoded Base64 data must then be decoded before it can be used.)
  • The maximum size of a data URL in Safari for IOS is technically 128KB, though in actual practice you can work with URLs much larger — at least up to several megabytes. However, performance of the Safari Bookmark manager suffers significantly when large amounts of data are stored inside a bookmark. Therefore, think thin for data URL-based applications.
  • Safari has issues working with complex JavaScript routines embedded in a data URL application.
  • If your development computer is a Mac then you should be okay working with data URLs. However, if you are working with the Windows version of Safari and trying to sync the bookmark with Safari on IOS, please note that Safari on Windows has limitations in the size of data it can store inside of a bookmark.

DEVELOPING A DATA URL APP

After examining these constraints, it is clear that the best candidates for data URL apps are those that are relatively small in both scope and overall code base. A tip calculator, for example, is a good example applet because its UI would be simple and its programming logic would be straightforward and would not require accessing complex JavaScript libraries. I walk you through the steps needed to create a data URL application in this section.

After reviewing the constraints and making sure that your application will likely work in an offline mode, begin designing and programming as if it were a normal IOS web app. For this sample applet, the interface of the tip calculator is based on a subset of a legacy version of the iUI framework. (To limit the size of the app, I am not including any references to the framework itself.)

The following Try It Out shows how to create a data URL app.

TRY IT OUT: Creating a Data URL App

For a demonstration of how to create your own data URL app, follow these steps.

1. Create the following HTML document in your text editor and then save the document as BIDHJ-Ch13-Ex1.html.

image
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<title>Tipster</title>
<meta name="viewport" content="width=320; initial-scale=1.0;
   maximum-scale=1.0; user-scalable=0;"/>
</head>
<body>
<div class="toolbar">
<h1 id="pageTitle">The Tipster</h1>
<a id="backButton" class="button" href="#"></a>
</div>
<div id="main" title="Tipster" class="panel" selected="true">
<h2 class="tip">Let the Tipster ease your pain and calculate the tip for you.</h2>
<fieldset>
<div class="row">
<label>Bill amount:</label>
<input type="text" id="fldBillTotal" value="20.00" tabindex="1"
   onfocus="clearTotal(this)" onchange="checkTotal(this)"/>
</div>
<div class="row">
<label>Rating:</label>
<select id="fldTipPercent" onchange="getRec()"
tabindex="2">
<option value="0">(Rate service)</option>
<option value="10">Very poor</option>
<option value="12.5">Poor</option>
<option value="15">
   Just as expected</option>
<option value="17.5">
   Above average</option>
<option value="20">Exceptional</option>
<option value="25">Wow!</option>
</select>
</div>
</fieldset>
<fieldset>
<div class="row">
<label>Tip: </label>
<input type="text" id="fldTipRec" value="0.00" readonly="true" disabled="true"/>
</div>
<div class="row">
<label>Final total:</label>
<input type="text" id="fldFinalTotal" value="0.00" readonly="true"
   disabled="true"/>
</div>
</fieldset>
</div>
</body>
</html>
</html>

Code snippet BIDHJ-Ch13-Ex1.html

2. Add the following JavaScript code in the page’s header:

image
<script type="application/x-javascript">
 
addEventListener('load', function()
{
  setTimeout(function() {
       window.scrollTo(0, 1);
  }, 100);
 }, false);
 
function checkTotal(fld)
{
     var x=fld.value;
     var n=/(^d+$)|(^d+.d+$)/;
     if (n.test(x))
     {
          if (fldTipPercent.selectedIndex != 0) getRec();
     }
     else
     {
          alert('Please enter a valid total')
          clearTotal(fld);
     }
}
 
function clearTotal(fld)
{
  fld.value = '';
}
 
function getRec()
{
    if (fldTipPercent.selectedIndex == 0)
    {
        alert('Please rate the service first.'), return;
    }
    var selPercent = Number( eval( fldTipPercent.
    var billAmount = Number( eval( fldBillTotal.value));
    var tipAmount = (selPercent*billAmount);
    var finalP = tipAmount + billAmount;
    fldTipRec.value = '$' + tipAmount.toFixed(2);
    fldFinalTotal.value = '$' + finalP.toFixed(2);
}
</script>

Code snippet BIDHJ-Ch13-Ex1.html

3. Add the following <style> element into the document header:

image
<style>
body > .toolbar
{
    box-sizing: border-box;
    -moz-box-sizing: border-box;
    border-bottom: 1px solid #2d3642;
    border-top: 1px solid #6d84a2;
    padding: 10px;
    height: 45px;
    background: url(
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAArCAIAAAA2QHWOAAAAGXRFWHRTb2Z0
d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAE1JREFUCNddjDEOgEAQAgn//5qltYWFnb1GB4vdSy4WBAY
StKyb9+O0FJMYyjMyMWCC35lJM71r6vF1P07/lFSfPx6ZxNLcy1HtihzpA/RWcOj0zlDhAAAAAElFTkSuQm
CC"
    ) #6d84a2 repeat-x;
}
body > .panel
{
    box-sizing: border-box;
    padding: 10px;
    background: #c8c8c8
url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAABCAIAAACdaSOZAAAAGXRFWHRT
b2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABdJREFUeNpiPHrmCgMC/GNjYwNSAAEGADdNA3dnzPl
QAAAAAElFTkSuQmCC'),
}
</style>

Code snippet BIDHJ-Ch13-Ex1.html

4. Save your HTML file.

5. Go to the data: URI Kitchen (see Figure 13-4) at http://software.hixie.ch/utilities/cgi/data/data.

6. Upload BIDHJ-Ch13-Ex1.html and then click Generate to create an encoded URL, which is displayed in the URL box of Safari (see Figure 13-5).

7. Drag the URL onto your Bookmarks bar.

8. Sync up with your IOS device and your application is now ready to run in offline mode.

How It Works

When you look at the app itself, the fldBillTotal input field captures the total before the tip. The fldTipPercent select list displays a set of ratings for the service, each corresponding with a percentage value. These two factors are then calculated to generate the output values in the fldTipRec and fldFinalTotal input fields.

Two of the styles reference external images for backgrounds. Therefore, in order to use them, you need to encode these images first. The easiest way to do this is to use an online converter, such as the data: URI Image Encoder (see Figure 13-6) at www.scalora.org/projects/uriencoder. This service performs a Base64 encoding of a local file or a URL. You can then replace the image file reference with the attached encoded string. Now that all external resources are embedded, the application is fully standalone.

The final steps involve getting the self-contained app into a form that is accessible when the browser is offline. In the example, I used the data: URI Kitchen to do the conversion, but there are several ways to automate this process for you:

  • Url2iphone (www.somewhere.com/url2iphone.html): This enables you to convert a URL into a bookmark. The most powerful aspect of this tool is that it looks for images, style sheets, and other files that are referenced and encode these as well.
  • data: URI image encoder (www.scalora.org/projects/uriencoder): This tool is great for encoding images into Base64 format. You can specify a URL or upload a local file.
  • Filemark Maker (http://mac.softpedia.com/get/Utilities/Filemark-Maker.shtml): This is a free Mac-based utility that is oriented toward storing Word, Excel, and PDF documents as data URLs. However, it can also be used for HTML pages.
  • Encoding bookmarklet: Developer David Lindquist developed a handy bookmarklet that grabs the current page’s source, generates a data: URL, and loads the URL. You can then drag the generated URL onto your Bookmarks bar. Here’s the JavaScript code:
    javascript:x=new XMLHttpRequest();x.onreadystatechange=function()
      {if(x.readyState==4)location='data:text/html;charset=utf-
    8;base64,'+btoa(x.responseText)};x.open('GET',location);x.send(''),
  • The following Perl syntax turns HTML into a data URL:
    perl -0777 -e 'use MIME::Base64; $text = <>; $text = encode_base64($text);
      $text =~ s/s+//g; print "data:text/html;charset=utf-8;base64,$text
    ";'
  • In PHP, you could create a function to do the same thing:
    <?php
    function data_url($file)
    {
      $contents = file_get_contents($file);
      $base64   = base64_encode($contents);
      return ('data:text/html;charset=utf-8;base64,' . $base64);
    }
    ?>

Figure 13-7 shows a fully functional tip calculator called The Tipster.

EXERCISES

1. What protocol is used to execute a bookmarklet?

2. What is the use of the data: protocol?

3. How can you reference images in a data URL?

Answers to the Exercises can be found in the Appendix.

• WHAT YOU LEARNED IN THIS CHAPTER

TOPIC KEY CONCEPTS
What is a bookmarklet? A bookmarklet is JavaScript stored as a URL and saved as a bookmark in the browser.
Storing an app in a data URL You can store the contents of a page and its assets inside an encoded data URL.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.46.29