``````// Vector is a data structure used to represent a point in 3d space
function Vector(x, y, z) {
// properties x, y, z represent each coordinate of the point
this.x = x;
this.y = y;
this.z = z;
// method set is used to change x and y coordinates of the given point
this.set = function (x, y) {
this.x = x;
this.y = y;
};
}

// PointCollection is a data structure used to represent all points forming our animation
function PointCollection() {
/* mousePos property stores coordinates of cursor
* predefined value is a point in upper left corner of 2d plane
*/
this.mousePos = new Vector(0, 0);

/* properties pointCollectionX and pointCollectionY stores
* additional deviation to the position of the point,
* initial value is 0
*/
this.pointCollectionX = 0;
this.pointCollectionY = 0;

/* property points stores all points forming our animation
* initial value is a empty array
*/
this.points = [];

// method update is used to track position of cursor and accordingly influence each point
this.update = function () {
// for every element in array points (...)
for (var i = 0; i < this.points.length; i++) {
/* Assign:
* - to variable point the current point (element at index i in array points)
* - to variable dx the horizontal distance between cursor and current point
* - to variable dy the vertical distance between cursor and current point
* - to variable d the distance in a straight line between cursor and current point,
*   this variable is calculated using the Pythagorean theorem
*/
var point = this.points[i];
var dx = this.mousePos.x - point.curPos.x;
var dy = this.mousePos.y - point.curPos.y;
var d = Math.sqrt((dx * dx) + (dy * dy));

/* Statements below are assignation operations combined with ternary operators [add_doc]
* If distance between cursor and current point is less than 150 then assign to property
* targetPos [add_lines] of current point the difference between current position of point and the
* distance between cursor and current position of point. [add_better_explanation] [optimize_code]
* Otherwise assign to property the targetPos [add_lines] of current point original position of this point.
*/
point.targetPos.x = d < 150 ? point.curPos.x - dx : point.originalPos.x;
point.targetPos.y = d < 150 ? point.curPos.y - dy : point.originalPos.y;

// trigger method update [add_line_numbers] for current point
point.update();
}
};

/* method shake is used to shake our collection of points. This method a significant part of the
* bounceName function [add_lines] used, for example, in this exercise:
*/
this.shake = function () {
// for every element in array points (...)
for (var i = 0; i < this.points.length; i++) {
/* Assign:
* - to variable point the current point (element at index i in array points)
* - to variable dx the horizontal distance between cursor and current point
* - to variable dy the vertical distance between cursor and current point
* - to variable d the distance in a straight line between cursor and current point,
*   this variable is calculated using the Pythagorean theorem
*/
var point = this.points[i];
var dx = this.mousePos.x - point.curPos.x;
var dy = this.mousePos.y - point.curPos.y;
var d = Math.sqrt((dx * dx) + (dy * dy));

// if distance between cursor and current point is less than 50 (...)
if (d < 50) {
/* (...) Assign to properties pointCollectionX and pointCollectionY two random integer numbers
* from set [-2, -1, 0, 1, 2]
*/
this.pointCollectionX = Math.floor(Math.random() * 5) - 2;
this.pointCollectionY = Math.floor(Math.random() * 5) - 2;
}

/* trigger method draw [add_lines] for current point with parameters
* pointCollectionX and pointCollectionY which affect the position of point
*/
point.draw(bubbleShape, this.pointCollectionX, this.pointCollectionY);
}
};

// method draw is used to draw our collection of points
this.draw = function (bubbleShape, reset) {
// for every element in the array called points (...)
for (var i = 0; i < this.points.length; i++) {
// (...) assign to the variable point the current point (element at index i in array points)
var point = this.points[i];

// if the current point does not exist (...)
if (point === null)
// (...) go to the next iteration, next point
continue;

// if the property reset of the window object is set to true (...)
if (window.reset) {
// (...) assign default, initial values to variables listed below
this.pointCollectionX = 0;
this.pointCollectionY = 0;
this.mousePos = new Vector(0, 0);
}

// trigger method draw for current points [add]
point.draw(bubbleShape, this.pointCollectionX, this.pointCollectionY, reset);
}
};
}

