text: module.exports = Navigation;
  
  function Navigation (events) {
    var self = this
      , currentSlideIndex = -1
      , started = null
      ;
  
    self.getCurrentSlideIndex = getCurrentSlideIndex;
    self.gotoSlide = gotoSlide;
    self.gotoPreviousSlide = gotoPreviousSlide;
    self.gotoNextSlide = gotoNextSlide;
    self.gotoFirstSlide = gotoFirstSlide;
    self.gotoLastSlide = gotoLastSlide;
    self.pause = pause;
    self.resume = resume;
  
    events.on('gotoSlide', gotoSlide);
    events.on('gotoPreviousSlide', gotoPreviousSlide);
    events.on('gotoNextSlide', gotoNextSlide);
    events.on('gotoFirstSlide', gotoFirstSlide);
    events.on('gotoLastSlide', gotoLastSlide);
  
    events.on('slidesChanged', function () {
      if (currentSlideIndex > self.getSlideCount()) {
        currentSlideIndex = self.getSlideCount();
      }
    });
  
    events.on('createClone', function () {
      if (!self.clone || self.clone.closed) {
        self.clone = window.open(location.href, self.getCloneTarget(), 'location=no');
      }
      else {
        self.clone.focus();
      }
    });
  
    events.on('resetTimer', function() {
      started = false;
    });
  
    function pause () {
      events.emit('pause');
    }
  
    function resume () {
      events.emit('resume');
    }
  
    function getCurrentSlideIndex () {
      return currentSlideIndex;
    }
  
    function gotoSlideByIndex(slideIndex, noMessage) {
      var alreadyOnSlide = slideIndex === currentSlideIndex
        , slideOutOfRange = slideIndex < 0 || slideIndex > self.getSlideCount()-1
        ;
  
      if (noMessage === undefined) noMessage = false;
  
      if (alreadyOnSlide || slideOutOfRange) {
        return;
      }
  
      if (currentSlideIndex !== -1) {
        events.emit('hideSlide', currentSlideIndex, false);
      }
  
      // Use some tri-state logic here.
      // null = We haven't shown the first slide yet.
      // false = We've shown the initial slide, but we haven't progressed beyond that.
      // true = We've issued the first slide change command.
      if (started === null) {
        started = false;
      } else if (started === false) {
        // We've shown the initial slide previously - that means this is a
        // genuine move to a new slide.
        events.emit('start');
        started = true;
      }
  
      events.emit('showSlide', slideIndex);
  
      currentSlideIndex = slideIndex;
  
      events.emit('slideChanged', slideIndex + 1);
  
      if (!noMessage) {
        if (self.clone && !self.clone.closed) {
          self.clone.postMessage('gotoSlide:' + (currentSlideIndex + 1), '*');
        }
  
        if (window.opener) {
          window.opener.postMessage('gotoSlide:' + (currentSlideIndex + 1), '*');
        }
      }
    }
  
    function gotoSlide (slideNoOrName, noMessage) {
      var slideIndex = getSlideIndex(slideNoOrName);
  
      gotoSlideByIndex(slideIndex, noMessage);
    }
  
    function gotoPreviousSlide() {
      gotoSlideByIndex(currentSlideIndex - 1);
    }
  
    function gotoNextSlide() {
      gotoSlideByIndex(currentSlideIndex + 1);
    }
  
    function gotoFirstSlide () {
      gotoSlideByIndex(0);
    }
  
    function gotoLastSlide () {
      gotoSlideByIndex(self.getSlideCount() - 1);
    }
  
    function getSlideIndex (slideNoOrName) {
      var slideNo
        , slide
        ;
  
      if (typeof slideNoOrName === 'number') {
        return slideNoOrName - 1;
      }
  
      slideNo = parseInt(slideNoOrName, 10);
      if (slideNo.toString() === slideNoOrName) {
        return slideNo - 1;
      }
  
      if(slideNoOrName.match(/^p\d+$/)){
        events.emit('forcePresenterMode');
        return parseInt(slideNoOrName.substr(1), 10)-1;
      }
  
      slide = self.getSlideByName(slideNoOrName);
      if (slide) {
        return slide.getSlideIndex();
      }
  
      return 0;
    }
  }
  