You are currently browsing the archives for the tag javascript.

jQuery Gestures

2008
21
Dec
jQuery-gestures demo.

jQuery-gestures demo.

I'm still working on the redesign, but from time to time I like to take a break and work on a one night project. Today's project was inspired by this article. Although I loved the idea, I found it lacked several things :

  • Visual feedback : you have no idea of the mouse gesture you've just done;
  • Customization : adding a gesture isn't obvious;
  • Complex gestures : you can't have sequences, such as Up, Left, Up, Right, Down

I rewrote the code, using jQuery, and added those missing features. In order to provide visual feedback, I use a canvas element which is displayed above the content (and can be enabled and disabled) and I simply draw lines on it.

The sequences of gestures are dealt with using a stack on which moves are pushed1, if two identical moves follow each other (such as Right, Right) only the first one is taken into account.

The last part is customization, complex gestures are recorded as simple strings containing the characters U,D,L,R, and each string is associated to a function.

You can play with the demo, download the plugin and look at the example below.

// initialize the engine, inactive by default and set the trace color to red
$.gestures.init({active:false,color:'#ff0000'});

// adds a new gesture : Down
$.gestures.register('D', function() {
  alert('down !');
  });

// a more complex gesture : Down, Left, Up, Right
$.gestures.register('DLUR', function() {
  alert('this is a rectangle, no ?');
  });

// you can log unknown gestures :
$.gestures.error(function(gesture) {
  alert("oops, I don't understand what \""+gesture+"\" means");
});

// useful keyboard tricks :
$(window).keydown(function(e) {
  if ($.gestures.active() && e.which==27) {
    // disable capture when user presses ESC
    $.gestures.disable();
  } else if (!$.gestures.active() && e.which==17) {
    // enable capture when CTRL is pressed
    $.gestures.enable();
  }
});
$(window).keyup(function(e) {
  // disable capture when CTRL is not pressed
  if ($.gestures.active() && e.which==17) {
  $.gestures.disable();
  }
});

I just have FF3 installed on this machine, so I haven't been able to test this on other browsers. I suppose it should work on Opera, Safari and Chrome, and not on IE (unsupported canvas). Anyway, if it doesn't, feel free to hack the code, or leave a comment

  1. A move is an atomic gesture, it can be Up, Down, Left or Right []

Namespacing JavaScript

2008
12
May

One of the biggest drawbacks in JS is the lack of proper namespaces. Some have long used objects to avoid global pollution, but I think it's possible to go further. That's how I came with those two functions :

(function (global) {
    bundle = function (name, func) {
      var cur = global;
      var name_arr = name.split('.');
      for (i = 0, ilen = name_arr.length; i != ilen; i++) {
        var n = name_arr[i];
        cur = cur[n] = cur[n] || {};
      }
      if (func) func.call(cur);
    };
    fetch = function(imports, func) {
      var ob = {};
      var inject_bundle = function (name, ob) {
        var name_arr = name.split('.');
        var cur = eval(name_arr[0]);
        for (i = 1, ilen = name_arr.length - 1; i != ilen; i++) {
          var n = name_arr[i];
          cur = cur[n];
        }
        var last = name_arr[name_arr.length - 1];
        if (last == '*') for(k in cur) ob[k] = cur[k];
        else ob[last] = cur[last];
      }
      if (typeof imports == 'string') inject_bundle(imports, ob);
      else for (k in imports) inject_bundle(imports[k], ob);
      return func.call(ob);
    };
  })(this)

bundle is used to create a new namespace, given as a string. If a function is passed as a second argument, then it is called, bounded to the namespace. For example, one could use the following :

bundle('net.friggeri.foo.bar', function () {
    this.bleh = "I'm a turtle !";
  });
bundle('net.friggeri.quux', function () {
    this.a = 32;
    this.b = 52;
  });
print(net.friggeri.foo.bar.bleh);
print(net.friggeri.quux.a * net.frigger.quux.b);

