TP WebSockets: écriture d'un programme de chat en HTML5

De $1

Version de 11:57, 20 Avr 2024

cette version.

Revenir à liste des archives.

Voir la version actuelle

 

Découverte des web sockets HTML5

Pour ce TP vous aurez besoin d'un micro serveur qui gère les web socketss. On va utiliser NodeJS un serveur web très populaire en ce moment pour les applications multi-participantes HTML5. Il est écrit en Python mais il embarque l'interpréteur JavaScript V8 de Chrome. Le code applicatif côté serveur n'est ni du PHP, ni du Java, mais du ... JavaScript !

Etude d'un programme de chat

Le serveur web NodeJS

NodeJS est un serveur très très minimal par défaut mais on peut l'étendre en lui ajoutant des "modules". 

Exemple de modules que nous allons utiliser:

  • express donne la possibilité de servir des pages HTML avec CSS, JavaScript etc. Par défaut NodeJS ne sait pas faire cela.
  • socket.io : librairie pour les WebSockets. Donne aussi des solutions de fallbacks si le navigateur utilisé ne supporte pas les WebSockets (comet, ajax, flash sockets)
  • nowJS : permet de faire des appels de méthodes distantes en JavaScript, via des WebSockets.

IMPORTANT: les auteurs de NowJS travaillaient dans une startup qui a été racheté et le site nowJS.com est fermé ("I'm no longer maintaining NowJS. I don't have much time to devote to NowJS as I'm working full-time on another startup. I'm sorry to drop the ball on development and bugs! You might want to look at other projects like dnode which are still being actively developed. Darshan") . Des développeurs ont repris le projet open source et ont annoncé qu'ils continuaient à le maintenir. En attendant, vous pouvez consulter la dernière version du site archivée ici : http://web.archive.org/web/201107142...p://nowjs.com/. Il existe d'autres solutions alternatives à nowJS comme : http://www.meteor.com ou 

Installation de NodeJS

Sous windows c'est assez compliqué à cause de certains modules qui nécessitent une recompilation à la volée. Pour ce TP au lieu d'installer NodeJS depuis le site officiel, puis utiliser la commande "npm" (Node Package Manager), qui télécharge les modules un par un, vous allez directement installer les logiciels qui vous sont fournis dans une archive:

  • Pour windows : archive ici (7Mo, idem clé USB) contenant:
    • Visual C++ redistribuable (vcredist.exe), si lors de l'install on vous dit que vous avez déjà une version, annulez l'installation.
    • NodeJS 0.6.8
    • Dezipper ChatNowJs.zip quelque part, on va s'en servir pendant ce TP.

Une fois tout ceci installé, ouvrez une fenêtre DOS et tapez "node --version", cela devrait afficher  "node 0.6.x..."

Test d'une première application NodeJS

  • Créez un fichier test.js file avec ce contenu :
var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(8124, "127.0.0.1");
console.log('Server running at http://127.0.0.1:8124/');

Sauvez le et exécutez la commande suivante :

node test.js


Vous venez juste de lancer le serveur NodeJS qui répond à cet URL : http://localhost:8124

Snap5.jpg

Ouvrez cet URL dans votre navigateur !

Vous devriez avoir :

Snap6.jpg

Vous venez d'exécuter votre première application NodeJS ;-)

Test de l'application de chat

Allez dans le répertoire où vous avez dézippé ChatNowJs.zip.

Exécutez la commande : "node simpleChat_server.js", et ouvrez http://localhost:8080 dans votre navigateur. Ouvrez deux onglets sur cet URL, vous devez avoir un champ de saisie dans chaque page. Tapez quelque chose, cela devrait apparaitre dans l'autre fenêtre (les deux sont en réalité des clients connectés au même serveur de WebSockets ).

Etude de l'application de chat

Deux fichiers tout petits à étudier !

  1. simpleChat_server.js
  2. simpleChat.html

Ouvrez ces fichiers avec un éditeur : ouch que c'est... petit ! Oui, on utilise ici la notion d'objets distribués, le code client qui tourne dans le navigateur appelle du code qui tourne dans le serveur et inversement. Pas de réseau au milieu ! En fait si... mais on fait comme si il n'y en avait pas ! Le module NodeJS qui fait ceci est magique, il s'appelle NowJS !