// Point is a data structure used to represent single points / bubbles in our animation
function Point(x, y, z, size, color) {
/* property curPos stores the current position of our bubble in 3d space,
* predefined value is equal to the coordinates defined in alphabet.js (parameters x, y, z)
*/
this.curPos = new Vector(x, y, z);
// property color stores the color of our bubble defined by us in main.js
this.color = color;

this.friction = document.Friction;
this.rotationForce = document.rotationForce;
this.springStrength = document.springStrength;

// property originalPos stores a Vector (point) with the coordinates defined in alphabet.js (parameters x, y, z)
this.originalPos = new Vector(x, y, z);
// basic value of radius and size is a value defined in alphabet.js (parameter size)
this.size = size;
// property targetPos stores the direction where bubble goes, predefined value is equal to originalPos
this.targetPos = new Vector(x, y, z);
// velocity in our script is represented by a vector, predefined velocity is equal to 0
this.velocity = new Vector(0.0, 0.0, 0.0);

/* method update is used to update the properties of Point object
* in accordance with current situation
*/
this.update = function () {
var dx = this.targetPos.x - this.curPos.x;
var dy = this.targetPos.y - this.curPos.y;
var ax = dx * this.springStrength - this.rotationForce * dy;
var ay = dy * this.springStrength + this.rotationForce * dx;

this.velocity.x += ax;
this.velocity.x *= this.friction;
this.curPos.x += this.velocity.x;

this.velocity.y += ay;
this.velocity.y *= this.friction;
this.curPos.y += this.velocity.y;

/* Assign: [todo]
* - to variable dx the horizontal distance between cursor and current point
* - to variable dy the vertical distance between cursor and current point
* - to variable d the distance in a straight line between cursor and current point,
*   this variable is calculated using the Pythagorean theorem
*/
var dox = this.originalPos.x - this.curPos.x;
var doy = this.originalPos.y - this.curPos.y;
var d = Math.sqrt((dox * dox) + (doy * doy));

this.targetPos.z = d / 100 + 1;
var dz = this.targetPos.z - this.curPos.z;
var az = dz * this.springStrength;
this.velocity.z += az;
this.velocity.z *= this.friction;
this.curPos.z += this.velocity.z;

};

// method draw is used to draw single point / bubble
this.draw = function (bubbleShape, dx, dy) {
// set the fill color to the color of the bubble
ctx.fillStyle = this.color;

if (bubbleShape == "square") {
// begin path
ctx.beginPath();
/* To draw a rectangle filled with the current fillStyle we use
*  fillRect(x, y, width, height)
*  where x, y is the upper, left corner of the rectangle.
*  In this case, we use this.radius * 1.5 for width and height, and
*  the upper, left vertex has coordinates (this.curPos.x + dx, this.curPos.y + dy)
*/
ctx.fillRect(this.curPos.x + dx, this.curPos.y + dy, this.radius * 1.5, this.radius * 1.5);
} else if (bubbleShape == "bird") {
// set the style of our lines
ctx.lineWidth = 3;
ctx.lineCap = "round";
ctx.lineJoin = "miter";
ctx.strokeStyle = this.color;
// start our path
ctx.beginPath();
// draw one wing, and then the other
ctx.arc(this.curPos.x + dx, this.curPos.y + dy, this.radius, Math.PI, false);
ctx.arc(this.curPos.x + dx + this.radius * 2, this.curPos.y + dy, this.radius, Math.PI, false);
ctx.stroke();

} else { // the default bubbleShape will be circles
// begin path
ctx.beginPath();
/* To draw a circle filled with the current fillStyle we use
*  arc(x, y, radius, startAngle, endAngle, anticlockwise)
*  where x, y is the center point of the circle.
*  center at coordinates (this.curPos.x + dx, this.curPos.y + dy)
*/
ctx.arc(this.curPos.x + dx, this.curPos.y + dy, this.radius, 0, Math.PI * 2, true);
// fill path and end path
ctx.fill();

}
};
}

/* function makeColor is used to convert an array of values
* for example [196, 77, 55] into color in HSL color model.
* http://en.wikibooks.org/wiki/Color_Models:_RGB,_HSV,_HSL
*/
var hue = hslList[0] /*- 17.0 * fade / 1000.0*/ ;
var sat = hslList[1] /*+ 81.0 * fade / 1000.0*/ ;
var lgt = hslList[2] /*+ 58.0 * fade / 1000.0*/ ;
return "hsl(" + hue + "," + sat + "%," + lgt + "%)";
}

