├── .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 | World Foodie 5 | 6 | 7 | 8 |

World Foodie

9 |

(use the javascript console)

10 | 15 | 16 | -------------------------------------------------------------------------------- /03_functions/exercises/pixel_art/starter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | i ♥ js 7 | 8 | 9 |

Pixel Art!

10 |
11 | 12 | 13 |
14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /03_functions/exercises/hogwarts_js/instructions.md: -------------------------------------------------------------------------------- 1 | # HogwartsJS 2 | 3 | Now let's build an entire Hogwarts school application from the single Gryffindor object that we created last class. 4 | 5 | ## Objectives 6 | 7 | - Work with constructor functions and prototype. 8 | - Explore some advanced enumeration concepts. 9 | 10 | ## Process 11 | 12 | Review the provided JavaScript and note the formar `Gryffindor` object and the new `Hogwarts` object below. Instructions and hints are documented inline within the code. -------------------------------------------------------------------------------- /03_functions/exercises/pixel_art/solution/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | i ♥ js 7 | 8 | 9 |

Pixel Art!

10 |
11 |
12 | 13 | 14 |
15 |
16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /05_backbone/exercises/contact_list/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font: 200 16px/1.5 "Helvetica Neue", Helvetica, sans-serif; 3 | margin: 30px; 4 | } 5 | 6 | #add-contact { 7 | background-color: whitesmoke; 8 | border: 1px solid #eee; 9 | border-radius: 5px; 10 | font-size: 14px; 11 | margin-bottom: 15px; 12 | padding: 10px; 13 | } 14 | 15 | #add-contact label { 16 | margin-right: 10px; 17 | } 18 | 19 | #list-contacts { 20 | width: 100%; 21 | } 22 | 23 | #list-contacts thead td { 24 | border-bottom: 2px solid black; 25 | width: 33%; 26 | } -------------------------------------------------------------------------------- /03_functions/exercises/pixel_art/starter/style.css: -------------------------------------------------------------------------------- 1 | @import url(http://fonts.googleapis.com/css?family=Monoton); 2 | 3 | .brush { 4 | width: 100px; 5 | height: 100px; 6 | background: #1B4370; 7 | margin: 15px auto; 8 | } 9 | 10 | .square { 11 | float: left; 12 | width: 100px; 13 | height: 100px; 14 | background: #E7E5DB; 15 | margin: 10px; 16 | } 17 | 18 | h1 { 19 | font-family: Monoton; 20 | font-size: 48px; 21 | text-align: center; 22 | } 23 | 24 | .controls { 25 | text-align: center; 26 | } 27 | 28 | body { 29 | background-image: url('ps_neutral.png'); 30 | } -------------------------------------------------------------------------------- /03_functions/exercises/pixel_art/solution/style.css: -------------------------------------------------------------------------------- 1 | @import url(http://fonts.googleapis.com/css?family=Monoton); 2 | 3 | .brush { 4 | width: 100px; 5 | height: 100px; 6 | background: #1B4370; 7 | margin: 15px auto; 8 | } 9 | 10 | .square { 11 | float: left; 12 | width: 10px; 13 | height: 10px; 14 | background: #E7E5DB; 15 | margin: 0px; 16 | } 17 | 18 | h1 { 19 | font-family: Monoton; 20 | font-size: 48px; 21 | text-align: center; 22 | } 23 | 24 | .controls { 25 | text-align: center; 26 | } 27 | 28 | body { 29 | background-image: url('ps_neutral.png'); 30 | } 31 | -------------------------------------------------------------------------------- /03_functions/exercises/expense_tracker/complete/stylesheets/expense.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | font-size: 20px; 3 | } 4 | tfoot { 5 | font-weight: bold; 6 | } 7 | 8 | .expense-tracker { 9 | display: table; 10 | margin: 50px auto; 11 | max-width: 1000px; 12 | table-layout: fixed; 13 | width: 90%; 14 | } 15 | 16 | .col { 17 | display: table-cell; 18 | } 19 | 20 | .expense-add { 21 | padding-right: 30px; 22 | width: 35%; 23 | } 24 | 25 | @media (max-width: 600px) { 26 | .col { 27 | display: block !important; 28 | padding: 0 !important; 29 | width: auto !important; 30 | } 31 | } -------------------------------------------------------------------------------- /03_functions/exercises/expense_tracker/starter/stylesheets/expense.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | font-size: 20px; 3 | } 4 | tfoot { 5 | font-weight: bold; 6 | } 7 | 8 | .expense-tracker { 9 | display: table; 10 | margin: 50px auto; 11 | max-width: 1000px; 12 | table-layout: fixed; 13 | width: 90%; 14 | } 15 | 16 | .col { 17 | display: table-cell; 18 | } 19 | 20 | .expense-add { 21 | padding-right: 30px; 22 | width: 35%; 23 | } 24 | 25 | @media (max-width: 600px) { 26 | .col { 27 | display: block !important; 28 | padding: 0 !important; 29 | width: auto !important; 30 | } 31 | } -------------------------------------------------------------------------------- /05_backbone/exercises/startup_idea/complete.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | What's your big startup idea? 5 | 11 | 12 | 13 | 14 |
15 |

It's like...

16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /05_backbone/exercises/startup_idea/starter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | What's your big startup idea? 5 | 11 | 12 | 13 | 14 |
15 |

It's like...

