├── License.md ├── README.md ├── css ├── icomoon.css ├── normalize.css └── style.css ├── fonts ├── icomoon.eot ├── icomoon.svg ├── icomoon.ttf └── icomoon.woff ├── index.html ├── jasmine └── spec │ └── feedreader.js └── js └── app.js /License.md: -------------------------------------------------------------------------------- 1 | 2 | Copyright © 2012 - 2020, Udacity, Inc. 3 | 4 | Udacity hereby grants you a license in and to the Educational Content, including but not limited to homework assignments, programming assignments, code samples, and other educational materials and tools (as further described in the Udacity Terms of Use), subject to, as modified herein, the terms and conditions of the Creative Commons Attribution-NonCommercial- NoDerivs 3.0 License located at http://creativecommons.org/licenses/by-nc-nd/4.0 and successor locations for such license (the "CC License") provided that, in each case, the Educational Content is specifically marked as being subject to the CC License. 5 | 6 | Udacity expressly defines the following as falling outside the definition of "non-commercial": 7 | (a) the sale or rental of (i) any part of the Educational Content, (ii) any derivative works based at least in part on the Educational Content, or (iii) any collective work that includes any part of the Educational Content; 8 | (b) the sale of access or a link to any part of the Educational Content without first obtaining informed consent from the buyer (that the buyer is aware that the Educational Content, or such part thereof, is available at the Website free of charge); 9 | (c) providing training, support, or editorial services that use or reference the Educational Content in exchange for a fee; 10 | (d) the sale of advertisements, sponsorships, or promotions placed on the Educational Content, or any part thereof, or the sale of advertisements, sponsorships, or promotions on any website or blog containing any part of the Educational Material, including without limitation any "pop-up advertisements"; 11 | (e) the use of Educational Content by a college, university, school, or other educational institution for instruction where tuition is charged; and 12 | (f) the use of Educational Content by a for-profit corporation or non-profit entity for internal professional development or training. 13 | 14 | 15 | 16 | THE SERVICES AND ONLINE COURSES (INCLUDING ANY CONTENT) ARE PROVIDED "AS IS" AND "AS AVAILABLE" WITH NO REPRESENTATIONS OR WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. YOU ASSUME TOTAL RESPONSIBILITY AND THE ENTIRE RISK FOR YOUR USE OF THE SERVICES, ONLINE COURSES, AND CONTENT. WITHOUT LIMITING THE FOREGOING, WE DO NOT WARRANT THAT (A) THE SERVICES, WEBSITES, CONTENT, OR THE ONLINE COURSES WILL MEET YOUR REQUIREMENTS OR EXPECTATIONS OR ACHIEVE THE INTENDED PURPOSES, (B) THE WEBSITES OR THE ONLINE COURSES WILL NOT EXPERIENCE OUTAGES OR OTHERWISE BE UNINTERRUPTED, TIMELY, SECURE OR ERROR-FREE, (C) THE INFORMATION OR CONTENT OBTAINED THROUGH THE SERVICES, SUCH AS CHAT ROOM SERVICES, WILL BE ACCURATE, COMPLETE, CURRENT, ERROR- FREE, COMPLETELY SECURE OR RELIABLE, OR (D) THAT DEFECTS IN OR ON THE SERVICES OR CONTENT WILL BE CORRECTED. YOU ASSUME ALL RISK OF PERSONAL INJURY, INCLUDING DEATH AND DAMAGE TO PERSONAL PROPERTY, SUSTAINED FROM USE OF SERVICES. 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Project Overview 2 | 3 | In this project you are given a web-based application that reads RSS feeds. The original developer of this application clearly saw the value in testing, they've already included [Jasmine](http://jasmine.github.io/) and even started writing their first test suite! Unfortunately, they decided to move on to start their own company and we're now left with an application with an incomplete test suite. That's where you come in. 4 | 5 | 6 | ## Why this Project? 7 | 8 | Testing is an important part of the development process and many organizations practice a standard of development known as "test-driven development." This is when developers write tests first, before they ever start developing their application. All the tests initially fail and then they start writing application code to make these tests pass. 9 | 10 | Whether you work in an organization that uses test-driven development or in an organization that uses tests to make sure future feature development doesn't break existing features, it's an important skill to have! 11 | 12 | 13 | ## What will I learn? 14 | 15 | You will learn how to use Jasmine to write a number of tests against a pre-existing application. These will test the underlying business logic of the application as well as the event handling and DOM manipulation. 16 | 17 | 18 | ## How will this help my career? 19 | 20 | Writing effective tests requires analyzing multiple aspects of an application including the HTML, CSS and JavaScript - an extremely important skill when changing teams or joining a new company. 21 | 22 | Good tests give you the ability to quickly analyze whether new code breaks an existing feature within your codebase, without having to manually test all of the functionality. 23 | 24 | 25 | # Development Strategy 26 | 27 | For a refresher (or reference) before you begin writing code, we recommend reviewing the content from [JavaScript Testing](https://www.udacity.com/course/javascript-testing--ud549). Your project will be evaluated by a Udacity code reviewer according to the [Feed Reader Testing project rubric](https://review.udacity.com/#!/rubrics/18/view). Please review for detailed project requirements. 28 | 29 | 1. Familiarize yourself with the starter code 30 | * Open up `index.html` and review the functionality of the application within your browser 31 | * What is all the code in `app.js` doing? Be sure to read all code comments 32 | * Check out `style.css`. How is styling applied to the application? 33 | 2. Explore the Jasmine spec file in `feedreader.js` 34 | * This is the file in which you'll be writing your tests 35 | * Make sure to read all code comments here as well 36 | * Review the [Jasmine documentation](http://jasmine.github.io) if needed 37 | 3. Edit the `allFeeds` variable in `app.js` to make the provided test fail 38 | * See how Jasmine visualizes this failure in your application 39 | * Return the `allFeeds` variable to a passing state after reviewing the failed test 40 | 4. Write a test that loops through each feed in the `allFeeds` object and ensures it has a URL defined _and_ that the URL is not empty 41 | * For example, how would you use a `for...of` loop in this test? 42 | 5. Write a test that loops through each feed in the `allFeeds` object and ensures it has a name defined and that the name is not empty 43 | * Think about how you wrote the previous test. What are you testing for this time? 44 | 6. Write a new test suite named `"The menu"` 45 | * What are you `describe`-ing in this test suite? 46 | 7. Write a test that ensures the menu element is hidden by default 47 | * You'll have to analyze the HTML and the CSS to determine how the hiding/showing of the menu element is implemented 48 | * What code in `app.js` is directly involved with toggling the menu on and off? 49 | 8. Write a test that ensures the menu changes visibility when the menu icon is clicked. This test should have two expectations: does the menu display itself when clicked, and does it hide when clicked again? 50 | * Think about how you wrote the previous test. What is different this time around? 51 | * Which clickable element are you checking for? 52 | * How do you "simulate" a mouse click that element without actually clicking it? 53 | 9. Write a test suite named `"Initial Entries"` 54 | * What are you `describe`-ing in this test suite? 55 | 10. Write a test that ensures when the `loadFeed` function is called and completes its work, there is at least a single `.entry` element within the `.feed` container 56 | * How does Jasmine's `beforeEach()`function work? 57 | * How does the `loadFeed()` function in `app.js` work? Is it synchronous or asynchronous? 58 | 11. Write a test suite named `"New Feed Selection"` 59 | * What are you `describe`-ing in this test suite? 60 | 12. Write a test that ensures when a new feed is loaded by the `loadFeed` function that the content actually changes 61 | * How is this test different from the previous test? 62 | 63 | Additionally, note that: 64 | 65 | * No test should be dependent on the results of another 66 | * Callbacks should be used to ensure that feeds are loaded before they are tested 67 | * Error handling should be implemented for undefined variables and out-of-bound array access 68 | * When complete, all of your tests should pass 69 | 70 | When you're all finished, write a `README` file detailing all steps required to successfully run the application. If you have added additional tests, provide documentation for what these future features are and what the tests are checking for. 71 | 72 | # Contributing 73 | 74 | This repository is the starter code for _all_ Udacity students. Therefore, we most likely will not accept pull requests. 75 | 76 | # Archival Note 77 | This repository is deprecated; therefore, we are going to archive it. However, learners will be able to fork it to their personal Github account but cannot submit PRs to this repository. If you have any issues or suggestions to make, feel free to: 78 | - Utilize the https://knowledge.udacity.com/ forum to seek help on content-specific issues. 79 | - Submit a support ticket along with the link to your forked repository if (learners are) blocked for other reasons. Here are the links for the [retail consumers](https://udacity.zendesk.com/hc/en-us/requests/new) and [enterprise learners](https://udacityenterprise.zendesk.com/hc/en-us/requests/new?ticket_form_id=360000279131). -------------------------------------------------------------------------------- /css/icomoon.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'icomoon'; 3 | src:url('../fonts/icomoon.eot?efsk18'); 4 | src:url('../fonts/icomoon.eot?#iefixefsk18') format('embedded-opentype'), 5 | url('../fonts/icomoon.woff?efsk18') format('woff'), 6 | url('../fonts/icomoon.ttf?efsk18') format('truetype'), 7 | url('../fonts/icomoon.svg?efsk18#icomoon') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | 12 | [class^="icon-"], [class*=" icon-"] { 13 | font-family: 'icomoon'; 14 | speak: none; 15 | font-style: normal; 16 | font-weight: normal; 17 | font-variant: normal; 18 | text-transform: none; 19 | line-height: 1; 20 | 21 | /* Better Font Rendering =========== */ 22 | -webkit-font-smoothing: antialiased; 23 | -moz-osx-font-smoothing: grayscale; 24 | } 25 | 26 | .icon-list:before { 27 | content: "\e600"; 28 | } 29 | -------------------------------------------------------------------------------- /css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability when focused and also mouse hovered in all browsers. 95 | */ 96 | 97 | a:active, 98 | a:hover { 99 | outline: 0; 100 | } 101 | 102 | /* Text-level semantics 103 | ========================================================================== */ 104 | 105 | /** 106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 107 | */ 108 | 109 | abbr[title] { 110 | border-bottom: 1px dotted; 111 | } 112 | 113 | /** 114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 115 | */ 116 | 117 | b, 118 | strong { 119 | font-weight: bold; 120 | } 121 | 122 | /** 123 | * Address styling not present in Safari and Chrome. 124 | */ 125 | 126 | dfn { 127 | font-style: italic; 128 | } 129 | 130 | /** 131 | * Address variable `h1` font-size and margin within `section` and `article` 132 | * contexts in Firefox 4+, Safari, and Chrome. 133 | */ 134 | 135 | h1 { 136 | font-size: 2em; 137 | margin: 0.67em 0; 138 | } 139 | 140 | /** 141 | * Address styling not present in IE 8/9. 142 | */ 143 | 144 | mark { 145 | background: #ff0; 146 | color: #000; 147 | } 148 | 149 | /** 150 | * Address inconsistent and variable font size in all browsers. 151 | */ 152 | 153 | small { 154 | font-size: 80%; 155 | } 156 | 157 | /** 158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 159 | */ 160 | 161 | sub, 162 | sup { 163 | font-size: 75%; 164 | line-height: 0; 165 | position: relative; 166 | vertical-align: baseline; 167 | } 168 | 169 | sup { 170 | top: -0.5em; 171 | } 172 | 173 | sub { 174 | bottom: -0.25em; 175 | } 176 | 177 | /* Embedded content 178 | ========================================================================== */ 179 | 180 | /** 181 | * Remove border when inside `a` element in IE 8/9/10. 182 | */ 183 | 184 | img { 185 | border: 0; 186 | } 187 | 188 | /** 189 | * Correct overflow not hidden in IE 9/10/11. 190 | */ 191 | 192 | svg:not(:root) { 193 | overflow: hidden; 194 | } 195 | 196 | /* Grouping content 197 | ========================================================================== */ 198 | 199 | /** 200 | * Address margin not present in IE 8/9 and Safari. 201 | */ 202 | 203 | figure { 204 | margin: 1em 40px; 205 | } 206 | 207 | /** 208 | * Address differences between Firefox and other browsers. 209 | */ 210 | 211 | hr { 212 | -moz-box-sizing: content-box; 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't attempt to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 354 | * (include `-moz` to future-proof). 355 | */ 356 | 357 | input[type="search"] { 358 | -webkit-appearance: textfield; /* 1 */ 359 | -moz-box-sizing: content-box; 360 | -webkit-box-sizing: content-box; /* 2 */ 361 | box-sizing: content-box; 362 | } 363 | 364 | /** 365 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 366 | * Safari (but not Chrome) clips the cancel button when the search input has 367 | * padding (and `textfield` appearance). 368 | */ 369 | 370 | input[type="search"]::-webkit-search-cancel-button, 371 | input[type="search"]::-webkit-search-decoration { 372 | -webkit-appearance: none; 373 | } 374 | 375 | /** 376 | * Define consistent border, margin, and padding. 377 | */ 378 | 379 | fieldset { 380 | border: 1px solid #c0c0c0; 381 | margin: 0 2px; 382 | padding: 0.35em 0.625em 0.75em; 383 | } 384 | 385 | /** 386 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 387 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 388 | */ 389 | 390 | legend { 391 | border: 0; /* 1 */ 392 | padding: 0; /* 2 */ 393 | } 394 | 395 | /** 396 | * Remove default vertical scrollbar in IE 8/9/10/11. 397 | */ 398 | 399 | textarea { 400 | overflow: auto; 401 | } 402 | 403 | /** 404 | * Don't inherit the `font-weight` (applied by a rule above). 405 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 406 | */ 407 | 408 | optgroup { 409 | font-weight: bold; 410 | } 411 | 412 | /* Tables 413 | ========================================================================== */ 414 | 415 | /** 416 | * Remove most spacing between table cells. 417 | */ 418 | 419 | table { 420 | border-collapse: collapse; 421 | border-spacing: 0; 422 | } 423 | 424 | td, 425 | th { 426 | padding: 0; 427 | } 428 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | *, 2 | *:after, 3 | *:before: { 4 | box-sizing: inherit; 5 | } 6 | 7 | html { 8 | box-sizing: border-box; 9 | } 10 | 11 | body { 12 | color: #111; 13 | font-family: 'Roboto', sans-serif; 14 | font-weight: 300; 15 | margin: 0; 16 | } 17 | 18 | a { 19 | text-decoration: none; 20 | color: #111; 21 | } 22 | 23 | a:hover h2 { 24 | text-decoration: underline; 25 | } 26 | 27 | h1 { 28 | display: inline-block; 29 | font-weight: 100; 30 | margin: 0 0 0 0.5em; 31 | } 32 | 33 | h2 { 34 | font-weight: 400; 35 | margin: 0; 36 | } 37 | 38 | .header { 39 | background: #4caf50; 40 | color: #fff; 41 | position: fixed; 42 | top: 0; 43 | width: 100%; 44 | padding: 0.5em 1em; 45 | } 46 | 47 | .icon-list { 48 | font-size: 1.5em; 49 | color: #FFF; 50 | } 51 | 52 | .slide-menu { 53 | position: absolute; 54 | top: 3.5em; 55 | padding: 1em; 56 | background: #4caf50; 57 | height: 100%; 58 | width: 10em; 59 | transform: translate3d(0, 0, 0); 60 | transition: transform 0.2s; 61 | } 62 | 63 | .slide-menu ul { 64 | list-style: none; 65 | margin: 0; 66 | padding: 0; 67 | } 68 | 69 | .slide-menu li { 70 | padding: 0; 71 | } 72 | 73 | .slide-menu li a { 74 | color: #fff; 75 | display: block; 76 | padding: 0.5em 0; 77 | } 78 | 79 | .menu-hidden .slide-menu { 80 | transform: translate3d(-12em, 0, 0); 81 | transition: transform 0.2s; 82 | } 83 | 84 | .feed { 85 | margin: 4em 0 0; 86 | } 87 | 88 | .entry { 89 | border-bottom: 1px solid #ddd; 90 | padding: 1em; 91 | margin: 0.5em 0; 92 | } 93 | -------------------------------------------------------------------------------- /fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/frontend-nanodegree-feedreader/49dd8624987bd8cf0e0a9a2e3a5dac9d430ef1f9/fonts/icomoon.eot -------------------------------------------------------------------------------- /fonts/icomoon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/frontend-nanodegree-feedreader/49dd8624987bd8cf0e0a9a2e3a5dac9d430ef1f9/fonts/icomoon.ttf -------------------------------------------------------------------------------- /fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/frontend-nanodegree-feedreader/49dd8624987bd8cf0e0a9a2e3a5dac9d430ef1f9/fonts/icomoon.woff -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | UdaciFeeds 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 |

