<commit_before>goog.provide('annotorious.modules.image.ImageModule');

goog.require('goog.dom');
goog.require('goog.array');
goog.require('goog.events');
goog.require('goog.structs.Map');

/**
 * The Image Module scans the page for images marked with the
 * 'annotatable' CSS class, and attaches an ImageAnnotator to
 * each one.
 * @constructor
 */
annotorious.modules.image.ImageModule = function() {   
  /** @private **/
  this._annotators = new goog.structs.Map();
  
  /** @private **/
  this._eventHandlers = [];

  /** @private **/
  this._plugins = [];
  
  /** @private **/
  this._allImages = [];
  
  /** @private **/
  this._imagesToLoad = [];
  
  /** @private **/
  this._bufferedForAdding = [];
  
  /** @private **/
  this._bufferedForRemoval = [];
}
  
/**
 * Standard module init() function.
 */
annotorious.modules.image.ImageModule.prototype.init = function() {
  // Query images marked with 'annotatable' CSS class
  var annotatableImages = goog.dom.query('img.annotatable', document);
  goog.array.extend(this._allImages, annotatableImages);
  goog.array.extend(this._imagesToLoad, annotatableImages);  

  // Make images in viewport annotatable
  this._lazyLoad();
  
  // Attach a listener to make images annotatable as they scroll into view
  var self = this;
  var key = goog.events.listen(window, goog.events.EventType.SCROLL, function() {
    if (self._imagesToLoad.length > 0)
      self._lazyLoad();
    else
      goog.events.unlistenByKey(key);
  });
}

/**
 * @private
 */
annotorious.modules.image.ImageModule.prototype._initPlugin = function(plugin, annotator) {
  if (plugin.onInitPopup)
    plugin.onInitPopup(annotator.getPopup());

  if (plugin.onInitEditor)  
    plugin.onInitEditor(annotator.getEditor());
}

/**
 * @private
 */
annotorious.modules.image.ImageModule.prototype._lazyLoad = function() {
  var self = this;
  goog.array.forEach(this._imagesToLoad, function(image) {
    if (annotorious.dom.isInViewport(image)) {
      var annotator = new annotorious.modules.image.ImageAnnotator(image);
      
      // Attach handlers that are already registered
      goog.array.forEach(self._eventHandlers, function(eventHandler) {
        annotator.addHandler(eventHandler.type, eventHandler.handler);
      });

      // Callback to registered plugins
      goog.array.forEach(self._plugins, function(plugin) {
        self._initPlugin(plugin, annotator);
      });
      
      // Cross-check with annotation add/remove buffers
      goog.array.forEach(self._bufferedForAdding, function(annotation) {
        if (annotation.src == image.src) {
          annotator.addAnnotation(annotation);
          goog.array.remove(self._bufferedForAdding, annotation);
        }
      });
      
      goog.array.forEach(self._bufferedForRemoval, function(annotation) {
        if (annotation.src == image.src) {
          annotator.removeAnnotation(annotation);
          goog.array.remove(self._bufferedForRemoval, annotation);
        }
      });
      
      // Update _annotators and _imagesToLoad lists
      self._annotators.set(image.src, annotator);
      goog.array.remove(self._imagesToLoad, image);
    }
  });  
}

/**
 * Standard module method: adds an annotation to the image with the specified src URL.
 * @param {Annotation} the annotation
 * @param {string} src the src URL of the image
 */<commit_msg>Lazy loading bugfix<commit_after>goog.provide('annotorious.modules.image.ImageModule');

goog.require('goog.dom');
goog.require('goog.array');
goog.require('goog.events');
goog.require('goog.structs.Map');

/**
 * The Image Module scans the page for images marked with the
 * 'annotatable' CSS class, and attaches an ImageAnnotator to
 * each one.
 * @constructor
 */
annotorious.modules.image.ImageModule = function() {   
  /** @private **/
  this._annotators = new goog.structs.Map();
  
  /** @private **/
  this._eventHandlers = [];

  /** @private **/
  this._plugins = [];
  
  /** @private **/
  this._allImages = [];
  
  /** @private **/
  this._imagesToLoad = [];
  
  /** @private **/
  this._bufferedForAdding = [];
  
  /** @private **/
  this._bufferedForRemoval = [];
}
  
/**
 * Standard module init() function.
 */
annotorious.modules.image.ImageModule.prototype.init = function() {
  // Query images marked with 'annotatable' CSS class
  var annotatableImages = goog.dom.query('img.annotatable', document);
  goog.array.extend(this._allImages, annotatableImages);
  goog.array.extend(this._imagesToLoad, annotatableImages);  

  // Make images in viewport annotatable
  this._lazyLoad();
  
  // Attach a listener to make images annotatable as they scroll into view
  var self = this;
  var key = goog.events.listen(window, goog.events.EventType.SCROLL, function() {
    if (self._imagesToLoad.length > 0)
      self._lazyLoad();
    else
      goog.events.unlistenByKey(key);
  });
}

/**
 * @private
 */
annotorious.modules.image.ImageModule.prototype._initPlugin = function(plugin, annotator) {
  if (plugin.onInitPopup)
    plugin.onInitPopup(annotator.getPopup());

  if (plugin.onInitEditor)  
    plugin.onInitEditor(annotator.getEditor());
}

/**
 * @private
 */
annotorious.modules.image.ImageModule.prototype._lazyLoad = function() {
  // Keep track of changes
  var loadedImages = [];
  var addedAnnotations = [];
  var removedAnnotations = [];
  
  var self = this;
  goog.array.forEach(this._imagesToLoad, function(image) {
    if (annotorious.dom.isInViewport(image)) {
      var annotator = new annotorious.modules.image.ImageAnnotator(image);
      
      // Attach handlers that are already registered
      goog.array.forEach(self._eventHandlers, function(eventHandler) {
        annotator.addHandler(eventHandler.type, eventHandler.handler);
      });

      // Callback to registered plugins
      goog.array.forEach(self._plugins, function(plugin) {
        self._initPlugin(plugin, annotator);
      });
      
      // Cross-check with annotation add/remove buffers
      goog.array.forEach(self._bufferedForAdding, function(annotation) {
        if (annotation.src == image.src) {
          annotator.addAnnotation(annotation);
          addedAnnotations.push(annotation);
        }
      });
      
      goog.array.forEach(self._bufferedForRemoval, function(annotation) {
        if (annotation.src == image.src) {
          annotator.removeAnnotation(annotation);
          removedAnnotations.push(annotation);
        }
      });
  
      // Update _annotators and _imagesToLoad lists
      self._annotators.set(image.src, annotator);
      loadedImages.push(image);
    }
  });

  // Apply changes
  goog.array.forEach(addedAnnotations, function(annotation) {
    goog.array.remove(self._bufferedForAdding, annotation);
  });
  
  goog.array.forEach(removedAnnotations, function(annotation) {
    goog.array.remove(self._bufferedForRemoval, annotation);
  });
  
  goog.array.forEach(loadedImages, function(image) {
    goog.array.remove(self._imagesToLoad, image);
  });
}

/**
 * Standard module method: adds an annotation to the image with the specified src URL.
 * @param {Annotation} the annotation
 * @param {string} src the src URL of the image
 */
{'fraction_non_alphanumeric': 0.13439508242525844, 'fraction_numerical': 0.0002794076557697681, 'mean_word_length': 3.2740298507462686, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, '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': '2994671', 'n_tokens_mistral': 2188, 'n_tokens_neox': 2106, 'n_words': 417}