You might think (and you'd be right) that having long names can be a pain (I would never use something like net.friggeri.quux.a). However, using the fetch function, you can populate an object with whatever slots in a given namespace. Then, using with, you can emulate some kind of pythonish import statement. An example might be useful at this point:

fetch([
  'net.friggeri.foo.bar',
  'net.friggeri.quux.*'
],
function () { with(this) {
  print(bar.bleh);
  print(a*b);
}})

The code is pretty ugly: I have to use eval at some point to make sure it still works if called in a nested function and I didn't find a way to avoid using with in the function passed to fetch. Of course it's possible to use a this.bar.bleh syntax, but the whole point of fetching the contents of the bundle is to avoid using such long variable names.

If you wish to expand this, or have any suggestions, you're welcome to comment.

Update : I just realized that it could be simplified a lot with having fetch returning directly the object, allowing something like :

with(fetch([
  'net.friggeri.foo.bar',
  'net.friggeri.quux.*'
]) {
  print(bar.bleh);
  print(a*b);
}

This is a very good example where having an all functional approach might not be the path to follow. ;)

JavaScript Shell Scripting

2008
08
May

A few days ago, Peter Michaux posted a very promising article concerning xjs. I've long wanted to be able to use JS to write shell scripts (as one would do using bash, python or perl) but it lacked several important features.

Peter has added a File module which wraps Java file operations, and I see this as the first step towards taking JS out of web development. Let's face it, JS is really present in browsers, SSJS is taking off very slowly and even though a few apps use JS for scripting, it's still not mainstream.

The way I dream of JavaScript's future ? As a general purpose scripting language. This means that several things have to be done. First, an easy way of binding whatever library to JS. Second, a CPAN like repository dedicated to JS. There is absolutely no control on how would evolve the latter. The former, however, can be conceived right now.

I'm not really sure if I agree with Peter's choice of Rhino instead of SpiderMonkey, but I guess he has more experience with Java than with C — which is quite the opposite of my own misconceptions. I guess there are pros and cons for each of the choices, but I wonder if it'll be easy to port generic C libraries to JS using Rhino. I must clarify something at this point : I have a deep hatred of Java — it's a kind of zealous religion — and everything that comes near it appears like obscure magic to me.

Nevertheless, I really wish a lot of good things to xjs, and I hope that Peter will take into account the fact that an easy mechanism for porting library is a must-have.

VI in JavaScript

2008
06
May

Completely useless but nevertheless awesome : jsvi is an implementation of Vi in Javascript which support several useful features, among those visual lines are really appreciated. I'm not quite sure how this may be used in everyday life, but I'm sure it could be adapted to replace textareas (without filling the whole page), allowing us to use more efficient text inputs than all the cluttered What You Get Is All But What You Want stuff we find nowadays.

One huge drawback : the :emacs command, why is this even implemented ?

A cool addition would be an implementation of auto completion, like the one I developed in Jim.

Emulating catchall, getter and setter in JS

2008
02
May

Getters and Setters are cool stuff, so is the notion of catchall. And, if it's a nice feature, it has to be implemented or emulated. I don't have nothing much to say about this except that I did it and that it works. The syntax isn't really nice, but it's usable. I used the same examples used here.

function CGSobject (catchall) {
  var o = function (k,v) {
    if (v) {
      var curv = o.content[k];
      (curv && curv.set && curv.set.call(o.content,v)) || (o.content[k] = v);
    } else {
      var v = o.content[k] || o.catchall(k);
      return (v.get && v.get.call(o.content)) || v;
    }
  }
  o.content = {};
  o.catchall = catchall || function () { return null };
  return o;
}

var o = CGSobject(function (x) { return x+1; });

// basic set
o("a", 7);

// basic get
print(o("a"));
// -> 7

// getter
o("b", {get: function () { return this.a+1;}});
print(o("b"));
// -> 8

// setter
o("c", {set: function (x) { this.a = x / 2 }});
o("c", 50);
print(o("a"));
// -> 25

// catchall
print(o(2));
// -> 3
print(o("foo "));
// -> "foo 1"