Appendix B. Model Solutions

Table of Contents
Web Programming I, JavaScript
Assignments From Chapter 2
Assignments From Chapter 3
Assignments From Chapter 4
Assignments From Chapter 5
Assignments From the section called “Iterations ”
Assignments From Chapter 7
Assignments From Chapter 8
Assignments From Chapter 9
Assignments From Chapter 10
Assignments From ???
Assignments From Chapter 12
Web Programming II, Node.js
Development Environments
Assignments From Chapter 26
Assignments From Chapter 28
Assignments From Chapter 32
Assignments From Chapter 33
Assignments From Chapter 34
Assignments From Chapter 35
Data Integration
Security

Web Programming I, JavaScript

Assignments From Chapter 2

Solutions from Assignments from Chapter 2

Assignment JS.15

Write a program into a file called js15.js.

The program must accept input of a number from the user. Assume the number is the temperature in Fahrenheit. Your program must convert the temperature to Celsius and print it on the console.

The conversion formula is c = 5/9(f-32) where c is the result in Celsius, and f is the temperature in Fahrenheit.

Model Solution Assignment JS.15
Example B.1. Fahrenheit to Celsius
'use strict';
var f = Number(prompt('Indtast fahrenheit'));
var c = 5 / 9 * (f - 32);
console.log(`${f} \xB0F = ${c} \xB0C`);

Example B.2. Solution Testable as a Webpage
'use strict';
var f = Number(prompt('Indtast fahrenheit'));
var c = 5 / 9 * (f - 32);
document.write(`<p>${f} &deg;F = ${c} &deg;C</p>`);
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>title</title>
    </head>
    <body>
        <h1>js15a.js</h1>
        <script src="./js15a.js"></script>
    </body>
</html>

Click here ;)


Assignment JS.20

Write a program into a file called js201.js.

The program must accept input of a number from the user. Assume the number is a year. Make JavaScript write on the console true or false as to whether the year is a leap year or not. If you don't know what a leap year is, Google it.

Write another program into a file called js202.js.

The program must accept input of a number from the user. Assume the number is a CPR number (a danish social security number). Make JavaScript write on the console true or false as to whether the person holding the number is a woman.

Model Solution Assignment JS.20
Example B.3. Leap Year?
'use strict';
var year = Number(prompt('Enter year'));

var isLeap = (year % 4 == 0 && !(year % 100 == 0)) || year % 400 == 0;

console.log(`Is ${year} a leap year? ${isLeap}`);

Example B.4. Are You a Woman?
'use strict';
var cpr = Number(prompt('Enter cpr'));
console.log(cpr);
console.log(`Womans cpr? ${99999999 < cpr && cpr < 9999999999 && cpr % 2 === 0}`);

Assignment JS.25

In todays lesson, re Example 2.7, there was an example of letting JavaScript print the truth table for a negation on the console.

  • Create a similar example in a file, js25c.js for printing the truth table for the conjunction.
  • Then create another file, js25d.js with the code for printing the truth table for the disjunction.

You will have noticed that a conjunction/disjunction of two questions have four possible outcomes, each condition may be true or false. Two conditions, 2 x 2 possible outcomes.

Three conditions that could each result in true or false thus gives you 2 x 2 x 2 = 8 possible outcomes.

  • Create another file, js25c3.js for printing the truth table for a conjunction of 3 conditions: var1, var2, and var3.
  • Then create yet another file, js25d3.js with the code for printing the truth table for a disjunction with three conditions.

For the latter two, it may be very helpful to visualize by drawing them on paper first.

Model Solution Assignment JS.25
Example B.5. Binary Conjunction and Disjunction
'use strict';
console.log('Truth table for conjunction and disjunction with 2 vars');
console.log('var1\tvar2\tvar1 && var2\tvar1 || var2');
var var1 = true;
var var2 = true;
console.log(var1 + '\t' + var2 + '\t' + (var1 && var2) + '\t\t' + (var1 || var2));
var2 = false;
console.log(var1 + '\t' + var2 + '\t' + (var1 && var2) + '\t\t' + (var1 || var2));
var1 = false;
var2 = true;
console.log(var1 + '\t' + var2 + '\t' + (var1 && var2) + '\t\t' + (var1 || var2));
var2 = false;
console.log(var1 + '\t' + var2 + '\t' + (var1 && var2) + '\t\t' + (var1 || var2));

Example B.6. Ternary Conjunction and Disjunction
'use strict';
document.write('<pre>' +  'Truth table for conjunction and disjunction with 3 vars' + '</pre>');
document.write('<pre>' +  'var1\tvar2\tvar3\tvar1 && var2 && var3\tvar1 || var2 || var3' + '</pre>');
var var1 = true;
var var2 = true;
var var3 = true;
document.write('<pre>' + var1 + '\t' + var2 + '\t' + var3 + '\t' + (var1 && var2 && var3) + '\t\t\t' + (var1 || var2 || var3) + '</pre>');
var3 = false;
document.write('<pre>' + var1 + '\t' + var2 + '\t' + var3 + '\t' + (var1 && var2 && var3) + '\t\t\t' + (var1 || var2 || var3) + '</pre>');
var2 = false;
var3 = true;
document.write('<pre>' + var1 + '\t' + var2 + '\t' + var3 + '\t' + (var1 && var2 && var3) + '\t\t\t' + (var1 || var2 || var3) + '</pre>');
var2 = false;
var3 = false;
document.write('<pre>' + var1 + '\t' + var2 + '\t' + var3 + '\t' + (var1 && var2 && var3) + '\t\t\t' + (var1 || var2 || var3) + '</pre>');
var1 = false;
var2 = true;
var3 = true;
document.write('<pre>' + var1 + '\t' + var2 + '\t' + var3 + '\t' + (var1 && var2 && var3) + '\t\t\t' + (var1 || var2 || var3) + '</pre>');
var3 = false;
document.write('<pre>' + var1 + '\t' + var2 + '\t' + var3 + '\t' + (var1 && var2 && var3) + '\t\t\t' + (var1 || var2 || var3) + '</pre>');
var2 = false;
var3 = true;
document.write('<pre>' + var1 + '\t' + var2 + '\t' + var3 + '\t' + (var1 && var2 && var3) + '\t\t\t' + (var1 || var2 || var3) + '</pre>');
var3 = false;
document.write('<pre>' + var1 + '\t' + var2 + '\t' + var3 + '\t' + (var1 && var2 && var3) + '\t\t\t' + (var1 || var2 || var3) + '</pre>');
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>title</title>
    </head>
    <body>
        <h1>js25c3.js</h1>
        <script src="./js25c3a.js"></script>
    </body>
</html>

Click here ;)


Assignment JS.30

Predict the results of

8 / 2 * (2 + 2)     // ?
8 / 2 * 2 + 2       // ?

Then let JavaScript solve it. Print the result on the console. Hand in as a file called js30.js

Model Solution Assignment JS.30
Example B.7. Operator Precedence
'use strict';
console.log(`8 / 2 * (2 + 2) = ${8 / 2 * (2 + 2)}`);
console.log('remember js processes from left to right with respect to precedence rules');
console.log(`the expression is first revalidated to: 8 / 2 * (2 + 2) => 8 / 2 * 4`);
console.log(`8 / 2 * 4 => 4 * 4 = ${4 * 4}`);

Example B.8. Operator Precedence, Web Version
'use strict';
 document.write(`<pre>8 / 2 * (2 + 2) = ${8 / 2 * (2 + 2)}</pre>`);
 document.write('<p>remember js processes from left to right with respect to precedence rules');
 document.write(`<p>the expression is first revalidated to: <code>8 / 2 * (2 + 2) => 8 / 2 * 4</code></p>`);
 document.write(`<pre>8 / 2 * 4 => 4 * 4 = ${4 * 4}</pre>`);
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>title</title>
    </head>
    <body>
        <h1>js30.js</h1>
        <script src="./js30a.js"></script>
    </body>
</html>
Click here ;)

Assignments From Chapter 3

Solutions from Assignments from Chapter 3

Assignment JS.Conds.0

I gave you a naive model solution to the coin flipping problem. It still resides at the repo:

git clone https://phidip@bitbucket.org/phidip/coinflipping.git

After cloning it, copy it into a file jsConds0.js, then change it into rolling a die 10 times. Let it tally the outcomes, and print the number of ones, twos, ..., sixes when it is done.

Hint: You need more counter variables, as a die has 6 sides. Consequently you also need more conditionals.

Place the code in jsConds0.js

Model Solution Assignment JS.Conds.0
Example B.9. jsConds0.js
'use strict';

const play = function() {
    return Math.floor(Math.random() * 6 + 1);
}

Example B.10. Execution Code
'use strict';

let ones = 0;
let twos = 0;
let threes = 0;
let fours = 0;
let fives = 0;
let sixes = 0;

let r = play();
if (r === 1)
    ones++;
else if (r === 2)
    twos++;
else if (r === 3)
    threes++;
else if (r === 4)
    fours++;
else if (r === 5)
    fives++;
else
    sixes++;

r = play();
if (r === 1)
    ones++;
else if (r === 2)
    twos++;
else if (r === 3)
    threes++;
else if (r === 4)
    fours++;
else if (r === 5)
    fives++;
else
    sixes++;

r = play();
if (r === 1)
    ones++;
else if (r === 2)
    twos++;
else if (r === 3)
    threes++;
else if (r === 4)
    fours++;
else if (r === 5)
    fives++;
else
    sixes++;

r = play();
if (r === 1)
    ones++;
else if (r === 2)
    twos++;
else if (r === 3)
    threes++;
else if (r === 4)
    fours++;
else if (r === 5)
    fives++;
else
    sixes++;

r = play();
if (r === 1)
    ones++;
else if (r === 2)
    twos++;
else if (r === 3)
    threes++;
else if (r === 4)
    fours++;
else if (r === 5)
    fives++;
else
    sixes++;

r = play();
if (r === 1)
    ones++;
else if (r === 2)
    twos++;
else if (r === 3)
    threes++;
else if (r === 4)
    fours++;
else if (r === 5)
    fives++;
else
    sixes++;

r = play();
if (r === 1)
    ones++;
else if (r === 2)
    twos++;
else if (r === 3)
    threes++;
else if (r === 4)
    fours++;
else if (r === 5)
    fives++;
else
    sixes++;

r = play();
if (r === 1)
    ones++;
else if (r === 2)
    twos++;
else if (r === 3)
    threes++;
else if (r === 4)
    fours++;
else if (r === 5)
    fives++;
else
    sixes++;

r = play();
if (r === 1)
    ones++;
else if (r === 2)
    twos++;
else if (r === 3)
    threes++;
else if (r === 4)
    fours++;
else if (r === 5)
    fives++;
else
    sixes++;

r = play();
if (r === 1)
    ones++;
else if (r === 2)
    twos++;
else if (r === 3)
    threes++;
else if (r === 4)
    fours++;
else if (r === 5)
    fives++;
else
    sixes++;

document.write(`<p>`);
document.write(`1's: ${ones}<br/>`);
document.write(`2's: ${twos}<br/>`);
document.write(`3's: ${threes}<br/>`);
document.write(`4's: ${fours}<br/>`);
document.write(`5's: ${fives}<br/>`);
document.write(`6's: ${sixes}<br/>`);
document.write(`total: ${ones+twos+threes+fours+fives+sixes}</p>`);

Example B.11. Host HTML5
<!doctype html>
<html>
    <head>
        <title>jsConds0</title>
        <meta charset='utf-8'/>
        <script src='jsConds0.js'></script>
    </head>
    <body>
        <h1>jsConds0a</h1>
        <script src='jsConds0a.js'></script>
    </body>
</html>

Test!


Assignments From Chapter 4

Solutions from Assignments from Chapter 4

Assignment JS.Funcs.0

Write three functions:

  • addVat(a), that returns amount including vat given an amount as input.
  • subVat(a), that returns amount excluding vat given an amount as input.
  • calcVat(a), that returns the vat from the given amount.

Use the Danish vat of 25.0%.

Hand in as a file called jsFuncs0.js

Model Solution Assignment JS.Funcs.0
Example B.12. Vat Functions
'use strict';
const VAT = 25;

const addVat = function(a) {
    return a * (1 + VAT / 100);
}

const subVat = function(a) {
    return a / (1 + VAT / 100);
}

const calcVat = function(a) {
    return a - subVat(a);
}

Example B.13. Execution Code
'use strict';

let amount = Number(prompt('Enter Amount'));

document.write(`<p>Amount incl vat: ${addVat(amount)}`);
document.write(`<p>Amount excl vat: ${subVat(addVat(amount))}`);
document.write(`<p>Vat: ${calcVat(addVat(amount))}`);

Example B.14. Host HTML5
<!doctype html>
<html>
    <head>
        <title>jsfuncs0</title>
        <meta charset='utf-8'/>
        <script src='jsFuncs0.js'></script>
    </head>
    <body>
        <h1>jsfuncs0</h1>
        <script src='jsFuncs0a.js'></script>
    </body>
</html>

Test!


Assignment JS.Funcs.1

Write three functions:

  • Write a function first(s), that returns the first character of the string s.
  • Write a function last(s), that returns the last character of the string s.
  • Write a function middle(s), that returns whatever is between the first and the last character of the string s.

Hint: Take a look a the string functions in your MDN reference, especially the substr function.

Hand in as one file called jsTextUtils.js

Model Solution Assignment JS.Funcs.1
Example B.15. Library jsTextUtils.js
'use strict';

const first = function (txt) {
    return txt.charAt(0);
}

const last = function (txt) {
    return txt.charAt(txt.length - 1);
}

