diff --git a/Chapter01/01_web.js b/Chapter01/01_web.js
new file mode 100644
index 0000000..872e76e
--- /dev/null
+++ b/Chapter01/01_web.js
@@ -0,0 +1,14 @@
+var http = require("http");
+
+function process_request(req, res) {
+ var body = 'Thanks for calling!\n';
+ var content_length = body.length;
+ res.writeHead(200, {
+ 'Content-Length': content_length,
+ 'Content-Type': 'text/plain'
+ });
+ res.end(body);
+}
+
+var s = http.createServer(process_request);
+s.listen(8080);
diff --git a/Chapter01/02_debug.js b/Chapter01/02_debug.js
new file mode 100644
index 0000000..90b3bc6
--- /dev/null
+++ b/Chapter01/02_debug.js
@@ -0,0 +1,18 @@
+
+var http = require("http");
+
+var s = http.createServer(function (req, res) {
+ var body = 'Thanks for calling!\n';
+ var content_length = body.lengtth;
+ res.writeHead(200, {
+ 'Content-Length': content_length,
+ 'Content-Type': 'text/plain'
+ });
+ res.end(body);
+});
+
+/**
+ * Now run the server, listening on port 8080
+ */
+s.listen(8080);
+
diff --git a/Chapter01/debugging.js b/Chapter01/debugging.js
new file mode 100644
index 0000000..9a00864
--- /dev/null
+++ b/Chapter01/debugging.js
@@ -0,0 +1,14 @@
+var http = require("http");
+
+function process_request(req, res) {
+ var body = 'Thanks for calling!\n';
+ var content_length = body.lenggth;
+ res.writeHead(200, {
+ 'Content-Length': content_length,
+ 'Content-Type': 'text/plain'
+ });
+ res.end(body);
+}
+
+var s = http.createServer(process_request);
+s.listen(8080);
diff --git a/Chapter02/arguments.js b/Chapter02/arguments.js
new file mode 100644
index 0000000..9ec00ff
--- /dev/null
+++ b/Chapter02/arguments.js
@@ -0,0 +1,76 @@
+
+
+
+
+
+function Shape () {
+}
+
+Shape.prototype.X = 0;
+Shape.prototype.Y = 0;
+
+Shape.prototype.move = function (x, y) {
+ this.X = x;
+ this.Y = y;
+}
+Shape.prototype.distance_from_origin = function () {
+ return Math.sqrt(this.X*this.X + this.Y*this.Y);
+}
+Shape.prototype.area = function () {
+ throw new Error("I'm not a real shape yet");
+}
+
+var s = new Shape();
+s.move(10, 10);
+console.log(s.distance_from_origin());
+
+
+function Square() {
+}
+
+Square.prototype = new Shape();
+Square.prototype.__proto__ = Shape.prototype;
+Square.prototype.Width = 0;
+
+Square.prototype.area = function () {
+ return this.Width * this.Width;
+}
+
+var sq = new Square();
+sq.move(-5, -5);
+sq.Width = 5;
+console.log(sq.area());
+console.log(sq.distance_from_origin());
+
+
+function Rectangle () {
+}
+
+Rectangle.prototype = new Square();
+Rectangle.prototype.__proto__ = Square.prototype;
+Rectangle.prototype.Height = 0;
+
+Rectangle.prototype.area = function () {
+ return this.Width * this.Height;
+}
+
+
+var re = new Rectangle();
+re.move(25, 25);
+re.Width = 10;
+re.Height = 5;
+console.log(re.area());
+console.log(re.distance_from_origin());
+
+
+console.log(typeof s);
+console.log(typeof sq);
+console.log(typeof re);
+
+console.log(sq instanceof Square);
+console.log(sq instanceof Shape);
+console.log(sq instanceof Rectangle);
+console.log(re instanceof Rectangle);
+console.log(sq instanceof Square);
+console.log(sq instanceof Shape);
+console.log(sq instanceof Date);
diff --git a/Chapter02/arrays.js b/Chapter02/arrays.js
new file mode 100644
index 0000000..149fd55
--- /dev/null
+++ b/Chapter02/arrays.js
@@ -0,0 +1,100 @@
+
+var car1 = [];
+var car2 = new Array();
+var car3 = new Array(10);
+var car4 = new Array(4, 34, 6, 8, 525, 8693, 281, 88, 28, 95, 346);
+
+
+// creating
+var arr1 = [];
+// set values
+
+for (var i = 0; i < 10; i++) {
+ arr1[i] = i;
+}
+
+// fills in undefined
+arr1.length = 20;
+arr1[20] = "new value";
+
+console.log(arr1.length);
+console.log(arr1[0]);
+console.log(arr1);
+
+
+// set values with string index
+var arr2 = [];
+
+arr2["cat"] = "meow";
+arr2["dog"] = "woof";
+
+console.log(arr2.length);
+console.log(arr2[0]);
+console.log(arr2);
+
+
+
+// mixed indexes (bad idea)
+var arr3 = [];
+
+arr3[2] = 2;
+arr3[3] = 3;
+arr3["horse"] = "neigh";
+arr3["狗"] = "王";
+
+
+console.log(arr3.length);
+console.log(arr3[0]);
+console.log(arr3);
+
+
+
+// multi-dimensional
+//var arr4 = [][]; not ok
+//var arr5 = [3][3]; // not ok
+
+// to create a 3x3
+
+var tx3A = new Array(new Array(3), new Array(3), new Array(3));
+var tx3B = [];
+
+for (var i = 0; i < 3; i++) {
+ tx3B[i] = new Array(3);
+}
+
+
+console.log(tx3A);
+console.log(tx3B);
+
+
+
+// why use arrays when objects contain much of the same functionality: V8 optmises heavily, extra operations slice(), push pop, shift, unshift
+
+
+// key operations push pop
+// shift unshift
+
+
+var random = new Array(1, 342, 53, 38, 85958, 3584934, 8459, 2, 69, 1396, 146, 194);
+
+
+// print squares
+random.forEach(function (element, index, array) {
+ console.log(element + "^2 = " + element * element);
+});
+
+
+var squares = random.map(function (element, index, array) {
+ return element * element;
+});
+console.log(squares);
+
+var evens_only = random.filter(function (element, index, array) {
+ return (element % 2) == 0;
+});
+console.log(evens_only);
+
+
+
+console.log(random.join(", "));
+
diff --git a/Chapter02/global.js b/Chapter02/global.js
new file mode 100644
index 0000000..0394c65
--- /dev/null
+++ b/Chapter02/global.js
@@ -0,0 +1,13 @@
+
+
+
+function printit(var_name) {
+ console.log(global[var_name]);
+}
+
+global.fish = "swordfish";
+global.pet = "cat";
+
+printit("fish");
+printit("pet");
+printit("fruit");
diff --git a/Chapter02/objects.js b/Chapter02/objects.js
new file mode 100644
index 0000000..c9db873
--- /dev/null
+++ b/Chapter02/objects.js
@@ -0,0 +1,5 @@
+
+
+var obj1 = {};
+var obj2 = new Object();
+
diff --git a/Chapter02/test.js b/Chapter02/test.js
new file mode 100644
index 0000000..4110d0e
--- /dev/null
+++ b/Chapter02/test.js
@@ -0,0 +1,9 @@
+
+
+var arr = [];
+
+arr[0] = 1;
+arr[1] = 2;
+arr["cat"] = 'meow';
+
+console.log(arr.length);
\ No newline at end of file
diff --git a/Chapter02/trycatch.js b/Chapter02/trycatch.js
new file mode 100644
index 0000000..8050b05
--- /dev/null
+++ b/Chapter02/trycatch.js
@@ -0,0 +1,16 @@
+
+
+function uhoh () {
+ throw new Error("Something bad happened!");
+}
+
+try {
+ uhoh();
+} catch (e) {
+ console.log("I caught an error: " + e.message);
+}
+
+console.log("program is still running");
+
+
+
diff --git a/Chapter02/types.txt b/Chapter02/types.txt
new file mode 100644
index 0000000..77a1fab
--- /dev/null
+++ b/Chapter02/types.txt
@@ -0,0 +1,67 @@
+BASIC TYPES:
+
+
+there are several core types:
+
+numbers, strings, booleans, functions, objects
+
+null, undefined are actually both types, although just particular instances of objects, and arrays a special case of objects.
+
+if we have a variable in javascript, type is
+
+typeof x
+
+note arrays will return 'object'. see section on arrays.
+
+
+
+NUMBERS
+
+- 64bit double precision floating point numbers -- there are no "integer" types in javascript.
+- that means you have 53 bits of precision.
+- if you are going to represent 64bits in javascript, use strings or separate library
+- if you're doing math ops on floating point numbers, be careful
+ - 0.1 + 0.2 != 0.3
+- integer numbers < 53 bits will be great because they can be exactly rep'd
+- dividing by zero gives you (-)Infinity
+- you can convert strings to numbers with parseInt or parseFloat
+ - if they fail, return NaN
+ - isNaN
+- can test for a "valid" number with isFinite()
+
+
+
+BOOLEANS:
+
+- can have the value true or false
+- you can force things to boolean with the Booelan(XXX) function, but rarely necessary
+
+
+STRINGS:
+
+- strings are sequences of unicode characters
+- great for most characters around the world
+- no separate character data type -- can just use 1-char strings
+- to get the length, jut use .length
+var s = "my string";
+console.log(s.length);
+// or
+console.log("my string".length);
+
+many interesting functions on strings:
+
+int str.indexOf("there");
+string "hello there".slice(5, 6) == " " // true
+string "hello there".substr(5, 1) == " " // true
+array "1,2,3,4,5".split(",")
+
+
+
+null is a special value indicates non-value
+undefined means no such thing or no value set yet
+
+var x;
+-> undefined
+console.log(x);
+-> undefined
+
diff --git a/Chapter03/01_php_example.php b/Chapter03/01_php_example.php
new file mode 100644
index 0000000..308709c
--- /dev/null
+++ b/Chapter03/01_php_example.php
@@ -0,0 +1,7 @@
+$file = fopen('info.txt', 'r');
+// wait until file is open
+
+$contents = fread($file, 100000);
+// wait until contents are read
+
+// do something with those contents
diff --git a/Chapter03/02_settimeout.js b/Chapter03/02_settimeout.js
new file mode 100644
index 0000000..a1264a6
--- /dev/null
+++ b/Chapter03/02_settimeout.js
@@ -0,0 +1,7 @@
+
+setTimeout(function () {
+ console.log("I've done my work!");
+}, 2000);
+
+
+console.log("I'm waiting for all my work to finish.");
diff --git a/Chapter03/03_async_bad.js b/Chapter03/03_async_bad.js
new file mode 100644
index 0000000..c9857b1
--- /dev/null
+++ b/Chapter03/03_async_bad.js
@@ -0,0 +1,20 @@
+var fs = require('fs');
+
+var file;
+var buf = new Buffer(100000);
+
+fs.open(
+ 'info.txt', 'r',
+ function (err, handle) {
+ file = handle;
+ }
+);
+
+fs.read(
+ file, buf, 0, 100000, null,
+ function (err, length) {
+ console.log(buf.toString());
+ fs.close(file, function () { /* don't care */ });
+ }
+);
+
diff --git a/Chapter03/04_async_good.js b/Chapter03/04_async_good.js
new file mode 100644
index 0000000..25ef76a
--- /dev/null
+++ b/Chapter03/04_async_good.js
@@ -0,0 +1,16 @@
+
+var fs = require('fs');
+
+fs.open(
+ 'info.txt', 'r',
+ function (err, handle) {
+ var buf = new Buffer(100000);
+ fs.read(
+ handle, buf, 0, 100000, null,
+ function (err, length) {
+ console.log(buf.toString('utf8', 0, length));
+ fs.close(handle, function () { /* don't care */ });
+ }
+ );
+ }
+);
diff --git a/Chapter03/05_async_with_error_handling.js b/Chapter03/05_async_with_error_handling.js
new file mode 100644
index 0000000..1a4d8be
--- /dev/null
+++ b/Chapter03/05_async_with_error_handling.js
@@ -0,0 +1,27 @@
+
+
+var fs = require('fs');
+
+fs.open(
+ 'info.txt', 'r',
+ function (err, handle) {
+ if (err) {
+ console.log("ERROR: " + err.code + " (" + err.message + ")");
+ return;
+ }
+ var buf = new Buffer(100000);
+ fs.read(
+ handle, buf, 0, 100000, null,
+ function (err, length) {
+ if (err) {
+ console.log("ERROR: " + err.code
+ + " (" + err.message + ")");
+ return;
+ }
+ console.log(buf.toString('utf8', 0, length));
+ fs.close(handle, function () { /* don't care */ });
+ }
+ );
+ }
+);
+
diff --git a/Chapter03/06_errors_async.js b/Chapter03/06_errors_async.js
new file mode 100644
index 0000000..8f60bdc
--- /dev/null
+++ b/Chapter03/06_errors_async.js
@@ -0,0 +1,11 @@
+
+
+try {
+ setTimeout(function () {
+ throw new Error("Uh oh, something bad!");
+ }, 2000);
+} catch (e) {
+ console.log("I caught the error: " + e.message);
+}
+
+
diff --git a/Chapter03/07_this_self_error.js b/Chapter03/07_this_self_error.js
new file mode 100644
index 0000000..d829012
--- /dev/null
+++ b/Chapter03/07_this_self_error.js
@@ -0,0 +1,44 @@
+
+var fs = require('fs');
+
+function FileObject () {
+
+ this.filename = '';
+
+ this.file_exists = function (callback) {
+ if (!this.filename) {
+ var e = new Error("invalid_filename");
+ e.description = "You need to provide a valid filename";
+ callback(e);
+ return;
+ }
+
+ console.log("About to open: " + this.filename);
+ fs.open(this.filename, 'r', function (err, handle) {
+ if (err) {
+ console.log("Can't open: " + this.filename);
+ callback(null, false);
+ return;
+ }
+
+ console.log("can open: " + this.filename);
+ fs.close(handle, function () { });
+ callback(null, true);
+ });
+ };
+}
+
+var fo = new FileObject();
+fo.filename = "file_that_does_not_exist";
+
+fo.file_exists(function (err, results) {
+ if (err) {
+ console.log("WAT: " + JSON.stringify(err));
+ return;
+ }
+
+ console.log(results ? "file exists!!!" : "bummer!");
+});
+
+
+
diff --git a/Chapter03/08_this_self_fixed.js b/Chapter03/08_this_self_fixed.js
new file mode 100644
index 0000000..c70dafd
--- /dev/null
+++ b/Chapter03/08_this_self_fixed.js
@@ -0,0 +1,45 @@
+
+var fs = require('fs');
+
+function FileObject () {
+
+ this.filename = '';
+
+ this.file_exists = function (callback) {
+ var self = this;
+
+ if (!this.filename) {
+ var e = new Error("invalid_filename");
+ e.description = "You need to provide a valid filename";
+ callback(e);
+ return;
+ }
+
+ console.log("About to open: " + self.filename);
+ fs.open(this.filename, 'r', function (err, handle) {
+ if (err) {
+ console.log("Can't open: " + self.filename);
+ callback(null, false);
+ return;
+ }
+
+ fs.close(handle, function () { });
+ callback(null, true);
+ });
+ };
+}
+
+var fo = new FileObject();
+fo.filename = "file_that_does_not_exist";
+
+fo.file_exists(function (err, results) {
+ if (err) {
+ console.log("Aw, bummer: " + JSON.stringify(err));
+ return;
+ }
+
+ console.log("file exists!!!");
+});
+
+
+
diff --git a/Chapter03/09_expensive.js b/Chapter03/09_expensive.js
new file mode 100644
index 0000000..1518366
--- /dev/null
+++ b/Chapter03/09_expensive.js
@@ -0,0 +1,14 @@
+
+
+function compute_intersection(arr1, arr2) {
+ var results = [];
+ for (var i = 0 ; i < arr1.length; i++) {
+ for (var j = 0; j < arr2.length; j++) {
+ if (arr2[j] == arr1[i]) {
+ results[results.length] = arr1[j];
+ break;
+ }
+ }
+ }
+}
+
diff --git a/Chapter03/10_expensive_nextTick.js b/Chapter03/10_expensive_nextTick.js
new file mode 100644
index 0000000..05b5fce
--- /dev/null
+++ b/Chapter03/10_expensive_nextTick.js
@@ -0,0 +1,52 @@
+
+function compute_intersection(arr1, arr2, callback) {
+
+ var bigger = arr1.length > arr2.length ? arr1 : arr2;
+ var smaller = bigger == arr1 ? arr2 : arr1;
+ var biglen = bigger.length;
+ var smlen = smaller.length;
+
+ var sidx = 0;
+ var size = 10; // 100 at a time, can adjust!
+ var results = [];
+
+ function sub_compute_intersection() {
+ for (var i = sidx; i < (sidx + size) && i < biglen; i++) {
+ for (var j = 0; j < smlen; j++) {
+ if (bigger[i] == smaller[j]) {
+ results.push(smaller[j]);
+ break;
+ }
+ }
+ }
+
+ if (i >= biglen) {
+ callback(null, results);
+ } else {
+ sidx += size;
+ process.nextTick(sub_compute_intersection);
+ }
+ }
+
+ sub_compute_intersection();
+}
+
+
+
+var a1 = [ 3476, 2457, 7547, 34523, 3, 6, 7,2, 77, 8, 2345,
+ 7623457, 2347, 23572457, 237457, 234869, 237,
+ 24572457524] ;
+var a2 = [ 3476, 75347547, 2457634563, 56763472, 34574, 2347,
+ 7, 34652364 , 13461346, 572346, 23723457234, 237,
+ 234, 24352345, 537, 2345235, 2345675, 34534,
+ 7582768, 284835, 8553577, 2577257,545634, 457247247,
+ 2345 ];
+
+compute_intersection(a1, a2, function (err, results) {
+ if (err) {
+ console.log(err);
+ } else {
+ console.log(results);
+ }
+});
+
diff --git a/Chapter03/11_sync_fileget.js b/Chapter03/11_sync_fileget.js
new file mode 100644
index 0000000..4af0eff
--- /dev/null
+++ b/Chapter03/11_sync_fileget.js
@@ -0,0 +1,8 @@
+
+var fs = require('fs');
+
+var handle = fs.openSync('info.txt', 'r');
+var buf = new Buffer(100000);
+var read = fs.readSync(handle, buf, 0, 10000, null);
+console.log(buf.toString('utf8', 0, read));
+fs.closeSync(handle);
\ No newline at end of file
diff --git a/Chapter03/info.txt b/Chapter03/info.txt
new file mode 100644
index 0000000..8bd6648
--- /dev/null
+++ b/Chapter03/info.txt
@@ -0,0 +1 @@
+asdf
diff --git a/Chapter04/01_simple_server.js b/Chapter04/01_simple_server.js
new file mode 100644
index 0000000..5511a3e
--- /dev/null
+++ b/Chapter04/01_simple_server.js
@@ -0,0 +1,14 @@
+
+var http = require('http');
+
+function handle_incoming_request(req, res) {
+ console.log("INCOMING REQUEST: " + req.method + " " + req.url);
+ res.writeHead(200, { "Content-Type" : "application/json" });
+ res.end(JSON.stringify( { error: null }) + "\n");
+}
+
+
+var s = http.createServer(handle_incoming_request);
+
+s.listen(8080);
+
diff --git a/Chapter04/02_load_albums.js b/Chapter04/02_load_albums.js
new file mode 100644
index 0000000..94466a5
--- /dev/null
+++ b/Chapter04/02_load_albums.js
@@ -0,0 +1,39 @@
+
+var http = require('http'),
+ fs = require('fs');
+
+function load_album_list(callback) {
+ // we will just assume that any directory in our 'albums'
+ // subfolder is an album.
+ fs.readdir(
+ "albums",
+ function (err, files) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ callback(null, files);
+ }
+ );
+}
+
+function handle_incoming_request(req, res) {
+ console.log("INCOMING REQUEST: " + req.method + " " + req.url);
+ load_album_list(function (err, albums) {
+ if (err) {
+ res.writeHead(500, {"Content-Type": "application/json"});
+ res.end(JSON.stringify(err) + "\n");
+ return;
+ }
+
+ var out = { error: null,
+ data: { albums: albums }};
+ res.writeHead(200, {"Content-Type": "application/json"});
+ res.end(JSON.stringify(out) + "\n");
+ });
+}
+
+var s = http.createServer(handle_incoming_request);
+
+s.listen(8080);
+
diff --git a/Chapter04/03_test_folder.js b/Chapter04/03_test_folder.js
new file mode 100644
index 0000000..298f026
--- /dev/null
+++ b/Chapter04/03_test_folder.js
@@ -0,0 +1,53 @@
+
+var http = require('http'),
+ fs = require('fs');
+
+function load_album_list(callback) {
+ // we will just assume that any directory in our 'albums'
+ // subfolder is an album.
+ fs.readdir(
+ "albums",
+ function (err, files) {
+ if (err) {
+ callback(err);
+ return;
+ }
+
+ var only_dirs = [];
+
+ for (var i = 0; files && i < files.length; i++) {
+ fs.stat(
+ "albums/" + files[i],
+ function(err, stats) {
+ if (stats.isDirectory()) {
+ only_dirs.push(files[i]);
+ }
+ }
+ );
+ }
+
+ callback(null, only_dirs);
+ }
+ );
+}
+
+function handle_incoming_request(req, res) {
+ console.log("INCOMING REQUEST: " + req.method + " " + req.url);
+ load_album_list(function (err, albums) {
+ if (err) {
+ res.writeHead(500, {"Content-Type": "application/json"});
+ res.end(JSON.stringify(err) + "\n");
+ return;
+ }
+
+ var out = { error: null,
+ data: { albums: albums }};
+ res.writeHead(200, {"Content-Type": "application/json"});
+ res.end(JSON.stringify(out) + "\n");
+ });
+}
+
+var s = http.createServer(handle_incoming_request);
+
+s.listen(8080);
+
diff --git a/Chapter04/04_test_folder.js b/Chapter04/04_test_folder.js
new file mode 100644
index 0000000..b849f6b
--- /dev/null
+++ b/Chapter04/04_test_folder.js
@@ -0,0 +1,61 @@
+
+var http = require('http'),
+ fs = require('fs');
+
+function load_album_list(callback) {
+ // we will just assume that any directory in our 'albums'
+ // subfolder is an album.
+ fs.readdir(
+ "albums",
+ function (err, files) {
+ if (err) {
+ callback(err);
+ return;
+ }
+
+ var only_dirs = [];
+
+ (function iterator(index) {
+ if (index == files.length) {
+ callback(null, only_dirs);
+ return;
+ }
+
+ fs.stat(
+ "albums/" + files[index],
+ function (err, stats) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ if (stats.isDirectory()) {
+ only_dirs.push(files[index]);
+ }
+ iterator(index + 1)
+ }
+ );
+ })(0);
+ }
+ );
+}
+
+function handle_incoming_request(req, res) {
+ console.log("INCOMING REQUEST: " + req.method + " " + req.url);
+ load_album_list(function (err, albums) {
+ if (err) {
+ res.writeHead(500, {"Content-Type": "application/json"});
+ res.end(JSON.stringify(err) + "\n");
+ return;
+ }
+
+ var out = { error: null,
+ data: { albums: albums }};
+ res.writeHead(200, {"Content-Type": "application/json"});
+ res.end(JSON.stringify(out) + "\n");
+ });
+}
+
+var s = http.createServer(handle_incoming_request);
+
+s.listen(8080);
+
diff --git a/Chapter04/05_multiple_requests.js b/Chapter04/05_multiple_requests.js
new file mode 100644
index 0000000..5c195d6
--- /dev/null
+++ b/Chapter04/05_multiple_requests.js
@@ -0,0 +1,167 @@
+
+var http = require('http'),
+ fs = require('fs');
+
+
+function load_album_list(callback) {
+ // we will just assume that any directory in our 'albums'
+ // subfolder is an album.
+ fs.readdir(
+ "albums",
+ function (err, files) {
+ if (err) {
+ callback(make_error("file_error", JSON.stringify(err)));
+ return;
+ }
+
+ var only_dirs = [];
+
+ (function iterator(index) {
+ if (index == files.length) {
+ callback(null, only_dirs);
+ return;
+ }
+
+ fs.stat(
+ "albums/" + files[index],
+ function (err, stats) {
+ if (err) {
+ callback(make_error("file_error",
+ JSON.stringify(err)));
+ return;
+ }
+ if (stats.isDirectory()) {
+ var obj = { name: files[index] };
+ only_dirs.push(obj);
+ }
+ iterator(index + 1)
+ }
+ );
+ })(0);
+ }
+ );
+}
+
+function load_album(album_name, callback) {
+ // we will just assume that any directory in our 'albums'
+ // subfolder is an album.
+ fs.readdir(
+ "albums/" + album_name,
+ function (err, files) {
+ if (err) {
+ if (err.code == "ENOENT") {
+ callback(no_such_album());
+ } else {
+ callback(make_error("file_error",
+ JSON.stringify(err)));
+ }
+ return;
+ }
+
+ var only_files = [];
+ var path = "albums/" + album_name + "/";
+
+ (function iterator(index) {
+ if (index == files.length) {
+ var obj = { short_name: album_name,
+ photos: only_files };
+ callback(null, obj);
+ return;
+ }
+
+ fs.stat(
+ path + files[index],
+ function (err, stats) {
+ if (err) {
+ callback(make_error("file_error",
+ JSON.stringify(err)));
+ return;
+ }
+ if (stats.isFile()) {
+ var obj = { filename: files[index],
+ desc: files[index] };
+ only_files.push(obj);
+ }
+ iterator(index + 1)
+ }
+ );
+ })(0);
+ }
+ );
+}
+
+
+function handle_incoming_request(req, res) {
+ console.log("INCOMING REQUEST: " + req.method + " " + req.url);
+ if (req.url == '/albums.json') {
+ handle_list_albums(req, res);
+ } else if (req.url.substr(0, 7) == '/albums'
+ && req.url.substr(req.url.length - 5) == '.json') {
+ handle_get_album(req, res);
+ } else {
+ send_failure(res, 404, invalid_resource());
+ }
+}
+
+function handle_list_albums(req, res) {
+ load_album_list(function (err, albums) {
+ if (err) {
+ send_failure(res, 500, err);
+ return;
+ }
+
+ send_success(res, { albums: albums });
+ });
+}
+
+function handle_get_album(req, res) {
+ // format of request is /albums/album_name.json
+ var album_name = req.url.substr(7, req.url.length - 12);
+ load_album(
+ album_name,
+ function (err, album_contents) {
+ if (err && err.error == "no_such_album") {
+ send_failure(res, 404, err);
+ } else if (err) {
+ send_failure(res, 500, err);
+ } else {
+ send_success(res, { album_data: album_contents });
+ }
+ }
+ );
+}
+
+
+function make_error(err, msg) {
+ var e = new Error(msg);
+ e.code = err;
+ return e;
+}
+
+function send_success(res, data) {
+ res.writeHead(200, {"Content-Type": "application/json"});
+ var output = { error: null, data: data };
+ res.end(JSON.stringify(output) + "\n");
+}
+
+function send_failure(res, code, err) {
+ var code = (err.code) ? err.code : err.name;
+ res.writeHead(code, { "Content-Type" : "application/json" });
+ res.end(JSON.stringify({ error: code, message: err.message }) + "\n");
+}
+
+
+function invalid_resource() {
+ return make_error("invalid_resource",
+ "the requested resource does not exist.");
+}
+
+function no_such_album() {
+ return make_error("no_such_album",
+ "The specified album does not exist");
+}
+
+
+var s = http.createServer(handle_incoming_request);
+s.listen(8080);
+
diff --git a/Chapter04/06_req_res.js b/Chapter04/06_req_res.js
new file mode 100644
index 0000000..48afecd
--- /dev/null
+++ b/Chapter04/06_req_res.js
@@ -0,0 +1,17 @@
+
+var http = require('http');
+
+function handle_incoming_request(req, res) {
+ console.log("---------------------------------------------------");
+ console.log(req.headers);
+ console.log("---------------------------------------------------");
+ console.log(res);
+ console.log("---------------------------------------------------");
+ res.writeHead(200, { "Content-Type" : "application/json" });
+ res.end(JSON.stringify( { error: null }) + "\n");
+}
+
+
+var s = http.createServer(handle_incoming_request);
+s.listen(8080);
+
diff --git a/Chapter04/07_get_params.js b/Chapter04/07_get_params.js
new file mode 100644
index 0000000..42196fd
--- /dev/null
+++ b/Chapter04/07_get_params.js
@@ -0,0 +1,188 @@
+
+var http = require('http'),
+ fs = require('fs'),
+ url = require('url');
+
+
+function load_album_list(callback) {
+ // we will just assume that any directory in our 'albums'
+ // subfolder is an album.
+ fs.readdir(
+ "albums",
+ function (err, files) {
+ if (err) {
+ callback(make_error("file_error", JSON.stringify(err)));
+ return;
+ }
+
+ var only_dirs = [];
+
+ (function iterator(index) {
+ if (index == files.length) {
+ callback(null, only_dirs);
+ return;
+ }
+
+ fs.stat(
+ "albums/" + files[index],
+ function (err, stats) {
+ if (err) {
+ callback(make_error("file_error",
+ JSON.stringify(err)));
+ return;
+ }
+ if (stats.isDirectory()) {
+ var obj = { name: files[index] };
+ only_dirs.push(obj);
+ }
+ iterator(index + 1)
+ }
+ );
+ })(0);
+ }
+ );
+}
+
+function load_album(album_name, page, page_size, callback) {
+ fs.readdir(
+ "albums/" + album_name,
+ function (err, files) {
+ if (err) {
+ if (err.code == "ENOENT") {
+ callback(no_such_album());
+ } else {
+ callback(make_error("file_error",
+ JSON.stringify(err)));
+ }
+ return;
+ }
+
+ var only_files = [];
+ var path = "albums/" + album_name + "/";
+
+ (function iterator(index) {
+ if (index == files.length) {
+ var ps;
+ // slice fails gracefully if params are out of range
+ ps = only_files.splice(page * page_size, page_size);
+ var obj = { short_name: album_name,
+ photos: ps };
+ callback(null, obj);
+ return;
+ }
+
+ fs.stat(
+ path + files[index],
+ function (err, stats) {
+ if (err) {
+ callback(make_error("file_error",
+ JSON.stringify(err)));
+ return;
+ }
+ if (stats.isFile()) {
+ var obj = { filename: files[index], desc: files[index] };
+ only_files.push(obj);
+ }
+ iterator(index + 1)
+ }
+ );
+ })(0);
+ }
+ );
+}
+
+
+function handle_incoming_request(req, res) {
+
+ // parse the query params into an object and get the path
+ // without them. (2nd param true = parse the params).
+ req.parsed_url = url.parse(req.url, true);
+ var core_url = req.parsed_url.pathname;
+
+ // test this fixed url to see what they're asking for
+ if (core_url == '/albums.json') {
+ handle_list_albums(req, res);
+ } else if (core_url.substr(0, 7) == '/albums'
+ && core_url.substr(core_url.length - 5) == '.json') {
+ handle_get_album(req, res);
+ } else {
+ send_failure(res, 404, invalid_resource());
+ }
+}
+
+function handle_list_albums(req, res) {
+ load_album_list(function (err, albums) {
+ if (err) {
+ send_failure(res, 500, err);
+ return;
+ }
+
+ send_success(res, { albums: albums });
+ });
+}
+
+function handle_get_album(req, res) {
+
+ // get the GET params
+ var getp = req.parsed_url.query;
+ var page_num = getp.page ? getp.page : 0;
+ var page_size = getp.page_size ? getp.page_size : 1000;
+
+ if (isNaN(parseInt(page_num))) page_num = 0;
+ if (isNaN(parseInt(page_size))) page_size = 1000;
+
+ // format of request is /albums/album_name.json
+ var core_url = req.parsed_url.pathname;
+
+ var album_name = core_url.substr(7, core_url.length - 12);
+ load_album(
+ album_name,
+ page_num,
+ page_size,
+ function (err, album_contents) {
+ if (err && err.error == "no_such_album") {
+ send_failure(res, 404, err);
+ } else if (err) {
+ send_failure(res, 500, err);
+ } else {
+ send_success(res, { album_data: album_contents });
+ }
+ }
+ );
+}
+
+
+
+function make_error(err, msg) {
+ var e = new Error(msg);
+ e.code = err;
+ return e;
+}
+
+function send_success(res, data) {
+ res.writeHead(200, {"Content-Type": "application/json"});
+ var output = { error: null, data: data };
+ res.end(JSON.stringify(output) + "\n");
+}
+
+function send_failure(res, code, err) {
+ var code = (err.code) ? err.code : err.name;
+ res.writeHead(code, { "Content-Type" : "application/json" });
+ res.end(JSON.stringify({ error: code, message: err.message }) + "\n");
+}
+
+
+function invalid_resource() {
+ return make_error("invalid_resource",
+ "the requested resource does not exist.");
+}
+
+function no_such_album() {
+ return make_error("no_such_album",
+ "The specified album does not exist");
+}
+
+
+var s = http.createServer(handle_incoming_request);
+s.listen(8080);
+
diff --git a/Chapter04/08_post_data.js b/Chapter04/08_post_data.js
new file mode 100644
index 0000000..dab37f1
--- /dev/null
+++ b/Chapter04/08_post_data.js
@@ -0,0 +1,304 @@
+
+var http = require('http'),
+ fs = require('fs'),
+ url = require('url');
+
+
+function load_album_list(callback) {
+ // we will just assume that any directory in our 'albums'
+ // subfolder is an album.
+ fs.readdir(
+ "albums",
+ function (err, files) {
+ if (err) {
+ callback(make_error("file_error", JSON.stringify(err)));
+ return;
+ }
+
+ var only_dirs = [];
+
+ (function iterator(index) {
+ if (index == files.length) {
+ callback(null, only_dirs);
+ return;
+ }
+
+ fs.stat(
+ "albums/" + files[index],
+ function (err, stats) {
+ if (err) {
+ callback(make_error("file_error",
+ JSON.stringify(err)));
+ return;
+ }
+ if (stats.isDirectory()) {
+ var obj = { name: files[index] };
+ only_dirs.push(obj);
+ }
+ iterator(index + 1)
+ }
+ );
+ })(0);
+ }
+ );
+}
+
+function load_album(album_name, page, page_size, callback) {
+ // we will just assume that any directory in our 'albums'
+ // subfolder is an album.
+ fs.readdir(
+ "albums/" + album_name,
+ function (err, files) {
+ if (err) {
+ if (err.code == "ENOENT") {
+ callback(no_such_album());
+ } else {
+ callback(make_error("file_error",
+ JSON.stringify(err)));
+ }
+ return;
+ }
+
+ var only_files = [];
+ var path = "albums/" + album_name + "/";
+
+ (function iterator(index) {
+ if (index == files.length) {
+ var ps;
+ // slice fails gracefully if params are out of range
+ ps = only_files.splice(page * page_size, page_size);
+ var obj = { short_name: album_name,
+ photos: ps };
+ callback(null, obj);
+ return;
+ }
+
+ fs.stat(
+ path + files[index],
+ function (err, stats) {
+ if (err) {
+ callback(make_error("file_error",
+ JSON.stringify(err)));
+ return;
+ }
+ if (stats.isFile()) {
+ var obj = { filename: files[index], desc: files[index] };
+ only_files.push(obj);
+ }
+ iterator(index + 1)
+ }
+ );
+ })(0);
+ }
+ );
+}
+
+
+
+function do_rename(old_name, new_name, callback) {
+
+ // rename the album folder.
+ fs.rename(
+ "albums/" + old_name,
+ "albums/" + new_name,
+ callback);
+}
+
+
+
+
+function handle_incoming_request(req, res) {
+
+ // parse the query params into an object and get the path
+ // without them. (2nd param true = parse the params).
+ req.parsed_url = url.parse(req.url, true);
+ var core_url = req.parsed_url.pathname;
+
+ // test this fixed url to see what they're asking for
+ if (core_url == '/albums.json' && req.method.toLowerCase() == 'get') {
+ handle_list_albums(req, res);
+ } else if (core_url.substr(core_url.length - 12) == '/rename.json'
+ && req.method.toLowerCase() == 'post') {
+ handle_rename_album(req, res);
+ } else if (core_url.substr(0, 7) == '/albums'
+ && core_url.substr(core_url.length - 5) == '.json'
+ && req.method.toLowerCase() == 'get') {
+ handle_get_album(req, res);
+ } else {
+ send_failure(res, 404, invalid_resource());
+ }
+}
+
+function handle_list_albums(req, res) {
+ load_album_list(function (err, albums) {
+ if (err) {
+ send_failure(res, 500, err);
+ return;
+ }
+
+ send_success(res, { albums: albums });
+ });
+}
+
+function handle_get_album(req, res) {
+
+ // get the GET params
+ var getp = req.parsed_url.query;
+ var page_num = getp.page ? getp.page : 0;
+ var page_size = getp.page_size ? getp.page_size : 1000;
+
+ if (isNaN(parseInt(page_num))) page_num = 0;
+ if (isNaN(parseInt(page_size))) page_size = 1000;
+
+ // format of request is /albums/album_name.json
+ var core_url = req.parsed_url.pathname;
+
+ var album_name = core_url.substr(7, core_url.length - 12);
+ load_album(
+ album_name,
+ page_num,
+ page_size,
+ function (err, album_contents) {
+ if (err && err.error == "no_such_album") {
+ send_failure(res, 404, err);
+ } else if (err) {
+ send_failure(res, 500, err);
+ } else {
+ send_success(res, { album_data: album_contents });
+ }
+ }
+ );
+}
+
+
+function handle_rename_album(req, res) {
+
+ // 1. Get the album name from the URL
+ var core_url = req.parsed_url.pathname;
+ var parts = core_url.split('/');
+ if (parts.length != 4) {
+ send_failure(res, 404, invalid_resource(core_url));
+ return;
+ }
+
+ var album_name = parts[2];
+
+ // 2. get the POST data for the request. this will have the JSON
+ // for the new name for the album.
+ var json_body = '';
+ req.on(
+ 'readable',
+ function () {
+ var d = req.read();
+ console.log(d);
+ console.log(typeof d);
+ if (d) {
+ if (typeof d == 'string') {
+ json_body += d;
+ } else if (typeof d == 'object' && d instanceof Buffer) {
+ json_body += d.toString('utf8');
+ }
+ }
+ }
+ );
+
+ // 3. when we have all the post data, make sure we have valid
+ // data and then try to do the rename.
+ req.on(
+ 'end',
+ function () {
+ // did we get a valid body?
+ if (json_body) {
+ try {
+ var album_data = JSON.parse(json_body);
+ if (!album_data.album_name) {
+ send_failure(res, 404, missing_data('album_name'));
+ return;
+ }
+ } catch (e) {
+ // got a body, but not valid json
+ send_failure(res, 403, bad_json());
+ return;
+ }
+
+ // we have a proposed new album name!
+ do_rename(
+ album_name, // old
+ album_data.album_name, // new
+ function (err, results) {
+ if (err && err.code == "ENOENT") {
+ send_failure(res, 403, no_such_album());
+ return;
+ } else if (err) {
+ send_failure(res, 500, file_error(err));
+ return;
+ }
+ send_success(res, null);
+ }
+ );
+ } else {
+ send_failure(res, 403, bad_json());
+ res.end();
+ }
+ }
+ );
+}
+
+
+
+
+
+
+
+function make_error(err, msg) {
+ var e = new Error(msg);
+ e.code = err;
+ return e;
+}
+
+
+function send_success(res, data) {
+ res.writeHead(200, {"Content-Type": "application/json"});
+ var output = { error: null, data: data };
+ res.end(JSON.stringify(output) + "\n");
+}
+
+
+function send_failure(res, code, err) {
+ var code = (err.code) ? err.code : err.name;
+ res.writeHead(code, { "Content-Type" : "application/json" });
+ res.end(JSON.stringify({ error: code, message: err.message }) + "\n");
+}
+
+
+function invalid_resource() {
+ return make_error("invalid_resource",
+ "the requested resource does not exist.");
+}
+
+function no_such_album() {
+ return make_error("no_such_album",
+ "The specified album does not exist");
+}
+
+function file_error(err) {
+ var msg = "There was a file error on the server: " + err.message;
+ return make_error("server_file_error", msg);
+}
+
+function missing_data (missing) {
+ var msg = missing
+ ? "Your request is missing: '" + missing + "'"
+ : "Your request is missing some data.";
+ return make_error("missing_data", msg);
+}
+
+function bad_json() {
+ return make_error("invalid_json",
+ "the provided data is not valid JSON");
+}
+
+
+var s = http.createServer(handle_incoming_request);
+s.listen(8080);
+
diff --git a/Chapter04/09_form_data.html b/Chapter04/09_form_data.html
new file mode 100644
index 0000000..265ab12
--- /dev/null
+++ b/Chapter04/09_form_data.html
@@ -0,0 +1,12 @@
+
+
+ Form Test
+
+
+
+
+
diff --git a/Chapter04/09_form_data.js b/Chapter04/09_form_data.js
new file mode 100644
index 0000000..5c4cd02
--- /dev/null
+++ b/Chapter04/09_form_data.js
@@ -0,0 +1,40 @@
+
+var http = require('http'), qs = require('querystring');
+
+function handle_incoming_request(req, res) {
+ var body = '';
+ req.on(
+ 'readable',
+ function () {
+ var d = req.read();
+ if (d) {
+ if (typeof d == 'string') {
+ body += d;
+ } else if (typeof d == 'object' && d instanceof Buffer) {
+ body += d.toString('utf8');
+ }
+ }
+ }
+ );
+
+ // 3. when we have all the post data, make sure we have valid
+ // data and then try to do the rename.
+ req.on(
+ 'end',
+ function () {
+ if (req.method.toLowerCase() == 'post') {
+ var POST_data = qs.parse(body);
+ console.log(POST_data);
+ }
+ res.writeHead(200, { "Content-Type" : "application/json" });
+ res.end(JSON.stringify( { error: null }) + "\n");
+ }
+ );
+
+}
+
+
+var s = http.createServer(handle_incoming_request);
+
+s.listen(8080);
+
diff --git a/Chapter04/albums/australia2010/aus_01.jpg b/Chapter04/albums/australia2010/aus_01.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/australia2010/aus_02.jpg b/Chapter04/albums/australia2010/aus_02.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/australia2010/aus_03.jpg b/Chapter04/albums/australia2010/aus_03.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/australia2010/aus_04.jpg b/Chapter04/albums/australia2010/aus_04.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/australia2010/aus_05.jpg b/Chapter04/albums/australia2010/aus_05.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/australia2010/aus_06.jpg b/Chapter04/albums/australia2010/aus_06.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/australia2010/aus_07.jpg b/Chapter04/albums/australia2010/aus_07.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/australia2010/aus_08.jpg b/Chapter04/albums/australia2010/aus_08.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/australia2010/aus_09.jpg b/Chapter04/albums/australia2010/aus_09.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/info.txt b/Chapter04/albums/info.txt
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/italy2012/picture_01.jpg b/Chapter04/albums/italy2012/picture_01.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/italy2012/picture_02.jpg b/Chapter04/albums/italy2012/picture_02.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/italy2012/picture_03.jpg b/Chapter04/albums/italy2012/picture_03.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/italy2012/picture_04.jpg b/Chapter04/albums/italy2012/picture_04.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/italy2012/picture_05.jpg b/Chapter04/albums/italy2012/picture_05.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/japan2010/picture_001.jpg b/Chapter04/albums/japan2010/picture_001.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/japan2010/picture_002.jpg b/Chapter04/albums/japan2010/picture_002.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/japan2010/picture_003.jpg b/Chapter04/albums/japan2010/picture_003.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/japan2010/picture_004.jpg b/Chapter04/albums/japan2010/picture_004.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/japan2010/picture_005.jpg b/Chapter04/albums/japan2010/picture_005.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/japan2010/picture_006.jpg b/Chapter04/albums/japan2010/picture_006.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter04/albums/japan2010/picture_007.jpg b/Chapter04/albums/japan2010/picture_007.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/01_mymodule.js b/Chapter05/01_mymodule.js
new file mode 100644
index 0000000..d2e6462
--- /dev/null
+++ b/Chapter05/01_mymodule.js
@@ -0,0 +1,25 @@
+
+
+function Greeter (lang) {
+ this.language = language;
+ this.greet() = function () {
+ switch (this.language) {
+ case "en": return "Hello!";
+ case "de": return "Hallo!";
+ case "jp": return "こんにちは!";
+ default: return "No speaka that language";
+ }
+ }
+}
+
+exports.hello_world = function () {
+ console.log("Hello World");
+}
+
+exports.goodbye = function () {
+ console.log("Bye bye!");
+}
+
+exports.greeter = function (lang) {
+ return new Greeter(lang);
+}
diff --git a/Chapter05/02_factory_model_module.js b/Chapter05/02_factory_model_module.js
new file mode 100644
index 0000000..6388720
--- /dev/null
+++ b/Chapter05/02_factory_model_module.js
@@ -0,0 +1,11 @@
+function ABC (parms) {
+ this.varA = 10;
+ this.varB = 20;
+ this.functionA = function (var1, var2) {
+ console.log(var1 + " " + var2);
+ }
+}
+
+exports.create_ABC = function (parms) {
+ return new ABC(parms);
+}
diff --git a/Chapter05/02_factory_model_test.js b/Chapter05/02_factory_model_test.js
new file mode 100644
index 0000000..bcbe409
--- /dev/null
+++ b/Chapter05/02_factory_model_test.js
@@ -0,0 +1,5 @@
+var fmm = require('./02_factory_model_module.js');
+
+var abc = fmm.create_ABC();
+
+abc.functionA(4, 5);
diff --git a/Chapter05/03_constructor_model_module.js b/Chapter05/03_constructor_model_module.js
new file mode 100644
index 0000000..ad8f05d
--- /dev/null
+++ b/Chapter05/03_constructor_model_module.js
@@ -0,0 +1,11 @@
+
+function ABC () {
+ this.varA = 10;
+ this.varB = 20;
+ this.functionA = function (var1, var2) {
+ console.log(var1 + " " + var2);
+ }
+}
+
+
+module.exports = ABC;
diff --git a/Chapter05/03_constructor_model_test.js b/Chapter05/03_constructor_model_test.js
new file mode 100644
index 0000000..7889355
--- /dev/null
+++ b/Chapter05/03_constructor_model_test.js
@@ -0,0 +1,3 @@
+var abc = require('./03_constructor_model_module.js');
+var obj = new abc();
+obj.functionA(1, 2);
diff --git a/Chapter05/04_album_module/album_mgr/Readme.md b/Chapter05/04_album_module/album_mgr/Readme.md
new file mode 100644
index 0000000..b40a0e8
--- /dev/null
+++ b/Chapter05/04_album_module/album_mgr/Readme.md
@@ -0,0 +1,21 @@
+# Album-Manager
+
+This is our module for managing photo albums based on a directory. We
+assume that, given a path, there is an albums sub-folder, and each of
+its individual sub-folders are themselves the albums. Files in those
+sub-folders are photos.
+
+
+## Album Manager
+
+The album manager exposes a single function, `albums`, which returns
+an array of `Album` objects for each album it contains.
+
+## Album Object
+
+The album object has the follow two properties and one method:
+
+* `name` -- The name of the album
+* `path` -- The path to the album
+* `photos()` -- Calling this method will return all the album's photos
+
diff --git a/Chapter05/04_album_module/album_mgr/lib/album.js b/Chapter05/04_album_module/album_mgr/lib/album.js
new file mode 100644
index 0000000..bcb0d73
--- /dev/null
+++ b/Chapter05/04_album_module/album_mgr/lib/album.js
@@ -0,0 +1,78 @@
+var path = require('path'),
+ fs = require('fs');
+
+function Album (album_path) {
+ this.name = path.basename(album_path);
+ this.path = album_path;
+}
+
+Album.prototype.name = null;
+Album.prototype.path = null;
+Album.prototype._photos = null;
+
+Album.prototype.photos = function (callback) {
+ if (this._photos != null) {
+ callback(null, this._photos);
+ return;
+ }
+
+ var self = this;
+
+ fs.readdir(
+ self.path,
+ function (err, files) {
+ if (err) {
+ if (err.code == "ENOENT") {
+ callback(no_such_album());
+ } else {
+ callback(make_error("file_error",
+ JSON.stringify(err)));
+ }
+ return;
+ }
+
+ var only_files = [];
+
+ (function iterator(index) {
+ if (index == files.length) {
+ callback(null, only_files);
+ return;
+ }
+
+ fs.stat(
+ self.path + "/" + files[index],
+ function (err, stats) {
+ if (err) {
+ callback(make_error("file_error",
+ JSON.stringify(err)));
+ return;
+ }
+ if (stats.isFile()) {
+ only_files.push(files[index]);
+ }
+ iterator(index + 1)
+ }
+ );
+ })(0);
+ }
+ );
+};
+
+
+
+exports.create_album = function (path) {
+ return new Album(path);
+};
+
+
+function make_error(err, msg) {
+ var e = new Error(msg);
+ e.code = err;
+ return e;
+}
+
+
+function no_such_album() {
+ return make_error("no_such_album",
+ "The specified album does not exist");
+}
diff --git a/Chapter05/04_album_module/album_mgr/lib/albums.js b/Chapter05/04_album_module/album_mgr/lib/albums.js
new file mode 100644
index 0000000..b8c731d
--- /dev/null
+++ b/Chapter05/04_album_module/album_mgr/lib/albums.js
@@ -0,0 +1,52 @@
+
+var fs = require('fs'),
+ album = require('./album.js');
+
+
+exports.version = "1.0.0";
+
+exports.albums = function (root, callback) {
+ // we will just assume that any directory in our 'albums'
+ // subfolder is an album.
+ fs.readdir(
+ root + "/albums",
+ function (err, files) {
+ if (err) {
+ callback(err);
+ return;
+ }
+
+ var album_list = [];
+
+ (function iterator(index) {
+ if (index == files.length) {
+ callback(null, album_list);
+ return;
+ }
+
+ fs.stat(
+ root + "albums/" + files[index],
+ function (err, stats) {
+ if (err) {
+ callback(make_error("file_error",
+ JSON.stringify(err)));
+ return;
+ }
+ if (stats.isDirectory()) {
+ var p = root + "albums/" + files[index];
+ album_list.push(album.create_album(p));
+ }
+ iterator(index + 1)
+ }
+ );
+ })(0);
+ }
+ );
+};
+
+function make_error(err, msg) {
+ var e = new Error(msg);
+ e.code = err;
+ return e;
+}
+
diff --git a/Chapter05/04_album_module/album_mgr/package.json b/Chapter05/04_album_module/album_mgr/package.json
new file mode 100644
index 0000000..036c1cc
--- /dev/null
+++ b/Chapter05/04_album_module/album_mgr/package.json
@@ -0,0 +1,3 @@
+{ "name": "album-manager",
+ "version": "1.0.0",
+ "main": "./lib/albums.js" }
diff --git a/Chapter05/04_album_module/album_mgr/test/album_test.js b/Chapter05/04_album_module/album_mgr/test/album_test.js
new file mode 100644
index 0000000..74fb07e
--- /dev/null
+++ b/Chapter05/04_album_module/album_mgr/test/album_test.js
@@ -0,0 +1,41 @@
+
+var album_mgr = require('../lib/albums.js'),
+ path = require('path');
+
+
+album_mgr.albums(
+ "./",
+ function (err, albums) {
+ if (err) {
+ console.log("TEST FAILURE: can't load albums\n"
+ + JSON.stringify(err));
+ return;
+ }
+
+ (function iterator(index) {
+ if (index == albums.length) {
+ console.log("PASS!");
+ return;
+ }
+
+ albums[index].photos(
+ function (err, photos) {
+ if (err) {
+ console.log("TEST FAILURE: load album\n"
+ + JSON.stringify(err));
+ return;
+ }
+
+ var a = albums[index];
+ console.log("Album: " + a.name
+ + "(" + a.path + ")");
+ for (var i = 0; i < photos.length; i++) {
+ console.log(" " + path.basename(photos[i]));
+ }
+ console.log("");
+ iterator(index+1);
+ }
+ );
+ })(0);
+ }
+);
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_01.jpg b/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_01.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_02.jpg b/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_02.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_03.jpg b/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_03.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_04.jpg b/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_04.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_05.jpg b/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_05.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_06.jpg b/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_06.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_07.jpg b/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_07.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_08.jpg b/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_08.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_09.jpg b/Chapter05/04_album_module/album_mgr/test/albums/australia2010/aus_09.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/info.txt b/Chapter05/04_album_module/album_mgr/test/albums/info.txt
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/italy2012/picture_01.jpg b/Chapter05/04_album_module/album_mgr/test/albums/italy2012/picture_01.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/italy2012/picture_02.jpg b/Chapter05/04_album_module/album_mgr/test/albums/italy2012/picture_02.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/italy2012/picture_03.jpg b/Chapter05/04_album_module/album_mgr/test/albums/italy2012/picture_03.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/italy2012/picture_04.jpg b/Chapter05/04_album_module/album_mgr/test/albums/italy2012/picture_04.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/italy2012/picture_05.jpg b/Chapter05/04_album_module/album_mgr/test/albums/italy2012/picture_05.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/japan2010/picture_001.jpg b/Chapter05/04_album_module/album_mgr/test/albums/japan2010/picture_001.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/japan2010/picture_002.jpg b/Chapter05/04_album_module/album_mgr/test/albums/japan2010/picture_002.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/japan2010/picture_003.jpg b/Chapter05/04_album_module/album_mgr/test/albums/japan2010/picture_003.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/japan2010/picture_004.jpg b/Chapter05/04_album_module/album_mgr/test/albums/japan2010/picture_004.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/japan2010/picture_005.jpg b/Chapter05/04_album_module/album_mgr/test/albums/japan2010/picture_005.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/japan2010/picture_006.jpg b/Chapter05/04_album_module/album_mgr/test/albums/japan2010/picture_006.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/album_mgr/test/albums/japan2010/picture_007.jpg b/Chapter05/04_album_module/album_mgr/test/albums/japan2010/picture_007.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/australia2010/aus_01.jpg b/Chapter05/04_album_module/albums/australia2010/aus_01.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/australia2010/aus_02.jpg b/Chapter05/04_album_module/albums/australia2010/aus_02.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/australia2010/aus_03.jpg b/Chapter05/04_album_module/albums/australia2010/aus_03.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/australia2010/aus_04.jpg b/Chapter05/04_album_module/albums/australia2010/aus_04.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/australia2010/aus_05.jpg b/Chapter05/04_album_module/albums/australia2010/aus_05.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/australia2010/aus_06.jpg b/Chapter05/04_album_module/albums/australia2010/aus_06.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/australia2010/aus_07.jpg b/Chapter05/04_album_module/albums/australia2010/aus_07.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/australia2010/aus_08.jpg b/Chapter05/04_album_module/albums/australia2010/aus_08.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/australia2010/aus_09.jpg b/Chapter05/04_album_module/albums/australia2010/aus_09.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/info.txt b/Chapter05/04_album_module/albums/info.txt
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/italy2012/picture_01.jpg b/Chapter05/04_album_module/albums/italy2012/picture_01.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/italy2012/picture_02.jpg b/Chapter05/04_album_module/albums/italy2012/picture_02.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/italy2012/picture_03.jpg b/Chapter05/04_album_module/albums/italy2012/picture_03.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/italy2012/picture_04.jpg b/Chapter05/04_album_module/albums/italy2012/picture_04.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/italy2012/picture_05.jpg b/Chapter05/04_album_module/albums/italy2012/picture_05.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/japan2010/picture_001.jpg b/Chapter05/04_album_module/albums/japan2010/picture_001.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/japan2010/picture_002.jpg b/Chapter05/04_album_module/albums/japan2010/picture_002.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/japan2010/picture_003.jpg b/Chapter05/04_album_module/albums/japan2010/picture_003.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/japan2010/picture_004.jpg b/Chapter05/04_album_module/albums/japan2010/picture_004.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/japan2010/picture_005.jpg b/Chapter05/04_album_module/albums/japan2010/picture_005.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/japan2010/picture_006.jpg b/Chapter05/04_album_module/albums/japan2010/picture_006.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/albums/japan2010/picture_007.jpg b/Chapter05/04_album_module/albums/japan2010/picture_007.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter05/04_album_module/test_album_mgr.js b/Chapter05/04_album_module/test_album_mgr.js
new file mode 100644
index 0000000..547df03
--- /dev/null
+++ b/Chapter05/04_album_module/test_album_mgr.js
@@ -0,0 +1,30 @@
+
+var amgr = require('./album_mgr');
+
+
+amgr.albums('./', function (err, albums) {
+ if (err) {
+ console.log("Unexpected error: " + JSON.stringify(err));
+ return;
+ }
+
+ (function iterator(index) {
+ if (index == albums.length) {
+ console.log("Done");
+ return;
+ }
+
+ albums[index].photos(function (err, photos) {
+ if (err) {
+ console.log("Err loading album: " + JSON.stringify(err));
+ return;
+ }
+
+ console.log(albums[index].name);
+ console.log(photos);
+ console.log("");
+ iterator(index + 1);
+ });
+ })(0);
+});
+
diff --git a/Chapter05/05_series.js b/Chapter05/05_series.js
new file mode 100644
index 0000000..0e81fe4
--- /dev/null
+++ b/Chapter05/05_series.js
@@ -0,0 +1,19 @@
+
+var async = require("async");
+
+async.series({
+
+ numbers: function (callback) {
+ setTimeout(function () {
+ callback(null, [ 1, 2, 3 ]);
+ }, 1500);
+ },
+ strings: function (callback) {
+ setTimeout(function () {
+ callback(null, [ "a", "b", "c" ]);
+ }, 2000);
+ }
+},
+function (err, results) {
+ console.log(results);
+});
diff --git a/Chapter05/06_parallel.js b/Chapter05/06_parallel.js
new file mode 100644
index 0000000..9ed0b8f
--- /dev/null
+++ b/Chapter05/06_parallel.js
@@ -0,0 +1,19 @@
+
+var async = require("async");
+
+async.parallel({
+
+ numbers: function (callback) {
+ setTimeout(function () {
+ callback(null, [ 1, 2, 3 ]);
+ }, 1500);
+ },
+ strings: function (callback) {
+ setTimeout(function () {
+ callback(null, [ "a", "b", "c" ]);
+ }, 2000);
+ }
+},
+function (err, results) {
+ console.log(results);
+});
diff --git a/Chapter05/07_auto.js b/Chapter05/07_auto.js
new file mode 100644
index 0000000..d2facf2
--- /dev/null
+++ b/Chapter05/07_auto.js
@@ -0,0 +1,31 @@
+
+var async = require("async");
+
+async.auto({
+
+ numbers: function (callback) {
+ setTimeout(function () {
+ callback(null, [ 1, 2, 3 ]);
+ }, 1500);
+ },
+ strings: function (callback) {
+ setTimeout(function () {
+ callback(null, [ "a", "b", "c" ]);
+ }, 2000);
+ },
+ assemble: [ 'numbers', 'strings', function (callback, thus_far) {
+ callback(null, {
+ numbers: thus_far.numbers.join(", "),
+ strings: "'" + thus_far.strings.join("', '") + "'"
+ });
+ }]
+},
+function (err, results) {
+ if (err)
+ console.log(err);
+ else
+ console.log(results);
+}
+
+);
+
diff --git a/Chapter05/08_async_vs_no_async.js b/Chapter05/08_async_vs_no_async.js
new file mode 100644
index 0000000..fa4c522
--- /dev/null
+++ b/Chapter05/08_async_vs_no_async.js
@@ -0,0 +1,110 @@
+
+var fs = require('fs');
+var async = require('async');
+
+
+function load_file_contents(path, callback) {
+ fs.open(path, 'r', function (err, f) {
+ if (err) {
+ callback(err);
+ return;
+ } else if (!f) {
+ callback(make_error("invalid_handle",
+ "bad file handle from fs.open"));
+ return;
+ }
+ fs.fstat(f, function (err, stats) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ if (stats.isFile()) {
+ var b = new Buffer(10000);
+ fs.read(f, b, 0, 10000, null, function (err, br, buf) {
+ if (err) {
+ callback(err);
+ return;
+ }
+
+ fs.close(f, function (err) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ callback(null, b.toString('utf8', 0, br));
+ });
+ });
+ } else {
+ calback(make_error("not_file", "Can't load directory"));
+ return;
+ }
+ });
+ });
+}
+
+
+
+function load_file_contents2(path, callback) {
+ var f;
+ async.waterfall([
+ function (cb) { // cb stands for "callback"
+ fs.open(path, 'r', cb);
+ },
+ // the handle was passed to the callback at the end of
+ // the fs.open function call. async passes ALL params to us.
+ function (handle, cb) {
+ f = handle
+ fs.fstat(f, cb);
+ },
+ function (stats, cb) {
+ var b = new Buffer(100000);
+ if (stats.isFile()) {
+ fs.read(f, b, 0, 100000, null, cb);
+ } else {
+ calback(make_error("not_file", "Can't load directory"));
+ }
+ },
+ function (bytes_read, buffer, cb) {
+ fs.close(f, function (err) {
+ if (err)
+ cb(err);
+ else
+ cb(null, buffer.toString('utf8', 0, bytes_read));
+ })
+ }
+ ],
+ // called after all fns have finished, or then there is an error.
+ function (err, file_contents) {
+ callback(err, file_contents);
+ });
+}
+
+
+
+
+load_file_contents(
+ "test.txt",
+ function (err, contents) {
+ if (err)
+ console.log(err);
+ else
+ console.log(contents);
+ }
+);
+
+load_file_contents2(
+ "test.txt",
+ function (err, contents) {
+ if (err)
+ console.log(err);
+ else
+ console.log(contents);
+ }
+);
+
+function make_error(err, msg) {
+ var e = new Error(msg);
+ e.code = msg;
+ return e;
+}
+
diff --git a/Chapter05/package.json b/Chapter05/package.json
new file mode 100644
index 0000000..a91dee5
--- /dev/null
+++ b/Chapter05/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "Modules-Demo",
+ "description": "Demonstrates Using Modules, specificaly async",
+ "version": "0.0.1",
+ "private": true,
+ "dependencies": {
+ "async": "0.1.x"
+ }
+}
diff --git a/Chapter05/test.txt b/Chapter05/test.txt
new file mode 100644
index 0000000..340fc4c
--- /dev/null
+++ b/Chapter05/test.txt
@@ -0,0 +1,2 @@
+Is that how you get ants, Barry?
+Yes it is, other Barry.
diff --git a/Chapter06/01_simple_stream.js b/Chapter06/01_simple_stream.js
new file mode 100644
index 0000000..e17a0b1
--- /dev/null
+++ b/Chapter06/01_simple_stream.js
@@ -0,0 +1,32 @@
+// before we used read(), now we'll use streams
+
+var fs = require('fs');
+var contents;
+
+// INCEPTION BWAAAAAAA!!!!
+var rs = fs.createReadStream("01_simple_stream.js");
+
+rs.on('readable', function () {
+ var str;
+ var d = rs.read();
+ if (d) {
+ if (typeof d == 'string') {
+ str = d;
+ } else if (typeof d == 'object' && d instanceof Buffer) {
+ str = d.toString('utf8');
+ }
+ if (str) {
+ if (!contents)
+ contents = d;
+ else
+ contents += str;
+ }
+ }
+});
+
+rs.on('end', function () {
+ console.log("read in the file contents: ");
+ console.log(contents.toString('utf8'));
+});
+
+
diff --git a/Chapter06/01x_old_streams/01_simple_stream.js b/Chapter06/01x_old_streams/01_simple_stream.js
new file mode 100644
index 0000000..739c80c
--- /dev/null
+++ b/Chapter06/01x_old_streams/01_simple_stream.js
@@ -0,0 +1,21 @@
+// before we used read(), now we'll use streams
+
+var fs = require('fs');
+var contents;
+
+// INCEPTION BWAAAAAAA!!!!
+var rs = fs.createReadStream("01_simple_stream.js");
+
+rs.on('data', function (data) {
+ if (!contents)
+ contents = data;
+ else
+ contents = contents.concat(data);
+});
+
+rs.on('end', function () {
+ console.log("read in the file contents: ");
+ console.log(contents.toString('utf8'));
+});
+
+
diff --git a/Chapter06/02_static_content_server.js b/Chapter06/02_static_content_server.js
new file mode 100644
index 0000000..e76bdbb
--- /dev/null
+++ b/Chapter06/02_static_content_server.js
@@ -0,0 +1,74 @@
+var http = require('http'),
+ path = require('path'),
+ fs = require('fs');
+
+
+function handle_incoming_request(req, res) {
+ if (req.method.toLowerCase() == 'get'
+ && req.url.substring(0, 9) == '/content/') {
+ serve_static_file(req.url.substring(9), res);
+ } else {
+ res.writeHead(404, { "Content-Type" : "application/json" });
+
+ var out = { error: "not_found",
+ message: "'" + req.url + "' not found" };
+ res.end(JSON.stringify(out) + "\n");
+ }
+}
+
+
+function serve_static_file(file, res) {
+ var rs = fs.createReadStream(file);
+ var ct = content_type_for_path(file);
+ res.writeHead(200, { "Content-Type" : ct });
+
+ rs.on(
+ 'error',
+ function (e) {
+ res.writeHead(404, { "Content-Type" : "application/json" });
+ var out = { error: "not_found",
+ message: "'" + file + "' not found" };
+ res.end(JSON.stringify(out) + "\n");
+ return;
+ }
+ );
+
+ rs.on(
+ 'readable',
+ function () {
+ var d = rs.read();
+ if (d) {
+ if (typeof d == 'string')
+ res.write(d);
+ else if (typeof d == 'object' && d instanceof Buffer)
+ res.write(d.toString('utf8'));
+ }
+ }
+ );
+
+ rs.on(
+ 'end',
+ function () {
+ res.end(); // we're done!!!
+ }
+ );
+}
+
+
+function content_type_for_path (file) {
+ var ext = path.extname(file);
+ switch (ext.toLowerCase()) {
+ case '.html': return "text/html";
+ case ".js": return "text/javascript";
+ case ".css": return 'text/css';
+ case '.jpg': case '.jpeg': return 'image/jpeg';
+ default: return 'text/plain';
+ }
+}
+
+
+
+var s = http.createServer(handle_incoming_request);
+
+s.listen(8080);
+
diff --git a/Chapter06/03_static_content_pauses.js b/Chapter06/03_static_content_pauses.js
new file mode 100644
index 0000000..d9c61f1
--- /dev/null
+++ b/Chapter06/03_static_content_pauses.js
@@ -0,0 +1,73 @@
+var http = require('http'),
+ path = require('path'),
+ fs = require('fs');
+
+
+function handle_incoming_request(req, res) {
+ if (req.method.toLowerCase() == 'get'
+ && req.url.substring(0, 9) == '/content/') {
+ serve_static_file(req.url.substring(9), res);
+ } else {
+ res.writeHead(404, { "Content-Type" : "application/json" });
+
+ var out = { error: "not_found",
+ message: "'" + req.url + "' not found" };
+ res.end(JSON.stringify(out) + "\n");
+ }
+}
+
+
+function serve_static_file(file, res) {
+ var rs = fs.createReadStream(file);
+
+ var ct = content_type_for_path(file);
+ res.writeHead(200, { "Content-Type" : ct });
+
+ rs.on(
+ 'error',
+ function (e) {
+ res.writeHead(404, { "Content-Type" : "application/json" });
+ var out = { error: "not_found",
+ message: "'" + file + "' not found" };
+ res.end(JSON.stringify(out) + "\n");
+ }
+ );
+
+ rs.on(
+ 'readable',
+ function () {
+ var data = rs.read();
+ if (!res.write(data)) {
+ rs.pause();
+ }
+ }
+ );
+
+ res.on('drain', function () {
+ rs.resume();
+ });
+
+ rs.on(
+ 'end',
+ function () {
+ res.end(); // we're done!!!
+ }
+ );
+}
+
+function content_type_for_path (file) {
+ var ext = path.extname(file);
+ switch (ext.toLowerCase()) {
+ case '.html': return "text/html";
+ case ".js": return "text/javascript";
+ case ".css": return 'text/css';
+ case '.jpg': case '.jpeg': return 'image/jpeg';
+ default: return 'text/plain';
+ }
+}
+
+
+var s = http.createServer(handle_incoming_request);
+
+s.listen(8080);
+
diff --git a/Chapter06/04_pipe.js b/Chapter06/04_pipe.js
new file mode 100644
index 0000000..6e1bc4e
--- /dev/null
+++ b/Chapter06/04_pipe.js
@@ -0,0 +1,54 @@
+var http = require('http'),
+ path = require('path'),
+ fs = require('fs');
+
+
+function handle_incoming_request(req, res) {
+ if (req.method.toLowerCase() == 'get'
+ && req.url.substring(0, 9) == '/content/') {
+ serve_static_file(req.url.substring(9), res);
+ } else {
+ res.writeHead(404, { "Content-Type" : "application/json" });
+
+ var out = { error: "not_found",
+ message: "'" + req.url + "' not found" };
+ res.end(JSON.stringify(out) + "\n");
+ }
+}
+
+
+function serve_static_file(file, res) {
+ var rs = fs.createReadStream(file);
+ var ct = content_type_for_path(file);
+ res.writeHead(200, { "Content-Type" : ct });
+
+ rs.on(
+ 'error',
+ function (e) {
+ res.writeHead(404, { "Content-Type" : "application/json" });
+ var out = { error: "not_found",
+ message: "'" + file + "' not found" };
+ res.end(JSON.stringify(out) + "\n");
+ return;
+ }
+ );
+
+ rs.pipe(res);
+}
+
+function content_type_for_path (file) {
+ var ext = path.extname(file);
+ switch (ext.toLowerCase()) {
+ case '.html': return "text/html";
+ case ".js": return "text/javascript";
+ case ".css": return 'text/css';
+ case '.jpg': case '.jpeg': return 'image/jpeg';
+ default: return 'text/plain';
+ }
+}
+
+
+var s = http.createServer(handle_incoming_request);
+
+s.listen(8080);
+
diff --git a/Chapter06/05_server_static.js b/Chapter06/05_server_static.js
new file mode 100644
index 0000000..0bac4d1
--- /dev/null
+++ b/Chapter06/05_server_static.js
@@ -0,0 +1,232 @@
+var http = require('http'),
+ path = require("path"),
+ fs = require('fs'),
+ url = require('url');
+
+
+function serve_static_file(file, res) {
+ var rs = fs.createReadStream("content/" + file);
+ var ct = content_type_for_path(file);
+ res.writeHead(200, { "Content-Type" : ct });
+
+ rs.on(
+ 'error',
+ function (e) {
+ res.writeHead(404, { "Content-Type" : "application/json" });
+ var out = { error: "not_found",
+ message: "'" + file + "' not found" };
+ res.end(JSON.stringify(out) + "\n");
+ return;
+ }
+ );
+
+ rs.pipe(res);
+}
+
+
+function content_type_for_path (file) {
+ var ext = path.extname(file);
+ switch (ext.toLowerCase()) {
+ case '.html': return "text/html";
+ case ".js": return "text/javascript";
+ case ".css": return 'text/css';
+ case '.jpg': case '.jpeg': return 'image/jpeg';
+ case '.png': return "image/png";
+ default: return 'text/plain';
+ }
+}
+
+
+
+function load_album_list(callback) {
+ // we will just assume that any directory in our 'albums'
+ // subfolder is an album.
+ fs.readdir(
+ "albums",
+ function (err, files) {
+ if (err) {
+ callback(make_error("file_error"), JSON.stringify(err));
+ return;
+ }
+
+ var only_dirs = [];
+
+ (function iterator(index) {
+ if (index == files.length) {
+ callback(null, only_dirs);
+ return;
+ }
+
+ fs.stat(
+ "albums/" + files[index],
+ function (err, stats) {
+ if (err) {
+ callback(make_error("file_error",
+ JSON.stringify(err)));
+ return;
+ }
+ if (stats.isDirectory()) {
+ var obj = { name: files[index] };
+ only_dirs.push(obj);
+ }
+ iterator(index + 1)
+ }
+ );
+ })(0);
+ }
+ );
+}
+
+function load_album(album_name, page, page_size, callback) {
+ fs.readdir(
+ "albums/" + album_name,
+ function (err, files) {
+ if (err) {
+ if (err.code == "ENOENT") {
+ callback(no_such_album());
+ } else {
+ callback(make_error("file_error",
+ JSON.stringify(err)));
+ }
+ return;
+ }
+
+ var only_files = [];
+
+ (function iterator(index) {
+ if (index == files.length) {
+ var ps;
+ // slice fails gracefully if params are out of range
+ ps = only_files.splice(page * page_size, page_size);
+ var obj = { short_name: album_name,
+ photos: ps };
+ callback(null, obj);
+ return;
+ }
+
+ var path = "albums/" + album_name + "/";
+
+ fs.stat(
+ path + files[index],
+ function (err, stats) {
+ if (err) {
+ callback(make_error("file_error",
+ JSON.stringify(err)));
+ return;
+ }
+ if (stats.isFile()) {
+ var obj = { filename: files[index], desc: files[index] };
+ only_files.push(obj);
+ }
+ iterator(index + 1)
+ }
+ );
+ })(0);
+ }
+ );
+}
+
+function handle_incoming_request(req, res) {
+
+ // parse the query params into an object and get the path
+ // without them. (2nd param true = parse the params).
+ req.parsed_url = url.parse(req.url, true);
+ var core_url = req.parsed_url.pathname;
+
+ // test this fixed url to see what they're asking for
+ if (req.method.toLowerCase() == 'get'
+ && req.url.substring(0, 9) == '/content/') {
+ serve_static_file(req.url.substring(9), res);
+ } else if (core_url == '/albums.json') {
+ handle_list_albums(req, res);
+ } else if (core_url.substr(0, 7) == '/albums'
+ && core_url.substr(core_url.length - 5) == '.json') {
+ handle_get_album(req, res);
+ } else {
+ send_failure(res, 404, invalid_resource());
+ }
+}
+
+function handle_list_albums(req, res) {
+ load_album_list(function (err, albums) {
+ if (err) {
+ send_failure(res, 500, err);
+ return;
+ }
+
+ send_success(res, { albums: albums });
+ });
+}
+
+function handle_get_album(req, res) {
+
+ // get the GET params
+ var getp = get_query_params(req);
+ var page_num = getp.page ? getp.page : 0;
+ var page_size = getp.page_size ? getp.page_size : 1000;
+
+ if (isNaN(parseInt(page_num))) page_num = 0;
+ if (isNaN(parseInt(page_size))) page_size = 1000;
+
+ // format of request is /albums/album_name.json
+ var core_url = req.parsed_url.pathname;
+
+ var album_name = core_url.substr(7, core_url.length - 12);
+ load_album(
+ album_name,
+ page_num,
+ page_size,
+ function (err, album_contents) {
+ if (err && err == "no_such_album") {
+ send_failure(res, 404, err);
+ } else if (err) {
+ send_failure(res, 500, err);
+ } else {
+ send_success(res, { album_photos: album_contents });
+ }
+ }
+ );
+}
+
+
+
+
+function make_error(err, msg) {
+ var e = new Error(msg);
+ e.code = err;
+ return e;
+}
+
+function send_success(res, data) {
+ res.writeHead(200, {"Content-Type": "application/json"});
+ var output = { error: null, data: data };
+ res.end(JSON.stringify(output) + "\n");
+}
+
+function send_failure(res, code, err) {
+ var code = (err.code) ? err.code : err.name;
+ res.writeHead(code, { "Content-Type" : "application/json" });
+ res.end(JSON.stringify({ error: code, message: err.message }) + "\n");
+}
+
+
+function invalid_resource() {
+ return make_error("invalid_resource",
+ "the requested resource does not exist.");
+}
+
+function no_such_album() {
+ return make_error("no_such_album",
+ "The specified album does not exist");
+}
+
+
+function get_query_params(req) {
+ return req.parsed_url.query;
+}
+
+
+
+
+var s = http.createServer(handle_incoming_request);
+s.listen(8080);
diff --git a/Chapter06/06_writing_a_file.js b/Chapter06/06_writing_a_file.js
new file mode 100644
index 0000000..a9d9acf
--- /dev/null
+++ b/Chapter06/06_writing_a_file.js
@@ -0,0 +1,14 @@
+// before we used read(), now we'll use streams
+
+var fs = require('fs');
+var contents;
+
+// INCEPTION BWAAAAAAA!!!!
+var rs = fs.createReadStream("01_simple_stream.js");
+var ws = fs.createWriteStream("copy of 01_simple_stream.js");
+
+
+rs.pipe(ws);
+
+
+
diff --git a/Chapter06/albums/australia2010/aus_01.jpg b/Chapter06/albums/australia2010/aus_01.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/australia2010/aus_02.jpg b/Chapter06/albums/australia2010/aus_02.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/australia2010/aus_03.jpg b/Chapter06/albums/australia2010/aus_03.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/australia2010/aus_04.jpg b/Chapter06/albums/australia2010/aus_04.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/australia2010/aus_05.jpg b/Chapter06/albums/australia2010/aus_05.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/australia2010/aus_06.jpg b/Chapter06/albums/australia2010/aus_06.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/australia2010/aus_07.jpg b/Chapter06/albums/australia2010/aus_07.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/australia2010/aus_08.jpg b/Chapter06/albums/australia2010/aus_08.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/australia2010/aus_09.jpg b/Chapter06/albums/australia2010/aus_09.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/info.txt b/Chapter06/albums/info.txt
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/italy2012/picture_01.jpg b/Chapter06/albums/italy2012/picture_01.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/italy2012/picture_02.jpg b/Chapter06/albums/italy2012/picture_02.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/italy2012/picture_03.jpg b/Chapter06/albums/italy2012/picture_03.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/italy2012/picture_04.jpg b/Chapter06/albums/italy2012/picture_04.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/italy2012/picture_05.jpg b/Chapter06/albums/italy2012/picture_05.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/japan2010/picture_001.jpg b/Chapter06/albums/japan2010/picture_001.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/japan2010/picture_002.jpg b/Chapter06/albums/japan2010/picture_002.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/japan2010/picture_003.jpg b/Chapter06/albums/japan2010/picture_003.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/japan2010/picture_004.jpg b/Chapter06/albums/japan2010/picture_004.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/japan2010/picture_005.jpg b/Chapter06/albums/japan2010/picture_005.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/japan2010/picture_006.jpg b/Chapter06/albums/japan2010/picture_006.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/albums/japan2010/picture_007.jpg b/Chapter06/albums/japan2010/picture_007.jpg
new file mode 100644
index 0000000..e69de29
diff --git a/Chapter06/buffer_sidebar.js b/Chapter06/buffer_sidebar.js
new file mode 100644
index 0000000..5340e23
--- /dev/null
+++ b/Chapter06/buffer_sidebar.js
@@ -0,0 +1,25 @@
+
+
+var b = new Buffer(10000);
+var str = "我叫王马克";
+
+b.write(str); // default is utf8, which is what we want
+
+console.log( b.length );
+
+
+// byteLength is useful for working with UTF-8 and buffers
+console.log( str.length );
+console.log( Buffer.byteLength(str) );
+
+
+var b1 = new Buffer("My name is ");
+var b2 = new Buffer("Marc");
+var b3 = Buffer.concat([ b1, b2 ]);
+console.log(b3.toString('utf8'));
+
+
+var bb = new Buffer(100);
+bb.fill("\0");
+
+console.log(bb.readInt8(0));
diff --git a/Chapter06/content/album.js b/Chapter06/content/album.js
new file mode 100644
index 0000000..218d444
--- /dev/null
+++ b/Chapter06/content/album.js
@@ -0,0 +1,46 @@
+$(function(){
+
+ var tmpl, // Main template HTML
+ tdata = {}; // JSON data object that feeds the template
+
+ // Initialise page
+ var initPage = function() {
+
+ // get our album name.
+ parts = window.location.href.split("/");
+ var album_name = parts[5];
+
+ // Load the HTML template
+ $.get("/templates/album.html", function(d){
+ tmpl = d;
+ });
+
+ // Retrieve the server data and then initialise the page
+ $.getJSON("/v1/albums/" + album_name + ".json", function (d) {
+ var photo_d = massage_album(d);
+ $.extend(tdata, photo_d);
+ });
+
+ // When AJAX calls are complete parse the template
+ // replacing mustache tags with vars
+ $(document).ajaxStop(function () {
+ var renderedPage = Mustache.to_html( tmpl, tdata );
+ $("body").html( renderedPage );
+ })
+ }();
+});
+
+
+
+function massage_album(d) {
+ if (d.error != null) return d;
+ var obj = { photos: [] };
+
+ var af = d.data.album_data;
+
+ for (var i = 0; i < af.photos.length; i++) {
+ var url = "/albums/" + af.short_name + "/" + af.photos[i];
+ obj.photos.push({ url: url, desc: af.photos[i] });
+ }
+ return obj;
+}
diff --git a/Chapter06/content/jquery.mustache.js b/Chapter06/content/jquery.mustache.js
new file mode 100644
index 0000000..4d3c952
--- /dev/null
+++ b/Chapter06/content/jquery.mustache.js
@@ -0,0 +1,648 @@
+/*
+Shameless port of a shameless port
+@defunkt => @janl => @aq
+
+See http://github.com/defunkt/mustache for more info.
+*/
+
+;(function($) {
+
+/*!
+ * mustache.js - Logic-less {{mustache}} templates with JavaScript
+ * http://github.com/janl/mustache.js
+ */
+
+/*global define: false*/
+
+var Mustache;
+
+(function (exports) {
+ if (typeof module !== "undefined" && module.exports) {
+ module.exports = exports; // CommonJS
+ } else if (typeof define === "function") {
+ define(exports); // AMD
+ } else {
+ Mustache = exports; //
+
+
+
+
+
+