Introduction to Advanced Techniques in JavaScript and jQuery

1 Introduction

1.1 Outline

PluralSight: Advanced Techniques in JavaScript and jQuery

Developers generally build their functions just enough for their immediate needs, without thinking about future development. Later, they tack on additional functionality and often break their current functions.

1.2 Traditional JavaScript Functions

PluralSight: Advanced Techniques in JavaScript and jQuery

Functions are a way of separating blocks of code from one another. The most common way for new developers to define functions is like this:

function thisIsAFunction(andAnArgument){
  //
} 

and later, this:

thisIsAFunction = function(andAnArgument){
  //
}

In both these cases, the function is assigned to a variable in the global scope. This is bad because if any other variable with the same name ends up in the global scope, it may overwrite this one. Whichever one loads last will define its value. This makes debugging very difficult. At the very least, this is how it should be written:

var thisIsAFunction = function(andAnArgument){
  //    
}

By using the var keyword, the function is limited to the scope of its parent. In this example, at least, it’s still ending up in the global scope. Here’s another way to make a function:

var someObject = {
  thisIsAFunction = function(andAnArgument){
   //   
  }
}

It’s within an object. An object is a thing that stores variables and functions. This encapsulates the function and “promotes reusability” – which means that it’s a good idea to, rather than copying the same code multiple times, to write it only once, keep it someplace central, and refer to it from across the app. In this case, the someObject uses the var keyword so that it’s only available within the scope of its parent (which in this case is still window, which makes it global). The author suggests that:

someObject = {
  var thisIsAFunction = function(andAnArgument){
   //   
  }
}

is an even better idea because it’s now a global object, accessible from anywhere. This would be called a namespace where you can fill the object with variables and functions that are encapsulated within this namespace. By placing it in the global namespace, other parts of the app, or other libraries, will have access to what’s inside it. He suggests that I should always use namespaces.

In the app I’m working on, I currently have 32 lines in the global namespace. Let’s see if I can cut those down.

The easiest is this one:

function createAxis(context, startx, starty, endx, endy){
    context.beginPath();
    context.moveTo(startx, starty);
    context.lineTo(endx, endy);
    context.closePath();
    context.stroke();
}

function createBar(context, x, y, width, height){
    context.beginPath();
    context.rect(x,y,width,height);
    context.closePath();
    context.stroke();
    context.fill();
}

function drawChart(){
    var canvas = document.getElementById("barChart");

    if (canvas) {
        var context = canvas.getContext('2d');
        createBarChart(context, visits, 30, 20, (canvas.height-20), 50);
    } else {
        console.log("this didn't work");
    }
}

function createBarChart(context, data, startX, barWidth, chartHeight){

    context.lineWidth = "1.2";
    var startY = 780;

    createAxis(context, startX, startY, startX, 30);  // vertical axis
    createAxis(context, startX, startY, 650, startY);  // horizontal axis

    context.lineWidth = "0.0";
    var maxValue = 0;

    for (var i = 0; i < data.length; i++){

        context.fillStyle = "blue";
        createBar(context, 20 + startX + (i * barWidth), (chartHeight - data[i].pain))
        console.log(data[i].pain)
        context.textAlign = "left";
        context.fillStyle = "black";
        // context.fillText(data[i].indication, 20 + startX + (i * barWidth) + i + (i * 30), chartHeight + 15)

    }
}

Which I’ll turn into this:

BarChart = {
    createAxis: function(context, startx, starty, endx, endy){
        context.beginPath();
        context.moveTo(startx, starty);
        context.lineTo(endx, endy);
        context.closePath();
        context.stroke();
    },

    createBar: function(context, x, y, width, height){
        context.beginPath();
        context.rect(x,y,width,height);
        context.closePath();
        context.stroke();
        context.fill();
    },

    drawChart: function(){
        var canvas = document.getElementById("barChart");

        if (canvas) {
            var context = canvas.getContext('2d');
            createBarChart(context, visits, 30, 20, (canvas.height-20), 50);
        } else {
            console.log("this didn't work");
        }
    },

    createBarChart: function(context, data, startX, barWidth, chartHeight){

        context.lineWidth = "1.2";
        var startY = 780;

        createAxis(context, startX, startY, startX, 30);  // vertical axis
        createAxis(context, startX, startY, 650, startY);  // horizontal axis

        context.lineWidth = "0.0";
        var maxValue = 0;

        for (var i = 0; i < data.length; i++){

            context.fillStyle = "blue";
            createBar(context, 20 + startX + (i * barWidth), (chartHeight - data[i].pain))
            console.log(data[i].pain)
            context.textAlign = "left";
            context.fillStyle = "black";
            // context.fillText(data[i].indication, 20 + startX + (i * barWidth) + i + (i * 30), chartHeight + 15)

        }
    }
}

