text:
const canvas = document.querySelector('.myCanvas');
const width = canvas.width = window.innerWidth;
const height = canvas.height = window.innerHeight-85;
const ctx = canvas.getContext('2d');

ctx.fillStyle = 'rgb(0,0,0)';
ctx.fillRect(0,0,width,height);

const colorPicker = document.querySelector('input[type="color"]');
const sizePicker = document.querySelector('input[type="range"]');
const output = document.querySelector('.output');
const clearBtn = document.querySelector('button');

// covert degrees to radians
function degToRad(degrees) {
  return degrees * Math.PI / 180;
};

// update sizepicker output value

sizePicker.addEventListener('input', () => output.textContent = sizePicker.value);

// store mouse pointer coordinates, and whether the button is pressed
let curX;
let curY;
let pressed = false;

// update mouse pointer coordinates
document.addEventListener('mousemove', e => {
  curX = (window.Event) ? e.pageX : e.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
  curY = (window.Event) ? e.pageY : e.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
});

canvas.addEventListener('mousedown', () => pressed = true);

canvas.addEventListener('mouseup', () => pressed = false);

clearBtn.addEventListener('click', () => {
  ctx.fillStyle = 'rgb(0,0,0)';
  ctx.fillRect(0,0,width,height);
});

function draw() {
  if (pressed) {
    ctx.fillStyle = colorPicker.value;
    ctx.beginPath();
    ctx.arc(curX, curY-85, sizePicker.value, degToRad(0), degToRad(360), false);
    ctx.fill();
  }

  requestAnimationFrame(draw);
}

draw();


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Canvas</title>

    <link href="style.css" rel="stylesheet">

    <canvas class="myCanvas">

Add suitable fallback here.

    </canvas>

repo_name: mdn/learning-area

repo_language: HTML

mime_type: text/plain


text:
body {
  margin: 0;
  overflow: hidden;
}


text:
const canvas = document.querySelector('.myCanvas');
const width = canvas.width = window.innerWidth;
const height = canvas.height = window.innerHeight;
const ctx = canvas.getContext('2d');

ctx.fillStyle = 'rgb(0,0,0)';
ctx.fillRect(0,0,width,height);

ctx.translate(width/2,height/2);

const image = new Image();
image.src = 'walk-right.png';
image.onload = draw;

let sprite = 0;
let posX = 0;

function draw() {
  ctx.fillRect(-(width/2),-(height/2),width,height);
  ctx.drawImage(image, (sprite*102), 0, 102, 148, 0+posX, -74, 102, 148);

  if (posX % 13 === 0) {
    if (sprite === 5) {
      sprite = 0;
    } else {
      sprite++;
    }
  }

  if (posX > width/2) {
    let newStartPos = -((width/2) + 102);
    posX = Math.ceil(newStartPos);
    console.log(posX);
  } else {
    posX += 2;
  }

  window.requestAnimationFrame(draw);
};


text:
Out- out are the lights- out all!
   And, over each quivering form,
The curtain, a funeral pall,
   Comes down with the rush of a storm,
While the angels, all pallid and wan,
   Uprising, unveiling, affirm
That the play is the tragedy, "Man,"
   And its hero the Conqueror Worm.


text:
<!DOCTYPE html>

    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <title>Fetch starting point</title>

Fetch starting point

    <form>
      Choose a verse
      <select id="verse-choose" name="verse-choose">
        <option>Verse 1</option>
        <option>Verse 2</option>
        <option>Verse 3</option>
        <option>Verse 4</option>
      </select>
    </form>

The Conqueror Worm, Edgar Allen Poe, 1843


text:
Lo! 'tis a gala night
   Within the lonesome latter years!
An angel throng, bewinged, bedight
   In veils, and drowned in tears,
Sit in a theatre, to see
   A play of hopes and fears,
While the orchestra breathes fitfully
   The music of the spheres.
Mimes, in the form of God on high,
   Mutter and mumble low,
And hither and thither fly-
   Mere puppets they, who come and go
At bidding of vast formless things
   That shift the scenery to and fro,
Flapping from out their Condor wings
   Invisible Woe!


text:
But see, amid the mimic rout
   A crawling shape intrude!
A blood-red thing that writhes from out
   The scenic solitude!
It writhes!- it writhes!- with mortal pangs
   The mimes become its food,
And seraphs sob at vermin fangs
   In human gore imbued.


text:
That motley drama- oh, be sure
   It shall not be forgot!
With its Phantom chased for evermore,
   By a crowd that seize it not,
Through a circle that ever returneth in
   To the self-same spot,
And much of Madness, and more of Sin,
   And Horror the soul of the plot.


text:
/* General */

html {
  font-family: 'Raleway', sans-serif;
  background-color: yellow;
  font-size: 10px;
}

body {
  width: 800px;
  margin: 0 auto;
}

/* Typography */

h1, h2 {
  font-family: 'Cherry Swash', cursive;
  margin: 0;
  text-align: center;
}

h1 {
  font-size: 5rem;
  background: url(icons/bean_can.png) no-repeat 20% center, url(icons/bean_can.png) no-repeat 80% center;
  background-size: 48px 48px;
}

h2 {
  font-size: 2rem;
  color: yellow;
  text-align: right;
  width: 160px;
  position: absolute;
  right: 4%;
  z-index: 3000;
  text-shadow: 2px 2px 1px black;
  line-height: 2rem;
  padding-top: 0.3rem;
}

p, label, input, select, button, li {
  font-size: 1.5rem;
}

li {
  line-height: 1.5;
}

/* block styling */

header, aside, main, footer {
  padding: 20px;
}

header {
  border: 5px solid black;
  border-bottom: 0;
}

aside {
  border-right: 5px solid black;
}

body > div {
  border: 5px solid black;
  display: flex;
}

footer {
  border: 5px solid black;
  border-top: 0;
}

aside {
  flex: 3;
}

main {
  flex: 7;
}

/* Form styling */

aside {
  display: flex;
  flex-flow: column;
  align-items: stretch;
}

aside div {
  margin-bottom: 20px;
}

label, select, input {
  display: block;
}

label {
  margin-bottom: 5px;
}

select, input, button {
  width: 100%;
}

input {
  background: black;
  box-shadow: inset 1px 1px 1px white;
  color: yellow;
  border: none;
  padding: 5px;
  width: 201.5px;
}

button {
  background: black;
  color: yellow;
  padding: 5px 0;
  border: none;
  border-radius: 10px;
  box-shadow: inset 1px 1px 2px white, inset -1px -1px 2px #ccc;
}

/* Styling the products */

main {
  display: flex;
  flex-flow: row wrap;
  justify-content: space-around;
}

section {
  width: 45%;
  margin: 5px;
  background-color: black;
  position: relative;
  height: 215px;
}

section img {
  width: 100%;
  display: block;
  position: absolute;
  bottom: 0px;
}

section p {
  color: yellow;
  background: black;
  width: 50px;
  height: 50px;
  line-height: 50px;
  text-align: center;
  border-radius: 50%;
  box-shadow: 2px 2px 1px yellow;
  position: absolute;
  right: 10px;
  bottom: -6px;
  z-index: 2000;
}

.vegetables {
  background: black url(icons/vegetable.png) no-repeat 2px 2px;
  background-size: 24px 24px;
}

.meat {
  background: black url(icons/meat.png) no-repeat 2px 2px;
  background-size: 24px 24px;
}

.soup {
  background: black url(icons/soup.png) no-repeat 2px 2px;
  background-size: 24px 24px;
}

/* mobile responsive styles*/
@media screen and (max-width: 800px){
  body, body div {
    display: grid;
    max-width: 480px;
  }
  header {
    grid-row: 1;
    margin-top: 10px;
  }
  header h1 {
    font-size: 3.5rem;
    font-weight: bold;
    background: url(icons/bean_can.png) no-repeat 0% center, url(icons/bean_can.png) no-repeat 100% center;
    background-size: 48px 48px;
  }
  aside {
    grid-row: 1;
    border-right: none;
    border-bottom: 5px solid black;
  }
  input{
    width: calc(100% - 5px);
    padding: 5px 0 5px 5px;
  }
  main {
    grid-row: 2;
  }
  footer {
    grid-row: 3;
    margin-bottom: 10px;
  }
  .vegetables, .meat, .soup {
    background: black;
  }
}


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>The Can Store</title>
    <link href="https://fonts.googleapis.com/css?family=Cherry+Swash|Raleway" rel="stylesheet">
    <link href="can-style.css" rel="stylesheet">

The Can Store

        <form>

            Choose a category:
            <select id="category">
              <option selected>All</option>
              <option>Vegetables</option>
              <option>Meat</option>
              <option>Soup</option>
            </select>

            Enter search term:
            <input type="text" id="searchTerm" placeholder="e.g. beans">

            <button>Filter results</button>

        </form>

All icons found at the Noun Project:

Bean can icon by Yazmin Alanis

Vegetable icon by Ricardo Moreira

Soup icon by Arthur Shlain

Meat Chunk icon by Oliviu Stoian.


text:
// use fetch to retrieve the products and pass them to init
// report any errors that occur in the fetch operation
// once the products have been successfully loaded and formatted as a JSON object
// using response.json(), run the initialize() function
fetch('products.json')
  .then( response => {
    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }
    return response.json();
  })
  .then( json => initialize(json) )
  .catch( err => console.error(`Fetch problem: ${err.message}`) );

