text: goog.provide('webfont.FontWatcher');
  
  goog.require('webfont.FontWatchRunner');
  goog.require('webfont.NativeFontWatchRunner');
  
  /**
   * @typedef {Object.<string, Array.<string>>}
   */
  webfont.FontTestStrings;
  
  /**
   * @constructor
   * @param {webfont.DomHelper} domHelper
   * @param {webfont.EventDispatcher} eventDispatcher
   * @param {number=} opt_timeout
   */
  webfont.FontWatcher = function(domHelper, eventDispatcher, opt_timeout) {
    this.domHelper_ = domHelper;
    this.eventDispatcher_ = eventDispatcher;
    this.currentlyWatched_ = 0;
    this.last_ = false;
    this.success_ = false;
    this.timeout_ = opt_timeout;
  };
  
  goog.scope(function () {
    var FontWatcher = webfont.FontWatcher,
        FontWatchRunner = webfont.FontWatchRunner,
        NativeFontWatchRunner = webfont.NativeFontWatchRunner;
  
    /**
     * @type {null|boolean}
     */
    FontWatcher.SHOULD_USE_NATIVE_LOADER = null;
  
    /**
     * @return {string}
     */
    FontWatcher.getUserAgent = function () {
      return window.navigator.userAgent;
    };
  
    /**
     * @return {string}
     */
    FontWatcher.getVendor = function () {
      return window.navigator.vendor;
    };
  
    /**
     * Returns true if this browser has support for
     * the CSS font loading API.
     *
     * @return {boolean}
     */
    FontWatcher.shouldUseNativeLoader = function () {
      if (FontWatcher.SHOULD_USE_NATIVE_LOADER === null) {
        if (!!window.FontFace) {
          var match = /Gecko.*Firefox\/(\d+)/.exec(FontWatcher.getUserAgent());
          var safari10Match = /OS X.*Version\/10\..*Safari/.exec(FontWatcher.getUserAgent()) && /Apple/.exec(FontWatcher.getVendor());
  
          if (match) {
            FontWatcher.SHOULD_USE_NATIVE_LOADER = parseInt(match[1], 10) > 42;
          } else if (safari10Match) {
            FontWatcher.SHOULD_USE_NATIVE_LOADER = false;
          } else {
            FontWatcher.SHOULD_USE_NATIVE_LOADER = true;
          }
        } else {
          FontWatcher.SHOULD_USE_NATIVE_LOADER = false;
        }
      }
      return FontWatcher.SHOULD_USE_NATIVE_LOADER;
    };
  
    /**
     * Watches a set of font families.
     * @param {Array.<webfont.Font>} fonts The fonts to watch.
     * @param {webfont.FontTestStrings} fontTestStrings The font test strings for
     *     each family.
     * @param {Object.<String, boolean>} metricCompatibleFonts
     * @param {boolean} last True if this is the last set of fonts to watch.
     */
    FontWatcher.prototype.watchFonts = function(fonts,
        fontTestStrings, metricCompatibleFonts, last) {
      var length = fonts.length,
          testStrings = fontTestStrings || {};
  
      if (length === 0 && last) {
        this.eventDispatcher_.dispatchInactive();
        return;
      }
  
      this.currentlyWatched_ += fonts.length;
  
      if (last) {
        this.last_ = last;
      }
  
      var i, fontWatchRunners = [];
      for (i = 0; i < fonts.length; i++) {
        var font = fonts[i],
            testString = testStrings[font.getName()];
  
        this.eventDispatcher_.dispatchFontLoading(font);
  
        var fontWatchRunner = null;
  
        if (FontWatcher.shouldUseNativeLoader()) {
          fontWatchRunner = new NativeFontWatchRunner(
              goog.bind(this.fontActive_, this),
              goog.bind(this.fontInactive_, this),
              this.domHelper_,
              font,
              this.timeout_,
              testString
            );
        } else {
          fontWatchRunner = new FontWatchRunner(
            goog.bind(this.fontActive_, this),
            goog.bind(this.fontInactive_, this),
            this.domHelper_,
            font,
            this.timeout_,
            metricCompatibleFonts,
            testString
          );
        }
  
        fontWatchRunners.push(fontWatchRunner);
      }
  
      for (i = 0; i < fontWatchRunners.length; i++) {
        fontWatchRunners[i].start();
      }
    };
  
    /**
     * Called by a FontWatchRunner when a font has been detected as active.
     * @param {webfont.Font} font
     * @private
     */
    FontWatcher.prototype.fontActive_ = function(font) {
      this.eventDispatcher_.dispatchFontActive(font);
      this.success_ = true;
      this.decreaseCurrentlyWatched_();
    };
  
    /**
     * Called by a FontWatchRunner when a font has been detected as inactive.
     * @param {webfont.Font} font
     * @private
     */
    FontWatcher.prototype.fontInactive_ = function(font) {
      this.eventDispatcher_.dispatchFontInactive(font);
      this.decreaseCurrentlyWatched_();
    };
  
    /**
     * @private
     */
    FontWatcher.prototype.decreaseCurrentlyWatched_ = function() {
      if (--this.currentlyWatched_ == 0 && this.last_) {
        if (this.success_) {
          this.eventDispatcher_.dispatchActive();
        } else {
          this.eventDispatcher_.dispatchInactive();
        }
      }
    };
  });
  