16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /03_functions/exercises/functions_deepdive/complete/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Function Spec Runner v2.0.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /03_functions/exercises/functions_deepdive/starter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Function Spec Runner v2.0.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /03_functions/exercises/pixel_art/solution/pixart.js: -------------------------------------------------------------------------------- 1 | var form = document.querySelector('#form'); 2 | var input = document.querySelector('#color-field'); 3 | var brush = document.querySelector('.brush'); 4 | 5 | form.addEventListener('submit', function(evt){ 6 | evt.preventDefault(); // Otherwise submitting a form reloads the page 7 | brush.style.backgroundColor = input.value; 8 | }); 9 | 10 | for( var i = 0; i < 8000; i++){ 11 | var div = document.createElement('div'); 12 | div.classList.add('square'); 13 | div.addEventListener('mouseover', function(){ 14 | this.style.backgroundColor = brush.style.backgroundColor; 15 | }); 16 | document.body.appendChild(div); 17 | } 18 | 19 | -------------------------------------------------------------------------------- /03_functions/exercises/closure.js: -------------------------------------------------------------------------------- 1 | // Kat's App 2 | (function() { 3 | // Kat's Code... 4 | var cookieOven = 'true'; 5 | var bakingSheet = true; 6 | var chocolateChips = true; 7 | })(); 8 | 9 | 10 | // Walter's App 11 | (function() { 12 | // Walter White's Code 13 | var cookieOven = true; 14 | var chemicals = true; 15 | var bakingSheets = true; 16 | })(); 17 | 18 | 19 | // Kat :-) 20 | var katPhotos = true; 21 | 22 | function washington() { 23 | var monuments = true; 24 | 25 | function brookland() { 26 | var catholicU = true; 27 | // Pam :-) 28 | } 29 | 30 | function shaw() { 31 | var bigBear = true; 32 | // Steve :-) 33 | } 34 | 35 | // Ngan :-) 36 | } -------------------------------------------------------------------------------- /01_fundamentals/exercises/deck_o_cards/starter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Deck o' Cards 5 | 14 | 15 | 16 |

Deck o' Cards

17 | Playing Cards. 18 |

(use the javascript console)

19 | 29 | 30 | -------------------------------------------------------------------------------- /02_dom/exercises/currency_converter/starter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Currency Converter 5 | 11 | 12 | 13 |

Currency Converter

14 | 15 |

16 |

17 | 18 | 24 | 25 | -------------------------------------------------------------------------------- /05_backbone/exercises/contact_list/complete.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Contact List 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
NameEmailRemove
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /05_backbone/exercises/contact_list/starter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Contact List 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
NameEmailRemove
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /02_dom/exercises/currency_converter/instructions.md: -------------------------------------------------------------------------------- 1 | # Currency Converter 2 | 3 | Here's a handy Google-style converter for switching numbers between US Dollars and Euros. 4 | 5 | **Objectives:** 6 | 7 | - Perform simple DOM selections. 8 | - Capture and handle user input events. 9 | - Make simple updates to the DOM. 10 | 11 | **Process:** 12 | 13 | 1. Select both the dollars and euros text input elements from the DOM (use their respective IDs). 14 | 15 | 2. Add an "input" event listener to each element. This event will be called whenever the users enters text into the bound element. 16 | 17 | 3. When an input element changes, get its `value` property and parse that into a number. 18 | 19 | 4. Adjust the parsed numeric value by the exchange rate, and then set the adjusted value to the opposite currency field. 20 | 21 | Hint: when displaying currency, we generally only want to display two decimal places. Look up JavaScript's `toFixed()` method to help with this! -------------------------------------------------------------------------------- /02_dom/exercises/name_generator/instructions.md: -------------------------------------------------------------------------------- 1 | # Name Generator 2 | 3 | Here's a fun little exercise to one-up the legendary name of Benedict Cumberbatch. 4 | 5 | **Objectives:** 6 | 7 | - Work with data arrays. 8 | - Familiarize with `Math` functions and order of operations. 9 | - Select and modify DOM elements. 10 | - Bind DOM events to capture user interactions. 11 | 12 | **Process:** 13 | 14 | 1. Check out the provided JavaScript (in the script tag). You'll find a `firstNames` and `lastNames` array, each filled with awesome names. 15 | 16 | 2. Select a random first and last name from each of the arrays. You may need to look up documentation for JavaScript's `Math.random()` and `Math.round()` functions. 17 | 18 | 3. Combine your randomly selected first and last names into a single string, and set that as the contents of the `#name-text` element. 19 | 20 | 4. Add a click event listener to the `#name-bttn` element. Perform steps 2 & 3 whenever the button is clicked. -------------------------------------------------------------------------------- /03_functions/sandbox/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sandbox: Launch Bay 5 | 26 | 27 | 28 | 29 |

Open the JavaScript Console
(CMD + OPT + J)

30 |

31 | 32 | 33 | 36 | 37 | -------------------------------------------------------------------------------- /01_fundamentals/exercises/deck_o_cards/instructions.md: -------------------------------------------------------------------------------- 1 | # Deck o' Cards 2 | 3 | Let's make a standard deck of playing cards... as JavaScript. 4 | 5 | **Objectives:** 6 | 7 | - Work with arrays and enumeration. 8 | - Create simple object structures. 9 | 10 | **Process:** 11 | 12 | 1. Check out the provided JavaScript (in the script tag). You'll find a `values` and `suits` array, each filled with standard playing card attributes. Also note the empty `deck` array... that will hold our 52 cards. 13 | 14 | 3. Compose an algorithm that enumerates both `values` and `suits`. Create a new object for each value/suit pairing, and add it to the `deck` array. These card objects should be formatted as: 15 | 16 | ``` 17 | {value: 3, suit: 'S'} 18 | ``` 19 | 20 | The `deck` array should print out into the JavaScript console. It should be filled with 52 object, each with a unique `value` and `suit` combination. 21 | 22 | ## Bonus 23 | 24 | - Loop through your deck, and print out each value/suit pairing as a string on a separate line in the JavaScript console. -------------------------------------------------------------------------------- /01_fundamentals/exercises/hammonds_mine/instructions.md: -------------------------------------------------------------------------------- 1 | # John Hammond's Mine 2 | 3 | Loe and behold, some data from John Hammond's Jurassic Park dinosaur DNA digs has been found! Let's do some digging to learn about what he found. 4 | 5 | **Objectives:** 6 | 7 | - Work with arrays, objects, and enumeration. 8 | - Familiarize with data access using brackets and dot notation. 9 | 10 | **Process:** 11 | 12 | 1. Check out the provided JavaScript (in the script tag). You'll find a very large (and deep!) `mine` data structure. 13 | 14 | 2. Write JavaScript to solve the following digging challenges. When you have solved for a value, print it out into the console. 15 | 16 | ## Digging challenges... 17 | 18 | 1. **Number of dig sites worldwide.** 19 | 20 | 2. **Total contributions given by donors.** 21 | 22 | 3. **Number of ACTIVE dig sites worldwide.** 23 | 24 | 4. **Total number of finds in North America.** 25 | 26 | 5. **Total Brachiosaurus specimens found worldwide.** 27 | 28 | All of these challenges are solvable with `for` loops. 29 | Good luck, and happy digging! -------------------------------------------------------------------------------- /01_fundamentals/exercises/world_foodie/instructions.md: -------------------------------------------------------------------------------- 1 | # World Foodie 2 | 3 | Let's get comfortable with the basics. For this exercise, use the browser `prompt` command to get input from the user: 4 | 5 | ``` 6 | var userName = prompt('What is your name?'); 7 | ``` 8 | 9 | **Objectives:** 10 | 11 | - Use variables. 12 | - Use conditionals. 13 | 14 | **Process:** 15 | 16 | 1. Ask for the user's name. 17 | 18 | 2. Ask for the user's favorite food. 19 | 20 | 3. Ask the user if they like to travel (`"yes"` or `"no"`) 21 | 22 | 4. Print the following outcomes into the console: 23 | 24 | ``` 25 | - Print out `"Hello, [user name]!"` 26 | - If the user's favorite food is "Tandoori Chicken" and they travel, tell them they should go to India. 27 | - If the user's favorite food is "Burritos" or "Tacos", and they travel, tell them they should go to Mexico. 28 | - If the user doesn't travel, tell them they should seek out bad American `[favorite food]`. 29 | ``` 30 | 31 | ## Bonus 32 | 33 | - Respond to all capitalizations of the provided food name (ex: "Burrito" or "burrito"). -------------------------------------------------------------------------------- /01_fundamentals/exercises/deck_o_cards/complete.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Deck o' Cards 5 | 14 | 15 | 16 |

Deck o' Cards

17 | Playing Cards. 18 |

(use the javascript console)

19 | 38 | 39 | -------------------------------------------------------------------------------- /01_fundamentals/exercises/world_foodie/complete.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | World Foodie 5 | 6 | 7 | 8 |

World Foodie

9 |

(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 | Currency Converter 5 | 11 | 12 | 13 |

Currency Converter

14 | 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 += ''+ exp.category +''+ exp.desc +'$'+ exp.amount.toFixed(2) +''; 31 | total += exp.amount; 32 | } 33 | 34 | document.querySelector('#expense-list').innerHTML = html; 35 | document.querySelector('#expense-total').innerText = '$'+ total.toFixed(2); 36 | } 37 | 38 | renderList(); 39 | })(); -------------------------------------------------------------------------------- /03_functions/exercises/expense_tracker/complete/instructions.md: -------------------------------------------------------------------------------- 1 | # Expense Tracker 2 | 3 | Here's a simple app to track your expenses this month. 4 | 5 | **Objectives:** 6 | 7 | - Capture and store user data input. 8 | - Work with arrays, objects, functions, and the DOM. 9 | - Dynamically generate DOM elements. 10 | 11 | **Process:** 12 | 13 | We'll be working in an external JavaScript file this time. All code should go in the "javascripts/expense.js" file. 14 | 15 | 1. Define an `expenses` variable set to an empty array. 16 | 17 | 2. Bind a "submit" event handler onto the "#expense-add" form element. When the form is submitted: cancel the browser's default action, then create a new expense object with `description`, `amount`, and `category` properties (pull these values from the DOM). 18 | 19 | 3. Make sure to parse each new expense object's `amount` property into a number. Assuming the `amount` is a valid number, add the expense object into the `expenses` array. 20 | 21 | 4. Declare a `renderList` function that will render the list of expenses. Invoke this function whever a new expense object is added to the expenses array. 22 | 23 | 5. Make the `renderList` function loop through the array of expense objects and construct a new table row HTML string for each expense, formatted as: 24 | 25 | ``` 26 | "categorydescription$amount" 27 | ``` 28 | 29 | After building a string with all expenses rendered as HTML, set this as the `innerHTML` of the "#expense-list" element. 30 | 31 | Also while rendering, tally up the total of all expenses, and set that number to the "#expense-total" element. 32 | -------------------------------------------------------------------------------- /03_functions/exercises/expense_tracker/starter/instructions.md: -------------------------------------------------------------------------------- 1 | # Expense Tracker 2 | 3 | Here's a simple app to track your expenses this month. 4 | 5 | **Objectives:** 6 | 7 | - Capture and store user data input. 8 | - Work with arrays, objects, functions, and the DOM. 9 | - Dynamically generate DOM elements. 10 | 11 | **Process:** 12 | 13 | We'll be working in an external JavaScript file this time. All code should go in the "javascripts/expense.js" file. 14 | 15 | 1. Define an `expenses` variable set to an empty array. 16 | 17 | 2. Bind a "submit" event handler onto the "#expense-add" form element. When the form is submitted: cancel the browser's default action, then create a new expense object with `description`, `amount`, and `category` properties (pull these values from the DOM). 18 | 19 | 3. Make sure to parse each new expense object's `amount` property into a number. Assuming the `amount` is a valid number, add the expense object into the `expenses` array. 20 | 21 | 4. Declare a `renderList` function that will render the list of expenses. Invoke this function whever a new expense object is added to the expenses array. 22 | 23 | 5. Make the `renderList` function loop through the array of expense objects and construct a new table row HTML string for each expense, formatted as: 24 | 25 | ``` 26 | "categorydescription$amount" 27 | ``` 28 | 29 | After building a string with all expenses rendered as HTML, set this as the `innerHTML` of the "#expense-list" element. 30 | 31 | Also while rendering, tally up the total of all expenses, and set that number to the "#expense-total" element. 32 | -------------------------------------------------------------------------------- /03_functions/exercises/lesson_practice/scope.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | scope 5 | 8 | 9 | 10 |

scope

11 |

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 | this 5 | 8 | 9 | 10 |

this

11 |

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 | Expense Tracker 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |

Add Expense

13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 |
22 | 23 | 30 |
31 |

32 |
33 | 34 |
35 |

Expenses

36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
CategoryDescriptionAmount
EntertainmentGuardians of the Galaxy$12.50
TOTAL$12.50
59 |
60 |
61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /03_functions/exercises/expense_tracker/starter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Expense Tracker 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |

Add Expense

13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 |
22 | 23 | 30 |
31 |

32 |
33 | 34 |
35 |

Expenses

36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
CategoryDescriptionAmount
EntertainmentGuardians of the Galaxy$12.50
TOTAL$12.50
59 |
60 |
61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /01_fundamentals/sandbox/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sandbox: Enter the Matrix 5 | 8 | 9 | 10 | 11 |