const middle = function (txt) {
    return txt.substring(1, txt.length - 1);
}

Example B.16. Execution Code
'use strict';
var phrase = prompt('Enter Phrase');

document.write(`<p>First - Middle - Last</p>
    <p><b>
    ${first(phrase)} - ${middle(phrase)} - ${last(phrase)}
    </b></p>`);

Example B.17. Host HTML5
<!doctype html>
<html>
    <head>
        <title>jsfuncs1</title>
        <meta charset='utf-8'/>
        <script src='jsTextUtils0.js'></script>
    </head>
    <body>
        <h1>jsfuncs1</h1>
        <script src='jsFuncs1a.js'></script>
    </body>
</html>

Test!


Assignment JS.Funcs.2

In todays lesson, I gave you play() that could flip a coin. We have, in the lesson about conditionals, worked with throwing a die. On principle this there is only one tiny difference between the two.

Now, I want you to identify this difference, and then alter play, so that it accomodates coin flipping, die throwing, turning a spindle with any number of sides, and even a roulette.

Place the function in myFuncLib.js.

Model Solution Assignment JS.Funcs.2 and 3
Example B.18. myFuncLib
'use strict';

/*
 * play requires an input arg
 * given 2, it will simulate a coin
 * given 6, it will simulate a die
 * calling sequence for a die: let result = play(6);
 */
const play = function(n) {
    return Math.floor(Math.random() * n + 1);
}

/*
 * Convert celsius input to fahrenheit
 */
const C2F = function(c) {
    return 9 / 5 * c + 32;
}

/*
 * Convert fahrenheit input to celsius
 */
const F2C = function(f) {
    return 5 / 9 * (f - 32);
}

Example B.19. Execution Code
'use strict';

document.write(`<p>Flip coin: ${play(2)}</p>`);
document.write(`<p>Flip coin: ${play(2)}</p>`);
document.write(`<p>Flip coin: ${play(2)}</p>`);

document.write(`<hr/>`);

document.write(`<p>Throw die: ${play(6)}</p>`);
document.write(`<p>Throw die: ${play(6)}</p>`);
document.write(`<p>Throw die: ${play(6)}</p>`);
document.write(`<p>Throw die: ${play(6)}</p>`);

document.write(`<hr/>`);

document.write(`<p>Spin the wheel: ${play(37)}</p>`);

document.write(`<hr/>`);

document.write(`<h2>Temps</h2>`);
let c = Number(window.prompt('Enter temp in C'));
document.write(`<p>${c} &deg;C = ${C2F(c)}&deg;F</p>`);
let f = Number(window.prompt('Enter temp in F'));
document.write(`<p>${f} &deg;F = ${F2C(f)}&deg;C</p>`);

Example B.20. Host HTML5
<!doctype html>
<html>
    <head>
        <title>myFuncLib</title>
        <meta charset='utf-8'/>
        <script src='myFuncLib.js'></script>
    </head>
    <body>
        <h1>jsfuncs23</h1>
        <script src='jsFuncs23.js'></script>
    </body>
</html>

Test!


Assignments From Chapter 5

Assignments JS.Arrays

Assignment JS.Arrays.1

In the assignments Conds.0, and Funcs.2, you were flipping a coin of rolling a die. In both cases you used counter variables to tally the number of the various outcomes. Now you must change that to using an array instead of individual variables. Write a comment or two about it in the code.

Hand in as a file called jsArrays1.js

Model Solution Assignment JS.Arrays.1
Example B.21. jsArrays1.js
'use strict';

const roll = function(n) {
    return Math.floor(Math.random() * n + 1);
}

Example B.22. Execution Code
'use strict';

let res;
let tally = [0, 0, 0, 0, 0, 0, 0];

res = roll(6);
tally[res]++;

res = roll(6);
tally[res]++;

res = roll(6);
tally[res]++;

res = roll(6);
tally[res]++;

res = roll(6);
tally[res]++;

res = roll(6);
tally[res]++;

res = roll(6);
tally[res]++;

res = roll(6);
tally[res]++;

res = roll(6);
tally[res]++;

res = roll(6);
tally[res]++;

let i = 1;
console.log(`${i}'s:\t ${tally[i++]}`);
console.log(`${i}'s:\t ${tally[i++]}`);
console.log(`${i}'s:\t ${tally[i++]}`);
console.log(`${i}'s:\t ${tally[i++]}`);
console.log(`${i}'s:\t ${tally[i++]}`);
console.log(`${i}'s:\t ${tally[i++]}`);

Example B.23. Host HTML5
<!doctype html>
<html>
    <head>
        <title>jsArrays1</title>
        <meta charset='utf-8'/>
        <script src='jsArrays1.js'></script>
    </head>
    <body>
        <h1>jsArrays1</h1>
        <script src='jsArraysExec1.js'></script>
    </body>
</html>

Test!


Assignment JS.Arrays.2

Write a function that checks whether an array is palindromic, ie whether the first and last elements are identical, and the middle is palindromic.

Hand in as a file called jsArrays2.js

Model Solution Assignment JS.Arrays.2
Example B.24. jsArrays3.js
'use strict';

const first = function (arr) {
    return arr[0];
}

const last = function (arr) {
    return arr[arr.length - 1];
}

const middle = function (arr) {
    return arr.slice(1,-1)
}

const isPalindrome = function(arr, debug) {
    if (arr.length <= 1)
        return true;
    if (debug)
        console.log(`${first(arr)} - ${middle(arr)} - ${last(arr)}`);
    return first(arr) === last(arr) && isPalindrome(middle(arr), debug);
}

Example B.25. Execution Code
'use strict';
let arr = [];
let input = null;

do {
    if (input === 'qw')
        break;
    if (input == null)
        continue;
    arr.push(input);
} while(input = prompt('Enter whatever, "qw" for stop'));

console.log(arr);
console.log(`is the array ${arr} palindromic: ${isPalindrome(arr) ? 'yes' : 'no'}`);

Example B.26. Host HTML5
<!doctype html>
<html>
    <head>
        <title>jsArrays2</title>
        <meta charset='utf-8'/>
        <script src='jsArrays2.js'></script>
    </head>
    <body>
        <h1>jsArrays2</h1>
        <script src='jsArraysExec2.js'></script>
    </body>
</html>

Test!


Assignment JS.Arrays.3

Write a program that rolls a die i times, and then two functions that check

  • whether all faces were the same.
  • whether there were any n's where n is between 1-6

Hand in as a file called jsArrays3.js

Model Solution Assignment JS.Arrays.3
Example B.27. jsArrays3.js
'use strict';

const play = function(n) {
    return Math.floor(Math.random() * n + 1);
}

/*
 * n is number of plays, arr is results array
 */
 const isOnly = function(n, arr) {
    if (arr.includes(n))
        return true;
    else
        return false;
}
/*
 * returns number of n's
 */
 const howMany = function(n, arr) {
    return arr[n];
}

Example B.28. Execution Code
'use strict';

let res = [0, 0, 0, 0, 0, 0, 0]

const n = Number(prompt('How many plays?'));
let i = 0;
while(i < n) {
    res[play(6)]++;
    i++;
}

const q = Number(prompt('What die are you looking for?'));

console.log(`Were all faces the same? ${isOnly(n, res)}`);
console.log(`There were ${howMany(q, res)} ${q}'s`);

Example B.29. Host HTML5
<!doctype html>
<html>
    <head>
        <title>jsArrays3</title>
        <meta charset='utf-8'/>
        <script src='jsArrays3.js'></script>
    </head>
    <body>
        <h1>jsArrays3</h1>
        <script src='jsArraysExec3.js'></script>
    </body>
</html>

Test!


Assignments JS.Loops

Assignment JS.Loops.2.2 Functions

Definition

A natural number p is called prime if p ≠ 1 and the only numbers that divide p are 1 and p itself.

Fundamental Theorem of Arithmetic

Every natural number greater than 1 is either prime or can be written uniquely as a product of prime numbers.

There is a nice proof of that theorem but since this is no math class, we shall skip that, and just trust that it is so, and use it.

Now you must solve at least number 1 of the following. If you have the energy and skill you may want to have a go at the next ones too, but please solve the rest of this chapter's assignments before spending time on that.

  1. Write a function isPrime(p) that returns true if p is prime, and false otherwise.
  2. Write a function that creates a collection of the primes dividing p if isPrime(p) === false.
  3. Write a procedure that lists the primes dividing p if isPrime(p) === false.
  4. If you haven't already done so in number 2 above, please augment that function so that it accepts and reflects that a prime may divide p more than once.

Write an HTML5 page that allows for testing your function(s).

Hint for beginners: You may prefer to work with embedded JavaScript while you test your code. Once it works as desired, separate the JavaScript into an individual .js file, test again, then hand in your solution.

Model Solution Assignment JS.Loops.2.2
Example B.30. primeLib.js
'use strict';

const isPrime = function (arg) {
    if (arg === 2 || arg === 3)
        return true;
    if (arg % 2 === 0 || arg % 3 === 0)
        return false;

    var i = 1;
    var lim = Math.sqrt(arg);
    var divm1 = 6 * i - 1;
    var divp1 = 6 * i + 1;

    while (divm1 <= lim || divp1 <= lim) {
        if (arg % divm1 === 0 || arg % divp1 === 0)
            return false;
        i++;
        divm1 = 6 * i - 1;
        divp1 = 6 * i + 1;
    }
    return true;
}

Example B.31. Execution Code
'use strict';
let arg = Number(window.prompt('Enter large integer, please'));

let start = new Date();
let b = isPrime(arg);
let stop = new Date();
let elapsed = stop - start;

let primeTime = `Is ${arg} prime: ${b}, duration: ${elapsed} ms`;
document.write(`${primeTime}`);

Example B.32. Host HTML5
<!doctype html>
<html>
    <head>
        <title>Test Prime</title>
        <meta charset='utf-8'/>
        <script src='primeLib.js'></script>
    </head>
    <body>
        <h1>Test and Time <code>isPrime</code></h1>
        <script src='executionCode.js'></script>
</html>

Test!


Assignment JS.Loops.71
Example B.33. jsloops71.js

[Dow16] Exercise 7.1

'use strict';
/* Downey: thinkjava, ex 71 */
const loop = function(n) {
    let i = n;
    while (i > 1) {
        console.log(i);
        if (i % 2 == 0) {
            i = i / 2;
        } else {
            i = i + 1;
        }
    }
}

loop(10);

  1. Draw a table that shows the value of the variables i and n during the execution of loop. The table should contain one column for each variable and one line for each iteration. You might trick the program into doing it for you.
  2. What is the output of this program?
  3. Can you prove/rationalize that this loop terminates for any positive value of n?
Model Solution Assignment JS.Loops.71
Example B.34. jsLoops71.js
'use strict';
/* Downey: thinkjava, ex 71 */
const loop = function(n) {
    let s = 's =';                 // logstring
    let i = n;
    while (i > 1) {
        s += ' ' + i;           // build logstring
        console.log(s);
        if (i % 2 === 0) {
            i = i / 2;
        } else {
            i = i + 1;
            if (i % 100 <= 1)
                break;
        }
    }
    return s;
}

/* find out if some values makes it divergent */

Example B.35. Execution Code
'use strict';

let n = Number(prompt('Chećk function, enter number < 100'));

document.write(`<h4>n = ${n}</h4>`);
document.write(`<p>${loop(n)}</p>`);


Example B.36. Host HTML5
<!doctype html>
<html>
    <head>
        <title>jsLoops71</title>
        <meta charset='utf-8'/>
        <script src='jsLoops71.js'></script>
    </head>
    <body>
        <h1>jsLoops71exec</h1>
        <script src='jsLoops71exec.js'></script>
    </body>
</html>

Test!


Assignment JS.Loops.72.1

[Hav19] Exercise FizzBuzz, chapter 2.

Write a program that uses console.log to print all the numbers from 1 to 100, with two exceptions. For numbers divisible by 3, print "Fizz" instead of the number, and for numbers divisible by 5 (and not 3), print "Buzz" instead. Save as fizzbuzz.js.

When you have that working, modify your program to print "FizzBuzz" for numbers that are divisible by both 3 and 5 (and still print "Fizz" or "Buzz" for numbers divisible by only one of those). Save as fizzbuzzBetter.js. (This is actually a job interview question that has been claimed to weed out a significant percentage of programmer candidates. So if you solved it, your labor market value just went up.)

Model Solution Assignment JS.Loops.72.1
Example B.37. fizzbuzz.js
'use strict';
for (let i = 1; i <= 100; i++) {
    if (i % 5 === 0 && i % 3 !== 0)
        console.log('Buzz');
    else if (i % 3 === 0)
        console.log('Fizz');
    else
        console.log(i)
}

Example B.38. Host HTML5
<!doctype html>
<html>
    <head>
        <title>fizzbuzz</title>
        <meta charset='utf-8'/>
    </head>
    <body>
        <h1>fizzbuzz</h1>
        <script src='fizzbuzz.js'></script>
    </body>
</html>

Test!


Example B.39. fizzbuzzBetter.js
'use strict';
for (let i = 1; i <= 100; i++) {
    if (i % (3 * 5) === 0)
        console.log('FizzBuzz');
    else if (i % 5 === 0)
        console.log('Buzz');
    else if (i % 3 === 0)
        console.log('Fizz');
    else
        console.log(i)
}

Example B.40. Host HTML5
<!doctype html>
<html>
    <head>
        <title>fizzbuzzBetter</title>
        <meta charset='utf-8'/>
    </head>
    <body>
        <h1>fizzbuzz</h1>
        <script src='fizzbuzzBetter.js'></script>
    </body>
