{"rowid": 182, "title": "Breaking Out The Edges of The Browser", "contents": "HTML5 contains more than just the new entities for a more meaningful document, it also contains an arsenal of JavaScript APIs. So many in fact, that some APIs have outgrown the HTML5 spec\u2019s backyard and have been sent away to grow up all on their own and been given the prestigious honour of being specs in their own right.\n\nSo when I refer to (bendy finger quote) \u201cHTML5\u201d, I mean the HTML5 specification and a handful of other specifications that help us authors build web applications.\n\nExamples of those specs I would include in the umbrella term would be: geolocation, web storage, web databases, web sockets and web workers, to name a few.\n\nFor all you guys and gals, on this special 2009 series of 24 ways, I\u2019m just going to focus on data storage and offline applications: boldly taking your browser where no browser has gone before!\n\nWeb Storage\n\nThe Web Storage API is basically cookies on steroids, a unhealthy dosage of steroids. Cookies are always a pain to work with. First of all you have the problem of setting, changing and deleting them. Typically solved by Googling and blindly relying on PPK\u2019s solution. If that wasn\u2019t enough, there\u2019s the 4Kb limit that some of you have hit when you really don\u2019t want to.\n\nThe Web Storage API gets around all of the hoops you have to jump through with cookies. Storage supports around 5Mb of data per domain (the spec\u2019s recommendation, but it\u2019s open to the browsers to implement anything they like) and splits in to two types of storage objects:\n\n\n\tsessionStorage \u2013 available to all pages on that domain while the window remains open\n\tlocalStorage \u2013 available on the domain until manually removed\n\n\nSupport\n\nIgnoring beta browsers for our support list, below is a list of the major browsers and their support for the Web Storage API:\n\n\n\tLatest: Internet Explorer, Firefox, Safari (desktop & mobile/iPhone)\n\tPartial: Google Chrome (only supports localStorage)\n\tNot supported: Opera (as of 10.10)\n\n\nUsage\n\nBoth sessionStorage and localStorage support the same interface for accessing their contents, so for these examples I\u2019ll use localStorage.\n\nThe storage interface includes the following methods:\n\n\n\tsetItem(key, value)\n\tgetItem(key)\n\tkey(index)\n\tremoveItem(key)\n\tclear()\n\n\nIn the simple example below, we\u2019ll use setItem and getItem to store and retrieve data:\n\nlocalStorage.setItem('name', 'Remy');\nalert( localStorage.getItem('name') );\n\nUsing alert boxes can be a pretty lame way of debugging. Conveniently Safari (and Chrome) include database tab in their debugging tools (cmd+alt+i), so you can get a visual handle on the state of your data:\n\n Viewing localStorage\n\nAs far as I know only Safari has this view on stored data natively in the browser. There may be a Firefox plugin (but I\u2019ve not found it yet!) and IE\u2026 well that\u2019s just IE.\n\nEven though we\u2019ve used setItem and getItem, there\u2019s also a few other ways you can set and access the data.\n\nIn the example below, we\u2019re accessing the stored value directly using an expando and equally, you can also set values this way:\n\nlocalStorage.name = \"Remy\";\nalert( localStorage.name ); // shows \"Remy\"\n\nThe Web Storage API also has a key method, which is zero based, and returns the key in which data has been stored. This should also be in the same order that you set the keys, for example:\n\nalert( localStorage.getItem(localStorage.key(0)) ); \n// shows \"Remy\"\n\nI mention the key() method because it\u2019s not an unlikely name for a stored value. This can cause serious problems though.\n\nWhen selecting the names for your keys, you need to be sure you don\u2019t take one of the method names that are already on the storage object, like key, clear, etc. As there are no warnings when you try to overwrite the methods, it means when you come to access the key() method, the call breaks as key is a string value and not a function.\n\nYou can try this yourself by creating a new stored value using localStorage.key = \"foo\" and you\u2019ll see that the Safari debugger breaks because it relies on the key() method to enumerate each of the stored values.\n\nUsage Notes\n\nCurrently all browsers only support storing strings. This also means if you store a numeric, it will get converted to a string:\n\nlocalStorage.setItem('count', 31);\nalert(typeof localStorage.getItem('count')); \n// shows \"string\"\n\nThis also means you can\u2019t store more complicated objects natively with the storage objects. To get around this, you can use Douglas Crockford\u2019s JSON parser (though Firefox 3.5 has JSON parsing support baked in to the browser \u2013 yay!) json2.js to convert the object to a stringified JSON object:\n\nvar person = {\n\tname: 'Remy',\n\theight: 'short',\n\tlocation: 'Brighton, UK'\n};\nlocalStorage.setItem('person', JSON.stringify(person));\nalert( JSON.parse(localStorage.getItem('person')).name ); \n// shows \"Remy\"\n\nAlternatives\n\nThere are a few solutions out there that provide storage solutions that detect the Web Storage API, and if it\u2019s not available, fall back to different technologies (for instance, using a flash object to store data). One comprehensive version of this is Dojo\u2019s storage library. I\u2019m personally more of a fan of libraries that plug missing functionality under the same namespace, just as Crockford\u2019s JSON parser does (above).\n\nFor those interested it what that might look like, I\u2019ve mocked together a simple implementation of sessionStorage. Note that it\u2019s incomplete (because it\u2019s missing the key method), and it could be refactored to not using the JSON stringify (but you would need to ensure that the values were properly and safely encoded):\n\n// requires json2.js for all browsers other than Firefox 3.5\nif (!window.sessionStorage && JSON) {\n\twindow.sessionStorage = (function () {\n\t\t// window.top.name ensures top level, and supports around 2Mb\n\t\tvar data = window.top.name ? JSON.parse(window.top.name) : {};\n\t\treturn { \n\t\t\tsetItem: function (key, value) {\n\t\t\t\tdata[key] = value+\"\"; // force to string\n\t\t\t\twindow.top.name = JSON.stringify(data);\n\t\t\t},\n\t\t\tremoveItem: function (key) {\n\t\t\t\tdelete data[key];\n\t\t\t\twindow.top.name = JSON.stringify(data); \n\t\t\t},\n\t\t\tgetItem: function (key) {\n\t\t\t\treturn data[key] || null;\n\t\t\t},\n\t\t\tclear: function () {\n\t\t\t\tdata = {};\n\t\t\t\twindow.top.name = '';\n\t\t\t}\n\t\t};\n\t})();\n}\n\nNow that we\u2019ve cracked the cookie jar with our oversized Web Storage API, let\u2019s have a look at how we take our applications offline entirely.\n\nOffline Applications\n\nOffline applications is (still) part of the HTML5 specification. It allows developers to build a web app and have it still function without an internet connection. The app is access via the same URL as it would be if the user were online, but the contents (or what the developer specifies) is served up to the browser from a local cache. From there it\u2019s just an everyday stroll through open web technologies, i.e. you still have access to the Web Storage API and anything else you can do without a web connection.\n\nFor this section, I\u2019ll refer you to a prototype demo I wrote recently of a contrived Rubik\u2019s cube (contrived because it doesn\u2019t work and it only works in Safari because I\u2019m using 3D transforms).\n\n Offline Rubik\u2019s cube\n\nSupport\n\nSupport for offline applications is still fairly limited, but the possibilities of offline applications is pretty exciting, particularly as we\u2019re seeing mobile support and support in applications such as Fluid (and I would expect other render engine wrapping apps).\n\nSupport currently, is as follows:\n\n\n\tLatest: Safari (desktop & mobile/iPhone)\n\tSort of: Firefox\u2021\n\tNot supported: Internet Explorer, Opera, Google Chrome\n\n\n\u2021 Firefox 3.5 was released to include offline support, but in fact has bugs where it doesn\u2019t work properly (certainly on the Mac), Minefield (Firefox beta) has resolved the bug.\n\nUsage\n\nThe status of the application\u2019s cache can be tested from the window.applicationCache object. However, we\u2019ll first look at how to enable your app for offline access.\n\nYou need to create a manifest file, which will tell the browser what to cache, and then we point our web page to that cache:\n\n\n\n\n\nFor the manifest to be properly read by the browser, your server needs to serve the .manifest files as text/manifest by adding the following to your mime.types:\n\ntext/cache-manifest manifest\n\nNext we need to populate our manifest file so the browser can read it:\n\nCACHE MANIFEST\n/demo/rubiks/index.html\n/demo/rubiks/style.css\n/demo/rubiks/jquery.min.js\n/demo/rubiks/rubiks.js\n# version 15\n\nThe first line of the manifest must read CACHE MANIFEST. Then subsequent lines tell the browser what to cache.\n\nThe HTML5 spec recommends that you include the calling web page (in my case index.html), but it\u2019s not required. If I didn\u2019t include index.html, the browser would cache it as part of the offline resources.\n\nThese resources are implicitly under the CACHE namespace (which you can specify any number of times if you want to).\n\nIn addition, there are two further namespaces: NETWORK and FALLBACK.\n\nNETWORK is a whitelist namespace that tells the browser not to cache this resource and always try to request it through the network.\n\nFALLBACK tells the browser that whilst in offline mode, if the resource isn\u2019t available, it should return the fallback resource.\n\nFinally, in my example I\u2019ve included a comment with a version number. This is because once you include a manifest, the only way you can tell the browser to reload the resources is if the manifest contents changes. So I\u2019ve included a version number in the manifest which I can change forcing the browser to reload all of the assets.\n\nHow it works\n\nIf you\u2019re building an app that makes use of the offline cache, I would strongly recommend that you add the manifest last. The browser implementations are very new, so can sometimes get a bit tricky to debug since once the resources are cached, they really stick in the browser.\n\nThese are the steps that happen during a request for an app with a manifest:\n\n\n\tBrowser: sends request for your app.html\n\tServer: serves all associated resources with app.html \u2013 as normal\n\tBrowser: notices that app.html has a manifest, it re-request the assets in the manifest\n\tServer: serves the requested manifest assets (again)\n\tBrowser: window.applicationCache has a status of UPDATEREADY\n\tBrowser: reloads\n\tBrowser: only request manifest file (which doesn\u2019t show on the net requests panel)\n\tServer: responds with 304 Not Modified on the manifest file\n\tBrowser: serves all the cached resources locally\n\n\nWhat might also add confusion to this process, is that the way the browsers work (currently) is if there is a cache already in place, it will use this first over updated resources. So if your manifest has changed, the browser will have already loaded the offline cache, so the user will only see the updated on the next reload. \n\nThis may seem a bit convoluted, but you can also trigger some of this manually through the applicationCache methods which can ease some of this pain.\n\nIf you bind to the online event you can manually try to update the offline cache. If the cache has then updated, swap the updated resources in to the cache and the next time the app loads it will be up to date. You could also prompt your user to reload the app (which is just a refresh) if there\u2019s an update available.\n\nFor example (though this is just pseudo code):\n\naddEvent(applicationCache, 'updateready', function () {\n\tapplicationCache.swapCache();\n\ttellUserToRefresh();\n});\naddEvent(window, 'online', function () {\n\tapplicationCache.update();\n});\n\nBreaking out of the Browser\n\nSo that\u2019s two different technologies that you can use to break out of the traditional browser/web page model and get your apps working in a more application-ny way.\n\nThere\u2019s loads more in the HTML5 and non-HTML5 APIs to play with, so take your Christmas break to check them out!", "year": "2009", "author": "Remy Sharp", "author_slug": "remysharp", "published": "2009-12-02T00:00:00+00:00", "url": "https://24ways.org/2009/breaking-out-the-edges-of-the-browser/", "topic": "code"}