JavaScript for Python Programmers

JavaScript for Python Programmers

Or Where's the standard library?

 

Jonathan Fine

LTS Strategic

The Open University

Milton Keynes

United Kingdom

email: J.Fine at open dot ac dot uk

PyCon UK 2008, Birmingham, UK (11 - 14 September 2008)

Overview

This is what we'll cover in this talk:

Why JavaScript?

Some reasons for using JavaScript:

Not because it's our favourite programming language

 

Caution Using JavaScript can make pages inacessible - visit w3c ARIA (Accessible Rich Internet Applications) to learn about problem and solutions.

Examples

Here are the sort of applications this talk will focus on:

Caution Be aware of accessibility.

Some JavaScript Gotchas

Empty objects aren't (dark matter is visible)

js> myCar = {}               // myCar is now an object.
js> myCar['owner']           // returns 'undefined'
js> myCar['driver']          // returns 'undefined'
js> myCar['constructor']     // returns a function!

Function definitions are hoisted (sometimes)

js> x = f(); x == 3 || ddt() ; function f(){ return 3 };
true

Unstable intermediate values

js> a.b()         // Always returns '1'.
js> c = a.b       // Store intermediate value. 
js> c()           // Always returns 'undefined'.
js> a = {}
js> a.b = function(){ return this.value }
js> a.value = 1

My Google Summer Of Code experience

JF = me (mentor), CH = Christoph Hafemeister (student). Project is autocomplete for TeX commands in a web editor. Part of it went something like this:

Since then, module system much improved. Next, we describe the new version.

Jsmod: modules for JavaScript

Importing modules

Bind module 'foobar' to variable 'foobar' in current namespace. If module 'foobar' not already loaded, first try to load it. If the load fails, throw an exception.

  var foobar = jsmod('foobar');        // JavaScript
  var ONE = jsmod('foobar').ONE;       // JavaScript
  import foobar                        # Python
  from foobar import ONE               # Python

Defining modules

In a suitable location, create 'foobar.js' and in it write:

jsmod(                        // This is standard boilerplate.
function(m){
    // This is the body of the module
    m.ONE = 1;
});  // One last piece of boilerplate.

Making a stream of modules

Making the page load quicker

When deploying application, want single stream of JavaScript modules, not lots of little files. This is easy.

Here's how

First load jsmodmin.js and then:

jsmod._begin('foobar');
jsmod(function(m){
    m.ONE = 1;
});
jsmod._end();

The next slide contains a minimal implementation of jsmodmin.js. Please first think for yourself how you might do this. (There is no one right answer.)

Essence of module stream loader