</html>

Test!


Assignment JS.Loops.73

Write an iterative function powi(r, e) that calculates the e-th power of r, re. Document it with a couple of testcases. Save the function in myFuncLib.js.

Model Solution Assignment JS.Loops.73
Example B.41. jsLoops73.js
'use strict';

const powi = function(r, e) {
    let res = 1;
    while (e > 0) {
        res *= r;
        e -= 1;
    }
    return res;
}

Example B.42. Execution Code
'use strict';

let r = Number(prompt('powi: Enter root'));
let e = Number(prompt('powi: Enter exponent'));

document.write(`<p>powi: ${r}<sup>${e}</sup> = ${powi(r, e)}</p>`);


Example B.43. Host HTML5
<!doctype html>
<html>
    <head>
        <title>jsLoops73</title>
        <meta charset='utf-8'/>
        <script src='jsLoops73.js'></script>
    </head>
    <body>
        <h1>jsLoops73exec</h1>
        <script src='jsLoops73exec.js'></script>
    </body>
</html>

Test!


Assignment JS.Loops.74

Write an iterative function facti(n) that calculates n!. Document it with a couple of testcases. Save the function in myFuncLib.js.

Model Solution Assignment JS.Loops.74
Example B.44. jsLoops74.js
'use strict';

const facti = function(n) {
    let res = 1;
    while (n > 0) {
        res *= n;
        n -= 1
    }
    return res;
}

Example B.45. Execution Code
'use strict';

let n = Number(prompt('facti: Enter number for factorial'));

document.write(`<p>facti: ${n}! = ${facti(n)}</p>`);


Example B.46. Host HTML5
<!doctype html>
<html>
    <head>
        <title>jsLoops74</title>
        <meta charset='utf-8'/>
        <script src='jsLoops74.js'></script>
    </head>
    <body>
        <h1>jsLoops74exec</h1>
        <script src='jsLoops74exec.js'></script>
    </body>
</html>

Test!


Assignment JS.Loops.75

We have flipped coins and dies 10 times. I want you to change the code so that the user may enter the number of times she wants to play dice, say 25, 1000, or even 100000000. Write a jsPlay75.html that does that, and of course uses the function we created earlier.

Change the program so that user can choose between flipping coins, rolling dice, playing roulette, etc.

Save them in a repo games.

Model Solution Assignment JS.Loops.75
Example B.47. jsLoops75.js
'use strict';

/*
 * n: what do we play, 2:coins, 6:dice, ...
 */
const roll = function(n) {
    return Math.floor(Math.random() * n + 1);
}

/*
 * n: how many times
 * a: what do we play, 2:coins, 6:dice, ...
 */
const play_n_times = function(n, a) {
    let res;
    let tally = [];

    let i = 0;
    while (i <= a) {        // dim and init array
        tally[i] = 0;
        i += 1;
    }

    let then = new Date();
    i = 0;
    while (i < n) {         // play n times
        res = roll(a);
        tally[res] += 1;
        i += 1;
    }
    let now = new Date();
    console.log(`Time: ${now - then} ms`);
    return tally;           // return results
}

/*
 * print results array on log
 */
const print_tally = function(arr) {
    for (let i = 1; i < arr.length; i += 1) {
        console.log(`${i}'s: ${arr[i]}`);
    }
    console.log('---------------------------');
}

Example B.48. jsPlay75.js
'use strict';
const init = function() {
    document.getElementById('play').addEventListener('click', function() {
            var howMany = Number(prompt('How many plays?'));
            var what = Number(prompt('2 for coins, 6 for dice'));
            let arr = play_n_times(howMany, what);
            print_tally(arr);
        });
}
window.addEventListener('load', init);

Example B.49. jsPlay75.html
<!doctype html>
<html>
    <head>
        <meta charset='utf-8'/>
        <script src='jsLoops75.js'></script>
        <script src='jsPlay75.js'></script>
    </head>
    <body>
        <h1>Play, Flip, Roll</h1>
        <p>
            <button id='play'>Play!</button>
        </p>
    </body>
</html>

Click here!


Output from 1,000,000,000, one billion, plays with a die and a coin respectively

Time: 6642 ms
1's: 166668770
2's: 166676350
3's: 166655799
4's: 166664282
5's: 166666453
6's: 166668346
---------------------------
Time: 6700 ms
1's: 500011997
2's: 499988003
---------------------------

Assignments From Chapter 7

Solutions to Assignments JS.Recursion

Assignment JS.Recursion.0

There is a mathematical koncept, factorial, important in probability and statistics. The definition of n!:

n! = n * (n-1) * (n - 2) * ... * 2 * 1

There is a genuinely recursive definition that is, perhaps, more elegant:

n! = n * (n - 1)!

Write a recursive function fact(n) that calculates the factorial of a given natural number n. Document it with a couple of testcases. Place the function in myRecurseLib.js.

Ref: https://en.wikipedia.org/wiki/Factorial

Model Solution Assignment JS.Loops.0
Example B.50. myRecurseLib.js
'use strict';

/*
 * returns nth fibonacci number
 */
const fibo = function(n) {
    if (n === 0 || n === 1)
        return n;
    return fibo(n - 1) + fibo(n - 2);
}

/*
 * returns n! iterative
 */
 let fact = function(n) {
    if (n <= 1)
        return 1;
    else
        return n * fact(n - 1);
}

/*
 * returns r to the e
 */
const pow = function(r, e) {
    if (e <= 0)
        return 1;
    return r * pow(r, e-1);
}

Example B.51. Execution Code
'use strict';

const n = Number(prompt('Enter n for n!'));
document.write(`<p>${n}! = ${fact(n)}</p>`);

Example B.52. Host HTML5
<!doctype html>
<html>
    <head>
        <title>jsLoops0</title>
        <meta charset='utf-8'/>
        <script src='myRecurseLib.js'></script>
    </head>
    <body>
        <h1>jsLoops0exec</h1>
        <script src='jsLoops0exec.js'></script>
    </body>
</html>

Test!


Assignment JS.Recursion.1

There is a mathematical koncept, Fibonacci numbers, important in various contexts eg closely related to calculating the golden ratio. The definition of the n-th Fibonacci number Fn:

F0 = 0; F1 = 1;

and:

Fn = Fn-1 + Fn-2

Write a recursive function fibo(n) that calculates the n-th Fibonacci number. Document it with a couple of testcases. Save the function in myRecurseLib.js.

Ref: https://en.wikipedia.org/wiki/Fibonacci_number

Model Solution Assignment JS.Loops.1
Example B.53. myRecurseLib.js
'use strict';

/*
 * returns nth fibonacci number
 */
const fibo = function(n) {
    if (n === 0 || n === 1)
        return n;
    return fibo(n - 1) + fibo(n - 2);
}

/*
 * returns n! iterative
 */
 let fact = function(n) {
    if (n <= 1)
        return 1;
    else
        return n * fact(n - 1);
}

/*
 * returns r to the e
 */
const pow = function(r, e) {
    if (e <= 0)
        return 1;
    return r * pow(r, e-1);
}

Example B.54. Execution Code
'use strict';

const n = Number(prompt('Enter n for nth Fibonacci number'));
document.write(`F<sub>${n}</sub> = ${fibo(n)}`);

Example B.55. Host HTML5
<!doctype html>
<html>
    <head>
        <title>jsLoops0</title>
        <meta charset='utf-8'/>
        <script src='myRecurseLib.js'></script>
    </head>
    <body>
        <h1>jsLoops1exec</h1>
        <script src='jsLoops1exec.js'></script>
    </body>
</html>

Test!


Assignment JS.Recursion.2

The mathematical powers, eg 27, or 34 may be calculated recursively.

Write a recursive function pow(r, e) that calculates re. It must work for r ∈ ℕ, and e ∈ ℕ. You might test whether it also works for r ∈ ℤ, and e ∈ ℤ

Put it into your myRecurseLib.js.

Model Solution Assignment JS.Recursion.2
Example B.56. myRecurseLib.js
'use strict';

/*
 * returns nth fibonacci number
 */
const fibo = function(n) {
    if (n === 0 || n === 1)
        return n;
    return fibo(n - 1) + fibo(n - 2);
}

/*
 * returns n! iterative
 */
 let fact = function(n) {
    if (n <= 1)
        return 1;
    else
        return n * fact(n - 1);
}

/*
 * returns r to the e
 */
const pow = function(r, e) {
    if (e <= 0)
        return 1;
    return r * pow(r, e-1);
}

Example B.57. Execution Code
'use strict';

let r = Number(prompt('Power: Enter root'));
let e = Number(prompt('Power: Enter exponent'));

document.write(`<p>${r}<sup>${e}</sup> = ${pow(r, e)}</p>`)


Example B.58. Host HTML5
<!doctype html>
<html>
    <head>
        <title>jsRecursion3</title>
        <meta charset='utf-8'/>
        <script src='myRecurseLib.js'></script>
    </head>
    <body>
        <h1>jsLoops3</h1>
        <script src='jsLoops3exec.js'></script>
    </body>
</html>

Test!


Assignments From Chapter 8

Assignments JS.DOM.0

Assignment JS.7.0

Take a look at this page. Play with the left side of the footer. Click, and click again.

Create equivalent handlers for the center and right of the footer so that it is dynamic in the same way. I do want, however, the text in the middle column to be yellow. Do remember, that UX is not to let your users guess what they have to do ;)

Model Solution Assignment JS.7.0
Example B.59. The Page
<!doctype html>
<html>
    <head>
        <meta charset='utf-8'/>
        <title>Dave Ellis Conventional Page</title>
        <link href='daveEllis.css' rel='stylesheet'/>
        <script src='nQuery.js'></script>
        <script src='davis1.js'></script>
    </head>
    <body>
        <header>
            <nav>
                <h1>Your Website</h1>
                <ul>
                    <li><a href='#'>Is</a></li>
                    <li><a href='#'>This</a></li>
                    <li><a href='#'>Your</a></li>
                    <li><a href='#'>Website</a></li>
                </ul>
            </nav>
        </header>
        <main class='vc1'>
            <p>
                This page is written, with permission,
                from <a href='http://www.novolume.co.uk/blog/all-websites-look-the-same/'>
                Dave Ellis' Blog</a> to mimic that page.
            </p>
        </main>
        <footer>
            <article id="left"></article>
            <article id="center"></article>
            <article id="right"></article>
        </footer>
        <p>
            Inspiration: <a href='http://www.novolume.co.uk/'>Dave Ellis</a>
        </p>
    </body>
</html>

Example B.60. The JS
"use strict";
/*
 * davis1.js
 */

let filla = function (ev) {
    let arri = [];
    let arrh = [];
    let arrt = [];
    arri["left"] = "book-icon.png";
    arri["center"] = "cogs-icon.png";
    arri["right"] = "shield-icon.png";
    arrh["left"] = "Always";
    arrh["center"] = "Three";
    arrh["right"] = "Columns";
    arrt["left"] = "You could have four columns here but you won't. You'll have three like everyone else.";
    arrt["center"] = "Have a cog icon above one of these columns if you are really feeling especially creative. ";
    arrt["right"] = "The perfect place to talk about your services. Because co-incidentally you have three of them.";

    let art = $(ev.target.id);
    if (art.innerHTML !== "") {
        while (art.firstChild) {
            art.removeChild(art.firstChild);
        }
    } else {
        let img = document.createElement("img"); // create element
        img.setAttribute("src", arri[ev.target.id]);
        img.setAttribute("alt", "icon");
        img.setAttribute("width", "64");

        let h1 = document.createElement("h1"); // create element
        let txt = document.createTextNode(arrh[ev.target.id]); // create text
        h1.appendChild(txt); // put on tree

        let par = document.createElement("p"); // create element
        txt = document.createTextNode(arrt[ev.target.id]); // create text
        par.appendChild(txt); // put onto tree

        art.appendChild(img);
        art.appendChild(h1);
        art.appendChild(par);
    }
}

let initialize = function () {
    $("left").addEventListener("click", filla);
    $("center").addEventListener("click", filla);
    $("right").addEventListener("click", filla);
}

window.addEventListener("load", initialize);

Click here!

Assignment JS.10.0

On the basis of the code from Example 8.20 create a page with two or three links to videos. Adapt the controls with Jeremy's code, and feel free to add eg sound controls as well.

Solution Assignment JS.10.0

Christian B's solution:

Example B.61. The JavaScript
'use strict';

(function() {

    function createVideoControls() {
      var vids = document.getElementsByTagName('video');
      for (var i = 0 ; i < vids.length ; i++) {
        addControls( vids[i] );
      }
    }

    function addControls( vid ) {

      vid.removeAttribute('controls');


      // vid.height = vid.videoHeight;
      // vid.width = vid.videoWidth;
      // vid.parentNode.style.height = vid.videoHeight + 'px';
      // vid.parentNode.style.width = vid.videoWidth + 'px';
      vid.removeAttribute('style')

      var controls = document.createElement('div');
      controls.setAttribute('class','controls');

      var play = document.createElement('button');
      play.setAttribute('title','Play');
      play.innerHTML = '&#x25BA;';

      controls.appendChild(play);

      vid.parentNode.insertBefore(controls, vid);

      play.onclick = function () {
        if (vid.ended) {
          vid.currentTime = 0;
        }
        if (vid.paused) {
          vid.play();
        } else {
          vid.pause();
        }
      };

      vid.addEventListener('play', function () {
        play.innerHTML = '&#x2590;&#x2590;';
        play.setAttribute('paused', true);
      }, false);

      vid.addEventListener('pause', function () {
        play.removeAttribute('paused');
        play.innerHTML = '&#x25BA;';
      }, false);

      vid.addEventListener('ended', function () {
        vid.pause();
      }, false);
    }  // end of addControls

    window.addEventListener('load', function() {
            createVideoControls();
        }
    );

})();

