[
  {
    "path": ".gitignore",
    "content": ".bundle\n*~\ntarget\ntmp\n_site\npkg\n"
  },
  {
    "path": ".travis.yml",
    "content": "before_install:\n  - wget https://s3.amazonaws.com/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2\n  - tar -xjf phantomjs-2.0.0-ubuntu-12.04.tar.bz2\n  - sudo mv /usr/local/phantomjs/bin/phantomjs /usr/local/phantomjs/bin/phantomjs1\n  - sudo mv phantomjs /usr/local/phantomjs/bin/phantomjs\nlanguage: node_js\n"
  },
  {
    "path": "CHANGELOG",
    "content": "v1.6.28 (March 27, 2017)\n\n* Clear timer timeout when promise resolves.\n\nv1.6.27 (November 29, 2016)\n\n* Disable native font loading for Safari 10\n\nv1.6.26 (July 8, 2016)\n\n* Add support for latin extended in the Google Fonts module.\n\nv1.6.25 (July 7, 2016)\n\n* Add \"loadAllFonts\" option to Monotype module.\n* Add documentation on how to use Web Font Loader with Edge Web Fonts.\n\nv1.6.24 (March 20, 2016)\n\n* Fix type annotation in DomHelper.\n\nv1.6.23 (March 20, 2016)\n\n* Add bower.json\n* Small code rewrite in order to workaround a bad type cast (Closure Compiler\nis correct, but reality doesn't always match).\n\nv1.6.22 (February 29, 2016)\n\n* Fix bug that caused long loading fonts to get stuck on wf-loading because\nthe timeout never fired (or took a really long time).\n\nv1.6.21 (February 2, 2016)\n\n* Fix bug in Google module that caused non-latin to fail on browsers that\nsupport the native font loading API.\n* Fix bug in IE8 where font events sometimes failed due to a timing issue.\n\nv1.6.20 (January 4, 2016)\n\n* Add all source files to the npm package.\n\nv1.6.19 (January 4, 2016)\n\n* Add src/core files to npm package.\n\nv1.6.18 (January 4, 2016)\n\n* Version bump.\n\nv1.6.17 (January 4, 2016)\n\n* Upgrade closure compiler and base.js to latest version (no code changes).\n\nv1.6.16 (December 8, 2015)\n\n* Add support for detecting font loads even if the font isn't in the CSSOM yet (workaround for a Chrome bug).\n\nv1.6.15 (November 26, 2015)\n\n* Temporarily disable the native font loading API in Firefox 42 and below due to a bug.\n\nv1.6.14 (November 24, 2015)\n\n* Change media type check to be more reliable on old IE.\n\nv1.6.13 (November 24, 2015)\n\n* Fix media type check in IE/Edge.\n\nv1.6.12 (November 23, 2015)\n\n* Fix bug that sometimes caused wf-inactive to show when asynchronously loading CSS.\n\nv1.6.11 (November 17, 2015)\n\n* Only include production build in npm package\n* Fix bug that caused duplicate font loads to fail (using the native font loading code)\n\nv1.6.10 (October 9, 2015)\n\n* Fix compile warning\n\nv1.6.9 (October 9, 2015)\n\n* Fix native font load detection when combined with asynchronous CSS loading\n* Fix support for font family names starting with a number\n\nv1.6.8 (October 1, 2015)\n\n* Add support for the native font loading API.\n\nv1.6.7 (September 24, 2015)\n\n* Update Monotype module\n\nv1.6.6 (August 7, 2015)\n\n* Fix weird check in insertInto that disallowed adding a link to an empty head element.\n* Load fonts asynchronously for the Typekit module.\n* Remove font rulers and trigger callback asynchronously to avoid unnecessary reflows.\n\nv1.6.5 (July 25, 2015)\n* Optimise layout calculations\n* Fix Rubygems source\n* Simplify internal Google module code\n\nv1.6.4 (July 9, 2015)\n* Optimise reflows when setting up test spans.\n\nv1.6.3 (June 1, 2015)\n* Fix invalid references to global this (window).\n\nv1.6.2 (May 29, 2015)\n* No changes.\n\nv1.6.1 (May 29, 2015)\n* Automatically update npm version.\n\nv1.6.0 (May 28, 2015)\n* Remove user agent string sniffing. From now on the Web Font Loader will always try to load fonts.\n\nv1.5.21 (May 26, 2015)\n* No changes.\n\nv1.5.20 (May 26, 2015)\n* Use setTimeout instead of window.setTimeout\n\nv1.5.19 (May 24, 2015)\n* Add UMD wrapper\n\nv1.5.18 (April 15, 2015)\n* Add configuration option to enable asynchronous CSS (internal API)\n\nv1.5.17 (April 14, 2015)\n* Load CSS files asynchronously\n\nv1.5.16 (March 30, 2015)\n* Optimise DOM performance in the font watcher.\n\nv1.5.15 (March 25, 2015)\n* Increase offset position for ruler so scrollbars are less likely to be triggered by large test strings\n\nv1.5.14 (January 23, 2015)\n* Refactor EventDispatcher\n\nv1.5.13 (January 20, 2015)\n* Remove unused exports\n* Move definition of FontTestStrings to where it is first used\n\nv1.5.12 (January 19, 2015)\n* Revert using visibility hidden (caused strange artifacts on some sites on iOS browsers)\n* Fix compiler warnings\n\nv1.5.11 (January 7, 2015)\n* Add support for explicitly setting the protocol\n\nv1.5.10 (December 18, 2014)\n* Fix parameters passed to Typekit KitJS\n\nv1.5.9 (December 18, 2014)\n* Use visibility hidden instead of off-screen positioning\n* Decouple Typekit internal API\n* Disable native font loading (see comment)\n\nv1.5.8 (November 17, 2014)\n* Add prebuilt library to repository\n\nv1.5.7 (November 17, 2014)\n* Decrease default timeout to 3 seconds\n* Add support for native font loading\n\nv1.5.6 (September 3, 2014)\n* Fix double inactive event when all modules fail to load\n\nv1.5.5 (June 5, 2014)\n* Add support for the Chromecast browser\n\nv1.5.4 (May 2, 2014)\n* Add support for the PlayStation 4 browser\n\nv1.5.3 (April 8, 2014)\n* Prevent a potential FOUT when setting the font event classes.\n* Add \"display:block\" to test rulers so \"* { display: none; }\" doesn't override it\n\nv1.5.2 (January 3, 2014)\n* Add Copyright, License and Version to the compiled output file(s)\n* Fix small bug in Google Font module that rejected font variations with dashes\n\nv1.5.1 (October 15, 2013)\n* Dispatch wf-loading event synchronously instead of waiting for asynchronous modules\n* Update README explaining how to use custom fonts without a stylesheet\n* Add license information to gemspec\n\nv1.5.0 (September 9, 2013)\n* Correctly categorize Internet Explorer 11 as an Internet Explorer version.\n* Add support for custom test strings in the custom module.\n* Various small bug fixes to the test suite so it runs on all browsers automatically.\n\nv1.4.12 (August 21, 2013)\n* Cleared up initialization code and made all modules optional and configurable at compile time\n\nv1.4.11 (August 8, 2013)\n* Remove Ascender module (according to Monotype the service has now been shutdown completely.)\n* Switch Monotype module to use fast.fonts.net instead of fast.fonts.com\n* Update license\n\nv1.4.10 (July 31, 2013)\n* Add loadStylesheet method with optional callback that replaces createCssLink\n* Remove supportForStyle check because it breaks XHTML/SVG and is no longer necessary\n* Fix inactive never called when script loading times out\n* Move test assets into fixtures directory\n\nv1.4.9 (July 24, 2013)\n* Disable terminal spec report when not using PhantomJS\n* Remove obsolete namespace declaration\n\nv1.4.8 (June 24, 2013)\n* Add support for the Chromium based Opera browser\n* Change the debug task to do a Closure Compiler debug build\n* Fix a global variable leak in the compiled output\n* Add support for the PhantomJS user agent string\n\nv1.4.7 (June 6, 2013)\n* Fix backwards compatibility with version strings for Chrome\n* Restore specs that test against version strings for backwards compatibility with the old UserAgent API.\n\nv1.4.6 (May 29, 2013)\n* Merge font watching strategies from core and the Google module\n* Add support for the Samsung Galaxy S4 user agent string\n\nv1.4.5 (May 23, 2013)\n* Move modules into their own namespace\n* Add new methods into DomHelper and add specs for all DomHelper methods\n* Rename watch method so as not to conflict with Firefox Object.prototype.watch\n\nv1.4.4 (May 22, 2013)\n* Change the UserAgent API so that it is backwards compatible with older Typekit kits.\n\nv1.4.3 (May 16, 2013)\n* UserAgent now maintains version numbers as instances of the Version class.\n\nv1.4.2 (April 11, 2013)\n* Namespace is now configurable at compile time\n* BrowserInfo and UserAgent are exported so Typekit KitJS and the standalone webfontloader can share data\n* Add \"aria-hidden=true\" to test spans so screen readers do not read test spans\n* Fix passing the test strings from the modules to core.\n\nv1.4.1 (April 8, 2013)\n* Internal rewrite of font and variations\n* Fix for the configurable timeout on the Google module\n\nv1.4.0 (March 28, 2013)\n* Stop exporting the `addModule` API to dynamically add modules (it didn't work anyway.)\n* Stop checking the height when monitoring for font load. This turned out to be inconsistent across platforms.\n\nv1.3.2 (March 27, 2013)\n* Add support for the Amazon 1 and 2+ browsers.\n\nv1.3.1 (March 14, 2013)\n* Change code to use explicit dependencies\n* Fix unit tests in older browsers\n* Fix google/FontApiParser.js to work in IE <= 8\n\nv1.3.0 (February 28, 2013)\n* New optional configuration parameter `timeout` which lets you customize the default timeout.\n* Change the Typekit module to use `use.typekit.net` instead of `use.typekit.com`.\n* Disable height check on OSX and iOS WebKit based browsers which suffer from a metrics bug when loading web fonts.\n\nv1.2.1 (February 26, 2013)\n* Fix the possibility of test strings wrapping to a new line and thereby breaking font watching.\n* Change the FontWatchRunner to not create DOM elements until it is started.\n* Fix the possibility of extraneous whitespace in class names.\n* Add a cache buster parameter to the Monotype/Fonts.com module.\n* Fix the case where there are no fonts to load. Webfontloader will now fire the inactive event correctly.\n* Test suite now uses the active browser to test font watching in addition to the mocked font watching tests.\n* Test suite is now using Jasmine instead of JSTestDriver.\n\nv1.2.0 (January 30, 2013)\n* Improved font watching for browsers with the WebKit web font fallback bug\n* Improved font watching in general by comparing both width and height\n* UserAgent user interface has changed with the introduction of a BrowserInfo object that contains information derived from the user agent string\n* The custom module now supports loading font variations\n\nv1.1.2 (January 21, 2013)\n* Added parameter to Google module to do character based subsetting.\n* Made WebKit useragent check less strict about capitalization for LG L160 phones that apparently use 'AppleWebkit' instead of 'AppleWebKit' in their useragent strings.\n\nv1.1.1 (December 12, 2012)\n* Added the ability to recognize BlackBerry devices, which have web font support (WOFF) in platform version 10+\n* Added a new browser name \"BuiltinBrowser\" to recognize built-in browsers on mobile platforms which we previously called \"Safari\" (applies to Android's \"Browser\" app and BlackBerry's built-in browser)\n* Fixed a bug in the Monotype module which caused a double active event in IE9 and no active event in IE8 (reported in issue #64)\n* Fixed some typos in the demo pages\n\nv1.1.0 (December 5, 2012)\n* Adds the ability to load fonts into a same-origin child window or iframe using the new optional `context` configuration option (thanks to @ryanwolf of Google).\n* Updates the demos to fix broken stuff and demonstrate the new context option in action.\n* DomHelper interface changed to take the main window and an optional separate window for loading.\n* Methods added to retrieve both windows and get the correct protocol for assets from the location's protocol.\n\nv1.0.31 (September 11, 2012)\n* Improvements to Google's module to recognize more variation descriptor formats (such as 100italic and 200n).\n\nv1.0.30 (August 17, 2012)\n* Added support for detecting the Windows Phone platform in UserAgentParser, which supports web fonts at version 8 and above.\n* Changes to make the global namespace of the library easier to configure when used in 3rd-party projects. (Thanks cbosco!)\n\nv1.0.29 (July 26, 2012)\n* Added test to ensure Firefox for Android is properly detected as \"Firefox\" by UserAgentParser.\n* Added test to ensure Opera Mobile for Android is properly detected as \"Opera\" by UserAgentParser.\n* Changed detection so that Chrome for iOS is detected as \"Chrome\" instead of \"Safari\".\n* Changed detection so that Opera Mini is correctly detected as \"OperaMini\" (without font support) instead of \"Opera\" (with font support).\n* Fixed a bug in Google Web Fonts module when requesting a font family with character sets and no variations.\n* Scaled back the number of fall back fonts used in the Google Web Fonts font watching code.\n\nv1.0.28 (June 4, 2012)\n* Added support for detecting the Chrome OS platform (\"CrOS\") in the UserAgentParser.\n\nv1.0.27 (April 20, 2012)\n* Updated DomHelper to not require a UserAgent instance. Feature-detection is not used instead of UA detection for switching DOM methods.\n\nv1.0.26 (February 8, 2012)\n* Updated the included version of the Closure JS compiler jar to 1741, to handle newer annotation styles.\n* Added a missing param annotation for the new FontWatcher.watch argument.\n* Added param annotations for Google's custom FontWatchRunner implementation.\n* Updated the Google Web Fonts API parser to accept family names with a plus sign.\n\nv1.0.25 (February 7, 2012)\n* Updated the user agent parser to recognize Chrome for Android properly as a Chrome browser.\n\nv1.0.24 (January 9, 2012)\n* Updated the standard test string from \"BESs\" to \"BESbswy\" for more width variability.\n* Improved Google's custom FontWatchRunner implementation for Webkit to work around an issue where the browser reports the 400 weight's width when it is already loaded.\n\nv1.0.23 (November 29, 2011)\n* Made the FontWatchRunner implementation configurable on a module-by-module basis.\n* Added a new .start() method to FontWatchRunner to actually kick off font loading detection.\n* Added a different FontWatchRunner implementation that Google's module uses to work around a Webkit browser bug. This implementation won't trigger active early, but may trigger active much later, so it's not the default for all modules.\n* Updated the implementation of Fontdeck's module to defer loading responsibility to their JS.\n\nv1.0.22 (July 1, 2011)\n* Fixed a bug in Webkit-based browsers with font detection where active would trigger without the font actually being active yet.\n* Increased the frequency of checking the widths of the font watcher spans.\n\nv1.0.21 (June 17, 2011)\n* Added a protocol detect for the Typekit module so JS is loaded securely on secure pages. Thanks to bee525 for the pull request.\n\nv1.0.20 (March 30, 2011)\n* Changed CSS style for hidden span so it's not affected by inline or floated elements at the end of the body\n\nv1.0.19 (March 3, 2011)\n* Add a module for Monotype.\n\nv1.0.18 (January 24, 2011)\n* Improve behavior of CSS classes over multiple loads on a single page. (Documented in docs/EVENTS.md)\n* Add support for international subsets in the Google module.\n* Add a module for Fontdeck.\n\nv1.0.17 (December 1, 2010)\n* Changed CSS style for hidden span in order to be less affected by environment\n* Removed restriction on iPhone/iPad/iPod in the google modules\n\nv1.0.16 (November 18, 2010)\n* Fix a bug where we fail to detect that fonts have loaded if they have the same width as the fallback font.\n\nv1.0.15 (October 14, 2010)\n* Fix an error parsing platform version in IE, when it has no items following the platform in the UA string.\n\nv1.0.14 (October 14, 2010)\n* Bugfix: fixed IE issue in google module.\n\nv1.0.13 (September 30, 2010)\n* Added support for detecting Adobe Air.\n\nv1.0.12 (September 30, 2010)\n* Bugfix: google module, change the url builder to handle integrations.\n\nv1.0.10 (September 24, 2010)\n* Bugfix: extra alert\n\nv1.0.10 (September 22, 2010)\n* Add support for customizable FontWatcher test string, for international\n  fonts.\n\nv1.0.9 (September 10, 2010)\n* Bugfix: of the bug fix\n\nv1.0.8 (September 10, 2010)\n* Bugfix: fix type definitions\n\nv1.0.7 (August 31, 2010)\n* Fix that wf-loading was not removed in the case of wf-inactive because of\n  a timeout.\n* Add UserAgent#getDocumentMode to aid in determining font support in IE.\n\nv1.0.6 (July 20, 2010)\n* Add JsDoc comments and type annotations for the Closure compiler. Fixes\n  several small bugs caught by the compiler in doing so.\n\nv1.0.5 (July 12, 2010)\n* webfont.UserAgent now provides getPlatformVersion. WebFont Loader is now\n  packaged as a ruby gem.\n\nv1.0.4 (June 14, 2010)\n* Add a module for Ascender's Fonts Live.\n\nv1.0.3 (June 6, 2010)\n* IE fixes.\n\nv1.0.2 (June 1, 2010)\n* Added a way of loading the WebFont Loader script asynchronously.\n\nv1.0.1 (May 20, 2010)\n* Fix namespace pollution by wrapping all of the code in a closure.\n\nv1.0.0 (May 19, 2010)\n* Initial release!\n* Modules: google, typekit, custom\n* Events: loading, active, inactive, fontloading, fontactive, fontintactive\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nPlease open [an issue](https://github.com/typekit/webfontloader/issues) if you find or suspect any problems. Sample pages and test cases are greatly appreciated.\n\n## Development requirements\n\nYou'll need a few rubygems to run the tests, demo server, and other rake tasks, which should be installed with [Bundler](http://gembundler.com/).\n\n    $ gem install bundler\n    $ bundle install\n\nTo run the tests in a headless WebKit you will also need to have [PhantomJS](http://www.phantomjs.org) installed. You can install PhantomJS by downloading a binary or using HomeBrew.\n\n    $ brew install phantomjs\n\n## Building\n\nTo build a JS file from source, just run rake:\n\n    $ rake\n\nIf you want to build a JS file with only specific modules you can specify them on the command line:\n\n    $ rake compile['custom google typekit']\n\nThis will compile a JS file with only the `custom`, `google` and `typekit` modules. The available modules are: `custom`, `google`, `typekit`, `ascender`, `monotype`, `fontdeck`. By default all modules are included.\n\n## Demos\n\nA full suite of demo pages is included in this source. Here you can find some\nlive examples using the JS and CSS events. Run the demo server with:\n\n    $ rake demo\n\nYou can also run the demos with uncompressed, debuggable code to aid in\ndevelopment. Just start the server in dev mode:\n\n    $ rake demodev\n\nBrowse the demos [source code](http://github.com/typekit/webfontloader/blob/master/lib/webfontloader/demo/public).\n\n## Testing\n\nWeb Font Loader has an extensive test suite that runs via Jasmine. The test suite\nshould be passing before submitting a pull request, and new tests should be added for any new functionality.\n\nTo run tests, open up `spec/index.html` in a browser and check the results. The\ntest suite will run automatically. Again, before submitting a pull request\nplease run the test suite in multiple browsers and list them in the pull request.\n\nTo run tests in a headless WebKit using [PhantomJS](http://www.phantomjs.org) run:\n\n    $ rake test\n"
  },
  {
    "path": "Gemfile",
    "content": "source 'https://rubygems.org'\n\ngemspec\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "README.md",
    "content": "# Web Font Loader\n\nWeb Font Loader gives you added control when using linked fonts via `@font-face`. It provides a common interface to loading fonts regardless of the source, then adds a standard set of events you may use to control the loading experience. The Web Font Loader is able to load fonts from [Google Fonts](http://www.google.com/fonts/), [Typekit](http://www.typekit.com/),  [Fonts.com](http://www.fonts.com/), and [Fontdeck](http://fontdeck.com/), as well as self-hosted web fonts. It is co-developed by [Google](http://www.google.com/) and [Typekit](http://www.typekit.com).\n\n[![Build Status](https://travis-ci.org/typekit/webfontloader.svg?branch=master)](https://travis-ci.org/typekit/webfontloader)\n\n## Contents\n\n* [Get started](#get-started)\n* [Configuration](#configuration)\n    * [Events](#events)\n    * [Timeout](#timeouts)\n    * [Iframes](#iframes)\n* [Modules](#modules)\n    * [Adobe Edge Web Fonts](#adobe-edge-web-fonts)\n    * [Custom](#custom)\n    * [Fontdeck](#fontdeck)\n    * [Fonts.com](#fontscom)\n    * [Google](#google)\n    * [Typekit](#typekit)\n* [Browser support](#browser-support)\n* [License](#copyright-and-license)\n\n## Get Started\n\nTo use the Web Font Loader library, just include it in your page and tell it which fonts to load. For example, you could load fonts from [Google Fonts](http://www.google.com/fonts/) using the Web Font Loader hosted on [Google Hosted Libraries](https://developers.google.com/speed/libraries/) using the following code.\n\n```html\n<script src=\"https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js\"></script>\n<script>\n  WebFont.load({\n    google: {\n      families: ['Droid Sans', 'Droid Serif']\n    }\n  });\n</script>\n```\n\nAlternatively, you can link to the latest `1.x` version of the Web Font Loader by using `https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js` as the `script` source. Note that the version in this url is less specific. It will always load the latest `1.x` version, but it also has a shorter cache time to ensure that your page gets updates in a timely manner. For performance reasons, we recommend using an explicit version number (such as `1.6.26`) in urls when using the Web Font Loader in production. You can manually update the Web Font Loader version number in the url when you want to adopt a new version.\n\nWeb Font Loader is also available on the [jsDelivr](http://www.jsdelivr.com/projects/webfontloader) & [CDNJS](https://cdnjs.com/libraries/webfont) CDNs.\n\nIt is also possible to use the Web Font Loader asynchronously. For example, to load [Typekit](http://www.typekit.com) fonts asynchronously, you could use the following code.\n\n```html\n<script>\n   WebFontConfig = {\n      typekit: { id: 'xxxxxx' }\n   };\n\n   (function(d) {\n      var wf = d.createElement('script'), s = d.scripts[0];\n      wf.src = 'https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js';\n      wf.async = true;\n      s.parentNode.insertBefore(wf, s);\n   })(document);\n</script>\n```\n\nUsing the Web Font Loader asynchronously avoids blocking your page while loading the JavaScript. Be aware that if the script is used asynchronously, the rest of the page might render before the Web Font Loader is loaded and executed, which can cause a [Flash of Unstyled Text (FOUT)](http://help.typekit.com/customer/portal/articles/6852).\n\nThe FOUT can be more easily avoided when loading the Web Font Loader synchronously, as it will automatically set the `wf-loading` class on the HTML element as soon as `Webfont.load` has been called. The browser will wait for the script to load before continuing to load the rest of the content, FOUT is avoided.\n\nWeb Font Loader is also available on npm as a CommonJS module. Just `npm install webfontloader` and then require it in your code.\n\n```js\n  var WebFont = require('webfontloader');\n\n  WebFont.load({\n    google: {\n      families: ['Droid Sans', 'Droid Serif']\n    }\n  });\n```\n\n## Configuration\n\nThe Web Font Loader configuration is defined by a global variable named `WebFontConfig`, or passed directly to the `WebFont.load` method. It defines which fonts to load from each web font provider and gives you the option to specify callbacks for certain events. When using the asynchronous approach, you must define the global variable `WebFontConfig` before the code that loads the Web Font Loader (as in the example above).\n\n### Events\n\nWeb Font Loader provides an event system that developers can hook into. It gives you notifications of the font loading sequence in both CSS and JavaScript.\n\n  * `loading` - This event is triggered when all fonts have been requested.\n  * `active` - This event is triggered when the fonts have rendered.\n  * `inactive` - This event is triggered when the browser does not support linked fonts *or* if none of the fonts could be loaded.\n  * `fontloading` - This event is triggered once for each font that's loaded.\n  * `fontactive` - This event is triggered once for each font that renders.\n  * `fontinactive` - This event is triggered if the font can't be loaded.\n\nCSS events are implemented as classes on the `html` element. The following classes are set on the `html` element:\n\n```css\n.wf-loading\n.wf-active\n.wf-inactive\n.wf-<familyname>-<fvd>-loading\n.wf-<familyname>-<fvd>-active\n.wf-<familyname>-<fvd>-inactive\n```\n\nThe `<familyname>` placeholder will be replaced by a sanitized version of the name of each font family. Spaces and underscores are removed from the name, and all characters are converted to lower case. For example, `Droid Sans` becomes `droidsans`. The `<fvd>` placeholder is a [Font Variation Description](https://github.com/typekit/fvd). Put simply, it's a shorthand for describing the style and weight of a particular font. Here are a few examples:\n\n```css\n/* n4 */\n@font-face { font-style: normal; font-weight: normal; }\n\n/* i7 */\n@font-face { font-style: italic; font-weight: bold; }\n```\n\nKeep in mind that `font-weight: normal` maps to `font-weight: 400` and `font-weight: bold` maps to `font-weight: 700`. If no style/weight is specified, the default `n4` (`font-style: normal; font-weight: normal;`) will be used.\n\nIf fonts are loaded multiple times on a single page, the CSS classes continue to update to reflect the current state of the page. The global `wf-loading` class is applied whenever fonts are being requested (even if other fonts are already active or inactive). The `wf-inactive` class is applied only if none of the fonts on the page have rendered. Otherwise, the `wf-active` class is applied (even if some fonts are inactive).\n\nJavaScript events are implemented as callback functions on the `WebFontConfig` configuration object.\n\n```javascript\nWebFontConfig = {\n  loading: function() {},\n  active: function() {},\n  inactive: function() {},\n  fontloading: function(familyName, fvd) {},\n  fontactive: function(familyName, fvd) {},\n  fontinactive: function(familyName, fvd) {}\n};\n```\n\nThe `fontloading`, `fontactive` and `fontinactive` callbacks are passed the family name and font variation description of the font that concerns the event.\n\nIt is possible to disable setting classes on the HTML element by setting the `classes` configuration parameter to `false` (defaults to `true`).\n\n```javascript\nWebFontConfig = {\n  classes: false\n};\n```\n\nYou can also disable font events (callbacks) by setting the `events` parameter to `false` (defaults to `true`).\n\n```javascript\nWebFontConfig = {\n  events: false\n};\n```\n\nIf both events and classes are disabled, the Web Font Loader does not perform font watching and only acts as a way to insert @font-face rules in the document.\n\n### Timeouts\n\nSince the Internet is not 100% reliable, it's possible that a font will fail to load. The `fontinactive` event will be triggered after 5 seconds if the font fails to render. If *at least* one font successfully renders, the `active` event will be triggered, else the `inactive` event will be triggered.\n\nYou can change the default timeout by using the `timeout` option on the `WebFontConfig` object.\n\n```javascript\nWebFontConfig = {\n  google: {\n    families: ['Droid Sans']\n  },\n  timeout: 2000 // Set the timeout to two seconds\n};\n```\n\nThe timeout value should be in milliseconds, and defaults to 3000 milliseconds (3 seconds) if not supplied.\n\n### Iframes\n\nUsually, it's easiest to include a copy of Web Font Loader in every window where fonts are needed, so that each window manages its own fonts. However, if you need to have a single window manage fonts for multiple same-origin child windows or iframes that are built up using JavaScript, Web Font Loader supports that as well. Just use the optional `context` configuration option and give it a reference to the target window for loading:\n\n```javascript\nWebFontConfig = {\n  google: {\n    families: ['Droid Sans']\n  },\n  context: frames['my-child']\n};\n```\n\nThis is an advanced configuration option that isn't needed for most use cases.\n\n## Modules\n\nWeb Font Loader provides a module system so that any web font provider can contribute code that allows their fonts to be loaded. This makes it possible to use multiple web font providers at the same time. The specifics of each provider currently supported by the library are documented here.\n\n### Adobe Edge Web Fonts\n\nWhen using [Adobe Edge Web Fonts](https://edgewebfonts.adobe.com/), you can use the `typekit` module by passing in a catenated list of fonts in the `id` parameter and set the `api` parameter to point to the Edge Web Fonts URL.\n\n```javascript\nWebFontConfig = {\n  typekit: {\n    id: 'adamina;advent-pro',\n    api: '//use.edgefonts.net'\n  }\n};\n```\n\n### Custom\n\nTo load fonts from any external stylesheet, use the `custom` module. Here you'll\nneed to specify the font family names you're trying to load, and optionally the url of the stylesheet that provides the `@font-face` declarations for those fonts.\n\nYou can specify a specific font variation or set of variations to load and watch\nby appending the variations separated by commas to the family name separated by\na colon. Variations are specified using [FVD notation](https://github.com/typekit/fvd).\n\n```javascript\nWebFontConfig = {\n  custom: {\n    families: ['My Font', 'My Other Font:n4,i4,n7'],\n    urls: ['/fonts.css']\n  }\n};\n```\n\nIn this example, the `fonts.css` file might look something like this:\n\n```css\n@font-face {\n  font-family: 'My Font';\n  src: ...;\n}\n@font-face {\n  font-family: 'My Other Font';\n  font-style: normal;\n  font-weight: normal; /* or 400 */\n  src: ...;\n}\n@font-face {\n  font-family: 'My Other Font';\n  font-style: italic;\n  font-weight: normal; /* or 400 */\n  src: ...;\n}\n@font-face {\n  font-family: 'My Other Font';\n  font-style: normal;\n  font-weight: bold; /* or 700 */\n  src: ...;\n}\n```\n\nIf your fonts are already included in another stylesheet you can also leave out the `urls` array and just specify font family names to start font loading. As long as the names match those that are declared in the `families` array, the proper loading classes will be applied to the html element.\n\n```html\n<script src=\"https://ajax.googleapis.com/ajax/libs/webfont/1.5.10/webfont.js\"></script>\n<script>\n  WebFont.load({\n    custom: {\n      families: ['My Font']\n    }\n  });\n</script>\n\n<style type=\"text/css\">\n  @font-face {\n    font-family:\"My Font\";\n    src:url(\"assets/fonts/my_font.woff\") format(\"woff\");\n  }\n</style>\n```\n\nThe custom module also supports customizing the test strings that are used to determine whether or not a font has loaded. This can be used to load fonts with custom subsets or glyphs in the private use unicode area.\n\n```javascript\nWebFontConfig = {\n  custom: {\n    families: ['My Font'],\n    testStrings: {\n      'My Font': '\\uE003\\uE005'\n    }\n  }\n};\n```\n\nTests strings should be specified on a per font basis and contain at least one character. If not specified the default test string (`BESbswy`) is used.\n\n### Fontdeck\n\nTo use the [Fontdeck](http://fontdeck.com/) module, specify the ID of your website. You can find this ID on the website page within your account settings.\n\n```javascript\nWebFontConfig = {\n  fontdeck: {\n    id: 'xxxxx'\n  }\n};\n```\n\n### Fonts.com\n\nWhen using [Fonts.com web fonts](http://www.fonts.com/web-fonts/) specify your Project ID.\n\n```javascript\nWebFontConfig = {\n  monotype: {\n    projectId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',\n    version: 12345, // (optional, flushes the CDN cache)\n    loadAllFonts: true //(optional, loads all project fonts)\n  }\n};\n```\n\nThe Fonts.com module has an optional `version` option which acts as a cache-buster, optional `loadAllFonts` loads all project fonts. By default, Fonts.com module loads only fonts used on the page.\n\n### Google\n\nUsing [Google's Font API](https://code.google.com/apis/webfonts/docs/getting_started.html), name the font families you'd like to load. You can use the same [syntax](https://developers.google.com/fonts/docs/getting_started#Syntax) as in the Font API to specify styles. Please note that the Google module does not support the FVD syntax that is used in the custom module. \n\n```javascript\nWebFontConfig = {\n  google: {\n    families: ['Droid Sans', 'Droid Serif:bold']\n  }\n};\n```\n\nSometimes the font you requested doesn't come in the default weight (e.g. 400) and you need to explicitly request the variation you want for font events to work (e.g. `300`, `700`, etc.):\n\n```javascript\nWebFontConfig = {\n  google: {\n    families: ['Open Sans Condensed:300,700']\n  }\n};\n```\n\nIf you need to specify character subsets other than the default (e.g.: greek script in addition to latin), you must append the subset string to the requested family string after a colon. The subset string should follow the [Google documentation](https://developers.google.com/fonts/docs/getting_started#Subsets) (subset names separated by commas):\n\n```javascript\nWebFontConfig = {\n  google: {\n    families: ['Open Sans Condensed:300,700:latin,greek']\n  }\n};\n```\n\nYou can also supply the `text` parameter to perform character subsetting:\n\n```javascript\nWebFontConfig = {\n  google: {\n    families: ['Droid Sans', 'Droid Serif'],\n    text: 'abcdefghijklmnopqrstuvwxyz!'\n  }\n};\n```\n\nThe `text` subsetting functionality is only available for the Google module.\n\n### Typekit\n\nWhen using [Typekit](http://www.typekit.com), specify the Kit to retrieve by its ID. You can find the Kit ID within Typekit's Kit Editor interface.\n\n```javascript\nWebFontConfig = {\n  typekit: {\n    id: 'xxxxxx'\n  }\n};\n```\n\n**FYI:** Typekit's own JavaScript is built using the Web Font Loader library and already provides all of the same font event functionality. If you're using Typekit, you should use their embed codes directly unless you also need to load web fonts from other providers on the same page.\n\n## Browser Support\n\nEvery web browser has varying levels of support for fonts linked via `@font-face`. Web Font Loader determines support for web fonts is using the browser's user agent string. The user agent string may claim to support a web font format when it in fact does not. This is especially noticeable on mobile browsers with a \"Desktop\" mode, which usually identify as Chrome on Linux. In this case a web font provider may decide to send WOFF fonts to the device because the real desktop Chrome supports it, while the mobile browser does not. The Web Font Loader is not designed to handle these cases and it defaults to believing what's in the user agent string. Web font providers can build on top of the basic Web Font Loader functionality to handle these special cases individually.\n\nIf Web Font Loader determines that the current browser does not support `@font-face`, the `inactive` event will be triggered.\n\nWhen loading fonts from multiple providers, each provider may or may not support a given browser. If Web Font Loader determines that the current browser can support `@font-face`, and *at least* one provider is able to serve fonts, the fonts from that provider will be loaded. When finished, the `active` event will be triggered.\n\nFor fonts loaded from supported providers, the `fontactive` event will be triggered. For fonts loaded from a provider that *does not* support the current browser, the `fontinactive` event will be triggered.\n\nFor example:\n\n```javascript\nWebFontConfig = {\n  providerA: 'Family1',\n  providerB: 'Family2'\n};\n```\n\nIf `providerA` can serve fonts to a browser, but `providerB` cannot, The `fontinactive` event will be triggered for `Family2`. The `fontactive` event will be triggered for `Family1` once it loads, as will the `active` event.\n\n## Copyright and License\n\nWeb Font Loader Copyright (c) 2010-2017 Adobe Systems Incorporated, Google Incorporated.\n\nLicensed 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\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless 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.\n"
  },
  {
    "path": "Rakefile",
    "content": "require 'rubygems'\nrequire 'rake'\nrequire 'date'\n\n#############################################################################\n#\n# Helper functions\n#\n#############################################################################\n\ndef name\n  @name ||= Dir['*.gemspec'].first.split('.').first\nend\n\ndef version\n  line = File.read(\"lib/#{name}.rb\")[/^\\s*VERSION\\s*=\\s*.*/]\n  line.match(/.*VERSION\\s*=\\s*['\"](.*)['\"]/)[1]\nend\n\ndef date\n  Date.today.to_s\nend\n\ndef gemspec_file\n  \"#{name}.gemspec\"\nend\n\ndef gem_file\n  \"#{name}-#{version}.gem\"\nend\n\ndef replace_header(head, header_name)\n  head.sub!(/(\\.#{header_name}\\s*= ').*'/) { \"#{$1}#{send(header_name)}'\"}\nend\n\n#############################################################################\n#\n# Standard tasks\n#\n#############################################################################\n\ndesc \"Open an irb session preloaded with this library\"\ntask :console do\n  sh \"irb -rubygems -r ./lib/#{name}.rb\"\nend\n\n#############################################################################\n#\n# Custom tasks (add your own tasks here)\n#\n#############################################################################\n\nrequire 'rake/clean'\n\n$LOAD_PATH.unshift File.dirname(__FILE__) + \"/lib\"\nrequire 'webfontloader'\n\n#\n# Setup\n#\n\n# Build targets (remove with `rake clean`)\nCLEAN.include(\"target\")\nCLEAN.include(\"tmp\")\n\n# JsCompiler\nJsCompilerJar = \"tools/compiler/compiler.jar\"\n\n# JS Source dependencies\nAllJs = FileList[\"{src,src-test}/**/*\"]\nSourceJs  = FileList[\"src/**/*\"]\n\n# JS Source loader\n@modules = WebFontLoader::Modules.new\n\n#\n# Build\n#\n\ndirectory \"target\"\ndirectory \"tmp\"\n\ndesc \"Compile the JavaScript into target/webfont.js\"\ntask :compile, [:modules] => \"target/webfont.js\"\n\nfile \"webfontloader.js\" => \"target/webfont.js\" do\n  cp \"target/webfont.js\", \"webfontloader.js\"\nend\n\nfile \"target/webfont.js\", [:modules] => SourceJs + [\"target\"] do |t, args|\n  args.with_defaults(:modules => 'custom google typekit monotype fontdeck')\n\n  modules = args[:modules].split ' '\n\n  output_marker = \"%output%\"\n  output_wrapper = @modules.js_output_wrapper(output_marker, version)\n\n  args = [\n    [\"-jar\", JsCompilerJar],\n    [\"--compilation_level\", \"ADVANCED_OPTIMIZATIONS\"],\n    [\"--js_output_file\", t.name],\n    [\"--output_wrapper\", %(\"#{output_wrapper}\")],\n    [\"--warning_level\", \"VERBOSE\"],\n    [\"--summary_detail_level\", \"3\"],\n    [\"--externs\", \"externs.js\"],\n    \"--define goog.DEBUG=false\"\n  ]\n\n  args.concat modules.map { |m| \"--define INCLUDE_\" + m.upcase + \"_MODULE\" }\n\n  # Extra args to add warnings.\n  args.concat([\n    [\"--warning_level\", \"VERBOSE\"],\n    [\"--summary_detail_level\", \"1\"]\n  ])\n\n  source = @modules.all_source_files\n  args.concat source.map { |f| [\"--js\", f] }\n\n  output = `java #{args.flatten.join(' ')} 2>&1`\n  $?.success? ? (puts output) : (fail output)\nend\n\ndesc \"Creates debug version into target/webfont.js\"\ntask :debug, [:modules] => \"target/webfont_debug.js\"\n\nfile \"target/webfont_debug.js\", [:modules] => SourceJs + [\"target\"] do |t, args|\n  args.with_defaults(:modules => 'custom google typekit monotype fontdeck')\n\n  modules = args[:modules].split ' '\n\n  output_marker = \"%output%\"\n  output_wrapper = @modules.js_output_wrapper(output_marker, version)\n\n  args = [\n    [\"-jar\", JsCompilerJar],\n    [\"--compilation_level\", \"ADVANCED_OPTIMIZATIONS\"],\n    [\"--js_output_file\", t.name],\n    [\"--output_wrapper\", %(\"#{output_wrapper}\")],\n    [\"--warning_level\", \"VERBOSE\"],\n    [\"--summary_detail_level\", \"3\"],\n    [\"--externs\", \"externs.js\"],\n    \"--debug=true\",\n    \"--formatting=PRETTY_PRINT\",\n    \"--formatting=PRINT_INPUT_DELIMITER\"\n  ]\n\n  args.concat modules.map { |m| \"--define INCLUDE_\" + m.upcase + \"_MODULE\" }\n\n  # Extra args to add warnings.\n  args.concat([\n    [\"--warning_level\", \"VERBOSE\"],\n    [\"--summary_detail_level\", \"1\"]\n  ])\n\n  source = @modules.all_source_files\n  args.concat source.map { |f| [\"--js\", f] }\n\n  output = `java #{args.flatten.join(' ')} 2>&1`\n  $?.success? ? (puts output) : (fail output)\nend\n\n#\n# Run\n#\ndesc \"BrowserStack tests\"\ntask :bstest do |t|\n  exec \"browserstack-test -u $BROWSERSTACK_USERNAME -p $BROWSERSTACK_PASSWORD -k $BROWSERSTACK_KEY -b browsers.json -t 300 http://localhost:9999/spec/index.html\"\nend\n\ndesc \"Test everything\"\ntask :default => [:clean, :gzipbytes, :test]\n\ndesc \"Run all tests\"\ntask :test do |t|\n  exec \"phantomjs tools/jasmine-phantomjs/jasmine-phantomjs.js spec/index.html\"\nend\n\ndesc \"Start the demo server\"\ntask :demo => \"target/webfont.js\" do |t|\n  js = t.prerequisites.first\n  exec \"bin/webfontloader-demos -F --compiled_js #{js}\"\nend\n\ndesc \"Start the demo server for development\"\ntask :demodev do\n  exec \"bin/webfontloader-demos -F -L --modules\"\nend\n\ndesc \"Find out how many bytes the source is\"\ntask :bytes => [:clean, \"target/webfont.js\"] do |t|\n  js = t.prerequisites.last\n  bytes = File.read(js).size\n  puts \"#{bytes} bytes uncompressed\"\nend\n\ndesc \"Find out how many bytes the source is when gzipped\"\ntask :gzipbytes => [:clean, \"target/webfont.js\"] do |t|\n  require 'zlib'\n  js = t.prerequisites.last\n  bytes = Zlib::Deflate.deflate(File.read(js)).size\n  puts \"#{bytes} bytes gzipped\"\nend\n\n\n#############################################################################\n#\n# Packaging tasks\n#\n#############################################################################\n\ntask :release => [:build] do\n  unless `git branch` =~ /^\\* master$/\n    puts \"You must be on the master branch to release!\"\n    exit!\n  end\n  sh \"git add webfontloader.js\"\n  sh \"git commit --allow-empty -a -m 'Release #{version}'\"\n  sh \"npm version #{version}\"\n  sh \"git push --tags origin master\"\n  sh \"gem push pkg/#{name}-#{version}.gem\"\n  sh \"npm publish\"\nend\n\ntask :build => :gemspec do\n  Rake::Task[\"target/webfont.js\"].execute\n  Rake::Task[\"webfontloader.js\"].execute\n  sh \"mkdir -p pkg\"\n  sh \"gem build #{gemspec_file}\"\n  sh \"mv #{gem_file} pkg\"\nend\n\ntask :gemspec => :validate do\n  # read spec file and split out manifest section\n  spec = File.read(gemspec_file)\n  head, manifest, tail = spec.split(\"  # = MANIFEST =\\n\")\n\n  # replace name version and date\n  replace_header(head, :name)\n  replace_header(head, :version)\n  replace_header(head, :date)\n\n  # determine file list from git ls-files\n  files = `git ls-files`.\n    split(\"\\n\").\n    sort.\n    reject { |file| file =~ /^\\./ }.\n    reject { |file| file =~ /^(rdoc|pkg)/ }.\n    map { |file| \"    #{file.gsub(/\\s/, '\\ ')}\" }.\n    join(\"\\n\")\n\n  # piece file back together and write\n  manifest = \"  s.files = %w[\\n#{files}\\n  ]\\n\"\n  spec = [head, manifest, tail].join(\"  # = MANIFEST =\\n\")\n  File.open(gemspec_file, 'w') { |io| io.write(spec) }\n  puts \"Updated #{gemspec_file}\"\nend\n\ntask :validate do\n  libfiles = Dir['lib/*'] - [\"lib/#{name}.rb\", \"lib/#{name}\"]\n  unless libfiles.empty?\n    puts \"Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir.\"\n    exit!\n  end\n  unless Dir['VERSION*'].empty?\n    puts \"A `VERSION` file at root level violates Gem best practices.\"\n    exit!\n  end\nend\n"
  },
  {
    "path": "bin/webfontloader-demos",
    "content": "#!/usr/bin/env ruby\n\nrequire 'rubygems'\n\n$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + \"/../lib\")\nrequire 'webfontloader'\n\nbegin\n  require 'webfontloader/demo/server'\nrescue LoadError => e\n  abort \"Please gem install sinatra\"\nend\n\nbegin\n  require 'vegas'\nrescue LoadError => e\n  abort \"Please gem install vegas\"\nend\n\nVegas::Runner.new(WebFontLoader::Demo::Server, 'font-demos', :host => \"localhost\") do |runner, opts, app|\n  opts.on('--compiled_js FILE', \"Dynamically build the JS with the given modules\") { |file|\n    app.set :compiled_js, File.read(file)\n  }\n  opts.on('--modules [MODULES]', \"Dynamically build the JS with the given modules\") { |opt_modules|\n    modules = opt_modules ? opt_modules.split(\",\") : []\n    app.set :modules, WebFontLoader::Modules.new(*modules)\n  }\nend\n"
  },
  {
    "path": "bower.json",
    "content": "{\n  \"name\": \"webfontloader\",\n  \"main\": \"webfontloader.js\",\n  \"description\": \"Web Font Loader gives you added control when using linked fonts via @font-face.\",\n  \"moduleType\": [\"amd\", \"node\"],\n  \"license\": \"Apache-2.0\",\n  \"ignore\": [\n    \"**/.*\",\n    \"node_modules\",\n    \"spec\",\n    \"tools\",\n    \"lib\",\n    \"bin\"\n  ],\n  \"keywords\": [\n    \"web\",\n    \"fonts\",\n    \"webfonts\",\n    \"font\",\n    \"loader\",\n    \"@font-face\"\n  ]\n}\n"
  },
  {
    "path": "browsers.json",
    "content": "[\n  {\n    \"os_version\": \"XP\",\n    \"os\": \"Windows\",\n    \"browser_version\": \"12.16\",\n    \"browser\": \"opera\"\n  },\n  {\n    \"os_version\": \"XP\",\n    \"os\": \"Windows\",\n    \"browser_version\": \"3.6\",\n    \"browser\": \"firefox\"\n  },\n  {\n    \"os_version\": \"XP\",\n    \"os\": \"Windows\",\n    \"browser_version\": \"20.0\",\n    \"browser\": \"firefox\"\n  },\n  {\n    \"os_version\": \"XP\",\n    \"os\": \"Windows\",\n    \"browser_version\": \"6.0\",\n    \"browser\": \"ie\",\n    \"timeout\": \"300\"\n  },\n  {\n    \"os_version\": \"XP\",\n    \"os\": \"Windows\",\n    \"browser_version\": \"7.0\",\n    \"browser\": \"ie\",\n    \"timeout\": \"300\"\n  },\n  {\n    \"os_version\": \"XP\",\n    \"os\": \"Windows\",\n    \"browser_version\": \"8.0\",\n    \"browser\": \"ie\"\n  },\n  {\n    \"os_version\": \"8\",\n    \"os\": \"Windows\",\n    \"browser_version\": \"10.0 Desktop\",\n    \"browser\": \"ie\"\n  },\n  {\n    \"os_version\": \"7\",\n    \"os\": \"Windows\",\n    \"browser_version\": \"9.0\",\n    \"browser\": \"ie\"\n  },\n  {\n    \"os_version\": \"Mountain Lion\",\n    \"os\": \"OS X\",\n    \"browser_version\": \"6.0\",\n    \"browser\": \"safari\"\n  },\n  {\n    \"os_version\": \"Mountain Lion\",\n    \"os\": \"OS X\",\n    \"browser_version\": \"25.0\",\n    \"browser\": \"chrome\"\n  },\n  {\n    \"os_version\": \"Snow Leopard\",\n    \"os\": \"OS X\",\n    \"browser_version\": \"5.0\",\n    \"browser\": \"safari\"\n  },\n  {\n    \"os_version\": \"5.0\",\n    \"device\": \"iPad 2 (5.0)\",\n    \"os\": \"ios\",\n    \"browser\": \"Mobile Safari\",\n    \"timeout\": \"300\"\n  },\n  {\n    \"os_version\": \"5.1\",\n    \"device\": \"iPad 3rd\",\n    \"os\": \"ios\",\n    \"browser\": \"Mobile Safari\",\n    \"timeout\": \"300\"\n  },\n  {\n    \"os_version\": \"6.0\",\n    \"device\": \"iPhone 5\",\n    \"os\": \"ios\",\n    \"browser\": \"Mobile Safari\",\n    \"timeout\": \"300\"\n  },\n  {\n    \"os_version\": \"4.3.2\",\n    \"device\": \"iPad 2\",\n    \"os\": \"ios\",\n    \"browser\": \"Mobile Safari\",\n    \"timeout\": \"300\"\n  },\n  {\n    \"os_version\": \"2.3\",\n    \"device\": \"Samsung Galaxy S II\",\n    \"os\": \"android\",\n    \"browser\": \"Android Browser\",\n    \"timeout\": \"300\"\n  },\n  {\n    \"os_version\": \"2.2\",\n    \"device\": \"Samsung Galaxy S\",\n    \"os\": \"android\",\n    \"browser\": \"Android Browser\",\n    \"timeout\": \"300\"\n  },\n  {\n    \"os_version\": \"4.2\",\n    \"device\": \"LG Nexus 4\",\n    \"os\": \"android\",\n    \"browser\": \"Android Browser\",\n    \"timeout\": \"300\"\n  },\n  {\n    \"os_version\": \"4.0\",\n    \"device\": \"Samsung Galaxy Nexus\",\n    \"os\": \"android\",\n    \"browser\": \"Android Browser\",\n    \"timeout\": \"300\"\n  },\n  {\n    \"os_version\": \"4.1\",\n    \"device\": \"Samsung Galaxy S III\",\n    \"os\": \"android\",\n    \"browser\": \"Android Browser\",\n    \"timeout\": \"300\"\n  }\n]\n"
  },
  {
    "path": "externs.js",
    "content": "/**\n * @type {function(function():*)}\n */\nvar define;\n\n/**\n * @type {boolean?}\n */\ndefine.amd;\n\n/**\n * @type {Object}\n */\nvar module;\n\n/**\n * @type {Object?}\n */\nmodule.exports;\n"
  },
  {
    "path": "lib/webfontloader/demo/public/basic.css",
    "content": "body {\n  line-height: 1;\n  font-size: 14px;\n  margin: 10px;\n}\nh1 {\n  font-size: 5em;\n  margin: 0;\n}\n"
  },
  {
    "path": "lib/webfontloader/demo/public/blank.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\">\n    <title>Blank page</title>\n  </head>\n  <body>\n  </body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/custom-iframe.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <title>Custom Module</title>\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <style >\n    iframe {\n      height: 100px;\n      width: 100%;\n    }\n  </style>\n</head>\n<body>\n  <script type=\"text/javascript\">\n    function loaded() {\n      var child = frames[\"child\"];\n      child.document.body.innerHTML = \"<h1 style=\\\"font-family: 'ChunkFiveRegular'\\\">Hello World. I am ChunkFive.</h1>\";\n      WebFont.load({\n        custom: {\n          families: ['ChunkFiveRegular'],\n          urls : ['http://seanmcb.com/typekit/wfl/stylesheet.css']\n        },\n        context: child\n      });\n    }\n  </script>\n\n  <iframe name=\"child\" src=\"/blank.html\" onload=\"loaded()\"></iframe>\n\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/custom.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to show how fonts load from a custom module in a\n    child iframe.\n  </p>\n\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/custom.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <title>Custom Module</title>\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <style type=\"text/css\">\n\n    h1 {\n      font-family: 'ChunkFiveRegular';\n    }\n\n    /* All Class hooks */\n    #classes { color: #ddd; }\n    html.wf-loading #classes .Loading,\n    html.wf-active #classes .Active,\n    html.wf-inactive #classes .Inactive,\n    html.wf-chunkfiveregular-n4-loading #classes .ChunkFiveLoading,\n    html.wf-chunkfiveregular-n4-active #classes .ChunkFiveActive,\n    html.wf-chunkfiveregular-n4-inactive #classes .ChunkFiveInactive {\n      color: #000;\n    }\n\n  </style>\n</head>\n<body>\n  <h1 class=\"tagesschrift\">\n    Hello World. I am ChunkFive.\n  </h1>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/custom.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to show how fonts load from a custom module.\n  </p>\n  <h2>CSS Hook Status</h2>\n  <ul id=\"classes\">\n    <li class=\"Loading\">Loading</li>\n    <li class=\"Active\">Active</li>\n    <li class=\"Inactive\">Inactive</li>\n    <li class=\"ChunkFiveLoading\">ChunkFive Loading</li>\n    <li class=\"ChunkFiveActive\">ChunkFive Active</li>\n    <li class=\"ChunkFiveInactive\">ChunkFive Inactive</li>\n  </ul>\n  <h2>JavaScript Event Progress</h2>\n  <ol id=\"events\"></ol>\n\n  <!-- loading event may fire before page is completely ready -->\n  <script type=\"text/javascript\">\n    function progress(message) {\n      var output = document.getElementById('events');\n      if (output) {\n        var e = document.createElement('li');\n        e.innerHTML = message;\n        output.appendChild(e);\n      }\n      if (window.console && window.console.log) {\n        window.console.log(message);\n      }\n    }\n    WebFont.load({\n      custom: {\n        families: ['ChunkFiveRegular'],\n        urls : ['http://seanmcb.com/typekit/wfl/stylesheet.css']\n      },\n      loading: function() {\n        progress('loading');\n      },\n      active: function() {\n        progress('active');\n      },\n      inactive: function() {\n        progress('inactive');\n      },\n      fontloading: function(fontFamily, fontDescription) {\n        progress('fontloading: ' + fontFamily + ' (' + fontDescription + ')');\n      },\n      fontactive: function(fontFamily, fontDescription) {\n        progress('fontactive: ' + fontFamily + ' (' + fontDescription + ')');\n      },\n      fontinactive: function(fontFamily, fontDescription) {\n        progress('fontinactive: ' + fontFamily + ' (' + fontDescription + ')');\n      }\n    });\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/event-css-active-multiple.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <link href=\"/basic.css\" rel=\"stylesheet\" type=\"text/css\">\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <script type=\"text/javascript\">\n    WebFont.load({\n      google: {\n        families: ['Droid Sans', 'Tangerine']\n      }\n    });\n  </script>\n  <style type=\"text/css\">\n    h1 {\n      visibility: hidden;\n    }\n    html.wf-droidsans-n4-active h1.droid {\n      visibility: visible;\n      font-family: 'Droid Sans';\n    }\n    html.wf-tangerine-n4-active h1.tangerine {\n      visibility: visible;\n      font-family: 'Tangerine';\n    }\n  </style>\n</head>\n<body>\n  <h1 class=\"droid\">\n    Hello World. I am Droid Sans.\n  </h1>\n  <h1 class=\"tangerine\">\n    Hello World. I am Tangerine.\n  </h1>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/event-css-active-multiple.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to use CSS to show each part of the page when\n    its font has loaded.\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/event-css-active.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <link href=\"/basic.css\" rel=\"stylesheet\" type=\"text/css\">\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <script type=\"text/javascript\">\n    WebFont.load({\n      google: {\n        families: ['Droid Sans'],\n        api: '/fonts/api'\n      }\n    });\n  </script>\n  <style type=\"text/css\">\n    h1 {\n      visibility: hidden;\n    }\n    html.wf-active h1 {\n      visibility: visible;\n      font-family: 'Droid Sans';\n    }\n  </style>\n</head>\n<body>\n  <h1>\n    Hello World. I am Droid Sans.\n  </h1>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/event-css-active.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to use CSS to hide the headline until its font\n    has completely rendered.\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/event-css-inactive.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <link href=\"/basic.css\" rel=\"stylesheet\" type=\"text/css\">\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <script type=\"text/javascript\">\n    WebFont.load({\n      google: {\n        families: ['Droid Sans'],\n        api: '/fonts/api'\n      }\n    });\n  </script>\n  <style type=\"text/css\">\n    h1 {\n      font-family: sans-serif;\n      font-weight: normal;\n    }\n    html.wf-active h1 {\n      font-family: 'Droid Sans';\n    }\n  </style>\n</head>\n<body>\n  <h1>\n    Hello World. I am Droid Sans.\n  </h1>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/event-css-inactive.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to use CSS to present a fallback font until the custom font\n    has completely rendered.\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/event-css-loading.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <link href=\"/basic.css\" rel=\"stylesheet\" type=\"text/css\">\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <script type=\"text/javascript\">\n    WebFont.load({\n      google: {\n        families: ['Droid Sans'],\n        api: '/fonts/api',\n        blocking: false\n      }\n    });\n  </script>\n  <style type=\"text/css\">\n\n    /* Show and style the loading message while fonts are loading */\n    html.wf-loading #loading-message {\n      display: block;\n      color: #999;\n    }\n\n    /* Hide the content while fonts are loading */\n    html.wf-loading #content {\n      display: none;\n    }\n\n    /* Hide the loading message after fonts render */\n    html.wf-active #loading-message {\n      display: none;\n    }\n    html.wf-active h1 {\n      font-family: 'Droid Sans';\n    }\n  </style>\n</head>\n<body>\n  <h1 id=\"loading-message\">\n    I'm loading!\n  </h1>\n  <h1 id=\"content\">\n    Hello World. I am Droid Sans.\n  </h1>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/event-css-loading.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to use CSS to show a loading message while the \n    headline's font is loading, then hide the loading message and show the \n    headline once the font has rendered.\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/event-js-active.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <link href=\"/basic.css\" rel=\"stylesheet\" type=\"text/css\">\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <script type=\"text/javascript\">\n    WebFont.load({\n      google: {\n        families: ['Droid Sans'],\n        api: '/fonts/api'\n      },\n      active: function() {\n        var h1 = document.getElementsByTagName('h1')[0];\n        h1.style.visibility = 'visible';\n        h1.style.fontFamily = 'Droid Sans';\n      }\n    });\n  </script>\n  <style type=\"text/css\">\n    h1 {\n      visibility: hidden;\n    }\n  </style>\n</head>\n<body>\n  <h1>\n    Hello World. I am Droid Sans.\n  </h1>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/event-js-active.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to use a combination of JavaScript and CSS to\n    hide the headline until its font has completely rendered.\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/event-js-font-active.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <link href=\"/basic.css\" rel=\"stylesheet\" type=\"text/css\">\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <script type=\"text/javascript\">\n    WebFont.load({\n      google: {\n        families: ['Droid Sans'],\n        api: '/fonts/api'\n      },\n      fontactive: function(familyName, fontDescription) {\n        if (familyName == 'Droid Sans') {\n          var h1 = document.getElementsByTagName('h1')[0];\n          h1.innerHTML = 'Hello World. I am ' + familyName + ' (' + fontDescription + ')';\n        }\n      }\n    });\n  </script>\n  <style type=\"text/css\">\n    h1 {\n      font-family: 'Droid Sans';\n    }\n  </style>\n</head>\n<body>\n  <h1>\n    Hello World.\n  </h1>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/event-js-font-active.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to use JavaScript to manipulate the page when\n    a particular font loads.\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/event-js-loading.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <link href=\"/basic.css\" rel=\"stylesheet\" type=\"text/css\">\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <script type=\"text/javascript\">\n    WebFont.load({\n      google: {\n        families: ['Droid Sans'],\n        api: '/fonts/api'\n      },\n\n      /*\n       * Style the document while fonts are loading.\n       */\n      loading: function() {\n        // The <body> doesn't exist yet, so wait a moment.\n        setTimeout(function() {\n          document.body.style.color = '#999';\n        }, 10);\n      },\n\n      /*\n       * When fonts are rendered, hide the loading message, show the\n       * content, and change the style of the document.\n       */\n      active: function() {\n        var loadingMessage = document.getElementById('loading-message');\n        var content = document.getElementById('content');\n        loadingMessage.style.display = 'none';\n        content.style.display = 'block';\n        document.body.style.color = '#000';\n      }\n\n    });\n  </script>\n  <style type=\"text/css\">\n    h1 {\n      font-family: 'Droid Sans';\n    }\n  </style>\n</head>\n<body>\n  <h1 id=\"loading-message\">\n    I'm loading!\n  </h1>\n  <h1 id=\"content\" style=\"display:none;\">\n    Hello World. I am Droid Sans.\n  </h1>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/event-js-loading.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to use JavaScript to manipulate the DOM while\n    fonts are loading, and once they have all rendered.\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/events-variations.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <script type=\"text/javascript\">\n    function progress(message) {\n      var output = document.getElementById('events');\n      if (output) {\n        var e = document.createElement('li');\n        e.innerHTML = message;\n        output.appendChild(e);\n      }\n      if (window.console && window.console.log) {\n        window.console.log(message);\n      }\n    }\n    WebFont.load({\n      google: {\n        families: ['Droid Serif:r,i,b,bi']\n      },\n      loading: function() {\n        progress('loading');\n      },\n      active: function() {\n        progress('active');\n      },\n      inactive: function() {\n        progress('inactive');\n      },\n      fontloading: function(fontFamily, fontDescription) {\n        progress('fontloading: ' + fontFamily + ' (' + fontDescription + ')');\n      },\n      fontactive: function(fontFamily, fontDescription) {\n        progress('fontactive: ' + fontFamily + ' (' + fontDescription + ')');\n      },\n      fontinactive: function(fontFamily, fontDescription) {\n        progress('fontinactive: ' + fontFamily + ' (' + fontDescription + ')');\n      }\n    });\n  </script>\n  <style type=\"text/css\">\n\n    .droid p {\n      font-family: 'Droid Serif';\n      font-size: 2em;\n      margin-bottom: 0;\n      visibility: hidden;\n    }\n\n    .wf-droidserif-n4-active #droidregular {\n      visibility: visible;\n    }\n    .wf-droidserif-i4-active #droiditalic {\n      visibility: visible;\n    }\n    .wf-droidserif-n7-active #droidbold {\n      visibility: visible;\n    }\n    .wf-droidserif-i7-active #droidbolditalic {\n      visibility: visible;\n    }\n\n    /* All Class hooks */\n    #classes { color: #ddd; }\n    html.wf-loading #classes .Loading,\n    html.wf-active #classes .Active,\n    html.wf-inactive #classes .Inactive,\n    html.wf-droidserif-n4-loading #classes #DroidSerifRegularLoading,\n    html.wf-droidserif-n4-active #classes #DroidSerifRegularActive,\n    html.wf-droidserif-n4-inactive #classes #DroidSerifRegularInactive,\n    html.wf-droidserif-i4-loading #classes #DroidSerifItalicLoading,\n    html.wf-droidserif-i4-active #classes #DroidSerifItalicActive,\n    html.wf-droidserif-i4-inactive #classes #DroidSerifItalicInactive,\n    html.wf-droidserif-n7-loading #classes #DroidSerifBoldLoading,\n    html.wf-droidserif-n7-active #classes #DroidSerifBoldActive,\n    html.wf-droidserif-n7-inactive #classes #DroidSerifBoldInactive,\n    html.wf-droidserif-i7-loading #classes #DroidSerifBoldItalicLoading,\n    html.wf-droidserif-i7-active #classes #DroidSerifBoldItalicActive,\n    html.wf-droidserif-i7-inactive #classes #DroidSerifBoldItalicInactive {\n      color: #000;\n    }\n\n  </style>\n</head>\n<body>\n  <div class=\"droid\">\n    <p id=\"droidregular\">\n      Droid Serif Regular\n    </p>\n    <p id=\"droiditalic\">\n      <em>Droid Serif Italic</em>\n    </p>\n    <p id=\"droidbold\">\n      <strong>Droid Serif Bold</strong>\n    </p>\n    <p id=\"droidbolditalic\">\n      <strong><em>Droid Serif Bold Italic</em></strong>\n    </p>\n  </div>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/events-variations.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to show all of the font loading event callbacks when using\n    multiple weights and styles of one typeface.\n  </p>\n  <h2>CSS Hook Status</h2>\n  <ul id=\"classes\">\n    <li class=\"Loading\">Loading</li>\n    <li class=\"Active\">Active</li>\n    <li class=\"Inactive\">Inactive</li>\n    <li id=\"DroidSerifRegularLoading\">Droid Serif Regular Loading</li>\n    <li id=\"DroidSerifRegularActive\">Droid Serif Regular Active</li>\n    <li id=\"DroidSerifRegularInactive\">Droid Serif Regular Inactive</li>\n    <li id=\"DroidSerifItalicLoading\">Droid Serif Italic Loading</li>\n    <li id=\"DroidSerifItalicActive\">Droid Serif Italic Active</li>\n    <li id=\"DroidSerifItalicInactive\">Droid Serif Italic Inactive</li>\n    <li id=\"DroidSerifBoldLoading\">Droid Serif Bold Loading</li>\n    <li id=\"DroidSerifBoldActive\">Droid Serif Bold Active</li>\n    <li id=\"DroidSerifBoldInactive\">Droid Serif Bold Inactive</li>\n    <li id=\"DroidSerifBoldItalicLoading\">Droid Serif Bold Italic Loading</li>\n    <li id=\"DroidSerifBoldItalicActive\">Droid Serif Bold Italic Active</li>\n    <li id=\"DroidSerifBoldItalicInactive\">Droid Serif Bold Italic Inactive</li>\n  </ul>\n  <h2>JavaScript Event Progress</h2>\n  <ol id=\"events\"></ol>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/events.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <script type=\"text/javascript\">\n    function progress(message) {\n      var output = document.getElementById('events');\n      if (output) {\n        var e = document.createElement('li');\n        e.innerHTML = message;\n        output.appendChild(e);\n      }\n      if (window.console && window.console.log) {\n        window.console.log(message);\n      }\n    }\n    WebFont.load({\n      google: {\n        families: ['Droid Sans', 'Tangerine']\n      },\n      loading: function() {\n        progress('loading');\n      },\n      active: function() {\n        progress('active');\n      },\n      inactive: function() {\n        progress('inactive');\n      },\n      fontloading: function(fontFamily, fontDescription) {\n        progress('fontloading: ' + fontFamily + ' (' + fontDescription + ')');\n      },\n      fontactive: function(fontFamily, fontDescription) {\n        progress('fontactive: ' + fontFamily + ' (' + fontDescription + ')');\n      },\n      fontinactive: function(fontFamily, fontDescription) {\n        progress('fontinactive: ' + fontFamily + ' (' + fontDescription + ')');\n      }\n    });\n  </script>\n  <style type=\"text/css\">\n    h1 {\n      visibility: hidden;\n    }\n    .wf-droidsans-n4-active h1.droid {\n      font-family: 'Droid Sans';\n      font-weight: normal;\n      visibility: visible;\n    }\n    .wf-tangerine-n4-active h1.tangerine {\n      font-family: 'Tangerine';\n      font-weight: normal;\n      visibility: visible;\n    }\n\n    /* All Class hooks */\n    #classes { color: #ddd; }\n    html.wf-loading #classes .Loading,\n    html.wf-active #classes .Active,\n    html.wf-inactive #classes .Inactive,\n    html.wf-droidsans-n4-loading #classes .DroidSansLoading,\n    html.wf-droidsans-n4-active #classes .DroidSansActive,\n    html.wf-droidsans-n4-inactive #classes .DroidSansInactive,\n    html.wf-tangerine-n4-loading #classes .TangerineLoading,\n    html.wf-tangerine-n4-active #classes .TangerineActive,\n    html.wf-tangerine-n4-inactive #classes .TangerineInactive {\n      color: #000;\n    }\n\n  </style>\n</head>\n<body>\n  <h1 class=\"droid\">\n    Hello World. I am Droid Sans.\n  </h1>\n  <h1 class=\"tangerine\">\n    Hello World. I am Tangerine.\n  </h1>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/events.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to show all of the font loading event callbacks when\n    using multiple typefaces.\n  </p>\n  <h2>CSS Hook Status</h2>\n  <ul id=\"classes\">\n    <li class=\"Loading\">Loading</li>\n    <li class=\"Active\">Active</li>\n    <li class=\"Inactive\">Inactive</li>\n    <li class=\"DroidSansLoading\">Droid Sans Loading</li>\n    <li class=\"DroidSansActive\">Droid Sans Active</li>\n    <li class=\"DroidSansInactive\">Droid Sans Inactive</li>\n    <li class=\"TangerineLoading\">Tangerine Loading</li>\n    <li class=\"TangerineActive\">Tangerine Active</li>\n    <li class=\"TangerineInactive\">Tangerine Inactive</li>\n  </ul>\n  <h2>JavaScript Event Progress</h2>\n  <ol id=\"events\"></ol>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/fontdeck.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <link href=\"/basic.css\" rel=\"stylesheet\" type=\"text/css\">\n  <script src=\"/webfont.js\"></script>\n  <script>\n    function progress(message) {\n      var output = document.getElementById('events');\n      if (output) {\n        var e = document.createElement('li');\n        e.innerHTML = message;\n        output.appendChild(e);\n      }\n      if (window.console && window.console.log) {\n        window.console.log(message);\n      }\n    }\n    WebFont.load({\n      fontdeck: {\n        id: 2282\n      },\n      loading: function() {\n        progress('loading');\n      },\n      active: function() {\n        progress('active');\n      },\n      inactive: function() {\n        progress('inactive');\n      },\n      fontloading: function(fontFamily, fontDescription) {\n        progress('fontloading: ' + fontFamily + ' (' + fontDescription + ')');\n      },\n      fontactive: function(fontFamily, fontDescription) {\n        progress('fontactive: ' + fontFamily + ' (' + fontDescription + ')');\n      },\n      fontinactive: function(fontFamily, fontDescription) {\n        progress('fontinactive: ' + fontFamily + ' (' + fontDescription + ')');\n      }\n    });\n  </script>\n  <style>\n    h1 {\n      font-family: 'Fertigo Pro Regular';\n      visibility: hidden;\n    }\n    h2 {\n      font-family: 'Bodoni Display Bold Italic';\n      font-weight: bold;\n      font-style: italic;\n      visibility: hidden;\n    }\n    .wf-fertigoproregular-n4-active h1,\n    .wf-bodonidisplaybolditalic-i7-active h2 {\n      visibility: visible;\n    }\n  </style>\n</head>\n<body>\n  <h1>Hello World. I am Fertigo Pro Regular.</h1>\n  <h2>Hello World. I am Bodoni Display Bold Italic.</h2>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/fontdeck.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to show how Fontdeck fonts load.\n  </p>\n  <p>\n    You must use \"localhost\" when testing Fontdeck fonts.\n  </p>\n  <ol id=\"events\"></ol>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/fontwatchrunner-default-fonts.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <link href=\"/basic.css\" rel=\"stylesheet\" type=\"text/css\">\n  <script type=\"text/javascript\" src=\"/jquery.min.js\"></script>\n  <script type=\"text/javascript\" src=\"/webfont-fontwatchrunner.js\"></script>\n  <style type=\"text/css\">\n    #results {\n      font-family: arial, sans-serif;\n      font-size: 16px;\n    }\n    .test-case {\n      font-size: 300px;\n    }\n  </style>\n  <script type=\"text/javascript\">\n    var styles = '<style type=\"text/css\">';\n    styles += '.default-fonts-a {font-family: ' + webfont.FontWatchRunner.DEFAULT_FONTS_A + ';}';\n    styles += '.default-fonts-b {font-family: ' + webfont.FontWatchRunner.DEFAULT_FONTS_B + ';}';\n    styles += '</style>';\n    document.write(styles);\n  </script>\n</head>\n<body>\n  <p id=\"results\">Calculating...</p>\n  <p>\n    The goal of this page is to verify that the two default font stacks in\n    FontWatchRunner have different widths on a given platform when rendering the\n    default test string. The pairs of headings below should render in different\n    fonts and the results above should indicate that they all have different\n    widths.\n  </p>\n  <hr>\n  <div id=\"test-cases\"></div>\n  <script type=\"text/javascript\">\n    // Calculate all the different combinations of styles to test\n    var styles = {\n      'font-weight': [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000],\n      'font-style': ['normal', 'italic'],\n      'text-rendering': [null, 'optimizeLegibility']\n    };\n    function calculateStyleCombos(props, styles) {\n      if (props.length <= 0) {\n        return [{}]\n      }\n      var remainingProps = $.extend(true, [], props);\n      var prop = remainingProps.pop();\n      var remainingCombos = calculateStyleCombos(remainingProps, styles);\n      var combos = [];\n      for (var i = 0; i < remainingCombos.length; i++) {\n        for (var j = 0; j < styles[prop].length; j++) {\n          var combo = {};\n          if (styles[prop][j] !== null) {\n            combo[prop] = styles[prop][j];\n          }\n          combos.push($.extend(combo, remainingCombos[i]));\n        }\n      }\n      return combos;\n    }\n    var styleCombos = calculateStyleCombos(['font-weight', 'font-style', 'text-rendering'], styles);\n\n    // Create test cases with default test strings for each style combo\n    var testCases = $('#test-cases');\n    for (var i = 0; i < styleCombos.length; i++) {\n      var test = $('<div class=\"test-case\"></div>').css(styleCombos[i]);\n      test.append($('<span class=\"default-fonts-a\"></span>').text(webfont.FontWatchRunner.DEFAULT_TEST_STRING));\n      test.append($('<span class=\"default-fonts-b\"></span>').text(webfont.FontWatchRunner.DEFAULT_TEST_STRING));\n      testCases.append(test);\n    }\n\n    // Calculate the width of the spans after timeout\n    setTimeout(function() {\n      var comparisons = $('<span></span>');\n      var allPassed = true;\n      $('.test-case').each(function() {\n        var a = $(this).find('span').first().width();\n        var b = $(this).find('span').last().width();\n        comparisons.append($('<span></span>').css('color', a != b ? 'green' : 'red').text(a - b)).append(' ');\n        allPassed = allPassed && a != b;\n      });\n      $('#results').text(allPassed ? 'SUCCESS | ' : 'FAIL | ').append(comparisons);\n    }, 500);\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/google-css.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <link href=\"/basic.css\" rel=\"stylesheet\" type=\"text/css\">\n\n  <link href=\"/fonts/api?family=Droid+Sans\" rel=\"stylesheet\" type=\"text/css\">\n\n  <style type=\"text/css\">\n    h1 {\n      font-family: 'Droid Sans';\n    }\n  </style>\n</head>\n<body>\n  <h1>\n    Hello World. I am Droid Sans.\n  </h1>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/google-css.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is simply to use fonts directly via CSS.\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/google-iframe.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <link href=\"/basic.css\" rel=\"stylesheet\" type=\"text/css\">\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <style type=\"text/css\">\n    iframe {\n      height: 100px;\n      width: 100%;\n    }\n  </style>\n</head>\n<body>\n  <script type=\"text/javascript\">\n    function loaded() {\n      var child = frames[\"child\"];\n      child.document.body.innerHTML = \"<h1 style=\\\"font-family: 'Droid Sans'\\\">Hello World. I am Droid Sans.</h1>\";\n      WebFont.load({\n        google: {\n          families: ['Droid Sans'],\n          api: '/fonts/api'\n        },\n        context: child\n      });\n    }\n  </script>\n\n  <iframe name=\"child\" src=\"/blank.html\" onload=\"loaded()\"></iframe>\n\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/google.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is demonstrate fonts loading from Google via\n    Javascript into a child iframe.\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/google.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <link href=\"/basic.css\" rel=\"stylesheet\" type=\"text/css\">\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <script type=\"text/javascript\">\n    WebFont.load({\n      google: {\n        families: ['Droid Sans'],\n        api: '/fonts/api'\n      }\n    });\n  </script>\n  <style type=\"text/css\">\n    h1 {\n      font-family: 'Droid Sans';\n    }\n  </style>\n</head>\n<body>\n  <h1>\n    Hello World. I am Droid Sans.\n  </h1>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/google.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is simply to use fonts via the JavaScript API.\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/ie-fast-js.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n  <title>Show me Fonts</title>\n\n  <!--\n    An external script will block page rendering in IE, but we take care to\n    correct that behavior.\n  -->\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <script type=\"text/javascript\">\n    WebFont.load({\n      google: {\n        families: ['Droid Sans'],\n        api: '/fonts/api',\n      }\n    });\n  </script>\n\n  <style type=\"text/css\">\n    .serif {\n      font-family: serif;\n    }\n    .droid {\n      font-family: 'Droid Sans', serif;\n    }\n  </style>\n</head>\n<body>\n  <h1 class=\"serif\">\n    I am a serif font.\n  </h1>\n  <h1 class=\"droid\">\n    I am Droid Sans.\n  </h1>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/ie-fast-js.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to show non-blocking behavior in MSIE. This\n    causes IE to load fonts without blocking the entire page.\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/ie-slow-js.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n  <title>Show me Fonts</title>\n\n  <!--\n    An external script will block page rendering in IE. Normally we fix that\n    behavior, but here we'll undo the override.\n  -->\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <script type=\"text/javascript\">\n    WebFont.load({\n      google: {\n        families: ['Droid Sans'],\n        blocking: true,\n        api: '/fonts/api'\n      }\n    });\n  </script>\n\n  <style type=\"text/css\">\n    .serif {\n      font-family: serif;\n    }\n    .droid {\n      font-family: 'Droid Sans', serif;\n    }\n  </style>\n</head>\n<body>\n  <h1 class=\"serif\">\n    I am a serif font.\n  </h1>\n  <h1 class=\"droid\">\n    I am Droid Sans.\n  </h1>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/ie-slow-js.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to restore blocking behavior in MSIE. This causes\n    IE to block the entire page while loading fonts.\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/ie-slow-link.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n  <title>Show me Fonts</title>\n\n  <!--\n    An external stylesheet will block page rendering in IE\n  -->\n  <link href=\"/fonts/api?family=Droid+Sans\" rel=\"stylesheet\" type=\"text/css\">\n\n  <style type=\"text/css\">\n    .serif {\n      font-family: serif;\n    }\n    .droid {\n      font-family: 'Droid Sans', serif;\n    }\n  </style>\n</head>\n<body>\n  <h1 class=\"serif\">\n    I am a serif font.\n  </h1>\n  <h1 class=\"droid\">\n    I am Droid Sans.\n  </h1>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/ie-slow-link.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to show that by default, MSIE will block the entire\n    page while an external font loads.\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/index.html",
    "content": "<!doctype html>\n<html>\n<head>\n</head>\n<body>\n  <h1>Web Font Loader Demos</h1>\n  <p>\n    Demonstrations of pure CSS and JavaScript-enhanced use of @font-face.\n  </p>\n  <p>\n    Note that many of these demonstrations use a <strong>slow proxy</strong> to\n    increase the amount of time it takes to load a font. We do this to make it\n    more obvious that the events system is working. It <em>does not</em> represent\n    real world usage.\n  </p>\n\n  <h2>Modules</h2>\n  <p>\n    Web Font Loader provides modules to load fonts from many places.\n  </p>\n  <ol>\n    <li><a href=\"/google-css.html\">Google / CSS Link</a>: Load fonts from Google with a <code>link</code> tag. Consider this a base case for font loading.</li>\n    <li><a href=\"/google.html\">Google / Web Font Loader</a>: Load fonts from Google with Web Font Loader.</li>\n    <li><a href=\"/typekit.html\">Typekit / Web Font Loader</a>: Load fonts from Typekit with Web Font Loader.</li>\n    <li><a href=\"/custom.html\">Custom / Web Font Loader</a>: Load fonts from your own CSS with Web Font Loader.</li>\n    <li><a href=\"/fontdeck.html\">Fontdeck / Web Font Loader</a>: Load fonts from Fontdeck with Web Font Loader.</li>\n    <li><a href=\"/monotype.html\">Monotype / Web Font Loader</a>: Load fonts from fonts.com with Web Font Loader.</li>\n  </ol>\n\n  <h2>Modules in Iframes</h2>\n  <p>\n    Web Font Loader provides the ability to load fonts in child iframes using modules, instead of the main window.\n  </p>\n  <ol>\n    <li><a href=\"/google-iframe.html\">Google / Web Font Loader</a>: Load fonts from Google in a child iframe with Web Font Loader.</li>\n    <li><a href=\"/typekit-iframe.html\">Typekit / Web Font Loader</a>: Load fonts from Typekit in a child iframe with Web Font Loader.</li>\n    <li><a href=\"/custom-iframe.html\">Custom / Web Font Loader</a>: Load fonts from your own CSS in a child iframe with Web Font Loader.</li>\n    <li>Fontdeck / Web Font Loader: Their demo fonts seem to be broken at the moment, so we don't have an iframe demo of this module.</li>\n    <li><a href=\"/monotype-iframe.html\">Monotype / Web Font Loader</a>: Load fonts from fonts.com in a child iframe with Web Font Loader.</li>\n  </ol>\n\n  <h2>Events</h2>\n  <p>\n    Web Font Loader provides events to help control font rendering across browsers. Here are some sample uses.\n  </p>\n  <ol>\n    <li><a href=\"/event-css-active.html\">Show when rendered (CSS)</a>: Use CSS to show part of the page only when the font has rendered. (Webkit style)</li>\n    <li><a href=\"/event-js-active.html\">Show when rendered (JS)</a>: Use JS to show part of the page only when the font has rendered. (Webkit style)</li>\n    <li><a href=\"/event-css-inactive.html\">Fallback before rendered (CSS)</a>: Use CSS to show fallback font before the font has rendered. (Mozilla style)</li>\n    <li><a href=\"/event-css-loading.html\">Show loading message (CSS)</a>: Use CSS to show a message while the font loads.</li>\n    <li><a href=\"/event-js-loading.html\">Show loading message (JS)</a>: Use JS to show a message while the font loads.</li>\n  </ol>\n\n  <h2>More Events</h2>\n  <p>\n    More complex samples using events.\n  </p>\n  <ol>\n    <li><a href=\"/event-css-active-multiple.html\">Multiple font loads</a>: Use CSS to control more than one font.</li>\n    <li><a href=\"/events.html\">Multiple typefaces</a>: The full CSS and JS event cycle when using multiple typefaces.</li>\n    <li><a href=\"/events-variations.html\">Multiple variations</a>: The full CSS and JS event cycle when using multiple weights and styles of one typeface.</li>\n  </ol>\n\n  <h2>IE Behavior</h2>\n  <p>\n    Web Font Loader helps workaround IE's page blocking behavior.\n  </p>\n  <ol>\n    <li><a href=\"/ie-slow-link.html\">Slow Link</a>: Demonstrate that IE blocks the whole page when loading fonts via a LINK tag.</li>\n    <li><a href=\"/ie-fast-js.html\">Fast JS</a>: By default, Web Font Loader works around the default IE loading behavior.</li>\n    <li><a href=\"/ie-slow-js.html\">Slow JS</a>: Restore the default IE loading behavior.</li>\n  </ol>\n\n  <h2>Tests</h2>\n  <p>\n    Additional demo pages to test specific functionality.\n  </p>\n  <ol>\n    <li><a href=\"/typekit-variations.html\">Typekit with Multiple Variations</a></li>\n    <li><a href=\"/fontwatchrunner-default-fonts.html\">Default font stacks for FontWatchRunner</li>\n  </ol>\n\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/monotype-iframe.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <link href=\"/basic.css\" rel=\"stylesheet\" type=\"text/css\">\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <style>\n    iframe {\n      height: 100px;\n      width: 100%;\n    }\n  </style>\n</head>\n<body>\n  <script type=\"text/javascript\">\n    function loaded() {\n      frames[\"child\"].document.body.innerHTML = \"<h1>Hello World. I am DIN Next Bold.</h1>\";\n      WebFont.load({\n        monotype: {\n          projectId: 'b726c28f-a28a-45be-993f-3db719bacfab'\n        },\n        context: frames[\"child\"]\n      });\n    }\n  </script>\n\n  <iframe name=\"child\" src=\"/blank.html\" onload=\"loaded()\"></iframe>\n\n  <hr>\n\n  <p>\n\n    <a href=\"#\" onClick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n\n    <a href=\"/monotype.html\">Reload Cached</a>\n\n  </p>\n\n  <p>\n    The goal of this page is to show how monotype fonts load in a child iframe.\n  </p>\n\n  <p>\n    You must use \"localhost\" when testing monotype fonts.\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/monotype.html",
    "content": "<!doctype html>\r\n<html>\r\n<head>\r\n  <link href=\"/basic.css\" rel=\"stylesheet\" type=\"text/css\">\r\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\r\n  <script type=\"text/javascript\">\r\n    WebFont.load({\r\n      monotype: {\r\n        projectId: 'b726c28f-a28a-45be-993f-3db719bacfab'\r\n      }\r\n    });\r\n  </script>\r\n</head>\r\n<body>\r\n   <h1>\r\r\n    Hello World. I am DIN Next Bold.\r\r\n  </h1>\r\r\n\r\r\n  <hr>\r\r\n  <p>\r\r\n    <a href=\"#\" onClick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\r\r\n    <a href=\"/monotype.html\">Reload Cached</a>\r\r\n  </p>\r\r\n  <p class=\"c1\">\r\r\n    The goal of this page is to show how monotype fonts load. And I'm Albertus Regular\r\r\n  </p>\r\r\n\r\r\n  <p class=\"c2\">\r\r\n    You must use \"localhost\" when testing monotype fonts. And this is Trade Gothic Condensed\r\r\n  </p>\r\n</body>\r\n</html>\r\n"
  },
  {
    "path": "lib/webfontloader/demo/public/typekit-iframe.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <link href=\"/basic.css\" rel=\"stylesheet\" type=\"text/css\">\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <style type=\"text/css\">\n    iframe {\n      height: 100px;\n      width: 100%;\n    }\n  </style>\n</head>\n<body>\n  <script type=\"text/javascript\">\n    function loaded() {\n      var child = frames[\"child\"];\n      child.document.body.innerHTML = \"<h1>Hello World. I am Futura PT.</h1>\";\n      WebFont.load({\n        typekit: {\n          id: 'bod7grh'\n        },\n        context: child\n      });\n    }\n  </script>\n\n  <iframe name=\"child\" src=\"/blank.html\" onload=\"loaded()\"></iframe>\n\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/typekit.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to show how Typekit fonts load into an iframe.\n  </p>\n  <p>\n    You must load the fonts on \"localhost\" for this demo to work.\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/typekit-variations.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <link href=\"/basic.css\" rel=\"stylesheet\" type=\"text/css\">\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <script type=\"text/javascript\">\n    WebFont.load({\n      typekit: {\n        id: 'kitwithgeorgia',\n        api: '/typekit'\n      }\n    });\n  </script>\n  <style type=\"text/css\">\n    /* Use classes to prove that Typekit triggers the event system correctly */\n    .georgia p {\n      font-family: 'Georgia';\n      font-size: 3em;\n      visibility: hidden;\n    }\n    .wf-georgia-i4-active #georgiaitalic {\n      visibility: visible;\n    }\n    .wf-georgia-i7-active #georgiabolditalic {\n      visibility: visible;\n    }\n  </style>\n</head>\n<body>\n  <div class=\"georgia\">\n    <p id=\"georgiaitalic\">\n      <em>Georgia Italic</em>\n    </p>\n    <p id=\"georgiabolditalic\">\n      <strong><em>Georgia Bold Italic</em></strong>\n    </p>\n  </div>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/typekit-variations.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to show how Typekit fonts load. Note that it uses\n    a minimal Typekit script in order to reduce dependencies. This script\n    simply provides the system font 'Georgia' in italic and bold italic\n    instead of loading a web font.\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/public/typekit.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <link href=\"/basic.css\" rel=\"stylesheet\" type=\"text/css\">\n  <script type=\"text/javascript\" src=\"/webfont.js\"></script>\n  <script type=\"text/javascript\">\n    WebFont.load({\n      typekit: {\n        id: 'bod7grh'\n      }\n    });\n  </script>\n  <style type=\"text/css\">\n    /* Use classes to prove that Typekit triggers the event system correctly */\n    h1 {\n      visibility: hidden;\n    }\n    .wf-futurapt-n7-active h1 {\n      visibility: visible;\n    }\n  </style>\n</head>\n<body>\n  <h1>\n    Hello World. I am Futura PT.\n  </h1>\n  <hr>\n  <p>\n    <a href=\"#\" onclick=\"document.getElementsByTagName('body')[0].style.color = '#fff';return false;\">Hide Page</a> |\n    <a href=\"/typekit.html\">Reload Cached</a>\n  </p>\n  <p>\n    The goal of this page is to show how Typekit fonts load.\n  </p>\n  <p>\n    You must load this page on \"localhost\" in order for the fonts to load.\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/webfontloader/demo/server.rb",
    "content": "require 'sinatra/base'\nrequire 'open-uri'\n\nmodule WebFontLoader\n  module Demo\n    class Server < Sinatra::Base\n\n      DemoRoot = File.expand_path(File.join(File.dirname(__FILE__)))\n      ProjectRoot = File.expand_path(File.join(File.dirname(__FILE__), \"..\", \"..\", \"..\"))\n\n      GoogleApi = \"https://fonts.googleapis.com/css\"\n      GoogleFontApi = \"https://themes.googleusercontent.com/font\"\n\n      set :app_file, __FILE__\n      set :sessions, false\n      set :static, true\n\n      set :modules, nil\n      set :compiled_js, nil\n\n      get '/' do\n        File.read(File.join(DemoRoot, \"public\", \"index.html\"))\n      end\n\n      get '/webfont.js' do\n        headers 'Content-Type' => \"application/javascript\"\n        headers 'Cache-Control' => 'max-age=300'\n        get_js_code\n      end\n\n      get '/webfont-fontwatchrunner.js' do\n        headers 'Content-Type' => 'application/javascript'\n        headers 'Cache-Control' => 'max-age=300'\n        [\n          'var webfont = {};',\n          File.read(File.join(WebFontLoader::ProjectRoot, 'src/core/fontwatchrunner.js'))\n        ]\n      end\n\n      get '/fonts/api' do\n        url = \"#{GoogleApi}?#{env['QUERY_STRING']}\"\n        headers 'Content-Type' => 'text/css'\n        headers 'Cache-Control' => 'max-age=300'\n        response = open(url, 'User-Agent' => env['HTTP_USER_AGENT'])\n        source = response.read\n        source.gsub!(%r[https://themes.googleusercontent.com/font], '/fonts/font')\n        source\n      end\n\n      get '/fonts/font' do\n        sleep 1\n        url = \"#{GoogleFontApi}?#{env['QUERY_STRING']}\"\n        headers 'Cache-Control' => 'max-age=300'\n        headers 'Content-Encoding' => 'gzip'\n        response = open(url, 'User-Agent' => env['HTTP_USER_AGENT'])\n        response.read\n      end\n\n      get %r[/typekit/(\\w+)\\.js] do |kit_id|\n        headers 'Content-Type' => 'application/javascript'\n        headers 'Cache-Control' => 'max-age=300'\n        case kit_id\n        when \"kitwitharialblack\"\n          families = \"['Arial Black']\"\n          variations = \"{}\"\n        when \"kitwithgeorgia\"\n          families = \"['Georgia']\"\n          variations = \"{ 'Georgia': ['i4', 'i7' ]}\"\n        else\n          families = \"[]\"\n          variations = \"{}\"\n        end\n        <<-JS\n          if (window.__webfonttypekitmodule__) {\n            var module = window.__webfonttypekitmodule__['#{kit_id}'];\n            if (module) {\n              module(function(userAgent, configuration, init) {\n                // Here you may use the userAgent object to determine\n                // browser support.\n                init(true, #{families}, #{variations});\n              });\n            }\n          }\n        JS\n      end\n\n    protected\n\n      def get_js_code\n        if settings.compiled_js\n          settings.compiled_js\n        elsif settings.modules\n          settings.modules.all_source_files.map { |file| File.read(File.join(WebFontLoader::ProjectRoot, file)) }\n        else\n          \"alert('No JavaScript has been configured in the demo server');\"\n        end\n      end\n\n    end\n  end\nend"
  },
  {
    "path": "lib/webfontloader/modules.rb",
    "content": "module WebFontLoader\n  class Modules\n\n    def initialize(*modules)\n      @project_root = WebFontLoader::ProjectRoot\n      @js_src = \"src\"\n      @js_test = \"src-test\"\n      @modules = modules.empty? ? config.keys : modules\n      # Make sure 'core' is first.\n      @modules.unshift \"core\"\n      @modules.uniq!\n    end\n\n    attr_reader :modules\n    attr_accessor :project_root, :js_src, :js_test\n\n    def all_source_files\n      @all_source_files ||= begin\n        modules.map { |mod| config[mod] }.compact.flatten.map { |f| File.join(js_src, f) }\n      end\n    end\n\n    def all_test_globs\n      @all_test_globs ||= begin\n        js_test_dirs = Dir[File.join(project_root, js_test, \"*\")].map { |d| File.basename(d) }\n        js_test_dirs.map { |dir| File.join(js_test, dir, \"*.js\") if modules.include?(dir) }.compact\n      end\n    end\n\n    def js_output_wrapper(source, version)\n      File.read(File.join(js_src, \"closure.js\")).sub(\"{{source}}\", source).sub(\"{{version}}\", version).gsub(/\\n|\\r/,\"\")\n    end\n\n  protected\n\n    def config\n      @config ||= begin\n        path = File.join(project_root, js_src)\n        YAML.load_file(File.join(path, \"modules.yml\"))\n      end\n    end\n\n  end\nend\n"
  },
  {
    "path": "lib/webfontloader.rb",
    "content": "require 'yaml'\n\nrequire 'webfontloader/modules'\n\nmodule WebFontLoader\n  VERSION = '1.6.28'\n\n  ProjectRoot = File.expand_path(File.dirname(__FILE__) + \"/..\")\n\nend\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"webfontloader\",\n  \"version\": \"1.6.28\",\n  \"description\": \"Web Font Loader gives you added control when using linked fonts via @font-face.\",\n  \"main\": \"webfontloader.js\",\n  \"scripts\": {\n    \"test\": \"phantomjs tools/jasmine-phantomjs/jasmine-phantomjs.js spec/index.html\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/typekit/webfontloader.git\"\n  },\n  \"keywords\": [\n    \"web\",\n    \"fonts\",\n    \"webfonts\",\n    \"font\",\n    \"loader\",\n    \"@font-face\"\n  ],\n  \"files\": [\n    \"webfontloader.js\",\n    \"src/**/*.js\"\n  ],\n  \"contributors\": [\n    \"Ryan Carver <ryan@typekit.com>\",\n    \"Jeremie Lenfant-engelmann <jeremiele@google.com>\",\n    \"Sean McBride <sean@typekit.com>\",\n    \"Bram Stein <bram@typekit.com>\"\n  ],\n  \"license\": \"Apache-2.0\",\n  \"bugs\": {\n    \"url\": \"https://github.com/typekit/webfontloader/issues\"\n  },\n  \"homepage\": \"https://github.com/typekit/webfontloader\",\n  \"devDependencies\": {}\n}\n"
  },
  {
    "path": "spec/core/cssclassname_spec.js",
    "content": "describe('CssClassName', function () {\n  var CssClassName = webfont.CssClassName,\n      sanitizer = new CssClassName();\n\n  describe('#sanitize', function () {\n    it('should sanitize spaces in names', function () {\n      expect(sanitizer.sanitize(' My Family ')).toEqual('myfamily');\n    });\n\n    it('should sanitize numbers in names', function () {\n      expect(sanitizer.sanitize('99 My Family 99')).toEqual('99myfamily99');;\n    });\n\n    it('should sanitize other characters', function () {\n      expect(sanitizer.sanitize('_My+Family!-')).toEqual('myfamily');\n    });\n  });\n\n  describe('#build', function () {\n    it('should build many parts', function () {\n      expect(sanitizer.build('pre_', 'My Family', '_post')).toEqual('pre-myfamily-post');\n    });\n\n    it('should build some parts', function () {\n      expect(sanitizer.build('pre!', 'My Family')).toEqual('pre-myfamily');\n    });\n  });\n\n  describe('#constructor', function () {\n    it('should use a hyphen as a default separator', function () {\n      var sanitizer = new CssClassName();\n\n      expect(sanitizer.build('pre', 'post')).toEqual('pre-post');\n    });\n\n    it('should use the configured separator', function () {\n      var sanitizer = new CssClassName('_');\n\n      expect(sanitizer.build('pre', 'post')).toEqual('pre_post');\n    });\n  });\n});\n"
  },
  {
    "path": "spec/core/domhelper_spec.js",
    "content": "describe('DomHelper', function () {\n  var DomHelper = webfont.DomHelper,\n      domHelper = new DomHelper(window);\n\n  describe('#createElement', function () {\n    it('should create an element', function () {\n      var div = domHelper.createElement('div');\n\n      expect(div).not.toBeNull();\n    });\n\n    it('should create an element with inline content', function () {\n      var div = domHelper.createElement('div', {}, 'moo');\n\n      expect(div).not.toBeNull();\n      expect(div.innerHTML).toEqual('moo');\n    });\n\n    it('should create an element with attributes and inline content', function () {\n      var div = domHelper.createElement('div', {\n            style: 'font-size: 42px',\n            id: 'mySpan'\n          }, 'hello');\n\n      expect(div).not.toBeNull();\n      expect(div.innerHTML).toEqual('hello');\n      expect(div.style.fontSize).toEqual('42px');\n      expect(div.id).toEqual('mySpan');\n    });\n\n    it('should work with augmented Object.prototype', function () {\n      Object.prototype.evil = function () {};\n\n      var div = domHelper.createElement('div', { id: 'augmented' });\n      var parentDiv = domHelper.createElement('div', { id: 'parentaugmented' });\n\n      parentDiv.appendChild(div);\n\n      expect(div).not.toBeNull();\n      expect(!!div.getAttribute('evil')).toBe(false);\n      expect(-1, parentDiv.innerHTML.indexOf('evil'));\n\n      delete Object.prototype.evil;\n    });\n  });\n\n  describe('#appendClassName', function () {\n    it('should have added a class name', function () {\n      var div = domHelper.createElement('div');\n      domHelper.appendClassName(div, 'moo');\n\n      expect(div.className).toEqual('moo');\n    });\n\n    it('should not add duplicate class names', function () {\n      var div = domHelper.createElement('div');\n\n      domHelper.appendClassName(div, 'moo');\n      domHelper.appendClassName(div, 'meu');\n      domHelper.appendClassName(div, 'moo');\n\n      expect(div.className).toEqual('moo meu');\n    });\n\n    it('should add multiple class names', function () {\n      var div = domHelper.createElement('div');\n\n      domHelper.appendClassName(div, 'moo meu moo');\n      expect(div.className).toEqual('moo meu moo');\n    });\n\n    it('should normalize spaces and tabs', function () {\n      var div = domHelper.createElement('div');\n\n      domHelper.appendClassName(div, 'meu');\n      domHelper.appendClassName(div, '      foo ');\n      expect(div.className).toEqual('meu foo');\n    });\n  });\n\n  describe('#removeClassName', function () {\n    it('should remove class names', function () {\n      var div = domHelper.createElement('div');\n\n      domHelper.appendClassName(div, 'meu moo');\n      expect(div.className).toEqual('meu moo');\n\n      domHelper.removeClassName(div, 'meu');\n      expect(div.className).toEqual('moo');\n    });\n\n    it('should not remove non-existing classes', function () {\n      var div = domHelper.createElement('div');\n\n      domHelper.appendClassName(div, 'moo');\n      expect(div.className).toEqual('moo');\n      domHelper.removeClassName(div, 'boo');\n      expect(div.className).toEqual('moo');\n    });\n  });\n\n  describe('#updateClassName', function () {\n    it('should handle optional arguments correctly', function () {\n      var div = domHelper.createElement('div');\n\n      domHelper.appendClassName(div, 'moo');\n\n      domHelper.updateClassName(div);\n      expect(div.className).toEqual('moo');\n\n      domHelper.updateClassName(div, [], []);\n      expect(div.className).toEqual('moo');\n\n      domHelper.updateClassName(div, null, null);\n      expect(div.className).toEqual('moo');\n    });\n\n    it('should have added a class name', function () {\n      var div = domHelper.createElement('div');\n      domHelper.updateClassName(div, ['moo']);\n\n      expect(div.className).toEqual('moo');\n    });\n\n    it('should not add duplicate class names', function () {\n      var div = domHelper.createElement('div');\n\n      domHelper.appendClassName(div, 'moo');\n      domHelper.updateClassName(div, ['moo']);\n\n      expect(div.className).toEqual('moo');\n    });\n\n    it('should add multiple class names', function () {\n      var div = domHelper.createElement('div');\n\n      domHelper.updateClassName(div, ['moo', 'meu', 'moo']);\n      expect(div.className).toEqual('moo meu');\n    });\n\n    it('should normalize spaces and tabs', function () {\n      var div = domHelper.createElement('div');\n\n      domHelper.updateClassName(div, ['meu', '      foo']);\n      expect(div.className).toEqual('meu foo');\n    });\n\n    it('should remove class names', function () {\n      var div = domHelper.createElement('div');\n\n      domHelper.appendClassName(div, 'meu moo');\n      expect(div.className).toEqual('meu moo');\n\n      domHelper.updateClassName(div, null, ['meu']);\n      expect(div.className).toEqual('moo');\n    });\n\n    it('should remove multiple class names', function () {\n      var div = domHelper.createElement('div');\n\n      domHelper.appendClassName(div, 'meu');\n      domHelper.appendClassName(div, 'moo');\n      expect(div.className).toEqual('meu moo');\n\n      domHelper.updateClassName(div, null, ['meu', 'moo']);\n      expect(div.className).toEqual('');\n    });\n\n    it('should not remove non-existing classes', function () {\n      var div = domHelper.createElement('div');\n\n      domHelper.appendClassName(div, 'moo');\n      expect(div.className).toEqual('moo');\n\n      domHelper.updateClassName(div, null, 'boo');\n      expect(div.className).toEqual('moo');\n    });\n\n    it('should add and remove class names', function () {\n      var div = domHelper.createElement('div');\n\n      domHelper.appendClassName(div, 'moo');\n      domHelper.appendClassName(div, 'meh');\n      expect(div.className).toEqual('moo meh');\n\n      domHelper.updateClassName(div, ['meu'], ['moo', 'meh']);\n      expect(div.className).toEqual('meu');\n    });\n\n    it('should update one of many class names', function () {\n      var div = domHelper.createElement('div');\n\n      domHelper.appendClassName(div, 'moo');\n      domHelper.appendClassName(div, 'meh');\n      expect(div.className).toEqual('moo meh');\n\n      domHelper.updateClassName(div, ['meu'], ['moo']);\n      expect(div.className).toEqual('meh meu');\n    });\n  });\n\n  describe('#hasClassName', function () {\n    var div = null;\n\n    beforeEach(function () {\n       div = domHelper.createElement('div');\n       domHelper.appendClassName(div, 'moo moo-meu');\n    });\n\n    it('should return true', function () {\n      expect(domHelper.hasClassName(div, 'moo')).toBe(true);\n      expect(domHelper.hasClassName(div, 'moo-meu')).toBe(true);\n    });\n\n    it('should return false', function () {\n      expect(domHelper.hasClassName(div, 'boo')).toBe(false);\n      expect(domHelper.hasClassName(div, 'meu')).toBe(false);\n    });\n  });\n\n  describe('#setStyle', function () {\n    var div = null;\n\n    beforeEach(function () {\n      div = domHelper.createElement('div');\n    });\n\n    it('should set the style correctly', function () {\n      domHelper.setStyle(div, 'left:3px;top:1px;');\n      expect(div.style.left).toEqual('3px');\n      expect(div.style.top).toEqual('1px');\n    });\n  });\n\n  describe('#createStyle', function () {\n    var style = null;\n\n    beforeEach(function () {\n      style = domHelper.createStyle('blockquote{font-size:300px}');\n      domHelper.insertInto('head', style);\n    });\n\n    afterEach(function () {\n      domHelper.removeElement(style);\n    });\n\n    it('should create a style element', function () {\n      expect(style).not.toBeNull();\n      expect(style.nodeName).toEqual('STYLE');\n    });\n\n    it('should set the css content correctly', function () {\n      var text = style.styleSheet ? style.styleSheet.cssText : style.textContent;\n      expect(text.replace(/[\\s;]/g, '').toLowerCase()).toEqual('blockquote{font-size:300px}');\n    });\n  });\n\n  describe('#loadStylesheet', function () {\n    it('should load the stylesheet', function () {\n      var el = null,\n          width = null,\n          link = null;\n\n      runs(function () {\n        el = domHelper.createElement('div', { id: 'TEST_ELEMENT' });\n        domHelper.insertInto('body', el);\n        width = el.offsetWidth;\n        link = domHelper.loadStylesheet('fixtures/external_stylesheet.css');\n      });\n\n      waitsFor(function () {\n        return width !== el.offsetWidth;\n      });\n\n      runs(function () {\n        expect(link).not.toBeNull();\n        expect(link.rel).toEqual('stylesheet');\n        expect(el.offsetWidth).toEqual(300);\n      });\n    });\n  });\n\n  describe('#loadStylesheet with callback', function () {\n    it('should load the stylesheet', function () {\n      var el = null,\n          width = null,\n          callbackMade = false;\n\n      function callback() {\n        callbackMade = true;\n      }\n\n      runs(function () {\n        el = domHelper.createElement('div', { id: 'TEST_ELEMENT' });\n        domHelper.insertInto('body', el);\n        width = el.offsetWidth;\n        domHelper.loadStylesheet('fixtures/external_stylesheet.css', callback);\n      });\n\n      waitsFor(function () {\n        return callbackMade;\n      });\n\n      runs(function () {\n        expect(el.offsetWidth).toEqual(300);\n      });\n    });\n  });\n\n  describe('#loadStylesheet with async and callback', function () {\n    it('should load the stylesheet', function () {\n      var el = null,\n          width = null,\n          callbackMade = false;\n\n      function callback() {\n        callbackMade = true;\n      }\n\n      runs(function () {\n        el = domHelper.createElement('div', { id: 'TEST_ELEMENT' });\n        domHelper.insertInto('body', el);\n        width = el.offsetWidth;\n        domHelper.loadStylesheet('fixtures/external_stylesheet.css', callback, true);\n      });\n\n      waitsFor(function () {\n        return callbackMade;\n      });\n\n      runs(function () {\n        expect(el.offsetWidth).toEqual(300);\n      });\n    });\n  });\n\n  describe('#loadScript', function () {\n    it('should load the script', function () {\n      runs(function () {\n        domHelper.loadScript('fixtures/external_script.js');\n      });\n\n      waitsFor(function () {\n        return window.EXTERNAL_SCRIPT_LOADED;\n      }, 'script was never inserted', 1000);\n\n      runs(function () {\n        expect(window.EXTERNAL_SCRIPT_LOADED).toBe(true);\n      });\n    });\n\n    it('should call the callback', function () {\n      var called = false,\n          error = null;\n\n      runs(function () {\n        domHelper.loadScript('fixtures/external_script.js', function (err) {\n          called = true;\n          error = err;\n        });\n      });\n\n      waitsFor(function () {\n        return called;\n      }, 'callback was never called', 1000);\n\n      runs(function () {\n        expect(called).toBe(true);\n        expect(error).toBeFalsy();\n      });\n    });\n\n    it('should return a script element', function () {\n      var script = domHelper.loadScript('fixtures/external_script.js');\n\n      expect(script).not.toBeNull();\n      expect(script.nodeName).toEqual('SCRIPT');\n    });\n\n    it('should timeout if the script does not load or is very slow', function () {\n      var called = false,\n          error = false;\n\n      // Spy on createElement so the all loadScript code is executed but\n      // the \"script\" won't actually load.\n      spyOn(domHelper, 'createElement').andCallFake(function (name) {\n        return document.createElement('div');\n      });\n\n      runs(function () {\n        domHelper.loadScript('fixtures/external_script.js', function (err) {\n          called = true;\n          error = err;\n        }, 100);\n      });\n\n      waitsFor(function () {\n        return called;\n      });\n\n      runs(function () {\n        expect(called).toBe(true);\n        expect(error).toBeTruthy();\n      });\n    });\n  });\n\n  describe('#getHostname', function () {\n    it('should return the hostname', function () {\n      var domHelper = new DomHelper({\n        location: {\n          hostname: 'example.com'\n        }\n      });\n\n      expect(domHelper.getHostName()).toEqual('example.com');\n    });\n\n    it('should return the hostname from the iframe if present', function () {\n      var domHelper = new DomHelper({\n        location: {\n          hostname: 'example.com'\n        }\n      }, {\n        location: {\n          hostname: 'example.org'\n        }\n      });\n\n      expect(domHelper.getHostName()).toEqual('example.org');\n    });\n  });\n\n  describe('#insertInto', function () {\n    it('should insert an element', function () {\n      var a = domHelper.createElement('div');\n\n      var result = domHelper.insertInto('body', a);\n\n      expect(result).toBe(true);\n      expect(a.parentNode.nodeName).toEqual('BODY');\n    });\n  });\n\n  describe('#whenBodyExists', function () {\n    var domHelper = null,\n        callback = null;\n\n    beforeEach(function () {\n      domHelper = new DomHelper({\n        document: {\n          addEventListener: function (event, callback) {\n            function check() {\n              if (domHelper.document_.body) {\n                callback();\n              } else {\n                setTimeout(check, 10);\n              }\n            }\n            check();\n          }\n        }\n      });\n\n      callback = jasmine.createSpy('callback');\n    });\n\n    it('should wait until the body exists before calling the callback', function () {\n      runs(function () {\n        domHelper.whenBodyExists(callback);\n      });\n\n      waits(200);\n\n      runs(function () {\n        domHelper.document_.body = true;\n      });\n\n      waitsFor(function () {\n        return callback.wasCalled;\n      }, 'callback was never called', 100);\n\n      runs(function () {\n        expect(callback).toHaveBeenCalled();\n      });\n    });\n\n    it('should not call the callback if the body is not available', function () {\n      runs(function () {\n        domHelper.whenBodyExists(callback);\n      });\n\n      waits(100);\n\n      runs(function () {\n        expect(callback).not.toHaveBeenCalled();\n      });\n    });\n  });\n\n  describe('#removeElement', function () {\n    it('should remove an element', function () {\n      var a = domHelper.createElement('div'),\n          b = domHelper.createElement('div');\n\n      a.appendChild(b);\n\n      var result = domHelper.removeElement(b);\n      expect(result).toBe(true);\n      expect(b.parentNode).not.toEqual(a);\n    });\n\n    it('should return false when failing to remove an element', function () {\n      var a = domHelper.createElement('div');\n\n      var result = domHelper.removeElement(a);\n\n      expect(result).toBe(false);\n    });\n  });\n\n  describe('#getMainWindow', function () {\n    it('should return the main window', function () {\n      var domHelper = new DomHelper(1, 2);\n      expect(domHelper.getMainWindow()).toEqual(1);\n    });\n  });\n\n  describe('#getLoadWindow', function () {\n    it('should return the load window', function () {\n      var domHelper = new DomHelper(1, 2);\n      expect(domHelper.getLoadWindow()).toEqual(2);\n    });\n  });\n});\n"
  },
  {
    "path": "spec/core/eventdispatcher_spec.js",
    "content": "describe('EventDispatcher', function () {\n  var EventDispatcher = webfont.EventDispatcher,\n      DomHelper = webfont.DomHelper,\n      Font = webfont.Font,\n      domHelper = new DomHelper(window),\n      element = null\n      eventDispatcher = null,\n      font = null;\n\n  beforeEach(function () {\n    element = domHelper.getLoadWindow().document.documentElement;\n    config = {\n      loading: jasmine.createSpy('loading'),\n      active: jasmine.createSpy('active'),\n      inactive: jasmine.createSpy('inactive'),\n      fontloading: jasmine.createSpy('fontloading'),\n      fontactive: jasmine.createSpy('fontactive'),\n      fontinactive: jasmine.createSpy('fontinactive'),\n      classes: true,\n      events: true\n    };\n\n    element.className = '';\n\n    eventDispatcher = new EventDispatcher(domHelper, config);\n\n    font = new Font('My Family', 'n4');\n  });\n\n  describe('#dispatchLoading', function () {\n    beforeEach(function () {\n      eventDispatcher.dispatchLoading();\n    });\n\n    it('should call the correct callback', function () {\n      expect(config.loading).toHaveBeenCalled();\n    });\n\n    it('should set the correct class name', function () {\n      expect(element.className).toEqual('wf-loading');\n    });\n  });\n\n  describe('#dispatchFontLoading', function () {\n    beforeEach(function () {\n      eventDispatcher.dispatchFontLoading(font);\n    });\n\n    it('should call the correct callback', function () {\n      expect(config.fontloading).toHaveBeenCalledWith('My Family', 'n4');\n    });\n\n    it('should set the correct class name', function () {\n      expect(element.className).toEqual('wf-myfamily-n4-loading');\n    });\n  });\n\n  describe('#dispatchFontInactive', function () {\n    beforeEach(function () {\n      eventDispatcher.dispatchFontInactive(font);\n    });\n\n    it('should call the correct callback', function () {\n      expect(config.fontinactive).toHaveBeenCalledWith('My Family', 'n4');\n    });\n\n    it('should set the correct class name', function () {\n      expect(element.className).toEqual('wf-myfamily-n4-inactive');\n    });\n  });\n\n  describe('#dispatchFontInactive - with loading class', function () {\n    beforeEach(function () {\n      eventDispatcher.dispatchFontLoading(font);\n      eventDispatcher.dispatchFontInactive(font);\n    });\n\n    it('should set the correct class name', function () {\n      expect(element.className).toEqual('wf-myfamily-n4-inactive');\n    });\n  });\n\n  describe('#dispatchFontInactive - with active class', function () {\n    beforeEach(function () {\n      eventDispatcher.dispatchFontActive(font);\n      eventDispatcher.dispatchFontInactive(font);\n    });\n\n    it('should not append the inactive class name', function () {\n      expect(element.className).toEqual('wf-myfamily-n4-active');\n    });\n\n    it('should still call the correct callback', function () {\n      expect(config.fontinactive).toHaveBeenCalledWith('My Family', 'n4');\n    });\n  });\n\n  describe('#dispatchFontActive', function () {\n    beforeEach(function () {\n      eventDispatcher.dispatchFontActive(font);\n    });\n\n    it('should call the correct callback', function () {\n      expect(config.fontactive).toHaveBeenCalledWith('My Family', 'n4');\n    });\n\n    it('should set the correct class name', function () {\n      expect(element.className).toEqual('wf-myfamily-n4-active');\n    });\n  });\n\n  describe('#dispatchFontActive - with loading class', function () {\n    beforeEach(function () {\n      eventDispatcher.dispatchFontLoading(font);\n      eventDispatcher.dispatchFontActive(font);\n    });\n\n    it('should set the correct class name', function () {\n      expect(element.className).toEqual('wf-myfamily-n4-active');\n    });\n  });\n\n  describe('#dispatchFontActive - with inactive class', function () {\n    beforeEach(function () {\n      eventDispatcher.dispatchFontInactive(font);\n      eventDispatcher.dispatchFontActive(font);\n    });\n\n    it('should set the correct class', function () {\n      expect(element.className).toEqual('wf-myfamily-n4-active');\n    });\n  });\n\n  describe('#dispatchInactive', function () {\n    beforeEach(function () {\n      eventDispatcher.dispatchInactive();\n    });\n\n    it('should call the correct callback', function () {\n      expect(config.inactive).toHaveBeenCalled();\n    });\n\n    it('should set the correct class name', function () {\n      expect(element.className).toEqual('wf-inactive');\n    });\n  });\n\n  describe('#dispatchInactive - with loading class', function () {\n    beforeEach(function () {\n      eventDispatcher.dispatchLoading();\n      eventDispatcher.dispatchInactive();\n    });\n\n    it('should set the correct class name', function () {\n      expect(element.className).toEqual('wf-inactive');\n    });\n  });\n\n  describe('#dispatchInactive - with active class', function () {\n    beforeEach(function () {\n      eventDispatcher.dispatchActive();\n      eventDispatcher.dispatchInactive();\n    });\n\n    it('should not set the the inactive class', function () {\n      expect(element.className).toEqual('wf-active');\n    });\n\n    it('should still call the inactive callback', function () {\n      expect(config.inactive).toHaveBeenCalled();\n    });\n  });\n\n  describe('#dispatchActive', function () {\n    beforeEach(function () {\n      eventDispatcher.dispatchActive();\n    });\n\n    it('should call the correct callback', function () {\n      expect(config.active).toHaveBeenCalled();\n    });\n\n    it('should set the correct class name', function () {\n      expect(element.className).toEqual('wf-active');\n    });\n  });\n\n  describe('#dispatchActive - with loading class', function () {\n    beforeEach(function () {\n      eventDispatcher.dispatchLoading();\n      eventDispatcher.dispatchActive();\n    });\n\n    it('should set the correct class name', function () {\n      expect(element.className).toEqual('wf-active');\n    });\n  });\n\n  describe('#dispatchActive - with inactive class', function () {\n    beforeEach(function () {\n      eventDispatcher.dispatchInactive();\n      eventDispatcher.dispatchActive();\n    });\n\n    it('should set the correct class name', function () {\n      expect(element.className).toEqual('wf-active');\n    });\n  });\n\n  describe('disable classes and events', function () {\n    beforeEach(function () {\n      config.classes = false;\n      config.events = false;\n      eventDispatcher = new EventDispatcher(domHelper, config);\n      eventDispatcher.dispatchInactive();\n      eventDispatcher.dispatchActive();\n      eventDispatcher.dispatchLoading();\n      eventDispatcher.dispatchFontInactive(font);\n      eventDispatcher.dispatchFontActive(font);\n      eventDispatcher.dispatchFontLoading(font);\n    });\n\n    afterEach(function () {\n      config.classes = true;\n      config.events = true;\n    });\n\n    it('should not fire any events', function () {\n      expect(config.inactive).not.toHaveBeenCalled();\n      expect(config.active).not.toHaveBeenCalled();\n      expect(config.loading).not.toHaveBeenCalled();\n      expect(config.fontinactive).not.toHaveBeenCalled();\n      expect(config.fontactive).not.toHaveBeenCalled();\n      expect(config.fontloading).not.toHaveBeenCalled();\n    });\n  });\n\n  describe('disable classes', function () {\n    beforeEach(function () {\n      config.classes = false;\n      eventDispatcher = new EventDispatcher(domHelper, config);\n      eventDispatcher.dispatchInactive();\n      eventDispatcher.dispatchActive();\n      eventDispatcher.dispatchLoading();\n      eventDispatcher.dispatchFontInactive(font);\n      eventDispatcher.dispatchFontActive(font);\n      eventDispatcher.dispatchFontLoading(font);\n    });\n\n    afterEach(function () {\n      config.classes = true;\n    });\n\n    it('should not fire any events', function () {\n      expect(element.className).toEqual('');\n    });\n  });\n});\n"
  },
  {
    "path": "spec/core/font_spec.js",
    "content": "describe('Font', function () {\n  var Font = webfont.Font;\n\n  describe('#quote', function () {\n    var quote = function (font) {\n      return new Font(font).getCssName();\n    };\n\n    it('should quote names with spaces', function () {\n      expect(quote('My Family')).toEqual(\"'My Family'\");\n    });\n\n    it('should quote names with spaces and double quotes', function () {\n      expect(quote('\"My Family\"')).toEqual(\"'My Family'\");\n    });\n\n    it('should quote names with spaces and single quotes', function () {\n      expect(quote(\"'My Family'\")).toEqual(\"'My Family'\");\n    });\n\n    it('should quote multiple single quoted names separated with a comma', function () {\n      expect(quote(\"'family 1','family 2'\")).toEqual(\"'family 1','family 2'\");\n    });\n\n    it('should quote multiple single quoted names separated with a comma and space', function () {\n      expect(quote(\"'family 1', 'family 2'\")).toEqual(\"'family 1','family 2'\");\n    });\n\n    it('should quote family names starting with a number', function () {\n      expect(quote('5test')).toEqual(\"'5test'\");\n    });\n\n    it('should not quote when there is no space', function () {\n      expect(quote('MyFamily')).toEqual('MyFamily');\n    });\n\n    it('should remove quotes when they are unnecesssary', function () {\n      expect(quote('\"MyFamily\"')).toEqual('MyFamily');\n    });\n\n    it('should not quote multiple names when there is no space', function () {\n      expect(quote(\"'family-1', 'family-2'\")).toEqual('family-1,family-2');\n    });\n  });\n\n  describe('#toCssString', function () {\n    function toCssString(fvd) {\n      return new Font('My Family', fvd).toCssString();\n    }\n\n    it('should expand font styles correctly', function () {\n      expect(toCssString('n4')).toEqual(\"normal 400 300px 'My Family'\");\n      expect(toCssString('i4')).toEqual(\"italic 400 300px 'My Family'\");\n      expect(toCssString('o4')).toEqual(\"oblique 400 300px 'My Family'\");\n    });\n\n    it('should expand weights correctly', function () {\n      for (var i = 1; i < 10; i += 1) {\n        expect(toCssString('n' + i)).toEqual(\"normal \" + (i * 100) + \" 300px 'My Family'\");\n      }\n    });\n  });\n\n  describe('#getCssVariation', function () {\n    function toCss(fvd) {\n      return new Font('My Family', fvd).getCssVariation();\n    }\n\n    it('should expand font-style', function () {\n      expect(toCss('n4')).toEqual('font-style:normal;font-weight:400;');\n      expect(toCss('i4')).toEqual('font-style:italic;font-weight:400;');\n      expect(toCss('o4')).toEqual('font-style:oblique;font-weight:400;');\n    });\n\n    it('should expand weights correctly', function () {\n      for (var i = 1; i < 10; i += 1) {\n        expect(toCss('n' + i)).toEqual('font-style:normal;font-weight:' + (i * 100) + ';');\n      }\n    });\n\n    it('should not expand incorrect input', function () {\n      expect(toCss('')).toEqual('font-style:normal;font-weight:400;');\n      expect(toCss('n')).toEqual('font-style:normal;font-weight:400;');\n      expect(toCss('1')).toEqual('font-style:normal;font-weight:400;');\n      expect(toCss('n1x')).toEqual('font-style:normal;font-weight:400;');\n    });\n  });\n\n  describe('parseCssVariation', function () {\n    function toFvd(css) {\n      return Font.parseCssVariation(css);\n    }\n\n    it('should default to n4 when there is no description', function () {\n      expect(toFvd('')).toEqual('n4');\n      expect(toFvd(null)).toEqual('n4');\n      expect(toFvd(undefined)).toEqual('n4');\n    });\n\n    it('should compact font style', function () {\n      expect(toFvd('font-style: normal;')).toEqual('n4');\n      expect(toFvd('font-style: italic;')).toEqual('i4');\n      expect(toFvd('font-style: oblique;')).toEqual('o4');\n    });\n\n    it('should return the default value when font-style is incorrect', function () {\n      expect(toFvd('font-style: other;')).toEqual('n4');\n    });\n\n    it('should compact font weight', function () {\n      expect(toFvd('font-weight: normal;')).toEqual('n4');\n      expect(toFvd('font-weight: bold;')).toEqual('n7');\n      for (var i = 1; i < 10; i += 1) {\n        expect(toFvd('font-weight: ' + (i * 100) + ';')).toEqual('n' + i);\n      }\n    });\n\n    it('should return the default value when font-weight is incorrect', function () {\n      expect(toFvd('font-weight: 140;')).toEqual('n4');\n      expect(toFvd('font-weight: other;')).toEqual('n4');\n    });\n\n    it('should compact multiple properties', function () {\n      expect(toFvd('font-weight: bold; font-style: italic;')).toEqual('i7');\n      expect(toFvd('; font-weight: bold; font-style: italic;')).toEqual('i7');\n      expect(toFvd('font-style:italic;font-weight:bold;')).toEqual('i7');\n      expect(toFvd('   font-style:   italic    ;\\n\\nfont-weight:   bold;  ')).toEqual('i7');\n    });\n\n    it('should return default values for incorrect individual properties', function () {\n      expect(toFvd('src: url(/font.otf)')).toEqual('n4');\n      expect(toFvd('font-weight: 900; src: url(/font.otf);')).toEqual('n9');\n      expect(toFvd('font-weight: 800; font-stretch:condensed;')).toEqual('n8');\n    });\n  });\n});\n"
  },
  {
    "path": "spec/core/fontmoduleloader_spec.js",
    "content": "describe('FontModuleLoader', function () {\n  var FontModuleLoader = webfont.FontModuleLoader;\n\n  describe('#getModules', function () {\n    var fontModuleLoader = null;\n\n    beforeEach(function () {\n      fontModuleLoader = new FontModuleLoader();\n    });\n\n    it('should return an empty array without modules', function () {\n      var modules = fontModuleLoader.getModules();\n\n      expect(modules).not.toBeNull();\n      expect(modules.length).toEqual(0);\n    });\n\n    it('should have modules', function () {\n      fontModuleLoader.addModuleFactory('booh', function () {\n        return {\n          scary: true\n        };\n      });\n\n      fontModuleLoader.addModuleFactory('haha', function () {\n        return {\n          funny: true\n        };\n      });\n\n      fontModuleLoader.addModuleFactory('moo', function () {\n        return {\n          cowy: true\n        };\n      });\n\n      var modules = fontModuleLoader.getModules({\n            booh: {},\n            moo: {},\n            nothing: {}\n          });\n\n      expect(modules).not.toBeNull();\n      expect(modules.length).toEqual(2);\n\n      var module = modules[0];\n      expect(module).not.toBeNull();\n      expect(module.scary || module.cowy).toBe(true);\n\n      var module = modules[1];\n      expect(module).not.toBeNull();\n      expect(module.scary || module.cowy).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "spec/core/fontruler_spec.js",
    "content": "describe('FontRuler', function () {\n  var Font = webfont.Font,\n      FontRuler = webfont.FontRuler,\n      DomHelper = webfont.DomHelper,\n      Size = webfont.Size,\n      domHelper = null,\n      font = null;\n\n  beforeEach(function () {\n    font = new Font('sans-serif');\n    domHelper = new DomHelper(window);\n  });\n\n  it('should prevent a long test string from word wrapping', function () {\n    var fontRulerA = new FontRuler(domHelper, 'abc'),\n        fontRulerB = new FontRuler(domHelper, 'abc HelloWorld,thisshouldwrap!!!!');\n\n    fontRulerA.insert();\n    fontRulerB.insert();\n\n    fontRulerA.setFont(font);\n    fontRulerB.setFont(font);\n\n    var widthA = fontRulerA.getWidth(),\n        widthB = fontRulerB.getWidth();\n\n    expect(widthA).not.toEqual(widthB);\n  });\n});\n"
  },
  {
    "path": "spec/core/fontwatcher_spec.js",
    "content": "describe('FontWatcher', function () {\n  var FontWatcher = webfont.FontWatcher,\n      FontWatchRunner = webfont.FontWatchRunner,\n      NativeFontWatchRunner = webfont.NativeFontWatchRunner,\n      Font = webfont.Font,\n      DomHelper = webfont.DomHelper,\n      Version = webfont.Version,\n      domHelper = new DomHelper(window),\n      eventDispatcher = {},\n      testStrings = null,\n      timeout = null,\n      font1 = null,\n      font2 = null,\n      font3 = null,\n      font4 = null,\n      activeFonts = [];\n\n  beforeEach(function () {\n    font1 = new Font('font1');\n    font2 = new Font('font2');\n    font3 = new Font('font3');\n    font4 = new Font('font4');\n    activeFonts = [];\n    testStrings = jasmine.createSpy('testStrings');\n    timeout = jasmine.createSpy('timeout');\n    eventDispatcher.dispatchLoading = jasmine.createSpy('dispatchLoading');\n    eventDispatcher.dispatchFontLoading = jasmine.createSpy('dispatchFontLoading');\n    eventDispatcher.dispatchFontActive = jasmine.createSpy('dispatchFontActive');\n    eventDispatcher.dispatchFontInactive = jasmine.createSpy('dispatchFontInactive');\n    eventDispatcher.dispatchActive = jasmine.createSpy('dispatchActive');\n    eventDispatcher.dispatchInactive = jasmine.createSpy('dispatchInactive');\n\n    var fakeStart = function (font, fontTestString) {\n      var found = false;\n\n      testStrings(this.fontTestString_);\n      timeout(this.timeout_);\n\n      for (var i = 0; i < activeFonts.length; i += 1) {\n        if (activeFonts[i].getName() === this.font_.getName()) {\n          found = true;\n          break;\n        }\n      }\n\n      if (found) {\n        this.activeCallback_(this.font_);\n      } else {\n        this.inactiveCallback_(this.font_);\n      }\n    };\n\n    spyOn(FontWatchRunner.prototype, 'start').andCallFake(fakeStart);\n    spyOn(NativeFontWatchRunner.prototype, 'start').andCallFake(fakeStart);\n  });\n\n  if (!!window.FontFace) {\n    describe('use native font loading API', function () {\n      beforeEach(function () {\n        FontWatcher.SHOULD_USE_NATIVE_LOADER = null;\n      });\n\n      it('works on Chrome', function () {\n        spyOn(FontWatcher, 'getUserAgent').andReturn('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36');\n        expect(FontWatcher.shouldUseNativeLoader()).toEqual(true);\n      });\n\n      it('is disabled on Firefox <= 42', function () {\n        spyOn(FontWatcher, 'getUserAgent').andReturn('Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:42.0) Gecko/20100101 Firefox/42.0')\n        expect(FontWatcher.shouldUseNativeLoader()).toEqual(false);\n      });\n\n      it('is enabled on Firefox > 43', function () {\n        spyOn(FontWatcher, 'getUserAgent').andReturn('Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:43.0) Gecko/20100101 Firefox/43.0');\n        expect(FontWatcher.shouldUseNativeLoader()).toEqual(true);\n      });\n\n      it('is disabled on Safari > 10', function () {\n        spyOn(FontWatcher, 'getUserAgent').andReturn('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/602.2.14 (KHTML, like Gecko) Version/10.0.1 Safari/602.2.14');\n        spyOn(FontWatcher, 'getVendor').andReturn('Apple');\n        expect(FontWatcher.shouldUseNativeLoader()).toEqual(false);\n      });\n    });\n  }\n\n  describe('watch zero fonts', function () {\n    it('should call inactive when there are no fonts to load', function () {\n      activeFonts = [];\n      var fontWatcher = new FontWatcher(domHelper, eventDispatcher);\n\n      fontWatcher.watchFonts([], {}, null, true);\n      expect(eventDispatcher.dispatchInactive).toHaveBeenCalled();\n    });\n\n    it('should not call inactive when there are no fonts to load, but this is not the last set', function () {\n      activeFonts = [];\n      var fontWatcher = new FontWatcher(domHelper, eventDispatcher);\n\n      fontWatcher.watchFonts([], {}, null, false);\n      expect(eventDispatcher.dispatchInactive).not.toHaveBeenCalled();\n    });\n  });\n\n  describe('watch one font not last', function () {\n    it('should not call font inactive, inactive or active', function () {\n      activeFonts = [font1];\n      var fontWatcher = new FontWatcher(domHelper, eventDispatcher);\n\n      fontWatcher.watchFonts([font1], {}, null, false);\n      expect(eventDispatcher.dispatchFontInactive).not.toHaveBeenCalled();\n      expect(eventDispatcher.dispatchActive).not.toHaveBeenCalled();\n      expect(eventDispatcher.dispatchInactive).not.toHaveBeenCalled();\n    });\n  });\n\n  describe('watch one font active', function () {\n    it('should call font active and active', function () {\n      activeFonts = [font1];\n      var fontWatcher = new FontWatcher(domHelper, eventDispatcher);\n\n      fontWatcher.watchFonts([font1], {}, null, true);\n      expect(eventDispatcher.dispatchFontLoading).toHaveBeenCalledWith(font1);\n      expect(eventDispatcher.dispatchFontActive).toHaveBeenCalledWith(font1);\n      expect(eventDispatcher.dispatchFontInactive).not.toHaveBeenCalled();\n      expect(eventDispatcher.dispatchActive).toHaveBeenCalled();\n      expect(eventDispatcher.dispatchInactive).not.toHaveBeenCalled();\n    });\n  });\n\n  describe('watch one font inactive', function () {\n    it('should call inactive', function () {\n      activeFonts = [];\n      var fontWatcher = new FontWatcher(domHelper, eventDispatcher);\n\n      fontWatcher.watchFonts([font1], {}, null, true);\n      expect(eventDispatcher.dispatchFontLoading).toHaveBeenCalledWith(font1);\n      expect(eventDispatcher.dispatchFontActive).not.toHaveBeenCalled();\n      expect(eventDispatcher.dispatchFontInactive).toHaveBeenCalledWith(font1);\n      expect(eventDispatcher.dispatchActive).not.toHaveBeenCalled();\n      expect(eventDispatcher.dispatchInactive).toHaveBeenCalled();\n    });\n  });\n\n  describe('watch multiple fonts active', function () {\n    it('should call font active and active', function () {\n      activeFonts = [font1, font2, font3];\n      var fontWatcher = new FontWatcher(domHelper, eventDispatcher);\n\n      fontWatcher.watchFonts([font1, font2, font3], {}, null, true);\n      expect(eventDispatcher.dispatchFontLoading).toHaveBeenCalledWith(font1);\n      expect(eventDispatcher.dispatchFontActive).toHaveBeenCalledWith(font1);\n      expect(eventDispatcher.dispatchFontInactive).not.toHaveBeenCalled();\n      expect(eventDispatcher.dispatchActive).toHaveBeenCalled();\n      expect(eventDispatcher.dispatchInactive).not.toHaveBeenCalled();\n    });\n  });\n\n  describe('watch multiple fonts inactive', function () {\n    it('should call inactive', function () {\n      activeFonts = [];\n      var fontWatcher = new FontWatcher(domHelper, eventDispatcher);\n\n      fontWatcher.watchFonts([font1, font2, font3], {}, null, true);\n      expect(eventDispatcher.dispatchFontLoading).toHaveBeenCalledWith(font1);\n      expect(eventDispatcher.dispatchFontActive).not.toHaveBeenCalled();\n      expect(eventDispatcher.dispatchFontInactive).toHaveBeenCalledWith(font1);\n      expect(eventDispatcher.dispatchActive).not.toHaveBeenCalled();\n      expect(eventDispatcher.dispatchInactive).toHaveBeenCalled();\n    });\n  });\n\n  describe('watch multiple fonts mixed', function () {\n    it('should call the correct callbacks', function () {\n      activeFonts = [font1, font3];\n      var fontWatcher = new FontWatcher(domHelper, eventDispatcher);\n\n      fontWatcher.watchFonts([font1, font2, font3], {}, null, true);\n      expect(eventDispatcher.dispatchFontLoading.callCount).toEqual(3);\n      expect(eventDispatcher.dispatchFontLoading.calls[0].args[0]).toEqual(font1);\n      expect(eventDispatcher.dispatchFontLoading.calls[1].args[0]).toEqual(font2);\n      expect(eventDispatcher.dispatchFontLoading.calls[2].args[0]).toEqual(font3);\n\n      expect(eventDispatcher.dispatchFontActive.callCount).toEqual(2);\n      expect(eventDispatcher.dispatchFontActive.calls[0].args[0]).toEqual(font1);\n      expect(eventDispatcher.dispatchFontActive.calls[1].args[0]).toEqual(font3);\n\n      expect(eventDispatcher.dispatchFontInactive.callCount).toEqual(1);\n      expect(eventDispatcher.dispatchFontInactive.calls[0].args[0]).toEqual(font2);\n\n      expect(eventDispatcher.dispatchActive).toHaveBeenCalled();\n      expect(eventDispatcher.dispatchInactive).not.toHaveBeenCalled();\n    });\n  });\n\n  describe('watch multiple fonts with descriptions', function () {\n    it('should call the correct callbacks', function () {\n      var font5 = new Font('font4', 'i7'),\n          font6 = new Font('font5'),\n          font7 = new Font('font6'),\n          font8 = new Font('font7', 'i4'),\n          font9 = new Font('font8', 'n7');\n\n      activeFonts = [font5, font6];\n      var fontWatcher = new FontWatcher(domHelper, eventDispatcher);\n\n      fontWatcher.watchFonts([font5, font6, font7, font8, font9], {}, null, true);\n      expect(eventDispatcher.dispatchFontLoading.callCount).toEqual(5);\n      expect(eventDispatcher.dispatchFontLoading).toHaveBeenCalledWith(font5);\n      expect(eventDispatcher.dispatchFontLoading).toHaveBeenCalledWith(font6);\n      expect(eventDispatcher.dispatchFontLoading).toHaveBeenCalledWith(font7);\n      expect(eventDispatcher.dispatchFontLoading).toHaveBeenCalledWith(font8);\n      expect(eventDispatcher.dispatchFontLoading).toHaveBeenCalledWith(font9);\n\n      expect(eventDispatcher.dispatchFontActive.callCount).toEqual(2);\n      expect(eventDispatcher.dispatchFontActive).toHaveBeenCalledWith(font5);\n      expect(eventDispatcher.dispatchFontActive).toHaveBeenCalledWith(font6);\n\n      expect(eventDispatcher.dispatchFontInactive.callCount).toEqual(3);\n      expect(eventDispatcher.dispatchFontInactive).toHaveBeenCalledWith(font7);\n      expect(eventDispatcher.dispatchFontInactive).toHaveBeenCalledWith(font8);\n      expect(eventDispatcher.dispatchFontInactive).toHaveBeenCalledWith(font9);\n\n      expect(eventDispatcher.dispatchInactive).not.toHaveBeenCalled();\n      expect(eventDispatcher.dispatchActive).toHaveBeenCalled();\n    });\n  });\n\n  describe('watch multiple fonts with test strings', function () {\n    it('should use the correct tests strings', function () {\n      activeFonts = [font1, font2];\n\n      var defaultTestString = FontWatcher.SHOULD_USE_NATIVE_LOADER ? undefined : FontWatchRunner.DEFAULT_TEST_STRING;\n      var fontWatcher = new FontWatcher(domHelper, eventDispatcher);\n\n      fontWatcher.watchFonts([font1, font2, font3, font4], {\n        'font1': 'testString1',\n        'font2': null,\n        'font3': 'testString2',\n        'font4': null\n      }, null, true);\n\n      expect(testStrings.callCount).toEqual(4);\n      expect(testStrings.calls[0].args[0]).toEqual('testString1');\n      expect(testStrings.calls[1].args[0]).toEqual(defaultTestString);\n      expect(testStrings.calls[2].args[0]).toEqual('testString2');\n      expect(testStrings.calls[3].args[0]).toEqual(defaultTestString);\n    });\n  });\n\n  it('should pass on the timeout to FontWatchRunner', function () {\n    var fontWatcher = new FontWatcher(domHelper, eventDispatcher, 4000);\n\n    fontWatcher.watchFonts([font1], {}, null, true);\n\n    expect(timeout).toHaveBeenCalledWith(4000);\n  });\n});\n"
  },
  {
    "path": "spec/core/fontwatchrunner_spec.js",
    "content": "describe('FontWatchRunner', function () {\n  var FontWatchRunner = webfont.FontWatchRunner,\n      Font = webfont.Font,\n      BrowserInfo = webfont.BrowserInfo,\n      DomHelper = webfont.DomHelper,\n      FontRuler = webfont.FontRuler;\n\n  var domHelper = null,\n      activeCallback = null,\n      inactiveCallback = null,\n      nullFont = null,\n      sourceSansA = null,\n      sourceSansB = null,\n      elena = null;\n\n  beforeEach(function () {\n    domHelper = new DomHelper(window);\n\n    activeCallback = jasmine.createSpy('activeCallback');\n    inactiveCallback = jasmine.createSpy('inactiveCallback');\n\n    nullFont = new Font('__webfontloader_test__');\n    sourceSansA = new Font('SourceSansA');\n    sourceSansB = new Font('SourceSansB');\n    elena = new Font('Elena');\n  });\n\n  it('should fail to load a null font', function () {\n    var fontWatchRunner = new FontWatchRunner(activeCallback, inactiveCallback,\n        domHelper, nullFont, 500, {});\n\n    runs(function () {\n      fontWatchRunner.start();\n    });\n\n    waitsFor(function () {\n      return activeCallback.wasCalled || inactiveCallback.wasCalled;\n    });\n\n    runs(function () {\n      expect(inactiveCallback).toHaveBeenCalledWith(nullFont);\n    });\n  });\n\n  it('should load font succesfully', function () {\n    var fontWatchRunner = new FontWatchRunner(activeCallback, inactiveCallback,\n        domHelper, sourceSansA,  5000),\n        ruler = new FontRuler(domHelper, 'abcdef'),\n        monospace = new Font('monospace'),\n        sourceSansAFallback = new Font(\"'SourceSansA', monospace\"),\n        activeWidth = null,\n        originalWidth = null,\n        finalCheck = false;\n\n    runs(function () {\n      ruler.insert();\n      ruler.setFont(monospace);\n      originalWidth = ruler.getWidth();\n      ruler.setFont(sourceSansAFallback);\n      fontWatchRunner.start();\n    });\n\n    waitsFor(function () {\n      return activeCallback.wasCalled || inactiveCallback.wasCalled;\n    });\n\n    runs(function () {\n      expect(activeCallback).toHaveBeenCalledWith(sourceSansA);\n      activeWidth = ruler.getWidth();\n      expect(activeWidth).not.toEqual(originalWidth);\n\n      window.setTimeout(function () {\n        finalCheck = true;\n      }, 200);\n    });\n\n    waitsFor(function () {\n      return finalCheck;\n    });\n\n    runs(function () {\n      expect(ruler.getWidth()).not.toEqual(originalWidth);\n      expect(ruler.getWidth()).toEqual(activeWidth);\n    });\n  });\n\n  it('should attempt to load a non-existing font', function () {\n    var fontWatchRunner = new FontWatchRunner(activeCallback, inactiveCallback,\n        domHelper, elena, 500, {});\n\n    runs(function () {\n      fontWatchRunner.start();\n    });\n\n    waitsFor(function () {\n      return activeCallback.wasCalled || inactiveCallback.wasCalled;\n    });\n\n    runs(function () {\n      expect(inactiveCallback).toHaveBeenCalledWith(elena);\n    });\n  });\n\n  it('should load even if @font-face is inserted after watching has started', function () {\n    var fontWatchRunner = new FontWatchRunner(activeCallback, inactiveCallback,\n        domHelper, sourceSansB, 5000),\n        ruler = new FontRuler(domHelper, 'abcdef'),\n        monospace = new Font('monospace'),\n        sourceSansBFallback = new Font(\"'SourceSansB', monospace\"),\n        activeWidth = null,\n        originalWidth = null,\n        finalCheck = false;\n\n    runs(function () {\n      ruler.insert();\n      ruler.setFont(monospace);\n      originalWidth = ruler.getWidth();\n      ruler.setFont(sourceSansBFallback);\n      fontWatchRunner.start();\n      var link = document.createElement('link');\n\n      link.rel = \"stylesheet\";\n      link.href= \"fixtures/fonts/sourcesansb.css\";\n\n      domHelper.insertInto('head', link);\n    });\n\n    waitsFor(function () {\n      return activeCallback.wasCalled || inactiveCallback.wasCalled;\n    });\n\n    runs(function () {\n      expect(activeCallback).toHaveBeenCalledWith(sourceSansB);\n      activeWidth = ruler.getWidth();\n      expect(activeWidth).not.toEqual(originalWidth);\n\n      window.setTimeout(function () {\n        finalCheck = true;\n      }, 200);\n    });\n\n    waitsFor(function () {\n      return finalCheck;\n    });\n\n    runs(function () {\n      expect(ruler.getWidth()).not.toEqual(originalWidth);\n      expect(ruler.getWidth()).toEqual(activeWidth);\n    });\n  });\n});\n"
  },
  {
    "path": "spec/core/nativefontwatchrunner_spec.js",
    "content": "describe('NativeFontWatchRunner', function () {\n  var NativeFontWatchRunner = webfont.NativeFontWatchRunner,\n      Font = webfont.Font,\n      DomHelper = webfont.DomHelper,\n      FontRuler = webfont.FontRuler;\n\n  var domHelper = null,\n      activeCallback = null,\n      inactiveCallback = null,\n      nullFont = null,\n      sourceSansC = null,\n      sourceSansDup = null,\n      elena = null;\n\n  beforeEach(function () {\n    domHelper = new DomHelper(window);\n\n    activeCallback = jasmine.createSpy('activeCallback');\n    inactiveCallback = jasmine.createSpy('inactiveCallback');\n\n    nullFont = new Font('__webfontloader_test_3__');\n    sourceSansC = new Font('SourceSansC');\n    sourceSansDup = new Font('SourceSansDup');\n    elena = new Font('Elena');\n  });\n\n  if (window['FontFace']) {\n    it('should fail to load a null font', function () {\n      var fontWatchRunner = new NativeFontWatchRunner(activeCallback, inactiveCallback,\n          domHelper, nullFont, 500);\n\n      runs(function () {\n        fontWatchRunner.start();\n      });\n\n      waitsFor(function () {\n        return activeCallback.wasCalled || inactiveCallback.wasCalled;\n      });\n\n      runs(function () {\n        expect(inactiveCallback).toHaveBeenCalledWith(nullFont);\n      });\n    });\n\n    function succesfulLoadingSpec(getFontToBeLoaded, getFontNameToBeLoaded) {\n      var fontToBeLoaded = getFontToBeLoaded(),\n          fontNameToBeLoaded = getFontNameToBeLoaded(),\n          fontWatchRunner = new NativeFontWatchRunner(activeCallback, inactiveCallback,\n          domHelper, fontToBeLoaded),\n          ruler = new FontRuler(domHelper, 'abcdef'),\n          monospace = new Font('monospace'),\n          fallbackFont = new Font(fontNameToBeLoaded + ', monospace'),\n          activeWidth = null,\n          originalWidth = null,\n          finalCheck = false;\n\n      runs(function () {\n        ruler.insert();\n        ruler.setFont(monospace);\n        originalWidth = ruler.getWidth();\n        ruler.setFont(fallbackFont);\n        fontWatchRunner.start();\n      });\n\n      waitsFor(function () {\n        return activeCallback.wasCalled || inactiveCallback.wasCalled;\n      });\n\n      runs(function () {\n        expect(activeCallback).toHaveBeenCalledWith(fontToBeLoaded);\n        activeWidth = ruler.getWidth();\n        expect(activeWidth).not.toEqual(originalWidth);\n\n        window.setTimeout(function () {\n          finalCheck = true;\n        }, 200);\n      });\n\n      waitsFor(function () {\n        return finalCheck;\n      });\n\n      runs(function () {\n        expect(ruler.getWidth()).not.toEqual(originalWidth);\n        expect(ruler.getWidth()).toEqual(activeWidth);\n      });\n    }\n\n    it('should load font succesfully',\n       succesfulLoadingSpec.bind(null, function() { return sourceSansC; }, function() { return 'SourceSansC'; }));\n\n    it('should load font succesfully even if it is duplicated',\n       succesfulLoadingSpec.bind(null, function() { return sourceSansDup; }, function() { return 'SourceSansDup'; }));\n\n    it('should attempt to load a non-existing font', function () {\n      var fontWatchRunner = new NativeFontWatchRunner(activeCallback, inactiveCallback,\n          domHelper, elena, 500);\n\n      runs(function () {\n        fontWatchRunner.start();\n      });\n\n      waitsFor(function () {\n        return activeCallback.wasCalled || inactiveCallback.wasCalled;\n      });\n\n      runs(function () {\n        expect(inactiveCallback).toHaveBeenCalledWith(elena);\n      });\n    });\n  }\n});\n"
  },
  {
    "path": "spec/core/size_spec.js",
    "content": "describe('Size', function () {\n  var Size = webfont.Size;\n\n  it('should return true on identical sizes', function () {\n    expect(new Size(10, 10).equals(new Size(10, 10))).toBe(true);\n  });\n\n  it('should return false when two sizes are different', function () {\n    expect(new Size(10, 10).equals(new Size(20, 20))).toBe(false);\n    expect(new Size(10, 10).equals(new Size(10, 20))).toBe(false);\n  });\n\n  it('should return false when one font is undefined or null', function () {\n    expect(new Size(10, 10).equals(undefined)).toBe(false);\n    expect(new Size(10, 10).equals(null)).toBe(false);\n  });\n});\n"
  },
  {
    "path": "spec/core/webfont_spec.js",
    "content": "describe('WebFont', function () {\n  var WebFont = webfont.WebFont,\n      Font = webfont.Font;\n      FontWatchRunner = webfont.FontWatchRunner,\n      NativeFontWatchRunner = webfont.NativeFontWatchRunner,\n      Version = webfont.Version,\n      Font = webfont.Font,\n      FontModuleLoader = webfont.FontModuleLoader,\n      fontModuleLoader = null;\n\n  beforeEach(function () {\n    fontModuleLoader = new FontModuleLoader();\n  });\n\n  describe('font load with context', function () {\n    var font = null,\n        testModule = null,\n        fakeMainWindow = {\n          document: {}\n        };\n\n    beforeEach(function () {\n      font = new WebFont(fakeMainWindow);\n      font.addModule('test', function (conf, domHelper) {\n        testModule = new function () {\n          this.domHelper = domHelper;\n        };\n        testModule.load = function (onReady) {\n          onReady([]);\n        };\n\n        return testModule;\n      });\n    });\n\n    it('should load with the correct context', function () {\n      font.load({\n        test: {\n          somedata: 'in french a cow says meuh'\n        },\n        context: window\n      });\n\n      expect(testModule.domHelper).not.toBeNull();\n      expect(testModule.domHelper).not.toBeUndefined();\n\n      expect(testModule.domHelper.getMainWindow()).toEqual(fakeMainWindow);\n      expect(testModule.domHelper.getLoadWindow()).toEqual(window);\n    });\n  });\n\n  describe('module failed to provide families and descriptions because it did not initialize properly', function () {\n    var webfont = null,\n        testModule = null,\n        font = null,\n        inactive = jasmine.createSpy('inactive'),\n        active = jasmine.createSpy('active');\n\n    beforeEach(function () {\n      font = new Font('Font1');\n      jasmine.Clock.useMock();\n      webfont = new WebFont(window);\n      webfont.addModule('test', function (conf, domHelper) {\n        testModule = new function () {\n          this.conf = conf;\n        };\n\n        spyOn(FontWatchRunner.prototype, 'start').andCallFake(function () {\n          if (conf.id) {\n            active(font);\n          } else {\n            inactive(font);\n          }\n        });\n\n        spyOn(NativeFontWatchRunner.prototype, 'start').andCallFake(function () {\n          if (conf.id) {\n            active(font);\n          } else {\n            inactive(font);\n          }\n        });\n\n        testModule.load = function (onReady) {\n          if (conf.id) {\n            onReady([font]);\n          } else {\n            onReady([]);\n          }\n        };\n\n        return testModule;\n      });\n    });\n\n    it('should load with a project id', function () {\n      webfont.load({\n        test: {\n          id: 'hello world'\n        },\n        inactive: inactive,\n        active: active\n      });\n\n      jasmine.Clock.tick(1);\n\n      expect(testModule).not.toBeNull();\n      expect(active).toHaveBeenCalled();\n    });\n\n    it('should not load without a project id', function () {\n      webfont.load({\n        test: {\n        },\n        inactive: inactive,\n        active: active\n      });\n\n      jasmine.Clock.tick(1);\n\n      expect(testModule).not.toBeNull();\n      expect(inactive).toHaveBeenCalled();\n    });\n  });\n\n  describe('should pass both fonts and test strings to onready', function () {\n    var font = null,\n        fontTestStrings = null,\n        testModule = null;\n\n    beforeEach(function () {\n      font = new WebFont(window);\n\n      font.addModule('test', function (conf, domHelper) {\n        testModule = new function () {};\n        testModule.load = function (onReady) {\n          onReady([new Font('Elena')], { 'Elena': '1234567' });\n        };\n\n        return testModule;\n      });\n\n      spyOn(font, 'onModuleReady_');\n    });\n\n    it('should have called onModuleReady with the correct font and test string', function () {\n      font.load({\n        'test': {}\n      });\n\n      expect(font.onModuleReady_).toHaveBeenCalled();\n      expect(font.onModuleReady_.calls[0].args[2]).toEqual([new Font('Elena')]);\n      expect(font.onModuleReady_.calls[0].args[3]).toEqual({ 'Elena': '1234567' });\n    });\n  });\n\n  describe('module fails to load', function () {\n    var font = null,\n        testModule = null,\n        inactive = null,\n        active = null;\n\n    beforeEach(function () {\n      inactive = jasmine.createSpy('inactive'),\n      active = jasmine.createSpy('active');\n\n      font = new WebFont(window, fontModuleLoader);\n\n      font.addModule('test', function (conf, domHelper) {\n        testModule = new function () {};\n        testModule.load = function (onReady) {\n          onReady([]);\n        };\n\n        return testModule;\n      });\n    });\n\n    it('times out and calls inactive', function () {\n      runs(function () {\n        font.load({\n          'test': {},\n          inactive: inactive,\n          active: active\n        });\n      });\n\n      waitsFor(function () {\n        return active.wasCalled || inactive.wasCalled;\n      });\n\n      runs(function () {\n        expect(inactive).toHaveBeenCalled();\n        expect(active).not.toHaveBeenCalled();\n      });\n    });\n  });\n\n  describe('synchronous load event', function () {\n    var font = null,\n        testModule = null,\n        inactive = null,\n        loading = null,\n        active = null;\n\n    beforeEach(function () {\n      inactive = jasmine.createSpy('inactive'),\n      active = jasmine.createSpy('active');\n      loading = jasmine.createSpy('loading');\n\n      font = new WebFont(window, fontModuleLoader);\n\n      font.addModule('test', function (conf, domHelper) {\n        testModule = new function () {};\n        testModule.load = function (onReady) {\n          onReady([new Font('Elena')]);\n        };\n\n        return testModule;\n      });\n    });\n\n    it('fires loading event correctly', function () {\n      runs(function () {\n        font.load({\n          'test': {},\n          inactive: inactive,\n          active: active,\n          loading: loading\n        });\n        expect(loading).toHaveBeenCalled();\n      });\n\n      waitsFor(function () {\n        return active.wasCalled || inactive.wasCalled;\n      });\n\n      runs(function () {\n        expect(inactive).toHaveBeenCalled();\n        expect(active).not.toHaveBeenCalled();\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "spec/deps.js",
    "content": "// This file was autogenerated by calcdeps.js\ngoog.addDependency(\"../../src/closure.js\", [], []);\ngoog.addDependency(\"../../src/core/cssclassname.js\", [\"webfont.CssClassName\"], []);\ngoog.addDependency(\"../../src/core/domhelper.js\", [\"webfont.DomHelper\"], []);\ngoog.addDependency(\"../../src/core/stylesheetwaiter.js\", [\"webfont.StyleSheetWaiter\"], []);\ngoog.addDependency(\"../../src/core/eventdispatcher.js\", [\"webfont.EventDispatcher\"], [\"webfont.CssClassName\"]);\ngoog.addDependency(\"../../src/core/font.js\", [\"webfont.Font\"], []);\ngoog.addDependency(\"../../src/core/fontmodule.js\", [\"webfont.FontModule\"], []);\ngoog.addDependency(\"../../src/core/fontmoduleloader.js\", [\"webfont.FontModuleLoader\",\"webfont.FontModuleFactory\"], []);\ngoog.addDependency(\"../../src/core/fontruler.js\", [\"webfont.FontRuler\"], []);\ngoog.addDependency(\"../../src/core/fontwatcher.js\", [\"webfont.FontWatcher\"], [\"webfont.FontWatchRunner\",\"webfont.NativeFontWatchRunner\"]);\ngoog.addDependency(\"../../src/core/fontwatchrunner.js\", [\"webfont.FontWatchRunner\"], [\"webfont.Font\",\"webfont.FontRuler\"]);\ngoog.addDependency(\"../../src/core/initialize.js\", [\"webfont\"], [\"webfont.WebFont\",\"webfont.modules.Typekit\",\"webfont.modules.Fontdeck\",\"webfont.modules.Monotype\",\"webfont.modules.Custom\",\"webfont.modules.google.GoogleFontApi\"]);\ngoog.addDependency(\"../../src/core/nativefontwatchrunner.js\", [\"webfont.NativeFontWatchRunner\"], [\"webfont.Font\"]);\ngoog.addDependency(\"../../src/core/webfont.js\", [\"webfont.WebFont\"], [\"webfont.DomHelper\",\"webfont.EventDispatcher\",\"webfont.FontWatcher\",\"webfont.FontModuleLoader\"]);\ngoog.addDependency(\"../../src/modules/custom.js\", [\"webfont.modules.Custom\"], [\"webfont.Font\", \"webfont.StyleSheetWaiter\"]);\ngoog.addDependency(\"../../src/modules/fontdeck.js\", [\"webfont.modules.Fontdeck\"], [\"webfont.Font\"]);\ngoog.addDependency(\"../../src/modules/google/fontapiparser.js\", [\"webfont.modules.google.FontApiParser\"], [\"webfont.Font\"]);\ngoog.addDependency(\"../../src/modules/google/fontapiurlbuilder.js\", [\"webfont.modules.google.FontApiUrlBuilder\"], []);\ngoog.addDependency(\"../../src/modules/google/googlefontapi.js\", [\"webfont.modules.google.GoogleFontApi\"], [\"webfont.modules.google.FontApiUrlBuilder\",\"webfont.modules.google.FontApiParser\",\"webfont.FontWatchRunner\", \"webfont.StyleSheetWaiter\"]);\ngoog.addDependency(\"../../src/modules/monotype.js\", [\"webfont.modules.Monotype\"], [\"webfont.Font\"]);\ngoog.addDependency(\"../../src/modules/typekit.js\", [\"webfont.modules.Typekit\"], [\"webfont.Font\"]);\n"
  },
  {
    "path": "spec/fixtures/external_script.js",
    "content": "window.EXTERNAL_SCRIPT_LOADED = true;\n"
  },
  {
    "path": "spec/fixtures/external_stylesheet.css",
    "content": "#TEST_ELEMENT {\n  display: block;\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 300px;\n}\n"
  },
  {
    "path": "spec/fixtures/fonts/LICENSE.txt",
    "content": "Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.\r\n\r\nThis Font Software is licensed under the SIL Open Font License, Version 1.1.\r\n\r\nThis license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL\r\n\r\n\r\n-----------------------------------------------------------\r\nSIL OPEN FONT LICENSE Version 1.1 - 26 February 2007\r\n-----------------------------------------------------------\r\n\r\nPREAMBLE\r\nThe goals of the Open Font License (OFL) are to stimulate worldwide\r\ndevelopment of collaborative font projects, to support the font creation\r\nefforts of academic and linguistic communities, and to provide a free and\r\nopen framework in which fonts may be shared and improved in partnership\r\nwith others.\r\n\r\nThe OFL allows the licensed fonts to be used, studied, modified and\r\nredistributed freely as long as they are not sold by themselves. The\r\nfonts, including any derivative works, can be bundled, embedded, \r\nredistributed and/or sold with any software provided that any reserved\r\nnames are not used by derivative works. The fonts and derivatives,\r\nhowever, cannot be released under any other type of license. The\r\nrequirement for fonts to remain under this license does not apply\r\nto any document created using the fonts or their derivatives.\r\n\r\nDEFINITIONS\r\n\"Font Software\" refers to the set of files released by the Copyright\r\nHolder(s) under this license and clearly marked as such. This may\r\ninclude source files, build scripts and documentation.\r\n\r\n\"Reserved Font Name\" refers to any names specified as such after the\r\ncopyright statement(s).\r\n\r\n\"Original Version\" refers to the collection of Font Software components as\r\ndistributed by the Copyright Holder(s).\r\n\r\n\"Modified Version\" refers to any derivative made by adding to, deleting,\r\nor substituting -- in part or in whole -- any of the components of the\r\nOriginal Version, by changing formats or by porting the Font Software to a\r\nnew environment.\r\n\r\n\"Author\" refers to any designer, engineer, programmer, technical\r\nwriter or other person who contributed to the Font Software.\r\n\r\nPERMISSION & CONDITIONS\r\nPermission is hereby granted, free of charge, to any person obtaining\r\na copy of the Font Software, to use, study, copy, merge, embed, modify,\r\nredistribute, and sell modified and unmodified copies of the Font\r\nSoftware, subject to the following conditions:\r\n\r\n1) Neither the Font Software nor any of its individual components,\r\nin Original or Modified Versions, may be sold by itself.\r\n\r\n2) Original or Modified Versions of the Font Software may be bundled,\r\nredistributed and/or sold with any software, provided that each copy\r\ncontains the above copyright notice and this license. These can be\r\nincluded either as stand-alone text files, human-readable headers or\r\nin the appropriate machine-readable metadata fields within text or\r\nbinary files as long as those fields can be easily viewed by the user.\r\n\r\n3) No Modified Version of the Font Software may use the Reserved Font\r\nName(s) unless explicit written permission is granted by the corresponding\r\nCopyright Holder. This restriction only applies to the primary font name as\r\npresented to the users.\r\n\r\n4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font\r\nSoftware shall not be used to promote, endorse or advertise any\r\nModified Version, except to acknowledge the contribution(s) of the\r\nCopyright Holder(s) and the Author(s) or with their explicit written\r\npermission.\r\n\r\n5) The Font Software, modified or unmodified, in part or in whole,\r\nmust be distributed entirely under this license, and must not be\r\ndistributed under any other license. The requirement for fonts to\r\nremain under this license does not apply to any document created\r\nusing the Font Software.\r\n\r\nTERMINATION\r\nThis license becomes null and void if any of the above conditions are\r\nnot met.\r\n\r\nDISCLAIMER\r\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\r\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\r\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\r\nOF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\r\nCOPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\r\nINCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\r\nDAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r\nFROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\r\nOTHER DEALINGS IN THE FONT SOFTWARE.\r\n"
  },
  {
    "path": "spec/fixtures/fonts/nullfont.css",
    "content": "@font-face{font-family:__webfontloader_test__;src:url(data:application/x-font-woff;base64,) format('woff'),url(data:font/truetype;base64,) format('truetype');}\n"
  },
  {
    "path": "spec/fixtures/fonts/nullfont1.css",
    "content": "@font-face{font-family:__webfontloader_test_1__;src:url(data:application/x-font-woff;base64,) format('woff'),url(data:font/truetype;base64,) format('truetype');}\n"
  },
  {
    "path": "spec/fixtures/fonts/nullfont2.css",
    "content": "@font-face{font-family:__webfontloader_test_2__;src:url(data:application/x-font-woff;base64,) format('woff'),url(data:font/truetype;base64,) format('truetype');}\n"
  },
  {
    "path": "spec/fixtures/fonts/nullfont3.css",
    "content": "@font-face{font-family:__webfontloader_test_3__;src:url(data:application/x-font-woff;base64,) format('woff'),url(data:font/truetype;base64,) format('truetype');}\n"
  },
  {
    "path": "spec/fixtures/fonts/sourcesansa.css",
    "content": "@font-face{font-family:SourceSansA;src:url(sourcesans.eot?#iefix) format('embedded-opentype'),url(sourcesans.woff) format('woff'),url(sourcesans.otf) format('opentype'),url(sourcesans.ttf) format('truetype'),url(sourcesans.svg#source_sans_proregular) format('svg');}\n"
  },
  {
    "path": "spec/fixtures/fonts/sourcesansb.css",
    "content": "@font-face{font-family:SourceSansB;src:url(sourcesans.eot?#iefix) format('embedded-opentype'),url(sourcesans.woff) format('woff'),url(sourcesans.otf) format('opentype'),url(sourcesans.ttf) format('truetype'),url(sourcesans.svg#source_sans_proregular) format('svg');}\n"
  },
  {
    "path": "spec/fixtures/fonts/sourcesansc.css",
    "content": "@font-face{font-family:SourceSansC;src:url(sourcesans.eot?#iefix) format('embedded-opentype'),url(sourcesans.woff) format('woff'),url(sourcesans.otf) format('opentype'),url(sourcesans.ttf) format('truetype'),url(sourcesans.svg#source_sans_proregular) format('svg');}\n"
  },
  {
    "path": "spec/fixtures/fonts/sourcesansd.css",
    "content": "@font-face{font-family:SourceSansD;src:url(sourcesans.eot?#iefix) format('embedded-opentype'),url(sourcesans.woff) format('woff'),url(sourcesans.otf) format('opentype'),url(sourcesans.ttf) format('truetype'),url(sourcesans.svg#source_sans_proregular) format('svg');}\n"
  },
  {
    "path": "spec/fixtures/fonts/sourcesansdup1.css",
    "content": "@font-face{font-family:SourceSansDup;src:url(sourcesans.eot?#iefix) format('embedded-opentype'),url(sourcesans.woff) format('woff'),url(sourcesans.otf) format('opentype'),url(sourcesans.ttf) format('truetype'),url(sourcesans.svg#source_sans_proregular) format('svg');}\n"
  },
  {
    "path": "spec/fixtures/fonts/sourcesansdup2.css",
    "content": "@font-face{font-family:SourceSansDup;src:url(sourcesans.eot?#iefix) format('embedded-opentype'),url(sourcesans.woff) format('woff'),url(sourcesans.otf) format('opentype'),url(sourcesans.ttf) format('truetype'),url(sourcesans.svg#source_sans_proregular) format('svg');}\n"
  },
  {
    "path": "spec/index.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <title>Webfontloader tests</title>\n    <link rel=\"stylesheet\" href=\"../tools/jasmine/jasmine.css\">\n\n    <link rel=\"stylesheet\" href=\"fixtures/fonts/nullfont.css\">\n    <link rel=\"stylesheet\" href=\"fixtures/fonts/nullfont1.css\">\n    <link rel=\"stylesheet\" href=\"fixtures/fonts/nullfont2.css\">\n    <link rel=\"stylesheet\" href=\"fixtures/fonts/nullfont3.css\">\n    <link rel=\"stylesheet\" href=\"fixtures/fonts/sourcesansa.css\">\n    <link rel=\"stylesheet\" href=\"fixtures/fonts/sourcesansc.css\">\n    <link rel=\"stylesheet\" href=\"fixtures/fonts/sourcesansdup1.css\">\n    <link rel=\"stylesheet\" href=\"fixtures/fonts/sourcesansdup2.css\">\n </head>\n <body>\n    <script>\n      CLOSURE_NO_DEPS = true;\n    </script>\n    <script src=\"../tools/jasmine/jasmine.js\"></script>\n    <script src=\"../tools/jasmine/jasmine-html.js\"></script>\n    <script src=\"../tools/jasmine-phantomjs/terminal-reporter.js\"></script>\n    <script src=\"../tools/jasmine-browserstack/jasmine-browserstack.js\"></script>\n    <script src=\"../tools/compiler/base.js\"></script>\n    <script src=\"../spec/deps.js\"></script>\n\n    <script>\n      goog.require('webfont');\n    </script>\n\n    <script src=\"../spec/core/domhelper_spec.js\"></script>\n    <script src=\"../spec/core/cssclassname_spec.js\"></script>\n    <script src=\"../spec/core/fontmoduleloader_spec.js\"></script>\n    <script src=\"../spec/core/eventdispatcher_spec.js\"></script>\n    <script src=\"../spec/core/font_spec.js\"></script>\n    <script src=\"../spec/core/fontruler_spec.js\"></script>\n    <script src=\"../spec/core/fontwatchrunner_spec.js\"></script>\n    <script src=\"../spec/core/nativefontwatchrunner_spec.js\"></script>\n    <script src=\"../spec/core/fontwatcher_spec.js\"></script>\n    <script src=\"../spec/core/webfont_spec.js\"></script>\n    <script src=\"../spec/modules/google/fontapiparser_spec.js\"></script>\n    <script src=\"../spec/modules/google/fontapiurlbuilder_spec.js\"></script>\n    <script src=\"../spec/modules/google/googlefontapi_spec.js\"></script>\n    <script src=\"../spec/modules/custom_spec.js\"></script>\n    <script src=\"../spec/modules/fontdeck_spec.js\"></script>\n    <script src=\"../spec/modules/monotype_spec.js\"></script>\n    <script src=\"../spec/modules/typekit_spec.js\"></script>\n\n    <script>\n      (function () {\n        var env = jasmine.getEnv();\n\n        env.updateInterval = 1000;\n\n        var htmlReporter = new jasmine.HtmlReporter(),\n            terminalReporter = new jasmine.TerminalReporter({\n              verbosity: 3,\n              color: true\n            }),\n            browserStackReporter = new jasmine.BrowserStackReporter();\n\n        if (/PhantomJS/.test(navigator.userAgent)) {\n          env.addReporter(terminalReporter);\n        }\n        env.addReporter(htmlReporter);\n        env.addReporter(browserStackReporter);\n        env.specFilter = function (spec) {\n          return htmlReporter.specFilter(spec);\n        };\n\n        var currentOnload = window.onload;\n\n        window.onload = function () {\n          if (currentOnload) {\n            currentOnload();\n          }\n          env.execute();\n        };\n      }());\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "spec/modules/custom_spec.js",
    "content": "describe('modules.Custom', function () {\n  var Custom = webfont.modules.Custom,\n      FontFamily = webfont.FontFamily,\n      Any = jasmine.Matchers.Any;\n\n  describe('insert links correctly', function () {\n    var fakeDomHelper = null,\n        load = null;\n\n    function notifySheetsLoaded() {\n      var argsForCall = fakeDomHelper.loadStylesheet.argsForCall;\n      for (var i = 0; i < argsForCall.length; i++) {\n        var args = argsForCall[i];\n        args[1]();\n      }\n    }\n\n    beforeEach(function () {\n      fakeDomHelper = {\n        loadStylesheet: jasmine.createSpy('createCssLink')\n      };\n\n      load = jasmine.createSpy('load');\n\n      var defaultModule = new Custom(fakeDomHelper, {\n        families: ['Font1', 'Font2', 'Font3'],\n        urls: ['https://moo', 'https://meuh'],\n        testStrings: {\n          Font3: 'hello world'\n        }\n      });\n\n      defaultModule.load(load);\n    });\n\n    it('should have inserted the links correctly', function () {\n      expect(fakeDomHelper.loadStylesheet.callCount).toEqual(2);\n      expect(fakeDomHelper.loadStylesheet).toHaveBeenCalledWith('https://moo', new Any(Function));\n      expect(fakeDomHelper.loadStylesheet).toHaveBeenCalledWith('https://meuh', new Any(Function));\n    });\n\n    if (webfont.DomHelper.CAN_WAIT_STYLESHEET) {\n      it('should not invoke callback before all CSS are loaded', function () {\n        expect(load.callCount).toEqual(0);\n        notifySheetsLoaded();\n        expect(load.callCount).toEqual(1);\n      });\n    }\n\n    it('should have loaded the families correctly', function () {\n      notifySheetsLoaded();\n      expect(load.callCount).toEqual(1);\n      expect(load.calls[0].args[0].length).toEqual(3);\n      expect(load.calls[0].args[0][0].getName()).toEqual('Font1');\n      expect(load.calls[0].args[0][1].getName()).toEqual('Font2');\n      expect(load.calls[0].args[0][2].getName()).toEqual('Font3');\n    });\n\n    it('should have set a custom test string', function () {\n      notifySheetsLoaded();\n      expect(load.callCount).toEqual(1);\n      expect(load.calls[0].args[1]).toEqual({ Font3: 'hello world' });\n    });\n  });\n\n});\n"
  },
  {
    "path": "spec/modules/fontdeck_spec.js",
    "content": "describe('modules.Fontdeck', function () {\n  var Fontdeck = webfont.modules.Fontdeck,\n      Font = webfont.Font;\n\n  var configuration = {\n    id: '2282'\n  };\n\n  var apiResponse = {\n    \"domain\" : \"localhost\",\n    \"cssurl\" : \"https://f.fontdeck.com/s/css/03BmCXiV2AHwX/Rp+OBFTfD2oFs/localhost/2282.css\",\n    \"project\" : 2282,\n    \"cssbase\" : \"https://f.fontdeck.com/s/css/03BmCXiV2AHwX/Rp+OBFTfD2oFs\",\n    \"fonts\" : [\n      {\n        \"font_family\" : \"'Fertigo Pro Regular', Fertigo, Constantia, Palatino, serif\",\n        \"font_size_adjust\" : 0.508,\n        \"name\" : \"Fertigo Pro Regular\",\n        \"style\" : \"normal\",\n        \"weight\" : \"normal\",\n        \"font_urls\" : {\n          \"eot\" : \"https://f.fontdeck.com/f/1/SUlFR0tid0kAA2vb11Ly/IGWDK+wV8TMAfV0J1Ej1J1GFRT1bssqrn6a.eot\",\n          \"ttf\" : \"https://f.fontdeck.com/f/1/SUlFR0tid0kAA2vb11Ly/IGWDK+wV8TMAfV0J1Ej1J1GFRT1bssqrn6a.ttf\",\n          \"woff\" : \"https://f.fontdeck.com/f/1/SUlFR0tid0kAA2vb11Ly/IGWDK+wV8TMAfV0J1Ej1J1GFRT1bssqrn6a.woff\",\n          \"svg\" : \"https://f.fontdeck.com/f/1/SUlFR0tid0kAA2vb11Ly/IGWDK+wV8TMAfV0J1Ej1J1GFRT1bssqrn6a.svg#104\"\n        },\n        \"id\" : 104\n      },\n      {\n        \"font_family\" : \"'Bodoni Display Bold Italic', Georgia, 'Times New Roman', Times, serif\",\n        \"font_size_adjust\" : 0.45,\n        \"name\" : \"Bodoni Display Bold Italic\",\n        \"style\" : \"italic\",\n        \"weight\" : \"bold\",\n        \"font_urls\" : {\n          \"eot\" : \"https://f.fontdeck.com/f/1/azJEbTVyc1QAA11+CAE5C93+l/bAQx1ipRo6Maba19w3Yy5ng+qVWlfj.eot\",\n          \"ttf\" : \"https://f.fontdeck.com/f/1/azJEbTVyc1QAA11+CAE5C93+l/bAQx1ipRo6Maba19w3Yy5ng+qVWlfj.ttf\",\n          \"woff\" : \"https://f.fontdeck.com/f/1/azJEbTVyc1QAA11+CAE5C93+l/bAQx1ipRo6Maba19w3Yy5ng+qVWlfj.woff\",\n          \"svg\" : \"https://f.fontdeck.com/f/1/azJEbTVyc1QAA11+CAE5C93+l/bAQx1ipRo6Maba19w3Yy5ng+qVWlfj.svg#2256\"\n        },\n        \"id\" : 2256\n      }\n    ]\n  };\n\n  var fakeDomHelper = null,\n      global = null;\n\n  beforeEach(function () {\n    global = {\n      location: {}\n    };\n\n    fakeDomHelper = {\n      loadScript: jasmine.createSpy('loadScript'),\n      getLoadWindow: jasmine.createSpy('getLoadWindow').andReturn(global),\n      getHostName: function () { return 'test-host-name';  }\n    };\n  });\n\n  describe('support and load life cycle', function () {\n    var fontdeck = null,\n        onReady = jasmine.createSpy('onReady');\n\n    beforeEach(function () {\n      fontdeck = new Fontdeck(fakeDomHelper, configuration);\n      fontdeck.load(onReady);\n    });\n\n    it('should create the script correctly', function () {\n      expect(fakeDomHelper.loadScript).toHaveBeenCalled();\n      expect(fakeDomHelper.loadScript.calls[0].args[0]).toEqual('https://f.fontdeck.com/s/css/js/test-host-name/2282.js');\n    });\n\n    it('should have created a global', function () {\n      expect(global.__webfontfontdeckmodule__).not.toBeNull();\n      expect(global.__webfontfontdeckmodule__['2282']).not.toBeNull();\n    });\n\n    it('should load correctly after calling the callback', function () {\n      global.__webfontfontdeckmodule__['2282'](true, apiResponse);\n\n      expect(fontdeck.fonts_).toEqual([new Font(apiResponse.fonts[0].name), new Font(apiResponse.fonts[1].name, 'i7')]);\n    });\n  });\n\n  describe('no project id', function () {\n    var fontdeck = null,\n        support = null;\n\n    beforeEach(function () {\n      fontdeck = new Fontdeck(fakeDomHelper, { id: null });\n    });\n\n    it('should not have loaded any fonts', function () {\n      expect(fontdeck.fonts_).toEqual([]);\n    });\n  });\n});\n"
  },
  {
    "path": "spec/modules/google/fontapiparser_spec.js",
    "content": "describe('modules.google.FontApiParser', function () {\n  var FontApiParser = webfont.modules.google.FontApiParser,\n      Font = webfont.Font;\n\n  describe('parsed values are coherent', function () {\n    var parser = null;\n\n    beforeEach(function () {\n      parser = new FontApiParser(['Tangerine', 'Droid Serif:bi', 'Yanone Kaffeesatz:200,300,400,700', 'Cantarell:italic,b', 'Exo:100italic', 'Lobster:200n']);\n      parser.parse();\n    });\n\n    it('should parse fonts correctly', function () {\n      var fonts = parser.getFonts();\n\n      expect(fonts.length).toEqual(10);\n      expect(fonts).toEqual([\n        new Font('Tangerine', 'n4'),\n        new Font('Droid Serif', 'i7'),\n        new Font('Yanone Kaffeesatz', 'n2'),\n        new Font('Yanone Kaffeesatz', 'n3'),\n        new Font('Yanone Kaffeesatz', 'n4'),\n        new Font('Yanone Kaffeesatz', 'n7'),\n        new Font('Cantarell', 'i4'),\n        new Font('Cantarell', 'n7'),\n        new Font('Exo', 'i1'),\n        new Font('Lobster', 'n2')\n      ]);\n    });\n  });\n\n  describe('mix of numeric weight and style', function () {\n    var parser = null;\n\n    beforeEach(function () {\n      parser = new FontApiParser(['Nobile:700i,b,200i,r,i700']);\n      parser.parse();\n    });\n\n    it('should parse fonts correctly', function () {\n      var fonts = parser.getFonts();\n\n      expect(fonts.length).toEqual(4);\n      expect(fonts).toEqual([\n        new Font('Nobile', 'i7'),\n        new Font('Nobile', 'n7'),\n        new Font('Nobile', 'i2'),\n        new Font('Nobile', 'n4')\n      ]);\n    });\n  });\n\n  describe('typo bild instead of bold', function () {\n    var parser = null;\n\n    beforeEach(function () {\n      parser = new FontApiParser(['Nobile:bild']);\n      parser.parse();\n    });\n\n    it('should parse families correctly', function () {\n      var fonts = parser.getFonts();\n\n      expect(fonts.length).toEqual(1);\n      expect(fonts[0]).toEqual(new Font('Nobile', 'n4'));\n    });\n  });\n\n  describe('variations with dashes', function () {\n    var parser = null;\n\n    beforeEach(function () {\n      parser = new FontApiParser(['Nobile:semi-bold']);\n      parser.parse();\n    });\n\n    it('should parse the variation correctly', function () {\n      var fonts = parser.getFonts();\n\n      expect(fonts.length).toEqual(1);\n      expect(fonts[0]).toEqual(new Font('Nobile', 'n6'));\n    });\n  });\n\n  describe('nonsense', function () {\n    var parser = null;\n\n    beforeEach(function () {\n      parser = new FontApiParser(['Nobile:dwe,^%^%fewf,$9940@#!@#$%^&*()_+}POIBJ{}{']);\n      parser.parse();\n    });\n\n    it('should parse families correctly', function () {\n      var fonts = parser.getFonts();\n\n      expect(fonts.length).toEqual(1);\n      expect(fonts[0]).toEqual(new Font('Nobile', 'n4'));\n    });\n  });\n\n  describe('no weight and one subset defined', function () {\n    var parser = null;\n\n    beforeEach(function () {\n      parser = new FontApiParser(['Cantarell::greek']);\n      parser.parse();\n    });\n\n    it('should parse families correctly', function () {\n      var fonts = parser.getFonts();\n\n      expect(fonts.length).toEqual(1);\n      expect(fonts[0]).toEqual(new Font('Cantarell', 'n4'));\n    });\n\n    it('should parse pick test strings correctly', function () {\n      var testStrings = parser.getFontTestStrings(),\n          cantarell = testStrings['Cantarell'];\n\n      expect(cantarell).not.toBeNull();\n      expect(cantarell).toEqual(FontApiParser.INT_FONTS['greek']);\n    });\n  });\n\n  describe('no weight and multiple subsets defined', function () {\n    var parser = null;\n\n    beforeEach(function () {\n      parser = new FontApiParser(['Cantarell::cyrillic,greek,latin']);\n      parser.parse();\n    });\n\n    it('should parse families correctly', function () {\n      var fonts = parser.getFonts();\n\n      expect(fonts.length).toEqual(1);\n      expect(fonts[0]).toEqual(new Font('Cantarell', 'n4'));\n    });\n\n    it('should parse pick test strings correctly', function () {\n      var testStrings = parser.getFontTestStrings(),\n          cantarell = testStrings['Cantarell'];\n\n      expect(cantarell).not.toBeNull();\n      expect(cantarell).toEqual(FontApiParser.INT_FONTS['cyrillic']);\n    });\n  });\n\n  describe('weight and multiple subsets defined', function () {\n    var parser = null;\n\n    beforeEach(function () {\n      parser = new FontApiParser(['Cantarell:regular,bold:cyrillic,greek,latin']);\n      parser.parse();\n    });\n\n    it('should parse families correctly', function () {\n      var fonts = parser.getFonts();\n\n      expect(fonts.length).toEqual(2);\n      expect(fonts).toEqual([\n        new Font('Cantarell', 'n4'),\n        new Font('Cantarell', 'n7')\n      ]);\n    });\n\n    it('should parse pick test strings correctly', function () {\n      var testStrings = parser.getFontTestStrings(),\n          cantarell = testStrings['Cantarell'];\n\n      expect(cantarell).not.toBeNull();\n      expect(cantarell).toEqual(FontApiParser.INT_FONTS['cyrillic']);\n    });\n  });\n\n  describe('Hanuman is backward compatible', function () {\n    var parser = null;\n\n    beforeEach(function () {\n      parser = new FontApiParser(['Hanuman']);\n      parser.parse();\n    });\n\n    it('should parse families correctly', function () {\n      var fonts = parser.getFonts();\n\n      expect(fonts.length).toEqual(1);\n      expect(fonts[0]).toEqual(new Font('Hanuman', 'n4'));\n    });\n\n    it('should parse pick test strings correctly', function () {\n      var testStrings = parser.getFontTestStrings(),\n          hanuman = testStrings['Hanuman'];\n\n      expect(hanuman).not.toBeNull();\n      expect(hanuman).toEqual(FontApiParser.INT_FONTS['Hanuman']);\n    });\n  });\n\n  describe('Hanuman is forward compatible', function () {\n    var parser = null;\n\n    beforeEach(function () {\n      parser = new FontApiParser(['Hanuman::khmer']);\n      parser.parse();\n    });\n\n    it('should parse families correctly', function () {\n      var fonts = parser.getFonts();\n\n      expect(fonts.length).toEqual(1);\n      expect(fonts[0]).toEqual(new Font('Hanuman', 'n4'));\n    });\n\n    it('should parse pick test strings correctly', function () {\n      var testStrings = parser.getFontTestStrings(),\n          hanuman = testStrings['Hanuman'];\n\n      expect(hanuman).not.toBeNull();\n      expect(hanuman).toEqual(FontApiParser.INT_FONTS['khmer']);\n    });\n  });\n\n  describe('plus replaced with space', function () {\n    var parser = null;\n\n    beforeEach(function () {\n      parser = new FontApiParser(['Erica+One', 'Droid+Serif::latin', 'Yanone+Kaffeesatz:400,700:latin']);\n      parser.parse();\n    });\n\n    it('should parse families correctly', function () {\n      var fonts = parser.getFonts();\n\n      expect(fonts.length).toEqual(4);\n      expect(fonts).toEqual([\n        new Font('Erica One', 'n4'),\n        new Font('Droid Serif', 'n4'),\n        new Font('Yanone Kaffeesatz', 'n4'),\n        new Font('Yanone Kaffeesatz', 'n7')\n      ]);\n    });\n  });\n});\n"
  },
  {
    "path": "spec/modules/google/fontapiurlbuilder_spec.js",
    "content": "describe('modules.google.FontApiUrlBuilder', function () {\n  var FontApiUrlBuilder = webfont.modules.google.FontApiUrlBuilder;\n\n  it('should throw an exception if there are no font families', function () {\n    var builder = new FontApiUrlBuilder('https://moo');\n    expect(builder.build).toThrow();\n  });\n\n  it('should build a proper url', function () {\n    var builder = new FontApiUrlBuilder('https://moo');\n    builder.setFontFamilies(['Font1', 'Font2']);\n    expect(builder.build()).toEqual('https://moo?family=Font1%7CFont2');\n  });\n\n  it('should build a proper url', function () {\n    var builder = new FontApiUrlBuilder(undefined);\n    builder.setFontFamilies(['Font1', 'Font2']);\n    expect(builder.build()).toEqual(\n      FontApiUrlBuilder.DEFAULT_API_URL +\n      '?family=Font1%7CFont2');\n  });\n\n  it('should build a proper url', function () {\n    var builder = new FontApiUrlBuilder(undefined);\n    builder.setFontFamilies(['Font1:bold:greek,cyrillic', 'Font2:italic', 'Font3']);\n    expect(builder.build()).toEqual(\n      FontApiUrlBuilder.DEFAULT_API_URL +\n      '?family=Font1:bold%7CFont2:italic%7CFont3' +\n      '&subset=greek,cyrillic');\n  });\n\n  it('should build a proper url', function () {\n    var builder = new FontApiUrlBuilder(undefined);\n    builder.setFontFamilies(['Font1:bold,italic:greek,cyrillic', 'Font2:italic', 'Font3::latin']);\n    expect(builder.build()).toEqual(\n      FontApiUrlBuilder.DEFAULT_API_URL +\n      '?family=Font1:bold,italic%7CFont2:italic%7CFont3' +\n      '&subset=greek,cyrillic,latin');\n  });\n});\n"
  },
  {
    "path": "spec/modules/google/googlefontapi_spec.js",
    "content": "describe('modules.google.GoogleFontApi', function () {\n  var GoogleFontApi = webfont.modules.google.GoogleFontApi,\n      Any = jasmine.Matchers.Any,\n      Font = webfont.Font,\n      link = '',\n      insert = '',\n      onload = null,\n      fakeDomHelper =  {\n        whenBodyExists: function (callback) {\n          callback();\n        },\n        loadStylesheet: function (cssLink, cb) {\n          link = cssLink;\n          onload = cb;\n        }\n      };\n\n\n  beforeEach(function () {\n    insert = '';\n    link = '';\n    onload = null;\n  });\n\n  function notifySheetsLoaded() {\n    if (onload)\n      onload();\n  };\n\n  describe('call onReady with font family loading', function () {\n    var googleFontApi = null,\n        fonts = null;\n\n    beforeEach(function () {\n      googleFontApi = new GoogleFontApi(fakeDomHelper, { families: ['Font1', 'Font2'] });\n      googleFontApi.load(function (f) {\n        fonts = f;\n      });\n    });\n\n    it('has inserted the link element correctly', function () {\n      expect(link).toEqual('https://fonts.googleapis.com/css?family=Font1%7CFont2');\n    });\n\n    it('has the correct families', function () {\n      notifySheetsLoaded();\n      expect(fonts).not.toBeNull();\n      expect(fonts.length).toEqual(2);\n      expect(fonts[0]).toEqual(new Font('Font1'));\n      expect(fonts[1]).toEqual(new Font('Font2'));\n    });\n  });\n\n  describe('call onReady with font family loading and custom API url', function () {\n    var googleFontApi = null;\n    var loaded  = false;\n\n    beforeEach(function () {\n      loaded  = false;\n      googleFontApi = new GoogleFontApi(fakeDomHelper, {\n        api: 'https://moo',\n        families: ['Font1', 'Font2']\n      });\n      googleFontApi.load(function () { loaded = true; });\n    });\n\n    it('has inserted the link element correctly', function () {\n      expect(link).toEqual('https://moo?family=Font1%7CFont2');\n    });\n\n    if (webfont.DomHelper.CAN_WAIT_STYLESHEET) {\n      it('does not call onReady until sheets are loaded', function () {\n        expect(onload).toMatch(new Any(Function));\n        expect(loaded).toBe(false);\n\n        notifySheetsLoaded();\n        expect(loaded).toBe(true);\n      });\n    }\n  });\n\n  describe('spaces replaced by plus', function () {\n    var googleFontApi = null;\n\n    beforeEach(function () {\n      googleFontApi = new GoogleFontApi(fakeDomHelper, { families: ['Font1 WithSpace', 'Font2 WithSpaceToo'] });\n      googleFontApi.load(function () {});\n    });\n\n    it('has inserted the link element correctly', function () {\n      expect(link).toEqual('https://fonts.googleapis.com/css?family=Font1+WithSpace%7CFont2+WithSpaceToo');\n    });\n });\n\n  describe('load with variations', function () {\n    var googleFontApi = null;\n\n    beforeEach(function () {\n      googleFontApi = new GoogleFontApi(fakeDomHelper, { families: ['Font1 WithSpace:bi', 'Font2 WithSpaceToo:b,r'] });\n      googleFontApi.load(function () {});\n    });\n\n    it('has inserted the link element correctly', function () {\n      expect(link).toEqual('https://fonts.googleapis.com/css?family=Font1+WithSpace:bi%7CFont2+WithSpaceToo:b,r');\n    });\n });\n});\n"
  },
  {
    "path": "spec/modules/monotype_spec.js",
    "content": "describe('modules.Monotype', function () {\n  var Monotype = webfont.modules.Monotype,\n      Font = webfont.Font,\n      BrowserInfo = webfont.BrowserInfo,\n      UserAgent = webfont.UserAgent,\n      Version = webfont.Version;\n\n  var configuration = {\n    projectId: '01e2ff27-25bf-4801-a23e-73d328e6c7cc',\n    api: 'https://fast.fonts.net/jsapidev'\n  };\n\n  var fakeDomHelper = null,\n      global = null,\n      script = {},\n      monotype = null,\n      load =  null,\n      support = null;\n\n  beforeEach(function () {\n    global = {};\n\n    fakeDomHelper = {\n      loadScript: jasmine.createSpy('loadScript').andCallFake(function (src, callback) {\n        script.onload = callback;\n        return script;\n      }),\n      getLoadWindow: jasmine.createSpy('getLoadWindow').andReturn(global)\n    };\n    support = jasmine.createSpy('support');\n    load = jasmine.createSpy('load');\n\n    monotype = new Monotype(fakeDomHelper, configuration);\n    monotype.load(load);\n\n    global[Monotype.HOOK + configuration.projectId] = function () {\n      return [{fontfamily: 'aachen bold'}, {fontfamily: 'kid print regular'}];\n    };\n\n    script.onload();\n  });\n\n\n  it('should create a script element', function () {\n    expect(fakeDomHelper.loadScript).toHaveBeenCalled();\n    expect(fakeDomHelper.loadScript.calls[0].args[0]).toEqual('https://fast.fonts.net/jsapidev/01e2ff27-25bf-4801-a23e-73d328e6c7cc.js');\n    expect(load).toHaveBeenCalledWith([new Font('aachen bold'), new Font('kid print regular')]);\n  });\n});\n"
  },
  {
    "path": "spec/modules/typekit_spec.js",
    "content": "describe('modules.Typekit', function () {\n  var Typekit = webfont.modules.Typekit,\n      Font = webfont.Font;\n\n  var configuration = {\n    id: 'abc'\n  };\n\n  var fakeDomHelper = null,\n      global = {},\n      load = null,\n      onReady = null;\n\n  beforeEach(function () {\n    global = {\n      Typekit: {\n        config: {\n          fn: ['Font1', ['n4'], 'Font2', ['n4', 'n7']]\n        },\n        load: jasmine.createSpy('load')\n      }\n    };\n\n    onReady = jasmine.createSpy('onReady');\n\n    load = jasmine.createSpy('load');\n\n    fakeDomHelper = {\n      loadScript: jasmine.createSpy('loadScript').andCallFake(function (url, cb) {\n        cb(null);\n      }),\n      getLoadWindow: jasmine.createSpy('getLoadWindow').andReturn(global)\n    };\n  });\n\n  it('should load with variations', function () {\n    var typekit = new Typekit(fakeDomHelper, configuration);\n\n    typekit.load(onReady);\n\n    expect(fakeDomHelper.loadScript).toHaveBeenCalled();\n    expect(fakeDomHelper.loadScript.calls[0].args[0]).toEqual('https://use.typekit.net/abc.js');\n\n    expect(global.Typekit.load).toHaveBeenCalled();\n    typekit.load(load);\n\n    expect(load).toHaveBeenCalledWith([new Font('Font1', 'n4'), new Font('Font2', 'n4'), new Font('Font2', 'n7')]);\n  });\n\n  it('should load through the alternative API', function () {\n    var typekit = new Typekit(fakeDomHelper, { id: 'abc', api: '/test' });\n\n    typekit.load(onReady);\n\n    expect(fakeDomHelper.loadScript).toHaveBeenCalled();\n    expect(fakeDomHelper.loadScript.calls[0].args[0]).toEqual('/test/abc.js');\n  });\n\n  it('should not load without a kit id', function () {\n    var typekit = new Typekit(fakeDomHelper, { id: null });\n\n    typekit.load(onReady);\n\n    expect(fakeDomHelper.loadScript).not.toHaveBeenCalled();\n\n    typekit.load(load);\n\n    expect(load).toHaveBeenCalledWith([]);\n  });\n});\n"
  },
  {
    "path": "src/closure.js",
    "content": "/* Web Font Loader v{{version}} - (c) Adobe Systems, Google. License: Apache 2.0 */\n(function(){{{source}}}());\n"
  },
  {
    "path": "src/core/cssclassname.js",
    "content": "goog.provide('webfont.CssClassName');\n\n/**\n * Handles sanitization and construction of css class names.\n * @param {string=} opt_joinChar The character to join parts of the name on.\n *    Defaults to '-'.\n * @constructor\n */\nwebfont.CssClassName = function(opt_joinChar) {\n  /** @type {string} */\n  this.joinChar_ = opt_joinChar || webfont.CssClassName.DEFAULT_JOIN_CHAR;\n};\n\n/**\n * @const\n * @type {string}\n */\nwebfont.CssClassName.DEFAULT_JOIN_CHAR = '-';\n\ngoog.scope(function () {\n  var CssClassName = webfont.CssClassName;\n\n  /**\n   * Sanitizes a string for use as a css class name. Removes non-word and\n   * underscore characters.\n   * @param {string} name The string.\n   * @return {string} The sanitized string.\n   */\n  CssClassName.prototype.sanitize = function(name) {\n    return name.replace(/[\\W_]+/g, '').toLowerCase();\n  };\n\n  /**\n   * Builds a complete css class name given a variable number of parts.\n   * Sanitizes, then joins the parts together.\n   * @param {...string} var_args The parts to join.\n   * @return {string} The sanitized and joined string.\n   */\n  CssClassName.prototype.build = function(var_args) {\n    var parts = []\n    for (var i = 0; i < arguments.length; i++) {\n      parts.push(this.sanitize(arguments[i]));\n    }\n    return parts.join(this.joinChar_);\n  };\n});\n"
  },
  {
    "path": "src/core/domhelper.js",
    "content": "goog.provide('webfont.DomHelper');\n\n/**\n * Handles common DOM manipulation tasks. The aim of this library is to cover\n * the needs of typical font loading. Not more, not less.\n * @param {Window} mainWindow The main window webfontloader.js is loaded in.\n * @param {Window=} opt_loadWindow The window we'll load the font into. By\n *   default, the main window is used.\n * @constructor\n */\nwebfont.DomHelper = function(mainWindow, opt_loadWindow) {\n  this.mainWindow_ = mainWindow;\n  this.loadWindow_ = opt_loadWindow || mainWindow;\n\n  /** @type {string} */\n  this.protocol_;\n\n  /** @type {Document} */\n  this.document_ = this.loadWindow_.document;\n};\n\ngoog.scope(function () {\n  var DomHelper = webfont.DomHelper;\n\n  /**\n   * The NativeFontWatchRunnner depends on the correct and reliable\n   * |onload| event, and browsers with the native font loading API\n   * have reliable @onload support as far as we know. So we use the\n   * event for such a case and unconditionally invokes the callback\n   * otherwise.\n   *\n   * @const\n   * @type {boolean}\n   */\n  DomHelper.CAN_WAIT_STYLESHEET = !!window['FontFace'];\n\n  /**\n   * Creates an element.\n   * @param {string} elem The element type.\n   * @param {Object=} opt_attr A hash of attribute key/value pairs.\n   * @param {string=} opt_innerHtml Contents of the element.\n   * @return {Element} the new element.\n   */\n  DomHelper.prototype.createElement = function(elem, opt_attr,\n      opt_innerHtml) {\n    var domElement = this.document_.createElement(elem);\n\n    if (opt_attr) {\n      for (var attr in opt_attr) {\n        // protect against native prototype augmentations\n        if (opt_attr.hasOwnProperty(attr)) {\n          if (attr == \"style\") {\n            this.setStyle(domElement, opt_attr[attr]);\n          } else {\n            domElement.setAttribute(attr, opt_attr[attr]);\n          }\n        }\n      }\n    }\n    if (opt_innerHtml) {\n      domElement.appendChild(this.document_.createTextNode(opt_innerHtml));\n    }\n    return domElement;\n  };\n\n  /**\n   * Inserts an element into the document. This is intended for unambiguous\n   * elements such as html, body, head.\n   * @param {string} tagName The element name.\n   * @param {Element} e The element to append.\n   * @return {boolean} True if the element was inserted.\n   */\n  DomHelper.prototype.insertInto = function(tagName, e) {\n    var t = this.document_.getElementsByTagName(tagName)[0];\n\n    if (!t) { // opera allows documents without a head\n      t = document.documentElement;\n    }\n\n    // This is safer than appendChild in IE. appendChild causes random\n    // JS errors in IE. Sometimes errors in other JS exectution, sometimes\n    // complete 'This page cannot be displayed' errors. For our purposes,\n    // it's equivalent because we don't need to insert at any specific\n    // location.\n    t.insertBefore(e, t.lastChild);\n    return true;\n  };\n\n  /**\n   * Calls a function when the body tag exists.\n   * @param {function()} callback The function to call.\n   */\n  DomHelper.prototype.whenBodyExists = function(callback) {\n    var that = this;\n\n    if (that.document_.body) {\n      callback();\n    } else {\n      if (that.document_.addEventListener) {\n        that.document_.addEventListener('DOMContentLoaded', callback);\n      } else {\n        that.document_.attachEvent('onreadystatechange', function () {\n          if (that.document_.readyState == 'interactive' || that.document_.readyState == 'complete') {\n            callback();\n          }\n        });\n      }\n    }\n  };\n\n  /**\n   * Removes an element from the DOM.\n   * @param {Element} node The element to remove.\n   * @return {boolean} True if the element was removed.\n   */\n  DomHelper.prototype.removeElement = function(node) {\n    if (node.parentNode) {\n      node.parentNode.removeChild(node);\n      return true;\n    }\n    return false;\n  };\n\n  /**\n   * @deprecated Use updateClassName().\n   *\n   * Appends a name to an element's class attribute.\n   * @param {Element} e The element.\n   * @param {string} name The class name to add.\n   */\n  DomHelper.prototype.appendClassName = function(e, name) {\n    this.updateClassName(e, [name]);\n  };\n\n  /**\n   * @deprecated Use updateClassName().\n   *\n   * Removes a name to an element's class attribute.\n   * @param {Element} e The element.\n   * @param {string} name The class name to remove.\n   */\n  DomHelper.prototype.removeClassName = function(e, name) {\n    this.updateClassName(e, null, [name]);\n  };\n\n  /**\n   * Updates an element's class attribute in a single change. This\n   * allows multiple updates in a single class name change so there\n   * is no chance for a browser to relayout in between changes.\n   *\n   * @param {Element} e The element.\n   * @param {Array.<string>=} opt_add List of class names to add.\n   * @param {Array.<string>=} opt_remove List of class names to remove.\n   */\n  DomHelper.prototype.updateClassName = function (e, opt_add, opt_remove) {\n    var add = opt_add || [],\n        remove = opt_remove || [];\n\n    var classes = e.className.split(/\\s+/);\n\n    for (var i = 0; i < add.length; i += 1) {\n      var found = false;\n\n      for (var j = 0; j < classes.length; j += 1) {\n        if (add[i] === classes[j]) {\n          found = true;\n          break;\n        }\n      }\n\n      if (!found) {\n        classes.push(add[i]);\n      }\n    }\n\n    var remainingClasses = [];\n\n    for (var i = 0; i < classes.length; i += 1) {\n      var found = false;\n\n      for (var j = 0; j < remove.length; j += 1) {\n        if (classes[i] === remove[j]) {\n          found = true;\n          break;\n        }\n      }\n\n      if (!found) {\n        remainingClasses.push(classes[i]);\n      }\n    }\n\n    e.className = remainingClasses.join(' ')\n                    .replace(/\\s+/g, ' ')\n                    .replace(/^\\s+|\\s+$/, '');\n  };\n\n  /**\n   * Returns true if an element has a given class name and false otherwise.\n   * @param {Element} e The element.\n   * @param {string} name The class name to check for.\n   * @return {boolean} Whether or not the element has this class name.\n   */\n  DomHelper.prototype.hasClassName = function(e, name) {\n    var classes = e.className.split(/\\s+/);\n    for (var i = 0, len = classes.length; i < len; i++) {\n      if (classes[i] == name) {\n        return true;\n      }\n    }\n    return false;\n  };\n\n  /**\n   * Sets the style attribute on an element.\n   * @param {Element} e The element.\n   * @param {string} styleString The style string.\n   */\n  DomHelper.prototype.setStyle = function(e, styleString) {\n    e.style.cssText = styleString;\n  };\n\n  /**\n   * @return {Window} The main window webfontloader.js is loaded in (for config).\n   */\n  DomHelper.prototype.getMainWindow = function() {\n    return this.mainWindow_;\n  };\n\n  /**\n   * @return {Window} The window that we're loading the font(s) into.\n   */\n  DomHelper.prototype.getLoadWindow = function() {\n    return this.loadWindow_;\n  };\n\n  /**\n   * Returns the hostname of the current document.\n   * @return {string} hostname.\n   */\n  DomHelper.prototype.getHostName = function() {\n    return this.getLoadWindow().location.hostname || this.getMainWindow().location.hostname;\n  };\n\n  /**\n   * Creates a style element.\n   * @param {string} css Contents of the style element.\n   * @return {Element} a DOM element.\n   */\n  DomHelper.prototype.createStyle = function(css) {\n    var e = this.createElement('style');\n\n    e.setAttribute('type', 'text/css');\n    if (e.styleSheet) { // IE\n      e.styleSheet.cssText = css;\n    } else {\n      e.appendChild(document.createTextNode(css));\n    }\n    return e;\n  };\n\n  /**\n   * Loads an external stylesheet.\n   *\n   * @param {string} href the URL of the stylesheet\n   * @param {function(Error)=} opt_callback Called when the stylesheet has loaded or failed to\n   * load. Note that the callback is *NOT* guaranteed to be called in all browsers. The first\n   * argument to the callback is an error object that is falsy when there are no errors and\n   * truthy when there are.\n   * @param {boolean=} opt_async True if the stylesheet should be loaded asynchronously. Defaults to false.\n   * @return {Element} The link element\n   */\n  DomHelper.prototype.loadStylesheet = function (href, opt_callback, opt_async) {\n    var link = this.createElement('link', {\n      'rel': 'stylesheet',\n      'href': href,\n      'media': (opt_async ? 'only x' : 'all')\n    });\n\n    var sheets = this.document_.styleSheets,\n        eventFired = false,\n        asyncResolved = !opt_async,\n        callbackArg = null,\n        callback = opt_callback || null;\n\n    function mayInvokeCallback() {\n      if (callback && eventFired && asyncResolved) {\n        callback(callbackArg);\n        callback = null;\n      }\n    }\n\n    if (DomHelper.CAN_WAIT_STYLESHEET) {\n      link.onload = function () {\n        eventFired = true;\n        mayInvokeCallback();\n      };\n\n      link.onerror = function () {\n        eventFired = true;\n        callbackArg = new Error('Stylesheet failed to load');\n        mayInvokeCallback();\n      };\n    } else {\n      // Some callers expect opt_callback being called asynchronously.\n      setTimeout(function () {\n        eventFired = true;\n        mayInvokeCallback();\n      }, 0);\n    }\n\n    function onStylesheetAvailable(callback) {\n      for (var i = 0; i < sheets.length; i++) {\n        if (sheets[i].href && sheets[i].href.indexOf(href) !== -1) {\n          return callback();\n        }\n      }\n\n      setTimeout(function () {\n        onStylesheetAvailable(callback);\n      }, 0);\n    }\n\n    function onMediaAvailable(callback) {\n      for (var i = 0; i < sheets.length; i++) {\n        if (sheets[i].href && sheets[i].href.indexOf(href) !== -1 && sheets[i].media) {\n          /**\n           * @type {string|MediaList|null}\n           */\n          var media = sheets[i].media;\n\n          if (media === \"all\" || (media.mediaText && media.mediaText === \"all\")) {\n            return callback();\n          }\n        }\n      }\n\n      setTimeout(function () {\n        onMediaAvailable(callback);\n      }, 0);\n    }\n\n    this.insertInto('head', link);\n\n    if (opt_async) {\n      onStylesheetAvailable(function () {\n        link.media = \"all\";\n        // The media type change doesn't take effect immediately on Chrome, so\n        // we'll query the media attribute on the stylesheet until it changes\n        // to \"all\".\n        onMediaAvailable(function () {\n          asyncResolved = true;\n          mayInvokeCallback();\n        });\n      });\n    }\n\n    return link;\n  };\n\n  /**\n   * Loads an external script file.\n   * @param {string} src URL of the script.\n   * @param {function(Error)=} opt_callback callback when the script has loaded. The first argument to\n   * the callback is an error object that is falsy when there are no errors and truthy when there are.\n   * @param {number=} opt_timeout The number of milliseconds after which the callback will be called\n   * with a timeout error. Defaults to 5 seconds.\n   * @return {Element} The script element\n   */\n  DomHelper.prototype.loadScript = function(src, opt_callback, opt_timeout) {\n    var head = this.document_.getElementsByTagName('head')[0];\n\n    if (head) {\n      var script = this.createElement('script', {\n        'src': src\n      });\n      var done = false;\n      script.onload = script.onreadystatechange = function() {\n        if (!done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete')) {\n          done = true;\n          if (opt_callback) {\n            opt_callback(null);\n          }\n          script.onload = script.onreadystatechange = null;\n          // Avoid a bizarre issue with unclosed <base> tag in IE6 - http://blog.dotsmart.net/2008/04/\n          if (script.parentNode.tagName == 'HEAD') head.removeChild(script);\n        }\n      };\n      head.appendChild(script);\n\n      setTimeout(function () {\n        if (!done) {\n          done = true;\n          if (opt_callback) {\n            opt_callback(new Error('Script load timeout'));\n          }\n        }\n      }, opt_timeout || 5000);\n\n      return script;\n    }\n\n    return null;\n  };\n});\n"
  },
  {
    "path": "src/core/eventdispatcher.js",
    "content": "goog.provide('webfont.EventDispatcher');\n\ngoog.require('webfont.CssClassName');\n\n/**\n * A class to dispatch events and manage the event class names on an html\n * element that represent the current state of fonts on the page. Active class\n * names always overwrite inactive class names of the same type, while loading\n * class names may be present whenever a font is loading (regardless of if an\n * associated active or inactive class name is also present).\n *\n * @param {webfont.DomHelper} domHelper\n * @param {Object} config\n * @constructor\n */\nwebfont.EventDispatcher = function(domHelper, config) {\n  this.domHelper_ = domHelper;\n  this.htmlElement_ = domHelper.getLoadWindow().document.documentElement;\n  this.callbacks_ = config;\n  this.namespace_ = webfont.EventDispatcher.DEFAULT_NAMESPACE;\n  this.cssClassName_ = new webfont.CssClassName('-');\n  this.dispatchEvents_ = config['events'] !== false;\n  this.setClasses_ = config['classes'] !== false;\n};\n\n/**\n * @const\n * @type {string}\n */\nwebfont.EventDispatcher.DEFAULT_NAMESPACE = 'wf';\n\n/**\n * @const\n * @type {string}\n */\nwebfont.EventDispatcher.LOADING = 'loading';\n\n/**\n * @const\n * @type {string}\n */\nwebfont.EventDispatcher.ACTIVE = 'active';\n\n/**\n * @const\n * @type {string}\n */\nwebfont.EventDispatcher.INACTIVE = 'inactive';\n\n/**\n * @const\n * @type {string}\n */\nwebfont.EventDispatcher.FONT = 'font';\n\ngoog.scope(function () {\n  var EventDispatcher = webfont.EventDispatcher;\n\n  /**\n   * Dispatch the loading event and append the loading class name.\n   */\n  EventDispatcher.prototype.dispatchLoading = function() {\n    if (this.setClasses_) {\n      this.domHelper_.updateClassName(this.htmlElement_,\n        [\n          this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.LOADING)\n        ]\n      );\n    }\n\n    this.dispatch_(webfont.EventDispatcher.LOADING);\n  };\n\n  /**\n   * Dispatch the font loading event and append the font loading class name.\n   * @param {webfont.Font} font\n   */\n  EventDispatcher.prototype.dispatchFontLoading = function(font) {\n    if (this.setClasses_) {\n      this.domHelper_.updateClassName(this.htmlElement_,\n        [\n          this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.LOADING)\n        ]\n      );\n    }\n\n    this.dispatch_(webfont.EventDispatcher.FONT + webfont.EventDispatcher.LOADING, font);\n  };\n\n  /**\n   * Dispatch the font active event, remove the font loading class name, remove\n   * the font inactive class name, and append the font active class name.\n   * @param {webfont.Font} font\n   */\n  EventDispatcher.prototype.dispatchFontActive = function(font) {\n    if (this.setClasses_) {\n      this.domHelper_.updateClassName(\n        this.htmlElement_,\n        [\n          this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.ACTIVE)\n        ],\n        [\n          this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.LOADING),\n          this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.INACTIVE)\n        ]\n      );\n    }\n\n    this.dispatch_(webfont.EventDispatcher.FONT + webfont.EventDispatcher.ACTIVE, font);\n  };\n\n  /**\n   * Dispatch the font inactive event, remove the font loading class name, and\n   * append the font inactive class name (unless the font active class name is\n   * already present).\n   * @param {webfont.Font} font\n   */\n  EventDispatcher.prototype.dispatchFontInactive = function(font) {\n    if (this.setClasses_) {\n      var hasFontActive = this.domHelper_.hasClassName(this.htmlElement_,\n            this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.ACTIVE)\n          ),\n          add = [],\n          remove = [\n            this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.LOADING)\n          ];\n\n      if (!hasFontActive) {\n        add.push(this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.INACTIVE));\n      }\n\n      this.domHelper_.updateClassName(this.htmlElement_, add, remove);\n    }\n\n    this.dispatch_(webfont.EventDispatcher.FONT + webfont.EventDispatcher.INACTIVE, font);\n  };\n\n  /**\n   * Dispatch the inactive event, remove the loading class name, and append the\n   * inactive class name (unless the active class name is already present).\n   */\n  EventDispatcher.prototype.dispatchInactive = function() {\n    if (this.setClasses_) {\n      var hasActive = this.domHelper_.hasClassName(this.htmlElement_,\n            this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.ACTIVE)\n          ),\n          add = [],\n          remove = [\n            this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.LOADING)\n          ];\n\n      if (!hasActive) {\n        add.push(this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.INACTIVE));\n      }\n\n      this.domHelper_.updateClassName(this.htmlElement_, add, remove);\n    }\n\n    this.dispatch_(webfont.EventDispatcher.INACTIVE);\n  };\n\n  /**\n   * Dispatch the active event, remove the loading class name, remove the inactive\n   * class name, and append the active class name.\n   */\n  EventDispatcher.prototype.dispatchActive = function() {\n    if (this.setClasses_) {\n      this.domHelper_.updateClassName(this.htmlElement_,\n        [\n          this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.ACTIVE)\n        ],\n        [\n          this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.LOADING),\n          this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.INACTIVE)\n        ]\n      );\n    }\n\n    this.dispatch_(webfont.EventDispatcher.ACTIVE);\n  };\n\n  /**\n   * @param {string} event\n   * @param {webfont.Font=} opt_font\n   */\n  EventDispatcher.prototype.dispatch_ = function(event, opt_font) {\n    if (this.dispatchEvents_ && this.callbacks_[event]) {\n      if (opt_font) {\n        this.callbacks_[event](opt_font.getName(), opt_font.getVariation());\n      } else {\n        this.callbacks_[event]();\n      }\n    }\n  };\n});\n"
  },
  {
    "path": "src/core/font.js",
    "content": "goog.provide('webfont.Font');\n\n/**\n * This class is an abstraction for a single font or typeface.\n * It contains the font name and the variation (i.e. style\n * and weight.) A collection Font instances can represent a\n * font family.\n *\n * @constructor\n * @param {string} name The font family name\n * @param {string=} opt_variation A font variation description\n */\nwebfont.Font = function (name, opt_variation) {\n  this.name_ = name;\n  this.weight_ = 4;\n  this.style_ = 'n'\n\n  var variation = opt_variation || 'n4',\n      match = variation.match(/^([nio])([1-9])$/i);\n\n  if (match) {\n    this.style_ = match[1];\n    this.weight_ = parseInt(match[2], 10);\n  }\n};\n\ngoog.scope(function () {\n  var Font = webfont.Font;\n\n  /**\n   * @return {string}\n   */\n  Font.prototype.getName = function () {\n    return this.name_;\n  };\n\n  /**\n   * @return {string}\n   */\n  Font.prototype.getCssName = function () {\n    return this.quote_(this.name_);\n  };\n\n  /**\n   * Returns a CSS string representation of the font that\n   * can be used as the CSS font property shorthand.\n   *\n   * @return {string}\n   */\n  Font.prototype.toCssString = function () {\n    return this.getCssStyle() + ' ' + this.getCssWeight() + ' 300px ' + this.getCssName();\n  };\n\n  /**\n   * @private\n   * @param {string} name\n   * @return {string}\n   */\n  Font.prototype.quote_ = function (name) {\n    var quoted = [];\n    var split = name.split(/,\\s*/);\n    for (var i = 0; i < split.length; i++) {\n      var part = split[i].replace(/['\"]/g, '');\n      if (part.indexOf(' ') == -1 && !(/^\\d/.test(part))) {\n        quoted.push(part);\n      } else {\n        quoted.push(\"'\" + part + \"'\");\n      }\n    }\n    return quoted.join(',');\n  };\n\n  /**\n   * @return {string}\n   */\n  Font.prototype.getVariation = function () {\n    return this.style_ + this.weight_;\n  };\n\n  /**\n   * @return {string}\n   */\n  Font.prototype.getCssVariation = function () {\n    return 'font-style:' + this.getCssStyle() + ';font-weight:' + this.getCssWeight() + ';';\n  };\n\n  /**\n   * @return {string}\n   */\n  Font.prototype.getCssWeight = function () {\n    return this.weight_ + '00';\n  };\n\n  /**\n   * @return {string}\n   */\n  Font.prototype.getCssStyle = function () {\n    var style = 'normal';\n\n    if (this.style_ === 'o') {\n      style = 'oblique';\n    } else if (this.style_ === 'i') {\n      style = 'italic';\n    }\n\n    return style;\n  };\n\n  /**\n   * Parses a CSS font declaration and returns a font\n   * variation description.\n   *\n   * @param {string} css\n   * @return {string}\n   */\n  Font.parseCssVariation = function (css) {\n    var weight = 4,\n        style = 'n',\n        m = null;\n\n    if (css) {\n      m = css.match(/(normal|oblique|italic)/i);\n\n      if (m && m[1]) {\n        style = m[1].substr(0, 1).toLowerCase();\n      }\n\n      m = css.match(/([1-9]00|normal|bold)/i);\n\n      if (m && m[1]) {\n        if (/bold/i.test(m[1])) {\n          weight = 7;\n        } else if (/[1-9]00/.test(m[1])) {\n          weight = parseInt(m[1].substr(0, 1), 10);\n        }\n      }\n    }\n    return style + weight;\n  }\n});\n"
  },
  {
    "path": "src/core/fontmodule.js",
    "content": "goog.provide('webfont.FontModule');\n\n/**\n * @interface\n */\nwebfont.FontModule = function () {};\n\ngoog.scope(function () {\n  var FontModule = webfont.FontModule;\n\n  /**\n   * @param {function(Array.<webfont.Font>,  webfont.FontTestStrings=, Object.<string, boolean>=)} onReady\n   */\n  FontModule.prototype.load = function (onReady) {};\n});\n\n"
  },
  {
    "path": "src/core/fontmoduleloader.js",
    "content": "goog.provide('webfont.FontModuleLoader');\ngoog.provide('webfont.FontModuleFactory');\n\n/** @typedef {function(Object, webfont.DomHelper): webfont.FontModule} */\nwebfont.FontModuleFactory;\n\n/**\n * @constructor\n */\nwebfont.FontModuleLoader = function() {\n  /**\n   * @type {Object.<string, webfont.FontModuleFactory>}\n   */\n  this.modules_ = {};\n};\n\ngoog.scope(function () {\n  var FontModuleLoader = webfont.FontModuleLoader;\n\n  /**\n   * @param {string} name\n   * @param {webfont.FontModuleFactory} factory\n   */\n  FontModuleLoader.prototype.addModuleFactory = function(name, factory) {\n    this.modules_[name] = factory;\n  };\n\n  /**\n   * @param {Object} configuration\n   * @param {webfont.DomHelper} domHelper\n   * @return {Array.<webfont.FontModule>}\n   */\n  FontModuleLoader.prototype.getModules = function(configuration, domHelper) {\n    var modules = [];\n\n    for (var key in configuration) {\n      if (configuration.hasOwnProperty(key)) {\n        var moduleFactory = this.modules_[key];\n\n        if (moduleFactory) {\n          modules.push(moduleFactory(configuration[key], domHelper));\n        }\n      }\n    }\n    return modules;\n  };\n});\n"
  },
  {
    "path": "src/core/fontruler.js",
    "content": "goog.provide('webfont.FontRuler');\n\n/**\n * An element that can be used to measure the metrics\n * of a given font and string.\n * @constructor\n * @param {webfont.DomHelper} domHelper\n * @param {string} fontTestString\n */\nwebfont.FontRuler = function (domHelper, fontTestString) {\n  this.domHelper_ = domHelper;\n  this.fontTestString_ = fontTestString;\n  this.el_ = this.domHelper_.createElement('span', {\n    \"aria-hidden\": \"true\"\n  }, this.fontTestString_);\n};\n\ngoog.scope(function () {\n  var FontRuler = webfont.FontRuler;\n\n  /**\n   * @param {webfont.Font} font\n   */\n  FontRuler.prototype.setFont = function(font) {\n    this.domHelper_.setStyle(this.el_, this.computeStyleString_(font));\n  };\n\n  /**\n   * Inserts the ruler into the DOM.\n   */\n  FontRuler.prototype.insert = function() {\n    this.domHelper_.insertInto('body', this.el_);\n  };\n\n  /**\n   * @private\n   * @param {webfont.Font} font\n   * @return {string}\n   */\n  FontRuler.prototype.computeStyleString_ = function(font) {\n    return \"display:block;position:absolute;top:-9999px;left:-9999px;\" +\n           \"font-size:300px;width:auto;height:auto;line-height:normal;margin:0;\" +\n           \"padding:0;font-variant:normal;white-space:nowrap;font-family:\" +\n           font.getCssName() + \";\" + font.getCssVariation();\n  };\n\n  /**\n   * @return {number}\n   */\n  FontRuler.prototype.getWidth = function() {\n    return this.el_.offsetWidth;\n  };\n\n  /**\n   * Removes the ruler element from the DOM.\n   */\n  FontRuler.prototype.remove = function() {\n    this.domHelper_.removeElement(this.el_);\n  };\n});\n"
  },
  {
    "path": "src/core/fontwatcher.js",
    "content": "goog.provide('webfont.FontWatcher');\n\ngoog.require('webfont.FontWatchRunner');\ngoog.require('webfont.NativeFontWatchRunner');\n\n/**\n * @typedef {Object.<string, Array.<string>>}\n */\nwebfont.FontTestStrings;\n\n/**\n * @constructor\n * @param {webfont.DomHelper} domHelper\n * @param {webfont.EventDispatcher} eventDispatcher\n * @param {number=} opt_timeout\n */\nwebfont.FontWatcher = function(domHelper, eventDispatcher, opt_timeout) {\n  this.domHelper_ = domHelper;\n  this.eventDispatcher_ = eventDispatcher;\n  this.currentlyWatched_ = 0;\n  this.last_ = false;\n  this.success_ = false;\n  this.timeout_ = opt_timeout;\n};\n\ngoog.scope(function () {\n  var FontWatcher = webfont.FontWatcher,\n      FontWatchRunner = webfont.FontWatchRunner,\n      NativeFontWatchRunner = webfont.NativeFontWatchRunner;\n\n  /**\n   * @type {null|boolean}\n   */\n  FontWatcher.SHOULD_USE_NATIVE_LOADER = null;\n\n  /**\n   * @return {string}\n   */\n  FontWatcher.getUserAgent = function () {\n    return window.navigator.userAgent;\n  };\n\n  /**\n   * @return {string}\n   */\n  FontWatcher.getVendor = function () {\n    return window.navigator.vendor;\n  };\n\n  /**\n   * Returns true if this browser has support for\n   * the CSS font loading API.\n   *\n   * @return {boolean}\n   */\n  FontWatcher.shouldUseNativeLoader = function () {\n    if (FontWatcher.SHOULD_USE_NATIVE_LOADER === null) {\n      if (!!window.FontFace) {\n        var match = /Gecko.*Firefox\\/(\\d+)/.exec(FontWatcher.getUserAgent());\n        var safari10Match = /OS X.*Version\\/10\\..*Safari/.exec(FontWatcher.getUserAgent()) && /Apple/.exec(FontWatcher.getVendor());\n\n        if (match) {\n          FontWatcher.SHOULD_USE_NATIVE_LOADER = parseInt(match[1], 10) > 42;\n        } else if (safari10Match) {\n          FontWatcher.SHOULD_USE_NATIVE_LOADER = false;\n        } else {\n          FontWatcher.SHOULD_USE_NATIVE_LOADER = true;\n        }\n      } else {\n        FontWatcher.SHOULD_USE_NATIVE_LOADER = false;\n      }\n    }\n    return FontWatcher.SHOULD_USE_NATIVE_LOADER;\n  };\n\n  /**\n   * Watches a set of font families.\n   * @param {Array.<webfont.Font>} fonts The fonts to watch.\n   * @param {webfont.FontTestStrings} fontTestStrings The font test strings for\n   *     each family.\n   * @param {Object.<String, boolean>} metricCompatibleFonts\n   * @param {boolean} last True if this is the last set of fonts to watch.\n   */\n  FontWatcher.prototype.watchFonts = function(fonts,\n      fontTestStrings, metricCompatibleFonts, last) {\n    var length = fonts.length,\n        testStrings = fontTestStrings || {};\n\n    if (length === 0 && last) {\n      this.eventDispatcher_.dispatchInactive();\n      return;\n    }\n\n    this.currentlyWatched_ += fonts.length;\n\n    if (last) {\n      this.last_ = last;\n    }\n\n    var i, fontWatchRunners = [];\n    for (i = 0; i < fonts.length; i++) {\n      var font = fonts[i],\n          testString = testStrings[font.getName()];\n\n      this.eventDispatcher_.dispatchFontLoading(font);\n\n      var fontWatchRunner = null;\n\n      if (FontWatcher.shouldUseNativeLoader()) {\n        fontWatchRunner = new NativeFontWatchRunner(\n            goog.bind(this.fontActive_, this),\n            goog.bind(this.fontInactive_, this),\n            this.domHelper_,\n            font,\n            this.timeout_,\n            testString\n          );\n      } else {\n        fontWatchRunner = new FontWatchRunner(\n          goog.bind(this.fontActive_, this),\n          goog.bind(this.fontInactive_, this),\n          this.domHelper_,\n          font,\n          this.timeout_,\n          metricCompatibleFonts,\n          testString\n        );\n      }\n\n      fontWatchRunners.push(fontWatchRunner);\n    }\n\n    for (i = 0; i < fontWatchRunners.length; i++) {\n      fontWatchRunners[i].start();\n    }\n  };\n\n  /**\n   * Called by a FontWatchRunner when a font has been detected as active.\n   * @param {webfont.Font} font\n   * @private\n   */\n  FontWatcher.prototype.fontActive_ = function(font) {\n    this.eventDispatcher_.dispatchFontActive(font);\n    this.success_ = true;\n    this.decreaseCurrentlyWatched_();\n  };\n\n  /**\n   * Called by a FontWatchRunner when a font has been detected as inactive.\n   * @param {webfont.Font} font\n   * @private\n   */\n  FontWatcher.prototype.fontInactive_ = function(font) {\n    this.eventDispatcher_.dispatchFontInactive(font);\n    this.decreaseCurrentlyWatched_();\n  };\n\n  /**\n   * @private\n   */\n  FontWatcher.prototype.decreaseCurrentlyWatched_ = function() {\n    if (--this.currentlyWatched_ == 0 && this.last_) {\n      if (this.success_) {\n        this.eventDispatcher_.dispatchActive();\n      } else {\n        this.eventDispatcher_.dispatchInactive();\n      }\n    }\n  };\n});\n"
  },
  {
    "path": "src/core/fontwatchrunner.js",
    "content": "goog.provide('webfont.FontWatchRunner');\n\ngoog.require('webfont.Font');\ngoog.require('webfont.FontRuler');\n\n/**\n * @constructor\n * @param {function(webfont.Font)} activeCallback\n * @param {function(webfont.Font)} inactiveCallback\n * @param {webfont.DomHelper} domHelper\n * @param {webfont.Font} font\n * @param {number=} opt_timeout\n * @param {Object.<string, boolean>=} opt_metricCompatibleFonts\n * @param {string=} opt_fontTestString\n */\nwebfont.FontWatchRunner = function(activeCallback, inactiveCallback, domHelper,\n    font, opt_timeout, opt_metricCompatibleFonts, opt_fontTestString) {\n  this.activeCallback_ = activeCallback;\n  this.inactiveCallback_ = inactiveCallback;\n  this.domHelper_ = domHelper;\n  this.font_ = font;\n  this.fontTestString_ = opt_fontTestString || webfont.FontWatchRunner.DEFAULT_TEST_STRING;\n  this.lastResortWidths_ = {};\n  this.timeout_ = opt_timeout || 3000;\n\n  this.metricCompatibleFonts_ = opt_metricCompatibleFonts || null;\n\n  this.fontRulerA_ = null;\n  this.fontRulerB_ = null;\n  this.lastResortRulerA_ = null;\n  this.lastResortRulerB_ = null;\n\n  this.setupRulers_();\n};\n\n/**\n * @enum {string}\n * @const\n */\nwebfont.FontWatchRunner.LastResortFonts = {\n  SERIF: 'serif',\n  SANS_SERIF: 'sans-serif'\n};\n\n/**\n * Default test string. Characters are chosen so that their widths vary a lot\n * between the fonts in the default stacks. We want each fallback stack\n * to always start out at a different width than the other.\n * @type {string}\n * @const\n */\nwebfont.FontWatchRunner.DEFAULT_TEST_STRING = 'BESbswy';\n\ngoog.scope(function () {\n  var FontWatchRunner = webfont.FontWatchRunner,\n      Font = webfont.Font,\n      FontRuler = webfont.FontRuler;\n\n  /**\n   * @type {null|boolean}\n   */\n  FontWatchRunner.HAS_WEBKIT_FALLBACK_BUG = null;\n\n  /**\n   * @return {string}\n   */\n  FontWatchRunner.getUserAgent = function () {\n    return window.navigator.userAgent;\n  };\n\n  /**\n   * Returns true if this browser is WebKit and it has the fallback bug\n   * which is present in WebKit 536.11 and earlier.\n   *\n   * @return {boolean}\n   */\n  FontWatchRunner.hasWebKitFallbackBug = function () {\n    if (FontWatchRunner.HAS_WEBKIT_FALLBACK_BUG === null) {\n      var match = /AppleWebKit\\/([0-9]+)(?:\\.([0-9]+))/.exec(FontWatchRunner.getUserAgent());\n\n      FontWatchRunner.HAS_WEBKIT_FALLBACK_BUG = !!match &&\n                                          (parseInt(match[1], 10) < 536 ||\n                                           (parseInt(match[1], 10) === 536 &&\n                                            parseInt(match[2], 10) <= 11));\n    }\n    return FontWatchRunner.HAS_WEBKIT_FALLBACK_BUG;\n  };\n\n  /**\n   * @private\n   */\n  FontWatchRunner.prototype.setupRulers_ = function() {\n    this.fontRulerA_ = new FontRuler(this.domHelper_, this.fontTestString_);\n    this.fontRulerB_ = new FontRuler(this.domHelper_, this.fontTestString_);\n    this.lastResortRulerA_ = new FontRuler(this.domHelper_, this.fontTestString_);\n    this.lastResortRulerB_ = new FontRuler(this.domHelper_, this.fontTestString_);\n\n    this.fontRulerA_.setFont(new Font(this.font_.getName() + ',' + FontWatchRunner.LastResortFonts.SERIF, this.font_.getVariation()));\n    this.fontRulerB_.setFont(new Font(this.font_.getName() + ',' + FontWatchRunner.LastResortFonts.SANS_SERIF, this.font_.getVariation()));\n    this.lastResortRulerA_.setFont(new Font(FontWatchRunner.LastResortFonts.SERIF, this.font_.getVariation()));\n    this.lastResortRulerB_.setFont(new Font(FontWatchRunner.LastResortFonts.SANS_SERIF, this.font_.getVariation()));\n\n    this.fontRulerA_.insert();\n    this.fontRulerB_.insert();\n    this.lastResortRulerA_.insert();\n    this.lastResortRulerB_.insert();\n  };\n\n  FontWatchRunner.prototype.start = function() {\n    this.lastResortWidths_[FontWatchRunner.LastResortFonts.SERIF] = this.lastResortRulerA_.getWidth();\n    this.lastResortWidths_[FontWatchRunner.LastResortFonts.SANS_SERIF] = this.lastResortRulerB_.getWidth();\n\n    this.started_ = goog.now();\n\n    this.check_();\n  };\n\n  /**\n   * Returns true if the given width matches the generic font family width.\n   *\n   * @private\n   * @param {number} width\n   * @param {string} lastResortFont\n   * @return {boolean}\n   */\n  FontWatchRunner.prototype.widthMatches_ = function(width, lastResortFont) {\n    return width === this.lastResortWidths_[lastResortFont];\n  };\n\n  /**\n   * Return true if the given widths match any of the generic font family\n   * widths.\n   *\n   * @private\n   * @param {number} a\n   * @param {number} b\n   * @return {boolean}\n   */\n  FontWatchRunner.prototype.widthsMatchLastResortWidths_ = function(a, b) {\n    for (var font in FontWatchRunner.LastResortFonts) {\n      if (FontWatchRunner.LastResortFonts.hasOwnProperty(font)) {\n        if (this.widthMatches_(a, FontWatchRunner.LastResortFonts[font]) &&\n            this.widthMatches_(b, FontWatchRunner.LastResortFonts[font])) {\n          return true;\n        }\n      }\n    }\n    return false;\n  };\n\n  /**\n   * @private\n   * Returns true if the loading has timed out.\n   * @return {boolean}\n   */\n  FontWatchRunner.prototype.hasTimedOut_ = function() {\n    return goog.now() - this.started_ >= this.timeout_;\n  };\n\n  /**\n   * Returns true if both fonts match the normal fallback fonts.\n   *\n   * @private\n   * @param {number} a\n   * @param {number} b\n   * @return {boolean}\n   */\n  FontWatchRunner.prototype.isFallbackFont_ = function (a, b) {\n    return this.widthMatches_(a, FontWatchRunner.LastResortFonts.SERIF) &&\n           this.widthMatches_(b, FontWatchRunner.LastResortFonts.SANS_SERIF);\n  };\n\n  /**\n   * Returns true if the WebKit bug is present and both widths match a last resort font.\n   *\n   * @private\n   * @param {number} a\n   * @param {number} b\n   * @return {boolean}\n   */\n  FontWatchRunner.prototype.isLastResortFont_ = function (a, b) {\n    return FontWatchRunner.hasWebKitFallbackBug() && this.widthsMatchLastResortWidths_(a, b);\n  };\n\n  /**\n   * Returns true if the current font is metric compatible. Also returns true\n   * if we do not have a list of metric compatible fonts.\n   *\n   * @private\n   * @return {boolean}\n   */\n  FontWatchRunner.prototype.isMetricCompatibleFont_ = function () {\n    return this.metricCompatibleFonts_ === null || this.metricCompatibleFonts_.hasOwnProperty(this.font_.getName());\n  };\n\n  /**\n   * Checks the width of the two spans against their original widths during each\n   * async loop. If the width of one of the spans is different than the original\n   * width, then we know that the font is rendering and finish with the active\n   * callback. If we wait more than 5 seconds and nothing has changed, we finish\n   * with the inactive callback.\n   *\n   * @private\n   */\n  FontWatchRunner.prototype.check_ = function() {\n    var widthA = this.fontRulerA_.getWidth();\n    var widthB = this.fontRulerB_.getWidth();\n\n    if (this.isFallbackFont_(widthA, widthB) || this.isLastResortFont_(widthA, widthB)) {\n      if (this.hasTimedOut_()) {\n        if (this.isLastResortFont_(widthA, widthB) && this.isMetricCompatibleFont_()) {\n          this.finish_(this.activeCallback_);\n        } else {\n          this.finish_(this.inactiveCallback_);\n        }\n     } else {\n        this.asyncCheck_();\n      }\n    } else {\n      this.finish_(this.activeCallback_);\n    }\n  };\n\n  /**\n   * @private\n   */\n  FontWatchRunner.prototype.asyncCheck_ = function() {\n    setTimeout(goog.bind(function () {\n      this.check_();\n    }, this), 50);\n  };\n\n  /**\n   * @private\n   * @param {function(webfont.Font)} callback\n   */\n  FontWatchRunner.prototype.finish_ = function(callback) {\n    // Remove elements and trigger callback (which adds active/inactive class) asynchronously to avoid reflow chain if\n    // several fonts are finished loading right after each other\n    setTimeout(goog.bind(function () {\n      this.fontRulerA_.remove();\n      this.fontRulerB_.remove();\n      this.lastResortRulerA_.remove();\n      this.lastResortRulerB_.remove();\n      callback(this.font_);\n    }, this), 0);\n  };\n\n});\n"
  },
  {
    "path": "src/core/initialize.js",
    "content": "goog.provide('webfont');\n\ngoog.require('webfont.WebFont');\n\ngoog.require('webfont.modules.Typekit');\ngoog.require('webfont.modules.Fontdeck');\ngoog.require('webfont.modules.Monotype');\ngoog.require('webfont.modules.Custom');\ngoog.require('webfont.modules.google.GoogleFontApi');\n\n/**\n * @define {boolean}\n */\nvar INCLUDE_CUSTOM_MODULE = false;\n\n/**\n * @define {boolean}\n */\nvar INCLUDE_FONTDECK_MODULE = false;\n\n/**\n * @define {boolean}\n */\nvar INCLUDE_MONOTYPE_MODULE = false;\n\n/**\n * @define {boolean}\n */\nvar INCLUDE_TYPEKIT_MODULE = false;\n\n/**\n * @define {boolean}\n */\nvar INCLUDE_GOOGLE_MODULE = false;\n\n/**\n * @define {string}\n */\nvar WEBFONT = 'WebFont';\n\n/**\n * @define {string}\n */\nvar WEBFONT_CONFIG = 'WebFontConfig';\n\n/**\n * @type {webfont.WebFont}\n */\nvar webFontLoader = new webfont.WebFont(window);\n\nif (INCLUDE_CUSTOM_MODULE) {\n  webFontLoader.addModule(webfont.modules.Custom.NAME, function (configuration, domHelper) {\n    return new webfont.modules.Custom(domHelper, configuration);\n  });\n}\n\nif (INCLUDE_FONTDECK_MODULE) {\n  webFontLoader.addModule(webfont.modules.Fontdeck.NAME, function (configuration, domHelper) {\n    return new webfont.modules.Fontdeck(domHelper, configuration);\n  });\n}\n\nif (INCLUDE_MONOTYPE_MODULE) {\n  webFontLoader.addModule(webfont.modules.Monotype.NAME, function (configuration, domHelper) {\n    return new webfont.modules.Monotype(domHelper, configuration);\n  });\n}\n\nif (INCLUDE_TYPEKIT_MODULE) {\n  webFontLoader.addModule(webfont.modules.Typekit.NAME, function (configuration, domHelper) {\n    return new webfont.modules.Typekit(domHelper, configuration);\n  });\n}\n\nif (INCLUDE_GOOGLE_MODULE) {\n  webFontLoader.addModule(webfont.modules.google.GoogleFontApi.NAME, function (configuration, domHelper) {\n    return new webfont.modules.google.GoogleFontApi(domHelper, configuration);\n  });\n}\n\nvar exports = {\n  'load': goog.bind(webFontLoader.load, webFontLoader)\n};\n\nif (typeof define === \"function\" && define.amd) {\n  define(function () {\n    return exports;\n  });\n} else if (typeof module !== \"undefined\" && module.exports) {\n  module.exports = exports;\n} else {\n  window[WEBFONT] = exports;\n\n  if (window[WEBFONT_CONFIG]) {\n    webFontLoader.load(window[WEBFONT_CONFIG]);\n  }\n}\n"
  },
  {
    "path": "src/core/nativefontwatchrunner.js",
    "content": "goog.provide('webfont.NativeFontWatchRunner');\n\ngoog.require('webfont.Font');\n\ngoog.scope(function () {\n  /**\n  * @constructor\n  * @param {function(webfont.Font)} activeCallback\n  * @param {function(webfont.Font)} inactiveCallback\n  * @param {webfont.DomHelper} domHelper\n  * @param {webfont.Font} font\n  * @param {number=} opt_timeout\n  * @param {string=} opt_fontTestString\n  */\n  webfont.NativeFontWatchRunner = function(activeCallback, inactiveCallback, domHelper, font, opt_timeout, opt_fontTestString) {\n    this.activeCallback_ = activeCallback;\n    this.inactiveCallback_ = inactiveCallback;\n    this.font_ = font;\n    this.domHelper_ = domHelper;\n    this.timeout_ = opt_timeout || 3000;\n    this.fontTestString_ = opt_fontTestString || undefined;\n  };\n\n  var NativeFontWatchRunner = webfont.NativeFontWatchRunner;\n\n  NativeFontWatchRunner.prototype.start = function () {\n    var doc = this.domHelper_.getLoadWindow().document,\n        that = this;\n\n    var start = goog.now();\n\n    var loader = new Promise(function (resolve, reject) {\n      var check = function () {\n        var now = goog.now();\n\n        if (now - start >= that.timeout_) {\n          reject();\n        } else {\n          doc.fonts.load(that.font_.toCssString(), that.fontTestString_).then(function (fonts) {\n            if (fonts.length >= 1) {\n              resolve();\n            } else {\n              setTimeout(check, 25);\n            }\n          }, function () {\n            reject();\n          });\n        }\n      };\n\n      check();\n    });\n\n    var timeoutId = null,\n      timer = new Promise(function (resolve, reject) {\n        timeoutId = setTimeout(reject, that.timeout_);\n      });\n\n    Promise.race([timer, loader]).then(function () {\n      if (timeoutId) {\n        clearTimeout(timeoutId);\n        timeoutId = null;\n      }\n      that.activeCallback_(that.font_);\n    }, function () {\n      that.inactiveCallback_(that.font_);\n    });\n  };\n});\n"
  },
  {
    "path": "src/core/stylesheetwaiter.js",
    "content": "goog.provide('webfont.StyleSheetWaiter');\n\n/**\n * A utility class for handling callback from DomHelper.loadStylesheet().\n *\n * @constructor\n */\nwebfont.StyleSheetWaiter = function() {\n  /** @private @type {number} */\n  this.waitingCount_ = 0;\n  /** @private @type {Function} */\n  this.onReady_ = null;\n};\n\ngoog.scope(function () {\n  var StyleSheetWaiter = webfont.StyleSheetWaiter;\n\n  /**\n   * @return {function(Error)}\n   */\n  StyleSheetWaiter.prototype.startWaitingLoad = function() {\n    var self = this;\n    self.waitingCount_++;\n    return function(error) {\n      self.waitingCount_--;\n      self.fireIfReady_();\n    };\n  };\n\n  /**\n   * @param {Function} fn\n   */\n  StyleSheetWaiter.prototype.waitWhileNeededThen = function(fn) {\n    this.onReady_ = fn;\n    this.fireIfReady_();\n  };\n\n  /**\n   * @private\n   */\n  StyleSheetWaiter.prototype.fireIfReady_ = function() {\n    var isReady = 0 == this.waitingCount_;\n    if (isReady && this.onReady_) {\n      this.onReady_();\n      this.onReady_ = null;\n    }\n  };\n});\n"
  },
  {
    "path": "src/core/webfont.js",
    "content": "goog.provide('webfont.WebFont');\n\ngoog.require('webfont.DomHelper');\ngoog.require('webfont.EventDispatcher');\ngoog.require('webfont.FontWatcher');\ngoog.require('webfont.FontModuleLoader');\n\n/**\n * @param {Window} mainWindow The main application window containing\n *   webfontloader.js.\n * @constructor\n */\nwebfont.WebFont = function(mainWindow) {\n  this.mainWindow_ = mainWindow;\n  this.fontModuleLoader_ = new webfont.FontModuleLoader();\n  this.moduleLoading_ = 0;\n  this.events_ = true;\n  this.classes_ = true;\n};\n\ngoog.scope(function () {\n  var WebFont = webfont.WebFont,\n      DomHelper = webfont.DomHelper,\n      EventDispatcher = webfont.EventDispatcher,\n      FontWatcher = webfont.FontWatcher;\n\n  /**\n   * @param {string} name\n   * @param {webfont.FontModuleFactory} factory\n   */\n  WebFont.prototype.addModule = function(name, factory) {\n    this.fontModuleLoader_.addModuleFactory(name, factory);\n  };\n\n  /**\n   * @param {Object} configuration\n   */\n  WebFont.prototype.load = function(configuration) {\n    var context = configuration['context'] || this.mainWindow_;\n    this.domHelper_ = new DomHelper(this.mainWindow_, context);\n\n    this.events_ = configuration['events'] !== false;\n    this.classes_ = configuration['classes'] !== false;\n\n    var eventDispatcher = new EventDispatcher(\n        this.domHelper_,\n        configuration\n    );\n\n    this.load_(eventDispatcher, configuration);\n  };\n\n  /**\n   * @param {webfont.EventDispatcher} eventDispatcher\n   * @param {webfont.FontWatcher} fontWatcher\n   * @param {Array.<webfont.Font>} fonts\n   * @param {webfont.FontTestStrings=} opt_fontTestStrings\n   * @param {Object.<string, boolean>=} opt_metricCompatibleFonts\n   */\n  WebFont.prototype.onModuleReady_ = function(eventDispatcher, fontWatcher, fonts, opt_fontTestStrings, opt_metricCompatibleFonts) {\n    var allModulesLoaded = --this.moduleLoading_ == 0;\n\n    if (this.classes_ || this.events_) {\n      setTimeout(function () {\n        fontWatcher.watchFonts(fonts, opt_fontTestStrings || null, opt_metricCompatibleFonts || null, allModulesLoaded);\n      }, 0);\n    }\n  };\n\n  /**\n   * @param {webfont.EventDispatcher} eventDispatcher\n   * @param {Object} configuration\n   */\n  WebFont.prototype.load_ = function(eventDispatcher, configuration) {\n    var modules = [],\n        timeout = configuration['timeout'],\n        self = this;\n\n    // Immediately dispatch the loading event before initializing the modules\n    // so we know for sure that the loading event is synchronous.\n    eventDispatcher.dispatchLoading();\n\n    modules = this.fontModuleLoader_.getModules(configuration, this.domHelper_);\n\n    var fontWatcher = new webfont.FontWatcher(this.domHelper_, eventDispatcher, timeout);\n\n    this.moduleLoading_ = modules.length;\n\n    for (var i = 0, len = modules.length; i < len; i++) {\n      var module = modules[i];\n\n      module.load(function (fonts, opt_fontTestStrings, opt_metricCompatibleFonts) {\n        self.onModuleReady_(eventDispatcher, fontWatcher, fonts, opt_fontTestStrings, opt_metricCompatibleFonts);\n      });\n    }\n  };\n});\n"
  },
  {
    "path": "src/modules/custom.js",
    "content": "goog.provide('webfont.modules.Custom');\n\ngoog.require('webfont.Font');\ngoog.require('webfont.StyleSheetWaiter');\n\n/**\n *\n * WebFont.load({\n *   custom: {\n *     families: ['Font1', 'Font2'],\n *    urls: [ 'https://moo', 'https://meuh' ] }\n * });\n *\n * @constructor\n * @implements {webfont.FontModule}\n */\nwebfont.modules.Custom = function(domHelper, configuration) {\n  this.domHelper_ = domHelper;\n  this.configuration_ = configuration;\n};\n\n/**\n * @const\n * @type {string}\n */\nwebfont.modules.Custom.NAME = 'custom';\n\ngoog.scope(function () {\n  var Custom = webfont.modules.Custom,\n      Font = webfont.Font,\n      StyleSheetWaiter = webfont.StyleSheetWaiter;\n\n  Custom.prototype.load = function(onReady) {\n    var i, len;\n    var urls = this.configuration_['urls'] || [];\n    var familiesConfiguration = this.configuration_['families'] || [];\n    var fontTestStrings = this.configuration_['testStrings'] || {};\n    var waiter = new StyleSheetWaiter();\n    for (i = 0, len = urls.length; i < len; i++) {\n      this.domHelper_.loadStylesheet(urls[i], waiter.startWaitingLoad());\n    }\n\n    var fonts = [];\n\n    for (i = 0, len = familiesConfiguration.length; i < len; i++) {\n      var components = familiesConfiguration[i].split(\":\");\n\n      if (components[1]) {\n        var variations = components[1].split(\",\");\n\n        for (var j = 0; j < variations.length; j += 1) {\n          fonts.push(new Font(components[0], variations[j]));\n        }\n      } else {\n        fonts.push(new Font(components[0]));\n      }\n    }\n\n    waiter.waitWhileNeededThen(function() {\n      onReady(fonts, fontTestStrings);\n    });\n  };\n});\n"
  },
  {
    "path": "src/modules/fontdeck.js",
    "content": "goog.provide('webfont.modules.Fontdeck');\n\ngoog.require('webfont.Font');\n\n/**\n * @constructor\n * @implements {webfont.FontModule}\n */\nwebfont.modules.Fontdeck = function(domHelper, configuration) {\n  this.domHelper_ = domHelper;\n  this.configuration_ = configuration;\n  this.fonts_ = [];\n};\n\n/**\n * @const\n * @type {string}\n */\nwebfont.modules.Fontdeck.NAME = 'fontdeck';\nwebfont.modules.Fontdeck.HOOK = '__webfontfontdeckmodule__';\nwebfont.modules.Fontdeck.API = 'https://f.fontdeck.com/s/css/js/';\n\ngoog.scope(function () {\n  var Fontdeck = webfont.modules.Fontdeck,\n      Font = webfont.Font,\n      FontVariationDescription = webfont.FontVariationDescription;\n\n  Fontdeck.prototype.getScriptSrc = function(projectId) {\n    // For empty iframes, fall back to main window's hostname.\n    var hostname = this.domHelper_.getHostName();\n    var api = this.configuration_['api'] || webfont.modules.Fontdeck.API;\n    return api + hostname + '/' + projectId + '.js';\n  };\n\n  Fontdeck.prototype.load = function(onReady) {\n    var projectId = this.configuration_['id'];\n    var loadWindow = this.domHelper_.getLoadWindow();\n    var self = this;\n\n    if (projectId) {\n      // Provide data to Fontdeck for processing.\n      if (!loadWindow[webfont.modules.Fontdeck.HOOK]) {\n        loadWindow[webfont.modules.Fontdeck.HOOK] = {};\n      }\n\n      // Fontdeck will call this function to indicate support status\n      // and what fonts are provided.\n      loadWindow[webfont.modules.Fontdeck.HOOK][projectId] = function(fontdeckSupports, data) {\n        for (var i = 0, j = data['fonts'].length; i<j; ++i) {\n          var font = data['fonts'][i];\n          self.fonts_.push(new Font(font['name'], Font.parseCssVariation('font-weight:' + font['weight'] + ';font-style:' + font['style'])));\n        }\n        onReady(self.fonts_);\n      };\n\n      // Call the Fontdeck API.\n      this.domHelper_.loadScript(this.getScriptSrc(projectId), function (err) {\n        if (err) {\n          onReady([]);\n        }\n      });\n    } else {\n      onReady([]);\n    }\n  };\n});\n"
  },
  {
    "path": "src/modules/google/fontapiparser.js",
    "content": "goog.provide('webfont.modules.google.FontApiParser');\n\ngoog.require('webfont.Font');\n\n/**\n * @constructor\n */\nwebfont.modules.google.FontApiParser = function(fontFamilies) {\n  this.fontFamilies_ = fontFamilies;\n  this.parsedFonts_ = [];\n  this.fontTestStrings_ = {};\n};\n\nwebfont.modules.google.FontApiParser.INT_FONTS = {\n  'latin': webfont.FontWatchRunner.DEFAULT_TEST_STRING,\n  'latin-ext': '\\u00E7\\u00F6\\u00FC\\u011F\\u015F',\n  'cyrillic': '\\u0439\\u044f\\u0416',\n  'greek': '\\u03b1\\u03b2\\u03a3',\n  'khmer': '\\u1780\\u1781\\u1782',\n  'Hanuman': '\\u1780\\u1781\\u1782' // For backward compatibility\n};\n\nwebfont.modules.google.FontApiParser.WEIGHTS = {\n  'thin': '1',\n  'extralight': '2',\n  'extra-light': '2',\n  'ultralight': '2',\n  'ultra-light': '2',\n  'light': '3',\n  'regular': '4',\n  'book': '4',\n  'medium': '5',\n  'semi-bold': '6',\n  'semibold': '6',\n  'demi-bold': '6',\n  'demibold': '6',\n  'bold': '7',\n  'extra-bold': '8',\n  'extrabold': '8',\n  'ultra-bold': '8',\n  'ultrabold': '8',\n  'black': '9',\n  'heavy': '9',\n  'l': '3',\n  'r': '4',\n  'b': '7'\n};\n\nwebfont.modules.google.FontApiParser.STYLES = {\n  'i': 'i',\n  'italic': 'i',\n  'n': 'n',\n  'normal': 'n'\n};\n\nwebfont.modules.google.FontApiParser.VARIATION_MATCH =\n    new RegExp(\"^(thin|(?:(?:extra|ultra)-?)?light|regular|book|medium|\" +\n        \"(?:(?:semi|demi|extra|ultra)-?)?bold|black|heavy|l|r|b|[1-9]00)?(n|i\" +\n        \"|normal|italic)?$\");\n\ngoog.scope(function () {\n  var FontApiParser = webfont.modules.google.FontApiParser,\n      Font = webfont.Font;\n\n  FontApiParser.prototype.parse = function() {\n    var length = this.fontFamilies_.length;\n\n    for (var i = 0; i < length; i++) {\n      var elements = this.fontFamilies_[i].split(\":\");\n      var fontFamily = elements[0].replace(/\\+/g, \" \");\n      var variations = ['n4'];\n\n      if (elements.length >= 2) {\n        var fvds = this.parseVariations_(elements[1]);\n\n        if (fvds.length > 0) {\n          variations = fvds;\n        }\n        if (elements.length == 3) {\n          var subsets = this.parseSubsets_(elements[2]);\n          if (subsets.length > 0) {\n            var fontTestString = FontApiParser.INT_FONTS[subsets[0]];\n\n            if (fontTestString) {\n              this.fontTestStrings_[fontFamily] = fontTestString;\n            }\n          }\n        }\n      }\n\n      // For backward compatibility\n      if (!this.fontTestStrings_[fontFamily]) {\n        var hanumanTestString = FontApiParser.INT_FONTS[fontFamily];\n        if (hanumanTestString) {\n          this.fontTestStrings_[fontFamily] = hanumanTestString;\n        }\n      }\n\n      for (var j = 0; j < variations.length; j += 1) {\n        this.parsedFonts_.push(new Font(fontFamily, variations[j]));\n      }\n    }\n  };\n\n  FontApiParser.prototype.generateFontVariationDescription_ = function(variation) {\n    if (!variation.match(/^[\\w-]+$/)) {\n      return '';\n    }\n    var normalizedVariation = variation.toLowerCase();\n    var groups = FontApiParser.VARIATION_MATCH.exec(normalizedVariation);\n    if (groups == null) {\n      return '';\n    }\n    var styleMatch = this.normalizeStyle_(groups[2]);\n    var weightMatch = this.normalizeWeight_(groups[1]);\n    return [styleMatch, weightMatch].join('');\n  };\n\n\n  FontApiParser.prototype.normalizeStyle_ = function(parsedStyle) {\n    if (parsedStyle == null || parsedStyle == '') {\n      return 'n';\n    }\n    return FontApiParser.STYLES[parsedStyle];\n  };\n\n\n  FontApiParser.prototype.normalizeWeight_ = function(parsedWeight) {\n    if (parsedWeight == null || parsedWeight == '') {\n      return '4';\n    }\n    var weight = FontApiParser.WEIGHTS[parsedWeight];\n    if (weight) {\n      return weight;\n    }\n    if (isNaN(parsedWeight)) {\n      return '4';\n    }\n    return parsedWeight.substr(0, 1);\n  };\n\n\n  FontApiParser.prototype.parseVariations_ = function(variations) {\n    var finalVariations = [];\n\n    if (!variations) {\n      return finalVariations;\n    }\n    var providedVariations = variations.split(\",\");\n    var length = providedVariations.length;\n\n    for (var i = 0; i < length; i++) {\n      var variation = providedVariations[i];\n      var fvd = this.generateFontVariationDescription_(variation);\n\n      if (fvd) {\n        finalVariations.push(fvd);\n      }\n    }\n    return finalVariations;\n  };\n\n\n  FontApiParser.prototype.parseSubsets_ = function(subsets) {\n    var finalSubsets = [];\n\n    if (!subsets) {\n      return finalSubsets;\n    }\n    return subsets.split(\",\");\n  };\n\n\n  FontApiParser.prototype.getFonts = function() {\n    return this.parsedFonts_;\n  };\n\n  FontApiParser.prototype.getFontTestStrings = function() {\n    return this.fontTestStrings_;\n  };\n});\n"
  },
  {
    "path": "src/modules/google/fontapiurlbuilder.js",
    "content": "goog.provide('webfont.modules.google.FontApiUrlBuilder');\n\n/**\n * @constructor\n */\nwebfont.modules.google.FontApiUrlBuilder = function(apiUrl, text) {\n  if (apiUrl) {\n    this.apiUrl_ = apiUrl;\n  } else {\n    this.apiUrl_ = webfont.modules.google.FontApiUrlBuilder.DEFAULT_API_URL;\n  }\n  this.fontFamilies_ = [];\n  this.subsets_ = [];\n  this.text_ = text || '';\n};\n\n\nwebfont.modules.google.FontApiUrlBuilder.DEFAULT_API_URL = 'https://fonts.googleapis.com/css';\n\ngoog.scope(function () {\n  var FontApiUrlBuilder = webfont.modules.google.FontApiUrlBuilder;\n\n  FontApiUrlBuilder.prototype.setFontFamilies = function(fontFamilies) {\n    this.parseFontFamilies_(fontFamilies);\n  };\n\n\n  FontApiUrlBuilder.prototype.parseFontFamilies_ =\n      function(fontFamilies) {\n    var length = fontFamilies.length;\n\n    for (var i = 0; i < length; i++) {\n      var elements = fontFamilies[i].split(':');\n\n      if (elements.length == 3) {\n        this.subsets_.push(elements.pop());\n      }\n      var joinCharacter = '';\n      if (elements.length == 2 && elements[1] != ''){\n        joinCharacter = ':';\n      }\n      this.fontFamilies_.push(elements.join(joinCharacter));\n    }\n  };\n\n\n  FontApiUrlBuilder.prototype.webSafe = function(string) {\n    return string.replace(/ /g, '+');\n  };\n\n\n  FontApiUrlBuilder.prototype.build = function() {\n    if (this.fontFamilies_.length == 0) {\n      throw new Error('No fonts to load!');\n    }\n    if (this.apiUrl_.indexOf(\"kit=\") != -1) {\n      return this.apiUrl_;\n    }\n    var length = this.fontFamilies_.length;\n    var sb = [];\n\n    for (var i = 0; i < length; i++) {\n      sb.push(this.webSafe(this.fontFamilies_[i]));\n    }\n    var url = this.apiUrl_ + '?family=' + sb.join('%7C'); // '|' escaped.\n\n    if (this.subsets_.length > 0) {\n      url += '&subset=' + this.subsets_.join(',');\n    }\n\n    if (this.text_.length > 0) {\n      url += '&text=' + encodeURIComponent(this.text_);\n    }\n\n    return url;\n  };\n});\n"
  },
  {
    "path": "src/modules/google/googlefontapi.js",
    "content": "goog.provide('webfont.modules.google.GoogleFontApi');\n\ngoog.require('webfont.modules.google.FontApiUrlBuilder');\ngoog.require('webfont.modules.google.FontApiParser');\ngoog.require('webfont.FontWatchRunner');\ngoog.require('webfont.StyleSheetWaiter');\n\n/**\n * @constructor\n * @implements {webfont.FontModule}\n */\nwebfont.modules.google.GoogleFontApi = function(domHelper, configuration) {\n  this.domHelper_ = domHelper;\n  this.configuration_ = configuration;\n};\n\n/**\n * @const\n * @type {string}\n */\nwebfont.modules.google.GoogleFontApi.NAME = 'google';\n\ngoog.scope(function () {\n  var GoogleFontApi = webfont.modules.google.GoogleFontApi,\n      FontWatchRunner = webfont.FontWatchRunner,\n      StyleSheetWaiter = webfont.StyleSheetWaiter,\n      FontApiUrlBuilder = webfont.modules.google.FontApiUrlBuilder,\n      FontApiParser = webfont.modules.google.FontApiParser;\n\n  GoogleFontApi.METRICS_COMPATIBLE_FONTS = {\n    \"Arimo\": true,\n    \"Cousine\": true,\n    \"Tinos\": true\n  };\n\n  GoogleFontApi.prototype.load = function(onReady) {\n    var waiter = new StyleSheetWaiter();\n    var domHelper = this.domHelper_;\n    var fontApiUrlBuilder = new FontApiUrlBuilder(\n        this.configuration_['api'],\n        this.configuration_['text']\n    );\n    var fontFamilies = this.configuration_['families'];\n    fontApiUrlBuilder.setFontFamilies(fontFamilies);\n\n    var fontApiParser = new FontApiParser(fontFamilies);\n    fontApiParser.parse();\n\n    domHelper.loadStylesheet(fontApiUrlBuilder.build(), waiter.startWaitingLoad());\n    waiter.waitWhileNeededThen(function() {\n      onReady(fontApiParser.getFonts(), fontApiParser.getFontTestStrings(), GoogleFontApi.METRICS_COMPATIBLE_FONTS);\n    });\n  };\n});\n"
  },
  {
    "path": "src/modules/monotype.js",
    "content": "goog.provide('webfont.modules.Monotype');\n\ngoog.require('webfont.Font');\n\n/**\nwebfont.load({\n  monotype: {\n    projectId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'//this is your Fonts.com Web fonts projectId\n  }\n});\n*/\n\n/**\n * @constructor\n * @implements {webfont.FontModule}\n */\nwebfont.modules.Monotype = function(domHelper, configuration) {\n  this.domHelper_ = domHelper;\n  this.configuration_ = configuration;\n};\n\n/**\n * name of the module through which external API is supposed to call the MonotypeFontAPI.\n *\n * @const\n * @type {string}\n */\nwebfont.modules.Monotype.NAME = 'monotype';\n\n/**\n * __mti_fntLst is the name of function that exposes Monotype's font list.\n * @const\n */\nwebfont.modules.Monotype.HOOK = '__mti_fntLst';\n\n/**\n * __MonotypeAPIScript__ is the id of script added by google API. Currently 'fonts.com' supports only one script in a page.\n * This may require change in future if 'fonts.com' begins supporting multiple scripts per page.\n * @const\n */\nwebfont.modules.Monotype.SCRIPTID = '__MonotypeAPIScript__';\n\n/**\n * __MonotypeConfiguration__ is function exposed to fonts.com. fonts.com will use this function to get webfontloader configuration\n * @const\n */\nwebfont.modules.Monotype.CONFIGURATION = '__MonotypeConfiguration__';\n\ngoog.scope(function() {\n  var Monotype = webfont.modules.Monotype,\n    Font = webfont.Font;\n    \n\n  Monotype.prototype.getScriptSrc = function(projectId, version) {\n    var api = (this.configuration_['api'] || 'https://fast.fonts.net/jsapi')\n    return api + '/' + projectId + '.js' + (version ? '?v=' + version : '');\n  };\n\n  Monotype.prototype.load = function(onReady) {\n    var self = this;\n    var projectId = self.configuration_['projectId'];\n    var version = self.configuration_['version'];\n  \n      \n    function checkAndLoadIfDownloaded() {\n      if (loadWindow[Monotype.HOOK + projectId]) {\n        var mti_fnts = loadWindow[Monotype.HOOK + projectId](),\n            fonts = [],\n            fntVariation;\n\n        if (mti_fnts) {\n          for (var i = 0; i < mti_fnts.length; i++) {\n            var fnt = mti_fnts[i][\"fontfamily\"];\n            \n            //Check if font-style and font-weight is available\n            if (mti_fnts[i][\"fontStyle\"] != undefined && mti_fnts[i][\"fontWeight\"] != undefined) {\n              fntVariation = mti_fnts[i][\"fontStyle\"] + mti_fnts[i][\"fontWeight\"];\n              fonts.push(new Font(fnt, fntVariation));\n            } else {\n              fonts.push(new Font(fnt));\n            }\n          }\n        }\n        onReady(fonts);\n      } else {\n        setTimeout(function() {\n          checkAndLoadIfDownloaded();\n        }, 50);\n      }\n    }\n    if (projectId) {\n      var loadWindow = self.domHelper_.getLoadWindow();\n\n      var script = this.domHelper_.loadScript(self.getScriptSrc(projectId, version), function(err) {\n        if (err) {\n          onReady([]);\n        } else {\n          loadWindow[Monotype.CONFIGURATION+ projectId] = function() {\n           return  self.configuration_;\n          };\n            \n          checkAndLoadIfDownloaded();\n        }\n      });\n      script[\"id\"] = Monotype.SCRIPTID + projectId;\n    } else {\n      onReady([]);\n    }\n  };\n});"
  },
  {
    "path": "src/modules/typekit.js",
    "content": "goog.provide('webfont.modules.Typekit');\n\ngoog.require('webfont.Font');\n\n/**\n * @constructor\n * @implements {webfont.FontModule}\n */\nwebfont.modules.Typekit = function(domHelper, configuration) {\n  this.domHelper_ = domHelper;\n  this.configuration_ = configuration;\n};\n\n/**\n * @const\n * @type {string}\n */\nwebfont.modules.Typekit.NAME = 'typekit';\n\ngoog.scope(function () {\n  var Typekit = webfont.modules.Typekit,\n      Font = webfont.Font;\n\n  Typekit.prototype.getScriptSrc = function(kitId) {\n    var api = this.configuration_['api'] || 'https://use.typekit.net';\n    return api + '/' + kitId + '.js';\n  };\n\n  Typekit.prototype.load = function(onReady) {\n    var kitId = this.configuration_['id'];\n    var configuration = this.configuration_;\n    var loadWindow = this.domHelper_.getLoadWindow();\n    var that = this;\n\n    if (kitId) {\n      // Load the Typekit script. Once it is done loading we grab its configuration\n      // and use that to populate the fonts we should watch.\n      this.domHelper_.loadScript(this.getScriptSrc(kitId), function (err) {\n        if (err) {\n          onReady([]);\n        } else {\n          if (loadWindow['Typekit'] && loadWindow['Typekit']['config'] && loadWindow['Typekit']['config']['fn']) {\n            var fn = loadWindow['Typekit']['config']['fn'],\n                fonts = [];\n\n            for (var i = 0; i < fn.length; i += 2) {\n              var font = fn[i],\n                  variations = fn[i + 1];\n\n              for (var j = 0; j < variations.length; j++) {\n                fonts.push(new Font(font, variations[j]));\n              }\n            }\n\n            // Kick off font loading but disable font events so\n            // we don't duplicate font watching.\n            try {\n              loadWindow['Typekit']['load']({\n                'events': false,\n                'classes': false,\n                'async': true\n              });\n            } catch (e) {}\n\n            onReady(fonts);\n          }\n        }\n      }, 2000);\n    } else {\n      onReady([]);\n    }\n  };\n});\n"
  },
  {
    "path": "src/modules.yml",
    "content": "core:\n  - ../tools/compiler/base.js\n  - core/domhelper.js\n  - core/stylesheetwaiter.js\n  - core/cssclassname.js\n  - core/font.js\n  - core/eventdispatcher.js\n  - core/fontmodule.js\n  - core/fontmoduleloader.js\n  - core/fontruler.js\n  - core/nativefontwatchrunner.js\n  - core/fontwatchrunner.js\n  - core/fontwatcher.js\n  - core/webfont.js\n  - core/initialize.js\n\n\n\ngoogle:\n  - modules/google/fontapiurlbuilder.js\n  - modules/google/fontapiparser.js\n  - modules/google/googlefontapi.js\n\nfontdeck:\n  - modules/fontdeck.js\n\ntypekit:\n  - modules/typekit.js\n\nmonotype:\n  - modules/monotype.js\n\ncustom:\n  - modules/custom.js\n"
  },
  {
    "path": "tools/compiler/base.js",
    "content": "// Copyright 2006 The Closure Library Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS-IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n/**\n * @fileoverview Bootstrap for the Google JS Library (Closure).\n *\n * In uncompiled mode base.js will write out Closure's deps file, unless the\n * global <code>CLOSURE_NO_DEPS</code> is set to true.  This allows projects to\n * include their own deps file(s) from different locations.\n *\n * @author arv@google.com (Erik Arvidsson)\n *\n * @provideGoog\n */\n\nvar CLOSURE_NO_DEPS = true;\n\n/**\n * @define {boolean} Overridden to true by the compiler when\n *     --process_closure_primitives is specified.\n */\nvar COMPILED = false;\n\n\n/**\n * Base namespace for the Closure library.  Checks to see goog is already\n * defined in the current scope before assigning to prevent clobbering if\n * base.js is loaded more than once.\n *\n * @const\n */\nvar goog = goog || {};\n\n\n/**\n * Reference to the global context.  In most cases this will be 'window'.\n */\ngoog.global = this;\n\n\n/**\n * A hook for overriding the define values in uncompiled mode.\n *\n * In uncompiled mode, {@code CLOSURE_UNCOMPILED_DEFINES} may be defined before\n * loading base.js.  If a key is defined in {@code CLOSURE_UNCOMPILED_DEFINES},\n * {@code goog.define} will use the value instead of the default value.  This\n * allows flags to be overwritten without compilation (this is normally\n * accomplished with the compiler's \"define\" flag).\n *\n * Example:\n * <pre>\n *   var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false};\n * </pre>\n *\n * @type {Object<string, (string|number|boolean)>|undefined}\n */\ngoog.global.CLOSURE_UNCOMPILED_DEFINES;\n\n\n/**\n * A hook for overriding the define values in uncompiled or compiled mode,\n * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code.  In\n * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence.\n *\n * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or\n * string literals or the compiler will emit an error.\n *\n * While any @define value may be set, only those set with goog.define will be\n * effective for uncompiled code.\n *\n * Example:\n * <pre>\n *   var CLOSURE_DEFINES = {'goog.DEBUG': false} ;\n * </pre>\n *\n * @type {Object<string, (string|number|boolean)>|undefined}\n */\ngoog.global.CLOSURE_DEFINES;\n\n\n/**\n * Returns true if the specified value is not undefined.\n * WARNING: Do not use this to test if an object has a property. Use the in\n * operator instead.\n *\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is defined.\n */\ngoog.isDef = function(val) {\n  // void 0 always evaluates to undefined and hence we do not need to depend on\n  // the definition of the global variable named 'undefined'.\n  return val !== void 0;\n};\n\n\n/**\n * Builds an object structure for the provided namespace path, ensuring that\n * names that already exist are not overwritten. For example:\n * \"a.b.c\" -> a = {};a.b={};a.b.c={};\n * Used by goog.provide and goog.exportSymbol.\n * @param {string} name name of the object that this file defines.\n * @param {*=} opt_object the object to expose at the end of the path.\n * @param {Object=} opt_objectToExportTo The object to add the path to; default\n *     is |goog.global|.\n * @private\n */\ngoog.exportPath_ = function(name, opt_object, opt_objectToExportTo) {\n  var parts = name.split('.');\n  var cur = opt_objectToExportTo || goog.global;\n\n  // Internet Explorer exhibits strange behavior when throwing errors from\n  // methods externed in this manner.  See the testExportSymbolExceptions in\n  // base_test.html for an example.\n  if (!(parts[0] in cur) && cur.execScript) {\n    cur.execScript('var ' + parts[0]);\n  }\n\n  // Certain browsers cannot parse code in the form for((a in b); c;);\n  // This pattern is produced by the JSCompiler when it collapses the\n  // statement above into the conditional loop below. To prevent this from\n  // happening, use a for-loop and reserve the init logic as below.\n\n  // Parentheses added to eliminate strict JS warning in Firefox.\n  for (var part; parts.length && (part = parts.shift());) {\n    if (!parts.length && goog.isDef(opt_object)) {\n      // last part and we have an object; use it\n      cur[part] = opt_object;\n    } else if (cur[part]) {\n      cur = cur[part];\n    } else {\n      cur = cur[part] = {};\n    }\n  }\n};\n\n\n/**\n * Defines a named value. In uncompiled mode, the value is retrieved from\n * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and\n * has the property specified, and otherwise used the defined defaultValue.\n * When compiled the default can be overridden using the compiler\n * options or the value set in the CLOSURE_DEFINES object.\n *\n * @param {string} name The distinguished name to provide.\n * @param {string|number|boolean} defaultValue\n */\ngoog.define = function(name, defaultValue) {\n  var value = defaultValue;\n  if (!COMPILED) {\n    if (goog.global.CLOSURE_UNCOMPILED_DEFINES &&\n        Object.prototype.hasOwnProperty.call(\n            goog.global.CLOSURE_UNCOMPILED_DEFINES, name)) {\n      value = goog.global.CLOSURE_UNCOMPILED_DEFINES[name];\n    } else if (goog.global.CLOSURE_DEFINES &&\n        Object.prototype.hasOwnProperty.call(\n            goog.global.CLOSURE_DEFINES, name)) {\n      value = goog.global.CLOSURE_DEFINES[name];\n    }\n  }\n  goog.exportPath_(name, value);\n};\n\n\n/**\n * @define {boolean} DEBUG is provided as a convenience so that debugging code\n * that should not be included in a production js_binary can be easily stripped\n * by specifying --define goog.DEBUG=false to the JSCompiler. For example, most\n * toString() methods should be declared inside an \"if (goog.DEBUG)\" conditional\n * because they are generally used for debugging purposes and it is difficult\n * for the JSCompiler to statically determine whether they are used.\n */\ngoog.define('goog.DEBUG', true);\n\n\n/**\n * @define {string} LOCALE defines the locale being used for compilation. It is\n * used to select locale specific data to be compiled in js binary. BUILD rule\n * can specify this value by \"--define goog.LOCALE=<locale_name>\" as JSCompiler\n * option.\n *\n * Take into account that the locale code format is important. You should use\n * the canonical Unicode format with hyphen as a delimiter. Language must be\n * lowercase, Language Script - Capitalized, Region - UPPERCASE.\n * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN.\n *\n * See more info about locale codes here:\n * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers\n *\n * For language codes you should use values defined by ISO 693-1. See it here\n * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from\n * this rule: the Hebrew language. For legacy reasons the old code (iw) should\n * be used instead of the new code (he), see http://wiki/Main/IIISynonyms.\n */\ngoog.define('goog.LOCALE', 'en');  // default to en\n\n\n/**\n * @define {boolean} Whether this code is running on trusted sites.\n *\n * On untrusted sites, several native functions can be defined or overridden by\n * external libraries like Prototype, Datejs, and JQuery and setting this flag\n * to false forces closure to use its own implementations when possible.\n *\n * If your JavaScript can be loaded by a third party site and you are wary about\n * relying on non-standard implementations, specify\n * \"--define goog.TRUSTED_SITE=false\" to the JSCompiler.\n */\ngoog.define('goog.TRUSTED_SITE', true);\n\n\n/**\n * @define {boolean} Whether a project is expected to be running in strict mode.\n *\n * This define can be used to trigger alternate implementations compatible with\n * running in EcmaScript Strict mode or warn about unavailable functionality.\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode\n *\n */\ngoog.define('goog.STRICT_MODE_COMPATIBLE', false);\n\n\n/**\n * @define {boolean} Whether code that calls {@link goog.setTestOnly} should\n *     be disallowed in the compilation unit.\n */\ngoog.define('goog.DISALLOW_TEST_ONLY_CODE', COMPILED && !goog.DEBUG);\n\n\n/**\n * @define {boolean} Whether to use a Chrome app CSP-compliant method for\n *     loading scripts via goog.require. @see appendScriptSrcNode_.\n */\ngoog.define('goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING', false);\n\n\n/**\n * Defines a namespace in Closure.\n *\n * A namespace may only be defined once in a codebase. It may be defined using\n * goog.provide() or goog.module().\n *\n * The presence of one or more goog.provide() calls in a file indicates\n * that the file defines the given objects/namespaces.\n * Provided symbols must not be null or undefined.\n *\n * In addition, goog.provide() creates the object stubs for a namespace\n * (for example, goog.provide(\"goog.foo.bar\") will create the object\n * goog.foo.bar if it does not already exist).\n *\n * Build tools also scan for provide/require/module statements\n * to discern dependencies, build dependency files (see deps.js), etc.\n *\n * @see goog.require\n * @see goog.module\n * @param {string} name Namespace provided by this file in the form\n *     \"goog.package.part\".\n */\ngoog.provide = function(name) {\n  if (!COMPILED) {\n    // Ensure that the same namespace isn't provided twice.\n    // A goog.module/goog.provide maps a goog.require to a specific file\n    if (goog.isProvided_(name)) {\n      throw Error('Namespace \"' + name + '\" already declared.');\n    }\n  }\n\n  goog.constructNamespace_(name);\n};\n\n\n/**\n * @param {string} name Namespace provided by this file in the form\n *     \"goog.package.part\".\n * @param {Object=} opt_obj The object to embed in the namespace.\n * @private\n */\ngoog.constructNamespace_ = function(name, opt_obj) {\n  if (!COMPILED) {\n    delete goog.implicitNamespaces_[name];\n\n    var namespace = name;\n    while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) {\n      if (goog.getObjectByName(namespace)) {\n        break;\n      }\n      goog.implicitNamespaces_[namespace] = true;\n    }\n  }\n\n  goog.exportPath_(name, opt_obj);\n};\n\n\n/**\n * Module identifier validation regexp.\n * Note: This is a conservative check, it is very possible to be more lenient,\n *   the primary exclusion here is \"/\" and \"\\\" and a leading \".\", these\n *   restrictions are intended to leave the door open for using goog.require\n *   with relative file paths rather than module identifiers.\n * @private\n */\ngoog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/;\n\n\n/**\n * Defines a module in Closure.\n *\n * Marks that this file must be loaded as a module and claims the namespace.\n *\n * A namespace may only be defined once in a codebase. It may be defined using\n * goog.provide() or goog.module().\n *\n * goog.module() has three requirements:\n * - goog.module may not be used in the same file as goog.provide.\n * - goog.module must be the first statement in the file.\n * - only one goog.module is allowed per file.\n *\n * When a goog.module annotated file is loaded, it is enclosed in\n * a strict function closure. This means that:\n * - any variables declared in a goog.module file are private to the file\n * (not global), though the compiler is expected to inline the module.\n * - The code must obey all the rules of \"strict\" JavaScript.\n * - the file will be marked as \"use strict\"\n *\n * NOTE: unlike goog.provide, goog.module does not declare any symbols by\n * itself. If declared symbols are desired, use\n * goog.module.declareLegacyNamespace().\n *\n *\n * See the public goog.module proposal: http://goo.gl/Va1hin\n *\n * @param {string} name Namespace provided by this file in the form\n *     \"goog.package.part\", is expected but not required.\n */\ngoog.module = function(name) {\n  if (!goog.isString(name) ||\n      !name ||\n      name.search(goog.VALID_MODULE_RE_) == -1) {\n    throw Error('Invalid module identifier');\n  }\n  if (!goog.isInModuleLoader_()) {\n    throw Error('Module ' + name + ' has been loaded incorrectly.');\n  }\n  if (goog.moduleLoaderState_.moduleName) {\n    throw Error('goog.module may only be called once per module.');\n  }\n\n  // Store the module name for the loader.\n  goog.moduleLoaderState_.moduleName = name;\n  if (!COMPILED) {\n    // Ensure that the same namespace isn't provided twice.\n    // A goog.module/goog.provide maps a goog.require to a specific file\n    if (goog.isProvided_(name)) {\n      throw Error('Namespace \"' + name + '\" already declared.');\n    }\n    delete goog.implicitNamespaces_[name];\n  }\n};\n\n\n/**\n * @param {string} name The module identifier.\n * @return {?} The module exports for an already loaded module or null.\n *\n * Note: This is not an alternative to goog.require, it does not\n * indicate a hard dependency, instead it is used to indicate\n * an optional dependency or to access the exports of a module\n * that has already been loaded.\n * @suppress {missingProvide}\n */\ngoog.module.get = function(name) {\n  return goog.module.getInternal_(name);\n};\n\n\n/**\n * @param {string} name The module identifier.\n * @return {?} The module exports for an already loaded module or null.\n * @private\n */\ngoog.module.getInternal_ = function(name) {\n  if (!COMPILED) {\n    if (goog.isProvided_(name)) {\n      // goog.require only return a value with-in goog.module files.\n      return name in goog.loadedModules_ ?\n          goog.loadedModules_[name] :\n          goog.getObjectByName(name);\n    } else {\n      return null;\n    }\n  }\n};\n\n\n/**\n * @private {?{moduleName: (string|undefined), declareLegacyNamespace:boolean}}\n */\ngoog.moduleLoaderState_ = null;\n\n\n/**\n * @private\n * @return {boolean} Whether a goog.module is currently being initialized.\n */\ngoog.isInModuleLoader_ = function() {\n  return goog.moduleLoaderState_ != null;\n};\n\n\n/**\n * Provide the module's exports as a globally accessible object under the\n * module's declared name.  This is intended to ease migration to goog.module\n * for files that have existing usages.\n * @suppress {missingProvide}\n */\ngoog.module.declareLegacyNamespace = function() {\n  if (!COMPILED && !goog.isInModuleLoader_()) {\n    throw new Error('goog.module.declareLegacyNamespace must be called from ' +\n        'within a goog.module');\n  }\n  if (!COMPILED && !goog.moduleLoaderState_.moduleName) {\n    throw Error('goog.module must be called prior to ' +\n        'goog.module.declareLegacyNamespace.');\n  }\n  goog.moduleLoaderState_.declareLegacyNamespace = true;\n};\n\n\n/**\n * Marks that the current file should only be used for testing, and never for\n * live code in production.\n *\n * In the case of unit tests, the message may optionally be an exact namespace\n * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra\n * provide (if not explicitly defined in the code).\n *\n * @param {string=} opt_message Optional message to add to the error that's\n *     raised when used in production code.\n */\ngoog.setTestOnly = function(opt_message) {\n  if (goog.DISALLOW_TEST_ONLY_CODE) {\n    opt_message = opt_message || '';\n    throw Error('Importing test-only code into non-debug environment' +\n                (opt_message ? ': ' + opt_message : '.'));\n  }\n};\n\n\n/**\n * Forward declares a symbol. This is an indication to the compiler that the\n * symbol may be used in the source yet is not required and may not be provided\n * in compilation.\n *\n * The most common usage of forward declaration is code that takes a type as a\n * function parameter but does not need to require it. By forward declaring\n * instead of requiring, no hard dependency is made, and (if not required\n * elsewhere) the namespace may never be required and thus, not be pulled\n * into the JavaScript binary. If it is required elsewhere, it will be type\n * checked as normal.\n *\n *\n * @param {string} name The namespace to forward declare in the form of\n *     \"goog.package.part\".\n */\ngoog.forwardDeclare = function(name) {};\n\n\n/**\n * Forward declare type information. Used to assign types to goog.global\n * referenced object that would otherwise result in unknown type references\n * and thus block property disambiguation.\n */\ngoog.forwardDeclare('Document');\ngoog.forwardDeclare('HTMLScriptElement');\ngoog.forwardDeclare('XMLHttpRequest');\n\n\nif (!COMPILED) {\n\n  /**\n   * Check if the given name has been goog.provided. This will return false for\n   * names that are available only as implicit namespaces.\n   * @param {string} name name of the object to look for.\n   * @return {boolean} Whether the name has been provided.\n   * @private\n   */\n  goog.isProvided_ = function(name) {\n    return (name in goog.loadedModules_) ||\n        (!goog.implicitNamespaces_[name] &&\n            goog.isDefAndNotNull(goog.getObjectByName(name)));\n  };\n\n  /**\n   * Namespaces implicitly defined by goog.provide. For example,\n   * goog.provide('goog.events.Event') implicitly declares that 'goog' and\n   * 'goog.events' must be namespaces.\n   *\n   * @type {!Object<string, (boolean|undefined)>}\n   * @private\n   */\n  goog.implicitNamespaces_ = {'goog.module': true};\n\n  // NOTE: We add goog.module as an implicit namespace as goog.module is defined\n  // here and because the existing module package has not been moved yet out of\n  // the goog.module namespace. This satisifies both the debug loader and\n  // ahead-of-time dependency management.\n}\n\n\n/**\n * Returns an object based on its fully qualified external name.  The object\n * is not found if null or undefined.  If you are using a compilation pass that\n * renames property names beware that using this function will not find renamed\n * properties.\n *\n * @param {string} name The fully qualified name.\n * @param {Object=} opt_obj The object within which to look; default is\n *     |goog.global|.\n * @return {?} The value (object or primitive) or, if not found, null.\n */\ngoog.getObjectByName = function(name, opt_obj) {\n  var parts = name.split('.');\n  var cur = opt_obj || goog.global;\n  for (var part; part = parts.shift(); ) {\n    if (goog.isDefAndNotNull(cur[part])) {\n      cur = cur[part];\n    } else {\n      return null;\n    }\n  }\n  return cur;\n};\n\n\n/**\n * Globalizes a whole namespace, such as goog or goog.lang.\n *\n * @param {!Object} obj The namespace to globalize.\n * @param {Object=} opt_global The object to add the properties to.\n * @deprecated Properties may be explicitly exported to the global scope, but\n *     this should no longer be done in bulk.\n */\ngoog.globalize = function(obj, opt_global) {\n  var global = opt_global || goog.global;\n  for (var x in obj) {\n    global[x] = obj[x];\n  }\n};\n\n\n/**\n * Adds a dependency from a file to the files it requires.\n * @param {string} relPath The path to the js file.\n * @param {!Array<string>} provides An array of strings with\n *     the names of the objects this file provides.\n * @param {!Array<string>} requires An array of strings with\n *     the names of the objects this file requires.\n * @param {boolean=} opt_isModule Whether this dependency must be loaded as\n *     a module as declared by goog.module.\n */\ngoog.addDependency = function(relPath, provides, requires, opt_isModule) {\n  if (goog.DEPENDENCIES_ENABLED) {\n    var provide, require;\n    var path = relPath.replace(/\\\\/g, '/');\n    var deps = goog.dependencies_;\n    for (var i = 0; provide = provides[i]; i++) {\n      deps.nameToPath[provide] = path;\n      deps.pathIsModule[path] = !!opt_isModule;\n    }\n    for (var j = 0; require = requires[j]; j++) {\n      if (!(path in deps.requires)) {\n        deps.requires[path] = {};\n      }\n      deps.requires[path][require] = true;\n    }\n  }\n};\n\n\n\n\n// NOTE(nnaze): The debug DOM loader was included in base.js as an original way\n// to do \"debug-mode\" development.  The dependency system can sometimes be\n// confusing, as can the debug DOM loader's asynchronous nature.\n//\n// With the DOM loader, a call to goog.require() is not blocking -- the script\n// will not load until some point after the current script.  If a namespace is\n// needed at runtime, it needs to be defined in a previous script, or loaded via\n// require() with its registered dependencies.\n//\n// User-defined namespaces may need their own deps file. For a reference on\n// creating a deps file, see:\n// Externally: https://developers.google.com/closure/library/docs/depswriter\n//\n// Because of legacy clients, the DOM loader can't be easily removed from\n// base.js.  Work is being done to make it disableable or replaceable for\n// different environments (DOM-less JavaScript interpreters like Rhino or V8,\n// for example). See bootstrap/ for more information.\n\n\n/**\n * @define {boolean} Whether to enable the debug loader.\n *\n * If enabled, a call to goog.require() will attempt to load the namespace by\n * appending a script tag to the DOM (if the namespace has been registered).\n *\n * If disabled, goog.require() will simply assert that the namespace has been\n * provided (and depend on the fact that some outside tool correctly ordered\n * the script).\n */\ngoog.define('goog.ENABLE_DEBUG_LOADER', true);\n\n\n/**\n * @param {string} msg\n * @private\n */\ngoog.logToConsole_ = function(msg) {\n  if (goog.global.console) {\n    goog.global.console['error'](msg);\n  }\n};\n\n\n/**\n * Implements a system for the dynamic resolution of dependencies that works in\n * parallel with the BUILD system. Note that all calls to goog.require will be\n * stripped by the JSCompiler when the --process_closure_primitives option is\n * used.\n * @see goog.provide\n * @param {string} name Namespace to include (as was given in goog.provide()) in\n *     the form \"goog.package.part\".\n * @return {?} If called within a goog.module file, the associated namespace or\n *     module otherwise null.\n */\ngoog.require = function(name) {\n  // If the object already exists we do not need do do anything.\n  if (!COMPILED) {\n    if (goog.ENABLE_DEBUG_LOADER && goog.IS_OLD_IE_) {\n      goog.maybeProcessDeferredDep_(name);\n    }\n\n    if (goog.isProvided_(name)) {\n      if (goog.isInModuleLoader_()) {\n        return goog.module.getInternal_(name);\n      } else {\n        return null;\n      }\n    }\n\n    if (goog.ENABLE_DEBUG_LOADER) {\n      var path = goog.getPathFromDeps_(name);\n      if (path) {\n        goog.writeScripts_(path);\n        return null;\n      }\n    }\n\n    var errorMessage = 'goog.require could not find: ' + name;\n    goog.logToConsole_(errorMessage);\n\n    throw Error(errorMessage);\n  }\n};\n\n\n/**\n * Path for included scripts.\n * @type {string}\n */\ngoog.basePath = '';\n\n\n/**\n * A hook for overriding the base path.\n * @type {string|undefined}\n */\ngoog.global.CLOSURE_BASE_PATH;\n\n\n/**\n * Whether to write out Closure's deps file. By default, the deps are written.\n * @type {boolean|undefined}\n */\ngoog.global.CLOSURE_NO_DEPS;\n\n\n/**\n * A function to import a single script. This is meant to be overridden when\n * Closure is being run in non-HTML contexts, such as web workers. It's defined\n * in the global scope so that it can be set before base.js is loaded, which\n * allows deps.js to be imported properly.\n *\n * The function is passed the script source, which is a relative URI. It should\n * return true if the script was imported, false otherwise.\n * @type {(function(string): boolean)|undefined}\n */\ngoog.global.CLOSURE_IMPORT_SCRIPT;\n\n\n/**\n * Null function used for default values of callbacks, etc.\n * @return {void} Nothing.\n */\ngoog.nullFunction = function() {};\n\n\n/**\n * When defining a class Foo with an abstract method bar(), you can do:\n * Foo.prototype.bar = goog.abstractMethod\n *\n * Now if a subclass of Foo fails to override bar(), an error will be thrown\n * when bar() is invoked.\n *\n * Note: This does not take the name of the function to override as an argument\n * because that would make it more difficult to obfuscate our JavaScript code.\n *\n * @type {!Function}\n * @throws {Error} when invoked to indicate the method should be overridden.\n */\ngoog.abstractMethod = function() {\n  throw Error('unimplemented abstract method');\n};\n\n\n/**\n * Adds a {@code getInstance} static method that always returns the same\n * instance object.\n * @param {!Function} ctor The constructor for the class to add the static\n *     method to.\n */\ngoog.addSingletonGetter = function(ctor) {\n  ctor.getInstance = function() {\n    if (ctor.instance_) {\n      return ctor.instance_;\n    }\n    if (goog.DEBUG) {\n      // NOTE: JSCompiler can't optimize away Array#push.\n      goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor;\n    }\n    return ctor.instance_ = new ctor;\n  };\n};\n\n\n/**\n * All singleton classes that have been instantiated, for testing. Don't read\n * it directly, use the {@code goog.testing.singleton} module. The compiler\n * removes this variable if unused.\n * @type {!Array<!Function>}\n * @private\n */\ngoog.instantiatedSingletons_ = [];\n\n\n/**\n * @define {boolean} Whether to load goog.modules using {@code eval} when using\n * the debug loader.  This provides a better debugging experience as the\n * source is unmodified and can be edited using Chrome Workspaces or similar.\n * However in some environments the use of {@code eval} is banned\n * so we provide an alternative.\n */\ngoog.define('goog.LOAD_MODULE_USING_EVAL', true);\n\n\n/**\n * @define {boolean} Whether the exports of goog.modules should be sealed when\n * possible.\n */\ngoog.define('goog.SEAL_MODULE_EXPORTS', goog.DEBUG);\n\n\n/**\n * The registry of initialized modules:\n * the module identifier to module exports map.\n * @private @const {!Object<string, ?>}\n */\ngoog.loadedModules_ = {};\n\n\n/**\n * True if goog.dependencies_ is available.\n * @const {boolean}\n */\ngoog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER;\n\n\nif (goog.DEPENDENCIES_ENABLED) {\n\n  /**\n   * This object is used to keep track of dependencies and other data that is\n   * used for loading scripts.\n   * @private\n   * @type {{\n   *   pathIsModule: !Object<string, boolean>,\n   *   nameToPath: !Object<string, string>,\n   *   requires: !Object<string, !Object<string, boolean>>,\n   *   visited: !Object<string, boolean>,\n   *   written: !Object<string, boolean>,\n   *   deferred: !Object<string, string>\n   * }}\n   */\n  goog.dependencies_ = {\n    pathIsModule: {}, // 1 to 1\n\n    nameToPath: {}, // 1 to 1\n\n    requires: {}, // 1 to many\n\n    // Used when resolving dependencies to prevent us from visiting file twice.\n    visited: {},\n\n    written: {}, // Used to keep track of script files we have written.\n\n    deferred: {} // Used to track deferred module evaluations in old IEs\n  };\n\n\n  /**\n   * Tries to detect whether is in the context of an HTML document.\n   * @return {boolean} True if it looks like HTML document.\n   * @private\n   */\n  goog.inHtmlDocument_ = function() {\n    /** @type {Document} */\n    var doc = goog.global.document;\n    return doc != null && 'write' in doc;  // XULDocument misses write.\n  };\n\n\n  /**\n   * Tries to detect the base path of base.js script that bootstraps Closure.\n   * @private\n   */\n  goog.findBasePath_ = function() {\n    if (goog.isDef(goog.global.CLOSURE_BASE_PATH)) {\n      goog.basePath = goog.global.CLOSURE_BASE_PATH;\n      return;\n    } else if (!goog.inHtmlDocument_()) {\n      return;\n    }\n    /** @type {Document} */\n    var doc = goog.global.document;\n    var scripts = doc.getElementsByTagName('SCRIPT');\n    // Search backwards since the current script is in almost all cases the one\n    // that has base.js.\n    for (var i = scripts.length - 1; i >= 0; --i) {\n      var script = /** @type {!HTMLScriptElement} */ (scripts[i]);\n      var src = script.src;\n      var qmark = src.lastIndexOf('?');\n      var l = qmark == -1 ? src.length : qmark;\n      if (src.substr(l - 7, 7) == 'base.js') {\n        goog.basePath = src.substr(0, l - 7);\n        return;\n      }\n    }\n  };\n\n\n  /**\n   * Imports a script if, and only if, that script hasn't already been imported.\n   * (Must be called at execution time)\n   * @param {string} src Script source.\n   * @param {string=} opt_sourceText The optionally source text to evaluate\n   * @private\n   */\n  goog.importScript_ = function(src, opt_sourceText) {\n    var importScript = goog.global.CLOSURE_IMPORT_SCRIPT ||\n        goog.writeScriptTag_;\n    if (importScript(src, opt_sourceText)) {\n      goog.dependencies_.written[src] = true;\n    }\n  };\n\n\n  /** @const @private {boolean} */\n  goog.IS_OLD_IE_ = !!(!goog.global.atob && goog.global.document &&\n      goog.global.document.all);\n\n\n  /**\n   * Given a URL initiate retrieval and execution of the module.\n   * @param {string} src Script source URL.\n   * @private\n   */\n  goog.importModule_ = function(src) {\n    // In an attempt to keep browsers from timing out loading scripts using\n    // synchronous XHRs, put each load in its own script block.\n    var bootstrap = 'goog.retrieveAndExecModule_(\"' + src + '\");';\n\n    if (goog.importScript_('', bootstrap)) {\n      goog.dependencies_.written[src] = true;\n    }\n  };\n\n\n  /** @private {!Array<string>} */\n  goog.queuedModules_ = [];\n\n\n  /**\n   * Return an appropriate module text. Suitable to insert into\n   * a script tag (that is unescaped).\n   * @param {string} srcUrl\n   * @param {string} scriptText\n   * @return {string}\n   * @private\n   */\n  goog.wrapModule_ = function(srcUrl, scriptText) {\n    if (!goog.LOAD_MODULE_USING_EVAL || !goog.isDef(goog.global.JSON)) {\n      return '' +\n          'goog.loadModule(function(exports) {' +\n          '\"use strict\";' +\n          scriptText +\n          '\\n' + // terminate any trailing single line comment.\n          ';return exports' +\n          '});' +\n          '\\n//# sourceURL=' + srcUrl + '\\n';\n    } else {\n      return '' +\n          'goog.loadModule(' +\n          goog.global.JSON.stringify(\n              scriptText + '\\n//# sourceURL=' + srcUrl + '\\n') +\n          ');';\n    }\n  };\n\n  // On IE9 and earlier, it is necessary to handle\n  // deferred module loads. In later browsers, the\n  // code to be evaluated is simply inserted as a script\n  // block in the correct order. To eval deferred\n  // code at the right time, we piggy back on goog.require to call\n  // goog.maybeProcessDeferredDep_.\n  //\n  // The goog.requires are used both to bootstrap\n  // the loading process (when no deps are available) and\n  // declare that they should be available.\n  //\n  // Here we eval the sources, if all the deps are available\n  // either already eval'd or goog.require'd.  This will\n  // be the case when all the dependencies have already\n  // been loaded, and the dependent module is loaded.\n  //\n  // But this alone isn't sufficient because it is also\n  // necessary to handle the case where there is no root\n  // that is not deferred.  For that there we register for an event\n  // and trigger goog.loadQueuedModules_ handle any remaining deferred\n  // evaluations.\n\n  /**\n   * Handle any remaining deferred goog.module evals.\n   * @private\n   */\n  goog.loadQueuedModules_ = function() {\n    var count = goog.queuedModules_.length;\n    if (count > 0) {\n      var queue = goog.queuedModules_;\n      goog.queuedModules_ = [];\n      for (var i = 0; i < count; i++) {\n        var path = queue[i];\n        goog.maybeProcessDeferredPath_(path);\n      }\n    }\n  };\n\n\n  /**\n   * Eval the named module if its dependencies are\n   * available.\n   * @param {string} name The module to load.\n   * @private\n   */\n  goog.maybeProcessDeferredDep_ = function(name) {\n    if (goog.isDeferredModule_(name) &&\n        goog.allDepsAreAvailable_(name)) {\n      var path = goog.getPathFromDeps_(name);\n      goog.maybeProcessDeferredPath_(goog.basePath + path);\n    }\n  };\n\n  /**\n   * @param {string} name The module to check.\n   * @return {boolean} Whether the name represents a\n   *     module whose evaluation has been deferred.\n   * @private\n   */\n  goog.isDeferredModule_ = function(name) {\n    var path = goog.getPathFromDeps_(name);\n    if (path && goog.dependencies_.pathIsModule[path]) {\n      var abspath = goog.basePath + path;\n      return (abspath) in goog.dependencies_.deferred;\n    }\n    return false;\n  };\n\n  /**\n   * @param {string} name The module to check.\n   * @return {boolean} Whether the name represents a\n   *     module whose declared dependencies have all been loaded\n   *     (eval'd or a deferred module load)\n   * @private\n   */\n  goog.allDepsAreAvailable_ = function(name) {\n    var path = goog.getPathFromDeps_(name);\n    if (path && (path in goog.dependencies_.requires)) {\n      for (var requireName in goog.dependencies_.requires[path]) {\n        if (!goog.isProvided_(requireName) &&\n            !goog.isDeferredModule_(requireName)) {\n          return false;\n        }\n      }\n    }\n    return true;\n  };\n\n\n  /**\n   * @param {string} abspath\n   * @private\n   */\n  goog.maybeProcessDeferredPath_ = function(abspath) {\n    if (abspath in goog.dependencies_.deferred) {\n      var src = goog.dependencies_.deferred[abspath];\n      delete goog.dependencies_.deferred[abspath];\n      goog.globalEval(src);\n    }\n  };\n\n\n  /**\n   * Load a goog.module from the provided URL.  This is not a general purpose\n   * code loader and does not support late loading code, that is it should only\n   * be used during page load. This method exists to support unit tests and\n   * \"debug\" loaders that would otherwise have inserted script tags. Under the\n   * hood this needs to use a synchronous XHR and is not recommeneded for\n   * production code.\n   *\n   * The module's goog.requires must have already been satisified; an exception\n   * will be thrown if this is not the case. This assumption is that no\n   * \"deps.js\" file exists, so there is no way to discover and locate the\n   * module-to-be-loaded's dependencies and no attempt is made to do so.\n   *\n   * There should only be one attempt to load a module.  If\n   * \"goog.loadModuleFromUrl\" is called for an already loaded module, an\n   * exception will be throw.\n   *\n   * @param {string} url The URL from which to attempt to load the goog.module.\n   */\n  goog.loadModuleFromUrl = function(url) {\n    // Because this executes synchronously, we don't need to do any additional\n    // bookkeeping. When \"goog.loadModule\" the namespace will be marked as\n    // having been provided which is sufficient.\n    goog.retrieveAndExecModule_(url);\n  };\n\n\n  /**\n   * @param {function(?):?|string} moduleDef The module definition.\n   */\n  goog.loadModule = function(moduleDef) {\n    // NOTE: we allow function definitions to be either in the from\n    // of a string to eval (which keeps the original source intact) or\n    // in a eval forbidden environment (CSP) we allow a function definition\n    // which in its body must call {@code goog.module}, and return the exports\n    // of the module.\n    var previousState = goog.moduleLoaderState_;\n    try {\n      goog.moduleLoaderState_ = {\n        moduleName: undefined,\n        declareLegacyNamespace: false\n      };\n      var exports;\n      if (goog.isFunction(moduleDef)) {\n        exports = moduleDef.call(goog.global, {});\n      } else if (goog.isString(moduleDef)) {\n        exports = goog.loadModuleFromSource_.call(goog.global, moduleDef);\n      } else {\n        throw Error('Invalid module definition');\n      }\n\n      var moduleName = goog.moduleLoaderState_.moduleName;\n      if (!goog.isString(moduleName) || !moduleName) {\n        throw Error('Invalid module name \\\"' + moduleName + '\\\"');\n      }\n\n      // Don't seal legacy namespaces as they may be uses as a parent of\n      // another namespace\n      if (goog.moduleLoaderState_.declareLegacyNamespace) {\n        goog.constructNamespace_(moduleName, exports);\n      } else if (goog.SEAL_MODULE_EXPORTS && Object.seal) {\n        Object.seal(exports);\n      }\n\n      goog.loadedModules_[moduleName] = exports;\n    } finally {\n      goog.moduleLoaderState_ = previousState;\n    }\n  };\n\n\n  /**\n   * @private @const {function(string):?}\n   *\n   * The new type inference warns because this function has no formal\n   * parameters, but its jsdoc says that it takes one argument.\n   * (The argument is used via arguments[0], but NTI does not detect this.)\n   * @suppress {newCheckTypes}\n   */\n  goog.loadModuleFromSource_ = function() {\n    // NOTE: we avoid declaring parameters or local variables here to avoid\n    // masking globals or leaking values into the module definition.\n    'use strict';\n    var exports = {};\n    eval(arguments[0]);\n    return exports;\n  };\n\n\n  /**\n   * Writes a new script pointing to {@code src} directly into the DOM.\n   *\n   * NOTE: This method is not CSP-compliant. @see goog.appendScriptSrcNode_ for\n   * the fallback mechanism.\n   *\n   * @param {string} src The script URL.\n   * @private\n   */\n  goog.writeScriptSrcNode_ = function(src) {\n    goog.global.document.write(\n        '<script type=\"text/javascript\" src=\"' + src + '\"></' + 'script>');\n  };\n\n\n  /**\n   * Appends a new script node to the DOM using a CSP-compliant mechanism. This\n   * method exists as a fallback for document.write (which is not allowed in a\n   * strict CSP context, e.g., Chrome apps).\n   *\n   * NOTE: This method is not analogous to using document.write to insert a\n   * <script> tag; specifically, the user agent will execute a script added by\n   * document.write immediately after the current script block finishes\n   * executing, whereas the DOM-appended script node will not be executed until\n   * the entire document is parsed and executed. That is to say, this script is\n   * added to the end of the script execution queue.\n   *\n   * The page must not attempt to call goog.required entities until after the\n   * document has loaded, e.g., in or after the window.onload callback.\n   *\n   * @param {string} src The script URL.\n   * @private\n   */\n  goog.appendScriptSrcNode_ = function(src) {\n    /** @type {Document} */\n    var doc = goog.global.document;\n    var scriptEl = /** @type {HTMLScriptElement} */ (\n        doc.createElement('script'));\n    scriptEl.type = 'text/javascript';\n    scriptEl.src = src;\n    scriptEl.defer = false;\n    scriptEl.async = false;\n    doc.head.appendChild(scriptEl);\n  };\n\n\n  /**\n   * The default implementation of the import function. Writes a script tag to\n   * import the script.\n   *\n   * @param {string} src The script url.\n   * @param {string=} opt_sourceText The optionally source text to evaluate\n   * @return {boolean} True if the script was imported, false otherwise.\n   * @private\n   */\n  goog.writeScriptTag_ = function(src, opt_sourceText) {\n    if (goog.inHtmlDocument_()) {\n      /** @type {!HTMLDocument} */\n      var doc = goog.global.document;\n\n      // If the user tries to require a new symbol after document load,\n      // something has gone terribly wrong. Doing a document.write would\n      // wipe out the page. This does not apply to the CSP-compliant method\n      // of writing script tags.\n      if (!goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING &&\n          doc.readyState == 'complete') {\n        // Certain test frameworks load base.js multiple times, which tries\n        // to write deps.js each time. If that happens, just fail silently.\n        // These frameworks wipe the page between each load of base.js, so this\n        // is OK.\n        var isDeps = /\\bdeps.js$/.test(src);\n        if (isDeps) {\n          return false;\n        } else {\n          throw Error('Cannot write \"' + src + '\" after document load');\n        }\n      }\n\n      var isOldIE = goog.IS_OLD_IE_;\n\n      if (opt_sourceText === undefined) {\n        if (!isOldIE) {\n          if (goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING) {\n            goog.appendScriptSrcNode_(src);\n          } else {\n            goog.writeScriptSrcNode_(src);\n          }\n        } else {\n          var state = \" onreadystatechange='goog.onScriptLoad_(this, \" +\n              ++goog.lastNonModuleScriptIndex_ + \")' \";\n          doc.write(\n              '<script type=\"text/javascript\" src=\"' +\n                  src + '\"' + state + '></' + 'script>');\n        }\n      } else {\n        doc.write(\n            '<script type=\"text/javascript\">' +\n            opt_sourceText +\n            '</' + 'script>');\n      }\n      return true;\n    } else {\n      return false;\n    }\n  };\n\n\n  /** @private {number} */\n  goog.lastNonModuleScriptIndex_ = 0;\n\n\n  /**\n   * A readystatechange handler for legacy IE\n   * @param {!HTMLScriptElement} script\n   * @param {number} scriptIndex\n   * @return {boolean}\n   * @private\n   */\n  goog.onScriptLoad_ = function(script, scriptIndex) {\n    // for now load the modules when we reach the last script,\n    // later allow more inter-mingling.\n    if (script.readyState == 'complete' &&\n        goog.lastNonModuleScriptIndex_ == scriptIndex) {\n      goog.loadQueuedModules_();\n    }\n    return true;\n  };\n\n  /**\n   * Resolves dependencies based on the dependencies added using addDependency\n   * and calls importScript_ in the correct order.\n   * @param {string} pathToLoad The path from which to start discovering\n   *     dependencies.\n   * @private\n   */\n  goog.writeScripts_ = function(pathToLoad) {\n    /** @type {!Array<string>} The scripts we need to write this time. */\n    var scripts = [];\n    var seenScript = {};\n    var deps = goog.dependencies_;\n\n    /** @param {string} path */\n    function visitNode(path) {\n      if (path in deps.written) {\n        return;\n      }\n\n      // We have already visited this one. We can get here if we have cyclic\n      // dependencies.\n      if (path in deps.visited) {\n        return;\n      }\n\n      deps.visited[path] = true;\n\n      if (path in deps.requires) {\n        for (var requireName in deps.requires[path]) {\n          // If the required name is defined, we assume that it was already\n          // bootstrapped by other means.\n          if (!goog.isProvided_(requireName)) {\n            if (requireName in deps.nameToPath) {\n              visitNode(deps.nameToPath[requireName]);\n            } else {\n              throw Error('Undefined nameToPath for ' + requireName);\n            }\n          }\n        }\n      }\n\n      if (!(path in seenScript)) {\n        seenScript[path] = true;\n        scripts.push(path);\n      }\n    }\n\n    visitNode(pathToLoad);\n\n    // record that we are going to load all these scripts.\n    for (var i = 0; i < scripts.length; i++) {\n      var path = scripts[i];\n      goog.dependencies_.written[path] = true;\n    }\n\n    // If a module is loaded synchronously then we need to\n    // clear the current inModuleLoader value, and restore it when we are\n    // done loading the current \"requires\".\n    var moduleState = goog.moduleLoaderState_;\n    goog.moduleLoaderState_ = null;\n\n    for (var i = 0; i < scripts.length; i++) {\n      var path = scripts[i];\n      if (path) {\n        if (!deps.pathIsModule[path]) {\n          goog.importScript_(goog.basePath + path);\n        } else {\n          goog.importModule_(goog.basePath + path);\n        }\n      } else {\n        goog.moduleLoaderState_ = moduleState;\n        throw Error('Undefined script input');\n      }\n    }\n\n    // restore the current \"module loading state\"\n    goog.moduleLoaderState_ = moduleState;\n  };\n\n\n  /**\n   * Looks at the dependency rules and tries to determine the script file that\n   * fulfills a particular rule.\n   * @param {string} rule In the form goog.namespace.Class or project.script.\n   * @return {?string} Url corresponding to the rule, or null.\n   * @private\n   */\n  goog.getPathFromDeps_ = function(rule) {\n    if (rule in goog.dependencies_.nameToPath) {\n      return goog.dependencies_.nameToPath[rule];\n    } else {\n      return null;\n    }\n  };\n\n  goog.findBasePath_();\n\n  // Allow projects to manage the deps files themselves.\n  if (!goog.global.CLOSURE_NO_DEPS) {\n    goog.importScript_(goog.basePath + 'deps.js');\n  }\n}\n\n\n/**\n * Normalize a file path by removing redundant \"..\" and extraneous \".\" file\n * path components.\n * @param {string} path\n * @return {string}\n * @private\n */\ngoog.normalizePath_ = function(path) {\n  var components = path.split('/');\n  var i = 0;\n  while (i < components.length) {\n    if (components[i] == '.') {\n      components.splice(i, 1);\n    } else if (i && components[i] == '..' &&\n        components[i - 1] && components[i - 1] != '..') {\n      components.splice(--i, 2);\n    } else {\n      i++;\n    }\n  }\n  return components.join('/');\n};\n\n\n/**\n * Loads file by synchronous XHR. Should not be used in production environments.\n * @param {string} src Source URL.\n * @return {string} File contents.\n * @private\n */\ngoog.loadFileSync_ = function(src) {\n  if (goog.global.CLOSURE_LOAD_FILE_SYNC) {\n    return goog.global.CLOSURE_LOAD_FILE_SYNC(src);\n  } else {\n    /** @type {XMLHttpRequest} */\n    var xhr = new goog.global['XMLHttpRequest']();\n    xhr.open('get', src, false);\n    xhr.send();\n    return xhr.responseText;\n  }\n};\n\n\n/**\n * Retrieve and execute a module.\n * @param {string} src Script source URL.\n * @private\n */\ngoog.retrieveAndExecModule_ = function(src) {\n  if (!COMPILED) {\n    // The full but non-canonicalized URL for later use.\n    var originalPath = src;\n    // Canonicalize the path, removing any /./ or /../ since Chrome's debugging\n    // console doesn't auto-canonicalize XHR loads as it does <script> srcs.\n    src = goog.normalizePath_(src);\n\n    var importScript = goog.global.CLOSURE_IMPORT_SCRIPT ||\n        goog.writeScriptTag_;\n\n    var scriptText = goog.loadFileSync_(src);\n\n    if (scriptText != null) {\n      var execModuleScript = goog.wrapModule_(src, scriptText);\n      var isOldIE = goog.IS_OLD_IE_;\n      if (isOldIE) {\n        goog.dependencies_.deferred[originalPath] = execModuleScript;\n        goog.queuedModules_.push(originalPath);\n      } else {\n        importScript(src, execModuleScript);\n      }\n    } else {\n      throw new Error('load of ' + src + 'failed');\n    }\n  }\n};\n\n\n//==============================================================================\n// Language Enhancements\n//==============================================================================\n\n\n/**\n * This is a \"fixed\" version of the typeof operator.  It differs from the typeof\n * operator in such a way that null returns 'null' and arrays return 'array'.\n * @param {?} value The value to get the type of.\n * @return {string} The name of the type.\n */\ngoog.typeOf = function(value) {\n  var s = typeof value;\n  if (s == 'object') {\n    if (value) {\n      // Check these first, so we can avoid calling Object.prototype.toString if\n      // possible.\n      //\n      // IE improperly marshals typeof across execution contexts, but a\n      // cross-context object will still return false for \"instanceof Object\".\n      if (value instanceof Array) {\n        return 'array';\n      } else if (value instanceof Object) {\n        return s;\n      }\n\n      // HACK: In order to use an Object prototype method on the arbitrary\n      //   value, the compiler requires the value be cast to type Object,\n      //   even though the ECMA spec explicitly allows it.\n      var className = Object.prototype.toString.call(\n          /** @type {!Object} */ (value));\n      // In Firefox 3.6, attempting to access iframe window objects' length\n      // property throws an NS_ERROR_FAILURE, so we need to special-case it\n      // here.\n      if (className == '[object Window]') {\n        return 'object';\n      }\n\n      // We cannot always use constructor == Array or instanceof Array because\n      // different frames have different Array objects. In IE6, if the iframe\n      // where the array was created is destroyed, the array loses its\n      // prototype. Then dereferencing val.splice here throws an exception, so\n      // we can't use goog.isFunction. Calling typeof directly returns 'unknown'\n      // so that will work. In this case, this function will return false and\n      // most array functions will still work because the array is still\n      // array-like (supports length and []) even though it has lost its\n      // prototype.\n      // Mark Miller noticed that Object.prototype.toString\n      // allows access to the unforgeable [[Class]] property.\n      //  15.2.4.2 Object.prototype.toString ( )\n      //  When the toString method is called, the following steps are taken:\n      //      1. Get the [[Class]] property of this object.\n      //      2. Compute a string value by concatenating the three strings\n      //         \"[object \", Result(1), and \"]\".\n      //      3. Return Result(2).\n      // and this behavior survives the destruction of the execution context.\n      if ((className == '[object Array]' ||\n           // In IE all non value types are wrapped as objects across window\n           // boundaries (not iframe though) so we have to do object detection\n           // for this edge case.\n           typeof value.length == 'number' &&\n           typeof value.splice != 'undefined' &&\n           typeof value.propertyIsEnumerable != 'undefined' &&\n           !value.propertyIsEnumerable('splice')\n\n          )) {\n        return 'array';\n      }\n      // HACK: There is still an array case that fails.\n      //     function ArrayImpostor() {}\n      //     ArrayImpostor.prototype = [];\n      //     var impostor = new ArrayImpostor;\n      // this can be fixed by getting rid of the fast path\n      // (value instanceof Array) and solely relying on\n      // (value && Object.prototype.toString.vall(value) === '[object Array]')\n      // but that would require many more function calls and is not warranted\n      // unless closure code is receiving objects from untrusted sources.\n\n      // IE in cross-window calls does not correctly marshal the function type\n      // (it appears just as an object) so we cannot use just typeof val ==\n      // 'function'. However, if the object has a call property, it is a\n      // function.\n      if ((className == '[object Function]' ||\n          typeof value.call != 'undefined' &&\n          typeof value.propertyIsEnumerable != 'undefined' &&\n          !value.propertyIsEnumerable('call'))) {\n        return 'function';\n      }\n\n    } else {\n      return 'null';\n    }\n\n  } else if (s == 'function' && typeof value.call == 'undefined') {\n    // In Safari typeof nodeList returns 'function', and on Firefox typeof\n    // behaves similarly for HTML{Applet,Embed,Object}, Elements and RegExps. We\n    // would like to return object for those and we can detect an invalid\n    // function by making sure that the function object has a call method.\n    return 'object';\n  }\n  return s;\n};\n\n\n/**\n * Returns true if the specified value is null.\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is null.\n */\ngoog.isNull = function(val) {\n  return val === null;\n};\n\n\n/**\n * Returns true if the specified value is defined and not null.\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is defined and not null.\n */\ngoog.isDefAndNotNull = function(val) {\n  // Note that undefined == null.\n  return val != null;\n};\n\n\n/**\n * Returns true if the specified value is an array.\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is an array.\n */\ngoog.isArray = function(val) {\n  return goog.typeOf(val) == 'array';\n};\n\n\n/**\n * Returns true if the object looks like an array. To qualify as array like\n * the value needs to be either a NodeList or an object with a Number length\n * property. As a special case, a function value is not array like, because its\n * length property is fixed to correspond to the number of expected arguments.\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is an array.\n */\ngoog.isArrayLike = function(val) {\n  var type = goog.typeOf(val);\n  // We do not use goog.isObject here in order to exclude function values.\n  return type == 'array' || type == 'object' && typeof val.length == 'number';\n};\n\n\n/**\n * Returns true if the object looks like a Date. To qualify as Date-like the\n * value needs to be an object and have a getFullYear() function.\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is a like a Date.\n */\ngoog.isDateLike = function(val) {\n  return goog.isObject(val) && typeof val.getFullYear == 'function';\n};\n\n\n/**\n * Returns true if the specified value is a string.\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is a string.\n */\ngoog.isString = function(val) {\n  return typeof val == 'string';\n};\n\n\n/**\n * Returns true if the specified value is a boolean.\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is boolean.\n */\ngoog.isBoolean = function(val) {\n  return typeof val == 'boolean';\n};\n\n\n/**\n * Returns true if the specified value is a number.\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is a number.\n */\ngoog.isNumber = function(val) {\n  return typeof val == 'number';\n};\n\n\n/**\n * Returns true if the specified value is a function.\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is a function.\n */\ngoog.isFunction = function(val) {\n  return goog.typeOf(val) == 'function';\n};\n\n\n/**\n * Returns true if the specified value is an object.  This includes arrays and\n * functions.\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is an object.\n */\ngoog.isObject = function(val) {\n  var type = typeof val;\n  return type == 'object' && val != null || type == 'function';\n  // return Object(val) === val also works, but is slower, especially if val is\n  // not an object.\n};\n\n\n/**\n * Gets a unique ID for an object. This mutates the object so that further calls\n * with the same object as a parameter returns the same value. The unique ID is\n * guaranteed to be unique across the current session amongst objects that are\n * passed into {@code getUid}. There is no guarantee that the ID is unique or\n * consistent across sessions. It is unsafe to generate unique ID for function\n * prototypes.\n *\n * @param {Object} obj The object to get the unique ID for.\n * @return {number} The unique ID for the object.\n */\ngoog.getUid = function(obj) {\n  // TODO(arv): Make the type stricter, do not accept null.\n\n  // In Opera window.hasOwnProperty exists but always returns false so we avoid\n  // using it. As a consequence the unique ID generated for BaseClass.prototype\n  // and SubClass.prototype will be the same.\n  return obj[goog.UID_PROPERTY_] ||\n      (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_);\n};\n\n\n/**\n * Whether the given object is already assigned a unique ID.\n *\n * This does not modify the object.\n *\n * @param {!Object} obj The object to check.\n * @return {boolean} Whether there is an assigned unique id for the object.\n */\ngoog.hasUid = function(obj) {\n  return !!obj[goog.UID_PROPERTY_];\n};\n\n\n/**\n * Removes the unique ID from an object. This is useful if the object was\n * previously mutated using {@code goog.getUid} in which case the mutation is\n * undone.\n * @param {Object} obj The object to remove the unique ID field from.\n */\ngoog.removeUid = function(obj) {\n  // TODO(arv): Make the type stricter, do not accept null.\n\n  // In IE, DOM nodes are not instances of Object and throw an exception if we\n  // try to delete.  Instead we try to use removeAttribute.\n  if (obj !== null && 'removeAttribute' in obj) {\n    obj.removeAttribute(goog.UID_PROPERTY_);\n  }\n  /** @preserveTry */\n  try {\n    delete obj[goog.UID_PROPERTY_];\n  } catch (ex) {\n  }\n};\n\n\n/**\n * Name for unique ID property. Initialized in a way to help avoid collisions\n * with other closure JavaScript on the same page.\n * @type {string}\n * @private\n */\ngoog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0);\n\n\n/**\n * Counter for UID.\n * @type {number}\n * @private\n */\ngoog.uidCounter_ = 0;\n\n\n/**\n * Adds a hash code field to an object. The hash code is unique for the\n * given object.\n * @param {Object} obj The object to get the hash code for.\n * @return {number} The hash code for the object.\n * @deprecated Use goog.getUid instead.\n */\ngoog.getHashCode = goog.getUid;\n\n\n/**\n * Removes the hash code field from an object.\n * @param {Object} obj The object to remove the field from.\n * @deprecated Use goog.removeUid instead.\n */\ngoog.removeHashCode = goog.removeUid;\n\n\n/**\n * Clones a value. The input may be an Object, Array, or basic type. Objects and\n * arrays will be cloned recursively.\n *\n * WARNINGS:\n * <code>goog.cloneObject</code> does not detect reference loops. Objects that\n * refer to themselves will cause infinite recursion.\n *\n * <code>goog.cloneObject</code> is unaware of unique identifiers, and copies\n * UIDs created by <code>getUid</code> into cloned results.\n *\n * @param {*} obj The value to clone.\n * @return {*} A clone of the input value.\n * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods.\n */\ngoog.cloneObject = function(obj) {\n  var type = goog.typeOf(obj);\n  if (type == 'object' || type == 'array') {\n    if (obj.clone) {\n      return obj.clone();\n    }\n    var clone = type == 'array' ? [] : {};\n    for (var key in obj) {\n      clone[key] = goog.cloneObject(obj[key]);\n    }\n    return clone;\n  }\n\n  return obj;\n};\n\n\n/**\n * A native implementation of goog.bind.\n * @param {Function} fn A function to partially apply.\n * @param {Object|undefined} selfObj Specifies the object which this should\n *     point to when the function is run.\n * @param {...*} var_args Additional arguments that are partially applied to the\n *     function.\n * @return {!Function} A partially-applied form of the function bind() was\n *     invoked as a method of.\n * @private\n * @suppress {deprecated} The compiler thinks that Function.prototype.bind is\n *     deprecated because some people have declared a pure-JS version.\n *     Only the pure-JS version is truly deprecated.\n */\ngoog.bindNative_ = function(fn, selfObj, var_args) {\n  return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));\n};\n\n\n/**\n * A pure-JS implementation of goog.bind.\n * @param {Function} fn A function to partially apply.\n * @param {Object|undefined} selfObj Specifies the object which this should\n *     point to when the function is run.\n * @param {...*} var_args Additional arguments that are partially applied to the\n *     function.\n * @return {!Function} A partially-applied form of the function bind() was\n *     invoked as a method of.\n * @private\n */\ngoog.bindJs_ = function(fn, selfObj, var_args) {\n  if (!fn) {\n    throw new Error();\n  }\n\n  if (arguments.length > 2) {\n    var boundArgs = Array.prototype.slice.call(arguments, 2);\n    return function() {\n      // Prepend the bound arguments to the current arguments.\n      var newArgs = Array.prototype.slice.call(arguments);\n      Array.prototype.unshift.apply(newArgs, boundArgs);\n      return fn.apply(selfObj, newArgs);\n    };\n\n  } else {\n    return function() {\n      return fn.apply(selfObj, arguments);\n    };\n  }\n};\n\n\n/**\n * Partially applies this function to a particular 'this object' and zero or\n * more arguments. The result is a new function with some arguments of the first\n * function pre-filled and the value of this 'pre-specified'.\n *\n * Remaining arguments specified at call-time are appended to the pre-specified\n * ones.\n *\n * Also see: {@link #partial}.\n *\n * Usage:\n * <pre>var barMethBound = goog.bind(myFunction, myObj, 'arg1', 'arg2');\n * barMethBound('arg3', 'arg4');</pre>\n *\n * @param {?function(this:T, ...)} fn A function to partially apply.\n * @param {T} selfObj Specifies the object which this should point to when the\n *     function is run.\n * @param {...*} var_args Additional arguments that are partially applied to the\n *     function.\n * @return {!Function} A partially-applied form of the function goog.bind() was\n *     invoked as a method of.\n * @template T\n * @suppress {deprecated} See above.\n */\ngoog.bind = function(fn, selfObj, var_args) {\n  // TODO(nicksantos): narrow the type signature.\n  if (Function.prototype.bind &&\n      // NOTE(nicksantos): Somebody pulled base.js into the default Chrome\n      // extension environment. This means that for Chrome extensions, they get\n      // the implementation of Function.prototype.bind that calls goog.bind\n      // instead of the native one. Even worse, we don't want to introduce a\n      // circular dependency between goog.bind and Function.prototype.bind, so\n      // we have to hack this to make sure it works correctly.\n      Function.prototype.bind.toString().indexOf('native code') != -1) {\n    goog.bind = goog.bindNative_;\n  } else {\n    goog.bind = goog.bindJs_;\n  }\n  return goog.bind.apply(null, arguments);\n};\n\n\n/**\n * Like goog.bind(), except that a 'this object' is not required. Useful when\n * the target function is already bound.\n *\n * Usage:\n * var g = goog.partial(f, arg1, arg2);\n * g(arg3, arg4);\n *\n * @param {Function} fn A function to partially apply.\n * @param {...*} var_args Additional arguments that are partially applied to fn.\n * @return {!Function} A partially-applied form of the function goog.partial()\n *     was invoked as a method of.\n */\ngoog.partial = function(fn, var_args) {\n  var args = Array.prototype.slice.call(arguments, 1);\n  return function() {\n    // Clone the array (with slice()) and append additional arguments\n    // to the existing arguments.\n    var newArgs = args.slice();\n    newArgs.push.apply(newArgs, arguments);\n    return fn.apply(this, newArgs);\n  };\n};\n\n\n/**\n * Copies all the members of a source object to a target object. This method\n * does not work on all browsers for all objects that contain keys such as\n * toString or hasOwnProperty. Use goog.object.extend for this purpose.\n * @param {Object} target Target.\n * @param {Object} source Source.\n */\ngoog.mixin = function(target, source) {\n  for (var x in source) {\n    target[x] = source[x];\n  }\n\n  // For IE7 or lower, the for-in-loop does not contain any properties that are\n  // not enumerable on the prototype object (for example, isPrototypeOf from\n  // Object.prototype) but also it will not include 'replace' on objects that\n  // extend String and change 'replace' (not that it is common for anyone to\n  // extend anything except Object).\n};\n\n\n/**\n * @return {number} An integer value representing the number of milliseconds\n *     between midnight, January 1, 1970 and the current time.\n */\ngoog.now = (goog.TRUSTED_SITE && Date.now) || (function() {\n  // Unary plus operator converts its operand to a number which in the case of\n  // a date is done by calling getTime().\n  return +new Date();\n});\n\n\n/**\n * Evals JavaScript in the global scope.  In IE this uses execScript, other\n * browsers use goog.global.eval. If goog.global.eval does not evaluate in the\n * global scope (for example, in Safari), appends a script tag instead.\n * Throws an exception if neither execScript or eval is defined.\n * @param {string} script JavaScript string.\n */\ngoog.globalEval = function(script) {\n  if (goog.global.execScript) {\n    goog.global.execScript(script, 'JavaScript');\n  } else if (goog.global.eval) {\n    // Test to see if eval works\n    if (goog.evalWorksForGlobals_ == null) {\n      goog.global.eval('var _evalTest_ = 1;');\n      if (typeof goog.global['_evalTest_'] != 'undefined') {\n        try {\n          delete goog.global['_evalTest_'];\n        } catch (ignore) {\n          // Microsoft edge fails the deletion above in strict mode.\n        }\n        goog.evalWorksForGlobals_ = true;\n      } else {\n        goog.evalWorksForGlobals_ = false;\n      }\n    }\n\n    if (goog.evalWorksForGlobals_) {\n      goog.global.eval(script);\n    } else {\n      /** @type {Document} */\n      var doc = goog.global.document;\n      var scriptElt = /** @type {!HTMLScriptElement} */ (\n          doc.createElement('SCRIPT'));\n      scriptElt.type = 'text/javascript';\n      scriptElt.defer = false;\n      // Note(user): can't use .innerHTML since \"t('<test>')\" will fail and\n      // .text doesn't work in Safari 2.  Therefore we append a text node.\n      scriptElt.appendChild(doc.createTextNode(script));\n      doc.body.appendChild(scriptElt);\n      doc.body.removeChild(scriptElt);\n    }\n  } else {\n    throw Error('goog.globalEval not available');\n  }\n};\n\n\n/**\n * Indicates whether or not we can call 'eval' directly to eval code in the\n * global scope. Set to a Boolean by the first call to goog.globalEval (which\n * empirically tests whether eval works for globals). @see goog.globalEval\n * @type {?boolean}\n * @private\n */\ngoog.evalWorksForGlobals_ = null;\n\n\n/**\n * Optional map of CSS class names to obfuscated names used with\n * goog.getCssName().\n * @private {!Object<string, string>|undefined}\n * @see goog.setCssNameMapping\n */\ngoog.cssNameMapping_;\n\n\n/**\n * Optional obfuscation style for CSS class names. Should be set to either\n * 'BY_WHOLE' or 'BY_PART' if defined.\n * @type {string|undefined}\n * @private\n * @see goog.setCssNameMapping\n */\ngoog.cssNameMappingStyle_;\n\n\n/**\n * Handles strings that are intended to be used as CSS class names.\n *\n * This function works in tandem with @see goog.setCssNameMapping.\n *\n * Without any mapping set, the arguments are simple joined with a hyphen and\n * passed through unaltered.\n *\n * When there is a mapping, there are two possible styles in which these\n * mappings are used. In the BY_PART style, each part (i.e. in between hyphens)\n * of the passed in css name is rewritten according to the map. In the BY_WHOLE\n * style, the full css name is looked up in the map directly. If a rewrite is\n * not specified by the map, the compiler will output a warning.\n *\n * When the mapping is passed to the compiler, it will replace calls to\n * goog.getCssName with the strings from the mapping, e.g.\n *     var x = goog.getCssName('foo');\n *     var y = goog.getCssName(this.baseClass, 'active');\n *  becomes:\n *     var x = 'foo';\n *     var y = this.baseClass + '-active';\n *\n * If one argument is passed it will be processed, if two are passed only the\n * modifier will be processed, as it is assumed the first argument was generated\n * as a result of calling goog.getCssName.\n *\n * @param {string} className The class name.\n * @param {string=} opt_modifier A modifier to be appended to the class name.\n * @return {string} The class name or the concatenation of the class name and\n *     the modifier.\n */\ngoog.getCssName = function(className, opt_modifier) {\n  var getMapping = function(cssName) {\n    return goog.cssNameMapping_[cssName] || cssName;\n  };\n\n  var renameByParts = function(cssName) {\n    // Remap all the parts individually.\n    var parts = cssName.split('-');\n    var mapped = [];\n    for (var i = 0; i < parts.length; i++) {\n      mapped.push(getMapping(parts[i]));\n    }\n    return mapped.join('-');\n  };\n\n  var rename;\n  if (goog.cssNameMapping_) {\n    rename = goog.cssNameMappingStyle_ == 'BY_WHOLE' ?\n        getMapping : renameByParts;\n  } else {\n    rename = function(a) {\n      return a;\n    };\n  }\n\n  if (opt_modifier) {\n    return className + '-' + rename(opt_modifier);\n  } else {\n    return rename(className);\n  }\n};\n\n\n/**\n * Sets the map to check when returning a value from goog.getCssName(). Example:\n * <pre>\n * goog.setCssNameMapping({\n *   \"goog\": \"a\",\n *   \"disabled\": \"b\",\n * });\n *\n * var x = goog.getCssName('goog');\n * // The following evaluates to: \"a a-b\".\n * goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled')\n * </pre>\n * When declared as a map of string literals to string literals, the JSCompiler\n * will replace all calls to goog.getCssName() using the supplied map if the\n * --process_closure_primitives flag is set.\n *\n * @param {!Object} mapping A map of strings to strings where keys are possible\n *     arguments to goog.getCssName() and values are the corresponding values\n *     that should be returned.\n * @param {string=} opt_style The style of css name mapping. There are two valid\n *     options: 'BY_PART', and 'BY_WHOLE'.\n * @see goog.getCssName for a description.\n */\ngoog.setCssNameMapping = function(mapping, opt_style) {\n  goog.cssNameMapping_ = mapping;\n  goog.cssNameMappingStyle_ = opt_style;\n};\n\n\n/**\n * To use CSS renaming in compiled mode, one of the input files should have a\n * call to goog.setCssNameMapping() with an object literal that the JSCompiler\n * can extract and use to replace all calls to goog.getCssName(). In uncompiled\n * mode, JavaScript code should be loaded before this base.js file that declares\n * a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is\n * to ensure that the mapping is loaded before any calls to goog.getCssName()\n * are made in uncompiled mode.\n *\n * A hook for overriding the CSS name mapping.\n * @type {!Object<string, string>|undefined}\n */\ngoog.global.CLOSURE_CSS_NAME_MAPPING;\n\n\nif (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) {\n  // This does not call goog.setCssNameMapping() because the JSCompiler\n  // requires that goog.setCssNameMapping() be called with an object literal.\n  goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING;\n}\n\n\n/**\n * Gets a localized message.\n *\n * This function is a compiler primitive. If you give the compiler a localized\n * message bundle, it will replace the string at compile-time with a localized\n * version, and expand goog.getMsg call to a concatenated string.\n *\n * Messages must be initialized in the form:\n * <code>\n * var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'});\n * </code>\n *\n * @param {string} str Translatable string, places holders in the form {$foo}.\n * @param {Object<string, string>=} opt_values Maps place holder name to value.\n * @return {string} message with placeholders filled.\n */\ngoog.getMsg = function(str, opt_values) {\n  if (opt_values) {\n    str = str.replace(/\\{\\$([^}]+)}/g, function(match, key) {\n      return (opt_values != null && key in opt_values) ?\n          opt_values[key] : match;\n    });\n  }\n  return str;\n};\n\n\n/**\n * Gets a localized message. If the message does not have a translation, gives a\n * fallback message.\n *\n * This is useful when introducing a new message that has not yet been\n * translated into all languages.\n *\n * This function is a compiler primitive. Must be used in the form:\n * <code>var x = goog.getMsgWithFallback(MSG_A, MSG_B);</code>\n * where MSG_A and MSG_B were initialized with goog.getMsg.\n *\n * @param {string} a The preferred message.\n * @param {string} b The fallback message.\n * @return {string} The best translated message.\n */\ngoog.getMsgWithFallback = function(a, b) {\n  return a;\n};\n\n\n/**\n * Exposes an unobfuscated global namespace path for the given object.\n * Note that fields of the exported object *will* be obfuscated, unless they are\n * exported in turn via this function or goog.exportProperty.\n *\n * Also handy for making public items that are defined in anonymous closures.\n *\n * ex. goog.exportSymbol('public.path.Foo', Foo);\n *\n * ex. goog.exportSymbol('public.path.Foo.staticFunction', Foo.staticFunction);\n *     public.path.Foo.staticFunction();\n *\n * ex. goog.exportSymbol('public.path.Foo.prototype.myMethod',\n *                       Foo.prototype.myMethod);\n *     new public.path.Foo().myMethod();\n *\n * @param {string} publicPath Unobfuscated name to export.\n * @param {*} object Object the name should point to.\n * @param {Object=} opt_objectToExportTo The object to add the path to; default\n *     is goog.global.\n */\ngoog.exportSymbol = function(publicPath, object, opt_objectToExportTo) {\n  goog.exportPath_(publicPath, object, opt_objectToExportTo);\n};\n\n\n/**\n * Exports a property unobfuscated into the object's namespace.\n * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction);\n * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod);\n * @param {Object} object Object whose static property is being exported.\n * @param {string} publicName Unobfuscated name to export.\n * @param {*} symbol Object the name should point to.\n */\ngoog.exportProperty = function(object, publicName, symbol) {\n  object[publicName] = symbol;\n};\n\n\n/**\n * Inherit the prototype methods from one constructor into another.\n *\n * Usage:\n * <pre>\n * function ParentClass(a, b) { }\n * ParentClass.prototype.foo = function(a) { };\n *\n * function ChildClass(a, b, c) {\n *   ChildClass.base(this, 'constructor', a, b);\n * }\n * goog.inherits(ChildClass, ParentClass);\n *\n * var child = new ChildClass('a', 'b', 'see');\n * child.foo(); // This works.\n * </pre>\n *\n * @param {!Function} childCtor Child class.\n * @param {!Function} parentCtor Parent class.\n */\ngoog.inherits = function(childCtor, parentCtor) {\n  /** @constructor */\n  function tempCtor() {}\n  tempCtor.prototype = parentCtor.prototype;\n  childCtor.superClass_ = parentCtor.prototype;\n  childCtor.prototype = new tempCtor();\n  /** @override */\n  childCtor.prototype.constructor = childCtor;\n\n  /**\n   * Calls superclass constructor/method.\n   *\n   * This function is only available if you use goog.inherits to\n   * express inheritance relationships between classes.\n   *\n   * NOTE: This is a replacement for goog.base and for superClass_\n   * property defined in childCtor.\n   *\n   * @param {!Object} me Should always be \"this\".\n   * @param {string} methodName The method name to call. Calling\n   *     superclass constructor can be done with the special string\n   *     'constructor'.\n   * @param {...*} var_args The arguments to pass to superclass\n   *     method/constructor.\n   * @return {*} The return value of the superclass method/constructor.\n   */\n  childCtor.base = function(me, methodName, var_args) {\n    // Copying using loop to avoid deop due to passing arguments object to\n    // function. This is faster in many JS engines as of late 2014.\n    var args = new Array(arguments.length - 2);\n    for (var i = 2; i < arguments.length; i++) {\n      args[i - 2] = arguments[i];\n    }\n    return parentCtor.prototype[methodName].apply(me, args);\n  };\n};\n\n\n/**\n * Call up to the superclass.\n *\n * If this is called from a constructor, then this calls the superclass\n * constructor with arguments 1-N.\n *\n * If this is called from a prototype method, then you must pass the name of the\n * method as the second argument to this function. If you do not, you will get a\n * runtime error. This calls the superclass' method with arguments 2-N.\n *\n * This function only works if you use goog.inherits to express inheritance\n * relationships between your classes.\n *\n * This function is a compiler primitive. At compile-time, the compiler will do\n * macro expansion to remove a lot of the extra overhead that this function\n * introduces. The compiler will also enforce a lot of the assumptions that this\n * function makes, and treat it as a compiler error if you break them.\n *\n * @param {!Object} me Should always be \"this\".\n * @param {*=} opt_methodName The method name if calling a super method.\n * @param {...*} var_args The rest of the arguments.\n * @return {*} The return value of the superclass method.\n * @suppress {es5Strict} This method can not be used in strict mode, but\n *     all Closure Library consumers must depend on this file.\n */\ngoog.base = function(me, opt_methodName, var_args) {\n  var caller = arguments.callee.caller;\n\n  if (goog.STRICT_MODE_COMPATIBLE || (goog.DEBUG && !caller)) {\n    throw Error('arguments.caller not defined.  goog.base() cannot be used ' +\n                'with strict mode code. See ' +\n                'http://www.ecma-international.org/ecma-262/5.1/#sec-C');\n  }\n\n  if (caller.superClass_) {\n    // Copying using loop to avoid deop due to passing arguments object to\n    // function. This is faster in many JS engines as of late 2014.\n    var ctorArgs = new Array(arguments.length - 1);\n    for (var i = 1; i < arguments.length; i++) {\n      ctorArgs[i - 1] = arguments[i];\n    }\n    // This is a constructor. Call the superclass constructor.\n    return caller.superClass_.constructor.apply(me, ctorArgs);\n  }\n\n  // Copying using loop to avoid deop due to passing arguments object to\n  // function. This is faster in many JS engines as of late 2014.\n  var args = new Array(arguments.length - 2);\n  for (var i = 2; i < arguments.length; i++) {\n    args[i - 2] = arguments[i];\n  }\n  var foundCaller = false;\n  for (var ctor = me.constructor;\n       ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) {\n    if (ctor.prototype[opt_methodName] === caller) {\n      foundCaller = true;\n    } else if (foundCaller) {\n      return ctor.prototype[opt_methodName].apply(me, args);\n    }\n  }\n\n  // If we did not find the caller in the prototype chain, then one of two\n  // things happened:\n  // 1) The caller is an instance method.\n  // 2) This method was not called by the right caller.\n  if (me[opt_methodName] === caller) {\n    return me.constructor.prototype[opt_methodName].apply(me, args);\n  } else {\n    throw Error(\n        'goog.base called from a method of one name ' +\n        'to a method of a different name');\n  }\n};\n\n\n/**\n * Allow for aliasing within scope functions.  This function exists for\n * uncompiled code - in compiled code the calls will be inlined and the aliases\n * applied.  In uncompiled code the function is simply run since the aliases as\n * written are valid JavaScript.\n *\n *\n * @param {function()} fn Function to call.  This function can contain aliases\n *     to namespaces (e.g. \"var dom = goog.dom\") or classes\n *     (e.g. \"var Timer = goog.Timer\").\n */\ngoog.scope = function(fn) {\n  fn.call(goog.global);\n};\n\n\n/*\n * To support uncompiled, strict mode bundles that use eval to divide source\n * like so:\n *    eval('someSource;//# sourceUrl sourcefile.js');\n * We need to export the globally defined symbols \"goog\" and \"COMPILED\".\n * Exporting \"goog\" breaks the compiler optimizations, so we required that\n * be defined externally.\n * NOTE: We don't use goog.exportSymbol here because we don't want to trigger\n * extern generation when that compiler option is enabled.\n */\nif (!COMPILED) {\n  goog.global['COMPILED'] = COMPILED;\n}\n\n\n//==============================================================================\n// goog.defineClass implementation\n//==============================================================================\n\n\n/**\n * Creates a restricted form of a Closure \"class\":\n *   - from the compiler's perspective, the instance returned from the\n *     constructor is sealed (no new properties may be added).  This enables\n *     better checks.\n *   - the compiler will rewrite this definition to a form that is optimal\n *     for type checking and optimization (initially this will be a more\n *     traditional form).\n *\n * @param {Function} superClass The superclass, Object or null.\n * @param {goog.defineClass.ClassDescriptor} def\n *     An object literal describing\n *     the class.  It may have the following properties:\n *     \"constructor\": the constructor function\n *     \"statics\": an object literal containing methods to add to the constructor\n *        as \"static\" methods or a function that will receive the constructor\n *        function as its only parameter to which static properties can\n *        be added.\n *     all other properties are added to the prototype.\n * @return {!Function} The class constructor.\n */\ngoog.defineClass = function(superClass, def) {\n  // TODO(johnlenz): consider making the superClass an optional parameter.\n  var constructor = def.constructor;\n  var statics = def.statics;\n  // Wrap the constructor prior to setting up the prototype and static methods.\n  if (!constructor || constructor == Object.prototype.constructor) {\n    constructor = function() {\n      throw Error('cannot instantiate an interface (no constructor defined).');\n    };\n  }\n\n  var cls = goog.defineClass.createSealingConstructor_(constructor, superClass);\n  if (superClass) {\n    goog.inherits(cls, superClass);\n  }\n\n  // Remove all the properties that should not be copied to the prototype.\n  delete def.constructor;\n  delete def.statics;\n\n  goog.defineClass.applyProperties_(cls.prototype, def);\n  if (statics != null) {\n    if (statics instanceof Function) {\n      statics(cls);\n    } else {\n      goog.defineClass.applyProperties_(cls, statics);\n    }\n  }\n\n  return cls;\n};\n\n\n/**\n * @typedef {{\n *   constructor: (!Function|undefined),\n *   statics: (Object|undefined|function(Function):void)\n * }}\n * @suppress {missingProvide}\n */\ngoog.defineClass.ClassDescriptor;\n\n\n/**\n * @define {boolean} Whether the instances returned by\n * goog.defineClass should be sealed when possible.\n */\ngoog.define('goog.defineClass.SEAL_CLASS_INSTANCES', goog.DEBUG);\n\n\n/**\n * If goog.defineClass.SEAL_CLASS_INSTANCES is enabled and Object.seal is\n * defined, this function will wrap the constructor in a function that seals the\n * results of the provided constructor function.\n *\n * @param {!Function} ctr The constructor whose results maybe be sealed.\n * @param {Function} superClass The superclass constructor.\n * @return {!Function} The replacement constructor.\n * @private\n */\ngoog.defineClass.createSealingConstructor_ = function(ctr, superClass) {\n  if (goog.defineClass.SEAL_CLASS_INSTANCES &&\n      Object.seal instanceof Function) {\n    // Don't seal subclasses of unsealable-tagged legacy classes.\n    if (superClass && superClass.prototype &&\n        superClass.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_]) {\n      return ctr;\n    }\n    /**\n     * @this {Object}\n     * @return {?}\n     */\n    var wrappedCtr = function() {\n      // Don't seal an instance of a subclass when it calls the constructor of\n      // its super class as there is most likely still setup to do.\n      var instance = ctr.apply(this, arguments) || this;\n      instance[goog.UID_PROPERTY_] = instance[goog.UID_PROPERTY_];\n      if (this.constructor === wrappedCtr) {\n        Object.seal(instance);\n      }\n      return instance;\n    };\n    return wrappedCtr;\n  }\n  return ctr;\n};\n\n\n// TODO(johnlenz): share these values with the goog.object\n/**\n * The names of the fields that are defined on Object.prototype.\n * @type {!Array<string>}\n * @private\n * @const\n */\ngoog.defineClass.OBJECT_PROTOTYPE_FIELDS_ = [\n  'constructor',\n  'hasOwnProperty',\n  'isPrototypeOf',\n  'propertyIsEnumerable',\n  'toLocaleString',\n  'toString',\n  'valueOf'\n];\n\n\n// TODO(johnlenz): share this function with the goog.object\n/**\n * @param {!Object} target The object to add properties to.\n * @param {!Object} source The object to copy properties from.\n * @private\n */\ngoog.defineClass.applyProperties_ = function(target, source) {\n  // TODO(johnlenz): update this to support ES5 getters/setters\n\n  var key;\n  for (key in source) {\n    if (Object.prototype.hasOwnProperty.call(source, key)) {\n      target[key] = source[key];\n    }\n  }\n\n  // For IE the for-in-loop does not contain any properties that are not\n  // enumerable on the prototype object (for example isPrototypeOf from\n  // Object.prototype) and it will also not include 'replace' on objects that\n  // extend String and change 'replace' (not that it is common for anyone to\n  // extend anything except Object).\n  for (var i = 0; i < goog.defineClass.OBJECT_PROTOTYPE_FIELDS_.length; i++) {\n    key = goog.defineClass.OBJECT_PROTOTYPE_FIELDS_[i];\n    if (Object.prototype.hasOwnProperty.call(source, key)) {\n      target[key] = source[key];\n    }\n  }\n};\n\n\n/**\n * Sealing classes breaks the older idiom of assigning properties on the\n * prototype rather than in the constructor.  As such, goog.defineClass\n * must not seal subclasses of these old-style classes until they are fixed.\n * Until then, this marks a class as \"broken\", instructing defineClass\n * not to seal subclasses.\n * @param {!Function} ctr The legacy constructor to tag as unsealable.\n */\ngoog.tagUnsealableClass = function(ctr) {\n  if (!COMPILED && goog.defineClass.SEAL_CLASS_INSTANCES) {\n    ctr.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_] = true;\n  }\n};\n\n\n/**\n * Name for unsealable tag property.\n * @const @private {string}\n */\ngoog.UNSEALABLE_CONSTRUCTOR_PROPERTY_ = 'goog_defineClass_legacy_unsealable';\n"
  },
  {
    "path": "tools/jasmine/MIT.LICENSE",
    "content": "Copyright (c) 2008-2011 Pivotal Labs\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "tools/jasmine/jasmine-html.js",
    "content": "jasmine.HtmlReporterHelpers = {};\n\njasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {\n  var el = document.createElement(type);\n\n  for (var i = 2; i < arguments.length; i++) {\n    var child = arguments[i];\n\n    if (typeof child === 'string') {\n      el.appendChild(document.createTextNode(child));\n    } else {\n      if (child) {\n        el.appendChild(child);\n      }\n    }\n  }\n\n  for (var attr in attrs) {\n    if (attr == \"className\") {\n      el[attr] = attrs[attr];\n    } else {\n      el.setAttribute(attr, attrs[attr]);\n    }\n  }\n\n  return el;\n};\n\njasmine.HtmlReporterHelpers.getSpecStatus = function(child) {\n  var results = child.results();\n  var status = results.passed() ? 'passed' : 'failed';\n  if (results.skipped) {\n    status = 'skipped';\n  }\n\n  return status;\n};\n\njasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {\n  var parentDiv = this.dom.summary;\n  var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';\n  var parent = child[parentSuite];\n\n  if (parent) {\n    if (typeof this.views.suites[parent.id] == 'undefined') {\n      this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);\n    }\n    parentDiv = this.views.suites[parent.id].element;\n  }\n\n  parentDiv.appendChild(childElement);\n};\n\n\njasmine.HtmlReporterHelpers.addHelpers = function(ctor) {\n  for(var fn in jasmine.HtmlReporterHelpers) {\n    ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];\n  }\n};\n\njasmine.HtmlReporter = function(_doc) {\n  var self = this;\n  var doc = _doc || window.document;\n\n  var reporterView;\n\n  var dom = {};\n\n  // Jasmine Reporter Public Interface\n  self.logRunningSpecs = false;\n\n  self.reportRunnerStarting = function(runner) {\n    var specs = runner.specs() || [];\n\n    if (specs.length == 0) {\n      return;\n    }\n\n    createReporterDom(runner.env.versionString());\n    doc.body.appendChild(dom.reporter);\n    setExceptionHandling();\n\n    reporterView = new jasmine.HtmlReporter.ReporterView(dom);\n    reporterView.addSpecs(specs, self.specFilter);\n  };\n\n  self.reportRunnerResults = function(runner) {\n    reporterView && reporterView.complete();\n  };\n\n  self.reportSuiteResults = function(suite) {\n    reporterView.suiteComplete(suite);\n  };\n\n  self.reportSpecStarting = function(spec) {\n    if (self.logRunningSpecs) {\n      self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');\n    }\n  };\n\n  self.reportSpecResults = function(spec) {\n    reporterView.specComplete(spec);\n  };\n\n  self.log = function() {\n    var console = jasmine.getGlobal().console;\n    if (console && console.log) {\n      if (console.log.apply) {\n        console.log.apply(console, arguments);\n      } else {\n        console.log(arguments); // ie fix: console.log.apply doesn't exist on ie\n      }\n    }\n  };\n\n  self.specFilter = function(spec) {\n    if (!focusedSpecName()) {\n      return true;\n    }\n\n    return spec.getFullName().indexOf(focusedSpecName()) === 0;\n  };\n\n  return self;\n\n  function focusedSpecName() {\n    var specName;\n\n    (function memoizeFocusedSpec() {\n      if (specName) {\n        return;\n      }\n\n      var paramMap = [];\n      var params = jasmine.HtmlReporter.parameters(doc);\n\n      for (var i = 0; i < params.length; i++) {\n        var p = params[i].split('=');\n        paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);\n      }\n\n      specName = paramMap.spec;\n    })();\n\n    return specName;\n  }\n\n  function createReporterDom(version) {\n    dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },\n      dom.banner = self.createDom('div', { className: 'banner' },\n        self.createDom('span', { className: 'title' }, \"Jasmine \"),\n        self.createDom('span', { className: 'version' }, version)),\n\n      dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),\n      dom.alert = self.createDom('div', {className: 'alert'},\n        self.createDom('span', { className: 'exceptions' },\n          self.createDom('label', { className: 'label', 'for': 'no_try_catch' }, 'No try/catch'),\n          self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))),\n      dom.results = self.createDom('div', {className: 'results'},\n        dom.summary = self.createDom('div', { className: 'summary' }),\n        dom.details = self.createDom('div', { id: 'details' }))\n    );\n  }\n\n  function noTryCatch() {\n    return window.location.search.match(/catch=false/);\n  }\n\n  function searchWithCatch() {\n    var params = jasmine.HtmlReporter.parameters(window.document);\n    var removed = false;\n    var i = 0;\n\n    while (!removed && i < params.length) {\n      if (params[i].match(/catch=/)) {\n        params.splice(i, 1);\n        removed = true;\n      }\n      i++;\n    }\n    if (jasmine.CATCH_EXCEPTIONS) {\n      params.push(\"catch=false\");\n    }\n\n    return params.join(\"&\");\n  }\n\n  function setExceptionHandling() {\n    var chxCatch = document.getElementById('no_try_catch');\n\n    if (noTryCatch()) {\n      chxCatch.setAttribute('checked', true);\n      jasmine.CATCH_EXCEPTIONS = false;\n    }\n    chxCatch.onclick = function() {\n      window.location.search = searchWithCatch();\n    };\n  }\n};\njasmine.HtmlReporter.parameters = function(doc) {\n  var paramStr = doc.location.search.substring(1);\n  var params = [];\n\n  if (paramStr.length > 0) {\n    params = paramStr.split('&');\n  }\n  return params;\n}\njasmine.HtmlReporter.sectionLink = function(sectionName) {\n  var link = '?';\n  var params = [];\n\n  if (sectionName) {\n    params.push('spec=' + encodeURIComponent(sectionName));\n  }\n  if (!jasmine.CATCH_EXCEPTIONS) {\n    params.push(\"catch=false\");\n  }\n  if (params.length > 0) {\n    link += params.join(\"&\");\n  }\n\n  return link;\n};\njasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);\njasmine.HtmlReporter.ReporterView = function(dom) {\n  this.startedAt = new Date();\n  this.runningSpecCount = 0;\n  this.completeSpecCount = 0;\n  this.passedCount = 0;\n  this.failedCount = 0;\n  this.skippedCount = 0;\n\n  this.createResultsMenu = function() {\n    this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},\n      this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: \"#\"}, '0 specs'),\n      ' | ',\n      this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: \"#\"}, '0 failing'));\n\n    this.summaryMenuItem.onclick = function() {\n      dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');\n    };\n\n    this.detailsMenuItem.onclick = function() {\n      showDetails();\n    };\n  };\n\n  this.addSpecs = function(specs, specFilter) {\n    this.totalSpecCount = specs.length;\n\n    this.views = {\n      specs: {},\n      suites: {}\n    };\n\n    for (var i = 0; i < specs.length; i++) {\n      var spec = specs[i];\n      this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);\n      if (specFilter(spec)) {\n        this.runningSpecCount++;\n      }\n    }\n  };\n\n  this.specComplete = function(spec) {\n    this.completeSpecCount++;\n\n    if (isUndefined(this.views.specs[spec.id])) {\n      this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);\n    }\n\n    var specView = this.views.specs[spec.id];\n\n    switch (specView.status()) {\n      case 'passed':\n        this.passedCount++;\n        break;\n\n      case 'failed':\n        this.failedCount++;\n        break;\n\n      case 'skipped':\n        this.skippedCount++;\n        break;\n    }\n\n    specView.refresh();\n    this.refresh();\n  };\n\n  this.suiteComplete = function(suite) {\n    var suiteView = this.views.suites[suite.id];\n    if (isUndefined(suiteView)) {\n      return;\n    }\n    suiteView.refresh();\n  };\n\n  this.refresh = function() {\n\n    if (isUndefined(this.resultsMenu)) {\n      this.createResultsMenu();\n    }\n\n    // currently running UI\n    if (isUndefined(this.runningAlert)) {\n      this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: \"runningAlert bar\" });\n      dom.alert.appendChild(this.runningAlert);\n    }\n    this.runningAlert.innerHTML = \"Running \" + this.completeSpecCount + \" of \" + specPluralizedFor(this.totalSpecCount);\n\n    // skipped specs UI\n    if (isUndefined(this.skippedAlert)) {\n      this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: \"skippedAlert bar\" });\n    }\n\n    this.skippedAlert.innerHTML = \"Skipping \" + this.skippedCount + \" of \" + specPluralizedFor(this.totalSpecCount) + \" - run all\";\n\n    if (this.skippedCount === 1 && isDefined(dom.alert)) {\n      dom.alert.appendChild(this.skippedAlert);\n    }\n\n    // passing specs UI\n    if (isUndefined(this.passedAlert)) {\n      this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: \"passingAlert bar\" });\n    }\n    this.passedAlert.innerHTML = \"Passing \" + specPluralizedFor(this.passedCount);\n\n    // failing specs UI\n    if (isUndefined(this.failedAlert)) {\n      this.failedAlert = this.createDom('span', {href: \"?\", className: \"failingAlert bar\"});\n    }\n    this.failedAlert.innerHTML = \"Failing \" + specPluralizedFor(this.failedCount);\n\n    if (this.failedCount === 1 && isDefined(dom.alert)) {\n      dom.alert.appendChild(this.failedAlert);\n      dom.alert.appendChild(this.resultsMenu);\n    }\n\n    // summary info\n    this.summaryMenuItem.innerHTML = \"\" + specPluralizedFor(this.runningSpecCount);\n    this.detailsMenuItem.innerHTML = \"\" + this.failedCount + \" failing\";\n  };\n\n  this.complete = function() {\n    dom.alert.removeChild(this.runningAlert);\n\n    this.skippedAlert.innerHTML = \"Ran \" + this.runningSpecCount + \" of \" + specPluralizedFor(this.totalSpecCount) + \" - run all\";\n\n    if (this.failedCount === 0) {\n      dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, \"Passing \" + specPluralizedFor(this.passedCount)));\n    } else {\n      showDetails();\n    }\n\n    dom.banner.appendChild(this.createDom('span', {className: 'duration'}, \"finished in \" + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + \"s\"));\n  };\n\n  return this;\n\n  function showDetails() {\n    if (dom.reporter.className.search(/showDetails/) === -1) {\n      dom.reporter.className += \" showDetails\";\n    }\n  }\n\n  function isUndefined(obj) {\n    return typeof obj === 'undefined';\n  }\n\n  function isDefined(obj) {\n    return !isUndefined(obj);\n  }\n\n  function specPluralizedFor(count) {\n    var str = count + \" spec\";\n    if (count > 1) {\n      str += \"s\"\n    }\n    return str;\n  }\n\n};\n\njasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);\n\n\njasmine.HtmlReporter.SpecView = function(spec, dom, views) {\n  this.spec = spec;\n  this.dom = dom;\n  this.views = views;\n\n  this.symbol = this.createDom('li', { className: 'pending' });\n  this.dom.symbolSummary.appendChild(this.symbol);\n\n  this.summary = this.createDom('div', { className: 'specSummary' },\n    this.createDom('a', {\n      className: 'description',\n      href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()),\n      title: this.spec.getFullName()\n    }, this.spec.description)\n  );\n\n  this.detail = this.createDom('div', { className: 'specDetail' },\n      this.createDom('a', {\n        className: 'description',\n        href: '?spec=' + encodeURIComponent(this.spec.getFullName()),\n        title: this.spec.getFullName()\n      }, this.spec.getFullName())\n  );\n};\n\njasmine.HtmlReporter.SpecView.prototype.status = function() {\n  return this.getSpecStatus(this.spec);\n};\n\njasmine.HtmlReporter.SpecView.prototype.refresh = function() {\n  this.symbol.className = this.status();\n\n  switch (this.status()) {\n    case 'skipped':\n      break;\n\n    case 'passed':\n      this.appendSummaryToSuiteDiv();\n      break;\n\n    case 'failed':\n      this.appendSummaryToSuiteDiv();\n      this.appendFailureDetail();\n      break;\n  }\n};\n\njasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {\n  this.summary.className += ' ' + this.status();\n  this.appendToSummary(this.spec, this.summary);\n};\n\njasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {\n  this.detail.className += ' ' + this.status();\n\n  var resultItems = this.spec.results().getItems();\n  var messagesDiv = this.createDom('div', { className: 'messages' });\n\n  for (var i = 0; i < resultItems.length; i++) {\n    var result = resultItems[i];\n\n    if (result.type == 'log') {\n      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));\n    } else if (result.type == 'expect' && result.passed && !result.passed()) {\n      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));\n\n      if (result.trace.stack) {\n        messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));\n      }\n    }\n  }\n\n  if (messagesDiv.childNodes.length > 0) {\n    this.detail.appendChild(messagesDiv);\n    this.dom.details.appendChild(this.detail);\n  }\n};\n\njasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {\n  this.suite = suite;\n  this.dom = dom;\n  this.views = views;\n\n  this.element = this.createDom('div', { className: 'suite' },\n    this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description)\n  );\n\n  this.appendToSummary(this.suite, this.element);\n};\n\njasmine.HtmlReporter.SuiteView.prototype.status = function() {\n  return this.getSpecStatus(this.suite);\n};\n\njasmine.HtmlReporter.SuiteView.prototype.refresh = function() {\n  this.element.className += \" \" + this.status();\n};\n\njasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);\n\n/* @deprecated Use jasmine.HtmlReporter instead\n */\njasmine.TrivialReporter = function(doc) {\n  this.document = doc || document;\n  this.suiteDivs = {};\n  this.logRunningSpecs = false;\n};\n\njasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {\n  var el = document.createElement(type);\n\n  for (var i = 2; i < arguments.length; i++) {\n    var child = arguments[i];\n\n    if (typeof child === 'string') {\n      el.appendChild(document.createTextNode(child));\n    } else {\n      if (child) { el.appendChild(child); }\n    }\n  }\n\n  for (var attr in attrs) {\n    if (attr == \"className\") {\n      el[attr] = attrs[attr];\n    } else {\n      el.setAttribute(attr, attrs[attr]);\n    }\n  }\n\n  return el;\n};\n\njasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {\n  var showPassed, showSkipped;\n\n  this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },\n      this.createDom('div', { className: 'banner' },\n        this.createDom('div', { className: 'logo' },\n            this.createDom('span', { className: 'title' }, \"Jasmine\"),\n            this.createDom('span', { className: 'version' }, runner.env.versionString())),\n        this.createDom('div', { className: 'options' },\n            \"Show \",\n            showPassed = this.createDom('input', { id: \"__jasmine_TrivialReporter_showPassed__\", type: 'checkbox' }),\n            this.createDom('label', { \"for\": \"__jasmine_TrivialReporter_showPassed__\" }, \" passed \"),\n            showSkipped = this.createDom('input', { id: \"__jasmine_TrivialReporter_showSkipped__\", type: 'checkbox' }),\n            this.createDom('label', { \"for\": \"__jasmine_TrivialReporter_showSkipped__\" }, \" skipped\")\n            )\n          ),\n\n      this.runnerDiv = this.createDom('div', { className: 'runner running' },\n          this.createDom('a', { className: 'run_spec', href: '?' }, \"run all\"),\n          this.runnerMessageSpan = this.createDom('span', {}, \"Running...\"),\n          this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, \"\"))\n      );\n\n  this.document.body.appendChild(this.outerDiv);\n\n  var suites = runner.suites();\n  for (var i = 0; i < suites.length; i++) {\n    var suite = suites[i];\n    var suiteDiv = this.createDom('div', { className: 'suite' },\n        this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, \"run\"),\n        this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));\n    this.suiteDivs[suite.id] = suiteDiv;\n    var parentDiv = this.outerDiv;\n    if (suite.parentSuite) {\n      parentDiv = this.suiteDivs[suite.parentSuite.id];\n    }\n    parentDiv.appendChild(suiteDiv);\n  }\n\n  this.startedAt = new Date();\n\n  var self = this;\n  showPassed.onclick = function(evt) {\n    if (showPassed.checked) {\n      self.outerDiv.className += ' show-passed';\n    } else {\n      self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');\n    }\n  };\n\n  showSkipped.onclick = function(evt) {\n    if (showSkipped.checked) {\n      self.outerDiv.className += ' show-skipped';\n    } else {\n      self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');\n    }\n  };\n};\n\njasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {\n  var results = runner.results();\n  var className = (results.failedCount > 0) ? \"runner failed\" : \"runner passed\";\n  this.runnerDiv.setAttribute(\"class\", className);\n  //do it twice for IE\n  this.runnerDiv.setAttribute(\"className\", className);\n  var specs = runner.specs();\n  var specCount = 0;\n  for (var i = 0; i < specs.length; i++) {\n    if (this.specFilter(specs[i])) {\n      specCount++;\n    }\n  }\n  var message = \"\" + specCount + \" spec\" + (specCount == 1 ? \"\" : \"s\" ) + \", \" + results.failedCount + \" failure\" + ((results.failedCount == 1) ? \"\" : \"s\");\n  message += \" in \" + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + \"s\";\n  this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);\n\n  this.finishedAtSpan.appendChild(document.createTextNode(\"Finished at \" + new Date().toString()));\n};\n\njasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {\n  var results = suite.results();\n  var status = results.passed() ? 'passed' : 'failed';\n  if (results.totalCount === 0) { // todo: change this to check results.skipped\n    status = 'skipped';\n  }\n  this.suiteDivs[suite.id].className += \" \" + status;\n};\n\njasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {\n  if (this.logRunningSpecs) {\n    this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');\n  }\n};\n\njasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {\n  var results = spec.results();\n  var status = results.passed() ? 'passed' : 'failed';\n  if (results.skipped) {\n    status = 'skipped';\n  }\n  var specDiv = this.createDom('div', { className: 'spec '  + status },\n      this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, \"run\"),\n      this.createDom('a', {\n        className: 'description',\n        href: '?spec=' + encodeURIComponent(spec.getFullName()),\n        title: spec.getFullName()\n      }, spec.description));\n\n\n  var resultItems = results.getItems();\n  var messagesDiv = this.createDom('div', { className: 'messages' });\n  for (var i = 0; i < resultItems.length; i++) {\n    var result = resultItems[i];\n\n    if (result.type == 'log') {\n      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));\n    } else if (result.type == 'expect' && result.passed && !result.passed()) {\n      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));\n\n      if (result.trace.stack) {\n        messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));\n      }\n    }\n  }\n\n  if (messagesDiv.childNodes.length > 0) {\n    specDiv.appendChild(messagesDiv);\n  }\n\n  this.suiteDivs[spec.suite.id].appendChild(specDiv);\n};\n\njasmine.TrivialReporter.prototype.log = function() {\n  var console = jasmine.getGlobal().console;\n  if (console && console.log) {\n    if (console.log.apply) {\n      console.log.apply(console, arguments);\n    } else {\n      console.log(arguments); // ie fix: console.log.apply doesn't exist on ie\n    }\n  }\n};\n\njasmine.TrivialReporter.prototype.getLocation = function() {\n  return this.document.location;\n};\n\njasmine.TrivialReporter.prototype.specFilter = function(spec) {\n  var paramMap = {};\n  var params = this.getLocation().search.substring(1).split('&');\n  for (var i = 0; i < params.length; i++) {\n    var p = params[i].split('=');\n    paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);\n  }\n\n  if (!paramMap.spec) {\n    return true;\n  }\n  return spec.getFullName().indexOf(paramMap.spec) === 0;\n};\n"
  },
  {
    "path": "tools/jasmine/jasmine.css",
    "content": "body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }\n\n#HTMLReporter { font-size: 11px; font-family: Monaco, \"Lucida Console\", monospace; line-height: 14px; color: #333333; }\n#HTMLReporter a { text-decoration: none; }\n#HTMLReporter a:hover { text-decoration: underline; }\n#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }\n#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }\n#HTMLReporter #jasmine_content { position: fixed; right: 100%; }\n#HTMLReporter .version { color: #aaaaaa; }\n#HTMLReporter .banner { margin-top: 14px; }\n#HTMLReporter .duration { color: #aaaaaa; float: right; }\n#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }\n#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }\n#HTMLReporter .symbolSummary li.passed { font-size: 14px; }\n#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: \"\\02022\"; }\n#HTMLReporter .symbolSummary li.failed { line-height: 9px; }\n#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: \"x\"; font-weight: bold; margin-left: -1px; }\n#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }\n#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: \"\\02022\"; }\n#HTMLReporter .symbolSummary li.pending { line-height: 11px; }\n#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: \"-\"; }\n#HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }\n#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }\n#HTMLReporter .runningAlert { background-color: #666666; }\n#HTMLReporter .skippedAlert { background-color: #aaaaaa; }\n#HTMLReporter .skippedAlert:first-child { background-color: #333333; }\n#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }\n#HTMLReporter .passingAlert { background-color: #a6b779; }\n#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }\n#HTMLReporter .failingAlert { background-color: #cf867e; }\n#HTMLReporter .failingAlert:first-child { background-color: #b03911; }\n#HTMLReporter .results { margin-top: 14px; }\n#HTMLReporter #details { display: none; }\n#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }\n#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }\n#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }\n#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }\n#HTMLReporter.showDetails .summary { display: none; }\n#HTMLReporter.showDetails #details { display: block; }\n#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }\n#HTMLReporter .summary { margin-top: 14px; }\n#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }\n#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }\n#HTMLReporter .summary .specSummary.failed a { color: #b03911; }\n#HTMLReporter .description + .suite { margin-top: 0; }\n#HTMLReporter .suite { margin-top: 14px; }\n#HTMLReporter .suite a { color: #333333; }\n#HTMLReporter #details .specDetail { margin-bottom: 28px; }\n#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }\n#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }\n#HTMLReporter .resultMessage span.result { display: block; }\n#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }\n\n#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: \"Helvetica Neue Light\", \"Lucida Grande\", \"Calibri\", \"Arial\", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }\n#TrivialReporter a:visited, #TrivialReporter a { color: #303; }\n#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }\n#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }\n#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }\n#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }\n#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }\n#TrivialReporter .runner.running { background-color: yellow; }\n#TrivialReporter .options { text-align: right; font-size: .8em; }\n#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }\n#TrivialReporter .suite .suite { margin: 5px; }\n#TrivialReporter .suite.passed { background-color: #dfd; }\n#TrivialReporter .suite.failed { background-color: #fdd; }\n#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }\n#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }\n#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }\n#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }\n#TrivialReporter .spec.skipped { background-color: #bbb; }\n#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }\n#TrivialReporter .passed { background-color: #cfc; display: none; }\n#TrivialReporter .failed { background-color: #fbb; }\n#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }\n#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }\n#TrivialReporter .resultMessage .mismatch { color: black; }\n#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }\n#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }\n#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }\n#TrivialReporter #jasmine_content { position: fixed; right: 100%; }\n#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }\n"
  },
  {
    "path": "tools/jasmine/jasmine.js",
    "content": "var isCommonJS = typeof window == \"undefined\" && typeof exports == \"object\";\n\n/**\n * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.\n *\n * @namespace\n */\nvar jasmine = {};\nif (isCommonJS) exports.jasmine = jasmine;\n/**\n * @private\n */\njasmine.unimplementedMethod_ = function() {\n  throw new Error(\"unimplemented method\");\n};\n\n/**\n * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just\n * a plain old variable and may be redefined by somebody else.\n *\n * @private\n */\njasmine.undefined = jasmine.___undefined___;\n\n/**\n * Show diagnostic messages in the console if set to true\n *\n */\njasmine.VERBOSE = false;\n\n/**\n * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.\n *\n */\njasmine.DEFAULT_UPDATE_INTERVAL = 250;\n\n/**\n * Maximum levels of nesting that will be included when an object is pretty-printed\n */\njasmine.MAX_PRETTY_PRINT_DEPTH = 40;\n\n/**\n * Default timeout interval in milliseconds for waitsFor() blocks.\n */\njasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;\n\n/**\n * By default exceptions thrown in the context of a test are caught by jasmine so that it can run the remaining tests in the suite.\n * Set to false to let the exception bubble up in the browser.\n *\n */\njasmine.CATCH_EXCEPTIONS = true;\n\njasmine.getGlobal = function() {\n  function getGlobal() {\n    return this;\n  }\n\n  return getGlobal();\n};\n\n/**\n * Allows for bound functions to be compared.  Internal use only.\n *\n * @ignore\n * @private\n * @param base {Object} bound 'this' for the function\n * @param name {Function} function to find\n */\njasmine.bindOriginal_ = function(base, name) {\n  var original = base[name];\n  if (original.apply) {\n    return function() {\n      return original.apply(base, arguments);\n    };\n  } else {\n    // IE support\n    return jasmine.getGlobal()[name];\n  }\n};\n\njasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');\njasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');\njasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');\njasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');\n\njasmine.MessageResult = function(values) {\n  this.type = 'log';\n  this.values = values;\n  this.trace = new Error(); // todo: test better\n};\n\njasmine.MessageResult.prototype.toString = function() {\n  var text = \"\";\n  for (var i = 0; i < this.values.length; i++) {\n    if (i > 0) text += \" \";\n    if (jasmine.isString_(this.values[i])) {\n      text += this.values[i];\n    } else {\n      text += jasmine.pp(this.values[i]);\n    }\n  }\n  return text;\n};\n\njasmine.ExpectationResult = function(params) {\n  this.type = 'expect';\n  this.matcherName = params.matcherName;\n  this.passed_ = params.passed;\n  this.expected = params.expected;\n  this.actual = params.actual;\n  this.message = this.passed_ ? 'Passed.' : params.message;\n\n  var trace = (params.trace || new Error(this.message));\n  this.trace = this.passed_ ? '' : trace;\n};\n\njasmine.ExpectationResult.prototype.toString = function () {\n  return this.message;\n};\n\njasmine.ExpectationResult.prototype.passed = function () {\n  return this.passed_;\n};\n\n/**\n * Getter for the Jasmine environment. Ensures one gets created\n */\njasmine.getEnv = function() {\n  var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();\n  return env;\n};\n\n/**\n * @ignore\n * @private\n * @param value\n * @returns {Boolean}\n */\njasmine.isArray_ = function(value) {\n  return jasmine.isA_(\"Array\", value);\n};\n\n/**\n * @ignore\n * @private\n * @param value\n * @returns {Boolean}\n */\njasmine.isString_ = function(value) {\n  return jasmine.isA_(\"String\", value);\n};\n\n/**\n * @ignore\n * @private\n * @param value\n * @returns {Boolean}\n */\njasmine.isNumber_ = function(value) {\n  return jasmine.isA_(\"Number\", value);\n};\n\n/**\n * @ignore\n * @private\n * @param {String} typeName\n * @param value\n * @returns {Boolean}\n */\njasmine.isA_ = function(typeName, value) {\n  return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';\n};\n\n/**\n * Pretty printer for expecations.  Takes any object and turns it into a human-readable string.\n *\n * @param value {Object} an object to be outputted\n * @returns {String}\n */\njasmine.pp = function(value) {\n  var stringPrettyPrinter = new jasmine.StringPrettyPrinter();\n  stringPrettyPrinter.format(value);\n  return stringPrettyPrinter.string;\n};\n\n/**\n * Returns true if the object is a DOM Node.\n *\n * @param {Object} obj object to check\n * @returns {Boolean}\n */\njasmine.isDomNode = function(obj) {\n  return obj.nodeType > 0;\n};\n\n/**\n * Returns a matchable 'generic' object of the class type.  For use in expecations of type when values don't matter.\n *\n * @example\n * // don't care about which function is passed in, as long as it's a function\n * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));\n *\n * @param {Class} clazz\n * @returns matchable object of the type clazz\n */\njasmine.any = function(clazz) {\n  return new jasmine.Matchers.Any(clazz);\n};\n\n/**\n * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the\n * attributes on the object.\n *\n * @example\n * // don't care about any other attributes than foo.\n * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: \"bar\"});\n *\n * @param sample {Object} sample\n * @returns matchable object for the sample\n */\njasmine.objectContaining = function (sample) {\n    return new jasmine.Matchers.ObjectContaining(sample);\n};\n\n/**\n * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.\n *\n * Spies should be created in test setup, before expectations.  They can then be checked, using the standard Jasmine\n * expectation syntax. Spies can be checked if they were called or not and what the calling params were.\n *\n * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).\n *\n * Spies are torn down at the end of every spec.\n *\n * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.\n *\n * @example\n * // a stub\n * var myStub = jasmine.createSpy('myStub');  // can be used anywhere\n *\n * // spy example\n * var foo = {\n *   not: function(bool) { return !bool; }\n * }\n *\n * // actual foo.not will not be called, execution stops\n * spyOn(foo, 'not');\n\n // foo.not spied upon, execution will continue to implementation\n * spyOn(foo, 'not').andCallThrough();\n *\n * // fake example\n * var foo = {\n *   not: function(bool) { return !bool; }\n * }\n *\n * // foo.not(val) will return val\n * spyOn(foo, 'not').andCallFake(function(value) {return value;});\n *\n * // mock example\n * foo.not(7 == 7);\n * expect(foo.not).toHaveBeenCalled();\n * expect(foo.not).toHaveBeenCalledWith(true);\n *\n * @constructor\n * @see spyOn, jasmine.createSpy, jasmine.createSpyObj\n * @param {String} name\n */\njasmine.Spy = function(name) {\n  /**\n   * The name of the spy, if provided.\n   */\n  this.identity = name || 'unknown';\n  /**\n   *  Is this Object a spy?\n   */\n  this.isSpy = true;\n  /**\n   * The actual function this spy stubs.\n   */\n  this.plan = function() {\n  };\n  /**\n   * Tracking of the most recent call to the spy.\n   * @example\n   * var mySpy = jasmine.createSpy('foo');\n   * mySpy(1, 2);\n   * mySpy.mostRecentCall.args = [1, 2];\n   */\n  this.mostRecentCall = {};\n\n  /**\n   * Holds arguments for each call to the spy, indexed by call count\n   * @example\n   * var mySpy = jasmine.createSpy('foo');\n   * mySpy(1, 2);\n   * mySpy(7, 8);\n   * mySpy.mostRecentCall.args = [7, 8];\n   * mySpy.argsForCall[0] = [1, 2];\n   * mySpy.argsForCall[1] = [7, 8];\n   */\n  this.argsForCall = [];\n  this.calls = [];\n};\n\n/**\n * Tells a spy to call through to the actual implemenatation.\n *\n * @example\n * var foo = {\n *   bar: function() { // do some stuff }\n * }\n *\n * // defining a spy on an existing property: foo.bar\n * spyOn(foo, 'bar').andCallThrough();\n */\njasmine.Spy.prototype.andCallThrough = function() {\n  this.plan = this.originalValue;\n  return this;\n};\n\n/**\n * For setting the return value of a spy.\n *\n * @example\n * // defining a spy from scratch: foo() returns 'baz'\n * var foo = jasmine.createSpy('spy on foo').andReturn('baz');\n *\n * // defining a spy on an existing property: foo.bar() returns 'baz'\n * spyOn(foo, 'bar').andReturn('baz');\n *\n * @param {Object} value\n */\njasmine.Spy.prototype.andReturn = function(value) {\n  this.plan = function() {\n    return value;\n  };\n  return this;\n};\n\n/**\n * For throwing an exception when a spy is called.\n *\n * @example\n * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'\n * var foo = jasmine.createSpy('spy on foo').andThrow('baz');\n *\n * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'\n * spyOn(foo, 'bar').andThrow('baz');\n *\n * @param {String} exceptionMsg\n */\njasmine.Spy.prototype.andThrow = function(exceptionMsg) {\n  this.plan = function() {\n    throw exceptionMsg;\n  };\n  return this;\n};\n\n/**\n * Calls an alternate implementation when a spy is called.\n *\n * @example\n * var baz = function() {\n *   // do some stuff, return something\n * }\n * // defining a spy from scratch: foo() calls the function baz\n * var foo = jasmine.createSpy('spy on foo').andCall(baz);\n *\n * // defining a spy on an existing property: foo.bar() calls an anonymnous function\n * spyOn(foo, 'bar').andCall(function() { return 'baz';} );\n *\n * @param {Function} fakeFunc\n */\njasmine.Spy.prototype.andCallFake = function(fakeFunc) {\n  this.plan = fakeFunc;\n  return this;\n};\n\n/**\n * Resets all of a spy's the tracking variables so that it can be used again.\n *\n * @example\n * spyOn(foo, 'bar');\n *\n * foo.bar();\n *\n * expect(foo.bar.callCount).toEqual(1);\n *\n * foo.bar.reset();\n *\n * expect(foo.bar.callCount).toEqual(0);\n */\njasmine.Spy.prototype.reset = function() {\n  this.wasCalled = false;\n  this.callCount = 0;\n  this.argsForCall = [];\n  this.calls = [];\n  this.mostRecentCall = {};\n};\n\njasmine.createSpy = function(name) {\n\n  var spyObj = function() {\n    spyObj.wasCalled = true;\n    spyObj.callCount++;\n    var args = jasmine.util.argsToArray(arguments);\n    spyObj.mostRecentCall.object = this;\n    spyObj.mostRecentCall.args = args;\n    spyObj.argsForCall.push(args);\n    spyObj.calls.push({object: this, args: args});\n    return spyObj.plan.apply(this, arguments);\n  };\n\n  var spy = new jasmine.Spy(name);\n\n  for (var prop in spy) {\n    spyObj[prop] = spy[prop];\n  }\n\n  spyObj.reset();\n\n  return spyObj;\n};\n\n/**\n * Determines whether an object is a spy.\n *\n * @param {jasmine.Spy|Object} putativeSpy\n * @returns {Boolean}\n */\njasmine.isSpy = function(putativeSpy) {\n  return putativeSpy && putativeSpy.isSpy;\n};\n\n/**\n * Creates a more complicated spy: an Object that has every property a function that is a spy.  Used for stubbing something\n * large in one call.\n *\n * @param {String} baseName name of spy class\n * @param {Array} methodNames array of names of methods to make spies\n */\njasmine.createSpyObj = function(baseName, methodNames) {\n  if (!jasmine.isArray_(methodNames) || methodNames.length === 0) {\n    throw new Error('createSpyObj requires a non-empty array of method names to create spies for');\n  }\n  var obj = {};\n  for (var i = 0; i < methodNames.length; i++) {\n    obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);\n  }\n  return obj;\n};\n\n/**\n * All parameters are pretty-printed and concatenated together, then written to the current spec's output.\n *\n * Be careful not to leave calls to <code>jasmine.log</code> in production code.\n */\njasmine.log = function() {\n  var spec = jasmine.getEnv().currentSpec;\n  spec.log.apply(spec, arguments);\n};\n\n/**\n * Function that installs a spy on an existing object's method name.  Used within a Spec to create a spy.\n *\n * @example\n * // spy example\n * var foo = {\n *   not: function(bool) { return !bool; }\n * }\n * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops\n *\n * @see jasmine.createSpy\n * @param obj\n * @param methodName\n * @return {jasmine.Spy} a Jasmine spy that can be chained with all spy methods\n */\nvar spyOn = function(obj, methodName) {\n  return jasmine.getEnv().currentSpec.spyOn(obj, methodName);\n};\nif (isCommonJS) exports.spyOn = spyOn;\n\n/**\n * Creates a Jasmine spec that will be added to the current suite.\n *\n * // TODO: pending tests\n *\n * @example\n * it('should be true', function() {\n *   expect(true).toEqual(true);\n * });\n *\n * @param {String} desc description of this specification\n * @param {Function} func defines the preconditions and expectations of the spec\n */\nvar it = function(desc, func) {\n  return jasmine.getEnv().it(desc, func);\n};\nif (isCommonJS) exports.it = it;\n\n/**\n * Creates a <em>disabled</em> Jasmine spec.\n *\n * A convenience method that allows existing specs to be disabled temporarily during development.\n *\n * @param {String} desc description of this specification\n * @param {Function} func defines the preconditions and expectations of the spec\n */\nvar xit = function(desc, func) {\n  return jasmine.getEnv().xit(desc, func);\n};\nif (isCommonJS) exports.xit = xit;\n\n/**\n * Starts a chain for a Jasmine expectation.\n *\n * It is passed an Object that is the actual value and should chain to one of the many\n * jasmine.Matchers functions.\n *\n * @param {Object} actual Actual value to test against and expected value\n * @return {jasmine.Matchers}\n */\nvar expect = function(actual) {\n  return jasmine.getEnv().currentSpec.expect(actual);\n};\nif (isCommonJS) exports.expect = expect;\n\n/**\n * Defines part of a jasmine spec.  Used in cominbination with waits or waitsFor in asynchrnous specs.\n *\n * @param {Function} func Function that defines part of a jasmine spec.\n */\nvar runs = function(func) {\n  jasmine.getEnv().currentSpec.runs(func);\n};\nif (isCommonJS) exports.runs = runs;\n\n/**\n * Waits a fixed time period before moving to the next block.\n *\n * @deprecated Use waitsFor() instead\n * @param {Number} timeout milliseconds to wait\n */\nvar waits = function(timeout) {\n  jasmine.getEnv().currentSpec.waits(timeout);\n};\nif (isCommonJS) exports.waits = waits;\n\n/**\n * Waits for the latchFunction to return true before proceeding to the next block.\n *\n * @param {Function} latchFunction\n * @param {String} optional_timeoutMessage\n * @param {Number} optional_timeout\n */\nvar waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {\n  jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);\n};\nif (isCommonJS) exports.waitsFor = waitsFor;\n\n/**\n * A function that is called before each spec in a suite.\n *\n * Used for spec setup, including validating assumptions.\n *\n * @param {Function} beforeEachFunction\n */\nvar beforeEach = function(beforeEachFunction) {\n  jasmine.getEnv().beforeEach(beforeEachFunction);\n};\nif (isCommonJS) exports.beforeEach = beforeEach;\n\n/**\n * A function that is called after each spec in a suite.\n *\n * Used for restoring any state that is hijacked during spec execution.\n *\n * @param {Function} afterEachFunction\n */\nvar afterEach = function(afterEachFunction) {\n  jasmine.getEnv().afterEach(afterEachFunction);\n};\nif (isCommonJS) exports.afterEach = afterEach;\n\n/**\n * Defines a suite of specifications.\n *\n * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared\n * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization\n * of setup in some tests.\n *\n * @example\n * // TODO: a simple suite\n *\n * // TODO: a simple suite with a nested describe block\n *\n * @param {String} description A string, usually the class under test.\n * @param {Function} specDefinitions function that defines several specs.\n */\nvar describe = function(description, specDefinitions) {\n  return jasmine.getEnv().describe(description, specDefinitions);\n};\nif (isCommonJS) exports.describe = describe;\n\n/**\n * Disables a suite of specifications.  Used to disable some suites in a file, or files, temporarily during development.\n *\n * @param {String} description A string, usually the class under test.\n * @param {Function} specDefinitions function that defines several specs.\n */\nvar xdescribe = function(description, specDefinitions) {\n  return jasmine.getEnv().xdescribe(description, specDefinitions);\n};\nif (isCommonJS) exports.xdescribe = xdescribe;\n\n\n// Provide the XMLHttpRequest class for IE 5.x-6.x:\njasmine.XmlHttpRequest = (typeof XMLHttpRequest == \"undefined\") ? function() {\n  function tryIt(f) {\n    try {\n      return f();\n    } catch(e) {\n    }\n    return null;\n  }\n\n  var xhr = tryIt(function() {\n    return new ActiveXObject(\"Msxml2.XMLHTTP.6.0\");\n  }) ||\n    tryIt(function() {\n      return new ActiveXObject(\"Msxml2.XMLHTTP.3.0\");\n    }) ||\n    tryIt(function() {\n      return new ActiveXObject(\"Msxml2.XMLHTTP\");\n    }) ||\n    tryIt(function() {\n      return new ActiveXObject(\"Microsoft.XMLHTTP\");\n    });\n\n  if (!xhr) throw new Error(\"This browser does not support XMLHttpRequest.\");\n\n  return xhr;\n} : XMLHttpRequest;\n/**\n * @namespace\n */\njasmine.util = {};\n\n/**\n * Declare that a child class inherit it's prototype from the parent class.\n *\n * @private\n * @param {Function} childClass\n * @param {Function} parentClass\n */\njasmine.util.inherit = function(childClass, parentClass) {\n  /**\n   * @private\n   */\n  var subclass = function() {\n  };\n  subclass.prototype = parentClass.prototype;\n  childClass.prototype = new subclass();\n};\n\njasmine.util.formatException = function(e) {\n  var lineNumber;\n  if (e.line) {\n    lineNumber = e.line;\n  }\n  else if (e.lineNumber) {\n    lineNumber = e.lineNumber;\n  }\n\n  var file;\n\n  if (e.sourceURL) {\n    file = e.sourceURL;\n  }\n  else if (e.fileName) {\n    file = e.fileName;\n  }\n\n  var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();\n\n  if (file && lineNumber) {\n    message += ' in ' + file + ' (line ' + lineNumber + ')';\n  }\n\n  return message;\n};\n\njasmine.util.htmlEscape = function(str) {\n  if (!str) return str;\n  return str.replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;');\n};\n\njasmine.util.argsToArray = function(args) {\n  var arrayOfArgs = [];\n  for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);\n  return arrayOfArgs;\n};\n\njasmine.util.extend = function(destination, source) {\n  for (var property in source) destination[property] = source[property];\n  return destination;\n};\n\n/**\n * Environment for Jasmine\n *\n * @constructor\n */\njasmine.Env = function() {\n  this.currentSpec = null;\n  this.currentSuite = null;\n  this.currentRunner_ = new jasmine.Runner(this);\n\n  this.reporter = new jasmine.MultiReporter();\n\n  this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;\n  this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;\n  this.lastUpdate = 0;\n  this.specFilter = function() {\n    return true;\n  };\n\n  this.nextSpecId_ = 0;\n  this.nextSuiteId_ = 0;\n  this.equalityTesters_ = [];\n\n  // wrap matchers\n  this.matchersClass = function() {\n    jasmine.Matchers.apply(this, arguments);\n  };\n  jasmine.util.inherit(this.matchersClass, jasmine.Matchers);\n\n  jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);\n};\n\n\njasmine.Env.prototype.setTimeout = jasmine.setTimeout;\njasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;\njasmine.Env.prototype.setInterval = jasmine.setInterval;\njasmine.Env.prototype.clearInterval = jasmine.clearInterval;\n\n/**\n * @returns an object containing jasmine version build info, if set.\n */\njasmine.Env.prototype.version = function () {\n  if (jasmine.version_) {\n    return jasmine.version_;\n  } else {\n    throw new Error('Version not set');\n  }\n};\n\n/**\n * @returns string containing jasmine version build info, if set.\n */\njasmine.Env.prototype.versionString = function() {\n  if (!jasmine.version_) {\n    return \"version unknown\";\n  }\n\n  var version = this.version();\n  var versionString = version.major + \".\" + version.minor + \".\" + version.build;\n  if (version.release_candidate) {\n    versionString += \".rc\" + version.release_candidate;\n  }\n  versionString += \" revision \" + version.revision;\n  return versionString;\n};\n\n/**\n * @returns a sequential integer starting at 0\n */\njasmine.Env.prototype.nextSpecId = function () {\n  return this.nextSpecId_++;\n};\n\n/**\n * @returns a sequential integer starting at 0\n */\njasmine.Env.prototype.nextSuiteId = function () {\n  return this.nextSuiteId_++;\n};\n\n/**\n * Register a reporter to receive status updates from Jasmine.\n * @param {jasmine.Reporter} reporter An object which will receive status updates.\n */\njasmine.Env.prototype.addReporter = function(reporter) {\n  this.reporter.addReporter(reporter);\n};\n\njasmine.Env.prototype.execute = function() {\n  this.currentRunner_.execute();\n};\n\njasmine.Env.prototype.describe = function(description, specDefinitions) {\n  var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);\n\n  var parentSuite = this.currentSuite;\n  if (parentSuite) {\n    parentSuite.add(suite);\n  } else {\n    this.currentRunner_.add(suite);\n  }\n\n  this.currentSuite = suite;\n\n  var declarationError = null;\n  try {\n    specDefinitions.call(suite);\n  } catch(e) {\n    declarationError = e;\n  }\n\n  if (declarationError) {\n    this.it(\"encountered a declaration exception\", function() {\n      throw declarationError;\n    });\n  }\n\n  this.currentSuite = parentSuite;\n\n  return suite;\n};\n\njasmine.Env.prototype.beforeEach = function(beforeEachFunction) {\n  if (this.currentSuite) {\n    this.currentSuite.beforeEach(beforeEachFunction);\n  } else {\n    this.currentRunner_.beforeEach(beforeEachFunction);\n  }\n};\n\njasmine.Env.prototype.currentRunner = function () {\n  return this.currentRunner_;\n};\n\njasmine.Env.prototype.afterEach = function(afterEachFunction) {\n  if (this.currentSuite) {\n    this.currentSuite.afterEach(afterEachFunction);\n  } else {\n    this.currentRunner_.afterEach(afterEachFunction);\n  }\n\n};\n\njasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {\n  return {\n    execute: function() {\n    }\n  };\n};\n\njasmine.Env.prototype.it = function(description, func) {\n  var spec = new jasmine.Spec(this, this.currentSuite, description);\n  this.currentSuite.add(spec);\n  this.currentSpec = spec;\n\n  if (func) {\n    spec.runs(func);\n  }\n\n  return spec;\n};\n\njasmine.Env.prototype.xit = function(desc, func) {\n  return {\n    id: this.nextSpecId(),\n    runs: function() {\n    }\n  };\n};\n\njasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) {\n  if (a.source != b.source)\n    mismatchValues.push(\"expected pattern /\" + b.source + \"/ is not equal to the pattern /\" + a.source + \"/\");\n\n  if (a.ignoreCase != b.ignoreCase)\n    mismatchValues.push(\"expected modifier i was\" + (b.ignoreCase ? \" \" : \" not \") + \"set and does not equal the origin modifier\");\n\n  if (a.global != b.global)\n    mismatchValues.push(\"expected modifier g was\" + (b.global ? \" \" : \" not \") + \"set and does not equal the origin modifier\");\n\n  if (a.multiline != b.multiline)\n    mismatchValues.push(\"expected modifier m was\" + (b.multiline ? \" \" : \" not \") + \"set and does not equal the origin modifier\");\n\n  if (a.sticky != b.sticky)\n    mismatchValues.push(\"expected modifier y was\" + (b.sticky ? \" \" : \" not \") + \"set and does not equal the origin modifier\");\n\n  return (mismatchValues.length === 0);\n};\n\njasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {\n  if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {\n    return true;\n  }\n\n  a.__Jasmine_been_here_before__ = b;\n  b.__Jasmine_been_here_before__ = a;\n\n  var hasKey = function(obj, keyName) {\n    return obj !== null && obj[keyName] !== jasmine.undefined;\n  };\n\n  for (var property in b) {\n    if (!hasKey(a, property) && hasKey(b, property)) {\n      mismatchKeys.push(\"expected has key '\" + property + \"', but missing from actual.\");\n    }\n  }\n  for (property in a) {\n    if (!hasKey(b, property) && hasKey(a, property)) {\n      mismatchKeys.push(\"expected missing key '\" + property + \"', but present in actual.\");\n    }\n  }\n  for (property in b) {\n    if (property == '__Jasmine_been_here_before__') continue;\n    if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {\n      mismatchValues.push(\"'\" + property + \"' was '\" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + \"' in expected, but was '\" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + \"' in actual.\");\n    }\n  }\n\n  if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {\n    mismatchValues.push(\"arrays were not the same length\");\n  }\n\n  delete a.__Jasmine_been_here_before__;\n  delete b.__Jasmine_been_here_before__;\n  return (mismatchKeys.length === 0 && mismatchValues.length === 0);\n};\n\njasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {\n  mismatchKeys = mismatchKeys || [];\n  mismatchValues = mismatchValues || [];\n\n  for (var i = 0; i < this.equalityTesters_.length; i++) {\n    var equalityTester = this.equalityTesters_[i];\n    var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);\n    if (result !== jasmine.undefined) return result;\n  }\n\n  if (a === b) return true;\n\n  if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {\n    return (a == jasmine.undefined && b == jasmine.undefined);\n  }\n\n  if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {\n    return a === b;\n  }\n\n  if (a instanceof Date && b instanceof Date) {\n    return a.getTime() == b.getTime();\n  }\n\n  if (a.jasmineMatches) {\n    return a.jasmineMatches(b);\n  }\n\n  if (b.jasmineMatches) {\n    return b.jasmineMatches(a);\n  }\n\n  if (a instanceof jasmine.Matchers.ObjectContaining) {\n    return a.matches(b);\n  }\n\n  if (b instanceof jasmine.Matchers.ObjectContaining) {\n    return b.matches(a);\n  }\n\n  if (jasmine.isString_(a) && jasmine.isString_(b)) {\n    return (a == b);\n  }\n\n  if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {\n    return (a == b);\n  }\n\n  if (a instanceof RegExp && b instanceof RegExp) {\n    return this.compareRegExps_(a, b, mismatchKeys, mismatchValues);\n  }\n\n  if (typeof a === \"object\" && typeof b === \"object\") {\n    return this.compareObjects_(a, b, mismatchKeys, mismatchValues);\n  }\n\n  //Straight check\n  return (a === b);\n};\n\njasmine.Env.prototype.contains_ = function(haystack, needle) {\n  if (jasmine.isArray_(haystack)) {\n    for (var i = 0; i < haystack.length; i++) {\n      if (this.equals_(haystack[i], needle)) return true;\n    }\n    return false;\n  }\n  return haystack.indexOf(needle) >= 0;\n};\n\njasmine.Env.prototype.addEqualityTester = function(equalityTester) {\n  this.equalityTesters_.push(equalityTester);\n};\n/** No-op base class for Jasmine reporters.\n *\n * @constructor\n */\njasmine.Reporter = function() {\n};\n\n//noinspection JSUnusedLocalSymbols\njasmine.Reporter.prototype.reportRunnerStarting = function(runner) {\n};\n\n//noinspection JSUnusedLocalSymbols\njasmine.Reporter.prototype.reportRunnerResults = function(runner) {\n};\n\n//noinspection JSUnusedLocalSymbols\njasmine.Reporter.prototype.reportSuiteResults = function(suite) {\n};\n\n//noinspection JSUnusedLocalSymbols\njasmine.Reporter.prototype.reportSpecStarting = function(spec) {\n};\n\n//noinspection JSUnusedLocalSymbols\njasmine.Reporter.prototype.reportSpecResults = function(spec) {\n};\n\n//noinspection JSUnusedLocalSymbols\njasmine.Reporter.prototype.log = function(str) {\n};\n\n/**\n * Blocks are functions with executable code that make up a spec.\n *\n * @constructor\n * @param {jasmine.Env} env\n * @param {Function} func\n * @param {jasmine.Spec} spec\n */\njasmine.Block = function(env, func, spec) {\n  this.env = env;\n  this.func = func;\n  this.spec = spec;\n};\n\njasmine.Block.prototype.execute = function(onComplete) {\n  if (!jasmine.CATCH_EXCEPTIONS) {\n    this.func.apply(this.spec);\n  }\n  else {\n    try {\n      this.func.apply(this.spec);\n    } catch (e) {\n      this.spec.fail(e);\n    }\n  }\n  onComplete();\n};\n/** JavaScript API reporter.\n *\n * @constructor\n */\njasmine.JsApiReporter = function() {\n  this.started = false;\n  this.finished = false;\n  this.suites_ = [];\n  this.results_ = {};\n};\n\njasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {\n  this.started = true;\n  var suites = runner.topLevelSuites();\n  for (var i = 0; i < suites.length; i++) {\n    var suite = suites[i];\n    this.suites_.push(this.summarize_(suite));\n  }\n};\n\njasmine.JsApiReporter.prototype.suites = function() {\n  return this.suites_;\n};\n\njasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {\n  var isSuite = suiteOrSpec instanceof jasmine.Suite;\n  var summary = {\n    id: suiteOrSpec.id,\n    name: suiteOrSpec.description,\n    type: isSuite ? 'suite' : 'spec',\n    children: []\n  };\n  \n  if (isSuite) {\n    var children = suiteOrSpec.children();\n    for (var i = 0; i < children.length; i++) {\n      summary.children.push(this.summarize_(children[i]));\n    }\n  }\n  return summary;\n};\n\njasmine.JsApiReporter.prototype.results = function() {\n  return this.results_;\n};\n\njasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {\n  return this.results_[specId];\n};\n\n//noinspection JSUnusedLocalSymbols\njasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {\n  this.finished = true;\n};\n\n//noinspection JSUnusedLocalSymbols\njasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {\n};\n\n//noinspection JSUnusedLocalSymbols\njasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {\n  this.results_[spec.id] = {\n    messages: spec.results().getItems(),\n    result: spec.results().failedCount > 0 ? \"failed\" : \"passed\"\n  };\n};\n\n//noinspection JSUnusedLocalSymbols\njasmine.JsApiReporter.prototype.log = function(str) {\n};\n\njasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){\n  var results = {};\n  for (var i = 0; i < specIds.length; i++) {\n    var specId = specIds[i];\n    results[specId] = this.summarizeResult_(this.results_[specId]);\n  }\n  return results;\n};\n\njasmine.JsApiReporter.prototype.summarizeResult_ = function(result){\n  var summaryMessages = [];\n  var messagesLength = result.messages.length;\n  for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {\n    var resultMessage = result.messages[messageIndex];\n    summaryMessages.push({\n      text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,\n      passed: resultMessage.passed ? resultMessage.passed() : true,\n      type: resultMessage.type,\n      message: resultMessage.message,\n      trace: {\n        stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined\n      }\n    });\n  }\n\n  return {\n    result : result.result,\n    messages : summaryMessages\n  };\n};\n\n/**\n * @constructor\n * @param {jasmine.Env} env\n * @param actual\n * @param {jasmine.Spec} spec\n */\njasmine.Matchers = function(env, actual, spec, opt_isNot) {\n  this.env = env;\n  this.actual = actual;\n  this.spec = spec;\n  this.isNot = opt_isNot || false;\n  this.reportWasCalled_ = false;\n};\n\n// todo: @deprecated as of Jasmine 0.11, remove soon [xw]\njasmine.Matchers.pp = function(str) {\n  throw new Error(\"jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!\");\n};\n\n// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]\njasmine.Matchers.prototype.report = function(result, failing_message, details) {\n  throw new Error(\"As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs\");\n};\n\njasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {\n  for (var methodName in prototype) {\n    if (methodName == 'report') continue;\n    var orig = prototype[methodName];\n    matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);\n  }\n};\n\njasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {\n  return function() {\n    var matcherArgs = jasmine.util.argsToArray(arguments);\n    var result = matcherFunction.apply(this, arguments);\n\n    if (this.isNot) {\n      result = !result;\n    }\n\n    if (this.reportWasCalled_) return result;\n\n    var message;\n    if (!result) {\n      if (this.message) {\n        message = this.message.apply(this, arguments);\n        if (jasmine.isArray_(message)) {\n          message = message[this.isNot ? 1 : 0];\n        }\n      } else {\n        var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });\n        message = \"Expected \" + jasmine.pp(this.actual) + (this.isNot ? \" not \" : \" \") + englishyPredicate;\n        if (matcherArgs.length > 0) {\n          for (var i = 0; i < matcherArgs.length; i++) {\n            if (i > 0) message += \",\";\n            message += \" \" + jasmine.pp(matcherArgs[i]);\n          }\n        }\n        message += \".\";\n      }\n    }\n    var expectationResult = new jasmine.ExpectationResult({\n      matcherName: matcherName,\n      passed: result,\n      expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],\n      actual: this.actual,\n      message: message\n    });\n    this.spec.addMatcherResult(expectationResult);\n    return jasmine.undefined;\n  };\n};\n\n\n\n\n/**\n * toBe: compares the actual to the expected using ===\n * @param expected\n */\njasmine.Matchers.prototype.toBe = function(expected) {\n  return this.actual === expected;\n};\n\n/**\n * toNotBe: compares the actual to the expected using !==\n * @param expected\n * @deprecated as of 1.0. Use not.toBe() instead.\n */\njasmine.Matchers.prototype.toNotBe = function(expected) {\n  return this.actual !== expected;\n};\n\n/**\n * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.\n *\n * @param expected\n */\njasmine.Matchers.prototype.toEqual = function(expected) {\n  return this.env.equals_(this.actual, expected);\n};\n\n/**\n * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual\n * @param expected\n * @deprecated as of 1.0. Use not.toEqual() instead.\n */\njasmine.Matchers.prototype.toNotEqual = function(expected) {\n  return !this.env.equals_(this.actual, expected);\n};\n\n/**\n * Matcher that compares the actual to the expected using a regular expression.  Constructs a RegExp, so takes\n * a pattern or a String.\n *\n * @param expected\n */\njasmine.Matchers.prototype.toMatch = function(expected) {\n  return new RegExp(expected).test(this.actual);\n};\n\n/**\n * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch\n * @param expected\n * @deprecated as of 1.0. Use not.toMatch() instead.\n */\njasmine.Matchers.prototype.toNotMatch = function(expected) {\n  return !(new RegExp(expected).test(this.actual));\n};\n\n/**\n * Matcher that compares the actual to jasmine.undefined.\n */\njasmine.Matchers.prototype.toBeDefined = function() {\n  return (this.actual !== jasmine.undefined);\n};\n\n/**\n * Matcher that compares the actual to jasmine.undefined.\n */\njasmine.Matchers.prototype.toBeUndefined = function() {\n  return (this.actual === jasmine.undefined);\n};\n\n/**\n * Matcher that compares the actual to null.\n */\njasmine.Matchers.prototype.toBeNull = function() {\n  return (this.actual === null);\n};\n\n/**\n * Matcher that compares the actual to NaN.\n */\njasmine.Matchers.prototype.toBeNaN = function() {\n\tthis.message = function() {\n\t\treturn [ \"Expected \" + jasmine.pp(this.actual) + \" to be NaN.\" ];\n\t};\n\n\treturn (this.actual !== this.actual);\n};\n\n/**\n * Matcher that boolean not-nots the actual.\n */\njasmine.Matchers.prototype.toBeTruthy = function() {\n  return !!this.actual;\n};\n\n\n/**\n * Matcher that boolean nots the actual.\n */\njasmine.Matchers.prototype.toBeFalsy = function() {\n  return !this.actual;\n};\n\n\n/**\n * Matcher that checks to see if the actual, a Jasmine spy, was called.\n */\njasmine.Matchers.prototype.toHaveBeenCalled = function() {\n  if (arguments.length > 0) {\n    throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');\n  }\n\n  if (!jasmine.isSpy(this.actual)) {\n    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');\n  }\n\n  this.message = function() {\n    return [\n      \"Expected spy \" + this.actual.identity + \" to have been called.\",\n      \"Expected spy \" + this.actual.identity + \" not to have been called.\"\n    ];\n  };\n\n  return this.actual.wasCalled;\n};\n\n/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */\njasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;\n\n/**\n * Matcher that checks to see if the actual, a Jasmine spy, was not called.\n *\n * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead\n */\njasmine.Matchers.prototype.wasNotCalled = function() {\n  if (arguments.length > 0) {\n    throw new Error('wasNotCalled does not take arguments');\n  }\n\n  if (!jasmine.isSpy(this.actual)) {\n    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');\n  }\n\n  this.message = function() {\n    return [\n      \"Expected spy \" + this.actual.identity + \" to not have been called.\",\n      \"Expected spy \" + this.actual.identity + \" to have been called.\"\n    ];\n  };\n\n  return !this.actual.wasCalled;\n};\n\n/**\n * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.\n *\n * @example\n *\n */\njasmine.Matchers.prototype.toHaveBeenCalledWith = function() {\n  var expectedArgs = jasmine.util.argsToArray(arguments);\n  if (!jasmine.isSpy(this.actual)) {\n    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');\n  }\n  this.message = function() {\n    var invertedMessage = \"Expected spy \" + this.actual.identity + \" not to have been called with \" + jasmine.pp(expectedArgs) + \" but it was.\";\n    var positiveMessage = \"\";\n    if (this.actual.callCount === 0) {\n      positiveMessage = \"Expected spy \" + this.actual.identity + \" to have been called with \" + jasmine.pp(expectedArgs) + \" but it was never called.\";\n    } else {\n      positiveMessage = \"Expected spy \" + this.actual.identity + \" to have been called with \" + jasmine.pp(expectedArgs) + \" but actual calls were \" + jasmine.pp(this.actual.argsForCall).replace(/^\\[ | \\]$/g, '')\n    }\n    return [positiveMessage, invertedMessage];\n  };\n\n  return this.env.contains_(this.actual.argsForCall, expectedArgs);\n};\n\n/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */\njasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;\n\n/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */\njasmine.Matchers.prototype.wasNotCalledWith = function() {\n  var expectedArgs = jasmine.util.argsToArray(arguments);\n  if (!jasmine.isSpy(this.actual)) {\n    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');\n  }\n\n  this.message = function() {\n    return [\n      \"Expected spy not to have been called with \" + jasmine.pp(expectedArgs) + \" but it was\",\n      \"Expected spy to have been called with \" + jasmine.pp(expectedArgs) + \" but it was\"\n    ];\n  };\n\n  return !this.env.contains_(this.actual.argsForCall, expectedArgs);\n};\n\n/**\n * Matcher that checks that the expected item is an element in the actual Array.\n *\n * @param {Object} expected\n */\njasmine.Matchers.prototype.toContain = function(expected) {\n  return this.env.contains_(this.actual, expected);\n};\n\n/**\n * Matcher that checks that the expected item is NOT an element in the actual Array.\n *\n * @param {Object} expected\n * @deprecated as of 1.0. Use not.toContain() instead.\n */\njasmine.Matchers.prototype.toNotContain = function(expected) {\n  return !this.env.contains_(this.actual, expected);\n};\n\njasmine.Matchers.prototype.toBeLessThan = function(expected) {\n  return this.actual < expected;\n};\n\njasmine.Matchers.prototype.toBeGreaterThan = function(expected) {\n  return this.actual > expected;\n};\n\n/**\n * Matcher that checks that the expected item is equal to the actual item\n * up to a given level of decimal precision (default 2).\n *\n * @param {Number} expected\n * @param {Number} precision, as number of decimal places\n */\njasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) {\n  if (!(precision === 0)) {\n    precision = precision || 2;\n  }\n  return Math.abs(expected - this.actual) < (Math.pow(10, -precision) / 2);\n};\n\n/**\n * Matcher that checks that the expected exception was thrown by the actual.\n *\n * @param {String} [expected]\n */\njasmine.Matchers.prototype.toThrow = function(expected) {\n  var result = false;\n  var exception;\n  if (typeof this.actual != 'function') {\n    throw new Error('Actual is not a function');\n  }\n  try {\n    this.actual();\n  } catch (e) {\n    exception = e;\n  }\n  if (exception) {\n    result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));\n  }\n\n  var not = this.isNot ? \"not \" : \"\";\n\n  this.message = function() {\n    if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {\n      return [\"Expected function \" + not + \"to throw\", expected ? expected.message || expected : \"an exception\", \", but it threw\", exception.message || exception].join(' ');\n    } else {\n      return \"Expected function to throw an exception.\";\n    }\n  };\n\n  return result;\n};\n\njasmine.Matchers.Any = function(expectedClass) {\n  this.expectedClass = expectedClass;\n};\n\njasmine.Matchers.Any.prototype.jasmineMatches = function(other) {\n  if (this.expectedClass == String) {\n    return typeof other == 'string' || other instanceof String;\n  }\n\n  if (this.expectedClass == Number) {\n    return typeof other == 'number' || other instanceof Number;\n  }\n\n  if (this.expectedClass == Function) {\n    return typeof other == 'function' || other instanceof Function;\n  }\n\n  if (this.expectedClass == Object) {\n    return typeof other == 'object';\n  }\n\n  return other instanceof this.expectedClass;\n};\n\njasmine.Matchers.Any.prototype.jasmineToString = function() {\n  return '<jasmine.any(' + this.expectedClass + ')>';\n};\n\njasmine.Matchers.ObjectContaining = function (sample) {\n  this.sample = sample;\n};\n\njasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {\n  mismatchKeys = mismatchKeys || [];\n  mismatchValues = mismatchValues || [];\n\n  var env = jasmine.getEnv();\n\n  var hasKey = function(obj, keyName) {\n    return obj != null && obj[keyName] !== jasmine.undefined;\n  };\n\n  for (var property in this.sample) {\n    if (!hasKey(other, property) && hasKey(this.sample, property)) {\n      mismatchKeys.push(\"expected has key '\" + property + \"', but missing from actual.\");\n    }\n    else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) {\n      mismatchValues.push(\"'\" + property + \"' was '\" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + \"' in expected, but was '\" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + \"' in actual.\");\n    }\n  }\n\n  return (mismatchKeys.length === 0 && mismatchValues.length === 0);\n};\n\njasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () {\n  return \"<jasmine.objectContaining(\" + jasmine.pp(this.sample) + \")>\";\n};\n// Mock setTimeout, clearTimeout\n// Contributed by Pivotal Computer Systems, www.pivotalsf.com\n\njasmine.FakeTimer = function() {\n  this.reset();\n\n  var self = this;\n  self.setTimeout = function(funcToCall, millis) {\n    self.timeoutsMade++;\n    self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);\n    return self.timeoutsMade;\n  };\n\n  self.setInterval = function(funcToCall, millis) {\n    self.timeoutsMade++;\n    self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);\n    return self.timeoutsMade;\n  };\n\n  self.clearTimeout = function(timeoutKey) {\n    self.scheduledFunctions[timeoutKey] = jasmine.undefined;\n  };\n\n  self.clearInterval = function(timeoutKey) {\n    self.scheduledFunctions[timeoutKey] = jasmine.undefined;\n  };\n\n};\n\njasmine.FakeTimer.prototype.reset = function() {\n  this.timeoutsMade = 0;\n  this.scheduledFunctions = {};\n  this.nowMillis = 0;\n};\n\njasmine.FakeTimer.prototype.tick = function(millis) {\n  var oldMillis = this.nowMillis;\n  var newMillis = oldMillis + millis;\n  this.runFunctionsWithinRange(oldMillis, newMillis);\n  this.nowMillis = newMillis;\n};\n\njasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {\n  var scheduledFunc;\n  var funcsToRun = [];\n  for (var timeoutKey in this.scheduledFunctions) {\n    scheduledFunc = this.scheduledFunctions[timeoutKey];\n    if (scheduledFunc != jasmine.undefined &&\n        scheduledFunc.runAtMillis >= oldMillis &&\n        scheduledFunc.runAtMillis <= nowMillis) {\n      funcsToRun.push(scheduledFunc);\n      this.scheduledFunctions[timeoutKey] = jasmine.undefined;\n    }\n  }\n\n  if (funcsToRun.length > 0) {\n    funcsToRun.sort(function(a, b) {\n      return a.runAtMillis - b.runAtMillis;\n    });\n    for (var i = 0; i < funcsToRun.length; ++i) {\n      try {\n        var funcToRun = funcsToRun[i];\n        this.nowMillis = funcToRun.runAtMillis;\n        funcToRun.funcToCall();\n        if (funcToRun.recurring) {\n          this.scheduleFunction(funcToRun.timeoutKey,\n              funcToRun.funcToCall,\n              funcToRun.millis,\n              true);\n        }\n      } catch(e) {\n      }\n    }\n    this.runFunctionsWithinRange(oldMillis, nowMillis);\n  }\n};\n\njasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {\n  this.scheduledFunctions[timeoutKey] = {\n    runAtMillis: this.nowMillis + millis,\n    funcToCall: funcToCall,\n    recurring: recurring,\n    timeoutKey: timeoutKey,\n    millis: millis\n  };\n};\n\n/**\n * @namespace\n */\njasmine.Clock = {\n  defaultFakeTimer: new jasmine.FakeTimer(),\n\n  reset: function() {\n    jasmine.Clock.assertInstalled();\n    jasmine.Clock.defaultFakeTimer.reset();\n  },\n\n  tick: function(millis) {\n    jasmine.Clock.assertInstalled();\n    jasmine.Clock.defaultFakeTimer.tick(millis);\n  },\n\n  runFunctionsWithinRange: function(oldMillis, nowMillis) {\n    jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);\n  },\n\n  scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {\n    jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);\n  },\n\n  useMock: function() {\n    if (!jasmine.Clock.isInstalled()) {\n      var spec = jasmine.getEnv().currentSpec;\n      spec.after(jasmine.Clock.uninstallMock);\n\n      jasmine.Clock.installMock();\n    }\n  },\n\n  installMock: function() {\n    jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;\n  },\n\n  uninstallMock: function() {\n    jasmine.Clock.assertInstalled();\n    jasmine.Clock.installed = jasmine.Clock.real;\n  },\n\n  real: {\n    setTimeout: jasmine.getGlobal().setTimeout,\n    clearTimeout: jasmine.getGlobal().clearTimeout,\n    setInterval: jasmine.getGlobal().setInterval,\n    clearInterval: jasmine.getGlobal().clearInterval\n  },\n\n  assertInstalled: function() {\n    if (!jasmine.Clock.isInstalled()) {\n      throw new Error(\"Mock clock is not installed, use jasmine.Clock.useMock()\");\n    }\n  },\n\n  isInstalled: function() {\n    return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;\n  },\n\n  installed: null\n};\njasmine.Clock.installed = jasmine.Clock.real;\n\n//else for IE support\njasmine.getGlobal().setTimeout = function(funcToCall, millis) {\n  if (jasmine.Clock.installed.setTimeout.apply) {\n    return jasmine.Clock.installed.setTimeout.apply(this, arguments);\n  } else {\n    return jasmine.Clock.installed.setTimeout(funcToCall, millis);\n  }\n};\n\njasmine.getGlobal().setInterval = function(funcToCall, millis) {\n  if (jasmine.Clock.installed.setInterval.apply) {\n    return jasmine.Clock.installed.setInterval.apply(this, arguments);\n  } else {\n    return jasmine.Clock.installed.setInterval(funcToCall, millis);\n  }\n};\n\njasmine.getGlobal().clearTimeout = function(timeoutKey) {\n  if (jasmine.Clock.installed.clearTimeout.apply) {\n    return jasmine.Clock.installed.clearTimeout.apply(this, arguments);\n  } else {\n    return jasmine.Clock.installed.clearTimeout(timeoutKey);\n  }\n};\n\njasmine.getGlobal().clearInterval = function(timeoutKey) {\n  if (jasmine.Clock.installed.clearTimeout.apply) {\n    return jasmine.Clock.installed.clearInterval.apply(this, arguments);\n  } else {\n    return jasmine.Clock.installed.clearInterval(timeoutKey);\n  }\n};\n\n/**\n * @constructor\n */\njasmine.MultiReporter = function() {\n  this.subReporters_ = [];\n};\njasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);\n\njasmine.MultiReporter.prototype.addReporter = function(reporter) {\n  this.subReporters_.push(reporter);\n};\n\n(function() {\n  var functionNames = [\n    \"reportRunnerStarting\",\n    \"reportRunnerResults\",\n    \"reportSuiteResults\",\n    \"reportSpecStarting\",\n    \"reportSpecResults\",\n    \"log\"\n  ];\n  for (var i = 0; i < functionNames.length; i++) {\n    var functionName = functionNames[i];\n    jasmine.MultiReporter.prototype[functionName] = (function(functionName) {\n      return function() {\n        for (var j = 0; j < this.subReporters_.length; j++) {\n          var subReporter = this.subReporters_[j];\n          if (subReporter[functionName]) {\n            subReporter[functionName].apply(subReporter, arguments);\n          }\n        }\n      };\n    })(functionName);\n  }\n})();\n/**\n * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults\n *\n * @constructor\n */\njasmine.NestedResults = function() {\n  /**\n   * The total count of results\n   */\n  this.totalCount = 0;\n  /**\n   * Number of passed results\n   */\n  this.passedCount = 0;\n  /**\n   * Number of failed results\n   */\n  this.failedCount = 0;\n  /**\n   * Was this suite/spec skipped?\n   */\n  this.skipped = false;\n  /**\n   * @ignore\n   */\n  this.items_ = [];\n};\n\n/**\n * Roll up the result counts.\n *\n * @param result\n */\njasmine.NestedResults.prototype.rollupCounts = function(result) {\n  this.totalCount += result.totalCount;\n  this.passedCount += result.passedCount;\n  this.failedCount += result.failedCount;\n};\n\n/**\n * Adds a log message.\n * @param values Array of message parts which will be concatenated later.\n */\njasmine.NestedResults.prototype.log = function(values) {\n  this.items_.push(new jasmine.MessageResult(values));\n};\n\n/**\n * Getter for the results: message & results.\n */\njasmine.NestedResults.prototype.getItems = function() {\n  return this.items_;\n};\n\n/**\n * Adds a result, tracking counts (total, passed, & failed)\n * @param {jasmine.ExpectationResult|jasmine.NestedResults} result\n */\njasmine.NestedResults.prototype.addResult = function(result) {\n  if (result.type != 'log') {\n    if (result.items_) {\n      this.rollupCounts(result);\n    } else {\n      this.totalCount++;\n      if (result.passed()) {\n        this.passedCount++;\n      } else {\n        this.failedCount++;\n      }\n    }\n  }\n  this.items_.push(result);\n};\n\n/**\n * @returns {Boolean} True if <b>everything</b> below passed\n */\njasmine.NestedResults.prototype.passed = function() {\n  return this.passedCount === this.totalCount;\n};\n/**\n * Base class for pretty printing for expectation results.\n */\njasmine.PrettyPrinter = function() {\n  this.ppNestLevel_ = 0;\n};\n\n/**\n * Formats a value in a nice, human-readable string.\n *\n * @param value\n */\njasmine.PrettyPrinter.prototype.format = function(value) {\n  this.ppNestLevel_++;\n  try {\n    if (value === jasmine.undefined) {\n      this.emitScalar('undefined');\n    } else if (value === null) {\n      this.emitScalar('null');\n    } else if (value === jasmine.getGlobal()) {\n      this.emitScalar('<global>');\n    } else if (value.jasmineToString) {\n      this.emitScalar(value.jasmineToString());\n    } else if (typeof value === 'string') {\n      this.emitString(value);\n    } else if (jasmine.isSpy(value)) {\n      this.emitScalar(\"spy on \" + value.identity);\n    } else if (value instanceof RegExp) {\n      this.emitScalar(value.toString());\n    } else if (typeof value === 'function') {\n      this.emitScalar('Function');\n    } else if (typeof value.nodeType === 'number') {\n      this.emitScalar('HTMLNode');\n    } else if (value instanceof Date) {\n      this.emitScalar('Date(' + value + ')');\n    } else if (value.__Jasmine_been_here_before__) {\n      this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>');\n    } else if (jasmine.isArray_(value) || typeof value == 'object') {\n      value.__Jasmine_been_here_before__ = true;\n      if (jasmine.isArray_(value)) {\n        this.emitArray(value);\n      } else {\n        this.emitObject(value);\n      }\n      delete value.__Jasmine_been_here_before__;\n    } else {\n      this.emitScalar(value.toString());\n    }\n  } finally {\n    this.ppNestLevel_--;\n  }\n};\n\njasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {\n  for (var property in obj) {\n    if (!obj.hasOwnProperty(property)) continue;\n    if (property == '__Jasmine_been_here_before__') continue;\n    fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && \n                                         obj.__lookupGetter__(property) !== null) : false);\n  }\n};\n\njasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;\njasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;\njasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;\njasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;\n\njasmine.StringPrettyPrinter = function() {\n  jasmine.PrettyPrinter.call(this);\n\n  this.string = '';\n};\njasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);\n\njasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {\n  this.append(value);\n};\n\njasmine.StringPrettyPrinter.prototype.emitString = function(value) {\n  this.append(\"'\" + value + \"'\");\n};\n\njasmine.StringPrettyPrinter.prototype.emitArray = function(array) {\n  if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) {\n    this.append(\"Array\");\n    return;\n  }\n\n  this.append('[ ');\n  for (var i = 0; i < array.length; i++) {\n    if (i > 0) {\n      this.append(', ');\n    }\n    this.format(array[i]);\n  }\n  this.append(' ]');\n};\n\njasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {\n  if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) {\n    this.append(\"Object\");\n    return;\n  }\n\n  var self = this;\n  this.append('{ ');\n  var first = true;\n\n  this.iterateObject(obj, function(property, isGetter) {\n    if (first) {\n      first = false;\n    } else {\n      self.append(', ');\n    }\n\n    self.append(property);\n    self.append(' : ');\n    if (isGetter) {\n      self.append('<getter>');\n    } else {\n      self.format(obj[property]);\n    }\n  });\n\n  this.append(' }');\n};\n\njasmine.StringPrettyPrinter.prototype.append = function(value) {\n  this.string += value;\n};\njasmine.Queue = function(env) {\n  this.env = env;\n\n  // parallel to blocks. each true value in this array means the block will\n  // get executed even if we abort\n  this.ensured = [];\n  this.blocks = [];\n  this.running = false;\n  this.index = 0;\n  this.offset = 0;\n  this.abort = false;\n};\n\njasmine.Queue.prototype.addBefore = function(block, ensure) {\n  if (ensure === jasmine.undefined) {\n    ensure = false;\n  }\n\n  this.blocks.unshift(block);\n  this.ensured.unshift(ensure);\n};\n\njasmine.Queue.prototype.add = function(block, ensure) {\n  if (ensure === jasmine.undefined) {\n    ensure = false;\n  }\n\n  this.blocks.push(block);\n  this.ensured.push(ensure);\n};\n\njasmine.Queue.prototype.insertNext = function(block, ensure) {\n  if (ensure === jasmine.undefined) {\n    ensure = false;\n  }\n\n  this.ensured.splice((this.index + this.offset + 1), 0, ensure);\n  this.blocks.splice((this.index + this.offset + 1), 0, block);\n  this.offset++;\n};\n\njasmine.Queue.prototype.start = function(onComplete) {\n  this.running = true;\n  this.onComplete = onComplete;\n  this.next_();\n};\n\njasmine.Queue.prototype.isRunning = function() {\n  return this.running;\n};\n\njasmine.Queue.LOOP_DONT_RECURSE = true;\n\njasmine.Queue.prototype.next_ = function() {\n  var self = this;\n  var goAgain = true;\n\n  while (goAgain) {\n    goAgain = false;\n    \n    if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) {\n      var calledSynchronously = true;\n      var completedSynchronously = false;\n\n      var onComplete = function () {\n        if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {\n          completedSynchronously = true;\n          return;\n        }\n\n        if (self.blocks[self.index].abort) {\n          self.abort = true;\n        }\n\n        self.offset = 0;\n        self.index++;\n\n        var now = new Date().getTime();\n        if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {\n          self.env.lastUpdate = now;\n          self.env.setTimeout(function() {\n            self.next_();\n          }, 0);\n        } else {\n          if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {\n            goAgain = true;\n          } else {\n            self.next_();\n          }\n        }\n      };\n      self.blocks[self.index].execute(onComplete);\n\n      calledSynchronously = false;\n      if (completedSynchronously) {\n        onComplete();\n      }\n      \n    } else {\n      self.running = false;\n      if (self.onComplete) {\n        self.onComplete();\n      }\n    }\n  }\n};\n\njasmine.Queue.prototype.results = function() {\n  var results = new jasmine.NestedResults();\n  for (var i = 0; i < this.blocks.length; i++) {\n    if (this.blocks[i].results) {\n      results.addResult(this.blocks[i].results());\n    }\n  }\n  return results;\n};\n\n\n/**\n * Runner\n *\n * @constructor\n * @param {jasmine.Env} env\n */\njasmine.Runner = function(env) {\n  var self = this;\n  self.env = env;\n  self.queue = new jasmine.Queue(env);\n  self.before_ = [];\n  self.after_ = [];\n  self.suites_ = [];\n};\n\njasmine.Runner.prototype.execute = function() {\n  var self = this;\n  if (self.env.reporter.reportRunnerStarting) {\n    self.env.reporter.reportRunnerStarting(this);\n  }\n  self.queue.start(function () {\n    self.finishCallback();\n  });\n};\n\njasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {\n  beforeEachFunction.typeName = 'beforeEach';\n  this.before_.splice(0,0,beforeEachFunction);\n};\n\njasmine.Runner.prototype.afterEach = function(afterEachFunction) {\n  afterEachFunction.typeName = 'afterEach';\n  this.after_.splice(0,0,afterEachFunction);\n};\n\n\njasmine.Runner.prototype.finishCallback = function() {\n  this.env.reporter.reportRunnerResults(this);\n};\n\njasmine.Runner.prototype.addSuite = function(suite) {\n  this.suites_.push(suite);\n};\n\njasmine.Runner.prototype.add = function(block) {\n  if (block instanceof jasmine.Suite) {\n    this.addSuite(block);\n  }\n  this.queue.add(block);\n};\n\njasmine.Runner.prototype.specs = function () {\n  var suites = this.suites();\n  var specs = [];\n  for (var i = 0; i < suites.length; i++) {\n    specs = specs.concat(suites[i].specs());\n  }\n  return specs;\n};\n\njasmine.Runner.prototype.suites = function() {\n  return this.suites_;\n};\n\njasmine.Runner.prototype.topLevelSuites = function() {\n  var topLevelSuites = [];\n  for (var i = 0; i < this.suites_.length; i++) {\n    if (!this.suites_[i].parentSuite) {\n      topLevelSuites.push(this.suites_[i]);\n    }\n  }\n  return topLevelSuites;\n};\n\njasmine.Runner.prototype.results = function() {\n  return this.queue.results();\n};\n/**\n * Internal representation of a Jasmine specification, or test.\n *\n * @constructor\n * @param {jasmine.Env} env\n * @param {jasmine.Suite} suite\n * @param {String} description\n */\njasmine.Spec = function(env, suite, description) {\n  if (!env) {\n    throw new Error('jasmine.Env() required');\n  }\n  if (!suite) {\n    throw new Error('jasmine.Suite() required');\n  }\n  var spec = this;\n  spec.id = env.nextSpecId ? env.nextSpecId() : null;\n  spec.env = env;\n  spec.suite = suite;\n  spec.description = description;\n  spec.queue = new jasmine.Queue(env);\n\n  spec.afterCallbacks = [];\n  spec.spies_ = [];\n\n  spec.results_ = new jasmine.NestedResults();\n  spec.results_.description = description;\n  spec.matchersClass = null;\n};\n\njasmine.Spec.prototype.getFullName = function() {\n  return this.suite.getFullName() + ' ' + this.description + '.';\n};\n\n\njasmine.Spec.prototype.results = function() {\n  return this.results_;\n};\n\n/**\n * All parameters are pretty-printed and concatenated together, then written to the spec's output.\n *\n * Be careful not to leave calls to <code>jasmine.log</code> in production code.\n */\njasmine.Spec.prototype.log = function() {\n  return this.results_.log(arguments);\n};\n\njasmine.Spec.prototype.runs = function (func) {\n  var block = new jasmine.Block(this.env, func, this);\n  this.addToQueue(block);\n  return this;\n};\n\njasmine.Spec.prototype.addToQueue = function (block) {\n  if (this.queue.isRunning()) {\n    this.queue.insertNext(block);\n  } else {\n    this.queue.add(block);\n  }\n};\n\n/**\n * @param {jasmine.ExpectationResult} result\n */\njasmine.Spec.prototype.addMatcherResult = function(result) {\n  this.results_.addResult(result);\n};\n\njasmine.Spec.prototype.expect = function(actual) {\n  var positive = new (this.getMatchersClass_())(this.env, actual, this);\n  positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);\n  return positive;\n};\n\n/**\n * Waits a fixed time period before moving to the next block.\n *\n * @deprecated Use waitsFor() instead\n * @param {Number} timeout milliseconds to wait\n */\njasmine.Spec.prototype.waits = function(timeout) {\n  var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);\n  this.addToQueue(waitsFunc);\n  return this;\n};\n\n/**\n * Waits for the latchFunction to return true before proceeding to the next block.\n *\n * @param {Function} latchFunction\n * @param {String} optional_timeoutMessage\n * @param {Number} optional_timeout\n */\njasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {\n  var latchFunction_ = null;\n  var optional_timeoutMessage_ = null;\n  var optional_timeout_ = null;\n\n  for (var i = 0; i < arguments.length; i++) {\n    var arg = arguments[i];\n    switch (typeof arg) {\n      case 'function':\n        latchFunction_ = arg;\n        break;\n      case 'string':\n        optional_timeoutMessage_ = arg;\n        break;\n      case 'number':\n        optional_timeout_ = arg;\n        break;\n    }\n  }\n\n  var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);\n  this.addToQueue(waitsForFunc);\n  return this;\n};\n\njasmine.Spec.prototype.fail = function (e) {\n  var expectationResult = new jasmine.ExpectationResult({\n    passed: false,\n    message: e ? jasmine.util.formatException(e) : 'Exception',\n    trace: { stack: e.stack }\n  });\n  this.results_.addResult(expectationResult);\n};\n\njasmine.Spec.prototype.getMatchersClass_ = function() {\n  return this.matchersClass || this.env.matchersClass;\n};\n\njasmine.Spec.prototype.addMatchers = function(matchersPrototype) {\n  var parent = this.getMatchersClass_();\n  var newMatchersClass = function() {\n    parent.apply(this, arguments);\n  };\n  jasmine.util.inherit(newMatchersClass, parent);\n  jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);\n  this.matchersClass = newMatchersClass;\n};\n\njasmine.Spec.prototype.finishCallback = function() {\n  this.env.reporter.reportSpecResults(this);\n};\n\njasmine.Spec.prototype.finish = function(onComplete) {\n  this.removeAllSpies();\n  this.finishCallback();\n  if (onComplete) {\n    onComplete();\n  }\n};\n\njasmine.Spec.prototype.after = function(doAfter) {\n  if (this.queue.isRunning()) {\n    this.queue.add(new jasmine.Block(this.env, doAfter, this), true);\n  } else {\n    this.afterCallbacks.unshift(doAfter);\n  }\n};\n\njasmine.Spec.prototype.execute = function(onComplete) {\n  var spec = this;\n  if (!spec.env.specFilter(spec)) {\n    spec.results_.skipped = true;\n    spec.finish(onComplete);\n    return;\n  }\n\n  this.env.reporter.reportSpecStarting(this);\n\n  spec.env.currentSpec = spec;\n\n  spec.addBeforesAndAftersToQueue();\n\n  spec.queue.start(function () {\n    spec.finish(onComplete);\n  });\n};\n\njasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {\n  var runner = this.env.currentRunner();\n  var i;\n\n  for (var suite = this.suite; suite; suite = suite.parentSuite) {\n    for (i = 0; i < suite.before_.length; i++) {\n      this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));\n    }\n  }\n  for (i = 0; i < runner.before_.length; i++) {\n    this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));\n  }\n  for (i = 0; i < this.afterCallbacks.length; i++) {\n    this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this), true);\n  }\n  for (suite = this.suite; suite; suite = suite.parentSuite) {\n    for (i = 0; i < suite.after_.length; i++) {\n      this.queue.add(new jasmine.Block(this.env, suite.after_[i], this), true);\n    }\n  }\n  for (i = 0; i < runner.after_.length; i++) {\n    this.queue.add(new jasmine.Block(this.env, runner.after_[i], this), true);\n  }\n};\n\njasmine.Spec.prototype.explodes = function() {\n  throw 'explodes function should not have been called';\n};\n\njasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {\n  if (obj == jasmine.undefined) {\n    throw \"spyOn could not find an object to spy upon for \" + methodName + \"()\";\n  }\n\n  if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {\n    throw methodName + '() method does not exist';\n  }\n\n  if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {\n    throw new Error(methodName + ' has already been spied upon');\n  }\n\n  var spyObj = jasmine.createSpy(methodName);\n\n  this.spies_.push(spyObj);\n  spyObj.baseObj = obj;\n  spyObj.methodName = methodName;\n  spyObj.originalValue = obj[methodName];\n\n  obj[methodName] = spyObj;\n\n  return spyObj;\n};\n\njasmine.Spec.prototype.removeAllSpies = function() {\n  for (var i = 0; i < this.spies_.length; i++) {\n    var spy = this.spies_[i];\n    spy.baseObj[spy.methodName] = spy.originalValue;\n  }\n  this.spies_ = [];\n};\n\n/**\n * Internal representation of a Jasmine suite.\n *\n * @constructor\n * @param {jasmine.Env} env\n * @param {String} description\n * @param {Function} specDefinitions\n * @param {jasmine.Suite} parentSuite\n */\njasmine.Suite = function(env, description, specDefinitions, parentSuite) {\n  var self = this;\n  self.id = env.nextSuiteId ? env.nextSuiteId() : null;\n  self.description = description;\n  self.queue = new jasmine.Queue(env);\n  self.parentSuite = parentSuite;\n  self.env = env;\n  self.before_ = [];\n  self.after_ = [];\n  self.children_ = [];\n  self.suites_ = [];\n  self.specs_ = [];\n};\n\njasmine.Suite.prototype.getFullName = function() {\n  var fullName = this.description;\n  for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {\n    fullName = parentSuite.description + ' ' + fullName;\n  }\n  return fullName;\n};\n\njasmine.Suite.prototype.finish = function(onComplete) {\n  this.env.reporter.reportSuiteResults(this);\n  this.finished = true;\n  if (typeof(onComplete) == 'function') {\n    onComplete();\n  }\n};\n\njasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {\n  beforeEachFunction.typeName = 'beforeEach';\n  this.before_.unshift(beforeEachFunction);\n};\n\njasmine.Suite.prototype.afterEach = function(afterEachFunction) {\n  afterEachFunction.typeName = 'afterEach';\n  this.after_.unshift(afterEachFunction);\n};\n\njasmine.Suite.prototype.results = function() {\n  return this.queue.results();\n};\n\njasmine.Suite.prototype.add = function(suiteOrSpec) {\n  this.children_.push(suiteOrSpec);\n  if (suiteOrSpec instanceof jasmine.Suite) {\n    this.suites_.push(suiteOrSpec);\n    this.env.currentRunner().addSuite(suiteOrSpec);\n  } else {\n    this.specs_.push(suiteOrSpec);\n  }\n  this.queue.add(suiteOrSpec);\n};\n\njasmine.Suite.prototype.specs = function() {\n  return this.specs_;\n};\n\njasmine.Suite.prototype.suites = function() {\n  return this.suites_;\n};\n\njasmine.Suite.prototype.children = function() {\n  return this.children_;\n};\n\njasmine.Suite.prototype.execute = function(onComplete) {\n  var self = this;\n  this.queue.start(function () {\n    self.finish(onComplete);\n  });\n};\njasmine.WaitsBlock = function(env, timeout, spec) {\n  this.timeout = timeout;\n  jasmine.Block.call(this, env, null, spec);\n};\n\njasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);\n\njasmine.WaitsBlock.prototype.execute = function (onComplete) {\n  if (jasmine.VERBOSE) {\n    this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');\n  }\n  this.env.setTimeout(function () {\n    onComplete();\n  }, this.timeout);\n};\n/**\n * A block which waits for some condition to become true, with timeout.\n *\n * @constructor\n * @extends jasmine.Block\n * @param {jasmine.Env} env The Jasmine environment.\n * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.\n * @param {Function} latchFunction A function which returns true when the desired condition has been met.\n * @param {String} message The message to display if the desired condition hasn't been met within the given time period.\n * @param {jasmine.Spec} spec The Jasmine spec.\n */\njasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {\n  this.timeout = timeout || env.defaultTimeoutInterval;\n  this.latchFunction = latchFunction;\n  this.message = message;\n  this.totalTimeSpentWaitingForLatch = 0;\n  jasmine.Block.call(this, env, null, spec);\n};\njasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);\n\njasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;\n\njasmine.WaitsForBlock.prototype.execute = function(onComplete) {\n  if (jasmine.VERBOSE) {\n    this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));\n  }\n  var latchFunctionResult;\n  try {\n    latchFunctionResult = this.latchFunction.apply(this.spec);\n  } catch (e) {\n    this.spec.fail(e);\n    onComplete();\n    return;\n  }\n\n  if (latchFunctionResult) {\n    onComplete();\n  } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {\n    var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');\n    this.spec.fail({\n      name: 'timeout',\n      message: message\n    });\n\n    this.abort = true;\n    onComplete();\n  } else {\n    this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;\n    var self = this;\n    this.env.setTimeout(function() {\n      self.execute(onComplete);\n    }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);\n  }\n};\n\njasmine.version_= {\n  \"major\": 1,\n  \"minor\": 3,\n  \"build\": 1,\n  \"revision\": 1354556913\n};\n"
  },
  {
    "path": "tools/jasmine-browserstack/jasmine-browserstack.js",
    "content": "(function (root, factory) {\n  if (typeof define === 'function' && define.amd) {\n    define(['jasmine'], factory);\n  } else {\n    factory(jasmine);\n  }\n}(this, function (jasmine) {\n  function stack(err) {\n    var str = err.stack || err.toString();\n\n    if (!~str.indexOf(err.message)) {\n      str = err.message + '\\n' + str;\n    }\n\n    if ('[object Error]' == str) {\n      str = err.message;\n    }\n\n    if (!err.stack && err.sourceURL && err.line !== undefined) {\n      str += '\\n(' + err.sourceURL + ':' + err.line + ')';\n    }\n    return str.replace(/^/gm, '  ');\n  }\n\n  function BrowserStackReporter() {\n    this.stats = {\n      suites: 0,\n      tests: 0,\n      passes: 0,\n      pending: 0,\n      failures: 0\n    };\n    this.tests = [];\n  }\n\n  BrowserStackReporter.prototype.reportRunnerStarting = function (runner) {\n    this.stats.start = new Date();\n  };\n\n  BrowserStackReporter.prototype.reportSpecStarting = function (spec) {\n    spec.startedAt = new Date().getTime();\n  };\n\n  BrowserStackReporter.prototype.reportSpecResults = function (spec) {\n    var currentTime = new Date().getTime();\n    spec.duration = currentTime - spec.startedAt;\n\n    var result = spec.results();\n\n    var test = {\n      status: null,\n      title: spec.getFullName().replace(/#/g, ''),\n      duration: currentTime - spec.startedAt\n    };\n\n    if (result.skipped) {\n      this.stats.pending += 1;\n      test.status = 'skipped';\n    } else if (result.failedCount === 0) {\n      this.stats.passes += 1;\n      test.status = 'passed';\n    } else {\n      var items = result.getItems(),\n          message = [];\n\n      for (var i = 0; i < items.length; i += 1) {\n        message.push(stack(items[i].trace));\n      }\n\n      test.err = message;\n      test.status = 'failed';\n      this.stats.failures += 1;\n    }\n\n    this.stats.tests += 1;\n    this.tests.push(test);\n  };\n\n  BrowserStackReporter.prototype.reportSuiteResults = function (suite) {\n  };\n\n  BrowserStackReporter.prototype.reportRunnerResults = function (runner) {\n    var suites = runner.suites();\n\n    this.stats.end = new Date();\n    this.stats.duration = this.stats.end - this.stats.end;\n\n    this.stats.suites = suites.length;\n\n    var result = '1..' + this.stats.tests + '\\n';\n\n    for (var i = 0; i < this.tests.length; i += 1) {\n      var count = i + 1;\n\n      if (this.tests[i].status === 'pending') {\n        result += 'ok ' + count + ' ' + this.tests[i].title + ' # SKIP -\\n';\n      } else if (this.tests[i].status === 'failed') {\n        result += 'not ok ' + count + ' ' + this.tests[i].title + '\\n';\n        for (var j = 0; j < this.tests[i].err.length; j += 1) {\n          result += this.tests[i].err[j] + '\\n';\n        }\n      } else {\n        result += 'ok ' + count + ' ' + this.tests[i].title + '\\n';\n      }\n    }\n\n    result += '# tests ' + this.stats.tests + '\\n';\n    result += '# pass ' + this.stats.passes + '\\n';\n    result += '# fail ' + this.stats.failures + '\\n';\n\n    if (/browser=/i.test(window.location.search)) {\n      var xhr = null;\n\n      if (window.XMLHttpRequest) {\n        xhr = new XMLHttpRequest();\n      } else {\n        xhr = new ActiveXObject('Microsoft.XMLHTTP');\n      }\n\n      xhr.open('POST', window.location.href);\n      xhr.setRequestHeader('Content-Type', 'text/plain');\n      xhr.send(result);\n    }\n  };\n\n  // Attach to the jasmine object like many other reporters do\n  jasmine.BrowserStackReporter = BrowserStackReporter;\n}));\n"
  },
  {
    "path": "tools/jasmine-phantomjs/jasmine-phantomjs.js",
    "content": "var webpage = require('webpage'),\n    system = require('system');\n\nif (system.args.length !== 1) {\n  var page = webpage.create();\n\n  page.onConsoleMessage = function (msg) {\n    console.log(msg);\n    if (/SUCCESS|FAILURE/.test(msg)) {\n      if (/SUCCESS/.test(msg)) {\n        phantom.exit(0);\n      } else {\n        phantom.exit(1);\n      }\n    }\n  };\n\n  page.open(system.args[1], function (status) {\n    if (status !== 'success') {\n      phantom.exit(1);\n    }\n  });\n} else {\n  console.log('Usage: jasmine-phantomjs [FILE]');\n  phantom.exit(1);\n}\n"
  },
  {
    "path": "tools/jasmine-phantomjs/terminal-reporter.js",
    "content": "/**\n * Taken from https://github.com/larrymyers/jasmine-reporters\n * Licensed under the MIT license.\n */\n(function() {\n    if (! jasmine) {\n        throw new Exception(\"jasmine library does not exist in global namespace!\");\n    }\n\n    /**\n     * Basic reporter that outputs spec results to the terminal.\n     * Use this reporter in your build pipeline.\n     *\n     * Usage:\n     *\n     * jasmine.getEnv().addReporter(new jasmine.TerminalReporter({\n           verbosity: 2,\n           color: true\n       }));\n     * jasmine.getEnv().execute();\n     */\n    var DEFAULT_VERBOSITY = 2,\n        ATTRIBUTES_TO_ANSI = {\n            \"off\": 0,\n            \"bold\": 1,\n            \"red\": 31,\n            \"green\": 32\n        };\n\n    var TerminalReporter = function(params) {\n        var parameters = params || {};\n\n        if (parameters.verbosity === 0) {\n            this.verbosity = 0;\n        } else {\n            this.verbosity = parameters.verbosity || DEFAULT_VERBOSITY;\n        }\n        this.color = parameters.color;\n\n        this.started = false;\n        this.finished = false;\n        this.current_suite_hierarchy = [];\n        this.indent_string = '  ';\n    };\n\n    TerminalReporter.prototype = {\n        reportRunnerResults: function(runner) {\n            var dur = (new Date()).getTime() - this.start_time,\n                failed = this.executed_specs - this.passed_specs,\n                spec_str = this.executed_specs + (this.executed_specs === 1 ? \" spec, \" : \" specs, \"),\n                fail_str = failed + (failed === 1 ? \" failure in \" : \" failures in \"),\n                summary_str = spec_str + fail_str + (dur/1000) + \"s.\",\n                result_str = (failed && \"FAILURE: \" || \"SUCCESS: \") + summary_str,\n                result_color = failed && \"red+bold\" || \"green+bold\";\n\n            if (this.verbosity === 2) {\n                this.log(\"\");\n            }\n\n            if (this.verbosity > 0) {\n                this.log(this.inColor(result_str, result_color));\n            }\n\n            this.finished = true;\n        },\n\n        reportRunnerStarting: function(runner) {\n            this.started = true;\n            this.start_time = (new Date()).getTime();\n            this.executed_specs = 0;\n            this.passed_specs = 0;\n        },\n\n        reportSpecResults: function(spec) {\n            var color = \"red\";\n\n            if (spec.results().passed()) {\n                this.passed_specs++;\n                color = \"green\";\n            }\n\n            if (this.verbosity === 2) {\n                var resultText = 'F';\n\n                if (spec.results().passed()) {\n                    resultText = '.';\n                }\n                this.log(this.inColor(resultText, color));\n            } else if (this.verbosity > 2) {\n                resultText = \"Failed\";\n\n                if (spec.results().passed()) {\n                    resultText = 'Passed';\n                }\n                this.log(' ' + this.inColor(resultText, color));\n            }\n        },\n\n        reportSpecStarting: function(spec) {\n            this.executed_specs++;\n            if (this.verbosity > 2) {\n                this.logCurrentSuite(spec.suite);\n\n                this.log(this.indentWithCurrentLevel(this.indent_string + spec.description + ' ...'));\n            }\n        },\n\n        reportSuiteResults: function(suite) {\n            var results = suite.results(),\n                failed = results.totalCount - results.passedCount,\n                color = failed ? \"red+bold\" : \"green+bold\";\n\n            if (this.verbosity > 2) {\n                this.logCurrentSuite(suite);\n                this.log(this.indentWithCurrentLevel(this.inColor(results.passedCount + \" of \"\n                    + results.totalCount + \" passed.\", color)));\n            }\n        },\n\n        indentWithCurrentLevel: function(string) {\n            return new Array(this.current_suite_hierarchy.length).join(this.indent_string) + string;\n        },\n\n        recursivelyUpdateHierarchyUpToRootAndLogNewBranches: function(suite) {\n            var suite_path = [],\n                current_level;\n\n            if (suite.parentSuite != null) {\n                suite_path = this.recursivelyUpdateHierarchyUpToRootAndLogNewBranches(suite.parentSuite);\n            }\n\n            suite_path.push(suite);\n            current_level = suite_path.length - 1;\n\n            if (this.current_suite_hierarchy.length <= current_level\n                || this.current_suite_hierarchy[current_level] !== suite) {\n\n                this.current_suite_hierarchy = suite_path.slice(0);\n                this.log(this.indentWithCurrentLevel(this.inColor(suite.description, \"bold\")));\n            }\n            return suite_path;\n        },\n\n        logCurrentSuite: function(suite) {\n            var suite_path = this.recursivelyUpdateHierarchyUpToRootAndLogNewBranches(suite);\n            // If we just popped down from a higher path, we need to update here\n            this.current_suite_hierarchy = suite_path;\n        },\n\n        inColor: function (string, color) {\n            var color_attributes = color && color.split(\"+\"),\n                ansi_string = \"\",\n                i, attr;\n\n            if (! this.color || ! color_attributes) {\n                return string;\n            }\n\n            for(i = 0; i < color_attributes.length; i++) {\n                ansi_string += \"\\033[\" + ATTRIBUTES_TO_ANSI[color_attributes[i]] + \"m\";\n            }\n            ansi_string += string + \"\\033[\" + ATTRIBUTES_TO_ANSI[\"off\"] + \"m\";\n\n            return ansi_string;\n        },\n\n        log: function(str) {\n            var console = jasmine.getGlobal().console;\n            if (console && console.log) {\n                console.log(str);\n            }\n        }\n    };\n\n    // export public\n    jasmine.TerminalReporter = TerminalReporter;\n})();\n"
  },
  {
    "path": "webfontloader.gemspec",
    "content": "## This is the rakegem gemspec template. Make sure you read and understand\n## all of the comments. Some sections require modification, and others can\n## be deleted if you don't need them. Once you understand the contents of\n## this file, feel free to delete any comments that begin with two hash marks.\n## You can find comprehensive Gem::Specification documentation, at\n## http://docs.rubygems.org/read/chapter/20\nGem::Specification.new do |s|\n  s.specification_version = 2 if s.respond_to? :specification_version=\n  s.required_rubygems_version = Gem::Requirement.new(\">= 0\") if s.respond_to? :required_rubygems_version=\n  s.rubygems_version = '1.3.5'\n\n  ## Leave these as is they will be modified for you by the rake gemspec task.\n  ## If your rubyforge_project name is different, then edit it and comment out\n  ## the sub! line in the Rakefile\n  s.name              = 'webfontloader'\n  s.version           = '1.6.28'\n  s.date              = '2017-05-27'\n\n  ## Make sure your summary is short. The description may be as long\n  ## as you like.\n  s.summary     = \"WebFont Loader gives you added control when using linked fonts via @font-face.\"\n  s.description = <<-DESC\nWebFont Loader gives you added control when using linked fonts via\n`@font-face`. It provides a common interface to loading fonts regardless of\nthe source, then adds a standard set of events you may use to control the\nloading experience.\nDESC\n\n  ## List the primary authors. If there are a bunch of authors, it's probably\n  ## better to set the email to an email list or something. If you don't have\n  ## a custom homepage, consider using your GitHub URL or the like.\n  s.authors  = [\"Ryan Carver\", \"Jeremie Lenfant-engelmann\"]\n  s.email    = 'ryan@typekit.com'\n  s.homepage = 'http://github.com/typekit/webfontloader'\n\n  ## License\n  s.license = \"Apache-2.0\"\n\n  ## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as\n  ## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb'\n  s.require_paths = %w[lib]\n\n  ## This sections is only necessary if you have C extensions.\n  # s.require_paths << 'ext'\n  # s.extensions = %w[ext/extconf.rb]\n\n  ## If your gem includes any executables, list them here.\n  # s.executables = []\n  # s.default_executable = 'name'\n\n  ## Specify any RDoc options here. You'll want to add your README and\n  ## LICENSE files to the extra_rdoc_files list.\n  s.rdoc_options = [\"--charset=UTF-8\"]\n  s.extra_rdoc_files = %w[README.md] + Dir[\"docs/*.md\"]\n\n  ## List your runtime dependencies here. Runtime dependencies are those\n  ## that are needed for an end user to actually USE your code.\n  # s.add_dependency('DEPNAME', [\">= 1.1.0\", \"< 2.0.0\"])\n\n  ## List your development dependencies here. Development dependencies are\n  ## those that are only needed during development\n  s.add_development_dependency('rake', '~>0')\n  s.add_development_dependency('rack', '~>1.5', '>=1.5.1')\n  s.add_development_dependency('sinatra', '~>1.3', '>=1.3.4')\n  s.add_development_dependency('vegas', '~>0.1.11')\n\n  ## Leave this section as-is. It will be automatically generated from the\n  ## contents of your Git repository via the gemspec task. DO NOT REMOVE\n  ## THE MANIFEST COMMENTS, they are used as delimiters by the task.\n  # = MANIFEST =\n  s.files = %w[\n    CHANGELOG\n    CONTRIBUTING.md\n    Gemfile\n    LICENSE\n    README.md\n    Rakefile\n    bin/webfontloader-demos\n    bower.json\n    browsers.json\n    externs.js\n    lib/webfontloader.rb\n    lib/webfontloader/demo/public/basic.css\n    lib/webfontloader/demo/public/blank.html\n    lib/webfontloader/demo/public/custom-iframe.html\n    lib/webfontloader/demo/public/custom.html\n    lib/webfontloader/demo/public/event-css-active-multiple.html\n    lib/webfontloader/demo/public/event-css-active.html\n    lib/webfontloader/demo/public/event-css-inactive.html\n    lib/webfontloader/demo/public/event-css-loading.html\n    lib/webfontloader/demo/public/event-js-active.html\n    lib/webfontloader/demo/public/event-js-font-active.html\n    lib/webfontloader/demo/public/event-js-loading.html\n    lib/webfontloader/demo/public/events-variations.html\n    lib/webfontloader/demo/public/events.html\n    lib/webfontloader/demo/public/fontdeck.html\n    lib/webfontloader/demo/public/fontwatchrunner-default-fonts.html\n    lib/webfontloader/demo/public/google-css.html\n    lib/webfontloader/demo/public/google-iframe.html\n    lib/webfontloader/demo/public/google.html\n    lib/webfontloader/demo/public/ie-fast-js.html\n    lib/webfontloader/demo/public/ie-slow-js.html\n    lib/webfontloader/demo/public/ie-slow-link.html\n    lib/webfontloader/demo/public/index.html\n    lib/webfontloader/demo/public/jquery.min.js\n    lib/webfontloader/demo/public/monotype-iframe.html\n    lib/webfontloader/demo/public/monotype.html\n    lib/webfontloader/demo/public/typekit-iframe.html\n    lib/webfontloader/demo/public/typekit-variations.html\n    lib/webfontloader/demo/public/typekit.html\n    lib/webfontloader/demo/server.rb\n    lib/webfontloader/modules.rb\n    package.json\n    spec/core/cssclassname_spec.js\n    spec/core/domhelper_spec.js\n    spec/core/eventdispatcher_spec.js\n    spec/core/font_spec.js\n    spec/core/fontmoduleloader_spec.js\n    spec/core/fontruler_spec.js\n    spec/core/fontwatcher_spec.js\n    spec/core/fontwatchrunner_spec.js\n    spec/core/nativefontwatchrunner_spec.js\n    spec/core/size_spec.js\n    spec/core/webfont_spec.js\n    spec/deps.js\n    spec/fixtures/external_script.js\n    spec/fixtures/external_stylesheet.css\n    spec/fixtures/fonts/LICENSE.txt\n    spec/fixtures/fonts/nullfont.css\n    spec/fixtures/fonts/nullfont1.css\n    spec/fixtures/fonts/nullfont2.css\n    spec/fixtures/fonts/nullfont3.css\n    spec/fixtures/fonts/sourcesans.eot\n    spec/fixtures/fonts/sourcesans.otf\n    spec/fixtures/fonts/sourcesans.svg\n    spec/fixtures/fonts/sourcesans.ttf\n    spec/fixtures/fonts/sourcesans.woff\n    spec/fixtures/fonts/sourcesansa.css\n    spec/fixtures/fonts/sourcesansb.css\n    spec/fixtures/fonts/sourcesansc.css\n    spec/fixtures/fonts/sourcesansd.css\n    spec/fixtures/fonts/sourcesansdup1.css\n    spec/fixtures/fonts/sourcesansdup2.css\n    spec/index.html\n    spec/modules/custom_spec.js\n    spec/modules/fontdeck_spec.js\n    spec/modules/google/fontapiparser_spec.js\n    spec/modules/google/fontapiurlbuilder_spec.js\n    spec/modules/google/googlefontapi_spec.js\n    spec/modules/monotype_spec.js\n    spec/modules/typekit_spec.js\n    src/closure.js\n    src/core/cssclassname.js\n    src/core/domhelper.js\n    src/core/eventdispatcher.js\n    src/core/font.js\n    src/core/fontmodule.js\n    src/core/fontmoduleloader.js\n    src/core/fontruler.js\n    src/core/fontwatcher.js\n    src/core/fontwatchrunner.js\n    src/core/initialize.js\n    src/core/nativefontwatchrunner.js\n    src/core/stylesheetwaiter.js\n    src/core/webfont.js\n    src/modules.yml\n    src/modules/custom.js\n    src/modules/fontdeck.js\n    src/modules/google/fontapiparser.js\n    src/modules/google/fontapiurlbuilder.js\n    src/modules/google/googlefontapi.js\n    src/modules/monotype.js\n    src/modules/typekit.js\n    tools/compiler/base.js\n    tools/compiler/compiler.jar\n    tools/jasmine-browserstack/jasmine-browserstack.js\n    tools/jasmine-phantomjs/jasmine-phantomjs.js\n    tools/jasmine-phantomjs/terminal-reporter.js\n    tools/jasmine/MIT.LICENSE\n    tools/jasmine/jasmine-html.js\n    tools/jasmine/jasmine.css\n    tools/jasmine/jasmine.js\n    webfontloader.gemspec\n    webfontloader.js\n  ]\n  # = MANIFEST =\n\n  ## Test files will be grabbed from the file list. Make sure the path glob\n  ## matches what you actually use.\n  s.test_files = s.files.select { |path| path =~ /^spec\\/.*_spec\\.rb/ }\nend\n"
  },
  {
    "path": "webfontloader.js",
    "content": "/* Web Font Loader v1.6.28 - (c) Adobe Systems, Google. License: Apache 2.0 */(function(){function aa(a,b,c){return a.call.apply(a.bind,arguments)}function ba(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}}function p(a,b,c){p=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf(\"native code\")?aa:ba;return p.apply(null,arguments)}var q=Date.now||function(){return+new Date};function ca(a,b){this.a=a;this.o=b||a;this.c=this.o.document}var da=!!window.FontFace;function t(a,b,c,d){b=a.c.createElement(b);if(c)for(var e in c)c.hasOwnProperty(e)&&(\"style\"==e?b.style.cssText=c[e]:b.setAttribute(e,c[e]));d&&b.appendChild(a.c.createTextNode(d));return b}function u(a,b,c){a=a.c.getElementsByTagName(b)[0];a||(a=document.documentElement);a.insertBefore(c,a.lastChild)}function v(a){a.parentNode&&a.parentNode.removeChild(a)}\nfunction w(a,b,c){b=b||[];c=c||[];for(var d=a.className.split(/\\s+/),e=0;e<b.length;e+=1){for(var f=!1,g=0;g<d.length;g+=1)if(b[e]===d[g]){f=!0;break}f||d.push(b[e])}b=[];for(e=0;e<d.length;e+=1){f=!1;for(g=0;g<c.length;g+=1)if(d[e]===c[g]){f=!0;break}f||b.push(d[e])}a.className=b.join(\" \").replace(/\\s+/g,\" \").replace(/^\\s+|\\s+$/,\"\")}function y(a,b){for(var c=a.className.split(/\\s+/),d=0,e=c.length;d<e;d++)if(c[d]==b)return!0;return!1}\nfunction ea(a){return a.o.location.hostname||a.a.location.hostname}function z(a,b,c){function d(){m&&e&&f&&(m(g),m=null)}b=t(a,\"link\",{rel:\"stylesheet\",href:b,media:\"all\"});var e=!1,f=!0,g=null,m=c||null;da?(b.onload=function(){e=!0;d()},b.onerror=function(){e=!0;g=Error(\"Stylesheet failed to load\");d()}):setTimeout(function(){e=!0;d()},0);u(a,\"head\",b)}\nfunction A(a,b,c,d){var e=a.c.getElementsByTagName(\"head\")[0];if(e){var f=t(a,\"script\",{src:b}),g=!1;f.onload=f.onreadystatechange=function(){g||this.readyState&&\"loaded\"!=this.readyState&&\"complete\"!=this.readyState||(g=!0,c&&c(null),f.onload=f.onreadystatechange=null,\"HEAD\"==f.parentNode.tagName&&e.removeChild(f))};e.appendChild(f);setTimeout(function(){g||(g=!0,c&&c(Error(\"Script load timeout\")))},d||5E3);return f}return null};function B(){this.a=0;this.c=null}function C(a){a.a++;return function(){a.a--;D(a)}}function E(a,b){a.c=b;D(a)}function D(a){0==a.a&&a.c&&(a.c(),a.c=null)};function F(a){this.a=a||\"-\"}F.prototype.c=function(a){for(var b=[],c=0;c<arguments.length;c++)b.push(arguments[c].replace(/[\\W_]+/g,\"\").toLowerCase());return b.join(this.a)};function G(a,b){this.c=a;this.f=4;this.a=\"n\";var c=(b||\"n4\").match(/^([nio])([1-9])$/i);c&&(this.a=c[1],this.f=parseInt(c[2],10))}function fa(a){return H(a)+\" \"+(a.f+\"00\")+\" 300px \"+I(a.c)}function I(a){var b=[];a=a.split(/,\\s*/);for(var c=0;c<a.length;c++){var d=a[c].replace(/['\"]/g,\"\");-1!=d.indexOf(\" \")||/^\\d/.test(d)?b.push(\"'\"+d+\"'\"):b.push(d)}return b.join(\",\")}function J(a){return a.a+a.f}function H(a){var b=\"normal\";\"o\"===a.a?b=\"oblique\":\"i\"===a.a&&(b=\"italic\");return b}\nfunction ga(a){var b=4,c=\"n\",d=null;a&&((d=a.match(/(normal|oblique|italic)/i))&&d[1]&&(c=d[1].substr(0,1).toLowerCase()),(d=a.match(/([1-9]00|normal|bold)/i))&&d[1]&&(/bold/i.test(d[1])?b=7:/[1-9]00/.test(d[1])&&(b=parseInt(d[1].substr(0,1),10))));return c+b};function ha(a,b){this.c=a;this.f=a.o.document.documentElement;this.h=b;this.a=new F(\"-\");this.j=!1!==b.events;this.g=!1!==b.classes}function ia(a){a.g&&w(a.f,[a.a.c(\"wf\",\"loading\")]);K(a,\"loading\")}function L(a){if(a.g){var b=y(a.f,a.a.c(\"wf\",\"active\")),c=[],d=[a.a.c(\"wf\",\"loading\")];b||c.push(a.a.c(\"wf\",\"inactive\"));w(a.f,c,d)}K(a,\"inactive\")}function K(a,b,c){if(a.j&&a.h[b])if(c)a.h[b](c.c,J(c));else a.h[b]()};function ja(){this.c={}}function ka(a,b,c){var d=[],e;for(e in b)if(b.hasOwnProperty(e)){var f=a.c[e];f&&d.push(f(b[e],c))}return d};function M(a,b){this.c=a;this.f=b;this.a=t(this.c,\"span\",{\"aria-hidden\":\"true\"},this.f)}function N(a){u(a.c,\"body\",a.a)}function O(a){return\"display:block;position:absolute;top:-9999px;left:-9999px;font-size:300px;width:auto;height:auto;line-height:normal;margin:0;padding:0;font-variant:normal;white-space:nowrap;font-family:\"+I(a.c)+\";\"+(\"font-style:\"+H(a)+\";font-weight:\"+(a.f+\"00\")+\";\")};function P(a,b,c,d,e,f){this.g=a;this.j=b;this.a=d;this.c=c;this.f=e||3E3;this.h=f||void 0}P.prototype.start=function(){var a=this.c.o.document,b=this,c=q(),d=new Promise(function(d,e){function f(){q()-c>=b.f?e():a.fonts.load(fa(b.a),b.h).then(function(a){1<=a.length?d():setTimeout(f,25)},function(){e()})}f()}),e=null,f=new Promise(function(a,d){e=setTimeout(d,b.f)});Promise.race([f,d]).then(function(){e&&(clearTimeout(e),e=null);b.g(b.a)},function(){b.j(b.a)})};function Q(a,b,c,d,e,f,g){this.v=a;this.B=b;this.c=c;this.a=d;this.s=g||\"BESbswy\";this.f={};this.w=e||3E3;this.u=f||null;this.m=this.j=this.h=this.g=null;this.g=new M(this.c,this.s);this.h=new M(this.c,this.s);this.j=new M(this.c,this.s);this.m=new M(this.c,this.s);a=new G(this.a.c+\",serif\",J(this.a));a=O(a);this.g.a.style.cssText=a;a=new G(this.a.c+\",sans-serif\",J(this.a));a=O(a);this.h.a.style.cssText=a;a=new G(\"serif\",J(this.a));a=O(a);this.j.a.style.cssText=a;a=new G(\"sans-serif\",J(this.a));a=\nO(a);this.m.a.style.cssText=a;N(this.g);N(this.h);N(this.j);N(this.m)}var R={D:\"serif\",C:\"sans-serif\"},S=null;function T(){if(null===S){var a=/AppleWebKit\\/([0-9]+)(?:\\.([0-9]+))/.exec(window.navigator.userAgent);S=!!a&&(536>parseInt(a[1],10)||536===parseInt(a[1],10)&&11>=parseInt(a[2],10))}return S}Q.prototype.start=function(){this.f.serif=this.j.a.offsetWidth;this.f[\"sans-serif\"]=this.m.a.offsetWidth;this.A=q();U(this)};\nfunction la(a,b,c){for(var d in R)if(R.hasOwnProperty(d)&&b===a.f[R[d]]&&c===a.f[R[d]])return!0;return!1}function U(a){var b=a.g.a.offsetWidth,c=a.h.a.offsetWidth,d;(d=b===a.f.serif&&c===a.f[\"sans-serif\"])||(d=T()&&la(a,b,c));d?q()-a.A>=a.w?T()&&la(a,b,c)&&(null===a.u||a.u.hasOwnProperty(a.a.c))?V(a,a.v):V(a,a.B):ma(a):V(a,a.v)}function ma(a){setTimeout(p(function(){U(this)},a),50)}function V(a,b){setTimeout(p(function(){v(this.g.a);v(this.h.a);v(this.j.a);v(this.m.a);b(this.a)},a),0)};function W(a,b,c){this.c=a;this.a=b;this.f=0;this.m=this.j=!1;this.s=c}var X=null;W.prototype.g=function(a){var b=this.a;b.g&&w(b.f,[b.a.c(\"wf\",a.c,J(a).toString(),\"active\")],[b.a.c(\"wf\",a.c,J(a).toString(),\"loading\"),b.a.c(\"wf\",a.c,J(a).toString(),\"inactive\")]);K(b,\"fontactive\",a);this.m=!0;na(this)};\nW.prototype.h=function(a){var b=this.a;if(b.g){var c=y(b.f,b.a.c(\"wf\",a.c,J(a).toString(),\"active\")),d=[],e=[b.a.c(\"wf\",a.c,J(a).toString(),\"loading\")];c||d.push(b.a.c(\"wf\",a.c,J(a).toString(),\"inactive\"));w(b.f,d,e)}K(b,\"fontinactive\",a);na(this)};function na(a){0==--a.f&&a.j&&(a.m?(a=a.a,a.g&&w(a.f,[a.a.c(\"wf\",\"active\")],[a.a.c(\"wf\",\"loading\"),a.a.c(\"wf\",\"inactive\")]),K(a,\"active\")):L(a.a))};function oa(a){this.j=a;this.a=new ja;this.h=0;this.f=this.g=!0}oa.prototype.load=function(a){this.c=new ca(this.j,a.context||this.j);this.g=!1!==a.events;this.f=!1!==a.classes;pa(this,new ha(this.c,a),a)};\nfunction qa(a,b,c,d,e){var f=0==--a.h;(a.f||a.g)&&setTimeout(function(){var a=e||null,m=d||null||{};if(0===c.length&&f)L(b.a);else{b.f+=c.length;f&&(b.j=f);var h,l=[];for(h=0;h<c.length;h++){var k=c[h],n=m[k.c],r=b.a,x=k;r.g&&w(r.f,[r.a.c(\"wf\",x.c,J(x).toString(),\"loading\")]);K(r,\"fontloading\",x);r=null;if(null===X)if(window.FontFace){var x=/Gecko.*Firefox\\/(\\d+)/.exec(window.navigator.userAgent),xa=/OS X.*Version\\/10\\..*Safari/.exec(window.navigator.userAgent)&&/Apple/.exec(window.navigator.vendor);\nX=x?42<parseInt(x[1],10):xa?!1:!0}else X=!1;X?r=new P(p(b.g,b),p(b.h,b),b.c,k,b.s,n):r=new Q(p(b.g,b),p(b.h,b),b.c,k,b.s,a,n);l.push(r)}for(h=0;h<l.length;h++)l[h].start()}},0)}function pa(a,b,c){var d=[],e=c.timeout;ia(b);var d=ka(a.a,c,a.c),f=new W(a.c,b,e);a.h=d.length;b=0;for(c=d.length;b<c;b++)d[b].load(function(b,d,c){qa(a,f,b,d,c)})};function ra(a,b){this.c=a;this.a=b}\nra.prototype.load=function(a){function b(){if(f[\"__mti_fntLst\"+d]){var c=f[\"__mti_fntLst\"+d](),e=[],h;if(c)for(var l=0;l<c.length;l++){var k=c[l].fontfamily;void 0!=c[l].fontStyle&&void 0!=c[l].fontWeight?(h=c[l].fontStyle+c[l].fontWeight,e.push(new G(k,h))):e.push(new G(k))}a(e)}else setTimeout(function(){b()},50)}var c=this,d=c.a.projectId,e=c.a.version;if(d){var f=c.c.o;A(this.c,(c.a.api||\"https://fast.fonts.net/jsapi\")+\"/\"+d+\".js\"+(e?\"?v=\"+e:\"\"),function(e){e?a([]):(f[\"__MonotypeConfiguration__\"+\nd]=function(){return c.a},b())}).id=\"__MonotypeAPIScript__\"+d}else a([])};function sa(a,b){this.c=a;this.a=b}sa.prototype.load=function(a){var b,c,d=this.a.urls||[],e=this.a.families||[],f=this.a.testStrings||{},g=new B;b=0;for(c=d.length;b<c;b++)z(this.c,d[b],C(g));var m=[];b=0;for(c=e.length;b<c;b++)if(d=e[b].split(\":\"),d[1])for(var h=d[1].split(\",\"),l=0;l<h.length;l+=1)m.push(new G(d[0],h[l]));else m.push(new G(d[0]));E(g,function(){a(m,f)})};function ta(a,b){a?this.c=a:this.c=ua;this.a=[];this.f=[];this.g=b||\"\"}var ua=\"https://fonts.googleapis.com/css\";function va(a,b){for(var c=b.length,d=0;d<c;d++){var e=b[d].split(\":\");3==e.length&&a.f.push(e.pop());var f=\"\";2==e.length&&\"\"!=e[1]&&(f=\":\");a.a.push(e.join(f))}}\nfunction wa(a){if(0==a.a.length)throw Error(\"No fonts to load!\");if(-1!=a.c.indexOf(\"kit=\"))return a.c;for(var b=a.a.length,c=[],d=0;d<b;d++)c.push(a.a[d].replace(/ /g,\"+\"));b=a.c+\"?family=\"+c.join(\"%7C\");0<a.f.length&&(b+=\"&subset=\"+a.f.join(\",\"));0<a.g.length&&(b+=\"&text=\"+encodeURIComponent(a.g));return b};function ya(a){this.f=a;this.a=[];this.c={}}\nvar za={latin:\"BESbswy\",\"latin-ext\":\"\\u00e7\\u00f6\\u00fc\\u011f\\u015f\",cyrillic:\"\\u0439\\u044f\\u0416\",greek:\"\\u03b1\\u03b2\\u03a3\",khmer:\"\\u1780\\u1781\\u1782\",Hanuman:\"\\u1780\\u1781\\u1782\"},Aa={thin:\"1\",extralight:\"2\",\"extra-light\":\"2\",ultralight:\"2\",\"ultra-light\":\"2\",light:\"3\",regular:\"4\",book:\"4\",medium:\"5\",\"semi-bold\":\"6\",semibold:\"6\",\"demi-bold\":\"6\",demibold:\"6\",bold:\"7\",\"extra-bold\":\"8\",extrabold:\"8\",\"ultra-bold\":\"8\",ultrabold:\"8\",black:\"9\",heavy:\"9\",l:\"3\",r:\"4\",b:\"7\"},Ba={i:\"i\",italic:\"i\",n:\"n\",normal:\"n\"},\nCa=/^(thin|(?:(?:extra|ultra)-?)?light|regular|book|medium|(?:(?:semi|demi|extra|ultra)-?)?bold|black|heavy|l|r|b|[1-9]00)?(n|i|normal|italic)?$/;\nfunction Da(a){for(var b=a.f.length,c=0;c<b;c++){var d=a.f[c].split(\":\"),e=d[0].replace(/\\+/g,\" \"),f=[\"n4\"];if(2<=d.length){var g;var m=d[1];g=[];if(m)for(var m=m.split(\",\"),h=m.length,l=0;l<h;l++){var k;k=m[l];if(k.match(/^[\\w-]+$/)){var n=Ca.exec(k.toLowerCase());if(null==n)k=\"\";else{k=n[2];k=null==k||\"\"==k?\"n\":Ba[k];n=n[1];if(null==n||\"\"==n)n=\"4\";else var r=Aa[n],n=r?r:isNaN(n)?\"4\":n.substr(0,1);k=[k,n].join(\"\")}}else k=\"\";k&&g.push(k)}0<g.length&&(f=g);3==d.length&&(d=d[2],g=[],d=d?d.split(\",\"):\ng,0<d.length&&(d=za[d[0]])&&(a.c[e]=d))}a.c[e]||(d=za[e])&&(a.c[e]=d);for(d=0;d<f.length;d+=1)a.a.push(new G(e,f[d]))}};function Ea(a,b){this.c=a;this.a=b}var Fa={Arimo:!0,Cousine:!0,Tinos:!0};Ea.prototype.load=function(a){var b=new B,c=this.c,d=new ta(this.a.api,this.a.text),e=this.a.families;va(d,e);var f=new ya(e);Da(f);z(c,wa(d),C(b));E(b,function(){a(f.a,f.c,Fa)})};function Ga(a,b){this.c=a;this.a=b}Ga.prototype.load=function(a){var b=this.a.id,c=this.c.o;b?A(this.c,(this.a.api||\"https://use.typekit.net\")+\"/\"+b+\".js\",function(b){if(b)a([]);else if(c.Typekit&&c.Typekit.config&&c.Typekit.config.fn){b=c.Typekit.config.fn;for(var e=[],f=0;f<b.length;f+=2)for(var g=b[f],m=b[f+1],h=0;h<m.length;h++)e.push(new G(g,m[h]));try{c.Typekit.load({events:!1,classes:!1,async:!0})}catch(l){}a(e)}},2E3):a([])};function Ha(a,b){this.c=a;this.f=b;this.a=[]}Ha.prototype.load=function(a){var b=this.f.id,c=this.c.o,d=this;b?(c.__webfontfontdeckmodule__||(c.__webfontfontdeckmodule__={}),c.__webfontfontdeckmodule__[b]=function(b,c){for(var g=0,m=c.fonts.length;g<m;++g){var h=c.fonts[g];d.a.push(new G(h.name,ga(\"font-weight:\"+h.weight+\";font-style:\"+h.style)))}a(d.a)},A(this.c,(this.f.api||\"https://f.fontdeck.com/s/css/js/\")+ea(this.c)+\"/\"+b+\".js\",function(b){b&&a([])})):a([])};var Y=new oa(window);Y.a.c.custom=function(a,b){return new sa(b,a)};Y.a.c.fontdeck=function(a,b){return new Ha(b,a)};Y.a.c.monotype=function(a,b){return new ra(b,a)};Y.a.c.typekit=function(a,b){return new Ga(b,a)};Y.a.c.google=function(a,b){return new Ea(b,a)};var Z={load:p(Y.load,Y)};\"function\"===typeof define&&define.amd?define(function(){return Z}):\"undefined\"!==typeof module&&module.exports?module.exports=Z:(window.WebFont=Z,window.WebFontConfig&&Y.load(window.WebFontConfig));}());\n"
  }
]