<commit_before>// Reminder: All external dependencies are globals
var $ = jQuery;
import Animation from './animation';
import PresenterView from './presenter';
import screenfull from 'screenfull';

/** @module Debut */

/**
 * The primary Debut object is responsible for handling the presentation.
 *
 * @constructor Debut
 */
var Debut = function Debut(element, options) {
  // Store jQuery objects for later reference
  this.$ = { };
  this.elements = { };

  this.options = $.extend({ }, Debut.defaultOptions, options);

  this.elements.container = element;
  this.$.container = $(this.elements.container);

  this.$.container.addClass('debut-container');
  this.$.container.attr('tabindex', '1');
  this.$.innerContainer = this.$.container.wrapInner('<div class="debut-container-inner">').children();
  this.$.innerContainer.css({ width: this.options.baseWidth, height: this.options.baseWidth / this.options.aspect });
  this.elements.innerContainer = this.$.innerContainer[0];

  this._presenterViewWindow = null;

  this.bounds = { };

  this.animationQueue = [ ];
  this.animationIndex = 0;

  this.milestones = [ ];

  if (this.options.full) {
    this.$.container.addClass('debut-full');
  }

  if (this.options.letterbox) {
    this.$.container.addClass('debut-letterbox');
  }

  this._addEventListeners();
  this.milestone('Start');
};

/**
 * Updates the width and height of the presentation based on the size of its container.
 * Called automatically on window resize. In other cases, you will need to call it yourself
 */
Debut.prototype.resize = function resize(event) {
  var bounds = this.bounds;
  bounds.width = this.$.innerContainer.width();
  bounds.height = this.$.innerContainer.height();
  bounds.outerWidth = this.$.container.width();
  bounds.outerHeight = this.$.container.height();
  bounds.aspect = bounds.outerWidth / bounds.outerHeight;

  if (this.options.letterbox) {
    bounds.visibleWidth = bounds.width;
    bounds.visibleHeight = bounds.height;
  } else {
    bounds.visibleWidth = bounds.outerWidth;
    bounds.visibleHeight = bounds.outerHeight;
  }

  if (bounds.aspect > this.options.aspect) {
    bounds.scale = bounds.outerHeight / (this.options.baseWidth / this.options.aspect);
    bounds.top = 0;
    bounds.left = (bounds.outerWidth - (this.options.baseWidth * bounds.scale)) / 2;
  } else {
    bounds.scale = bounds.outerWidth / this.options.baseWidth;
    bounds.top = (bounds.outerHeight - (this.options.baseWidth / this.options.aspect * bounds.scale)) / 2;
    bounds.left = 0;
  }

  this.$.innerContainer.css({ scale: bounds.scale, top: bounds.top, left: bounds.left });
};

/**
 * Listens to relevant events and binds callbacks
 * @private
 */
Debut.prototype._addEventListeners = function addEventListeners() {
  this.resize();
  $(window).on('resize', this.resize.bind(this));

  // First click lets the presentation gain focus, second lets you proceed
  this.$.container.click(function () {
    if (this._focusState === 1) {
      this._focusState = 0;
    } else if (this._focusState === 0) {
      this.next();
    }
  }.bind(this));

  this.$.container.on('focus', function () {
    this._focusState = 1;
  }.bind(this));

  this.$.container.on('blur', function () {
    this._focusState = 2;
  }.bind(this));

  $(window).on('beforeunload', function () {
    if (this._presenterViewWindow) {
      this._presenterViewWindow.close();
    }
  }.bind(this));

  if (this.options.keys) {
    var checkKeys = function (keys, e, fn) {
      var self = this;
      this.options.keys[keys].forEach(function (key) {
        if (e.which === key) {
          fn.call(self);
        }
      });
    }.bind(this);

    this.$.container.keyup(function (e) {
      checkKeys('next', e, this.next);
      checkKeys('prev', e, this.prev);
      checkKeys('presenter', e, this.openPresenterView.bind(this, this.options.presenterUrl));
      checkKeys('fullscreen', e, this.toggleFullscreen);
    }.bind(this));
  }
};

/**
 * Adds an animation to the animation queue
 */
