text: /*
   * Copyright 2012 Small Batch, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License"); you may not
   * use this file except in compliance with the License. You may obtain a copy of
   * the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   * License for the specific language governing permissions and limitations under
   * the License.
   */
  var webfont = {};
  
  /**
   * @param {Object} context
   * @param {function(...)} func
   * @param {...*} opt_args
   */
  webfont.bind = function(context, func, opt_args) {
    var args = arguments.length > 2 ?
        Array.prototype.slice.call(arguments, 2) : [];
  
    return function() {
      args.push.apply(args, arguments);
      return func.apply(context, args);
    };
  };
  
  webfont.extendsClass = function(baseClass, subClass) {
  
    // Avoid polluting the baseClass prototype object with methods from the
    // subClass
    /** @constructor */
    function baseExtendClass() {};
    baseExtendClass.prototype = baseClass.prototype;
    subClass.prototype = new baseExtendClass();
  
    subClass.prototype.constructor = subClass;
    subClass.superCtor_ = baseClass;
    subClass.super_ = baseClass.prototype;
  };
  
  /**
   * Handles common DOM manipulation tasks. The aim of this library is to cover
   * the needs of typical font loading. Not more, not less.
   * @param {HTMLDocument} doc The HTML document we'll manipulate.
   * @constructor
   */
  webfont.DomHelper = function(doc) {
    this.document_ = doc;
  
    /** @type {boolean|undefined} */
    this.supportForStyle_ = undefined;
  };
  
  /**
   * Creates an element.
   * @param {string} elem The element type.
   * @param {Object=} opt_attr A hash of attribute key/value pairs.
   * @param {string=} opt_innerHtml Contents of the element.
   * @return {Element} the new element.
   */
  webfont.DomHelper.prototype.createElement = function(elem, opt_attr,
      opt_innerHtml) {
    var domElement = this.document_.createElement(elem);
  
    if (opt_attr) {
      for (var attr in opt_attr) {
        // protect against native prototype augmentations
        if (opt_attr.hasOwnProperty(attr)) {
          if (attr == "style") {
            this.setStyle(domElement, opt_attr[attr]);
  	} else {
            domElement.setAttribute(attr, opt_attr[attr]);
          }
        }
      }
    }
    if (opt_innerHtml) {
      domElement.appendChild(this.document_.createTextNode(opt_innerHtml));
    }
    return domElement;
  };
  
  /**
   * Inserts an element into the document. This is intended for unambiguous
   * elements such as html, body, head.
   * @param {string} tagName The element name.
   * @param {Element} e The element to append.
   * @return {boolean} True if the element was inserted.
   */
  webfont.DomHelper.prototype.insertInto = function(tagName, e) {
    var t = this.document_.getElementsByTagName(tagName)[0];
  
    if (!t) { // opera allows documents without a head
      t = document.documentElement;
    }
  
    if (t && t.lastChild) {
      // This is safer than appendChild in IE. appendChild causes random
      // JS errors in IE. Sometimes errors in other JS exectution, sometimes
      // complete 'This page cannot be displayed' errors. For our purposes,
      // it's equivalent because we don't need to insert at any specific
      // location.
      t.insertBefore(e, t.lastChild);
      return true;
    }
    return false;
  };
  
  /**
   * Calls a function when the body tag exists.
   * @param {function()} callback The function to call.
   */
  webfont.DomHelper.prototype.whenBodyExists = function(callback) {
    var check = function() {
      if (document.body) {
        callback();
      } else {
        setTimeout(check, 0);
      }
    }
    check();
  };
  
  /**
   * Removes an element from the DOM.
   * @param {Element} node The element to remove.
   * @return {boolean} True if the element was removed.
   */
  webfont.DomHelper.prototype.removeElement = function(node) {
    if (node.parentNode) {
      node.parentNode.removeChild(node);
      return true;
    }
    return false;
  };
  
  /**
   * Creates a link to a CSS document.
   * @param {string} src The URL of the stylesheet.
   * @return {Element} a link element.
   */
  webfont.DomHelper.prototype.createCssLink = function(src) {
    return this.createElement('link', {
      'rel': 'stylesheet',
      'href': src
    });
  };
  
  /**
   * Creates a link to a javascript document.
   * @param {string} src The URL of the script.
   * @return {Element} a script element.
   */
  webfont.DomHelper.prototype.createScriptSrc = function(src) {
    return this.createElement('script', {
      'src': src
    });
  };
  
  /**
   * Appends a name to an element's class attribute.
   * @param {Element} e The element.
   * @param {string} name The class name to add.
   */
  webfont.DomHelper.prototype.appendClassName = function(e, name) {
    var classes = e.className.split(/\s+/);
    for (var i = 0, len = classes.length; i < len; i++) {
      if (classes[i] == name) {
        return;
      }
    }
    classes.push(name);
    e.className = classes.join(' ').replace(/^\s+/, '');
  };
  
  /**
   * Removes a name to an element's class attribute.
   * @param {Element} e The element.
   * @param {string} name The class name to remove.
   */
  webfont.DomHelper.prototype.removeClassName = function(e, name) {
    var classes = e.className.split(/\s+/);
    var remainingClasses = [];
    for (var i = 0, len = classes.length; i < len; i++) {
      if (classes[i] != name) {
        remainingClasses.push(classes[i]);
      }
    }
    e.className = remainingClasses.join(' ').replace(/^\s+/, '')
        .replace(/\s+$/, '');
  };
  
  /**
   * Returns true if an element has a given class name and false otherwise.
   * @param {Element} e The element.
   * @param {string} name The class name to check for.
   * @return {boolean} Whether or not the element has this class name.
   */
  webfont.DomHelper.prototype.hasClassName = function(e, name) {
    var classes = e.className.split(/\s+/);
    for (var i = 0, len = classes.length; i < len; i++) {
      if (classes[i] == name) {
        return true;
      }
    }
    return false;
  };
  
  /**
   * Sets the style attribute on an element.
   * @param {Element} e The element.
   * @param {string} styleString The style string.
   */
  webfont.DomHelper.prototype.setStyle = function(e, styleString) {
    if (this.hasSupportForStyle_()) {
      e.setAttribute("style", styleString);
    } else {
      e.style.cssText = styleString;
    }
  };
  
  /**
   * Check if getting and setting the style attribute on an element with
   * getAttribute/setAttribute is supported. In old IE, you must use style.cssText
   * instead. Feature detection is only done the first time this is called.
   * @private
   * @return {boolean} Whether or not the feature is supported.
   */
  webfont.DomHelper.prototype.hasSupportForStyle_ = function() {
    if (this.supportForStyle_ === undefined) {
      var e = this.document_.createElement('p');
      e.innerHTML = '<a style="top:1px;">w</a>';
      this.supportForStyle_ = /top/.test(e.getElementsByTagName('a')[0].getAttribute('style'));
    }
    return this.supportForStyle_
  };
  
  /**
   * @param {string} name
   * @param {string} version
   * @param {string} engine
   * @param {string} engineVersion
   * @param {string} platform
   * @param {string} platformVersion
   * @param {number|undefined} documentMode
   * @param {boolean} webFontSupport
   * @constructor
   */
  webfont.UserAgent = function(name, version, engine, engineVersion, platform,
      platformVersion, documentMode, webFontSupport) {
    this.name_ = name;
    this.version_ = version;
    this.engine_ = engine;
    this.engineVersion_ = engineVersion;
    this.platform_ = platform;
    this.platformVersion_ = platformVersion;
    this.documentMode_ = documentMode;
    this.webFontSupport_ = webFontSupport;
  };
  
  /**
   * @return {string}
   */
  webfont.UserAgent.prototype.getName = function() {
    return this.name_;
  };
  
  /**
   * @return {string}
   */
  webfont.UserAgent.prototype.getVersion = function() {
    return this.version_;
  };
  
  /**
   * @return {string}
   */
  webfont.UserAgent.prototype.getEngine = function() {
    return this.engine_;
  };
  
  /**
   * @return {string}
   */
  webfont.UserAgent.prototype.getEngineVersion = function() {
    return this.engineVersion_;
  };
  
  /**
   * @return {string}
   */
  webfont.UserAgent.prototype.getPlatform = function() {
    return this.platform_;
  };
  
  /**
   * @return {string}
   */
  webfont.UserAgent.prototype.getPlatformVersion = function() {
    return this.platformVersion_;
  };
  
  /**
   * @return {number|undefined}
   */
  webfont.UserAgent.prototype.getDocumentMode = function() {
    return this.documentMode_;
  };
  
  /**
   * @return {boolean}
   */
  webfont.UserAgent.prototype.isSupportingWebFont = function() {
    return this.webFontSupport_;
  };
  
  /**
   * @param {string} userAgent The browser userAgent string to parse.
   * @constructor
   */
  webfont.UserAgentParser = function(userAgent, doc) {
    this.userAgent_ = userAgent;
    this.doc_ = doc;
  };
  
  /**
   * @const
   * @type {string}
   */
  webfont.UserAgentParser.UNKNOWN = "Unknown";
  
  /**
   * @const
   * @type {webfont.UserAgent}
   */
  webfont.UserAgentParser.UNKNOWN_USER_AGENT = new webfont.UserAgent(
      webfont.UserAgentParser.UNKNOWN,
      webfont.UserAgentParser.UNKNOWN,
      webfont.UserAgentParser.UNKNOWN,
      webfont.UserAgentParser.UNKNOWN,
      webfont.UserAgentParser.UNKNOWN,
      webfont.UserAgentParser.UNKNOWN,
      undefined,
      false);
  
  /**
   * Parses the user agent string and returns an object.
   * @return {webfont.UserAgent}
   */
  webfont.UserAgentParser.prototype.parse = function() {
    if (this.isIe_()) {
      return this.parseIeUserAgentString_();
    } else if (this.isOpera_()) {
      return this.parseOperaUserAgentString_();
    } else if (this.isWebKit_()) {
      return this.parseWebKitUserAgentString_();
    } else if (this.isGecko_()) {
      return this.parseGeckoUserAgentString_();
    } else {
      return webfont.UserAgentParser.UNKNOWN_USER_AGENT;
    }
  };
  
  /**
   * @private
   */
  webfont.UserAgentParser.prototype.getPlatform_ = function() {
    var mobileOs = this.getMatchingGroup_(this.userAgent_,
        /(iPod|iPad|iPhone|Android)/, 1);
  
    if (mobileOs != "") {
      return mobileOs;
    }
    var os = this.getMatchingGroup_(this.userAgent_,
        /(Linux|Mac_PowerPC|Macintosh|Windows|CrOS)/, 1);
  
    if (os != "") {
      if (os == "Mac_PowerPC") {
        os = "Macintosh";
      }
      return os;
    }
    return webfont.UserAgentParser.UNKNOWN;
  };
  
  /**
   * @private
   */
  webfont.UserAgentParser.prototype.getPlatformVersion_ = function() {
    var genericVersion = this.getMatchingGroup_(this.userAgent_,
        /(OS X|Windows NT|Android|CrOS) ([^;)]+)/, 2);
    if (genericVersion) {
      return genericVersion;
    }
    var iVersion = this.getMatchingGroup_(this.userAgent_,
        /(iPhone )?OS ([\d_]+)/, 2);
    if (iVersion) {
      return iVersion;
    }
    var linuxVersion = this.getMatchingGroup_(this.userAgent_,
        /Linux ([i\d]+)/, 1);
    if (linuxVersion) {
      return linuxVersion;
    }
  
    return webfont.UserAgentParser.UNKNOWN;
  };
  
  /**
   * @private
   */
  webfont.UserAgentParser.prototype.isIe_ = function() {
    return this.userAgent_.indexOf("MSIE") != -1;
  };
  
  /**
   * @private
   */
  webfont.UserAgentParser.prototype.parseIeUserAgentString_ = function() {
    var browser = this.getMatchingGroup_(this.userAgent_, /(MSIE [\d\w\.]+)/, 1);
    var engineName = webfont.UserAgentParser.UNKNOWN;
    var engineVersion = webfont.UserAgentParser.UNKNOWN;
  
    if (browser != "") {
      var pair = browser.split(' ');
      var name = pair[0];
      var version = pair[1];
  
      // For IE we give MSIE as the engine name and the version of IE
      // instead of the specific Trident engine name and version
      return new webfont.UserAgent(name, version, name, version,
          this.getPlatform_(), this.getPlatformVersion_(),
          this.getDocumentMode_(this.doc_), this.getMajorVersion_(version) >= 6);
    }
    return new webfont.UserAgent("MSIE", webfont.UserAgentParser.UNKNOWN,
        "MSIE", webfont.UserAgentParser.UNKNOWN,
        this.getPlatform_(), this.getPlatformVersion_(),
        this.getDocumentMode_(this.doc_), false);
  };
  
  /**
   * @private
   */
  webfont.UserAgentParser.prototype.isOpera_ = function() {
    return this.userAgent_.indexOf("Opera") != -1;
  };
  
  /**
   * @private
   */
  webfont.UserAgentParser.prototype.parseOperaUserAgentString_ = function() {
    var engineName = webfont.UserAgentParser.UNKNOWN;
    var engineVersion = webfont.UserAgentParser.UNKNOWN;
    var enginePair = this.getMatchingGroup_(this.userAgent_,
        /(Presto\/[\d\w\.]+)/, 1);
  
    if (enginePair != "") {
      var splittedEnginePair = enginePair.split('/');
  
      engineName = splittedEnginePair[0];
      engineVersion = splittedEnginePair[1];
    } else {
      if (this.userAgent_.indexOf("Gecko") != -1) {
        engineName = "Gecko";
      }
      var geckoVersion = this.getMatchingGroup_(this.userAgent_, /rv:([^\)]+)/, 1);
  
      if (geckoVersion != "") {
        engineVersion = geckoVersion;
      }
    }
  
    // Check for Opera Mini first, since it looks like normal Opera
    if (this.userAgent_.indexOf("Opera Mini/") != -1) {
      var version = this.getMatchingGroup_(this.userAgent_, /Opera Mini\/([\d\.]+)/, 1);
  
      if (version == "") {
        version = webfont.UserAgentParser.UNKNOWN;
      }
  
      return new webfont.UserAgent("OperaMini", version, engineName,
          engineVersion, this.getPlatform_(), this.getPlatformVersion_(),
          this.getDocumentMode_(this.doc_), false);
    }
  
    // Otherwise, find version information for normal Opera or Opera Mobile
    if (this.userAgent_.indexOf("Version/") != -1) {
      var version = this.getMatchingGroup_(this.userAgent_, /Version\/([\d\.]+)/, 1);
  
      if (version != "") {
        return new webfont.UserAgent("Opera", version, engineName, engineVersion,
            this.getPlatform_(), this.getPlatformVersion_(),
            this.getDocumentMode_(this.doc_), this.getMajorVersion_(version) >= 10);
      }
    }
    var version = this.getMatchingGroup_(this.userAgent_, /Opera[\/ ]([\d\.]+)/, 1);
  
    if (version != "") {
      return new webfont.UserAgent("Opera", version, engineName, engineVersion,
          this.getPlatform_(), this.getPlatformVersion_(),
          this.getDocumentMode_(this.doc_), this.getMajorVersion_(version) >= 10);
    }
    return new webfont.UserAgent("Opera", webfont.UserAgentParser.UNKNOWN,
        engineName, engineVersion, this.getPlatform_(),
        this.getPlatformVersion_(), this.getDocumentMode_(this.doc_), false);
  };
  
  /**
   * @private
   */
  webfont.UserAgentParser.prototype.isWebKit_ = function() {
    return this.userAgent_.indexOf("AppleWebKit") != -1;
  };
  
  /**
   * @private
   */
  webfont.UserAgentParser.prototype.parseWebKitUserAgentString_ = function() {
    var platform = this.getPlatform_();
    var platformVersion = this.getPlatformVersion_();
    var webKitVersion = this.getMatchingGroup_(this.userAgent_,
        /AppleWebKit\/([\d\.\+]+)/, 1);
  
    if (webKitVersion == "") {
      webKitVersion = webfont.UserAgentParser.UNKNOWN;
    }
    var name = webfont.UserAgentParser.UNKNOWN;
  
    if (this.userAgent_.indexOf("Chrome") != -1 || this.userAgent_.indexOf("CrMo") != -1 || this.userAgent_.indexOf("CriOS") != -1) {
      name = "Chrome";
    } else if (this.userAgent_.indexOf("Safari") != -1) {
      name = "Safari";
    } else if (this.userAgent_.indexOf("AdobeAIR") != -1) {
      name = "AdobeAIR";
    }
    var version = webfont.UserAgentParser.UNKNOWN;
  
    if (this.userAgent_.indexOf("Version/") != -1) {
      version = this.getMatchingGroup_(this.userAgent_,
          /Version\/([\d\.\w]+)/, 1);
    } else if (name == "Chrome") {
      version = this.getMatchingGroup_(this.userAgent_,
          /(Chrome|CrMo|CriOS)\/([\d\.]+)/, 2);
    } else if (name == "AdobeAIR") {
      version = this.getMatchingGroup_(this.userAgent_,
          /AdobeAIR\/([\d\.]+)/, 1);
    }
    var supportWebFont = false;
    if (name == "AdobeAIR") {
      var minor = this.getMatchingGroup_(version, /\d+\.(\d+)/, 1);
      supportWebFont = this.getMajorVersion_(version) > 2 ||
        this.getMajorVersion_(version) == 2 && parseInt(minor, 10) >= 5;
    } else {
      var minor = this.getMatchingGroup_(webKitVersion, /\d+\.(\d+)/, 1);
      supportWebFont = this.getMajorVersion_(webKitVersion) >= 526 ||
        this.getMajorVersion_(webKitVersion) >= 525 && parseInt(minor, 10) >= 13;
    }
  
    return new webfont.UserAgent(name, version, "AppleWebKit", webKitVersion,
        platform, platformVersion, this.getDocumentMode_(this.doc_), supportWebFont);
  };
  
  /**
   * @private
   */
  webfont.UserAgentParser.prototype.isGecko_ = function() {
    return this.userAgent_.indexOf("Gecko") != -1;
  };
  
  /**
   * @private
   */
  webfont.UserAgentParser.prototype.parseGeckoUserAgentString_ = function() {
    var name = webfont.UserAgentParser.UNKNOWN;
    var version = webfont.UserAgentParser.UNKNOWN;
    var supportWebFont = false;
  
    if (this.userAgent_.indexOf("Firefox") != -1) {
      name = "Firefox";
      var versionNum = this.getMatchingGroup_(this.userAgent_,
          /Firefox\/([\d\w\.]+)/, 1);
  
      if (versionNum != "") {
        var minor = this.getMatchingGroup_(versionNum, /\d+\.(\d+)/, 1);
  
        version = versionNum;
        supportWebFont = versionNum != "" && this.getMajorVersion_(versionNum) >= 3 &&
            parseInt(minor, 10) >= 5;
      }
    } else if (this.userAgent_.indexOf("Mozilla") != -1) {
      name = "Mozilla";
    }
    var geckoVersion = this.getMatchingGroup_(this.userAgent_, /rv:([^\)]+)/, 1);
  
    if (geckoVersion == "") {
      geckoVersion = webfont.UserAgentParser.UNKNOWN;
    } else {
      if (!supportWebFont) {
        var majorVersion = this.getMajorVersion_(geckoVersion);
        var intMinorVersion = parseInt(this.getMatchingGroup_(geckoVersion, /\d+\.(\d+)/, 1), 10);
        var subVersion = parseInt(this.getMatchingGroup_(geckoVersion, /\d+\.\d+\.(\d+)/, 1), 10);
  
        supportWebFont = majorVersion > 1 ||
            majorVersion == 1 && intMinorVersion > 9 ||
            majorVersion == 1 && intMinorVersion == 9 && subVersion >= 2 ||
            geckoVersion.match(/1\.9\.1b[123]/) != null ||
            geckoVersion.match(/1\.9\.1\.[\d\.]+/) != null;
      }
    }
    return new webfont.UserAgent(name, version, "Gecko", geckoVersion,
        this.getPlatform_(), this.getPlatformVersion_(), this.getDocumentMode_(this.doc_),
        supportWebFont);
  };
  
  /**
   * @private
   */
  webfont.UserAgentParser.prototype.getMajorVersion_ = function(version) {
    var majorVersion = this.getMatchingGroup_(version, /(\d+)/, 1);
  
    if (majorVersion != "") {
      return parseInt(majorVersion, 10);
    }
    return -1;
  };
  
  /**
   * @private
   */
  webfont.UserAgentParser.prototype.getMatchingGroup_ = function(str,
      regexp, index) {
    var groups = str.match(regexp);
  
    if (groups && groups[index]) {
      return groups[index];
    }
    return "";
  };
  
  /**
   * @private
   */
  webfont.UserAgentParser.prototype.getDocumentMode_ = function(doc) {
    if (doc.documentMode) return doc.documentMode;
    return undefined;
  };
  
  /**
   * A class to dispatch events and manage the event class names on an html
   * element that represent the current state of fonts on the page. Active class
   * names always overwrite inactive class names of the same type, while loading
   * class names may be present whenever a font is loading (regardless of if an
   * associated active or inactive class name is also present).
   * @param {webfont.DomHelper} domHelper
   * @param {HTMLElement} htmlElement
   * @param {Object} callbacks
   * @param {string=} opt_namespace
   * @constructor
   */
  webfont.EventDispatcher = function(domHelper, htmlElement, callbacks,
      opt_namespace) {
    this.domHelper_ = domHelper;
    this.htmlElement_ = htmlElement;
    this.callbacks_ = callbacks;
    this.namespace_ = opt_namespace || webfont.EventDispatcher.DEFAULT_NAMESPACE;
    this.cssClassName_ = new webfont.CssClassName('-');
  };
  
  /**
   * @const
   * @type {string}
   */
  webfont.EventDispatcher.DEFAULT_NAMESPACE = 'wf';
  
  /**
   * @const
   * @type {string}
   */
  webfont.EventDispatcher.LOADING = 'loading';
  
  /**
   * @const
   * @type {string}
   */
  webfont.EventDispatcher.ACTIVE = 'active';
  
  /**
   * @const
   * @type {string}
   */
  webfont.EventDispatcher.INACTIVE = 'inactive';
  
  /**
   * @const
   * @type {string}
   */
  webfont.EventDispatcher.FONT = 'font';
  
  /**
   * Dispatch the loading event and append the loading class name.
   */
  webfont.EventDispatcher.prototype.dispatchLoading = function() {
    this.domHelper_.appendClassName(this.htmlElement_,
        this.cssClassName_.build(
            this.namespace_, webfont.EventDispatcher.LOADING));
    this.dispatch_(webfont.EventDispatcher.LOADING);
  };
  
  /**
   * Dispatch the font loading event and append the font loading class name.
   * @param {string} fontFamily
   * @param {string} fontDescription
   */
  webfont.EventDispatcher.prototype.dispatchFontLoading = function(fontFamily, fontDescription) {
    this.domHelper_.appendClassName(this.htmlElement_,
        this.cssClassName_.build(
            this.namespace_, fontFamily, fontDescription, webfont.EventDispatcher.LOADING));
    this.dispatch_(
        webfont.EventDispatcher.FONT + webfont.EventDispatcher.LOADING, fontFamily, fontDescription);
  };
  
  /**
   * Dispatch the font active event, remove the font loading class name, remove
   * the font inactive class name, and append the font active class name.
   * @param {string} fontFamily
   * @param {string} fontDescription
   */
  webfont.EventDispatcher.prototype.dispatchFontActive = function(fontFamily, fontDescription) {
    this.domHelper_.removeClassName(this.htmlElement_,
        this.cssClassName_.build(
            this.namespace_, fontFamily, fontDescription, webfont.EventDispatcher.LOADING));
    this.domHelper_.removeClassName(this.htmlElement_,
        this.cssClassName_.build(
            this.namespace_, fontFamily, fontDescription, webfont.EventDispatcher.INACTIVE));
    this.domHelper_.appendClassName(this.htmlElement_,
        this.cssClassName_.build(
            this.namespace_, fontFamily, fontDescription, webfont.EventDispatcher.ACTIVE));
    this.dispatch_(
        webfont.EventDispatcher.FONT + webfont.EventDispatcher.ACTIVE, fontFamily, fontDescription);
  };
  
  /**
   * Dispatch the font inactive event, remove the font loading class name, and
   * append the font inactive class name (unless the font active class name is
   * already present).
   * @param {string} fontFamily
   * @param {string} fontDescription
   */
  webfont.EventDispatcher.prototype.dispatchFontInactive = function(fontFamily, fontDescription) {
    this.domHelper_.removeClassName(this.htmlElement_,
        this.cssClassName_.build(
            this.namespace_, fontFamily, fontDescription, webfont.EventDispatcher.LOADING));
    var hasFontActive = this.domHelper_.hasClassName(this.htmlElement_,
        this.cssClassName_.build(
            this.namespace_, fontFamily, fontDescription, webfont.EventDispatcher.ACTIVE));
    if (!hasFontActive) {
      this.domHelper_.appendClassName(this.htmlElement_,
          this.cssClassName_.build(
              this.namespace_, fontFamily, fontDescription, webfont.EventDispatcher.INACTIVE));
    }
    this.dispatch_(
        webfont.EventDispatcher.FONT + webfont.EventDispatcher.INACTIVE, fontFamily, fontDescription);
  };
  
  /**
   * Dispatch the inactive event, remove the loading class name, and append the
   * inactive class name (unless the active class name is already present).
   */
  webfont.EventDispatcher.prototype.dispatchInactive = function() {
    this.domHelper_.removeClassName(this.htmlElement_,
        this.cssClassName_.build(
            this.namespace_, webfont.EventDispatcher.LOADING));
    var hasActive = this.domHelper_.hasClassName(this.htmlElement_,
        this.cssClassName_.build(
            this.namespace_, webfont.EventDispatcher.ACTIVE));
    if (!hasActive) {
      this.domHelper_.appendClassName(this.htmlElement_,
          this.cssClassName_.build(
            this.namespace_, webfont.EventDispatcher.INACTIVE));
    }
    this.dispatch_(webfont.EventDispatcher.INACTIVE);
  };
  
  /**
   * Dispatch the active event, remove the loading class name, remove the inactive
   * class name, and append the active class name.
   */
  webfont.EventDispatcher.prototype.dispatchActive = function() {
    this.domHelper_.removeClassName(this.htmlElement_,
        this.cssClassName_.build(
            this.namespace_, webfont.EventDispatcher.LOADING));
    this.domHelper_.removeClassName(this.htmlElement_,
        this.cssClassName_.build(
            this.namespace_, webfont.EventDispatcher.INACTIVE));
    this.domHelper_.appendClassName(this.htmlElement_,
        this.cssClassName_.build(
            this.namespace_, webfont.EventDispatcher.ACTIVE));
    this.dispatch_(webfont.EventDispatcher.ACTIVE);
  };
  
  /**
   * @param {string} event
   * @param {string=} opt_arg1
   * @param {string=} opt_arg2
   */
  webfont.EventDispatcher.prototype.dispatch_ = function(event, opt_arg1, opt_arg2) {
    if (this.callbacks_[event]) {
      this.callbacks_[event](opt_arg1, opt_arg2);
    }
  };
  
  /**
   * @constructor
   */
  webfont.FontModuleLoader = function() {
    this.modules_ = {};
  };
  
  webfont.FontModuleLoader.prototype.addModuleFactory = function(name, factory) {
    this.modules_[name] = factory;
  };
  
  webfont.FontModuleLoader.prototype.getModules = function(configuration) {
    var modules = [];
  
    for (var key in configuration) {
      if (configuration.hasOwnProperty(key)) {
        var moduleFactory = this.modules_[key];
  
        if (moduleFactory) {
          modules.push(moduleFactory(configuration[key]));
        }
      }
    }
    return modules;
  };
  
  /**
   * @constructor
   * @param {webfont.DomHelper} domHelper
   * @param {webfont.EventDispatcher} eventDispatcher
   * @param {Object.<string, function(Object): number>} fontSizer
   * @param {function(function(), number=)} asyncCall
   * @param {function(): number} getTime
   */
  webfont.FontWatcher = function(domHelper, eventDispatcher, fontSizer,
      asyncCall, getTime) {
    this.domHelper_ = domHelper;
    this.eventDispatcher_ = eventDispatcher;
    this.fontSizer_ = fontSizer;
    this.asyncCall_ = asyncCall;
    this.getTime_ = getTime;
    this.currentlyWatched_ = 0;
    this.last_ = false;
    this.success_ = false;
  };
  
  /**
   * @type {string}
   * @const
   */
  webfont.FontWatcher.DEFAULT_VARIATION = 'n4';
  
  /**
   * Watches a set of font families.
   * @param {Array.<string>} fontFamilies The font family names to watch.
   * @param {Object.<string, Array.<string>>} fontDescriptions The font variations
   *     of each family to watch. Described with FVD.
   * @param {Object.<string, string>} fontTestStrings The font test strings for
   *     each family.
   * @param {function(new:webfont.FontWatchRunner, function(string, string),
   *     function(string, string), webfont.DomHelper,
   *     Object.<string, function(Object): number>,
   *     function(function(), number=), function(): number, string, string,
   *     string=)} fontWatchRunnerCtor The font watch runner constructor.
   * @param {boolean} last True if this is the last set of families to watch.
   */
  webfont.FontWatcher.prototype.watch = function(fontFamilies, fontDescriptions,
      fontTestStrings, fontWatchRunnerCtor, last) {
    var length = fontFamilies.length;
  
    for (var i = 0; i < length; i++) {
      var fontFamily = fontFamilies[i];
      if (!fontDescriptions[fontFamily]) {
        fontDescriptions[fontFamily] = [webfont.FontWatcher.DEFAULT_VARIATION];
      }
      this.currentlyWatched_ += fontDescriptions[fontFamily].length;
    }
  
    if (last) {
      this.last_ = last;
    }
  
    for (var i = 0; i < length; i++) {
      var fontFamily = fontFamilies[i];
      var descriptions = fontDescriptions[fontFamily];
      var fontTestString  = fontTestStrings[fontFamily];
  
      for (var j = 0, len = descriptions.length; j < len; j++) {
        var fontDescription = descriptions[j];
  
        this.eventDispatcher_.dispatchFontLoading(fontFamily, fontDescription);
  
        var activeCallback = webfont.bind(this, this.fontActive_);
        var inactiveCallback = webfont.bind(this, this.fontInactive_)
        var fontWatchRunner = new fontWatchRunnerCtor(activeCallback,
            inactiveCallback, this.domHelper_, this.fontSizer_, this.asyncCall_,
            this.getTime_, fontFamily, fontDescription, fontTestString);
  
        fontWatchRunner.start();
      }
    }
  };
  
  /**
   * Called by a FontWatchRunner when a font has been detected as active.
   * @param {string} fontFamily
   * @param {string} fontDescription
   * @private
   */
  webfont.FontWatcher.prototype.fontActive_ = function(fontFamily, fontDescription) {
    this.eventDispatcher_.dispatchFontActive(fontFamily, fontDescription);
    this.success_ = true;
    this.decreaseCurrentlyWatched_();
  };
  
  /**
   * Called by a FontWatchRunner when a font has been detected as inactive.
   * @param {string} fontFamily
   * @param {string} fontDescription
   * @private
   */
  webfont.FontWatcher.prototype.fontInactive_ = function(fontFamily, fontDescription) {
    this.eventDispatcher_.dispatchFontInactive(fontFamily, fontDescription);
    this.decreaseCurrentlyWatched_();
  };
  
  /**
   * @private
   */
  webfont.FontWatcher.prototype.decreaseCurrentlyWatched_ = function() {
    if (--this.currentlyWatched_ == 0 && this.last_) {
      if (this.success_) {
        this.eventDispatcher_.dispatchActive();
      } else {
        this.eventDispatcher_.dispatchInactive();
      }
    }
  };
  
  /**
   * @constructor
   * @param {function(string, string)} activeCallback
   * @param {function(string, string)} inactiveCallback
   * @param {webfont.DomHelper} domHelper
   * @param {Object.<string, function(Object): number>} fontSizer
   * @param {function(function(), number=)} asyncCall
   * @param {function(): number} getTime
   * @param {string} fontFamily
   * @param {string} fontDescription
   * @param {string=} opt_fontTestString
   */
  webfont.FontWatchRunner = function(activeCallback, inactiveCallback, domHelper,
      fontSizer, asyncCall, getTime, fontFamily, fontDescription, opt_fontTestString) {
    this.activeCallback_ = activeCallback;
    this.inactiveCallback_ = inactiveCallback;
    this.domHelper_ = domHelper;
    this.fontSizer_ = fontSizer;
    this.asyncCall_ = asyncCall;
    this.getTime_ = getTime;
    this.nameHelper_ = new webfont.CssFontFamilyName();
    this.fvd_ = new webfont.FontVariationDescription();
    this.fontFamily_ = fontFamily;
    this.fontDescription_ = fontDescription;
    this.fontTestString_ = opt_fontTestString || webfont.FontWatchRunner.DEFAULT_TEST_STRING;
    this.originalSizeA_ = this.getDefaultFontSize_(
        webfont.FontWatchRunner.DEFAULT_FONTS_A);
    this.originalSizeB_ = this.getDefaultFontSize_(
        webfont.FontWatchRunner.DEFAULT_FONTS_B);
    this.lastObservedSizeA_ = this.originalSizeA_;
    this.lastObservedSizeB_ = this.originalSizeB_;
    this.requestedFontA_ = this.createHiddenElementWithFont_(
        webfont.FontWatchRunner.DEFAULT_FONTS_A);
    this.requestedFontB_ = this.createHiddenElementWithFont_(
        webfont.FontWatchRunner.DEFAULT_FONTS_B);
  };
  
  /**
   * A set of sans-serif fonts and a generic family that cover most platforms:
   * Windows - arial - 99.71%
   * Mac - arial - 97.67%
   * Linux - 97.67%
   * (Based on http://www.codestyle.org/css/font-family/sampler-CombinedResults.shtml)
   * @type {string}
   * @const
   */
  webfont.FontWatchRunner.DEFAULT_FONTS_A = "arial,'URW Gothic L',sans-serif";
  
  /**
   * A set of serif fonts and a generic family that cover most platforms. We
   * want each of these fonts to have a different width when rendering the test
   * string than each of the fonts in DEFAULT_FONTS_A:
   * Windows - Georgia - 98.98%
   * Mac - Georgia - 95.60%
   * Linux - Century Schoolbook L - 97.97%
   * (Based on http://www.codestyle.org/css/font-family/sampler-CombinedResults.shtml)
   * @type {string}
   * @const
   */
  webfont.FontWatchRunner.DEFAULT_FONTS_B = "Georgia,'Century Schoolbook L',serif";
  
  /**
   * Default test string. Characters are chosen so that their widths vary a lot
   * between the fonts in the default stacks. We want each fallback stack
   * to always start out at a different width than the other.
   * @type {string}
   * @const
   */
  webfont.FontWatchRunner.DEFAULT_TEST_STRING = 'BESbswy';
  
  webfont.FontWatchRunner.prototype.start = function() {
    this.started_ = this.getTime_();
    this.check_();
  };
  
  /**
   * Checks the size of the two spans against their original sizes during each
   * async loop. If the size of one of the spans is different than the original
   * size, then we know that the font is rendering and finish with the active
   * callback. If we wait more than 5 seconds and nothing has changed, we finish
   * with the inactive callback.
   *
   * Because of an odd Webkit quirk, we wait to observe the new width twice
   * in a row before finishing with the active callback. Sometimes, Webkit will
   * render the spans with a changed width for one iteration even though the font
   * is broken. This only happens for one async loop, so waiting for 2 consistent
   * measurements allows us to work around the quirk.
   *
   * @private
   */
  webfont.FontWatchRunner.prototype.check_ = function() {
    var sizeA = this.fontSizer_.getWidth(this.requestedFontA_);
    var sizeB = this.fontSizer_.getWidth(this.requestedFontB_);
  
    if ((this.originalSizeA_ != sizeA || this.originalSizeB_ != sizeB) &&
        this.lastObservedSizeA_ == sizeA && this.lastObservedSizeB_ == sizeB) {
      this.finish_(this.activeCallback_);
    } else if (this.getTime_() - this.started_ >= 5000) {
      this.finish_(this.inactiveCallback_);
    } else {
      this.lastObservedSizeA_ = sizeA;
      this.lastObservedSizeB_ = sizeB;
      this.asyncCheck_();
    }
  };
  
  /**
   * @private
   */
  webfont.FontWatchRunner.prototype.asyncCheck_ = function() {
    this.asyncCall_(function(context, func) {
      return function() {
        func.call(context);
      }
    }(this, this.check_), 25);
  };
  
  /**
   * @private
   * @param {function(string, string)} callback
   */
  webfont.FontWatchRunner.prototype.finish_ = function(callback) {
    this.domHelper_.removeElement(this.requestedFontA_);
    this.domHelper_.removeElement(this.requestedFontB_);
    callback(this.fontFamily_, this.fontDescription_);
  };
  
  /**
   * @private
   * @param {string} defaultFonts
   */
  webfont.FontWatchRunner.prototype.getDefaultFontSize_ = function(defaultFonts) {
    var defaultFont = this.createHiddenElementWithFont_(defaultFonts, true);
    var size = this.fontSizer_.getWidth(defaultFont);
  
    this.domHelper_.removeElement(defaultFont);
    return size;
  };
  
  /**
   * @private
   * @param {string} defaultFonts
   * @param {boolean=} opt_withoutFontFamily
   */
  webfont.FontWatchRunner.prototype.createHiddenElementWithFont_ = function(
      defaultFonts, opt_withoutFontFamily) {
    var styleString = this.computeStyleString_(defaultFonts,
        this.fontDescription_, opt_withoutFontFamily);
    var span = this.domHelper_.createElement('span', { 'style': styleString },
        this.fontTestString_);
  
    this.domHelper_.insertInto('body', span);
    return span;
  };
  
  webfont.FontWatchRunner.prototype.computeStyleString_ = function(defaultFonts,
      fontDescription, opt_withoutFontFamily) {
    var variationCss = this.fvd_.expand(fontDescription);
    var styleString = "position:absolute;top:-999px;left:-999px;" +
        "font-size:300px;width:auto;height:auto;line-height:normal;margin:0;" +
        "padding:0;font-variant:normal;font-family:"
        + (opt_withoutFontFamily ? "" :
          this.nameHelper_.quote(this.fontFamily_) + ",")
        + defaultFonts + ";" + variationCss;
    return styleString;
  };
  
  /**
   * @constructor
   */
  webfont.WebFont = function(domHelper, fontModuleLoader, htmlElement, asyncCall,
      userAgent) {
    this.domHelper_ = domHelper;
    this.fontModuleLoader_ = fontModuleLoader;
    this.htmlElement_ = htmlElement;
    this.asyncCall_ = asyncCall;
    this.userAgent_ = userAgent;
    this.moduleLoading_ = 0;
    this.moduleFailedLoading_ = 0;
  };
  
  webfont.WebFont.prototype.addModule = function(name, factory) {
    this.fontModuleLoader_.addModuleFactory(name, factory);
  };
  
  webfont.WebFont.prototype.load = function(configuration) {
    var eventDispatcher = new webfont.EventDispatcher(
        this.domHelper_, this.htmlElement_, configuration);
  
    if (this.userAgent_.isSupportingWebFont()) {
      this.load_(eventDispatcher, configuration);
    } else {
      eventDispatcher.dispatchInactive();
    }
  };
  
  webfont.WebFont.prototype.isModuleSupportingUserAgent_ = function(module, eventDispatcher,
      fontWatcher, support) {
    var fontWatchRunnerCtor = module.getFontWatchRunnerCtor ?
        module.getFontWatchRunnerCtor() : webfont.FontWatchRunner;
    if (!support) {
      var allModulesLoaded = --this.moduleLoading_ == 0;
  
      this.moduleFailedLoading_--;
      if (allModulesLoaded) {
        if (this.moduleFailedLoading_ == 0) {
          eventDispatcher.dispatchInactive();
        } else {
          eventDispatcher.dispatchLoading();
        }
      }
      fontWatcher.watch([], {}, {}, fontWatchRunnerCtor, allModulesLoaded);
      return;
    }
    module.load(webfont.bind(this, this.onModuleReady_, eventDispatcher,
        fontWatcher, fontWatchRunnerCtor));
  };
  
  webfont.WebFont.prototype.onModuleReady_ = function(eventDispatcher, fontWatcher,
      fontWatchRunnerCtor, fontFamilies, opt_fontDescriptions, opt_fontTestStrings) {
    var allModulesLoaded = --this.moduleLoading_ == 0;
  
    if (allModulesLoaded) {
      eventDispatcher.dispatchLoading();
    }
    this.asyncCall_(webfont.bind(this, function(_fontWatcher, _fontFamilies,
        _fontDescriptions, _fontTestStrings, _fontWatchRunnerCtor,
        _allModulesLoaded) {
          _fontWatcher.watch(_fontFamilies, _fontDescriptions || {},
            _fontTestStrings || {}, _fontWatchRunnerCtor, _allModulesLoaded);
        }, fontWatcher, fontFamilies, opt_fontDescriptions, opt_fontTestStrings,
        fontWatchRunnerCtor, allModulesLoaded));
  };
  
  webfont.WebFont.prototype.load_ = function(eventDispatcher, configuration) {
    var modules = this.fontModuleLoader_.getModules(configuration),
        self = this;
  
    this.moduleFailedLoading_ = this.moduleLoading_ = modules.length;
  
    var fontWatcher = new webfont.FontWatcher(this.domHelper_,
        eventDispatcher, {
          getWidth: function(elem) {
            return elem.offsetWidth;
          }}, self.asyncCall_, function() {
            return new Date().getTime();
          });
  
    for (var i = 0, len = modules.length; i < len; i++) {
      var module = modules[i];
  
      module.supportUserAgent(this.userAgent_,
          webfont.bind(this, this.isModuleSupportingUserAgent_, module,
          eventDispatcher, fontWatcher));
    }
  };
  
  /**
   * Handles sanitization and construction of css class names.
   * @param {string=} opt_joinChar The character to join parts of the name on.
   *    Defaults to '-'.
   * @constructor
   */
  webfont.CssClassName = function(opt_joinChar) {
    /** @type {string} */
    this.joinChar_ = opt_joinChar || webfont.CssClassName.DEFAULT_JOIN_CHAR;
  };
  
  /**
   * @const
   * @type {string}
   */
  webfont.CssClassName.DEFAULT_JOIN_CHAR = '-';
  
  /**
   * Sanitizes a string for use as a css class name. Removes non-word and
   * underscore characters.
   * @param {string} name The string.
   * @return {string} The sanitized string.
   */
  webfont.CssClassName.prototype.sanitize = function(name) {
    return name.replace(/[\W_]+/g, '').toLowerCase();
  };
  
  /**
   * Builds a complete css class name given a variable number of parts.
   * Sanitizes, then joins the parts together.
   * @param {...string} var_args The parts to join.
   * @return {string} The sanitized and joined string.
   */
  webfont.CssClassName.prototype.build = function(var_args) {
    var parts = []
    for (var i = 0; i < arguments.length; i++) {
      parts.push(this.sanitize(arguments[i]));
    }
    return parts.join(this.joinChar_);
  };
  
  
  /**
   * Handles quoting rules for a font family name in css.
   * @constructor
   */
  webfont.CssFontFamilyName = function() {
    /** @type {string} */
    this.quote_ = "'";
  };
  
  /**
   * Quotes the name.
   * @param {string} name The name to quote.
   * @return {string} The quoted name.
   */
  webfont.CssFontFamilyName.prototype.quote = function(name) {
    var quoted = [];
    var split = name.split(/,\s*/);
    for (var i = 0; i < split.length; i++) {
      var part = split[i].replace(/['"]/g, '');
      if (part.indexOf(' ') == -1) {
        quoted.push(part);
      } else {
        quoted.push(this.quote_ + part + this.quote_);
      }
    }
    return quoted.join(',');
  };
  
  /**
   * @constructor
   */
  webfont.FontVariationDescription = function() {
    this.properties_ = webfont.FontVariationDescription.PROPERTIES;
    this.values_ = webfont.FontVariationDescription.VALUES;
  };
  
  /**
   * @const
   */
  webfont.FontVariationDescription.PROPERTIES = [
    'font-style',
    'font-weight'
  ];
  
  /**
   * @const
   */
  webfont.FontVariationDescription.VALUES = {
    'font-style': [
      ['n', 'normal'],
      ['i', 'italic'],
      ['o', 'oblique']
    ],
    'font-weight': [
      ['1', '100'],
      ['2', '200'],
      ['3', '300'],
      ['4', '400'],
      ['5', '500'],
      ['6', '600'],
      ['7', '700'],
      ['8', '800'],
      ['9', '900'],
      ['4', 'normal'],
      ['7', 'bold']
    ]
  };
  
  /**
   * @private
   * @constructor
   */
  webfont.FontVariationDescription.Item = function(index, property, values) {
    this.index_ = index;
    this.property_ = property;
    this.values_ = values;
  }
  
  webfont.FontVariationDescription.Item.prototype.compact = function(output, value) {
    for (var i = 0; i < this.values_.length; i++) {
      if (value == this.values_[i][1]) {
        output[this.index_] = this.values_[i][0];
        return;
      }
    }
  }
  
  webfont.FontVariationDescription.Item.prototype.expand = function(output, value) {
    for (var i = 0; i < this.values_.length; i++) {
      if (value == this.values_[i][0]) {
        output[this.index_] = this.property_ + ':' + this.values_[i][1];
        return;
      }
    }
  }
  
  /**
   * Compacts CSS declarations into an FVD.
   * @param {string} input A string of CSS declarations such as
   *    'font-weight:normal;font-style:italic'.
   * @return {string} The equivalent FVD such as 'n4'.
   */
  webfont.FontVariationDescription.prototype.compact = function(input) {
    var result = ['n', '4'];
    var descriptors = input.split(';');
  
    for (var i = 0, len = descriptors.length; i < len; i++) {
      var pair = descriptors[i].replace(/\s+/g, '').split(':');
      if (pair.length == 2) {
        var property = pair[0];
        var value = pair[1];
        var item = this.getItem_(property);
        if (item) {
          item.compact(result, value);
        }
      }
    }
  
    return result.join('');
  };
  
  /**
   * Expands a FVD string into equivalent CSS declarations.
   * @param {string} fvd The FVD string, such as 'n4'.
   * @return {?string} The equivalent CSS such as
   *    'font-weight:normal;font-style:italic' or null if it cannot be parsed.
   */
  webfont.FontVariationDescription.prototype.expand = function(fvd) {
    if (fvd.length != 2) {
      return null;
    }
  
    var result = [null, null];
  
    for (var i = 0, len = this.properties_.length; i < len; i++) {
      var property = this.properties_[i];
      var key = fvd.substr(i, 1);
      var values = this.values_[property];
      var item = new webfont.FontVariationDescription.Item(i, property, values);
      item.expand(result, key);
    }
  
    if (result[0] && result[1]) {
      return result.join(';') + ';';
    } else {
      return null;
    }
  }
  
  /**
   * @private
   */
  webfont.FontVariationDescription.prototype.getItem_ = function(property) {
    for (var i = 0; i < this.properties_.length; i++) {
      if (property == this.properties_[i]) {
        var values = this.values_[property];
        return new webfont.FontVariationDescription.Item(i, property, values);
      }
    }
  
    return null;
  };
  
  // Name of the global object.
  var globalName = 'WebFont';
  
  // Provide an instance of WebFont in the global namespace.
  window[globalName] = (function() {
    var userAgentParser = new webfont.UserAgentParser(navigator.userAgent, document);
    var userAgent = userAgentParser.parse();
    var domHelper = new webfont.DomHelper(document);
    var asyncCall = function(func, timeout) { setTimeout(func, timeout); };
  
    return new webfont.WebFont(domHelper, new webfont.FontModuleLoader(),
        document.documentElement, asyncCall, userAgent);
  })();
  
  // Export the public API.
  window[globalName]['load'] = window[globalName].load;
  window[globalName]['addModule'] = window[globalName].addModule;
  
  // Export the UserAgent API because we pass this object to external modules.
  webfont.UserAgent.prototype['getName'] = webfont.UserAgent.prototype.getName;
  webfont.UserAgent.prototype['getVersion'] = webfont.UserAgent.prototype.getVersion;
  webfont.UserAgent.prototype['getEngine'] = webfont.UserAgent.prototype.getEngine;
  webfont.UserAgent.prototype['getEngineVersion'] = webfont.UserAgent.prototype.getEngineVersion;
  webfont.UserAgent.prototype['getPlatform'] = webfont.UserAgent.prototype.getPlatform;
  webfont.UserAgent.prototype['getPlatformVersion'] = webfont.UserAgent.prototype.getPlatformVersion;
  webfont.UserAgent.prototype['getDocumentMode'] = webfont.UserAgent.prototype.getDocumentMode;
  webfont.UserAgent.prototype['isSupportingWebFont'] = webfont.UserAgent.prototype.isSupportingWebFont;
  
  /**
   * @constructor
   */
  webfont.TypekitScript = function(global, domHelper, configuration) {
    this.global_ = global;
    this.domHelper_ = domHelper;
    this.configuration_ = configuration;
    this.fontFamilies_ = [];
    this.fontVariations_ = {};
  };
  
  webfont.TypekitScript.NAME = 'typekit';
  webfont.TypekitScript.HOOK = '__webfonttypekitmodule__';
  
  webfont.TypekitScript.prototype.getScriptSrc = function(kitId) {
    var protocol = 'https:' == window.location.protocol ? 'https:' : 'http:';
    var api = this.configuration_['api'] || protocol + '//use.typekit.com';
    return api + '/' + kitId + '.js';
  };
  
  webfont.TypekitScript.prototype.supportUserAgent = function(userAgent, support) {
    var kitId = this.configuration_['id'];
    var configuration = this.configuration_;
    var self = this;
  
    if (kitId) {
      // Provide data to Typekit for processing.
      if (!this.global_[webfont.TypekitScript.HOOK]) {
        this.global_[webfont.TypekitScript.HOOK] = {};
      }
  
      // Typekit will call 'init' to indicate whether it supports fonts
      // and what fonts will be provided.
      this.global_[webfont.TypekitScript.HOOK][kitId] = function(callback) {
        var init = function(typekitSupports, fontFamilies, fontVariations) {
          self.fontFamilies_ = fontFamilies;
          self.fontVariations_ = fontVariations;
          support(typekitSupports);
        };
        callback(userAgent, configuration, init);
      };
  
      // Load the Typekit script.
      var script = this.domHelper_.createScriptSrc(this.getScriptSrc(kitId))
      this.domHelper_.insertInto('head', script);
  
    } else {
      support(true);
    }
  };
  
  webfont.TypekitScript.prototype.load = function(onReady) {
    onReady(this.fontFamilies_, this.fontVariations_);
  };
  
  window['WebFont'].addModule(webfont.TypekitScript.NAME, function(configuration) {
    var domHelper = new webfont.DomHelper(document);
    return new webfont.TypekitScript(window, domHelper, configuration);
  });
  
  
  /**
   * @constructor
   * @param {function(string, string)} activeCallback
   * @param {function(string, string)} inactiveCallback
   * @param {webfont.DomHelper} domHelper
   * @param {Object.<string, function(Object): number>} fontSizer
   * @param {function(function(), number=)} asyncCall
   * @param {function(): number} getTime
   * @param {string} fontFamily
   * @param {string} fontDescription
   * @param {string=} opt_fontTestString
   * @extends webfont.FontWatchRunner
   */
  webfont.LastResortWebKitFontWatchRunner = function(activeCallback,
      inactiveCallback, domHelper, fontSizer, asyncCall, getTime, fontFamily,
      fontDescription, opt_fontTestString) {
    webfont.LastResortWebKitFontWatchRunner.superCtor_.call(this,
        activeCallback, inactiveCallback, domHelper, fontSizer, asyncCall,
        getTime, fontFamily, fontDescription, opt_fontTestString);
    this.webKitLastResortFontSizes_ = this.setUpWebKitLastResortFontSizes_();
    this.webKitLastResortSizeChange_ = false;
  };
  webfont.extendsClass(webfont.FontWatchRunner, webfont.LastResortWebKitFontWatchRunner);
  
  webfont.LastResortWebKitFontWatchRunner.METRICS_COMPATIBLE_FONTS = {
      "Arimo": true,
      "Cousine": true,
      "Tinos": true
  };
  
  /**
   * While loading a web font webkit applies a last resort fallback font to the
   * element on which the web font is applied.
   * See file: WebKit/Source/WebCore/css/CSSFontFaceSource.cpp.
   * Looking at the different implementation for the different platforms,
   * the last resort fallback font is different. This code uses the default
   * OS/browsers values.
   */
  webfont.LastResortWebKitFontWatchRunner.prototype
      .setUpWebKitLastResortFontSizes_ = function() {
    var lastResortFonts = ['Times New Roman', 'Arial', 'Times', 'Sans', 'Serif'];
    var lastResortFontSizes = lastResortFonts.length;
    var webKitLastResortFontSizes = {};
    var element = this.createHiddenElementWithFont_(lastResortFonts[0], true);
  
    webKitLastResortFontSizes[this.fontSizer_.getWidth(element)] = true;
    for (var i = 1; i < lastResortFontSizes; i++) {
      var font = lastResortFonts[i];
      this.domHelper_.setStyle(element, this.computeStyleString_(font,
          this.fontDescription_, true));
      webKitLastResortFontSizes[this.fontSizer_.getWidth(element)] = true;
  
      // Another WebKit quirk if the normal weight/style is loaded first,
      // the size of the normal weight is returned when loading another weight.
      if (this.fontDescription_[1] != '4') {
        this.domHelper_.setStyle(element, this.computeStyleString_(font,
          this.fontDescription_[0] + '4', true));
        webKitLastResortFontSizes[this.fontSizer_.getWidth(element)] = true;
      }
    }
    this.domHelper_.removeElement(element);
    return webKitLastResortFontSizes;
  };
  
  webfont.LastResortWebKitFontWatchRunner.prototype.check_ = function() {
    var sizeA = this.fontSizer_.getWidth(this.requestedFontA_);
    var sizeB = this.fontSizer_.getWidth(this.requestedFontB_);
  
    if (!this.webKitLastResortSizeChange_ && sizeA == sizeB &&
        this.webKitLastResortFontSizes_[sizeA]) {
      this.webKitLastResortFontSizes_ = {};
      this.webKitLastResortFontSizes_[sizeA] = true;
      this.webKitLastResortSizeChange_ = true;
    }
    if ((this.originalSizeA_ != sizeA || this.originalSizeB_ != sizeB) &&
        (!this.webKitLastResortFontSizes_[sizeA] &&
         !this.webKitLastResortFontSizes_[sizeB])) {
      this.finish_(this.activeCallback_);
    } else if (this.getTime_() - this.started_ >= 5000) {
  
      // In order to handle the fact that a font could be the same size as the
      // default browser font on a webkit browser, mark the font as active
      // after 5 seconds if the latest 2 sizes are in webKitLastResortFontSizes_
      // and the font name is known to be metrics compatible.
      if (this.webKitLastResortFontSizes_[sizeA]
          && this.webKitLastResortFontSizes_[sizeB] &&
          webfont.LastResortWebKitFontWatchRunner.METRICS_COMPATIBLE_FONTS[
            this.fontFamily_]) {
        this.finish_(this.activeCallback_);
      } else {
        this.finish_(this.inactiveCallback_);
      }
    } else {
      this.asyncCheck_();
    }
  };
  
  /**
   * @constructor
   */
  webfont.FontApiUrlBuilder = function(apiUrl) {
    if (apiUrl) {
      this.apiUrl_ = apiUrl;
    } else {
      var protocol = 'https:' == window.location.protocol ? 'https:' : 'http:';
  
      this.apiUrl_ = protocol + webfont.FontApiUrlBuilder.DEFAULT_API_URL;
    }
    this.fontFamilies_ = [];
    this.subsets_ = [];
  };
  
  
  webfont.FontApiUrlBuilder.DEFAULT_API_URL = '//fonts.googleapis.com/css';
  
  
  webfont.FontApiUrlBuilder.prototype.setFontFamilies = function(fontFamilies) {
    this.parseFontFamilies_(fontFamilies);
  };
  
  
  webfont.FontApiUrlBuilder.prototype.parseFontFamilies_ =
      function(fontFamilies) {
    var length = fontFamilies.length;
  
    for (var i = 0; i < length; i++) {
      var elements = fontFamilies[i].split(':');
  
      if (elements.length == 3) {
        this.subsets_.push(elements.pop());
      }
      var joinCharacter = '';
      if (elements.length == 2 && elements[1] != ''){
        joinCharacter = ':';
      }
      this.fontFamilies_.push(elements.join(joinCharacter));
    }
  };
  
  
  webfont.FontApiUrlBuilder.prototype.webSafe = function(string) {
    return string.replace(/ /g, '+');
  };
  
  
  webfont.FontApiUrlBuilder.prototype.build = function() {
    if (this.fontFamilies_.length == 0) {
      throw new Error('No fonts to load !');
    }
    if (this.apiUrl_.indexOf("kit=") != -1) {
      return this.apiUrl_;
    }
    var length = this.fontFamilies_.length;
    var sb = [];
  
    for (var i = 0; i < length; i++) {
      sb.push(this.webSafe(this.fontFamilies_[i]));
    }
    var url = this.apiUrl_ + '?family=' + sb.join('%7C'); // '|' escaped.
  
    if (this.subsets_.length > 0) {
      url += '&subset=' + this.subsets_.join(',');
    }
  
    return url;
  };
  
  /**
   * @constructor
   */
  webfont.FontApiParser = function(fontFamilies) {
    this.fontFamilies_ = fontFamilies;
    this.parsedFontFamilies_ = [];
    this.variations_ = {};
    this.fontTestStrings_ = {};
    this.fvd_ = new webfont.FontVariationDescription();
  };
  
  webfont.FontApiParser.VARIATIONS = {
    'ultralight': 'n2',
    'light': 'n3',
    'regular': 'n4',
    'bold': 'n7',
    'italic': 'i4',
    'bolditalic': 'i7',
    'ul': 'n2',
    'l': 'n3',
    'r': 'n4',
    'b': 'n7',
    'i': 'i4',
    'bi': 'i7'
  };
  
  webfont.FontApiParser.INT_FONTS = {
    'latin': webfont.FontWatchRunner.DEFAULT_TEST_STRING,
    'cyrillic': '&#1081;&#1103;&#1046;',
    'greek': '&#945;&#946;&#931;',
    'khmer': '&#x1780;&#x1781;&#x1782;',
    'Hanuman': '&#x1780;&#x1781;&#x1782;' // For backward compatibility
  };
  
  webfont.FontApiParser.prototype.parse = function() {
    var length = this.fontFamilies_.length;
  
    for (var i = 0; i < length; i++) {
      var elements = this.fontFamilies_[i].split(":");
      var fontFamily = elements[0].replace(/\+/g, " ");
      var variations = ['n4'];
  
      if (elements.length >= 2) {
        var fvds = this.parseVariations_(elements[1]);
  
        if (fvds.length > 0) {
          variations = fvds;
        }
        if (elements.length == 3) {
          var subsets = this.parseSubsets_(elements[2]);
          if (subsets.length > 0) {
            var fontTestString = webfont.FontApiParser.INT_FONTS[subsets[0]];
  
            if (fontTestString) {
  	    this.fontTestStrings_[fontFamily] = fontTestString;
  	  }
  	}
        }
      }
  
      // For backward compatibility
      if (!this.fontTestStrings_[fontFamily]) {
        var hanumanTestString = webfont.FontApiParser.INT_FONTS[fontFamily];
        if (hanumanTestString) {
          this.fontTestStrings_[fontFamily] = hanumanTestString;
        }
      }
      this.parsedFontFamilies_.push(fontFamily);
      this.variations_[fontFamily] = variations;
    }
  };
  
  webfont.FontApiParser.prototype.generateFontVariationDescription_ = function(variation) {
    if (!variation.match(/^[\w ]+$/)) {
      return '';
    }
  
    var fvd = webfont.FontApiParser.VARIATIONS[variation];
  
    if (fvd) {
      return fvd;
    } else {
      var groups = variation.match(/^(\d*)(\w*)$/);
      var numericMatch = groups[1];
      var styleMatch = groups[2];
      var s = styleMatch ? styleMatch : 'n';
      var w = numericMatch ? numericMatch.substr(0, 1) : '4';
      var css = this.fvd_.expand([s, w].join(''));
      if (css) {
        return this.fvd_.compact(css);
      } else {
        return null;
      }
    }
  };
  
  webfont.FontApiParser.prototype.parseVariations_ = function(variations) {
    var finalVariations = [];
  
    if (!variations) {
      return finalVariations;
    }
    var providedVariations = variations.split(",");
    var length = providedVariations.length;
  
    for (var i = 0; i < length; i++) {
      var variation = providedVariations[i];
      var fvd = this.generateFontVariationDescription_(variation);
  
      if (fvd) {
        finalVariations.push(fvd);
      }
    }
    return finalVariations;
  };
  
  
  webfont.FontApiParser.prototype.parseSubsets_ = function(subsets) {
    var finalSubsets = [];
  
    if (!subsets) {
      return finalSubsets;
    }
    return subsets.split(",");
  };
  
  
  webfont.FontApiParser.prototype.getFontFamilies = function() {
    return this.parsedFontFamilies_;
  };
  
  webfont.FontApiParser.prototype.getVariations = function() {
    return this.variations_;
  };
  
  webfont.FontApiParser.prototype.getFontTestStrings = function() {
    return this.fontTestStrings_;
  };
  
  /**
   * @constructor
   */
  webfont.GoogleFontApi = function(userAgent, domHelper, configuration) {
    this.userAgent_ = userAgent;
    this.domHelper_ = domHelper;
    this.configuration_ = configuration;
  };
  
  webfont.GoogleFontApi.NAME = 'google';
  
  webfont.GoogleFontApi.prototype.supportUserAgent = function(userAgent, support) {
    support(userAgent.isSupportingWebFont());
  };
  
  webfont.GoogleFontApi.prototype.getFontWatchRunnerCtor = function() {
    if (this.userAgent_.getEngine() == "AppleWebKit") {
      return webfont.LastResortWebKitFontWatchRunner;
    }
    return webfont.FontWatchRunner;
  };
  
  webfont.GoogleFontApi.prototype.load = function(onReady) {
    var domHelper = this.domHelper_;
    var nonBlockingIe = this.userAgent_.getName() == 'MSIE' &&
        this.configuration_['blocking'] != true;
  
    if (nonBlockingIe) {
      domHelper.whenBodyExists(webfont.bind(this, this.insertLink_, onReady));
    } else {
      this.insertLink_(onReady);
    }
  };
  
  webfont.GoogleFontApi.prototype.insertLink_ = function(onReady) {
    var domHelper = this.domHelper_;
    var fontApiUrlBuilder = new webfont.FontApiUrlBuilder(
        this.configuration_['api']);
    var fontFamilies = this.configuration_['families'];
    fontApiUrlBuilder.setFontFamilies(fontFamilies);
  
    var fontApiParser = new webfont.FontApiParser(fontFamilies);
    fontApiParser.parse();
  
    domHelper.insertInto('head', domHelper.createCssLink(
        fontApiUrlBuilder.build()));
    onReady(fontApiParser.getFontFamilies(), fontApiParser.getVariations(),
        fontApiParser.getFontTestStrings());
  };
  
  window['WebFont'].addModule(webfont.GoogleFontApi.NAME, function(configuration) {
    var userAgentParser = new webfont.UserAgentParser(navigator.userAgent, document);
    var userAgent = userAgentParser.parse();
    return new webfont.GoogleFontApi(userAgent, new webfont.DomHelper(document),
        configuration);
  });
  
  /**
   *
   * WebFont.load({
   *   ascender: {
   *     key:'ec2de397-11ae-4c10-937f-bf94283a70c1',
   *     families:['AyitaPro:regular,bold,bolditalic,italic']
   *   }
   * });
   *
   * @constructor
   */
  webfont.AscenderScript = function(domHelper, configuration) {
    this.domHelper_ = domHelper;
    this.configuration_ = configuration;
  };
  
  webfont.AscenderScript.NAME = 'ascender';
  
  webfont.AscenderScript.VARIATIONS = {
    'regular': 'n4',
    'bold': 'n7',
    'italic': 'i4',
    'bolditalic': 'i7',
    'r': 'n4',
    'b': 'n7',
    'i': 'i4',
    'bi': 'i7'
  };
  
  webfont.AscenderScript.prototype.supportUserAgent = function(userAgent, support) {
    return support(userAgent.isSupportingWebFont());
  };
  
  webfont.AscenderScript.prototype.load = function(onReady) {
    var key = this.configuration_['key'];
    var protocol = (('https:' == document.location.protocol) ? 'https:' : 'http:');
    var url = protocol + '//webfonts.fontslive.com/css/' + key + '.css';
    this.domHelper_.insertInto('head', this.domHelper_.createCssLink(url));
    var fv = this.parseFamiliesAndVariations(this.configuration_['families']);
    onReady(fv.families, fv.variations);
  };
  
  webfont.AscenderScript.prototype.parseFamiliesAndVariations = function(providedFamilies){
    var families, variations, fv;
    families = [];
    variations = {};
    for(var i = 0, len = providedFamilies.length; i < len; i++){
      fv = this.parseFamilyAndVariations(providedFamilies[i]);
      families.push(fv.family);
      variations[fv.family] = fv.variations;
    }
    return { families:families, variations:variations };
  };
  
  webfont.AscenderScript.prototype.parseFamilyAndVariations = function(providedFamily){
    var family, variations, parts;
    parts = providedFamily.split(':');
    family = parts[0];
    variations = [];
    if(parts[1]){
      variations = this.parseVariations(parts[1]);
    }else{
      variations = ['n4'];
    }
    return { family:family, variations:variations };
  };
  
  webfont.AscenderScript.prototype.parseVariations = function(source){
    var providedVariations = source.split(',');
    var variations = [];
    for(var i = 0, len = providedVariations.length; i < len; i++){
      var pv = providedVariations[i];
      if(pv){
        var v = webfont.AscenderScript.VARIATIONS[pv];
        variations.push(v ? v : pv);
      }
    }
    return variations;
  };
  
  window['WebFont'].addModule(webfont.AscenderScript.NAME, function(configuration) {
    var domHelper = new webfont.DomHelper(document);
    return new webfont.AscenderScript(domHelper, configuration);
  });
  
  /**
   *
   * WebFont.load({
   *   custom: {
   *     families: ['Font1', 'Font2'],
   *    urls: [ 'http://moo', 'http://meuh' ] }
   * });
   *
   * @constructor
   */
  webfont.CustomCss = function(domHelper, configuration) {
    this.domHelper_ = domHelper;
    this.configuration_ = configuration;
  };
  
  webfont.CustomCss.NAME = 'custom';
  
  webfont.CustomCss.prototype.load = function(onReady) {
    var urls = this.configuration_['urls'] || [];
    var families = this.configuration_['families'] || [];
  
    for (var i = 0, len = urls.length; i < len; i++) {
      var url = urls[i];
  
      this.domHelper_.insertInto('head', this.domHelper_.createCssLink(url));
    }
    onReady(families);
  };
  
  webfont.CustomCss.prototype.supportUserAgent = function(userAgent, support) {
    return support(userAgent.isSupportingWebFont());
  };
  
  window['WebFont'].addModule(webfont.CustomCss.NAME, function(configuration) {
    var domHelper = new webfont.DomHelper(document);
    return new webfont.CustomCss(domHelper, configuration);
  });
  
  /**
   * @constructor
   */
  webfont.FontdeckScript = function(global, domHelper, configuration) {
    this.global_ = global;
    this.domHelper_ = domHelper;
    this.configuration_ = configuration;
    this.fontFamilies_ = [];
    this.fontVariations_ = {};
    this.fvd_ = new webfont.FontVariationDescription();
  };
  
  webfont.FontdeckScript.NAME = 'fontdeck';
  webfont.FontdeckScript.HOOK = '__webfontfontdeckmodule__';
  webfont.FontdeckScript.API = '//f.fontdeck.com/s/css/js/';
  
  webfont.FontdeckScript.prototype.getScriptSrc = function(projectId) {
    var protocol = 'https:' == this.global_.location.protocol ? 'https:' : 'http:';
    var api = this.configuration_['api'] || webfont.FontdeckScript.API;
    return protocol + api + this.global_.document.location.hostname + '/' + projectId + '.js';
  };
  
  webfont.FontdeckScript.prototype.supportUserAgent = function(userAgent, support) {
    var projectId = this.configuration_['id'];
    var self = this;
  
    if (projectId) {
      // Provide data to Fontdeck for processing.
      if (!this.global_[webfont.FontdeckScript.HOOK]) {
        this.global_[webfont.FontdeckScript.HOOK] = {};
      }
  
      // Fontdeck will call this function to indicate support status
      // and what fonts are provided.
      this.global_[webfont.FontdeckScript.HOOK][projectId] = function(fontdeckSupports, data) {
        for (var i = 0, j = data['fonts'].length; i<j; ++i) {
          var font = data['fonts'][i];
          // Add the FVDs
          self.fontFamilies_.push(font['name']);
          self.fontVariations_[font['name']] = [self.fvd_.compact("font-weight:" + font['weight'] + ";font-style:" + font['style'])];
        }
        support(fontdeckSupports);
      };
  
      // Call the Fontdeck API.
      var script = this.domHelper_.createScriptSrc(this.getScriptSrc(projectId));
      this.domHelper_.insertInto('head', script);
  
    } else {
      support(true);
    }
  };
  
  webfont.FontdeckScript.prototype.load = function(onReady) {
    onReady(this.fontFamilies_, this.fontVariations_);
  };
  
  window['WebFont'].addModule(webfont.FontdeckScript.NAME, function(configuration) {
    var domHelper = new webfont.DomHelper(document);
    return new webfont.FontdeckScript(window, domHelper, configuration);
  });
  
  /**
  webfont.load({
  monotype: {
  projectId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'//this is your Fonts.com Web fonts projectId
  }
  });
  */
  
  /**
   * @constructor
   */
  webfont.MonotypeScript = function (global, userAgent, domHelper, doc, configuration) {
    this.global_ = global;
    this.userAgent_ = userAgent;
    this.domHelper_ = domHelper;
    this.doc_ = doc;
    this.configuration_ = configuration;
    this.fontFamilies_ = [];
    this.fontVariations_ = {};
  };
  
  /**
   * name of the module through which external API is supposed to call the MonotypeFontAPI.
   * @const
   */
  webfont.MonotypeScript.NAME = 'monotype';
  
  /**
   * __mti_fntLst is the name of function that exposes Monotype's font list.
   * @const
   */
  webfont.MonotypeScript.HOOK = '__mti_fntLst';
  
  /**
   * __MonotypeAPIScript__ is the id of script added by google API. Currently 'webfonts.fonts.com' supports only one script in a page. 
   * This may require change in future if 'webfonts.fonts.com' begins supporting multiple scripts per page.
   * @const
   */
  webfont.MonotypeScript.SCRIPTID = '__MonotypeAPIScript__';
  
  webfont.MonotypeScript.prototype.supportUserAgent = function (userAgent, support) {
    var self = this;
    var projectId = self.configuration_['projectId'];
    if (projectId) {
      var sc = self.domHelper_.createScriptSrc(self.getScriptSrc(projectId));
      sc["id"] = webfont.MonotypeScript.SCRIPTID + projectId;
  
      sc["onreadystatechange"] = function (e) {
        if (sc["readyState"] === "loaded" || sc["readyState"] === "complete") {
          sc["onreadystatechange"] = null;
          sc["onload"](e);
        }
      };
  
      sc["onload"] = function (e) {
        if (self.global_[webfont.MonotypeScript.HOOK + projectId]) {
          var mti_fnts = self.global_[webfont.MonotypeScript.HOOK + projectId]();
          if (mti_fnts && mti_fnts.length) {
            var i;
            for (i = 0; i < mti_fnts.length; i++) {
              self.fontFamilies_.push(mti_fnts[i]["fontfamily"]);
            }
          }
        }
        support(userAgent.isSupportingWebFont());
      };
  
      this.domHelper_.insertInto('head', sc);
    }
    else {
      support(true);
    }
  };
  
  webfont.MonotypeScript.prototype.getScriptSrc = function (projectId) {
    var p = this.protocol();
    var api = (this.configuration_['api'] || 'fast.fonts.com/jsapi').replace(/^.*http(s?):(\/\/)?/, "");
    return p + "//" + api + '/' + projectId + '.js';
  };
  
  webfont.MonotypeScript.prototype.load = function (onReady) {
    onReady(this.fontFamilies_, this.fontVariations_);
  };
  
  webfont.MonotypeScript.prototype.protocol = function () {
    var supportedProtocols = ["http:", "https:"];
    var defaultProtocol = supportedProtocols[0];
    if (this.doc_ && this.doc_.location && this.doc_.location.protocol) {
      var i = 0;
      for (i = 0; i < supportedProtocols.length; i++) {
        if (this.doc_.location.protocol === supportedProtocols[i]) {
          return this.doc_.location.protocol;
        }
      }
    }
  
    return defaultProtocol;
  };
  
  window['WebFont'].addModule(webfont.MonotypeScript.NAME, function (configuration) {
    var userAgentParser = new webfont.UserAgentParser(navigator.userAgent, document);
    var userAgent = userAgentParser.parse();
    var domHelper = new webfont.DomHelper(document);
    return new webfont.MonotypeScript(window, userAgent, domHelper, document, configuration);
  });
  
  if (window['WebFontConfig']) {
    window['WebFont']['load'](window['WebFontConfig']);
  }
  
  