The Karma Project: Code Less, Teach More

February 18, 2010

Introducing the Karma Lesson Template

Filed under: News — Tags: , , , , , , , , — bryanwb @ 8:19 am

I have created a simple template for individual karma lessons. In this post, I will explain how to use the template and what still needs to be implemented. The template still has some rough spots that I would very much appreciate feedback on.

Before reading on, you should download the lesson template directly or use git to clone the repository.

$ git clone git://git.sugarlabs.org/karma-lesson/mainline.git

What the Template Provides

  • Layout that matches the XO’s screen resolution
  • Navigation bar – with links to help text, lesson plan, and teacher’s note
  • Viewer for lesson plan and teacher’s note
  • Scoreboard, timer, Start, Stop, Restart buttons all in the footer
  • Helpers to make your lesson event-driven rather than using a single control loop
  • Basic i18n for strings, but this is still in flux

Most Important Files in the Template

These are the files that will modify to create your lesson

  • index.html – the markup for your lesson
  • js/lesson.js – the code for your lesson
  • css/lesson.css – the layout for your lesson

Meet index.html

index.html

The html markup in index.html is quite simple

<body>
        <div id="kHeader">
        </div>

	<!-- Put the help text inside #kHelp -->
	<div id="kHelp" title="Help Title"> Help text here</div>
	<div id="feedback"></div>
	<!-- #kMain is where the magic happens, the main frame where your lesson
              its stuff -->
	<div id="kMain">
	  <strong id="itWorks">It Works!</strong>
	</div>
	<div id="kFooter">
	</div>
</body>

That is very little html for actual page that is rendered. That is because #kHeader, #feedback, and kFooter are just scaffolding that we use to hang jQuery UI widgets on.

Here is a wireframe of index.html that will help you understand the dimensions and layout

As you can see from the wireframe, the lesson has a default width of 1200px and height of 900px. This gives you 1200px X 760px in #kMain to use for your lesson.

This is to match the dimensions of the XO. We could default to a smaller resolution that matches most desktops, like 1024 x 760 or 800×600 and scale up the lesson on the XO. We don’t do this for two reasons. First, scaling up is cpu-intensive and slow as molasses on the XO and second, scaled up raster images (.png, .jpg) ‘pixelate’ and look like crap. If we only used SVG images throughout the template, this would be a non-issue, as SVG scales up and down without performance cost.

I have created a function in the Karma.scaleWindow that will scale down the entire lesson to 950px X 760px. This leaves you only 950px X 460px. Please don’t develop a Karma lesson at this resolution if you intend it to run on the XO. I created this function to scale down your lesson to run on non-XO’s with the resolution 1024 x 760. With a bit of work you can make sure your lesson works at both 1200px X 900px. By far the easiest way to do this is to only use SVG.

Karma.scaleWindow();   /*scales window to 1024 x 760 if current browser window is narrower than 1150px */

If anyone has a better idea how to handle different screen resolutions while still accommodating the XO’s performance issues, please let me know.

Additional files that may be of special interest

  • start.html
  • kDoc.html
  • lessonPlan.html
  • teachersNote.html

The start.html page is an introductory page to the lesson that provides big obvious links to the Lesson Plan and Teacher’s Note. It also indicates very clearly which grade and academic subject the lesson is for. The Lesson Plan describes how to lead the children through the lesson in a classroom. The Teacher’s Note explains what the purpose of the lesson is and where it fits into the curriculum. index.html also has quick links to the Lesson Plan and Teacher’s Note but they are harder to find. The start page may seem unnecessary to some, but the teachers in Nepal tell us that they like it.

I have created a simple viewer kDoc.html for viewing the lesson plan and teacher’s note.
I cannot understate the importance of the lesson plan and teacher’s note. Figuring out how to use educational software effectively with 30+ energetic kids is not a trivial task.

Widgets

Remember the minimalist markup for kHeader, kFooter, feedback. All three are jQuery UI widgets that have to be attached to HTML <div> elements.