// sets up the app logic, declares required variables, contains all the other functions
function initialize(products) {
  // grab the UI elements that we need to manipulate
  const category = document.querySelector('#category');
  const searchTerm = document.querySelector('#searchTerm');
  const searchBtn = document.querySelector('button');
  const main = document.querySelector('main');

  // keep a record of what the last category and search term entered were
  let lastCategory = category.value;
  // no search has been made yet
  let lastSearch = '';

  // these contain the results of filtering by category, and search term
  // finalGroup will contain the products that need to be displayed after
  // the searching has been done. Each will be an array containing objects.
  // Each object will represent a product
  let categoryGroup;
  let finalGroup;

  // To start with, set finalGroup to equal the entire products database
  // then run updateDisplay(), so ALL products are displayed initially.
  finalGroup = products;
  updateDisplay();

  // Set both to equal an empty array, in time for searches to be run
  categoryGroup = [];
  finalGroup = [];

  // when the search button is clicked, invoke selectCategory() to start
  // a search running to select the category of products we want to display
  searchBtn.addEventListener('click', selectCategory);

  function selectCategory(e) {
    // Use preventDefault() to stop the form submitting — that would ruin
    // the experience
    e.preventDefault();

    // Set these back to empty arrays, to clear out the previous search
    categoryGroup = [];
    finalGroup = [];

    // if the category and search term are the same as they were the last time a
    // search was run, the results will be the same, so there is no point running
    // it again — just return out of the function
    if (category.value === lastCategory && searchTerm.value.trim() === lastSearch) {
      return;
    } else {
      // update the record of last category and search term
      lastCategory = category.value;
      lastSearch = searchTerm.value.trim();
      // In this case we want to select all products, then filter them by the search
      // term, so we just set categoryGroup to the entire JSON object, then run selectProducts()
      if (category.value === 'All') {
        categoryGroup = products;
        selectProducts();
      // If a specific category is chosen, we need to filter out the products not in that
      // category, then put the remaining products inside categoryGroup, before running
      // selectProducts()
      } else {
        // the values in the <option> elements are uppercase, whereas the categories
        // store in the JSON (under "type") are lowercase. We therefore need to convert
        // to lower case before we do a comparison
        const lowerCaseType = category.value.toLowerCase();
        // Filter categoryGroup to contain only products whose type includes the category
        categoryGroup = products.filter( product => product.type === lowerCaseType );

        // Run selectProducts() after the filtering has been done
        selectProducts();
      }
    }
  }

  // selectProducts() Takes the group of products selected by selectCategory(), and further
  // filters them by the tiered search term (if one has been entered)
  function selectProducts() {
    // If no search term has been entered, just make the finalGroup array equal to the categoryGroup
    // array — we don't want to filter the products further.
    if (searchTerm.value.trim() === '') {
      finalGroup = categoryGroup;
    } else {
      // Make sure the search term is converted to lower case before comparison. We've kept the
      // product names all lower case to keep things simple
      const lowerCaseSearchTerm = searchTerm.value.trim().toLowerCase();
      // Filter finalGroup to contain only products whose name includes the search term
      finalGroup = categoryGroup.filter( product => product.name.includes(lowerCaseSearchTerm));
    }
    // Once we have the final group, update the display
    updateDisplay();
  }

  // start the process of updating the display with the new set of products
  function updateDisplay() {
    // remove the previous contents of the <main> element
    while (main.firstChild) {
      main.removeChild(main.firstChild);
    }

    // if no products match the search term, display a "No results to display" message
    if (finalGroup.length === 0) {
      const para = document.createElement('p');
      para.textContent = 'No results to display!';
      main.appendChild(para);
    // for each product we want to display, pass its product object to fetchBlob()
    } else {
      for (const product of finalGroup) {
        fetchBlob(product);
      }
    }
  }

  // fetchBlob uses fetch to retrieve the image for that product, and then sends the
  // resulting image display URL and product object on to showProduct() to finally
  // display it
  function fetchBlob(product) {
    // construct the URL path to the image file from the product.image property
    const url = `images/${product.image}`;
    // Use fetch to fetch the image, and convert the resulting response to a blob
    // Again, if any errors occur we report them in the console.
    fetch(url)
      .then( response => {
        if (!response.ok) {
          throw new Error(`HTTP error: ${response.status}`);
        }
        return response.blob();
      })
      .then( blob => showProduct(blob, product) )
      .catch( err => console.error(`Fetch problem: ${err.message}`) );
  }

  // Display a product inside the <main> element
  function showProduct(blob, product) {
    // Convert the blob to an object URL — this is basically an temporary internal URL
    // that points to an object stored inside the browser
    const objectURL = URL.createObjectURL(blob);
    // create <section>, <h2>, <p>, and <img> elements
    const section = document.createElement('section');
    const heading = document.createElement('h2');
    const para = document.createElement('p');
    const image = document.createElement('img');

    // give the <section> a classname equal to the product "type" property so it will display the correct icon
    section.setAttribute('class', product.type);

    // Give the <h2> textContent equal to the product "name" property, but with the first character
    // replaced with the uppercase version of the first character
    heading.textContent = product.name.replace(product.name.charAt(0), product.name.charAt(0).toUpperCase());

    // Give the <p> textContent equal to the product "price" property, with a $ sign in front
    // toFixed(2) is used to fix the price at 2 decimal places, so for example 1.40 is displayed
    // as 1.40, not 1.4.
    para.textContent = `$${product.price.toFixed(2)}`;

    // Set the src of the <img> element to the ObjectURL, and the alt to the product "name" property
    image.src = objectURL;
    image.alt = product.name;

    // append the elements to the DOM as appropriate, to add the product to the UI
    main.appendChild(section);
    section.appendChild(heading);
    section.appendChild(para);
    section.appendChild(image);
  }
}


text:
/* General */

html {
  font-family: 'Raleway', sans-serif;
  background-color: yellow;
  font-size: 10px;
}

body {
  width: 800px;
  margin: 0 auto;
}

/* Typography */

h1, h2 {
  font-family: 'Cherry Swash', cursive;
  margin: 0;
  text-align: center;
}

h1 {
  font-size: 5rem;
  background: url(icons/bean_can.png) no-repeat 20% center, url(icons/bean_can.png) no-repeat 80% center;
  background-size: 48px 48px;
}

h2 {
  font-size: 2rem;
  color: yellow;
  text-align: right;
  width: 160px;
  position: absolute;
  right: 4%;
  z-index: 3000;
  text-shadow: 2px 2px 1px black;
  line-height: 2rem;
  padding-top: 0.3rem;
}

p, label, input, select, button, li {
  font-size: 1.5rem;
}

li {
  line-height: 1.5;
}

/* block styling */

header, aside, main, footer {
  padding: 20px;
}

header {
  border: 5px solid black;
  border-bottom: 0;
}

aside {
  border-right: 5px solid black;
}

body > div {
  border: 5px solid black;
  display: flex;
}

footer {
  border: 5px solid black;
  border-top: 0;
}

aside {
  flex: 3;
}

main {
  flex: 7;
}

/* Form styling */

aside {
  display: flex;
  flex-flow: column;
  align-items: stretch;
}

aside div {
  margin-bottom: 20px;
}

label, select, input {
  display: block;
}

label {
  margin-bottom: 5px;
}

select, input, button {
  width: 100%;
}

input {
  background: black;
  box-shadow: inset 1px 1px 1px white;
  color: yellow;
  border: none;
  padding: 5px;
  width: 201.5px;
}

button {
  background: black;
  color: yellow;
  padding: 5px 0;
  border: none;
  border-radius: 10px;
  box-shadow: inset 1px 1px 2px white, inset -1px -1px 2px #ccc;
}

/* Styling the products */

main {
  display: flex;
  flex-flow: row wrap;
  justify-content: space-around;
}

section {
  width: 45%;
  margin: 5px;
  background-color: black;
  position: relative;
  height: 215px;
}

section img {
  width: 100%;
  display: block;
  position: absolute;
  bottom: 0px;
}

section p {
  color: yellow;
  background: black;
  width: 50px;
  height: 50px;
  line-height: 50px;
  text-align: center;
  border-radius: 50%;
  box-shadow: 2px 2px 1px yellow;
  position: absolute;
  right: 10px;
  bottom: -6px;
  z-index: 2000;
}

.vegetables {
  background: black url(icons/vegetable.png) no-repeat 2px 2px;
  background-size: 24px 24px;
}

.meat {
  background: black url(icons/meat.png) no-repeat 2px 2px;
  background-size: 24px 24px;
}

.soup {
  background: black url(icons/soup.png) no-repeat 2px 2px;
  background-size: 24px 24px;
}


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>The Can Store</title>
    <link href="https://fonts.googleapis.com/css?family=Cherry+Swash|Raleway" rel="stylesheet">
    <link href="can-style.css" rel="stylesheet">

The Can Store

        <form>

            Choose a category:
            <select id="category">
              <option selected>All</option>
              <option>Vegetables</option>
              <option>Meat</option>
              <option>Soup</option>
            </select>

            Enter search term:
            <input type="text" id="searchTerm" placeholder="e.g. beans">

            <button>Filter results</button>

        </form>

All icons found at the Noun Project:

Bean can icon by Yazmin Alanis

Vegetable icon by Ricardo Moreira

Soup icon by Arthur Shlain

Meat Chunk icon by Oliviu Stoian.


text:
// use fetch to retrieve the products and pass them to init
// report any errors that occur in the fetch operation
// once the products have been successfully loaded and formatted as a JSON object
// using response.json(), run the initialize() function
const request = new XMLHttpRequest();
request.open('GET', 'products.json');
request.responseType = 'json';

request.onload = function() {
  if(request.status === 200) {
    let products = request.response;
    initialize(products);
  } else {
    console.log('Network request for products.json failed with response ' + request.status + ': ' + request.statusText)
  }
};

request.send();

