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)
This is what we'll cover in this talk:
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.
Here are the sort of applications this talk will focus on:
Caution Be aware of accessibility.
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
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.
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
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.
When deploying application, want single stream of JavaScript modules, not lots of little files. This is easy.
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.)
(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;
};
jsmod('html',
function(m, div){
div =jQuery(div);
div.text(m.ONE);
});
jsmod('test',
function(m){
m.ONE == 1 || ddt();
});
jsmod(
function(m){
m.ONE = 1;
});
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.
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);
});
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.
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.
from metatest.js.mymod import plus # Succeeds, creates test. plus(2, 2) == 4 # As above. plus(2, '') == '2' plus(2, '2') == '22'
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.
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;
}
};
};
});
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.
});
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.