Skip to content
4thex edited this page Dec 3, 2014 · 7 revisions

Using the ProgressEvent interface and the HTML5 progress element.

index.html

<!DOCTYPE html>

<html>
  <head>
    <title>Progress</title>
    <link rel="stylesheet" href="styles.css">
    <script src="deck.js"></script>
    <script src="shuffler.js"></script>
    <script src="progress.js"></script>
    <script src="main.js"></script>
  </head>

  <body>
    <div id="result">0</div>
    <form>
      <input id="shuffle" type="button" value="Shuffle" />
    </form>
  </body>
</html>

deck.js

// Namespace
if(!NSMscsbend) {
  var NSMscsbend = {};
}

NSMscsbend.deck = function(spec) {
  var that = {
    sequence: [
      "AS", "2S", "3S", "4S", "5S", "6S", "7S", "8S", "9S", "10S", "JS", "QS", "KS",
      "AH", "2H", "3H", "4H", "5H", "6H", "7H", "8H", "9H", "10H", "JH", "QH", "KH",
      "AD", "2D", "3D", "4D", "5D", "6D", "7D", "8D", "9D", "10D", "JD", "QD", "KD",
      "AC", "2C", "3C", "4C", "5C", "6C", "7C", "8C", "9C", "10C", "JC", "QC", "KC",
    ]
  };
  return that;
};

shuffler.js

// Namespace
if(!NSMscsbend) {
  var NSMscsbend = {};
}

NSMscsbend.shuffler = function(spec) {
  var that = {
    spec: spec
  };

  var state = {
    running: false,
    totalExecuted: 0,
    swapIndex: 0,
    sequence: spec.sequence
  };

  var steps = spec.swaps * 10;

  var pick = function() {
    var result = Math.floor(Math.random() * state.sequence.length);
    return result;
  };

  var report = function(executed) {
    state.totalExecuted += executed;
    if(!(state.totalExecuted % Math.floor(steps/1000))) {
      var event = new ProgressEvent("progress",
      {
        lengthComputable: true,
        loaded: state.totalExecuted,
        total: steps
      });
      that.onprogress(event);
      return true;
    }
    return false;
  };

  that.execute = function() {
    state.running = true;

    while(state.swapIndex < spec.swaps) {
      var firstIndex = pick(state.sequence.length);
      var secondIndex = pick(state.sequence.length);
      var first = state.sequence[firstIndex];
      var second = state.sequence[secondIndex];
      state.sequence[firstIndex] = second;
      state.sequence[secondIndex] = first;
      state.swapIndex++;
      if(report(10)) {
        state.timer = window.setTimeout(that.execute, 0);
        return;
      }
    }
    state.running = false;
    state.swapIndex = 0;
    state.totalExecuted = 0;
    that.spec.complete(state.sequence);
  };

  that.cancel = function() {
    if(!state.running) return;
    state.running = false;
    state.swapIndex = 0;
    state.totalExecuted = 0;
    if(state.timer) {
      window.clearTimeout(state.timer);
    }
  };

  var listeners = [];

  that.addEventListener = function(type, listener) {
    listeners.push({type: type, listener: listener});
  };

  that.removeEventListener = function(type, listener) {
    var remaining = listeners.filter(function(element) {
      return element.type === type && element.listener === listener;
    });
    return remaining;
  };

  that.onprogress = function(event) {
    listeners.forEach(function(element, index, array) {
      if(element.type === "progress") {
        element.listener(event);
      }
    });
  };

  return that;
};

progress.js

// Namespace
if(!NSMscsbend) {
  var NSMscsbend = {};
}

NSMscsbend.progressBar = function(spec) {
  var that = {};

  var cleanup = function() {
    container.remove();
    spec.element.disabled = false;
  };

  spec.element.disabled = true;
  var container = document.createElement("div");
  container.style.width = "100%";
  var bar = document.createElement("progress");
  bar.style.width = "100%";
  container.appendChild(bar);
  var button = document.createElement("input");
  button.style.width = "8em";
  button.setAttribute("type", "button");
  button.setAttribute("value", "Cancel");
  button.addEventListener("click", function(event) {
    cleanup();
    oncancel();
  });
  container.appendChild(button);
  var sibling;
  if(sibling = spec.element.nextElementSibling) {
    document.insertBefore(container, sibling);
  } else {
    spec.element.parentNode.appendChild(container);
  }

  that.onprogress = function(event) {
    bar.setAttribute("value", event.loaded);
    bar.setAttribute("max", event.total);
    if(event.loaded === event.total) {
      cleanup();
    }
  };

  var oncancel = function() {
    if(spec.cancel) {
      spec.cancel();
    }
  };

  return that;
};

main.js

(function(){

  window.addEventListener("load", function(event) {
    var button = document.querySelector("#shuffle");
    var resultElement = document.querySelector("#result");
    var deck = NSMscsbend.deck();
    button.addEventListener("click", function(event) {
      var argument = {
        sequence: deck.sequence,
        swaps: 100000,
        complete: function(sequence) {
          resultElement.innerText = sequence.join();
        }
      };
      var shuffler = NSMscsbend.shuffler(argument);

      var progressBar = NSMscsbend.progressBar({
        element: button,
        cancel: shuffler.cancel
      });

      shuffler.addEventListener("progress", progressBar.onprogress);

      shuffler.execute();
    });
  });

}());

Introduction
[Install CDE](Install CDE)

Part 1 - The basics

HTML5
CSS
JavaScript
DOM

Part 2 - Projects

[A Ringing Bell](A Ringing Bell)
[Projectile Movement](Projectile Movement)
[Map with location](Map with location)
[Slide 15 Puzzle](Slide 15 Puzzle)
[A Running Man](A Running Man)
Sudoku

Part 3 - Getting it out there

[Package Chrome Application](Package Chrome Application)
[Uploading Your Application](Uploading Your Application)

Clone this wiki locally