// sets up the app logic, declares required variables, contains all the other functions
function initialize(products) {
  // grab the UI elements that we need to manipulate
  const category = document.querySelector('#category');
  const searchTerm = document.querySelector('#searchTerm');
  const searchBtn = document.querySelector('button');
  const main = document.querySelector('main');

  // keep a record of what the last category and search term entered were
  let lastCategory = category.value;
  // no search has been made yet
  let lastSearch = '';

  // these contain the results of filtering by category, and search term
  // finalGroup will contain the products that need to be displayed after
  // the searching has been done. Each will be an array containing objects.
  // Each object will represent a product
  let categoryGroup;
  let finalGroup;

  // To start with, set finalGroup to equal the entire products database
  // then run updateDisplay(), so ALL products are displayed initially.
  finalGroup = products;
  updateDisplay();

  // Set both to equal an empty array, in time for searches to be run
  categoryGroup = [];
  finalGroup = [];

  // when the search button is clicked, invoke selectCategory() to start
  // a search running to select the category of products we want to display
  searchBtn.onclick = selectCategory;

  function selectCategory(e) {
    // Use preventDefault() to stop the form submitting — that would ruin
    // the experience
    e.preventDefault();

    // Set these back to empty arrays, to clear out the previous search
    categoryGroup = [];
    finalGroup = [];

    // if the category and search term are the same as they were the last time a
    // search was run, the results will be the same, so there is no point running
    // it again — just return out of the function
    if(category.value === lastCategory && searchTerm.value.trim() === lastSearch) {
      return;
    } else {
      // update the record of last category and search term
      lastCategory = category.value;
      lastSearch = searchTerm.value.trim();
      // In this case we want to select all products, then filter them by the search
      // term, so we just set categoryGroup to the entire JSON object, then run selectProducts()
      if(category.value === 'All') {
        categoryGroup = products;
        selectProducts();
      // If a specific category is chosen, we need to filter out the products not in that
      // category, then put the remaining products inside categoryGroup, before running
      // selectProducts()
      } else {
        // the values in the <option> elements are uppercase, whereas the categories
        // store in the JSON (under "type") are lowercase. We therefore need to convert
        // to lower case before we do a comparison
        let lowerCaseType = category.value.toLowerCase();
        for(let i = 0; i < products.length ; i++) {
          // If a product's type property is the same as the chosen category, we want to
          // display it, so we push it onto the categoryGroup array
          if(products[i].type === lowerCaseType) {
            categoryGroup.push(products[i]);
          }
        }

        // Run selectProducts() after the filtering has been done
        selectProducts();
      }
    }
  }

  // selectProducts() Takes the group of products selected by selectCategory(), and further
  // filters them by the tiered search term (if one has been entered)
  function selectProducts() {
    // If no search term has been entered, just make the finalGroup array equal to the categoryGroup
    // array — we don't want to filter the products further — then run updateDisplay().
    if(searchTerm.value.trim() === '') {
      finalGroup = categoryGroup;
      updateDisplay();
    } else {
      // Make sure the search term is converted to lower case before comparison. We've kept the
      // product names all lower case to keep things simple
      let lowerCaseSearchTerm = searchTerm.value.trim().toLowerCase();
      // For each product in categoryGroup, see if the search term is contained inside the product name
      // (if the indexOf() result doesn't return -1, it means it is) — if it is, then push the product
      // onto the finalGroup array
      for(let i = 0; i < categoryGroup.length ; i++) {
        if(categoryGroup[i].name.indexOf(lowerCaseSearchTerm) !== -1) {
          finalGroup.push(categoryGroup[i]);
        }
      }

      // run updateDisplay() after this second round of filtering has been done
      updateDisplay();
    }

  }

  // start the process of updating the display with the new set of products
  function updateDisplay() {
    // remove the previous contents of the <main> element
    while (main.firstChild) {
      main.removeChild(main.firstChild);
    }

    // if no products match the search term, display a "No results to display" message
    if(finalGroup.length === 0) {
      const para = document.createElement('p');
      para.textContent = 'No results to display!';
      main.appendChild(para);
    // for each product we want to display, pass its product object to fetchBlob()
    } else {
      for(var i = 0; i < finalGroup.length; i++) {
        fetchBlob(finalGroup[i]);
      }
    }
  }

  // fetchBlob uses XHR to retrieve the image for that product, and then sends the
  // resulting image display URL and product object on to showProduct() to finally
  // display it
  function fetchBlob(product) {
    // construct the URL path to the image file from the product.image property
    let url = 'images/' + product.image;
    // Use XHR to fetch the image, as a blob
    // Again, if any errors occur we report them in the console.
    const request = new XMLHttpRequest();
    request.open('GET', url);
    request.responseType = 'blob';

    request.onload = function() {
      if(request.status === 200) {
          // Convert the blob to an object URL — this is basically an temporary internal URL
          // that points to an object stored inside the browser
          let blob = request.response;
          let objectURL = URL.createObjectURL(blob);
          // invoke showProduct
          showProduct(objectURL, product);
      } else {
        console.log('Network request for "' + product.name + '" image failed with response ' + request.status + ': ' + request.statusText);
      }
    };

    request.send();
  }

  // Display a product inside the <main> element
  function showProduct(objectURL, product) {
    // create <section>, <h2>, <p>, and <img> elements
    const section = document.createElement('section');
    const heading = document.createElement('h2');
    const para = document.createElement('p');
    const image = document.createElement('img');

    // give the <section> a classname equal to the product "type" property so it will display the correct icon
    section.setAttribute('class', product.type);

    // Give the <h2> textContent equal to the product "name" property, but with the first character
    // replaced with the uppercase version of the first character
    heading.textContent = product.name.replace(product.name.charAt(0), product.name.charAt(0).toUpperCase());

    // Give the <p> textContent equal to the product "price" property, with a $ sign in front
    // toFixed(2) is used to fix the price at 2 decimal places, so for example 1.40 is displayed
    // as 1.40, not 1.4.
    para.textContent = '$' + product.price.toFixed(2);

    // Set the src of the <img> element to the ObjectURL, and the alt to the product "name" property
    image.src = objectURL;
    image.alt = product.name;

    // append the elements to the DOM as appropriate, to add the product to the UI
    main.appendChild(section);
    section.appendChild(heading);
    section.appendChild(para);
    section.appendChild(image);
  }
}


text:
<!DOCTYPE HTML>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  	<title>Video player example</title>
  	<link rel="stylesheet" type="text/css" href="style.css">

      <video controls>
        <source src="video/sintel-short.mp4" type="video/mp4">
        <source src="video/sintel-short.webm" type="video/webm">

      </video>

        <button class="play" data-icon="P" aria-label="play pause toggle"></button>
        <button class="stop" data-icon="S" aria-label="stop"></button>

00:00

        <button class="rwd" data-icon="B" aria-label="rewind"></button>
        <button class="fwd" data-icon="F" aria-label="fast forward"></button>

Sintel © copyright Blender Foundation | www.sintel.org.


text:
body {
  margin: 0;
}

@font-face {
    font-family: 'HeydingsControlsRegular';
    src: url('fonts/heydings_controls-webfont.eot');
    src: url('fonts/heydings_controls-webfont.eot?#iefix') format('embedded-opentype'),
         url('fonts/heydings_controls-webfont.woff') format('woff'),
         url('fonts/heydings_controls-webfont.ttf') format('truetype');
    font-weight: normal;
    font-style: normal;
}

video {
  max-width: 100%;
}

p {
  position: absolute;
  top: 310px;
}

.player {
  position: absolute;
}

.controls {
  visibility: hidden;
  opacity: 0.5;
  width: 400px;
  border-radius: 10px;
  position: absolute;
  bottom: 20px;
  left: 50%;
  margin-left: -200px;
  background-color: black;
  box-shadow: 3px 3px 5px black;
  transition: 1s all;
  display: flex;
}

