This is probably not the page you're looking for. You most likely want the 2015S website.

JavaScript (Fall 2014)

Intro to Front End Web Development

Course meets Tuesday 3:00pm – 4:30pm in Towne 319.

Instructor: Geoffrey Vedernikoff (veg@seas.upenn.edu)

TAs:

Office hours: See Piazza staff page

Course Description

This course provides an introduction to modern web development frameworks, techniques, and practices used to deliver robust client side applications on the web. The emphasis will be on developing JavaScript programs that run in the browser, not server-side applications in traditional languages like Java, Python, or Ruby. See the lecture schedule for more info.

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 in the browser.

Prerequisites

Policies

Extensions on homework assignments will generally be granted if you ask for them in advance. Assignments come with the opportunity for revision up to a week after grading. See the course syllabus for more detailed policies and grading info.

Assignments

Assignments are due at 9am unless otherwise indicated. Code submissions are to be made using the Github submission system. Refer to the style guide for coding JavaScript.

hw0, due Tues, Sept. 9: Set up Github submission.

hw1, due Tues, Sept. 16: Finger exercises.

hw2, due Fri, Sept. 26: XML object modeling.

hw3, due Tues, Oct. 7: Pokémon map building.

hw4, due Tues, Oct. 21: Backbone.js starter.

hw5, due Fri, Oct. 31: Intermediate Backbone.js app.

hw6, due Wed, November 19: Autocompleter.

hw7, due Wed, December 3: Product Hunt Client and Final project proposal.

Final Project

The final project will be an open-ended JavaScript application created with client-side framework of the student's choice. It should demonstrate 10-15 hours of work. The due date is December 16.

See the proposal spec for more information.

Lectures

Code samples from lectures are available in this Github repository.

Lecture Date Content Lecture Code Reading, Resources
0 9/2 JS basics & syntax
How to run JS client-side and server-side
Eloquent JS, Chapter 2
[MDN] A re-introduction to JavaScript
1 9/9 JS functions Lec 1 Eloquent JS, Chapters 3
2 9/16 JS objects & inheritance Lec 2 [MDN] Document Object Model
Eloquent JS, Chapters 12
3 9/23 JS in browsers
DOM Manipulation
Lec 3 Chrome DevTools docs
4 9/30 Browser Events Lec 4 Eloquent JS, Chapter 13
5 10/7 Functional Programming in JS
Lec 5 Eloquent JS, Chapter 6
[DailyJS] Functional Programming in JS
DBA: Intro, Fundamentals
6 10/14 Introduction to client-side MVC Lec 6 Backbone docs
7 10/21 A Deeper Dive into Backbone Lec 7 Todo app
Todo app source
8 10/28 Promises Lec 8 Promises in Wicked Detail
9 11/4 EventEmitters
Backbone vs. jQuery
Lec 9
10 11/11 Testing
CoffeeScript
Lec 10 CoffeeScript
JS to Coffee Compiler
11 11/18 React.js Lec 11 React Tutorial
Thinking in React
12 12/2 Future of JavaScript Lec 12 ES6 Compatibility Table
ES6 Features
Going Async with ES6 Generators
An Intro to ES6 Classes

Reading

CIS197 Code Submission

CIS 197 (January 22, 2013)

Adapted from CIS 192.

Prerequisites

Setup

