//
//	Lightbox version 3.0.0
//	by Lokesh Dhakar - http://www.huddletogether.com
//	04/03/2008
//
//	For more information on this script, visit:
//	http://huddletogether.com/projects/lightbox2/
//
//	Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/
//
//	Credit also due to those who have helped, inspired, and made their code available to the public.
//	Including: Scott Upton(uptonic.com), Peter-Paul Koch(quirksmode.org), Thomas Fuchs(mir.aculo.us), and others.
//

/*

  Table of Contents
  -----------------

  Extending Built-in Objects
  - Object.extend(Element)
  - Array.prototype.removeDuplicates()

  Lightbox Class Declaration
  - block()
  - onload()
  - updateImageList()
  
  Miscellaneous Functions
  - getPageScroll()
  - getPageSize()
  - showSelectBoxes()
  - hideSelectBoxes()
  - showObjects()
  - hideObjects()
  - pause()

*/

//	Additional methods for Element added by SU, Couloir
//	- further additions by Lokesh Dhakar (huddletogether.com)
Object.extend(Element, {
  setWidth: function (element, width) {
    $(element).style.width = width + "px";
  },
  setHeight: function (element, height) {
    $(element).style.height = height + "px";
  },
  setTop: function (element, top) {
    $(element).style.top = top + "px";
  },
  setLeft: function (element, left) {
    $(element).style.left = left + "px";
  },
  setSrc: function (element, src) {
    $(element).src = src;
  },
  setInnerHTML: function (element, content) {
    $(element).innerHTML = content;
  }
});

//	Extending built-in Array object
//	- array.removeDuplicates()
Array.prototype.removeDuplicates = function () {
  for (i = 1; i < this.length; i++) {
    if (this[i][0] == this[i - 1][0]) { this.splice(i, 1); }
  }
}

//	Lightbox Class Declaration
//	- block()
//  - onload()
//  - updateImageList()

//	Structuring of code inspired by Scott Upton (http://www.uptonic.com/)

