├── .gitignore ├── .travis.yml ├── CHANGELOG ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── bin └── webfontloader-demos ├── bower.json ├── browsers.json ├── externs.js ├── lib ├── webfontloader.rb └── webfontloader │ ├── demo │ ├── public │ │ ├── basic.css │ │ ├── blank.html │ │ ├── custom-iframe.html │ │ ├── custom.html │ │ ├── event-css-active-multiple.html │ │ ├── event-css-active.html │ │ ├── event-css-inactive.html │ │ ├── event-css-loading.html │ │ ├── event-js-active.html │ │ ├── event-js-font-active.html │ │ ├── event-js-loading.html │ │ ├── events-variations.html │ │ ├── events.html │ │ ├── fontdeck.html │ │ ├── fontwatchrunner-default-fonts.html │ │ ├── google-css.html │ │ ├── google-iframe.html │ │ ├── google.html │ │ ├── ie-fast-js.html │ │ ├── ie-slow-js.html │ │ ├── ie-slow-link.html │ │ ├── index.html │ │ ├── jquery.min.js │ │ ├── monotype-iframe.html │ │ ├── monotype.html │ │ ├── typekit-iframe.html │ │ ├── typekit-variations.html │ │ └── typekit.html │ └── server.rb │ └── modules.rb ├── package.json ├── spec ├── core │ ├── cssclassname_spec.js │ ├── domhelper_spec.js │ ├── eventdispatcher_spec.js │ ├── font_spec.js │ ├── fontmoduleloader_spec.js │ ├── fontruler_spec.js │ ├── fontwatcher_spec.js │ ├── fontwatchrunner_spec.js │ ├── nativefontwatchrunner_spec.js │ ├── size_spec.js │ └── webfont_spec.js ├── deps.js ├── fixtures │ ├── external_script.js │ ├── external_stylesheet.css │ └── fonts │ │ ├── LICENSE.txt │ │ ├── nullfont.css │ │ ├── nullfont1.css │ │ ├── nullfont2.css │ │ ├── nullfont3.css │ │ ├── sourcesans.eot │ │ ├── sourcesans.otf │ │ ├── sourcesans.svg │ │ ├── sourcesans.ttf │ │ ├── sourcesans.woff │ │ ├── sourcesansa.css │ │ ├── sourcesansb.css │ │ ├── sourcesansc.css │ │ ├── sourcesansd.css │ │ ├── sourcesansdup1.css │ │ └── sourcesansdup2.css ├── index.html └── modules │ ├── custom_spec.js │ ├── fontdeck_spec.js │ ├── google │ ├── fontapiparser_spec.js │ ├── fontapiurlbuilder_spec.js │ └── googlefontapi_spec.js │ ├── monotype_spec.js │ └── typekit_spec.js ├── src ├── closure.js ├── core │ ├── cssclassname.js │ ├── domhelper.js │ ├── eventdispatcher.js │ ├── font.js │ ├── fontmodule.js │ ├── fontmoduleloader.js │ ├── fontruler.js │ ├── fontwatcher.js │ ├── fontwatchrunner.js │ ├── initialize.js │ ├── nativefontwatchrunner.js │ ├── stylesheetwaiter.js │ └── webfont.js ├── modules.yml └── modules │ ├── custom.js │ ├── fontdeck.js │ ├── google │ ├── fontapiparser.js │ ├── fontapiurlbuilder.js │ └── googlefontapi.js │ ├── monotype.js │ └── typekit.js ├── tools ├── compiler │ ├── base.js │ └── compiler.jar ├── jasmine-browserstack │ └── jasmine-browserstack.js ├── jasmine-phantomjs │ ├── jasmine-phantomjs.js │ └── terminal-reporter.js └── jasmine │ ├── MIT.LICENSE │ ├── jasmine-html.js │ ├── jasmine.css │ └── jasmine.js ├── webfontloader.gemspec └── webfontloader.js /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle 2 | *~ 3 | target 4 | tmp 5 | _site 6 | pkg 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | before_install: 2 | - wget https://s3.amazonaws.com/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2 3 | - tar -xjf phantomjs-2.0.0-ubuntu-12.04.tar.bz2 4 | - sudo mv /usr/local/phantomjs/bin/phantomjs /usr/local/phantomjs/bin/phantomjs1 5 | - sudo mv phantomjs /usr/local/phantomjs/bin/phantomjs 6 | language: node_js 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Please open [an issue](https://github.com/typekit/webfontloader/issues) if you find or suspect any problems. Sample pages and test cases are greatly appreciated. 4 | 5 | ## Development requirements 6 | 7 | You'll need a few rubygems to run the tests, demo server, and other rake tasks, which should be installed with [Bundler](http://gembundler.com/). 8 | 9 | $ gem install bundler 10 | $ bundle install 11 | 12 | To 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. 13 | 14 | $ brew install phantomjs 15 | 16 | ## Building 17 | 18 | To build a JS file from source, just run rake: 19 | 20 | $ rake 21 | 22 | If you want to build a JS file with only specific modules you can specify them on the command line: 23 | 24 | $ rake compile['custom google typekit'] 25 | 26 | This 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. 27 | 28 | ## Demos 29 | 30 | A full suite of demo pages is included in this source. Here you can find some 31 | live examples using the JS and CSS events. Run the demo server with: 32 | 33 | $ rake demo 34 | 35 | You can also run the demos with uncompressed, debuggable code to aid in 36 | development. Just start the server in dev mode: 37 | 38 | $ rake demodev 39 | 40 | Browse the demos [source code](http://github.com/typekit/webfontloader/blob/master/lib/webfontloader/demo/public). 41 | 42 | ## Testing 43 | 44 | Web Font Loader has an extensive test suite that runs via Jasmine. The test suite 45 | should be passing before submitting a pull request, and new tests should be added for any new functionality. 46 | 47 | To run tests, open up `spec/index.html` in a browser and check the results. The 48 | test suite will run automatically. Again, before submitting a pull request 49 | please run the test suite in multiple browsers and list them in the pull request. 50 | 51 | To run tests in a headless WebKit using [PhantomJS](http://www.phantomjs.org) run: 52 | 53 | $ rake test 54 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rake' 3 | require 'date' 4 | 5 | ############################################################################# 6 | # 7 | # Helper functions 8 | # 9 | ############################################################################# 10 | 11 | def name 12 | @name ||= Dir['*.gemspec'].first.split('.').first 13 | end 14 | 15 | def version 16 | line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/] 17 | line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1] 18 | end 19 | 20 | def date 21 | Date.today.to_s 22 | end 23 | 24 | def gemspec_file 25 | "#{name}.gemspec" 26 | end 27 | 28 | def gem_file 29 | "#{name}-#{version}.gem" 30 | end 31 | 32 | def replace_header(head, header_name) 33 | head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"} 34 | end 35 | 36 | ############################################################################# 37 | # 38 | # Standard tasks 39 | # 40 | ############################################################################# 41 | 42 | desc "Open an irb session preloaded with this library" 43 | task :console do 44 | sh "irb -rubygems -r ./lib/#{name}.rb" 45 | end 46 | 47 | ############################################################################# 48 | # 49 | # Custom tasks (add your own tasks here) 50 | # 51 | ############################################################################# 52 | 53 | require 'rake/clean' 54 | 55 | $LOAD_PATH.unshift File.dirname(__FILE__) + "/lib" 56 | require 'webfontloader' 57 | 58 | # 59 | # Setup 60 | # 61 | 62 | # Build targets (remove with `rake clean`) 63 | CLEAN.include("target") 64 | CLEAN.include("tmp") 65 | 66 | # JsCompiler 67 | JsCompilerJar = "tools/compiler/compiler.jar" 68 | 69 | # JS Source dependencies 70 | AllJs = FileList["{src,src-test}/**/*"] 71 | SourceJs = FileList["src/**/*"] 72 | 73 | # JS Source loader 74 | @modules = WebFontLoader::Modules.new 75 | 76 | # 77 | # Build 78 | # 79 | 80 | directory "target" 81 | directory "tmp" 82 | 83 | desc "Compile the JavaScript into target/webfont.js" 84 | task :compile, [:modules] => "target/webfont.js" 85 | 86 | file "webfontloader.js" => "target/webfont.js" do 87 | cp "target/webfont.js", "webfontloader.js" 88 | end 89 | 90 | file "target/webfont.js", [:modules] => SourceJs + ["target"] do |t, args| 91 | args.with_defaults(:modules => 'custom google typekit monotype fontdeck') 92 | 93 | modules = args[:modules].split ' ' 94 | 95 | output_marker = "%output%" 96 | output_wrapper = @modules.js_output_wrapper(output_marker, version) 97 | 98 | args = [ 99 | ["-jar", JsCompilerJar], 100 | ["--compilation_level", "ADVANCED_OPTIMIZATIONS"], 101 | ["--js_output_file", t.name], 102 | ["--output_wrapper", %("#{output_wrapper}")], 103 | ["--warning_level", "VERBOSE"], 104 | ["--summary_detail_level", "3"], 105 | ["--externs", "externs.js"], 106 | "--define goog.DEBUG=false" 107 | ] 108 | 109 | args.concat modules.map { |m| "--define INCLUDE_" + m.upcase + "_MODULE" } 110 | 111 | # Extra args to add warnings. 112 | args.concat([ 113 | ["--warning_level", "VERBOSE"], 114 | ["--summary_detail_level", "1"] 115 | ]) 116 | 117 | source = @modules.all_source_files 118 | args.concat source.map { |f| ["--js", f] } 119 | 120 | output = `java #{args.flatten.join(' ')} 2>&1` 121 | $?.success? ? (puts output) : (fail output) 122 | end 123 | 124 | desc "Creates debug version into target/webfont.js" 125 | task :debug, [:modules] => "target/webfont_debug.js" 126 | 127 | file "target/webfont_debug.js", [:modules] => SourceJs + ["target"] do |t, args| 128 | args.with_defaults(:modules => 'custom google typekit monotype fontdeck') 129 | 130 | modules = args[:modules].split ' ' 131 | 132 | output_marker = "%output%" 133 | output_wrapper = @modules.js_output_wrapper(output_marker, version) 134 | 135 | args = [ 136 | ["-jar", JsCompilerJar], 137 | ["--compilation_level", "ADVANCED_OPTIMIZATIONS"], 138 | ["--js_output_file", t.name], 139 | ["--output_wrapper", %("#{output_wrapper}")], 140 | ["--warning_level", "VERBOSE"], 141 | ["--summary_detail_level", "3"], 142 | ["--externs", "externs.js"], 143 | "--debug=true", 144 | "--formatting=PRETTY_PRINT", 145 | "--formatting=PRINT_INPUT_DELIMITER" 146 | ] 147 | 148 | args.concat modules.map { |m| "--define INCLUDE_" + m.upcase + "_MODULE" } 149 | 150 | # Extra args to add warnings. 151 | args.concat([ 152 | ["--warning_level", "VERBOSE"], 153 | ["--summary_detail_level", "1"] 154 | ]) 155 | 156 | source = @modules.all_source_files 157 | args.concat source.map { |f| ["--js", f] } 158 | 159 | output = `java #{args.flatten.join(' ')} 2>&1` 160 | $?.success? ? (puts output) : (fail output) 161 | end 162 | 163 | # 164 | # Run 165 | # 166 | desc "BrowserStack tests" 167 | task :bstest do |t| 168 | exec "browserstack-test -u $BROWSERSTACK_USERNAME -p $BROWSERSTACK_PASSWORD -k $BROWSERSTACK_KEY -b browsers.json -t 300 http://localhost:9999/spec/index.html" 169 | end 170 | 171 | desc "Test everything" 172 | task :default => [:clean, :gzipbytes, :test] 173 | 174 | desc "Run all tests" 175 | task :test do |t| 176 | exec "phantomjs tools/jasmine-phantomjs/jasmine-phantomjs.js spec/index.html" 177 | end 178 | 179 | desc "Start the demo server" 180 | task :demo => "target/webfont.js" do |t| 181 | js = t.prerequisites.first 182 | exec "bin/webfontloader-demos -F --compiled_js #{js}" 183 | end 184 | 185 | desc "Start the demo server for development" 186 | task :demodev do 187 | exec "bin/webfontloader-demos -F -L --modules" 188 | end 189 | 190 | desc "Find out how many bytes the source is" 191 | task :bytes => [:clean, "target/webfont.js"] do |t| 192 | js = t.prerequisites.last 193 | bytes = File.read(js).size 194 | puts "#{bytes} bytes uncompressed" 195 | end 196 | 197 | desc "Find out how many bytes the source is when gzipped" 198 | task :gzipbytes => [:clean, "target/webfont.js"] do |t| 199 | require 'zlib' 200 | js = t.prerequisites.last 201 | bytes = Zlib::Deflate.deflate(File.read(js)).size 202 | puts "#{bytes} bytes gzipped" 203 | end 204 | 205 | 206 | ############################################################################# 207 | # 208 | # Packaging tasks 209 | # 210 | ############################################################################# 211 | 212 | task :release => [:build] do 213 | unless `git branch` =~ /^\* master$/ 214 | puts "You must be on the master branch to release!" 215 | exit! 216 | end 217 | sh "git add webfontloader.js" 218 | sh "git commit --allow-empty -a -m 'Release #{version}'" 219 | sh "npm version #{version}" 220 | sh "git push --tags origin master" 221 | sh "gem push pkg/#{name}-#{version}.gem" 222 | sh "npm publish" 223 | end 224 | 225 | task :build => :gemspec do 226 | Rake::Task["target/webfont.js"].execute 227 | Rake::Task["webfontloader.js"].execute 228 | sh "mkdir -p pkg" 229 | sh "gem build #{gemspec_file}" 230 | sh "mv #{gem_file} pkg" 231 | end 232 | 233 | task :gemspec => :validate do 234 | # read spec file and split out manifest section 235 | spec = File.read(gemspec_file) 236 | head, manifest, tail = spec.split(" # = MANIFEST =\n") 237 | 238 | # replace name version and date 239 | replace_header(head, :name) 240 | replace_header(head, :version) 241 | replace_header(head, :date) 242 | 243 | # determine file list from git ls-files 244 | files = `git ls-files`. 245 | split("\n"). 246 | sort. 247 | reject { |file| file =~ /^\./ }. 248 | reject { |file| file =~ /^(rdoc|pkg)/ }. 249 | map { |file| " #{file.gsub(/\s/, '\ ')}" }. 250 | join("\n") 251 | 252 | # piece file back together and write 253 | manifest = " s.files = %w[\n#{files}\n ]\n" 254 | spec = [head, manifest, tail].join(" # = MANIFEST =\n") 255 | File.open(gemspec_file, 'w') { |io| io.write(spec) } 256 | puts "Updated #{gemspec_file}" 257 | end 258 | 259 | task :validate do 260 | libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"] 261 | unless libfiles.empty? 262 | puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir." 263 | exit! 264 | end 265 | unless Dir['VERSION*'].empty? 266 | puts "A `VERSION` file at root level violates Gem best practices." 267 | exit! 268 | end 269 | end 270 | -------------------------------------------------------------------------------- /bin/webfontloader-demos: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'rubygems' 4 | 5 | $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + "/../lib") 6 | require 'webfontloader' 7 | 8 | begin 9 | require 'webfontloader/demo/server' 10 | rescue LoadError => e 11 | abort "Please gem install sinatra" 12 | end 13 | 14 | begin 15 | require 'vegas' 16 | rescue LoadError => e 17 | abort "Please gem install vegas" 18 | end 19 | 20 | Vegas::Runner.new(WebFontLoader::Demo::Server, 'font-demos', :host => "localhost") do |runner, opts, app| 21 | opts.on('--compiled_js FILE', "Dynamically build the JS with the given modules") { |file| 22 | app.set :compiled_js, File.read(file) 23 | } 24 | opts.on('--modules [MODULES]', "Dynamically build the JS with the given modules") { |opt_modules| 25 | modules = opt_modules ? opt_modules.split(",") : [] 26 | app.set :modules, WebFontLoader::Modules.new(*modules) 27 | } 28 | end 29 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webfontloader", 3 | "main": "webfontloader.js", 4 | "description": "Web Font Loader gives you added control when using linked fonts via @font-face.", 5 | "moduleType": ["amd", "node"], 6 | "license": "Apache-2.0", 7 | "ignore": [ 8 | "**/.*", 9 | "node_modules", 10 | "spec", 11 | "tools", 12 | "lib", 13 | "bin" 14 | ], 15 | "keywords": [ 16 | "web", 17 | "fonts", 18 | "webfonts", 19 | "font", 20 | "loader", 21 | "@font-face" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /browsers.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "os_version": "XP", 4 | "os": "Windows", 5 | "browser_version": "12.16", 6 | "browser": "opera" 7 | }, 8 | { 9 | "os_version": "XP", 10 | "os": "Windows", 11 | "browser_version": "3.6", 12 | "browser": "firefox" 13 | }, 14 | { 15 | "os_version": "XP", 16 | "os": "Windows", 17 | "browser_version": "20.0", 18 | "browser": "firefox" 19 | }, 20 | { 21 | "os_version": "XP", 22 | "os": "Windows", 23 | "browser_version": "6.0", 24 | "browser": "ie", 25 | "timeout": "300" 26 | }, 27 | { 28 | "os_version": "XP", 29 | "os": "Windows", 30 | "browser_version": "7.0", 31 | "browser": "ie", 32 | "timeout": "300" 33 | }, 34 | { 35 | "os_version": "XP", 36 | "os": "Windows", 37 | "browser_version": "8.0", 38 | "browser": "ie" 39 | }, 40 | { 41 | "os_version": "8", 42 | "os": "Windows", 43 | "browser_version": "10.0 Desktop", 44 | "browser": "ie" 45 | }, 46 | { 47 | "os_version": "7", 48 | "os": "Windows", 49 | "browser_version": "9.0", 50 | "browser": "ie" 51 | }, 52 | { 53 | "os_version": "Mountain Lion", 54 | "os": "OS X", 55 | "browser_version": "6.0", 56 | "browser": "safari" 57 | }, 58 | { 59 | "os_version": "Mountain Lion", 60 | "os": "OS X", 61 | "browser_version": "25.0", 62 | "browser": "chrome" 63 | }, 64 | { 65 | "os_version": "Snow Leopard", 66 | "os": "OS X", 67 | "browser_version": "5.0", 68 | "browser": "safari" 69 | }, 70 | { 71 | "os_version": "5.0", 72 | "device": "iPad 2 (5.0)", 73 | "os": "ios", 74 | "browser": "Mobile Safari", 75 | "timeout": "300" 76 | }, 77 | { 78 | "os_version": "5.1", 79 | "device": "iPad 3rd", 80 | "os": "ios", 81 | "browser": "Mobile Safari", 82 | "timeout": "300" 83 | }, 84 | { 85 | "os_version": "6.0", 86 | "device": "iPhone 5", 87 | "os": "ios", 88 | "browser": "Mobile Safari", 89 | "timeout": "300" 90 | }, 91 | { 92 | "os_version": "4.3.2", 93 | "device": "iPad 2", 94 | "os": "ios", 95 | "browser": "Mobile Safari", 96 | "timeout": "300" 97 | }, 98 | { 99 | "os_version": "2.3", 100 | "device": "Samsung Galaxy S II", 101 | "os": "android", 102 | "browser": "Android Browser", 103 | "timeout": "300" 104 | }, 105 | { 106 | "os_version": "2.2", 107 | "device": "Samsung Galaxy S", 108 | "os": "android", 109 | "browser": "Android Browser", 110 | "timeout": "300" 111 | }, 112 | { 113 | "os_version": "4.2", 114 | "device": "LG Nexus 4", 115 | "os": "android", 116 | "browser": "Android Browser", 117 | "timeout": "300" 118 | }, 119 | { 120 | "os_version": "4.0", 121 | "device": "Samsung Galaxy Nexus", 122 | "os": "android", 123 | "browser": "Android Browser", 124 | "timeout": "300" 125 | }, 126 | { 127 | "os_version": "4.1", 128 | "device": "Samsung Galaxy S III", 129 | "os": "android", 130 | "browser": "Android Browser", 131 | "timeout": "300" 132 | } 133 | ] 134 | -------------------------------------------------------------------------------- /externs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {function(function():*)} 3 | */ 4 | var define; 5 | 6 | /** 7 | * @type {boolean?} 8 | */ 9 | define.amd; 10 | 11 | /** 12 | * @type {Object} 13 | */ 14 | var module; 15 | 16 | /** 17 | * @type {Object?} 18 | */ 19 | module.exports; 20 | -------------------------------------------------------------------------------- /lib/webfontloader.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | 3 | require 'webfontloader/modules' 4 | 5 | module WebFontLoader 6 | VERSION = '1.6.28' 7 | 8 | ProjectRoot = File.expand_path(File.dirname(__FILE__) + "/..") 9 | 10 | end 11 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/basic.css: -------------------------------------------------------------------------------- 1 | body { 2 | line-height: 1; 3 | font-size: 14px; 4 | margin: 10px; 5 | } 6 | h1 { 7 | font-size: 5em; 8 | margin: 0; 9 | } 10 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/blank.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Blank page 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/custom-iframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Custom Module 5 | 6 | 12 | 13 | 14 | 27 | 28 | 29 | 30 |
31 |

32 | Hide Page | 33 | Reload Cached 34 |

35 |

36 | The goal of this page is to show how fonts load from a custom module in a 37 | child iframe. 38 |

39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/custom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Custom Module 5 | 6 | 24 | 25 | 26 |

27 | Hello World. I am ChunkFive. 28 |

29 |
30 |

31 | Hide Page | 32 | Reload Cached 33 |

34 |

35 | The goal of this page is to show how fonts load from a custom module. 36 |

37 |

CSS Hook Status

38 | 46 |

JavaScript Event Progress