button, .controls {
  background: linear-gradient(to bottom,#222,#666);
}

.player:hover .controls, player:focus .controls {
  opacity: 1;
}

button:before {
  font-family: HeydingsControlsRegular;
  font-size: 20px;
  position: relative;
  content: attr(data-icon);
  color: #aaa;
  text-shadow: 1px 1px 0px black;
}

.play:before {
  font-size: 22px;
}

button, .timer {
  height: 38px;
  line-height: 19px;
  box-shadow: inset 0 -5px 25px rgba(0,0,0,0.3);
  border-right: 1px solid #333;
}

button {
  position: relative;
  border: 0;
  flex: 1;
  outline: none;
}

.play {
  border-radius: 10px 0 0 10px;
}

.fwd {
  border-radius: 0 10px 10px 0;
}

.timer {
  line-height: 38px;
  font-size: 10px;
  font-family: monospace;
  text-shadow: 1px 1px 0px black;
  color: white;
  flex: 5;
  position: relative;
}

.timer div {
  position: absolute;
  background-color: rgba(255,255,255,0.2);
  left: 0;
  top: 0;
  width: 0;
  height: 38px;
  z-index: 2;
}

.timer span {
  position: absolute;
  z-index: 3;
  left: 19px;
}

button:hover,button:focus {
  box-shadow: inset 1px 1px 2px black;
}

button:active {
  box-shadow: inset 3px 3px 2px black;
}

.active:before {
  color: red;
}


text:
const media = document.querySelector('video');
const controls = document.querySelector('.controls');

const play = document.querySelector('.play');
const stop = document.querySelector('.stop');
const rwd = document.querySelector('.rwd');
const fwd = document.querySelector('.fwd');

const timerWrapper = document.querySelector('.timer');
const timer = document.querySelector('.timer span');
const timerBar = document.querySelector('.timer div');

media.removeAttribute('controls');
controls.style.visibility = 'visible';

play.addEventListener('click', playPauseMedia);
stop.addEventListener('click', stopMedia);
media.addEventListener('ended', stopMedia);
rwd.addEventListener('click', mediaBackward);
fwd.addEventListener('click', mediaForward);
media.addEventListener('timeupdate', setTime);

function playPauseMedia() {
  rwd.classList.remove('active');
  fwd.classList.remove('active');
  clearInterval(intervalRwd);
  clearInterval(intervalFwd);
  if(media.paused) {
    play.setAttribute('data-icon','u');
    media.play();
  } else {
    play.setAttribute('data-icon','P');
    media.pause();
  }
}

function stopMedia() {
  media.pause();
  media.currentTime = 0;
  rwd.classList.remove('active');
  fwd.classList.remove('active');
  clearInterval(intervalRwd);
  clearInterval(intervalFwd);
  play.setAttribute('data-icon','P');
}

let intervalFwd;
let intervalRwd;

function mediaBackward() {
  clearInterval(intervalFwd);
  fwd.classList.remove('active');

  if(rwd.classList.contains('active')) {
    rwd.classList.remove('active');
    clearInterval(intervalRwd);
    media.play();
  } else {
    rwd.classList.add('active');
    media.pause();
    intervalRwd = setInterval(windBackward, 200);
  }
}

function mediaForward() {
  clearInterval(intervalRwd);
  rwd.classList.remove('active');

  if(fwd.classList.contains('active')) {
    fwd.classList.remove('active');
    clearInterval(intervalFwd);
    media.play();
  } else {
    fwd.classList.add('active');
    media.pause();
    intervalFwd = setInterval(windForward, 200);
  }
}

function windBackward() {
  if(media.currentTime <= 3) {
    stopMedia();
  } else {
    media.currentTime -= 3;
  }
}

function windForward() {
  if(media.currentTime >= media.duration - 3) {
    stopMedia();
  } else {
    media.currentTime += 3;
  }
}

function setTime() {
  const minutes = Math.floor(media.currentTime / 60);
  const seconds = Math.floor(media.currentTime - minutes * 60);

  const minuteValue = minutes.toString().padStart(2, '0');
  const secondValue = seconds.toString().padStart(2, '0');

  const mediaTime = `${minuteValue}:${secondValue}`;
  timer.textContent = mediaTime;

  const barLength = timerWrapper.clientWidth * (media.currentTime/media.duration);
  timerBar.style.width = `${barLength}px`;
}


text:
<!DOCTYPE HTML>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  	<title>Video player example</title>
  	<link rel="stylesheet" type="text/css" href="style.css">

        <video controls>
          <source src="video/sintel-short.mp4" type="video/mp4">
          <source src="video/sintel-short.webm" type="video/webm">

        </video>

          <button class="play" data-icon="P" aria-label="play pause toggle"></button>
          <button class="stop" data-icon="S" aria-label="stop"></button>

00:00

          <button class="rwd" data-icon="B" aria-label="rewind"></button>
          <button class="fwd" data-icon="F" aria-label="fast forward"></button>

Sintel © copyright Blender Foundation | www.sintel.org.


text:
body {
  margin: 0;
}

@font-face {
    font-family: 'HeydingsControlsRegular';
    src: url('fonts/heydings_controls-webfont.eot');
    src: url('fonts/heydings_controls-webfont.eot?#iefix') format('embedded-opentype'),
         url('fonts/heydings_controls-webfont.woff') format('woff'),
         url('fonts/heydings_controls-webfont.ttf') format('truetype');
    font-weight: normal;
    font-style: normal;
}

video {
  max-width: 100%;
}

p {
  position: absolute;
  top: 310px;
}

.player {
  position: absolute;
}

.controls {
  visibility: hidden;
  opacity: 0.5;
  width: 400px;
  border-radius: 10px;
  position: absolute;
  bottom: 20px;
  left: 50%;
  margin-left: -200px;
  background-color: black;
  box-shadow: 3px 3px 5px black;
  transition: 1s all;
  display: flex;
}

button, .controls {
  background: linear-gradient(to bottom,#222,#666);
}

.player:hover .controls, .player:focus .controls {
  opacity: 1;
}

button:before {
  font-family: HeydingsControlsRegular;
  font-size: 20px;
  position: relative;
  content: attr(data-icon);
  color: #aaa;
  text-shadow: 1px 1px 0px black;
}

.play:before {
  font-size: 22px;
}

button, .timer {
  height: 38px;
  line-height: 19px;
  box-shadow: inset 0 -5px 25px rgba(0,0,0,0.3);
  border-right: 1px solid #333;
}

button {
  position: relative;
  border: 0;
  flex: 1;
  outline: none;
}

.play {
  border-radius: 10px 0 0 10px;
}

.fwd {
  border-radius: 0 10px 10px 0;
}

.timer {
  line-height: 38px;
  font-size: 10px;
  font-family: monospace;
  text-shadow: 1px 1px 0px black;
  color: white;
  flex: 5;
  position: relative;
}

.timer div {
  position: absolute;
  background-color: rgba(255,255,255,0.2);
  left: 0;
  top: 0;
  width: 0;
  height: 38px;
  z-index: 2;
}

.timer span {
  position: absolute;
  z-index: 3;
  left: 19px;
}

button:hover,button:focus {
  box-shadow: inset 1px 1px 2px black;
}

button:active {
  box-shadow: inset 3px 3px 2px black;
}

.active:before {
  color: red;
}


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>IndexedDB video store offline</title>
    <link href="style.css" rel="stylesheet">

IndexedDB video store with service worker

Copyright nobody. Use the code as you like.


text:
// Create constants
const section = document.querySelector('section');
const videos = [
  { 'name' : 'crystal' },
  { 'name' : 'elf' },
  { 'name' : 'frog' },
  { 'name' : 'monster' },
  { 'name' : 'pig' },
  { 'name' : 'rabbit' }
];
// Create an instance of a db object for us to store our database in
let db;

function init() {
  // Loop through the video names one by one
  for(const video of videos) {
    // Open transaction, get object store, and get() each video by name
    const objectStore = db.transaction('videos_os').objectStore('videos_os');
    const request = objectStore.get(video.name);
    request.addEventListener('success', () => {
      // If the result exists in the database (is not undefined)
      if(request.result) {
        // Grab the videos from IDB and display them using displayVideo()
        console.log('taking videos from IDB');
        displayVideo(request.result.mp4, request.result.webm, request.result.name);
      } else {
        // Fetch the videos from the network
        fetchVideoFromNetwork(video);
      }
    });
  }
}

// Define the fetchVideoFromNetwork() function
function fetchVideoFromNetwork(video) {
  console.log('fetching videos from network');
  // Fetch the MP4 and WebM versions of the video using the fetch() function,
  // then expose their response bodies as blobs
  const mp4Blob = fetch(`videos/${video.name}.mp4`).then(response => response.blob());
  const webmBlob = fetch(`videos/${video.name}.webm`).then(response => response.blob());

  // Only run the next code when both promises have fulfilled
  Promise.all([mp4Blob, webmBlob]).then(values => {
    // display the video fetched from the network with displayVideo()
    displayVideo(values[0], values[1], video.name);
    // store it in the IDB using storeVideo()
    storeVideo(values[0], values[1], video.name);
  });
}

// Define the storeVideo() function
function storeVideo(mp4Blob, webmBlob, name) {
  // Open transaction, get object store; make it a readwrite so we can write to the IDB
  const objectStore = db.transaction(['videos_os'], 'readwrite').objectStore('videos_os');
  // Create a record to add to the IDB
  const record = {
    mp4 : mp4Blob,
    webm : webmBlob,
    name : name
  }

  // Add the record to the IDB using add()
  const request = objectStore.add(record);

  request.addEventListener('success', () => console.log('Record addition attempt finished'));
  request.addEventListener('error', () => console.error(request.error));
}

// Define the displayVideo() function
function displayVideo(mp4Blob, webmBlob, title) {
  // Create object URLs out of the blobs
  const mp4URL = URL.createObjectURL(mp4Blob);
  const webmURL = URL.createObjectURL(webmBlob);

  // Create DOM elements to embed video in the page
  const article = document.createElement('article');
  const h2 = document.createElement('h2');
  h2.textContent = title;
  const video = document.createElement('video');
  video.controls = true;
  const source1 = document.createElement('source');
  source1.src = mp4URL;
  source1.type = 'video/mp4';
  const source2 = document.createElement('source');
  source2.src = webmURL;
  source2.type = 'video/webm';

  // Embed DOM elements into page
  section.appendChild(article);
  article.appendChild(h2);
  article.appendChild(video);
  video.appendChild(source1);
  video.appendChild(source2);
}

// Open our database; it is created if it doesn't already exist
// (see upgradeneeded below)
const request = window.indexedDB.open('videos_db', 1);

// error handler signifies that the database didn't open successfully
request.addEventListener('error', () => console.error('Database failed to open'));

// success handler signifies that the database opened successfully
request.addEventListener('success', () => {
  console.log('Database opened succesfully');

  // Store the opened database object in the db variable. This is used a lot below
  db = request.result;
  init();
});

// Setup the database tables if this has not already been done
request.addEventListener('upgradeneeded', e => {

  // Grab a reference to the opened database
  const db = e.target.result;

  // Create an objectStore to store our videos in (basically like a single table)
  // including a auto-incrementing key
  const objectStore = db.createObjectStore('videos_os', { keyPath: 'name' });

  // Define what data items the objectStore will contain
  objectStore.createIndex('mp4', 'mp4', { unique: false });
  objectStore.createIndex('webm', 'webm', { unique: false });

  console.log('Database setup complete');
});

// Register service worker to control making site work offline
if('serviceWorker' in navigator) {
  navigator.serviceWorker
    .register('/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/sw.js')
    .then(() => console.log('Service Worker Registered'));
}


text:
html {
  font-family: sans-serif;
}

body {
  margin: 0 auto;
  max-width: 800px;
}

header, footer {
  background-color: green;
  color: white;
  line-height: 100px;
  padding: 0 20px;
}

article {
  padding: 10px;
  margin: 10px 0;
  border: 1px solid black;
  background-color: #eee;
}

h2 {
  margin: 10px 0;
}

video {
  margin: 0 auto;
  border: 1px solid black;
  display: block;
}

button {
  display: block;
  margin-top: 10px;
}


text:
self.addEventListener('install', e => {
 e.waitUntil(
   caches.open('video-store').then(function(cache) {
     return cache.addAll([
       '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/',
       '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/index.html',
       '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/index.js',
       '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/style.css'
     ]);
   })
 );
});

self.addEventListener('fetch', e => {
  console.log(e.request.url);
  e.respondWith(
    caches.match(e.request).then(response => response || fetch(e.request))
  );
});


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>IndexedDB video store</title>
    <link href="style.css" rel="stylesheet">

IndexedDB video store

Copyright nobody. Use the code as you like.


text:
// Create constants
const section = document.querySelector('section');
const videos = [
  { 'name' : 'crystal' },
  { 'name' : 'elf' },
  { 'name' : 'frog' },
  { 'name' : 'monster' },
  { 'name' : 'pig' },
  { 'name' : 'rabbit' }
];
// Create an instance of a db object for us to store our database in
let db;

function init() {
  // Loop through the video names one by one
  for(const video of videos) {
    // Open transaction, get object store, and get() each video by name
    const objectStore = db.transaction('videos_os').objectStore('videos_os');
    const request = objectStore.get(video.name);
    request.addEventListener('success', () => {
      // If the result exists in the database (is not undefined)
      if(request.result) {
        // Grab the videos from IDB and display them using displayVideo()
        console.log('taking videos from IDB');
        displayVideo(request.result.mp4, request.result.webm, request.result.name);
      } else {
        // Fetch the videos from the network
        fetchVideoFromNetwork(video);
      }
    });
  }
}

// Define the fetchVideoFromNetwork() function
function fetchVideoFromNetwork(video) {
  console.log('fetching videos from network');
  // Fetch the MP4 and WebM versions of the video using the fetch() function,
  // then expose their response bodies as blobs
  const mp4Blob = fetch(`videos/${video.name}.mp4`).then(response => response.blob());
  const webmBlob = fetch(`videos/${video.name}.webm`).then(response => response.blob());

  // Only run the next code when both promises have fulfilled
  Promise.all([mp4Blob, webmBlob]).then(values => {
    // display the video fetched from the network with displayVideo()
    displayVideo(values[0], values[1], video.name);
    // store it in the IDB using storeVideo()
    storeVideo(values[0], values[1], video.name);
  });
}

// Define the storeVideo() function
function storeVideo(mp4Blob, webmBlob, name) {
  // Open transaction, get object store; make it a readwrite so we can write to the IDB
  const objectStore = db.transaction(['videos_os'], 'readwrite').objectStore('videos_os');
  // Create a record to add to the IDB
  const record = {
    mp4 : mp4Blob,
    webm : webmBlob,
    name : name
  }

  // Add the record to the IDB using add()
  const request = objectStore.add(record);

  request.addEventListener('success', () => console.log('Record addition attempt finished'));
  request.addEventListener('error', () => console.error(request.error));
}

// Define the displayVideo() function
function displayVideo(mp4Blob, webmBlob, title) {
  // Create object URLs out of the blobs
  const mp4URL = URL.createObjectURL(mp4Blob);
  const webmURL = URL.createObjectURL(webmBlob);

  // Create DOM elements to embed video in the page
  const article = document.createElement('article');
  const h2 = document.createElement('h2');
  h2.textContent = title;
  const video = document.createElement('video');
  video.controls = true;
  const source1 = document.createElement('source');
  source1.src = mp4URL;
  source1.type = 'video/mp4';
  const source2 = document.createElement('source');
  source2.src = webmURL;
  source2.type = 'video/webm';

  // Embed DOM elements into page
  section.appendChild(article);
  article.appendChild(h2);
  article.appendChild(video);
  video.appendChild(source1);
  video.appendChild(source2);
}

// Open our database; it is created if it doesn't already exist
// (see upgradeneeded below)
const request = window.indexedDB.open('videos_db', 1);

// error handler signifies that the database didn't open successfully
request.addEventListener('error', () => console.error('Database failed to open'));

// success handler signifies that the database opened successfully
request.addEventListener('success', () => {
  console.log('Database opened succesfully');

  // Store the opened database object in the db variable. This is used a lot below
  db = request.result;
  init();
});

// Setup the database tables if this has not already been done
request.addEventListener('upgradeneeded', e => {

  // Grab a reference to the opened database
  const db = e.target.result;

  // Create an objectStore to store our videos in (basically like a single table)
  // including a auto-incrementing key
  const objectStore = db.createObjectStore('videos_os', { keyPath: 'name' });

  // Define what data items the objectStore will contain
  objectStore.createIndex('mp4', 'mp4', { unique: false });
  objectStore.createIndex('webm', 'webm', { unique: false });

  console.log('Database setup complete');
});


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>IndexedDB demo</title>
    <link href="style.css" rel="stylesheet">

IndexedDB notes demo

Notes

Enter a new note

        <form>

            Note title
            <input id="title" type="text" required>

            Note text
            <input id="body" type="text" required>

            <button>Create new note</button>

        </form>

Copyright nobody. Use the code as you like.


text:
// Create needed constants
const list = document.querySelector('ul');
const titleInput = document.querySelector('#title');
const bodyInput = document.querySelector('#body');
const form = document.querySelector('form');
const submitBtn = document.querySelector('form button');

// Create an instance of a db object for us to store the open database in
let db;

// Open our database; it is created if it doesn't already exist
// (see the upgradeneeded handler below)
const openRequest = window.indexedDB.open('notes_db', 1);

// error handler signifies that the database didn't open successfully
openRequest.addEventListener('error', () => console.error('Database failed to open'));

// success handler signifies that the database opened successfully
openRequest.addEventListener('success', () => {
  console.log('Database opened succesfully');

  // Store the opened database object in the db variable. This is used a lot below
  db = openRequest.result;

  // Run the displayData() function to display the notes already in the IDB
  displayData();
});

// Set up the database tables if this has not already been done
openRequest.addEventListener('upgradeneeded', e => {

  // Grab a reference to the opened database
  db = e.target.result;

  // Create an objectStore to store our notes in (basically like a single table)
  // including a auto-incrementing key
  const objectStore = db.createObjectStore('notes_os', { keyPath: 'id', autoIncrement:true });

  // Define what data items the objectStore will contain
  objectStore.createIndex('title', 'title', { unique: false });
  objectStore.createIndex('body', 'body', { unique: false });

  console.log('Database setup complete');
});

// Create an submit handler so that when the form is submitted the addData() function is run
form.addEventListener('submit', addData);

// Define the addData() function
function addData(e) {
  // prevent default - we don't want the form to submit in the conventional way
  e.preventDefault();

  // grab the values entered into the form fields and store them in an object ready for being inserted into the DB
  const newItem = { title: titleInput.value, body: bodyInput.value };

  // open a read/write db transaction, ready for adding the data
  const transaction = db.transaction(['notes_os'], 'readwrite');

  // call an object store that's already been added to the database
  const objectStore = transaction.objectStore('notes_os');

  // Make a request to add our newItem object to the object store
  const addRequest = objectStore.add(newItem);

  addRequest.addEventListener('success', () => {
    // Clear the form, ready for adding the next entry
    titleInput.value = '';
    bodyInput.value = '';
  });

  // Report on the success of the transaction completing, when everything is done
  transaction.addEventListener('complete', () => {
    console.log('Transaction completed: database modification finished.');

    // update the display of data to show the newly added item, by running displayData() again.
    displayData();
  });

  transaction.addEventListener('error', () => console.log('Transaction not opened due to error'));
}

// Define the displayData() function
function displayData() {
  // Here we empty the contents of the list element each time the display is updated
  // If you ddn't do this, you'd get duplicates listed each time a new note is added
  while (list.firstChild) {
    list.removeChild(list.firstChild);
  }

  // Open our object store and then get a cursor - which iterates through all the
  // different data items in the store
  const objectStore = db.transaction('notes_os').objectStore('notes_os');
  objectStore.openCursor().addEventListener('success', e => {
    // Get a reference to the cursor
    const cursor = e.target.result;

    // If there is still another data item to iterate through, keep running this code
    if(cursor) {
      // Create a list item, h3, and p to put each data item inside when displaying it
      // structure the HTML fragment, and append it inside the list
      const listItem = document.createElement('li');
      const h3 = document.createElement('h3');
      const para = document.createElement('p');

      listItem.appendChild(h3);
      listItem.appendChild(para);
      list.appendChild(listItem);

      // Put the data from the cursor inside the h3 and para
      h3.textContent = cursor.value.title;
      para.textContent = cursor.value.body;

      // Store the ID of the data item inside an attribute on the listItem, so we know
      // which item it corresponds to. This will be useful later when we want to delete items
      listItem.setAttribute('data-note-id', cursor.value.id);

      // Create a button and place it inside each listItem
      const deleteBtn = document.createElement('button');
      listItem.appendChild(deleteBtn);
      deleteBtn.textContent = 'Delete';

      // Set an event handler so that when the button is clicked, the deleteItem()
      // function is run
      deleteBtn.addEventListener('click', deleteItem);

      // Iterate to the next item in the cursor
      cursor.continue();
    } else {
      // Again, if list item is empty, display a 'No notes stored' message
      if(!list.firstChild) {
        const listItem = document.createElement('li');
        listItem.textContent = 'No notes stored.'
        list.appendChild(listItem);
      }
      // if there are no more cursor items to iterate through, say so
      console.log('Notes all displayed');
    }
  });
}

// Define the deleteItem() function
function deleteItem(e) {
  // retrieve the name of the task we want to delete. We need
  // to convert it to a number before trying it use it with IDB; IDB key
  // values are type-sensitive.
  const noteId = Number(e.target.parentNode.getAttribute('data-note-id'));

  // open a database transaction and delete the task, finding it using the id we retrieved above
  const transaction = db.transaction(['notes_os'], 'readwrite');
  const objectStore = transaction.objectStore('notes_os');
  const deleteRequest = objectStore.delete(noteId);

  // report that the data item has been deleted
  transaction.addEventListener('complete', () => {
    // delete the parent of the button
    // which is the list item, so it is no longer displayed
    e.target.parentNode.parentNode.removeChild(e.target.parentNode);
    console.log(`Note ${noteId} deleted.`);

    // Again, if list item is empty, display a 'No notes stored' message
    if(!list.firstChild) {
      const listItem = document.createElement('li');
      listItem.textContent = 'No notes stored.';
      list.appendChild(listItem);
    }
  });
}


text:
html {
  font-family: sans-serif;
}

body {
  margin: 0 auto;
  max-width: 800px;
}

header, footer {
  background-color: green;
  color: white;
  line-height: 100px;
  padding: 0 20px;
}

.new-note, .note-display {
  padding: 20px;
}

.new-note {
  background: #ddd;
}

h1 {
  margin: 0;
}

ul {
  list-style-type: none;
}

div {
  margin-bottom: 10px;
}


text:
// Create needed constants
const list = document.querySelector('ul');
const titleInput = document.querySelector('#title');
const bodyInput = document.querySelector('#body');
const form = document.querySelector('form');
const submitBtn = document.querySelector('form button');


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Personal greeting</title>

Our website

      <form>

          Enter your name:
          <input id="entername" type="text" required>
          <input id="submitname" type="submit" value="Say hello">

          Want me to forget you?
          <input id="forgetname" type="reset" value="Forget">

      </form>

Welcome to our website.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam malesuada, metus ut mollis volutpat, felis est rhoncus turpis, in blandit est risus malesuada dui. Phasellus tempus elit at purus vestibulum suscipit. Donec quis est nec dui pretium venenatis sit amet eu nulla. Donec finibus, ipsum non semper dignissim, massa magna sagittis est, vitae vehicula nunc magna vitae diam. Integer ultrices mauris aliquet arcu tempor, at mattis justo sagittis. Nunc ut nulla et erat viverra tincidunt. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer vitae bibendum justo. Vestibulum porta velit sit amet libero accumsan fermentum.

Ut id mauris urna. In sed porttitor lectus. Suspendisse dignissim dolor id lectus pellentesque, eu bibendum lectus malesuada. Phasellus volutpat sollicitudin suscipit. Donec id libero nisl. Praesent gravida purus vel euismod facilisis. Maecenas sit amet velit non lacus aliquam dictum vitae eu augue. Donec euismod enim elementum elit laoreet sodales. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Integer vulputate, libero sed vulputate eleifend, magna libero malesuada ligula, sit amet tincidunt dui mi vitae mauris. Aliquam aliquam turpis mauris, in sagittis orci rutrum efficitur. Sed vel purus fringilla, pretium sapien sed, accumsan erat. Morbi scelerisque tempor purus in faucibus. Nunc fringilla nulla ut aliquam posuere. Vivamus id lectus eleifend, bibendum urna non, ornare nibh. Fusce bibendum vulputate mollis.

Copyright nobody. Use the code as you like.


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web storage demo</title>

Web storage demo


text:
// create needed constants
const rememberDiv = document.querySelector('.remember');
const forgetDiv = document.querySelector('.forget');
const form = document.querySelector('form');
const nameInput = document.querySelector('#entername');
const submitBtn = document.querySelector('#submitname');
const forgetBtn = document.querySelector('#forgetname');

const h1 = document.querySelector('h1');
const personalGreeting = document.querySelector('.personal-greeting');

// Stop the form from submitting when a button is pressed
form.addEventListener('submit', e => e.preventDefault());

// run function when the 'Say hello' button is clicked
submitBtn.addEventListener('click', () => {
  // store the entered name in web storage
  localStorage.setItem('name', nameInput.value);
  // run nameDisplayCheck() to sort out displaying the personalised greetings and updating the form display
  nameDisplayCheck();
});

// run function when the 'Forget' button is clicked
forgetBtn.addEventListener('click', () => {
  // Remove the stored name from web storage
  localStorage.removeItem('name');
  // run nameDisplayCheck() to sort out displaying the generic greeting again and updating the form display
  nameDisplayCheck();
});

// define the nameDisplayCheck() function
function nameDisplayCheck() {
  // check whether the 'name' data item is stored in web Storage
  if(localStorage.getItem('name')) {
    // If it is, display personalized greeting
    const name = localStorage.getItem('name');
    h1.textContent = `Welcome, ${name}`;
    personalGreeting.textContent = `Welcome to our website, ${name}! We hope you have fun while you are here.`;
    // hide the 'remember' part of the form and show the 'forget' part
    forgetDiv.style.display = 'block';
    rememberDiv.style.display = 'none';
  } else {
    // if not, display generic greeting
    h1.textContent = 'Welcome to our website ';
    personalGreeting.textContent = 'Welcome to our website. We hope you have fun while you are here.';
    // hide the 'forget' part of the form and show the 'remember' part
    forgetDiv.style.display = 'none';
    rememberDiv.style.display = 'block';
  }
}

// run nameDisplayCheck() when the page first loads to check wether a personal name was previously
// set, and if so display the personalized greeting. If not, show the generic greeting
nameDisplayCheck();


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Bouncing balls</title>
    <link rel="stylesheet" href="style.css">

bouncing balls

    <canvas></canvas>


text:
html, body {
  margin: 0;
}

html {
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  height: 100%;
}

body {
  overflow: hidden;
  height: inherit;
}

h1 {
  font-size: 2rem;
  letter-spacing: -1px;
  position: absolute;
  margin: 0;
  top: -4px;
  right: 5px;

  color: transparent;
  text-shadow: 0 0 4px white;
}


text:
// setup canvas

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

const width = canvas.width = window.innerWidth;
const height = canvas.height = window.innerHeight;

// function to generate random number

function random(min,max) {
  return Math.floor(Math.random()*(max-min)) + min;
}

// function to generate rnadom color

function randomRGB() {
  return `rgb(${random(0, 255)},${random(0, 255)},${random(0, 255)})`;
}

// define Ball constructor

function Ball() {
  this.x = random(0,width);
  this.y = random(0,height);
  this.velX = random(-7,7);
  this.velY = random(-7,7);
  this.color = randomRGB();
  this.size = random(10,20);
}

// define ball draw method

Ball.prototype.draw = function() {
  ctx.beginPath();
  ctx.fillStyle = this.color;
  ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
  ctx.fill();
};

// define ball update method

Ball.prototype.update = function() {
  if((this.x + this.size) >= width) {
    this.velX = -(this.velX);
  }

  if((this.x - this.size) <= 0) {
    this.velX = -(this.velX);
  }

  if((this.y + this.size) >= height) {
    this.velY = -(this.velY);
  }

  if((this.y - this.size) <= 0) {
    this.velY = -(this.velY);
  }

  this.x += this.velX;
  this.y += this.velY;
};

// define ball collision detection

Ball.prototype.collisionDetect = function() {
  for (const ball of balls) {
    if ( (!(this.x === ball.x && this.y === ball.y && this.velX === ball.velX && this.velY === ball.velY)) ) {
      const dx = this.x - ball.x;
      const dy = this.y - ball.y;
      const distance = Math.sqrt(dx * dx + dy * dy);

      if (distance < this.size + ball.size) {
        ball.color = this.color =  randomRGB();
      }
    }
  }
};

// define array to store balls

const balls = [];

// define loop that keeps drawing the scene constantly

function loop() {
  ctx.fillStyle = 'rgba(0,0,0,0.25)';
  ctx.fillRect(0,0,width,height);

  while(balls.length < 25) {
    const ball = new Ball();
    balls.push(ball);
  }

  for (const ball of balls) {
    ball.draw();
    ball.update();
    ball.collisionDetect();
  }

  requestAnimationFrame(loop);
}

loop();


text:
<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Basic Web Audio example</title>
    <style>
      input {
        height: 1.5em;
      }
    </style>
  </head>
  <body>
    <audio src="outfoxing.mp3"></audio>

    <button class="paused">Play</button>
    <br />
    <input type="range" min="0" max="1" step="0.01" value="1" class="volume" />

    <script>
      // Create an AudioContext (cross browser)
      const AudioContext = window.AudioContext || window.webkitAudioContext;
      const audioCtx = new AudioContext();

      // store references to our HTML elements
      const audioElement = document.querySelector("audio");
      const playBtn = document.querySelector("button");
      const volumeSlider = document.querySelector(".volume");

      // load the audio source into our audio graph
      const audioSource = audioCtx.createMediaElementSource(audioElement);

      // play/pause audio
      playBtn.addEventListener("click", () => {
        // check if context is in suspended state (autoplay policy)
        if (audioCtx.state === "suspended") {
          audioCtx.resume();
        }

        // if track is stopped, play it
        if (playBtn.getAttribute("class") === "paused") {
          audioElement.play();
          playBtn.setAttribute("class", "playing");
          playBtn.textContent = "Pause";
          // if track is playing, stop it
        } else if (playBtn.getAttribute("class") === "playing") {
          audioElement.pause();
          playBtn.setAttribute("class", "paused");
          playBtn.textContent = "Play";
        }
      });

      // if track ends
      audioElement.addEventListener("ended", () => {
        playBtn.setAttribute("class", "paused");
        playBtn.textContent = "Play";
      });

      // volume
      const gainNode = audioCtx.createGain();

      volumeSlider.addEventListener("input", () => {
        gainNode.gain.value = volumeSlider.value;
      });

      // connect our graph
      audioSource.connect(gainNode).connect(audioCtx.destination);

      // Track credit: Outfoxing the Fox by Kevin MacLeod under Creative Commons
    </script>
  </body>
</html>


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Simple switch example</title>

    Select the weather type today:
    <select id="weather">
      <option value="">--Make a choice--</option>
      <option value="sunny">Sunny</option>
      <option value="rainy">Rainy</option>
      <option value="snowing">Snowing</option>
      <option value="overcast">Overcast</option>
    </select>


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Simple ternary operator example</title>

    Select theme:
    <select id="theme">
      <option value="white">White</option>
      <option value="black">Black</option>
    </select>

This is my website


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>More color choices!</title>

    Select theme:
    <select id="theme">
      <option value="white">White</option>
      <option value="black">Black</option>
      <option value="purple">Purple</option>
      <option value="yellow">Yellow</option>
      <option value="psych">Psychedelic</option>
    </select>

This is my website


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Simple calendar example</title>

    Select month:
    <select id="month">
      <option value="January">January</option>
      <option value="February">February</option>
      <option value="March">March</option>
      <option value="April">April</option>
      <option value="May">May</option>
      <option value="June">June</option>
      <option value="July">July</option>
      <option value="August">August</option>
      <option value="September">September</option>
      <option value="October">October</option>
      <option value="November">November</option>
      <option value="December">December</option>
    </select>


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Simple else if example</title>

    Select the weather type today:
    <select id="weather">
      <option value="">--Make a choice--</option>
      <option value="sunny">Sunny</option>
      <option value="rainy">Rainy</option>
      <option value="snowing">Snowing</option>
      <option value="overcast">Overcast</option>
    </select>


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Allowance updater example</title>

    Has the shopping been done?
    <input type="checkbox" id="shopping-check">


text:
<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Random canvas circles</title>
    <style>
      html {
        width: 100%;
        height: inherit;
      }

      canvas {
        display: block;
      }

      body {
        margin: 0;
      }

      button {
        position: absolute;
        top: 5px;
        left: 5px;
      }
    </style>
  </head>
  <body>

  <button>Update</button>

  <canvas></canvas>

    <script>
    const btn = document.querySelector('button');
    const canvas = document.querySelector('canvas');
    const ctx = canvas.getContext('2d');

    let WIDTH = document.documentElement.clientWidth;
    let HEIGHT = document.documentElement.clientHeight;

    canvas.width = WIDTH;
    canvas.height = HEIGHT;

    function random(number) {
      return Math.floor(Math.random()*number);
    }

    function draw() {
      ctx.clearRect(0,0,WIDTH,HEIGHT);
      for(let i = 0; i < 100; i++) {
        ctx.beginPath();
        ctx.fillStyle = 'rgba(255,0,0,0.5)';
        ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
        ctx.fill();
      }
    }

    btn.addEventListener('click',draw);

    </script>

  </body>
</html>


text:
<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Integer squares generator</title>
    <style>

    </style>
  </head>
  <body>

  <label for="number">Enter number: </label>
  <input id="number" type="text">
  <button>Generate integer squares</button>

  <p>Output: </p>

    <script>
    const para = document.querySelector('p');
    const input = document.querySelector('input');
    const btn = document.querySelector('button');

    btn.addEventListener('click', () => {
      para.textContent = 'Output: ';
      const num = input.value;
      input.value = '';
      input.focus();
      for (let i = 1; i <= num; i++) {
        let sqRoot = Math.sqrt(i);
        if (Math.floor(sqRoot) !== sqRoot) {
          continue;
        }
        para.textContent += `${i} `;
      }
    });

    </script>

  </body>
</html>


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Guest list example</title>

Admit:

Refuse:


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Basic do...while loop example</title>


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Basic for loop example</title>


text:
<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Launch countdown example</title>
    <style>

    </style>
  </head>
  <body>

    <script>

    let i = 10;

    while(i >= 0) {
      const para = document.createElement('p');
      if(i === 10) {
        para.textContent = 'Countdown ' + i;
      } else if(i === 0) {
        para.textContent = 'Blast off!';
      } else {
        para.textContent = i;
      }

      document.body.appendChild(para);

      i--;
    }

    </script>

  </body>
</html>


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Basic while loop example</title>


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Simple contact search example</title>

  Search by contact name:
  <input id="search" type="text">
  <button>Search</button>


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Basic for loop example improved</title>


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Image gallery</title>

    <link rel="stylesheet" href="style.css">

Image gallery example

      <button class="dark">Darken</button>


text:
h1 {
  font-family: helvetica, arial, sans-serif;
  text-align: center;
}

body {
  width: 640px;
  margin: 0 auto;
}

.full-img {
  position: relative;
  display: block;
  width: 640px;
  height: 480px;
}

.overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 640px;
  height: 480px;
  background-color: rgba(0,0,0,0);
}

button {
  border: 0;
  background: rgba(150,150,150,0.6);
  text-shadow: 1px 1px 1px white;
  border: 1px solid #999;
  position: absolute;
  cursor: pointer;
  top: 2px;
  left: 2px;
}

.thumb-bar img {
  display: block;
  width: 20%;
  float: left;
  cursor: pointer;
}


text:
const displayedImage = document.querySelector('.displayed-img');
const thumbBar = document.querySelector('.thumb-bar');

const btn = document.querySelector('button');
const overlay = document.querySelector('.overlay');

/* Declaring the array of image filenames */

const images = ['pic1.jpg', `pic2.jpg`, `pic3.jpg`, `pic4.jpg`, `pic5.jpg`];
const alts = {
  'pic1.jpg' : 'Closeup of a human eye',
  'pic2.jpg' : 'Rock that looks like a wave',
  'pic3.jpg' : 'Purple and white pansies',
  'pic4.jpg' : 'Section of wall from a pharoah\'s tomb',
  'pic5.jpg' : 'Large moth on a leaf'
}

/* Looping through images */

for (const image of images) {
  const newImage = document.createElement('img');
  newImage.setAttribute('src', `images/${image}`);
  newImage.setAttribute('alt', alts[image]);
  thumbBar.appendChild(newImage);
  newImage.addEventListener('click', e => {
    displayedImage.src = e.target.src;
    displayedImage.alt = e.target.alt;
  });
}

/* Wiring up the Darken/Lighten button */

btn.addEventListener('click', () => {
  const btnClass = btn.getAttribute('class');
  if (btnClass === 'dark') {
    btn.setAttribute('class','light');
    btn.textContent = 'Lighten';
    overlay.style.backgroundColor = 'rgba(0,0,0,0.5)';
  } else {
    btn.setAttribute('class','dark');
    btn.textContent = 'Darken';
    overlay.style.backgroundColor = 'rgba(0,0,0,0)';
  }
});


text:
# Marking guide for "Image gallery"
The following guide outlines a marking guide for the MDN Learning Area JavaScript Topic — [Image gallery](https://developer.mozilla.org/en-US/Learn/JavaScript/Building_blocks/Image_gallery). Each subtask detailed in the assessment is listed below, along with an explanation of how many marks the task is worth, and the mark breakdown.

Note: These are guidelines, not set in stone rules — you are of course free to use your judgement on mark awarding when you meet an edge case, or something that isn't clear cut.

The overall mark awarded is out of 22. Work out their final mark, and then divide by 21 and multiply by 100 to give a percentage mark. For reference, you can find a [finished program](main.js) that would be awarded top marks.

## Declaring the array of filenames

1. **Declaring a constant that holds the names of image files**: One mark for this. It should be an array of strings, where each string is the name of one of the image files, such as `'pic1.jpg'`. It should be declared using `const`.

## Looping through the images

1. **Creating the loop**: Four marks for this — the actual solution is fairy simple, but there are a few details to get right (any suitable loop type can be used):
    * The initializer should start at 1, and the loop should iterate until a value of 5. This is useful, as the first image has the number 1 in its filename, then 2, 3, etc.
    * The iterator should add 1 to the initializer after each iteration.

2. **Building the image path for each loop iteration<**: Three marks for this. The student basically just needs to replace the `xxx` placeholder with a template literal that will use the initializer to build the image path in each case. The pattern we need is this: `images/pic${i}.jpg`.

## Adding an click event listener to each thumbnail image

1. **Find the value of the src attribute of the current image.**: Four marks for this. An anonymous function would make most sense:

  ```js
  newImage.addEventListener('click', e => displayedImage.src = e.target.src);
  ```

## Writing a handler that runs the darken/lighten button

1. **Adding an onclick handler**: Two marks; the `<button>` is referenced in the `btn` constant, so the handler will need to look something like `btn.addEventListener('click', () => {`. In this case, invoking a named function would also be ok, e.g. `btn.addEventListener('click', myFunction);`

2. **Checking the current class name set on the `<button>` element**: Two marks; this again uses `getAttribute()`, and you just need to run this on the `btn` constant — `const btnClass = btn.getAttribute('class');`

3. **The conditional statement**: Six marks for this. It is not that complex, but there is a fair bit of work to do here. The conditional statement just needs to look like this:

  ```js
  if (btnClass === 'dark') {
    /* ... */
  } else {
    /* ... */
  }
  ```

  Then the student needs to grab the three lines provided in the assessment text, and modify it to set the things that are needed in each state. So a finished event handler could look something like this:

  ```js
  btn.addEventListener('click', () => {
    const btnClass = btn.getAttribute('class');
    if (btnClass === 'dark') {
      btn.setAttribute('class','light');
      btn.textContent = 'Lighten';
      overlay.style.backgroundColor = 'rgba(0,0,0,0.5)';
    } else {
      btn.setAttribute('class','dark');
      btn.textContent = 'Darken';
      overlay.style.backgroundColor = 'rgba(0,0,0,0)';
    }
  });
  ```


text:
<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Random color event object example</title>
    <style>
      button {
        margin: 10px;
        font-size: 300%;
        padding: 30px;
      };
    </style>
  </head>
  <body>
    <button>Change color</button>
    <script>
      const btn = document.querySelector('button');

      function random(number) {
        return Math.floor(Math.random() * (number+1));
      }

      function bgChange(e) {
        const rndCol = `rgb(${random(255)}, ${random(255)}, ${random(255)})`;
        e.target.style.backgroundColor = rndCol;
        console.log(e);
      }

      btn.addEventListener('click', bgChange);
    </script>
  </body>
</html>


text:
<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Random color example — event handler property</title>
    <style>
      button {
        margin: 10px;
      }
    </style>
  </head>
  <body>
    <button>Change color</button>
    <script>
      const btn = document.querySelector('button');

      function random(number) {
        return Math.floor(Math.random()*number);
      }

      function bgChange() {
        const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
        document.body.style.backgroundColor = rndCol;
      }

      btn.onclick = bgChange;
    </script>
  </body>
</html>


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Prevent default example</title>

    <form>

        First name:
        <input id="fname" type="text">

        Last name:
        <input id="lname" type="text">

        <input id="submit" type="submit">

    </form>


text:
<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Random color example — event handler attribute</title>
    <style>
      button {
        margin: 10px
      };
    </style>
  </head>
  <body>
    <button onclick="bgChange()">Change color</button>
    <script>
      function random(number) {
        return Math.floor(Math.random()*number);
      }

      function bgChange() {
        const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
        document.body.style.backgroundColor = rndCol;
      }
    </script>
  </body>
</html>


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Show video box example</title>

    <button>Display video</button>

      <video>
        <source src="rabbit320.mp4" type="video/mp4">
        <source src="rabbit320.webm" type="video/webm">

Your browser doesn't support HTML5 video. Here is a link to the video instead.

      </video>


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Useful event target example</title>


text:
<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Random color example — event handler attribute</title>
    <style>
      button {
        margin: 10px
      };
    </style>
  </head>
  <body>
    <button>Change color</button>
    <script>
      const btn = document.querySelector('button');

      function random(number) {
        return Math.floor(Math.random() * (number+1));
      }

      btn.addEventListener('click', () => {
        const rndCol = `rgb(${random(255)}, ${random(255)}, ${random(255)})`;
        document.body.style.backgroundColor = rndCol;
      });
    </script>
  </body>
</html>


text:
/* Some default styling for cookbook examples */
/*
rgb(53,43,34)
rgb(75,70,74)
rgb(95,97,110)
rgb(137,151,188)
rgb(160,178,226)
*/
body {
    background-color: #fff;
    color: #333;
    font: 1em / 1.4 Helvetica Neue, Helvetica, Arial, sans-serif;
    padding: 0;
    margin: 0;
}

h1 {
  font-size: 2rem;
  margin: 0;
}

h2 {
  font-size: 1.6rem;
  margin: 0;
}

p {
  margin: 0.5em 0;
}

/* styles for the editor */

.playable {
    font-family: monospace;
    display: block;
    margin-bottom: 10px;
    background-color: #F4F7F8;
    border: none;
    border-left: 6px solid #558ABB;
    color: #4D4E53;
    width: 90%;
    max-width: 700px;
    padding: 10px 10px 0px;
    font-size: 90%;
  }

  .playable-css {
    height: 80px;
  }

  .playable-js {
    height: 160px;
  }

  .playable-buttons {
    text-align: right;
    width: 90%;
    max-width: 700px;
    padding: 5px 10px 5px 26px;
    font-size: 100%;
  }

  .preview {
    width: 90%;
    max-width: 700px;
    border: 1px solid #4D4E53;
    border-radius: 2px;
    padding: 10px 14px 10px 10px;
    margin-bottom: 10px;
  }

  .preview input {
    display: block;
    margin: 5px;
  }


text:
var section = document.querySelector('section');
var editable = document.querySelector('.editable');
var textareaJS = document.querySelector('.playable-js');
var reset = document.getElementById('reset');
var jsCode = textareaJS.value;

function fillCode() {
    editable.textContent = textareaJS.value;
    section.innerHTML = ' ';
    try {
      eval(editable.textContent);
    } catch(e) {
      let para = document.createElement('p');
      para.textContent = e;
      section.appendChild(para);
    }
}

reset.addEventListener('click', function () {
    textareaJS.value = jsCode;
    fillCode();
});

textareaJS.addEventListener('input', fillCode);
window.addEventListener('load', fillCode);


text:
# JavaScript loops marking guide

The aim of the tasks is to demonstrate an understanding of the JavaScript features covered in the [Looping code](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Looping_code) lesson in Learn Web Development on MDN.

Note: If there is an error in your code, it will be outputted into the results panel on the page, to help you try to figure out the answer (or into the browser's JavaScript console, in the case of the downloadable version).

Note: We didn't provide live editable versions of these tasks because of the risk of creating infinite loops and crashing the assessment page.

## Task 1

In our first looping task we want you start by creating a simple `for...of` loop that goes through all the items in the provided `myArray` and prints them out on the screen inside list items, which are appended to the provided `list`.

Note that using a `for()` loop would also be acceptable, as would a different loop structure such as `while()`.

The finished code should look something like this:

```
const myArray = ['tomatoes', 'chick peas', 'onions', 'rice', 'black beans'];

const list = document.createElement('ul');

for (let item of myArray) {
  const listItem = document.createElement('li');
  listItem.textContent = item;
  list.appendChild(listItem);
}
```

## Task 2

In this next task, we want you to write a simple program that, given a name, searches an array of objects containing names and emails (`phonebook`) and, if it finds the name, outputs the name and phone number and then uses `break` to exit the loop.

If the name is not found, it lets the user know.

You should use a type of loop that you've not used in the previous task.

The finished code should look something like this:

```
const name = 'Mustafa';
const para = document.createElement('p');

const phonebook = [
  { name : 'Chris', number : '1549' },
  { name : 'Li Kang', number : '9634' },
  { name : 'Anne', number : '9065' },
  { name : 'Francesca', number : '3001' },
  { name : 'Mustafa', number : '6888' },
  { name : 'Tina', number : '4312' },
  { name : 'Bert', number : '7780' },
  { name : 'Jada', number : '2282' },
]

// Add your code here
for (let i = 0; i < phonebook.length; i++) {
  if(phonebook[i].name === name) {
    para.textContent = `${ phonebook[i].name }'s number is ${ phonebook[i].number }.`;
    break;
  }

  if(i === phonebook.length - 1) {
    para.textContent = 'Name not found in the phonebook';
  }
}
```

## Task 3

Finally, you'll need to use a loop to go through the numbers 1 to 500, backwards, and run the `isPrime()` function on them. For each number that isn't a prime number, use the `continue` keyword to move on to the next loop iteration. For each one that is a prime number, add it to the paragraph's `textContent`.

You should use a type of loop that you've not used in the previous two tasks.

The code should look something like this:

```
do {
  if (isPrime(i)) {
    para.textContent += `${i} `;
  }
  i--;
} while (i > 1);
```

(Answer provided by harryghgim; well done!)


text:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <title>Loops: Task 3</title>
    <style>
      p {
        color: purple;
        margin: 0.5em 0;
      }

      * {
        box-sizing: border-box;
      }
    </style>
    <link rel="stylesheet" href="../styles.css" />
  </head>

  <body>

    <section class="preview">

    </section>

  </body>
  <script>
    let i = 500;
    const para = document.createElement('p');

    function isPrime(num) {
      for(let i = 2; i < num; i++) {
        if(num % i === 0) {
          return false;
        }
      }

      return true;
    }

    // Add your code here

    // Don't edit the code below here!
    const section = document.querySelector('section');
    section.appendChild(para);
  </script>

</html>


text:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <title>Loops: Task 1</title>
    <style>
      p {
        color: purple;
        margin: 0.5em 0;
      }

      * {
        box-sizing: border-box;
      }
    </style>
    <link rel="stylesheet" href="../styles.css" />
  </head>

  <body>

    <section class="preview">

    </section>

  </body>
  <script>
    const myArray = ['tomatoes', 'chick peas', 'onions', 'rice', 'black beans'];
    const list = document.createElement('ul');

    // Add your code here

    // Don't edit the code below here!

    const section = document.querySelector('section');
    section.appendChild(list);
  </script>

</html>


text:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <title>Loops: Task 2</title>
    <style>
      p {
        color: purple;
        margin: 0.5em 0;
      }

      * {
        box-sizing: border-box;
      }
    </style>
    <link rel="stylesheet" href="../styles.css" />
  </head>

  <body>

    <section class="preview">

    </section>

  </body>
  <script>
    const name = 'Mustafa';
    const para = document.createElement('p');

    const phonebook = [
      { name : 'Chris', number : '1549' },
      { name : 'Li Kang', number : '9634' },
      { name : 'Anne', number : '9065' },
      { name : 'Francesca', number : '3001' },
      { name : 'Mustafa', number : '6888' },
      { name : 'Tina', number : '4312' },
      { name : 'Bert', number : '7780' },
      { name : 'Jada', number : '2282' },
    ]

    // Add your code here

    // Don't edit the code below here!
    const section = document.querySelector('section');
    section.appendChild(para);
  </script>

</html>


text:
<!DOCTYPE html>

    <meta charset="utf-8" />
    <title>Conditionals: Task 3</title>
    <link rel="stylesheet" href="../styles.css" />

    <textarea class="playable playable-js" style="height: 220px;">

let response;
let score = 75;
let machineActive = false;

if(machineActive) {
	// Add your code here
} else {
  response = 'The machine is turned off. Turn it on to process your score.';
}

// Don't edit the code below here!

section.innerHTML = ' ';
let para1 = document.createElement('p');
let para2 = document.createElement('p');

para1.textContent = `Your score is ${ score }.`;
para2.textContent = response;

section.appendChild(para1);
section.appendChild(para2);
    </textarea>

      <input id="reset" type="button" value="Reset" />


text:
# JavaScript conditionals marking guide

The aim of the tasks is to demonstrate an understanding of the JavaScript features covered in the [Making decisions in your code — conditionals](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/conditionals) lesson in Learn Web Development on MDN.

Note: If there is an error in your code, it will be outputted into the results panel on the page, to help you try to figure out the answer (or into the browser's JavaScript console, in the case of the downloadable version).

## Task 1

In this task you are supposed to create your own basic `if ... else` statement that tests whether the season is summer or not, and stores an appropriate response in the `response` variable, which is then printed out in the results panel. The `else` clause should print some kind of generic response.

To finish off, you need to add an `else if` clause to also check whether it is winter, and if so, put an appropriate response inside `response`.

The finished code should look something like this:

```
let season = 'summer';
let response;

if (season === 'summer') {
  response = 'It\'s probably nice and warm where you are; enjoy the sun!';
} else if(season === 'winter') {
  response = 'I hope you are not too cold. Put some warm clothes on!';
} else {
  response = 'I don\'t know what the season is where you are. Hope you are well.';
}
```

## Task 2

Task 2 tests some more complex conditionals, like not equal, less than, greater than, etc., along with a nested structure.

You are given two variables containing an indicator of the answer machine being switched on or not (`true`/`false`), and a score. You are also given an uninitialized `response` variable.

You need to create one `if ... else` structure that checks whether the machine is switched on and puts a message into the `response` variable if it isn't, telling the user to switch the machine on.

Inside the `if` part, you need to nest an `if ... else if` structure that puts appropriate messages into the `response` variable depending on different scores. The conditional operator tests should look like this:

* `score < 0 || score > 100` - "This is not possible, an error has occurred." This could also be done just by an `else` clause, as the scores between 0 and 100 are all covered by the other clauses. But it's nice to be exact.
* `score >= 0 && score < 20` - "That was a terrible score — total fail!"
* `score >= 20 && score < 40` - "You know some things, but it\'s a pretty bad score. Needs improvement."
* `score >= 40 && score < 70` — "You did a passable job, not bad!"
* `score >= 70 && score < 90` — "That\'s a great score, you really know your stuff."
* `score >= 90 && score <= 100` — "What an amazing score! Did you cheat? Are you for real?"

The finished code should look something like this:

```
let response;
let score = 75;
let machineActive = false;

if (machineActive) {
  if (score < 0 || score > 100) {
    response = 'This is not possible, an error has occurred.';
  } else if(score >= 0 && score < 20) {
    response = 'That was a terrible score — total fail!';
  } else if(score >= 20 && score < 40) {
    response = 'You know some things, but it\'s a pretty bad score. Needs improvement.';
  } else if(score >= 40 && score < 70) {
    response = 'You did a passable job, not bad!';
  } else if(score >= 70 && score < 90) {
    response = 'That\'s a great score, you really know your stuff.';
  } else if(score >= 90 && score <= 100) {
    response = 'What an amazing score! Did you cheat? Are you for real?';
  }
} else {
  response = 'The machine is turned off. Turn it on to process your score.';
}
```

## Task 3

In this task we want you to take the code you wrote for the previous example, and convert it so that the inner structure uses a `switch` statement.

Use the switch `default` option for the "not possible" option.

The full `if ... else` block including added `switch` statement should look like this:

```
if (machineActive) {
	switch (true) {
		case (score >= 0 && score < 20):
		  response = 'That was a terrible score — total fail!';
		  break;
		case (score >= 20 && score < 40):
		  response = 'You know some things, but it\'s a pretty bad score. Needs improvement.';
		  break;
		case (score >= 40 && score < 70):
		  response = 'You did a passable job, not bad!';
		  break;
		case (score >= 70 && score < 90):
		  response = 'That\'s a great score, you really know your stuff.';
		  break;
		case (score >= 90 && score <= 100):
		  response = 'What an amazing score! Did you cheat? Are you for real?';
		  break;
		default:
		  response = 'This is not possible, an error has occurred.';
	}
} else {
  response = 'The machine is turned off. Turn it on to process your score.';
}
```

## Task 4

For the final task we have to offer in this set, we need you to first write an `if ... else` statement that checks whether `machineActive` is `true`. If so, set `machineResult` to a string telling the user they can successfully log in. If not, set it to a message telling them they need to activate the machine before they can log in.

Inside the `if` part of the structure, you need to write a ternary operator that checks whether `pwd` is equal to `cheese`. If so, it assigns a string saying that the login was successful; if not assign a string saying the log in failed. The result should be assigned to a variable called `pwdResult`.

Your solution should look something like this:

```
let machineActive = true;
let pwd = 'cheese';

let machineResult;
let pwdResult;

if (machineActive) {
  machineResult = 'Machine is active. Trying login.'
  pwdResult = pwd === 'cheese' ? 'Login successful.' : 'Password incorrect; login failed.'
} else {
  machineResult = 'Machine is inactive. Activate and try logging in again.';
}
```


text:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <title>Conditionals: Task 3</title>
    <style>
      p {
        color: purple;
        margin: 0.5em 0;
      }

      * {
        box-sizing: border-box;
      }
    </style>
    <link rel="stylesheet" href="../styles.css" />
  </head>

  <body>

    <section class="preview">

    </section>

  </body>
  <script>
    let response;
    let score = 75;
    let machineActive = false;

    if(machineActive) {
      // Add your code here
    } else {
      response = 'The machine is turned off. Turn it on to process your score.';
    }

    // Don't edit the code below here!

    const section = document.querySelector('section');

    let para1 = document.createElement('p');
    let para2 = document.createElement('p');

    para1.textContent = `Your score is ${ score }.`;
    para2.textContent = response;

    section.appendChild(para1);
    section.appendChild(para2);
  </script>

</html>


text:
<!DOCTYPE html>

    <meta charset="utf-8"/>
    <title>Conditionals: Task 1</title>

    <link rel="stylesheet" href="../styles.css" />


text:
<!DOCTYPE html>

    <meta charset="utf-8" />
    <title>Conditionals: Task 1</title>
    <link rel="stylesheet" href="../styles.css" />

    <textarea class="playable playable-js" style="height: 220px;">

let season = 'summer';
let response;

// Add your code here

// Don't edit the code below here!

const section = document.querySelector('.preview');
section.innerHTML = ' ';
let para1 = document.createElement('p');
para1.textContent = response;
section.appendChild(para1);
    </textarea>

      <input id="reset" type="button" value="Reset" />


text:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <title>Conditionals: Task 4</title>
    <style>
      p {
        color: purple;
        margin: 0.5em 0;
      }

      * {
        box-sizing: border-box;
      }
    </style>
    <link rel="stylesheet" href="../styles.css" />
  </head>

  <body>

    <section class="preview">

    </section>

  </body>
  <script>
    let machineActive = true;
    let pwd = 'cheese';

    let machineResult;
    let pwdResult;

    // Add your code here

    // Don't edit the code below here!

    const section = document.querySelector('section');

    let para1 = document.createElement('p');
    let para2 = document.createElement('p');

    para1.textContent = machineResult;
    para2.textContent = pwdResult;

    section.appendChild(para1);
    section.appendChild(para2);
  </script>

</html>


text:
<!DOCTYPE html>

    <meta charset="utf-8" />
    <title>Conditionals: Task 2</title>
    <link rel="stylesheet" href="../styles.css" />

    <textarea class="playable playable-js" style="height: 220px;">

let response;
let score = 75;
let machineActive = false;

// Add your code here

// Don't edit the code below here!

section.innerHTML = ' ';
let para1 = document.createElement('p');
let para2 = document.createElement('p');

para1.textContent = `Your score is ${ score }`;
para2.textContent = response;

section.appendChild(para1);
section.appendChild(para2);
    </textarea>

      <input id="reset" type="button" value="Reset" />


text:
<!DOCTYPE html>

    <meta charset="utf-8" />
    <title>Conditionals: Task 4</title>
    <link rel="stylesheet" href="../styles.css" />

    <textarea class="playable playable-js" style="height: 220px;">

let machineActive = true;
let pwd = 'cheese';

let machineResult;
let pwdResult;

// Add your code here

// Don't edit the code below here!

section.innerHTML = ' ';
let para1 = document.createElement('p');
let para2 = document.createElement('p');

para1.textContent = machineResult;
para2.textContent = pwdResult;

section.appendChild(para1);
section.appendChild(para2);
    </textarea>

      <input id="reset" type="button" value="Reset" />


text:
<!DOCTYPE html>

    <meta charset="utf-8"/>
    <title>Conditionals: Task 2</title>

    <link rel="stylesheet" href="../styles.css" />