Team:Paris Bettencourt/js/RunOnLoad.js

From 2012.igem.org

/*

javascript.js

JavaScript for http://code.stephenmorley.org/

Created by Stephen Morley - http://stephenmorley.org/ - and released under the terms of the CC0 1.0 Universal legal code:

http://creativecommons.org/publicdomain/zero/1.0/legalcode

  • /

// create the runOnLoad function var runOnLoad = (function(){

 // set that the tasks have not yet run
 var tasksRun = false;
 // initialise the task list
 var tasks = [];
 /* Adds a task. The parameter is:
  *
  * task - the task
  */
 function add(task){
   // check whether tasks have been run
   if (tasksRun){
     // run the task immediately
     task();
   }else{
     // add the task to the list
     tasks.push(task);
   }
 }
 // Runs the tasks.
 function runTasks(){
   // set that the tasks have run
   tasksRun = true;
   // loop over the tasks, running them
   while (tasks.length) tasks.shift()();
 }
 // check which method of adding event listeners is supported
 if (document.addEventListener){
   // listen for the DOMContentLoaded event
   document.addEventListener('DOMContentLoaded', runTasks, false);
   // listen for the load event
   window.addEventListener('load', runTasks, false);
 }else{
   // listen for the readystatechange event
   document.attachEvent(
       'onreadystatechange',
       function(){
         if (document.readyState == 'complete') runTasks();
       });
   // listen for the load event
   window.attachEvent('onload', runTasks);
 }
 // return the public API
 return add;

})();

/* Creates a SmoothMovement. A SmoothMovement produces integer position values

* representing movement towards a target position, with a maximum acceleration
* or deceleration of one distance unit per time unit squared. The parameters
* are:
*
* position - the initial position
* target   - the target position
*/

function SmoothMovement(position, target){

 // initialise the position, target, velocity, and animation interval
 this.position          = position;
 this.target            = target;
 this.velocity          = 0;
 this.animationInterval = null;

}

/* Updates the position an velocity for this SmoothMovement, and returns the

* new position.
*/

SmoothMovement.prototype.update = function(){

 // check whether the velocity is negative
 if (this.velocity < 0){
   // check whether we must decelerate or can accelerate
   if (this.target > this.position - this.velocity * (this.velocity - 1) / 2){
     // we must decelerate to avoid overshooting, so decrease the speed
     this.velocity ++;
   }else if (this.target <=
       this.position - (this.velocity - 1) * (this.velocity - 2) / 2){
     // we can accelerate without overshooting, so increase the speed
     this.velocity --;
   }
 }else{
   // check whether we must decelerate or can accelerate
   if (this.target < this.position + this.velocity * (this.velocity + 1) / 2){
     // we must decelerate to avoid overshooting, so decrease the speed
     this.velocity--;
   }else if (this.target >=
       this.position + (this.velocity + 1) * (this.velocity + 2) / 2){
     // we can accelerate without overshooting, so increase the speed
     this.velocity++;
   }
 }
 // update the position
 this.position += this.velocity;
 // return the new position
 return this.position;

}

/* Returns true if this SmoothMovement has stopped, and false otherwise. Note

* that this means that both the velocity and acceleration are zero (or
* equivalently, that the velocity is zero and the position is at the target).
*/

SmoothMovement.prototype.hasStopped = function(){

 // return whether we have stopped
 return (this.position == this.target && this.velocity == 0);

}

/* Animates this SmoothMovement bycalling the update function repeatedly until

* the SmoothMovement has stopped. The parameters are:
*
* interval       - the interval between updates, in milliseconds
* updateListener - a function to call after each update. This function is
*                  passed the new position and the SmoothMovement as its
*                  first and second parameters.
* stopListener   - a function to call when the SmoothMovement has stopped. This
*                  function is passed the SmoothMovement as its parameter. This
*                  parameter is optional.
*/