47 |
    48 | 49 | 50 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/event-css-active-multiple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 26 | 27 | 28 |

    29 | Hello World. I am Droid Sans. 30 |

    31 |

    32 | Hello World. I am Tangerine. 33 |

    34 |
    35 |

    36 | Hide Page | 37 | Reload Cached 38 |

    39 |

    40 | The goal of this page is to use CSS to show each part of the page when 41 | its font has loaded. 42 |

    43 | 44 | 45 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/event-css-active.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 14 | 23 | 24 | 25 |

    26 | Hello World. I am Droid Sans. 27 |

    28 |
    29 |

    30 | Hide Page | 31 | Reload Cached 32 |

    33 |

    34 | The goal of this page is to use CSS to hide the headline until its font 35 | has completely rendered. 36 |

    37 | 38 | 39 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/event-css-inactive.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 14 | 23 | 24 | 25 |

    26 | Hello World. I am Droid Sans. 27 |

    28 |
    29 |

    30 | Hide Page | 31 | Reload Cached 32 |

    33 |

    34 | The goal of this page is to use CSS to present a fallback font until the custom font 35 | has completely rendered. 36 |

    37 | 38 | 39 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/event-css-loading.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 36 | 37 | 38 |

    39 | I'm loading! 40 |

    41 |

    42 | Hello World. I am Droid Sans. 43 |

    44 |
    45 |

    46 | Hide Page | 47 | Reload Cached 48 |

    49 |

    50 | The goal of this page is to use CSS to show a loading message while the 51 | headline's font is loading, then hide the loading message and show the 52 | headline once the font has rendered. 53 |

    54 | 55 | 56 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/event-js-active.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 19 | 24 | 25 | 26 |

    27 | Hello World. I am Droid Sans. 28 |

    29 |
    30 |

    31 | Hide Page | 32 | Reload Cached 33 |

    34 |

    35 | The goal of this page is to use a combination of JavaScript and CSS to 36 | hide the headline until its font has completely rendered. 37 |

    38 | 39 | 40 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/event-js-font-active.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 20 | 25 | 26 | 27 |

    28 | Hello World. 29 |

    30 |
    31 |

    32 | Hide Page | 33 | Reload Cached 34 |

    35 |

    36 | The goal of this page is to use JavaScript to manipulate the page when 37 | a particular font loads. 38 |

    39 | 40 | 41 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/event-js-loading.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 37 | 42 | 43 | 44 |

    45 | I'm loading! 46 |

    47 |

    48 | Hello World. I am Droid Sans. 49 |

    50 |
    51 |

    52 | Hide Page | 53 | Reload Cached 54 |

    55 |

    56 | The goal of this page is to use JavaScript to manipulate the DOM while 57 | fonts are loading, and once they have all rendered. 58 |

    59 | 60 | 61 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/events-variations.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 41 | 84 | 85 | 86 |
    87 |

    88 | Droid Serif Regular 89 |

    90 |

    91 | Droid Serif Italic 92 |

    93 |

    94 | Droid Serif Bold 95 |

    96 |

    97 | Droid Serif Bold Italic 98 |

    99 |
    100 |
    101 |

    102 | Hide Page | 103 | Reload Cached 104 |

    105 |

    106 | The goal of this page is to show all of the font loading event callbacks when using 107 | multiple weights and styles of one typeface. 108 |

    109 |

    CSS Hook Status

    110 | 127 |

    JavaScript Event Progress

    128 |
      129 | 130 | 131 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/events.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 41 | 71 | 72 | 73 |

      74 | Hello World. I am Droid Sans. 75 |

      76 |

      77 | Hello World. I am Tangerine. 78 |

      79 |
      80 |

      81 | Hide Page | 82 | Reload Cached 83 |

      84 |

      85 | The goal of this page is to show all of the font loading event callbacks when 86 | using multiple typefaces. 87 |

      88 |

      CSS Hook Status

      89 | 100 |

      JavaScript Event Progress

      101 |
        102 | 103 | 104 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/fontdeck.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 42 | 58 | 59 | 60 |

        Hello World. I am Fertigo Pro Regular.

        61 |

        Hello World. I am Bodoni Display Bold Italic.

        62 |
        63 |

        64 | Hide Page | 65 | Reload Cached 66 |

        67 |

        68 | The goal of this page is to show how Fontdeck fonts load. 69 |

        70 |

        71 | You must use "localhost" when testing Fontdeck fonts. 72 |

        73 |
          74 | 75 | 76 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/fontwatchrunner-default-fonts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | 23 | 24 | 25 |

          Calculating...

          26 |

          27 | The goal of this page is to verify that the two default font stacks in 28 | FontWatchRunner have different widths on a given platform when rendering the 29 | default test string. The pairs of headings below should render in different 30 | fonts and the results above should indicate that they all have different 31 | widths. 32 |

          33 |
          34 |
          35 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/google-css.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 15 |

          16 | Hello World. I am Droid Sans. 17 |

          18 |
          19 |

          20 | Hide Page | 21 | Reload Cached 22 |

          23 |

          24 | The goal of this page is simply to use fonts directly via CSS. 25 |

          26 | 27 | 28 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/google-iframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 27 | 28 | 29 | 30 |
          31 |

          32 | Hide Page | 33 | Reload Cached 34 |

          35 |

          36 | The goal of this page is demonstrate fonts loading from Google via 37 | Javascript into a child iframe. 38 |

          39 | 40 | 41 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/google.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 14 | 19 | 20 | 21 |

          22 | Hello World. I am Droid Sans. 23 |

          24 |
          25 |

          26 | Hide Page | 27 | Reload Cached 28 |

          29 |

          30 | The goal of this page is simply to use fonts via the JavaScript API. 31 |

          32 | 33 | 34 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/ie-fast-js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Show me Fonts 6 | 7 | 11 | 12 | 20 | 21 | 29 | 30 | 31 |

          32 | I am a serif font. 33 |

          34 |

          35 | I am Droid Sans. 36 |

          37 |
          38 |

          39 | Hide Page | 40 | Reload Cached 41 |

          42 |

          43 | The goal of this page is to show non-blocking behavior in MSIE. This 44 | causes IE to load fonts without blocking the entire page. 45 |

          46 | 47 | 48 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/ie-slow-js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Show me Fonts 6 | 7 | 11 | 12 | 21 | 22 | 30 | 31 | 32 |

          33 | I am a serif font. 34 |

          35 |

          36 | I am Droid Sans. 37 |

          38 |
          39 |

          40 | Hide Page | 41 | Reload Cached 42 |

          43 |

          44 | The goal of this page is to restore blocking behavior in MSIE. This causes 45 | IE to block the entire page while loading fonts. 46 |

          47 | 48 | 49 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/ie-slow-link.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Show me Fonts 6 | 7 | 10 | 11 | 12 | 20 | 21 | 22 |

          23 | I am a serif font. 24 |

          25 |

          26 | I am Droid Sans. 27 |

          28 |
          29 |

          30 | Hide Page | 31 | Reload Cached 32 |

          33 |

          34 | The goal of this page is to show that by default, MSIE will block the entire 35 | page while an external font loads. 36 |

          37 | 38 | 39 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

          Web Font Loader Demos

          7 |

          8 | Demonstrations of pure CSS and JavaScript-enhanced use of @font-face. 9 |

          10 |

          11 | Note that many of these demonstrations use a slow proxy to 12 | increase the amount of time it takes to load a font. We do this to make it 13 | more obvious that the events system is working. It does not represent 14 | real world usage. 15 |

          16 | 17 |

          Modules

          18 |

          19 | Web Font Loader provides modules to load fonts from many places. 20 |

          21 |
            22 |
          1. Google / CSS Link: Load fonts from Google with a link tag. Consider this a base case for font loading.
          2. 23 |
          3. Google / Web Font Loader: Load fonts from Google with Web Font Loader.
          4. 24 |
          5. Typekit / Web Font Loader: Load fonts from Typekit with Web Font Loader.
          6. 25 |
          7. Custom / Web Font Loader: Load fonts from your own CSS with Web Font Loader.
          8. 26 |
          9. Fontdeck / Web Font Loader: Load fonts from Fontdeck with Web Font Loader.
          10. 27 |
          11. Monotype / Web Font Loader: Load fonts from fonts.com with Web Font Loader.
          12. 28 |
          29 | 30 |

          Modules in Iframes

          31 |

          32 | Web Font Loader provides the ability to load fonts in child iframes using modules, instead of the main window. 33 |

          34 |
            35 |
          1. Google / Web Font Loader: Load fonts from Google in a child iframe with Web Font Loader.
          2. 36 |
          3. Typekit / Web Font Loader: Load fonts from Typekit in a child iframe with Web Font Loader.
          4. 37 |
          5. Custom / Web Font Loader: Load fonts from your own CSS in a child iframe with Web Font Loader.
          6. 38 |
          7. 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.
          8. 39 |
          9. Monotype / Web Font Loader: Load fonts from fonts.com in a child iframe with Web Font Loader.
          10. 40 |
          41 | 42 |

          Events

          43 |

          44 | Web Font Loader provides events to help control font rendering across browsers. Here are some sample uses. 45 |

          46 |
            47 |
          1. Show when rendered (CSS): Use CSS to show part of the page only when the font has rendered. (Webkit style)
          2. 48 |
          3. Show when rendered (JS): Use JS to show part of the page only when the font has rendered. (Webkit style)
          4. 49 |
          5. Fallback before rendered (CSS): Use CSS to show fallback font before the font has rendered. (Mozilla style)
          6. 50 |
          7. Show loading message (CSS): Use CSS to show a message while the font loads.
          8. 51 |
          9. Show loading message (JS): Use JS to show a message while the font loads.
          10. 52 |
          53 | 54 |

          More Events

          55 |

          56 | More complex samples using events. 57 |

          58 |
            59 |
          1. Multiple font loads: Use CSS to control more than one font.
          2. 60 |
          3. Multiple typefaces: The full CSS and JS event cycle when using multiple typefaces.
          4. 61 |
          5. Multiple variations: The full CSS and JS event cycle when using multiple weights and styles of one typeface.
          6. 62 |
          63 | 64 |

          IE Behavior

          65 |

          66 | Web Font Loader helps workaround IE's page blocking behavior. 67 |

          68 |
            69 |
          1. Slow Link: Demonstrate that IE blocks the whole page when loading fonts via a LINK tag.
          2. 70 |
          3. Fast JS: By default, Web Font Loader works around the default IE loading behavior.
          4. 71 |
          5. Slow JS: Restore the default IE loading behavior.
          6. 72 |
          73 | 74 |

          Tests

          75 |

          76 | Additional demo pages to test specific functionality. 77 |

          78 |
            79 |
          1. Typekit with Multiple Variations
          2. 80 |
          3. Default font stacks for FontWatchRunner
          4. 81 |
          82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/monotype-iframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 25 | 26 | 27 | 28 |
          29 | 30 |

          31 | 32 | Hide Page | 33 | 34 | Reload Cached 35 | 36 |

          37 | 38 |

          39 | The goal of this page is to show how monotype fonts load in a child iframe. 40 |

          41 | 42 |

          43 | You must use "localhost" when testing monotype fonts. 44 |

          45 | 46 | 47 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/monotype.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 |

          16 | Hello World. I am DIN Next Bold. 17 |

          18 | 19 |
          20 |

          21 | Hide Page | 22 | Reload Cached 23 |

          24 |

          25 | The goal of this page is to show how monotype fonts load. And I'm Albertus Regular 26 |

          27 | 28 |

          29 | You must use "localhost" when testing monotype fonts. And this is Trade Gothic Condensed 30 |

          31 | 32 | 33 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/typekit-iframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 26 | 27 | 28 | 29 |
          30 |

          31 | Hide Page | 32 | Reload Cached 33 |

          34 |

          35 | The goal of this page is to show how Typekit fonts load into an iframe. 36 |

          37 |

          38 | You must load the fonts on "localhost" for this demo to work. 39 |

          40 | 41 | 42 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/typekit-variations.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 14 | 28 | 29 | 30 |
          31 |

          32 | Georgia Italic 33 |

          34 |

          35 | Georgia Bold Italic 36 |

          37 |
          38 |
          39 |

          40 | Hide Page | 41 | Reload Cached 42 |

          43 |

          44 | The goal of this page is to show how Typekit fonts load. Note that it uses 45 | a minimal Typekit script in order to reduce dependencies. This script 46 | simply provides the system font 'Georgia' in italic and bold italic 47 | instead of loading a web font. 48 |

          49 | 50 | 51 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/public/typekit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 22 | 23 | 24 |

          25 | Hello World. I am Futura PT. 26 |

          27 |
          28 |

          29 | Hide Page | 30 | Reload Cached 31 |

          32 |

          33 | The goal of this page is to show how Typekit fonts load. 34 |

          35 |

          36 | You must load this page on "localhost" in order for the fonts to load. 37 |

          38 | 39 | 40 | -------------------------------------------------------------------------------- /lib/webfontloader/demo/server.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra/base' 2 | require 'open-uri' 3 | 4 | module WebFontLoader 5 | module Demo 6 | class Server < Sinatra::Base 7 | 8 | DemoRoot = File.expand_path(File.join(File.dirname(__FILE__))) 9 | ProjectRoot = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..")) 10 | 11 | GoogleApi = "https://fonts.googleapis.com/css" 12 | GoogleFontApi = "https://themes.googleusercontent.com/font" 13 | 14 | set :app_file, __FILE__ 15 | set :sessions, false 16 | set :static, true 17 | 18 | set :modules, nil 19 | set :compiled_js, nil 20 | 21 | get '/' do 22 | File.read(File.join(DemoRoot, "public", "index.html")) 23 | end 24 | 25 | get '/webfont.js' do 26 | headers 'Content-Type' => "application/javascript" 27 | headers 'Cache-Control' => 'max-age=300' 28 | get_js_code 29 | end 30 | 31 | get '/webfont-fontwatchrunner.js' do 32 | headers 'Content-Type' => 'application/javascript' 33 | headers 'Cache-Control' => 'max-age=300' 34 | [ 35 | 'var webfont = {};', 36 | File.read(File.join(WebFontLoader::ProjectRoot, 'src/core/fontwatchrunner.js')) 37 | ] 38 | end 39 | 40 | get '/fonts/api' do 41 | url = "#{GoogleApi}?#{env['QUERY_STRING']}" 42 | headers 'Content-Type' => 'text/css' 43 | headers 'Cache-Control' => 'max-age=300' 44 | response = open(url, 'User-Agent' => env['HTTP_USER_AGENT']) 45 | source = response.read 46 | source.gsub!(%r[https://themes.googleusercontent.com/font], '/fonts/font') 47 | source 48 | end 49 | 50 | get '/fonts/font' do 51 | sleep 1 52 | url = "#{GoogleFontApi}?#{env['QUERY_STRING']}" 53 | headers 'Cache-Control' => 'max-age=300' 54 | headers 'Content-Encoding' => 'gzip' 55 | response = open(url, 'User-Agent' => env['HTTP_USER_AGENT']) 56 | response.read 57 | end 58 | 59 | get %r[/typekit/(\w+)\.js] do |kit_id| 60 | headers 'Content-Type' => 'application/javascript' 61 | headers 'Cache-Control' => 'max-age=300' 62 | case kit_id 63 | when "kitwitharialblack" 64 | families = "['Arial Black']" 65 | variations = "{}" 66 | when "kitwithgeorgia" 67 | families = "['Georgia']" 68 | variations = "{ 'Georgia': ['i4', 'i7' ]}" 69 | else 70 | families = "[]" 71 | variations = "{}" 72 | end 73 | <<-JS 74 | if (window.__webfonttypekitmodule__) { 75 | var module = window.__webfonttypekitmodule__['#{kit_id}']; 76 | if (module) { 77 | module(function(userAgent, configuration, init) { 78 | // Here you may use the userAgent object to determine 79 | // browser support. 80 | init(true, #{families}, #{variations}); 81 | }); 82 | } 83 | } 84 | JS 85 | end 86 | 87 | protected 88 | 89 | def get_js_code 90 | if settings.compiled_js 91 | settings.compiled_js 92 | elsif settings.modules 93 | settings.modules.all_source_files.map { |file| File.read(File.join(WebFontLoader::ProjectRoot, file)) } 94 | else 95 | "alert('No JavaScript has been configured in the demo server');" 96 | end 97 | end 98 | 99 | end 100 | end 101 | end -------------------------------------------------------------------------------- /lib/webfontloader/modules.rb: -------------------------------------------------------------------------------- 1 | module WebFontLoader 2 | class Modules 3 | 4 | def initialize(*modules) 5 | @project_root = WebFontLoader::ProjectRoot 6 | @js_src = "src" 7 | @js_test = "src-test" 8 | @modules = modules.empty? ? config.keys : modules 9 | # Make sure 'core' is first. 10 | @modules.unshift "core" 11 | @modules.uniq! 12 | end 13 | 14 | attr_reader :modules 15 | attr_accessor :project_root, :js_src, :js_test 16 | 17 | def all_source_files 18 | @all_source_files ||= begin 19 | modules.map { |mod| config[mod] }.compact.flatten.map { |f| File.join(js_src, f) } 20 | end 21 | end 22 | 23 | def all_test_globs 24 | @all_test_globs ||= begin 25 | js_test_dirs = Dir[File.join(project_root, js_test, "*")].map { |d| File.basename(d) } 26 | js_test_dirs.map { |dir| File.join(js_test, dir, "*.js") if modules.include?(dir) }.compact 27 | end 28 | end 29 | 30 | def js_output_wrapper(source, version) 31 | File.read(File.join(js_src, "closure.js")).sub("{{source}}", source).sub("{{version}}", version).gsub(/\n|\r/,"") 32 | end 33 | 34 | protected 35 | 36 | def config 37 | @config ||= begin 38 | path = File.join(project_root, js_src) 39 | YAML.load_file(File.join(path, "modules.yml")) 40 | end 41 | end 42 | 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webfontloader", 3 | "version": "1.6.28", 4 | "description": "Web Font Loader gives you added control when using linked fonts via @font-face.", 5 | "main": "webfontloader.js", 6 | "scripts": { 7 | "test": "phantomjs tools/jasmine-phantomjs/jasmine-phantomjs.js spec/index.html" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/typekit/webfontloader.git" 12 | }, 13 | "keywords": [ 14 | "web", 15 | "fonts", 16 | "webfonts", 17 | "font", 18 | "loader", 19 | "@font-face" 20 | ], 21 | "files": [ 22 | "webfontloader.js", 23 | "src/**/*.js" 24 | ], 25 | "contributors": [ 26 | "Ryan Carver ", 27 | "Jeremie Lenfant-engelmann ", 28 | "Sean McBride ", 29 | "Bram Stein " 30 | ], 31 | "license": "Apache-2.0", 32 | "bugs": { 33 | "url": "https://github.com/typekit/webfontloader/issues" 34 | }, 35 | "homepage": "https://github.com/typekit/webfontloader", 36 | "devDependencies": {} 37 | } 38 | -------------------------------------------------------------------------------- /spec/core/cssclassname_spec.js: -------------------------------------------------------------------------------- 1 | describe('CssClassName', function () { 2 | var CssClassName = webfont.CssClassName, 3 | sanitizer = new CssClassName(); 4 | 5 | describe('#sanitize', function () { 6 | it('should sanitize spaces in names', function () { 7 | expect(sanitizer.sanitize(' My Family ')).toEqual('myfamily'); 8 | }); 9 | 10 | it('should sanitize numbers in names', function () { 11 | expect(sanitizer.sanitize('99 My Family 99')).toEqual('99myfamily99');; 12 | }); 13 | 14 | it('should sanitize other characters', function () { 15 | expect(sanitizer.sanitize('_My+Family!-')).toEqual('myfamily'); 16 | }); 17 | }); 18 | 19 | describe('#build', function () { 20 | it('should build many parts', function () { 21 | expect(sanitizer.build('pre_', 'My Family', '_post')).toEqual('pre-myfamily-post'); 22 | }); 23 | 24 | it('should build some parts', function () { 25 | expect(sanitizer.build('pre!', 'My Family')).toEqual('pre-myfamily'); 26 | }); 27 | }); 28 | 29 | describe('#constructor', function () { 30 | it('should use a hyphen as a default separator', function () { 31 | var sanitizer = new CssClassName(); 32 | 33 | expect(sanitizer.build('pre', 'post')).toEqual('pre-post'); 34 | }); 35 | 36 | it('should use the configured separator', function () { 37 | var sanitizer = new CssClassName('_'); 38 | 39 | expect(sanitizer.build('pre', 'post')).toEqual('pre_post'); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /spec/core/eventdispatcher_spec.js: -------------------------------------------------------------------------------- 1 | describe('EventDispatcher', function () { 2 | var EventDispatcher = webfont.EventDispatcher, 3 | DomHelper = webfont.DomHelper, 4 | Font = webfont.Font, 5 | domHelper = new DomHelper(window), 6 | element = null 7 | eventDispatcher = null, 8 | font = null; 9 | 10 | beforeEach(function () { 11 | element = domHelper.getLoadWindow().document.documentElement; 12 | config = { 13 | loading: jasmine.createSpy('loading'), 14 | active: jasmine.createSpy('active'), 15 | inactive: jasmine.createSpy('inactive'), 16 | fontloading: jasmine.createSpy('fontloading'), 17 | fontactive: jasmine.createSpy('fontactive'), 18 | fontinactive: jasmine.createSpy('fontinactive'), 19 | classes: true, 20 | events: true 21 | }; 22 | 23 | element.className = ''; 24 | 25 | eventDispatcher = new EventDispatcher(domHelper, config); 26 | 27 | font = new Font('My Family', 'n4'); 28 | }); 29 | 30 | describe('#dispatchLoading', function () { 31 | beforeEach(function () { 32 | eventDispatcher.dispatchLoading(); 33 | }); 34 | 35 | it('should call the correct callback', function () { 36 | expect(config.loading).toHaveBeenCalled(); 37 | }); 38 | 39 | it('should set the correct class name', function () { 40 | expect(element.className).toEqual('wf-loading'); 41 | }); 42 | }); 43 | 44 | describe('#dispatchFontLoading', function () { 45 | beforeEach(function () { 46 | eventDispatcher.dispatchFontLoading(font); 47 | }); 48 | 49 | it('should call the correct callback', function () { 50 | expect(config.fontloading).toHaveBeenCalledWith('My Family', 'n4'); 51 | }); 52 | 53 | it('should set the correct class name', function () { 54 | expect(element.className).toEqual('wf-myfamily-n4-loading'); 55 | }); 56 | }); 57 | 58 | describe('#dispatchFontInactive', function () { 59 | beforeEach(function () { 60 | eventDispatcher.dispatchFontInactive(font); 61 | }); 62 | 63 | it('should call the correct callback', function () { 64 | expect(config.fontinactive).toHaveBeenCalledWith('My Family', 'n4'); 65 | }); 66 | 67 | it('should set the correct class name', function () { 68 | expect(element.className).toEqual('wf-myfamily-n4-inactive'); 69 | }); 70 | }); 71 | 72 | describe('#dispatchFontInactive - with loading class', function () { 73 | beforeEach(function () { 74 | eventDispatcher.dispatchFontLoading(font); 75 | eventDispatcher.dispatchFontInactive(font); 76 | }); 77 | 78 | it('should set the correct class name', function () { 79 | expect(element.className).toEqual('wf-myfamily-n4-inactive'); 80 | }); 81 | }); 82 | 83 | describe('#dispatchFontInactive - with active class', function () { 84 | beforeEach(function () { 85 | eventDispatcher.dispatchFontActive(font); 86 | eventDispatcher.dispatchFontInactive(font); 87 | }); 88 | 89 | it('should not append the inactive class name', function () { 90 | expect(element.className).toEqual('wf-myfamily-n4-active'); 91 | }); 92 | 93 | it('should still call the correct callback', function () { 94 | expect(config.fontinactive).toHaveBeenCalledWith('My Family', 'n4'); 95 | }); 96 | }); 97 | 98 | describe('#dispatchFontActive', function () { 99 | beforeEach(function () { 100 | eventDispatcher.dispatchFontActive(font); 101 | }); 102 | 103 | it('should call the correct callback', function () { 104 | expect(config.fontactive).toHaveBeenCalledWith('My Family', 'n4'); 105 | }); 106 | 107 | it('should set the correct class name', function () { 108 | expect(element.className).toEqual('wf-myfamily-n4-active'); 109 | }); 110 | }); 111 | 112 | describe('#dispatchFontActive - with loading class', function () { 113 | beforeEach(function () { 114 | eventDispatcher.dispatchFontLoading(font); 115 | eventDispatcher.dispatchFontActive(font); 116 | }); 117 | 118 | it('should set the correct class name', function () { 119 | expect(element.className).toEqual('wf-myfamily-n4-active'); 120 | }); 121 | }); 122 | 123 | describe('#dispatchFontActive - with inactive class', function () { 124 | beforeEach(function () { 125 | eventDispatcher.dispatchFontInactive(font); 126 | eventDispatcher.dispatchFontActive(font); 127 | }); 128 | 129 | it('should set the correct class', function () { 130 | expect(element.className).toEqual('wf-myfamily-n4-active'); 131 | }); 132 | }); 133 | 134 | describe('#dispatchInactive', function () { 135 | beforeEach(function () { 136 | eventDispatcher.dispatchInactive(); 137 | }); 138 | 139 | it('should call the correct callback', function () { 140 | expect(config.inactive).toHaveBeenCalled(); 141 | }); 142 | 143 | it('should set the correct class name', function () { 144 | expect(element.className).toEqual('wf-inactive'); 145 | }); 146 | }); 147 | 148 | describe('#dispatchInactive - with loading class', function () { 149 | beforeEach(function () { 150 | eventDispatcher.dispatchLoading(); 151 | eventDispatcher.dispatchInactive(); 152 | }); 153 | 154 | it('should set the correct class name', function () { 155 | expect(element.className).toEqual('wf-inactive'); 156 | }); 157 | }); 158 | 159 | describe('#dispatchInactive - with active class', function () { 160 | beforeEach(function () { 161 | eventDispatcher.dispatchActive(); 162 | eventDispatcher.dispatchInactive(); 163 | }); 164 | 165 | it('should not set the the inactive class', function () { 166 | expect(element.className).toEqual('wf-active'); 167 | }); 168 | 169 | it('should still call the inactive callback', function () { 170 | expect(config.inactive).toHaveBeenCalled(); 171 | }); 172 | }); 173 | 174 | describe('#dispatchActive', function () { 175 | beforeEach(function () { 176 | eventDispatcher.dispatchActive(); 177 | }); 178 | 179 | it('should call the correct callback', function () { 180 | expect(config.active).toHaveBeenCalled(); 181 | }); 182 | 183 | it('should set the correct class name', function () { 184 | expect(element.className).toEqual('wf-active'); 185 | }); 186 | }); 187 | 188 | describe('#dispatchActive - with loading class', function () { 189 | beforeEach(function () { 190 | eventDispatcher.dispatchLoading(); 191 | eventDispatcher.dispatchActive(); 192 | }); 193 | 194 | it('should set the correct class name', function () { 195 | expect(element.className).toEqual('wf-active'); 196 | }); 197 | }); 198 | 199 | describe('#dispatchActive - with inactive class', function () { 200 | beforeEach(function () { 201 | eventDispatcher.dispatchInactive(); 202 | eventDispatcher.dispatchActive(); 203 | }); 204 | 205 | it('should set the correct class name', function () { 206 | expect(element.className).toEqual('wf-active'); 207 | }); 208 | }); 209 | 210 | describe('disable classes and events', function () { 211 | beforeEach(function () { 212 | config.classes = false; 213 | config.events = false; 214 | eventDispatcher = new EventDispatcher(domHelper, config); 215 | eventDispatcher.dispatchInactive(); 216 | eventDispatcher.dispatchActive(); 217 | eventDispatcher.dispatchLoading(); 218 | eventDispatcher.dispatchFontInactive(font); 219 | eventDispatcher.dispatchFontActive(font); 220 | eventDispatcher.dispatchFontLoading(font); 221 | }); 222 | 223 | afterEach(function () { 224 | config.classes = true; 225 | config.events = true; 226 | }); 227 | 228 | it('should not fire any events', function () { 229 | expect(config.inactive).not.toHaveBeenCalled(); 230 | expect(config.active).not.toHaveBeenCalled(); 231 | expect(config.loading).not.toHaveBeenCalled(); 232 | expect(config.fontinactive).not.toHaveBeenCalled(); 233 | expect(config.fontactive).not.toHaveBeenCalled(); 234 | expect(config.fontloading).not.toHaveBeenCalled(); 235 | }); 236 | }); 237 | 238 | describe('disable classes', function () { 239 | beforeEach(function () { 240 | config.classes = false; 241 | eventDispatcher = new EventDispatcher(domHelper, config); 242 | eventDispatcher.dispatchInactive(); 243 | eventDispatcher.dispatchActive(); 244 | eventDispatcher.dispatchLoading(); 245 | eventDispatcher.dispatchFontInactive(font); 246 | eventDispatcher.dispatchFontActive(font); 247 | eventDispatcher.dispatchFontLoading(font); 248 | }); 249 | 250 | afterEach(function () { 251 | config.classes = true; 252 | }); 253 | 254 | it('should not fire any events', function () { 255 | expect(element.className).toEqual(''); 256 | }); 257 | }); 258 | }); 259 | -------------------------------------------------------------------------------- /spec/core/font_spec.js: -------------------------------------------------------------------------------- 1 | describe('Font', function () { 2 | var Font = webfont.Font; 3 | 4 | describe('#quote', function () { 5 | var quote = function (font) { 6 | return new Font(font).getCssName(); 7 | }; 8 | 9 | it('should quote names with spaces', function () { 10 | expect(quote('My Family')).toEqual("'My Family'"); 11 | }); 12 | 13 | it('should quote names with spaces and double quotes', function () { 14 | expect(quote('"My Family"')).toEqual("'My Family'"); 15 | }); 16 | 17 | it('should quote names with spaces and single quotes', function () { 18 | expect(quote("'My Family'")).toEqual("'My Family'"); 19 | }); 20 | 21 | it('should quote multiple single quoted names separated with a comma', function () { 22 | expect(quote("'family 1','family 2'")).toEqual("'family 1','family 2'"); 23 | }); 24 | 25 | it('should quote multiple single quoted names separated with a comma and space', function () { 26 | expect(quote("'family 1', 'family 2'")).toEqual("'family 1','family 2'"); 27 | }); 28 | 29 | it('should quote family names starting with a number', function () { 30 | expect(quote('5test')).toEqual("'5test'"); 31 | }); 32 | 33 | it('should not quote when there is no space', function () { 34 | expect(quote('MyFamily')).toEqual('MyFamily'); 35 | }); 36 | 37 | it('should remove quotes when they are unnecesssary', function () { 38 | expect(quote('"MyFamily"')).toEqual('MyFamily'); 39 | }); 40 | 41 | it('should not quote multiple names when there is no space', function () { 42 | expect(quote("'family-1', 'family-2'")).toEqual('family-1,family-2'); 43 | }); 44 | }); 45 | 46 | describe('#toCssString', function () { 47 | function toCssString(fvd) { 48 | return new Font('My Family', fvd).toCssString(); 49 | } 50 | 51 | it('should expand font styles correctly', function () { 52 | expect(toCssString('n4')).toEqual("normal 400 300px 'My Family'"); 53 | expect(toCssString('i4')).toEqual("italic 400 300px 'My Family'"); 54 | expect(toCssString('o4')).toEqual("oblique 400 300px 'My Family'"); 55 | }); 56 | 57 | it('should expand weights correctly', function () { 58 | for (var i = 1; i < 10; i += 1) { 59 | expect(toCssString('n' + i)).toEqual("normal " + (i * 100) + " 300px 'My Family'"); 60 | } 61 | }); 62 | }); 63 | 64 | describe('#getCssVariation', function () { 65 | function toCss(fvd) { 66 | return new Font('My Family', fvd).getCssVariation(); 67 | } 68 | 69 | it('should expand font-style', function () { 70 | expect(toCss('n4')).toEqual('font-style:normal;font-weight:400;'); 71 | expect(toCss('i4')).toEqual('font-style:italic;font-weight:400;'); 72 | expect(toCss('o4')).toEqual('font-style:oblique;font-weight:400;'); 73 | }); 74 | 75 | it('should expand weights correctly', function () { 76 | for (var i = 1; i < 10; i += 1) { 77 | expect(toCss('n' + i)).toEqual('font-style:normal;font-weight:' + (i * 100) + ';'); 78 | } 79 | }); 80 | 81 | it('should not expand incorrect input', function () { 82 | expect(toCss('')).toEqual('font-style:normal;font-weight:400;'); 83 | expect(toCss('n')).toEqual('font-style:normal;font-weight:400;'); 84 | expect(toCss('1')).toEqual('font-style:normal;font-weight:400;'); 85 | expect(toCss('n1x')).toEqual('font-style:normal;font-weight:400;'); 86 | }); 87 | }); 88 | 89 | describe('parseCssVariation', function () { 90 | function toFvd(css) { 91 | return Font.parseCssVariation(css); 92 | } 93 | 94 | it('should default to n4 when there is no description', function () { 95 | expect(toFvd('')).toEqual('n4'); 96 | expect(toFvd(null)).toEqual('n4'); 97 | expect(toFvd(undefined)).toEqual('n4'); 98 | }); 99 | 100 | it('should compact font style', function () { 101 | expect(toFvd('font-style: normal;')).toEqual('n4'); 102 | expect(toFvd('font-style: italic;')).toEqual('i4'); 103 | expect(toFvd('font-style: oblique;')).toEqual('o4'); 104 | }); 105 | 106 | it('should return the default value when font-style is incorrect', function () { 107 | expect(toFvd('font-style: other;')).toEqual('n4'); 108 | }); 109 | 110 | it('should compact font weight', function () { 111 | expect(toFvd('font-weight: normal;')).toEqual('n4'); 112 | expect(toFvd('font-weight: bold;')).toEqual('n7'); 113 | for (var i = 1; i < 10; i += 1) { 114 | expect(toFvd('font-weight: ' + (i * 100) + ';')).toEqual('n' + i); 115 | } 116 | }); 117 | 118 | it('should return the default value when font-weight is incorrect', function () { 119 | expect(toFvd('font-weight: 140;')).toEqual('n4'); 120 | expect(toFvd('font-weight: other;')).toEqual('n4'); 121 | }); 122 | 123 | it('should compact multiple properties', function () { 124 | expect(toFvd('font-weight: bold; font-style: italic;')).toEqual('i7'); 125 | expect(toFvd('; font-weight: bold; font-style: italic;')).toEqual('i7'); 126 | expect(toFvd('font-style:italic;font-weight:bold;')).toEqual('i7'); 127 | expect(toFvd(' font-style: italic ;\n\nfont-weight: bold; ')).toEqual('i7'); 128 | }); 129 | 130 | it('should return default values for incorrect individual properties', function () { 131 | expect(toFvd('src: url(/font.otf)')).toEqual('n4'); 132 | expect(toFvd('font-weight: 900; src: url(/font.otf);')).toEqual('n9'); 133 | expect(toFvd('font-weight: 800; font-stretch:condensed;')).toEqual('n8'); 134 | }); 135 | }); 136 | }); 137 | -------------------------------------------------------------------------------- /spec/core/fontmoduleloader_spec.js: -------------------------------------------------------------------------------- 1 | describe('FontModuleLoader', function () { 2 | var FontModuleLoader = webfont.FontModuleLoader; 3 | 4 | describe('#getModules', function () { 5 | var fontModuleLoader = null; 6 | 7 | beforeEach(function () { 8 | fontModuleLoader = new FontModuleLoader(); 9 | }); 10 | 11 | it('should return an empty array without modules', function () { 12 | var modules = fontModuleLoader.getModules(); 13 | 14 | expect(modules).not.toBeNull(); 15 | expect(modules.length).toEqual(0); 16 | }); 17 | 18 | it('should have modules', function () { 19 | fontModuleLoader.addModuleFactory('booh', function () { 20 | return { 21 | scary: true 22 | }; 23 | }); 24 | 25 | fontModuleLoader.addModuleFactory('haha', function () { 26 | return { 27 | funny: true 28 | }; 29 | }); 30 | 31 | fontModuleLoader.addModuleFactory('moo', function () { 32 | return { 33 | cowy: true 34 | }; 35 | }); 36 | 37 | var modules = fontModuleLoader.getModules({ 38 | booh: {}, 39 | moo: {}, 40 | nothing: {} 41 | }); 42 | 43 | expect(modules).not.toBeNull(); 44 | expect(modules.length).toEqual(2); 45 | 46 | var module = modules[0]; 47 | expect(module).not.toBeNull(); 48 | expect(module.scary || module.cowy).toBe(true); 49 | 50 | var module = modules[1]; 51 | expect(module).not.toBeNull(); 52 | expect(module.scary || module.cowy).toBe(true); 53 | }); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /spec/core/fontruler_spec.js: -------------------------------------------------------------------------------- 1 | describe('FontRuler', function () { 2 | var Font = webfont.Font, 3 | FontRuler = webfont.FontRuler, 4 | DomHelper = webfont.DomHelper, 5 | Size = webfont.Size, 6 | domHelper = null, 7 | font = null; 8 | 9 | beforeEach(function () { 10 | font = new Font('sans-serif'); 11 | domHelper = new DomHelper(window); 12 | }); 13 | 14 | it('should prevent a long test string from word wrapping', function () { 15 | var fontRulerA = new FontRuler(domHelper, 'abc'), 16 | fontRulerB = new FontRuler(domHelper, 'abc HelloWorld,thisshouldwrap!!!!'); 17 | 18 | fontRulerA.insert(); 19 | fontRulerB.insert(); 20 | 21 | fontRulerA.setFont(font); 22 | fontRulerB.setFont(font); 23 | 24 | var widthA = fontRulerA.getWidth(), 25 | widthB = fontRulerB.getWidth(); 26 | 27 | expect(widthA).not.toEqual(widthB); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /spec/core/fontwatchrunner_spec.js: -------------------------------------------------------------------------------- 1 | describe('FontWatchRunner', function () { 2 | var FontWatchRunner = webfont.FontWatchRunner, 3 | Font = webfont.Font, 4 | BrowserInfo = webfont.BrowserInfo, 5 | DomHelper = webfont.DomHelper, 6 | FontRuler = webfont.FontRuler; 7 | 8 | var domHelper = null, 9 | activeCallback = null, 10 | inactiveCallback = null, 11 | nullFont = null, 12 | sourceSansA = null, 13 | sourceSansB = null, 14 | elena = null; 15 | 16 | beforeEach(function () { 17 | domHelper = new DomHelper(window); 18 | 19 | activeCallback = jasmine.createSpy('activeCallback'); 20 | inactiveCallback = jasmine.createSpy('inactiveCallback'); 21 | 22 | nullFont = new Font('__webfontloader_test__'); 23 | sourceSansA = new Font('SourceSansA'); 24 | sourceSansB = new Font('SourceSansB'); 25 | elena = new Font('Elena'); 26 | }); 27 | 28 | it('should fail to load a null font', function () { 29 | var fontWatchRunner = new FontWatchRunner(activeCallback, inactiveCallback, 30 | domHelper, nullFont, 500, {}); 31 | 32 | runs(function () { 33 | fontWatchRunner.start(); 34 | }); 35 | 36 | waitsFor(function () { 37 | return activeCallback.wasCalled || inactiveCallback.wasCalled; 38 | }); 39 | 40 | runs(function () { 41 | expect(inactiveCallback).toHaveBeenCalledWith(nullFont); 42 | }); 43 | }); 44 | 45 | it('should load font succesfully', function () { 46 | var fontWatchRunner = new FontWatchRunner(activeCallback, inactiveCallback, 47 | domHelper, sourceSansA, 5000), 48 | ruler = new FontRuler(domHelper, 'abcdef'), 49 | monospace = new Font('monospace'), 50 | sourceSansAFallback = new Font("'SourceSansA', monospace"), 51 | activeWidth = null, 52 | originalWidth = null, 53 | finalCheck = false; 54 | 55 | runs(function () { 56 | ruler.insert(); 57 | ruler.setFont(monospace); 58 | originalWidth = ruler.getWidth(); 59 | ruler.setFont(sourceSansAFallback); 60 | fontWatchRunner.start(); 61 | }); 62 | 63 | waitsFor(function () { 64 | return activeCallback.wasCalled || inactiveCallback.wasCalled; 65 | }); 66 | 67 | runs(function () { 68 | expect(activeCallback).toHaveBeenCalledWith(sourceSansA); 69 | activeWidth = ruler.getWidth(); 70 | expect(activeWidth).not.toEqual(originalWidth); 71 | 72 | window.setTimeout(function () { 73 | finalCheck = true; 74 | }, 200); 75 | }); 76 | 77 | waitsFor(function () { 78 | return finalCheck; 79 | }); 80 | 81 | runs(function () { 82 | expect(ruler.getWidth()).not.toEqual(originalWidth); 83 | expect(ruler.getWidth()).toEqual(activeWidth); 84 | }); 85 | }); 86 | 87 | it('should attempt to load a non-existing font', function () { 88 | var fontWatchRunner = new FontWatchRunner(activeCallback, inactiveCallback, 89 | domHelper, elena, 500, {}); 90 | 91 | runs(function () { 92 | fontWatchRunner.start(); 93 | }); 94 | 95 | waitsFor(function () { 96 | return activeCallback.wasCalled || inactiveCallback.wasCalled; 97 | }); 98 | 99 | runs(function () { 100 | expect(inactiveCallback).toHaveBeenCalledWith(elena); 101 | }); 102 | }); 103 | 104 | it('should load even if @font-face is inserted after watching has started', function () { 105 | var fontWatchRunner = new FontWatchRunner(activeCallback, inactiveCallback, 106 | domHelper, sourceSansB, 5000), 107 | ruler = new FontRuler(domHelper, 'abcdef'), 108 | monospace = new Font('monospace'), 109 | sourceSansBFallback = new Font("'SourceSansB', monospace"), 110 | activeWidth = null, 111 | originalWidth = null, 112 | finalCheck = false; 113 | 114 | runs(function () { 115 | ruler.insert(); 116 | ruler.setFont(monospace); 117 | originalWidth = ruler.getWidth(); 118 | ruler.setFont(sourceSansBFallback); 119 | fontWatchRunner.start(); 120 | var link = document.createElement('link'); 121 | 122 | link.rel = "stylesheet"; 123 | link.href= "fixtures/fonts/sourcesansb.css"; 124 | 125 | domHelper.insertInto('head', link); 126 | }); 127 | 128 | waitsFor(function () { 129 | return activeCallback.wasCalled || inactiveCallback.wasCalled; 130 | }); 131 | 132 | runs(function () { 133 | expect(activeCallback).toHaveBeenCalledWith(sourceSansB); 134 | activeWidth = ruler.getWidth(); 135 | expect(activeWidth).not.toEqual(originalWidth); 136 | 137 | window.setTimeout(function () { 138 | finalCheck = true; 139 | }, 200); 140 | }); 141 | 142 | waitsFor(function () { 143 | return finalCheck; 144 | }); 145 | 146 | runs(function () { 147 | expect(ruler.getWidth()).not.toEqual(originalWidth); 148 | expect(ruler.getWidth()).toEqual(activeWidth); 149 | }); 150 | }); 151 | }); 152 | -------------------------------------------------------------------------------- /spec/core/nativefontwatchrunner_spec.js: -------------------------------------------------------------------------------- 1 | describe('NativeFontWatchRunner', function () { 2 | var NativeFontWatchRunner = webfont.NativeFontWatchRunner, 3 | Font = webfont.Font, 4 | DomHelper = webfont.DomHelper, 5 | FontRuler = webfont.FontRuler; 6 | 7 | var domHelper = null, 8 | activeCallback = null, 9 | inactiveCallback = null, 10 | nullFont = null, 11 | sourceSansC = null, 12 | sourceSansDup = null, 13 | elena = null; 14 | 15 | beforeEach(function () { 16 | domHelper = new DomHelper(window); 17 | 18 | activeCallback = jasmine.createSpy('activeCallback'); 19 | inactiveCallback = jasmine.createSpy('inactiveCallback'); 20 | 21 | nullFont = new Font('__webfontloader_test_3__'); 22 | sourceSansC = new Font('SourceSansC'); 23 | sourceSansDup = new Font('SourceSansDup'); 24 | elena = new Font('Elena'); 25 | }); 26 | 27 | if (window['FontFace']) { 28 | it('should fail to load a null font', function () { 29 | var fontWatchRunner = new NativeFontWatchRunner(activeCallback, inactiveCallback, 30 | domHelper, nullFont, 500); 31 | 32 | runs(function () { 33 | fontWatchRunner.start(); 34 | }); 35 | 36 | waitsFor(function () { 37 | return activeCallback.wasCalled || inactiveCallback.wasCalled; 38 | }); 39 | 40 | runs(function () { 41 | expect(inactiveCallback).toHaveBeenCalledWith(nullFont); 42 | }); 43 | }); 44 | 45 | function succesfulLoadingSpec(getFontToBeLoaded, getFontNameToBeLoaded) { 46 | var fontToBeLoaded = getFontToBeLoaded(), 47 | fontNameToBeLoaded = getFontNameToBeLoaded(), 48 | fontWatchRunner = new NativeFontWatchRunner(activeCallback, inactiveCallback, 49 | domHelper, fontToBeLoaded), 50 | ruler = new FontRuler(domHelper, 'abcdef'), 51 | monospace = new Font('monospace'), 52 | fallbackFont = new Font(fontNameToBeLoaded + ', monospace'), 53 | activeWidth = null, 54 | originalWidth = null, 55 | finalCheck = false; 56 | 57 | runs(function () { 58 | ruler.insert(); 59 | ruler.setFont(monospace); 60 | originalWidth = ruler.getWidth(); 61 | ruler.setFont(fallbackFont); 62 | fontWatchRunner.start(); 63 | }); 64 | 65 | waitsFor(function () { 66 | return activeCallback.wasCalled || inactiveCallback.wasCalled; 67 | }); 68 | 69 | runs(function () { 70 | expect(activeCallback).toHaveBeenCalledWith(fontToBeLoaded); 71 | activeWidth = ruler.getWidth(); 72 | expect(activeWidth).not.toEqual(originalWidth); 73 | 74 | window.setTimeout(function () { 75 | finalCheck = true; 76 | }, 200); 77 | }); 78 | 79 | waitsFor(function () { 80 | return finalCheck; 81 | }); 82 | 83 | runs(function () { 84 | expect(ruler.getWidth()).not.toEqual(originalWidth); 85 | expect(ruler.getWidth()).toEqual(activeWidth); 86 | }); 87 | } 88 | 89 | it('should load font succesfully', 90 | succesfulLoadingSpec.bind(null, function() { return sourceSansC; }, function() { return 'SourceSansC'; })); 91 | 92 | it('should load font succesfully even if it is duplicated', 93 | succesfulLoadingSpec.bind(null, function() { return sourceSansDup; }, function() { return 'SourceSansDup'; })); 94 | 95 | it('should attempt to load a non-existing font', function () { 96 | var fontWatchRunner = new NativeFontWatchRunner(activeCallback, inactiveCallback, 97 | domHelper, elena, 500); 98 | 99 | runs(function () { 100 | fontWatchRunner.start(); 101 | }); 102 | 103 | waitsFor(function () { 104 | return activeCallback.wasCalled || inactiveCallback.wasCalled; 105 | }); 106 | 107 | runs(function () { 108 | expect(inactiveCallback).toHaveBeenCalledWith(elena); 109 | }); 110 | }); 111 | } 112 | }); 113 | -------------------------------------------------------------------------------- /spec/core/size_spec.js: -------------------------------------------------------------------------------- 1 | describe('Size', function () { 2 | var Size = webfont.Size; 3 | 4 | it('should return true on identical sizes', function () { 5 | expect(new Size(10, 10).equals(new Size(10, 10))).toBe(true); 6 | }); 7 | 8 | it('should return false when two sizes are different', function () { 9 | expect(new Size(10, 10).equals(new Size(20, 20))).toBe(false); 10 | expect(new Size(10, 10).equals(new Size(10, 20))).toBe(false); 11 | }); 12 | 13 | it('should return false when one font is undefined or null', function () { 14 | expect(new Size(10, 10).equals(undefined)).toBe(false); 15 | expect(new Size(10, 10).equals(null)).toBe(false); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /spec/core/webfont_spec.js: -------------------------------------------------------------------------------- 1 | describe('WebFont', function () { 2 | var WebFont = webfont.WebFont, 3 | Font = webfont.Font; 4 | FontWatchRunner = webfont.FontWatchRunner, 5 | NativeFontWatchRunner = webfont.NativeFontWatchRunner, 6 | Version = webfont.Version, 7 | Font = webfont.Font, 8 | FontModuleLoader = webfont.FontModuleLoader, 9 | fontModuleLoader = null; 10 | 11 | beforeEach(function () { 12 | fontModuleLoader = new FontModuleLoader(); 13 | }); 14 | 15 | describe('font load with context', function () { 16 | var font = null, 17 | testModule = null, 18 | fakeMainWindow = { 19 | document: {} 20 | }; 21 | 22 | beforeEach(function () { 23 | font = new WebFont(fakeMainWindow); 24 | font.addModule('test', function (conf, domHelper) { 25 | testModule = new function () { 26 | this.domHelper = domHelper; 27 | }; 28 | testModule.load = function (onReady) { 29 | onReady([]); 30 | }; 31 | 32 | return testModule; 33 | }); 34 | }); 35 | 36 | it('should load with the correct context', function () { 37 | font.load({ 38 | test: { 39 | somedata: 'in french a cow says meuh' 40 | }, 41 | context: window 42 | }); 43 | 44 | expect(testModule.domHelper).not.toBeNull(); 45 | expect(testModule.domHelper).not.toBeUndefined(); 46 | 47 | expect(testModule.domHelper.getMainWindow()).toEqual(fakeMainWindow); 48 | expect(testModule.domHelper.getLoadWindow()).toEqual(window); 49 | }); 50 | }); 51 | 52 | describe('module failed to provide families and descriptions because it did not initialize properly', function () { 53 | var webfont = null, 54 | testModule = null, 55 | font = null, 56 | inactive = jasmine.createSpy('inactive'), 57 | active = jasmine.createSpy('active'); 58 | 59 | beforeEach(function () { 60 | font = new Font('Font1'); 61 | jasmine.Clock.useMock(); 62 | webfont = new WebFont(window); 63 | webfont.addModule('test', function (conf, domHelper) { 64 | testModule = new function () { 65 | this.conf = conf; 66 | }; 67 | 68 | spyOn(FontWatchRunner.prototype, 'start').andCallFake(function () { 69 | if (conf.id) { 70 | active(font); 71 | } else { 72 | inactive(font); 73 | } 74 | }); 75 | 76 | spyOn(NativeFontWatchRunner.prototype, 'start').andCallFake(function () { 77 | if (conf.id) { 78 | active(font); 79 | } else { 80 | inactive(font); 81 | } 82 | }); 83 | 84 | testModule.load = function (onReady) { 85 | if (conf.id) { 86 | onReady([font]); 87 | } else { 88 | onReady([]); 89 | } 90 | }; 91 | 92 | return testModule; 93 | }); 94 | }); 95 | 96 | it('should load with a project id', function () { 97 | webfont.load({ 98 | test: { 99 | id: 'hello world' 100 | }, 101 | inactive: inactive, 102 | active: active 103 | }); 104 | 105 | jasmine.Clock.tick(1); 106 | 107 | expect(testModule).not.toBeNull(); 108 | expect(active).toHaveBeenCalled(); 109 | }); 110 | 111 | it('should not load without a project id', function () { 112 | webfont.load({ 113 | test: { 114 | }, 115 | inactive: inactive, 116 | active: active 117 | }); 118 | 119 | jasmine.Clock.tick(1); 120 | 121 | expect(testModule).not.toBeNull(); 122 | expect(inactive).toHaveBeenCalled(); 123 | }); 124 | }); 125 | 126 | describe('should pass both fonts and test strings to onready', function () { 127 | var font = null, 128 | fontTestStrings = null, 129 | testModule = null; 130 | 131 | beforeEach(function () { 132 | font = new WebFont(window); 133 | 134 | font.addModule('test', function (conf, domHelper) { 135 | testModule = new function () {}; 136 | testModule.load = function (onReady) { 137 | onReady([new Font('Elena')], { 'Elena': '1234567' }); 138 | }; 139 | 140 | return testModule; 141 | }); 142 | 143 | spyOn(font, 'onModuleReady_'); 144 | }); 145 | 146 | it('should have called onModuleReady with the correct font and test string', function () { 147 | font.load({ 148 | 'test': {} 149 | }); 150 | 151 | expect(font.onModuleReady_).toHaveBeenCalled(); 152 | expect(font.onModuleReady_.calls[0].args[2]).toEqual([new Font('Elena')]); 153 | expect(font.onModuleReady_.calls[0].args[3]).toEqual({ 'Elena': '1234567' }); 154 | }); 155 | }); 156 | 157 | describe('module fails to load', function () { 158 | var font = null, 159 | testModule = null, 160 | inactive = null, 161 | active = null; 162 | 163 | beforeEach(function () { 164 | inactive = jasmine.createSpy('inactive'), 165 | active = jasmine.createSpy('active'); 166 | 167 | font = new WebFont(window, fontModuleLoader); 168 | 169 | font.addModule('test', function (conf, domHelper) { 170 | testModule = new function () {}; 171 | testModule.load = function (onReady) { 172 | onReady([]); 173 | }; 174 | 175 | return testModule; 176 | }); 177 | }); 178 | 179 | it('times out and calls inactive', function () { 180 | runs(function () { 181 | font.load({ 182 | 'test': {}, 183 | inactive: inactive, 184 | active: active 185 | }); 186 | }); 187 | 188 | waitsFor(function () { 189 | return active.wasCalled || inactive.wasCalled; 190 | }); 191 | 192 | runs(function () { 193 | expect(inactive).toHaveBeenCalled(); 194 | expect(active).not.toHaveBeenCalled(); 195 | }); 196 | }); 197 | }); 198 | 199 | describe('synchronous load event', function () { 200 | var font = null, 201 | testModule = null, 202 | inactive = null, 203 | loading = null, 204 | active = null; 205 | 206 | beforeEach(function () { 207 | inactive = jasmine.createSpy('inactive'), 208 | active = jasmine.createSpy('active'); 209 | loading = jasmine.createSpy('loading'); 210 | 211 | font = new WebFont(window, fontModuleLoader); 212 | 213 | font.addModule('test', function (conf, domHelper) { 214 | testModule = new function () {}; 215 | testModule.load = function (onReady) { 216 | onReady([new Font('Elena')]); 217 | }; 218 | 219 | return testModule; 220 | }); 221 | }); 222 | 223 | it('fires loading event correctly', function () { 224 | runs(function () { 225 | font.load({ 226 | 'test': {}, 227 | inactive: inactive, 228 | active: active, 229 | loading: loading 230 | }); 231 | expect(loading).toHaveBeenCalled(); 232 | }); 233 | 234 | waitsFor(function () { 235 | return active.wasCalled || inactive.wasCalled; 236 | }); 237 | 238 | runs(function () { 239 | expect(inactive).toHaveBeenCalled(); 240 | expect(active).not.toHaveBeenCalled(); 241 | }); 242 | }); 243 | }); 244 | }); 245 | -------------------------------------------------------------------------------- /spec/deps.js: -------------------------------------------------------------------------------- 1 | // This file was autogenerated by calcdeps.js 2 | goog.addDependency("../../src/closure.js", [], []); 3 | goog.addDependency("../../src/core/cssclassname.js", ["webfont.CssClassName"], []); 4 | goog.addDependency("../../src/core/domhelper.js", ["webfont.DomHelper"], []); 5 | goog.addDependency("../../src/core/stylesheetwaiter.js", ["webfont.StyleSheetWaiter"], []); 6 | goog.addDependency("../../src/core/eventdispatcher.js", ["webfont.EventDispatcher"], ["webfont.CssClassName"]); 7 | goog.addDependency("../../src/core/font.js", ["webfont.Font"], []); 8 | goog.addDependency("../../src/core/fontmodule.js", ["webfont.FontModule"], []); 9 | goog.addDependency("../../src/core/fontmoduleloader.js", ["webfont.FontModuleLoader","webfont.FontModuleFactory"], []); 10 | goog.addDependency("../../src/core/fontruler.js", ["webfont.FontRuler"], []); 11 | goog.addDependency("../../src/core/fontwatcher.js", ["webfont.FontWatcher"], ["webfont.FontWatchRunner","webfont.NativeFontWatchRunner"]); 12 | goog.addDependency("../../src/core/fontwatchrunner.js", ["webfont.FontWatchRunner"], ["webfont.Font","webfont.FontRuler"]); 13 | goog.addDependency("../../src/core/initialize.js", ["webfont"], ["webfont.WebFont","webfont.modules.Typekit","webfont.modules.Fontdeck","webfont.modules.Monotype","webfont.modules.Custom","webfont.modules.google.GoogleFontApi"]); 14 | goog.addDependency("../../src/core/nativefontwatchrunner.js", ["webfont.NativeFontWatchRunner"], ["webfont.Font"]); 15 | goog.addDependency("../../src/core/webfont.js", ["webfont.WebFont"], ["webfont.DomHelper","webfont.EventDispatcher","webfont.FontWatcher","webfont.FontModuleLoader"]); 16 | goog.addDependency("../../src/modules/custom.js", ["webfont.modules.Custom"], ["webfont.Font", "webfont.StyleSheetWaiter"]); 17 | goog.addDependency("../../src/modules/fontdeck.js", ["webfont.modules.Fontdeck"], ["webfont.Font"]); 18 | goog.addDependency("../../src/modules/google/fontapiparser.js", ["webfont.modules.google.FontApiParser"], ["webfont.Font"]); 19 | goog.addDependency("../../src/modules/google/fontapiurlbuilder.js", ["webfont.modules.google.FontApiUrlBuilder"], []); 20 | goog.addDependency("../../src/modules/google/googlefontapi.js", ["webfont.modules.google.GoogleFontApi"], ["webfont.modules.google.FontApiUrlBuilder","webfont.modules.google.FontApiParser","webfont.FontWatchRunner", "webfont.StyleSheetWaiter"]); 21 | goog.addDependency("../../src/modules/monotype.js", ["webfont.modules.Monotype"], ["webfont.Font"]); 22 | goog.addDependency("../../src/modules/typekit.js", ["webfont.modules.Typekit"], ["webfont.Font"]); 23 | -------------------------------------------------------------------------------- /spec/fixtures/external_script.js: -------------------------------------------------------------------------------- 1 | window.EXTERNAL_SCRIPT_LOADED = true; 2 | -------------------------------------------------------------------------------- /spec/fixtures/external_stylesheet.css: -------------------------------------------------------------------------------- 1 | #TEST_ELEMENT { 2 | display: block; 3 | position: absolute; 4 | top: 0; 5 | left: 0; 6 | width: 300px; 7 | } 8 | -------------------------------------------------------------------------------- /spec/fixtures/fonts/LICENSE.txt: -------------------------------------------------------------------------------- 1 | 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. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | 5 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /spec/fixtures/fonts/nullfont.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:__webfontloader_test__;src:url(data:application/x-font-woff;base64,) format('woff'),url(data:font/truetype;base64,) format('truetype');} 2 | -------------------------------------------------------------------------------- /spec/fixtures/fonts/nullfont1.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:__webfontloader_test_1__;src:url(data:application/x-font-woff;base64,) format('woff'),url(data:font/truetype;base64,) format('truetype');} 2 | -------------------------------------------------------------------------------- /spec/fixtures/fonts/nullfont2.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:__webfontloader_test_2__;src:url(data:application/x-font-woff;base64,) format('woff'),url(data:font/truetype;base64,) format('truetype');} 2 | -------------------------------------------------------------------------------- /spec/fixtures/fonts/nullfont3.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:__webfontloader_test_3__;src:url(data:application/x-font-woff;base64,) format('woff'),url(data:font/truetype;base64,) format('truetype');} 2 | -------------------------------------------------------------------------------- /spec/fixtures/fonts/sourcesans.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typekit/webfontloader/117e48d521d6408f78cbfe4d23923cc828fdf576/spec/fixtures/fonts/sourcesans.eot -------------------------------------------------------------------------------- /spec/fixtures/fonts/sourcesans.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typekit/webfontloader/117e48d521d6408f78cbfe4d23923cc828fdf576/spec/fixtures/fonts/sourcesans.otf -------------------------------------------------------------------------------- /spec/fixtures/fonts/sourcesans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typekit/webfontloader/117e48d521d6408f78cbfe4d23923cc828fdf576/spec/fixtures/fonts/sourcesans.ttf -------------------------------------------------------------------------------- /spec/fixtures/fonts/sourcesans.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typekit/webfontloader/117e48d521d6408f78cbfe4d23923cc828fdf576/spec/fixtures/fonts/sourcesans.woff -------------------------------------------------------------------------------- /spec/fixtures/fonts/sourcesansa.css: -------------------------------------------------------------------------------- 1 | @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');} 2 | -------------------------------------------------------------------------------- /spec/fixtures/fonts/sourcesansb.css: -------------------------------------------------------------------------------- 1 | @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');} 2 | -------------------------------------------------------------------------------- /spec/fixtures/fonts/sourcesansc.css: -------------------------------------------------------------------------------- 1 | @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');} 2 | -------------------------------------------------------------------------------- /spec/fixtures/fonts/sourcesansd.css: -------------------------------------------------------------------------------- 1 | @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');} 2 | -------------------------------------------------------------------------------- /spec/fixtures/fonts/sourcesansdup1.css: -------------------------------------------------------------------------------- 1 | @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');} 2 | -------------------------------------------------------------------------------- /spec/fixtures/fonts/sourcesansdup2.css: -------------------------------------------------------------------------------- 1 | @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');} 2 | -------------------------------------------------------------------------------- /spec/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Webfontloader tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /spec/modules/custom_spec.js: -------------------------------------------------------------------------------- 1 | describe('modules.Custom', function () { 2 | var Custom = webfont.modules.Custom, 3 | FontFamily = webfont.FontFamily, 4 | Any = jasmine.Matchers.Any; 5 | 6 | describe('insert links correctly', function () { 7 | var fakeDomHelper = null, 8 | load = null; 9 | 10 | function notifySheetsLoaded() { 11 | var argsForCall = fakeDomHelper.loadStylesheet.argsForCall; 12 | for (var i = 0; i < argsForCall.length; i++) { 13 | var args = argsForCall[i]; 14 | args[1](); 15 | } 16 | } 17 | 18 | beforeEach(function () { 19 | fakeDomHelper = { 20 | loadStylesheet: jasmine.createSpy('createCssLink') 21 | }; 22 | 23 | load = jasmine.createSpy('load'); 24 | 25 | var defaultModule = new Custom(fakeDomHelper, { 26 | families: ['Font1', 'Font2', 'Font3'], 27 | urls: ['https://moo', 'https://meuh'], 28 | testStrings: { 29 | Font3: 'hello world' 30 | } 31 | }); 32 | 33 | defaultModule.load(load); 34 | }); 35 | 36 | it('should have inserted the links correctly', function () { 37 | expect(fakeDomHelper.loadStylesheet.callCount).toEqual(2); 38 | expect(fakeDomHelper.loadStylesheet).toHaveBeenCalledWith('https://moo', new Any(Function)); 39 | expect(fakeDomHelper.loadStylesheet).toHaveBeenCalledWith('https://meuh', new Any(Function)); 40 | }); 41 | 42 | if (webfont.DomHelper.CAN_WAIT_STYLESHEET) { 43 | it('should not invoke callback before all CSS are loaded', function () { 44 | expect(load.callCount).toEqual(0); 45 | notifySheetsLoaded(); 46 | expect(load.callCount).toEqual(1); 47 | }); 48 | } 49 | 50 | it('should have loaded the families correctly', function () { 51 | notifySheetsLoaded(); 52 | expect(load.callCount).toEqual(1); 53 | expect(load.calls[0].args[0].length).toEqual(3); 54 | expect(load.calls[0].args[0][0].getName()).toEqual('Font1'); 55 | expect(load.calls[0].args[0][1].getName()).toEqual('Font2'); 56 | expect(load.calls[0].args[0][2].getName()).toEqual('Font3'); 57 | }); 58 | 59 | it('should have set a custom test string', function () { 60 | notifySheetsLoaded(); 61 | expect(load.callCount).toEqual(1); 62 | expect(load.calls[0].args[1]).toEqual({ Font3: 'hello world' }); 63 | }); 64 | }); 65 | 66 | }); 67 | -------------------------------------------------------------------------------- /spec/modules/fontdeck_spec.js: -------------------------------------------------------------------------------- 1 | describe('modules.Fontdeck', function () { 2 | var Fontdeck = webfont.modules.Fontdeck, 3 | Font = webfont.Font; 4 | 5 | var configuration = { 6 | id: '2282' 7 | }; 8 | 9 | var apiResponse = { 10 | "domain" : "localhost", 11 | "cssurl" : "https://f.fontdeck.com/s/css/03BmCXiV2AHwX/Rp+OBFTfD2oFs/localhost/2282.css", 12 | "project" : 2282, 13 | "cssbase" : "https://f.fontdeck.com/s/css/03BmCXiV2AHwX/Rp+OBFTfD2oFs", 14 | "fonts" : [ 15 | { 16 | "font_family" : "'Fertigo Pro Regular', Fertigo, Constantia, Palatino, serif", 17 | "font_size_adjust" : 0.508, 18 | "name" : "Fertigo Pro Regular", 19 | "style" : "normal", 20 | "weight" : "normal", 21 | "font_urls" : { 22 | "eot" : "https://f.fontdeck.com/f/1/SUlFR0tid0kAA2vb11Ly/IGWDK+wV8TMAfV0J1Ej1J1GFRT1bssqrn6a.eot", 23 | "ttf" : "https://f.fontdeck.com/f/1/SUlFR0tid0kAA2vb11Ly/IGWDK+wV8TMAfV0J1Ej1J1GFRT1bssqrn6a.ttf", 24 | "woff" : "https://f.fontdeck.com/f/1/SUlFR0tid0kAA2vb11Ly/IGWDK+wV8TMAfV0J1Ej1J1GFRT1bssqrn6a.woff", 25 | "svg" : "https://f.fontdeck.com/f/1/SUlFR0tid0kAA2vb11Ly/IGWDK+wV8TMAfV0J1Ej1J1GFRT1bssqrn6a.svg#104" 26 | }, 27 | "id" : 104 28 | }, 29 | { 30 | "font_family" : "'Bodoni Display Bold Italic', Georgia, 'Times New Roman', Times, serif", 31 | "font_size_adjust" : 0.45, 32 | "name" : "Bodoni Display Bold Italic", 33 | "style" : "italic", 34 | "weight" : "bold", 35 | "font_urls" : { 36 | "eot" : "https://f.fontdeck.com/f/1/azJEbTVyc1QAA11+CAE5C93+l/bAQx1ipRo6Maba19w3Yy5ng+qVWlfj.eot", 37 | "ttf" : "https://f.fontdeck.com/f/1/azJEbTVyc1QAA11+CAE5C93+l/bAQx1ipRo6Maba19w3Yy5ng+qVWlfj.ttf", 38 | "woff" : "https://f.fontdeck.com/f/1/azJEbTVyc1QAA11+CAE5C93+l/bAQx1ipRo6Maba19w3Yy5ng+qVWlfj.woff", 39 | "svg" : "https://f.fontdeck.com/f/1/azJEbTVyc1QAA11+CAE5C93+l/bAQx1ipRo6Maba19w3Yy5ng+qVWlfj.svg#2256" 40 | }, 41 | "id" : 2256 42 | } 43 | ] 44 | }; 45 | 46 | var fakeDomHelper = null, 47 | global = null; 48 | 49 | beforeEach(function () { 50 | global = { 51 | location: {} 52 | }; 53 | 54 | fakeDomHelper = { 55 | loadScript: jasmine.createSpy('loadScript'), 56 | getLoadWindow: jasmine.createSpy('getLoadWindow').andReturn(global), 57 | getHostName: function () { return 'test-host-name'; } 58 | }; 59 | }); 60 | 61 | describe('support and load life cycle', function () { 62 | var fontdeck = null, 63 | onReady = jasmine.createSpy('onReady'); 64 | 65 | beforeEach(function () { 66 | fontdeck = new Fontdeck(fakeDomHelper, configuration); 67 | fontdeck.load(onReady); 68 | }); 69 | 70 | it('should create the script correctly', function () { 71 | expect(fakeDomHelper.loadScript).toHaveBeenCalled(); 72 | expect(fakeDomHelper.loadScript.calls[0].args[0]).toEqual('https://f.fontdeck.com/s/css/js/test-host-name/2282.js'); 73 | }); 74 | 75 | it('should have created a global', function () { 76 | expect(global.__webfontfontdeckmodule__).not.toBeNull(); 77 | expect(global.__webfontfontdeckmodule__['2282']).not.toBeNull(); 78 | }); 79 | 80 | it('should load correctly after calling the callback', function () { 81 | global.__webfontfontdeckmodule__['2282'](true, apiResponse); 82 | 83 | expect(fontdeck.fonts_).toEqual([new Font(apiResponse.fonts[0].name), new Font(apiResponse.fonts[1].name, 'i7')]); 84 | }); 85 | }); 86 | 87 | describe('no project id', function () { 88 | var fontdeck = null, 89 | support = null; 90 | 91 | beforeEach(function () { 92 | fontdeck = new Fontdeck(fakeDomHelper, { id: null }); 93 | }); 94 | 95 | it('should not have loaded any fonts', function () { 96 | expect(fontdeck.fonts_).toEqual([]); 97 | }); 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /spec/modules/google/fontapiparser_spec.js: -------------------------------------------------------------------------------- 1 | describe('modules.google.FontApiParser', function () { 2 | var FontApiParser = webfont.modules.google.FontApiParser, 3 | Font = webfont.Font; 4 | 5 | describe('parsed values are coherent', function () { 6 | var parser = null; 7 | 8 | beforeEach(function () { 9 | parser = new FontApiParser(['Tangerine', 'Droid Serif:bi', 'Yanone Kaffeesatz:200,300,400,700', 'Cantarell:italic,b', 'Exo:100italic', 'Lobster:200n']); 10 | parser.parse(); 11 | }); 12 | 13 | it('should parse fonts correctly', function () { 14 | var fonts = parser.getFonts(); 15 | 16 | expect(fonts.length).toEqual(10); 17 | expect(fonts).toEqual([ 18 | new Font('Tangerine', 'n4'), 19 | new Font('Droid Serif', 'i7'), 20 | new Font('Yanone Kaffeesatz', 'n2'), 21 | new Font('Yanone Kaffeesatz', 'n3'), 22 | new Font('Yanone Kaffeesatz', 'n4'), 23 | new Font('Yanone Kaffeesatz', 'n7'), 24 | new Font('Cantarell', 'i4'), 25 | new Font('Cantarell', 'n7'), 26 | new Font('Exo', 'i1'), 27 | new Font('Lobster', 'n2') 28 | ]); 29 | }); 30 | }); 31 | 32 | describe('mix of numeric weight and style', function () { 33 | var parser = null; 34 | 35 | beforeEach(function () { 36 | parser = new FontApiParser(['Nobile:700i,b,200i,r,i700']); 37 | parser.parse(); 38 | }); 39 | 40 | it('should parse fonts correctly', function () { 41 | var fonts = parser.getFonts(); 42 | 43 | expect(fonts.length).toEqual(4); 44 | expect(fonts).toEqual([ 45 | new Font('Nobile', 'i7'), 46 | new Font('Nobile', 'n7'), 47 | new Font('Nobile', 'i2'), 48 | new Font('Nobile', 'n4') 49 | ]); 50 | }); 51 | }); 52 | 53 | describe('typo bild instead of bold', function () { 54 | var parser = null; 55 | 56 | beforeEach(function () { 57 | parser = new FontApiParser(['Nobile:bild']); 58 | parser.parse(); 59 | }); 60 | 61 | it('should parse families correctly', function () { 62 | var fonts = parser.getFonts(); 63 | 64 | expect(fonts.length).toEqual(1); 65 | expect(fonts[0]).toEqual(new Font('Nobile', 'n4')); 66 | }); 67 | }); 68 | 69 | describe('variations with dashes', function () { 70 | var parser = null; 71 | 72 | beforeEach(function () { 73 | parser = new FontApiParser(['Nobile:semi-bold']); 74 | parser.parse(); 75 | }); 76 | 77 | it('should parse the variation correctly', function () { 78 | var fonts = parser.getFonts(); 79 | 80 | expect(fonts.length).toEqual(1); 81 | expect(fonts[0]).toEqual(new Font('Nobile', 'n6')); 82 | }); 83 | }); 84 | 85 | describe('nonsense', function () { 86 | var parser = null; 87 | 88 | beforeEach(function () { 89 | parser = new FontApiParser(['Nobile:dwe,^%^%fewf,$9940@#!@#$%^&*()_+}POIBJ{}{']); 90 | parser.parse(); 91 | }); 92 | 93 | it('should parse families correctly', function () { 94 | var fonts = parser.getFonts(); 95 | 96 | expect(fonts.length).toEqual(1); 97 | expect(fonts[0]).toEqual(new Font('Nobile', 'n4')); 98 | }); 99 | }); 100 | 101 | describe('no weight and one subset defined', function () { 102 | var parser = null; 103 | 104 | beforeEach(function () { 105 | parser = new FontApiParser(['Cantarell::greek']); 106 | parser.parse(); 107 | }); 108 | 109 | it('should parse families correctly', function () { 110 | var fonts = parser.getFonts(); 111 | 112 | expect(fonts.length).toEqual(1); 113 | expect(fonts[0]).toEqual(new Font('Cantarell', 'n4')); 114 | }); 115 | 116 | it('should parse pick test strings correctly', function () { 117 | var testStrings = parser.getFontTestStrings(), 118 | cantarell = testStrings['Cantarell']; 119 | 120 | expect(cantarell).not.toBeNull(); 121 | expect(cantarell).toEqual(FontApiParser.INT_FONTS['greek']); 122 | }); 123 | }); 124 | 125 | describe('no weight and multiple subsets defined', function () { 126 | var parser = null; 127 | 128 | beforeEach(function () { 129 | parser = new FontApiParser(['Cantarell::cyrillic,greek,latin']); 130 | parser.parse(); 131 | }); 132 | 133 | it('should parse families correctly', function () { 134 | var fonts = parser.getFonts(); 135 | 136 | expect(fonts.length).toEqual(1); 137 | expect(fonts[0]).toEqual(new Font('Cantarell', 'n4')); 138 | }); 139 | 140 | it('should parse pick test strings correctly', function () { 141 | var testStrings = parser.getFontTestStrings(), 142 | cantarell = testStrings['Cantarell']; 143 | 144 | expect(cantarell).not.toBeNull(); 145 | expect(cantarell).toEqual(FontApiParser.INT_FONTS['cyrillic']); 146 | }); 147 | }); 148 | 149 | describe('weight and multiple subsets defined', function () { 150 | var parser = null; 151 | 152 | beforeEach(function () { 153 | parser = new FontApiParser(['Cantarell:regular,bold:cyrillic,greek,latin']); 154 | parser.parse(); 155 | }); 156 | 157 | it('should parse families correctly', function () { 158 | var fonts = parser.getFonts(); 159 | 160 | expect(fonts.length).toEqual(2); 161 | expect(fonts).toEqual([ 162 | new Font('Cantarell', 'n4'), 163 | new Font('Cantarell', 'n7') 164 | ]); 165 | }); 166 | 167 | it('should parse pick test strings correctly', function () { 168 | var testStrings = parser.getFontTestStrings(), 169 | cantarell = testStrings['Cantarell']; 170 | 171 | expect(cantarell).not.toBeNull(); 172 | expect(cantarell).toEqual(FontApiParser.INT_FONTS['cyrillic']); 173 | }); 174 | }); 175 | 176 | describe('Hanuman is backward compatible', function () { 177 | var parser = null; 178 | 179 | beforeEach(function () { 180 | parser = new FontApiParser(['Hanuman']); 181 | parser.parse(); 182 | }); 183 | 184 | it('should parse families correctly', function () { 185 | var fonts = parser.getFonts(); 186 | 187 | expect(fonts.length).toEqual(1); 188 | expect(fonts[0]).toEqual(new Font('Hanuman', 'n4')); 189 | }); 190 | 191 | it('should parse pick test strings correctly', function () { 192 | var testStrings = parser.getFontTestStrings(), 193 | hanuman = testStrings['Hanuman']; 194 | 195 | expect(hanuman).not.toBeNull(); 196 | expect(hanuman).toEqual(FontApiParser.INT_FONTS['Hanuman']); 197 | }); 198 | }); 199 | 200 | describe('Hanuman is forward compatible', function () { 201 | var parser = null; 202 | 203 | beforeEach(function () { 204 | parser = new FontApiParser(['Hanuman::khmer']); 205 | parser.parse(); 206 | }); 207 | 208 | it('should parse families correctly', function () { 209 | var fonts = parser.getFonts(); 210 | 211 | expect(fonts.length).toEqual(1); 212 | expect(fonts[0]).toEqual(new Font('Hanuman', 'n4')); 213 | }); 214 | 215 | it('should parse pick test strings correctly', function () { 216 | var testStrings = parser.getFontTestStrings(), 217 | hanuman = testStrings['Hanuman']; 218 | 219 | expect(hanuman).not.toBeNull(); 220 | expect(hanuman).toEqual(FontApiParser.INT_FONTS['khmer']); 221 | }); 222 | }); 223 | 224 | describe('plus replaced with space', function () { 225 | var parser = null; 226 | 227 | beforeEach(function () { 228 | parser = new FontApiParser(['Erica+One', 'Droid+Serif::latin', 'Yanone+Kaffeesatz:400,700:latin']); 229 | parser.parse(); 230 | }); 231 | 232 | it('should parse families correctly', function () { 233 | var fonts = parser.getFonts(); 234 | 235 | expect(fonts.length).toEqual(4); 236 | expect(fonts).toEqual([ 237 | new Font('Erica One', 'n4'), 238 | new Font('Droid Serif', 'n4'), 239 | new Font('Yanone Kaffeesatz', 'n4'), 240 | new Font('Yanone Kaffeesatz', 'n7') 241 | ]); 242 | }); 243 | }); 244 | }); 245 | -------------------------------------------------------------------------------- /spec/modules/google/fontapiurlbuilder_spec.js: -------------------------------------------------------------------------------- 1 | describe('modules.google.FontApiUrlBuilder', function () { 2 | var FontApiUrlBuilder = webfont.modules.google.FontApiUrlBuilder; 3 | 4 | it('should throw an exception if there are no font families', function () { 5 | var builder = new FontApiUrlBuilder('https://moo'); 6 | expect(builder.build).toThrow(); 7 | }); 8 | 9 | it('should build a proper url', function () { 10 | var builder = new FontApiUrlBuilder('https://moo'); 11 | builder.setFontFamilies(['Font1', 'Font2']); 12 | expect(builder.build()).toEqual('https://moo?family=Font1%7CFont2'); 13 | }); 14 | 15 | it('should build a proper url', function () { 16 | var builder = new FontApiUrlBuilder(undefined); 17 | builder.setFontFamilies(['Font1', 'Font2']); 18 | expect(builder.build()).toEqual( 19 | FontApiUrlBuilder.DEFAULT_API_URL + 20 | '?family=Font1%7CFont2'); 21 | }); 22 | 23 | it('should build a proper url', function () { 24 | var builder = new FontApiUrlBuilder(undefined); 25 | builder.setFontFamilies(['Font1:bold:greek,cyrillic', 'Font2:italic', 'Font3']); 26 | expect(builder.build()).toEqual( 27 | FontApiUrlBuilder.DEFAULT_API_URL + 28 | '?family=Font1:bold%7CFont2:italic%7CFont3' + 29 | '&subset=greek,cyrillic'); 30 | }); 31 | 32 | it('should build a proper url', function () { 33 | var builder = new FontApiUrlBuilder(undefined); 34 | builder.setFontFamilies(['Font1:bold,italic:greek,cyrillic', 'Font2:italic', 'Font3::latin']); 35 | expect(builder.build()).toEqual( 36 | FontApiUrlBuilder.DEFAULT_API_URL + 37 | '?family=Font1:bold,italic%7CFont2:italic%7CFont3' + 38 | '&subset=greek,cyrillic,latin'); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /spec/modules/google/googlefontapi_spec.js: -------------------------------------------------------------------------------- 1 | describe('modules.google.GoogleFontApi', function () { 2 | var GoogleFontApi = webfont.modules.google.GoogleFontApi, 3 | Any = jasmine.Matchers.Any, 4 | Font = webfont.Font, 5 | link = '', 6 | insert = '', 7 | onload = null, 8 | fakeDomHelper = { 9 | whenBodyExists: function (callback) { 10 | callback(); 11 | }, 12 | loadStylesheet: function (cssLink, cb) { 13 | link = cssLink; 14 | onload = cb; 15 | } 16 | }; 17 | 18 | 19 | beforeEach(function () { 20 | insert = ''; 21 | link = ''; 22 | onload = null; 23 | }); 24 | 25 | function notifySheetsLoaded() { 26 | if (onload) 27 | onload(); 28 | }; 29 | 30 | describe('call onReady with font family loading', function () { 31 | var googleFontApi = null, 32 | fonts = null; 33 | 34 | beforeEach(function () { 35 | googleFontApi = new GoogleFontApi(fakeDomHelper, { families: ['Font1', 'Font2'] }); 36 | googleFontApi.load(function (f) { 37 | fonts = f; 38 | }); 39 | }); 40 | 41 | it('has inserted the link element correctly', function () { 42 | expect(link).toEqual('https://fonts.googleapis.com/css?family=Font1%7CFont2'); 43 | }); 44 | 45 | it('has the correct families', function () { 46 | notifySheetsLoaded(); 47 | expect(fonts).not.toBeNull(); 48 | expect(fonts.length).toEqual(2); 49 | expect(fonts[0]).toEqual(new Font('Font1')); 50 | expect(fonts[1]).toEqual(new Font('Font2')); 51 | }); 52 | }); 53 | 54 | describe('call onReady with font family loading and custom API url', function () { 55 | var googleFontApi = null; 56 | var loaded = false; 57 | 58 | beforeEach(function () { 59 | loaded = false; 60 | googleFontApi = new GoogleFontApi(fakeDomHelper, { 61 | api: 'https://moo', 62 | families: ['Font1', 'Font2'] 63 | }); 64 | googleFontApi.load(function () { loaded = true; }); 65 | }); 66 | 67 | it('has inserted the link element correctly', function () { 68 | expect(link).toEqual('https://moo?family=Font1%7CFont2'); 69 | }); 70 | 71 | if (webfont.DomHelper.CAN_WAIT_STYLESHEET) { 72 | it('does not call onReady until sheets are loaded', function () { 73 | expect(onload).toMatch(new Any(Function)); 74 | expect(loaded).toBe(false); 75 | 76 | notifySheetsLoaded(); 77 | expect(loaded).toBe(true); 78 | }); 79 | } 80 | }); 81 | 82 | describe('spaces replaced by plus', function () { 83 | var googleFontApi = null; 84 | 85 | beforeEach(function () { 86 | googleFontApi = new GoogleFontApi(fakeDomHelper, { families: ['Font1 WithSpace', 'Font2 WithSpaceToo'] }); 87 | googleFontApi.load(function () {}); 88 | }); 89 | 90 | it('has inserted the link element correctly', function () { 91 | expect(link).toEqual('https://fonts.googleapis.com/css?family=Font1+WithSpace%7CFont2+WithSpaceToo'); 92 | }); 93 | }); 94 | 95 | describe('load with variations', function () { 96 | var googleFontApi = null; 97 | 98 | beforeEach(function () { 99 | googleFontApi = new GoogleFontApi(fakeDomHelper, { families: ['Font1 WithSpace:bi', 'Font2 WithSpaceToo:b,r'] }); 100 | googleFontApi.load(function () {}); 101 | }); 102 | 103 | it('has inserted the link element correctly', function () { 104 | expect(link).toEqual('https://fonts.googleapis.com/css?family=Font1+WithSpace:bi%7CFont2+WithSpaceToo:b,r'); 105 | }); 106 | }); 107 | }); 108 | -------------------------------------------------------------------------------- /spec/modules/monotype_spec.js: -------------------------------------------------------------------------------- 1 | describe('modules.Monotype', function () { 2 | var Monotype = webfont.modules.Monotype, 3 | Font = webfont.Font, 4 | BrowserInfo = webfont.BrowserInfo, 5 | UserAgent = webfont.UserAgent, 6 | Version = webfont.Version; 7 | 8 | var configuration = { 9 | projectId: '01e2ff27-25bf-4801-a23e-73d328e6c7cc', 10 | api: 'https://fast.fonts.net/jsapidev' 11 | }; 12 | 13 | var fakeDomHelper = null, 14 | global = null, 15 | script = {}, 16 | monotype = null, 17 | load = null, 18 | support = null; 19 | 20 | beforeEach(function () { 21 | global = {}; 22 | 23 | fakeDomHelper = { 24 | loadScript: jasmine.createSpy('loadScript').andCallFake(function (src, callback) { 25 | script.onload = callback; 26 | return script; 27 | }), 28 | getLoadWindow: jasmine.createSpy('getLoadWindow').andReturn(global) 29 | }; 30 | support = jasmine.createSpy('support'); 31 | load = jasmine.createSpy('load'); 32 | 33 | monotype = new Monotype(fakeDomHelper, configuration); 34 | monotype.load(load); 35 | 36 | global[Monotype.HOOK + configuration.projectId] = function () { 37 | return [{fontfamily: 'aachen bold'}, {fontfamily: 'kid print regular'}]; 38 | }; 39 | 40 | script.onload(); 41 | }); 42 | 43 | 44 | it('should create a script element', function () { 45 | expect(fakeDomHelper.loadScript).toHaveBeenCalled(); 46 | expect(fakeDomHelper.loadScript.calls[0].args[0]).toEqual('https://fast.fonts.net/jsapidev/01e2ff27-25bf-4801-a23e-73d328e6c7cc.js'); 47 | expect(load).toHaveBeenCalledWith([new Font('aachen bold'), new Font('kid print regular')]); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /spec/modules/typekit_spec.js: -------------------------------------------------------------------------------- 1 | describe('modules.Typekit', function () { 2 | var Typekit = webfont.modules.Typekit, 3 | Font = webfont.Font; 4 | 5 | var configuration = { 6 | id: 'abc' 7 | }; 8 | 9 | var fakeDomHelper = null, 10 | global = {}, 11 | load = null, 12 | onReady = null; 13 | 14 | beforeEach(function () { 15 | global = { 16 | Typekit: { 17 | config: { 18 | fn: ['Font1', ['n4'], 'Font2', ['n4', 'n7']] 19 | }, 20 | load: jasmine.createSpy('load') 21 | } 22 | }; 23 | 24 | onReady = jasmine.createSpy('onReady'); 25 | 26 | load = jasmine.createSpy('load'); 27 | 28 | fakeDomHelper = { 29 | loadScript: jasmine.createSpy('loadScript').andCallFake(function (url, cb) { 30 | cb(null); 31 | }), 32 | getLoadWindow: jasmine.createSpy('getLoadWindow').andReturn(global) 33 | }; 34 | }); 35 | 36 | it('should load with variations', function () { 37 | var typekit = new Typekit(fakeDomHelper, configuration); 38 | 39 | typekit.load(onReady); 40 | 41 | expect(fakeDomHelper.loadScript).toHaveBeenCalled(); 42 | expect(fakeDomHelper.loadScript.calls[0].args[0]).toEqual('https://use.typekit.net/abc.js'); 43 | 44 | expect(global.Typekit.load).toHaveBeenCalled(); 45 | typekit.load(load); 46 | 47 | expect(load).toHaveBeenCalledWith([new Font('Font1', 'n4'), new Font('Font2', 'n4'), new Font('Font2', 'n7')]); 48 | }); 49 | 50 | it('should load through the alternative API', function () { 51 | var typekit = new Typekit(fakeDomHelper, { id: 'abc', api: '/test' }); 52 | 53 | typekit.load(onReady); 54 | 55 | expect(fakeDomHelper.loadScript).toHaveBeenCalled(); 56 | expect(fakeDomHelper.loadScript.calls[0].args[0]).toEqual('/test/abc.js'); 57 | }); 58 | 59 | it('should not load without a kit id', function () { 60 | var typekit = new Typekit(fakeDomHelper, { id: null }); 61 | 62 | typekit.load(onReady); 63 | 64 | expect(fakeDomHelper.loadScript).not.toHaveBeenCalled(); 65 | 66 | typekit.load(load); 67 | 68 | expect(load).toHaveBeenCalledWith([]); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /src/closure.js: -------------------------------------------------------------------------------- 1 | /* Web Font Loader v{{version}} - (c) Adobe Systems, Google. License: Apache 2.0 */ 2 | (function(){{{source}}}()); 3 | -------------------------------------------------------------------------------- /src/core/cssclassname.js: -------------------------------------------------------------------------------- 1 | goog.provide('webfont.CssClassName'); 2 | 3 | /** 4 | * Handles sanitization and construction of css class names. 5 | * @param {string=} opt_joinChar The character to join parts of the name on. 6 | * Defaults to '-'. 7 | * @constructor 8 | */ 9 | webfont.CssClassName = function(opt_joinChar) { 10 | /** @type {string} */ 11 | this.joinChar_ = opt_joinChar || webfont.CssClassName.DEFAULT_JOIN_CHAR; 12 | }; 13 | 14 | /** 15 | * @const 16 | * @type {string} 17 | */ 18 | webfont.CssClassName.DEFAULT_JOIN_CHAR = '-'; 19 | 20 | goog.scope(function () { 21 | var CssClassName = webfont.CssClassName; 22 | 23 | /** 24 | * Sanitizes a string for use as a css class name. Removes non-word and 25 | * underscore characters. 26 | * @param {string} name The string. 27 | * @return {string} The sanitized string. 28 | */ 29 | CssClassName.prototype.sanitize = function(name) { 30 | return name.replace(/[\W_]+/g, '').toLowerCase(); 31 | }; 32 | 33 | /** 34 | * Builds a complete css class name given a variable number of parts. 35 | * Sanitizes, then joins the parts together. 36 | * @param {...string} var_args The parts to join. 37 | * @return {string} The sanitized and joined string. 38 | */ 39 | CssClassName.prototype.build = function(var_args) { 40 | var parts = [] 41 | for (var i = 0; i < arguments.length; i++) { 42 | parts.push(this.sanitize(arguments[i])); 43 | } 44 | return parts.join(this.joinChar_); 45 | }; 46 | }); 47 | -------------------------------------------------------------------------------- /src/core/eventdispatcher.js: -------------------------------------------------------------------------------- 1 | goog.provide('webfont.EventDispatcher'); 2 | 3 | goog.require('webfont.CssClassName'); 4 | 5 | /** 6 | * A class to dispatch events and manage the event class names on an html 7 | * element that represent the current state of fonts on the page. Active class 8 | * names always overwrite inactive class names of the same type, while loading 9 | * class names may be present whenever a font is loading (regardless of if an 10 | * associated active or inactive class name is also present). 11 | * 12 | * @param {webfont.DomHelper} domHelper 13 | * @param {Object} config 14 | * @constructor 15 | */ 16 | webfont.EventDispatcher = function(domHelper, config) { 17 | this.domHelper_ = domHelper; 18 | this.htmlElement_ = domHelper.getLoadWindow().document.documentElement; 19 | this.callbacks_ = config; 20 | this.namespace_ = webfont.EventDispatcher.DEFAULT_NAMESPACE; 21 | this.cssClassName_ = new webfont.CssClassName('-'); 22 | this.dispatchEvents_ = config['events'] !== false; 23 | this.setClasses_ = config['classes'] !== false; 24 | }; 25 | 26 | /** 27 | * @const 28 | * @type {string} 29 | */ 30 | webfont.EventDispatcher.DEFAULT_NAMESPACE = 'wf'; 31 | 32 | /** 33 | * @const 34 | * @type {string} 35 | */ 36 | webfont.EventDispatcher.LOADING = 'loading'; 37 | 38 | /** 39 | * @const 40 | * @type {string} 41 | */ 42 | webfont.EventDispatcher.ACTIVE = 'active'; 43 | 44 | /** 45 | * @const 46 | * @type {string} 47 | */ 48 | webfont.EventDispatcher.INACTIVE = 'inactive'; 49 | 50 | /** 51 | * @const 52 | * @type {string} 53 | */ 54 | webfont.EventDispatcher.FONT = 'font'; 55 | 56 | goog.scope(function () { 57 | var EventDispatcher = webfont.EventDispatcher; 58 | 59 | /** 60 | * Dispatch the loading event and append the loading class name. 61 | */ 62 | EventDispatcher.prototype.dispatchLoading = function() { 63 | if (this.setClasses_) { 64 | this.domHelper_.updateClassName(this.htmlElement_, 65 | [ 66 | this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.LOADING) 67 | ] 68 | ); 69 | } 70 | 71 | this.dispatch_(webfont.EventDispatcher.LOADING); 72 | }; 73 | 74 | /** 75 | * Dispatch the font loading event and append the font loading class name. 76 | * @param {webfont.Font} font 77 | */ 78 | EventDispatcher.prototype.dispatchFontLoading = function(font) { 79 | if (this.setClasses_) { 80 | this.domHelper_.updateClassName(this.htmlElement_, 81 | [ 82 | this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.LOADING) 83 | ] 84 | ); 85 | } 86 | 87 | this.dispatch_(webfont.EventDispatcher.FONT + webfont.EventDispatcher.LOADING, font); 88 | }; 89 | 90 | /** 91 | * Dispatch the font active event, remove the font loading class name, remove 92 | * the font inactive class name, and append the font active class name. 93 | * @param {webfont.Font} font 94 | */ 95 | EventDispatcher.prototype.dispatchFontActive = function(font) { 96 | if (this.setClasses_) { 97 | this.domHelper_.updateClassName( 98 | this.htmlElement_, 99 | [ 100 | this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.ACTIVE) 101 | ], 102 | [ 103 | this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.LOADING), 104 | this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.INACTIVE) 105 | ] 106 | ); 107 | } 108 | 109 | this.dispatch_(webfont.EventDispatcher.FONT + webfont.EventDispatcher.ACTIVE, font); 110 | }; 111 | 112 | /** 113 | * Dispatch the font inactive event, remove the font loading class name, and 114 | * append the font inactive class name (unless the font active class name is 115 | * already present). 116 | * @param {webfont.Font} font 117 | */ 118 | EventDispatcher.prototype.dispatchFontInactive = function(font) { 119 | if (this.setClasses_) { 120 | var hasFontActive = this.domHelper_.hasClassName(this.htmlElement_, 121 | this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.ACTIVE) 122 | ), 123 | add = [], 124 | remove = [ 125 | this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.LOADING) 126 | ]; 127 | 128 | if (!hasFontActive) { 129 | add.push(this.cssClassName_.build(this.namespace_, font.getName(), font.getVariation().toString(), webfont.EventDispatcher.INACTIVE)); 130 | } 131 | 132 | this.domHelper_.updateClassName(this.htmlElement_, add, remove); 133 | } 134 | 135 | this.dispatch_(webfont.EventDispatcher.FONT + webfont.EventDispatcher.INACTIVE, font); 136 | }; 137 | 138 | /** 139 | * Dispatch the inactive event, remove the loading class name, and append the 140 | * inactive class name (unless the active class name is already present). 141 | */ 142 | EventDispatcher.prototype.dispatchInactive = function() { 143 | if (this.setClasses_) { 144 | var hasActive = this.domHelper_.hasClassName(this.htmlElement_, 145 | this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.ACTIVE) 146 | ), 147 | add = [], 148 | remove = [ 149 | this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.LOADING) 150 | ]; 151 | 152 | if (!hasActive) { 153 | add.push(this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.INACTIVE)); 154 | } 155 | 156 | this.domHelper_.updateClassName(this.htmlElement_, add, remove); 157 | } 158 | 159 | this.dispatch_(webfont.EventDispatcher.INACTIVE); 160 | }; 161 | 162 | /** 163 | * Dispatch the active event, remove the loading class name, remove the inactive 164 | * class name, and append the active class name. 165 | */ 166 | EventDispatcher.prototype.dispatchActive = function() { 167 | if (this.setClasses_) { 168 | this.domHelper_.updateClassName(this.htmlElement_, 169 | [ 170 | this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.ACTIVE) 171 | ], 172 | [ 173 | this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.LOADING), 174 | this.cssClassName_.build(this.namespace_, webfont.EventDispatcher.INACTIVE) 175 | ] 176 | ); 177 | } 178 | 179 | this.dispatch_(webfont.EventDispatcher.ACTIVE); 180 | }; 181 | 182 | /** 183 | * @param {string} event 184 | * @param {webfont.Font=} opt_font 185 | */ 186 | EventDispatcher.prototype.dispatch_ = function(event, opt_font) { 187 | if (this.dispatchEvents_ && this.callbacks_[event]) { 188 | if (opt_font) { 189 | this.callbacks_[event](opt_font.getName(), opt_font.getVariation()); 190 | } else { 191 | this.callbacks_[event](); 192 | } 193 | } 194 | }; 195 | }); 196 | -------------------------------------------------------------------------------- /src/core/font.js: -------------------------------------------------------------------------------- 1 | goog.provide('webfont.Font'); 2 | 3 | /** 4 | * This class is an abstraction for a single font or typeface. 5 | * It contains the font name and the variation (i.e. style 6 | * and weight.) A collection Font instances can represent a 7 | * font family. 8 | * 9 | * @constructor 10 | * @param {string} name The font family name 11 | * @param {string=} opt_variation A font variation description 12 | */ 13 | webfont.Font = function (name, opt_variation) { 14 | this.name_ = name; 15 | this.weight_ = 4; 16 | this.style_ = 'n' 17 | 18 | var variation = opt_variation || 'n4', 19 | match = variation.match(/^([nio])([1-9])$/i); 20 | 21 | if (match) { 22 | this.style_ = match[1]; 23 | this.weight_ = parseInt(match[2], 10); 24 | } 25 | }; 26 | 27 | goog.scope(function () { 28 | var Font = webfont.Font; 29 | 30 | /** 31 | * @return {string} 32 | */ 33 | Font.prototype.getName = function () { 34 | return this.name_; 35 | }; 36 | 37 | /** 38 | * @return {string} 39 | */ 40 | Font.prototype.getCssName = function () { 41 | return this.quote_(this.name_); 42 | }; 43 | 44 | /** 45 | * Returns a CSS string representation of the font that 46 | * can be used as the CSS font property shorthand. 47 | * 48 | * @return {string} 49 | */ 50 | Font.prototype.toCssString = function () { 51 | return this.getCssStyle() + ' ' + this.getCssWeight() + ' 300px ' + this.getCssName(); 52 | }; 53 | 54 | /** 55 | * @private 56 | * @param {string} name 57 | * @return {string} 58 | */ 59 | Font.prototype.quote_ = function (name) { 60 | var quoted = []; 61 | var split = name.split(/,\s*/); 62 | for (var i = 0; i < split.length; i++) { 63 | var part = split[i].replace(/['"]/g, ''); 64 | if (part.indexOf(' ') == -1 && !(/^\d/.test(part))) { 65 | quoted.push(part); 66 | } else { 67 | quoted.push("'" + part + "'"); 68 | } 69 | } 70 | return quoted.join(','); 71 | }; 72 | 73 | /** 74 | * @return {string} 75 | */ 76 | Font.prototype.getVariation = function () { 77 | return this.style_ + this.weight_; 78 | }; 79 | 80 | /** 81 | * @return {string} 82 | */ 83 | Font.prototype.getCssVariation = function () { 84 | return 'font-style:' + this.getCssStyle() + ';font-weight:' + this.getCssWeight() + ';'; 85 | }; 86 | 87 | /** 88 | * @return {string} 89 | */ 90 | Font.prototype.getCssWeight = function () { 91 | return this.weight_ + '00'; 92 | }; 93 | 94 | /** 95 | * @return {string} 96 | */ 97 | Font.prototype.getCssStyle = function () { 98 | var style = 'normal'; 99 | 100 | if (this.style_ === 'o') { 101 | style = 'oblique'; 102 | } else if (this.style_ === 'i') { 103 | style = 'italic'; 104 | } 105 | 106 | return style; 107 | }; 108 | 109 | /** 110 | * Parses a CSS font declaration and returns a font 111 | * variation description. 112 | * 113 | * @param {string} css 114 | * @return {string} 115 | */ 116 | Font.parseCssVariation = function (css) { 117 | var weight = 4, 118 | style = 'n', 119 | m = null; 120 | 121 | if (css) { 122 | m = css.match(/(normal|oblique|italic)/i); 123 | 124 | if (m && m[1]) { 125 | style = m[1].substr(0, 1).toLowerCase(); 126 | } 127 | 128 | m = css.match(/([1-9]00|normal|bold)/i); 129 | 130 | if (m && m[1]) { 131 | if (/bold/i.test(m[1])) { 132 | weight = 7; 133 | } else if (/[1-9]00/.test(m[1])) { 134 | weight = parseInt(m[1].substr(0, 1), 10); 135 | } 136 | } 137 | } 138 | return style + weight; 139 | } 140 | }); 141 | -------------------------------------------------------------------------------- /src/core/fontmodule.js: -------------------------------------------------------------------------------- 1 | goog.provide('webfont.FontModule'); 2 | 3 | /** 4 | * @interface 5 | */ 6 | webfont.FontModule = function () {}; 7 | 8 | goog.scope(function () { 9 | var FontModule = webfont.FontModule; 10 | 11 | /** 12 | * @param {function(Array., webfont.FontTestStrings=, Object.=)} onReady 13 | */ 14 | FontModule.prototype.load = function (onReady) {}; 15 | }); 16 | 17 | -------------------------------------------------------------------------------- /src/core/fontmoduleloader.js: -------------------------------------------------------------------------------- 1 | goog.provide('webfont.FontModuleLoader'); 2 | goog.provide('webfont.FontModuleFactory'); 3 | 4 | /** @typedef {function(Object, webfont.DomHelper): webfont.FontModule} */ 5 | webfont.FontModuleFactory; 6 | 7 | /** 8 | * @constructor 9 | */ 10 | webfont.FontModuleLoader = function() { 11 | /** 12 | * @type {Object.} 13 | */ 14 | this.modules_ = {}; 15 | }; 16 | 17 | goog.scope(function () { 18 | var FontModuleLoader = webfont.FontModuleLoader; 19 | 20 | /** 21 | * @param {string} name 22 | * @param {webfont.FontModuleFactory} factory 23 | */ 24 | FontModuleLoader.prototype.addModuleFactory = function(name, factory) { 25 | this.modules_[name] = factory; 26 | }; 27 | 28 | /** 29 | * @param {Object} configuration 30 | * @param {webfont.DomHelper} domHelper 31 | * @return {Array.} 32 | */ 33 | FontModuleLoader.prototype.getModules = function(configuration, domHelper) { 34 | var modules = []; 35 | 36 | for (var key in configuration) { 37 | if (configuration.hasOwnProperty(key)) { 38 | var moduleFactory = this.modules_[key]; 39 | 40 | if (moduleFactory) { 41 | modules.push(moduleFactory(configuration[key], domHelper)); 42 | } 43 | } 44 | } 45 | return modules; 46 | }; 47 | }); 48 | -------------------------------------------------------------------------------- /src/core/fontruler.js: -------------------------------------------------------------------------------- 1 | goog.provide('webfont.FontRuler'); 2 | 3 | /** 4 | * An element that can be used to measure the metrics 5 | * of a given font and string. 6 | * @constructor 7 | * @param {webfont.DomHelper} domHelper 8 | * @param {string} fontTestString 9 | */ 10 | webfont.FontRuler = function (domHelper, fontTestString) { 11 | this.domHelper_ = domHelper; 12 | this.fontTestString_ = fontTestString; 13 | this.el_ = this.domHelper_.createElement('span', { 14 | "aria-hidden": "true" 15 | }, this.fontTestString_); 16 | }; 17 | 18 | goog.scope(function () { 19 | var FontRuler = webfont.FontRuler; 20 | 21 | /** 22 | * @param {webfont.Font} font 23 | */ 24 | FontRuler.prototype.setFont = function(font) { 25 | this.domHelper_.setStyle(this.el_, this.computeStyleString_(font)); 26 | }; 27 | 28 | /** 29 | * Inserts the ruler into the DOM. 30 | */ 31 | FontRuler.prototype.insert = function() { 32 | this.domHelper_.insertInto('body', this.el_); 33 | }; 34 | 35 | /** 36 | * @private 37 | * @param {webfont.Font} font 38 | * @return {string} 39 | */ 40 | FontRuler.prototype.computeStyleString_ = function(font) { 41 | return "display:block;position:absolute;top:-9999px;left:-9999px;" + 42 | "font-size:300px;width:auto;height:auto;line-height:normal;margin:0;" + 43 | "padding:0;font-variant:normal;white-space:nowrap;font-family:" + 44 | font.getCssName() + ";" + font.getCssVariation(); 45 | }; 46 | 47 | /** 48 | * @return {number} 49 | */ 50 | FontRuler.prototype.getWidth = function() { 51 | return this.el_.offsetWidth; 52 | }; 53 | 54 | /** 55 | * Removes the ruler element from the DOM. 56 | */ 57 | FontRuler.prototype.remove = function() { 58 | this.domHelper_.removeElement(this.el_); 59 | }; 60 | }); 61 | -------------------------------------------------------------------------------- /src/core/fontwatcher.js: -------------------------------------------------------------------------------- 1 | goog.provide('webfont.FontWatcher'); 2 | 3 | goog.require('webfont.FontWatchRunner'); 4 | goog.require('webfont.NativeFontWatchRunner'); 5 | 6 | /** 7 | * @typedef {Object.>} 8 | */ 9 | webfont.FontTestStrings; 10 | 11 | /** 12 | * @constructor 13 | * @param {webfont.DomHelper} domHelper 14 | * @param {webfont.EventDispatcher} eventDispatcher 15 | * @param {number=} opt_timeout 16 | */ 17 | webfont.FontWatcher = function(domHelper, eventDispatcher, opt_timeout) { 18 | this.domHelper_ = domHelper; 19 | this.eventDispatcher_ = eventDispatcher; 20 | this.currentlyWatched_ = 0; 21 | this.last_ = false; 22 | this.success_ = false; 23 | this.timeout_ = opt_timeout; 24 | }; 25 | 26 | goog.scope(function () { 27 | var FontWatcher = webfont.FontWatcher, 28 | FontWatchRunner = webfont.FontWatchRunner, 29 | NativeFontWatchRunner = webfont.NativeFontWatchRunner; 30 | 31 | /** 32 | * @type {null|boolean} 33 | */ 34 | FontWatcher.SHOULD_USE_NATIVE_LOADER = null; 35 | 36 | /** 37 | * @return {string} 38 | */ 39 | FontWatcher.getUserAgent = function () { 40 | return window.navigator.userAgent; 41 | }; 42 | 43 | /** 44 | * @return {string} 45 | */ 46 | FontWatcher.getVendor = function () { 47 | return window.navigator.vendor; 48 | }; 49 | 50 | /** 51 | * Returns true if this browser has support for 52 | * the CSS font loading API. 53 | * 54 | * @return {boolean} 55 | */ 56 | FontWatcher.shouldUseNativeLoader = function () { 57 | if (FontWatcher.SHOULD_USE_NATIVE_LOADER === null) { 58 | if (!!window.FontFace) { 59 | var match = /Gecko.*Firefox\/(\d+)/.exec(FontWatcher.getUserAgent()); 60 | var safari10Match = /OS X.*Version\/10\..*Safari/.exec(FontWatcher.getUserAgent()) && /Apple/.exec(FontWatcher.getVendor()); 61 | 62 | if (match) { 63 | FontWatcher.SHOULD_USE_NATIVE_LOADER = parseInt(match[1], 10) > 42; 64 | } else if (safari10Match) { 65 | FontWatcher.SHOULD_USE_NATIVE_LOADER = false; 66 | } else { 67 | FontWatcher.SHOULD_USE_NATIVE_LOADER = true; 68 | } 69 | } else { 70 | FontWatcher.SHOULD_USE_NATIVE_LOADER = false; 71 | } 72 | } 73 | return FontWatcher.SHOULD_USE_NATIVE_LOADER; 74 | }; 75 | 76 | /** 77 | * Watches a set of font families. 78 | * @param {Array.} fonts The fonts to watch. 79 | * @param {webfont.FontTestStrings} fontTestStrings The font test strings for 80 | * each family. 81 | * @param {Object.} metricCompatibleFonts 82 | * @param {boolean} last True if this is the last set of fonts to watch. 83 | */ 84 | FontWatcher.prototype.watchFonts = function(fonts, 85 | fontTestStrings, metricCompatibleFonts, last) { 86 | var length = fonts.length, 87 | testStrings = fontTestStrings || {}; 88 | 89 | if (length === 0 && last) { 90 | this.eventDispatcher_.dispatchInactive(); 91 | return; 92 | } 93 | 94 | this.currentlyWatched_ += fonts.length; 95 | 96 | if (last) { 97 | this.last_ = last; 98 | } 99 | 100 | var i, fontWatchRunners = []; 101 | for (i = 0; i < fonts.length; i++) { 102 | var font = fonts[i], 103 | testString = testStrings[font.getName()]; 104 | 105 | this.eventDispatcher_.dispatchFontLoading(font); 106 | 107 | var fontWatchRunner = null; 108 | 109 | if (FontWatcher.shouldUseNativeLoader()) { 110 | fontWatchRunner = new NativeFontWatchRunner( 111 | goog.bind(this.fontActive_, this), 112 | goog.bind(this.fontInactive_, this), 113 | this.domHelper_, 114 | font, 115 | this.timeout_, 116 | testString 117 | ); 118 | } else { 119 | fontWatchRunner = new FontWatchRunner( 120 | goog.bind(this.fontActive_, this), 121 | goog.bind(this.fontInactive_, this), 122 | this.domHelper_, 123 | font, 124 | this.timeout_, 125 | metricCompatibleFonts, 126 | testString 127 | ); 128 | } 129 | 130 | fontWatchRunners.push(fontWatchRunner); 131 | } 132 | 133 | for (i = 0; i < fontWatchRunners.length; i++) { 134 | fontWatchRunners[i].start(); 135 | } 136 | }; 137 | 138 | /** 139 | * Called by a FontWatchRunner when a font has been detected as active. 140 | * @param {webfont.Font} font 141 | * @private 142 | */ 143 | FontWatcher.prototype.fontActive_ = function(font) { 144 | this.eventDispatcher_.dispatchFontActive(font); 145 | this.success_ = true; 146 | this.decreaseCurrentlyWatched_(); 147 | }; 148 | 149 | /** 150 | * Called by a FontWatchRunner when a font has been detected as inactive. 151 | * @param {webfont.Font} font 152 | * @private 153 | */ 154 | FontWatcher.prototype.fontInactive_ = function(font) { 155 | this.eventDispatcher_.dispatchFontInactive(font); 156 | this.decreaseCurrentlyWatched_(); 157 | }; 158 | 159 | /** 160 | * @private 161 | */ 162 | FontWatcher.prototype.decreaseCurrentlyWatched_ = function() { 163 | if (--this.currentlyWatched_ == 0 && this.last_) { 164 | if (this.success_) { 165 | this.eventDispatcher_.dispatchActive(); 166 | } else { 167 | this.eventDispatcher_.dispatchInactive(); 168 | } 169 | } 170 | }; 171 | }); 172 | -------------------------------------------------------------------------------- /src/core/initialize.js: -------------------------------------------------------------------------------- 1 | goog.provide('webfont'); 2 | 3 | goog.require('webfont.WebFont'); 4 | 5 | goog.require('webfont.modules.Typekit'); 6 | goog.require('webfont.modules.Fontdeck'); 7 | goog.require('webfont.modules.Monotype'); 8 | goog.require('webfont.modules.Custom'); 9 | goog.require('webfont.modules.google.GoogleFontApi'); 10 | 11 | /** 12 | * @define {boolean} 13 | */ 14 | var INCLUDE_CUSTOM_MODULE = false; 15 | 16 | /** 17 | * @define {boolean} 18 | */ 19 | var INCLUDE_FONTDECK_MODULE = false; 20 | 21 | /** 22 | * @define {boolean} 23 | */ 24 | var INCLUDE_MONOTYPE_MODULE = false; 25 | 26 | /** 27 | * @define {boolean} 28 | */ 29 | var INCLUDE_TYPEKIT_MODULE = false; 30 | 31 | /** 32 | * @define {boolean} 33 | */ 34 | var INCLUDE_GOOGLE_MODULE = false; 35 | 36 | /** 37 | * @define {string} 38 | */ 39 | var WEBFONT = 'WebFont'; 40 | 41 | /** 42 | * @define {string} 43 | */ 44 | var WEBFONT_CONFIG = 'WebFontConfig'; 45 | 46 | /** 47 | * @type {webfont.WebFont} 48 | */ 49 | var webFontLoader = new webfont.WebFont(window); 50 | 51 | if (INCLUDE_CUSTOM_MODULE) { 52 | webFontLoader.addModule(webfont.modules.Custom.NAME, function (configuration, domHelper) { 53 | return new webfont.modules.Custom(domHelper, configuration); 54 | }); 55 | } 56 | 57 | if (INCLUDE_FONTDECK_MODULE) { 58 | webFontLoader.addModule(webfont.modules.Fontdeck.NAME, function (configuration, domHelper) { 59 | return new webfont.modules.Fontdeck(domHelper, configuration); 60 | }); 61 | } 62 | 63 | if (INCLUDE_MONOTYPE_MODULE) { 64 | webFontLoader.addModule(webfont.modules.Monotype.NAME, function (configuration, domHelper) { 65 | return new webfont.modules.Monotype(domHelper, configuration); 66 | }); 67 | } 68 | 69 | if (INCLUDE_TYPEKIT_MODULE) { 70 | webFontLoader.addModule(webfont.modules.Typekit.NAME, function (configuration, domHelper) { 71 | return new webfont.modules.Typekit(domHelper, configuration); 72 | }); 73 | } 74 | 75 | if (INCLUDE_GOOGLE_MODULE) { 76 | webFontLoader.addModule(webfont.modules.google.GoogleFontApi.NAME, function (configuration, domHelper) { 77 | return new webfont.modules.google.GoogleFontApi(domHelper, configuration); 78 | }); 79 | } 80 | 81 | var exports = { 82 | 'load': goog.bind(webFontLoader.load, webFontLoader) 83 | }; 84 | 85 | if (typeof define === "function" && define.amd) { 86 | define(function () { 87 | return exports; 88 | }); 89 | } else if (typeof module !== "undefined" && module.exports) { 90 | module.exports = exports; 91 | } else { 92 | window[WEBFONT] = exports; 93 | 94 | if (window[WEBFONT_CONFIG]) { 95 | webFontLoader.load(window[WEBFONT_CONFIG]); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/core/nativefontwatchrunner.js: -------------------------------------------------------------------------------- 1 | goog.provide('webfont.NativeFontWatchRunner'); 2 | 3 | goog.require('webfont.Font'); 4 | 5 | goog.scope(function () { 6 | /** 7 | * @constructor 8 | * @param {function(webfont.Font)} activeCallback 9 | * @param {function(webfont.Font)} inactiveCallback 10 | * @param {webfont.DomHelper} domHelper 11 | * @param {webfont.Font} font 12 | * @param {number=} opt_timeout 13 | * @param {string=} opt_fontTestString 14 | */ 15 | webfont.NativeFontWatchRunner = function(activeCallback, inactiveCallback, domHelper, font, opt_timeout, opt_fontTestString) { 16 | this.activeCallback_ = activeCallback; 17 | this.inactiveCallback_ = inactiveCallback; 18 | this.font_ = font; 19 | this.domHelper_ = domHelper; 20 | this.timeout_ = opt_timeout || 3000; 21 | this.fontTestString_ = opt_fontTestString || undefined; 22 | }; 23 | 24 | var NativeFontWatchRunner = webfont.NativeFontWatchRunner; 25 | 26 | NativeFontWatchRunner.prototype.start = function () { 27 | var doc = this.domHelper_.getLoadWindow().document, 28 | that = this; 29 | 30 | var start = goog.now(); 31 | 32 | var loader = new Promise(function (resolve, reject) { 33 | var check = function () { 34 | var now = goog.now(); 35 | 36 | if (now - start >= that.timeout_) { 37 | reject(); 38 | } else { 39 | doc.fonts.load(that.font_.toCssString(), that.fontTestString_).then(function (fonts) { 40 | if (fonts.length >= 1) { 41 | resolve(); 42 | } else { 43 | setTimeout(check, 25); 44 | } 45 | }, function () { 46 | reject(); 47 | }); 48 | } 49 | }; 50 | 51 | check(); 52 | }); 53 | 54 | var timeoutId = null, 55 | timer = new Promise(function (resolve, reject) { 56 | timeoutId = setTimeout(reject, that.timeout_); 57 | }); 58 | 59 | Promise.race([timer, loader]).then(function () { 60 | if (timeoutId) { 61 | clearTimeout(timeoutId); 62 | timeoutId = null; 63 | } 64 | that.activeCallback_(that.font_); 65 | }, function () { 66 | that.inactiveCallback_(that.font_); 67 | }); 68 | }; 69 | }); 70 | -------------------------------------------------------------------------------- /src/core/stylesheetwaiter.js: -------------------------------------------------------------------------------- 1 | goog.provide('webfont.StyleSheetWaiter'); 2 | 3 | /** 4 | * A utility class for handling callback from DomHelper.loadStylesheet(). 5 | * 6 | * @constructor 7 | */ 8 | webfont.StyleSheetWaiter = function() { 9 | /** @private @type {number} */ 10 | this.waitingCount_ = 0; 11 | /** @private @type {Function} */ 12 | this.onReady_ = null; 13 | }; 14 | 15 | goog.scope(function () { 16 | var StyleSheetWaiter = webfont.StyleSheetWaiter; 17 | 18 | /** 19 | * @return {function(Error)} 20 | */ 21 | StyleSheetWaiter.prototype.startWaitingLoad = function() { 22 | var self = this; 23 | self.waitingCount_++; 24 | return function(error) { 25 | self.waitingCount_--; 26 | self.fireIfReady_(); 27 | }; 28 | }; 29 | 30 | /** 31 | * @param {Function} fn 32 | */ 33 | StyleSheetWaiter.prototype.waitWhileNeededThen = function(fn) { 34 | this.onReady_ = fn; 35 | this.fireIfReady_(); 36 | }; 37 | 38 | /** 39 | * @private 40 | */ 41 | StyleSheetWaiter.prototype.fireIfReady_ = function() { 42 | var isReady = 0 == this.waitingCount_; 43 | if (isReady && this.onReady_) { 44 | this.onReady_(); 45 | this.onReady_ = null; 46 | } 47 | }; 48 | }); 49 | -------------------------------------------------------------------------------- /src/core/webfont.js: -------------------------------------------------------------------------------- 1 | goog.provide('webfont.WebFont'); 2 | 3 | goog.require('webfont.DomHelper'); 4 | goog.require('webfont.EventDispatcher'); 5 | goog.require('webfont.FontWatcher'); 6 | goog.require('webfont.FontModuleLoader'); 7 | 8 | /** 9 | * @param {Window} mainWindow The main application window containing 10 | * webfontloader.js. 11 | * @constructor 12 | */ 13 | webfont.WebFont = function(mainWindow) { 14 | this.mainWindow_ = mainWindow; 15 | this.fontModuleLoader_ = new webfont.FontModuleLoader(); 16 | this.moduleLoading_ = 0; 17 | this.events_ = true; 18 | this.classes_ = true; 19 | }; 20 | 21 | goog.scope(function () { 22 | var WebFont = webfont.WebFont, 23 | DomHelper = webfont.DomHelper, 24 | EventDispatcher = webfont.EventDispatcher, 25 | FontWatcher = webfont.FontWatcher; 26 | 27 | /** 28 | * @param {string} name 29 | * @param {webfont.FontModuleFactory} factory 30 | */ 31 | WebFont.prototype.addModule = function(name, factory) { 32 | this.fontModuleLoader_.addModuleFactory(name, factory); 33 | }; 34 | 35 | /** 36 | * @param {Object} configuration 37 | */ 38 | WebFont.prototype.load = function(configuration) { 39 | var context = configuration['context'] || this.mainWindow_; 40 | this.domHelper_ = new DomHelper(this.mainWindow_, context); 41 | 42 | this.events_ = configuration['events'] !== false; 43 | this.classes_ = configuration['classes'] !== false; 44 | 45 | var eventDispatcher = new EventDispatcher( 46 | this.domHelper_, 47 | configuration 48 | ); 49 | 50 | this.load_(eventDispatcher, configuration); 51 | }; 52 | 53 | /** 54 | * @param {webfont.EventDispatcher} eventDispatcher 55 | * @param {webfont.FontWatcher} fontWatcher 56 | * @param {Array.} fonts 57 | * @param {webfont.FontTestStrings=} opt_fontTestStrings 58 | * @param {Object.=} opt_metricCompatibleFonts 59 | */ 60 | WebFont.prototype.onModuleReady_ = function(eventDispatcher, fontWatcher, fonts, opt_fontTestStrings, opt_metricCompatibleFonts) { 61 | var allModulesLoaded = --this.moduleLoading_ == 0; 62 | 63 | if (this.classes_ || this.events_) { 64 | setTimeout(function () { 65 | fontWatcher.watchFonts(fonts, opt_fontTestStrings || null, opt_metricCompatibleFonts || null, allModulesLoaded); 66 | }, 0); 67 | } 68 | }; 69 | 70 | /** 71 | * @param {webfont.EventDispatcher} eventDispatcher 72 | * @param {Object} configuration 73 | */ 74 | WebFont.prototype.load_ = function(eventDispatcher, configuration) { 75 | var modules = [], 76 | timeout = configuration['timeout'], 77 | self = this; 78 | 79 | // Immediately dispatch the loading event before initializing the modules 80 | // so we know for sure that the loading event is synchronous. 81 | eventDispatcher.dispatchLoading(); 82 | 83 | modules = this.fontModuleLoader_.getModules(configuration, this.domHelper_); 84 | 85 | var fontWatcher = new webfont.FontWatcher(this.domHelper_, eventDispatcher, timeout); 86 | 87 | this.moduleLoading_ = modules.length; 88 | 89 | for (var i = 0, len = modules.length; i < len; i++) { 90 | var module = modules[i]; 91 | 92 | module.load(function (fonts, opt_fontTestStrings, opt_metricCompatibleFonts) { 93 | self.onModuleReady_(eventDispatcher, fontWatcher, fonts, opt_fontTestStrings, opt_metricCompatibleFonts); 94 | }); 95 | } 96 | }; 97 | }); 98 | -------------------------------------------------------------------------------- /src/modules.yml: -------------------------------------------------------------------------------- 1 | core: 2 | - ../tools/compiler/base.js 3 | - core/domhelper.js 4 | - core/stylesheetwaiter.js 5 | - core/cssclassname.js 6 | - core/font.js 7 | - core/eventdispatcher.js 8 | - core/fontmodule.js 9 | - core/fontmoduleloader.js 10 | - core/fontruler.js 11 | - core/nativefontwatchrunner.js 12 | - core/fontwatchrunner.js 13 | - core/fontwatcher.js 14 | - core/webfont.js 15 | - core/initialize.js 16 | 17 | 18 | 19 | google: 20 | - modules/google/fontapiurlbuilder.js 21 | - modules/google/fontapiparser.js 22 | - modules/google/googlefontapi.js 23 | 24 | fontdeck: 25 | - modules/fontdeck.js 26 | 27 | typekit: 28 | - modules/typekit.js 29 | 30 | monotype: 31 | - modules/monotype.js 32 | 33 | custom: 34 | - modules/custom.js 35 | -------------------------------------------------------------------------------- /src/modules/custom.js: -------------------------------------------------------------------------------- 1 | goog.provide('webfont.modules.Custom'); 2 | 3 | goog.require('webfont.Font'); 4 | goog.require('webfont.StyleSheetWaiter'); 5 | 6 | /** 7 | * 8 | * WebFont.load({ 9 | * custom: { 10 | * families: ['Font1', 'Font2'], 11 | * urls: [ 'https://moo', 'https://meuh' ] } 12 | * }); 13 | * 14 | * @constructor 15 | * @implements {webfont.FontModule} 16 | */ 17 | webfont.modules.Custom = function(domHelper, configuration) { 18 | this.domHelper_ = domHelper; 19 | this.configuration_ = configuration; 20 | }; 21 | 22 | /** 23 | * @const 24 | * @type {string} 25 | */ 26 | webfont.modules.Custom.NAME = 'custom'; 27 | 28 | goog.scope(function () { 29 | var Custom = webfont.modules.Custom, 30 | Font = webfont.Font, 31 | StyleSheetWaiter = webfont.StyleSheetWaiter; 32 | 33 | Custom.prototype.load = function(onReady) { 34 | var i, len; 35 | var urls = this.configuration_['urls'] || []; 36 | var familiesConfiguration = this.configuration_['families'] || []; 37 | var fontTestStrings = this.configuration_['testStrings'] || {}; 38 | var waiter = new StyleSheetWaiter(); 39 | for (i = 0, len = urls.length; i < len; i++) { 40 | this.domHelper_.loadStylesheet(urls[i], waiter.startWaitingLoad()); 41 | } 42 | 43 | var fonts = []; 44 | 45 | for (i = 0, len = familiesConfiguration.length; i < len; i++) { 46 | var components = familiesConfiguration[i].split(":"); 47 | 48 | if (components[1]) { 49 | var variations = components[1].split(","); 50 | 51 | for (var j = 0; j < variations.length; j += 1) { 52 | fonts.push(new Font(components[0], variations[j])); 53 | } 54 | } else { 55 | fonts.push(new Font(components[0])); 56 | } 57 | } 58 | 59 | waiter.waitWhileNeededThen(function() { 60 | onReady(fonts, fontTestStrings); 61 | }); 62 | }; 63 | }); 64 | -------------------------------------------------------------------------------- /src/modules/fontdeck.js: -------------------------------------------------------------------------------- 1 | goog.provide('webfont.modules.Fontdeck'); 2 | 3 | goog.require('webfont.Font'); 4 | 5 | /** 6 | * @constructor 7 | * @implements {webfont.FontModule} 8 | */ 9 | webfont.modules.Fontdeck = function(domHelper, configuration) { 10 | this.domHelper_ = domHelper; 11 | this.configuration_ = configuration; 12 | this.fonts_ = []; 13 | }; 14 | 15 | /** 16 | * @const 17 | * @type {string} 18 | */ 19 | webfont.modules.Fontdeck.NAME = 'fontdeck'; 20 | webfont.modules.Fontdeck.HOOK = '__webfontfontdeckmodule__'; 21 | webfont.modules.Fontdeck.API = 'https://f.fontdeck.com/s/css/js/'; 22 | 23 | goog.scope(function () { 24 | var Fontdeck = webfont.modules.Fontdeck, 25 | Font = webfont.Font, 26 | FontVariationDescription = webfont.FontVariationDescription; 27 | 28 | Fontdeck.prototype.getScriptSrc = function(projectId) { 29 | // For empty iframes, fall back to main window's hostname. 30 | var hostname = this.domHelper_.getHostName(); 31 | var api = this.configuration_['api'] || webfont.modules.Fontdeck.API; 32 | return api + hostname + '/' + projectId + '.js'; 33 | }; 34 | 35 | Fontdeck.prototype.load = function(onReady) { 36 | var projectId = this.configuration_['id']; 37 | var loadWindow = this.domHelper_.getLoadWindow(); 38 | var self = this; 39 | 40 | if (projectId) { 41 | // Provide data to Fontdeck for processing. 42 | if (!loadWindow[webfont.modules.Fontdeck.HOOK]) { 43 | loadWindow[webfont.modules.Fontdeck.HOOK] = {}; 44 | } 45 | 46 | // Fontdeck will call this function to indicate support status 47 | // and what fonts are provided. 48 | loadWindow[webfont.modules.Fontdeck.HOOK][projectId] = function(fontdeckSupports, data) { 49 | for (var i = 0, j = data['fonts'].length; i= 2) { 74 | var fvds = this.parseVariations_(elements[1]); 75 | 76 | if (fvds.length > 0) { 77 | variations = fvds; 78 | } 79 | if (elements.length == 3) { 80 | var subsets = this.parseSubsets_(elements[2]); 81 | if (subsets.length > 0) { 82 | var fontTestString = FontApiParser.INT_FONTS[subsets[0]]; 83 | 84 | if (fontTestString) { 85 | this.fontTestStrings_[fontFamily] = fontTestString; 86 | } 87 | } 88 | } 89 | } 90 | 91 | // For backward compatibility 92 | if (!this.fontTestStrings_[fontFamily]) { 93 | var hanumanTestString = FontApiParser.INT_FONTS[fontFamily]; 94 | if (hanumanTestString) { 95 | this.fontTestStrings_[fontFamily] = hanumanTestString; 96 | } 97 | } 98 | 99 | for (var j = 0; j < variations.length; j += 1) { 100 | this.parsedFonts_.push(new Font(fontFamily, variations[j])); 101 | } 102 | } 103 | }; 104 | 105 | FontApiParser.prototype.generateFontVariationDescription_ = function(variation) { 106 | if (!variation.match(/^[\w-]+$/)) { 107 | return ''; 108 | } 109 | var normalizedVariation = variation.toLowerCase(); 110 | var groups = FontApiParser.VARIATION_MATCH.exec(normalizedVariation); 111 | if (groups == null) { 112 | return ''; 113 | } 114 | var styleMatch = this.normalizeStyle_(groups[2]); 115 | var weightMatch = this.normalizeWeight_(groups[1]); 116 | return [styleMatch, weightMatch].join(''); 117 | }; 118 | 119 | 120 | FontApiParser.prototype.normalizeStyle_ = function(parsedStyle) { 121 | if (parsedStyle == null || parsedStyle == '') { 122 | return 'n'; 123 | } 124 | return FontApiParser.STYLES[parsedStyle]; 125 | }; 126 | 127 | 128 | FontApiParser.prototype.normalizeWeight_ = function(parsedWeight) { 129 | if (parsedWeight == null || parsedWeight == '') { 130 | return '4'; 131 | } 132 | var weight = FontApiParser.WEIGHTS[parsedWeight]; 133 | if (weight) { 134 | return weight; 135 | } 136 | if (isNaN(parsedWeight)) { 137 | return '4'; 138 | } 139 | return parsedWeight.substr(0, 1); 140 | }; 141 | 142 | 143 | FontApiParser.prototype.parseVariations_ = function(variations) { 144 | var finalVariations = []; 145 | 146 | if (!variations) { 147 | return finalVariations; 148 | } 149 | var providedVariations = variations.split(","); 150 | var length = providedVariations.length; 151 | 152 | for (var i = 0; i < length; i++) { 153 | var variation = providedVariations[i]; 154 | var fvd = this.generateFontVariationDescription_(variation); 155 | 156 | if (fvd) { 157 | finalVariations.push(fvd); 158 | } 159 | } 160 | return finalVariations; 161 | }; 162 | 163 | 164 | FontApiParser.prototype.parseSubsets_ = function(subsets) { 165 | var finalSubsets = []; 166 | 167 | if (!subsets) { 168 | return finalSubsets; 169 | } 170 | return subsets.split(","); 171 | }; 172 | 173 | 174 | FontApiParser.prototype.getFonts = function() { 175 | return this.parsedFonts_; 176 | }; 177 | 178 | FontApiParser.prototype.getFontTestStrings = function() { 179 | return this.fontTestStrings_; 180 | }; 181 | }); 182 | -------------------------------------------------------------------------------- /src/modules/google/fontapiurlbuilder.js: -------------------------------------------------------------------------------- 1 | goog.provide('webfont.modules.google.FontApiUrlBuilder'); 2 | 3 | /** 4 | * @constructor 5 | */ 6 | webfont.modules.google.FontApiUrlBuilder = function(apiUrl, text) { 7 | if (apiUrl) { 8 | this.apiUrl_ = apiUrl; 9 | } else { 10 | this.apiUrl_ = webfont.modules.google.FontApiUrlBuilder.DEFAULT_API_URL; 11 | } 12 | this.fontFamilies_ = []; 13 | this.subsets_ = []; 14 | this.text_ = text || ''; 15 | }; 16 | 17 | 18 | webfont.modules.google.FontApiUrlBuilder.DEFAULT_API_URL = 'https://fonts.googleapis.com/css'; 19 | 20 | goog.scope(function () { 21 | var FontApiUrlBuilder = webfont.modules.google.FontApiUrlBuilder; 22 | 23 | FontApiUrlBuilder.prototype.setFontFamilies = function(fontFamilies) { 24 | this.parseFontFamilies_(fontFamilies); 25 | }; 26 | 27 | 28 | FontApiUrlBuilder.prototype.parseFontFamilies_ = 29 | function(fontFamilies) { 30 | var length = fontFamilies.length; 31 | 32 | for (var i = 0; i < length; i++) { 33 | var elements = fontFamilies[i].split(':'); 34 | 35 | if (elements.length == 3) { 36 | this.subsets_.push(elements.pop()); 37 | } 38 | var joinCharacter = ''; 39 | if (elements.length == 2 && elements[1] != ''){ 40 | joinCharacter = ':'; 41 | } 42 | this.fontFamilies_.push(elements.join(joinCharacter)); 43 | } 44 | }; 45 | 46 | 47 | FontApiUrlBuilder.prototype.webSafe = function(string) { 48 | return string.replace(/ /g, '+'); 49 | }; 50 | 51 | 52 | FontApiUrlBuilder.prototype.build = function() { 53 | if (this.fontFamilies_.length == 0) { 54 | throw new Error('No fonts to load!'); 55 | } 56 | if (this.apiUrl_.indexOf("kit=") != -1) { 57 | return this.apiUrl_; 58 | } 59 | var length = this.fontFamilies_.length; 60 | var sb = []; 61 | 62 | for (var i = 0; i < length; i++) { 63 | sb.push(this.webSafe(this.fontFamilies_[i])); 64 | } 65 | var url = this.apiUrl_ + '?family=' + sb.join('%7C'); // '|' escaped. 66 | 67 | if (this.subsets_.length > 0) { 68 | url += '&subset=' + this.subsets_.join(','); 69 | } 70 | 71 | if (this.text_.length > 0) { 72 | url += '&text=' + encodeURIComponent(this.text_); 73 | } 74 | 75 | return url; 76 | }; 77 | }); 78 | -------------------------------------------------------------------------------- /src/modules/google/googlefontapi.js: -------------------------------------------------------------------------------- 1 | goog.provide('webfont.modules.google.GoogleFontApi'); 2 | 3 | goog.require('webfont.modules.google.FontApiUrlBuilder'); 4 | goog.require('webfont.modules.google.FontApiParser'); 5 | goog.require('webfont.FontWatchRunner'); 6 | goog.require('webfont.StyleSheetWaiter'); 7 | 8 | /** 9 | * @constructor 10 | * @implements {webfont.FontModule} 11 | */ 12 | webfont.modules.google.GoogleFontApi = function(domHelper, configuration) { 13 | this.domHelper_ = domHelper; 14 | this.configuration_ = configuration; 15 | }; 16 | 17 | /** 18 | * @const 19 | * @type {string} 20 | */ 21 | webfont.modules.google.GoogleFontApi.NAME = 'google'; 22 | 23 | goog.scope(function () { 24 | var GoogleFontApi = webfont.modules.google.GoogleFontApi, 25 | FontWatchRunner = webfont.FontWatchRunner, 26 | StyleSheetWaiter = webfont.StyleSheetWaiter, 27 | FontApiUrlBuilder = webfont.modules.google.FontApiUrlBuilder, 28 | FontApiParser = webfont.modules.google.FontApiParser; 29 | 30 | GoogleFontApi.METRICS_COMPATIBLE_FONTS = { 31 | "Arimo": true, 32 | "Cousine": true, 33 | "Tinos": true 34 | }; 35 | 36 | GoogleFontApi.prototype.load = function(onReady) { 37 | var waiter = new StyleSheetWaiter(); 38 | var domHelper = this.domHelper_; 39 | var fontApiUrlBuilder = new FontApiUrlBuilder( 40 | this.configuration_['api'], 41 | this.configuration_['text'] 42 | ); 43 | var fontFamilies = this.configuration_['families']; 44 | fontApiUrlBuilder.setFontFamilies(fontFamilies); 45 | 46 | var fontApiParser = new FontApiParser(fontFamilies); 47 | fontApiParser.parse(); 48 | 49 | domHelper.loadStylesheet(fontApiUrlBuilder.build(), waiter.startWaitingLoad()); 50 | waiter.waitWhileNeededThen(function() { 51 | onReady(fontApiParser.getFonts(), fontApiParser.getFontTestStrings(), GoogleFontApi.METRICS_COMPATIBLE_FONTS); 52 | }); 53 | }; 54 | }); 55 | -------------------------------------------------------------------------------- /src/modules/monotype.js: -------------------------------------------------------------------------------- 1 | goog.provide('webfont.modules.Monotype'); 2 | 3 | goog.require('webfont.Font'); 4 | 5 | /** 6 | webfont.load({ 7 | monotype: { 8 | projectId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'//this is your Fonts.com Web fonts projectId 9 | } 10 | }); 11 | */ 12 | 13 | /** 14 | * @constructor 15 | * @implements {webfont.FontModule} 16 | */ 17 | webfont.modules.Monotype = function(domHelper, configuration) { 18 | this.domHelper_ = domHelper; 19 | this.configuration_ = configuration; 20 | }; 21 | 22 | /** 23 | * name of the module through which external API is supposed to call the MonotypeFontAPI. 24 | * 25 | * @const 26 | * @type {string} 27 | */ 28 | webfont.modules.Monotype.NAME = 'monotype'; 29 | 30 | /** 31 | * __mti_fntLst is the name of function that exposes Monotype's font list. 32 | * @const 33 | */ 34 | webfont.modules.Monotype.HOOK = '__mti_fntLst'; 35 | 36 | /** 37 | * __MonotypeAPIScript__ is the id of script added by google API. Currently 'fonts.com' supports only one script in a page. 38 | * This may require change in future if 'fonts.com' begins supporting multiple scripts per page. 39 | * @const 40 | */ 41 | webfont.modules.Monotype.SCRIPTID = '__MonotypeAPIScript__'; 42 | 43 | /** 44 | * __MonotypeConfiguration__ is function exposed to fonts.com. fonts.com will use this function to get webfontloader configuration 45 | * @const 46 | */ 47 | webfont.modules.Monotype.CONFIGURATION = '__MonotypeConfiguration__'; 48 | 49 | goog.scope(function() { 50 | var Monotype = webfont.modules.Monotype, 51 | Font = webfont.Font; 52 | 53 | 54 | Monotype.prototype.getScriptSrc = function(projectId, version) { 55 | var api = (this.configuration_['api'] || 'https://fast.fonts.net/jsapi') 56 | return api + '/' + projectId + '.js' + (version ? '?v=' + version : ''); 57 | }; 58 | 59 | Monotype.prototype.load = function(onReady) { 60 | var self = this; 61 | var projectId = self.configuration_['projectId']; 62 | var version = self.configuration_['version']; 63 | 64 | 65 | function checkAndLoadIfDownloaded() { 66 | if (loadWindow[Monotype.HOOK + projectId]) { 67 | var mti_fnts = loadWindow[Monotype.HOOK + projectId](), 68 | fonts = [], 69 | fntVariation; 70 | 71 | if (mti_fnts) { 72 | for (var i = 0; i < mti_fnts.length; i++) { 73 | var fnt = mti_fnts[i]["fontfamily"]; 74 | 75 | //Check if font-style and font-weight is available 76 | if (mti_fnts[i]["fontStyle"] != undefined && mti_fnts[i]["fontWeight"] != undefined) { 77 | fntVariation = mti_fnts[i]["fontStyle"] + mti_fnts[i]["fontWeight"]; 78 | fonts.push(new Font(fnt, fntVariation)); 79 | } else { 80 | fonts.push(new Font(fnt)); 81 | } 82 | } 83 | } 84 | onReady(fonts); 85 | } else { 86 | setTimeout(function() { 87 | checkAndLoadIfDownloaded(); 88 | }, 50); 89 | } 90 | } 91 | if (projectId) { 92 | var loadWindow = self.domHelper_.getLoadWindow(); 93 | 94 | var script = this.domHelper_.loadScript(self.getScriptSrc(projectId, version), function(err) { 95 | if (err) { 96 | onReady([]); 97 | } else { 98 | loadWindow[Monotype.CONFIGURATION+ projectId] = function() { 99 | return self.configuration_; 100 | }; 101 | 102 | checkAndLoadIfDownloaded(); 103 | } 104 | }); 105 | script["id"] = Monotype.SCRIPTID + projectId; 106 | } else { 107 | onReady([]); 108 | } 109 | }; 110 | }); -------------------------------------------------------------------------------- /src/modules/typekit.js: -------------------------------------------------------------------------------- 1 | goog.provide('webfont.modules.Typekit'); 2 | 3 | goog.require('webfont.Font'); 4 | 5 | /** 6 | * @constructor 7 | * @implements {webfont.FontModule} 8 | */ 9 | webfont.modules.Typekit = function(domHelper, configuration) { 10 | this.domHelper_ = domHelper; 11 | this.configuration_ = configuration; 12 | }; 13 | 14 | /** 15 | * @const 16 | * @type {string} 17 | */ 18 | webfont.modules.Typekit.NAME = 'typekit'; 19 | 20 | goog.scope(function () { 21 | var Typekit = webfont.modules.Typekit, 22 | Font = webfont.Font; 23 | 24 | Typekit.prototype.getScriptSrc = function(kitId) { 25 | var api = this.configuration_['api'] || 'https://use.typekit.net'; 26 | return api + '/' + kitId + '.js'; 27 | }; 28 | 29 | Typekit.prototype.load = function(onReady) { 30 | var kitId = this.configuration_['id']; 31 | var configuration = this.configuration_; 32 | var loadWindow = this.domHelper_.getLoadWindow(); 33 | var that = this; 34 | 35 | if (kitId) { 36 | // Load the Typekit script. Once it is done loading we grab its configuration 37 | // and use that to populate the fonts we should watch. 38 | this.domHelper_.loadScript(this.getScriptSrc(kitId), function (err) { 39 | if (err) { 40 | onReady([]); 41 | } else { 42 | if (loadWindow['Typekit'] && loadWindow['Typekit']['config'] && loadWindow['Typekit']['config']['fn']) { 43 | var fn = loadWindow['Typekit']['config']['fn'], 44 | fonts = []; 45 | 46 | for (var i = 0; i < fn.length; i += 2) { 47 | var font = fn[i], 48 | variations = fn[i + 1]; 49 | 50 | for (var j = 0; j < variations.length; j++) { 51 | fonts.push(new Font(font, variations[j])); 52 | } 53 | } 54 | 55 | // Kick off font loading but disable font events so 56 | // we don't duplicate font watching. 57 | try { 58 | loadWindow['Typekit']['load']({ 59 | 'events': false, 60 | 'classes': false, 61 | 'async': true 62 | }); 63 | } catch (e) {} 64 | 65 | onReady(fonts); 66 | } 67 | } 68 | }, 2000); 69 | } else { 70 | onReady([]); 71 | } 72 | }; 73 | }); 74 | -------------------------------------------------------------------------------- /tools/compiler/compiler.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typekit/webfontloader/117e48d521d6408f78cbfe4d23923cc828fdf576/tools/compiler/compiler.jar -------------------------------------------------------------------------------- /tools/jasmine-browserstack/jasmine-browserstack.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['jasmine'], factory); 4 | } else { 5 | factory(jasmine); 6 | } 7 | }(this, function (jasmine) { 8 | function stack(err) { 9 | var str = err.stack || err.toString(); 10 | 11 | if (!~str.indexOf(err.message)) { 12 | str = err.message + '\n' + str; 13 | } 14 | 15 | if ('[object Error]' == str) { 16 | str = err.message; 17 | } 18 | 19 | if (!err.stack && err.sourceURL && err.line !== undefined) { 20 | str += '\n(' + err.sourceURL + ':' + err.line + ')'; 21 | } 22 | return str.replace(/^/gm, ' '); 23 | } 24 | 25 | function BrowserStackReporter() { 26 | this.stats = { 27 | suites: 0, 28 | tests: 0, 29 | passes: 0, 30 | pending: 0, 31 | failures: 0 32 | }; 33 | this.tests = []; 34 | } 35 | 36 | BrowserStackReporter.prototype.reportRunnerStarting = function (runner) { 37 | this.stats.start = new Date(); 38 | }; 39 | 40 | BrowserStackReporter.prototype.reportSpecStarting = function (spec) { 41 | spec.startedAt = new Date().getTime(); 42 | }; 43 | 44 | BrowserStackReporter.prototype.reportSpecResults = function (spec) { 45 | var currentTime = new Date().getTime(); 46 | spec.duration = currentTime - spec.startedAt; 47 | 48 | var result = spec.results(); 49 | 50 | var test = { 51 | status: null, 52 | title: spec.getFullName().replace(/#/g, ''), 53 | duration: currentTime - spec.startedAt 54 | }; 55 | 56 | if (result.skipped) { 57 | this.stats.pending += 1; 58 | test.status = 'skipped'; 59 | } else if (result.failedCount === 0) { 60 | this.stats.passes += 1; 61 | test.status = 'passed'; 62 | } else { 63 | var items = result.getItems(), 64 | message = []; 65 | 66 | for (var i = 0; i < items.length; i += 1) { 67 | message.push(stack(items[i].trace)); 68 | } 69 | 70 | test.err = message; 71 | test.status = 'failed'; 72 | this.stats.failures += 1; 73 | } 74 | 75 | this.stats.tests += 1; 76 | this.tests.push(test); 77 | }; 78 | 79 | BrowserStackReporter.prototype.reportSuiteResults = function (suite) { 80 | }; 81 | 82 | BrowserStackReporter.prototype.reportRunnerResults = function (runner) { 83 | var suites = runner.suites(); 84 | 85 | this.stats.end = new Date(); 86 | this.stats.duration = this.stats.end - this.stats.end; 87 | 88 | this.stats.suites = suites.length; 89 | 90 | var result = '1..' + this.stats.tests + '\n'; 91 | 92 | for (var i = 0; i < this.tests.length; i += 1) { 93 | var count = i + 1; 94 | 95 | if (this.tests[i].status === 'pending') { 96 | result += 'ok ' + count + ' ' + this.tests[i].title + ' # SKIP -\n'; 97 | } else if (this.tests[i].status === 'failed') { 98 | result += 'not ok ' + count + ' ' + this.tests[i].title + '\n'; 99 | for (var j = 0; j < this.tests[i].err.length; j += 1) { 100 | result += this.tests[i].err[j] + '\n'; 101 | } 102 | } else { 103 | result += 'ok ' + count + ' ' + this.tests[i].title + '\n'; 104 | } 105 | } 106 | 107 | result += '# tests ' + this.stats.tests + '\n'; 108 | result += '# pass ' + this.stats.passes + '\n'; 109 | result += '# fail ' + this.stats.failures + '\n'; 110 | 111 | if (/browser=/i.test(window.location.search)) { 112 | var xhr = null; 113 | 114 | if (window.XMLHttpRequest) { 115 | xhr = new XMLHttpRequest(); 116 | } else { 117 | xhr = new ActiveXObject('Microsoft.XMLHTTP'); 118 | } 119 | 120 | xhr.open('POST', window.location.href); 121 | xhr.setRequestHeader('Content-Type', 'text/plain'); 122 | xhr.send(result); 123 | } 124 | }; 125 | 126 | // Attach to the jasmine object like many other reporters do 127 | jasmine.BrowserStackReporter = BrowserStackReporter; 128 | })); 129 | -------------------------------------------------------------------------------- /tools/jasmine-phantomjs/jasmine-phantomjs.js: -------------------------------------------------------------------------------- 1 | var webpage = require('webpage'), 2 | system = require('system'); 3 | 4 | if (system.args.length !== 1) { 5 | var page = webpage.create(); 6 | 7 | page.onConsoleMessage = function (msg) { 8 | console.log(msg); 9 | if (/SUCCESS|FAILURE/.test(msg)) { 10 | if (/SUCCESS/.test(msg)) { 11 | phantom.exit(0); 12 | } else { 13 | phantom.exit(1); 14 | } 15 | } 16 | }; 17 | 18 | page.open(system.args[1], function (status) { 19 | if (status !== 'success') { 20 | phantom.exit(1); 21 | } 22 | }); 23 | } else { 24 | console.log('Usage: jasmine-phantomjs [FILE]'); 25 | phantom.exit(1); 26 | } 27 | -------------------------------------------------------------------------------- /tools/jasmine-phantomjs/terminal-reporter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Taken from https://github.com/larrymyers/jasmine-reporters 3 | * Licensed under the MIT license. 4 | */ 5 | (function() { 6 | if (! jasmine) { 7 | throw new Exception("jasmine library does not exist in global namespace!"); 8 | } 9 | 10 | /** 11 | * Basic reporter that outputs spec results to the terminal. 12 | * Use this reporter in your build pipeline. 13 | * 14 | * Usage: 15 | * 16 | * jasmine.getEnv().addReporter(new jasmine.TerminalReporter({ 17 | verbosity: 2, 18 | color: true 19 | })); 20 | * jasmine.getEnv().execute(); 21 | */ 22 | var DEFAULT_VERBOSITY = 2, 23 | ATTRIBUTES_TO_ANSI = { 24 | "off": 0, 25 | "bold": 1, 26 | "red": 31, 27 | "green": 32 28 | }; 29 | 30 | var TerminalReporter = function(params) { 31 | var parameters = params || {}; 32 | 33 | if (parameters.verbosity === 0) { 34 | this.verbosity = 0; 35 | } else { 36 | this.verbosity = parameters.verbosity || DEFAULT_VERBOSITY; 37 | } 38 | this.color = parameters.color; 39 | 40 | this.started = false; 41 | this.finished = false; 42 | this.current_suite_hierarchy = []; 43 | this.indent_string = ' '; 44 | }; 45 | 46 | TerminalReporter.prototype = { 47 | reportRunnerResults: function(runner) { 48 | var dur = (new Date()).getTime() - this.start_time, 49 | failed = this.executed_specs - this.passed_specs, 50 | spec_str = this.executed_specs + (this.executed_specs === 1 ? " spec, " : " specs, "), 51 | fail_str = failed + (failed === 1 ? " failure in " : " failures in "), 52 | summary_str = spec_str + fail_str + (dur/1000) + "s.", 53 | result_str = (failed && "FAILURE: " || "SUCCESS: ") + summary_str, 54 | result_color = failed && "red+bold" || "green+bold"; 55 | 56 | if (this.verbosity === 2) { 57 | this.log(""); 58 | } 59 | 60 | if (this.verbosity > 0) { 61 | this.log(this.inColor(result_str, result_color)); 62 | } 63 | 64 | this.finished = true; 65 | }, 66 | 67 | reportRunnerStarting: function(runner) { 68 | this.started = true; 69 | this.start_time = (new Date()).getTime(); 70 | this.executed_specs = 0; 71 | this.passed_specs = 0; 72 | }, 73 | 74 | reportSpecResults: function(spec) { 75 | var color = "red"; 76 | 77 | if (spec.results().passed()) { 78 | this.passed_specs++; 79 | color = "green"; 80 | } 81 | 82 | if (this.verbosity === 2) { 83 | var resultText = 'F'; 84 | 85 | if (spec.results().passed()) { 86 | resultText = '.'; 87 | } 88 | this.log(this.inColor(resultText, color)); 89 | } else if (this.verbosity > 2) { 90 | resultText = "Failed"; 91 | 92 | if (spec.results().passed()) { 93 | resultText = 'Passed'; 94 | } 95 | this.log(' ' + this.inColor(resultText, color)); 96 | } 97 | }, 98 | 99 | reportSpecStarting: function(spec) { 100 | this.executed_specs++; 101 | if (this.verbosity > 2) { 102 | this.logCurrentSuite(spec.suite); 103 | 104 | this.log(this.indentWithCurrentLevel(this.indent_string + spec.description + ' ...')); 105 | } 106 | }, 107 | 108 | reportSuiteResults: function(suite) { 109 | var results = suite.results(), 110 | failed = results.totalCount - results.passedCount, 111 | color = failed ? "red+bold" : "green+bold"; 112 | 113 | if (this.verbosity > 2) { 114 | this.logCurrentSuite(suite); 115 | this.log(this.indentWithCurrentLevel(this.inColor(results.passedCount + " of " 116 | + results.totalCount + " passed.", color))); 117 | } 118 | }, 119 | 120 | indentWithCurrentLevel: function(string) { 121 | return new Array(this.current_suite_hierarchy.length).join(this.indent_string) + string; 122 | }, 123 | 124 | recursivelyUpdateHierarchyUpToRootAndLogNewBranches: function(suite) { 125 | var suite_path = [], 126 | current_level; 127 | 128 | if (suite.parentSuite != null) { 129 | suite_path = this.recursivelyUpdateHierarchyUpToRootAndLogNewBranches(suite.parentSuite); 130 | } 131 | 132 | suite_path.push(suite); 133 | current_level = suite_path.length - 1; 134 | 135 | if (this.current_suite_hierarchy.length <= current_level 136 | || this.current_suite_hierarchy[current_level] !== suite) { 137 | 138 | this.current_suite_hierarchy = suite_path.slice(0); 139 | this.log(this.indentWithCurrentLevel(this.inColor(suite.description, "bold"))); 140 | } 141 | return suite_path; 142 | }, 143 | 144 | logCurrentSuite: function(suite) { 145 | var suite_path = this.recursivelyUpdateHierarchyUpToRootAndLogNewBranches(suite); 146 | // If we just popped down from a higher path, we need to update here 147 | this.current_suite_hierarchy = suite_path; 148 | }, 149 | 150 | inColor: function (string, color) { 151 | var color_attributes = color && color.split("+"), 152 | ansi_string = "", 153 | i, attr; 154 | 155 | if (! this.color || ! color_attributes) { 156 | return string; 157 | } 158 | 159 | for(i = 0; i < color_attributes.length; i++) { 160 | ansi_string += "\033[" + ATTRIBUTES_TO_ANSI[color_attributes[i]] + "m"; 161 | } 162 | ansi_string += string + "\033[" + ATTRIBUTES_TO_ANSI["off"] + "m"; 163 | 164 | return ansi_string; 165 | }, 166 | 167 | log: function(str) { 168 | var console = jasmine.getGlobal().console; 169 | if (console && console.log) { 170 | console.log(str); 171 | } 172 | } 173 | }; 174 | 175 | // export public 176 | jasmine.TerminalReporter = TerminalReporter; 177 | })(); 178 | -------------------------------------------------------------------------------- /tools/jasmine/MIT.LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2011 Pivotal Labs 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /tools/jasmine/jasmine.css: -------------------------------------------------------------------------------- 1 | body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } 2 | 3 | #HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } 4 | #HTMLReporter a { text-decoration: none; } 5 | #HTMLReporter a:hover { text-decoration: underline; } 6 | #HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } 7 | #HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } 8 | #HTMLReporter #jasmine_content { position: fixed; right: 100%; } 9 | #HTMLReporter .version { color: #aaaaaa; } 10 | #HTMLReporter .banner { margin-top: 14px; } 11 | #HTMLReporter .duration { color: #aaaaaa; float: right; } 12 | #HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } 13 | #HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } 14 | #HTMLReporter .symbolSummary li.passed { font-size: 14px; } 15 | #HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } 16 | #HTMLReporter .symbolSummary li.failed { line-height: 9px; } 17 | #HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } 18 | #HTMLReporter .symbolSummary li.skipped { font-size: 14px; } 19 | #HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } 20 | #HTMLReporter .symbolSummary li.pending { line-height: 11px; } 21 | #HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } 22 | #HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } 23 | #HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } 24 | #HTMLReporter .runningAlert { background-color: #666666; } 25 | #HTMLReporter .skippedAlert { background-color: #aaaaaa; } 26 | #HTMLReporter .skippedAlert:first-child { background-color: #333333; } 27 | #HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } 28 | #HTMLReporter .passingAlert { background-color: #a6b779; } 29 | #HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } 30 | #HTMLReporter .failingAlert { background-color: #cf867e; } 31 | #HTMLReporter .failingAlert:first-child { background-color: #b03911; } 32 | #HTMLReporter .results { margin-top: 14px; } 33 | #HTMLReporter #details { display: none; } 34 | #HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } 35 | #HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } 36 | #HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } 37 | #HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } 38 | #HTMLReporter.showDetails .summary { display: none; } 39 | #HTMLReporter.showDetails #details { display: block; } 40 | #HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } 41 | #HTMLReporter .summary { margin-top: 14px; } 42 | #HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } 43 | #HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } 44 | #HTMLReporter .summary .specSummary.failed a { color: #b03911; } 45 | #HTMLReporter .description + .suite { margin-top: 0; } 46 | #HTMLReporter .suite { margin-top: 14px; } 47 | #HTMLReporter .suite a { color: #333333; } 48 | #HTMLReporter #details .specDetail { margin-bottom: 28px; } 49 | #HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } 50 | #HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } 51 | #HTMLReporter .resultMessage span.result { display: block; } 52 | #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; } 53 | 54 | #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;*/ /*}*/ } 55 | #TrivialReporter a:visited, #TrivialReporter a { color: #303; } 56 | #TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } 57 | #TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } 58 | #TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } 59 | #TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } 60 | #TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } 61 | #TrivialReporter .runner.running { background-color: yellow; } 62 | #TrivialReporter .options { text-align: right; font-size: .8em; } 63 | #TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } 64 | #TrivialReporter .suite .suite { margin: 5px; } 65 | #TrivialReporter .suite.passed { background-color: #dfd; } 66 | #TrivialReporter .suite.failed { background-color: #fdd; } 67 | #TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } 68 | #TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } 69 | #TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } 70 | #TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } 71 | #TrivialReporter .spec.skipped { background-color: #bbb; } 72 | #TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } 73 | #TrivialReporter .passed { background-color: #cfc; display: none; } 74 | #TrivialReporter .failed { background-color: #fbb; } 75 | #TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } 76 | #TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } 77 | #TrivialReporter .resultMessage .mismatch { color: black; } 78 | #TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } 79 | #TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } 80 | #TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } 81 | #TrivialReporter #jasmine_content { position: fixed; right: 100%; } 82 | #TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } 83 | -------------------------------------------------------------------------------- /webfontloader.gemspec: -------------------------------------------------------------------------------- 1 | ## This is the rakegem gemspec template. Make sure you read and understand 2 | ## all of the comments. Some sections require modification, and others can 3 | ## be deleted if you don't need them. Once you understand the contents of 4 | ## this file, feel free to delete any comments that begin with two hash marks. 5 | ## You can find comprehensive Gem::Specification documentation, at 6 | ## http://docs.rubygems.org/read/chapter/20 7 | Gem::Specification.new do |s| 8 | s.specification_version = 2 if s.respond_to? :specification_version= 9 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 10 | s.rubygems_version = '1.3.5' 11 | 12 | ## Leave these as is they will be modified for you by the rake gemspec task. 13 | ## If your rubyforge_project name is different, then edit it and comment out 14 | ## the sub! line in the Rakefile 15 | s.name = 'webfontloader' 16 | s.version = '1.6.28' 17 | s.date = '2017-05-27' 18 | 19 | ## Make sure your summary is short. The description may be as long 20 | ## as you like. 21 | s.summary = "WebFont Loader gives you added control when using linked fonts via @font-face." 22 | s.description = <<-DESC 23 | WebFont Loader gives you added control when using linked fonts via 24 | `@font-face`. It provides a common interface to loading fonts regardless of 25 | the source, then adds a standard set of events you may use to control the 26 | loading experience. 27 | DESC 28 | 29 | ## List the primary authors. If there are a bunch of authors, it's probably 30 | ## better to set the email to an email list or something. If you don't have 31 | ## a custom homepage, consider using your GitHub URL or the like. 32 | s.authors = ["Ryan Carver", "Jeremie Lenfant-engelmann"] 33 | s.email = 'ryan@typekit.com' 34 | s.homepage = 'http://github.com/typekit/webfontloader' 35 | 36 | ## License 37 | s.license = "Apache-2.0" 38 | 39 | ## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as 40 | ## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb' 41 | s.require_paths = %w[lib] 42 | 43 | ## This sections is only necessary if you have C extensions. 44 | # s.require_paths << 'ext' 45 | # s.extensions = %w[ext/extconf.rb] 46 | 47 | ## If your gem includes any executables, list them here. 48 | # s.executables = [] 49 | # s.default_executable = 'name' 50 | 51 | ## Specify any RDoc options here. You'll want to add your README and 52 | ## LICENSE files to the extra_rdoc_files list. 53 | s.rdoc_options = ["--charset=UTF-8"] 54 | s.extra_rdoc_files = %w[README.md] + Dir["docs/*.md"] 55 | 56 | ## List your runtime dependencies here. Runtime dependencies are those 57 | ## that are needed for an end user to actually USE your code. 58 | # s.add_dependency('DEPNAME', [">= 1.1.0", "< 2.0.0"]) 59 | 60 | ## List your development dependencies here. Development dependencies are 61 | ## those that are only needed during development 62 | s.add_development_dependency('rake', '~>0') 63 | s.add_development_dependency('rack', '~>1.5', '>=1.5.1') 64 | s.add_development_dependency('sinatra', '~>1.3', '>=1.3.4') 65 | s.add_development_dependency('vegas', '~>0.1.11') 66 | 67 | ## Leave this section as-is. It will be automatically generated from the 68 | ## contents of your Git repository via the gemspec task. DO NOT REMOVE 69 | ## THE MANIFEST COMMENTS, they are used as delimiters by the task. 70 | # = MANIFEST = 71 | s.files = %w[ 72 | CHANGELOG 73 | CONTRIBUTING.md 74 | Gemfile 75 | LICENSE 76 | README.md 77 | Rakefile 78 | bin/webfontloader-demos 79 | bower.json 80 | browsers.json 81 | externs.js 82 | lib/webfontloader.rb 83 | lib/webfontloader/demo/public/basic.css 84 | lib/webfontloader/demo/public/blank.html 85 | lib/webfontloader/demo/public/custom-iframe.html 86 | lib/webfontloader/demo/public/custom.html 87 | lib/webfontloader/demo/public/event-css-active-multiple.html 88 | lib/webfontloader/demo/public/event-css-active.html 89 | lib/webfontloader/demo/public/event-css-inactive.html 90 | lib/webfontloader/demo/public/event-css-loading.html 91 | lib/webfontloader/demo/public/event-js-active.html 92 | lib/webfontloader/demo/public/event-js-font-active.html 93 | lib/webfontloader/demo/public/event-js-loading.html 94 | lib/webfontloader/demo/public/events-variations.html 95 | lib/webfontloader/demo/public/events.html 96 | lib/webfontloader/demo/public/fontdeck.html 97 | lib/webfontloader/demo/public/fontwatchrunner-default-fonts.html 98 | lib/webfontloader/demo/public/google-css.html 99 | lib/webfontloader/demo/public/google-iframe.html 100 | lib/webfontloader/demo/public/google.html 101 | lib/webfontloader/demo/public/ie-fast-js.html 102 | lib/webfontloader/demo/public/ie-slow-js.html 103 | lib/webfontloader/demo/public/ie-slow-link.html 104 | lib/webfontloader/demo/public/index.html 105 | lib/webfontloader/demo/public/jquery.min.js 106 | lib/webfontloader/demo/public/monotype-iframe.html 107 | lib/webfontloader/demo/public/monotype.html 108 | lib/webfontloader/demo/public/typekit-iframe.html 109 | lib/webfontloader/demo/public/typekit-variations.html 110 | lib/webfontloader/demo/public/typekit.html 111 | lib/webfontloader/demo/server.rb 112 | lib/webfontloader/modules.rb 113 | package.json 114 | spec/core/cssclassname_spec.js 115 | spec/core/domhelper_spec.js 116 | spec/core/eventdispatcher_spec.js 117 | spec/core/font_spec.js 118 | spec/core/fontmoduleloader_spec.js 119 | spec/core/fontruler_spec.js 120 | spec/core/fontwatcher_spec.js 121 | spec/core/fontwatchrunner_spec.js 122 | spec/core/nativefontwatchrunner_spec.js 123 | spec/core/size_spec.js 124 | spec/core/webfont_spec.js 125 | spec/deps.js 126 | spec/fixtures/external_script.js 127 | spec/fixtures/external_stylesheet.css 128 | spec/fixtures/fonts/LICENSE.txt 129 | spec/fixtures/fonts/nullfont.css 130 | spec/fixtures/fonts/nullfont1.css 131 | spec/fixtures/fonts/nullfont2.css 132 | spec/fixtures/fonts/nullfont3.css 133 | spec/fixtures/fonts/sourcesans.eot 134 | spec/fixtures/fonts/sourcesans.otf 135 | spec/fixtures/fonts/sourcesans.svg 136 | spec/fixtures/fonts/sourcesans.ttf 137 | spec/fixtures/fonts/sourcesans.woff 138 | spec/fixtures/fonts/sourcesansa.css 139 | spec/fixtures/fonts/sourcesansb.css 140 | spec/fixtures/fonts/sourcesansc.css 141 | spec/fixtures/fonts/sourcesansd.css 142 | spec/fixtures/fonts/sourcesansdup1.css 143 | spec/fixtures/fonts/sourcesansdup2.css 144 | spec/index.html 145 | spec/modules/custom_spec.js 146 | spec/modules/fontdeck_spec.js 147 | spec/modules/google/fontapiparser_spec.js 148 | spec/modules/google/fontapiurlbuilder_spec.js 149 | spec/modules/google/googlefontapi_spec.js 150 | spec/modules/monotype_spec.js 151 | spec/modules/typekit_spec.js 152 | src/closure.js 153 | src/core/cssclassname.js 154 | src/core/domhelper.js 155 | src/core/eventdispatcher.js 156 | src/core/font.js 157 | src/core/fontmodule.js 158 | src/core/fontmoduleloader.js 159 | src/core/fontruler.js 160 | src/core/fontwatcher.js 161 | src/core/fontwatchrunner.js 162 | src/core/initialize.js 163 | src/core/nativefontwatchrunner.js 164 | src/core/stylesheetwaiter.js 165 | src/core/webfont.js 166 | src/modules.yml 167 | src/modules/custom.js 168 | src/modules/fontdeck.js 169 | src/modules/google/fontapiparser.js 170 | src/modules/google/fontapiurlbuilder.js 171 | src/modules/google/googlefontapi.js 172 | src/modules/monotype.js 173 | src/modules/typekit.js 174 | tools/compiler/base.js 175 | tools/compiler/compiler.jar 176 | tools/jasmine-browserstack/jasmine-browserstack.js 177 | tools/jasmine-phantomjs/jasmine-phantomjs.js 178 | tools/jasmine-phantomjs/terminal-reporter.js 179 | tools/jasmine/MIT.LICENSE 180 | tools/jasmine/jasmine-html.js 181 | tools/jasmine/jasmine.css 182 | tools/jasmine/jasmine.js 183 | webfontloader.gemspec 184 | webfontloader.js 185 | ] 186 | # = MANIFEST = 187 | 188 | ## Test files will be grabbed from the file list. Make sure the path glob 189 | ## matches what you actually use. 190 | s.test_files = s.files.select { |path| path =~ /^spec\/.*_spec\.rb/ } 191 | end 192 | --------------------------------------------------------------------------------