├── .gitignore ├── 03_functions ├── exercises │ ├── pixel_art │ │ ├── starter │ │ │ ├── pixart.js │ │ │ ├── ps_neutral.png │ │ │ ├── index.html │ │ │ ├── style.css │ │ │ └── README.md │ │ └── solution │ │ │ ├── ps_neutral.png │ │ │ ├── index.html │ │ │ ├── style.css │ │ │ ├── pixart.js │ │ │ └── README.md │ ├── expense_tracker │ │ ├── starter │ │ │ ├── javascripts │ │ │ │ └── expense.js │ │ │ ├── stylesheets │ │ │ │ └── expense.css │ │ │ ├── instructions.md │ │ │ └── index.html │ │ └── complete │ │ │ ├── stylesheets │ │ │ └── expense.css │ │ │ ├── javascripts │ │ │ └── expense.js │ │ │ ├── instructions.md │ │ │ └── index.html │ ├── hogwarts_js │ │ ├── instructions.md │ │ ├── starter.html │ │ └── complete.html │ ├── functions_deepdive │ │ ├── complete │ │ │ ├── index.html │ │ │ └── function_spec.js │ │ ├── starter │ │ │ ├── index.html │ │ │ └── function_spec.js │ │ └── lib │ │ │ ├── jasmine.css │ │ │ ├── console.js │ │ │ ├── boot.js │ │ │ └── jasmine-html.js │ ├── closure.js │ ├── lesson_practice │ │ ├── scope.html │ │ └── this.html │ └── gryffindor_house │ │ ├── instructions.md │ │ ├── starter.html │ │ └── complete.html ├── sandbox │ ├── sandbox.js │ ├── bg.jpg │ └── index.html ├── introduction │ ├── images │ │ ├── abyss.jpg │ │ ├── depth.jpg │ │ ├── intro.png │ │ ├── texture.png │ │ └── texture2.png │ ├── fonts │ │ ├── BEBAS___-webfont.eot │ │ ├── BEBAS___-webfont.ttf │ │ ├── montserrat-400.woff2 │ │ ├── montserrat-700.woff2 │ │ └── BEBAS___-webfont.woff │ └── stylesheets │ │ └── intro.css └── intro_to_functions.js ├── 05_backbone ├── exercises │ ├── contact_list │ │ ├── js │ │ │ ├── app-starter.js │ │ │ ├── app-complete.js │ │ │ └── vendor │ │ │ │ ├── backbone.localstorage.js │ │ │ │ └── underscore.js │ │ ├── css │ │ │ └── styles.css │ │ ├── complete.html │ │ ├── starter.html │ │ └── instructions.md │ └── startup_idea │ │ ├── js │ │ ├── app-starter.js │ │ ├── app-complete.js │ │ └── vendor │ │ │ └── underscore.js │ │ ├── complete.html │ │ ├── starter.html │ │ └── instructions.md └── intro_to_backbone.md ├── 02_dom ├── sandbox │ ├── sandbox.js │ └── index.html ├── exercises │ ├── currency_converter │ │ ├── starter.html │ │ ├── instructions.md │ │ └── complete.html │ └── name_generator │ │ ├── instructions.md │ │ ├── starter.html │ │ └── complete.html └── intro_to_dom.md ├── 01_fundamentals ├── sandbox │ ├── sandbox.js │ ├── bg.jpg │ └── index.html ├── introduction │ ├── images │ │ ├── javascript.png │ │ └── growth-mindset-video.png │ ├── fonts │ │ ├── BEBAS___-webfont.eot │ │ ├── BEBAS___-webfont.ttf │ │ ├── BEBAS___-webfont.woff │ │ ├── montserrat-400.woff2 │ │ └── montserrat-700.woff2 │ ├── index.html │ └── stylesheets │ │ └── intro.css └── exercises │ ├── world_foodie │ ├── starter.html │ ├── instructions.md │ └── complete.html │ ├── deck_o_cards │ ├── starter.html │ ├── instructions.md │ └── complete.html │ └── hammonds_mine │ ├── instructions.md │ ├── starter.html │ └── complete.html ├── README.md └── 04_regex └── intro_to_regex.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /03_functions/exercises/pixel_art/starter/pixart.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /05_backbone/exercises/contact_list/js/app-starter.js: -------------------------------------------------------------------------------- 1 | // go... -------------------------------------------------------------------------------- /05_backbone/exercises/startup_idea/js/app-starter.js: -------------------------------------------------------------------------------- 1 | // go... -------------------------------------------------------------------------------- /03_functions/exercises/expense_tracker/starter/javascripts/expense.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /02_dom/sandbox/sandbox.js: -------------------------------------------------------------------------------- 1 | // You may write JavaScript here... 2 | 3 | console.log('Knock knock.'); -------------------------------------------------------------------------------- /01_fundamentals/sandbox/sandbox.js: -------------------------------------------------------------------------------- 1 | // You may write JavaScript here... 2 | 3 | console.log('Knock knock.'); -------------------------------------------------------------------------------- /03_functions/sandbox/sandbox.js: -------------------------------------------------------------------------------- 1 | // You may write JavaScript here... 2 | 3 | console.log('Ready to dive.'); -------------------------------------------------------------------------------- /03_functions/sandbox/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/03_functions/sandbox/bg.jpg -------------------------------------------------------------------------------- /01_fundamentals/sandbox/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/01_fundamentals/sandbox/bg.jpg -------------------------------------------------------------------------------- /03_functions/introduction/images/abyss.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/03_functions/introduction/images/abyss.jpg -------------------------------------------------------------------------------- /03_functions/introduction/images/depth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/03_functions/introduction/images/depth.jpg -------------------------------------------------------------------------------- /03_functions/introduction/images/intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/03_functions/introduction/images/intro.png -------------------------------------------------------------------------------- /03_functions/introduction/images/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/03_functions/introduction/images/texture.png -------------------------------------------------------------------------------- /03_functions/introduction/images/texture2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/03_functions/introduction/images/texture2.png -------------------------------------------------------------------------------- /01_fundamentals/introduction/images/javascript.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/01_fundamentals/introduction/images/javascript.png -------------------------------------------------------------------------------- /03_functions/introduction/fonts/BEBAS___-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/03_functions/introduction/fonts/BEBAS___-webfont.eot -------------------------------------------------------------------------------- /03_functions/introduction/fonts/BEBAS___-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/03_functions/introduction/fonts/BEBAS___-webfont.ttf -------------------------------------------------------------------------------- /03_functions/introduction/fonts/montserrat-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/03_functions/introduction/fonts/montserrat-400.woff2 -------------------------------------------------------------------------------- /03_functions/introduction/fonts/montserrat-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/03_functions/introduction/fonts/montserrat-700.woff2 -------------------------------------------------------------------------------- /03_functions/introduction/fonts/BEBAS___-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/03_functions/introduction/fonts/BEBAS___-webfont.woff -------------------------------------------------------------------------------- /01_fundamentals/introduction/fonts/BEBAS___-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/01_fundamentals/introduction/fonts/BEBAS___-webfont.eot -------------------------------------------------------------------------------- /01_fundamentals/introduction/fonts/BEBAS___-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/01_fundamentals/introduction/fonts/BEBAS___-webfont.ttf -------------------------------------------------------------------------------- /01_fundamentals/introduction/fonts/BEBAS___-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/01_fundamentals/introduction/fonts/BEBAS___-webfont.woff -------------------------------------------------------------------------------- /01_fundamentals/introduction/fonts/montserrat-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/01_fundamentals/introduction/fonts/montserrat-400.woff2 -------------------------------------------------------------------------------- /01_fundamentals/introduction/fonts/montserrat-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/01_fundamentals/introduction/fonts/montserrat-700.woff2 -------------------------------------------------------------------------------- /03_functions/exercises/pixel_art/solution/ps_neutral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/03_functions/exercises/pixel_art/solution/ps_neutral.png -------------------------------------------------------------------------------- /03_functions/exercises/pixel_art/starter/ps_neutral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/03_functions/exercises/pixel_art/starter/ps_neutral.png -------------------------------------------------------------------------------- /01_fundamentals/introduction/images/growth-mindset-video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmac/professional-javascript/HEAD/01_fundamentals/introduction/images/growth-mindset-video.png -------------------------------------------------------------------------------- /05_backbone/intro_to_backbone.md: -------------------------------------------------------------------------------- 1 | # Intro to Backbone 2 | 3 | Good introductions (maintained by the author) include: 4 | 5 | * [Backbone - Getting Started](http://backbonejs.org/#Model-View-separation) 6 | * [Backbone, The Primer](https://github.com/jashkenas/backbone/wiki/Backbone%2C-The-Primer) -------------------------------------------------------------------------------- /01_fundamentals/exercises/world_foodie/starter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |(use the javascript console)
10 | 15 | 16 | -------------------------------------------------------------------------------- /03_functions/exercises/pixel_art/starter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |It's like...
16 | 17 |It's like...
16 | 17 |
18 | (use the javascript console)
19 | 29 | 30 | -------------------------------------------------------------------------------- /02_dom/exercises/currency_converter/starter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
15 |
16 |
17 |
18 |
24 |
25 |
--------------------------------------------------------------------------------
/05_backbone/exercises/contact_list/complete.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | | Name | 18 |Remove | 20 | 21 | 22 |
| Name | 18 |Remove | 20 | 21 | 22 |
Open the JavaScript Console
(CMD + OPT + J)
18 | (use the javascript console)
19 | 38 | 39 | -------------------------------------------------------------------------------- /01_fundamentals/exercises/world_foodie/complete.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |(use the javascript console)
10 | 29 | 30 | -------------------------------------------------------------------------------- /05_backbone/exercises/startup_idea/js/app-complete.js: -------------------------------------------------------------------------------- 1 | // A model for loading and managing idea data: 2 | 3 | var IdeaModel = Backbone.Model.extend({ 4 | url: 'http://itsthisforthat.com/api.php?json', 5 | 6 | defaults: { 7 | 'this': '', 8 | 'that': '' 9 | }, 10 | 11 | reload: function() { 12 | return this.fetch({dataType: 'jsonp', jsonp: 'call'}); 13 | } 14 | }); 15 | 16 | 17 | // A view for displaying loaded idea data: 18 | 19 | var IdeaView = Backbone.View.extend({ 20 | el: '#idea', 21 | 22 | initialize: function() { 23 | this.listenTo(this.model, 'change', this.render); 24 | }, 25 | 26 | render: function() { 27 | var text = this.model.get('this') +' for '+ this.model.get('that'); 28 | this.$('#idea-text').html(text); 29 | }, 30 | 31 | events: { 32 | 'click #idea-reload': 'onReload' 33 | }, 34 | 35 | onReload: function(evt) { 36 | this.model.reload(); 37 | } 38 | }); 39 | 40 | 41 | // Create instances of the model and view: 42 | // the view is aware of the model, while the model is NOT aware of the view. 43 | 44 | var idea = new IdeaModel(); 45 | var ideaView = new IdeaView({model: idea}); 46 | -------------------------------------------------------------------------------- /03_functions/exercises/pixel_art/solution/README.md: -------------------------------------------------------------------------------- 1 | #Pixart 2 | 3 | Exercise to help nail down Event Listeners 4 | 5 | ###Step 1 6 | 7 | * When I click the "Set Color" button, it should change the color of the "brush" box to the color I specify in the input field. 8 | 9 | ###Step 2 10 | 11 | * The same thing should happen when I press the enter key from inside the input field 12 | 13 | ###Step 3 14 | 15 | * Using JavaScript, create 20 divs of the "square" class and append them to the body 16 | 17 | ###Step 4 18 | 19 | * Add functionality so that when I click on each "square", it changes the color of that individual square to "green" 20 | 21 | ###Step 5 22 | 23 | * Modify your code so that when I click on each "square", it changes to the color I set using my input instead of "green" every time. 24 | 25 | ###Step 6 26 | 27 | * Modify the CSS so that the "square" class has a height and width of 10px and a margin of 0. 28 | * Modify your code so that you are creating 8000 divs instead of 20. 29 | * Change the event that changes your box colors from 'click' to 'mouseover' 30 | * Paint a picture! 31 | 32 | ## Bonus 33 | 34 | * Add a color swatch. You should have 3 boxes with the most recent 3 colors used. When you click on each of those boxes, it should set the current brush color back to that color. -------------------------------------------------------------------------------- /03_functions/exercises/pixel_art/starter/README.md: -------------------------------------------------------------------------------- 1 | #Pixart 2 | 3 | Exercise to help nail down Event Listeners 4 | 5 | ###Step 1 6 | 7 | * When I click the "Set Color" button, it should change the color of the "brush" box to the color I specify in the input field. 8 | 9 | ###Step 2 10 | 11 | * The same thing should happen when I press the enter key from inside the input field 12 | 13 | ###Step 3 14 | 15 | * Using JavaScript, create 20 divs of the "square" class and append them to the body 16 | 17 | ###Step 4 18 | 19 | * Add functionality so that when I click on each "square", it changes the color of that individual square to "green" 20 | 21 | ###Step 5 22 | 23 | * Modify your code so that when I click on each "square", it changes to the color I set using my input instead of "green" every time. 24 | 25 | ###Step 6 26 | 27 | * Modify the CSS so that the "square" class has a height and width of 10px and a margin of 0. 28 | * Modify your code so that you are creating 8000 divs instead of 20. 29 | * Change the event that changes your box colors from 'click' to 'mouseover' 30 | * Paint a picture! 31 | 32 | ## Bonus 33 | 34 | * Add a color swatch. You should have 3 boxes with the most recent 3 colors used. When you click on each of those boxes, it should set the current brush color back to that color. -------------------------------------------------------------------------------- /02_dom/exercises/currency_converter/complete.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
15 |
16 |
17 |
18 |
34 |
35 |
--------------------------------------------------------------------------------
/03_functions/exercises/expense_tracker/complete/javascripts/expense.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var expenses = JSON.parse(localStorage.getItem('expenses') || '[]');
3 | var addFormEl = document.querySelector('#expense-add');
4 |
5 | addFormEl.addEventListener('submit', function(evt) {
6 | evt.preventDefault();
7 | var expense = {
8 | desc: document.querySelector('#expense-desc').value,
9 | amount: document.querySelector('#expense-amount').value,
10 | category: document.querySelector('#expense-category').value
11 | };
12 |
13 | expense.amount = parseFloat(expense.amount);
14 |
15 | if (!isNaN(expense.amount)) {
16 | expenses.push(expense);
17 | localStorage.setItem('expenses', JSON.stringify(expenses));
18 |
19 | addFormEl.reset();
20 | renderList();
21 | }
22 | });
23 |
24 | function renderList() {
25 | var html = '';
26 | var total = 0;
27 |
28 | for (var i=0; i < expenses.length; i++) {
29 | var exp = expenses[i];
30 | html += 'Take 5 minutes and read through the JavaScript in this file. What do you expect to be printed into the console? Then examine the console output and explain why each line prints what it does.
12 |Also try commenting and uncommenting different lines and think about what the var keyword does.
13 | 14 | 60 | 61 | -------------------------------------------------------------------------------- /03_functions/exercises/gryffindor_house/instructions.md: -------------------------------------------------------------------------------- 1 | # Gryffindor House 2 | 3 | Gryffindor needs help keeping track of the points awarded to their students. Let's build a simple program to assist. 4 | 5 | ## Objectives 6 | 7 | - Work with objects and functions. 8 | - Work with the "this" keyword. 9 | 10 | ## Process 11 | 12 | Review the provided JavaScript and note the `Gryffindor` object declaration and its empty methods. We want to configure the Gryffindor object to do the following: 13 | 14 | ### 1. Fill in the `addStudent` method(s) 15 | 16 | ``` 17 | Gryffindor.addStudent("Harry Potter"); 18 | 19 | // BONUS: 20 | Gryffindor.addStudents("Ron Weasley", "Hermionie Granger"); 21 | ``` 22 | 23 | These methods should add student objects to Gryffindor's `students` array. A new student object should be formatted as: 24 | 25 | ``` 26 | { 27 | name: "Harry Potter", 28 | points: 0 29 | } 30 | ``` 31 | 32 | ### 2. Fill in the `getStudent` method 33 | 34 | ``` 35 | Gryffindor.getStudent("Harry Potter"); 36 | 37 | // returns -> {name: "Harry Potter", points: 0} 38 | // or returns null if student isn't in the house. 39 | ``` 40 | 41 | Gets an existing student object by name from the array of students. You'll need to loop through the students array until you find a student with a matching name. 42 | 43 | ### 3. Fill in the `awardPointsTo` method 44 | 45 | ``` 46 | Gryffindor.awardPointsTo("Ron Weasley", 25); 47 | ``` 48 | 49 | Gets a student by name, and then add points to that student. Use the "getStudent" method to access the student object, and then add points to it. 50 | 51 | 52 | ### 4. Fill in the `getHousePoints` method 53 | 54 | ``` 55 | Gryffindor.getHousePoints(); 56 | ``` 57 | 58 | Gets the total points among all students in the house. You'll need to loop through all student objects and tally up their points. -------------------------------------------------------------------------------- /03_functions/exercises/lesson_practice/this.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |Take 5 minutes to read through the JavaScript in this file. What do you expect to be printed into the console? Then examine the console output and explain why each line prints what it does.
12 | 13 | 78 | 79 | -------------------------------------------------------------------------------- /03_functions/exercises/expense_tracker/complete/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 || Category | 40 |Description | 41 |Amount | 42 |
|---|---|---|
| Entertainment | 47 |Guardians of the Galaxy | 48 |$12.50 | 49 |
| TOTAL | 54 |55 | | $12.50 | 56 |
| Category | 40 |Description | 41 |Amount | 42 |
|---|---|---|
| Entertainment | 47 |Guardians of the Galaxy | 48 |$12.50 | 49 |
| TOTAL | 54 |55 | | $12.50 | 56 |
Open the JavaScript Console
(CMD + OPT + J)
14 |
15 | Contents
16 |The Muppet Show is a family-oriented comedy-variety television series that was produced by puppeteer Jim Henson and features The Muppets. After two pilot episodes produced in 1974 and 1975 failed to get the attention of America's network heads, Lew Grade approached Henson to produce the programme for ATV's ITV franchise in the UK. The show lasted for five series consisting of 120 episodes which were first broadcast in Britain between 5 September 1976 and 15 March 1981 over ATV and syndicated to local broadcast stations elsewhere. The programmes were recorded at ATV's Elstree Studios just north of London.
26 |Your favorite character:
38 |(Work in the JavaScript Console)
22 | 82 | 83 | -------------------------------------------------------------------------------- /05_backbone/exercises/contact_list/js/app-complete.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Model for managing a single contact: 3 | */ 4 | var ContactModel = Backbone.Model.extend({ 5 | defaults: { 6 | name: '', 7 | email: '' 8 | } 9 | }); 10 | 11 | /** 12 | * Collection for managing a list of contact models: 13 | */ 14 | var ContactList = Backbone.Collection.extend({ 15 | // Tell this collection what type of model to manage: 16 | model: ContactModel, 17 | 18 | // Configure the model to persist data in localStorage: 19 | // (we're providing a hash string to use as the namespace for our application data) 20 | localStorage: new Backbone.LocalStorage('4136a932eb7724a00cb87c3fb9e1ea1d') 21 | }); 22 | 23 | /** 24 | * View for managing the #add-contact form submissions: 25 | */ 26 | var AddContactView = Backbone.View.extend({ 27 | el: '#add-contact', 28 | 29 | events: { 30 | 'submit': 'onSubmit' 31 | }, 32 | 33 | onSubmit: function(evt) { 34 | // Stop form from refreshing the page: 35 | evt.preventDefault(); 36 | 37 | // Create a new contact: 38 | this.collection.create({ 39 | name: this.$('[name="name"]').val(), 40 | email: this.$('[name="email"]').val() 41 | }); 42 | 43 | // Blank out all form fields: 44 | this.el.reset(); 45 | } 46 | }); 47 | 48 | /** 49 | * View for displaying the list of contacts: 50 | */ 51 | var ListContactsView = Backbone.View.extend({ 52 | el: '#list-contacts', 53 | 54 | initialize: function() { 55 | this.listenTo(this.collection, 'sync remove', this.render); 56 | }, 57 | 58 | render: function() { 59 | // Start and empty string for building the list HTML: 60 | var list = ''; 61 | 62 | // Add table row HTML for each model stored in the collection: 63 | this.collection.each(function(model) { 64 | list += '
Benedict Cumberbatch
28 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /05_backbone/exercises/contact_list/js/vendor/backbone.localstorage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Backbone localStorage Adapter 3 | * Version 1.1.16 4 | * 5 | * https://github.com/jeromegn/Backbone.localStorage 6 | */ 7 | (function(a,b){typeof exports=="object"&&typeof require=="function"?module.exports=b(require("backbone")):typeof define=="function"&&define.amd?define(["backbone"],function(c){return b(c||a.Backbone)}):b(Backbone)})(this,function(a){function b(){return((1+Math.random())*65536|0).toString(16).substring(1)}function c(){return b()+b()+"-"+b()+"-"+b()+"-"+b()+"-"+b()+b()+b()}function d(a){return a===Object(a)}function e(a,b){var c=a.length;while(c--)if(a[c]===b)return!0;return!1}function f(a,b){for(var c in b)a[c]=b[c];return a}function g(a,b){if(a==null)return void 0;var c=a[b];return typeof c=="function"?a[b]():c}return a.LocalStorage=window.Store=function(a,b){if(!this.localStorage)throw"Backbone.localStorage: Environment does not support localStorage.";this.name=a,this.serializer=b||{serialize:function(a){return d(a)?JSON.stringify(a):a},deserialize:function(a){return a&&JSON.parse(a)}};var c=this.localStorage().getItem(this.name);this.records=c&&c.split(",")||[]},f(a.LocalStorage.prototype,{save:function(){this.localStorage().setItem(this.name,this.records.join(","))},create:function(a){return!a.id&&a.id!==0&&(a.id=c(),a.set(a.idAttribute,a.id)),this.localStorage().setItem(this._itemName(a.id),this.serializer.serialize(a)),this.records.push(a.id.toString()),this.save(),this.find(a)},update:function(a){this.localStorage().setItem(this._itemName(a.id),this.serializer.serialize(a));var b=a.id.toString();return e(this.records,b)||(this.records.push(b),this.save()),this.find(a)},find:function(a){return this.serializer.deserialize(this.localStorage().getItem(this._itemName(a.id)))},findAll:function(){var a=[];for(var b=0,c,d;b(Work in the JavaScript Console)
22 | 96 | 97 | -------------------------------------------------------------------------------- /02_dom/exercises/name_generator/complete.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
Benedict Cumberbatch
28 | 29 | 43 | 44 | -------------------------------------------------------------------------------- /03_functions/exercises/functions_deepdive/complete/function_spec.js: -------------------------------------------------------------------------------- 1 | // PART 1: Sunlight Zone: Arguments & Return 2 | // ----------------------------------------- 3 | 4 | it('will accept arguments and return values.', function() { 5 | 6 | function add(a, b) { 7 | return a + b; 8 | } 9 | 10 | expect( add(5, 10) ).toEqual(15); 11 | }); 12 | 13 | 14 | 15 | it('provides an arguments *object* that contains all arguments passed into the function.', function() { 16 | 17 | function test() { 18 | expect(arguments[0]).toBe(10); 19 | expect(arguments[1]).toBe(20); 20 | expect(arguments instanceof Array).toBeFalsy(); 21 | } 22 | 23 | test(10, 20); 24 | }); 25 | 26 | 27 | 28 | // PART 2: Twilight Zone: Closure & Scope 29 | // -------------------------------------- 30 | 31 | it('allows function scopes to reference outward, but not to look inward at nested closures.', function() { 32 | 33 | var outer = 10; 34 | 35 | function test() { 36 | var inner = 20; 37 | expect(outer).toBe(10); 38 | expect(inner).toBe(20); 39 | } 40 | 41 | test(); 42 | expect(outer).toBe(10); 43 | expect(typeof inner).toBe('undefined'); 44 | }); 45 | 46 | 47 | 48 | it('will override conflicting variable declarations in an inner scope. The outer scope is unaffected.', function() { 49 | 50 | var n = 5; 51 | 52 | function test() { 53 | var n = 20; 54 | expect(n).toBe(20); 55 | } 56 | 57 | test(); 58 | expect(n).toBe(5); 59 | }); 60 | 61 | 62 | 63 | it('allows inner scopes to access and modify variables declared in an outer scope.', function() { 64 | 65 | var n = 5; 66 | 67 | function test() { 68 | n = 20; 69 | } 70 | 71 | test(); 72 | expect(n).toBe(20); 73 | }); 74 | 75 | 76 | 77 | it('assigns all undeclared variables into global (window) scope.', function() { 78 | 79 | function test() { 80 | n = 20; 81 | } 82 | 83 | test(); 84 | expect(window.n).toBe(20); 85 | }); 86 | 87 | 88 | 89 | it('allows Immedaitely-Invoked Function Expressions (IIFE) to set up a private scope.', function() { 90 | 91 | var invoked = false; 92 | 93 | (function() { 94 | invoked = true; 95 | 96 | // My code goes here... 97 | 98 | })(); 99 | 100 | expect(invoked).toBe(true); 101 | }); 102 | 103 | 104 | 105 | // PART 3: Midnight Zone: Contextual Invocation 106 | // -------------------------------------------- 107 | 108 | it('will bind the Function Invocation pattern to the global object.', function() { 109 | 110 | function test() { 111 | return this; 112 | } 113 | 114 | expect(test()).toBe(window); 115 | }); 116 | 117 | 118 | 119 | it('will bind the Call Invocation pattern to a passed object.', function() { 120 | 121 | function test(a, b) { 122 | expect(a).toBe(5); 123 | expect(b).toBe(10); 124 | return this; 125 | } 126 | 127 | var target = {}; 128 | var calledOn = test.call(target, 5, 10); 129 | 130 | expect( calledOn ).toBe(target); 131 | }); 132 | 133 | 134 | 135 | it('will bind the Method Invocation pattern to the host object.', function() { 136 | 137 | var obj = { 138 | test: function() { 139 | return this; 140 | } 141 | }; 142 | 143 | expect( obj.test() ).toBe(obj); 144 | }); 145 | 146 | 147 | 148 | it('will bind the Constructor Invocation pattern to a brand new object instance.', function() { 149 | 150 | function TestWidget(name) { 151 | this.name = name; 152 | } 153 | 154 | var beep = new TestWidget('beep'); 155 | var bop = new TestWidget('bop'); 156 | 157 | expect(beep.name).toBe('beep'); 158 | expect(bop.name).toBe('bop'); 159 | }); -------------------------------------------------------------------------------- /01_fundamentals/exercises/hammonds_mine/starter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
18 | (use the javascript console)
19 | 20 | 134 | 135 | -------------------------------------------------------------------------------- /03_functions/exercises/functions_deepdive/lib/jasmine.css: -------------------------------------------------------------------------------- 1 | body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } 2 | 3 | .html-reporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } 4 | .html-reporter a { text-decoration: none; } 5 | .html-reporter a:hover { text-decoration: underline; } 6 | .html-reporter p, .html-reporter h1, .html-reporter h2, .html-reporter h3, .html-reporter h4, .html-reporter h5, .html-reporter h6 { margin: 0; line-height: 14px; } 7 | .html-reporter .banner, .html-reporter .symbol-summary, .html-reporter .summary, .html-reporter .result-message, .html-reporter .spec .description, .html-reporter .spec-detail .description, .html-reporter .alert .bar, .html-reporter .stack-trace { padding-left: 9px; padding-right: 9px; } 8 | .html-reporter .banner .version { margin-left: 14px; } 9 | .html-reporter #jasmine_content { position: fixed; right: 100%; } 10 | .html-reporter .version { color: #aaaaaa; } 11 | .html-reporter .banner { margin-top: 14px; } 12 | .html-reporter .duration { color: #aaaaaa; float: right; } 13 | .html-reporter .symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; } 14 | .html-reporter .symbol-summary li { display: inline-block; height: 8px; width: 14px; font-size: 16px; } 15 | .html-reporter .symbol-summary li.passed { font-size: 14px; } 16 | .html-reporter .symbol-summary li.passed:before { color: #5e7d00; content: "\02022"; } 17 | .html-reporter .symbol-summary li.failed { line-height: 9px; } 18 | .html-reporter .symbol-summary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } 19 | .html-reporter .symbol-summary li.disabled { font-size: 14px; } 20 | .html-reporter .symbol-summary li.disabled:before { color: #bababa; content: "\02022"; } 21 | .html-reporter .symbol-summary li.pending { line-height: 17px; } 22 | .html-reporter .symbol-summary li.pending:before { color: #ba9d37; content: "*"; } 23 | .html-reporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } 24 | .html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } 25 | .html-reporter .bar.failed { background-color: #b03911; } 26 | .html-reporter .bar.passed { background-color: #a6b779; } 27 | .html-reporter .bar.skipped { background-color: #bababa; } 28 | .html-reporter .bar.menu { background-color: #fff; color: #aaaaaa; } 29 | .html-reporter .bar.menu a { color: #333333; } 30 | .html-reporter .bar a { color: white; } 31 | .html-reporter.spec-list .bar.menu.failure-list, .html-reporter.spec-list .results .failures { display: none; } 32 | .html-reporter.failure-list .bar.menu.spec-list, .html-reporter.failure-list .summary { display: none; } 33 | .html-reporter .running-alert { background-color: #666666; } 34 | .html-reporter .results { margin-top: 14px; } 35 | .html-reporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } 36 | .html-reporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } 37 | .html-reporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } 38 | .html-reporter.showDetails .summary { display: none; } 39 | .html-reporter.showDetails #details { display: block; } 40 | .html-reporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } 41 | .html-reporter .summary { margin-top: 14px; } 42 | .html-reporter .summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; } 43 | .html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; } 44 | .html-reporter .summary li.passed a { color: #5e7d00; } 45 | .html-reporter .summary li.failed a { color: #b03911; } 46 | .html-reporter .summary li.pending a { color: #ba9d37; } 47 | .html-reporter .description + .suite { margin-top: 0; } 48 | .html-reporter .suite { margin-top: 14px; } 49 | .html-reporter .suite a { color: #333333; } 50 | .html-reporter .failures .spec-detail { margin-bottom: 28px; } 51 | .html-reporter .failures .spec-detail .description { background-color: #b03911; } 52 | .html-reporter .failures .spec-detail .description a { color: white; } 53 | .html-reporter .result-message { padding-top: 14px; color: #333333; white-space: pre; } 54 | .html-reporter .result-message span.result { display: block; } 55 | .html-reporter .stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } 56 | -------------------------------------------------------------------------------- /01_fundamentals/exercises/hammonds_mine/complete.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
18 | (use the javascript console)
19 | 20 | 162 | 163 | -------------------------------------------------------------------------------- /03_functions/exercises/functions_deepdive/lib/console.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2008-2013 Pivotal Labs 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | function getJasmineRequireObj() { 24 | if (typeof module !== "undefined" && module.exports) { 25 | return exports; 26 | } else { 27 | window.jasmineRequire = window.jasmineRequire || {}; 28 | return window.jasmineRequire; 29 | } 30 | } 31 | 32 | getJasmineRequireObj().console = function(jRequire, j$) { 33 | j$.ConsoleReporter = jRequire.ConsoleReporter(); 34 | }; 35 | 36 | getJasmineRequireObj().ConsoleReporter = function() { 37 | 38 | var noopTimer = { 39 | start: function(){}, 40 | elapsed: function(){ return 0; } 41 | }; 42 | 43 | function ConsoleReporter(options) { 44 | var print = options.print, 45 | showColors = options.showColors || false, 46 | onComplete = options.onComplete || function() {}, 47 | timer = options.timer || noopTimer, 48 | specCount, 49 | failureCount, 50 | failedSpecs = [], 51 | pendingCount, 52 | ansi = { 53 | green: '\x1B[32m', 54 | red: '\x1B[31m', 55 | yellow: '\x1B[33m', 56 | none: '\x1B[0m' 57 | }; 58 | 59 | this.jasmineStarted = function() { 60 | specCount = 0; 61 | failureCount = 0; 62 | pendingCount = 0; 63 | print("Started"); 64 | printNewline(); 65 | timer.start(); 66 | }; 67 | 68 | this.jasmineDone = function() { 69 | printNewline(); 70 | for (var i = 0; i < failedSpecs.length; i++) { 71 | specFailureDetails(failedSpecs[i]); 72 | } 73 | 74 | printNewline(); 75 | var specCounts = specCount + " " + plural("spec", specCount) + ", " + 76 | failureCount + " " + plural("failure", failureCount); 77 | 78 | if (pendingCount) { 79 | specCounts += ", " + pendingCount + " pending " + plural("spec", pendingCount); 80 | } 81 | 82 | print(specCounts); 83 | 84 | printNewline(); 85 | var seconds = timer.elapsed() / 1000; 86 | print("Finished in " + seconds + " " + plural("second", seconds)); 87 | 88 | printNewline(); 89 | 90 | onComplete(failureCount === 0); 91 | }; 92 | 93 | this.specDone = function(result) { 94 | specCount++; 95 | 96 | if (result.status == "pending") { 97 | pendingCount++; 98 | print(colored("yellow", "*")); 99 | return; 100 | } 101 | 102 | if (result.status == "passed") { 103 | print(colored("green", '.')); 104 | return; 105 | } 106 | 107 | if (result.status == "failed") { 108 | failureCount++; 109 | failedSpecs.push(result); 110 | print(colored("red", 'F')); 111 | } 112 | }; 113 | 114 | return this; 115 | 116 | function printNewline() { 117 | print("\n"); 118 | } 119 | 120 | function colored(color, str) { 121 | return showColors ? (ansi[color] + str + ansi.none) : str; 122 | } 123 | 124 | function plural(str, count) { 125 | return count == 1 ? str : str + "s"; 126 | } 127 | 128 | function repeat(thing, times) { 129 | var arr = []; 130 | for (var i = 0; i < times; i++) { 131 | arr.push(thing); 132 | } 133 | return arr; 134 | } 135 | 136 | function indent(str, spaces) { 137 | var lines = (str || '').split("\n"); 138 | var newArr = []; 139 | for (var i = 0; i < lines.length; i++) { 140 | newArr.push(repeat(" ", spaces).join("") + lines[i]); 141 | } 142 | return newArr.join("\n"); 143 | } 144 | 145 | function specFailureDetails(result) { 146 | printNewline(); 147 | print(result.fullName); 148 | 149 | for (var i = 0; i < result.failedExpectations.length; i++) { 150 | var failedExpectation = result.failedExpectations[i]; 151 | printNewline(); 152 | print(indent(failedExpectation.stack, 2)); 153 | } 154 | 155 | printNewline(); 156 | } 157 | } 158 | 159 | return ConsoleReporter; 160 | }; 161 | -------------------------------------------------------------------------------- /01_fundamentals/introduction/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |(Work in the JavaScript Console)
22 | 159 | 160 | -------------------------------------------------------------------------------- /01_fundamentals/introduction/stylesheets/intro.css: -------------------------------------------------------------------------------- 1 | html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font:inherit;font-size:100%;vertical-align:baseline}html{line-height:1}ol,ul{list-style:none}table{border-collapse:collapse;border-spacing:0}caption,th,td{text-align:left;font-weight:normal;vertical-align:middle}q,blockquote{quotes:none}q:before,q:after,blockquote:before,blockquote:after{content:"";content:none}a img{border:none}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}@font-face{font-family:'BebasRegular';src:url("../fonts/BEBAS___-webfont.eot");src:url("../fonts/BEBAS___-webfont.eot?#iefix") format("embedded-opentype"),url("../fonts/BEBAS___-webfont.woff") format("woff"),url("../fonts/BEBAS___-webfont.ttf") format("truetype"),url("../fonts/BEBAS___-webfont.svg#BebasRegular") format("svg");font-weight:normal;font-style:normal}@font-face{font-family:'Montserrat';font-style:normal;font-weight:400;src:local("Montserrat-Regular"),url("../fonts/montserrat-400.woff2") format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2212,U+2215,U+E0FF,U+EFFD,U+F000}@font-face{font-family:'Montserrat';font-style:normal;font-weight:700;src:local("Montserrat-Bold"),url("../fonts/montserrat-700.woff2") format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2212,U+2215,U+E0FF,U+EFFD,U+F000}html,body,.slide{background-color:#f4f4f4;height:100%;margin:0}body{font:200 30px/1.75 "Montserrat", "Helvetica Neue", Helvetica, sans-serif}h1,h2,h3{color:#6F96A8;font:400 80px/1.4 "BebasRegular", "Helvetica Neue", Helvetica, sans-serif, sans-serif;word-spacing:0.15em}h1 em,h2 em,h3 em{display:block;font-size:50%;font-style:normal}h1 u,h2 u,h3 u{border-bottom:8px solid #6F96A8;padding-bottom:5px;text-decoration:none}h3{color:#a1bac6;font-size:40px;margin-bottom:0.25em}em{font-style:italic}code{font-family:"Courier", monospace}.slide{border:1px solid transparent;box-sizing:border-box;overflow:hidden;position:relative}.slide iframe{height:100%;left:0;position:absolute;top:0;width:100%;z-index:1}.slide img{max-width:90%}.slide blockquote{margin:5% auto 0;padding:0;width:80%}.slide blockquote footer{font-size:0.75em}.section,.sub-section{background-color:#0d283a;color:white}.section h1,.section h2,.sub-section h1,.sub-section h2{color:white}.sub-section{background-color:#6F96A8}.top-left{left:10%;position:absolute;top:10%;z-index:100}.bottom-left{bottom:10%;left:10%;position:absolute;z-index:100}.bottom-right{bottom:10%;right:10%;position:absolute;z-index:100}.bottom-panel{background-color:rgba(0,0,0,0.5);bottom:0;box-sizing:border-box;left:0;padding:1% 10% 2.5%;position:absolute;width:100%;z-index:100}.bottom-panel p{margin:0}.center-content{position:absolute;text-align:center;top:50%;-webkit-transform:translateY(-55%);transform:translateY(-55%);width:100%;z-index:100}.center-content h1,.center-content h2{margin-bottom:50px}.center-content h1+p,.center-content h2+p{margin-top:-40px}.center-content p{padding:0 0 50px}.column-list{-webkit-column-count:2;-moz-column-count:2;column-count:2;margin:0 auto;text-align:left;width:60%}.column-layout{display:table;margin:0 auto;table-layout:fixed}.column-layout .column{display:table-cell;text-align:left}.bullet-list{list-style:disc outside none;margin-left:15px;padding-left:15px;text-align:left}.smaller{font-size:0.75em}h2.smaller{font-size:65px}blockquote .smaller{font-size:0.9em}.vox-logo{position:relative;top:15px;width:200px}.vox-cardstack{bottom:0;max-height:65%;position:absolute;right:5%}.cycle-of-change{padding-top:40px}.growth-mindset{margin:0 auto;width:85%}.growth-mindset img{float:left;position:relative;top:0.3em;width:40%}.growth-mindset blockquote{box-sizing:border-box;margin:0;overflow:hidden;padding-left:5%;text-align:left;width:60%}.growth-mindset footer{font-size:22px}.junior-senior{min-width:300px}.learn-languages ul{margin:0 auto;width:75%}.learn-languages img{opacity:0.3;padding-top:50px}.learn-languages .attribution{font-size:0.5em;padding-top:50px}.javascript-intro{max-height:50% !important;max-width:60% !important}.javascript-understanding{width:70% !important}.javascript-difficulty{max-height:calc(69%)}.real-parts{width:80%}.real-parts .column{font-size:0.75em;text-align:center}.backend-frontend{width:70%}.mv-separation{margin:0 auto;position:relative;width:80%}.mv-separation .divider{border-left:1px solid #bbb;display:block;height:calc(100%);left:50%;position:absolute;top:0;width:0;z-index:1}.mv-separation img,.mv-separation .column-list{position:relative;width:100%;z-index:2}.mv-separation .right{padding-right:30px;text-align:right}.mv-separation .left{padding-left:30px}.mv-one-to-many{max-width:900px}.backbone-rest{border:3px solid #6F96A8;font:24px/2 "Courier", monospace;margin:0 auto 20px;width:70%}.backbone-rest td{padding:0 20px}.backbone-rest em{color:#ccc;font-style:normal}.backbone-rest .component{background-color:#6F96A8;color:white;text-align:center;width:25%}.backbone-rest.model{border-color:#6F96A8}.backbone-rest.model .component{background-color:#6F96A8}@media (max-width: 900px){.backbone-rest{width:80%}.backbone-rest td{font-size:22px !important}}.backbone-parse{font:22px/1.4 "Courier", monospace;padding-top:25px}.backbone-parse b{color:#EF4836;font-weight:bold}.backbone-parse .column.left{border-right:1px solid #ccc;padding-right:75px}.backbone-parse .column.right{padding-left:75px}@media (max-width: 900px){.backbone-parse{font-size:20px}.backbone-parse .column.left{padding-right:50px}.backbone-parse .column.right{padding-left:50px}}.backbone-pattern{left:1.5%;position:relative}.routers{max-width:800px;width:60%}.conway{background:url("../images/conway.gif") no-repeat center center;background-size:cover}.logo-soup{margin-top:1em}.logo-soup li{display:inline-block;height:50px;margin-right:1em;vertical-align:middle}.logo-soup img{max-height:50px;max-width:230px} 2 | .left { 3 | text-align: left !important; 4 | } 5 | .bottom-right-note { 6 | background-color: #6F96A8; 7 | border-radius: 15px; 8 | bottom: 30px; 9 | color: white; 10 | padding: 20px 30px; 11 | position: absolute; 12 | right: 30px; 13 | z-index: 100; 14 | } 15 | .bottom-right-note h3 { 16 | color: white; 17 | } 18 | .graphic { 19 | background-position: center center; 20 | background-repeat: no-repeat; 21 | background-size: contain; 22 | height: 90%; 23 | margin: 5%; 24 | } -------------------------------------------------------------------------------- /03_functions/introduction/stylesheets/intro.css: -------------------------------------------------------------------------------- 1 | html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font:inherit;font-size:100%;vertical-align:baseline}html{line-height:1}ol,ul{list-style:none}table{border-collapse:collapse;border-spacing:0}caption,th,td{text-align:left;font-weight:normal;vertical-align:middle}q,blockquote{quotes:none}q:before,q:after,blockquote:before,blockquote:after{content:"";content:none}a img{border:none}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}@font-face{font-family:'BebasRegular';src:url("../fonts/BEBAS___-webfont.eot");src:url("../fonts/BEBAS___-webfont.eot?#iefix") format("embedded-opentype"),url("../fonts/BEBAS___-webfont.woff") format("woff"),url("../fonts/BEBAS___-webfont.ttf") format("truetype"),url("../fonts/BEBAS___-webfont.svg#BebasRegular") format("svg");font-weight:normal;font-style:normal}@font-face{font-family:'Montserrat';font-style:normal;font-weight:400;src:local("Montserrat-Regular"),url("../fonts/montserrat-400.woff2") format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2212,U+2215,U+E0FF,U+EFFD,U+F000}@font-face{font-family:'Montserrat';font-style:normal;font-weight:700;src:local("Montserrat-Bold"),url("../fonts/montserrat-700.woff2") format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2212,U+2215,U+E0FF,U+EFFD,U+F000}html,body,.slide{background-color:#f4f4f4;height:100%;margin:0}body{font:200 30px/1.75 "Montserrat", "Helvetica Neue", Helvetica, sans-serif}h1,h2,h3{color:#6F96A8;font:400 80px/1.4 "BebasRegular", "Helvetica Neue", Helvetica, sans-serif, sans-serif;word-spacing:0.15em}h1 em,h2 em,h3 em{display:block;font-size:50%;font-style:normal}h1 u,h2 u,h3 u{border-bottom:8px solid #6F96A8;padding-bottom:5px;text-decoration:none}h3{color:#a1bac6;font-size:40px;margin-bottom:0.25em}em{font-style:italic}code{font-family:"Courier", monospace}.slide{border:1px solid transparent;box-sizing:border-box;overflow:hidden;position:relative}.slide iframe{height:100%;left:0;position:absolute;top:0;width:100%;z-index:1}.slide img{max-width:90%}.slide blockquote{margin:5% auto 0;padding:0;width:80%}.slide blockquote footer{font-size:0.75em}.section,.sub-section{background-color:#0d283a;color:white}.section h1,.section h2,.sub-section h1,.sub-section h2{color:white}.sub-section{background-color:#6F96A8}.top-left{left:10%;position:absolute;top:10%;z-index:100}.bottom-left{bottom:10%;left:10%;position:absolute;z-index:100}.bottom-right{bottom:10%;right:10%;position:absolute;z-index:100}.bottom-panel{background-color:rgba(0,0,0,0.5);bottom:0;box-sizing:border-box;left:0;padding:1% 10% 2.5%;position:absolute;width:100%;z-index:100}.bottom-panel p{margin:0}.center-content{position:absolute;text-align:center;top:50%;-webkit-transform:translateY(-55%);transform:translateY(-55%);width:100%;z-index:100}.center-content h1,.center-content h2{margin-bottom:50px}.center-content h1+p,.center-content h2+p{margin-top:-40px}.center-content p{padding:0 0 50px}.column-list{-webkit-column-count:2;-moz-column-count:2;column-count:2;margin:0 auto;text-align:left;width:60%}.column-layout{display:table;margin:0 auto;table-layout:fixed}.column-layout .column{display:table-cell;text-align:left}.bullet-list{list-style:disc outside none;margin-left:15px;padding-left:15px;text-align:left}.smaller{font-size:0.75em}h2.smaller{font-size:65px}blockquote .smaller{font-size:0.9em}.vox-logo{position:relative;top:15px;width:200px}.vox-cardstack{bottom:0;max-height:65%;position:absolute;right:5%}.cycle-of-change{padding-top:40px}.growth-mindset{margin:0 auto;width:85%}.growth-mindset img{float:left;position:relative;top:0.3em;width:40%}.growth-mindset blockquote{box-sizing:border-box;margin:0;overflow:hidden;padding-left:5%;text-align:left;width:60%}.growth-mindset footer{font-size:22px}.junior-senior{min-width:300px}.learn-languages ul{margin:0 auto;width:75%}.learn-languages img{opacity:0.3;padding-top:50px}.learn-languages .attribution{font-size:0.5em;padding-top:50px}.javascript-intro{max-height:50% !important;max-width:60% !important}.javascript-understanding{width:70% !important}.javascript-difficulty{max-height:calc(69%)}.real-parts{width:80%}.real-parts .column{font-size:0.75em;text-align:center}.backend-frontend{width:70%}.mv-separation{margin:0 auto;position:relative;width:80%}.mv-separation .divider{border-left:1px solid #bbb;display:block;height:calc(100%);left:50%;position:absolute;top:0;width:0;z-index:1}.mv-separation img,.mv-separation .column-list{position:relative;width:100%;z-index:2}.mv-separation .right{padding-right:30px;text-align:right}.mv-separation .left{padding-left:30px}.mv-one-to-many{max-width:900px}.backbone-rest{border:3px solid #6F96A8;font:24px/2 "Courier", monospace;margin:0 auto 20px;width:70%}.backbone-rest td{padding:0 20px}.backbone-rest em{color:#ccc;font-style:normal}.backbone-rest .component{background-color:#6F96A8;color:white;text-align:center;width:25%}.backbone-rest.model{border-color:#6F96A8}.backbone-rest.model .component{background-color:#6F96A8}@media (max-width: 900px){.backbone-rest{width:80%}.backbone-rest td{font-size:22px !important}}.backbone-parse{font:22px/1.4 "Courier", monospace;padding-top:25px}.backbone-parse b{color:#EF4836;font-weight:bold}.backbone-parse .column.left{border-right:1px solid #ccc;padding-right:75px}.backbone-parse .column.right{padding-left:75px}@media (max-width: 900px){.backbone-parse{font-size:20px}.backbone-parse .column.left{padding-right:50px}.backbone-parse .column.right{padding-left:50px}}.backbone-pattern{left:1.5%;position:relative}.routers{max-width:800px;width:60%}.conway{background:url("../images/conway.gif") no-repeat center center;background-size:cover}.logo-soup{margin-top:1em}.logo-soup li{display:inline-block;height:50px;margin-right:1em;vertical-align:middle}.logo-soup img{max-height:50px;max-width:230px} 2 | .left { 3 | text-align: left !important; 4 | } 5 | .bottom-right-note { 6 | background-color: #6F96A8; 7 | border-radius: 15px; 8 | bottom: 30px; 9 | color: white; 10 | padding: 20px 30px; 11 | position: absolute; 12 | right: 30px; 13 | z-index: 100; 14 | } 15 | .bottom-right-note h3 { 16 | color: white; 17 | } 18 | .graphic { 19 | background-position: center center; 20 | background-repeat: no-repeat; 21 | background-size: contain; 22 | height: 90%; 23 | margin: 5%; 24 | } -------------------------------------------------------------------------------- /05_backbone/exercises/startup_idea/instructions.md: -------------------------------------------------------------------------------- 1 | # Startup Ideas 2 | 3 | This exercise uses the small (but incredibly entertaining) [This for That API](http://itsthisforthat.com/api.php) to build a simple single-page application. 4 | 5 | **Learning Objectives** 6 | 7 | - What is a Model? 8 | - What is a View? 9 | - Why do we practice Model/View separation? 10 | 11 | ## 1. Make a Model 12 | 13 | Add the following at the top of your `app.js` file: 14 | 15 | ``` 16 | // A model for loading and managing idea data: 17 | 18 | var IdeaModel = Backbone.Model.extend({ 19 | // Define a remote URL for this model to fetch data from: 20 | url: 'http://itsthisforthat.com/api.php?json', 21 | 22 | // Set default values for the fields of data that we expect to load: 23 | defaults: { 24 | 'this': '', 25 | 'that': '' 26 | }, 27 | 28 | // Create a method that loads model data: 29 | // (normally we'd just call "fetch" on a model to load data, 30 | // however this API requires some special parameters to load data from it) 31 | reload: function() { 32 | return this.fetch({dataType: 'jsonp', jsonp: 'call'}); 33 | } 34 | }); 35 | ``` 36 | 37 | This model will manage the data used by our application. It does a few things: 38 | 39 | * It defines a `url` that we'll load data from. 40 | 41 | * It defines default values for the fields of data we expect to load from the API. 42 | 43 | * Lastly, this API requires some special configuration settings to load data from it. Rather than including these configuration settings everwhere that we need to request data, we've just written a method that will handle loading data with the proper configuration. Now, we simply need to call `.reload()` on our model to have it fetch data from the API. 44 | 45 | ## 2. Instantiate the model 46 | 47 | Whenever we configure a component, Backbone gives us a component _class_. Think of a class as a blueprint: once we have a blueprint for a house, we can build _instances_ of that house using the blueprint. In order to actually use our model, we'll need to create an instance of its class: 48 | 49 | Add this to the bottom of `app.js`: 50 | 51 | ``` 52 | /* 53 | * Instantiate Components: 54 | */ 55 | 56 | var idea = new IdeaModel(); 57 | ``` 58 | 59 | **DID IT WORK?** 60 | 61 | Reload the page... nothing happened (that we could see!). This is where the JavaScript Console is our best friend: it allows us to interact with our application before graphics are present on the screen. In Chrome, open up the JavaScript console using `View > Developer > JavaScript Console`. 62 | 63 | In the console, type the following and then hit ENTER: 64 | 65 | ``` 66 | idea.toJSON(); 67 | ``` 68 | 69 | Here, we've asked our `idea` model to render out its data. It gives us something that looks suspiciously like its default settings: 70 | 71 | ``` 72 | Object {this: "", that: ""} 73 | ``` 74 | 75 | Okay, now let's tell our model to `reload`: 76 | 77 | ``` 78 | idea.reload(); 79 | ``` 80 | 81 | Did anything happen? Well, try asking it to print out its data again: 82 | 83 | ``` 84 | idea.toJSON(); 85 | ``` 86 | 87 | This time, you should see something that looks like random ideas for a great startup pitch! Try reloading and printing out data again... fun! 88 | 89 | ## 3. Make a view 90 | 91 | Alright, this console stuff is fun, but we want to see these great ideas print out on the screen! This is pretty simple... now that we have a model to fetch and store data, we just need something that watches the model, and prints out its data whenever it changes. 92 | 93 | Add this just _above_ the `/* Instantiate Components */` section of `app.js`: 94 | 95 | ``` 96 | // A view for displaying loaded idea data: 97 | 98 | var IdeaView = Backbone.View.extend({ 99 | // Attach this view to the "idea" element in the DOM: 100 | el: '#idea', 101 | 102 | // Upon creating this view, have it listen for "change" events in its model. 103 | // Whenever the model changes, we want our view to re-render its graphics: 104 | initialize: function() { 105 | this.listenTo(this.model, 'change', this.render); 106 | }, 107 | 108 | // Renders model data onto the screen. 109 | // This is called whenever the model changes. 110 | render: function() { 111 | // Build a text representation of our model data, 112 | // and then insert it into the "idea-text" element within the view. 113 | var text = this.model.get('this') +' for '+ this.model.get('that'); 114 | this.$('#idea-text').html(text); 115 | }, 116 | 117 | // Have Backbone configure a click event on the "idea-reload" button: 118 | // whenever "idea-reload" is clicked, we'll call this view's "onReload" method: 119 | events: { 120 | 'click #idea-reload': 'onReload' 121 | }, 122 | 123 | // Called whenever the user clicks the "idea-reload" button: 124 | // this simply tells the model to reload itself. 125 | onReload: function(evt) { 126 | this.model.reload(); 127 | } 128 | }); 129 | ``` 130 | 131 | This view is responsible for translating data stored in our model into graphics that will appear on the screen. Whenever data within our model changes, this view will observe the change, and render the newest data onto the screen. 132 | 133 | ## 4. Instantiate the view 134 | 135 | Just like our model, our view also needs to be instantiated. Adjust the `/* Instantiate Components */` section in `app.js` to this: 136 | 137 | ``` 138 | /* 139 | * Instantiate Components: 140 | */ 141 | 142 | var idea = new IdeaModel(); 143 | var ideaView = new IdeaView({model: idea}); 144 | ``` 145 | 146 | Now we're instantiating both the model and the view; and also instructing the view on what model we want it to render. Reload the page and start clicking the "Hit Me!" button... fun! 147 | 148 | **DID WE DO IT RIGHT?** 149 | 150 | Well-designed applications have a signature: their design is so robust, that they can be fully controlled via the console. Let's try it... open the JavaScript console again, watch your app screen, and run: 151 | 152 | ``` 153 | idea.reload(); 154 | ``` 155 | 156 | Did your view auto-magically update? Congratulations, you're on your way to building better application. 157 | 158 | **OKAY, BUT WHY?! HOW?!?** 159 | 160 | Classic "spaghetti code" apps make a mess by intertwining data and display. The same proceedures attempt to manage data and display, and more importantly, to keep them in sync. As an application gets bigger, this task of synchronizing becomes increasingly difficult. 161 | 162 | Model/View separation takes a different approach. PUT THE DATA FIRST. Graphics should be fairly negligable in application design... They're simply a human-friendly presentation of what is inherently stateful data. By focusing on data first (the model), we're establishing the core of what our application _does_. Once our model is doing its primary job of managing data, a view can simply observe that model, and render out state whenever the data changes. 163 | -------------------------------------------------------------------------------- /03_functions/exercises/functions_deepdive/lib/boot.js: -------------------------------------------------------------------------------- 1 | /** 2 | Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project. 3 | 4 | If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms. 5 | 6 | The location of `boot.js` can be specified and/or overridden in `jasmine.yml`. 7 | 8 | [jasmine-gem]: http://github.com/pivotal/jasmine-gem 9 | */ 10 | 11 | (function() { 12 | 13 | /** 14 | * ## Require & Instantiate 15 | * 16 | * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference. 17 | */ 18 | window.jasmine = jasmineRequire.core(jasmineRequire); 19 | 20 | /** 21 | * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference. 22 | */ 23 | jasmineRequire.html(jasmine); 24 | 25 | /** 26 | * Create the Jasmine environment. This is used to run all specs in a project. 27 | */ 28 | var env = jasmine.getEnv(); 29 | 30 | /** 31 | * ## The Global Interface 32 | * 33 | * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged. 34 | */ 35 | var jasmineInterface = { 36 | describe: function(description, specDefinitions) { 37 | return env.describe(description, specDefinitions); 38 | }, 39 | 40 | xdescribe: function(description, specDefinitions) { 41 | return env.xdescribe(description, specDefinitions); 42 | }, 43 | 44 | it: function(desc, func) { 45 | return env.it(desc, func); 46 | }, 47 | 48 | xit: function(desc, func) { 49 | return env.xit(desc, func); 50 | }, 51 | 52 | beforeEach: function(beforeEachFunction) { 53 | return env.beforeEach(beforeEachFunction); 54 | }, 55 | 56 | afterEach: function(afterEachFunction) { 57 | return env.afterEach(afterEachFunction); 58 | }, 59 | 60 | expect: function(actual) { 61 | return env.expect(actual); 62 | }, 63 | 64 | pending: function() { 65 | return env.pending(); 66 | }, 67 | 68 | spyOn: function(obj, methodName) { 69 | return env.spyOn(obj, methodName); 70 | }, 71 | 72 | jsApiReporter: new jasmine.JsApiReporter({ 73 | timer: new jasmine.Timer() 74 | }) 75 | }; 76 | 77 | /** 78 | * Add all of the Jasmine global/public interface to the proper global, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`. 79 | */ 80 | if (typeof window == "undefined" && typeof exports == "object") { 81 | extend(exports, jasmineInterface); 82 | } else { 83 | extend(window, jasmineInterface); 84 | } 85 | 86 | /** 87 | * Expose the interface for adding custom equality testers. 88 | */ 89 | jasmine.addCustomEqualityTester = function(tester) { 90 | env.addCustomEqualityTester(tester); 91 | }; 92 | 93 | /** 94 | * Expose the interface for adding custom expectation matchers 95 | */ 96 | jasmine.addMatchers = function(matchers) { 97 | return env.addMatchers(matchers); 98 | }; 99 | 100 | /** 101 | * Expose the mock interface for the JavaScript timeout functions 102 | */ 103 | jasmine.clock = function() { 104 | return env.clock; 105 | }; 106 | 107 | /** 108 | * ## Runner Parameters 109 | * 110 | * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface. 111 | */ 112 | 113 | var queryString = new jasmine.QueryString({ 114 | getWindowLocation: function() { return window.location; } 115 | }); 116 | 117 | var catchingExceptions = queryString.getParam("catch"); 118 | env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions); 119 | 120 | /** 121 | * ## Reporters 122 | * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any). 123 | */ 124 | var htmlReporter = new jasmine.HtmlReporter({ 125 | env: env, 126 | onRaiseExceptionsClick: function() { queryString.setParam("catch", !env.catchingExceptions()); }, 127 | getContainer: function() { return document.body; }, 128 | createElement: function() { return document.createElement.apply(document, arguments); }, 129 | createTextNode: function() { return document.createTextNode.apply(document, arguments); }, 130 | timer: new jasmine.Timer() 131 | }); 132 | 133 | /** 134 | * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript. 135 | */ 136 | env.addReporter(jasmineInterface.jsApiReporter); 137 | env.addReporter(htmlReporter); 138 | 139 | /** 140 | * Filter which specs will be run by matching the start of the full name against the `spec` query param. 141 | */ 142 | var specFilter = new jasmine.HtmlSpecFilter({ 143 | filterString: function() { return queryString.getParam("spec"); } 144 | }); 145 | 146 | env.specFilter = function(spec) { 147 | return specFilter.matches(spec.getFullName()); 148 | }; 149 | 150 | /** 151 | * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack. 152 | */ 153 | window.setTimeout = window.setTimeout; 154 | window.setInterval = window.setInterval; 155 | window.clearTimeout = window.clearTimeout; 156 | window.clearInterval = window.clearInterval; 157 | 158 | /** 159 | * ## Execution 160 | * 161 | * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded. 162 | */ 163 | var currentWindowOnload = window.onload; 164 | 165 | window.onload = function() { 166 | if (currentWindowOnload) { 167 | currentWindowOnload(); 168 | } 169 | htmlReporter.initialize(); 170 | env.execute(); 171 | }; 172 | 173 | /** 174 | * Helper function for readability above. 175 | */ 176 | function extend(destination, source) { 177 | for (var property in source) destination[property] = source[property]; 178 | return destination; 179 | } 180 | 181 | }()); 182 | -------------------------------------------------------------------------------- /02_dom/intro_to_dom.md: -------------------------------------------------------------------------------- 1 | # The DOM 2 | 3 | **Learning Objectives** 4 | 5 | - What is the DOM? How is it different from HTML? 6 | - How do we select elements from the DOM? 7 | - How do we exchange data with elements in the DOM? 8 | - How do we capture user interaction with the DOM? 9 | 10 | ## What is the DOM? 11 | 12 | The DOM, or Document Object Model, is the living representation of an HTML document. HTML itself is just plain old text. A web browser simply parses HTML into data, and then uses that data to assemble an interactive program. This interactive program is the DOM. 13 | 14 | ### Is JavaScript the DOM? 15 | 16 | In a word: NO. JavaScript is a general purpose *programming language*. The DOM is a very specific *program*. The DOM is very much its own beast; we simply talk to it using JavaScript. 17 | 18 | ### What does the DOM do? 19 | 20 | The DOM allows us to dynamically respond to and manipulate a web page. Generally speaking, there are three major tasks that we use the DOM for: 21 | 22 | 1. We **select** individual elements (or sets of elements) out of the DOM to work with them. 23 | 24 | 2. We **exchange data** with the DOM: this involves reading data from specific elements, and/or writing new data into the DOM structure. 25 | 26 | 3. We **listen to** the DOM for user interactions, and then respond to events (such as clicks and text input). 27 | 28 | ## Selecting Elements 29 | 30 | The DOM is composed of individual **elements**. If we want to manipulate an element, we must first select it, or reference it, from the DOM. In JavaScript, we prefer to select elements by their `id` attribute, and leave CSS classes for styling purposes. 31 | 32 | ```html 33 |Let's .
155 |(Work in the JavaScript Console)
22 | 209 | 210 | -------------------------------------------------------------------------------- /03_functions/intro_to_functions.js: -------------------------------------------------------------------------------- 1 | // ----------------------- 2 | // PART 1: Basic Functions 3 | // ----------------------- 4 | 5 | 6 | // Arguments & Return 7 | // ------------------ 8 | // A function accepts arguments, and returns a value: 9 | 10 | function add(a, b) { 11 | return a + b; 12 | } 13 | 14 | var sum = add(5, 10); 15 | 16 | 17 | // The "arguments" Object 18 | // ---------------------- 19 | // Functions provide an array-like object called "arguments" 20 | // that contains references to all passed params: 21 | 22 | function mult() { 23 | return arguments[0] * arguments[1]; 24 | } 25 | 26 | var product = mult(5, 10); 27 | 28 | 29 | // The "arguments" object is useful for operating 30 | // on an unknown number of passed parameters: 31 | 32 | function total() { 33 | var t = 0; 34 | for (var i=0; i < arguments.length; i++) { 35 | t += arguments[i]; 36 | } 37 | return t; 38 | } 39 | 40 | var totalSum = total(5, 10, 25, 50); 41 | 42 | 43 | 44 | // Reference vs. Invocation 45 | // ------------------------ 46 | // Functions may be referenced just like any other data type; 47 | // they only execute when "invoked" using the "()" operator: 48 | 49 | function beep() { 50 | return 'beep'; 51 | } 52 | 53 | // Reference the function object as data. 54 | // This references the function object WITHOUT executing it: 55 | console.log( beep ); 56 | 57 | // Invoke the function, telling it to run. 58 | // The "()" invocation operator is what executes the function: 59 | console.log( beep() ); 60 | 61 | 62 | 63 | // Declaration vs. Assignment 64 | // -------------------------- 65 | // Functions may be declared, or assigned to variables. 66 | // Declarations are universally available (thanks to hoisting), 67 | // while assignments are only available after being set. 68 | 69 | // beepD(); // << This would work here. 70 | 71 | function beepD() { 72 | console.log('beep'); 73 | } 74 | 75 | // beepA(); // << This would cause an error here. 76 | 77 | var beepA = function() { 78 | console.log('beep'); 79 | }; 80 | 81 | 82 | 83 | // Functions as First-Class Objects 84 | // -------------------------------- 85 | // Functions are objects just like any other type of data, 86 | // therefore they may be used in the same way as anything else. 87 | // This includes: 88 | 89 | // 1) Functions may be assigned to variables: 90 | 91 | var honk = function() { 92 | // do stuff... 93 | }; 94 | 95 | 96 | // 2) Functions may be stored within data structures: 97 | 98 | var car = { 99 | model: 'sedan', 100 | honk: function() { 101 | // do stuff... 102 | } 103 | }; 104 | 105 | 106 | // 3) Functions may be passed as arguments to other functions: 107 | 108 | function setTimer(secs, callback) { 109 | // Wait for [secs] seconds, then... 110 | callback(); 111 | } 112 | 113 | setTimer(1, function() { 114 | console.log('Timer Complete!'); 115 | }); 116 | 117 | 118 | // 4) Functions may be returned from other functions: 119 | 120 | function meta() { 121 | return function() { 122 | // do stuff... 123 | }; 124 | } 125 | 126 | 127 | 128 | // ----------------------- 129 | // PART 2: Closure & Scope 130 | // ----------------------- 131 | 132 | // Immediately-Invoking Function Expressions (IIFEs) 133 | // ------------------------------------------------- 134 | // We need to isolate our programs within their own scope 135 | // to do this, we use a function wrapper that invokes itself: 136 | 137 | (function() { 138 | 139 | console.log('I run within my own private scope!'); 140 | 141 | })(); 142 | 143 | 144 | 145 | // Variable Scoping 146 | // ---------------- 147 | // Variables are unique to the scope in which they are declared. 148 | // Declarations are: 149 | // - "var boom" 150 | // - "function boom" 151 | 152 | (function() { 153 | 154 | var n = 5; 155 | 156 | function inner() { 157 | var n = 10; 158 | console.log(n); // <- 10 159 | } 160 | 161 | inner(); 162 | console.log(n); // <- 5 163 | 164 | })(); 165 | 166 | 167 | // Scope Chain 168 | // ----------- 169 | // When a variable is accessed in a scope that does not declare it, 170 | // the variable is accessed from up the chain of nested scopes. 171 | 172 | (function() { 173 | 174 | var n = 5; 175 | 176 | function inner() { 177 | n = 10; 178 | } 179 | 180 | inner(); 181 | console.log(n); // <- 10 182 | 183 | })(); 184 | 185 | 186 | // Global Scope 187 | // ------------ 188 | // When a variable has no scoped declaration, 189 | // access will bubble up to the highest-level scope: global. 190 | // In web browsers, global scope is the "window" object: 191 | 192 | (function() { 193 | 194 | function inner() { 195 | n = 15; 196 | } 197 | 198 | inner(); 199 | console.log(window.n); // <- 15 200 | 201 | })(); 202 | 203 | 204 | 205 | // ----------------------------- 206 | // PART 3: Contextual Invocation 207 | // ----------------------------- 208 | 209 | // 1) Function Invocation 210 | // ---------------------- 211 | // When a function is invoked without context, 212 | // it binds to global scope: 213 | 214 | function goBoom() { 215 | console.log(this); 216 | } 217 | 218 | goBoom(); // this -> window 219 | 220 | 221 | 222 | // 2) Call Invocation 223 | // ------------------ 224 | // Function objects have a "call" method. 225 | // Using "call" will invoke a function, bound to a passed object: 226 | 227 | var xwing = {}; 228 | 229 | function goBoom() { 230 | console.log(this); 231 | } 232 | 233 | goBoom.call(xwing); // this -> xwing 234 | 235 | 236 | 237 | // 3) Method Invocation 238 | // -------------------- 239 | // Functions attached to an object 240 | // may be invoked as "methods" of the object: 241 | 242 | var deathstar = { 243 | goBoom: function() { 244 | console.log(this); 245 | } 246 | }; 247 | 248 | deathstar.goBoom(); 249 | 250 | 251 | 252 | // 4) Constructor Invocation 253 | // ------------------------- 254 | // When functions are invoked with the "new" keyword, 255 | // a brand new object is created and bound to the invocation. 256 | 257 | function PodRacer() { 258 | console.log(this); 259 | } 260 | 261 | var pod = new PodRacer(); // this -> brand new object 262 | 263 | // ------------------------------ 264 | // PART 4: Prototypal Inheritance 265 | // ------------------------------ 266 | 267 | // Shared properties: 268 | 269 | function Car() { 270 | // configure new instance... 271 | } 272 | 273 | Car.prototype = { 274 | wheels: 4, 275 | doors: 2, 276 | honk: function() { 277 | console.log('beep beep'); 278 | } 279 | }; 280 | 281 | var car1 = new Car(); 282 | var car2 = new Car(); 283 | 284 | car1.honk(); // beep beep 285 | car2.honk(); // beep beep 286 | 287 | car1.honk === car2.honk; // <- true 288 | 289 | 290 | // Customization: 291 | 292 | function Player(firstName, lastName) { 293 | this.firstName = firstName; 294 | this.lastName = lastName; 295 | } 296 | 297 | Player.prototype = { 298 | score: 0, 299 | fullName: function() { 300 | return this.firstName +' '+ this.lastName; 301 | } 302 | }; 303 | 304 | var player1 = new Player('Micky', 'Mouse'); 305 | var player2 = new Player('Donald', 'Duck'); 306 | 307 | player1.fullName(); // Micky Mouse 308 | player2.fullName(); // Donald Duck 309 | 310 | 311 | // Trait assignment: 312 | 313 | player1.score += 100; 314 | console.log(player1.score); // 100 315 | console.log(player2.score); // 0 316 | 317 | 318 | 319 | // Shared data: 320 | 321 | function House() { 322 | // configure new object... 323 | } 324 | 325 | House.prototype = { 326 | students: [], 327 | addStudent: function(name) { 328 | this.students.push(name); 329 | } 330 | }; 331 | 332 | var gryff = new House(); 333 | var huff = new House(); 334 | 335 | 336 | gryff.addStudent('Harry'); 337 | huff.addStudent('Cedric'); 338 | 339 | // ... uh, oh. Something is wrong here. 340 | // Gryff and Huff each have two students?! -------------------------------------------------------------------------------- /04_regex/intro_to_regex.md: -------------------------------------------------------------------------------- 1 | # Regular Expressions 2 | 3 | ## Learning Objectives 4 | 5 | - What are Regular Expressions? 6 | - Why are they incredibly useful? 7 | - How does RegEx matching work? 8 | - How does RegEx replacement work? 9 | 10 | ## What are Regular Expressions? 11 | 12 | Regular Expressions provide text searching and replacement capabilities, similar to using the "Find & Replace" (F&R) operation in a word processor. However, most common F&Rs only search for a literal sequence... 13 | 14 | **Find literal: `"eat"`** 15 | 16 | > I will `eat` with you on Tuesday at the local `eat`ery, Joe's Eats. 17 | 18 | 19 | Regular Expressions (RegEx) are considerably more powerful because they search for _patterns_ in text, which allows for contextual awareness... 20 | 21 | **Find pattern: `/eats?\b/ig`** 22 | - Find `"eat"` as a standalone word (surrounded by spaces and/or punctuation). 23 | - Allow the standalone word to be plural (may end in "s"). 24 | - Allow the standalone word to have both upper and lowercase letters. 25 | 26 | > I will `eat` with you on Tuesday at the local eatery, Joe's `Eats`. 27 | 28 | 29 | ## Why are Regular Expressions awesome? 30 | 31 | Regular Expressions allow for an unprecidented level of text manipulation that would be extremely difficult (or virtually impossible) to achieve with literal sequences alone. Regexs are particularily useful in the realm of programming, where we frequently need to read specially-formatted text strings as data. 32 | 33 | **Find the text of the header tag:** 34 | ```html 35 |Not this paragraph
38 |hello world
214 |hello world
215 |hello world
216 | ``` 217 | 218 | ## Replacement 219 | 220 | So far, we've only done finding. RegEx becomes far more powerful when applied to dynamic replacement. Let's write a RegEx that finds all standalone instances of the word "wood", and swaps them for "ham": 221 | 222 | ``` 223 | How much wood would a woodchuck chuck if a woodchuck could chuck wood? 224 | ``` 225 | -------------------------------------------------------------------------------- /05_backbone/exercises/contact_list/instructions.md: -------------------------------------------------------------------------------- 1 | # 1. Make a Model class 2 | 3 | First, we need a model that will manage data for each individual contact. Put this at the top of your `app.js` JavaScript file: 4 | 5 | ```javascript 6 | // Model for managing a single contact: 7 | 8 | var ContactModel = Backbone.Model.extend({ 9 | defaults: { 10 | name: '', 11 | email: '' 12 | } 13 | }); 14 | ``` 15 | 16 | A model may define as many fields of data as you need to represent the subject that you're "modeling". In the future, we'd likely want our contacts to include phone numbers, addresses, a photo, etc. We can easily expand this model in the future to include those sorts of additional fields. 17 | 18 | While we're not working with a formal database in this exercise, keep in mind that this model mirrors the structure of a database that would store our contacts in a full-stack web application. 19 | 20 | # 2. Make a Collection class 21 | 22 | Models store data about our individual contacts. However, we need to manage a _list_ of contacts. A Collection is a data structure designed to manage a list of models. Add this into `app.js` below your model class: 23 | 24 | ```javascript 25 | // Collection for managing a list of contact models: 26 | 27 | var ContactList = Backbone.Collection.extend({ 28 | // Tell this collection what type of model to manage: 29 | model: ContactModel, 30 | 31 | // Configure the model to persist data in localStorage: 32 | // stored data will be namespaced under "contacts": 33 | localStorage: new Backbone.LocalStorage('contacts') 34 | }); 35 | ``` 36 | 37 | We're doing two things here: 38 | 39 | * First, we're telling the collection what type of model it will manage. In this case, our collection will manage a list of `ContactModel` objects. 40 | 41 | * Next, we're configuring a Backbone plugin that allows our collection to store data locally within the web browser. Otherwise, our Collection would attempt to make network requests that send our data to a backend web application for storage. We don't have a backend application, so for now we're cheating and just using `localStorage`. 42 | 43 | # 3. A View for the add-contact form: 44 | 45 | Next, we'll setup a View class that manages the `#add-contacts` form. Whenever this form is submitted, we want to collect data from the form and use it to create a new contact in the collection. Add this into `app.js` below your collection class: 46 | 47 | ```javascript 48 | // View for managing the #add-contact form submissions: 49 | 50 | var AddContactView = Backbone.View.extend({ 51 | // Bind this view to the "add-contact" form element: 52 | el: '#add-contact', 53 | 54 | // Make DOM "submit" events call this view's "onSubmit" method: 55 | events: { 56 | 'submit': 'onSubmit' 57 | }, 58 | 59 | // "submit" event handler... 60 | // Called whenever the "add-contact" form is submitted: 61 | onSubmit: function(evt) { 62 | // Stop form submit from refreshing the page: 63 | evt.preventDefault(); 64 | 65 | // Create a new contact based on form data: 66 | this.collection.create({ 67 | name: this.$('[name="name"]').val(), 68 | email: this.$('[name="email"]').val() 69 | }); 70 | 71 | // Clear all form fields: 72 | this.el.reset(); 73 | } 74 | }); 75 | ``` 76 | 77 | We're doing several things here: 78 | 79 | * First, we're providing the selector `#add-contact` as the view's `el`, or element. Backbone will select that element from the DOM and attach it to this view. A view's job is to manage the behaviors of its attached element. _Note: while we're defining `el` as a selector string, Backbone will replace the `el` property with the actual DOM element._ 80 | 81 | * Next, we've used Backbone's event delegation table to make any `"submit"` event within the DOM trigger the view's `onSubmit` method. 82 | 83 | * Finally, whenever the form is submitted, we're collecting user input from the form fields and using that data to create a new contact in the collection. 84 | 85 | # 4. Create Instances 86 | 87 | If you were to refresh the contacts list page at the moment, you'll see that for all of our JavaScript, nothing has happened. That's okay. What we've done so far is to set up _classes_ for each component in our application. Think of a class as a blueprint for building an object; the same way we'd make a blueprint for a house, and then build one (or more) physical houses using it. What we need to do now is to build physical _instances_ of each of our component classes. 88 | 89 | Add the following to the bottom of your `app.js` file: 90 | 91 | ```javascript 92 | /** 93 | * Instantiate Components: 94 | */ 95 | 96 | // Instance the contacts list collection: 97 | var contacts = new ContactList(); 98 | 99 | // Instance the "add-contacts" view: 100 | // We pass in our contacts list as it's "collection" to manage. 101 | var addView = new AddContactView({collection: contacts}); 102 | ``` 103 | 104 | Here we've created _instances_ of the `ContactList` collection and the `AddContactView`. Once our components have been instanced, our application should be live! Try it out: refresh the app page and add a few contacts using the form... 105 | 106 | **NOTHING HAPPENED. DID IT WORK?** 107 | 108 | This is where the JavaScript console is our best friend. While we haven't changed anything in the page that we can _visually_ see, that doesn't mean our application isn't silently collecting and caching data. Let's find out. Open the JavaScript console (Chrome: `View > Developer > JavaScript Console`). This is a live console where you can interact with the state of JavaScript on the current page. For starters, type this and then press ENTER: 109 | 110 | ``` 111 | contacts.length 112 | ``` 113 | 114 | Here, we're asking for the number of `ContactModel` instances stored within our collection. It should match the number of contacts you've submitted through the form (resubmit the form, and then rerun the above command. You should have an additional contact now). To inspect the models in your collection, run this: 115 | 116 | ``` 117 | contacts.toJSON() 118 | ``` 119 | 120 | The Chrome console should give you a list of inspectable objects, the data on each object (or, "model", as is the proper term) should look familiar! 121 | 122 | BUT – is this data getting stored for future use? Well, let's have a look at the browser's `localStorage` object: 123 | 124 | ``` 125 | localStorage 126 | ``` 127 | 128 | Your local storage should contain data that looks something like this: 129 | 130 | ``` 131 | contacts: "21414242-8d82-6c16-9c62-1bd831d6f59d", 132 | contacts-21414242-8d82-6c16-9c62-1bd831d6f59d: "{"name":"Bob Baker","email":"thepriceiswrong@gmail.com","id":"21414242-8d82-6c16-9c62-1bd831d6f59d"}" 133 | ``` 134 | 135 | The above is Backbone's local storage adaptor writing application data into our browser cache (in a real application, we'd send this to a back-end application that would store the data in a database). 136 | 137 | **SO, IS THIS GOOD?** 138 | 139 | Yeah, this is REALLY good. While we've placed absolutely nothing on the screen, we've accomplished something far more fundamental: we've established a data storage system that manages and persists data. At the end of the day, _graphics are meaningless_. Applications revolve around the safe management and exchange of data. Data is essential; graphics are just a nicety for human-friendly interfaces... and the good news is that once we have good data services established, then presenting the data as graphics on screen is fairly trivial. 140 | 141 | # 5. Present the data on-screen 142 | 143 | Yeah, yeah... so you want to see pretty data show up on screen rather than just inspecting your collection through the command line? Well, then let's create another view that handles the translation of models stored in the collection into graphics on the screen. Add this into `app.js` just _above_ the `/* Instantiate Components */` section: 144 | 145 | ```javascript 146 | // View for displaying the list of contacts: 147 | 148 | var ListContactsView = Backbone.View.extend({ 149 | // Attach this view to the "list-contacts" table element: 150 | el: '#list-contacts', 151 | 152 | // Initializes the view once when it is first created: 153 | initialize: function() { 154 | // Listen for "sync" and "remove" events from the contacts collection: 155 | // Whenever the collection adds or removes a model, 156 | // then we'll re-render this view with the latest data. 157 | this.listenTo(this.collection, 'sync remove', this.render); 158 | }, 159 | 160 | // Method used to render graphics for this view: 161 | // This method will simply build an HTML representation of contacts, 162 | // and then insert that HTML into the DOM. 163 | render: function() { 164 | // Start and empty string for building the list HTML: 165 | var list = ''; 166 | 167 | // Append table row HTML for each model stored in the collection: 168 | this.collection.each(function(model) { 169 | list += '