// function phraseToHex is used to convert ASCII text into HEX coded text
function phraseToHex(phrase) {
// assign an empty string to hexphrase
var hexphrase = "";
// for every character in the parameter phrase (...)
for (var i = 0; i < phrase.length; i++) {
hexphrase += phrase.charCodeAt(i).toString(16);
}
// return converted string
return hexphrase;
}

// this function initializes the event listeners
function initEventListeners() {
/* this statement triggers function updateCanvasDimensions [add_line_numbers] if our page is resized by the user
* and triggers function onMove [add_line_numbers] when the cursor is moved
*/
\$(window).bind('resize', updateCanvasDimensions).bind('mousemove', onMove);

// this function will be triggered if the user touches a screen and moves their finger (for example in smartphones)
canvas.ontouchmove = function (e) {
// preventDefault statement terminates default action of the event
e.preventDefault();
onTouchMove(e);
};
// this function will be triggered if the user touches a screen
canvas.ontouchstart = function (e) {
// preventDefault statement terminates default action of the event
e.preventDefault();
};
}

// function updateCanvasDimensions is used to control the size of the canvas
function updateCanvasDimensions() {
// basic variables, you can change them to resize the canvas element
canvas.attr({
height: 500,
width: 1000
});
// assign to variables the values defined above
canvasWidth = canvas.width();
canvasHeight = canvas.height();
draw();
}

// function onMove checks position of cursor and accordingly affects the animation
function onMove(e) {
// if pointCollection exists (...)
if (pointCollection) {
/* (...) set value of the property mousePos of pointCollection to mouse coordinates
* relative to the canvas element
*/
pointCollection.mousePos.set(e.pageX - canvas.offset().left, e.pageY - canvas.offset().top);
}
}

// function onTouchMove checks position of a finger on touch screen and accordingly affects the animation
function onTouchMove(e) {
// if pointCollection exists (...)
if (pointCollection) {
/* (...) set value of property mousePos of pointCollection to the mouse coordinates
* relative to canvas element
*/
pointCollection.mousePos.set(e.targetTouches[0].pageX - canvas.offset().left, e.targetTouches[0].pageY - canvas.offset().top);
}
}

// function bounceName is used to repeatedly bounce our name
function bounceName() {
shake();
// trigger again this function (bounceName) after 30 ms
setTimeout(bounceName, 30);
}

function bounceBubbles() {
draw();
update();
// trigger again this function (bounceBubbles) after 30 ms
setTimeout(bounceBubbles, 30);
}

// function draw is used to draw all points / bubbles forming the animation
function draw(reset) {
// assign to a local variable tmpCanvas our canvas (element at index 0 of our canvas object)
var tmpCanvas = canvas.get(0);

// if property getContext of our canvas is not defined (...)
if (tmpCanvas.getContext === null) {
// (...) end function
return;
}

// assign to variable ctx the context of the canvas element
ctx = tmpCanvas.getContext('2d');

// statement below is used to erase everything from canvas element
ctx.clearRect(0, 0, canvasWidth, canvasHeight);

/* syntax below is example of ternary operator - shorthand for if ... else construction
* if shape of our bubbles is not defined use "circle" shape
* otherwise use current shape
*/
bubbleShape = typeof bubbleShape !== 'undefined' ? bubbleShape : "circle";

// if pointCollection exist (...)
if (pointCollection) {
// (...) trigger function draw of pointCollection object [add_line_numbers]
pointCollection.draw(bubbleShape, reset);
}
}

function shake() {
// assign to local variable tmpCanvas our canvas (element at index 0 of our canvas object)
var tmpCanvas = canvas.get(0);

// if property getContext of our canvas is not defined (...)
if (tmpCanvas.getContext === null) {
// (...) end function
return;
}

// assign to variable ctx context of the canvas element
ctx = tmpCanvas.getContext('2d');

// statement below is used to erase everything from canvas element
ctx.clearRect(0, 0, canvasWidth, canvasHeight);

/* syntax below is example of ternary operator - shorthand for if ... else construction
* if shape of our bubbles is not defined use "circle" shaped
* otherwise use current shape
*/
bubbleShape = typeof bubbleShape !== 'undefined' ? bubbleShape : "circle";

// if pointCollection exist (...)
if (pointCollection) {
// (...) trigger function shake of pointCollection object [add_line_numbers]
pointCollection.shake(bubbleShape);
}
}

/* function update is used to safely (only if object exist)
* update the pointCollection object
*/
function update() {
// if pointCollection exists (...)
if (pointCollection)
// (...) trigger method update of pointCollection object [add_line_numbers]
pointCollection.update();
}