Note: This system is still in beta. If you encounter errors in this guide or the course website, please post a private Piazza question. If you have a question about Git or Github, please post a public question.

  1. Complete the intro survey if you haven't already. Once you get an email saying you have been added to the cis197f14 organization, you may proceed with the next steps.

  2. Log onto Github. Change your view to the CIS 197 class context. switch context

  3. Click on the repository we've created for your homework. The name will be in the format "hw_pennkey". Note this repository is private; only you and the instructors have access to it. repo name

  4. Copy the repository url from its page. repo url

  5. Open your terminal. You're going to clone a copy of this repository so that you can do work on it and push updates back to Github. First cd into the directory you want the homework folder in (if you don't know what that means, please take a few minutes to read Section 3 of this document. Now execute:

     git clone repo_url_you_copied
    

    put together, that all looks like

     bash-3.2 $ cd ~/Github/cis197/
    
     bash-3.2 $ git clone https://github.com/cis197f14/hw_cis197bot.git
     Cloning into 'hw_cis197bot'...
     Username for 'https://github.com': cis197bot
     Password for 'https://cis197bot@github.com':
     warning: You appeared to have cloned an empty repository.
     Checking connectivity... done.
     bash-3.2 $
    

    You've now copied the (empty) repository onto our computer, so you can go into that directory:

     bash-3.2 $ cd hw_cis197bot
    
  6. Now you're going to add the class homework repository as an "upstream" to our personal homework repository. This will allow you to pull required homework files directly from Github. You'll need to execute the following command inside the directory you just cloned:

     git remote add upstream https://github.com/cis197f14/hw.git
    

    This added the class repository as a git "remote", which means that you can pull changes down from that repository and into your personal repository. Let's do that now. Run the command:

     git pull upstream master
    

    and you should see that files were copied from the server.

  7. Almost done. You need to create a git branch where your code will be submitted. First, create a new branch called "submit":

     git branch submit
    

    Now push that branch to Github:

     git push origin submit
    

    And also push the master branch to Github:

     git push origin master
    

Great! Now that the setup is done, you can get started on homework.

Homework 0

The first submission is just to make sure you've set everything up correctly. It's a simple JavaScript Hello World program. For future homeworks, please refer to the next section.

  1. First, create a hw0 folder and create an empty hello.js file in it.

     mkdir hw0 && touch hw0/hello.js
    
  2. Now, write the Hello World program into the file.

     echo "console.log('Hello world');" > hw0/hello.js
    
  3. Run the program to make sure it prints 'Hello world'.

     node hw0/hello.js
    
  4. Now, you need to stage and commit all the relevant files:

     git add hw0/hello.js && git commit -m "done with hw0"
    

    Push changes to Github:

     git push origin master
    
  5. Follow steps 4 and 5 in the next section to submit the Pull Request and finish Homework 0.

Working on homework

  1. First, get any files/folders that were added to the class homework repository:

     git pull upstream master
    
  2. Now complete your assignment. Please make sure to create a different folder in the repository for each homework assignment (hw1, hw2, etc.)

  3. Here's how you commit to the repository and push the work to Github. Let's say you wrote code in two files, file1.js and file2.js, in the directory hw1 inside your homework repository. First you need to stage all the relevant files:

     git add hw1/file1.js hw1/file2.js
    

    Now make a commit:

     git commit -m "done with hw1"
    

    Push changes to Github:

     git push origin master
    
  4. Now your homework files are on Github. All you need to do now is create a pull request to complete the submission. Go to your homework repository and create a new Pull Request from the Pull Requests tab. pull request

  5. Set up a pull request with the base branch set as "submit", and finally send it. send pull

Do not click the "Merge Pull Request" button on Github after your submission (a TA will do that after grading homework). Though we require that you submit your homework with git, it would be wise to use git to keep track of your changes before you submit.

Notes

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. 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:

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


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


while (condition) {

}

Functions

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) {
  ...
};

Alternatively, you can use a function definition syntax like the following example, but keep in mind that scoping works slightly differently – this function becomes available at 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

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.

Similarly, with should not be used.


