JavaScript


Creative Commons License
This -JavaScript- tutorial is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License
Preamble

This tutorial is about basic and advanced features of JavaScript (a.k.a. ECMAScript). Since JavaScript is not statically typed, early fault detection at compilation time is impossible. JavaScript may thus benefit from being substituted by TypeScript (here…). This tutorial further discusses JavaScript 6 & 7 (a.k.a. ECMAScript 2015 & 2016). Key novelties of ECMAScript 2015 are summarized here

Headlines
The big picture: As “The language of Internet”, JavaScript shares such title with Java. The latter rather takes place at the backend while the former dominates at the frontend (as browser native programming language). Things radically changed with Node.js.

JavaScript positioning in programming languages' competition

Rule(s)

Styles of programming

Rule(s)

JavaScript functions are powerful tools in the sense that they have a first-class status as plain objects. The absence of static typing in JavaScript suppresses any control about functions' arguments and returned objects: their types and number.

Rule(s)

Example

var my_function = function (my_parameter) {
    // Forbidden in strict mode (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/):
    // console.log(arguments.callee);
    return ++my_parameter;
};
console.assert(typeof my_function === 'function');
console.assert(my_function.constructor === Function);
console.log(my_function(0)); // '1' is displayed...

Arguments' default values

Rule(s)

Example