var Lightbox = {

  onload: function (event) {
    var options = { 'minWidth' : 300};
    return Lightbox._create(options);
  },

  // Fades to overlay and runs an animated loading gif (e.g after form submit)
  loading: function(options) {
    if (!document.getElementsByTagName) { return true; }

     //Lightbox._options(options);

     var buttons = document.getElementsByClassName("buttons");
     buttons[0].style.visibility = "hidden";

    var parent = document.getElementsByTagName("body").item(0);
    var overlay = Lightbox._createElement("div", "overlay", "none", null, null, function (event) {} );
    parent.appendChild(overlay);

    var pageSize = getPageSize();
    Element.setWidth('overlay', pageSize[0]);
    Element.setHeight('overlay', pageSize[1]);

    if (Lightbox._animate) {
      new Effect.Appear('overlay', { duration: Lightbox._overlayBlockDuration, from: 0.0, to: Lightbox._blockOpacity });
    }
    else {
      Element.setOpacity('overlay', Lightbox._opacity);
    }

    if (Lightbox._animate) {
        var loading = Lightbox._createElement("div", 'loading');
        parent.appendChild(loading);

        // document.all should be detected in IE and Opera, workaround for IE gif freezing.
        if(document.all) {
            new Effect.Pulsate(loading);
        }
        else {
            Element.show('loading');
        }
    }

  },


  // Blocks the page (after form submit)
  block: function(message, options) {
    if (!document.getElementsByTagName) { return true; }

    Lightbox._options(options);
    
    var parent = document.getElementsByTagName("body").item(0);
    var overlay = Lightbox._createElement("div", "overlay", "none", null, null, function (event) {} );
    parent.appendChild(overlay);
        
    // The rest of this code inserts html at the bottom of the page that looks similar to this:
    //
    // <div id="lightbox">
    //   <div id="outerImageContainer">
    //     <div id="imageContainer">
    //       <div id="loading"></div>
    //       <div id="imageDataContainer"> message </div>
    //     </div>
    //   </div>
    // </div>

    var lightbox = Lightbox._createElement("div", "lightbox", "none");
    parent.appendChild(lightbox);

    var outerImageContainer = Lightbox._createElement("div", "outerImageContainer");
    lightbox.appendChild(outerImageContainer);

    var imageContainer = Lightbox._createElement("div", 'imageContainer');
    outerImageContainer.appendChild(imageContainer);

	imageContainer.appendChild(Lightbox._createElement("div", 'loading'));
    imageDataContainer = Lightbox._createElement("div", 'imageDataContainer');
    imageContainer.appendChild(imageDataContainer);
    imageDataContainer.appendChild(document.createTextNode(message));

    hideSelectBoxes();
    hideObjects();
    var pageSize = getPageSize();
    Element.setWidth('overlay', pageSize[0]);
    Element.setHeight('overlay', pageSize[1]);
    Element.setOpacity('overlay', Lightbox._blockOpacity);
    Element.show('overlay');
    Element.setTop('lightbox', Number(getPageScroll()[1] + (getPageSize()[3] / 15)).toFixed());
    Element.show('lightbox');
    Element.show('loading');
    return true;
  },
  
  // Loops through anchor tags looking for 'lightbox' references and applies onclick
  // events to appropriate links. You can rerun after dynamically adding images w/ajax.
  updateImageList: function () {
    if (!document.getElementsByTagName) { return; }

    // loop through all anchor tags
    var anchors = document.getElementsByTagName('a');
    for (var i = 0, l = anchors.length; i < l; i++) {
      var anchor = anchors[i];
      // use the string.match() method to catch 'lightbox' references in the rel attribute
      var relAttribute = String(anchor.getAttribute('rel'));
      if (anchor.getAttribute('href') && (relAttribute.toLowerCase().match('lightbox'))) {
        anchor.onclick = function () { return Lightbox._start(this); };
      }
    }

    // loop through all area tags
    // todo: combine anchor & area tag loops
    var areas = document.getElementsByTagName('area');
    for (var i = 0, l = areas.length; i < l; i++) {
      var area = areas[i];
      // use the string.match() method to catch 'lightbox' references in the rel attribute
      var relAttribute = String(area.getAttribute('rel'));
      if (area.getAttribute('href') && (relAttribute.toLowerCase().match('lightbox'))) {
        Area.onclick = function () { return Lightbox.start(this); }
      }
    }
  },

  // Loops through anchor tags looking for 'lightbox' references and applies onclick events 
  // to appropriate links. The 2nd section of the function inserts html at the bottom of the 
  // page which is used to display the shadow overlay and the image container.
  _create: function(options) {
    if (!document.getElementsByTagName) return;

    Lightbox._options(options);

    var parent = document.getElementsByTagName("body").item(0);
    var overlay = Lightbox._createElement("div", "overlay", "none", null, null, Lightbox._overlayEnd);
    parent.appendChild(overlay);

    Lightbox.updateImageList();
        
    // The rest of this code inserts html at the bottom of the page that looks similar to this:
    //
    // <div id="lightbox">
    //   <div id="outerImageContainer">
    //     <div id="imageContainer">
    //       <img id="lightboxImage" />
    //       <div id="hoverNav">
    //         <a href="#" id="prevLink"></a>
    //         <a href="#" id="nextLink"></a>
    //       </div>
    //       <div id="loading"></div>
    //     </div>
    //   </div>
    //   <div id="imageDataContainer">
    //     <div id="imageData">
    //       <div id="imageDetails">
    //         <span id="caption"></span>
    //         <span id="numberDisplay"></span>
    //       </div>
    //       <div id="bottomNav">
    //         <img id="bottomNavPrev" />
    //         <img id="bottomNavNext" />
    //         <img id="bottomNavClose" />
    //       </div>
    //     </div>
    //   </div>
    // </div>

    var lightbox = Lightbox._createElement("div", "lightbox", "none", null, null, Lightbox._overlayEnd);
    parent.appendChild(lightbox);

    var outerImageContainer = Lightbox._createElement("div", "outerImageContainer");
    lightbox.appendChild(outerImageContainer);

    // When Lightbox starts it will resize itself from 250 by 250 to the current image dimension.
    // If animations are turned off, it will be hidden as to prevent a flicker of a
    // white 250 by 250 box.
    if (Lightbox._animate) {
      Element.setWidth('outerImageContainer', 250);
      Element.setHeight('outerImageContainer', 250);
    }
    else {
      Element.setWidth('outerImageContainer', 1);
      Element.setHeight('outerImageContainer', 1);
    }

    var imageContainer = Lightbox._createElement("div", 'imageContainer');
    outerImageContainer.appendChild(imageContainer);
    imageContainer.appendChild(Lightbox._createElement("img", 'lightboxImage'));

    var hoverNav = Lightbox._createElement("div", 'hoverNav');
    imageContainer.appendChild(hoverNav);

    hoverNav.appendChild(Lightbox._createElement("a", 'prevLink', null, null, '#'));
    hoverNav.appendChild(Lightbox._createElement("a", 'nextLink', null, null, '#'));

    imageContainer.appendChild(Lightbox._createElement("div", 'loading', null, null, null, Lightbox._end));

    imageDataContainer = Lightbox._createElement("div", 'imageDataContainer');
    lightbox.appendChild(imageDataContainer);

    imageData = Lightbox._createElement("div", 'imageData');
    imageDataContainer.appendChild(imageData);

    var imageDetails = Lightbox._createElement("div", 'imageDetails');
    imageData.appendChild(imageDetails);

    imageDetails.appendChild(Lightbox._createElement("span", 'caption'));
    imageDetails.appendChild(Lightbox._createElement("span", 'numberDisplay'));

    bottomNav = Lightbox._createElement("div", 'bottomNav');
    imageData.appendChild(bottomNav);

    bottomNav.appendChild(Lightbox._createElement("img", 'bottomNavPrev',  null, Lightbox._imagePath + "miniprev.jpg",   null, Lightbox._prevImage));
    bottomNav.appendChild(Lightbox._createElement("img", 'bottomNavNext',  null, Lightbox._imagePath + "mininext.jpg",   null, Lightbox._nextImage));
    bottomNav.appendChild(Lightbox._createElement("img", 'bottomNavClose', null, Lightbox._imagePath + "closelabel.gif", null, Lightbox._end));
  },

  _createElement: function (type, id, display, src, href, onclick) {
    elem = document.createElement(type);
    if (id)      { elem.setAttribute('id', id); }
    if (display) { elem.style.display = display; }
    if (src)     { elem.setAttribute('src', src); }
    if (href)    { elem.setAttribute('href', href); }
	if (onclick) { elem.onclick = onclick; }
    return elem;
  },

  _options: function(options) {
    if (options) {
      var option = options['borderSize'];
      if (option) { Lightbox._borderSize = option; }
      option = options['overlayDuration'];
      if (option) { Lightbox._overlayDuration = option; }
      option = options['resizeDuration'];
      if (option) { Lightbox._resizeDuration = option; }
      option = options['minWidth'];
      if (option) { Lightbox._minWidth = option; }
      option = options['imagePath'];
      if (option) { Lightbox._imagePath = option; }
      option = options['opacity'];
      if (option) { Lightbox._opacity = option; }
      option = options['blockOpacity'];
      if (option) { Lightbox._blockOpacity = option; }
      option = options['animate'];
      if (option) { Lightbox._animate = option; }
      option = options['text'];
      if (option) { Lightbox._text = option; }
    }    
  },

  // Display overlay and lightbox. If image is part of a set, add siblings to imageArray.
  _start: function (imageLink) {
    hideSelectBoxes();
    hideObjects();

    // stretch overlay to fill page and fade in
    var pageSize = getPageSize();
    Element.setWidth('overlay', pageSize[0]);
    Element.setHeight('overlay', pageSize[1]);

    if (Lightbox._animate) {
      new Effect.Appear('overlay', { duration: Lightbox._overlayDuration, from: 0.0, to: Lightbox._opacity });
    }
    else {
      Element.setOpacity('overlay', Lightbox._opacity);
    }

    Lightbox._activeImage = 0;
    Lightbox._imageArray = [];
    var imageNum = 0;
    if ((imageLink.getAttribute('rel') == 'lightbox')) { // if image is NOT part of a set..
	  Lightbox._imageArray.push(new Array(imageLink.getAttribute('href'), imageLink.getAttribute('title')));
    }
    else { // if image is part of a set..
      // loop through anchors, find other images in set, and add them to imageArray
      var rel = imageLink.getAttribute('rel');
      var anchors = document.getElementsByTagName(imageLink.tagName);
      for (var i = 0, l = anchors.length; i < l; i++) {
        var anchor = anchors[i];
        if (anchor.getAttribute('href') && (anchor.getAttribute('rel') == rel)) {
          Lightbox._imageArray.push(new Array(anchor.getAttribute('href'), anchor.getAttribute('title')));
        }
      }
      Lightbox._imageArray.removeDuplicates();
      var href = imageLink.getAttribute('href');
      while (Lightbox._imageArray[imageNum][0] != href) { imageNum++; }
    }

    // calculate top offset for the lightbox and display
    var pageScroll = getPageScroll();
    Element.setTop('lightbox', Number(pageScroll[1] + (pageSize[3] / 10)).toFixed());
    Element.setLeft('lightbox', Number(pageScroll[0]).toFixed());
    Element.show('lightbox');
    Lightbox._changeImage(imageNum);
    return false;
  },

  _overlayEnd: function (event) {
    if (!event) { event = window.event; }
    var id = Event.element(event).id;
    if (id == 'overlay' || id == 'lightbox') { return Lightbox._end(); }
    return true;
  },

  _end: function (event) {
    Lightbox._disableKeyboardNav();
    Element.hide('lightbox');
    if (Lightbox._animate) {
      new Effect.Fade('overlay', { duration: Lightbox._overlayDuration });
    } 
    else {
      Element.hide('overlay');
    }
    showSelectBoxes();
    showObjects();
    return false;
  },

  _hasNext: function () {
    return Lightbox._activeImage < (Lightbox._imageArray.length - 1);
  },

  _nextImage: function () {
    Lightbox._changeImage(Lightbox._activeImage + 1);
    return false;
  },

  _hasPrev: function () {
    return Lightbox._activeImage > 0;
  },

  _prevImage: function () {
    Lightbox._changeImage(Lightbox._activeImage - 1);
    return false;
  },

  // Hide most elements and preload image in preparation for resizing image container.
  _changeImage: function (imageNum) {
    Lightbox._activeImage = imageNum;
    
    // hide elements during transition
    if (Lightbox._animate) { Element.show('loading'); }
    Element.hide('lightboxImage');
    Element.hide('hoverNav');
    Element.hide('prevLink');
    Element.hide('nextLink');
    Element.hide('bottomNavPrev');
    Element.hide('bottomNavNext');
    Element.hide('imageDataContainer');
    Element.hide('caption');
    Element.hide('numberDisplay');

    // once image is preloaded, resize image container
    Lightbox._preloader = new Image();
    Lightbox._preloader.onload = function () {
      Element.setSrc('lightboxImage', Lightbox._imageArray[imageNum][0]);
      Lightbox._preloader.onload = function () { }; // clear onLoad, IE behaves irratically with animated gifs otherwise 
      Lightbox._resizeImageContainer(Lightbox._preloader.width, Lightbox._preloader.height);
    };
    Lightbox._preloader.src = Lightbox._imageArray[imageNum][0];
  },

  _resizeImageContainer: function (imgWidth, imgHeight) {
    var borders = Lightbox._borderSize * 2;
    
    // keep to a minimum width, if specified
    if (Lightbox._minWidth > 0 && (imgWidth + borders) < Lightbox._minWidth) {
      imgWidth = Lightbox._minWidth - borders;
    }

    // get current height and width
    var widthCurrent = Element.getWidth('outerImageContainer');
    var heightCurrent = Element.getHeight('outerImageContainer');

    // get new width and height
    var widthNew = imgWidth + borders;
    var heightNew = imgHeight + borders;

    // scalars based on change from old to new
    var xScale = (widthNew / widthCurrent) * 100;
    var yScale = (heightNew / heightCurrent) * 100;
    
    // calculate size difference between new and old image, and resize if necessary
    var widthDiff = widthCurrent - widthNew;
	var heightDiff = heightCurrent - heightNew;
    if (heightDiff != 0) {
      new Effect.Scale('outerImageContainer', yScale, { scaleX: false, duration: Lightbox._resizeDuration, queue: 'front' });
    }
	if (widthDiff != 0) {
	  new Effect.Scale('outerImageContainer', xScale, { scaleY: false, duration: Lightbox._resizeDuration, delay: Lightbox._resizeDuration });
	}

    // if new and old image are same size and no scaling transition is necessary,
    // do a quick pause to prevent image flicker.
    if ((heightDiff == 0) && (widthDiff == 0)) {
      if (navigator.appVersion.indexOf("MSIE") != -1) { pause(250); } else { pause(100); }
    }

    Element.setHeight('prevLink', imgHeight);
    Element.setHeight('nextLink', imgHeight);
    Element.setWidth('imageDataContainer', widthNew);
    Lightbox._showImage();
  },

  // Display image.
  _showImage: function () {
    Element.hide('loading');
    new Effect.Appear('lightboxImage', { duration: Lightbox._resizeDuration, queue: 'end', afterFinish: Lightbox._updateDetails });
	Lightbox._preloadNeighborImages();
  },

  // Display caption, image number, and bottom nav.
  _updateDetails: function () {
    // if caption is not null
	var caption = Lightbox._imageArray[Lightbox._activeImage][1];
    if (caption) {
      Element.show('caption');
      Element.setInnerHTML('caption', caption);
    }
		
    // if image is part of set display 'Image x of x'
    if (Lightbox._imageArray.length > 1) {
      Element.show('numberDisplay');
      var text = Lightbox._text.replace("*", (Lightbox._activeImage + 1));
      text = text.replace("*", Lightbox._imageArray.length);
      Element.setInnerHTML('numberDisplay', text);
    } 

    if (Lightbox._hasPrev()) { Element.show('bottomNavPrev'); }	
    if (Lightbox._hasNext()) { Element.show('bottomNavNext'); }

	new Effect.Parallel(
	  [ new Effect.SlideDown('imageDataContainer', { sync: true, duration: Lightbox._resizeDuration }),
        new Effect.Appear('imageDataContainer', { sync: true, duration: Lightbox._resizeDuration }) ],
      { duration: Lightbox._resizeDuration, afterFinish: Lightbox._updateNav });
  },

  // Display appropriate previous and next hover navigation.
  _updateNav: function () {
    $('imageDataContainer').style.overflow = 'auto'; // css float fix

    Element.setHeight('overlay', getPageSize()[1]);

    Element.show('hoverNav');

    // if not first image in set, display prev image button
    if (Lightbox._hasPrev()) {
      document.getElementById('prevLink').onclick = Lightbox._prevImage;
      Element.show('prevLink');
      }

    // if not last image in set, display next image button
    if (Lightbox._hasNext()) {
      document.getElementById('nextLink').onclick = Lightbox._nextImage;
      Element.show('nextLink');
    }
   Lightbox._enableKeyboardNav();
  },

  _enableKeyboardNav: function () {
    document.onkeydown = Lightbox._keyboardAction;
  },

  _disableKeyboardNav: function () {
    document.onkeydown = '';
  },

  _keyboardAction: function (evnt) {
    var keycode = 0, escapeKey = 0, key = 0;
    if (evnt == null) { // ie
      keycode = event.keyCode;
      escapeKey = 27;
    }
    else { // mozilla
      keycode = evnt.keyCode;
      escapeKey = evnt.DOM_VK_ESCAPE;
    }

    key = String.fromCharCode(keycode).toLowerCase();
    if ((key == 'x') || (key == 'o') || (key == 'c') || (keycode == escapeKey)) { // close lightbox
      Lightbox._end();
      return true;
    } 
    else if((key == 'p') || (keycode == 37)) { // display previous image
      if (Lightbox._hasPrev()) {
        Lightbox._disableKeyboardNav();
        Lightbox._prevImage();
        return true;
      }
    } 
    else if((key == 'n') || (keycode == 39)) { // display next image
      if (Lightbox._hasNext()) {
        Lightbox._disableKeyboardNav();
        Lightbox._nextImage();
        return true;
      }
    }
  return false;
  },

  _preloadNeighborImages: function () {
    if (Lightbox._hasNext()) {
      Lightbox._preloadNextImage = new Image();
      Lightbox._preloadNextImage.src = Lightbox._imageArray[Lightbox._activeImage + 1][0];
    }
    if (Lightbox._hasPrev()) {
      Lightbox._preloadPrevImage = new Image();
      Lightbox._preloadPrevImage.src = Lightbox._imageArray[Lightbox._activeImage - 1][0];
    }
  },

  _borderSize: 10,
  _overlayDuration: 0.2,
  _overlayBlockDuration: 0.6,
  _resizeDuration: 0.4,
  _minWidth: 0,
  _imagePath: "/gnuMims/images/",
  _opacity: 0.6,
  _blockOpacity: 0.1,
  _animate: true,
  _text: "Image * of *",
  _activeImage: 0,
  _imageArray: []
}