Debut.prototype.step = function step(element, animation, options) {
  if (typeof element === 'string') {
    element = $(element);
  }

  if (typeof animation === 'string') {
    animation = Debut.animations[animation];
  }

  animation = new Animation(animation, $.extend({ }, { element: element }, options));

  // Group animations that are to be played together
  if (animation.start !== 'step') {
    if (this.animationQueue.length === 0) {
      throw new Error('First animation start type must be step');
    }

    var oldAnimation = this.animationQueue.pop();

    if (oldAnimation instanceof Array) {
      oldAnimation.push(animation);
      animation = oldAnimation;
    } else {
      animation = [ oldAnimation, animation ];
    }
  }

  this.animationQueue.push(animation);

  return this;
};

/**
 * Adds an animation to the animation queue that starts with the previous animation
 */
Debut.prototype.and = function and(element, animation, options) {
  options = $.extend({ start: 'with' }, options);

  return this.step(element, animation, options);
};

/**
 * Adds an animation to the queue that starts after the previous animation
 */
Debut.prototype.then = function then(element, animation, options) {
  options = $.extend({ start: 'after' }, options);

  return this.step(element, animation, options);
};

/**
 * Proceed to the next state of the presentation
 */
Debut.prototype.next = function next() {
  if (this.animationIndex < this.animationQueue.length) {
    this.proceed(1);
  }
};

/**
 * Proceed to the previous state of the presentation
 */
Debut.prototype.prev = function prev() {
  if (this.animationIndex > 0) {
    this.proceed(-1);
  }
};

/**
 * Go forwards or backwards in the presentation state
 *
 * @param {Number} direction - Forwards (1) or backwards (-1)?
 *
 * @private
 */<commit_msg>Add some boilerplate for DOM API<commit_after>// Reminder: All external dependencies are globals
var $ = jQuery;
import Animation from './animation';
import PresenterView from './presenter';
import screenfull from 'screenfull';

/** @module Debut */

/**
 * The primary Debut object is responsible for handling the presentation.
 *
 * @constructor Debut
 */
var Debut = function Debut(element, options) {
  // Store jQuery objects for later reference
  this.$ = { };
  this.elements = { };

  this.options = $.extend({ }, Debut.defaultOptions, options);

  this.elements.container = element;
  this.$.container = $(this.elements.container);

  this.$.container.addClass('debut-container');
  this.$.container.attr('tabindex', '1');
  this.$.innerContainer = this.$.container.wrapInner('<div class="debut-container-inner">').children();
  this.$.innerContainer.css({ width: this.options.baseWidth, height: this.options.baseWidth / this.options.aspect });
  this.elements.innerContainer = this.$.innerContainer[0];

  this._presenterViewWindow = null;

  this._lastDomElement = null;
  this._animationAdders = [ ];

  this.bounds = { };

  this.animationQueue = [ ];
  this.animationIndex = 0;

  this.milestones = [ ];

  if (this.options.full) {
    this.$.container.addClass('debut-full');
  }

  if (this.options.letterbox) {
    this.$.container.addClass('debut-letterbox');
  }

  this._addEventListeners();
  this.milestone('Start');
};

/**
 * Updates the width and height of the presentation based on the size of its container.
 * Called automatically on window resize. In other cases, you will need to call it yourself
 */
Debut.prototype.resize = function resize(event) {
  var bounds = this.bounds;
  bounds.width = this.$.innerContainer.width();
  bounds.height = this.$.innerContainer.height();
  bounds.outerWidth = this.$.container.width();
  bounds.outerHeight = this.$.container.height();
  bounds.aspect = bounds.outerWidth / bounds.outerHeight;

  if (this.options.letterbox) {
    bounds.visibleWidth = bounds.width;
    bounds.visibleHeight = bounds.height;
  } else {
    bounds.visibleWidth = bounds.outerWidth;
    bounds.visibleHeight = bounds.outerHeight;
  }

  if (bounds.aspect > this.options.aspect) {
    bounds.scale = bounds.outerHeight / (this.options.baseWidth / this.options.aspect);
    bounds.top = 0;
    bounds.left = (bounds.outerWidth - (this.options.baseWidth * bounds.scale)) / 2;
  } else {
    bounds.scale = bounds.outerWidth / this.options.baseWidth;
    bounds.top = (bounds.outerHeight - (this.options.baseWidth / this.options.aspect * bounds.scale)) / 2;
    bounds.left = 0;
  }

  this.$.innerContainer.css({ scale: bounds.scale, top: bounds.top, left: bounds.left });
};