Now, I have 29 lines in the global namespace. Next, I put all my User things into one. 25 lines. Now I’ve moved everything regarding patient visits into one. 24 lines. Navigation. 20. Some other stuff…13. And then tuck everything I can figure out how to into a single object: 5.

1.3 Draw Version 1

PluralSight: Advanced Techniques in JavaScript and jQuery

Let’s practicing listening skills:

  • Recently launched website
  • Would like to include html5 drawings
    • Want their logo, a square, in upper left corner
    • Silver square with a black border

So to begin with, we add this CSS:

#theCanvas {
  background-image: -moz-radial-gradient(30px 30px, #FFF, #EEF);
}

And then HTML:

<body>
  <canvas id="theCanvas">
    Attention! Your browser does not support the canvas.
  </canvas>
</body>

And now I’d expect a box to show up on the screen, but it doesn’t. So what’s wrong? My guess is that since I’m working in Chrome, and the CSS rule says –moz-, that I need to write the rule a different way. So I look up CSS gradients here and this works:

#theCanvas {
  background-image: -moz-radial-gradient(30px 30px, #FFF, #EEF);
  background-image: radial-gradient(30px 30px, #FFF, #EEF); 
}

Next, the JavaScript for the first version:

// global var, so they chose an odd name that they could quickly check isn't already being used
_KSM = 
  // this is how you'll call the draw function to make something show up on screen
  { draw: function()
   { // Draw something on the canvas
     var canvas = document.getElementById('theCanvas');
     // the HTML displays something to the user if the browser doesn't support canvas, but there's no way for
     // the javascript to know whether or not it does unless we run this check
     if (canvas.getContext)
       // this is just convention, it seems, as he breezes past it and i'm pretty sure there's no other 
       // argument in current use
     { var context = canvas.getContext('2d');
      context.strokeStyle = 'black';
      context.fillStyle = 'silver';
        // left, top, height, width
      context.fillRect(10,10,100,100);
      context.strokeRect(10,10,100,100);
     }
   }
}
// and then don't forget to call the function
_KSM.draw();

This creates the square properly.

1.4 Extending Draw: Two parameters

PluralSight: Advanced Techniques in JavaScript and jQuery

Now the boss wants to add another square, but on the bottom of the page. To make this happen, we add two parameters (left and top), and have the fillRect and strokeRect reference these arguments, which makes it more readable too. First, we change the HTML so that the canvas is the size of the page (640×480)

<body>
  <canvas id="theCanvas" width="640" height="480">
    Attention! Your browser does not support the canvas.
  </canvas>
</body>

Then, the JavaScript:

// global var, so they chose an odd name that they could quickly check isn't already being used
_KSM = 
  // this is how you'll call the draw function to make something show up on screen
  { draw: function(left, top)
   { // Draw something on the canvas
     var canvas = document.getElementById('theCanvas');
     // the HTML displays something to the user if the browser doesn't support canvas, but there's no way for
     // the javascript to know whether or not it does unless we run this check
     if (canvas.getContext)
       // this is just convention, it seems, as he breezes past it and i'm pretty sure there's no other 
       // argument in current use
     { var context = canvas.getContext('2d');
      context.strokeStyle = 'black';
      context.fillStyle = 'silver';
        // left, top, height, width
      context.fillRect(left,top,100,100);
      context.strokeRect(left,top,100,100);
     }
   }
}
// and then don't forget to call the function
// LEGACY CODE
_KSM.draw();

//NEW CODE
_KSM.draw(530,370);

And now the box is showing up at the bottom of the page properly. But it’s not showing up at the top left anymore. The legacy code is broken.

1.5 Draw – Four Parameters

PluralSight: Advanced Techniques in JavaScript and jQuery

A new requirement has come in that we can make rectangles as well as squares. So we add an additional two parameters for height and width, which makes the code even more readable.

// global var, so they chose an odd name that they could quickly check isn't already being used
_KSM = 
  // this is how you'll call the draw function to make something show up on screen
  { draw: function(left, top, width, height)
   { // Draw something on the canvas
     var canvas = document.getElementById('theCanvas');
     // the HTML displays something to the user if the browser doesn't support canvas, but there's no way for
     // the javascript to know whether or not it does unless we run this check
     if (canvas.getContext)
       // this is just convention, it seems, as he breezes past it and i'm pretty sure there's no other 
       // argument in current use
     { var context = canvas.getContext('2d');
      context.strokeStyle = 'black';
      context.fillStyle = 'silver';
        // left, top, height, width
      context.fillRect(left,top,width,height);
      context.strokeRect(left,top,width,height);
     }
   }
}
// and then don't forget to call the function
// LEGACY CODE
_KSM.draw();
_KSM.draw(530,370);

//NEW CODE
_KSM.draw(100,100,440,280)

Now we can make rectangles, however we have two problems:
– All the legacy code is broken
– The new code accepts four parameters, which makes it as unreadable as the first function and we have to keep referring back to the function to remind us what value goes where

1.6 Six Parameters

PluralSight: Advanced Techniques in JavaScript and jQuery

Now a new requirement that we add stroke and fill colors.

// global var, so they chose an odd name that they could quickly check isn't already being used
_KSM = 
  // this is how you'll call the draw function to make something show up on screen
  { draw: function(left, top, width, height, stroke, fill)
   { // Draw something on the canvas
     var canvas = document.getElementById('theCanvas');
     // the HTML displays something to the user if the browser doesn't support canvas, but there's no way for
     // the javascript to know whether or not it does unless we run this check
     if (canvas.getContext)
       // this is just convention, it seems, as he breezes past it and i'm pretty sure there's no other 
       // argument in current use
     { var context = canvas.getContext('2d');
      context.strokeStyle = stroke;
      context.fillStyle = fill;
        // left, top, height, width
      context.fillRect(left,top,width,height);
      context.strokeRect(left,top,width,height);
     }
   }
}
// and then don't forget to call the function
// LEGACY CODE
_KSM.draw();
_KSM.draw(530,370);
_KSM.draw(100,100,440,280)

//NEW CODE
_KSM.draw(120,120,400,240, 'burlywood', 'lemonchiffon')

Now we have the new rectangle inside the old rectangle, but the old one is entirely black. This is because the stroke and fill parameters become undefined, which is interpreted as black. The legacy squares don’t show at all because they need the number values for width and placement.

Now, to fix the legacy code.

1.7 Stubs

PluralSight: Advanced Techniques in JavaScript and jQuery

One method of extending functions without breaking legacy code is through stubs.

“A stub is a proxy function that legacy code calls instead of the latest version of the function. It honors the legacy contract [i.e., number of expected parameters vs received parameters] by accepting the expected number of parameters, maybe none, and calling the latest version of the function with default values. This lets legacy code continue to work while allowing new code to make use of the latest version of the function.”

So, in short, we create a new function to emulate the behavior of the old function? It all seems easier said than done.

  • Create new function with name like drawNew() which accepts all the newest parameters
  • Then create a function for the legacy version that accepts the parameters it expects

In his example, he shows how cumbersome it can be, as each time we create a new draw function we’d have to give it a new function name. In the current situation, we’d have four different drawn functions. The whole point is that old code should not have to be changed so that the old draw() continues to work, while new code will use drawNew().

// global var, so they chose an odd name that they could quickly check isn't already being used
_KSM = 
  // this is how you'll call the draw function to make something show up on screen
  { 
    draw: function(left,top,width,height)
    {
     _KSM.drawNew(left,top,width,height,'black','silver');
    },

    drawNew: function(left, top, width, height, stroke, fill)
    { // Draw something on the canvas
     var canvas = document.getElementById('theCanvas');
     // the HTML displays something to the user if the browser doesn't support canvas, but there's no way for
     // the javascript to know whether or not it does unless we run this check
     if (canvas.getContext)
       // this is just convention, it seems, as he breezes past it and i'm pretty sure there's no other 
       // argument in current use
     { var context = canvas.getContext('2d');
      context.strokeStyle = stroke;
      context.fillStyle = fill;
        // left, top, height, width
      context.fillRect(left,top,width,height);
      context.strokeRect(left,top,width,height);
     }
   }
}
// and then don't forget to call the function
// LEGACY CODE
_KSM.draw();
_KSM.draw(530,370);
_KSM.draw(100,100,440,280)

//NEW CODE
_KSM.drawNew(120,120,400,240, 'burlywood', 'lemonchiffon')

What I didn’t pick up on by descriptions alone is that the new functions for legacy code will translate to the new functions. A stub code should only be a single line of code that calls the new, extended version of the code. Anything else in addition to that and it’s no longer a stub—it’s just part of a “function chain” that becomes difficult to debug or maintain. Stubs are a stopgap measure for quickly extending functions without full testing. They should be temporary.

Issues that may arise:
– developers frequently copy code when writing new code, so they may see a call to a legacy function and copy that—thereby continuing to use legacy code and ignoring the new code. “Adding a stub dramatically increases the need for internal documentation.”
– Stub functions don’t address multiple versions of legacy code, i.e., the two squares that still are broken in our current code

1.8 Default Parameters

PluralSight: Advanced Techniques in JavaScript and jQuery

Instead of using a stub function, we’ll just assign default parameters. So if the function is called without all the parameters included, it’ll just fill in the ones that are undefined.

// global var, so they chose an odd name that they could quickly check isn't already being used
_KSM = 
  // this is how you'll call the draw function to make something show up on screen
  { 
    draw: function(left, top, width, height, stroke, fill)
    { // Draw something on the canvas
     var canvas = document.getElementById('theCanvas');
     // the HTML displays something to the user if the browser doesn't support canvas, but there's no way for
     // the javascript to know whether or not it does unless we run this check
     if (canvas.getContext)
       // this is just convention, it seems, as he breezes past it and i'm pretty sure there's no other 
       // argument in current use
     { var context = canvas.getContext('2d');
      if (typeof fill === 'undefined')
        fill = 'silver'
      if (typeof stroke === 'undefined')
        stroke = 'black'
      context.strokeStyle = stroke;
      context.fillStyle = fill;
        // left, top, height, width
      context.fillRect(left,top,width,height);
      context.strokeRect(left,top,width,height);
     }
   }
}
// and then don't forget to call the function
// LEGACY CODE
_KSM.draw();
_KSM.draw(530,370);
_KSM.draw(100,100,440,280)

//NEW CODE
_KSM.draw(120,120,400,240, 'burlywood', 'lemonchiffon')

Now we have new requirements for another couple squares:

// global var, so they chose an odd name that they could quickly check isn't already being used
_KSM = 
  // this is how you'll call the draw function to make something show up on screen
  { 
    draw: function(left, top, width, height, stroke, fill)
    { // Draw something on the canvas
     var canvas = document.getElementById('theCanvas');
     // the HTML displays something to the user if the browser doesn't support canvas, but there's no way for
     // the javascript to know whether or not it does unless we run this check
     if (canvas.getContext)
       // this is just convention, it seems, as he breezes past it and i'm pretty sure there's no other 
       // argument in current use
     { var context = canvas.getContext('2d');
      if (typeof fill === 'undefined')
        fill = 'silver'
      if (typeof stroke === 'undefined')
        stroke = 'black'
      context.strokeStyle = stroke;
      context.fillStyle = fill;
        // left, top, height, width
      context.fillRect(left,top,width,height);
      context.strokeRect(left,top,width,height);
     }
   }
}
// and then don't forget to call the function
// LEGACY CODE
_KSM.draw();
_KSM.draw(530,370);
_KSM.draw(100,100,440,280)

//NEW CODE
_KSM.draw(120,120,400,240, 'burlywood', 'lemonchiffon')
_KSM.draw(530,10,100,100, 'red', 'teal')
_KSM.draw(10,310,160,160, 'red')

1.9 All Default Values

PluralSight: Advanced Techniques in JavaScript and jQuery

This continues the strategy from above by adding defaults for all parameters.

// global var, so they chose an odd name that they could quickly check isn't already being used
_KSM = 
  // this is how you'll call the draw function to make something show up on screen
  { 
    draw: function(left, top, width, height, stroke, fill)
    { // Draw something on the canvas
     var canvas = document.getElementById('theCanvas');
     // the HTML displays something to the user if the browser doesn't support canvas, but there's no way for
     // the javascript to know whether or not it does unless we run this check
     if (canvas.getContext)
       // this is just convention, it seems, as he breezes past it and i'm pretty sure there's no other 
       // argument in current use
     { var context = canvas.getContext('2d');
      if (typeof left === 'undefined')
         left = 10
      if (typeof top === 'undefined')
        top = 10
      if (typeof height === 'undefined')
        height = 100
      if (typeof width === 'undefined')
        width = 100
      if (typeof fill === 'undefined')
        fill = 'silver'
      if (typeof stroke === 'undefined')
        stroke = 'black'
      context.strokeStyle = stroke;
      context.fillStyle = fill;
        // left, top, height, width
      context.fillRect(left,top,width,height);
      context.strokeRect(left,top,width,height);
     }
   }
}
// and then don't forget to call the function
// LEGACY CODE
_KSM.draw();
_KSM.draw(530,370);
_KSM.draw(100,100,440,280)

//NEW CODE
_KSM.draw(120,120,400,240, 'burlywood', 'lemonchiffon')
_KSM.draw(530,10,100,100, 'red', 'teal')
_KSM.draw(10,310,160,160, 'red')

Now the requirements have changed to include a green square at the center top of the page. This requires typing undefined for most of the parameters now that we have defaults.

// global var, so they chose an odd name that they could quickly check isn't already being used
_KSM = 
  // this is how you'll call the draw function to make something show up on screen
  { 
    draw: function(left, top, width, height, stroke, fill)
    { // Draw something on the canvas
     var canvas = document.getElementById('theCanvas');
     // the HTML displays something to the user if the browser doesn't support canvas, but there's no way for
     // the javascript to know whether or not it does unless we run this check
     if (canvas.getContext)
       // this is just convention, it seems, as he breezes past it and i'm pretty sure there's no other 
       // argument in current use
     { var context = canvas.getContext('2d');
      if (typeof left === 'undefined')
         left = 10
      if (typeof top === 'undefined')
        top = 10
      if (typeof height === 'undefined')
        height = 100
      if (typeof width === 'undefined')
        width = 100
      if (typeof fill === 'undefined')
        fill = 'silver'
      if (typeof stroke === 'undefined')
        stroke = 'black'
      context.strokeStyle = stroke;
      context.fillStyle = fill;
        // left, top, height, width
      context.fillRect(left,top,width,height);
      context.strokeRect(left,top,width,height);
     }
   }
}
// and then don't forget to call the function
// LEGACY CODE
_KSM.draw();
_KSM.draw(530,370);
_KSM.draw(100,100,440,280)
_KSM.draw(120,120,400,240, 'burlywood', 'lemonchiffon')
_KSM.draw(530,10,100,100, 'red', 'teal')
_KSM.draw(10,310,160,160, 'red')

// NEW CODE
_KSM.draw(270,undefined,undefined,undefined,undefined, 'green')

1.10 Changing to an Object Parameter

PluralSight: Advanced Techniques in JavaScript and jQuery

Because so many parameters makes the code unreadable, how about making an object and passing that in instead?

// global var, so they chose an odd name that they could quickly check isn't already being used
_KSM = 
  // this is how you'll call the draw function to make something show up on screen
  { 
    // instead of a bunch of parameters, we only accept a single parameter: an object
    draw: function(options)
    { // Draw something on the canvas
     var canvas = document.getElementById('theCanvas');
     // the HTML displays something to the user if the browser doesn't support canvas, but there's no way for
     // the javascript to know whether or not it does unless we run this check
     if (canvas.getContext)
       // this is just convention, it seems, as he breezes past it and i'm pretty sure there's no other 
       // argument in current use
     { var context = canvas.getContext('2d');
      // if options isn't an object, make it one. which means that anything passed in will be lost unless
      // it's an object.
      if (typeof options !== 'object')
        options = {}
      if (typeof options.left === 'undefined')
         options.left = 10
      if (typeof options.top === 'undefined')
        options.top = 10
      if (typeof options.height === 'undefined')
        options.height = 100
      if (typeof options.width === 'undefined')
        options.width = 100
      if (typeof options.fill === 'undefined')
        options.fill = 'silver'
      if (typeof options.stroke === 'undefined')
        options.stroke = 'black'
      context.strokeStyle = options.stroke;
      context.fillStyle = options.fill;
        // left, top, height, width
      context.fillRect(options.left,options.top,options.width,options.height);
      context.strokeRect(options.left,options.top,options.width,options.height);
     }
   }
}
// and then don't forget to call the function
// LEGACY CODE
_KSM.draw();
_KSM.draw(530,370);
_KSM.draw(100,100,440,280)
_KSM.draw(120,120,400,240, 'burlywood', 'lemonchiffon')
_KSM.draw(530,10,100,100, 'red', 'teal')
_KSM.draw(10,310,160,160, 'red')
_KSM.draw(270,undefined,undefined,undefined,undefined, 'green')

// LATEST
_KSM.draw({stroke: 'red', left:140, top:450, height:20, width:360});

This works well, but now the issue is that the legacy code is still broken. Since the legacy code doesn’t pass in objects, each time it runs it’s creating an object using only our default values. So the square at the top left is actually a stack of these squares.

1.11 Legacy Support With Object

PluralSight: Advanced Techniques in JavaScript and jQuery

Since the legacy code may pass in up to six parameters, we need to make sure the new code can handle these. But also it should be able to handle the object parameter, which is the new way of doing things.

// global var, so they chose an odd name that they could quickly check isn't already being used
_KSM = 
  // this is how you'll call the draw function to make something show up on screen
  { 
    // accept the legacy parameters
    draw: function(left, top, width, height, stroke, fill)
    { // Draw something on the canvas
     var canvas = document.getElementById('theCanvas'),
         // create the empty options object locally
         options = {}
     // the HTML displays something to the user if the browser doesn't support canvas, but there's no way for
     // the javascript to know whether or not it does unless we run this check
     if (canvas.getContext)
       // this is just convention, it seems, as he breezes past it and i'm pretty sure there's no other 
       // argument in current use
     { var context = canvas.getContext('2d');

      // if the first argument is an object, then use it
      // otherwise, fill in the new object's properties using the parameters
      if (typeof left !== 'undefined')
        if (typeof left === 'object')
          options = left;
        else
          options = {
            left: left,
            top: top,
            width: width,
            height: height,
            stroke: stroke,
            fill: fill
          }

      // if options isn't an object, make it one. which means that anything passed in will be lost unless
      // it's an object.
      if (typeof options !== 'object')
        options = {}
      if (typeof options.left === 'undefined')
         options.left = 10
      if (typeof options.top === 'undefined')
        options.top = 10
      if (typeof options.height === 'undefined')
        options.height = 100
      if (typeof options.width === 'undefined')
        options.width = 100
      if (typeof options.fill === 'undefined')
        options.fill = 'silver'
      if (typeof options.stroke === 'undefined')
        options.stroke = 'black'
      context.strokeStyle = options.stroke;
      context.fillStyle = options.fill;
        // left, top, height, width
      context.fillRect(options.left,options.top,options.width,options.height);
      context.strokeRect(options.left,options.top,options.width,options.height);
     }
   }
}
// and then don't forget to call the function
// LEGACY CODE
_KSM.draw();
_KSM.draw(530,370);
_KSM.draw(100,100,440,280)
_KSM.draw(120,120,400,240, 'burlywood', 'lemonchiffon')
_KSM.draw(530,10,100,100, 'red', 'teal')
_KSM.draw(10,310,160,160, 'red')
_KSM.draw(270,undefined,undefined,undefined,undefined, 'green')

// LATEST
_KSM.draw({stroke: 'red', left:140, top:450, height:20, width:360});

1.12 Default Values In an Object

PluralSight: Advanced Techniques in JavaScript and jQuery

We’d like to add something so that in the future we can change default values so that we don’t have to keep typing the same values in each time if we want, for instance, three boxes that are the same size.

Here’s the new code, with comments explaining what’s going on:

// global var, so they chose an odd name that they could quickly check isn't already being used
_KSM = {
  // this is how you'll call the draw function to make something show up on screen
    // accept the legacy parameters
    draw: function(left, top, width, height, stroke, fill){
    // Draw something on the canvas
     var canvas = document.getElementById('theCanvas'),
         // create the empty options object locally
         options = {};
     // the HTML displays something to the user if the browser doesn't support canvas, but there's no way for
     // the javascript to know whether or not it does unless we run this check
     if (canvas.getContext){
       // this is just convention, it seems, as he breezes past it and i'm pretty sure there's no other 
       // argument in current use
      var context = canvas.getContext('2d');

      // if the first argument is an object, then use it
      // otherwise, fill in the new object's properties using the parameters
      if (typeof left !== 'undefined')
        if (typeof left === 'object')
          options = left;
        else
          options = {
            left: left,
            top: top,
            width: width,
            height: height,
            stroke: stroke,
            fill: fill
          }

      // to clear the canvas prior to each redraw, just set its width. in this case, we're setting it
      // to its current value
      if (options.clear)
        canvas.width = canvas.width

      // And then each of these says that each option should re-use the previous (current) option value
      // if it exists, otherwise use the default (which is a new object we've created)
      options.left = options.left || _KSM.drawOptions.left;
      options.top = options.top || _KSM.drawOptions.top;
      options.width = options.width || _KSM.drawOptions.width;
      options.height = options.height || _KSM.drawOptions.height;
      options.stroke = options.stroke || _KSM.drawOptions.stroke;
      options.fill = options.fill || _KSM.drawOptions.fill;

      if (typeof options.left === 'undefined')
         options.left = 10
      if (typeof options.top === 'undefined')
        options.top = 10
      if (typeof options.height === 'undefined')
        options.height = 100
      if (typeof options.width === 'undefined')
        options.width = 100
      if (typeof options.fill === 'undefined')
        options.fill = 'silver'
      if (typeof options.stroke === 'undefined')
        options.stroke = 'black'
      context.strokeStyle = options.stroke;
      context.fillStyle = options.fill;
        // left, top, height, width
      context.fillRect(options.left,options.top,options.width,options.height);
      context.strokeRect(options.left,options.top,options.width,options.height);
     }
   },
   drawOptions: 
   {  left: 10, 
      top: 10, 
      width: 100, 
      height: 100, 
      stroke: 'black', 
      fill: 'silver', 
      clear: false 
    }
}
// and then don't forget to call the function
// LEGACY CODE
_KSM.draw();
_KSM.draw(530,370);
_KSM.draw(100,100,440,280)
_KSM.draw(120,120,400,240, 'burlywood', 'lemonchiffon')
_KSM.draw(530,10,100,100, 'red', 'teal')
_KSM.draw(10,310,160,160, 'red')
_KSM.draw(270,undefined,undefined,undefined,undefined, 'green')
_KSM.draw({stroke: 'red', left:140, top:450, height:20, width:360});

It doesn’t break the previous code. Everything still shows up the same.

Now, here’s a new test:

<br />_KSM.drawOptions.stroke = '#44F'
_KSM.drawOptions.fill = '#FFE';
_KSM.draw({clear: true});
_KSM.draw(530,370)

What this does is change the default stroke and fill values, clear the current canvas, and draw two boxes. The first one is drawn based only on defaults, and clear: true. The second is drawn to different coordinates.

Next, we comment out the last two lines and move the ones redefining the defaults to be before the rest of the drawing.

// NEW
_KSM.drawOptions.stroke = '#44F'
_KSM.drawOptions.fill = '#FFE';
// LEGACY CODE
_KSM.draw();
_KSM.draw(530,370);
_KSM.draw(100,100,440,280)
_KSM.draw(120,120,400,240, 'burlywood', 'lemonchiffon')
_KSM.draw(530,10,100,100, 'red', 'teal')
_KSM.draw(10,310,160,160, 'red')
_KSM.draw(270,undefined,undefined,undefined,undefined, 'green')
_KSM.draw({stroke: 'red', left:140, top:450, height:20, width:360});


// LATEST


// _KSM.draw({clear: true});
// _KSM.draw(530,370)

And now any of the legacy functions that didn’t set a color are using the new default colors instead of just black.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s