Open the JavaScript Console
(CMD + OPT + J)

12 |

13 | 14 | 15 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /02_dom/sandbox/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sandbox: The Muppet Show 5 | 8 | 9 | 10 | 11 |

The Muppet Show

12 | 13 | 14 | 15 |

Contents

16 | 21 | 22 |
23 |

Overview

24 | 25 |

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 |
27 | 28 |
29 |

Characters

30 | 31 | 36 | 37 |

Your favorite character:

38 |
39 | 40 |
41 |

Recurring skits

42 | 43 | 48 |
49 | 50 | 51 | 54 | 55 | -------------------------------------------------------------------------------- /03_functions/exercises/functions_deepdive/starter/function_spec.js: -------------------------------------------------------------------------------- 1 | // PART 1: Sunlight Zone: Arguments & Return 2 | // ----------------------------------------- 3 | 4 | it('will accept arguments and return values.', function() { 5 | 6 | function test(a, b) { 7 | 8 | } 9 | 10 | }); 11 | 12 | 13 | it('provides an arguments *object* that contains all arguments passed into the function.', function() { 14 | 15 | function test() { 16 | 17 | } 18 | 19 | }); 20 | 21 | 22 | 23 | // PART 2: Twilight Zone: Scope & Closure 24 | // -------------------------------------- 25 | 26 | it('allows function scopes to reference outward, but not to look inward at nested closures.', function() { 27 | 28 | var outer = 10; 29 | 30 | function test() { 31 | var inner = 20; 32 | } 33 | 34 | }); 35 | 36 | 37 | 38 | it('will override conflicting variable declarations in an inner scope. The outer scope is unaffected.', function() { 39 | 40 | var n = 5; 41 | 42 | function test() { 43 | var n = 20; 44 | } 45 | 46 | }); 47 | 48 | 49 | 50 | it('allows inner scopes to access and modify variables declared in an outer scope.', function() { 51 | 52 | var n = 5; 53 | 54 | function test() { 55 | n = 20; 56 | } 57 | 58 | }); 59 | 60 | 61 | 62 | it('assigns all undeclared variables into global (window) scope.', function() { 63 | 64 | function test() { 65 | n = 20; 66 | } 67 | 68 | }); 69 | 70 | 71 | 72 | it('allows Immedaitely-Invoked Function Expressions (IIFE) to set up a private scope.', function() { 73 | 74 | // Write an IIFE... 75 | 76 | }); 77 | 78 | 79 | 80 | // PART 3: Midnight Zone: Contextual Invocation 81 | // -------------------------------------------- 82 | 83 | it('will bind the Function Invocation pattern to the global object.', function() { 84 | 85 | function test() { 86 | return this; 87 | } 88 | 89 | }); 90 | 91 | 92 | 93 | it('will bind the Call Invocation pattern to a passed object.', function() { 94 | 95 | function test(a, b) { 96 | return this; 97 | } 98 | 99 | var target = {}; 100 | 101 | }); 102 | 103 | 104 | 105 | it('will bind the Method Invocation pattern to the host object.', function() { 106 | 107 | var obj = { 108 | test: function() { 109 | return this; 110 | } 111 | }; 112 | 113 | }); 114 | 115 | 116 | 117 | it('will bind the Constructor Invocation pattern to a brand new object instance.', function() { 118 | 119 | function TestWidget(name) { 120 | this.name = name; 121 | } 122 | 123 | }); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Professional JavaScript 2 | 3 | This workshop introduces professional-level JavaScript development. The curriculum covers foundation concepts of JavaScript programming and basic computer science. The goal of this workshop is to establish a technical knowledge base for our participants to continue building upon in their careers. While this is a beginner-level course, the curriculum is expansive and highly technical. Participants of all experience levels should come prepared to be challenged. 4 | 5 | ## Pre-requisites 6 | 7 | You should be familiar with basic HTML and CSS. A pre-course primer on JavaScript will also help you succeed. You can pursue free lessons on these subjects at [dash.generalassemb.ly](http://dash.generalassemb.ly). 8 | 9 | ## Takeaways 10 | - Foundation concepts of computer science. 11 | - Intermediate-level concepts of the JavaScript programming language. 12 | - Foundation knowledge of how JavaScript makes the web interactive. 13 | 14 | ## Day 1: Fundamentals 15 | 16 | Our first session will cover the basics of computer science, using JavaScript as our language of study. For participants with past programming experience, this cirriculum will be generally familiar. 17 | 18 | **Morning** 19 | 20 | - Introduction 21 | - JavaScript Fundamentals 22 | - Datatypes 23 | - Variables 24 | - Conditionals 25 | - Arrays & Objects 26 | - Enumeration 27 | - Passing Value vs. Reference 28 | 29 | **Afternoon** 30 | 31 | - Simple Functions 32 | - Intro to DOM 33 | - Selecting elements 34 | - Reading and writing document data 35 | - Binding event handlers 36 | - Lab exercises 37 | 38 | ## Day 2: Functions & Object-Orientation 39 | 40 | Our second session will deep-dive into the exceptionally unique aspect of JavaScript: Functions. The concepts discussed here are significantly more advanced than those in session-1, so participants should come prepared to be challenged. 41 | 42 | **Morning** 43 | 44 | - Introduction 45 | - **Part 1: Basic Functions** 46 | - Arguments & Return 47 | - The "arguments" Object 48 | - Invocation vs. Reference 49 | - Declaration vs. Assignment 50 | - Functions as First-Class Objects 51 | - **Part 2: Closure & Scope** 52 | - What is Closure? 53 | - What is Scope? 54 | - Declarations & Scope Chain 55 | - Hoisting 56 | - Scope exercises 57 | 58 | **Afternoon** 59 | 60 | - **Part 3: Contextual Invocation** 61 | - What is "this"? 62 | - Function Invocation 63 | - Call Invocation 64 | - Method Invocation 65 | - **Part 4: Prototypal Inheritance** 66 | - Constructor Invocation 67 | - Prototype Chains 68 | - Using "hasOwnProperty" 69 | - Using "typeof" and "instanceof" 70 | - Invocation exercises 71 | -------------------------------------------------------------------------------- /03_functions/exercises/gryffindor_house/starter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Gryffindor House 5 | 19 | 20 | 21 |

(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 += ''; 65 | list += ''+ model.get('name') +''+ model.get('email') +''; 66 | list += ''; 67 | list += ''; 68 | }); 69 | 70 | // Insert the rendered HTML into the table body: 71 | this.$('tbody').html(list); 72 | }, 73 | 74 | events: { 75 | 'click [data-remove]': 'onRemove' 76 | }, 77 | 78 | onRemove: function(evt) { 79 | // Read the "data-remove" attribute from the clicked element: 80 | // this attribute contains an ID for the corresponding model. 81 | var cid = this.$(evt.target).data('remove'); 82 | 83 | // Get the model from the collection: 84 | var model = this.collection.get(cid); 85 | 86 | // Destory the model: 87 | // this removes the model from the collection, 88 | // and clears its data from persisted storage (localStorage). 89 | model.destroy(); 90 | } 91 | }); 92 | 93 | /** 94 | * Create model & view instances: 95 | */ 96 | var contacts = new ContactList(); 97 | var addContactView = new AddContactView({collection: contacts}); 98 | var listContactsView = new ListContactsView({collection: contacts}); 99 | 100 | contacts.fetch(); -------------------------------------------------------------------------------- /02_dom/exercises/name_generator/starter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Name Generator 5 | 22 | 23 | 24 |

Name Generator

25 |

Benedict Cumberbatch.

26 |

27 |

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 2 | 3 | 4 | Gryffindor House 5 | 19 | 20 | 21 |

(Work in the JavaScript Console)

22 | 96 | 97 | -------------------------------------------------------------------------------- /02_dom/exercises/name_generator/complete.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Name Generator 5 | 22 | 23 | 24 |

Name Generator

25 |

Benedict Cumberbatch.

26 |

27 |

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 | John Hammond's Mine 5 | 14 | 15 | 16 |

John Hammond's Mine

17 | Hammond's Mine. 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 | John Hammond's Mine 5 | 14 | 15 | 16 |

John Hammond's Mine

17 | Hammond's Mine. 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 | Professional JavaScript 9 | 10 | 27 | 28 | 29 | 30 |
31 |
32 |

Professional JavaScript Part 1: Fundamentals

33 |
34 |
35 |

Greg MacWilliam, Vox Media

36 |
37 |
38 |

Welcome.

39 |

Please download/install:

40 |
    41 |
  • Course files
  • 42 |
  • Google Chrome
  • 43 |
  • Sublime Text
  • 44 |
45 |
46 |
47 | 48 |
49 |
50 |

Objectives

51 |
52 |
53 |
    54 |
  • Computer Science.
  • 55 |
  • Day 1: JavaScript Fundamentals.
  • 56 |
  • Day 2: Functions & Object-Orientation.
  • 57 |
58 |
59 |
60 | 61 | 85 | 86 |
87 |
88 |

Mindsets

89 | Fixed versus Growth Mindset. 90 |
91 |
92 | 93 |
94 |

JavaScript.

95 |
96 | 97 |
98 |
99 |

History

100 |
101 |