(Don't fret the details. Point is, its not much code.)

var jsmod = function(obj){
    switch(typeof obj){
    case 'string':
	return jsmod._modules[obj];
    case 'function':
	jsmod._body.push(obj);
    default:
	throw "Can't process: " + typeof obj;
    };
};
jsmod._begin = function(str){
    jsmod._modules[str] = jsmod._module = {};
    jsmod._body = {};
};
jsmod._end = function(){
    var fns = jsmod._body;
    for (var i=0; i < fns.length; i++)
	fns[i](jsmod._module);
    delete jsmod._module;
    delete jsmod._body;
};

Demo and test
as well as module body

Demo code for the module

jsmod('html',
function(m, div){
    div =jQuery(div);
    div.text(m.ONE);
});

Test code for the module

jsmod('test',
function(m){
    m.ONE == 1 || ddt();
});

The module body

jsmod(
function(m){
    m.ONE = 1;
});

Real demo code - mathtran.dom

How to use mathtran.dom.span and div

jsmod('html',
function(m, div){
   
    div = jQuery(div);
    div.append(m.span('x^2'));
    div.append(m.div('\\int_0^1xdx'));
});

Note: The demo code can precede the body code in the module file. The first thing we want to read is how to use the module.

Real demo code - mte.string

In editor wish to preview only the paragraph that contains the cursor. Therefore, need to be able to find portion in the value of the text area.

jsmod('html',      // This is demo code for 'findPiece' function.
function(m, div){
    div = jQuery(div);
    var ta = jQuery('<textarea rows=5 cols=40 />');
    var pre = jQuery('<pre />');
    var showme = function(){
        var val = ta.val();
        var locn = ta.getSelection().start;

        // Now we call the function we are testing.
        var bounds = m.findPiece( m.parPattern, val, locn);
        pre.text(val.substring(bounds[0], bounds[1]));
    };
    ta.keyup(showme);
    div.append(ta)
        .append(pre);
});

Real test code - mte.string

In addition to demo code, we can also place test code in the module file.

The test code can go before the code that it is testing. This helps with test first programming.

jsmod('test',
function(m){

    var str = "aaaaa";
    var locn = 3;
    var bounds = m.findPiece( m.parPattern, str, locn);
    bounds == '0,5' || ddt();    // JavaScript converts when comparing!

});

Note that 'findPiece' is a pure function (has no side effects, same input always produces same output). In addition, both input and output are (almost) JSON objects.

 

We'd like reward the authoring of pure JSON functions by making it much easier to test them.

xfrac - simple testing for simple functions

Play on the name Xunit - proper fractions are smaller than units. In addition, fractions are about breaking (fracture), and testing checks to see if it breaks.

 

Rationale Testing a put JSON function should be as simple as possible.

Here goes: test code for a 'plus' function.

jsmod('test',
function(m){
    xfrac('mymod.plus', [2, 2], 4);  // Here's the test.
});

And here is the implementation:

jsmod(
function(m){
    m.plus = function(a, b){
        return a + b;
    };
});

Xfrac is work-in-progress - currently found as jsfrac in mathtran-javascript.

MetaTest - test authoring framework

Background

from metatest.js.mymod import plus   # Succeeds, creates test.

plus(2, 2) == 4                      # As above.
plus(2, '') == '2'
plus(2, '2') == '22'

Implementation (for experts)

Use PEP 302 - New Import Hooks - to provide stub modules on the fly. These stub modules are not real modules, and provide stub objects on the fly. Now provide new __eq__ and other methods for these stub objects.

Dispatch event handler

There may be several hundred TeX commands to choose from. MathTran editor wants to go into different mode when user is entering a TeX command. Here's some code for changing dispatch based on the mode.

jsmod(
function(m){

    // Returns get, set and handler methods that acccess mode.
    // The handler can be attached to DOM object events.
    m.Dispatch = function(){
        var mode = {};
        return {
            setMode: function(newMode){
                mode = newMode;
            },
            getMode: function(){
                return mode;
            },
            handler: function(event){
                var fn = mode[event.type];
                return fn ? fn.call(mode, event) : undefined;
            }
        };
    };
});

Demo for dispatch event handler

jsmod('html',
function(m, div){

    // Give ourselves a text area to work on.
    var p1 = jQuery('<p>1</p>'), p2 = jQuery('<p>2</p>');
    var ta = jQuery('<textarea />');
    div = jQuery(div);
    div.append(p1).append(p2).append(ta);

    // Create and install a dispatch event handler.
    var dispatch = m.Dispatch();
    ta.keypress(dispatch.handler);

    // A simple mode switcher.  Type '1' to get 'next'.
    var Cycle = function(name, display){
        this.s = '';
	this.name = name;
	this.display = display;
    };

    Cycle.prototype.keypress =  function(event){

	event.charCode = event.which; // To get it to work on IE.
        var chr = String.fromCharCode(event.charCode);
        if (chr == '1'){
            dispatch.setMode(this.next);
        } else {
            this.s += chr;
            this.display.text(this.name + ' ' + this.s);
        }
    };

    // Instantiate and link.
    var mode1 = new Cycle('1', p1);
    var mode2 = new Cycle('2', p2);
    mode1.next = mode2;
    mode2.next = mode1;

    dispatch.setMode(mode1);    // Set the initial mode.
});

Words of advice

JavaScript advice for Python programmers

How you can help

Present situation

Goals

Approach

The key thing is to have people using jsmod as a practical tool for real projects. Only this will give the experience that can inform the development of jsmod. If you'd like to use jsmod in this way, please let me know.