Feeds

27 |
28 | 29 |
30 | 31 |
32 | 33 |
34 | 35 | 43 | 44 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /jasmine/spec/feedreader.js: -------------------------------------------------------------------------------- 1 | /* feedreader.js 2 | * 3 | * This is the spec file that Jasmine will read and contains 4 | * all of the tests that will be run against your application. 5 | */ 6 | 7 | /* We're placing all of our tests within the $() function, 8 | * since some of these tests may require DOM elements. We want 9 | * to ensure they don't run until the DOM is ready. 10 | */ 11 | $(function() { 12 | /* This is our first test suite - a test suite just contains 13 | * a related set of tests. This suite is all about the RSS 14 | * feeds definitions, the allFeeds variable in our application. 15 | */ 16 | describe('RSS Feeds', function() { 17 | /* This is our first test - it tests to make sure that the 18 | * allFeeds variable has been defined and that it is not 19 | * empty. Experiment with this before you get started on 20 | * the rest of this project. What happens when you change 21 | * allFeeds in app.js to be an empty array and refresh the 22 | * page? 23 | */ 24 | it('are defined', function() { 25 | expect(allFeeds).toBeDefined(); 26 | expect(allFeeds.length).not.toBe(0); 27 | }); 28 | 29 | 30 | /* TODO: Write a test that loops through each feed 31 | * in the allFeeds object and ensures it has a URL defined 32 | * and that the URL is not empty. 33 | */ 34 | 35 | 36 | /* TODO: Write a test that loops through each feed 37 | * in the allFeeds object and ensures it has a name defined 38 | * and that the name is not empty. 39 | */ 40 | }); 41 | 42 | 43 | /* TODO: Write a new test suite named "The menu" */ 44 | 45 | /* TODO: Write a test that ensures the menu element is 46 | * hidden by default. You'll have to analyze the HTML and 47 | * the CSS to determine how we're performing the 48 | * hiding/showing of the menu element. 49 | */ 50 | 51 | /* TODO: Write a test that ensures the menu changes 52 | * visibility when the menu icon is clicked. This test 53 | * should have two expectations: does the menu display when 54 | * clicked and does it hide when clicked again. 55 | */ 56 | 57 | /* TODO: Write a new test suite named "Initial Entries" */ 58 | 59 | /* TODO: Write a test that ensures when the loadFeed 60 | * function is called and completes its work, there is at least 61 | * a single .entry element within the .feed container. 62 | * Remember, loadFeed() is asynchronous so this test will require 63 | * the use of Jasmine's beforeEach and asynchronous done() function. 64 | */ 65 | 66 | /* TODO: Write a new test suite named "New Feed Selection" */ 67 | 68 | /* TODO: Write a test that ensures when a new feed is loaded 69 | * by the loadFeed function that the content actually changes. 70 | * Remember, loadFeed() is asynchronous. 71 | */ 72 | }()); 73 | -------------------------------------------------------------------------------- /js/app.js: -------------------------------------------------------------------------------- 1 | /* app.js 2 | * 3 | * This is our RSS feed reader application. It uses the Google 4 | * Feed Reader API to grab RSS feeds as JSON object we can make 5 | * use of. It also uses the Handlebars templating library and 6 | * jQuery. 7 | */ 8 | 9 | // The names and URLs to all of the feeds we'd like available. 10 | var allFeeds = [ 11 | { 12 | name: 'Udacity Blog', 13 | url: 'http://blog.udacity.com/feed' 14 | }, { 15 | name: 'CSS Tricks', 16 | url: 'http://feeds.feedburner.com/CssTricks' 17 | }, { 18 | name: 'HTML5 Rocks', 19 | url: 'http://feeds.feedburner.com/html5rocks' 20 | }, { 21 | name: 'Linear Digressions', 22 | url: 'http://feeds.feedburner.com/udacity-linear-digressions' 23 | } 24 | ]; 25 | 26 | /* This function starts up our application. The Google Feed 27 | * Reader API is loaded asynchonously and will then call this 28 | * function when the API is loaded. 29 | */ 30 | function init() { 31 | // Load the first feed we've defined (index of 0). 32 | loadFeed(0); 33 | } 34 | 35 | /* This function performs everything necessary to load a 36 | * feed using the Google Feed Reader API. It will then 37 | * perform all of the DOM operations required to display 38 | * feed entries on the page. Feeds are referenced by their 39 | * index position within the allFeeds array. 40 | * This function all supports a callback as the second parameter 41 | * which will be called after everything has run successfully. 42 | */ 43 | function loadFeed(id, cb) { 44 | var feedUrl = allFeeds[id].url, 45 | feedName = allFeeds[id].name; 46 | 47 | $.ajax({ 48 | type: "POST", 49 | url: 'https://rsstojson.udacity.com/parseFeed', 50 | data: JSON.stringify({url: feedUrl}), 51 | contentType:"application/json", 52 | success: function (result, status){ 53 | 54 | var container = $('.feed'), 55 | title = $('.header-title'), 56 | entries = result.feed.entries, 57 | entriesLen = entries.length, 58 | entryTemplate = Handlebars.compile($('.tpl-entry').html()); 59 | 60 | title.html(feedName); // Set the header text 61 | container.empty(); // Empty out all previous entries 62 | 63 | /* Loop through the entries we just loaded via the Google 64 | * Feed Reader API. We'll then parse that entry against the 65 | * entryTemplate (created above using Handlebars) and append 66 | * the resulting HTML to the list of entries on the page. 67 | */ 68 | entries.forEach(function(entry) { 69 | container.append(entryTemplate(entry)); 70 | }); 71 | 72 | if (cb) { 73 | cb(); 74 | } 75 | }, 76 | error: function (result, status, err){ 77 | //run only the callback without attempting to parse result due to error 78 | if (cb) { 79 | cb(); 80 | } 81 | }, 82 | dataType: "json" 83 | }); 84 | } 85 | 86 | /* Google API: Loads the Feed Reader API and defines what function 87 | * to call when the Feed Reader API is done loading. 88 | */ 89 | google.setOnLoadCallback(init); 90 | 91 | /* All of this functionality is heavily reliant upon the DOM, so we 92 | * place our code in the $() function to ensure it doesn't execute 93 | * until the DOM is ready. 94 | */ 95 | $(function() { 96 | var container = $('.feed'), 97 | feedList = $('.feed-list'), 98 | feedItemTemplate = Handlebars.compile($('.tpl-feed-list-item').html()), 99 | feedId = 0, 100 | menuIcon = $('.menu-icon-link'); 101 | 102 | /* Loop through all of our feeds, assigning an id property to 103 | * each of the feeds based upon its index within the array. 104 | * Then parse that feed against the feedItemTemplate (created 105 | * above using Handlebars) and append it to the list of all 106 | * available feeds within the menu. 107 | */ 108 | allFeeds.forEach(function(feed) { 109 | feed.id = feedId; 110 | feedList.append(feedItemTemplate(feed)); 111 | 112 | feedId++; 113 | }); 114 | 115 | /* When a link in our feedList is clicked on, we want to hide 116 | * the menu, load the feed, and prevent the default action 117 | * (following the link) from occurring. 118 | */ 119 | feedList.on('click', 'a', function() { 120 | var item = $(this); 121 | 122 | $('body').addClass('menu-hidden'); 123 | loadFeed(item.data('id')); 124 | return false; 125 | }); 126 | 127 | /* When the menu icon is clicked on, we need to toggle a class 128 | * on the body to perform the hiding/showing of our menu. 129 | */ 130 | menuIcon.on('click', function() { 131 | $('body').toggleClass('menu-hidden'); 132 | }); 133 | }()); 134 | --------------------------------------------------------------------------------