Finally, you should be including an IIFE in all your JavaScript files (to avoid polluting the global namespace), along with a 'use strict' statement, which invokes stricter interpretation of your JS source (here's a more detailed explanation).

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

  // do stuff


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

For a very detailed style guide, refer to idiomatic.js. (I generally agree with most of the conventions used there).

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.

(Note: in hindsight, there's actually a built-in method called Array.prototype.concat already. So you wouldn't want to overwrite it like this.)

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 Tuesday, September 23 at 9 AM.

Implementation stub available in the homework repository.

Before starting this assignment

Install the sax.js library by running npm install sax 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.
   */
  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 (whether certain nodes
   * have that attribute at all).
   */
  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. If you define properties or methods that you intend to be private (not part of the XML document object model), it's good practice to signify this with an underscore _ prefixed to the property / method name. For example, you might define a _setAttributes function on XMLElements that takes a hash and updates the stored attributes for a node.

However, the XMLElement constructor should take in a single config object (hash) that specifies its 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. Ensure it has these additional properties / methods:

{
  // 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 -- they are a method of asynchronous control flow that allows you to define a function to be run when an event occurs. 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 should be produced that models the whole tree. You should be able to interact with the document object by invoking its tree filtering methods.

I have provided some implementation already in the XMLDocumentBuilder class 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 reference in the document builder class) of the current context 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.

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

var builder = new XMLDocumentBuilder(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 XMLDocumentBuilder) 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,
  XMLDocumentBuilder: XMLDocumentBuilder,
  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 Saturday, February 22 at 9 AM.

Implementation stub available in the homework repository.

Before starting this assignment

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 with HTML, CSS, and JS. 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

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 script that you will produce in this assignment, map-builder.js.

TODO: Go ahead and create this file with the familiar IIFE skeleton:

// map-builder.js
(function () {

})();

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.

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.

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). Again, we're using the pseudo-class more as a method of organization and encapsulation rather than for more "traditional" object-oriented purposes.

TODO: Create an anonymous function at the bottom of map-builder.js and bind it to document ready invocation using jQuery's convenient syntax as illustrated at the bottom of the browser events notes. Invoke the MapBuilder constructor in this function appropriately so that it runs after DOM-load.


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="selected swatch grass"></li>

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

  1. Ensures only one swatch is ever 'selected' (check out jQuery's .removeClass and .addClass 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
    

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. Otherwise, you'll have to go in and make some CSS edits.

In jQuery, it's possible to generate a DOM element 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 (and related ones) 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
});

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.

A playable character (optional)

If you're feeling adventurous like Ash Ketchum, you can go on to implement a playable character within this map once it's built. There are image assets available for you to leverage for this task in the folders hw3/lib/img/tilesets and hw3/lib/images/sprites (image tiles for Pikachu, Ash, etc).

Implement a separate class called Player (like we did in class) that generates a player on the map when a 'Play' button is clicked on the page. This player can move around with the arrow keys. They should be able to move over terrain but not through obstructions (look at the images folder or main.css to see which tiles are terrain and which are obstructions).


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: Backbone.js Starter

Due Tuesday, Oct. 21 at 9 AM.

Implementation stub available in the homework repository.

Before starting this assignment

Review the lecture 5 notes on functional programming, Underscore.js, MVC, and Backbone.js. It's probably also helpful to reference the Fundamentals and Backbone Basics sections of the Backbone Fundamentals book.

Refactoring map builder

map-builder

Ah, our old friend, the Pokémon map builder from hw3. In this assignment, we're going to refactor the jQuery-centric code from the last project into a Backbone application and also add some functionality. The new app will still have the restriction that maps are lost on a page refresh (no persistence), but the code will be more structured and modular.

Here are the essential parts to map builder v2, broken down into MVC components:

Note: Backbone Collections aren't really necessary for the Map Builder. You might consider the map layout as a collection of tiles, but they really don't have much relationship to each other as modeled objects, and so we'll just keep track of tiles as a 2D array in the Builder model.


Backbone intro

This time around, there's some starter code provided for you in map-builder.js. You'll also notice some more map tiles to play with in the swatches palette!

Take a look the bottom of the JS file, where the Backbone application is invoked within jQuery's document-ready function.

var dimensions = {
      width: 30,
      height: 10
},
    builder = new Builder(dimensions);

When builder is created, the first argument it receives gets parsed as an attributes hash. Backbone.Model internally sets all the (key, value) pairs as attributes on the model and fills in any defaults if they were defined in Builder. However, attributes is an internal storage mechanism for models that you shouldn't touch externally. It's better to use the getter / setter methods:

builder.get('width');     // 30
builder.get('height');    // 10

builder.set('width', 20);                   // fires 'change:width' event
builder.set({ width: 20, height: 10 });     // no events fired
builder.set('width', 30, { silent: true }); // no events fired