SmoothMovement.prototype.animate = function(

   interval, updateListener, stopListener){
 // clear any current animation interval
 if (this.animationInterval) window.clearInterval(this.animationInterval);
 // create the new animation interval
 this.animationInterval = window.setInterval(
     this.createAnimationClosure(updateListener, stopListener), interval);

}

/* Creates a closure for use in the animate function. This function is not

* intended to be used elsewhere. The parameters are:
*
* updateListener - a function to call after each update.
* stopListener   - a function to call when the SmoothMovement has stopped
*/

SmoothMovement.prototype.createAnimationClosure = function(

   updateListener, stopListener){
 // store a reference to the 'this' object
 var thisObject = this;
 // return the animation closure
 return function(){
   // update the SmoothMovement
   thisObject.update();
   // call the update listener
   updateListener(thisObject.position, thisObject);
   // check whether the SmoothMovement has stopped
   if (thisObject.hasStopped()){
     // clear the animation interval
     window.clearInterval(thisObject.animationInterval);
     thisObject.animationInterval = null;
     // call the stop listener if one was supplied
     if (stopListener) stopListener(thisObject);
   }
 }

}

// create the Website object var Website =

   {
     // define the list of share buttons
     SHARE_BUTTONS : [
       [
         'email',
         'mailto:?body=<url>&subject=<title>',
         0, 0
       ],
       [
         'twitter',
         'https://twitter.com/intent/tweet?url=<url>&text=<title>',
         550, 420 // specified by Twitter
       ],
       [
         'facebook',
         'https://www.facebook.com/sharer.php?u=<url>',
         600, 400
       ],
       [
         'google-plus',
         'https://plus.google.com/share?url=<url>',
         600, 600 // specified by Google
       ],
       [
         'linkedin',
         'http://www.linkedin.com/shareArticle?mini=true&url=<url>',
         600, 400
       ]
     ],
     /* Creates the header of random bits and creates an interval to flip bits
      * four times a second.
      */
     createHeader : function(){
       // store a reference to the header node
       Website.header = document.getElementById('header');
       // loop over the rows and columns
       for (var row = 0; row < 5; row ++){
         for (var column = 0; column < 80; column ++){
           // create the DOM node for the bit
           var div = document.createElement('div');
           div.style.left = (column * 12) + 'px';
           div.style.top  = (row * 16) + 'px';
           div.style.opacity = 1;
           // randomly assign the bit a 0 or 1 value
           div.appendChild(
               document.createTextNode(Math.floor(2 * Math.random())));
           // add the bit to the header
           Website.header.appendChild(div);
         }
       }
       // store the list of bits in the header
       Website.headerBits = Website.header.getElementsByTagName('div');
       // create the bit animation interval
       Website.bitAnimationInterval =
           window.setInterval(Website.flipRandomBit, 500);
     },
     /* Selects a random bit from the header and animates it flipping. The
      * selected bit is ignored if it is already in the process of flipping.
      */
     flipRandomBit : function(){
       // select a random bit
       var bit =
           Website.headerBits[
               Math.floor(Website.headerBits.length * Math.random())];
       // check that the bit is not in the process of being flipped
       if (bit.className == ){
         // mark the bit as being in the process of being flipped
         bit.className = 'flipping';
         // flip the bit after one second
         window.setTimeout(
             function(){
               bit.className            = ;
               bit.firstChild.nodeValue = 1 - bit.firstChild.nodeValue;
             },
             1000);
       }
     },
     /* Handles the page being scrolled by ensuring the navigation is always in
      * view.
      */
     handleScroll : function(){
       // check that this is a relatively modern browser
       if (window.XMLHttpRequest){
         // determine the distance scrolled down the page
         var offset = window.pageYOffset
                    ? window.pageYOffset
                    : document.documentElement.scrollTop;
         // check whether the header has moved into or out of view
         if (offset <= 104 && !Website.bitAnimationInterval){
           // start the header animation
           Website.bitAnimationInterval =
               window.setInterval(Website.flipRandomBit, 250);
           // clear the class on the navigation
           document.getElementById('navigation').className = ;
         }else if (offset > 104 && Website.bitAnimationInterval){
           // stop the header animation
           window.clearInterval(Website.bitAnimationInterval);
           Website.bitAnimationInterval = null;
           // set the class on the navigation
           document.getElementById('navigation').className = 'fixed';
         }
       }
     },
     // define a SmoothMovement instance for use with the navigation animation
     navigationAnimation : new SmoothMovement(878, 62),
     // expands or collapses the navigation
     animateNavigation : function(){
       // store references to the DOM nodes of the toolbar components
       Website.navigationBar = document.getElementById('navigationBar');
       Website.twitterButton = document.getElementById('twitterButton');
       Website.collapseArrow = document.getElementById('collapseArrow');
       // animate the navigation
       Website.navigationAnimation.animate(
           20,
           function(width){
             // set the navigation width
             Website.navigationBar.style.width = width + 'px';
             // move the twitter button
             Website.twitterButton.style.left = (width - 430) + 'px';
             // scale the arrow
             Website.collapseArrow.setAttribute(
                 'src',
                 '/images/collapse-arrow-'
                     + (width > 470 ? 'left' : 'right')
                     + '/');
             Website.collapseArrow.setAttribute(
                 'width',
                 Math.max(1, Math.round(Math.abs((width - 470) / 408 * 20))));
           },
           function(animation){
             // update the animation target and animation button title text
             animation.target = (animation.target == 62 ? 878 : 62);
             document.getElementById('collapseButton').setAttribute(
                 'title',
                 (animation.target == 62
                     ? 'Collapse navigation'
                     : 'Expand navigation'));
           });
     },
     // Toggles the display of the share buttons.
     toggleShareButtons : function(){
       // find the footer and share button nodes
       var footer = document.getElementById('footer');
       var shareButtons = document.getElementById('shareButtons');
       // toggle the display of the share buttons
       if (shareButtons){
         footer.removeChild(shareButtons);
       }else{
         footer.appendChild(Website.createShareButtons());
       }
     },
     // Creates and returns a DOM node for the share buttons.
     createShareButtons : function(){
       // create the container
       var container = document.createElement('div');
       container.id = 'shareButtons';
       // loop over the buttons, adding them to the container
       for (var index = 0; index < Website.SHARE_BUTTONS.length; index ++){
         var button = document.createElement('img');
         button.src =
             '/images/share-icons/'
             + Website.SHARE_BUTTONS[index][0]
             + '/';
         button.onclick =
             Website.createShareButtonClickHandler(
                 Website.SHARE_BUTTONS[index][1],
                 Website.SHARE_BUTTONS[index][2],
                 Website.SHARE_BUTTONS[index][3]);
         container.appendChild(button);
       }
       // return the share buttons node
       return container;
     },
     /* Returns a click handler for a share button. The parameter is:
      *
      * url    - the URL for sharing; the string <url> will be replaced with
      *          the URL of the current page and <title> will be replaced with
      *          the title of the current page
      * width  - the width of the pop-up window
      * height - the height of the pop-up window
      *
      * If width and height are 0 the link will open in the current window.
      */
     createShareButtonClickHandler : function(url, width, height){
       // return the click handler
       return function(){
         // hide the share buttons
         Website.toggleShareButtons();
         // determine the URL
         var finalUrl =
             url.replace('<url>', encodeURIComponent(location.href));
         finalUrl =
             finalUrl.replace('<title>', encodeURIComponent(document.title));
         // open the URL
         if (width == 0 || height == 0){
           location.href = finalUrl;
         }else{
           window.open(
               finalUrl,
               '_blank',
               'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,width='
                   + width
                   + ',height='
                   + height);
         }
       }
     }
   };

// add the event listeners if (window.addEventListener){

 window.addEventListener('load',   Website.createHeader, false);
 window.addEventListener('scroll', Website.handleScroll, false);

}else{

 window.attachEvent('onload',   Website.createHeader);
 window.attachEvent('onscroll', Website.handleScroll);

}