// Returns array with x, y page scroll values.
// Core code from - quirksmode.org
function getPageScroll(){
  var xScroll = 0, yScroll = 0;
  if (self.pageYOffset) {
    xScroll = self.pageXOffset;
    yScroll = self.pageYOffset;
  }
  else if (document.documentElement && document.documentElement.scrollTop) {	// Explorer 6 Strict
    xScroll = document.documentElement.scrollLeft;
    yScroll = document.documentElement.scrollTop;
  } 
  else if (document.body) { // all other Explorers
    xScroll = document.body.scrollLeft;	
    yScroll = document.body.scrollTop;
  }
  return new Array(xScroll, yScroll)
}

// Returns array with page width, height and window width, height
// Core code from - quirksmode.org
// Edit for Firefox by pHaez
function getPageSize() {
  var xScroll = 0, yScroll = 0;
  var docBody = document.body;
  var docElem = document.documentElement;
  if (window.innerHeight && window.scrollMaxY) {
    xScroll = window.innerWidth + window.scrollMaxX;
    yScroll = window.innerHeight + window.scrollMaxY;
  }
  else if (docBody.scrollHeight > docBody.offsetHeight) { // all but Explorer Mac
    xScroll = docBody.scrollWidth;
    yScroll = docBody.scrollHeight;
  } 
  else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
    xScroll = docBody.offsetWidth;
    yScroll = docBody.offsetHeight;
  }

  var windowWidth = 0, windowHeight = 0, pageHeight = 0, pageWidth = 0;
  if (self.innerHeight) {	// all except Explorer
    if(docElem.clientWidth) {
      windowWidth = docElem.clientWidth; 
    }
    else {
      windowWidth = self.innerWidth;
    }
    windowHeight = self.innerHeight;
  }
  else if (docElem && docElem.clientHeight) { // Explorer 6 Strict Mode
    windowWidth = docElem.clientWidth;
    windowHeight = docElem.clientHeight;
  }
  else { // other Explorers
    windowWidth = docBody.clientWidth;
    windowHeight = docBody.clientHeight;
  }

  // for small pages with total height less then height of the viewport
  if (yScroll < windowHeight) {
    pageHeight = windowHeight;
  }	
  else {
    pageHeight = yScroll;
  }

  // for small pages with total width less then width of the viewport
  if (xScroll < windowWidth) {
    pageWidth = xScroll;
  }
  else {
    pageWidth = windowWidth;
  }
  return new Array(pageWidth, pageHeight, windowWidth, windowHeight)
}