Example B.62. The HTML5
<!doctype html>
<html>
    <head>
        <meta charset='utf-8'/>
        <title>Video Controle</title>
        <!-- <link href='js7_DOM.css' rel='stylesheet'/> -->
        <script src='nQuery.js'></script>
        <!-- <script src='js10_videoControls.js'></script> -->
    </head>
    <body>
        <header>
            <nav>
                <h1>Video Controle</h1>
                <ul>
                    <li><a href='#'>vidONE</a></li>
                    <li><a href='#'>vidTWO</a></li>
                    <li><a href='#'>vidTHREE</a></li>
                </ul>
            </nav>
        </header>
        <main>
          <div class="video-wrapper">
            <video id="movie" controls>
                <source src="http://media.w3.org/2010/05/sintel/trailer.mp4" />
                <source src="http://media.w3.org/2010/05/sintel/trailer.webm"
                    type='video/webm; codecs="vp8, vorbis"' />
                <source src="http://media.w3.org/2010/05/sintel/trailer.ogv"
                    type='video/ogg; codecs="theora, vorbis"' />
                <p>Download movie as
                    <a href="http://media.w3.org/2010/05/sintel/trailer.mp4">MP4</a>,
                    <a href="http://media.w3.org/2010/05/sintel/trailer.webm">WebM</a>,
                    or <a href="http://media.w3.org/2010/05/sintel/trailer.ogv">Ogg</a>.</p>
            </video>
        </div>


        </main>
        <script type="text/javascript">
        (function() {

            function createVideoControls() {
              var vids = document.getElementsByTagName('video');
              for (var i = 0 ; i < vids.length ; i++) {
                addControls( vids[i] );
              }
            }

            function addControls( vid ) {

              vid.removeAttribute('controls');


              // vid.height = vid.videoHeight;
              // vid.width = vid.videoWidth;
              // vid.parentNode.style.height = vid.videoHeight + 'px';
              // vid.parentNode.style.width = vid.videoWidth + 'px';
              vid.removeAttribute('style')

              var controls = document.createElement('div');
              controls.setAttribute('class','controls');

              var play = document.createElement('button');
              play.setAttribute('title','Play');
              play.innerHTML = '&#x25BA;';

              controls.appendChild(play);

              var speedUp = document.createElement('button');
              speedUp.setAttribute('id','speedUp');
              speedUp.innerHTML = 'speedUp';

              controls.appendChild(speedUp);
              var speedDown = document.createElement('button');
              speedDown.setAttribute('id','speedDown');
              speedDown.innerHTML = 'speedDown';

              controls.appendChild(speedDown);

              vid.parentNode.insertBefore(controls, vid);

              play.onclick = function () {
                if (vid.ended) {
                  vid.currentTime = 0;
                }
                if (vid.paused) {
                  vid.play();
                } else {
                  vid.pause();
                }
              };

              speedUp.addEventListener('click', function() {
                vid.playbackRate += 0.2;
                console.log(vid.playbackRate)
              }, false);

              speedDown.addEventListener('click', function() {
                vid.playbackRate -= 0.2;
                console.log(vid.playbackRate)
              }, false);

              vid.addEventListener('play', function () {
                play.innerHTML = '&#x2590;&#x2590;';
                play.setAttribute('paused', true);
              }, false);

              vid.addEventListener('pause', function () {
                play.removeAttribute('paused');
                play.innerHTML = '&#x25BA;';
              }, false);

              vid.addEventListener('ended', function () {
                vid.pause();
              }, false);
            }  // end of addControls

            window.addEventListener('load', function() {
                    createVideoControls();
                }
            );

        })()

        </script>
    </body>
</html>

Xtrn JS u speedup!


Assignment JS.10.1

Create a page with a form containing, at least the following: a date, a slider for a number, and a number. The slider should cover integers from -279 - +6000 °K. The number a value between 0 - 100. The date must be valid. Submit the form to http://x15.dk/hitme.php. Verify results, and iterate until ok.

I want you to test in Opera/Chrome and Firefox. Your hand in must provide screenshots of the form just before submit in both browsers, as well as the code of course.

Solution Assignment JS.10.1

Kenneths solution

Example B.63. The JavaScript
'use strict';

const $ = function (foo) {
    return document.getElementById(foo);
}

const isDate = function (date) {
    return true;
}

let validate = function (e) {
    let date = $('date');
    var number = $('number');
    if (number.value > 100 || number.value < 0){
        number.focus();
        e.preventDefault();
        window.alert('The number must be between 0 and 100!');
        return false;
    } else {
        return true;
    }
    if (!isDate(date.value)) {
        date.focus();
        e.preventDefault();
        return false;
    }

    return true;
}

let dispSlide = function () {
    $('ranger').innerHTML =
            $('degree').value;
}

let followme = function () {
    $('formal').addEventListener('submit', validate);
    dispSlide();
    $('degree').addEventListener('mousemove', dispSlide);
}
window.addEventListener('load', followme);

Example B.64. The HTML5
<!DOCTYPE html>
<html>
    <head>
        <title>Assignment JS.10.1</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <style>
            td { padding: 1em .5em; }
        </style>
        <script src="validate.js"></script>
    </head>
    <body>
        <h1>HTML5 Form Exercise</h1>
        <form action="http://x15.dk/hitme.php" method="post" id="formal">
        <table>
            <tr>
                <td><label for="date">Date</label></td>
                <td><input type="date" id="date" name="date" required></td>
            </tr>
            <tr>
                <td><label for="degree">Degree</label></td>
                <td><input type="range" id="degree" name="degree" min="-272" max="6000">
                <span id='ranger'></span></td>
            </tr>
            <tr>
                <td><label for="number">Number between 0 and 100</label></td>
                <td><input type="number" id="number" name="number" required></td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td><input type="submit" value="Submit"></td>
            </tr>
        </table>
        </form>
    </body>
</html>

Kenneths solution: test it!


Assignments From Chapter 9

Assignments JS.DOM.1

Assignment JS.DOM.0

Find your solution to the section called “Assignment JS.Conds.0”, make a branch and do the following:

  • In the copy replace the 10 times if-else constructions with switches. Use my first suggestion without default. Test until it works.
  • Try my second suggestion with default at the end. test that it works.
  • Try moving the default up, so that it occurs just before the case 3: clause. Now test thoroughly, and tell me your conslusion.
Model Solution Assignment JS.DOM.0
Example B.65. Switch
'use strict';

const roll = function(n) {
    return Math.floor(Math.random() * n + 1);
}

let res;
let ones = 0;
let twos = 0;
let threes = 0;
let fours = 0;
let fives = 0;
let sixes = 0;

res = roll(6);
switch (res) {
    case 1:
        ones++;
        break;
    case 2:
        twos++;
        break;
    case 3:
        threes++;
        break;
    case 4:
        fours++;
        break;
    case 5:
        fives++;
        break;
    default:
        sixes++;
}

res = roll(6);
switch (res) {
    case 1:
        ones++;
        break;
    case 2:
        twos++;
        break;
    case 3:
        threes++;
        break;
    case 4:
        fours++;
        break;
    case 5:
        fives++;
        break;
    default:
        sixes++;
}

res = roll(6);
switch (res) {
    case 1:
        ones++;
        break;
    case 2:
        twos++;
        break;
    case 3:
        threes++;
        break;
    case 4:
        fours++;
        break;
    case 5:
        fives++;
        break;
    default:
        sixes++;
}

res = roll(6);
switch (res) {
    case 1:
        ones++;
        break;
    case 2:
        twos++;
        break;
    case 3:
        threes++;
        break;
    case 4:
        fours++;
        break;
    case 5:
        fives++;
        break;
    default:
        sixes++;
}

res = roll(6);
switch (res) {
    case 1:
        ones++;
        break;
    case 2:
        twos++;
        break;
    case 3:
        threes++;
        break;
    case 4:
        fours++;
        break;
    case 5:
        fives++;
        break;
    default:
        sixes++;
}

res = roll(6);
switch (res) {
    case 1:
        ones++;
        break;
    case 2:
        twos++;
        break;
    case 3:
        threes++;
        break;
    case 4:
        fours++;
        break;
    case 5:
        fives++;
        break;
    default:
        sixes++;
}

res = roll(6);
switch (res) {
    case 1:
        ones++;
        break;
    case 2:
        twos++;
        break;
    case 3:
        threes++;
        break;
    case 4:
        fours++;
        break;
    case 5:
        fives++;
        break;
    default:
        sixes++;
}

res = roll(6);
switch (res) {
    case 1:
        ones++;
        break;
    case 2:
        twos++;
        break;
    case 3:
        threes++;
        break;
    case 4:
        fours++;
        break;
    case 5:
        fives++;
        break;
    default:
        sixes++;
}

res = roll(6);
switch (res) {
    case 1:
        ones++;
        break;
    case 2:
        twos++;
        break;
    case 3:
        threes++;
        break;
    case 4:
        fours++;
        break;
    case 5:
        fives++;
        break;
    default:
        sixes++;
}

res = roll(6);
switch (res) {
    case 1:
        ones++;
        break;
    case 2:
        twos++;
        break;
    case 3:
        threes++;
        break;
    case 4:
        fours++;
        break;
    case 5:
        fives++;
        break;
    default:
        sixes++;
}

console.log(`ones:\t ${ones}`);
console.log(`twos:\t ${twos}`);
console.log(`threes:\t ${threes}`);
console.log(`fours:\t ${fours}`);
console.log(`fives:\t ${fives}`);
console.log(`sixes:\t ${sixes}`);

Assignment JS.DOM.1

Write a page with an HTML5 form to enter a name and a birthday. The name must be more than one character long, and the birthday must be a valid date.

The entered data must be caught by JavaScript and used to make a cookie with a name-value pair. The name must be the concatenation of name and birthday, and the value must be '42' as in 'nml251145=42'. Give the cookie a lifespan of 10 minutes. Ten minutes are 0.00694444 years! Test repeatedly until it works.

You may check the cookie content by means of the log.

Once the JavaScript works, close the browser, take 10, and then restart your browser to ascertain that the expiration mechanism works.

Model Solution Assignment JS.DOM.1

Important Notice: Page must be served by web server, ie by the HTTP protocol, for the cookie mechanism to work.

Example B.66. ppkCookieLib.js
"use strict";

function createCookie(name, value, days) {
    let expires;
    let samesite = "; sameSite=lax";            // nml
    if (days) {
        let date = new Date();
        date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
        expires = "; expires=" + date.toGMTString();
    }
    else {
        expires = "";
    }
    document.cookie = name + "=" + value + expires + samesite + "; path=/"; // nml
}

function readCookie(name) {
    let nameEQ = name + "=";
    let ca = document.cookie.split(";");
    for (let i = 0; i < ca.length; i++) {
        let c = ca[i];
        while (c.charAt(0) == " ") {
            c = c.substring(1,c.length);
        }
        if (c.indexOf(nameEQ) == 0) {
            return c.substring(nameEQ.length, c.length);
        }
    }
    return null;
}

function eraseCookie(name) {
    createCookie(name, "", -1);
}


Example B.67. execution.js
'use strict';
const isValid = function(e) {
    let name = document.formal.name;
    let born = document.formal.born;
    if (born.value === "" || name.value === "") {
        window.alert("Both fields must be filled in");
        name.focus();
        e.preventDefault();
        return false;
    } else {
        createCookie(name.value + born.value, 42, 0.00694444);
        return true;
    }
}

const getstarted = function() {
    console.log(`Cookie niels1945-11-25? ${readCookie('niels1945-11-25')}`);
    console.log(`Cookies: ${document.cookie}`)
    document.formal.addEventListener('submit', isValid);
}
window.addEventListener('load', getstarted);

Example B.68. Cookie Tester
<!doctype html>
<html>
    <head>
        <title>NMLs Cookie Demo</title>
        <meta charset='utf-8'/>
        <meta name='viewport' content='width=device-width, initial-scale=1.0'>
        <style>
            form p { font-family: monospace; font-size: 1.6em; }
        </style>
        <script src='../jsn/ppkCookieLib.js'></script>
        <script src='./execution.js'></script>
    </head>
    <body>
        <h1>Cookie Assignment</h1>
        <form name='formal' method='post' action='http://x15.dk/hitme.php'>
            <p>
                Name: <input type='text' name='name' required/>
            </p>
            <p>
                Born: <input type='date' name='born' required/>
            </p>
            <p>
                <input type='submit' value='Go'/>
            </p>
        </form>
    </body>
</html>

Click here!


Assignments From Chapter 10

Assignments JS.OOP.0

Assignment JS.OO.0

Create a JavaScript object for tunes, songs. The have titles, composers, and year as properties as well as an array of performers.

The Tune object must be used in an HTML5 page allowing the user to enter tunes data through a form. The entered Tune objects must be stored in an array. The page has a div element, where the array of tunes is displayed. Whenever the array is changed, the display must automatically be updated.

Optional extra: Consider introducing a possibility for deleting tunes from the tunes array.

Solution Assignment JS.4.4
Example B.69. Tune.js
export class Tune {

    constructor(title, composer, year) {
        this.title = title;
        this.composer = composer;
        this.year = year;
        this.performers = [];
    }

    addPerformer(performer) {
        this.performers.push(performer);
    }

