elementResizeDetector = function(options) {
    options = options || {};

    //idHandler is currently not an option to the listenTo function, so it should not be added to globalOptions.
    var idHandler$$1;

    if (options.idHandler) {
      // To maintain compatability with idHandler.get(element, readonly), make sure to wrap the given idHandler
      // so that readonly flag always is true when it's used here. This may be removed next major version bump.
      idHandler$$1 = {
        get: function(element) {
          return options.idHandler.get(element, true);
        },
        set: options.idHandler.set,
      };
    } else {
      var idGenerator$$1 = idGenerator();
      var defaultIdHandler = idHandler({
        idGenerator: idGenerator$$1,
        stateHandler: stateHandler,
      });
      idHandler$$1 = defaultIdHandler;
    }

    //reporter is currently not an option to the listenTo function, so it should not be added to globalOptions.
    var reporter$$1 = options.reporter;

    if (!reporter$$1) {
      //If options.reporter is false, then the reporter should be quiet.
      var quiet = reporter$$1 === false;
      reporter$$1 = reporter(quiet);
    }

    //batchProcessor is currently not an option to the listenTo function, so it should not be added to globalOptions.
    var batchProcessor$$1 = getOption(options, 'batchProcessor', batchProcessor({ reporter: reporter$$1 }));

    //Options to be used as default for the listenTo function.
    var globalOptions = {};
    globalOptions.callOnAdd = !!getOption(options, 'callOnAdd', true);
    globalOptions.debug = !!getOption(options, 'debug', false);

    var eventListenerHandler = listenerHandler(idHandler$$1);
    var elementUtils$$1 = elementUtils({
      stateHandler: stateHandler,
    });

    //The detection strategy to be used.
    var detectionStrategy;
    var desiredStrategy = getOption(options, 'strategy', 'object');
    var strategyOptions = {
      reporter: reporter$$1,
      batchProcessor: batchProcessor$$1,
      stateHandler: stateHandler,
      idHandler: idHandler$$1,
    };

    if (desiredStrategy === 'scroll') {
      if (browserDetector.isLegacyOpera()) {
        reporter$$1.warn('Scroll strategy is not supported on legacy Opera. Changing to object strategy.');
        desiredStrategy = 'object';
      } else if (browserDetector.isIE(9)) {
        reporter$$1.warn('Scroll strategy is not supported on IE9. Changing to object strategy.');
        desiredStrategy = 'object';
      }
    }

    if (desiredStrategy === 'scroll') {
      detectionStrategy = scroll(strategyOptions);
    } else if (desiredStrategy === 'object') {
      detectionStrategy = object(strategyOptions);
    } else {
      throw new Error('Invalid strategy name: ' + desiredStrategy);
    }

    //Calls can be made to listenTo with elements that are still being installed.
    //Also, same elements can occur in the elements list in the listenTo function.
    //With this map, the ready callbacks can be synchronized between the calls
    //so that the ready callback can always be called when an element is ready - even if
    //it wasn't installed from the function itself.
    var onReadyCallbacks = {};

    /**
     * Makes the given elements resize-detectable and starts listening to resize events on the elements. Calls the event callback for each event for each element.
     * @public
     * @param {Options?} options Optional options object. These options will override the global options. Some options may not be overriden, such as idHandler.
     * @param {element[]|element} elements The given array of elements to detect resize events of. Single element is also valid.
     * @param {function} listener The callback to be executed for each resize event for each element.
     */
    function listenTo(options, elements, listener) {
      function onResizeCallback(element) {
        var listeners = eventListenerHandler.get(element);
        forEach$1(listeners, function callListenerProxy(listener) {
          listener(element);
        });
      }

      function addListener(callOnAdd, element, listener) {
        eventListenerHandler.add(element, listener);

        if (callOnAdd) {
          listener(element);
        }
      }

      //Options object may be omitted.
      if (!listener) {
        listener = elements;
        elements = options;
        options = {};
      }

      if (!elements) {
        throw new Error('At least one element required.');
      }

      if (!listener) {
        throw new Error('Listener required.');
      }

      if (isElement(elements)) {
        // A single element has been passed in.
        elements = [elements];
      } else if (isCollection(elements)) {
        // Convert collection to array for plugins.
        // TODO: May want to check so that all the elements in the collection are valid elements.
        elements = toArray(elements);
      } else {
        return reporter$$1.error('Invalid arguments. Must be a DOM element or a collection of DOM elements.');
      }

      var elementsReady = 0;

      var callOnAdd = getOption(options, 'callOnAdd', globalOptions.callOnAdd);
      var onReadyCallback = getOption(options, 'onReady', function noop() {});
      var debug = getOption(options, 'debug', globalOptions.debug);

      forEach$1(elements, function attachListenerToElement(element) {
        if (!stateHandler.getState(element)) {
          stateHandler.initState(element);
          idHandler$$1.set(element);
        }

        var id = idHandler$$1.get(element);

        debug && reporter$$1.log('Attaching listener to element', id, element);

        if (!elementUtils$$1.isDetectable(element)) {
          debug && reporter$$1.log(id, 'Not detectable.');
          if (elementUtils$$1.isBusy(element)) {
            debug && reporter$$1.log(id, 'System busy making it detectable');

            //The element is being prepared to be detectable. Do not make it detectable.
            //Just add the listener, because the element will soon be detectable.
            addListener(callOnAdd, element, listener);
            onReadyCallbacks[id] = onReadyCallbacks[id] || [];
            onReadyCallbacks[id].push(function onReady() {
              elementsReady++;

              if (elementsReady === elements.length) {
                onReadyCallback();
              }
            });
            return;
          }

          debug && reporter$$1.log(id, 'Making detectable...');
          //The element is not prepared to be detectable, so do prepare it and add a listener to it.
          elementUtils$$1.markBusy(element, true);
          return detectionStrategy.makeDetectable({ debug: debug }, element, function onElementDetectable(element) {
            debug && reporter$$1.log(id, 'onElementDetectable');

            if (stateHandler.getState(element)) {
              elementUtils$$1.markAsDetectable(element);
              elementUtils$$1.markBusy(element, false);
              detectionStrategy.addListener(element, onResizeCallback);
              addListener(callOnAdd, element, listener);

              // Since the element size might have changed since the call to "listenTo", we need to check for this change,
              // so that a resize event may be emitted.
              // Having the startSize object is optional (since it does not make sense in some cases such as unrendered elements), so check for its existance before.
              // Also, check the state existance before since the element may have been uninstalled in the installation process.
              var state = stateHandler.getState(element);
              if (state && state.startSize) {
                var width = element.offsetWidth;
                var height = element.offsetHeight;
                if (state.startSize.width !== width || state.startSize.height !== height) {
                  onResizeCallback(element);
                }
              }

              if (onReadyCallbacks[id]) {
                forEach$1(onReadyCallbacks[id], function(callback) {
                  callback();
                });
              }
            } else {
              // The element has been unisntalled before being detectable.
              debug && reporter$$1.log(id, 'Element uninstalled before being detectable.');
            }

            delete onReadyCallbacks[id];

            elementsReady++;
            if (elementsReady === elements.length) {
              onReadyCallback();
            }
          });
        }

        debug && reporter$$1.log(id, 'Already detecable, adding listener.');

        //The element has been prepared to be detectable and is ready to be listened to.
        addListener(callOnAdd, element, listener);
        elementsReady++;
      });

      if (elementsReady === elements.length) {
        onReadyCallback();
      }
    }

    function uninstall(elements) {
      if (!elements) {
        return reporter$$1.error('At least one element is required.');
      }

      if (isElement(elements)) {
        // A single element has been passed in.
        elements = [elements];
      } else if (isCollection(elements)) {
        // Convert collection to array for plugins.
        // TODO: May want to check so that all the elements in the collection are valid elements.
        elements = toArray(elements);
      } else {
        return reporter$$1.error('Invalid arguments. Must be a DOM element or a collection of DOM elements.');
      }

      forEach$1(elements, function(element) {
        eventListenerHandler.removeAllListeners(element);
        detectionStrategy.uninstall(element);
        stateHandler.cleanState(element);
      });
    }

    return {
      listenTo: listenTo,
      removeListener: eventListenerHandler.removeListener,
      removeAllListeners: eventListenerHandler.removeAllListeners,
      uninstall: uninstall,
    };
  }
Creates an element resize detector instance.
@public
@param {Options?} options Optional global options object that will decide how this instance will work.