NowJS définit une "poche magique" appelée "now" : en fait une variable JavaScript que l'on manipule depuis le code qui tourne sur le client, dans le navigateur, et qui contient les variables et méthodes et objets partagés entre le client et le serveur distant.

Le serveur peut utiliser une variable "everyone.now" pour définir et utiliser les variables et méthodes partagées.

Par exemple, côté serveur on définit la fonctionn distributeMessage dans la variable everyone.now Ceci va rendre visible cette fonction dans tous les codes clients tournant dans les navigateurs, à travers la variable now. "everyone.now" signifie : pour toutes les variables now de tous les clients, je rajoute la fonction distributeMessage dedans.

Serveur :

everyone.now.distributeMessage = function(message){
  everyone.now.receiveMessage(this.now.name, message);
};

Appel par un des clients (voir simpleChat.html) :

$("#send-button").click(function(){
    now.distributeMessage($("#text-input").val());
    $("#text-input").val("");
});

Ainsi un clic sur le bouton "send" dans la page exécute now.distributeMessage() qui s'exécute sur le serveur, avec pour paramètre d'apple le contenu du champs de saisie de la page, c'est le texte qu'on veut envoyer par le chat. Le serveur récupère ce paramètre dans la fonction everyone.now.distributeMessage, qui appelle à son tour everyone.now.receiveMessage, une fonction définie.... dans le code JavaScript de la page web des clients ! La partie"everyone" signifie qu'on appelle la méthode chez tous les clients connectés.

Client :

now.receiveMessage = function(name, message){
    $("#messages").append("<br>" + name + ": " + message);
}

Et cette fonction, localisée côté client, va juste afficher dans la page le texte du message reçu !

De la même manière, une variable déclarée côté client comme:

now.name = prompt("What's your name?", "");

Peut être récupérée directement par le serveur à l'aide de this.now :

Serveur :

everyone.now.distributeMessage = function(message){
  everyone.now.receiveMessage(this.now.name, message);
};

A l'aide de ce paradigme, NowJS permet de développer très rapidement des applications collaboratives avec des WebSockets. Remarquez que les objets sont envoyés tels quels, pas en XML ou en JSON ! (Ils sont en réalité transformés en JSON en coulisse).

Etude d'un chat gérant des "salles de chat"

Maintenant, dans l'archive, vous allez tester et étudier l'exemple "multiroomchat" qui se compose de deux fichiers: multiroomchat.html et multiroomchat_server.js.

Ouvrez ces deux fichiers dans un éditeur de texte, et testez en parallèle l'application:

Cet exemple montre les fonctionnalités de gestion de "groupes" ou de "salles de jeux" de NodeJS.

Simple non ?

Maintenant nous passons à la suite du TP, nous allons étudier ce que l'on peut faire avec un canvas...

 

Découverte d'un logiciel de dessin

Télécharger l'archive à étudier

Toutes les sources de ce TP sont dans  cette archive (4Mbytes). Dézippez-la, vous devriez avoir un répertoire "Paint_HTML5_Multi_Tutorial". Et à l'intérieur des sous-répertoires step1, step2, etc... qui correspondent à chaque étape dans ce document.

Ouvrez donc ce répertoire avec l'outil WebStorm, que votre enseignant va vous distribuer (menu file/open directory). Vous devriez donc pouvoir étudier et exécuter facilement les diverses versions du petit programme de dessin sur lequel nous allons travailler.

Step 1 : le paint le plus simple !

Allez dans le répertoire"step 1". Vous pouvez tester cet exemple en ouvrant paint.html dans votre navigateur.

Remarquez ces lignes en tête du fichier :

<!DOCTYPE HTML>
<!-- Note : in HTML5 the DOCTYPE can be memorized by a normal human being -->
<!-- Common mistake : if we put a comment before the doctype element, Internet Explorer will not
     consider the document as a HTML5 document and the canvas tag for example,
     will not be displayed ! -->

Le DOCTYPE est le tout nouveay ultra-simple DOCTYPE de HTML5 DOCTYPE, yeah !

Si vous ecrivez n'importe quoi avant la déclaration du DOCTYPE, IE9 ne considérera plus le fichier comme du HTML5, et cet exemple ne fonctionnera pas ! L'élément<canvas> de la page sera ignoré. Les autres navigateurs cependant tolèrent qu'on puisse avoir des choses avant le DOCTYPE, pas la série des Internet Explorers...  