    toString() {
        let s = '<p>';
        s += this.composer + ': ';
        s += '<i>' + this.title + '</i>, ';
        s += this.year;
        s += '</p>';
        return s;
    }
}
        

Example B.70. testTuneObj.js
'use strict';
import {$} from './nQuery.js';
import {Tune} from './Tune.js';

let begin = function() {
    let tunes = [];
    let now = new Date();
    $('year').setAttribute('max', now.getFullYear());

/* the following array elements are for test purposes
 * with ajax the array might come by json from server file, re ajax lesson
 * with php the array may be generated on the server side from json, re REST
 */
    let tune = new Tune("Mac the Knife", "Kurt Weil", 1928);
    tune.addPerformer("Lotte Lenya");
    tunes.push(tune);

    tune = new Tune("Ode an der Freude", "Beethoven", 1824);
    tune.addPerformer("Berliner");
    tune.addPerformer("Wiener");
    tunes.push(tune);

    tune = new Tune("What a Wonderful World", "George Douglas", 1967);
    tune.addPerformer("Louis Armstrong");
    tunes.push(tune);
/* end of test data */

    let disp = function() {
        let div1 = $('tunearea');
        while (div1.firstChild) {    // Removing all children from an element
            div1.removeChild(div1.firstChild);
        }
        for (let tune of tunes) {
            div1.innerHTML += tune.toString();
        }
    };

    disp();
    $('butt').addEventListener('click', function() {
            let cmp = $('cmp').value;
            let title = $('title').value;
            let year = $('year').value;
            let perf = $('perf').value;
            if (cmp !== '' && title !== '' && perf !== '') {
                let tune = new Tune(title, cmp, year);
                tune.addPerformer(perf);
                tunes.push(tune);
                disp();
                $('cmp').value = '';
                $('title').value = '';
                $('perf').value = '';
            }
        }
    );
}
window.addEventListener('load', begin);

        

Example B.71. testTuneObj.html
<!doctype html>
<html>
    <head>
        <meta charset="utf-8"/>
        <title>Object-oriented JavaScript example</title>
        <script type='module' src='./testTuneObj.js'></script>
        <style>
            div {
                float: left;
                height: 500px;
                margin: 1em;
                outline: 1px solid blue;
                padding: 1em;
                width: 40%;
            }
        </style>
    </head>

    <body>
        <h1>Tunes</h1>
        <div id='left'>
            <h2>Entry form</h2>
            <p>
                <label for='title'>Title:</label><br/>
                <input type='text' id='title' required/>
            </p>
            <p>
                <label for='cmp'>Composer:</label><br/>
                <input type='text' id='cmp' required/>
            </p>
            <p>
                <label for='year'>Year:</label><br/>
                <input type='number' id='year' min='1970' value='1999' required/>
            </p>
            <p>
                <label for='perf'>Singer:</label><br/>
                <input type='text' id='perf' required/>
            </p>
            <p>
            </p>
            <p>
                <button id='butt'>Enter</button>
            </p>
        </div>
        <div id='right'>
            <h2>List of Tunes</h2>
            <div id='tunearea'></div>
        </div>
    </body>
</html>
        

Show in browser.


Assignment JS.OO.1

Write a Planet class. Use it to create ten planets storing planetary information. You may find the info on: http://www.opencourse.info/astronomy/introduction/05.motion_planets/#sidereal%20period and http://www.opencourse.info/astronomy/introduction/05.motion_planets/#aphelion . You don't need more than the sample output suggests. Treat the Sun as the first planet, with all values set to zero. Write a web information page with a form that allows the user to choose a planet via a radio button. Create a change event with a handler that writes the planet's information on the screen.

Sample output for Jupiter:

Distance = 5.2028 AU
Sidereal Period = 11.862 years
Synodic Period = 398.9 days
Eccentricity = 0.048
Orbital Inclination = 1.31°
Solution Assignment JS.4.1
Example B.72. planets.js
'use strict';
/*
 * array of planets
 * ie an array of planet objects
 */
export const planets = [
    {
        name: "Sun",
        distance: 0,
        siderialP: 0,
        synodicP: 0,
        eccentricity: 0,
        incline: 0
    },
    {
        name: "Mercury",
        distance: 0.3871,
        siderialP: 0.2408,
        synodicP: 115.88,
        eccentricity: 0.206,
        incline: 7.0
    },
    {
        name: "Venus",
        distance: 0.7233,
        siderialP: 0.6152,
        synodicP: 583.92,
        eccentricity: 0.007,
        incline: 3.39
    },
    {
        name: "Earth",
        distance: 1,
        siderialP: 1.0,
        synodicP: 0,
        eccentricity: 0.017,
        incline: 0
    },
    {
        name: "Mars",
        distance: 1.5237,
        siderialP: 1.8809,
        synodicP: 779.94,
        eccentricity: 0.093,
        incline: 1.85
    },
    {
        name: "Jupiter",
        distance: 5.2028,
        siderialP: 11.862,
        synodicP: 398.9,
        eccentricity: 0.048,
        incline: 1.31
    },
    {
        name: "Saturn",
        distance: 9.5388,
        siderialP: 29.458,
        synodicP: 378.1,
        eccentricity: 0.056,
        incline: 2.49
    },
    {
        name: "Uranus",
        distance: 19.1914,
        siderialP: 84.01,
        synodicP: 369.7,
        eccentricity: 0.046,
        incline: 0.77
    },
    {
        name: "Neptune",
        distance: 30.0611,
        siderialP: 164.79,
        synodicP: 367.5,
        eccentricity: 0.010,
        incline: 1.77
    },
    {
        name: "Pluto",
        distance: 39.5294,
        siderialP: 248.5,
        synodicP: 366.7,
        eccentricity: 0.248,
        incline: 17.15
    }
];
        

Example B.73. obj41M.js
'use strict';
import {planets} from './planets.js';

const $ = function(foo) {
    return document.getElementById(foo);
}

/*
 *  Handler for radio buttons
 */
const dowhatever = function(e) {
    // først clear
    for (let planet of planets) {
        if (planet.name === e.target.id) {
            $('output').innerHTML = planet.toString();
            break;
        }
    }
}

/*
 * Here we have objects with data
 * they have no methods so
 * on load we create and assign several methods to the objects
 * defined in the array of planets
 * this method will be common, ie exist once for all objects
 * in planets
 */
const init = function() {
    const getName = function() { return this.name; }
    const getDistance = function() { return this.distance; }
    const getSid = function() { return this.siderialP; }
    const getSyn = function() { return this.synodicP; }
    const getEcc = function() { return this.eccentricity; }
    const getInc = function() { return this.incline; }
    const toString = function() {
        return '<h3>' + this.getName() + '</h3>' +
               '<p>' +
                'Distance = ' + this.getDistance() + ' AU<br/>' +
                'Siderial Period = ' + this.getSiderialP() + ' years<br/>' +
                'Synodic Period = ' + this.getSynodicP() + ' days<br/>' +
                'Eccentricity = ' + this.getEccentricity() + '<br/>' +
                'Incline = ' + this.getIncline() + '&deg;'
                '</p>';
    }
    const toRadio = function() {
        return '<input type="radio" name="planet" id="'
               + this.getName() + '"/>' + this.getName() + '<br/>';
    }

    /* attach methods to each object */
    for (let planet of planets) {
        planet.getName = getName;
        planet.getDistance = getDistance;
        planet.getSiderialP = getSid;
        planet.getSynodicP = getSyn;
        planet.getEccentricity = getEcc;
        planet.getIncline = getInc;
        planet.toString = toString;
        planet.toRadio = toRadio;
    }

    /* print radio buttons in form */
    $('theradio').innerHTML = "";
    for (let planet of planets) {
        $('theradio').innerHTML += planet.toRadio();
    }

    /*
     * handler for radio buttons
     * invoked as change event on whole form
     * no buttons required
     */
    $('formid').addEventListener('change', dowhatever);
} // end of init

window.addEventListener('load', init);
        

Example B.74. obj41M.html
<!doctype html>
<html>
    <head>
        <meta charset='utf-8'/>
        <title>Obj.4.2</title>
        <style>
            form {
                font-family: monospace;
                font-size: 1.2em;
            }
            #output {
                background-color: silver;
                font-size: 1.4em;
                min-height: 2em;
                outline: 1px solid black;
                padding: .5em;
                width: 20em;
            }
        </style>
        <script type='module' src='./obj41M.js'></script>
    </head>
    <body>
        <h1>Obj.4.2 Planets</h1>
        <form id='formid' action='#' method='post'>
            <p id='theradio'></p>
        </form>
        <section id='output'></section>
    </body>
</html>
        

Click here!.


Assignment JS.OO.2

Write an HTML5 page with a form for entering credit card information. On submitting the form the content must be validated. For this purpose you must create a credit card object holding the following:

  • Credit card number, re the given test numbers below.
  • Expiration info, month/year
  • name,
  • cvv. Must be a three digit number except when the the credit card is from AMEX, where it must be 4 digits long.
  • A method for checking the expiration info,
  • a method for checking the validity of the number re rules described below. The method must return true or false.
  • a method checking that the cvv number is syntactically correct

Correct information should be sent to http://x15.dk/hitme.php. Incorrect should not be submitted but return an appropriate error message asking the user for correction.

You may refer to https://www.codeproject.com/tips/253704/test-credit-card-account-numbers for numbers suitable for testing.

Rules for Credit Card Number Validation

The checking mechanism is know as the Luhn algorithm[20] also known as the "modulus 10" or "mod 10" algorithm. Your validity checking method must implement the following formulation of the algorithm using an exemplary number 7992739871x

  1. From the rightmost digit (excluding the check digit) and moving left, double the value of every second digit. The check digit is neither doubled nor included in this calculation; the first digit doubled is the digit located immediately left of the check digit. If the result of this doubling operation is greater than 9 (e.g., 8 × 2 = 16), then add the digits of the result (e.g., 16: 1 + 6 = 7, 18: 1 + 8 = 9) or, alternatively, the same final result can be found by subtracting 9 from that result (e.g., 16: 16 − 9 = 7, 18: 18 − 9 = 9).
  2. Take the sum of all the digits. In the example the sum will be 67 + x.
  3. If the total modulo 10 is equal to 0 (if the total ends in zero) then the number is valid according to the Luhn formula; otherwise it is not valid. Again in the example only a 3 as the last digit would make a valid number.
Example B.75. Code Outline
'use strict';
class CreditCard {
    constructor(ccno, expdate, name, cvv) {
        this.ccnumber = ccno;
        //...
    }

    isValid() {
        // code for checking
        // return false if invalid
        // else return true
        return true;
    }
}

const validate = function(e) {
    let ccnumber = document.getElementById('ccno');
    let expdate = document.getElementById('exp').value;
    // more values from form
    let cc = new CreditCard(ccnumber.value, expdate, 'Niels', '666');
    if (!cc.isValid()) {
        ccnumber.focus();
        e.preventDefault();
        return false;
    } /* if (! other tests)

    } */ else {
        return true;
    }
}

const init = function() {
    document.getElementById('formid').addEventListener('submit', validate);
}

window.addEventListener('load', init);

Solution Assignment JS.4.2
Example B.76. obj4Module.js
'use strict';

export const $ = function(foo) {
    return document.getElementById(foo);
}

export class CreditCard {
    constructor(ccno, expdate, name, cvv) {
        this.ccnumber = ccno;
        this.expdate = expdate;
    }

    isValid() {
        let s = this.ccnumber;
        let a = [];
        let sum = 0;
        for (let i = 0; i < s.length; i++) {         // CCNO to array
            a[i] = Number(s.slice(i, i+1));
        }
        for (let i = a.length - 2; i >= 0; i -= 2) { // backw mul by 2
            a[i] *= 2;
        }
        for (let i = 0; i < a.length; i++) {         // sum of all digits
            let n = a[i];
            if (n > 9) {                             // phrasing of the algo
                n -= 9;
            }
            sum += n;
        }
        return !(sum % 10) // zero is valid mod 10, but zero is false, hence !
    }

    isExpDate() {
        let now = new Date();
        let a = this.expdate.split("/");
        let norm = Number(a[1] * 100) + Number(a[0]);
        now = now.getFullYear() % 100 * 100 + now.getMonth() + 1;
        if (norm >= now) {
            return true;
        } else {
            return false;
        }
    }
}

        

Example B.77. obj4M.js
'use strict';
import {$, CreditCard} from './obj4Module.js';

const validate = function(e) {
    let ccnumber = $('ccno');
    let expdate = $('exp');
    let cc = new CreditCard(ccnumber.value, expdate.value, 'Niels', '666');
    if (!cc.isValid()) {
        ccnumber.focus();
        e.preventDefault();
        $('errmsg').innerHTML = 'Erroneous CreditCard Number';
        return false;
    } else if (!cc.isExpDate()) {
        exp.focus();
        e.preventDefault();
        $('errmsg').innerHTML = 'Erroneous Expiration Date';
        return false;
    } else {
        return true;
    }
}

const init = function() {
    $('formid').addEventListener('submit', validate);
}

window.addEventListener('load', init);
        

Example B.78. obj4M.html
<!doctype html>
<html>
    <head>
        <meta charset='utf-8'/>
        <title>Obj.4.2</title>
        <style>
            form {
                font-family: monospace;
                font-size: 1.2em;
            }
            #errmsg {
                color: red;
                font-weight: 900;
            }
        </style>
        <script type='module' src='./obj4M.js'></script>
    </head>
    <body>
        <h1>Obj.4.2 Credit Card Numbers</h1>
        <p id='errmsg'></p>
        <form id='formid' action='http://x15.dk/hitme.php' method='post'>
            <p>CCNo: <input type='text' name='ccno' id='ccno'/></p>
            <p>&nbsp;Exp: <input type='text' name='exp' id='exp'/></p>
            <p>&nbsp; &nbsp; &nbsp; <input type='submit' value='go'/></p>
        </form>
    </body>