function showSelectBoxes() {
  var selects = document.getElementsByTagName("select");
  for (var i = 0, l = selects.length; i < l; i++) {
    selects[i].style.visibility = "visible";
  }
}

function hideSelectBoxes() {
  var selects = document.getElementsByTagName("select");
  for (var i = 0, l = selects.length; i < l; i++) {
    selects[i].style.visibility = "hidden";
  }
}

function showObjects() {
  var objects = document.getElementsByTagName("object");
  for (var i = 0, l = objects.length; i < l; i++) {
    objects[i].style.visibility = "visible";
  }

  var embeds = document.getElementsByTagName("embed");
  for (var i = 0, l = embeds.length; i < l; i++) {
    embeds[i].style.visibility = "visible";
  }
}

function hideObjects() {
  var objects = document.getElementsByTagName("object");
  for (var i = 0, l = objects.length; i < l; i++) {
    objects[i].style.visibility = "hidden";
  }

  var embeds = document.getElementsByTagName("embed");
  for (var i = 0, l = embeds.length; i < l; i++) {
    embeds[i].style.visibility = "hidden";
  }
}

// pause(numberMillis)
// Pauses code execution for specified time. Uses busy code, not good.
// Code from http://www.faqts.com/knowledge_base/view.phtml/aid/1602
// Help from Ran Bar-On [ran2103@gmail.com]
function pause(ms) {
  var date = new Date();
  var curDate = null;
  do {
    curDate = new Date();
  } while (curDate - date < ms);
}