Ensuite, dans le fichier, on délcare une feuille de style CSS et quelques scripts JavaScript. Oui, dans cette partie du TP on utilise jQuery pour la manipulation du DOM et pour la gestion des événements. HTML5 proposes la méthode document.querySelector(...) qui supporte des sélécteurs du DOM similaires à ceux de jQuery mais c'est plus long à écrire que $(...),  etjQuery utilises querySelector en interne...

<link rel="stylesheet" href="css/paint.css" />

<script src="js/jquery-1.7.2.min.js"></script>
<script src="js/utils.js"></script>
<script src="js/paint.js"></script>

Le fichier paint.js contient le code de la partie "dessiner quand la souris bouge" du programme de paint, alors que utils.js contient juste une fonction utilitaire pour récupérer correctement la position de la souris dans le <canvas>.

Lorsque le document est entièrement chargé (et le DOM prêt), on crée un objet Paint, qui contient la majeure partie des fonctionnalités écrites en JavaScript. Oui, ici on fait du JavaScript objet. On verra qu'en JavaScript, une fonction est un objet, une variable un attribut et une fonction dans une fonction: une méthode !

Le $(document).ready(function() {.....}); est une manière d'exécuter un bout de code uniquement quand le DOM est prêt. C'est une méthode proposée par jQuery pour remplacer les <body onload=...> et autres document.addActionListener("load", init());...

<script type="text/javascript">
// Run when the DOM is ready
$(document).ready(function () {
  // Create the pseudo object which will handle the main canvas
  paint = new PaintObject("canvasMain");
});
</script>

Le paramètre ("canvasMain") est l'Id du tag <canvas> que vous trouverez un peu plus loin dans la page, dans la partie "HTML" :

<canvas id="canvasMain">
    <p>Canvas tag not supported by your browser</p>
</canvas>

Regardons maintenant le code de l'objet PaintObject dans le fichier paint.js ... Tout d'abord on voir qu'on récupère une référence sur le canvas, et aussi sur le contexe associé à ce canvas. Ce contexte contient les méthodes et propriétés pour dessiner dans le canvas.