/**
 * Listens to relevant events and binds callbacks
 * @private
 */
Debut.prototype._addEventListeners = function addEventListeners() {
  this.resize();
  $(window).on('resize', this.resize.bind(this));

  // First click lets the presentation gain focus, second lets you proceed
  this.$.container.click(function () {
    if (this._focusState === 1) {
      this._focusState = 0;
    } else if (this._focusState === 0) {
      this.next();
    }
  }.bind(this));

  this.$.container.on('focus', function () {
    this._focusState = 1;
  }.bind(this));

  this.$.container.on('blur', function () {
    this._focusState = 2;
  }.bind(this));

  $(window).on('beforeunload', function () {
    if (this._presenterViewWindow) {
      this._presenterViewWindow.close();
    }
  }.bind(this));

  if (this.options.keys) {
    var checkKeys = function (keys, e, fn) {
      var self = this;
      this.options.keys[keys].forEach(function (key) {
        if (e.which === key) {
          fn.call(self);
        }
      });
    }.bind(this);

    this.$.container.keyup(function (e) {
      checkKeys('next', e, this.next);
      checkKeys('prev', e, this.prev);
      checkKeys('presenter', e, this.openPresenterView.bind(this, this.options.presenterUrl));
      checkKeys('fullscreen', e, this.toggleFullscreen);
    }.bind(this));
  }
};

/**
 * Adds an animation to the animation queue
 */
Debut.prototype.step = function step(element, animation, options) {
  if (typeof element === 'string') {
    element = $(element);
  }

  if (typeof animation === 'string') {
    animation = Debut.animations[animation];
  }

  animation = new Animation(animation, $.extend({ }, { element: element }, options));

  // Group animations that are to be played together
  if (animation.start !== 'step') {
    if (this.animationQueue.length === 0) {
      throw new Error('First animation start type must be step');
    }

    var oldAnimation = this.animationQueue.pop();

    if (oldAnimation instanceof Array) {
      oldAnimation.push(animation);
      animation = oldAnimation;
    } else {
      animation = [ oldAnimation, animation ];
    }
  }

  this.animationQueue.push(animation);

  return this;
};

/**
 * Adds an animation to the animation queue that starts with the previous animation
 */
Debut.prototype.and = function and(element, animation, options) {
  options = $.extend({ start: 'with' }, options);

  return this.step(element, animation, options);
};

/**
 * Adds an animation to the queue that starts after the previous animation
 */
Debut.prototype.then = function then(element, animation, options) {
  options = $.extend({ start: 'after' }, options);

  return this.step(element, animation, options);
};

/**
 * Adds a hook for animations to be added for certain selectors in the DOM
 *
 * Does not work
 *
 * @param {String} selector - CSS selector to determine elements
 * @param {Function} hook - Function of the form hook(debut, element) to be called for valid elements
 * @param {Object} [options] - Extra options
 */
Debut.prototype.all = function all(selector, hook, options) {
  options = options || { };

  // TODO: implement functionality

  return this
};

/**
 * Proceed to the next state of the presentation
 */
Debut.prototype.next = function next() {
  if (this.animationIndex < this.animationQueue.length) {
    this.proceed(1);
  }
};

/**
 * Proceed to the previous state of the presentation
 */
Debut.prototype.prev = function prev() {
  if (this.animationIndex > 0) {
    this.proceed(-1);
  }
};

/**
 * Go forwards or backwards in the presentation state
 *
 * @param {Number} direction - Forwards (1) or backwards (-1)?
 *
 * @private
 */
{'fraction_non_alphanumeric': 0.14854154751205165, 'fraction_numerical': 0.002941416782416864, 'mean_word_length': 3.429967426710098, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1913806', 'n_tokens_mistral': 3738, 'n_tokens_neox': 3610, 'n_words': 933}