// 'Run' as a static function in a class:
static Run(name = "FranckBarbier", job = "Trainer") { …

Anonymous functions

Rule(s)

Example

var my_f = function (my_g) { // 'my_f' is defined
    console.log("'arguments' is a predefined array that lets the access to the passed arguments: " + arguments.length);
    my_g();
};
my_f(function () { // 'my_f' is called
    console.log("This no-name function plays the role of 'my_g' in 'my_f'");
});

Lambda expressions

Rule(s)

Example

const f = (g, i = 0) => { // 'f' is defined by means of a lambda expression
    console.log(g(i));
};
// Calling f...
f(function (my_i) { // 'f' is called with an anonymous function as parameter
    return ++my_i;
}/*, 1*/);
// Calling f, another style...
f(my_i => { // 'f' is called with an arrow as parameter
    return ++my_i;
}/*, 1*/);
// Calling f, yet another style...
f(my_i => ++my_i/*, 1*/); // Implicit 'return'

Variable number of arguments

Rule(s)

Example

const h = function (a = -1, ...bs /*, c*/ /* <- error! */) {
    let sum = a;
    bs.forEach(b => {
        console.log("b: " + b);
        sum += b;
    });
    console.log("Sum: " + sum);
};
// Note that default value for 'a' is useless:
h(1, 2, 3); // 'Sum: 6' is displayed, i.e., '1' is substituted for 'a'
h(...[1, 2, 3]); // <=> 'h(1, 2, 3);'

Meta-functions

Rule(s)

Example

var her_function = function (her_parameter) {
    console.log("'this': " + this + " 'her_parameter': " + her_parameter);
    // Forbidden in strict mode (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/):
    // console.log(arguments.callee);
};
// First arg. plays the role of 'this' inside the function while '0' is substituted for 'her_parameter':
her_function.call("Edith", 0); // ''this': Edith 'her_parameter': 0' is displayed...
// First arg. plays the role of 'this' inside the function while '[0]' is a one-element array containing '0' as unique element:
her_function.apply("Edith", [0]); // ''this': Edith 'her_parameter': 0' is displayed...

Generator functions

Rule(s)

Example Generator.js.zip 

let meal = {when: "today", taste: "medium"};

let {when: first_part, ...remaining_part} = meal;

// window.alert(first_part); // 'today'
// window.alert(JSON.stringify(remaining_part)); // '{"taste":"medium"}'

meal[Symbol.iterator] = function* (from = "yesterday") { // Key 'Symbol.iterator' (i.e., '@@iterator') is added as 'Generator' object
    yield from;
    yield "today";
    yield "tomorrow";
};
// window.alert(Object.getOwnPropertySymbols(meal)[0] === Symbol.iterator); // 'true'

let iterator = meal[Symbol.iterator]();
// window.alert(iterator.next().value); // 'yesterday'

// Spread syntax only applies on iterable objects:
window.alert([...meal]); // 'yesterday,today,tomorrow'

for (let value of meal) {
    console.log(value); // 'yesterday' 'today' 'tomorrow'
}
Although JavaScript 5 is not a OO programming language, OO principles may apply through a disciplined design.

Rule(s)

Example (old fashion) Temperature.js.zip 

var temperature = { // The notion of temperature
    Min: -273.15, // in Celsius
    _value: 0, // in Celsius
    _step: 0.0001,
    asCelsius: function () {
        return this._value;
    }
    // Etc.
};

Example (JavaScript 6 alternate syntax) Temperature.js.zip 

var temperature = { // The notion of temperature
    Min: -273.15, // in Celsius
    _value: 0, // in Celsius
    _step: 0.0001,
    asCelsius() { // Not so clear because confusing with a call of 'asCelsius'...
        return this._value;
    }
    // Etc.
};

Rule(s)

Example (better, but still not up-to-date) Temperature.js.zip 

var Temperature_unit = {
    Celsius: 0,
    Fahrenheit: 1,
    Kelvin: 2
};

var Invalid_temperature_exception = function (value) { // Exception type
    this._message = "Invalid temperature";
    this._value = value;
};

var Temperature = function (value, unit) {
    this.Min = -273.15; // in Celsius
    this._value = 0; // in Celsius
    switch (unit) {
        case Temperature_unit.Celsius:
            this._value = value;
            break;
        case Temperature_unit.Fahrenheit:
            this._value = (value - 32.) * 5. / 9.;
            break;
        case Temperature_unit.Kelvin:
            this._value = value + this.Min;
            break;
        default:
            throw "Illegal temperature unit";
    }
    if (this._value < this.Min) {
        throw new Invalid_temperature_exception(this._value);
    }
    this._step = 0.0001;
    this.asCelsius = function () {
        return this._value;
    };
    this.asFahrenheit = function () {
        return 9. / 5. * this._value + 32.;
    };
// Better:
    Object.defineProperty(this, "asKelvin", {value: function () {
                return this._value - this.Min;
            }, enumerable: true, configurable: false, writable: false}
    );
    // Etc.     
};

Example (general approach)

const Dish = function (main_ingredient) {
    this.ingredients = [];
    this.main_ingredient = main_ingredient;
    this.add_ingredient = function (ingredient) {
        this.ingredients.push(ingredient);
    };
};
const lasagna = new Dish(Symbol("Pasta"));
console.log(lasagna.main_ingredient); // 'Symbol(Pasta)' is displayed
lasagna.add_ingredient(Symbol("Cheese"));
console.assert(lasagna.constructor === Dish);
console.assert(Object.getPrototypeOf(lasagna) === Dish.prototype); // "Prototype inheritance"...
console.log(Dish.prototype.constructor); // Constructor function
console.assert(Dish.prototype.constructor.name === "Dish");

Rule(s)

Example Temperature.js.zip 

Object.defineProperty(Temperature.prototype, "Min", {value: -273.15, enumerable: true, configurable: false, writable: false});

Temperature.prototype.asCelsius = function () {
    return this._value;
};

Temperature.prototype.asFahrenheit = function () {
    return 9. / 5. * this._value + 32.;
};

// Better:
Object.defineProperty(Temperature.prototype, "asKelvin", {value: function () {
        return this._value - this.Min;
    }, enumerable: true, configurable: false, writable: false}
);

// Etc.

Rule(s)

Example

var Pair = (first, second) => {
    this.first = first;
    this.second = second;
};
const p = new Pair('a', 'z'); // Bug: 'TypeError: Pair is not a constructor'
Pair('a', 'z'); // Bug: 'this' is 'undefined'!

Rule(s)

Example

var Pair = function (first, second) {
    this.first = first;
    this.second = second;
};
Pair.prototype.getFirst = () => {
    return this.first;
};
const p = new Pair('a', 'z'); // This works now...
window.alert(p.getFirst()); // Bug: 'this' is 'undefined'!
Up to JavaScript 5, no native support for inheritance and polymorphism exists. However, Inheritance and polymorphism can be simulated (here…)

Example of inheritance tree (in French)

Rule(s)

Example Inheritance_polymorphism.ts.zip 

var Compte_bancaire = function (id, solde) {
    this._id = id;
    this._solde = solde;
    this._cumul_interets;
};
Compte_bancaire.Information = function () {
    return "Classe générale des comptes bancaires";
};
Compte_bancaire.prototype.id = function () {
    return this._id;
};
Compte_bancaire.prototype.solde = function () {
    return this._solde;
};
Compte_bancaire.prototype.cumul_interets = function () {
    return this._cumul_interets;
};
Compte_bancaire.prototype.mise_a_jour = function (montant) {
    this._solde += montant;
    return this._solde;
};
Compte_bancaire.prototype.taux_interet = function () {
    throw "Undefined function due to abstract nature of the class...";
};
Compte_bancaire.prototype.appliquer_taux_interet = function () {
    this._cumul_interets = this._solde * (1. + (this.taux_interet() / 100.));
};
Compte_bancaire.prototype.compareTo = function (cb) {
    return this._solde > cb._solde ? 1 : this._solde < cb._solde ? -1 : 0;
};
var Compte_cheque = function (id, solde, taux_interet, seuil) {
    Compte_bancaire.call(this, id, solde); // 'super' in Java
    this._taux_interet = taux_interet;
    this._seuil = seuil;
};
Compte_cheque.prototype = Object.create(Compte_bancaire.prototype); // Inheritance link
Compte_cheque.prototype.constructor = Compte_cheque;
Compte_cheque.Information = function () {
    return "Classe des comptes bancaires courants ou \"comptes chèque\" - rémunération à la tête du client !";
};
Compte_cheque.prototype.taux_interet = function () {
    return this._taux_interet;
};
Compte_cheque.prototype.appliquer_taux_interet = function () {
    if (this._solde > this._seuil)
        Compte_bancaire.prototype.appliquer_taux_interet.call(this); // 'super' in Java
};

Rule(s)

Example Inheritance_polymorphism.ts.zip 

console.log(Compte_bancaire.Information());
let cb = null;
try {
    cb = new Compte_bancaire("cb", 100.);
    cb.appliquer_taux_interet(); // This fails since 'Compte_bancaire' is implemented as an abstract class
    console.log(cb.cumul_interets());
} catch (e) {
    console.log(e);
}
console.log(Compte_cheque.Information());
cb = new Compte_cheque("cc", 200., 0.02, 100.);
cb.appliquer_taux_interet();
console.log(cb.cumul_interets());
Inheritance and polymorphism are natively supported from JavaScript 6 (ECMAscript 2015).

Rule(s)

Example (static attribute)

class Temperature {
    static Min = -273.15; // in Celsius
    …
}

Rule(s)

Example

class NoLanguageSettings {
    …
    static Universe_radius = 3000;
    static Half_sphere_geometry = new THREE.SphereBufferGeometry(NoLanguageSettings.Universe_radius / 25, 30, 30, 0, Math.PI);
    static _Initializer = (() => {
        NoLanguageSettings.Half_sphere_geometry.computeBoundingSphere();
    })(); // <- Caution: automatic call...
    …
}

Example (full inheritance hierarchy) Inheritance_polymorphism.ts.zip 

class Compte_bancaire_ {
    constructor(id, solde) {
        this._id = id;
        this._solde = solde;
        this._cumul_interets;
    }
    static Information() {
        return "Classe générale des comptes bancaires";
    }
    id() {
        return this._id;
    }
    solde() {
        return this._solde;
    }
    cumul_interets() {
        return this._cumul_interets;
    }
    mise_a_jour(montant) {
        this._solde += montant;
        return this._solde;
    }
    taux_interet() {
        throw "Undefined function due to abstract nature of the class...";
    }
    appliquer_taux_interet() {
        this._cumul_interets = this._solde * (1. + (this.taux_interet() / 100.));
    }
    compareTo(cb) {
        return this._solde > cb._solde ? 1 : this._solde < cb._solde ? -1 : 0;
    }
}

class Compte_cheque_ extends Compte_bancaire_ {
    constructor(id, solde, taux_interet, seuil) {
        super(id, solde);
        this._taux_interet = taux_interet;
        this._seuil = seuil;
    }
    static Information() {
        return "Classe des comptes bancaires courants ou \"comptes chèque\" - rémunération à la tête du client !";
    }
    taux_interet() {
        return this._taux_interet;
    }
    appliquer_taux_interet() {
        if (this._solde > this._seuil) {
            super.appliquer_taux_interet();
        }
    }
}

Rule(s)

Example Inheritance_polymorphism.ts.zip 

console.log(Compte_bancaire_.Information());
let cb_ = null;
try {
    cb_ = new Compte_bancaire_("cb", 100.);
    cb_.appliquer_taux_interet(); // This fails since 'Compte_bancaire' is implemented as an abstract class...
    console.log(cb_.cumul_interets());
} catch (e) {
    console.log(e);
}
console.log(Compte_cheque_.Information());
cb_ = new Compte_cheque_("cc", 200., 0.02, 100.);
cb_.appliquer_taux_interet();
console.log(cb_.cumul_interets());
JavaScript has a native introspection support.

Rule(s)

Example Introspection_utilities.js.zip 

const Get_attributes = (object) => {
    return Object.getOwnPropertyNames(object);
};

const Get_functions = (object) => {
    let functions = [];
    for (let f in object)
        if (typeof object[f] === "function" /*&& object.hasOwnProperty(f)*/) // Inherited functions (with comment) as well...
            functions.push(f);
    return functions;
};

const Display_attributes = (object) => {
    window.alert(Get_attributes(object).join(" - "));
};

const Display_functions = (object) => {
    window.alert(Get_functions(object).join(" - "));
};
The foundation of JavaScript is its event model. Beyond the basic Event type, a lot of event types are available in JavaScript. Occurrences of events (mouse event occurrences for instance) are (automatically) dispatched by JavaScript for asynchronous processing: events are queued and delivered to handling functions at appropriate moments.

Rule(s)

Example (mouse events)

var My_controller = function (…) { // 'My_controller' class (JavaScript 5 style)
    …
    this._my_canvas = window.document.getElementById("my_canvas");
    …
    this._my_canvas.addEventListener('mousedown', My_controller.prototype.pointerdown.bind(this), false);
    this._my_canvas.addEventListener('mousemove', My_controller.prototype.pointermove.bind(this), false);
    this._my_canvas.addEventListener('mouseover', My_controller.prototype.pointerover.bind(this), false);
    this._my_canvas.addEventListener('mouseup', My_controller.prototype.pointerup.bind(this), false);
    …
};
…
My_controller.prototype.pointerdown = function (mouse_event) {
    mouse_event.preventDefault(); // Default behaviors are canceled
    mouse_event.stopPropagation(); // The event occurrence is processed once and for all
    if (mouse_event.button === 0) { // Mouse left button pressed…
        …
    }
    if (mouse_event.button === 2) { // Mouse right button pressed…
        …
    }        
    …
};

Example (drag&drop events)

var My_controller = function (…) { // 'My_controller' class (JavaScript 5 style)
    this._file_reader = new FileReader();
    this._file_reader.onerror = function (event) {
        throw("this._file_reader.onerror");
    };
    const file_reader_onload = function (event) {
        const image = new Image();
        image.onload = function () {
           // Image has been loaded after drag&drop
        };
        image.src = event.target.result;
    };
    this._file_reader.onload = file_reader_onload.bind(this);
    …
    this._my_canvas = document.getElementById("my_canvas");
    …
    this._my_canvas.addEventListener('drop', this._drop.bind(this), false);
    …
};
…
My_controller.prototype._drop = function (drag_drop_event) {
    drag_drop_event.preventDefault(); // Default behaviors are canceled
    drag_drop_event.stopPropagation(); // The event occurrence is processed once and for all
    if (drag_drop_event.dataTransfer.types && drag_drop_event.dataTransfer.types.length > 0 && drag_drop_event.dataTransfer.types[0].toLowerCase().includes("file"))
        this._file_reader.readAsDataURL(drag_drop_event.dataTransfer.files[0]);
};

Rule(s)

Example (user-defined events) Catch_the_bus_three_js.zip 

var Time_management = function (image_name, …) { // 'Time_management' class (JavaScript 5 style)
    …
    // Event handling policy:
    window.addEventListener('Image_is_ready', this._create_3D_object.bind(this, image_name), false);
    window.addEventListener('3D_object_is_ready', function (custom_event) {
        if (custom_event.detail.name !== this._image_name)
            throw("Abnormal situation…");
        …
    }.bind(this), false);
    …
    this._image = new Image();
    this._image.onload = function () {
        // Image loaded:
        window.dispatchEvent(new Event('Image_is_ready'));
    };
    this._image.src = image_name; // Loading image from here…
};
…
Time_management.prototype._create_3D_object = function (name) {
    if (!(this._image !== undefined && this._image !== null && this._image instanceof Image && this._image.complete))
        throw("Abnormal situation…");
    … // 3D object is constructed from 'this._image' as texture
    window.dispatchEvent(new CustomEvent('3D_object_is_ready', {'detail': {name: name}}));
};
…
Timers are the way of performing periodic and/or repetitive tasks: window.setInterval (cyclic timer) and window.setTimeout (one-shot timer) are ready-to-use facilities for time management.

Example Catch_the_bus_three_js.zip 

var Time_management = function (…) { // 'Time_management' class (JavaScript 5 style)
    …
    this._interval_id = null; // 'Time_management' instance attribute
    …
    window.addEventListener('3D_object_is_ready', function () {
        // The timer is launched once the '3D_object_is_ready' event occurrence is received:
        this._interval_id = window.setInterval(this._time_out.bind(this), 5000); // Each 5 sec., the '_time_out' function of the 'Time_management' class is called…
    }.bind(this), false);
    …
};
…
Time_management.prototype._time_out = function () {
    if (…) // Some test
        … // This code is executed each 5 sec. until the timer is cancelled!
    else { // The timer is cancelled:
        window.clearInterval(this._interval_id);
        …
    }
};
Animation is the ability to register a function, which is called each time the browser repaints frames by means of the window.requestAnimationFrame facility.

Rule(s)

Example Catch_the_bus_three_js.zip 

var Time_management = function (…) { // 'Time_management' class (JavaScript 5 style)
    …
    this._animation_id = null; 'Time_management' instance attribute
    …
    // The animation is launched once the '3D_object_is_ready' event occurrence is received:
    window.addEventListener('3D_object_is_ready', function (…) {
        …
        this._animation_id = window.requestAnimationFrame(this._animate_3D_object.bind(this));
    }.bind(this), false);
    …
};

Time_management.prototype._animate_3D_object = function (timestamp) {
    if (timestamp !== undefined) { /* first call: 'timestamp === undefined' */
        // One may handle the elapsed time from the first call to '_animate_3D_object'
    }
    … // This code is executed around 60 times per sec. until the animation is cancelled!
    // A new call is queued (mandatory)
    this._animation_id = window.requestAnimationFrame(this._animate_3D_object.bind(this));
};
…
Time_management.prototype._time_out = function () {
    if (…) // Some test
        … // This code is executed each 5 sec. until the timer is cancelled!
    else { // The animation is cancelled:
        …
        window.cancelAnimationFrame(this._animation_id);
        …
    }
};
Illustration: JavaScript event model
Web services are server-side programs accessible throughout the Web. By appropriate calls in JavaScript, one is able to get any data and to reduce computations on the client side.

Rule(s)

Example

Get Web_Services.js.zip 

const request = new XMLHttpRequest();
request.onreadystatechange = function () {
    window.console.log(request.getAllResponseHeaders());
    if (request.readyState === XMLHttpRequest.DONE) {
        if (request.getResponseHeader("Content-Type").includes("application/json")) {
            const response = JSON.parse(request.responseText);
            window.alert("For 1 US Dollar (USD) to MAD (Moroccan Dirham): " + response.rates.MAD);
        }
    }
};
 // This Web site requires a license key:
request.open("GET", "http://openexchangerates.org/api/latest.json" + "?app_id=" + "<your license key here>", true);
// 'request' must already be opened:
request.send(null);

Post Web_Services.js.zip 

const request2 = new XMLHttpRequest(); // Stuck by CORS
request2.onreadystatechange = function () {
    if (request2.readyState === XMLHttpRequest.DONE) {
        window.alert(request2.responseText);
    }
};
request2.open("POST", "http://www.w3schools.com" + "/xml/tempconvert.asmx/CelsiusToFahrenheit", true);
// request2.setRequestHeader("Content-Length", 10); // The number of bytes of data in the body of the request or response
request2.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); // As a HTML form
request2.send("Celsius=20");

