unhosted web apps

freedom from web 2.0's monopoly platforms

2. An unhosted editor

(en Français)

Welcome to the second episode of Unhosted Adventures! If you log out of all websites, and close all desktop applications, putting your browser fullscreen, you will not be able to do many things. In order to get some work done, you will most likely need at least a text editor. So that is the first thing we will build. We will use CodeMirror as the basis. This is a client-side text editor component, developed by Marijn Haverbeke, which can easily be embedded in web apps, whether hosted or unhosted. But since it requires no hosted code, it is especially useful for use in unhosted web apps. The following code allows you to edit javascript, html and markdown right inside your browser:


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>codemirror</title>
    <script 
      src="http://codemirror.net/lib/codemirror.js">
    </script>
    <link rel="stylesheet"
      href="http://codemirror.net/lib/codemirror.css" />

    <script
      src="http://codemirror.net/mode/xml/xml.js">
    </script>
    <script 
      src="http://codemirror.net/mode/javascript/javascript.js">
    </script>
    <script 
      src="http://codemirror.net/mode/css/css.js">
    </script>
    <script 
      src="http://codemirror.net/mode/htmlmixed/htmlmixed.js">
    </script>
    <script 
      src="http://codemirror.net/mode/markdown/markdown.js">
    </script>
  </head>
  <body>
    <div id="editor"></div>
    <div>
      <input type="submit" value="js" 
        onclick="myCodeMirror.setOption('mode', 'javascript');">
      <input type="submit" value="html" 
        onclick="myCodeMirror.setOption('mode', 'htmlmixed');">
      <input type="submit" value="markdown" 
        onclick="myCodeMirror.setOption('mode', 'markdown');">
    </div>
  </body>
  <script>
    var myCodeMirror = CodeMirror(
      document.getElementById('editor'),
      { lineNumbers: true }
    );
  </script>
</html>

Funny fact is, I wrote this example without having an editor. The way I did it was to visit example.com, and then open the web console and type


document.body.innerHTML = ''

to clear the screen. That way I could use the console to add code to the page, and bootstrap myself into my first editor, which I could then save to a file with "Save Page As" from the "File" menu. Once I had it displaying a CodeMirror component, I could start using that to type in, and from there write more editor versions and then other things. But since I already went through that first bootstrap phase, you don't have to and can simply use this data url (in Firefox you may have to click the small "shield" icon to unblock the content). Type some markdown or javascript in there to try out what the different syntax highlightings look like. You can bookmark this data URL, or click 'Save Page As...' from the 'File' menu of your browser. Then you can browse to your device's file system by typing "file:///" into the address bar, and clicking through the directories until you find the file you saved. To view the source code, an easy trick is to replace the first part of the data URL:


data:text/html;charset=utf-8,

with:


data:text/plain;charset=utf-8,

Also fun is opening the web console while viewing the editor (Ctrl-Shift-K, Cmd-Alt-K, Ctrl-Shift-I, or Cmd-Alt-I depending on your OS and browser), and pasting the following line in there:


myCodeMirror.setValue(decodeURIComponent(location.href
  .substring('data:text/html;charset=utf-8,'.length)));

This will decode the data URL and load the editor into itself. Now you can edit your editor. :) To update it, use the "opposite" line, which will take you to the new version you just created:


location.href = 'data:text/html;charset=utf-8,'
  + encodeURIComponent(myCodeMirror.getValue());

Try it, by adding some text at the end of the document body and executing that second line. After that you can execute the first line again to load your modified version of the editor back into itself (use the up-arrow to see your command history in the web console).

The next step is to add some buttons to make loading and saving the files you edit from and to the local filesystem easier. For that, we add the following code into the document body:


<input type="file" onchange="localLoad(this.files);" />
<input type="submit" value="save" 
      onclick="window.open(localSave());">

And we add the following functions to the script tag at the end:


//LOCAL
function localSave() {
  return 'data:text/plain;charset=utf-8,'
    + encodeURIComponent(myCodeMirror.getValue());
}
    
function localLoad(files) {
  if (files.length === 1) {
    document.title = escape(files[0].name);
    var reader = new FileReader();
    reader.onload = function(e) {
      myCodeMirror.setValue(e.target.result);
    };
    reader.readAsText(files[0]);
  }
}

The 'localLoad' code is copied from MDN; you should also check out the html5rocks tutorial, which describes how you can add nice drag-and-drop. But since I use no desktop applications, I usually have Firefox fullscreen and have no other place to drag any items from, so that is useless in my case.

The 'save' button is not ideal. It opens the editor contents in a new window so that you can save it with "Save Page As...", but that dialog then is not prefilled with the file path to which you saved the file the last time, and also it asks you to confirm if you really want to replace the file, and you need to close the extra window again, meaning saving the current file takes five clicks instead of one. But it is the best I could get working. The reason the window.open() call is in the button element and not in a function as it would normally be, is that popup blockers may block that if it is too deep in the call stack.

UPDATE: nowadays, you can use the download attribute to do this in a much nicer way. At the time of writing, that still only existed as a Chrome feature; it arrived in Firefox in spring 2013. Thanks to Felix for pointing this out!

You may have noticed that these examples include some js and css files from http://codemirror.net/ which don't load if you have no network connection. So here is the full code of this tutorial, with all the CodeMirror files copied into it from version 2.34: unhosted editor. So by bookmarking that, or saving it to your filesystem, you will always be able to edit files in your browser, whether online or offline. I am using this editor right now to write this blogpost, and I will use it to write all other apps, scripts and texts that come up in the coming weeks. It is a good idea to save one working version which you don't touch, so that if you break your editor while editing it, you have a way to bootstrap back into your editor-editing world. :)

That's it for this week. We have created the first unhosted web app of this blog series, and we will be using it as the development environment to create all other apps, as well as all server-side scripts in the coming episodes. I hope you liked it. If you did, then please share it with your friends and followers. Next week we'll discuss how to set up your own personal server.

Comments welcome!

Next: Setting up your personal server