A jQuery UI widget typically consists of a JavaScript file and a CSS file. Here are the widgets and the files that make them up:

  • kHeader — js/ui.kHeader.js and css/ui.kHeader.css
  • feedback — js/ui.feedback.js and css/ui.feedback.css
  • kFooter — js/ui.kFooter.js and css/ui.kFooter.css

See the API documentation online or at docs/index.html in the template

Attaching a widget to a page

var $kFooter = $('#kFooter').kFooter();  //where #kFooter is a <div> element

Using different initialization parameters

var $kFooter = $('#kFooter').kFooter({'winningScore': 6, timer: true, scoreboard: false,
						pauseButton: true, startButton: true});

Notice that II assigned the result of the kFooter method to the variable $kFooter. This variable is cached reference that I can use to call new methods on the kFooter later and it is much faster than using a fresh jQuery selector each time like $('#kFooter').kFooter(/* some method */);. The naming convention for such variables is to prefix them with the ‘$’ character to signify the variable refers to a jQuery object.

All three widgets have their own methods that you can use to interact with them. The kFooter widget, for example, has methods getScore(), inc(), dec(), setScore(), startTimer(), stopTimer(), and more to manage its built-in scoreboard and timer

Calling methods on jQuery UI widgets is bit different. You pass the method name as an argument in order to call it. Here are examples of calling methods on the kFooter widget:

$('#kFooter').kFooter('inc');   //increments the score
//or if u have a reference
$kFooter.kFooter('inc');

// To pass arguments to the method, simply append them as additional arguments
$kFooter.kFooter('inc', 2);  //increment score by 2 rather than the default of 1

Event-Driven User Interface

When you create your first lesson, it is very tempting to use a loop like

while(true){

  if(/* user does X */){
       exit the loop
  }

}

This code, while well-intentioned will cause your browser to display the horrible “Unresponsive script” message. The code also makes the user interface sluggish and generally unresponsive.

You can avoid this terrible message by making your code event-driven. This is a big word for using events and flags to control the behavior of your lesson.

Here is some code that look event-driven but actually is just a modified for loop and should be avoided. It can also cause the dreaded “Unresponsive script” message.

var timerId;

var game = function(){
      /* do stuff */
      timerId = setTimeout(game, 10);
}

if(/* user wants to quit */){
     clearInterval(timerId);
}

Here is a good example of event-driven code, modified code take from ui.kFooter.js

var timerRunning = true;
var runTimer = function(){
	if (timerRunning) === false){
	    return;  /* setting timerRunning to false stops the timer */
       } 
        /* code for manipulating the time displayed */
				      
	timerId = setTimeout(runTimer, 1000);
};

//start the timer
runTimer();

//to stop the timer, set the flag timerRunning to false
timerRunning = true;

Things to Remember:
* Use setTimeout rather than setInterval
* Set flags to stop a background animation or timer rather than clearInterval()

Triggering custom Events and listening for them

If you initialize the kFooter widget with a winning score, it will emit a custom event “kFooterWinGame” event when that score is reached.

var $kFooter = $('#kFooter').kFooter({'winningScore': 6});

//show congratulations message when kFooterWinGame event is emitted
kFooter.bind('kFooterWinGame', 
		function(){
		    $('#congratulationsMessage').show();
		});

The API documentation details the events that each widget emits.

Unresolved Issues

Here are some unresolved issues that I will continue working on and would most appreciated help with.

  • Scaling – scaling and raster graphics (.png, .jpg files) just don’t play well together
  • Icons – jQuery UI icons are all png’s, which means they scale poorly. They are also too small and hard to override
  • Sound plays poorly on chromium on linux — pls up vote this issue in the chromium bug tracker to encourage the chromium guys to fix it.
  • internationalization — still in flux, doesn’t integrate with pootle
  • Firefox 3.6 performance is pretty sluggish on the XO. We really need to figure out how to get chromium running on the XO. I believe it will be at least 2x faster.