Simple Object Access Protocol (SOAP) Web_Services.js.zip 

const request3 = new XMLHttpRequest(); // Stuck by CORS
request3.onreadystatechange = function () {
    if (request3.readyState === XMLHttpRequest.DONE) {
        // Process response as XML text…
    }
};
request3.open("POST", "http://www.webservicex.net/ConvertTemperature.asmx/ConvertTemp", true);
request3.setRequestHeader("Content-Type", "application/soap+xml");
const content =
            "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
            "<soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap12=\"http://www.w3.org/2003/05/soap-envelope\">" +
            "  <soap12:Body>" +
            "   <ConvertTemp xmlns=\"http://www.webservicex.net/\">" +
            "     <Temperature>18</Temperature>" +
            "     <FromUnit>degreeCelsius</FromUnit>" +
            "     <ToUnit>degreeFahrenheit</ToUnit>" +
            "   </ConvertTemp>" +
            " </soap12:Body>" +
            "</soap12:Envelope>";
request3.send(content);

fetch (Post) Web_Services.js.zip 

class ReqRes {
    static URL() { return 'https://reqres.in'; }
    static Path() { return '/api/users'; }
    static Run1(name = "FranckBarbier", job = "Trainer") {
        fetch(ReqRes.URL() + ReqRes.Path(), {
            body: JSON.stringify({
                name: name,
                job: job
            }),
            headers: {// Mandatory to get 'result' in appropriate shape...
                'accept': 'application/json', // -> response
                'content-type': 'application/json; charset=UTF-8'
            },
            method: 'POST'
        }).then(response => {
            response.json().then(result => {
                window.console.assert(result.name === name); // 'Req-Res' just returns data for "simulation"...
                window.alert("'ReqRes' ('fetch') RUN1: " + JSON.stringify(result));
            });
        });
    }
…
Note(s)
WebSockets is a technology that promotes bi-directional full-duplex communication between (not exclusively) browsers and WebSockets servers (example here…). The general way of dealing with WebSockets in JavaScript is described here

Example WebSockets_Tyrus_1_17.Java.Maven.zip 

const my_WebSocket = new WebSocket("ws:127.0.0.1:8080", "FranckBarbier");
my_WebSocket.onmessage = (event) => {
    let message_data_as_object = JSON.parse(event.data);
    …
};
my_WebSocket.onopen = () => {
    my_WebSocket.send(JSON.stringify({Response: "Handshaking"}));
};
my_WebSocket.onclose = () => {
    // …
};
my_WebSocket.onerror = () => {
    // …
};
Web Workers is a support for dealing with a kind of multithreading. More precisely, some JavaScript code may run outside the browser so that this code lightens the load on the browser. Web Workers mainly consist in facilities to assign background tasks from a browser to a given Web worker that runs in parallel with the said browser.

Rule(s)

Example (“main” thread in the browser) Web_Workers.js.zip 

/*
 * Web_Workers_main.js
 */

"use strict";

function main() {
// jQuery is used:
    var $canvas = $("#My_canvas").get(0); // jQuery access to DOM canvas elem. with 'My_canvas' as id.
    var image = new Image();
    var worker = new Worker("js/Web_Workers_parallel.js"); // Behind the browser stage, this "parallel" code is that of the worker…

    worker.addEventListener("message", response_from_process_image_by_worker); // Subscription about worker's response
    function response_from_process_image_by_worker(message) { // Handler of worker's response
        swal({// Use of the nice 'sweetalert' JS library!
            title: "Web Workers",
            text: "Worker just terminated image processing… Show it?",
            type: "warning",
            showCancelButton: true,
            confirmButtonColor: "#DD6B55",
            confirmButtonText: "Yes!",
            closeOnConfirm: true
        }, function () {
            $canvas.getContext('2d').putImageData(message.data, 0, 0); // Reload of processed image…
        });
        worker.terminate(); // Worker is no longer used, 'close' optional statement
    }

    $(document).on("go!", process_image_by_worker); // Wait for image availability
    function process_image_by_worker() { // Handler of image availability
        if (window.Worker) { // Test if the browser supports the Web Workers technology
            // Instance of 'ImageData' must be sent since it is processable by the new HTML5 structured clone algorithm:
            worker.postMessage($canvas.getContext('2d').getImageData(0, 0, $canvas.width, $canvas.height));
        }
    }

    image.onload = function () {
        $canvas.width = image.width;
        $canvas.height = image.height;
        $canvas.getContext('2d').drawImage(image, 0, 0); // Image is loaded in canvas
        $(document).trigger("go!"); // Image availability: event triggering in jQuery
    };
    image.src = "img/Image.jpg"; // Image load…
}

Example (“parallel” thread outside the browser) Web_Workers.js.zip 

/*
 * Web_Workers_parallel.js
 */

"use strict";

self.importScripts("chai.js"); // Reused libraries require local-scope load…

function process(image_data) {
    var buffer = new Uint32Array(image_data.data.buffer);
    for (var pixel_number = 0; pixel_number < buffer.length; pixel_number++) {
        var alpha = buffer[pixel_number] >>> 24; // Most left byte
        var blue = (buffer[pixel_number] & 0x00FF0000) >> 16;
        var green = (buffer[pixel_number] & 0x0000FF00) >> 8;
        var red = buffer[pixel_number] & 0x000000FF; // Most right byte
// Simplistic image processing, please improve:
        blue = ~blue;
        green = ~green;
        // red = ~red;
        buffer[pixel_number] = (alpha << 24) | (blue << 16) | (green << 8) | red;
    }
}

onmessage = function (message) { // Message from 'main' thread, i.e., 'worker.postMessage($canvas.getContext('2d').getImageData(0, 0, $canvas.width, $canvas.height));', is received…
    chai.assert.isTrue(message.data instanceof ImageData); // Check received data
    process(message.data);
    postMessage(message.data); // Response from worker to 'main' thread…
};
JavaScript supports rich APIs for multimedia management (image, video, sound…) in general and gaming in particular.
Full screen is simply the ability to use the machine's full screen and vice versa when leaving the full screen mode. The full screen API still stumbles over incompatible browser behaviors, requiring the FullScreen object for activations/desactivations.

Example

var FullScreen = FullScreen || {};
FullScreen.available = function () { // Are full screen capabilities available?
    return this._hasWebkitFullScreen || this._hasMozFullScreen;
};

FullScreen.activated = function () { // Test if full screen is currently activated…
    if (this._hasWebkitFullScreen) {
        return document.webkitIsFullScreen;
    } else if (this._hasMozFullScreen) {
        return document.mozFullScreen;
    } else {
        window.console.assert(false);
    }
};

FullScreen.request = function (element) { // Request full screen on a given element…
    element = element || document.body;
    if (this._hasWebkitFullScreen) {
        element.webkitRequestFullScreen();
    } else if (this._hasMozFullScreen) {
        element.mozRequestFullScreen();
    } else {
        window.console.assert(false);
    }
};

FullScreen.cancel = function () { // Cancel full screen…
    if (this._hasWebkitFullScreen) {
        document.webkitCancelFullScreen();
    } else if (this._hasMozFullScreen) {
        document.mozCancelFullScreen();
    } else {
        window.console.assert(false);
    }
};

// Internal functions to know which full screen API implementation is available
FullScreen._hasWebkitFullScreen = 'webkitCancelFullScreen' in document ? true : false;
FullScreen._hasMozFullScreen = 'mozCancelFullScreen' in document ? true : false;
In a standardization logic, JavaScript lets access to device equipment with presence testing capability. Typically, the Vibration API may enact the vibrator, if any, in a software manner.

Example Vibration_API.js.zip 

window.addEventListener("DOMContentLoaded", () => { // DOM just loaded...
    window.console.assert(window.document.readyState !== "loading");

    const FB = window.document.getElementById("FB");

    let angle = 10;
    const interval_id = window.setInterval(() => {
        FB.style.transform = "rotate(" + angle + "deg)";
        angle = -angle;
        window.navigator.vibrate(50);
    }, 100);

    FB.addEventListener("click", () => {
        window.clearInterval(interval_id);
        window.document.getElementById("Feel_it_").innerHTML = "Vibration just stops...";
    });
});
In a smartphone-based logic, JavaScript supports accelerometer and/or gyroscope measures for device motion and device orientation. The devicemotion and deviceorientation events are devoted to this task.

Example Google_cardboard_three_js.zip 

window.addEventListener('deviceorientation', event => {
    data.innerHTML = "'z' motion (0/360): " + event.alpha + "
"; data.innerHTML += "'x' motion (-180/180): " + event.beta + "
"; data.innerHTML += "'y' motion (-90/90): " + event.gamma; }); // Three.js support: const device_orientation_controls = new THREE.DeviceOrientationControls(camera, true); device_orientation_controls.connect(); … device_orientation_controls.update(); // Animation loop...
WebRTC standing for Web Real-Time Communications allows the direct access to input device, namely webcams and microphones.

Rule(s)

Example (finding available devices)

// A promise is returned:
window.navigator.mediaDevices.enumerateDevices().then(function (devices) { // 'window.navigator.mediaDevices !== undefined' -> WebRTC available!
    devices.forEach(function (device) {
        window.alert(device.kind + ": " + device.label + " id = " + device.deviceId);
    });
})
.catch(function (error) {
    window.console.log(error.name + ": " + error.message);
});

Rule(s)

Example Selfie.js.zip 

<video id="my_video" width="200" height="300" poster="./img/FranckBarbier.jpg"></video>
DOM_ready.then(value => { // DOM is ready as a promise...
    /* Webcam shooting **/
    // Tested with Chrome >= 68, Firefox >= 62, Edge >= 42, and Safari >= 11
    let _my_video = document.getElementById('my_video');
    _my_video.addEventListener('canplay', () => {
        window.console.log("The video is playing...");
        window.console.log("_my_video.videoHeight: " + _my_video.videoHeight + " _my_video.videoWidth: " + _my_video.videoWidth);
    });
    let _working_canvas = window.document.createElement('canvas');

// window.alert("typeof navigator.mediaDevices.getUserMedia: " + typeof navigator.mediaDevices.getUserMedia); // 'function'
// window.alert("Is Promise? " + (navigator.mediaDevices.getUserMedia({audio: false, video: true}) instanceof Promise)); // 'true'
// Safari : voir aussi onglet "Développement", option WebRTC
    /* Non supporté par Safari : {video: {width: 512, height: 512, facingMode: "user"}} */
// Looking at device capabilities:
// window.alert(JSON.stringify(navigator.mediaDevices.getSupportedConstraints()));

    let constraints = {audio: false, video: true};
    if (window.navigator.mediaDevices.getSupportedConstraints().hasOwnProperty('facingMode'))
        constraints = {audio: false, video: {facingMode: 'user'}}; // Selfie mode 

    let _video_management = () => {
        if (_my_video.srcObject !== null) {
            if (_my_video.srcObject.getVideoTracks()[0].readyState === "live") { // Video is running, take photo...
                Snapshot.then(sound => {
                    sound.play();
                });
                // Before stopping the camera, one records the last frame:
                _working_canvas.setAttribute('width', _my_video.videoWidth);
                _working_canvas.setAttribute('height', _my_video.videoHeight);
                _working_canvas.getContext('2d').drawImage(_my_video, 0, 0, _my_video.videoWidth, _my_video.videoHeight);
                let _selfie = new Image();
                _selfie.onload = function () {
                    // This immediately stops the webcam (Firefox keeps the last frame on screen while Chrome generates a black screen):
                    _my_video.srcObject.getVideoTracks()[0].stop();
                    // The stream is no longer active:
                    window.console.assert(_my_video.srcObject.active === false);
                    window.console.assert(_my_video.srcObject.getVideoTracks()[0].readyState === "ended");
                    // The stream is detached from the '<video>' tag:
                    _my_video.srcObject = null; // For next shoot...
                    _my_video.setAttribute('poster', _selfie.src);
                };
                _selfie.src = _working_canvas.toDataURL("image/png"); // From canvas to image...
            } else
                window.alert("_my_video.srcObject.getVideoTracks()[0].readyState === \"ended\"");
        } else { // *New* stream is *required* for additional shoots!
            // Firefox asks for permission again...
            window.navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
                // Safari issue: running video does not appear inside browser (from second round) while we're really filmed! Worse still, shooting works!
                // Issue not solved despite this:
                // https://help.milkshake.tv/hc/en-us/articles/115005464689-Videos-not-playing-when-using-Safari-11-web-browser
                _my_video.srcObject = stream; // Assign the webcam to the "my_video" HTML element
                _my_video.play(); // Start playing video...
            }).catch((error) => {
                window.alert("navigator.mediaDevices.getUserMedia: " + error.message);
            });
        }
    };

    if (window.PointerEvent) // May not work for some browsers...
        _my_video.onpointerup = _video_management;
    else // Safari does not yet support 'window.PointerEvent' (https://caniuse.com/#feat=pointer):
        _my_video.addEventListener('mouseup', _video_management, true);
});
/** End of DOM ready */
The Web Audio API benefits from being used through high-end devoted libraries, notably SOUNDJS.

Example Theo_is_crying.js.zip 

createjs.Sound.on("fileload", event => { // This is fired for each sound that is registered...
 // 'PlayPropsConfig' class of SOUNDJS API:
    const instance = createjs.Sound.play(event.id, {interrupt: createjs.Sound.INTERRUPT_ANY, loop: 1, volume: 0.5, duration: 500});
    instance.on("complete", () => {
        window.console.log("End of: " + event.id);
    });
});
createjs.Sound.registerSound("./sounds/Baby_laughing.mp3", "Baby_laughing_sound_ID");
…
createjs.Sound.registerSound("./sounds/Baby_crying.mp3", "Baby_crying_sound_ID");
…
const result = createjs.Sound.play("Baby_crying_sound_ID"); // Potential bug: the sound may not yet be loaded...
if (result.playState !== createjs.Sound.PLAY_SUCCEEDED)
    window.console.warn(result.src + ": " + result.playState);
       
The Speech synthesis mainly works with Chrome, intro. here….
The Speech recognition mainly works with Chrome, intro. here….