Models in Backbone communicate asynchronously through events. All Backbone.Models and their extensions have the Backbone.Events mixin. This means that you can listen for events on the model like so:

builder.on('change:width', function (builder) {
  builder.changing;      // true
  bulder.changed;        // { width: 20 }
});

See Backbone.Model documentation for more information.

To create a BuilderView tied to this model, we have following:

var builderView = new BuilderView({ model: builder });

Assuming that BuilderView internally manages its placement on the DOM, we can invoke its render function to make it appear on the page:

builderView.render();

Similarly for the Player model and PlayerView, we have the following code:

var player = new Player({
      builderModel: builder,
      dimensions: dimensions
})
  , playerView = new PlayerView({ model: player });

playerView.setElement(builderView.$el);
playerView.render();

Player is passed a reference to the builder so that it can know about the location of tiles on the map. Also, PlayerView has its containing element set to builderView's element so that it knows where to place itself in the DOM upon render.


Now that we know how the top-level application is structured and how our Backbone Models and Views are used, we can go ahead and fill in their implementations. But first, a few utility methods will be useful...

Utility functions

TODO:


We're now going to create custom model and view classes. This is done with the extend syntax:

var Builder = Backbone.Model.extend({
  ...
});

Each method defined in this custom class has its context bound to the newly constructed instance of the class. See the Backbone docs for all the possible customization properties / methods.

Models

Builder

TODO:

Player

Next, the Player model. It keeps track of where the player is on the canvas and its orientation. It also knows about the canvas dimensions (they are passed in on instantiation of player). Look at the provided defaults object and the arguments passed to new Player(...) to see what this model's attributes look like.

TODO:


Views

Backbone.View life cycle

To review: when a Backbone View is created, a few things happen:

  1. An element is created for the view. It can be customized by supplying options (like tagName, className, el, etc.) to Backbone.View.extend, but otherwise it's just a plain <div>. This element is not placed in the DOM yet; it only exists in your script.

  2. Event handlers defined by the events hash are bound to the view's element using a mechanism similar to jQuery's delegate.

  3. The view's initialize method, if it exists, is called.

Technically, rendering a view is optional. You could set a view's element to something that already exists in the DOM and the view will attach events accordingly. But if you have any nontrivial rendering logic to accomplish (like compiling a template), you'll define and invoke a render method.

Keep in mind that all of the custom view's defined properties are computed upon script execution. The document may or may not be "ready" by this time, so we'll ensure that we only call things like jQuery selectors (to grab templates on the page) once the view is actually created and initialized.

Templating

Refer to the MVC notes for an introduction to client-side templating. Once you have a template function (generated by _.template), you compile it with a view context. A context is simply a JS object representing values available to the template. See the Underscore docs for more info.

PaletteView

This view will do the work of the _setupPalette function from HW3.

TODO:

BuilderView

There is some starter code here which gets template functions for you. We don't really need a template for this view since it's all static markup. We do, however, need a template for the canvas "sub-view".

TODO:

PlayerView

This view will display Pikachu on the map canvas and update its position in response to user interaction (keypresses). We'll use a library called Mousetrap so that we have a simple API for binding key-based event handlers.

TODO:


That's it! At this point you should have an editable canvas of map tiles and a playable Pikachu character that moves around with WASD controls. I encourage you to thoroughly explore the Backbone docs to get a handle on how the library is used to build a simple app like this.

player-moving

Notes

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: Playing Pokémon

Due Fri, Oct 31 at 9 AM.

Implementation stub available in the homework repository.

This assignment will extend your Backbone.js experience to include topics we've recently covered in class:

Here's a general outline of the new functionality we're adding to the Pokémon game:

Before starting this assignment

Review the documentation on Backbone's Router and these code samples from lecture.

Application Structure