</html>
        

Click here!.


Assignment JS.OO.4

In the beginning were the natural numbers, ℕ, they are the numbers used for counting. Later we got what is today know as the integers, ℤ, by adding zero, 0, and the negative numbers. Later again we got the fractions, in mathematical speak the rational numbers ℚ, and all was good.

The rational numbers are the integers plus the fractions. Fractions are represented as the ratio between two integers. As an example, 3/7 is a rational number. The 3 is called the numerator. The 7 is called the denominator. Integers are fractions with a denominator of 1.

In this assignment you are going to create an object for rational numbers, and several methods for doing calculations with them. You see, some of us love the rational numbers because they are beautiful and exact. The real numbers ℝ, the numbers with decimals, are approximations.

  1. Create an object Rational. It must have two properties, numerator, and denominator. The creating code must give values to these two properties.
  2. If the caller does not provide parameters, the function should create the fraction 0/1.
  3. Write a method toString() that will present the fraction as the string "3/7" for example. If the fraction is an integer it should be presented as a string with only the numerator, ie without the "/1".
  4. Write a method negate which will reverse the sign of the fraction from minus to plus, or vice versa.
  5. Write a method invert which will invert the fraction by swapping the numerator and the denominator. This is sometimes the reciprocal value of the fraction.
  6. Write a method toFloat. It must return the value of the fraction as a floating point number with decimals, as approximate asd they may be.
  7. Write a method reduce. It must reduce the fraction as much as possible. The calculation is done by dividing the numerator and the denominator with their greatest common denominator, the biggest number that divides both. I shall provide you with a function gcd(n, d) that will return this number given two integers as input.
  8. Write four methods add, sub, mul, and div that will add, subtract, multiply, and divide two fractions that are both represented as Rational objects. The calculations must always end with reducing the resulting fractions.
  9. /**
     * The world's oldest non trivial algorithm:
     * Euclid's Elements, Book 7, ca. 300 BC.
     * Based on
     * if r = the remainder from the division a/b,
     * then a og b's commom divisors are the same as b's and r's.
     * This means that gcd(a,b) = gcd(b,r)
     *
     * By doing this continuously we get:
     * gcd(36,20) = gcd(20,16) = gcd(16,4) = gcd(4,0)
     * It is possible to prove that we will end with a pair
     * where the second number is 0. When that happens
     * the first number of the pair equals their gcd.
     *
     * This is the quintessential example of recursive function programming!
     */
    
    /**
     * Euclid's algorithm
     *
     * calling sequence
     * let n = parseInt(prompt('Give me an integer:'));
     * let d = parseInt(prompt('Give me an integer:'));
     * n = Number(n);
     * d = Number(d);
     * gcd(n, d);
     */
    let gcd = function (a, b) {
        if (b == 0) {
            return a;
        } else {
            return gcd(b, a % b);
        }
    }
    
  10. In order to test your code, of course you must create an HTML5 page explaining the fraction calculation rules to children in 3rd-4th grade. This page must have a fraction calculator to support the explanations.
Solution Assignment JS.4.3
Example B.79. Rational.js
'use strict';
import {MyMath} from './MyMath.js';

export class Rational {
    constructor(n, d) {
        this.numerator = n ? Number(n) : 0;
        this.denominator = d ? Number(d) : 1;
        this.reduce();
    }

    toString() {
        let s = '';
        if (this.numerator * this.denominator < 0) {
            s = '-';
        }
        s += Math.abs(this.numerator);
        if (Math.abs(this.denominator) === 1) {
            return s;
        } else if (this.numerator === 0) {
            return this.numerator;
        } else {
            return s + '/' + Math.abs(this.denominator);
        }
    }

    reduce() {
        let g = MyMath.gcd(this.numerator, this.denominator);
        this.numerator /= g;
        this.denominator /= g;
        return;
    }

    invert() {
        let t = new Rational(this.denominator, this.numerator);
        return t;
    }

    negate() {
        let t = new Rational(-this.numerator, this.denominator);
        return t;
    }

    toFloat() {
        return this.numerator / this.denominator;
    }

    add(oq) {
        let nq = new Rational(this.numerator * oq.denominator + oq.numerator * this.denominator, this.denominator * oq.denominator);
        nq.reduce();
        return nq;
    }

    sub(oq) {
        return this.add(oq.negate());
    }

    mul(oq) {
        let nq = new Rational(this.numerator * oq.numerator, this.denominator * oq.denominator);
        nq.reduce();
        return nq;
    }

    div(oq) {
        return this.mul(oq.invert());
    }
}
        

Example B.80. testRational.js
'use strict';
import {Rational} from './Rational.js';
import {$} from './nQuery.js';

let str2frac = function(s) {
    let arr = s.value.split('/');
    let obj = new Rational(Number(arr[0]), Number(arr[1]));
    return obj;
}

let calc = function(ev) {
    let f1 = str2frac($('f1'));
    let f2 = str2frac($('f2'));
    switch(ev.target.id) {
        case 'plus': f1 = f1.add(f2);
            break;
        case 'minus': f1 = f1.sub(f2);
            break;
        case 'mul': f1 = f1.mul(f2);
            break;
        case 'div': f1 = f1.div(f2);
    }
    $('f1').value = f1.toString();
    $('f2').value = '';
    $('f2').focus();
}


let begin = function() {
    $('plus').addEventListener('click', calc);
    $('minus').addEventListener('click', calc);
    $('mul').addEventListener('click', calc);
    $('div').addEventListener('click', calc);
}
window.addEventListener('load', begin);

        

Example B.81. testRational.html
<!doctype html>
<html>
    <head>
        <meta charset="utf-8"/>
        <title>Object-oriented JavaScript example</title>
        <link rel='stylesheet' href='style.css'/>
        <script type='module' src='testRational.js'></script>
    </head>

    <body>
        <table>
            <tr>
                <td><label for="jscode">Enter fraction:</label></td>
                <td>&nbsp;</td>
                <td><label for="jscode">Enter fraction:</label></td>
            </tr>
            <tr>
                <td><input type="text" id="f1"/></td>
                <td>
                    <button id='plus'>+</button><br/>
                    <button id='minus'>-</button><br/>
                    <button id='mul'>*</button><br/>
                    <button id='div'>/</button>
                </td>
                <td><input type="text" id="f2"/></td>
            </tr>
        </table>

        <p></p>
    </body>
</html>


        

Show in browser.


Assignments From ???

Assignments JS.Canvas

Assignment JS.Can.0

Please refer to code in Example 11.2. Now alter the drawing of the polygon in such a way that you strike all three sides with the context method lineTo. Use a line width of 10.

Hand in an HTML5 as well as a JavaScript file. HTML5 must validate. JavaScript must be validated without errors on jslint.com, or jshint.org.

Assignment JS.Can.1

Please refer to code in Example 11.2. Now alter the drawing of the polygon in such a way that you close the region of the polygon with the context method closePath. Use a line width of 10.

Hand in an HTML5 as well as a JavaScript file. HTML5 must validate. JavaScript must be validated without errors on jslint.com, or jshint.com.

Solutions Assignment JS.Can.0 og JS.Can.1
Example B.82. Code
/*globals document, window */
'use strict';

/* 
 * assignment 0 solution
 *
 * function drawing a polygon 
 */
var poly35 = function () {
    var canvas = document.getElementById('myCanvas35');
    if (canvas.getContext) {
        var ctx = canvas.getContext('2d');

        ctx.beginPath();        // new path for polygon
        ctx.moveTo(50, 200);    // goto coordinate in canvas
        ctx.lineTo(150, 50);    // line to coordinate
        ctx.lineTo(180, 150);   // another line to coord

        ctx.lineTo(50, 200);    // connect to start of polygon

        ctx.fillStyle = 'silver';
        ctx.strokeStyle = 'black';
        ctx.lineWidth = 40;
        ctx.fill();             // fills poly
        ctx.stroke();           // draws lines    
    }
}

/* 
 * assignment 1 solution
 * function drawing a polygon 
 */
var poly36 = function () {
    var canvas = document.getElementById('myCanvas36');
    if (canvas.getContext) {
        var ctx = canvas.getContext('2d');

        ctx.beginPath();        // new path for polygon
        ctx.moveTo(50, 200);    // goto coordinate in canvas
        ctx.lineTo(150, 50);    // line to coordinate
        ctx.lineTo(180, 150);   // another line to coord

        ctx.closePath();        // close path to start of polygon

        ctx.fillStyle = 'silver';
        ctx.strokeStyle = 'black';
        ctx.lineWidth = 10;
        ctx.fill();             // fills poly
        ctx.stroke();           // draws lines    
    }
}

var init = function () {
    poly35();
    poly36();
}

window.addEventListener('load', init);

        

Example B.83. 
<!doctype html>
<html language="en">
    <head>
        <meta charset="utf-8"/>
        <title>Canvas Example II</title>
        <script src="jsCan1.js"></script>
    </head>
    <body>
        <h1>Canvas Example II, Solution 0 and 1</h1>
        <p>
            Polygon assignment 0.
        </p>
        <canvas id="myCanvas35" width="300" height="300" style="outline: 1px solid blue;">
            <p>Powered by &html;5 canvas if only</p>
        </canvas>
        <p>
            Polygon assignment 1.
        </p>
        <canvas id="myCanvas36" width="300" height="300" style="outline: 1px solid red;">
            <p>Powered by &html;5 canvas if only</p>
        </canvas>
    </body>
</html>

        

Show in browser.


Assignment JS.Can.2

Create a webpage. In the right hand side of the webpage you must create a 'toolbox' canvas with four rectangles of different sizes, and a half circle. They represent the standard shapes in the toolbox.

In addition you must create another, separate canvas on the page. Let us call it the room. The size of that canvas must be with user entered width and height. Please let it's outline be visible.

I expect an html file, a css file, and one or more js files to make up the solution.

Hint for beginners: You may prefer to work with embedded JavaScript while you test your code. Once it works as desired, separate the JavaScript into an individual .js file, test again, then hand in your solution.

Assignment JS.Can.3

With the webpage created in the previous assignment you must create an event such that you may click on a toolbox shape to select it. Then you must be able to click in the other canvas so that the standard shape will be drawn there.

The shapes must be kept at a minimum distance from the walls of the room.

This assignment should be solved by adding code to the JavaScript code file(s) made in the previous assignment.

Solution Assignment JS.4.3
Example B.84. nmlCanvas80.html
<!doctype html>
<html language="en">
    <!-- nmlCanvas80.html -->
    <head>
        <meta charset="utf-8"/>
        <title>Canvas Experiment LXXX</title>
        <style>
            canvas {
                margin-left: 10px;
                outline: 1px solid magenta;
            }
        </style>
        <script type='module' src="./nmlCanvas80.js"></script>
    </head>
    <body>
        <h1>Canvas Experiment LXXX</h1>
        <p>
            Select shapes from right canvas and reproduce them in the
            left canvas where the mouse is clicked.
        </p>
        <canvas id="myCanvas2" width="400" height="400">
            <p>Powered by &html;5 canvas</p>
        </canvas>
        <canvas id="myCanvas1" width="400" height="400">
            <p>Powered by &html;5 canvas</p>
        </canvas>
    </body>
</html>

        

Show in browser.


Example B.85. The Page JavaScript nmlCanvas80.js
'use strict';
import {Canvas} from './nmlCanvas.js';
import {Shape} from './nmlShape.js';
/*
 * nmlCanvas80.js
 */
export const initialize = function () {
    /*
     * create two canvases and put two shapes into the right one
     * via an array
     */
    mycv1 = new Canvas('myCanvas1', 'transparent');
    mycv1.canvas.addEventListener('click', select);
    mycv2 = new Canvas('myCanvas2', 'transparent');

    let shape1 = new Shape(mycv1, 20, 10, 120, 40, 'blue');
    let shape2 = new Shape(mycv1, 200, 100, 80, 60, 'green');
    shapes.push(shape1);
    shapes.push(shape2);
    repeater(mycv1, shapes);
}

const redraw = function (cv, arr) {
    /* clear and redraw canvas from an array */
    cv.clear();
    cv.prep();
    for (let shape of arr) {
        shape.draw();
    }
}

const repeater = function (cv, arr) {
    /* if this is an animation build a setInterval loop here
     * if not, just draw
     */
    redraw(cv, arr);
}

const select = function (ev) {
    for (let shape of shapes) {
        let cx = shape.ctx;
        cx.beginPath();
        cx.rect(shape.x, shape.y, shape.width, shape.height);
        cx.closePath();
        let bb = this.getBoundingClientRect();    // get canvas as std obj
        // convert mouse coordinates to canvas coordinates
        let x = (ev.clientX - bb.left) * (this.width / bb.width);
        let y = (ev.clientY - bb.top) * (this.height / bb.height);
        if (cx.isPointInPath(x, y)) {
            console.log('hit');
            // we're in a loop, is this array element the
            // one we clicked? If yes click in other canvas
            mycv2.canvas.addEventListener('click', function placeInRoom(e) {
                console.log('place');
                let bb1 = this.getBoundingClientRect();    // yes
                // other canvas as std object
                // convert mouse coordinates to canvas coordinates
                let x1 = (e.clientX - bb1.left) * (this.width / bb1.width);
                let y1 = (e.clientY - bb1.top) * (this.height / bb1.height);
                let obj = new Shape(mycv2, x1, y1,
                            shape.width, shape.height,
                            shape.color);
                othershapes.push(obj);
                console.log(othershapes);
                mycv1.canvas.removeEventListener('click', select);
                repeater(mycv2, othershapes);
                mycv2.canvas.removeEventListener('click', placeInRoom);
                mycv1.canvas.addEventListener('click', select);
            });
        } else {
            // window.alert("nohit: "+x+","+y);
        }
    }
}