JavaScript was originally developed in May of 1995, over the course of 10 days, by Brendan Eich (now of Mozilla) while working on Netscape Navigator.

102 |

It was designed as a simple browser scripting langauge that would allow web developers to interact closer with the Netscape web browser.

103 |
104 |
105 |
106 | 107 |
108 |
109 |

NOT Java!!

110 |
111 |

"Java" is a completely different programming language. JavaScript was originally called "LiveScript", but was regrettably renamed by the Netscape marketing team to piggy-back off the 90's vogue of Java.

112 |

The name "JavaScript" has been confused ever since.

113 |
114 |
115 |
116 | 117 |
118 |
119 |

What is JavaScript?

120 |
121 |

JavaScript quickly gained universal adoption among web browsers, making it the ubiquitous solution for browser-based application programming. Then in 2012, NodeJS brought JavaScript to the server, making it a full-stack language.

122 |

At present, JavaScript is the most prevelant language used in web development.

123 |
124 |
125 |
126 | 127 |
128 |
129 |

What is ES6?

130 |
131 |

ECMAScript 2015, or ES6, is a new set of syntax features being introduced into the JavaScript language. It's all the rage. However, these features are not yet widely supported by web browsers, so we'll be focusing on ES5.

132 |

Under the hood, all ES6 features are simply fancy new ways of writing ES5 JavaScript.

133 |
134 |
135 |
136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /03_functions/exercises/hogwarts_js/starter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Gryffindor House 5 | 19 | 20 | 21 |

