JavaScript (Spring 2015)

Course meets Friday 1:30pm – 3:00pm in Towne 319.

Instructor: Ian Sibner (isibner@seas.upenn.edu)

TAs:

See Piazza staff page for office hours info.

Course Description

This course provides an introduction to modern JavaScript: frameworks, design patterns, techniques, and best practices used to create robust applications both server-side and client-side. The emphasis will be on sampling a wide range of JavaScript use cases. The topics covered will include:

This course is most useful for students who have some programming and web development experience and want to develop moderate JavaScript skills to be able to build complex, interactive applications.

Prerequisites

Policies

Your homework submissions will be graded out of 25 points: 15 for program correctness (automatically graded), 5 for JavaScript best practices (automatically graded), and 5 for code style (manually graded). Grading will follow the style guide for the course - please ask on Piazza if you have a specific question that is not covered here.

Please note that the code style grade is additive, not subtractive; if you do not complete a homework, you will not receive 5 style points for ‘making no errors,’ but rather 0 style points for writing no code at all.

Late submissions will be docked 20% for every day late. Extensions will only be granted in truly extenuating circumstances.

If a submission doesn't run (i.e. has syntax errors), we will not be lenient -- you will likely receive zero credit. Please start assignments early and use office hours to your advantage.

See the course syllabus for more detailed policies and grading info.

Assignments

Assignments are due at midnight unless otherwise indicated. Code submissions will be made via the CIS 197 submission system (powered by BRUCE). Refer to the style guide for coding JavaScript.

hw1, due Friday, January 23: Finger exercises.

hw2, due Friday, January 30: XML object modeling.

hw3, due Friday, February 6: Pokémon map building.

hw4, due Friday, February 27: Penn Course Review Lite™.

hw5, due Friday, April 3: AngularJS.

hw6, due Friday, April 10: Chrome Extension.

Lectures