let mycv1;
let mycv2;
let shapes = [];
let othershapes = [];

window.addEventListener('load', initialize);

        

Example B.86. The Standard Canvas Object nmlCanvas.js
import {$} from './nQuery.js';
/**
 * Canvas object
 */
export class Canvas {
    constructor(canvasId, color) {
        this.canvas = $(canvasId);
        this.context = this.canvas.getContext("2d");
        this.color = color;
        this.prep();
    }
    prep() {
        this.context.fillStyle = this.color;
        this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
    }
    clear() {
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
    }
    getContext() {
        return this.context;
    }
    getHeight() {
        return this.canvas.height;
    }
    getWidth() {
        return this.canvas.width;
    }
};
        

Example B.87. The Standard Shape Object nmlShape.js
'use strict';

/**
 * Shape object, simple
 */
export class Shape {
    constructor(cv, x, y, width, height, color) {
        this.ctx = cv.context;
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
        this.color = color;
    }

    draw() {
        this.ctx.fillStyle = this.color;
        this.ctx.fillRect(this.x, this.y, this.width, this.height);
    }
};

        

Assignment JS.Can.4

With the result of the previous assignment you must improve the placing of the shapes such that in addition to the minimum distance from the walls, the shapes may touch, but not overlap each other.

Again, this assignment should be solved by adding code to the JavaScript code files resulting from the previous assignment.

Solution Assignment JS.Can.4 Pseudo Code
Example B.88. Pseudo Code

  • Describe in words what "not overlapping" means. Discuss it in your group. When you agree
  • Code the Shape.isOverlapping(obj) method as part of the Shape class. Internally this refers to the current object. The parameter obj must refer to the object with which we test for overlap. The method must return true if there's an overlap, otherwise it must result false.
  • Whenever a shape is placed in the room, or moved inside the room, it must be tested for overlap against all objects already in that room.

Assignment JS.Can.5
  1. Write this HTML5 page. The outlined area is a canvas with the id myCanvas0. Show it to your prof.
    Figure B.1. 

  2. Create the necessary JavaScript to fill the canvas with fillStyle = 'silver'. Show it to your prof.
    Figure B.2. 

  3. This is a die in perspective:
    Figure B.3. 

    Here is the die seen from one side:
    Figure B.4. 

  4. Construct a die seen from one side with canvas drawing tools. Draw it on the canvas. Then show it to your prof.
    Figure B.5. 

  5. Change your HTML5 to add a Play! button such that when you click it, the code rolls the die; wipes the canvas; and redraws the canvas with the die reflecting its new value.
  6. Change the number of dice from 1 to 5. The behaviour, ie rolling the individual die to a new value when hitting play, must be on all the 5 dice.
  7. Add a lock to a die when you click it. If locked, the die does not change its value when you click Play! If you click a locked die, it must be released.

Click here! (Teacher's host only).

Solution Assignment JS.5.0 - Yatzy (Partial Solution)
Example B.89. The Page index.html
<!doctype html>
<html>
    <head>
        <title>Assignment 5.0</title>
        <meta charset="utf-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
        <link rel='stylesheet' href='./css/index.css'/>
        <script type='module' src='./js/index.js'></script>
    </head>
    <body>
        <header>
            <h1>Canvas Dice</h1>
        </header>
        <main id='main'>
            <section id='divr'></div>
            <section id='divl'></div>
        </main>
    </body>
</html>

        

Show in browser.


Example B.90. nmlCanvas.js
import {$} from './nQuery.js';
/**
 * Canvas object
 */
export class Canvas {
    constructor(canvasId, color) {
        this.canvas = $(canvasId);
        this.context = this.canvas.getContext("2d");
        this.color = color;
        this.prep();
    }
    prep() {
        this.context.fillStyle = this.color;
        this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
    }
    clear() {
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
    }
    getContext() {
        return this.context;
    }
    getHeight() {
        return this.canvas.height;
    }
    getWidth() {
        return this.canvas.width;
    }
};
        

Example B.91. nDie.js
/**
 * Die object
 */
export class Die {
    constructor(cv, x, y, width, height, color) {
        this.ctx = cv.getContext();
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
        this.color = color;
        this.locked = false;
        this.value = 0;
        this.roll();
    }

    draw() {
        this.ctx.clearRect(this.x, this.y, this.width, this.height);
        this.ctx.beginPath();
        this.ctx.fillStyle = this.color;
        this.ctx.rect(this.x, this.y, this.width, this.height);
        if (this.locked) {
            this.ctx.fillStyle = "yellow";
            this.ctx.strokeStyle = "black";
            this.ctx.lineWidth = 2;
            this.ctx.stroke();
        }
        this.ctx.fill();
        this.ctx.closePath();
        this.drawFace();
    }

    drawFace() {
        var offset = 12;
        var r = 5;
        this.ctx.fillStyle = 'black';
        if (this.value % 2 === 1) {
            this.ctx.beginPath();
            this.ctx.arc(this.x + this.width / 2, this.y + this.height / 2,
                         r, 0, Math.PI * 2, true);
            this.ctx.fill();
            this.ctx.closePath();
        }
        if (this.value > 1) {
            this.ctx.beginPath();
            this.ctx.arc(this.x + this.width - offset, this.y + offset,
                         r, 0, Math.PI * 2, true);
            this.ctx.arc(this.x + offset, this.y + this.height - offset,
                         r, 0, Math.PI * 2, true);
            this.ctx.fill();
            this.ctx.closePath();
        }
        if (this.value >= 4) {
            this.ctx.beginPath();
            this.ctx.arc(this.x + offset, this.y + offset,
                         r, 0, Math.PI * 2, true);
            this.ctx.arc(this.x + this.width - offset, this.y + this.height - offset,
                         r, 0, Math.PI * 2, true);
            this.ctx.fill();
            this.ctx.closePath();
        }
        if (this.value === 6) {
            this.ctx.beginPath();
            this.ctx.arc(this.x + offset, this.y + this.height / 2,
                         r, 0, Math.PI * 2, true);
            this.ctx.arc(this.x + this.width - offset, this.y + this.height / 2,
                         r, 0, Math.PI * 2, true);
            this.ctx.fill();
            this.ctx.closePath();
        }
    }

    move(dx, dy) {
        this.x += dx;
        this.y += dy;
    }

    roll() {
        let e = Math.floor(Math.random() * 6 ) + 1;
        this.value = e;
    }

    lockUnlock() {
        this.locked = this.locked ? false : true;
    }
};

        

Example B.92. nYPad.js
import {$} from './nQuery.js';
//import {record, dice, locked, magic} from './nYLogics.js';

/*
 * nYPad.js Niels' Yatzy Pad
 * @version 0.9, 2016-04
 * @author nml
 */

const NoOfDice = 5;
const headers = ["Play", " ", "You", "Opponent"];
export let rec;

const plays = [
    ["1's", NoOfDice * 1],
    ["2's", NoOfDice * 2],
    ["3's", NoOfDice * 3],
    ["4's", NoOfDice * 4],
    ["5's", NoOfDice * 5],
    ["6's", NoOfDice * 6],
    ["Sum", NoOfDice * 21],
    ["Bonus", 50],
    ["One pair", 2 * 6],
    ["Two pairs", 2 * 6 + 2 * 5],
    ["Three alike", 3 * 6],
    ["Four alike", 4 * 6],
    ["Small straight", 1+2+3+4+5],
    ["Large straight", 2+3+4+5+6],
    ["Full house", 3 * 6 + 2 * 5],
    ["Chance", NoOfDice * 6],
    ["Yatzy", 50],
    ["Total", "x"]
];

const generate = function() {
    let tab = document.createElement("table");
    tab.setAttribute("id", "ytable");

    let row = document.createElement("tr");
    for (let i = 0; i < headers.length; i++) {
        let cell = document.createElement("td");
        let t = document.createTextNode(headers[i]);
        cell.appendChild(t);
        row.appendChild(cell);
    }
    tab.appendChild(row);

    for (let i = 0; i < plays.length; i++) {
        let row = document.createElement("tr");

        let cell = document.createElement("td");
        if (i === 6 || i === 7 || i === plays.length - 1) {
            cell.setAttribute("style", "font-weight: 900");
        }
        let t1 = document.createTextNode(plays[i][0]);
        cell.appendChild(t1);
        row.appendChild(cell);

        cell = document.createElement("td");
        cell.setAttribute("class", "right");
        t1 = document.createTextNode(plays[i][1]);
        cell.appendChild(t1);
        row.appendChild(cell);

        let t2 = document.createTextNode(" ");
        cell = document.createElement("td");
        cell.setAttribute("id", "me" + i);
        cell.setAttribute("class", "right");
        if (!(i === 6 || i === 7 || i === plays.length - 1)) {
            cell.addEventListener("click", function rec() {
                record("me" + i);
                console.log(cell.getAttribute("id"));
            });
        }
        cell.appendChild(t2);
        row.appendChild(cell);

        cell = document.createElement("td");
        cell.setAttribute("id", "they" + i);
        cell.appendChild(t2);
        row.appendChild(cell);

        tab.appendChild(row);
    }
    return tab;
}

export const disScorePad = function(foo) {
    let c = generate();
    $(foo).appendChild(c);
}
        

Assignment JS.Can.7 - TicTacToe

Today we, you shall write a tic-tac-toe game. The game is also known as naughts and crosses.

Take a look at this page: Click here :)

And then consider

  • A win is three Xs or three Os in a line. A line may be a row, a column, or a diagonal.
  • If there's two Xs or two Os in a line, then the opponent must play the third to block or to win.
  • Knowing where to play when you mustn't block or play to win.

The playing ground, the board, is a 3x3 matrix. The obvious choice will be to map the board into an array. You have two choices. A one dimensional array such as

[0, 1, 2, 3, 4, 5, 6, 7, 8]

which you may decide to look at as

[0, 1, 2
 3, 4, 5
 6, 7, 8]

is definitely simpler. You may also choose

[ [0, 1, 2],
  [0, 1, 2],
  [0, 1, 2] ]

Either way, looping through the board to check for points will be finding three rows, three columns, two diagonals, and counting points in them. Associating a 4 with an X, and a 1 with an O, a play means placing an X or an O on the screen, and a 4, or a 1 in the array.

  • 12 points show 3 Xs, and the human wins.
  • 3 points show 3 Os and the computer wins.
  • 8 means 2 Xs, 2 means 2 Os. In either case the human or the computer must make a blocking move to survive.
  • Any other sum, the human or the computer must choose an intelligent next play.

We need the following functionality:

  • To (re)initialize the array and the bord in case of a new game.
  • To put an O into an empty place. The empty place given as a parameter.
  • Read the board and populate the array with 4s for every X. Count rows, columns, and diagonals for points.
  • Find an intelligent empty square for the computer to play

Write the handlers, functions, and create the proper event listeners to activate them.

Test and repeat until it works.

Model Solution Assignment JS.Can.7 - TicTacToe
Example B.93. The Page index.html
<!doctype html>
<html>
    <head>
        <meta charset='utf-8'/>
        <meta name='viewport' content='width=device-width, initial-scale=1.0'/>
        <title>TicTacToe NOO</title>
        <link href='./css/index.css' rel='stylesheet'/>
        <script src='./js/nQuery.js'></script>
        <script src='./js/tictactoe.js'></script>
    </head>
    <body>
        <header id='head'></header>
        <div id='ttt'></div>
        <footer id='foot'></footer>
    </body>
</html>

Click Here!


Example B.94. The JS, tictactoe.js
'use strict';


const SIZE = 58;
const DIM = 3;
const STRATEGY = [4, 0, 2, 6, 8, 1, 3, 5, 7];
let foot;
let head;
let node;
let squares = [];

/*
 * Check rows, columns, and diagonals
 * for a winning situation
 * n is the winning score
 * if n == 12 human wins
 * if n == 3 program wins
 */
const check = function (n) {
    console.log(`check, squares = ${squares}`)
    for (let i = 0; i < 7; i += 3) {   // check rows
        let sum = squares[i] + squares [i + 1] + squares[i + 2];
        if (sum === n) {
            return true;
        }
    }

    for (let i = 0; i < 3; ++i) {      // check columns
        let sum = squares[i] + squares [i + 3] + squares[i + 6];
        if (sum === n) {
            return true;
        }
    }

    let sum = squares[0] + squares[4] + squares[8];  // check diag \
    if (sum === n) {
        return true;
    }

    sum = squares[2] + squares[4] + squares[6];  // check diag /
    if (sum === n) {
        return true;
    }

    return false;
};

/*
 * Check for blocking, winning, or optimal move
 * if a row, column, or diagonal has
 * an 8, play to block
 * a 2, play to win
 * else find optimal move in STRATEGY
 */