Internationalization is still very much in flux – I have created a basic jQuery plugin for handling it but it leaves much to be desired. I just recently discovered google chrome’s i18n mechanism for chrome extensions and I believe that it is far superior. I hope to port it to jQuery, which should take some time. I really think that chrome, jQuery, and Firefox’s jetpack should agree on a common API for i18n. Integration with pootle will be a pain but there more incentive for others to integrate with it if all three platforms used the same tooling.

December 14, 2009

An Introduction to karma.js – Making HTML 5 Easy

Filed under: News — Tags: , , , , — bryanwb @ 10:04 am

This is the first in a series tutorials about karma.js

  1. This article, an Introduction to karma.js
  2. Comparing HTML 5 Canvas and SVG
  3. Digging into Inkscape
  4. JavaScript and SVG

While the Karma Project is a broad initiative to develop education software using openweb technologies, at its core is a relatively simple JavaScript library. The karma.js library has three primary functions

  1. Preload assets such as images, svgs, canvases, audio, and video and do some basic error checking on them
  2. Put those assets into collections where they can easily be referenced
  3. Attach helper methods to the individual references.

What Karma Doesn’t Yet Do

  1. It doesn’t provide animation functions. The current mix of Karma lessons use a hodge-podge of HTML 5 canvas, Raphael.js, and jQuery SVG to provide animation. Raphael.js is perhaps the most fully-featured and most robust of these three methods. I personally find that jQuery SVG is the least mature but the easiest to use. We may later promote a particular tool as the default for animation.
  2. Doesn’t fully support internationalization. You can use the _() GNU gettext function with karma lessons but there is currently no open-source mechanism for internationalizing the strings embedded in html markup. GNU gettext only supports localization of strings in application code. The main goal for version 0.3 of karma.js will be support for localizing inline text.
  3. It doesn’t support video yet

What Karma isn’t

Karma’s name is not a religious reference but to the first two syllables of Rabi Karmacharya’s last name. The logo for Karma means “om” or “everything” in Sanskrit. It is in not the character for karma. I made the logo “om” after Om Prakash Yadav, the amazing graphic designer/artist/manager at OLE Nepal. I

First Things First

Your first step in using karma.js is to make sure you set your document type to HTML 5. karma.js will throw a big fat error if you try to use your good ‘ol html 4.01 or XML declaration.

<!DOCTYPE html>

That’s it! Who would have thought migrating to HTML 5 would be so painless?

Next, include the karma.js file, which you can get here . Also, you should put in a link to your application code. Let’s say for the sake of this article you put your application code in js/lesson.js though karma.js does not require you to use that convention.

 <script type="text/javascript" src="js/karma.js"></script>
<!-- Your application code in lesson.js -->
 <script type="text/javascript" src="js/lesson.js"></script>

While we are at it, let’s add in a css file called lesson.css. Again, karma doesn’t require you to use that convention. In the future, our team here in Nepal will probably put the lesson specific styles in lesson.css and styles for all the lessons we use in Nepal in nepal.css. Iran, Bolivia, Equatorial Guinea, etc. may want to do the same.

    <link rel="stylesheet" type="text/css" href="css/lesson.css" />

Initializing Karma

karma.js only adds the value “Karma” to the global namespace. All of the Karma library lives within the Karma namespace.

The Karma() function initializes the Karma.karma object and its “collections” of images, audio, svg, videos, and canvases. During this initialization step karma.js creates the collections and does basic error-checking on them. For example, Karma( ) will throw errors for invalid properties passed in to it. In essence, you tell the Karma about the assets you want it to know about and Karma will 1) start loading them and 2) throw errors if there are problems accessing those assets. In the future, the Karma() constructor will support loading different locales and a variety of other options.

