Saturday, May 24, 2008

js2-mode - the second most important Javascript tool

I have been using js2-mode (an Emacs Javascript mode) for a couple of weeks now. I started using it out of curiosity, at the time I gave up on TextMate as all-purpose editor and turned back to Emacs, mainly because Emacs has so much better Erlang support. Now I consider js2-mode as the second most important Javascript tool (nothing can beat Firebug of course, and Firebug 1.2B1 on Firefox 3RC1 really rocks !). It fits perfectly into my work flow, you don't have to learn anything specifically to use it, it shows on-the-fly any syntax errors, global variables (which usually are unintentional), does a great job of line indenting, but because line indenting is often a question of personal style, js2-mode offers several possibilities you can cycle through via TAB. And in case you don't like how js2-mode works out-of-the-box, there are tons of customization options.

The only thing I am missing: I would like to use js2-mode as well as minor mode in HTML, but that doesn't seem to work yet.

Update: After writing this post, I just discovered another recent article about js2-mode with focus on syntax highlighting.

Friday, May 23, 2008

Dojo Widget for in-browser editor CodeMirror

CodeMirror, a very impressive in-browser code editor for Javascript, XML/HTML or CSS (or any language, you just have to plug in a your own parser) made some nice progress in the last months. CodeMirror has no dependency on other frameworks or libraries. If you want to use it in a dojo environment as a dojo compatible widget, here I am gonna share here a little tutorial how to write such a thing:

First download CodeMirror and transform CodeMirror.js (the main file which loads the other files into an iframe), into something like this:

dojo.provide("mystuff.widget.CodeMirror");
dojo.require("dijit._Widget");


dojo.declare("mystuff.widget.CodeMirror", dijit._Widget, {

initialized: false,

// currently supported: 'xml' (HTML), 'js' or 'css'
type: 'xml',

options: {
stylesheet: "",
path: "/static/codemirror/js/",
parserfiles: [],
basefiles: ["codemirror_iframe.js"],
linesPerPass: 15,
passDelay: 200,
continuousScanning: false,
saveFunction: function() {
console.log('save');
},
content: " ",
undoDepth: 20,
undoDelay: 800,
disableSpellcheck: true,
textWrapping: true,
readOnly: false,
width: "100%",
height: "100%",
parserConfig: null
},

postMixInProperties: function() {
this.options.stylesheet = "/static/codemirror/css/" + this.type + "colors.css";
this.options.parserfiles = ["parse" + this.type + ".js"];
},

postCreate: function() {
this.inherited(arguments);
},

startup: function() {
if (dijit._isElementShown(this.domNode.parentNode))
this.initialize();
},

initialize: function() {
if (this.initialized)
return;

frame = document.createElement("IFRAME");
frame.style.border = "0";
frame.style.width = this.options.width;
frame.style.height = this.options.height;
// display: block occasionally suppresses some Firefox bugs, so we
// always add it, redundant as it sounds.
frame.style.display = "block";

this.domNode.appendChild(frame);

// Link back to this object, so that the editor can fetch options
// and add a reference to itself.
frame.CodeMirror = this;
this.win = frame.contentWindow;

var _this = this;
var html = ["<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"" + this.options.stylesheet + "\"/>"];
dojo.forEach(this.options.basefiles.concat(this.options.parserfiles), function(file) {
html.push("<script type=\"text/javascript\" src=\"" + _this.options.path + file + "\"></script>");
});
html.push("</head><body style=\"border-width: 0;\" class=\"editbox\" spellcheck=\"" +
(this.options.disableSpellcheck ? "false" : "true") + "\"></body></html>");

var doc = this.win.document;
doc.open();
doc.write(html.join(""));
doc.close();

this.initialized = true;
},

getCode: function() {
return this.editor.getCode();
},

setCode: function(code) {
this.editor.importCode(code);
},

focus: function() {
this.win.focus();
},

jumpToChar: function(start, end) {
this.editor.jumpToChar(start, end);
this.focus();
},

jumpToLine: function(line) {
this.editor.jumpToLine(line);
this.focus();
},

currentLine: function() {
return this.editor.currentLine();
},

selection: function() {
return this.editor.selectedText();
},

reindent: function() {
this.editor.reindent();
},

replaceSelection: function(text, focus) {
this.editor.replaceSelection(text);
if (focus) this.focus();
},

replaceChars: function(text, start, end) {
this.editor.replaceChars(text, start, end);
},

getSearchCursor: function(string, fromCursor) {
return this.editor.getSearchCursor(string, fromCursor);
}
});
Then your should concatenate and minifiy (I use YUI compressor) all the JS files:

util.js, stringstream.js, select.js, undo.js, editor.js
=> codemirror_iframe.js

Also minify the parsers: parsejavascript.js, tokenizejavascript.js, parsecss.js, parsexml.js

At this point you can embed the widget into a test page:
<div
id="html_editor"
dojoType="mystuff.widget.CodeMirror"
type="xml"
style="height:100%;">
</div>
Now comes the tricky part: the editor does not initialize automatically at page load, because with dojo, chances are very high, you are gonna use this widget inside a container, where the editor is hidden at page load, and that would cause trouble with some browsers. So you need to subscribe to an event which triggers the visibility of the editor, so it can be lazy-initialized at first time it becomes visible. For example to use CodeMirror inside a tab container with a tab with id 'html_editor_tab' I do something like this:
dojo.declare("MyApp", null, {
main_tab_selected: function(page) {
if (page.id == 'html_editor_tab'){
dijit.byId('html_editor').initialize();
}
}
});

var myAppInstance = new MyApp();

dojo.subscribe("main_tabs-selectChild", myAppInstance, "main_tab_selected");
Update: Uhh, where was my brain when I wrote this article, I accidentally "misspelled" CodeMirror with CodePress at several places, including the title, it's corrected now ...

New Erlang web framework by Torbjorn Tornkvist

Tobbe just announced the very first release of a new, very simple Erlang based web framework fully integrated into erlware. It has support for sgte templates. Tobbe also has a collection of other highly interesting web related projects in his git repositry, including domerl, a simple, yaws and JQuery based Comet lib.

Hard to find technical docs about dojo internals

Of course an open source framework has no real untold secrets, but digging into the source code can be time consuming and and without in-deep documentation it's difficult to extract the authors thoughts behind it, so I was pleasantly surprised when I stumbled upon this links about dojo internals (which currently can't be found, or at least not by me, at dojo documentation):