Our code is starting to get a little complicated and disparate for one file, so we're going to split it up and move all scripts to a app/ directory. Take a look at index.html. To share information between our scripts, we'll use a global object called POKEMON, which is declared and initialized in index.html before all of our application scripts. (There are better ways to do this, including dependency injection and requireJS modules, but for now we'll stick to basic script loading.)

Builder, PaletteView, and BuilderView functionality (up to hw4) is provided for you already in app/builder.js. One difference in rendering you might notice from hw4 is that a new <div> is created for BuilderView rather than attaching to the existing #map element on the page (explicitly setting the view's el). This is necessary for removing / re-rendering of views that occurs when switching game modes.

Player and PlayerView are left blank in app/player.js -- you should copy over your code from hw4 to use as a implementation stub. Try to update your code to use the global variables from POKEMON.globals. In addition, for the same reason as above for BuilderView, if you were explicitly setting PlayerView's element in your implementation, you'll likely need to edit your code to instead rely on Backbone's internally created el.

Note: if you are lacking working hw4 code for this portion, please email the TAs.

app/pokemon.js contains code to initialize models and start off a custom Backbone router.


Separate Game Modes

If you open up index.html, you'll notice a few controls available at the top right of the page: Build, Play, and Reset. We'll use url routes to invoke all these actions, so these controls simply point to different routes (#build, #play, #reset).

TODO: Create a 'build' route in pokemon.js. Its handler should construct a new BuilderView (but only if it doesn't exist already). render the view and call delegateEvents to re-bind event handlers. Remember that you'll need to wait until DOM ready to construct any views. All the necessary models you need are already available here.

TODO: Create a 'play' route whose handler constructs a new MapView and a new PlayerView if they don't exist. Render these views and delegate their events. Be sure to set the playerView's element to that of the mapView (this is the same as in hw4).

Note: Reference the hw4 implementation stub if you need help constructing these views.

TODO: Revisit both of these route handlers to ensure that the old view is destroyed when the game mode is switched. For example, when the build route is hit, you should undo the work of render and delegateEvents by calling undelegateEvents and remove (in that order) on both mapView and playerView.

game-modes

Although MapView isn't implemented completely, you should be able to switch game modes at this point between the map builder and an empty map playing view (using the controls at the top right).


Visible Map Area

In "play" mode, we're only going to expose a small portion of the map (of width & height equal to POKEMON.globals.MAP_SIZE) to emulate a handheld-like game experience.

build-play

The coordinates of the top-left corner of the visible map area will be encoded in the url; the route will now look something like the following (it represents row 5 and column 8):

play/r5/c8

Hint: take a look at the lecture 5 code to see how to define route parameters like this.

TODO: Once you define this route and parse the row and column in its handler, you'll need to decide how to make MapView only show the relevant slice of the map (Hint: you may want to make MapView's model point to the Builder model when that view is constructed). There are edge cases that need to be handled as well, as you can see in the above demonstration.

TODO: When the player reaches the edge of the visible map, make the application re-route to shift the visible map area (this will force your views to be rebuilt). For example, if the current route is play/r0/c0 and Pikachu just tried to move to the tile at row 0, col 10, you might want to shift the map to play/r0/c2. You should do this using Backbone's router.navigate (docs). Be sure to pass in the appropriate options for trigger and replace to this function.

This is the tricky part of the whole assignment, especially the edge cases. One strategy is to let the Player model know about the visible map area and have it re-route under certain conditions in its moving functions.


TODO: Go back to the play route handler you defined earlier. Make it re-route the application (using navigate) to a map slice centered on the player's current position (of the form play/r0/c0).

Extra Credit (3pts): create a minimap next to the regular map that shows you where you are in relation to the whole map (only in "play" mode).


Map Persistence

To persist the state of the map tiles, we'll use remarkably simple localStorage API. This is an HTML5 feature that stores information in the browser to use between sessions. It is a global object available as window.localStorage that has special getter / setter semantics, but a simple syntax that works just like objects.

localStorage['foo'] = 'bar';

console.log(localStorage['foo']); // 'bar';

The one disadvantage is that it can only store strings, so to store any array-like or object-like data, you'll need to use the built-in JSON.stringify (and JSON.parse to complement this when retrieving data).

TODO: Edit the definition of the Builder model to save the current map tiles to localStorage whenever the tiles change using Model events. Use the global constant POKEMON.globals.LOCALSTORAGE_KEY to index into the localStorage object. In addition, edit this model's initialize method to parse stored tiles from localStorage if they are available (or fall back to generating new ones).

Now when you reload your web page, the map tiles should stay in place! Magic!

map-persistence

TODO: Add a method to Builder that resets all the map tiles to the default swatch (essentially erases the whole map). Then, add a Backbone route handler for the route 'reset' that invokes this function. If you implement this correctly, the map should reset when you click "Reset" in the controls on the top right of the page.


Notes

If you find yourself spending an inordinate amount of time on this assignment, please seek help on Piazza, in office hours, or by appointment.

Your final output should function like the demo gif under the "Visible Map Area" heading above.

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: Autocomplete

Due Wednesday, November 19 at 9 AM.

Implementation stub available in the homework repository.

This assignment will tie up what you've learned about classes, promises, Backbone, and jQuery into a single widget: an autocompleter (much like the one that is used for Penn Course Review).

Before starting the assignment

Review the docs for Backbone Views, the Bluebird promise API, and jQuery's .get function.

Running the code

You may need the http-server npm package in order to run this code because of protocol restrictions (in some browsers, you cannot make an AJAX HTTP request from a filesystem HTML page). Install it with npm install -g http-server and run it from you project directory with http-server ./. Navigate to http://localhost:8080/ to run your code.

Part 1: Tries (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.

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

addWord(word, trieNode) =
  1. If word is the empty string:
    1. Add a 'true' node with key "end" (in our case set, trieNode["end"] =  true)
    2. Return
  2. If trieNode does not have a child labeled with the first character of `word`:
    1. Add a child 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 addWord(newWord, newNode)

Call this algorithm starting with the input word and the root of the trie (this.data). 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.

Part 2: AJAX and Promises (promisifiedAjaxSimple.js, promisifiedAjax.js)

In this section we are going to use promises to grab dictionary data from a public server. Just like Penn Course Review, the words we are going to work with will be the names of classes at Penn.

TODO: Implement ajaxGet.

This function takes in a URL that points to some JSON and returns a promise that resolves to the JSON data. Use Bluebird; this should be a one-liner.

TODO: Implement trieFromURL.

This function takes in a URL that points to some JSON and returns a promise that resolves to a trie formed from the JSON data. Use ajaxGet; this should be <5 lines.

TODO: Implement multiGet.

This method takes in a URL that returns an array of more documents to get, and a base URL for those documents. Go to cis197.herokuapp.com/departmentURLs.json to see an example. The method returns a promise for an array that represents all the data from all the documents (do not include the original document from the URL in this array).

See stub for step-by-step detailed instructions.

HINT: Use Promise.all

TODO: Implement trieFromDataPromise.

This method takes in a promise for data (a string array)and returns a trie constructed from that data. Use multiGet; this should be <5 lines. There is a test case to make sure this method is working correctly.

Part 3: AppView

Now that we can make tries from remotely-hosted JSON dictionaries, all that's left is to implement the autocompleter in HTML code. Luckily, we've taken care of the CSS and the templating for you. All you need to do is implement a few missing methods on appView.

TODO: Define events in AppView.

The events app should do a search (the next method to do) every time a key is pressed. You will find the Backbone Views documentation useful here!

TODO: Define a search in AppView.

This function should be called every time a keypress is detected. It will use the trie promise passed in during view initialization to give back suggestions for autocompletion and then call render with the suggestions array as an argument.

When you're all done it should look something like this:

Demo

You can comment/uncomment the two different implementations (one JSON file vs. multiple JSON files) in main.js (this demo uses the one-file implementation);

Notes

As a general rule, please remove "TODO" comment blocks after you implement that portion of the assignment.

Extra Credit (3pts): Implement "ExtraCreditTrie" (in ExtraCreditTrie.js). This trie functions very similarly to the simple trie that we implemented in part 1, except that it is partially case-insensitive. That is, the trie distinguishes between different capitalizations when adding words; but when getting suggestions, it returns all matches, regardless of case, in the original case they were added.

To give a concrete example: you could add "PennApps", "Upenn", and "Pennapps" to an ExtraCreditTrie. Then running getSuggestions('penn') would return the array ["PennApps", "Pennapps"].

Extra Credit (2pts): Modify ExtraCreditTrie so that it can start matching at any point in the a word. For instance, you could add "PennApps", "Upenn", and "pineapple" to an ExtraCreditTrie. Then running getSuggestions('app') would return the array ["PennApps", "pineapple"].

Homework 7: Product Hunt Client in React.js

Due Wednesday, December 3 at 9 AM.

Implementation stub available in the homework repository.

This assignment will show you the power and awesomeness of React.js. You will be implementing an entirely client side Product Hunt reader. Product Hunt is a link aggregator much like Hacker News, Reddit, and Twitter where products (read: apps) are posted and voted on. We'll be writing a Product Hunt client that lets us see today's top posts and view any of the posts' top level comments.

For this assignment, I've provided two API endpoints. One fetches the post data and can be found here: http://ph-api.herokuapp.com/posts. The other fetches comment data and can be found here: http://ph-api.herokuapp.com/comments?post_id=11287. Notice that the second endpoint requires a post_id parameter to be passed in the URL. The post_id parameter specifies which post's comments you are looking for.

Our client is going to display the posts we get from the posts API endpoint and let the user select which post he wants to read the comments for. There can only be one selected post at a time which means only one post's comments can be shown at a time.

The final product should look like this:

Demo

Running the code

As in the last homework, you may need the http-server npm package in order to run this code because of protocol restrictions (in some browsers, you cannot make an AJAX HTTP request from a filesystem HTML page). Install it with npm install -g http-server and run it from you project directory with http-server ./. Navigate to http://localhost:8080/ to run your code.

Part 1: Static Site

Complete the render function of the App component so that it renders some Post components. Feel free (and I encourage you) to pass in dummy data into the Post components to see just how they're rendered. You can grab post data from the posts API endpoint.

Part 2: Figuring out State

You are going to need to figure out what state your application needs to keep track of. Again, React.js uses state and the setState function to figure out when to re-render certain views. To help you figure out what state you need, try to think of any variables that will be changing. Those variables will comprise your state object.

Once you think you've got it, modify the getInitialState function of the App component to return the correct initial state. You can think of the initial state of your entire application as the state before the first AJAX call is executed.

Part 3: Manipulating State

Once you're setting the correct initial state, finish the componentDidMount function of the App component to load the post data from the posts API endpoint and set the state appropriately (use setState to set the state). Again, componentDidMount is a function that's executed once a component has been rendered.

Now, you should see that as soon as the AJAX request loads, the state should be set and the Post components should re-render (make sure to hook up the Post components with real data).

Part 4: Manipulating State with Comments

Keep in mind that you only need to show the currently selected post's comments.

Once you have the post data, attach a click handler to the <li> component within Post. Every time a post is clicked, your application should fetch the comments for the clicked post from the server and render the Comments component directly under the clicked post (again, think of state). Also, as in class and the React tutorial, make sure to keep in mind that only the App component can change the state so make sure to pass down a callback from App to Post through the properties.

As a finishing touch, use the same click handler from the <li> component to enable toggling comments so you can hide the currently selected post's comments by clicking on the post a second time.

Notes

Extra Credit (2pts): Implement a cache so that once you fetch a post's comments once from the server you don't have to fetch that same post's comments directly from the server again.

Final Project Proposal

Due Wednesday, December 3 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.

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 December 3rd 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.


Proposal 1-on-1s

After you've submitted your proposal, please email me your availabilities over the next week (from Dec 7th to Dec 10th) so we can arrange a meeting to talk about your proposal.


Project Demos

The demo day time slot is December 16th from 6 - 8pm in Levine 307. We'll 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 your final code (due on demo day). Please also submit a short README.md file that tells me how to run your code / app.


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