Lecture Date Content Reading, Resources
1 1/16 JS basics & syntax Eloquent JS, Chapter 2
[MDN] A re-introduction to JavaScript
2 1/23 JS Objects and Inheritance
Callbacks
Eloquent JS, Chapter 12
3 1/30 DOM Manipulation
jQuery
[MDN] Document Object Model
Chrome DevTools docs
jQuery API
4 2/6 HTTP and Express HTTP Made Really Easy
Express Docs
5 2/13 Backbone Backbone.js Docs
[O'Reilly] Developing Backbone.js Applications
6 3/20 Angular Google's AngularJS Guide
7 4/3 Chrome Extensions Chrome Extension API
Code from Lecture
8 4/10 Deploying JavaScript Apps Code from Lecture
Heroku NodeJS Quickstart AWS EC2 NodeJS Quickstart

Reading

Lecture 1: Intro & JS Basics

CIS 197 (January 22, 2013)

Intro

As a browser scripting language, one of JavaScript's main uses is to implement view logic -- how a user interface on the web is drawn, manipulated, and interacted with. But as the language has matured, its scope has widened. Today, you can do things like build a web server in JavaScript using Node.js or compile video games from C++ to JS using Emscripten to play Quake III in a browser.

A few years ago, the traditional workflow of the front end developer was to get some JS (probably jQuery) code working locally, FTP it over to a server, and then handle the myriad cross-browser compatibility issues that ensued (*cough* IE6 *cough*). The past few years have seen somewhat of a transformation of the front end dev role, with a much greater emphasis on tools and practices. It's a result of:

One of my goals in this class is to introduce you to modern tools, practices, and workflows in front end development. There's a sort of baseline of things that you're expected to be familiar with (Node, npm, homebrew, git, tests, build systems, etc.) in order to be successful and not feel left behind. I'll talk more about this at the end of today's lecture.

Today, I'm going to talk about language basics like syntax and variable scoping and there won't be much mention of the web. As a programming language, JS is an implementation of the ECMAScript standard, which makes no reference to web technology. We're currently on v5 of ECMAScript, with v6 expected to be a significant expansion to the language due in 2014.

Language Basics

See JS basics module.

Exercises

Using these basics, can you write out a Fizzbuzz program?

Solution:

for (var i = 0; i < 15; i++) {
  if (i % 3 === 0 && i % 5 === 0) {
    console.log("fizzbuzz\n");
  } else if (i % 3 === 0) {
    console.log("fizz\n");
  } else if (i % 5 === 0) {
    console.log("buzz\n");
  } else {
    console.log(i + "\n");
  }
}

Homework

Most of your homework will be to get up to speed with the tools you need for front end development.

The first important thing you're expected to know is git version control. Using version control allows you to collaborate on software projects and participate in the rich open-source community (JS is the most popular language on Github!) To boost your git skills, use online resources like:

For this class, I also expect a basic to moderate understanding of HTML/CSS. If you are lacking in this area, please use online resources like Codecademy and Mozilla Developer Network.

The rest of your homework is as follows:

JavaScript Basics

Reference: Eloquent JavaScript and JavaScript: The Good Parts.

← Back to Lecture 1

Introduction

JavaScript is a programming language being used to do all sorts of clever and horrible things on the web.

-- Marijn Haverbeke, EloquentJS

It's the world's most widely available application runtime. It runs in all modern web browsers and is one of the easiest languages to start using since you don't have to download anything. Even if you don't do much web programming, which this course focuses on, I hope I'll be able to expose you to some interesting (or even mind-boggling) programs that influence how you write good code in other languages and platforms. The good parts of JS (as Douglas Crockford points out) really make it a great functional language -- deep down, it actually shares more with Lisp and Scheme than C or Java (even though it looks like C or Java on the surface).

You might have heard a great deal of criticism about JS coming into the course (I'm sure I did when I first picked up JS). When you first start writing programs, you'll notice that it's very liberal in what it allows. However, what you type might end up being interpreted very differently from what you imagined. Consider this seemingly basic arithmetic with various types:

1 + '2'        // --> '12'
[] + 4         // --> '4'
[] - 4         // --> -4
{} + 4         // --> '[object Object]4'

Sure, it might seem weird that we're adding together arrays, objects, and numbers (where other languages would throw type errors), but you somewhat get the idea.

This kind of weak dynamic typing accounts for much of the language's flexibility, but can also be troubling for beginners since the interpreter won't help you identify all errors. On the flip side, it allows for many techniques that are impossible in more strict languages. By learning the language properly and working with it for a while, I've grown to really like JS -- you might too!

Getting Started

We're going to learn JavaScript by reading, writing, and running code. Like I said, JavaScript usually runs in a browser, but it also runs on a platform called node.js, which is built on Google Chrome's V8 JavaScript runtime. We'll talk more about Node later in the course, but for now I'll just use it as a JS interpreter and runtime to run code snippets.

You can fire up a REPL (interactive interpreter) simply with the node command. If you don't have Node and want to follow along, you can use repl.it. (JS code can also be written to a .js file and run with node). Alternatively, you can simply open up the console in your web browser and type expressions:

browser-console

Ready? Let's start by looking at the different kinds of values in JS.

Values

Number: only one type, written literally.

String: written with single quotes or double quotes. Use single quotes.

Boolean: true, false

A note about falsy values: The following values, when evaluated as Booleans, are false:

All other values, including true, the string 'false', and all objects, are truthy. Control flows like this are common:

var course = {
  title: 'JavaScript',
  dept: 'CIS',
  code: 197
};

if (course.instructor) {
  console.log(course.instructor.name);
} else {
  console.error("No instructor found!");
}

Object: curly braces (really convenient literal notation!)

Array: square brackets literal notation.

Function: function keyword + curly braces contain block of statements.

Undefined: void, null, un-assigned-to variables.

var x;                    // --> undefined
var f = function () { };
f();                      // --> undefined

Values are garbage collected by the JS runtime (no malloc, etc. like in C).

Let's examine some more language grammar & terminology...

Expressions

Expressions are anything that produces a value:

Expressions cannot have side effects, meaning they cannot affect state of the program or the world. They only produce a value.

Statements

JavaScript programs are built as a list of statements. Each is typically terminated by a semicolon ; (C-style syntax). You can also sequence statements with , but let's not have any of that in this course.

Programs are executed sequentially statement-by-statement from top to bottom; you can guarantee order of execution.

Programs

A JavaScript file contains a program. You can run multiple files at the same time and they will share a common global environment. There is no built-in module system in ECMAScript (coming soon in v6), but we will discuss later how you can properly organize your code to achieve the kinds of module or package systems you might be familiar with from other programming languages.

In a browser environment, each <script> tag loads a compilation unit and executes the contained code immediately. Here we encounter one of the bad parts of JavaScript: reliance on global variables.

JavaScript lacks a linker (like C), so when it interprets a compilation unit, it throws all variables together into a common global namespace & scope.

Variable Scoping

We've seen a few variables in the above examples; let's look at them more closely. You declare variables simply with the var keyword (look ma, no static types!).

Variables exist in an environment and can one of two scopes:

The rule for distinguishing the two is actually pretty simple: variables declared outside of a function definition are global while those inside are local.

Local variables are created and destroyed every time a function is run (which is often just once; we'll see how this is exploited).

Variables are evaluated as if they were declared at the top of the scope they exist in. This is referred to as hoisting. Take a look at this buggy program:

var a = 100;
show();

function show () {
  console.log(a);    // --> "undefined"

  if (true) {
    var a = 123;
  }
}

Why does line 5 print "undefined"?

Before JavaScript executes a function, it looks for all variable declarations within the current scope. It then declares all those variables but doesn't assign them (effectively executing the statement var a;). Only then does the function block get executed. Assignment of a variable occurs when the function execution reaches the line where you actually assign a variable.

In the example above, a is re-declared as a local variable at the top of the show function. So when we try to print out its value with console.log, its value is undefined.

JavaScript Functions

Reference: Eloquent JavaScript and JavaScript: The Good Parts.


Generally, the craft of programming is the factoring of a set of requirements into a set of functions and data structures.

-- Douglas Crockford (JS: The Good Parts)

I won't too talk about the data structures, but let's examine the basics of what a function does in a programming language. Functions embody the logic of your application.

Pure functions have no side effects -- they don't affect the state of the world. They are easy to use and reason about since they require no context, and they are also more widely applicable to solving problems through code. However, they do not solve all the possible problems you might come across when developing software. I did say, of course, that JS is mainly used for view logic on the web; if we can't manipulate the state of a web page, then what good is our program?

This next bit of advice applies to nearly all programming languages: if a function seems simple enough that it can be written purely, then do so. In terms of programming language theory, JavaScript borrows a lot from functional languages, so it would be wise (and more natural to the language) to remain thinking functionally, trying to keep as much of the impure application logic contained to a few modules.

One more thing: like I said earlier, nearly everything in JS is an object, and that includes functions as well. They have a few special things about them, including invocation capabilities. We'll cover more of the nuances of the function object later.

Invocation

In addition to its arguments (which are immediately defined as variables), a function receives two extra special variables in its context: this and arguments.

this refers to the containing scope or context of a function invocation:

Method Invocation: when invoked as a property of an object, a function's containing object is its context.

var myObject = {
  value: 0,
  increment: function (inc) {
    inc = inc ? inc : 1;
    this.value += inc;
  }
}

myObject.increment();
myObject.increment(50);
console.log(myObject.value);  // --> 51

Function Invocation: functions not invoked as an object property are attached to the global object; this is somewhat of a flaw in JavaScript's design. This flaw is best demonstrated by example.

Let's say I wanted to augment myObject with a double method which doubles the contained value property. And maybe I'd like to use a helper function to encode this logic, like so:

myObject.double = function () {
  var helper = function () {
    this.value = this.value * 2;
  };

  helper();
};

myObject.double();
console.log(myObject.value);  // --> 51

But this doesn't work! myObject.value has not been doubled; function invocation is the reason why. Even though the helper function is being defined within a method of myObject, it gets executed in the global context. As a result, this within helper references the global context (where the value property is meaningless).

The solution ends up being a strange construction like the following. Functions don't retain a reference to their parent this, so we must manually keep a reference, usually called that:

myObject.double = function () {
  var that = this;

  var helper = function () {
    that.value = that.value * 2;
  };

  helper();
};

myObject.double();
console.log(myObject.value);  // --> 102

Constructor Invocation: when a function invocation is proceeded by the new keyword, a new object is created and the function's this scope is bound to that new object. This is strongly tied to prototypical inheritance, which is covered in the notes on objects.

Apply Invocation: because they're just objects, functions can have their own methods as well (crazy, I know). One in particular that's useful is the .apply method, which allows you to specify a this context and arguments list.

var anotherObject = {
  value: -10
}

myObject.increment.apply(anotherObject, [5]);

console.log(anotherObject.value)  // --> -5

Related: Call Invocation: works almost the same as the .apply method, but instead of using an array, you pass arguments directly:

myObject.increment.call(anotherObject, 5);

Closures

Because of the limited types of variable scoping available in JS, hiding private variables might seem difficult at first. But as as is often the case in the language, the incredible flexibility of functions comes to the rescue.

A closure in JavaScript is a function that, when invoked, is able to use variables not available in the current scope -- they were stored in the closure by the function that generated it. Here's a simple example.

var genClosure = function () {
  var privateUser = {
    firstName: 'Adi'
  , lastName: 'Dahiya'
  };

  var printName = function () {
    console.log(privateUser.firstName + ' ' + privateUser.lastName);
  };

  return printName;
}

console.log(genClosure());    // --> 'Adi Dahiya'

Here, the global environment doesn't have access to the privateUser variable, but the function returned from genClosure is still able to access it. You'll see this immediately in any code intended for execution in Node: it will be wrapped in an Immediately-Invoked Function Expression (IIFE).

IIFE

We know by now that global variables in JavaScript are a Bad Thing. Luckily, JS programmers have developed a pattern to mitigate this concern. An immediately-invoked function expression is an anonymous function used at the top level of a JS program to provide a lexical scope for just that file / module. It looks like this:

(function () {

  var Person = function () { ... };
  Person.prototype.getDescription = function () { ... };
  ...

  module.exports = {
    Person: Person,
    ...
  };

})();

What we've done here is define an anonymous function and immediately invoked it so that the contained code is run immediately when the file is loaded. The use of a function makes any an all var declarations scoped to the anonymous function, not the global context.

Note the exports exposed by this module at the end of the IIFE. This is an instance of a closure!. When another file (or the Node interpreter) imports this module, it can use any properties or functions from the exports object, but it can't access the rest of the variables in the IIFE scope. Specifically, this example uses AMD (asynchronous module definition) syntax, which is the standard in Node.js.

Code Style

Code style is extremely important when developing code that you hope to last longer than its first writing. This is especially important with JavaScript because the interpreter is very tolerant and will allow a great deal of syntactic mistakes to pass undetected when code is executed. JS developers must exhibit great discipline in writing idiomatic code that is easily readable to all other developers.

Because of this, we're going to be adhering to a style guide for code written in this class. If you ever go out and write JavaScript professionally, you would do well to adhere to a structured style guide as well.

Indentation: use spaces, not tabs. 2 or 4 spaces.

Line Length: avoid lines longer than 80 characters.

Comments: be generous with comments, but try to describe more about why a function exists and the assumptions it makes rather than how it does things. JavaScript is an expressive enough language (with first class functions, etc.) that you can write very readable code that expresses intent properly.

Whitespace: most tokens should be separated by whitespace. Specifically, you should always have a space before and after your infix operators (+, -, &&, etc). Declare functions like this:

var parseInt = function (string, radix) {
  ...
};

if / else blocks and for / while loops should always be split into lines, as per the K&R standard (aka The One True Brace Style):

if (predicate) {
  ...
} else {
  ...
}


for (var i = 0; i < len; i++) {
  ...
}


while (condition) {

}

Functions: functions should generally be declared by assigning to an identifier:

var parseInt = function (string, radix) {
  ...
};

If you use an identifier with a function expression, it provides the added benefit of showing up with a proper identifier in stack traces.

var parseInt = function parseInt (string, radix) {
  ...
};

I generally recommend against using a function definition syntax like the following example, because the scoping works slightly differently – this function is "hoisted" to the top of its scope while the above function expression syntax means that a function is only available when its expression is evaluated (and stored to that variable).

function parseInt (string, radix) {
  ...
};

Operators

Never use the double-equal ==. It does type coercion and is generally recognized as a bad idea. Use the triple-equal ===, which does strict equality, instead.

Avoid doing assignments in the conditional of if and while statements:

if (a = b) {

...looks like a statement where intent can easily be misinterpreted as

if (a === b) {

eval is evil and misused. Don't use it in your programs. It has aliases, so avoid those as well. Don't use the Function constructor or pass strings to setTimeout / setInterval.

Bitwise operators (&, |, and ~) should never be used in place of logical operators (&&, ||, and !).

Similarly, with should not be used.

for-in loops should never be used for arrays , and should almost never be used for objects. If you really need to iterate over an object's properties, you should probably use an Underscore method (or absolutely, positively know what you're doing with Object.hasOwnProperty).

Code Cruft

Don't leave debugging-type functions (like console.log or alert) in your finished code. The obvious exception is in cases where writing to the console, or displaying an alert message, is the intended function of the code.


Finally, you may want to include an Immediately Invoked Function Expression (IIFE) in your JavaScript files to avoid polluting the global namespace - but this is not strictly necessary if you follow general best practices.

// file.js
(function () {
  'use strict';

  // do stuff


  module.exports = {
    // export stuff
  };
})();

ESLint

In order to do ensure that your JavaScript submissions adhere to this style guide, we will be using ESLint. You should definitely check your code style with ESLint before submitting - see Lecture 1 for instructions on how to use ESLint.

We will provide you with a specific .eslintrc file for each homework, but they will all have these rules (which are among the best-accepted and most general JavaScript style guidelines).

{
  "rules": {
    "brace-style": [1, "1tbs"],
    "camelcase": 0,
    "no-alert": 1,
    "no-bitwise": 1,
    "no-empty": 1,
    "no-ex-assign": 1,
    "no-lonely-if": 1,
    "no-loop-func": 1,
    "no-reserved-keys": 1,
    "quotes": [1, "single", "avoid-escape"],
    "space-after-keywords": 1,
    "space-before-blocks": 1,
    "space-infix-ops": 1,
    "strict": 0
  }
}

The specific definitions for these rules can be found in the ESLint rules documentation. Including them in your final project would probably be a good idea as well - it could save you a great deal of trouble down the line!

JavaScript Objects & Classes

Reference: Eloquent JavaScript and JavaScript: The Good Parts.


Prototypical Inheritance

All objects are linked to their prototype, an object from which they can inherit properties. Those created as object literals are linked to Object.prototype (built into the language).

When you attempt to retrieve an object property, JS will walk up the prototype chain until it finds a matching property name (known as delegation).

Prototypes are dynamic: changing or adding a property on one will make it immediately available to all objects that are linked to it.

Keep in mind: The delegation of properties sometimes creates problems when you are only concerned with the immediate object in question (for reflection or enumeration). In this case, it's helpful to use the hasOwnProperty function available to all objects:

for (prop in flight) {
  if (flight.hasOwnProperty(prop)) {
    console.log(prop + ": " + flight[prop]);
  }
}

There's actually another way to solve this problem by being more specific about what type of property you are defining. The Object.defineProperty method lets you set a property on an object and configure it with a few parameters, including whether the property is enumerable, writable, etc. See its documentation.

Object.defineProperty(flight, 'toString', {
  enumerable: false,
  value: function () { ... },
  writable: false
});

Note: you are not guaranteed the order of properties with a for in loop on objects. If you are really concerned with enumerating the properties in order, it's best to list them out manually in an array:

var categories = [
      'cold'
    , 'hot'
    , 'espresso'
    ]
  , coffeeProducts = {
      cold: [ 'iced', 'cold brew' ]
    , hot: [ 'pour over', 'french press', 'drip' ]
    , espresso: [ 'americano', 'macchiato', 'shot' ]
    };

for (var i = 0; l = categories.length; i < l; i++) {
  var prop = categories[i];
  console.log(coffeeProducts[prop]);
}

Pseudo-classes

JavaScript is slightly conflicted about the nature of its inheritance, so its constructor syntax makes it seem somewhat classical. You will see many objects created with the new keyword:

var a = new Array(16);

var americano = new Coffee({
  type: 'espresso'
, variations: [ 'hot', 'iced' ]
, preparation: 'Pull two espresso shots and pour them into a mug of water...'
};

The new keyword essentially tells the runtime to invoke the constructor method from a function's prototype.

Before we further inspect how constructors work, let's consider what happens in the JS runtime when a function is created (they're objects too, after all).

When a function object is created, the internal Function constructor that produces the function object runs some code like this:

this.prototype = { constructor: this }

This behavior seems somewhat strange. Why do we need regular ol' functions to have constructors associated with them? Only those functions that actually construct class-like objects should behave like this, right? Here we encounter one of the flaws in the way JS does inheritance. JavaScript doesn't discriminate whether functions are meant to be constructors. This means that you as a programmer ought to demarcate constructors yourself through naming conventions (capitalize the first letter of the function name).

When JS runs a line of code invoking the new keyword, some additional code gets run after this.prototype = { constructor: this }. Note that this represents the constructor function being invoked):

var that = Object.create(this.prototype),
    other = this.apply(that, arguments);

if (typeof other === 'object' && other !== null) {
  return other;
} else {
  return that;
}

The method Object.create (see documentation) simply creates a new empty object with its prototype set to a provided value. Then the constructor function is applied to the new object with proper arguments forwarded. If the constructor returned a value, that value is returned as the newly constructed object. Otherwise, the new object from Object.create is returned.

Knowing this, we can write some pseudoclassical code like this:

var Person = function (first, last, email) {
  this.firstName = first;
  this.lastName = last;
  this.email = email;
};

Person.prototype.getName = function () {
  return this.firstName + " " + this.lastName;
};

Person.prototype.getEmail = function () {
  return this.email;
};

The Person.prototype object now looks something like a class definition:

{
  constructor: function (first, last, email) { ... },
  getName: function () { ... },
  getEmail: function () { ... }
}

A Person object would behave like so:

var adi = new Person('Adi', 'Dahiya', 'adahiya@seas.upenn.edu');
console.log(adi.getName());       // --> "Adi Dahiya"

And let's say we wanted to inherit from this Person "class":

var Instructor = function (first, last, email, course) {
  this.firstName = first;
  this.lastName = last;
  this.email = email;
  this.course = course;
};

Instructor.prototype = new Person();

Instructor.prototype.getDescription = function () {
  var name = this.getName();
  return name + " teaches " + this.course;
};

var adi = new Instructor('Adi', 'Dahiya', 'adahiya@seas.upenn.edu', 'CIS 197');
console.log(adi.getDescription());  // --> "Adi Dahiya teaches CIS 197"

A couple things to note:


We seem to be getting somewhere now! This behavior looks kinda object-oriented, doesn't it? But there are some wonky things going on.

Also, as I indicated above, there is some danger in defining a constructor like this. What would happen if you forgot the new keyword when trying to use a constructor? (hint: how are functions regularly invoked?)

Again, this design flaw in JavaScript is mitigated by capitalizing the first letter of constructor functions. The hope is that a programmer reading the code can easily identify missing new keywords.

As for the flaws mentioned above:

Object.create

In ES5 (the current JS standard), there was a new method introduced called Object.create, which provides some simple syntax for differential inheritance. Its first argument is the prototype of the object you want to inherit from (or null), and the second (optional) argument is map of properties you want to set on the new object.

Another way to express the above Instructor class using Object.create:

var Instructor = function (first, last, email, course) {
  Person.apply(this, arguments);
  this.course = course;
};

Instructor.prototype = Object.create(Person.prototype);

Instructor.prototype.getDescription = function () {
  var name = this.getName();
  return name + " teaches " + this.course;
};

This accomplishes the earlier goal of separating Instructor and Person's prototype (there's a level of indirection since Object.create produces a new object) while also invoking the super constructor.

Both this and the above method (in the section "Pseudo-classes") for object-oriented programming in JavaScript are acceptable. The Object.create method slightly reduces the number of lookups on the prototype chain when invoking "class" methods.


Here's a real world example of inheritance from the source code for the CIS 197 website. Note that the actual code in the repository is compiled from Coffeescript, not hand-written (but you'll notice the compiler produces very readable code!).

/*
* Manages state of the section-navigation functionality in the sidebar.
*/
var SectionNav = (function () {

  SectionNav.ACTIVE_CLASS = 'active';   // why can we do this up here?

  function SectionNav (target) {
    this.selected = '';
    this.target = target;
    this.sections = this.target.children('section');
    ...
  };

  SectionNav.prototype.navigateTo = function (sectionName) {
    this.sections.removeClass(SectionNav.ACTIVE_CLASS);
    this.target.children("." + sectionName).addClass(SectionNav.ACTIVE_CLASS);

    this.nav.children().removeClass(SectionNav.ACTIVE_CLASS);
    this.nav.find("." + sectionName).addClass(SectionNav.ACTIVE_CLASS);
    this.selected = sectionName;
  }

  ...

  return SectionNav;

})();

JavaScript in the browser

Reference: Eloquent JavaScript & Mozilla Developer Network.


The browser is a really hostile programming environment.

-- Douglas Crockford, The JavaScript Programming Language (video lecture)

The web browser is actually a great place that's experienced a huge deal of development in the past few years (again, this is correlated with an influx of front end dev tooling). But this quote does have a nugget of truth to it, as you'll experience yourself in writing JS for the browser.

The reason that web APIs and browsers have been able to advance quickly is that their development has been relatively decentralized. There is a standardizing body, but there is no central authority developing browsers and implementing language features. Various browser vendors (Google, Mozilla, Microsoft, etc.) have implemented features at different rates and this has made cross-browser support somewhat haphazard.

What is a browser?

A web browser has two main components that allow you to view and interact with web pages:

Here are the major JS engines and rendering engines, respectively:

There are even sometimes differences in the core JavaScript runtime across browsers. For example, IE's runtime is known to have a memory leak when a circular reference is retained between a user-created object and a built-in object (such as a DOM reference).

Most of the development in these engines has been concerning performance. Mozilla, generally the leader in cutting-edge JS research, has released JS engines fast enough to run 3D games like Quake at 60fps in the browser.

A bit of syntax

Before going further, let's recall what a basic HTML document looks like:

<!doctype html>
<html>
  <head>
    <title>CIS 197 JavaScript</title>
  </head>
  <body>
    <h1>Welcome to CIS 197</h1>
    <p>
      This is a course about ...
    </p>
  </body>
</html>

In my examples, I'll usually eschew the <doctype>, <html>, <body>, etc. tags and write a simplified version of the page.

Loading JavaScript in the browser

Well, we're not quite at the level of developing browser games right now. Let's first understand how JS is loaded in the browser environment.

Web browser programs differ fundamentally from most other programs you are used to writing in some key ways that have nothing to do with the JavaScript language. First off, think about what you're actually doing when you download JavaScript from the Internet to your web browser and executing it -- you're trusting source code from a huge number of websites of varying degrees of well-meaning to run on your computer. Sounds dangerous, doesn't it?

But we all get off just fine on the web, don't we? This is largely due to a neat little concept called sandboxing. Browsers severely limit the scope of capabilities that a JavaScript program can act on. It can't look at the files on your computer, modify things outside of the web page it's embedded in, or contact servers other than the one which served its web page.


Just like in a Node.js runtime, JS is executed in a global context, except this time it's called window. It represents the current session in your browser tab, which is cleared with every page refresh. That means your browser requests new JS files and starts up a new global context when you switch or reload pages.

There are a few ways to invoke JS in an HTML page:

The first two methods result in the JS code being parsed & run immediately as the browser engine encounters it (while parsing the HTML document), while the last (as indicated) runs some bit of JS code as an event callback.

Note: It's good practice to load your <script> tags as late as possible in the HTML. This way, your browser displays a (possibly functioning) correctly-rendered HTML document with CSS before using precious CPU cycles on parsing and interpreting JavaScript code. Also, avoid inline script tags in favor of linked .js files. These files should be loaded at the end of <body>:

<!doctype html>
<html>
  <head ... </head>
  <body>
    <h1>Welcome to CIS 197</h1>
    ...

    <script type="text/javascript" src="scripts/foo.js"></script>
    <script type="text/javascript" src="scripts/bar.js"></script>
  </body>
</html>

There's no convenient way to load multiple scripts at once like this; you'll simply have to link all of them. But as we'll see later, various tools allow you to package your application into a single script, which is usually also good practice.

Document Object Model

Web pages structure is modeled using the Document Object Model, or DOM for short. We know that HTML is a heirarchical markup language, so it makes sense for our DOM to be a tree data structure.

DOM tree

Source: Eloquent JavaScript

In the above diagram we can see the two main kinds of DOM nodes: regular nodes and text nodes. Regular nodes are those with angle brackets in HTML markup < > and text nodes are simply inline strings (for example, the content of a <p> tag.

The root of an HTML document is available as a global variable in the browser JS runtime called document -- this is the starting point for locating any other node in the tree. There are a number of properties available on nodes; explore them in a browser console and/or the MDN DOM Reference.

document.body;

var firstNode = document.body.firstChild;

if (firstNode.nodeType === 3) {
  // Text node
  console.log("First node contains text: " + firstNode.nodeValue);
} else {
  // Regular node
  var n = firstNode.childNodes.length;
  console.log("First node has the tag <" + firstNode.nodeName + "> and
  " + n + " child nodes");
}

So far what we've observed is an application programming interface for an HTML document. Our web browser did the work to parse the DOM and model it for us. Can we write a function that does the opposite (given a DOM node, print out its HTML and the HTML of its children)?

var isTextNode = function (node) {
      return node.nodeType === 3;
    }
  , withBrackets = function (str, isClosing) {
      if (isClosing) {
        return "<" + str.toLowerCase() + "/>";
      } else {
        return "<" + str.toLowerCase() + ">";
      }
    };

var toHTML = function (node) {
  if (isTextNode(node)) {

    return node.nodeValue;        // TODO: escape HTML

  } else {

    var buffer = withBrackets(node.nodeName)
      , len = node.childNodes.length;

    if (len) {
      for (var i = 0; i < len; i++) {
        buffer += toHTML(node.childNodes[i]);
      }

      buffer += withBrackets(node.nodeName, true);
    }

    return buffer;
  }
};

console.log(toHTML(document.body));

This is a neat little implementation, but it turns out there's a built-in function with pretty similar (and more robust) functionality. The outerHtml property on DOM nodes returns a string of the HTML representation of a node (itself included). innerHtml is the same, but it only looks at children of the node.

document.body.outerHtml;    // --> "<body><h1>...</h1>...</body>"

Homework 2: XML Document Modeling

Due Fri, January 30.

Implementation stub available for download.

Before starting this assignment

Review Lecture 2.

Install the sax.js library by running npm install in the root of your homework directory. Also, add the line "node_modules" to your .gitignore file in this same folder (this prevents libraries from being committed to the git repository).

See this page to understand the relationship between XML and HTML.

Modeling XML documents

In this assignment you're going to develop an API for working with XML documents. Similar to how web browsers parse an HTML document (which is very similar to XML in structure anyway) and produce the Document Object Model for us to work with in JavaScript, the tool you produce will be able to take an XML document (as a string) as input and generate a JavaScript object that can inspect and filter the document tree.

First off, we ought to decide on a data structure to keep track of the XML nodes found during parsing. Since this is an exercise in object-oriented JS, we'll model XMLElements and the overall XMLDocument as objects.

TODO: Define a JavaScript class (using prototypes) called XMLElement with the following properties and methods. The sample values show the expected representation for an XML node like <question seq="1">Who framed Roger Rabbit?</question>.

{
  // Is this a document node or an element in the tree?
  type: 'element',

  // Name of the XML node
  name: 'question',

  // Hash of the node attributes
  attributes: { seq: 1 },

  /*
   * Array of child nodes. Publicly provided in the API to allow for
   * iteration over child nodes.
   */
  children: [ ... ],

  /*
   * Returns a list of nodes in this subtree that match the given `nodeName`
   * by doing a recursive lookup. DO NOT include the current node (that is,
   the one on which this method was called).
   */
  find: function (nodeName) { },

  /*
   * Returns a list of nodes in this subtree that have matching attributes
   * as provided by an input hash of attrs (names & value pairs).
   * If a particular attribute value is not provided (is `undefined`), this
   * method should match simply on attribute names (i.e. if the node has an
   attribute with that name at all). Again, DO NOT include the current node.
   */
  findWithAttribute: function (attrs) { }
}

These are not the only properties and methods allowed in the class; feel free to add more as necessary for your implementation. However, the XMLElement constructor should take in a single config object (hash) that specifiesits name and attributes. You should be able to call it like this:

var elmnt = new XMLElement({
  name: "My awesome node",
  attributes: {
    title: "The Most Awesome Node",
    isAwesome: true
  }
});

TODO: Define a class called XMLText that simply wraps a text string in the XML tree. For example, the text inside <greeting>Hello world!</greeting> would be an XMLText object with the properties:

{
  type: 'text',
  value: 'Hello world!'
}

The constructor for your XMLText object should take in value, like so:

var txt = new XMLText('My awesome text node');

Here's an example of what the children array in <xml>Hello, <who name="world">world</who>!</xml> would look like:

children: [
  { type: 'text', value: 'Hello' },       // XMLText
  { type: 'element', name: 'who', ... },  // XMLElement
  { type: 'text', value: '!' }            // XMLText
]

TODO: Define another class called XMLDocument that inherits from XMLELement. It will represent the top-level element of the XML documnent tree. Ensure it has these properties / methods (in addition to those inherited from XMLElement):

{
  // Is this a document node or an element in the tree?
  type: 'document'

  /*
   * Creates an XMLElement from a hash of params (structured in the same way
   * as the XMLElement class definition above):
   *
   * params = { name: 'answer', attributes: { seq: 1 }, children: [...] }
   *
   * This element should be appended to the children of the document.
   */
  createElement: function (params) { },

  /*
   * Similar to `createElement`, this generates an XMLText node and adds it
   * to the document.
   */
  createTextNode: function (string) { }
}

Generating the model

To actually do the string parsing of the XML Document, we're going to be using a library called sax. While understanding how a parser works is interesting on its own, I'll leave this topic for another course.

This parser reads in an XML document and provides callback hooks for events that occur during parsing. If you're not familiar with callbacks -- check out the slides form Lecture 2. Some resources for more information about callbacks:

Parser events we are interested in are the following:

See the sax.js documentation for further reference. We don't care about CDATA for this assignment (even though it's in the XML standard).

Your implementation should decide what to do when each of these events occurs. In the end, a single XMLDocument that models the whole tree should be produced and passed to the callback. You should be able to interact with the document object by invoking its tree filtering methods.

I have provided some implementation already in the buildXMLDocument function that starts off the parser. The tricky part is to now fill in the parser callback hooks in a way that builds a proper tree model. While parsing, it would behoove you to keep track (with a closure reference) of the current position of the parser, or where it is in the tree. You won't get this information in the parser callback; simply the relevant information that was just parsed.

Hint: A stack might be a good way to keep track of the tag the parser's on.

In the end, you should be able to call buildXMLDocument like so:

buildXMLDocument(SAMPLE_XML, function (doc) {
  // This callback is invoked when the XML document model is fully built.
  console.log(doc);
});

Debugging

In the implementation stub, there are module exports set up for the xml-model file. We will be using the provided 3 attributes (XMLElement, XMLDocument, and buildXMLDocument) to test your code, so you should not take them out. However, you are welcome to add more exports for testing. For example, if you set up a testing function called testModel, your exports at the bottom of the file might look like:

module.exports = {
  XMLElement: XMLElement,
  XMLDocument: XMLDocument,
  buildXMLDocument: buildXMLDocument,
  testModel: testModel
};

To run the file and load these exports into a node interpreter, use the following commands (in the hw2 folder):

bash-3.2$ node
node> var xml = require('./xml-model');
node> xml.testModel();

Also, if you're feeling adventurous, you could learn about the node debugger (related: node-inspector).

DOM Manipulation

DOM review

The document object gives us ways of accessing and changing the DOM of the current web page.

General strategy for DOM manipulation:

  1. Find a DOM node using an access method and store it into a variable.
  2. Manipulate the DOM node by changing its attributes, styles, inner HTML, or appending new nodes to it.

Basic access methods: getElementById, getElementsByTagName HTML5 access methods: getElementsByClassName, querySelector

Attributes are attached directly to the DOM element object; you can also use a getter / setter.

var img = document.getElementById('lolcat');

var oldSrc = img.getAttribute('src');
img.setAttribute('src', 'http://placekitten.com/100/500');

img.src = oldSrc;

Styles are applied with the style object property on nodes:

img.style.padding = '10px';

Changing content is possible with the .innerHtml property on nodes:

var photoContainer = document.getElementById('#photo-container');

photoContainer.innerHtml = "Here's the photo!";

Creating nodes is possible with document methods:

var photo = document.creatElement('img');
photo.src = 'http://placekitten.com/100/500';

photoContainer.appendChild(photo);

These are the basics of working with the DOM. The rest of the lecture will build on these techniques and introduce browser events.


Chrome DevTools

Before we move on, though, we need to upgrade our toolkit for front end development. You all have a pretty powerful set of tools to inspect, debug, and profile web pages built into your browsers already – today I'll demonstrate the tools that ship in the Chrome browser (in-class demo).

All of this applies to Firefox, Safari, and IE as well (at least their modern versions), as they have similar built-in dev tooling.

For a web tutorial, refer to Google's DevTools docs.


So far in this course we've been programming in vanilla JavaScript; that is, we haven't really introduced any dependencies to our source code.

What is jQuery?

It's a JavaScript library. The most widely-used one ever. Here's why it's cool:

It'll make your life a whole lot easier.

Let's look at a few of the things jQuery is useful for.

Selecting elements

jQuery uses Sizzle, which is a CSS selector engine written in JavaScript. That means that you can select DOM elements in your JS source the same way you're selecting them to write CSS. Here are some examples:

// ID
// ----------------------------------------------------------
var myEl  = document.getElementById('myEl')
  , $myEl = $('#myEl');

// Tag name
// ----------------------------------------------------------
var myTable   = document.getElementsByTagName('table')
  , $myTable  = $('table');

// Descendant of ID
// ----------------------------------------------------------
var lessons   = document.getElementById('content').getElementsByClassName('lesson')
  , $lessons  = $('#content lesson');

// Every other node
// ----------------------------------------------------------
var inputs = document.getElementsByTagName('input')
  , oddInputs = [];

for (var i = 1, l = inputs.length; i < l; i += 2) {
  oddInputs.push(inputs[i]);
}

var $oddInputs = $('input:odd');

You can see here that jQuery allows much more expressive and elegant selection functionality than that which is exposed by the DOM API. However, take caution – the Sizzle selector API hides some underlying performance assumptions you can make about your selection code. It's sort of a leaky abstraction, one which requires you to be knowledgeable about the lower-level functionality in order to write good, performant code.

If you take a look at how the style system matches rules and similarly, how Sizzle selectors are evaluated, you'll start to notice some pitfalls. Most of these apply to selectors in your CSS as well.

The key to writing fast jQuery selectors is understanding which native functions the library can leverage (most importantly, document.getElementById, document.getElementsByTagName, and element.getElementsByTagName). Without these, jQuery must rely on a large DOM traversal loop that checks a large number of elements.

To check the compatibility of DOM API methods like document.getElementsByClassName and document.querySelectorAll (which tries to consolidate selection in the same fashion as Sizzle), refer to the compatibility table at the bottom of a Mozilla Developer Network DOM reference page for that method.

One more thing: keep in mind that when you select something with jQuery, it gets wrapped into a jQuery object instance containing all* the library's functionality. Think this has a greater runtime cost than using the DOM API? You bet it does! So you'll want to avoid recurring selectors:

$('#myDiv').fadeout(function () {
  $('#myDiv').css('color', 'red');
  $('#myDiv').fadein();
});

... is better written as ...

var $elem = $('#elem_id');
$elem.fadeout(function () {
  $elem.css('color', 'red').fadein();
});

In this example you also see an example jQuery's chaining syntax, which makes for a nice-to-use API.

* Well, not exactly all the functionality -- there are some global methods & constructors exposed on the jQuery object itself, like $.Deferred, etc.

Manipulating elements

Once you have your target element selected, what can you do with it? I won't go over all the methods available to jQuery object instances (see the docs for that), but it suffices to say that you have a lot of DOM manipulation power once you select an element with jQuery.

var $myDiv = $('#myDiv')
  , $otherDiv = $('.other').clone();

$myDiv.addClass('active');

$myDiv.after('here comes another...');

$myDiv.append($otherDiv);

// Prepend numbers to the children of a node
$myDiv.children().forEach(function (child, i) {
  $(child).before(i + '. ');
});

// Query within a node subtree with CSS selector syntax
$myDiv.find('a').each(function (i, anchor) {
  // Keep in mind that in args  ^ index is first!

  $a = $(anchor);

  // Print contents, then clear them
  console.log($a.text());
  $a.empty();
});

var $inputs = $myDiv.find('input')
$inputs.attr('username');

if (!$myDiv.next()) {
  console.log('#myDiv is the last child of its parent');
}

$myDiv.fadeOut(500);
$myDiv.fadeIn(500);

// Remove from DOM
$myDiv.remove();

As we saw above, we can chain some of these methods:

$myDiv.addClass('active').append($otherDiv);

$mydiv.find('inputs').attr('username');

$myDiv.fadeOut(500).fadeIn(500);

This is possible because many jQuery methods return this, a self-referential pointer to the jQuery object instance being manipulated.

$.fn.empty = function () {
  var elem, i = 0;

  for (; (elem = this[i]) != null; i++) {
    elem.innerHTML = '';
  }

  return this;
};

"Things I Learned from the jQuery Source"

See the screencast here. Browser the source here.

Undefined

To ensure a valid undefined keyword, jQuery and other libraries do something like this in their IIFE:

(function (window, document, undefined) {
  ...
})(this, document);

Animation

For animations: $.delay() only works if there's an animation queue already there, so this doesn't work:

$(elem).delay(2000).fadeOut()

You can do one of the following instead:

$(elem).fadeIn().delay(2000).fadeOut()

$(elem).queue(function () {
  this.delay(2000).fadeOut()
});

Browser Support

$.support is the result of a bunch of tests on the current DOM to notify you of browser bugs, unsupported things, etc. Also use $.browser similarly.

Stylish things

When you're trying to assign a variable, you can do sometimes elegantly affect some state as well:

var head = document.getElementsByTagName('head')[0];

var base = document.getElementsByTagName('base')[0] || (function () {
  return head.insertBefore(d.createElement('base'), head.firstChild);
})();

Similarly, nested ternary operators let you be concise in assignment:

if (data === 'true') {
  data = true;
} else if (data === 'false') {
  data = false;
} else if (data === 'null') {
  data = null;
} else if (!jQuery.isNaN(data)) {
  data = parseFloat(data);
}

// ^ above can be rewritten as:

data = data === 'true' ? true :
  data === 'false' ? false :
  data === 'null' ? null :
  !jQuery.isNaN(data) ? parseFloat(data) :
  data;

One last thing...

jQuery.fn === jQuery.prototype

You'll see jQuery functions and plugins being defined on jQuery.fn (and it might show up in stack traces). It's just a prototype object (but it doesn't correspond to the pseudo-classes we've been talking about).


Browser events →

References:

Browser Events

← DOM Manipulation

To build any application or web page that allows user interaction, you'll need to work with browser events.

If you recall the JS in browsers notes, we saw an event handler being attached to a "click" event on a DOM node:

<button class="submit" onclick="myApp.submitFormAsync();"> Submit </button>

"onclick" handlers like this are not advised (you should generally keep JS code only in JS files), so here's the API for it in plain JS:

domNode.addEventListener( eventType, eventListener, useCapture );

(the last argument indicates whether the event should bubble up in the DOM tree as normal)

... so the above handler becomes:

// assume myApp.submitFormAsync is a defined method

var button = document.getElementsByTagName('button');

button.addEventListener('click', function () {
  myApp.submitFormAsync();
});

Events types

Browsers can trigger many different events (see the full reference here). Here are some important ones:

Listeners are functions bound to events that get executed when a certain event occurs (also known as event handlers). They are passed a properties object relevant to and created by the event. For example, a MouseEvent reports mouse coordinates:

document.addEventListener('mousemove', function (e) {
  console.log('Moused to (' + e.clientX + ', ' + e.clientY + ')');
});

Event targets are also often available to handlers with event.target; for example, it's often useful to know which element on a page was clicked.

Note: since browser JavaScript is single-threaded, no two event handlers run at once. If an event occurs during the execution of a handler, it gets added to an event queue to be handled after the main thread is freed up.


Event propagation

Events bubble up the DOM tree in a phenomenon known as event propagation. Let's take the example of a block of text in a <p> tag with links in it:

<body>
  <h1>DOGECOIN</h1>
  <p>
    dogecoin is an open source peer-to-peer cryptocurrency, favored by Shiba
    Inus worldwide.

    much newbie?
    <a href="/get-started">read the getting started guide</a>, then
    <a href="http://www.reddit.com/r/dogecoin" target="_blank">join the community at /r/dogecoin</a>
  </p>
</body>

When you click on one of these links, the browser follows this chain of events:

If you want to prevent this kind of propagation up the document tree, you can use event.stopPropagation. event.preventDefault also exists to let you specify when the built-in browser functionality for an event should be ignored.

Cross-browser compatibility

If you want your website to work across all browsers, there are a number of inconsistencies you're going to have to handle in your code. For example, IE8 and below use this alternative method for attaching a click handler:

domNode.attachEvent('onclick', function () {
  // do stuff
});

... and it doesn't even support the third argument, useCapture.

We might try and be clever by creating a wrapper registerEventHandler function like so:

function registerEventHandler (elem, event, handler) {
  if (typeof elem.addEventListener === 'function') {

    // Regular browser
    elem.addEventListener(event, handler);

  } else {

    // IE
    elem.attachEvent('on + event, handler);

  }
}

Similarly, to detach events, you'd need something like this:

function unregisterEventHandler (elem, event, handler) {
  if (typeof elem.removeEventListener === 'function') {

    // Regular browser
    elem.removeEventListener(event, handler, false);

  } else {

    // IE
    elem.detachEvent('on' + event, handler);

  }
}

And while normally the event object passed to a handler contains event properties, IE8 and below attach these properties to a global event variable, so you might see code like this:

function onKeypress (event, handler) {
  event = event || window.event;

  // do stuff
}

registerEventHandler(document, 'keypress', onKeypress);

For a detailed run down of these kinds of cross-browser incompatibilities, see Chapter 13 of Eloquent JS.

jQuery events to the rescue!

Much in the same way that jQuery abstracts DOM manipulation for us, it can also come to the rescue for dealing with events. Let's say we have a hover event handler function like this:

function onHover (e) {
  $(this).attr('hovered', true);
}

There are a few methods jQuery provides to bind handlers, including:

This article, although slightly outdated, does a great job of explaining the differences in these approaches.

Document ready state

After your browser successfully receives a web page response from an HTTP request and parses its HTML, it has to do work to layout elements on the page from the HTML markup. There's a delay (on the order of milliseconds) between content appearing in your browser window and the DOM actually being ready. (Note: keep in mind that script execution can occur either before or after HTML layout, depending on where the <script> tag is in the HTML markup).

Once a document is "ready", you can reliably interact with it in a script. document.readyState is set to 'complete' when this event occurs. Most logic in your JS applications should occur only after this event, so it's a common pattern you'll encounter.

Typically, you can listen for the 'DOMContentLoaded' event on the document and run all your important DOM-interacting code within an event handler like this:

function onDOMReady () {
  // do everything
}

document.addEventListener('DOMContentLoaded', onDOMReady);

Unfortunately, this event isn't fired in all versions of IE. The 'load' event on the window object is a pretty reliable callback:

window.addEventListener('load', onDOMReady);

But this doesn't capture all of the state of document ready. Using jQuery is usually your best bet for a reliable document ready state across all browsers as it handles most of the compatibility edge cases you might encounter. There is a convenient event binding method called ready on a jQuery selection of the document:

$(document).ready(onDOMReady);

A shorter syntax for this exists in modern jQuery. If you pass a function as the first argument to the $ function, that function gets executed on document ready.

$(onDOMReady);

In practice, an anonymous function is typically used (this is currently the preferred syntax):

$(function () {
  // do everything
});

If possible, you should keep your DOM-ready code small and organized, such that application logic that doesn't deal specifically with the DOM exists outside of this function. A typical file structure might look like this (recall the advice about separation of pure functions from impure ones, and think about how it relates here):

// myScript.js
(function () {
  'use strict';

  /*
   * File-scoped global variables & imports
   */
  // var foo = {}, bar = 'baz', ...;


  // JS functions and classes that don't interact with the DOM


  // Document ready
  $(function () {

    // DOM interaction logic

  });


  // Exports, if any
  module.exports = {
    // ...
  };

})();

References:

Homework 3: Map Building

Due Friday, February 6.

Implementation stub available for download.

Before starting this assignment

Review Lecture 3.

Review notes about DOM Manipulation and Browser Events. If you are completely new to jQuery, you might want to go through this tutorial on the jQuery website.

Building a map builder

map-builder

This project will introduce you some important jQuery concepts while helping you design a map-building application. It will run entirely in the browser; you're going to submit the whole client-side application (well, the JS parts at least - we'll provide the HTML and CSS). It's inspired a lot by Pokémon maps. Here's a general outline of the functionality:

  1. Pick a swatch from the palette
  2. Hover over the map canvas to see swatch in context
  3. Click on the canvas to paint it with a selected swatch
  4. Use the arrow keys to navigate a player sprite around the map.

To begin, take a look at the provided HTML and CSS files that lay out the map building interface. There are a few files linked to the index.html web page - styles in main.css, the jQuery library in lib/js/jquery-1.11.0.js, and the scripts that you will produce in this assignment: map-builder.js and player.js.

There are two main sections of concern in index.html: the swatches palette at the top and the map canvas below it. The map swatches are already populated for you and have associated tile images (they show up as background images applied through CSS). A palette swatch looks like this:

<li class="swatch water-se"></li>

water-se is the swatch name; this will be the same class name applied to map tiles to build up the map. A 'selected' swatch will have the special 'selected' class applied to it - the default selected swatch should be 'grass'. Do not change the default selected swatch - we'll expect it to start out as grass in our tests.

TODO: Start off by creating a JavaScript class called MapBuilder to encapsulate the map builder logic (this will make it easy to convert to a JS plugin later on). Its constructor should be accept two arguments:

  1. A jQuery element that represents the map-builder container. It should contain a .palette element and .map element within it. In the provided HTML page, you can immediately see that this corresponds to the <div id="map-builder"> container in the document body. The constructor should save this provided $elem (note the prepended $ on jQuery variable names) as a property in the MapBuilder instance.

  2. An optional params object with width and height properties indicating how large the map should be in tile dimensions. When this argument isn't provided, the MapBuilder should resort to some reasonable stored defaults - in this case, a width of 30 and a height of 15.

Note: For this project, it is likely that all the work done by MapBuilder will occur in its constructor (not through any instance methods after construction). We're using the class more as a method of organization and encapsulation rather than for more "traditional" object-oriented purposes. However, note that this does allow us to pass a MapBuilder object to our Player class, which will greatly simplify things later on.


Selecting swatches

select-swatches

The first task will be to have MapBuilder keep track of which swatch is currently selected in the palette. Visually (in CSS), we'll represent this with the 'selected' class on swatches. If the 'grass' swatch is selected, its palette element looks like this:

<li class="swatch grass selected"></li>

TODO: Write a function on the MapBuilder prototype called setupPalette that gets invoked in the builder constructor (in main.js). This setup function should bind a click handler to palette swatches that

  1. Ensures only one swatch is ever 'selected' (check out jQuery's [.addClass]https://api.jquery.com/addclass/ and .removeClass methods).
  2. Stores the currently selected swatch name in the MapBuilder instance. You'll probably want a helper function to extract the swatch name from a DOM element. It will be helpful to get access to the raw DOM element classList. For example:

     $grassSwatch = $('.swatch.grass');  // jQuery element
    
     grassSwatch = $grassSwatch.get(0);  // DOM element
    
     grassSwatch.classList;      // DOM NodeList, which works like an array (an array-like object!)
    

Debugging: Try logging the swatch name to the console every time the selected swatch changes. You ought to be able to click a bunch of swatches and see this value update to values like 'water-se', 'fence-nw', 'cave', etc.


Drawing the map canvas

TODO: Next, write another similar setup function for the map canvas. It should generate <div>s for the map tiles according to the provided map size dimensions and insert them into the DOM inside the .map element. A 2 x 2 map would look like this on the page:

<div class="map">
  <h3>Map Canvas</h3>
  <div class="row">
    <div class="tile swatch grass"></div>
    <div class="tile swatch grass"></div>
  </div>
  <div class="row">
    <div class="tile swatch grass"></div>
    <div class="tile swatch grass"></div>
  </div>
</div>

If you use the same class names as listed here, the styles provided with the assignment should work right off the bat.

As mentioned in lecture, it's possible to generate a DOM element with jQuery by passing an HTML tag string to $, like this:

var $newDiv = $('<div>');

// We can invoke jQuery methods on this new div
$newDiv.addClass('swatch');

Until you write $newDiv into the document, however, it only exists in your script. Take a look at jQuery's .append method to write elements into the page DOM.

Note: this layout could have been implemented with a <table> (it is, after all, simply a grid), but using <div>s allows us to write simpler CSS.


Previewing swatches

preview-swatches

While building our Pokémon level map, it would be nice to preview what a map tile would look like in position on the canvas. There are a few ways you can make this work with classes, some of which don't require edits to the provided CSS file. A map tile simply looks like:

<div class="tile swatch grass"></div>

So if you swap out 'grass' for another swatch name, it takes on that swatch's appearance.

TODO: On construction, MapBuilder should bind event handlers to the newly generated tiles. In particular, we are interested in the mouseenter and mouseout events on each tile <div>. When the mouse hovers over a tile, it should take the appearance of MapBuilder's selected swatch.

Remember that jQuery event handlers have this general form:

function onMouseEnter (e) {
  // An event handler's context gets bound to its target element on the
  // page. Here, we create a jQuery object handle for the element.
  var $this = $(this);

  // do stuff
}

$myDiv.on('mouseenter', onMouseEnter);

Hint: If you need to store information on a DOM element, it's common to use a data- attribute. You can give this any name; it looks something like this:

<li class="swatch grass" data-seq="1"></li>

You can then access these attributes in jQuery:

var $grass = $('.swatch.grass');

$grass.data('seq');       // --> '1'
$grass.data('seq', 2);    // updates the 'data-seq' attr

Painting swatches

Now the fun part! To allow a user to "paint" a swatch (make it stay in place on the map canvas), we'll make them click on a tile. That tile should then take the appearance of the selected swatch.

paint-swatches

TODO: On construction, MapBuilder should bind a mousedown event handler to the map tiles. This handler will be similar to mouseenter, but you'll have to do something special to ensure that the swatch appearance doesn't revert when the mouse leaves the tile.

TODO: In addition, you should implement tile painting by dragging. This can all be done with just the mousedown and mouseenter events. Think about what should happen to a tile when the mouse enters it AND the mouse button is already clicked.

Hint: You can access the pressed key or mouse button in an event handler through the event object:

var LEFT_MOUSE_BUTTON = 1;

$grass.on('mousedown', function (e) {
  e.which === LEFT_MOUSE_BUTTON;      // --> true
});

However, you can do it without accessing the e.which property by storing the mouse state with a closure - and I encourage you to try to do it this way!


A playable character

preview-swatches

It's time to get adventurous like Ash Ketchum and implement a playable character within this map once it's built. There are sprites for Pikachu in all four orientations (left, up, right, and down) in hw3/lib/images/sprites - they correspond to the classes 'facing-left', 'facing-up', 'facing-right', and 'facing-down' in the CSS.

We are going to implement a separate class called Player that generates a player on the map page. This player can move around with the arrow keys. They should be able to move over terrain (e.g. grass) but not through obstructions.

The Player constructor will take 3 arguments: an x-coordinate, a y-coordinate, and a MapBuilder instance. This is where making the MapBuilder class comes in handy - we've assigned the builder.$elem to be the container for the entire map builder, and we can access this from Player now. We can also tack on useful properties like width and height to the builder and reference those from Player. Neat!

TODO: Create an element for the player and add it to the DOM. Again, you'll need to create an element by using the jQuery syntax:

$('<div>').addClass('player facing-down');

Once you've created the element, you'll need to add it to the DOM. The correct element to add the player to is a child of the map builder element with the class .map. If you don't add it to the correct element, its position will be messed up!

TODO: Display the player element at the correct position on the page. You must do this by setting the left and top properties in CSS. For instance, if the player's current coordinates are (x, y), then you would set the left property to (x * SWATCH_SIZE) and the top property to (y * SWATCH_SIZE).

TODO: Listen for keydown events on document and move the player accordingly.

Actually listening for these events is pretty easy. You'll wrap the document in a jQuery object, and bind an event for keydown. You'll need to look at the event object to determine which key was pressed. The key codes for [left, up, right, down] are [37, 38, 39, 40], respectively; and the relevant property is event.which.

The tricky part is determining whether a move is valid. You need to ensure that

  1. The player does not go out of the map boundaries, and
  2. The tile that the player is moving into is terrain and not an obstacle.

We've provided you with a handy isTerrain function that, given a swatch name, will tell you whether the player can be moved into it or not. However, you'll need to dig into the map itself to determine the swatch name for a given coordinate.

Hint: Check out the .eq method in jQuery - it grabs the nth object from a set of matches.

Make sure that you're changing the orientation of the player on each move, regardless of whether the move is valid or not. For instance, if the player starts off facing down at (0, 0) and you receive a 'left' keydown event, you cannot move the player (since this would run them off the map). However, you would still change their orientation class to 'facing-left' even though the coordinates haven't changed.


Make cool maps

If you build any cool Pokémon maps once you're finished with the assignment, please share them! Post a screenshot to our Piazza forum.


Debugging

Check out the Chrome DevTools docs / tutorials on JavaScript debugging.

Functional Programming in JavaScript

As engineers and problem solvers, we often try to reduce the complexity of our problems and solutions. This is especially important in programming, where we are presented with a dichotomy of two seemingly incongruous systems: a very low-level computer and a high-level thinking human. Computer scientists use abstractions to keep the problem space at hand small.

When presenting a solution to a problem (essentially what a piece of code does), it is helpful to assume prior knowledge about all low-level details around the problem. Similarly, when I tell you a food recipe, I hope I don't have to explain to you how to boil water or dice onions -- rather, I'd like to tell you how abstract, agreed-upon elements fit and work together to accomplish the task of preparing a certain meal.

One approach to abstraction we've already looked at is object-oriented programming. This lecture will instead focus on functional programming (FP), a method of abstraction through clever uses of functions & their composition. As I've been hinting at, JavaScript is a prime candidate for functional programming (mainly due to first-class function objects).

The simplest motivating example that applies here is the all-too-tired for loop. We've written this kind of code often:

for (var i = 0, l = arr.length; i < l; i++) {
  // do stuff to arr[i]
}

You probably already have ideas for how do stuff to arr[i] here can be abstracted. Functions, are, after all, quite useful for "doing stuff":

function forEach (array, fn) {
  for (var i = 0, l = array.length; i < l; i++) {
    fn(arr[i], i);
  }
}

Here, we've abstracted the low-level details of a for loop into forEach, which better expresses our intent with the code. What if we wanted to take this further to other functions that iterate over arrays?

function parseIntArray (array) {
  var newArr = [];

  forEach(array, function (el) {
    newArr.push(parseInt(el, 10));
  });

  return newArr;
}

function getClasses (elements) {
  var classes = [];

  forEach(elements, function (el) {
    classes.concat(el.classList);
  });

  return classes;
}

You start to get the idea. We can create another useful abstraction here -- notice how the above functions both take an array of elements and return a "transformed" array.

var map = function (array, fn) {
  var newArr;

  // Recall that we were nice enough to provide the index to this iterator
  forEach(array, function (el, i) {
    newArr.push(fn(el, i));
  });

  return newArr;
}

var parseIntBaseTen = function  (str) { return parseInt(str, 10) };

var parseIntArray = function (array) {
  map(array, parseIntBaseTen);
};

map is the first important higher-order function in FP. But we may want to do more things with arrays than just transform them into other arrays. Consider:

var sum = function (array) {
  var total = 0;

  forEach(array, function (el) {
    total += el;
  });

  return total;
};

var find = function (elements, tag) {
  var filtered = [];

  forEach(elements, function (el) {
    if (el.name === tag) {
      filtered.push(el);
    }
  });

  return filtered;
}

sum and find might not seem immediately similar, but we can in fact abstract such logic into a higher-order function called fold (or reduce), which builds up a single value from an array of values:

var foldl = function (arr, fn, base) {
  forEach(arr, function (el, i) {
    base = fn(base, el, i);
  });

  return base;
};

var sum = function (arr) {
  function mkTotal (base, el) {
    base += el;
  }

  return foldl(arr, mkTotal, 0);
}

base here is the initial value of the fold as well as the final built-up value. Can you trace the execution of sum here as implemented with a fold?

Prompt: try implementing filter with a fold. It takes two arguments: an array to filter and a predicate function that tests an array element for inclusion in the output.


In creating parseIntArray above, we almost touched on a couple more important bits of FP: composition and partial application. Function composition works as you're familiar with it from mathematics.

var addTen = function (i) { return i + 10; },
    double = function (i) { return i * 2; };

var comp = function (f, g) {
  return (function (input) {
    return f(g(input));
  });
};

// Note the order of operations.
var doubleThenAddTen = comp(addTen, double);

We had to make a function to accomplish the composition. Other more functional languages have convenient syntax for this (like $ in Haskell), but we'll have to make do with our functions in JavaScript.

Let's go back to the parseIntArray example. You can also accomplish partial application in a similar manner:

// Syntactic sugar for the most common use of `parseInt`
var parseIntBaseTen = function (i) { parseInt(i, 10); };

// Partially apply `map` by returning an anonymous function
var mapWithFunction = function (fn) {
  return (function (arr) {
    return map(arr, fn);
  });
};

var parseIntArray = mapWithFunction(parseIntBaseTen);

These are some examples of the powerful nature of first-class function objects. The variables defined in the above block are all functions -- hopefully this helps you understand why some might prefer the var fn = function ... syntax to some extent.


Underscore.js

Working with functions and throwing them around in this way is fun, but soon we'd end up implementing a lot of higher-order functions and wonder if only there was someone out there who had the sense to build a library with all the useful ones in it...

Lucky you! Some crafty JS developers at DocumentCloud (mainly, Jeremy Ashkenas) have built just that library.

Underscore is broadly defined as the JavaScript utility belt. Or, what should have been built into the standard library. On the library's home page, they describe it as the answer to the question:

If I sit down in front of a blank HTML page, and want to start being productive immediately, what do I need?

There are four essential parts to this library. I've listed some of the important functions from each section:

Collections

Note: "Collections" functions work on both Arrays and Objects.

Arrays

Functions

Objects


Using Underscore

If we're really trying to be functional, we'll compose most of our logic as a series of operations done on a piece of data. Imperative programming broadly does this through a sequence of statements, but we want to do it with functions because hey, this is a lecture on functional programming! There are two ways to do this sequential composition in Underscore.

In a manner similar to jQuery object instances, Underscore provides functionality for turning arrays and objects (generically, "collections") into wrapped objects. Here's the difference:

function double (n) { return n * 2; }

_.map([1, 2, 3], double);   // Array

_([1, 2, 3]).map(double);   // wrapped collection

Wrapped collections have the benefit that you can chain methods on them. Notice the difference in the two styles of composition:

var stooges = [
  { name: 'curly',  age: 25 },
  { name: 'moe',    age: 21 },
  { name: 'larry',  age: 23 }
];

var getAge = function (stooge) { return stooge.age; };
var rejectShortNames = function (stooge) { return stooge.name.length > 3; };

var youngest = _.first(_.sortBy(_.filter(stooges, rejectShortNames), getAge));

youngest = _.chain(stooges)
            .filter(rejectShortNames)
            .sortBy(getAge)
            .first()
            .value();

The first expression to generate youngest looks kind of ugly, while the second uses the nicer-looking chaining syntax.

Note that in the second case I had to explicitly call chain to turn stooges into a wrapped object for chaining. This is required when some methods in your function chain do not return wrapped objects, like filter and sort; chain ensures that a wrapped object is returned on every call (it even delegates built-in Array.prototype methods). To get back an Array or Object from a wrapped collection, you must call .value() on it.


Many of these are abstractions over varying JavaScript implementations, in the same way that jQuery abstracts over differing DOM API implementations. So, in the spirit of abstraction, readability, and robustness, I generally recommend it on all projects.

(Time permitting) Let's explore the source!

FP libraries

While Underscore is quite a popular library, there are a few libraries that facilitate functional programming JavaScript by providing features borrowed from more pure functional languages.

Model-View-Controller

Definition

MVC is an architectural design pattern in software that tries to organize application logic by applying separation of concerns. As the name suggests, there are three types of components:

  1. Models contain business data & logic.
  2. Views contain interface logic & representation.
  3. Controllers capture input & interactions and coordinate between Views & Models.

Again, the key here is a separation of concerns. Models shouldn't have to know anything about what Views do and vice versa.

MVC in web servers

When building a web server, your method of interaction with the user is through HTTP requests. Requests are stateless, so there is a new context created between the browser and server on every interaction. This is quite different from the initial MVC frameworks developed for operating systems (where it was originally defined by the Gang of Four), so it's worthwhile to study how MVC is applied to web servers before proceeding.

Ruby on Rails is an MVC framework for programming web servers.

server mvc

Source: Backbone Fundamentals

There is usually an additional component, a router, which analyzes a web request and determines which controller to employ and which action to invoke on it (hence, ActionController in Rails). For example consider these web application URLs:

This turns out to be pretty good system to deliver web applications in a maintainable way. Servers are fast, interpreted languages are friendly, and MVC encourages separation of concerns. Great.

No system is perfect, though. The weak part at this point becomes the HTTP requests that allow browsers and servers to interact. Even with heavy caching, page reloads are relatively slow. So what can we do?

Motivation: MVC on the client

Up to this point, we've used JavaScript to add some interspersed interactivity to our web page. An element click here, a mouse hover there, etc. There is a new paradigm of delivering web applications that has become popular in the last few years, one that places a lot more responsibility on client-side JS rather than on the server.

Single Page Applications (SPAs) work by loading data into the browser once from a server request and then reacting to data changes (& user interaction) on the client side without complete page refreshes (new full request cycles) to the server. When more data is needed from the server, an AJAX request is initiated with a server API endpoint. On reponse, the client-side application reacts accordingly.

With all this new logic happening on the client, your JavaScript applications quickly start to get quite complicated. Rather than just displaying information in the form of HTML/CSS and responding to some browser events, your app now has to maintain state of models and initiate web requests of its own.

You might be tempted to accomplish this all with something like jQuery, but this approach often leads to lots of unorganized spaghetti code. It's all too easy to end up frantically trying to keep data in sync between the HTML UI, your JavaScript logic, and the database on the server. Eventually, you'll be reimplementing a lot of the things that existing client-side frameworks or libraries already include. Here's where client-side MVC comes in.

Manifestation: Backbone.js

Backbone.js is an MV* "framework" for client-side programming. It isn't quite MVC, but it's a good start.

Backbone.js is a lightweight JavaScript library that adds structure to your client-side code. It makes it easy to manage and decouple concerns in your application, leaving you with code that is more maintainable in the long term.

Backbone focuses on giving you helpful methods for querying and manipulating your data rather than re-inventing the JavaScript object model. It's a library, rather than a framework, that plays well with others and scales well, from embedded widgets to large-scale applications.

Backbone provides a minimal set of data-structuring (Models, Collections) and user interface (Views, URLs) primitives that are helpful when building dynamic applications using JavaScript. It’s not opinionated, meaning you have the freedom and flexibility to build the best experience for your web application how you see fit. You can either use the prescribed architecture it offers out of the box or extend it to meet your requirements.

Source: Backbone Fundamentals

Some encouraging thoughts from Addy Osmani! Hopefully this has piqued your interest in using a library like Backbone.

Now, of course, Backbone isn't the only client-side framework or library out there. There are many others and they suit various needs. We're going to start with Backbone, however, because it's mature, popular, and has a vibrant developer community. It's also relatively simple; you'll spend less time wondering about "magic" occurring under the hood in your application.

You can read more about Backbone on its website.

Diving into Backbone

The one hard dependency that Backbone requires is Underscore.js. In addition, developers usually include jQuery (the functionality doesn't overlap) or some other DOM manipulation library.

Here's a high-level overview of Backbone's features. The following are all JS "classes" that can be extended and constructed:

In addition, there's a handy mixin utility class:

Let's start by looking at the tried-and-true to do list example. Markup in index.html might look like:

<!doctype html>
<html>
<head>
  <title>To Do List</title>
</head>
<body>
  <div id="todo-list">
  </div>

  <script type="text/template" id="item-template">
    <input id="todo-complete" type="checkbox" <%= completed ? 'checked="checked"' : '' %> />
    <%= title %>
    <span class="owner"><%= owner %>
  </script>

  <!-- load scripts in order -->
  <script src="lib/jquery.js"></script>
  <script src="lib/underscore.js"></script>
  <script src="lib/backbone.js"></script>
  <script src="todo.js"></script>
</body>
</html>

todo.js includes the following code (within its IIFE):

var Todo = Backbone.Model.extend({

  defaults: {
    title: '',
    completed: false
  }

});

Immediately you might notice that we are defining attributes to extend from the globally-available Backbone.Model. This is similar to class inheritance -- a Todo is a special kind of Backbone.Model. (It's not quite prototypical inheritance since extend only clones an object and adds additional properties).

The Todo model holds the data for each todo item. Later, we will group models together into Collections. We'll also explore a way of persisting models to some data store (API, database, etc.).

Now here's the todo View:

// Renders a todo item and manages its UI interactions
var TodoView = Backbone.View.extend({

  // Directive to Backbone telling it wrap this view in an 'li'
  tagName: 'li',

  // Returns a function that generates an HTML string
  tmpl: (function () {
    var $tmpl = $('#item-template');
    return _.template($tmpl.html());
  })(),

  // Maps events on elements within this view to view methods
  events: {
    'dblclick label': 'edit',
    'keypress .edit': 'updateOnEnter',
    'blur .edit':     'close'
  },

  // Executed when a Todo is created
  initialize: function () {
    this.$el = $('#todo-list');
  },

  // Writes this view's HTML representation to the DOM
  render: function () {
    var renderedTmpl = this.tmpl(this.model.toJSON());
    this.$el.html(renderedTmpl);
    this.input = this.$('.edit');
    return this;
  },

  // Executed when todo item is double-clicked
  edit: function () {

  },

  // Executed when todo item loses focus
  close: function () {

  },

  // Executed on each keypress when editing a todo item
  updateOnEnter: function (e) {

  }

});

There are a lot of things happening here, and it's not entirely obvious which functions get called first. When a Backbone.View is created, its initialize method is called and its events object is parsed to register event handlers.

Later, when this view is rendered with the render function, it looks up its template and generates an HTML string from a JSON representation of its model. Only then can you query for elements available in the view's template (they become available in the document).

A note on client-side templating: It is possible to generate HTML as strings or through jQuery methods and add classes, attributes, etc. to them. But this quickly becomes tedious in any moderately-sized application. A better solution is to use a templating framework, which allows you to define common templates interface elements as HTML skeletons with interpolated values built-in. These values are later filled in by a JSON object representing some application data. For example, we have defined the following Underscore template in our HTML page, which uses <%= %> as interpolation delimiters:

<script type="text/template" id="item-template">
  <input id="todo-complete" type="checkbox" <%= completed ? 'checked="checked"' : '' %> />
  <%= title %>
  <span class="owner"><%= owner %>
</script>

The method _.template will accept a template string and parse it into a function.

// Returns a string-generating function
var tmpl = _.template($('#item-template').html())
  , data = {
      title: 'Complete homework 3 by Saturday',
      owner: 'Joe Student'
    };

var renderedHTML = tmpl(data);

Back to TodoView.

Backbone doesn't quite separate the 'V' and 'C' in MVC. You can see that Controller responsibilities are fulfilled in the Backbone.View defined here. Its configuration for DOM interactions is managed by the events object, which delegates browser events to methods defined in the View.

With the model and view in place, we can invoke their constructors:

var todo = new Todo({
      title: 'Lorem ipsum dolor sit amet',
      owner: 'Adi'
    })
  , todoView = new TodoView({
      model: myTodo;
    });

todoView.render();

Notice that a View is directly tied to a Model (it can also be tied to a collection).

This is the picture of MVC we have now in Backbone.

backbone-mvc

Source: Backbone Fundamentals

Fairly similar to the server MVC picture, but the View has swallowed the Controller and interaction (requests / responses for data) happens through the web page DOM.

See the course lecture repository for a simple working Backbone app.

(Time permitting) Let's explore the source!


References

Homework 4: Penn Course Review Lite™

Due Friday, February 27

Implementation stubs for download.

Before You Start

Review the Lectures

Review Lecture 4 and Lecture 5.

Set Up MongoDB

You can install MongoDB from their website here. After installing, make sure you have the path /data/db in your root directory (i.e. run sudo mkdir -p /data/db if you're not sure). Then, open a new tab in terminal and execute the mongod command.

If mongod starts running and then immediately shuts down, you may need to edit your permissions, in which case, running sudo chown `whoami` /data/db should fix that problem.

After setting up MongoDB and mongod is running, run node db/seed.js to seed your database. You should look at the db/seedData.json file to familiarize yourself with the various properties courses have. You should also continue to have mongod running in an open tab because your app will crash otherwise.

Database Methods Reference

Because figuring out how to access the database isn't the topic of this assignment, the relevant methods to do so will be provided to you. These methods are encapsulated in coursesDb (required from db/course.js) and reviewsDb (required from db/review.js). You will need to use all of them in this assignment

coursesDb.findCourses(callback) This method passes an Array of all the courses to its callback. The callback has two arguments, and it follows the convention that the first argument is the error, and the second argument is the result of the database query.

coursesDb.findCourse(courseId, callback) This method passes a specific course to its callback. The courseId is the id of the course you want. This callback also follows convention in that its first argument is the error while the second argument is the result of the database query.

reviewsDb.createReview(reviewData, callback) This method saves a new review. reviewData, in this case, is an object with a courseId property with the id of the relevant course and a text property with the text of the new review. The callback will only have one argument, the error.

reviewDb.updateReview(reviewData, callback) This method updates a review. reviewData, in this case, is an object with a courseId property with the id of the relevant course, a reviewId property with the id of the relevant review, and a text property with the new text for the review. The callback will only have one argument, the error.

reviewDb.deleteReview(reviewData, callback) This method deletes a review. reviewData, in this case, is an object with a courseId property with the id of the relevant course and a reviewId property with the id of the relevant review. The callback will only have one argument, the error.

Part 1 - Backend (Express)

Running the server

Once you have mongod running (see above for instructions on setting up Mongo), you can go to another terminal window and run the server with npm start. Don't forget to npm install the dependencies first! Once it's running, you can access your server by going to localhost:3000 in your browser.

Start Using Routes

/app.js

You'll want your app to start using the routes we configured in the routes directory. They're already required into the app.js file, and it should only take one line for your app to start using them.

TODO: Make the app use your routes.

Set up Routes

/routes/index.js

GET /

Here, you want to handle how the app handles a GET request to /. Naturally, since this is the root path, you'll want to render the index.html file, so the user will start seeing content right away.

TODO: Render the index.html file when there's a GET request to /.

/routes/courses.js

In this file, there are two routes to configure.

GET /courses

When the app handles a GET request to /courses, you should respond with an array of all the courses. You should respond to the GET request with this course data. (Keep in mind that the default format Backbone receives data is in JSON).

TODO: Respond with all course data.

GET /courses/:id

When the app handles a GET request to /courses/:id, all you have to do is render the index.html page, just like you did with /. (No worries, your Backbone code will make the page look different from the home page).

TODO: Render the index.html file.

/routes/reviews.js

In this file, there are four routes to configure, one for each CRUD (Create, Read, Update, Delete) operation.

GET /courses/:courseId/reviews

The app should respond to a GET request to this route with an array of course reviews. The course's id is in the route, and you can access it with req.params.

TODO: Respond with all the course's reviews.

POST /courses/:courseId/reviews

The app should respond to a POST request to this route by creating a new review (i.e. saving it to the database). Send the error message from the callback as a response (even if it's null) for both better error handling for when there is an error, and Backbone will need some kind of response to know if the POST request was successful.

TODO: Save the course's reviews and respond with the error message.

PUT /courses/:courseId/reviews/:reviewId

The app should respond to a PUT request to this route by updating that review. As with the POST request, respond with the error message.

TODO: Update the review and respond with the error message.

DELETE /courses/:courseId/reviews/:reviewId

The app should respond to a DELETE request to this route by deleting that review. Respond with the error message.

TODO: Delete the review and respond with the error message.

Part 2 - Frontend (Trie + Backbone)

In this project, you will be creating a basic version of Penn Course Review using Express and Backbone. By the end, you'll be able to search for courses with autocompletion and write, update, and delete reviews for them.

The finished product will look something like this:

Trie

/public/js/trie.js

The first thing we'll need to write the autocompleter is an efficient way to store the valid words to match. The correct data structure here is called a trie (short for retrieval), and if you've taken CIS 121, you're already familiar with it. Just in case you're rusty, though, here's an excellent article to help jog your memory. In order to simplify the implementation, we will be using upper-case only. All words should be converted using toUpperCase() before adding or retreiving.

JavaScript conveniently has hashes (object literals) built right in - we'll use them for the nodes of our simple trie. The top-level node of the trie (this.data in the stub) starts out as an empty object. Your job is to implement the addWord and getSuggestions functions.

TODO: Implement addWord.

Note that when addWord is called, an object is passed in; it has the name and _id properties, among several others.

The pseudocode for adding an element to a node in the trie is:

function addWordToNode(word, trieNode, courseId) =
1. If `word` is the empty string:
  1. Add a courseId node with key "end" (i.e. do: trieNode["end"] =  courseId)
  2. Return
2. If trieNode does not have a child labeled with the first character of `word`:
  1. Add a new child (empty object) labeled with that character to trieNode
3. Let `newWord` equal `word` but with the first character removed
4. Let `newNode` be the child of trieNode labeled with the first character of `word`
5. Recursively call addWordToNode(newWord, newNode, courseId)

Call this algorithm starting with the input word, the root of the trie (this.data), and the course's id. There are some simple test cases in the code to help get you started. Don't forget to convert the input to upper case!

TODO: Implement getSuggestions.

This one is a little trickier than addWord, and you won't have pseudocode to guide you through this time. In order to get the suggestions for a given prefix, you'll need to first see if any word starting with the prefix is in the trie by traversing from the root. If there are words starting with that prefix, then you should return an array of all of them. Keep in mind that in this trie implementation, having an "end" key means that a node is the end of a valid word in the trie.

If no words are found, then this method should return the empty array. Again, there are some simple test cases in the code to help get you started. Again, don't forget to convert the input to upper case!

Make sure you have implemented Trie correctly before going on to the next part of the assignment.

Backbone

Your backbone code will be in /public/js/app. As mentioned in lecture, it will be split into several parts (models, collections, routers, and views). You will fill in each of these. The Backbone app will render DOM elements, coordinate how that happens, and send AJAX requests to the server.

Models

/public/js/app/models/courseModel.js

Set its idAttribute to be _id since this will tell Backbone that what the actual name of our id property is. When instantiating a new course, its reviews (represented as a property of the model) should be a new review collection, and the reviews should have a url property corresponding to that course's reviews.

TODO: Implement the course model.

/public/js/app/models/reviewModel.js

Set its idAttribute to be _id. Furthermore, the review's url should be passed into the constructor, so it can be set when the review is instantiated.

TODO: Implement the review model.

Collections

/public/js/app/collections/courseCollection.js

Set its model to be the course model and set its url to be /courses.

TODO: Implement the course's collection.

/public/js/app/collections/reviewCollection.js

Set its model to be the review model.

TODO: Implement the review's collection.

Router

/public/js/app/routers/courseRouter.js

routes Set the routes, so when the / route is requested, the index method is called and when the /courses/:id route is requested, the getCourse method is called.

index Render the searchView using its render method (this should only take one line).

TODO: Implement index.

getCourse Render the searchView and find all courses using courseCollection's fetch method. Add all the courses to the collection, using the add method. This will let you use the get method to find a course by its id. Set the course with the id in the url as window.model, and render the courseView. This should all be done in a success callback in an object (an options hash) that's passed into the fetch method (This is also how the Backbone documentation shows the success callback). You should do this, as opposed to the success method off fetch, because we hook into the success event in our tests, and it is only emitted if it is passed into an options hash. This pattern for success callbacks should be followed for all fetch, save, and destroy methods in this assignment.

TODO: Implement getCourse.

Views

/public/js/app/views/searchView.js

TODO: Implement searchView's render().

When the searchView is rendered, all courses should be fetched (again), and a new instance of Trie should be created. As a note about this, the response is an array of course models, but Trie takes in just an array of the attributes of the courses. You can convert the former into the latter by calling the toJSON method. The renderSearchBar method should also be called at this point, since we don't want users to use the search bar until we finished querying for our courses.

TODO: Implement searchView's renderSearchBar

Now, if you look at the index.html file, you will see that at the bottom, there is an HTML template tag with the id search-template. Select it with jQuery and convert it into a string with the html() method. Pass this into Mustache.render to get an HTML string you can insert inside the div with the id search-container(Mustache is the templating engine we're using in this lab. The documentation can be found here). Keep in mind that you should remove all elements inside the divs with ids search-container and suggestion-container at this point as well because you will be inserting elements in there in the future, and you want to remove them when rendering the page again.

Rendering with mustache should look something like this.

var templateHtml = $('#template').html();
var domElement = Mustache.render(templateHtml);
$(domElement).appendTo($('#container'));

A successfully rendered search bar should look like this Successfully rendered search bar

TODO: Implement searchView's events

Define the events such that as the user types into the search bar, the autocomplete method is called, and when the user clicks on an element with the class course-link, the redirect method is called.

TODO: Implement searchView's autocomplete() Get the letters typed into the search bar and pass them into trie's getSuggestions method. Clear out all text in the div with the id suggestion-container. You should use the HTML template tag at the bottom with the id suggestion-template to display suggestions. Convert it into a string and pass it into Mustache.render as its first argument. Then, from trie's getSuggestions method, pass in the object of each course and its info as a second argument. If you look in the body of this template, you'll see a number of variables enclosed by {{ }}. These variables are properties in the object passed into Mustache.render, and Mustache will insert the values of these properties. It will return the resulting string, which should be inserted into the suggestion container.

TODO: Implement searchView's redirect() The event's default action should be prevented since the targets are links. Get the id from the target's data attribute and use the courseRouter to navigate to that course's route.

/public/js/app/views/courseView.js

TODO: Implement courseView's render()

Render the div with id show-template and render it with Mustache and the window's model. Insert this in the div with id show-course-container. Then, get all reviews and render them.

TODO: Implement courseView's readReviews()

This is where all the logic to get all reviews and rendering them should go. I would also suggest calling toJSON on the review models you get from fetching for them since they'd be easier to work with.

TODO: Implement courseView's events

Define events such that when #submit is clicked, submitReview is called, when .save is clicked, updateReview is called, when .update is clicked, changeToUpdate is called, and when .delete is clicked, deleteReview is called.

TODO: Implement courseView's submitReview

First and foremost, make sure to use preventDefault() so the form isn't actually submitted - this would cause a redirect. Then, take the text in the textarea, save it (i.e. call createReview), and clear the text in the textarea.

TODO: Implement courseView's createReview() All the logic to save reviews should be here. Instantiate a new review and save it with the text from the text area. After it's been successfully saved, render courseView again.

TODO: Implement courseView's changeToUpdate() This method changes the view so that you can update an individual review. Delete the input, update, and delete fields (accessing the target's parent's children and turning it into a jQuery object will be helpful). Then, render the template with the id review-update-template with Mustache.

TODO: Implement courseView's updateReview() Update the review (you can find its id in the parent div) and upon a successful update, render courseView again.

TODO: Implement courseView's deleteReview() Delete the review (you can find its id in the parent div) and upon a successful deletion, render courseView again.

Submitting

Now, if everything works, you could be done! Make sure that everything works locally, and complies to this specification, before submitting to BRUCE.

In order to submit, run the following command and submit the resulting zip file:

$ zip -r files.zip app.js public routes

This should create a files.zip file, which you can then turn in to BRUCE. It will not work if your file is named something other than files.zip!

Backbone.js Applications

Most of what was covered in lecture can be learned from the Backbone.js docs.

A few useful diagrams are available in the lecture slides.

Be sure to browse the associated code examples.

Server-Side JavaScript

Last time, we saw some examples of how a client-side application might talk to a web server to save its state & data.

var player = new Player({...});

player.fetch();

player.set({
  position: {
    x: 10,
    y: 0
  }
});

player.save();

This last line relies on a custom URL defined in the Player model to save the model state to the server. Backbone will internally call sync to perform an AJAX request to some API you've specified. This lecture will cover the details of such an API.

REST

Data transfer on the web these days happens almost exclusively through what are known as RESTful web services. REST (Representational State Transfer) is kind of a specification for how these services interface with one another. Some guidelines:

Use semantic HTTP requests

RESTful HTTP requests should do something to or with a resource:

Notice the direct mapping to typical CRUD actions for data models.

This structure can be abused, however. For example, a service might allow state changes with a GET route, which can pose a number of problems:

GET /adduser?name=Robert

This action is better represented as a POST with a JSON payload:

POST /users

Content-type:application/json
{
  "name": "Robert",
  "email": "robert@example.com"
}

Be stateless

Web servers that keep track of a client's state and respond accordingly are difficult to maintain and pose issues of synchronization, serialization, etc. RESTful web services strive to be stateless; when you interact with an API, you as the client must keep track of your state over multiple requests and use request parameters as necessary.

Expose directory-like URIs

URLs in RESTful web services should have semantic meaning within them. A good example of this occurs in most web publications, where the URL contains much of the metadata relevant to an article:

http://www.nytimes.com/2014/03/05/movies/awardsseason/keeping-faith-in-oscar-filmmakers-work-and-sacrifice.html

The service's router will have some logic to parse URLS and figure out which models to work with, etc. (similar to Backbone's router).

/:year/:month/:day/:section/:subsection/...

One of our next goals is to build a RESTful web API ourselves in JavaScript for our client-side applications to interact with. To do this, we'll leverage node.js. We've been using it simply as an interpreter for JS code thus far, but today we'll explore its extensive capabilities as a library for building web servers.

Web services with Node

From the node website (emphasis mine):

Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.

Node basically provides two things: an environment and a library. In the browser, we had our own special environment to play around with, but loading files was done somewhat crudely with <script> tags. Node provides a much more structured module system.

When you open up a REPL or a .js file, you'll have some variables already defined and available to you -- these are global values in Node such as:

Within any module (basically any .js file run with Node), you also have:

Then there are a handful of core modules available on your system when Node is installed, but you'll have to require them individually to gain access to them. A few popular ones include:

Modules

Node follows the CommonJS module system; the easiest way to include modules from separate files is through use of the require function. In a nutshell, it parses another script and returns the exports object.

var fs = require('fs');

console.log(fs);   // { Stats: [Function], exists: [Function], ...}

To define your own module, you might have something like this in a JS file:

console.log("evaluating example.js");

var privateFn = function () {
  console.log("invisible");
};

exports.message = "hi";

exports.say = function () {
  console.log(exports.message);
}

And its import would look like this (note the ./ filepath syntax):

var example = require('./example');
console.log(example);       // { message: 'hi', say: [Function] }

You don't have to include the .js file extension; require will figure it out and do the right thing.

Note: if you re-require an already-loaded file, Node will use its module cache instead of reloading the file.

You can export a function instead, if you'd like:

// example2.js
module.exports = function () {
  console.log('hello');
}

// Node REPL
> require('./example2')();    // 'hello'

Note: we couldn't simply define exports = ... because this would have created a new global variable (however, 'strict mode' would likely guard against this).

Node Package Manager

Soon, you'll want to use packages written by the Node community. To get these on to your computer, you'll use the Node Package Manager, or npm. It consists of an online repository of published open-source projects and a command-line utility (bundled with every Node installation) to manage packages, versions, etc. on your system. Package installation is very simple; here's how you'd install the async package:

$ npm install async

If you install a package locally like this, it gets put in a node_modules folder in the current directory. You can also install a package globally; its main executable (if any) will hopefully get sym-linked to your PATH and you can use it anywhere:

$ npm install -g coffee-script

$ coffee -c foo.coffee -o ./build/

The online npm registry contains information about all published packages.

Finally, most node projects will include a file called package.json at their root, which specifies metadata about the package and allows you to declaratively list dependencies. For example, here's a portion of the package.json for the Express framework:

{
  "name":         "express",
  "description":  "Sinatra inspired web development framework",
  "version":      "4.0.0-rc2",
  "author": {
    "name":       "TJ Holowaychuk",
    "email":      "tj@vision-media.ca"
  },
  "contributors": [ ...  ],
  "dependencies": {
    "accepts":            "1.0.0",
    "type-is":            "1.0.0",
    "range-parser":       "1.0.0",
    "cookie":             "0.1.0",
    "buffer-crc32":       "0.2.1",
    "fresh":              "0.2.2",
    "methods":            "0.1.0",
    "send":               "0.2.0",
    "cookie-signature":   "1.0.3",
    "merge-descriptors":  "0.0.2",
    "utils-merge":        "1.0.0",
    "escape-html":        "1.0.1",
    "qs":                 "0.6.6",
    "debug":              ">= 0.7.3 < 1"
  },
  "devDependencies": {
    "ejs":                "~0.8.4",
    "mocha":              "~1.15.1",
    "jade":               "~0.30.0",
    "hjs":                "~0.0.6",
    "stylus":             "~0.40.0",
    "should":             "~2.1.1",
    "connect-redis":      "~1.4.5",
    "marked":             "0.2.10",
    "supertest":          "~0.8.1",
    "body-parser":        "1.0.0",
    "cookie-parser":      "1.0.1",
    "static-favicon":     "1.0.0",
    "express-session":    "1.0.1",
    "morgan":             "1.0.0"
  },
  "keywords": [
    "express", "framework", "sinatra", "web",
    "rest", "restful", "router", "app", "api"
  ],
  "repository": {
    "type":       "git",
    "url":        "git://github.com/visionmedia/express"
  },
  "scripts": {
    "prepublish": "npm prune",
    "test":       "make test"
  },
  "engines": {
    "node":       ">= 0.8.0"
  },
  "license":      "MIT",

  ...
}

Notice that you can separate project dependencies by development and production. To automatically install local packages with a package.json specified like this, you can simply run npm install in the project directory. More information about this file is available here.


Asynchronous Programming

Node is a framework that emphasizes asynchronous programming from the ground up (in contrast to other web frameworks). There are often two functions to do the same thing; as a general rule of thumb, you should avoid the synchronous ones and instead use the asynchronous versions.

Let's look at an example of reading in a file:

var fs = require('fs');

// async
// ----------------------------------------

fs.readFile('example.file', 'utf8', function (err, data) {
  if (err) {
    return console.log(err);
  }
  console.log(data);
});


// sync
// ----------------------------------------

var data = fs.readFileSync('example.file','utf8');
console.log(data);

At first, it seems like the synchronous code is simpler and more concise. However, the async code is complicated for a reason. When you invoke fs.readFileSync, the node process will sit there waiting for the OS to read a file before continuing in the script. On the other hand, fs.readFile will ask the OS to start a process to read the file and listen for the OS event indicating that it is done reading the file, at which point the callback we pass into readFile here will be run. In the meantime (let's say you're reading a large file), your node process can continue on to other things in the script. The same kind of logic could be applied to expensive database queries.

You may have to adjust your mental model a bit and have to think about synchronization issues, but overall the benefits of async code contribute to some of Node's speed as a web development library; you may realize its advantages in the long run.

Note about "callbacks": callbacks are the term we generally use to refer to the last argument passed to an async function; they are also functions themselves. It's kind of like saying "do this with some parameters, and then when you're done, do this other thing." Node generally has a convention for its callback functions that makes it so an error object is typically passed as the first argument to a callback (you can see this is the case in the above fs.readFile).

One more relevant part of async programming in Node is EventEmitter. This is an interface used when there are multiple stages to a task that a client might want to be notified about. For example, you might need to parse multi-part form data or read in a large file with chunks at a time (using Streams). For more information, see the EventEmitter docs.

One "gotcha" of async: a common mistake might be to write code like this:

for (var i = 0; i < 5; i++) {
  setTimeout(function () {
    console.log(i);
  }, i);
}

You'd get an unexpected output like the following:

5
5
5
5
5

Prompt: why does this occur?

Answer: setTimeout is passed a closure which contains a reference to the for loop's i. When that closure finally executes, i has already been incremented all the way to 5.

You can accomplish the intended functionality like this:

for (var i = 0; i < 5; i++) {
  (function(i) {
    setTimeout(function () {
      console.log(i);
    }, i);
  })(i);
}

A simple web server with Node built-ins

Now that we have somewhat of a handle on the Node environment and ecosystem, we can jump into writing a RESTful web server. The basic parts we need to get working are as follows:

To do any of this, we have to start with the http package. This is what the most basic web server looks like in Node:

// server.js
var http = require('http');

var requestHandler = function (req, res) {
  console.log('Request received');

  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.write('Hello World.\n');

  res.end();
};

var server = http.createServer(requestHandler);

server.listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

Try running this code as a file -- when you do so, the process will hang, simply waiting for connections. If you open your browser to localhost:1337, however, you ought to get back a 'Hello World' string!

Even though the listen function is blocking, this is still an instance of asynchronous programming. An HTTP server is constructed with a request handler function passed in; this function is invoked every time a client hits the port(s) which the server is listening on. The work is done later, not immediately.

Aside: For completeness' sake, here's how you would go about creating and invoking an HTTP request in Node (in this case, a POST request).

var options = {
  host: 'localhost',
  path: '/',
  port: '1337',
  method: 'POST'
};

// Note: working with an EventEmitter
var callback = function(response) {
  var str = ''

  response.on('data', function (chunk) {
    str += chunk;
  });

  response.on('end', function () {
    console.log(str);
  });
}

var req = http.request(options, callback);

req.write("hello world!");
req.end();

References:

Homework 5 - AngularJS

Due Friday, April 3rd, 11:59 PM.

Implementation stub is available for download here. This is what your final product will look like: Reddit API search

Before starting this assignment

Make sure you read the lecture notes on AngularJS. The AngularJS Docs will also probably prove to be invaluable in completing this assignment.

Files

Step 1: Easing into things...

Having attended lecture and gone through the notes, you have a decent idea of how an AngularJS app is structured. Each AngularJS app is a module that can be instantiated within just a <div> or the entire <body>, and there can be multiple AngularJS apps within a single webpage. For the purposes of the app we're going to build, we just need one, and we've instantiated it for you.

We've also given you stubs for each of the files you're going to need for this homework. There's a controller, a factory, a directive and a main file. There's also a CSS file that will add minimal styling to your page; make sure that you follow our naming convention to ensure that we can test your code thoroughly. You will lose points if you don't follow our naming conventions, since our autograder won't pass those tests.

There's also a file for the infinite-scroll , and your first task (along with familiarizing yourself with the structure of our app) is to figure out a way to import this external module called ng-infinte-scroll.min.js. You'll need to find a way to import it into both the HTML and Javascript. Look in the implementation stubs for hints and look at the AngularJS Docs for ideas.

Step 2: Factories

Almost time to jump into the meat of this. Before we do so, it's important to understand how we plan to build this app.

We're going to build a Factory, a Controller and a Directive, while using some of AngularJS' built in Services. The breakdown of tasks is as follows:

Factory - The Reddit factory will return a JavaScript class (this class, like any object return from a factory, is called a Service) that is capable of making HTTP requests of the Reddit API, storing them in an Array, and requesting more results. It is important that we also know when a request has not yet been completed, when there are no results for a given query and when there are no more results for a given query.

Controller - The controller creates a Service using our Factory and provides a way to "hook in" to the Service from the DOM. It also watches the search box and adjusts the search results whenever the input text changes.

Directive - We also want to create a custom HTML tag that encloses our search box and search results. This tag should be called <reddit-pane>. It needs to have the appropriate scope and controller to work properly.

DOM - In the HTML, we'll place the markup that tells our app how and where to display information. There should be minimal logic in the DOM, though AngularJS forces us to place some function calls and bindings in HTML attributes (that usually take the form ng-attribute) - a questionable design choice.

Now that we know where we're headed, let's start by building the Factory. We want to build a service to search the r/all sub-reddit, so you'll want to look over the Reddit API documentation here.

This Factory is where the most important parts of your app live. It will be called to create a Service in your controller so that you can actually access this stuff from the DOM. Think about this part carefully and make sensible design choices.

Step 3: Controllers

Now that your Service is ready to make requests, let's move to the Controller. We've already made the Service using the Factory for you, so again, there are only two things you need to do:

You should now have a fully functioning app. You should be able to refresh the page and see that there are no results for your empty query, then type something into the textbox and see updates appear live as you type (along with some text saying "loading results..." when results are being loaded up). If you scroll down, more results should appear. If you go far enough down (or your search phrase is obscure enough), you should see a message telling you that there are no more results for your query. Ensure that your messages for the status of a search have the same IDs as below (note that this isn't the exact HTML you'll have, there are a few attributes you'll want to add to make these show and hide at appropriate times):

<div id="busy">
    Loading data...
</div>
<div id="noResults">
    No results found for your query.
</div>
<div id="noMoreResults">
    No more results found for your query.
</div>

Just for a sanity-check, the HTML rendered in the browser should look like this for each search result (though not exactly, we've removed anything that might give away the actual angular-related code):

<div class="result">
    <span class="score ng-binding">178</span>
    <span class="title">
      <a ng-href="http://www.angularcourse.com" target="_blank" class="ng-binding" href="http://www.angularcourse.com">I quit my job to teach you how to launch a full product with AngularJS (over 7 hrs of high quality video)</a>
    </span>
    <small class="ng-binding">by gordonmzhu -
      <a ng-href="http://reddit.com/r/angularjs/comments/2f7ziz/i_quit_my_job_to_teach_you_how_to_launch_a_full/" target="_blank" class="ng-binding" href="http://reddit.com/r/angularjs/comments/2f7ziz/i_quit_my_job_to_teach_you_how_to_launch_a_full/">108 comments</a>
    </small>
</div>

We're almost done now.

Step 4: Directives

Let's try and clean up that HTML code a bit. For motivation, it's possible that we might want to create multiple/other instances of this search tool, so we want to avoid repeating too much of the code. So we're going to create a Directive that allows us to use <reddit-pane> as an HTML tag to represent a chunk of HTML. Keep in mind that the <reddit-pane> tag can contain more HTML, so you need to structure your HTML chunk such that you specify where any code inside the tag goes somewhere.

Your <reddit-pane> needs to create the following structure:

<div id="results" class="results">
    <div id="contents">
        ...pane code goes here...
    </div>
    <!-- Your searchbox, held within a form with name "searchform" here -->
    <!-- Any messages you need to display here -->
</div>

Since the searchbox is in the <reddit-pane> tag, be careful to specify the appropriate $scope and controller.

You can now create an instance of a <reddit-pane> in your HTML. In your HTML, you should include an <h2> element with an Angular Expression that tells you what you're seraching for. It should also contain the searchbox automatically (since that HTML should be part of the <reddit-pane> directive's template). This is an example of seeing AngularJS' bindings in action since your h2 should update live as you enter or delete anything from the textbox.

This is what it should look like now that you're done:

The final product:

The final product!

No results for query:

No results for query

No more results:

No more results

On page load:

On page load

Congratulations, you've successfully built your first AngularJS app, and it's actually pretty useful!

BONUS: If you want, feel free to edit the CSS by adding classes (DO NOT CHANGE IDs, it will break our testing and YOU WILL LOSE POINTS) and styling the page nicely. You can get away with using tags other than the ones we've used to actually display the results as long as they have the same IDs as well.

Submitting

Now that you're done, make sure that everything works locally (and complies to this specification) before submitting to BRUCE.

In order to submit, run the following command and submit the resulting zip file:

$ zip -r files.zip assets index.html

This should create a files.zip file, which you can then turn in to BRUCE. It will not work if your file is named something other than files.zip!

More Server-Side JavaScript

Last lecture, we talked about the building blocks of a web server in JavaScript. This time, we'll look into request routing, the Express framework for Node, and stringing the JavaScript web application stack together.

Routing requests

Recall the server-side MVC diagram.

server mvc

Some kind of URL parsing is certainly necessary to route web requests to various controller actions / handlers. You might use functions like the following to inspect parts of the URL and query string (using only core modules):

|                                   url.parse(string).query
|                                           |
|           url.parse(string).pathname      |
|                       |                   |
|                       |                   |
|                     ------ -------------------
http://localhost:8888/start?foo=bar&hello=world
|                               ---       -----
|                                |          |
|                                |          |
|             querystring(string)["foo"]    |
|                                           |
|                       querystring(string)["hello"]

Source: Node Beginner Book

You can imagine how we might program our server to send requests for certain pathnames to different handlers and pass along relevant data (for example, from the query string).

Now consider a RESTful route like this example we saw a few weeks ago:

/:year/:month/:day/:section/:subsection/:story.html

Prompt: how would you parse out the relevant bits of data here? Imagine you got a URL looking like

http://www.nytimes.com/2014/03/05/movies/awardsseason/some-long-story-name.html

Answer: One way would be to construct some regular expressions from the route definition above and use them to match on the incoming URL request route.

With the fundamentals of server-side JS under our belt, we can move onto what it's like to build a real web application in Node. As was the case with the DOM API, you will likely use some kind of abstraction layer to build your web application...


Express.js framework

As you might expect from something called a "framework", Express is an abstraction over the fundamental web server logic in a Node application. It's small, flexible, extensible, and widely used. Mainly, it provides common HTTP utilities (like a robust router) and middleware APIs (sessions, cookies, auth, etc.).

The most basic Express server looks something like this:

var express = require('express');

// create an app skeleton
var app = express();

// register a route & its handler
app.get('/hello', function (req, res) {
  res.send('Hello World');
});

// asynchronously start the server
var server = app.listen(3000);
console.log('Express app listening on port %d', server.address().port);

Requests are routed using functions that mirror the HTTP method names (in the above example, a GET method). There are no "controllers" built in, but you may easily create your own. If we add in some standard middleware (a term broadly used to define plugins for a web framework) and move routes to a separate module, we get something like this:

// server.js
var express = require('express')
  , app     = express()
  , routes  = require('./routes');

// Config
// ----------------------------------------------------

// View engine
app.set('view engine', 'jade');
app.set('views', __dirname + '/views');

// Logger
app.use(express.logger());

// Get / set user cookies
app.use(express.cookieParser());

// Enable JSON, urlencoded, and multipart request bodies
app.use(express.bodyParser());

// Serve static files
app.use(express.directory(__dirname + '/public'));
app.use(express.static(__dirname + '/public'));


// Routes
// ----------------------------------------------------

app.get('/', routes.site.index);


// User routes

app.all('/users',           routes.users.list);
app.all('/user/:id/:op?*',  routes.users.load);

app.get('/user/:id',        routes.users.view);
app.get('/user/:id/view',   routes.users.view);
app.get('/user/:id/edit',   routes.users.edit);
app.put('/user/:id/edit',   routes.users.update);

// Posts routes

app.get('/posts',         routes.posts.list);


// Start server
// ----------------------------------------------------
var server = app.listen(3000);
console.log('Express app listening on port %d', server.address().port);

A few new things are introduced here:

There's also another kind of routing function in use: app.all, which attaches "global" logic to a route. In the above example, if we ever hit a route which pertains to a specific user, the first handler that gets run will be router.user.load (presumably, fetch a user's data from the database, or a handle for it). The /op?* part of the route will make anything past :id optional in that route.

Important: now that there are multiple (asynchronous) handlers attached to a route, we need to explicitly tell express when one handler is finished so that the next one can run (it's impossible to know otherwise). This is done by calling the next function in a handler. Here's what the routes file for the above example might look like:

// routes.js

// assume database functionality is defined elsewhere...
var db = require('./db');

module.exports = {

  user {

    list: function (req, res) {
      var users = db.getUsers();
      res.send('users: ' + users.join(', '));
    },

    load: function (req, res, next) {
      req.user = db.loadUser(req.params.id);
      next();
    },

    view: function (req, res, next) {
      res.send('viewing user ' + req.user.name);
    },

    edit: function (req, res) {
      res.send('editing user ' + req.user.name);
    },

    update: function (req, res) {
      var success = db.updateUser(req.user, req.params);

      if (success) {
        res.send('user successfully updated');
      } else {
        res.send('user update unsucessful');
      }
    }

  },

  posts: {

    list: function (req, res) {
      res.send('list of posts');
    }

  }

};

If we didn't call next() in load above, then the request would hang and a client wouldn't get a response on that route.


An alternative way to organize routes might be like this:

app.map = function (a, route) {
  route = route || '';

  for (var key in a) {
    switch (typeof a[key]) {

      // { '/path': {...} }
      case 'object':
        app.map(a[key], route + key);
        break;

      // get: function () {...}
      case 'function':
        if (verbose) console.log('%s %s', key, route);
        app[key](route, a[key]);
        break;

    }
  }
};

app.map({
  '/users': {
    get:          routes.users.list,
    del:          routes.users.del,

    '/:uid': {
      get:        routes.users.get,

      '/pets': {
        get:      routes.pets.list,

        '/:pid': {
          del:    routes.pets.del
        }
      }
    }
  }
});

The above example abstracts away the route mapping functions so you can simply provide a literal map (object) of routes.

Note: If in your application you ever need to inspect / reflect upon your currently defined routes, simply look at the app.routes object.

Middleware isn't just restricted to framework plugins; there's such a thing as route middleware, which act like a chained series of actions on a route. Notice how errors are passed along the middleware chain as necessary.

// Dummy users
var users = [
    { uid: 0, name: 'tj', email: 'tj@vision-media.ca', role: 'member' }
  , { uid: 1, name: 'ciaran', email: 'ciaranj@gmail.com', role: 'member' }
  , { uid: 2, name: 'aaron', email: 'aaron.heckmann+github@gmail.com', role: 'admin' }
];

// Middleware
// --------------------------------------------------------

function fauxAuthentication (req, res, next) {
  req.authenticatedUser = users[0];
  next();
}

app.use(fauxAuthentication);


// Routes middleware
// --------------------------------------------------------

function loadUser (req, res, next) {
  // You would fetch your user from the db
  var user = users[req.params.uid];
  if (user) {
    req.user = user;
    next();
  } else {
    next(new Error('Failed to load user ' + req.params.id));
  }
}

function andRestrictToSelf (req, res, next) {
  // If our authenticated user is the user we are viewing
  // then everything is fine
  if (req.authenticatedUser.uid == req.user.uid) {
    next();
  } else {
    next(new Error('Unauthorized'));
  }
}

function andRestrictTo (role) {
  return function(req, res, next) {
    if (req.authenticatedUser.role === role) {
      next();
    } else {
      next(new Error('Unauthorized'));
    }
  }
}

// Routes
// --------------------------------------------------------

app.get('/', function (req, res) {
  res.redirect('/user/0');
});

app.get('/user/:id', loadUser, function (req, res) {
  res.send('Viewing user ' + req.user.name);
});

app.get('/user/:id/edit', loadUser, andRestrictToSelf, function (req, res) {
  res.send('Editing user ' + req.user.name);
});

app.del('/user/:id', loadUser, andRestrictTo('admin'), function (req, res) {
  res.send('Deleted user ' + req.user.name);
});

Another way to configure the above user loading is to use app.param, which maps logic to URL parameters. In the below example, any time :user shows up in an Express route, the callback will be invoked.

app.param('user', function (req, res, next, id) {
  User.find(id, function (err, user) {
    if (err) {
      next(err);
    } else if (user) {
      req.user = user;
      next();
    } else {
      next(new Error('failed to load user'));
    }
  });
});

Source: Express.js examples

Here's a rough diagram of how all the above routing & middleware works together in the web server:

express-diagram

Request Handlers

So far, we've seen the features of Express that allow us to set up a web server, its middleware, and routes. Now we deal with the last part of the request loop: writing handlers that respond to a client with relevant data.

Request handlers are passed two arguments - req and res - which are the same as the request and response we saw in the barebones Node http server. (As we saw above, there's also a third argument, next, used for chaining handler logic together). The middleware in an Express application mostly serves to augment the req and res objects with added functionality.

In a request handler, you'll want to inspect the request, do some business logic, and then construct a response. res contains functionality such as:


References:

Homework 6: Chrome Extensions

Due Friday, April 10th, 11:59 PM.

Implementation stub available for download here.

Before Starting on this Assignment

Review Lecture 7. You may also want to check out the sample code from lecture.

Review notes on jQuery and DOM manipulation if needed. Also if needed check out Chrome's Getting Started.

While this may seem obvious, YOU MUST HAVE CHROME.

While I have your attention

Please send me your final project proposals via email! I'm reachable at isibner@seas.upenn.edu. You should aim to have your project approved by the time this one is due.

Our Chrome Extension

Chrome extensions are built with the same tools as websites: HTML, CSS, and Javascript.

We'll be making an extension that runs on IAmA Reddit posts and hides all comment chains that don't include a comment by the original poster (OP). If you are unfamiliar with IAmA's you can check out their structure with Bill Gate's IAmA, Arnold Schwarzenegger's, or the President's. The intent is to reduce the noise and focus on the questions that the OP has answered. The end product will also allow the user to make some small configurations.

The end product should function as follows:

This homework will be mostly in small steps, and it would be prudent not to jump around.

Loading Your Extension

To actually use your extension in Chrome, you first have to load it. Go to the Extensions page (chrome://extensions/) and check Developer Mode in the top right corner. Click Load Unpacked Extensions and find the homework directory. Make sure the extension is enabled. Whenever you change any of the source files, you need to return to this page and reload the extension before the changes take affect.

Manifest JSON

Every chrome extension needs a manifest file to declare its meta-information.

This is in the form of a JSON file manifest.json, that has the structure:

{
    "manifest_version": 2,

    "name": "IAmA Parser",
    "description": "Removes all comment chains from Reddit post without OP",

    "version": "1.0",

    "background": {
        "scripts": [ "background.js" ],
        "persistent": false
    },

    "permissions": [
        // your permissions here, e.g. declarativeContent
    ],

    ...
}

TODO: The permissions array declares what resources or APIs your app can use. You should begin by adding in the declarativeContent permission which is used in background.js to add the extension icon on the right pages.

As we continue you will add new elements to the manifest as well.

Content Scripts

In order to inject JavaScript into a page as it loads, we must use content scripts. Content scripts can be JavaScript or CSS and are declared in the manifest file. You will inject two JavaScript files (comment-parser.js, main.js. jquery.min.js) and one CSS file (parse.css). We have provided parse.css and main.js which you should quickly look over (both are only a couple lines) and jQuery. These must all be declared in the manifest as follows:

    "content_scripts": [
        {
            "matches": [ "..." ],
            "css": [ "..." ],
            "js": [ "..." ] 
        }
    ],

The css and js arrays should have the paths to the content script files, and matches is an array of URLs in which the scripts should be injected. For example, "http://www.facebook.com/*" would be all sites with the Facebook domain only over HTTP. "http://www.facebook.com/messages/*" would match anything on Facebook that had a path starting with /messages/.

TODO: You'll need to write some matches for www.reddit.com. Keep in mind that the only paths you're interested in start with /r/IAmA/comments/ - we're only filtering comment chains in /r/IAmA. Be sure to include both 'http' and 'https' versions for your matches.

Once you have added these, the next step is to actually write the JavaScript!

CommentParser

To implement this extention we will not be using the Reddit API, but instead will be adding classes to the elements in the page. The Reddit DOM is a bit odd (lots of divs have the class 'thing' for some reason?), so the work to find the comments and OP name is done in main.js (which is provided). You will be writing the CommentParser which takes in its constructor a jQuery object that has all top level comment chains and the OP username as a string. You will implement two functions for CommentParser: parse, and filter.

TODO: Implement parse.

Parse should take a username (String) as a parameter and apply the classes answered-chain and unanswered-chain to each top level comment based on whether the username shows up in the chain. Note that only one of these classes should ever be applied at a time!

Usernames appear in links with the author class, and it is sufficent to simply find all authors within a top level comment. If you have any trouble with this part, we would encourage you to inspect an IAmA's DOM manually yourself with the Chrome console. If an empty string or no username is given to the parse function, parse using the OP's username as the default. Parse should also turn on the filtering.

Finally, parse should call this.filter(true) so that whenever a new username is parsed, comment chains without that username are immediately filtered.

TODO: Implement filter.

Filter shoul take in a boolean indicating whether unanswered chains should be filtered, and appropriately toggle the visibility of unanswered-chains. I.e. when the page first loads, filter(true) will be called in the parse method, and all unanswered chains should be hidden; if filter(false) is subsequently called they should be shown. This should not change any classes; rather, you should use $topLevelComment.css('display', 'block') to show comments, and $topLevelComment.css('display', none') to hide comments.

Please make sure you use prototypal inheritance when adding these functions to the CommentParser. If you need a refresher on how to add functions prototypally, see the lecture slides on the subject.

To test this part, just run the extention on an IAmA page - content-parser.js should be loaded thanks to our manifest file. Any console logging done in CommentParser will go to the page's console.

Once you have gotten the extension to successfully hide comment chains without an OP answer, its time to create a popup in order to toggle the visibility of these comments. The popup is simply an HTML document that has been given to you. In order for this HTML file to be used as a the popup, it must be declared in the manifest:

    "page_action": {
        "default_popup": "popup/popup.html",
        "default_icon": "popup/icon.png",
        "default_title": "IAmA Parser"
    }

Also add the background script which we have provided for you:

"background": {
    "scripts": [ "background.js" ],
    "persistent": false
},

Reload the extension. You should now see an icon on the right end of the URL that should show a popup when clicked. This icon should only show up for IAmA pages -- this is done in background.js, which you should check out (but don't need to edit).

TODO: Implement popup.js.

Next, we'll add functionality to the input in the popup. When the 'REMOVE FILTER' button is pressed, the unanswered chains should show up (window.parser.filter(false)); when the 'APPLY FILTER' button is pressed, the unanswered chains should be hidden (with a call to window.parser.filter(true). For testing purposes, be sure that you use a click event listener for these. When a username is entered into the text input, the comments should be hidden based on which chains that username has been in - you'll want to call window.parser.parse(new_username). For the text input, listen for the keydown event. If you listen for the wrong events, you'll fail our tests even if things "work," so please be careful.

Note that any jQuery selecting done in popup.js will only search popup.html.In order to run JS in the IAmA tab itself, the Chrome tab's executeScript must be used. You can find the full documentation on that here. Here are a few things to note, however:

This will require another permission -- the activeTab permission -- so be sure to add this to the manifest.

Now, everything should be working! You should be able to reload your extension and cruise around reddit.com/r/IAmA, filtering the comment chains as you wish.

Submission

Now that you're done, make sure that everything works locally (and complies to this specification) before submitting to BRUCE.

In order to submit, run the following command and submit the resulting zip file:

zip -r files.zip content_scripts/comment-parser.js manifest.json popup/popup.js

NOTE: this won't work if you use a name other than files.zip!

Final Project Proposal

Due Wednesday, April 16 at 9AM.

The final project will be an opportunity to work on any kind of JavaScript project of your choosing that demonstrates the things you learned this semester. These projects will generally fall into one of 3 categories:

  1. Client-heavy application (like a game or other complex UI).
  2. Full-stack application (that does something interesting with the internet, maybe with a 3rd party API)
  3. Node.js application or library

You are welcome to use any JavaScript framework, including Backbone, its extensions, Express.js, and others.

Another option altogether is to contribute to an open source JavaScript framework or library. See some ideas below.

Group work: you may form a teams of two students for the final project; keep in mind that you will still have to demonstrate 10-15 hours of work per student.

Ideas to get you started

Resources for inspiration

Useful / interesting libraries to use or hack on

Also check out Github explore for more libraries.

Other open source projects to hack on

You might get some more ideas by looking at the most popular Github repositories.

If you need to brush up on HTML/CSS for the purpose of designing a nice front end interface, I recommend An Advanced Guide to HTML & CSS.


Proposal Specification

By April 16th at 9AM, please submit a project proposal outlining your ideas and aspirations for the final project. Your submission should have at least 300 words. Try to answer questions like the following:

In addition, we expect you to do some project research at this point and include a rough outline of your implementation plans. You might answer questions like:

Submission instructions

To create this submission, please create a new branch in your homework repo called final. Add a folder at the root called final/ and markdown document within this folder called proposal.md. (If you don't know markdown syntax, check it out here. It's fairly easy to learn and considered useful language for simple documents -- this website is powered by markdown.)

If you plan on including any images in the proposal, please organize them into a folder at final/images and use markdown's image linking syntax. Markdown support is built into Github so if you created your document correctly, it should show up nicely formatted when you access the .md file on the Github website.

Submit the new file(s) using a pull request called "Final Project Proposal" from the final branch to the master branch.

If you are working in a group: only one person needs to make this submission. Be sure to include both group members' names in the proposal.


Project Demos

The demo day time slot is TBD, but it will be during finals period. We'll get a room and have some food / drinks. It'll be fun and you'll get to see what cool things your classmates built.

You will be required to submit a short writeup along with the final code submission (due on demo day). More details about this coming soon.


Final Project Grading

Scaling Client-Side Applications

Module systems in the browser

By now, we've experienced the benefits that module systems can provide in JavaScript. Thing is, these only exist in platforms like Node.js, where standards are loose (or nonexistent). Browser implementations of JavaScript, which follow the ECMAScript standard, don't have built-in support for modules yet (it's coming soon in ES6, though). Luckily, the JS community has come up with some good workarounds. The most popular is the require.js library, which is an implementation of the Asynchronous Module Definition (AMD) standard.

With requireJS (and in particular, its optional CommonJS syntax), you can write client-side JS modules much like you would write node modules (the functions define and require are globally exposed by requireJS).

// scripts/main.js
define(function (require, exports, module) {

  var $ = require('jquery'),
      _ = require('underscore');

  function init () {
    // do stuff
  }

  exports.init = init;

});

This script would be loaded onto the page not with a <script> tag, but by the requireJS asynchronous loader. Below, the data-main attribute tells requireJS to load scripts/main.js after require.js loads.

<!DOCTYPE html>
<html>
  <head>
    <title>Sample RequireJS Project</title>
  </head>
  <body>
    <!-- ... -->

    <!-- load scripts -->
    <script data-main="scripts/main" src="scripts/require.js"></script>
  </body>
</html>

Note: to load jQuery and Underscore with the exact syntax as shown above, an additional shim would be needed to let requireJS know where those libraries live in the application.

For a more thorough walkthrough on getting requireJS set up, refer to the docs.


Backbone Events

I feel it's necessary at this point to do a deeper review of Backbone Events. While you may have not felt the need to use them very heavily in your homework thus far, they become very useful in larger applications for control architecture.

Fundamentally, Events are an inversion of control. Rather than reaching a statement in code and calling another function...

function doStuff () {
  // ...
  doSomeOtherStuff();
  // ...
}

you tell some part (or all) of your application that an "event" occurred:

_.extend(app, Backbone.Events);

function doStuff () {
  // ...
  app.trigger('stuff-happened');
  // ...
}

app.on('stuff-happened', doSomeOtherStuff);

Notice how doStuff now doesn't have to know about what kinds of external things should happen in the case of the 'stuff-happened' event. As long as the modules in your app can agree on a common events "interface", you can use a system like this to enable separation of concerns. Indeed, this is one of the more powerful features of Backbone since it allows your business logic to be completely unaware of how the UI works. Events are leveraged in all consequential front-end applications because they allow you to write modular, reactive code.

Backbone.Events is mixed into almost all other Backbone classes, including:

Remember that events can also pass along data to their handlers simply as a second argument to trigger.

Note: since Backbone is globally available in your app, you can use it as a simple universal event bus or pub/sub system!

function doStuff () {
  Backbone.trigger('stuff-happened', {
    how_much: 'lots of'
  });
}

Backbone.on('stuff-happened', function (params) {
  var how_much = params.how_much || 'some';
  console.log('yo, ' + how_much + ' stuff happened!');
});

Of course you are able to unsubscribe from events using the off method (and you can also remove a specific event handler by passing that handler function as a second argument).

Backbone.off('stuff-happened');

Backbone.off('all', someUniversalHandler);

There's another way for "evented" objects (those with Backbone.Events mixed in) to listen to events as well. Consider a situation from the Pokémon game we've been building:

builderView.listenTo(builder, 'change:selectedSwatch', function (attrs) {
  // ...
});

Subscribe / unsubscribe now become listenTo / stopListening, and you can see that we can "direct" our listening toward an object. The reasons why you might want to use listenTo instead of on have to do with memory management and garbage collection.

View management

Typically, to clean up a Backbone view, you simply call .remove(), which delegates to jQuery's .remove, thereby destroying the DOM element, its data in the DOM, and attached event handlers.

If there are multiple views attached to a DOM element, you might instead call .undelegateEvents() for just one of them and remove any links to that view in your code (so that it gets garbage collected).

Take a look at Backbone.View.remove in the source -- it calls stopListening() on itself, which will remove any event listeners you have bound using .listenTo or .on. It will not, however, remove any listeners that another object has "directed" towards the view. Some code to illustrate the point:

var myModel = new Backbone.Model({ foo: 'bar' }),
    myView  = new Backbone.View({ model: myModel });

$('body').append(myView.render().$el);

myModel.on('change:foo', function (attrs) {
  myView.render();
});

myModel.listenTo(myView, 'some-view-event', function (params) {
  myModel.sync();
});

myView.remove();

Neither of the event listeners above will be unbound when myView is removed. This means that myModel will retain a reference to myView in JavaScript and the garbage collector won't destroy the view. This is a memory leak and is considered a Bad Thing in any programming environment.

Lessons to be learned: every on or listenTo in your application should be matched by a corresponding off or stopListening call to ensure bulletproof memory management. When you need a view to react to a model, recognize the tradeoff between using model.on and view.listenTo.

For more information about view "zombies" that cause memory leaks like this, check out this article. It's from 2011 though, so be wary of changes in Backbone style / syntax that have occurred since then.


Common client-side problems & solutions

As we've seen thus far, Backbone is a small library; as such, it doesn't solve all the front end UI programming problems that you might encounter in building complex applications. Many of the common issues you'll deal with will have to do with views and view management. Much is adapted from the Backbone Fundamentals book.

Create and render sub-views

  1. The simplest method to create a "sub-view" is through direct DOM-level inheritance:

     var MyView = Backbone.View.extend({
    
       tmpl: _.template($('#my-view-template').html()),
    
       initialize: function () {
         this.subView = new SubView({
           model: this.model.get('sub-model')
         });
       },
    
       render: function () {
         this.$el.empty();
         this.$el.html(this.tmpl(this.model.toJSON()));
    
         this.$el.append(this.subView.$el);
         this.subView.render();
       }
    
     });
    

    Note that by rendering this.subView after placing its element in the DOM, we can utilize rendering logic that sizes the sub-view based on the dimensions of another element in the page.

  2. You might use setElement instead here, but that has a few tradeoffs (agnostic to DOM element order, but you can't specify things like tag name, etc.)

     render: function () {
       // ...
       this.subView.setElement('.sub-view-container').render();
     }
    
  3. Here's another solution that can potentially affect performance:

     var MyView = Backbone.View.extend({
    
       // ...
    
       initialize: function () {
         this.render();
       },
    
       render: function () {
         this.$el.html(template);
         this.subView = new SubView();
         this.$el.append(this.subView.$el);
       }
    
     });
    
     var SubView = Backbone.View.extend({
       initialize: function () {
         this.render();
       },
    
       render: function () {
         this.$el.html(template);
       }
     });
    

Looking at these options, we find that #1 is generally preferred. Sub-views don't have to be re-initialized when the outer view is rendered (re-initializations have the potential to cause memory leaks and issues with existing event bindings). Also, views rendering logic is able to reliably assume that a view's element already exists in the DOM.

Accessing models in child views

Once you start having sub-views as shown above, you may end up needing to create a model hierarchy as well. This wasn't the case in the Pokémon homework, but let's say we had a Palette model separate from the Builder model.

var Builder = Backbone.Model.extend({

  initialize: function () {
    this.palette = new Palette();
    this.palette.parent = this;
  }

});

If we maintain a .parent model link like this, then palette can easily access the builder model as necessary.

var Palette = Backbone.Model.extend({

  initialize: function () {
    this.on('change:selectedSwatch', function (attrs) {
      this.parent.trigger('selectSwatch', attrs.selectedSwatch);
    });
  }

});

Similarly, we can create a link to parent views.

var BuilderView = Backbone.View.extend({

  initialize: function () {
    this.paletteView = new PaletteView({
      model: palette
    });

    this.paletteView.parentView = this;
  },

  render: function () {
    // ...
    this.$el.append(this.paletteView.$el);
    this.paletteView.render();
  },

  // ...

});

var PaletteView = Backbone.View.extend({

  events: {
    'click .swatch': 'onSwatchClick'
  },

  initialize: function () {
    this.listenTo(this.model.parent, 'reset', this.render);
  },

  onSwatchClick: function () {
    this.parentView.notify();
  },

  // ...

});

Keep in mind that this kind of indirection is most times best handled with events (it keeps code modular), but you may come across scenarios where direct hierarchical access to models and views is helpful.

Refreshing a parent view within a child view

Once you have this parentView link and come across a point in your application where a sub-view event should trigger a parent-view render, you may be tempted to call

this.parentView.render();

This will probably work right off the bat. However, if you want inversion of control and more modularity, you'll want to use events.

// Parent initialize
this.listenTo(this.subView, 'refreshAll', this.render);

// Parent removal
this.stopListening(this.subView, 'refreshAll');

Disposing of view hierarchies

If you have nested views, it would be nice to be able to simply call a close function on the top-level view to be able to dispose of the entire hierarchy.

Backbone.View.prototype.close = function () {
  if (this.onClose) {
    this.onClose();
  }
  this.remove();
};

var ListView = Backbone.View.extend({

  tagName: 'ul',

  initialize: function (params) {
    this.childViews = params.children || [];
  },

  renderChildren: function (itemView) {
    this.$el.append(itemView.$el);
    this.childViews.push(itemView.render());
  },

  onClose: function () {
    _(this.childViews).invoke('close');
  }

});

var ListItemView = Backbone.View.extend({
  tagName: 'li',

  render: function () {

  }
});

var myView = new MyView({
  children: [
    new ListItemView(),
    new ListItemView()
  ]
});

myView.render();
myView.renderChildren();
myView.close();

Working with nested models & collections

The fact that Backbone doesn't support nested models / collections nicely out of the box is somewhat of an endorsement for simpler client-side models. However, if you do find yourself with nested data structures, here's how you might build your models. Imagine you are modeling "rooms" inside "buildings" on the client.

var Rooms = Backbone.Collection.extend({
  // ...
});

var Building = Backbone.Model.extend({

  initialize: function () {
    this.rooms = new Rooms();
    this.rooms.url = '/building/' + this.id + '/rooms';
    this.rooms.on('reset', this.updateCounts);
  },

  // ...

});

var townHall = new Building();

// lazy load the building's rooms
townHall.rooms.fetch({
  reset: true
});

Note that there are some Backbone plugins, such as Backbone Relational, which allow you to set up different kinds of relations (much like a relational database) for your models. For example, it relieves the issues around chained model getters like this:

this.model.get('player').get('position')....

Inheritance in Backbone views and models

We've been using Backbone inheritance in the form of Backbone.*.extend without looking at it too closely. This method (inspired by the inherits function in the Google Closure Library) is designed to mimic classical object-oriented inheritance with some prototypical stuff thrown in; there are some nuances you might want to take note of. Link to its source.

You are able to simply inherit from other views / models like so:

var PlayerView = Backbone.View.extend({
  initialize: function (params) {
    console.log('player init');
    this.row = params.row || 0;
    this.col = params.col || 0;
    this.orientation = 'right';
  }
});

var PikachuView = PlayerView.extend({
  initialize: function (params) {
    PlayerView.prototype.initialize.call(this, params);
    console.log('pikachu init');
    this.orientation = 'down';
  }
});

var playerView  = new PlayerView(),
    pikachuView = new PikachuView();

Note how it was necessary to explicitly call a parent view's method to re-invoke some functionality if that method was overriden in the child view. This can be cumbersome as our apps scale. Here's a proposed improvement:

var PlayerView = function (params) {
  console.log('player init');
  this.row = params.row || 0;
  this.col = params.col || 0;
  this.orientation = 'right';

  Backbone.View.apply(this, params);
};

_.extend(PlayerView.prototype, Backbone.View.prototype, {
  // all of PlayerView's methods go here
  orient: function (dir) {
    this.orientation = dir;
  }
});

PlayerView.extend = Backbone.View.extend;

var PikachuView = PlayerView.extend({
  initialize: function (params) {
    console.log('pikachu init');
    this.orientation = 'down';
  }
});

var pikachuView = new PikachuView();
pikachuView.orient('right');

Neat! _.extend saved us a lot of time and redundant code.


MarionetteJS

Marionette is a Backbone extension that provides APIs and functionality for many of the most common situations & architectures in a non-trivial Backbone.js app. For example, it has a notion of nested views and abstractions for view life cycle management. If you write more Backbone apps, you'll find yourself writing the same boilerplate over and over again. Marionette will help you reduce this boilerplate code. Studying its functionality may help you to more easily gain access to the more complex & prescriptive front end frameworks (Ember, AngularJS, etc).

Make your Backbone applications dance!


References

Advanced JS Programming Topics

Motivation for Asynchronous Abstraction

I'm going to start with a simple example of asynchronous programming and build up to something not-so-simple that presents all sorts of problems in our current model of async JavaScript.

Let's say we're trying to read a file in Node. We might create a wrapper for the fs.readFile function like this:

function read (name) {
  fs.readFile(name, function (err, data) {
    if (err) { throw err; }
    return data;
  });
}

But hold on a minute – this doesn't work. The return data; line doesn't send the result anywhere useful and the read function ends up returning undefined whenever it's invoked.

To get a result, we have to switch up the control flow.

function read (name, cb) {
  fs.readFile(name, function (err, data) {
    if (err) { throw err; }
    cb(data);
  });
}

read('foo.json', function (data) {
  // ...
});

Sure, this doesn't look much different from simply using fs.readFile, but when you want to keep code modular and have read called from a separate file, this abstraction might make sense. We aren't doing much to handle the potential error, however (keep this in mind for later).

What if we instead want to read an entire directory's contents? This requires an fs call to list the files in a directory and then multiple readFile calls to get the contents of those files. (A similar situation might occur with other async tasks as well, e.g. one API call that is required to get data necessary to make successive calls).

function getDirectoryContents (dir, cb) {
  fs.readdir(dir, function (err, files) {
    // `files` is an array of filenames excluding '.' and '..'

    var contents = {};

    _(files).each(function (filename) {
      fs.readFile(filename, function (err, data) {
        contents[filename] = data;
      });
    });

    cb(contents);
  });
}

getDirectoryContents('myDir', function (files) {
  console.log(files);   // {}
});

I tried to be clever here by pushing results of the readFile calls into an object and sending that object to the callback (remember, it's passed by reference, not copy).

Does this work as intended? What might be a problem with this approach?

If you inspect the files object within getDirectoryContents, you'll find that it's not quite populated with the contents you expect upon immediate execution. We could choose to wait a few milliseconds before looking at the contents, to allow for the time it takes for the OS to run its file system functions:

getDirectoryContents('myDir', function (files) {
  setTimeout(function () {
    console.log(files);
  }, 5);
});

But now we've introduced more asynchronous code and an arbitrarily-defined control scheme involving setTimeout. The code looks ugly and is brittle. What if we encountered a large JSON file and reading it took a long time?


Promises

Let's go back to the asynchronous task of getting the file contents in a directory. There's certainly more than one way to solve this problem. I'm going to present another Node programming paradigm you can add to your toolkit.

A Promise is an object that represents an eventual value from a computation or operation. It is the crucial idea that lets you go from having callback-oriented code like this:

function doSomethingWithResult (value) {
  // ...
}

doSomething(doSomethingWithResult);

to more procedural code like this:

doSomething().then(doSomethingWithResult);

Now you might wonder why this is useful at all – we're actually writing more code with the second snippet. The benefits of promises become apparent in their more complex usage scenarios – namely, with chaining and error handling.

Instead of descending into callback hell with nesting like this:

fs.readdir(dirname, function (err, files) {
  if (err) {
    console.log(err);
    return;
  }

  var fileToRead = dirname + '/' + files[0];

  fs.stat(fileToRead, function (err, stats) {
    if (err) {
      console.log(err);
      return;
    }
    if (isRecentFile(stats)) {
      fs.readFile(fileToRead, function (err, contents) {
        if (err) {
          console.log(err);
          return;
        }
        doSomeOtherAsyncTask(function (err, data) {
          // ...
        });
      });
    }
  });
});

...you might write a series of promise transformations:

fs.readdir(dirname)
  .then(getFirstFile)
  .then(getFileStats)
  .then(filterRecentFile)
  .then(doSomeOtherAsyncTask)
  .catch(errorHandler);

Note that you probably could have refactored the above "callback hell" example by creating named functions. But you still wouldn't get syntax as nice as this and more importantly, you wouldn't be able to handle errors so elegantly.

By throwing in a .catch() call at the end of this promise chain, we can simply write one function that handles any error along the chain. In other words, errors are propagated through the chain.

Note: If you're familiar with monads in mathematics or functional programming, you can actually think of promises as monads in JS. Do-notation in Haskell serves to turn functionally-composed control flow into an imperative one, similar to how we just reconciled the JS callback pyramid into a series of promise-based steps.

Implementing promises

OK, so you might be on board with the idea of promises (that's good, they're pretty cool). But how are they implemented? By giving you some ideas about how they're implemented, I hope to give you some better tools to understand trickier situations involving Promises.

Going back to the doSomething function example, we know that we need to change the result of such a function from this:

function doubleValue (val, callback) {
  callback(value * 2);
}

...into this:

function doubleValue (val) {
  return {
    then: function (callback) {
      callback(value * 2);
    }
  }
}

This is simply some (relatively uninteresting) sugar for the callback pattern so far, but it's a start. You can see that we have to return an object. But this is very far from being modular.

Let's create a Promise class. The callback within then should have the opportunity to resolve itself (signify that a result value has been determined). We're still using raw values (not async results) to keep things simple, though.

function Promise (fn) {
  var callback;

  this.then = function (cb) {
    callback = cb;
  };

  function resolve (value) {
    callback(value);
  }

  fn(resolve);

  return this;
}

function doubleValue = (val) {
  return new Promise(function (resolve) {
    var value = val * 2;
    resolve(value);
  });
}

doubleValue(21).then(console.log);

The structure is kind of getting somewhere... but does it work? If you try out this code you'll realize there's a timing problem – resolve is called when callback hasn't been set. Here's a hack that might fix that:

function Promise (fn) {
  // ...

  function resolve (value) {
    setTimeout(function () {
      callback(value);
    }, 1);
  }

  // ...
}

Using setTimeout like this forces the callback to be called on the next tick of the event loop, giving then a chance to set the callback variable.

But of course, this code is very hacky. It's brittle and will break if we introduce any sort of asynchronicity. This reveals something you may not have realized about promises thus far: they have state.

A promise can be pending waiting for a value, resolved with a value, or failed with an error. The above example was set up for failure so that it could be simple enough to wrap your head around.

If we start keeping track of state within Promise, we can remove the setTimeout hack:

function Promise (fn) {
  var state = 'pending';

  var value, deferredCallback;

  function resolve (newValue) {
    value = newValue;
    state = 'resolved';

    if (deferredCallback) {
      handle(defferedCallback);
    }
  }

  function handle (onResolved) {
    if (state === 'pending') {
      deferredCallback = onResolved;
      return;
    }

    onResolved(value);
  }

  this.then = function (onResolved) {
    handle(onResolved);
  }

  fn(resolve);
}

This is getting complicated, but now we can invoke then() whenever we want on the Promise and its callback can invoke resolve() whenever it wants – it basically works with synchronous or asynchronous code.

The above implementation works because both then() and resolve() hand off to a new function, handle(), which decides what to do based on the state of the Promise:

There's more work to be done (implement chaining and error handling), but these are the basics, and they're pretty powerful.

For a more detailed analysis, I recommend this article: Promises In Wicked Detail.

What's next

Now that we've seen all their great benefits, you should go check out some promise libraries! Unfortunately they are not included in the ECMAScript standard, so yes, we do have to resort to libraries here. Some good ones are bluebird, Q and rsvp.js. These implement the "Promises/A" spec, which will soon be included in ES6 (yay built-in promises!). They also have some good documentation which is good to read up on.

Testing, Tools, & Build Systems

Does my code work?

By this point, I hope you've had a chance to get your feet wet with the wonderful Chrome debugger. However, as you're well aware, stepping through code isn't the only way to verify your programs; testing is an important tool for any kind of software developer.

In JavaScript's quest to become a "real" language, one of the important milestones is a community & standards that encourage safe & valid code through testing, both for TDD and code maintainability. The language's dynamic nature and loose typing makes tests all the more important. The browser environment also complicates things, especially when you throw in cross-browser compatibility.

One positive outcome of the whole situation is that the browser sandbox environment ensures that your code can't be too harmful for the user. In Node, however, this protection doesn't exist, so you generally have to be a little more careful.

There are no testing features built-in to the ECMAScript standard (no assert or the like). There are, however, multiple libraries out there that will help you build test suites using what's already built-in (because all you really need at a low level are equality checks and typeof).

Testing libraries & frameworks

JS testing libraries include both (1) assertion libraries and (2) testing frameworks. They are built with modularity in mind such that the former are typically able to power any given testing framework.

Assertion libraries:

Testing frameworks:

All of these are fairly popular in their own right, so you may choose from them as you wish. You'll also find other more customized libraries out there.

I mentioned TDD earlier -- you'll find many of these libraries fall in line with TDD (and its close relative, BDD) standard practices. Some of the syntax is derived from RSpec and other testing standards in the Rails ecosystem; this may be attributed to the fact that many web developers transitioned from writing web applications in RoR to heavier client-side applications in JS.

Writing unit tests

Let's take a look at what it takes to test some simple functions with Mocha and should.js. We'll be writing some tests for the HW1 assignment. Mocha provides a way to run test suites using its command line tool, which automatically loads the framework, runs a test file, and generates some output based on a specified test "reporter":

mocha --reporter spec test-suite.js

test-suite.js, then, might look like this:

/*
 * Import assertion library and the modules to test.
 */
var should  = require('should')
  , hw1     = require('../exercises')
  , key     = require('../exercises-solution');

/*
 * Lifted from hw1 stub
 */
var priceList = {
  eggs: 2.50,
  milk: 3.00,
  bread: 2.75,
  orangeJuice: 4.25,
  chocolate: 1.50
};

/*
 * Describe a test suite. These may be nested to provide more structure.
 */
describe('(Exercise 1) Array methods', function () {
  var a;

  /*
   * Runs once before each test within this suite.
   */
  beforeEach(function () {
    a = [ 1, 2, 3, 4, 5 ];
  });

  /*
   * A single test that contains one or more assertions. Mocha's DSL
   * allows us to write tests that look like plain english.
   */
  it('should sum elements', function () {
    hw1.arrayMethods.sum(a).should.equal(15);
  });

  /*
   * It's good practice to provide descriptive test names so you can easily
   * tell which part of the code is failing at a glance.
   */
  it('should throw an error if summing non-Number elements', function () {
    var b = [ 1, '2', 3 ];

    (function () {
      hw1.arrayMethods.sum(b);
    }).should.throw();
  });

  it('should remove an element', function () {
    var b = hw1.arrayMethods.remove(a, 2);

    b.should.be.an.Array;
    b.indexOf(2).should.equal(-1);
  });

  it('should find duplicates', function () {
    var b = [ 1, 1, 2, 3, 'abc', 'def', 'abc', a, a, { foo: 'bar' } ]
      , c = hw1.arrayMethods.findDuplicates(b)
      , cExpected = [ 1, 'abc', a ];

    c.should.be.an.Array;
    c.should.eql(cExpected);
  });
});

The above suite shows off some simple usage of our testing libraries of choice. Next, let's examine some more advanced scenarios. Mocha supports async testing simply with a done function provided to test and test suite callbacks. Let's say we're testing the server-side map-storage module in HW6:

var should      = require('should')
  , mapStorage  = require('../map-storage');

// ------------------------------------------------------------------------

describe('MapStorage', function () {

  // Assume a `sampleMapTiles` array exists in scope...
  describe('#saveMap', function () {
    it('should save a map to disk', function (done) {
      var mapParams = {
        name: 'newMap'
      , tiles: sampleMapTiles
      };

      /*
       * The `done` method is flexible enough to work as a node-style
       * callback that takes `err` as its first argument.
       */
      mapStorage.saveMap(mapParams, done);
    });
  });

  describe('#getMaps', function () {
    var maps = [];

    beforeEach(function (done) {
      mapStorage.getMaps(function (mapsCollection) {
        maps = mapsCollection;
        done();
      });
    });

    /*
     * Assertions are chainable!
     */
    it('should fetch a valid maps collection from disk', function () {
      maps.should.not.be.empty;
      maps.should.be.an.Array.and.have.length(4);
      maps[0].should.be.an.Object.and.have.keys('name', 'tiles');
      maps[0].name.should.be.a.String;
      maps[0].tiles.should.be.an.Array;
    });
  });

});

Cool! We've isolated the functional pieces of this node module and tested them separately. But what if we want to write tests for UI code running in the browser? Here, we run into a number of challenges:

We'll get back to the logistics of testing UI code. First, some ideas about testability:


Is my code testable?

Right off the bat, we know there are a few avoidable anti-patterns that make a front end application difficult to test; these should mostly be apparent to you if you' been paying attention to JS code style.

<html>
  <head>
    <title>Anti-patterns for testable UI code</title>
    <style type="text/css">
      .form-msg { display: none; }
    </style>
  </head>

  <body>
    <h2>Log in</h2>
    <form id="login" action="#">
      <input type="text" id="username" name="username" placeholder="Username" />
      <input type="password" id="password" name="password" placeholder="Password" />
      <input type="submit" id="submit" value="Submit" />

      <p class="form-msg error fields-required">
        Both username and password fields are required.
      </p>

      <p class="form-msg success auth">
        Successfully logged in!
      </p>

      <p class="form-msg error auth">
        The username/password combination is incorrect.
      </p>

      <p class="form-msg error server">
        There was a problem authenticating the user. Please try again later.
      </p>
    </form>

    <!-- scripts -->
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js">
    </script>

    <script type="text/javascript">
      /*
       * This code is inaccessible to tests; you can either run all of it or
       * none of it at once.
       */
      $(function () {

        $('#login').on('submit', function () {

          // Mixture of form logic and event logic here...
          event.preventDefault();

          var username = $('#username').val()
            , password = $('#password').val();

          if (username && password) {
            /*
              * Requires a functioning back end...
              */
            $.ajax({
              type: 'POST'
            , url: '/authenticate_user'
            , data: {
                username: username
              , password: password
              }
            , success: function (data, status, jqXHR) {
                $('.form-msg.success.auth').show();
              }
            , error: function (jqXHR, textStatus, errorThrown) {
                $('.form-msg.error.auth').show();
              }
            });
          } else {
            $('.form-msg.fields-required').show();
          }
        });
      });
    </script>
  </body>
</html>

Problems with the above code:

Improving testability

Much of the advice we've learned so far about client-side application structure and separation of concerns applies here in helping us create more testable code. To create really robust tests, however, we need to go a few steps farther and look into some additional testing libraries.

SinonJS is a library that provides "standalone test spies, stubs, and mocks for JavaScript". There are other libraries that provide these features (Mockery, etc.), but SinonJS appears to be the most popular right now. Its Getting Started page gives you a good idea of the kind of testing power it provides you -- you can verify the behavior of callbacks & their arguments, stub out parts of your application to reduce dynamic behavior, and present mock interfaces for things like AJAX requests.

In addition to being full-featured and all-encompassing (there are, of course, also code coverage tools for JS), your tests ought to have good style on their own and be maintainable for future developers. This is a good article with advice on writing more readable tests.


Does my full stack work?

In addition to unit tests, you will eventually also encounter integration tests and end-to-end tests as a front end developer.

Integration tests are meant to verify the joint functionality of two parts of your application stack -- usually, these ensure that system interfaces work as intended.

End-to-end tests, as the name implies, test the overall functionality of an application from the point of view of an end user. They are also the hardest to write and maintain because they require things like mocking out a database, simulating a headless browser, etc.

Libraries:


JS Workflow

(Demonstrated in class)

TDD is just one part of a serious JS developer's workflow. As your projects expand in scope, you'll find the need to run automated build scripts to compile assets & templates. There are some great tools out there to do this job, but unfortunately there's no single standard yet. The one I use these days is called Gulp -- it's a build system that makes heavy use of Streams. It's a step forward from the widely-used Grunt, which doesn't handle chaining & composition of tasks very well. Even more experimental is a tool called Broccoli, which aims to provide fast rebuilds and a nicer API for complicated plugin composition.

There is a good amount of development being done on all of these libraries (notice that they're all at version <1.0). When selecting a build tool, keep in mind that for most people, value is only derived if the community plugins for the tool (CoffeeScript & SASS compilers, test runners, requireJS builders, etc.) are sufficiently populated.

CoffeeScript

Why use a language that compiles to JavaScript?

As you write more JavaScript, you might get tired of avoiding the bad parts, want things like static typing, or even just find JavaScript's syntax too clunky for your taste. However, you can't just start using a random other language instead of JavaScript because no other languages run in the browser.

That's why compile to JavaScript languages exist. They let you write in a different language, and a cross-compiler converts it to JS so you can run it in the browser.

CoffeeScript is probably the most popular compile-to-js language. It lets you write less code that automatically passes JSHint and is arguably more readable. There are other options like Dart by Google and TypeScript by Microsoft that provides static typing, but those won't be covered here.

Quick overview of CoffeeScript syntax

Note: Taken and adapted from coffeescript.org

# Assignment:
number   = 42

# Conditions:
if number == 42
  console.log 'here'
  console.log 'number is 42'
else
  console.log("number is ")
# Existence:
alert "I knew it!" if elvis?

# Functions:
square = (x) -> x * x

# Objects:
math =
  root:   Math.sqrt
  square: square
  cube:   (x) -> x * square x
  nested1: {a: 1, b: 2}
  nested2:
    a: 1
    b: 2

list = [1, 2, 3, 4, 5]
# Array comprehensions:
cubes = (math.cube num for num in list when num > 2)

# for in loops
# prints 1, 2, 3, 4, 5 not 0, 1, 2, 3, 4, 5
for el in list
  console.log e

for k, v of math
  console.log k, v

Some things to note:

CoffeeScript's more interesting features

Classes:

CoffeeScript provides some nice abstraction for object oriented programming.

class Animal
  constructor: (@name) ->

  move: (meters) ->
    alert @name + " moved m."

class Snake extends Animal
  move: ->
    alert "Slithering..."
    super 5

class Horse extends Animal
  move: ->
    alert "Galloping..."
    super 45

sam = new Snake "Sammy the Python"
tom = new Horse "Tommy the Palomino"

sam.move()
tom.move()

Some things to note:

Fat Arrow:

When defining a function, you can use a fat arrow => (instead of ->) to bind a function to the current value of this. This is useful for setting callbacks, among other things.

Account = (customer, cart) ->
  @customer = customer
  @cart = cart

  $('.shopping_cart').bind 'click', (event) =>
    @customer.purchase @cart

How to use CoffeeScript

Install coffee globally with sudo npm install -g coffee-script.

The standard file extension is .coffee. You can run a file without compiling it using coffee <name>.coffee.

You'll generally want to have your CoffeeScript files automatically compiled. You can do this with a command like coffee -mo js/ -cw coffee/. In particular, this will:

Data Visualization with D3.js

April 30, 2014