//lesson.js

 var k = Karma({
	image: [
	    {name: "ball",   file: "ball37px.png"},
	    {name: "balloon", file: "balloon37px.png"},
	    {name: "banana", file: "banana37px.png"},
	    {name: "chilli", file: "chilli.png"},
	    {name: "fish"  , file: "fish64px.png"},
	    {name: "flower", file: "flower37px.png"},
	    {name: "normalChimp", file: "normalChimp_120x125.png"},
	    {name: "happyChimp", file: "happyChimp_120x125.png"},
	    {name: "sadChimp", file: "sadChimp_120x125.png"}],
	audio: [
	    {name: "correct",  file: "correct.ogg"},
	    {name: "incorrect", file: "incorrect.ogg"},
	    {name: "trigger", file: "trigger.ogg"}
	],
         svg :[    
                              {name:'capitals', domId: 'capitals'},
			      {name:'alien', domId: 'alien'},
			      {name:'spaceship', domId: 'spaceship'},
			      {name: 'help', domId: 'helpScreen'},
			      {name:'playAgain', domId:'playAgain'},
			      {name:'helpIcon', domId:'helpIcon'},
			      {name:'quitIcon', domId:'quitIcon'}
	],
         canvas: [
			      {name:"topLt", domId:"topLtCanvas"},
			      {name:"topRt", domId:"topRtCanvas"},
			      {name:"bottomLt", domId:"bottomLtCanvas"},
			      {name:"bottomMd", domId:"bottomMdCanvas"},
			      {name:"bottomRt", domId:"bottomRtCanvas"},
			      {name:"timer", domId:"timerCanvas"},
			      {name:"scorebox", domId:"scoreboxCanvas"},
			      {name:"chimp", domId:"chimpCanvas"}
	]
    });

I include both svg and canvases in this example but in a real application you would likely only use one or the other.

Notice that every asset has the “name” property. You define the name property. It is not tied to any actual characteristic of the asset. Its purpose is to give you a short, easy identifier for the asset. DOM and file ID’s are frequently long, descriptive, and painful to type repeatedly.

You access the asset from each collection like so:

k.image.ball, k.audio.correct, k.svg.alien

You can also iterate through the collections using jQuery’s $.each function. You cannot use JavaScript’s map, forEach, and filter methods because each collection is an object, not an array.

$.each(k.audio, function(audio) { audio.play();});

The Karma() function attaches helper functions specific to each asset type. The helper functions support function chaining. Here are a few examples. For details please see the API documentation. I have not yet done a good job of documenting the helper methods for k.audio or k.canvas

k.audio.correct.stop().play();
k.svg.capitals.getElementById('capitalMontevideo').setProperty('display', 'none');
k.canvas.scorebox.clear().drawImage(k.image.monkey.media, x, y).strokeStyle('#000000').fillStroke(); 

As you might have guessed, this can save a lot of typing. I intend to rapidly add helper functions as I work more with karma.js

I have added helper functions for almost the entire HTML 5 canvas API but not yet documented it.

How does Karma locate your assets?

Well the location of some of these assets will be specified inside your html but others may not be, particularly audio and video. karma.js requires you to put your assets in the following directory structure:

 assets/
           audio/
           image/
           svg/
           video/

If your image chilli.png is in pictures/ instead of assets/image/ , Karma will throw an error. However, this is only true for the assets you tell Karma about in the Karma() constructor. You can locate images that are not passed in to Karma anywhere that you want

Get Ready

Before you proceed with your application, you need to wrap your application code with Karma.ready() much like you would with jQuery’s $(document).ready(function () { …});

// lesson.js
Karma.ready( function () {
      // your code here
});

Karma.ready blocks your program from running until all the assets you passed to the Karma() constructor are fully loaded.

If you are using jQuery, simply wrap your the Karma.ready() within your $(document).ready

// lesson.js
$(document).ready(function () {
    var k = Karma ( { /* your assets */ });
    k.ready( function () {
         // your code here
     });
});

So that is karma.js in a nutshell. Very soon I hope to have a extended tutorial that walks you through an example lesson. If you are curious about the internals of karma.js please checkout the code, run the tests, and play with the demos.

Blog at WordPress.com.