/* function drawName is the main function in this script, it is used to
* draw a string (parameter name) on the canvas in colors defined in the parameter
* letterColors and to initialize the event listeners
*/
function drawName(name, letterColors) {
updateCanvasDimensions();
// variable g will store all points forming our animation, init value is a empty array
var g = [];
// variable offset will store current width of our animation
var offset = 0;

/* Function addLetter is used to retrieve data from alhpabet.js
* for each given letter and transform them into Point objects.
* This is only the definition of the function, it will be triggered later in the code.
*/
// if the variable passed as the letterCols parameter is defined (...)
if (typeof letterCols !== 'undefined') {
if (Object.prototype.toString.call(letterCols) === '[object Array]' && Object.prototype.toString.call(letterCols[0]) === '[object Array]') {
letterColors = letterCols;
}
if (Object.prototype.toString.call(letterCols) === '[object Array]' && typeof letterCols[0] === "number") {
letterColors = [letterCols];
}
} else { // if the variable passed as the letterCols parameter is not defined (...)
// (...) set variable letterColors to one value (dark gray color) array
letterColors = [[0, 0, 27]];
}

if (document.alphabet.hasOwnProperty(cc_hex)) {
var chr_data = document.alphabet[cc_hex].P;
var bc = letterColors[ix % letterColors.length];
var fontSizeMultiplier = 0.75;

for (var i = 0; i < chr_data.length; ++i) {
point = chr_data[i];

g.push(
new Point(point[0] * fontSizeMultiplier + offset,
point[1] * fontSizeMultiplier,
0.0,
point[2] * fontSizeMultiplier ,
makeColor(bc, point[3])));
}
offset += document.alphabet[cc_hex].W * fontSizeMultiplier;
}
}

//
var hexphrase = phraseToHex(name);

var col_ix = -1;
for (var i = 0; i < hexphrase.length; i += 2) {
var cc_hex = "A" + hexphrase.charAt(i) + hexphrase.charAt(i + 1);
if (cc_hex != "A20") {
col_ix++;
}
}

for (var j = 0; j < g.length; j++) {
g[j].curPos.x = (canvasWidth / 2 - offset / 2) + g[j].curPos.x;
g[j].curPos.y = (canvasHeight / 2 - 105) + g[j].curPos.y;
g[j].originalPos.x = (canvasWidth / 2 - offset / 2) + g[j].originalPos.x;
g[j].originalPos.y = (canvasHeight / 2 - 105) + g[j].originalPos.y;
}

// assign to variable pointColletion new object of class PointCollection
pointCollection = new PointCollection();
// set property points of our newly created object to g, array of points
pointCollection.points = g;
initEventListeners();
}

/* Set property reset of window object to false, this is
* predefined value. This property is used only in
* method draw of pointCollection object [add_line_numbers]
*/
window.reset = false;

// when the cursor leaves the site / document (...)
\$(window).mouseleave(function () {
// (...) set property reset of window object to true
window.reset = true;
});

// when the cursor enters the site / document (...)
\$(window).mouseenter(function () {
// (...) set property reset of window object to false
window.reset = false;
});

// assign to a variable the canvas element with id `myCanvas`
var canvas = \$("#myCanvas");

// declaration of the basic variables
var canvasHeight;
var canvasWidth;
var ctx;
var pointCollection;

// settings of our animation, you can try to change them, have fun :)
document.rotationForce = 0.0;
document.Friction = 0.85;
document.springStrength = 0.01;

/* basic, predefined colors, used for example in exercise:
*/
var white = [0, 0, 100];
var black = [0, 0, 27];
var red = [0, 100, 63];
var orange = [40, 100, 60];
var green = [75, 100, 40];
var blue = [196, 77, 55];
var purple = [280, 50, 60];

// this statement will trigger function updateCanvasDimensions after 30 ms [add_line_numbers]
setTimeout(updateCanvasDimensions, 30);

// here is main.js

var lightGray = [202, 14, 65];
var mediumGray = [202, 14, 45];
var darkGray = [202, 14, 25];

var letterColors = [lightGray, mediumGray, darkGray];
var myName = "Flock of Seagulls";

bubbleShape = "bird";

drawName(myName, letterColors);
bounceBubbles();

``````
``````<!DOCTYPE html>
<html>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.10.2.min.js"></script>