(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 |
34 |

The Raven

35 |

Edgar Allen Poe

36 |
37 | 38 | 41 | ``` 42 | 43 | **Modern element selector methods include:** 44 | 45 | - `document.querySelector("#css-selector")` : Gets ONE element. 46 | - `document.querySelectorAll(".css-selector")` : Gets ALL matching elements. 47 | 48 | **Older selector methods include:** 49 | 50 | - `document.getElementById('id-name')`: Gets ONE element. 51 | - `document.getElementsByClassName('class-name')`: Gets ALL matching class elements. 52 | - `document.getElementsByTagName('tagname')`: Gets ALL matching tag elements. 53 | 54 | ## Exchanging Data 55 | 56 | Once we've selected the DOM elements that we're interested in, we can get and/or set their data. This can be as simple as reading attributes of an element, or as significant as replacing an element's entire HTML content. 57 | 58 | ```html 59 |

Edgar Allen Poe

60 | 61 | 68 | ``` 69 | 70 | **Common element data methods include:** 71 | 72 | - `className`: gets/sets the class name of an element. 73 | - `innerText`: gets/sets the plain text content of an element. 74 | - `innerHTML`: gets/sets the HTML markup within an element. 75 | - `value`: gets/sets the value of `` field elements. 76 | - `getAttribute(attr)`: gets an attribute value of the element. 77 | - `setAttribute(attr, value)`: sets the value of an attribute. 78 | 79 | ## Listening For User Interaction 80 | 81 | To make our applications interactive, we need to know when the user interacts with the DOM (ie: clicking elements, entering text, etc). Thankfully, the DOM does all the heavy lifting of tracking user input for us. The DOM then fires off an **event**, or notification, when an interaction occurs. We use JavaScript to *listen to* the DOM for these event notifications: 82 | 83 | ```html 84 | 85 | 86 | 93 | ``` 94 | 95 | To monitor user input, we select an element that we're interested in and then use `addEventListener` to bind an event. The `addEventListener` method takes two parameters: 96 | 97 | - The **event name** to listen for. 98 | - A **handler function** (callback) to run when the event occurs. 99 | 100 | **Commonly tracked event names:** 101 | 102 | - `"click"`: triggered when elements are clicked. 103 | - `"focus"`: triggered when an element receives browser focus. 104 | - `"input"`: triggered when an input form element recieves input. 105 | - `"change"`: triggered when an input element changes value. 106 | - `"submit"`: triggered by a form element upon submission. 107 | - `"keydown"`: triggered when the user presses a keyboard key. 108 | 109 | ### The Event Object 110 | 111 | When we bind an event handler, that function may declare an argument (usually called `evt`) to recieve the *Event Object*: 112 | 113 | ```html 114 | 115 | 116 | 125 | ``` 126 | 127 | The Event Object contains all kinds of useful data detailing the state of the browser at the time the event occured, including the coordinates of the cursor, the keystroke code, other control keys pressed, etc. The event object also allows us to cancel the browser's default behavior for an event by invoking the `preventDefault` method: 128 | 129 | ```html 130 | Home 131 | 132 | 141 | ``` 142 | 143 | See [event documentation](https://developer.mozilla.org/en-US/docs/Web/API/Event) for a complete summary of event data and methods. 144 | 145 | ### Event Propagation (Bubbling) 146 | 147 | Consider the following DOM structure: 148 | 149 | ```html 150 | 151 | 152 |
153 |

Fun With Propagation

154 |

Let's .

155 |
156 | 157 | 158 | ``` 159 | 160 | Let's say that a user clicks on the "bubble" button. The click event will trigger on the clicked `button` element; however it won't stop there... that `button` is part of a `p` element, so the parent `p` has also been clicked; and that `p` is part of a `section` element, so the parent `section` has also been clicked; and so on in goes. 161 | 162 | In this way, event notifications originate on the elements where the event has actually occured, and then *bubble* up through the DOM to each higher parent node in sequence. This allows us to capture events at any level within the DOM. For example, listening for clicks on the `document` responds to a click anywhere on the page. -------------------------------------------------------------------------------- /03_functions/exercises/hogwarts_js/complete.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Gryffindor House 5 | 19 | 20 | 21 |

(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 |
36 |

Get this headline text!

37 |

Not this paragraph

38 |
39 | ``` 40 | 41 | **Okay, here's a RegEx:** 42 | ```javascript 43 | /]*>(.*?)<\/h1>/g 44 | ``` 45 | 46 | Modern regular expressions were introduced with the Perl language during the 1980's, and are notorious for their daunting illegibility. 47 | 48 | ```javascript 49 | /(\d{1,3}[\.-]?)?\d{3}[\.-]?\d{3}[\.-]?\d{4}/g 50 | ``` 51 | 52 | Don't panic! With time and practice, your eyes will train to read regular expressions... and you'll discover that they are an invaluable tool. 53 | 54 | -- 55 | 56 | ## Let's Get Started! 57 | 58 | Go to [http://www.regexr.com](http://www.regexr.com). This is a handy online Regular Expressions workbench, perfect for learning, testing, and refining RegEx patterns. Workbench tools are your friends! 59 | 60 | Copy and paste the following text corpus into RegExr: 61 | 62 | ``` 63 | The Battle of the Bottles 64 | 65 | 99 bottles of beer on the wall, 99 bottles of beer. 66 | Take a few down, pass them around... 1 bottle of beer on the wall. 67 | ``` 68 | 69 | ## Basic Matchers 70 | 71 | A basic RegEx is formatted as a text pattern wrapped in `/` delimiters: 72 | 73 | ```javascript 74 | /bottle/ 75 | ``` 76 | 77 | ### Search Flags 78 | 79 | A RegEx may include one or more _search flags_ at the end to govern how the pattern is searched: 80 | 81 | ```javascript 82 | // Find all occurances (globally) of the word "bottle": 83 | /bottle/g 84 | ``` 85 | 86 | * `g` Global search (matches all occurances of the pattern). 87 | * `i` Case-Insensitive (matches upper and lowercase letters with indifference). 88 | * `m` Multiline (matches the pattern across line breaks in the text). 89 | 90 | ### Alternation 91 | 92 | A RegEx may match against two or more alternative patterns: 93 | 94 | ```javascript 95 | // Find all occurances of the words "bottle", "battle", and/or "beer" 96 | /bottle|battle|beer/g 97 | ``` 98 | 99 | * `a|b` Match "a" and/or "b". 100 | 101 | ### Reserved Characters 102 | 103 | RegExs reserve several characters for their own operations. The following characters are reserved by RegEx: 104 | 105 | ``` 106 | ^ $ * + . ( ) [ ] { } | / 107 | ``` 108 | 109 | To include a reserved character in a search pattern, you must escape it using `\` (forward slash): 110 | 111 | ```javascript 112 | // "Costs $100 (USD)" 113 | // escaped for RegEx: 114 | /Costs \$100 \(USD\)/ 115 | ``` 116 | 117 | ### Character Sets 118 | 119 | RegExs provide several built-in character classifications for matching common text patterns. 120 | 121 | * `.` Any single character (wild card). 122 | * `\s` Any space character. 123 | * `\S` Any non-space character. 124 | * `\d` Any digit (numbers 0-9). 125 | * `\D` Any non-digit (anything BUT numbers 0-9). 126 | * `\w` Any word character (matches letters, numbers, and underscore). 127 | * `\W` Any non-word character (anything BUT letters, numbers, and underscore). 128 | * `\b` Any word boundary (separators including spaces, commas, and periods). 129 | 130 | ### Classes 131 | 132 | Custom classes allow you to build your own character sets. 133 | 134 | * `[abc]` Positive character class (matches "a", "b", or "c"). 135 | * `[^abc]` Negative character class (matches anything _except_ "a", "b", or "c"). 136 | * `[a-z]` Character range (matches all lowercase letters from A to Z). 137 | * `[a-zA-Z]` Multiple ranges (matches all uppercase and lowercase letters from A to Z). 138 | 139 | ### Anchors 140 | 141 | These flags anchor a search pattern to the start or end of a line of text. Extremely useful! 142 | 143 | * `^` Start of line (ex: `/^hello/` matches "hello world" but not "say hello"). 144 | * `$` End of line (ex: `/hello$/` matches "say hello" but not "hello world"). 145 | 146 | ### Repetitions 147 | 148 | These flags match a pattern repeatedly, turning a single character match into many. 149 | 150 | * `?` Match preceding pattern zero or one times (ex: `/cars?/` will match `"car"` or `"cars"`). 151 | * `*` Match preceding pattern zero or more times (ex: `/sou*p/` will match `"sop"`, `"soup"`, or `"souuuup"`). 152 | * `+` Match preceding pattern one or more times (ex: `/zo+m/` will match `"zom"`, `"zoom"`, or `"zoooooooom"`). 153 | * `{3}` Matches preceding pattern exactly N times (ex: `/bo{3}m/` will match `"booom"`). 154 | * `{1,3}` Matches preceding between N and M times (ex: `/bo{1,3}m/` will match `"bom"`, `"boom"`, or `"booom"`). 155 | 156 | One of the most common repitions you'll see used in RegEx is the universal matcher pattern: `.*`. The dot-star pattern matches any character zero or more times... thus it will match literally _anything_! 157 | 158 | ### Greedy vs. Lazy Repetition 159 | 160 | All repetitions are _greedy_ by default, meaning they will match as many characters as possible before stopping. For example, let's try to match all texts in parenthesis in the following example: 161 | 162 | **Find _greedy_ pattern: `/\(.+\)/g`** 163 | > Greater DC includes Maryland `(MD) and Virginia (NOVA)`." 164 | 165 | The results are unexpected. Our RegEx matches from the first opening parenthesis all the way through to the final closing parenthesis. This is because the RegEx has greedily matched as many characters as possible while fulfilling the repetition pattern. Actually though, we'd like the pattern to match as _few_ characters as possible so that we capture each set of open/close parenthesis individually. To do that, we need _lazy repetition_. These flags match a pattern repeatedly as few times as possible: 166 | 167 | * `*?` Match zero or more of the preceding pattern, as few times as possible. 168 | * `+?` Match one or more of the preceding pattern, as few times as possible. 169 | 170 | **Find _lazy_ pattern: `/\(.+?\)/g`** 171 | > Greater DC includes Maryland `(MD)` and Virginia `(NOVA)`." 172 | 173 | Now for a fancy trick... Lazy repetition is slower than greediness because the RegEx algorithm must perform redundant work. However, we can frequently achieve the same results as a lazy search by using greedy repetition with a negative matcher. This trick works by greedily searching for anything that is NOT our endpoint: 174 | 175 | * `[^x]*` Greedy repetition with a negative matcher (repeatedly find anything that is NOT `x`). 176 | 177 | **Find _greedy-negative_ pattern: `/\([^\)]+\)/g`** 178 | > Greater DC includes Maryland `(MD)` and Virginia `(NOVA)`." 179 | 180 | ## Groups 181 | 182 | Groups are where RegEx starts getting REALLY powerful. With groupings, we can write patterns inside of patterns. This makes individual RegEx operators even more powerful when they can operate on larger patterns. 183 | 184 | * `(...)` Parenthesis establishes a new group. 185 | 186 | **Match both URLs: `/(http:)?\/\/regexr\.com/`** 187 | ``` 188 | http://regexr.com 189 | //regex.com 190 | ``` 191 | 192 | ## Matching Exercises 193 | 194 | Copy and paste the each of the following texts into RegExr, and enable the `global` and `multiline` flags. Then write a regular expression that matches all lines of the text. 195 | 196 | **Match phone numbers:** 197 | ``` 198 | 1-555-555-5555 199 | 555-555-5555 200 | 555.555.5555 201 | 5555555555 202 | ``` 203 | 204 | **Match URLs:** 205 | ``` 206 | http://regexr.com 207 | https://regexr.com 208 | //regexr.com 209 | ``` 210 | 211 | **Match HTML Tags, capturing their text content in a group:** 212 | ``` 213 |

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 += ''+ model.get('name') +''+ model.get('email') +''; 170 | }); 171 | 172 | // Insert the rendered HTML into the table body: 173 | this.$('tbody').html(list); 174 | } 175 | }); 176 | ``` 177 | 178 | This view is not very intelligent. What it's doing is simply listening to our contacts collection for `sync` and `remove` events, which indicate that contacts have been added or removed from the collection. When either of these events occur, the view calls its `render` method, which builds an HTML representation of the current contacts data, and displays that HTML on the screen. 179 | 180 | This is a fundamentally simple pattern... rather than a complex algorithm to manage data and display together (and attempt to keep them synchronized), we've split these concerns apart: our collection manages a list of models. When any of these models change, the view simply re-renders with their revised state. 181 | 182 | **DID WE DO IT RIGHT?** 183 | 184 | The signature of a well-designed application is that you can fully control it from the command line. Let's try it... open the JavaScript console and run this command: 185 | 186 | ``` 187 | contacts.create({name: 'Fluffy Kitty', email: 'kitty@cat.com'}) 188 | ``` 189 | 190 | If your application is hooked up right, you should see your new contact appear within the app, even when you add the contact through the command line! 191 | 192 | # 6. Reload data on page refresh 193 | 194 | Problem: when I reload the page, I don't get my data back! Now, we've already looked within local storage and validated that our data is getting saved, so what's the deal? 195 | 196 | The problem is simple: when we refresh the page, we're seeing a fresh and empty instance of the `ContactsList` collection. We just need to tell it to restore our saved data from the local storage cache. Add this as the last line of your `app.js` file: 197 | 198 | ```javascript 199 | contacts.fetch(); 200 | ``` 201 | 202 | Reload the page and... bam. Our collection fetches data from local storage to restore our previous app state. -------------------------------------------------------------------------------- /03_functions/exercises/functions_deepdive/lib/jasmine-html.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 | jasmineRequire.html = function(j$) { 24 | j$.ResultsNode = jasmineRequire.ResultsNode(); 25 | j$.HtmlReporter = jasmineRequire.HtmlReporter(j$); 26 | j$.QueryString = jasmineRequire.QueryString(); 27 | j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter(); 28 | }; 29 | 30 | jasmineRequire.HtmlReporter = function(j$) { 31 | 32 | var noopTimer = { 33 | start: function() {}, 34 | elapsed: function() { return 0; } 35 | }; 36 | 37 | function HtmlReporter(options) { 38 | var env = options.env || {}, 39 | getContainer = options.getContainer, 40 | createElement = options.createElement, 41 | createTextNode = options.createTextNode, 42 | onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {}, 43 | timer = options.timer || noopTimer, 44 | results = [], 45 | specsExecuted = 0, 46 | failureCount = 0, 47 | pendingSpecCount = 0, 48 | htmlReporterMain, 49 | symbols; 50 | 51 | this.initialize = function() { 52 | htmlReporterMain = createDom("div", {className: "html-reporter"}, 53 | //createDom("div", {className: "banner"}, 54 | //createDom("span", {className: "title"}, "Jasmine"), 55 | //createDom("span", {className: "version"}, j$.version) 56 | //), 57 | createDom("ul", {className: "symbol-summary"}), 58 | createDom("div", {className: "alert"}), 59 | createDom("div", {className: "results"}, 60 | createDom("div", {className: "failures"}) 61 | ) 62 | ); 63 | getContainer().appendChild(htmlReporterMain); 64 | 65 | symbols = find(".symbol-summary"); 66 | }; 67 | 68 | var totalSpecsDefined; 69 | this.jasmineStarted = function(options) { 70 | totalSpecsDefined = options.totalSpecsDefined || 0; 71 | timer.start(); 72 | }; 73 | 74 | var summary = createDom("div", {className: "summary"}); 75 | 76 | var topResults = new j$.ResultsNode({}, "", null), 77 | currentParent = topResults; 78 | 79 | this.suiteStarted = function(result) { 80 | currentParent.addChild(result, "suite"); 81 | currentParent = currentParent.last(); 82 | }; 83 | 84 | this.suiteDone = function(result) { 85 | if (currentParent == topResults) { 86 | return; 87 | } 88 | 89 | currentParent = currentParent.parent; 90 | }; 91 | 92 | this.specStarted = function(result) { 93 | currentParent.addChild(result, "spec"); 94 | }; 95 | 96 | var failures = []; 97 | this.specDone = function(result) { 98 | if (result.status != "disabled") { 99 | specsExecuted++; 100 | } 101 | 102 | symbols.appendChild(createDom("li", { 103 | className: result.status, 104 | id: "spec_" + result.id, 105 | title: result.fullName 106 | } 107 | )); 108 | 109 | if (result.status == "failed") { 110 | failureCount++; 111 | 112 | var failure = 113 | createDom("div", {className: "spec-detail failed"}, 114 | createDom("div", {className: "description"}, 115 | createDom("a", {title: result.fullName, href: specHref(result)}, result.fullName) 116 | ), 117 | createDom("div", {className: "messages"}) 118 | ); 119 | var messages = failure.childNodes[1]; 120 | 121 | for (var i = 0; i < result.failedExpectations.length; i++) { 122 | var expectation = result.failedExpectations[i]; 123 | messages.appendChild(createDom("div", {className: "result-message"}, expectation.message)); 124 | messages.appendChild(createDom("div", {className: "stack-trace"}, expectation.stack)); 125 | } 126 | 127 | failures.push(failure); 128 | } 129 | 130 | if (result.status == "pending") { 131 | pendingSpecCount++; 132 | } 133 | }; 134 | 135 | this.jasmineDone = function() { 136 | var banner = find(".banner"); 137 | //banner.appendChild(createDom("span", {className: "duration"}, "finished in " + timer.elapsed() / 1000 + "s")); 138 | 139 | var alert = find(".alert"); 140 | 141 | alert.appendChild(createDom("span", { className: "exceptions" }, 142 | createDom("label", { className: "label", 'for': "raise-exceptions" }, "raise exceptions"), 143 | createDom("input", { 144 | className: "raise", 145 | id: "raise-exceptions", 146 | type: "checkbox" 147 | }) 148 | )); 149 | var checkbox = find("input"); 150 | 151 | checkbox.checked = !env.catchingExceptions(); 152 | checkbox.onclick = onRaiseExceptionsClick; 153 | 154 | if (specsExecuted < totalSpecsDefined) { 155 | var skippedMessage = "Ran " + specsExecuted + " of " + totalSpecsDefined + " specs - run all"; 156 | alert.appendChild( 157 | createDom("span", {className: "bar skipped"}, 158 | createDom("a", {href: "?", title: "Run all specs"}, skippedMessage) 159 | ) 160 | ); 161 | } 162 | var statusBarMessage = "" + pluralize("spec", specsExecuted) + ", " + pluralize("failure", failureCount); 163 | if (pendingSpecCount) { statusBarMessage += ", " + pluralize("pending spec", pendingSpecCount); } 164 | 165 | var statusBarClassName = "bar " + ((failureCount > 0) ? "failed" : "passed"); 166 | alert.appendChild(createDom("span", {className: statusBarClassName}, statusBarMessage)); 167 | 168 | var results = find(".results"); 169 | results.appendChild(summary); 170 | 171 | summaryList(topResults, summary); 172 | 173 | function summaryList(resultsTree, domParent) { 174 | var specListNode; 175 | for (var i = 0; i < resultsTree.children.length; i++) { 176 | var resultNode = resultsTree.children[i]; 177 | if (resultNode.type == "suite") { 178 | var suiteListNode = createDom("ul", {className: "suite", id: "suite-" + resultNode.result.id}, 179 | createDom("li", {className: "suite-detail"}, 180 | createDom("a", {href: specHref(resultNode.result)}, resultNode.result.description) 181 | ) 182 | ); 183 | 184 | summaryList(resultNode, suiteListNode); 185 | domParent.appendChild(suiteListNode); 186 | } 187 | if (resultNode.type == "spec") { 188 | if (domParent.getAttribute("class") != "specs") { 189 | specListNode = createDom("ul", {className: "specs"}); 190 | domParent.appendChild(specListNode); 191 | } 192 | specListNode.appendChild( 193 | createDom("li", { 194 | className: resultNode.result.status, 195 | id: "spec-" + resultNode.result.id 196 | }, 197 | createDom("a", {href: specHref(resultNode.result)}, resultNode.result.description) 198 | ) 199 | ); 200 | } 201 | } 202 | } 203 | 204 | if (failures.length) { 205 | alert.appendChild( 206 | createDom('span', {className: "menu bar spec-list"}, 207 | createDom("span", {}, "Spec List | "), 208 | createDom('a', {className: "failures-menu", href: "#"}, "Failures"))); 209 | alert.appendChild( 210 | createDom('span', {className: "menu bar failure-list"}, 211 | createDom('a', {className: "spec-list-menu", href: "#"}, "Spec List"), 212 | createDom("span", {}, " | Failures "))); 213 | 214 | find(".failures-menu").onclick = function() { 215 | setMenuModeTo('failure-list'); 216 | }; 217 | find(".spec-list-menu").onclick = function() { 218 | setMenuModeTo('spec-list'); 219 | }; 220 | 221 | setMenuModeTo('failure-list'); 222 | 223 | var failureNode = find(".failures"); 224 | for (var i = 0; i < failures.length; i++) { 225 | failureNode.appendChild(failures[i]); 226 | } 227 | } 228 | }; 229 | 230 | return this; 231 | 232 | function find(selector) { 233 | return getContainer().querySelector(selector); 234 | } 235 | 236 | function createDom(type, attrs, childrenVarArgs) { 237 | var el = createElement(type); 238 | 239 | for (var i = 2; i < arguments.length; i++) { 240 | var child = arguments[i]; 241 | 242 | if (typeof child === 'string') { 243 | el.appendChild(createTextNode(child)); 244 | } else { 245 | if (child) { 246 | el.appendChild(child); 247 | } 248 | } 249 | } 250 | 251 | for (var attr in attrs) { 252 | if (attr == "className") { 253 | el[attr] = attrs[attr]; 254 | } else { 255 | el.setAttribute(attr, attrs[attr]); 256 | } 257 | } 258 | 259 | return el; 260 | } 261 | 262 | function pluralize(singular, count) { 263 | var word = (count == 1 ? singular : singular + "s"); 264 | 265 | return "" + count + " " + word; 266 | } 267 | 268 | function specHref(result) { 269 | return "?spec=" + encodeURIComponent(result.fullName); 270 | } 271 | 272 | function setMenuModeTo(mode) { 273 | htmlReporterMain.setAttribute("class", "html-reporter " + mode); 274 | } 275 | } 276 | 277 | return HtmlReporter; 278 | }; 279 | 280 | jasmineRequire.HtmlSpecFilter = function() { 281 | function HtmlSpecFilter(options) { 282 | var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); 283 | var filterPattern = new RegExp(filterString); 284 | 285 | this.matches = function(specName) { 286 | return filterPattern.test(specName); 287 | }; 288 | } 289 | 290 | return HtmlSpecFilter; 291 | }; 292 | 293 | jasmineRequire.ResultsNode = function() { 294 | function ResultsNode(result, type, parent) { 295 | this.result = result; 296 | this.type = type; 297 | this.parent = parent; 298 | 299 | this.children = []; 300 | 301 | this.addChild = function(result, type) { 302 | this.children.push(new ResultsNode(result, type, this)); 303 | }; 304 | 305 | this.last = function() { 306 | return this.children[this.children.length - 1]; 307 | }; 308 | } 309 | 310 | return ResultsNode; 311 | }; 312 | 313 | jasmineRequire.QueryString = function() { 314 | function QueryString(options) { 315 | 316 | this.setParam = function(key, value) { 317 | var paramMap = queryStringToParamMap(); 318 | paramMap[key] = value; 319 | options.getWindowLocation().search = toQueryString(paramMap); 320 | }; 321 | 322 | this.getParam = function(key) { 323 | return queryStringToParamMap()[key]; 324 | }; 325 | 326 | return this; 327 | 328 | function toQueryString(paramMap) { 329 | var qStrPairs = []; 330 | for (var prop in paramMap) { 331 | qStrPairs.push(encodeURIComponent(prop) + "=" + encodeURIComponent(paramMap[prop])); 332 | } 333 | return "?" + qStrPairs.join('&'); 334 | } 335 | 336 | function queryStringToParamMap() { 337 | var paramStr = options.getWindowLocation().search.substring(1), 338 | params = [], 339 | paramMap = {}; 340 | 341 | if (paramStr.length > 0) { 342 | params = paramStr.split('&'); 343 | for (var i = 0; i < params.length; i++) { 344 | var p = params[i].split('='); 345 | var value = decodeURIComponent(p[1]); 346 | if (value === "true" || value === "false") { 347 | value = JSON.parse(value); 348 | } 349 | paramMap[decodeURIComponent(p[0])] = value; 350 | } 351 | } 352 | 353 | return paramMap; 354 | } 355 | 356 | } 357 | 358 | return QueryString; 359 | }; 360 | -------------------------------------------------------------------------------- /05_backbone/exercises/contact_list/js/vendor/underscore.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.6.0 2 | // http://underscorejs.org 3 | // (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 4 | // Underscore may be freely distributed under the MIT license. 5 | (function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,g=e.filter,d=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,w=Object.keys,_=i.bind,j=function(n){return n instanceof j?n:this instanceof j?void(this._wrapped=n):new j(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=j),exports._=j):n._=j,j.VERSION="1.6.0";var A=j.each=j.forEach=function(n,t,e){if(null==n)return n;if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a=j.keys(n),u=0,i=a.length;i>u;u++)if(t.call(e,n[a[u]],a[u],n)===r)return;return n};j.map=j.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e.push(t.call(r,n,u,i))}),e)};var O="Reduce of empty array with no initial value";j.reduce=j.foldl=j.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=j.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},j.reduceRight=j.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=j.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=j.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},j.find=j.detect=function(n,t,r){var e;return k(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},j.filter=j.select=function(n,t,r){var e=[];return null==n?e:g&&n.filter===g?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&e.push(n)}),e)},j.reject=function(n,t,r){return j.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},j.every=j.all=function(n,t,e){t||(t=j.identity);var u=!0;return null==n?u:d&&n.every===d?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var k=j.some=j.any=function(n,t,e){t||(t=j.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};j.contains=j.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:k(n,function(n){return n===t})},j.invoke=function(n,t){var r=o.call(arguments,2),e=j.isFunction(t);return j.map(n,function(n){return(e?t:n[t]).apply(n,r)})},j.pluck=function(n,t){return j.map(n,j.property(t))},j.where=function(n,t){return j.filter(n,j.matches(t))},j.findWhere=function(n,t){return j.find(n,j.matches(t))},j.max=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.max.apply(Math,n);var e=-1/0,u=-1/0;return A(n,function(n,i,a){var o=t?t.call(r,n,i,a):n;o>u&&(e=n,u=o)}),e},j.min=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.min.apply(Math,n);var e=1/0,u=1/0;return A(n,function(n,i,a){var o=t?t.call(r,n,i,a):n;u>o&&(e=n,u=o)}),e},j.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=j.random(r++),e[r-1]=e[t],e[t]=n}),e},j.sample=function(n,t,r){return null==t||r?(n.length!==+n.length&&(n=j.values(n)),n[j.random(n.length-1)]):j.shuffle(n).slice(0,Math.max(0,t))};var E=function(n){return null==n?j.identity:j.isFunction(n)?n:j.property(n)};j.sortBy=function(n,t,r){return t=E(t),j.pluck(j.map(n,function(n,e,u){return{value:n,index:e,criteria:t.call(r,n,e,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={};return r=E(r),A(t,function(i,a){var o=r.call(e,i,a,t);n(u,o,i)}),u}};j.groupBy=F(function(n,t,r){j.has(n,t)?n[t].push(r):n[t]=[r]}),j.indexBy=F(function(n,t,r){n[t]=r}),j.countBy=F(function(n,t){j.has(n,t)?n[t]++:n[t]=1}),j.sortedIndex=function(n,t,r,e){r=E(r);for(var u=r.call(e,t),i=0,a=n.length;a>i;){var o=i+a>>>1;r.call(e,n[o])t?[]:o.call(n,0,t)},j.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},j.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},j.rest=j.tail=j.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},j.compact=function(n){return j.filter(n,j.identity)};var M=function(n,t,r){return t&&j.every(n,j.isArray)?c.apply(r,n):(A(n,function(n){j.isArray(n)||j.isArguments(n)?t?a.apply(r,n):M(n,t,r):r.push(n)}),r)};j.flatten=function(n,t){return M(n,t,[])},j.without=function(n){return j.difference(n,o.call(arguments,1))},j.partition=function(n,t){var r=[],e=[];return A(n,function(n){(t(n)?r:e).push(n)}),[r,e]},j.uniq=j.unique=function(n,t,r,e){j.isFunction(t)&&(e=r,r=t,t=!1);var u=r?j.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:j.contains(a,r))||(a.push(r),i.push(n[e]))}),i},j.union=function(){return j.uniq(j.flatten(arguments,!0))},j.intersection=function(n){var t=o.call(arguments,1);return j.filter(j.uniq(n),function(n){return j.every(t,function(t){return j.contains(t,n)})})},j.difference=function(n){var t=c.apply(e,o.call(arguments,1));return j.filter(n,function(n){return!j.contains(t,n)})},j.zip=function(){for(var n=j.max(j.pluck(arguments,"length").concat(0)),t=new Array(n),r=0;n>r;r++)t[r]=j.pluck(arguments,""+r);return t},j.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},j.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=j.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},j.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},j.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=new Array(e);e>u;)i[u++]=n,n+=r;return i};var R=function(){};j.bind=function(n,t){var r,e;if(_&&n.bind===_)return _.apply(n,o.call(arguments,1));if(!j.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));R.prototype=n.prototype;var u=new R;R.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},j.partial=function(n){var t=o.call(arguments,1);return function(){for(var r=0,e=t.slice(),u=0,i=e.length;i>u;u++)e[u]===j&&(e[u]=arguments[r++]);for(;r=f?(clearTimeout(a),a=null,o=l,i=n.apply(e,u),e=u=null):a||r.trailing===!1||(a=setTimeout(c,f)),i}},j.debounce=function(n,t,r){var e,u,i,a,o,c=function(){var l=j.now()-a;t>l?e=setTimeout(c,t-l):(e=null,r||(o=n.apply(i,u),i=u=null))};return function(){i=this,u=arguments,a=j.now();var l=r&&!e;return e||(e=setTimeout(c,t)),l&&(o=n.apply(i,u),i=u=null),o}},j.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},j.wrap=function(n,t){return j.partial(t,n)},j.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},j.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},j.keys=function(n){if(!j.isObject(n))return[];if(w)return w(n);var t=[];for(var r in n)j.has(n,r)&&t.push(r);return t},j.values=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},j.pairs=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},j.invert=function(n){for(var t={},r=j.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},j.functions=j.methods=function(n){var t=[];for(var r in n)j.isFunction(n[r])&&t.push(r);return t.sort()},j.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},j.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},j.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)j.contains(r,u)||(t[u]=n[u]);return t},j.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]===void 0&&(n[r]=t[r])}),n},j.clone=function(n){return j.isObject(n)?j.isArray(n)?n.slice():j.extend({},n):n},j.tap=function(n,t){return t(n),n};var S=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof j&&(n=n._wrapped),t instanceof j&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==String(t);case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;var a=n.constructor,o=t.constructor;if(a!==o&&!(j.isFunction(a)&&a instanceof a&&j.isFunction(o)&&o instanceof o)&&"constructor"in n&&"constructor"in t)return!1;r.push(n),e.push(t);var c=0,f=!0;if("[object Array]"==u){if(c=n.length,f=c==t.length)for(;c--&&(f=S(n[c],t[c],r,e)););}else{for(var s in n)if(j.has(n,s)&&(c++,!(f=j.has(t,s)&&S(n[s],t[s],r,e))))break;if(f){for(s in t)if(j.has(t,s)&&!c--)break;f=!c}}return r.pop(),e.pop(),f};j.isEqual=function(n,t){return S(n,t,[],[])},j.isEmpty=function(n){if(null==n)return!0;if(j.isArray(n)||j.isString(n))return 0===n.length;for(var t in n)if(j.has(n,t))return!1;return!0},j.isElement=function(n){return!(!n||1!==n.nodeType)},j.isArray=x||function(n){return"[object Array]"==l.call(n)},j.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){j["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),j.isArguments(arguments)||(j.isArguments=function(n){return!(!n||!j.has(n,"callee"))}),"function"!=typeof/./&&(j.isFunction=function(n){return"function"==typeof n}),j.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},j.isNaN=function(n){return j.isNumber(n)&&n!=+n},j.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},j.isNull=function(n){return null===n},j.isUndefined=function(n){return n===void 0},j.has=function(n,t){return f.call(n,t)},j.noConflict=function(){return n._=t,this},j.identity=function(n){return n},j.constant=function(n){return function(){return n}},j.property=function(n){return function(t){return t[n]}},j.matches=function(n){return function(t){if(t===n)return!0;for(var r in n)if(n[r]!==t[r])return!1;return!0}},j.times=function(n,t,r){for(var e=Array(Math.max(0,n)),u=0;n>u;u++)e[u]=t.call(r,u);return e},j.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},j.now=Date.now||function(){return(new Date).getTime()};var T={escape:{"&":"&","<":"<",">":">",'"':""","'":"'"}};T.unescape=j.invert(T.escape);var I={escape:new RegExp("["+j.keys(T.escape).join("")+"]","g"),unescape:new RegExp("("+j.keys(T.unescape).join("|")+")","g")};j.each(["escape","unescape"],function(n){j[n]=function(t){return null==t?"":(""+t).replace(I[n],function(t){return T[n][t]})}}),j.result=function(n,t){if(null==n)return void 0;var r=n[t];return j.isFunction(r)?r.call(n):r},j.mixin=function(n){A(j.functions(n),function(t){var r=j[t]=n[t];j.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),z.call(this,r.apply(j,n))}})};var N=0;j.uniqueId=function(n){var t=++N+"";return n?n+t:t},j.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\t|\u2028|\u2029/g;j.template=function(n,t,r){var e;r=j.defaults({},r,j.templateSettings);var u=new RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(D,function(n){return"\\"+B[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=new Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,j);var c=function(n){return e.call(this,n,j)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},j.chain=function(n){return j(n).chain()};var z=function(n){return this._chain?j(n).chain():n};j.mixin(j),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];j.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],z.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];j.prototype[n]=function(){return z.call(this,t.apply(this._wrapped,arguments))}}),j.extend(j.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}}),"function"==typeof define&&define.amd&&define("underscore",[],function(){return j})}).call(this); 6 | //# sourceMappingURL=underscore-min.map -------------------------------------------------------------------------------- /05_backbone/exercises/startup_idea/js/vendor/underscore.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.6.0 2 | // http://underscorejs.org 3 | // (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 4 | // Underscore may be freely distributed under the MIT license. 5 | (function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,g=e.filter,d=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,w=Object.keys,_=i.bind,j=function(n){return n instanceof j?n:this instanceof j?void(this._wrapped=n):new j(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=j),exports._=j):n._=j,j.VERSION="1.6.0";var A=j.each=j.forEach=function(n,t,e){if(null==n)return n;if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a=j.keys(n),u=0,i=a.length;i>u;u++)if(t.call(e,n[a[u]],a[u],n)===r)return;return n};j.map=j.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e.push(t.call(r,n,u,i))}),e)};var O="Reduce of empty array with no initial value";j.reduce=j.foldl=j.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=j.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},j.reduceRight=j.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=j.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=j.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},j.find=j.detect=function(n,t,r){var e;return k(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},j.filter=j.select=function(n,t,r){var e=[];return null==n?e:g&&n.filter===g?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&e.push(n)}),e)},j.reject=function(n,t,r){return j.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},j.every=j.all=function(n,t,e){t||(t=j.identity);var u=!0;return null==n?u:d&&n.every===d?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var k=j.some=j.any=function(n,t,e){t||(t=j.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};j.contains=j.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:k(n,function(n){return n===t})},j.invoke=function(n,t){var r=o.call(arguments,2),e=j.isFunction(t);return j.map(n,function(n){return(e?t:n[t]).apply(n,r)})},j.pluck=function(n,t){return j.map(n,j.property(t))},j.where=function(n,t){return j.filter(n,j.matches(t))},j.findWhere=function(n,t){return j.find(n,j.matches(t))},j.max=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.max.apply(Math,n);var e=-1/0,u=-1/0;return A(n,function(n,i,a){var o=t?t.call(r,n,i,a):n;o>u&&(e=n,u=o)}),e},j.min=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.min.apply(Math,n);var e=1/0,u=1/0;return A(n,function(n,i,a){var o=t?t.call(r,n,i,a):n;u>o&&(e=n,u=o)}),e},j.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=j.random(r++),e[r-1]=e[t],e[t]=n}),e},j.sample=function(n,t,r){return null==t||r?(n.length!==+n.length&&(n=j.values(n)),n[j.random(n.length-1)]):j.shuffle(n).slice(0,Math.max(0,t))};var E=function(n){return null==n?j.identity:j.isFunction(n)?n:j.property(n)};j.sortBy=function(n,t,r){return t=E(t),j.pluck(j.map(n,function(n,e,u){return{value:n,index:e,criteria:t.call(r,n,e,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={};return r=E(r),A(t,function(i,a){var o=r.call(e,i,a,t);n(u,o,i)}),u}};j.groupBy=F(function(n,t,r){j.has(n,t)?n[t].push(r):n[t]=[r]}),j.indexBy=F(function(n,t,r){n[t]=r}),j.countBy=F(function(n,t){j.has(n,t)?n[t]++:n[t]=1}),j.sortedIndex=function(n,t,r,e){r=E(r);for(var u=r.call(e,t),i=0,a=n.length;a>i;){var o=i+a>>>1;r.call(e,n[o])t?[]:o.call(n,0,t)},j.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},j.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},j.rest=j.tail=j.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},j.compact=function(n){return j.filter(n,j.identity)};var M=function(n,t,r){return t&&j.every(n,j.isArray)?c.apply(r,n):(A(n,function(n){j.isArray(n)||j.isArguments(n)?t?a.apply(r,n):M(n,t,r):r.push(n)}),r)};j.flatten=function(n,t){return M(n,t,[])},j.without=function(n){return j.difference(n,o.call(arguments,1))},j.partition=function(n,t){var r=[],e=[];return A(n,function(n){(t(n)?r:e).push(n)}),[r,e]},j.uniq=j.unique=function(n,t,r,e){j.isFunction(t)&&(e=r,r=t,t=!1);var u=r?j.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:j.contains(a,r))||(a.push(r),i.push(n[e]))}),i},j.union=function(){return j.uniq(j.flatten(arguments,!0))},j.intersection=function(n){var t=o.call(arguments,1);return j.filter(j.uniq(n),function(n){return j.every(t,function(t){return j.contains(t,n)})})},j.difference=function(n){var t=c.apply(e,o.call(arguments,1));return j.filter(n,function(n){return!j.contains(t,n)})},j.zip=function(){for(var n=j.max(j.pluck(arguments,"length").concat(0)),t=new Array(n),r=0;n>r;r++)t[r]=j.pluck(arguments,""+r);return t},j.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},j.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=j.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},j.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},j.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=new Array(e);e>u;)i[u++]=n,n+=r;return i};var R=function(){};j.bind=function(n,t){var r,e;if(_&&n.bind===_)return _.apply(n,o.call(arguments,1));if(!j.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));R.prototype=n.prototype;var u=new R;R.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},j.partial=function(n){var t=o.call(arguments,1);return function(){for(var r=0,e=t.slice(),u=0,i=e.length;i>u;u++)e[u]===j&&(e[u]=arguments[r++]);for(;r=f?(clearTimeout(a),a=null,o=l,i=n.apply(e,u),e=u=null):a||r.trailing===!1||(a=setTimeout(c,f)),i}},j.debounce=function(n,t,r){var e,u,i,a,o,c=function(){var l=j.now()-a;t>l?e=setTimeout(c,t-l):(e=null,r||(o=n.apply(i,u),i=u=null))};return function(){i=this,u=arguments,a=j.now();var l=r&&!e;return e||(e=setTimeout(c,t)),l&&(o=n.apply(i,u),i=u=null),o}},j.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},j.wrap=function(n,t){return j.partial(t,n)},j.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},j.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},j.keys=function(n){if(!j.isObject(n))return[];if(w)return w(n);var t=[];for(var r in n)j.has(n,r)&&t.push(r);return t},j.values=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},j.pairs=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},j.invert=function(n){for(var t={},r=j.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},j.functions=j.methods=function(n){var t=[];for(var r in n)j.isFunction(n[r])&&t.push(r);return t.sort()},j.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},j.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},j.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)j.contains(r,u)||(t[u]=n[u]);return t},j.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]===void 0&&(n[r]=t[r])}),n},j.clone=function(n){return j.isObject(n)?j.isArray(n)?n.slice():j.extend({},n):n},j.tap=function(n,t){return t(n),n};var S=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof j&&(n=n._wrapped),t instanceof j&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==String(t);case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;var a=n.constructor,o=t.constructor;if(a!==o&&!(j.isFunction(a)&&a instanceof a&&j.isFunction(o)&&o instanceof o)&&"constructor"in n&&"constructor"in t)return!1;r.push(n),e.push(t);var c=0,f=!0;if("[object Array]"==u){if(c=n.length,f=c==t.length)for(;c--&&(f=S(n[c],t[c],r,e)););}else{for(var s in n)if(j.has(n,s)&&(c++,!(f=j.has(t,s)&&S(n[s],t[s],r,e))))break;if(f){for(s in t)if(j.has(t,s)&&!c--)break;f=!c}}return r.pop(),e.pop(),f};j.isEqual=function(n,t){return S(n,t,[],[])},j.isEmpty=function(n){if(null==n)return!0;if(j.isArray(n)||j.isString(n))return 0===n.length;for(var t in n)if(j.has(n,t))return!1;return!0},j.isElement=function(n){return!(!n||1!==n.nodeType)},j.isArray=x||function(n){return"[object Array]"==l.call(n)},j.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){j["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),j.isArguments(arguments)||(j.isArguments=function(n){return!(!n||!j.has(n,"callee"))}),"function"!=typeof/./&&(j.isFunction=function(n){return"function"==typeof n}),j.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},j.isNaN=function(n){return j.isNumber(n)&&n!=+n},j.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},j.isNull=function(n){return null===n},j.isUndefined=function(n){return n===void 0},j.has=function(n,t){return f.call(n,t)},j.noConflict=function(){return n._=t,this},j.identity=function(n){return n},j.constant=function(n){return function(){return n}},j.property=function(n){return function(t){return t[n]}},j.matches=function(n){return function(t){if(t===n)return!0;for(var r in n)if(n[r]!==t[r])return!1;return!0}},j.times=function(n,t,r){for(var e=Array(Math.max(0,n)),u=0;n>u;u++)e[u]=t.call(r,u);return e},j.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},j.now=Date.now||function(){return(new Date).getTime()};var T={escape:{"&":"&","<":"<",">":">",'"':""","'":"'"}};T.unescape=j.invert(T.escape);var I={escape:new RegExp("["+j.keys(T.escape).join("")+"]","g"),unescape:new RegExp("("+j.keys(T.unescape).join("|")+")","g")};j.each(["escape","unescape"],function(n){j[n]=function(t){return null==t?"":(""+t).replace(I[n],function(t){return T[n][t]})}}),j.result=function(n,t){if(null==n)return void 0;var r=n[t];return j.isFunction(r)?r.call(n):r},j.mixin=function(n){A(j.functions(n),function(t){var r=j[t]=n[t];j.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),z.call(this,r.apply(j,n))}})};var N=0;j.uniqueId=function(n){var t=++N+"";return n?n+t:t},j.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\t|\u2028|\u2029/g;j.template=function(n,t,r){var e;r=j.defaults({},r,j.templateSettings);var u=new RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(D,function(n){return"\\"+B[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=new Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,j);var c=function(n){return e.call(this,n,j)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},j.chain=function(n){return j(n).chain()};var z=function(n){return this._chain?j(n).chain():n};j.mixin(j),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];j.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],z.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];j.prototype[n]=function(){return z.call(this,t.apply(this._wrapped,arguments))}}),j.extend(j.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}}),"function"==typeof define&&define.amd&&define("underscore",[],function(){return j})}).call(this); 6 | //# sourceMappingURL=underscore-min.map --------------------------------------------------------------------------------