const whereToPlay = function() {
    for (let i = 0; i < 7; i += 3) {   // check rows
        let sum = squares[i] + squares [i + 1] + squares[i + 2];
        if (sum === 8 || sum === 2) {
            for (let j = 0; j < 3; ++j) {
                if (squares[i + j] === 0)
                    return i + j;
            }
        }
    }
    for (let i = 0; i < 3; ++i) {      // check columns
        let sum = squares[i] + squares [i + 3] + squares[i + 6];
        if (sum === 8 || sum === 2) {
            for (let j = 0; j < 7; j += 3) {
                if (squares[i + j] === 0)
                    return i + j;
            }
        }
    }

    let sum = squares[0] + squares[4] + squares[8];  // check diag \
    if (sum === 8 || sum === 2) {
        for (let i = 0; i < 9; i += 4) {
                if (squares[i] === 0)
                    return i;
        }
    }

    sum = squares[2] + squares[4] + squares[6];  // check diag /
    if (sum === 8 || sum === 2) {
        for (let i = 0; i < 7; i += 2) {
                if (squares[i] === 0)
                    return i;
        }
    }

    for (let i = 0; i < DIM * DIM; ++i) {
        let j = STRATEGY[i];
        if (squares[j] === 0)
            return j;
    }
};

/*
 * Computer plays after human move
 * function whereToPlay decides where
 */
const playO = function () {
    let i = whereToPlay();
    if (squares[i] === 0) {
        $('ij'+i).removeEventListener('click', play);
        let cx = $('ij'+i).getContext('2d');
        cx.lineWidth = 5;
        cx.strokeStyle = 'red'
        cx.arc(SIZE/2, SIZE/2, SIZE/2 - cx.lineWidth/2, 0, Math.PI * 2, false);
        cx.stroke();
        squares[i] = 1;
    }
    if (check(3)) {                                // score the x
        $('feedback').innerHTML = 'I win!';                  // and go check
        freeze();
    }
    // dont call play for human to play.
    // It operates on click
};

/*
 * Human plays by click event
 * if no win pass to computer
 */
const play = function (e) {
    $(e.target.id).removeEventListener('click', play);
    let cx = $(e.target.id).getContext('2d');           // draw an x
    cx.lineWidth = 5;
    cx.strokeStyle = 'blue'
    cx.moveTo(0, 0);
    cx.lineTo(SIZE, SIZE);
    cx.moveTo(SIZE, 0);
    cx.lineTo(0, SIZE);
    cx.stroke();
    let i = e.target.id.charAt(2);
    squares[i] = 4;
    if (check(12)) {                                // score the x
        $('feedback').innerHTML = 'Human wins!';                  // and go check
        freeze();
    } else {
        playO();
    }
};

/*
 * Remove eventlisteners if game is over
 */
const freeze = function () {
    for (let i = 0; i < DIM * DIM; ++i) {
        $('ij' + i).removeEventListener('click', play);
    }
};

const wipeHTML = function (elm) {
    let dombranch = $(elm);
    while (dombranch.firstChild) {
        dombranch.removeChild(dombranch.firstChild);
    }
};

const initLogics = function () {
    for (let i = 0; i < DIM * DIM; i++) {
        squares[i] = 0;
    }
    return;
};

const setupPage = function () {
    node = $('ttt');
    head = $('head');
    foot = $('foot');
    // create board
    let h1 = document.createElement('h1');
    let h1t = document.createTextNode('TicTacToe');
    h1.appendChild(h1t);
    head.appendChild(h1);

    let k = 0;
    for (let i = 0; i < DIM; ++i) {
        for (let j = 0; j < DIM; ++j) {
            let sq = document.createElement('canvas');
            sq.setAttribute('width', SIZE);
            sq.setAttribute('height', SIZE);
            sq.setAttribute('id', 'ij' + k++);
            sq.addEventListener('click', play);
            node.appendChild(sq);
        }
    }

    let p = document.createElement('p');
    p.setAttribute('id', 'feedback');
    foot.appendChild(p);

    p = document.createElement('p');
    let butt = document.createElement('button');
    butt.setAttribute('type', 'button');
    let t = document.createTextNode('Reset/New Game!');
    butt.appendChild(t);
    p.appendChild(butt);
    foot.appendChild(p);
    butt.addEventListener('click', restart);

};

const restart = function () {
    wipeHTML('head');
    wipeHTML('ttt');
    wipeHTML('foot');
    initLogics();
    setupPage();
};

window.addEventListener('load', restart);

Assignments From Chapter 12

Assignments JS Animation

Assignment JS.Anim.N0

On the basis of the code from today's lesson please place 10 or more moving disks on a 500 x 600 canvas. They must all move linearly and bounce when hitting the wall. They must have different colors, and different sizes. Their trajectories and their speeds must be randomized.

Hand in as jsanimN0.js, and jsanimN0.html

Model Solution Assignment JS.Anim.N0
Example B.95. nmlUmo0.js
'use strict';

/**
 * Umo object, version 0.9.1
 */
export class Umo {
    constructor(canvas, color) {
        this.canvas = canvas;
        this.r = Math.random() * 9 + 3;
        this.x = Math.random() * (this.canvas.getWidth() - this.r - 2);
        this.y = Math.random() * (this.canvas.getHeight() - this.r - 2);
        this.dx = Math.random() * 3;
        this.dy = Math.random() * 3;
        this.color = color;
    }

    draw() {
        this.canvas.getContext().beginPath();
        this.canvas.getContext().strokeStyle = '#222';
        this.canvas.getContext().fillStyle = this.color;
        this.canvas.getContext().arc(this.x, this.y, this.r,
                                     0, Math.PI * 2,
                                     false);
        this.canvas.getContext().fill();
        this.canvas.getContext().stroke();
        this.canvas.getContext().closePath();
    }

    move() {
        if (this.x + this.dx + this.r > this.canvas.getWidth()
                || this.x + this.dx - this.r < 0)
              this.dx = -this.dx;
        if (this.y + this.dy + this.r > this.canvas.getHeight()
                || this.y + this.dy - this.r < 0)
              this.dy = -this.dy;

        this.x += this.dx;
        this.y += this.dy;
    }

    toString() {
        s = '';
        s += this.x + ':' + this.y + ', ' + this.r + " \n " + this.color;
        return s;
    }
};

Example B.96. jsanimN0.html
<!doctype html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Canvas Animation of at Least 10 Objects</title>
        <script type='module'>
            import {Canvas} from './nmlCanvas.js';
            import {Umo} from './nmlUmo0.js';
            import {randomColor, animate} from './nQuery.js';

            const initialize = function () {
                canvas = new Canvas('canvas', '#ffff88');

                for (let i = 0; i < COUNT; i += 1) {
                    let bubble = new Umo(canvas, randomColor());
                    bubbles.push(bubble);
                }
                animate(canvas, bubbles, null, 25);
            }

            let bubbles = [];
            let canvas;
            let COUNT = 3;
            window.addEventListener('load', initialize);
        </script>
    </head>
    <body>
        <h1><code>jsanimN0.html</code></h1>
        <section>
            <canvas id='canvas' width='500' height='600'>
                If you see this text, your browser is very old.
            </canvas>
        </section>
    </body>
</html>

Click here!


Assignment JS.Anim.N1

On the basis of the code from today's lesson please place 10 or more moving disks on a 500 x 600 canvas. They must all move linearly and bounce when hitting the wall. They must have different colors, and different sizes. Their trajectories and their speeds must be randomized.

The above is equivalent to the previous assignment. Now you must change the coding, so that when a moving disk hits another disk, the bigger disk swallows the the smaller disk, and its area grows with the area of the swallowed disk. When there is but one disk left, the animation must stop, and an appropriate message must appear.

Hand in as jsanimN1.js, and jsanimN1.html

Model Solution Assignment JS.Anim.N1
Example B.97. nmlUmo1.js
'use strict';
import {roll} from './nQuery.js';

/**
 * Umo object, version 1.0 with hittest
 */
export class Umo {
    constructor(canvas, color, id) {
        this.canvas = canvas;
        this.r = roll(15, 3);
        this.x = roll(this.canvas.getWidth() - 2 * this.r, this.r);
        this.y = roll(this.canvas.getHeight() - 2 * this.r, this.r);
        this.dx = roll(2, 2);
        this.dy = roll(2, 2);
        this.color = color;
        this.id = id;
    }

    draw() {
        this.canvas.getContext().beginPath();
        this.canvas.getContext().strokeStyle = '#222';
        this.canvas.getContext().fillStyle = this.color;
        this.canvas.getContext().arc(this.x, this.y, this.r,
                                     0, Math.PI * 2,
                                     false);
        this.canvas.getContext().fill();
        this.canvas.getContext().stroke();
        this.canvas.getContext().strokeText(this.id, this.x -3, this.y+3);
        this.canvas.getContext().closePath();
    }

    move() {
        if (this.x + this.dx + this.r > this.canvas.getWidth()
                || this.x + this.dx - this.r < 0)
              this.dx = -this.dx;
        if (this.y + this.dy + this.r > this.canvas.getHeight()
                || this.y + this.dy - this.r < 0)
              this.dy = -this.dy;

        this.x += this.dx;
        this.y += this.dy;
    }

    getArea() {
        return Math.PI * Math.pow(this.r, 2);
    }

    hittest(ob) {
        return Math.sqrt(Math.pow(this.x - ob.x, 2) + Math.pow(this.y - ob.y, 2)) <= (this.r + ob.r)
    }

    toString() {
        let s = '';
        s += this.x + ':' + this.y + ', ' + this.r + " \n " + this.color;
        return s;
    }
};

Example B.98. jsanimN1.html
<!doctype html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Canvas Animation of at Least 10 Objects</title>
        <script type='module'>
            import {Canvas} from './nmlCanvas.js';
            import {Umo} from './nmlUmo1.js';
            import {randomColor} from './nQuery.js';

            const redraw = function () {
                canvas.clear();     // clear canvas
                canvas.prep();      // prep canvas with background color
                for (let i = 0; i < bubbles.length; i++) {
                    bubbles[i].move();
                    for (let j = i + 1; j < bubbles.length; j++) {
                        if (bubbles[i].hittest(bubbles[j])) {
                            let a1 = bubbles[i].getArea();
                            let a2 = bubbles[j].getArea();
                            a1 += a2;
                            a1 /= Math.PI;
                            a1 = Math.sqrt(a1);
                            if (bubbles[i].r >= bubbles[j].r) {
                                bubbles[i].r = a1;
                                console.log('ll ' + bubbles.length);
                                bubbles.splice(j, 1);
                                console.log('go away ' + j);
                                console.log('ll ' + bubbles.length);
                            } else {
                                bubbles[j].r = a1;
                                console.log('ll ' + bubbles.length);
                                bubbles.splice(i, 1);
                                console.log('go away ' + i);
                                console.log('ll ' + bubbles.length);
                            }
                        }
                    }
                    bubbles[i].draw();
                }
            }

            const repeater = function () {
                setInterval(redraw, 25);
            }

            const initialize = function () {
                console.log('big bang');
                console.log('--------');
                canvas = new Canvas('canvas', '#ffff88');

                for (let i = 0; i < COUNT; i += 1) {
                    let bubble = new Umo(canvas, randomColor(), i);
                    bubbles.push(bubble);
                }
                repeater();
            }

            let bubbles = [];
            let canvas;
            const COUNT = 12;

            window.addEventListener('load', initialize);
        </script>
    </head>
    <body>
        <h1><code>jsanimN1.html</code></h1>
        <section>
            <canvas id='canvas' width='500' height='600'>
                If you see this text, your browser is very old.
            </canvas>
        </section>
    </body>
</html>

Click here!


Assignment JS.Anim.N2

Copy the results of the previous assignment so that jsanimN1.js becomes jsanimN2.js, and jsanimN1.html becomes jsanimN2.html.

Change the moving objects so that half of them are squares or rectangles and the other half remain disks (circles). When they move around, occasionally one hits another. The assignement is the same as the previous one. The shape with the larger area swallows the smaller and grows proportionately. When one shape is left the animation must stop with an appropriate message.

Hand in as jsanimN2.js, and jsanimN2.html

Model Solution Assignment JS.Anim.N2
Example B.99. Missing jsanimN2.html

Assignment JS.Anim.N3

If you have time create a pale blue disk, small in relation to a 800 x 400 canvas, and animate it so that it moves around the canvas in an elliptical orbit around the yellow center of the black canvas.

Hand in as jsanimN3.js, and jsanimN3.html

Model Solution Assignment JS.Anim.N3
Example B.100. jsanimN3.html
<!doctype html>
<html>
    <head>
        <title>NMLs JS Ellipse</title>
        <meta charset='utf-8'/>
        <meta name='viewport' content='width=device-width, initial-scale=1.0'>
        <style>
            canvas {
                margin: 1em auto;
                padding: 0;
            }
        </style>
        <script type='module'>
            import {Canvas} from './nmlCanvas.js';
            import {Disk} from './nmlDisk.js';
            import {animate} from './nQuery.js';

            const go = function() {
                let cv = new Canvas('ellipsen', 'black');
                let dot = new Disk(cv, 0, 0, 5, '#afeeee', 0, 2, 390, 190); // the pale blue dot
                things.push(dot);
                dot = new Disk(cv, 0, 0, 5, 'yellow', 0, 0, 0, 0);          // the gravitational center
                things.push(dot);
                animate(cv, things, onoff, 100)
            }
            let onoff;
            let things = [];
            window.addEventListener('load', go);
        </script>
    </head>
    <body>
        <header><h1>TODO write content</h1></header>
        <canvas id='ellipsen' width='800' height='400'></canvas>
        <footer>footer</footer>
    </body>
</html>

Click here!




[20] This adapted from https://en.wikipedia.org/wiki/Luhn_algorithm. If you refer to that beware of the psudocode implementation. It is in a programming language quite different from JavaScript