function PaintObject(canvas) {
    // get handle of the main canvas, as a DOM object, not as a jQuery Object.
    var mainCanvas = $("#"+canvas).get(0);
    var mainContext = mainCanvas.getContext('2d');
    ...

On ajoute aussi un écouteur pour les mouvements souris sur le canvas (là c'est fait avec jQuery) :

// Bind event on the canvas
$("#canvasMain").mousemove(this.mouseMoveListener);

this.mouseListener est une méthode de PaintObject qui dessine une ligne à chaque déplacement souris, depuis la position précédente à la position courante :

// Mouse motion listener for drawing lines
    this.mouseMoveListener = function (event) {
        // we delegate the computation of the mouse position
        // to a utility function (in utils.js) as this is not so trivial
        // we must take into account insets, etc.
        var mousePos = getMousePos(mainCanvas, event);

        // Let's draw some lines that follow the mouse pos
        if (!started) {
            previousMousePos = mousePos;
            started = true;
        } else {
            mainContext.beginPath();
            mainContext.moveTo(previousMousePos.x, previousMousePos.y);
            mainContext.lineTo(mousePos.x, mousePos.y);
            mainContext.closePath();
            // draws as wireframe the drawing orders between beginPath() and closePath()
            mainContext.stroke();

            previousMousePos = mousePos;
        }
    };

Remarquez que pour dessiner une ligne on déclare un chemin avec mainContext.beginPath(), on se place le curseur à une position pour "poser le crayon" (moveTo), on trace une ligne depuis cette position jusqu'à une autre (lineTo), et on ferme le chemin (closePath). Le chemin est dessiné réellement par l'appel de mainContext.stroke(). "stroke" = en mode fil de fer, "fill" = en mode remplissage. Dans notre cas on dessine en fil de fer et l'apparence est définie par la valeur courante de mainContext.strokeStyle, par défaut c'est la couleur noire.

Attention : si vous oubliez de fermer le chemin (pas de closePath), alors à chaque mouvement de la souris on redessine le chemin en entier et ce chemin contiendra toutes les lignes depuis le début ! En effet: mainContext.stroke() dessine tous les ordres de dessin depuis l'appel de  context.beginPath() ... si un beginPath() est appelé alors qu'un chemin a déjà été commencé, il est ignoré.

Remarquez aussi comment nous calculons la taille du canvas en fonction de la taille de la fenêtre du navigateur. On aurait pu le faire à l'aide d'une règle CSS avec des pourcentages sur les propriétés width et height du canvas, mais malheureusement cela "grossit" aussi le contenu du canvas et produisant des gros carrés comme lorsqu'on grossit une image. Ainsi il est préférable d'ajuster la taille du canvas depuis du JavaScript :

// Canvas doesnt scale well with '%' in CSS so we use a little trick.
// We give them the size of one of their parent node which can be scalable.
mainCanvas.height = $("#content")[0].clientHeight;
mainCanvas.width = $("#content")[0].clientWidth;

Step 2 : ajout des événements mouseUp, mouseMove, mouseDown, création d'un objet pour le dessin

Allez dans le dossier "step 2". Vous pouvez tester cet exemple en ouvrant paint.html dans votre navigateur.

Cette fois-ci on ne dessine que lorsque le bouton de la souris est enfoncé.

On a aussi séparé le code qui effectue le dessin du code d'initialisation du PaintObject. Le fichier drawingtools.js contiendra dorénavant les "objets de dessin" comme le crayon, lignes, rectangles, cercles etc... alors que paint.js s'occupe juste de créer les contextes, déclarer les événements etc.

Encore une fois on utilise jQuery pour les événements (paint.js):

// Create the drawing tool
var drawingTool = new pencilDrawingTool(); 

// Bind events on the canvas
$("#canvasMain").mousedown(drawingTool.mouseDownListener);
$("#canvasMain").mousemove(drawingTool.mouseMoveListener);
$("#canvasMain").mouseup(drawingTool.mouseUpListener);

Voici le contenu du fichier drawingtool.js avec simplement in outil qui dessine en mode "crayon". Remarquez les trois écouteurs pour les événements souris:

// Previous position of the mouse
var previousMousePos;

// Drawing tool object
function pencilDrawingTool() {
    this.mouseDownListener = function (event) {
        paint.started = true;
        previousMousePos = getMousePos(paint.getFrontCanvas(), event);
    };

    this.mouseMoveListener = function (event) {
        // we delegate the computation of the mouse position
        // to a utility function as this is not so trivial
        var mousePos = getMousePos(paint.getMainCanvas(), event);

        // Let's draw some lines that follow the mouse pos
        if (paint.started) {
            paint.getMainContext().beginPath();
            paint.getMainContext().moveTo(previousMousePos.x, previousMousePos.y);
            paint.getMainContext().lineTo(mousePos.x, mousePos.y);
            paint.getMainContext().closePath();
            paint.getMainContext().stroke();
        }
        previousMousePos = mousePos;
    };

    this.mouseUpListener = function (event) {
        paint.started = false;
    }
};

Step 3 : ajout d'un second canvas transparent superposé au canvas principal, pour dessiner des "lignes élastiques"

Allez dans le répertoire "step 3" directory. Vous pouvez tester cet exemple en ouvrant paint.html dans votre navigateur.

Cet exemple montre une utilisation très intéressante des canvas: leur utilisation en "overlay" ou si vous préférez, comme des "calques photoshop". En utilisant des propriétés CSS de positionnement relatif et en donnant à la propriété z-index de chaque canvas une valeur différente on peut choisir lequel est devant l'autre. Par défaut les canvas sont transparents. Si on efface une partie d'un canvas à l'aide de la méthode  context.clearRect(x, y, width, height) alors on pourra "voir à travers cette partie".

On va utiliser cet effet pour dessiner des lignes "élastiques" (des lignes qui suivent la souris) dans un canvas transparent, le "front canvas", placé devant le canvas normal (le "main canvas").

Pour animer la ligne qu'on trace quand la souris bouge, on efface le canvas, on dessine la ligne, on efface le canvas, on dessine la ligne à la nouvelle position, etc... 

Ajout d'un canvas au-dessus de l'autre:

Cette opération est réalisée dans paint.js :

// Prepare a second canvas on top of the previous one, kind of second "layer" 
 var frontCanvas = document.createElement('canvas');
 frontCanvas.id = 'canvasFront';

 // Add the temporary canvas as a second child of the mainCanvas parent.
 mainCanvas.parentNode.appendChild(frontCanvas);

// Get the context for drawing in the canvas
var frontContext = frontCanvas.getContext('2d');

this.getFrontCanvas = function () {
        return frontCanvas;
}

this.getFrontContext = function () {
        return frontContext;
}

Ajout d'un outil "ligne" dans drawingtools.js

On déclare un tableau qui va contenir les différents outils (pencil, line, etc) :

var DEFAULT_TOOL = 'pencil';
...
// Create the drawing tool
var drawingTool = new setOfDrawingTools[DEFAULT_TOOL]();

Et on a modifié la gestion des événements souris pour qu'on appelle le bon écouteur sur l'outil courant.

// bind events. We use a function multiplexEvent that will call the proper listeners
// methods of the currentTool.
this.bindMultiplexEvents = function () {
    $("#canvasFront").mousedown(this.multiplexEvents);
    $("#canvasFront").mousemove(this.multiplexEvents);
    $("#canvasFront").mouseup(this.multiplexEvents);
}

// if currentTool is pencil, and event.type is mousemove, will
// call pencil.mousemouve(event), if currentTool is line and
// event.type is mouseup, will call line.mouseup(event) etc.
this.multiplexEvents = function (event) {
    currentTool[event.type](event);
}

...remarquez que dans drawingtools.js, dans l'outil "ligne", on ne dessine que dans le front canvas, et juste avant de dessiner la ligne élastique, on efface le contenu de ce canvas, ce qui donne l'illusion que la ligne est animée et qu'elle suit la souris:

// the Line Drawing Tool Object
setOfDrawingTools.line = function () {
    this.mousedown = function (event) {
        paint.started = true;
        previousMousePos = getMousePos(paint.getFrontCanvas(), event);
    };

    this.mousemove = function (event) {
        var mousePos = getMousePos(paint.getFrontCanvas(), event);
        if (paint.started) {
            // Clear front canvas before drawing the elastic line in a new position
            paint.getFrontContext().clearRect(0, 0, paint.getFrontCanvas().width, 
                                                    paint.getFrontCanvas().height);

            paint.getFrontContext().beginPath();
            paint.getFrontContext().moveTo(previousMousePos.x, previousMousePos.y);
            paint.getFrontContext().lineTo(mousePos.x, mousePos.y);
            paint.getFrontContext().stroke();
            paint.getFrontContext().closePath();
        }
    };

    this.mouseup = function (event) {
        paint.started = false;
    }
};

Dans le fichier paint.html , on a jouté quelques spans pour pouvoir choisir les différents outis dans un menu sur la droite de l'écran:

<div id="drawCommands">
    <h6>Pick a tool</h6>
    <span id="line">line</span>
    <span id="pencil">pencil</span>
</div>

Et dans paint.js on détecte les clicks souris sur ces spans :

// Handle the drawing tools menu. The selected entry value can be 'Pencil',
// 'Line' etc.
this.changeDrawingTool = function () {
    // this.id is the id of the selected menu item
    drawingTool = new setOfDrawingTools[this.id]();
}

// Bind the changeDrawingTool function onClick to every menu items.
$("#drawCommands").find("span").click(this.changeDrawingTool);

Petit problème : comment conserver la ligne dans le dessin final quand on relâche le bouton de la souris ?

Step 4 : dessiner le contenu du front canvas dans le main canvas quand on relâche la souris en mode ligne

Allez dans le répertoire step 4. Vous pouvez tester cet exemple en ouvrant paint.html dans votre navigateur.

Cette fois on va conserver la ligne qu'on est en train de dessiner dans le dessin final, lorsqu'on relâche le bouton de la souris.

On va détecter sur le font canvas l'événement mouseUp et on va juste dessiner le front canvas dans le main canvas. La fonction de dessin d'image permet en effet de prendre comme paramètre une image mais aussi une vidéo ou un canvas !

Dans paint.js cela se passe dans la méthode drawFrontCanvasOnMainCanvas() de PaintObject. On utilise mainContext pour dessiner le front canvas dans le "main canvas", juste après on efface le contenu du front canvas avec le frontContext.

this.drawFrontCanvasOnMainCanvas = function () {
    mainContext.drawImage(frontCanvas, 0, 0);
    // after copying the front canvas content, we clear it !
    frontContext.clearRect(0, 0, frontCanvas.width, frontCanvas.height);
}

Et on appelle cette méthode depuis l'écouteur d'événements mouseUp() dans l'outil  "ligne"  :

// The Line Drawing Tool Object
setOfDrawingTools.line = function () {
    ...
    this.mouseup = function (event) {
        paint.started = false;
        paint.drawFrontCanvasOnMainCanvas();
    }
};

Step 5 : ajout d'outils pour dessiner des rectangles et des cercles

Allez dans le répertoire step 5. Vous pouvez tester cet exemple en ouvrant paint.html dans votre navigateur.

Cette fois-ci, nous avons suivi une méthode similaire à l'ajout de lignes élastiques, mais pour ajouter des outils de dessin de cercles et de rectangles. 

Vous pouvez les étudier dans le fichier drawingtools.js. Regardez en parallèle les sites contenant des tutoriaux sur les canvas (ou le cours) pour voir comment fonctionnent les méthodes de tracé de cercles ou de rectangles.

Remarquez aussi qu'en ayant conçu intelligemment notre petit programme, le framework très simple permet de rajouter facilement des fonctions pour dessiner du texte, des images, etc.

Step 6 : utilisation des styles de dessin, de nouveaux champs input HTML5 (color, range)

Allez dans le répertoire step 6. Vous pouvez tester cet exemple en ouvrant paint.html dans votre navigateur.

Cette fois-ci on a ajouté des nouveaux champs input dans la page, pour choisir la couleur et l'épaisseur du trait. Vous pourrez une fois testé, remplacer les <input class="color"/> par des vrais champs html5 de type color comme indiqué dans les commentaires.

<ul>
  <li>Fill color:
      <!-- if running opera you may try : <input id="fillColor" type="color" 
           value="FFFFFF" /> instead of this jscolor input field -->
      <input id="fillColor" class="color" value="FFFFFF" />
   </li>
   <li>Stroke color:
       <!--  if running opera you may try :<input id="strokeColor" type="color" 
             value="FFFFFF" /> -->
       <input id="strokeColor" class="color" value="000000" />
   </li>
   <li>
       <!-- works only in Chrome and Opera, will display a standard input text in Firefox -->
        Stroke size: <input id="strokeSize" type="range" 
                            min="0" max="20" value="1" step="0.5" 
                            style="position:relative;top:6px;" />
   </li>
   <li>
       Fill shapes : <input id="fillShapes" type="checkbox" checked />
   </li>
</ul>

On a utilisé un <input type="range"... /> pour sélectionner l'épaisseur du trait. 

Pour la selection de couleurs, on a utilisé un "polyfill", un champ <input class="color" /> qui fonctionne avec la librairie jscolor.js . Si vous utilisez un navigateur qui supporte <input type="color" /> il est préférable d'utiliser la version native. Modifiez juste le code et voyez que cela marche toujours. Vous venez de faire la connaissance avec la manière dont certaines librairies de "rétro compatibilité" comme modernizr fonctionnent pour donner à de vieux navigateurs la possibilité de faire fonctionner des pages utilisant des nouveautés de HTML5.

Dans paint.js on gère les valeurs de ces champs qui vont affecter les styles de dessin (dans mainCanvas et dans frontCanvas) :

// Handle the color menus
    mainContext.strokeStyle = frontContext.strokeStyle = "#" + $("#strokeColor").val();
    $("#strokeColor").change(function() {
        mainContext.strokeStyle = frontContext.strokeStyle = "#" + $("#strokeColor").val();
    });

    mainContext.fillStyle = frontContext.fillStyle = "#" + $("#fillColor").val();
    $("#fillColor").change(function() {
        mainContext.fillStyle = frontContext.fillStyle = "#" + $("#fillColor").val();
    });

    // handle the stroke size
    mainContext.lineWidth = frontContext.lineWidth = $("#strokeSize").val();
    $("#strokeSize").change(function() {
        mainContext.lineWidth = frontContext.lineWidth = $("#strokeSize").val();
    });

    var fillShapes = true;
    // handle the check box that specifies if we fill shapes
    //this.fillShapes = $("#fillShapes").attr('checked');
    $("#fillShapes").change(function(){
        fillShapes = $(this).attr("checked");
    });

Ces valeurs sont "globales" et tout dessin fait avec stroke ou fill par les contextes les prendra en compte.