1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5'use strict'; 6 7/** 8 * @fileoverview The Advanced Font Settings Extension implementation. 9 */ 10 11function $(id) { 12 return document.getElementById(id); 13} 14 15/** 16 * @namespace 17 */ 18var advancedFonts = {}; 19 20/** 21 * The ICU script code for the Common, or global, script, which is used as the 22 * fallback when the script is undeclared. 23 * @const 24 */ 25advancedFonts.COMMON_SCRIPT = 'Zyyy'; 26 27/** 28 * The scripts supported by the Font Settings Extension API. 29 * @const 30 */ 31advancedFonts.scripts = [ 32 { scriptCode: advancedFonts.COMMON_SCRIPT, scriptName: 'Default'}, 33 { scriptCode: 'Afak', scriptName: 'Afaka'}, 34 { scriptCode: 'Arab', scriptName: 'Arabic'}, 35 { scriptCode: 'Armi', scriptName: 'Imperial Aramaic'}, 36 { scriptCode: 'Armn', scriptName: 'Armenian'}, 37 { scriptCode: 'Avst', scriptName: 'Avestan'}, 38 { scriptCode: 'Bali', scriptName: 'Balinese'}, 39 { scriptCode: 'Bamu', scriptName: 'Bamum'}, 40 { scriptCode: 'Bass', scriptName: 'Bassa Vah'}, 41 { scriptCode: 'Batk', scriptName: 'Batak'}, 42 { scriptCode: 'Beng', scriptName: 'Bengali'}, 43 { scriptCode: 'Blis', scriptName: 'Blissymbols'}, 44 { scriptCode: 'Bopo', scriptName: 'Bopomofo'}, 45 { scriptCode: 'Brah', scriptName: 'Brahmi'}, 46 { scriptCode: 'Brai', scriptName: 'Braille'}, 47 { scriptCode: 'Bugi', scriptName: 'Buginese'}, 48 { scriptCode: 'Buhd', scriptName: 'Buhid'}, 49 { scriptCode: 'Cakm', scriptName: 'Chakma'}, 50 { scriptCode: 'Cans', scriptName: 'Unified Canadian Aboriginal Syllabics'}, 51 { scriptCode: 'Cari', scriptName: 'Carian'}, 52 { scriptCode: 'Cham', scriptName: 'Cham'}, 53 { scriptCode: 'Cher', scriptName: 'Cherokee'}, 54 { scriptCode: 'Cirt', scriptName: 'Cirth'}, 55 { scriptCode: 'Copt', scriptName: 'Coptic'}, 56 { scriptCode: 'Cprt', scriptName: 'Cypriot'}, 57 { scriptCode: 'Cyrl', scriptName: 'Cyrillic'}, 58 { scriptCode: 'Cyrs', scriptName: 'Old Church Slavonic Cyrillic'}, 59 { scriptCode: 'Deva', scriptName: 'Devanagari'}, 60 { scriptCode: 'Dsrt', scriptName: 'Deseret'}, 61 { scriptCode: 'Dupl', scriptName: 'Duployan shorthand'}, 62 { scriptCode: 'Egyd', scriptName: 'Egyptian demotic'}, 63 { scriptCode: 'Egyh', scriptName: 'Egyptian hieratic'}, 64 { scriptCode: 'Egyp', scriptName: 'Egyptian hieroglyphs'}, 65 { scriptCode: 'Elba', scriptName: 'Elbasan'}, 66 { scriptCode: 'Ethi', scriptName: 'Ethiopic'}, 67 { scriptCode: 'Geok', scriptName: 'Georgian Khutsuri'}, 68 { scriptCode: 'Geor', scriptName: 'Georgian'}, 69 { scriptCode: 'Glag', scriptName: 'Glagolitic'}, 70 { scriptCode: 'Goth', scriptName: 'Gothic'}, 71 { scriptCode: 'Gran', scriptName: 'Grantha'}, 72 { scriptCode: 'Grek', scriptName: 'Greek'}, 73 { scriptCode: 'Gujr', scriptName: 'Gujarati'}, 74 { scriptCode: 'Guru', scriptName: 'Gurmukhi'}, 75 { scriptCode: 'Hang', scriptName: 'Hangul'}, 76 { scriptCode: 'Hani', scriptName: 'Han'}, 77 { scriptCode: 'Hano', scriptName: 'Hanunoo'}, 78 { scriptCode: 'Hans', scriptName: 'Simplified Han'}, 79 { scriptCode: 'Hant', scriptName: 'Traditional Han'}, 80 { scriptCode: 'Hebr', scriptName: 'Hebrew'}, 81 { scriptCode: 'Hluw', scriptName: 'Anatolian Hieroglyphs'}, 82 { scriptCode: 'Hmng', scriptName: 'Pahawh Hmong'}, 83 { scriptCode: 'Hung', scriptName: 'Old Hungarian'}, 84 { scriptCode: 'Inds', scriptName: 'Indus'}, 85 { scriptCode: 'Ital', scriptName: 'Old Italic'}, 86 { scriptCode: 'Java', scriptName: 'Javanese'}, 87 { scriptCode: 'Jpan', scriptName: 'Japanese'}, 88 { scriptCode: 'Jurc', scriptName: 'Jurchen'}, 89 { scriptCode: 'Kali', scriptName: 'Kayah Li'}, 90 { scriptCode: 'Khar', scriptName: 'Kharoshthi'}, 91 { scriptCode: 'Khmr', scriptName: 'Khmer'}, 92 { scriptCode: 'Khoj', scriptName: 'Khojki'}, 93 { scriptCode: 'Knda', scriptName: 'Kannada'}, 94 { scriptCode: 'Kpel', scriptName: 'Kpelle'}, 95 { scriptCode: 'Kthi', scriptName: 'Kaithi'}, 96 { scriptCode: 'Lana', scriptName: 'Lanna'}, 97 { scriptCode: 'Laoo', scriptName: 'Lao'}, 98 { scriptCode: 'Latf', scriptName: 'Fraktur Latin'}, 99 { scriptCode: 'Latg', scriptName: 'Gaelic Latin'}, 100 { scriptCode: 'Latn', scriptName: 'Latin'}, 101 { scriptCode: 'Lepc', scriptName: 'Lepcha'}, 102 { scriptCode: 'Limb', scriptName: 'Limbu'}, 103 { scriptCode: 'Lina', scriptName: 'Linear A'}, 104 { scriptCode: 'Linb', scriptName: 'Linear B'}, 105 { scriptCode: 'Lisu', scriptName: 'Fraser'}, 106 { scriptCode: 'Loma', scriptName: 'Loma'}, 107 { scriptCode: 'Lyci', scriptName: 'Lycian'}, 108 { scriptCode: 'Lydi', scriptName: 'Lydian'}, 109 { scriptCode: 'Mand', scriptName: 'Mandaean'}, 110 { scriptCode: 'Mani', scriptName: 'Manichaean'}, 111 { scriptCode: 'Maya', scriptName: 'Mayan hieroglyphs'}, 112 { scriptCode: 'Mend', scriptName: 'Mende'}, 113 { scriptCode: 'Merc', scriptName: 'Meroitic Cursive'}, 114 { scriptCode: 'Mero', scriptName: 'Meroitic'}, 115 { scriptCode: 'Mlym', scriptName: 'Malayalam'}, 116 { scriptCode: 'Mong', scriptName: 'Mongolian'}, 117 { scriptCode: 'Moon', scriptName: 'Moon'}, 118 { scriptCode: 'Mroo', scriptName: 'Mro'}, 119 { scriptCode: 'Mtei', scriptName: 'Meitei Mayek'}, 120 { scriptCode: 'Mymr', scriptName: 'Myanmar'}, 121 { scriptCode: 'Narb', scriptName: 'Old North Arabian'}, 122 { scriptCode: 'Nbat', scriptName: 'Nabataean'}, 123 { scriptCode: 'Nkgb', scriptName: 'Naxi Geba'}, 124 { scriptCode: 'Nkoo', scriptName: 'N’Ko'}, 125 { scriptCode: 'Nshu', scriptName: 'Nüshu'}, 126 { scriptCode: 'Ogam', scriptName: 'Ogham'}, 127 { scriptCode: 'Olck', scriptName: 'Ol Chiki'}, 128 { scriptCode: 'Orkh', scriptName: 'Orkhon'}, 129 { scriptCode: 'Orya', scriptName: 'Oriya'}, 130 { scriptCode: 'Osma', scriptName: 'Osmanya'}, 131 { scriptCode: 'Palm', scriptName: 'Palmyrene'}, 132 { scriptCode: 'Perm', scriptName: 'Old Permic'}, 133 { scriptCode: 'Phag', scriptName: 'Phags-pa'}, 134 { scriptCode: 'Phli', scriptName: 'Inscriptional Pahlavi'}, 135 { scriptCode: 'Phlp', scriptName: 'Psalter Pahlavi'}, 136 { scriptCode: 'Phlv', scriptName: 'Book Pahlavi'}, 137 { scriptCode: 'Phnx', scriptName: 'Phoenician'}, 138 { scriptCode: 'Plrd', scriptName: 'Pollard Phonetic'}, 139 { scriptCode: 'Prti', scriptName: 'Inscriptional Parthian'}, 140 { scriptCode: 'Rjng', scriptName: 'Rejang'}, 141 { scriptCode: 'Roro', scriptName: 'Rongorongo'}, 142 { scriptCode: 'Runr', scriptName: 'Runic'}, 143 { scriptCode: 'Samr', scriptName: 'Samaritan'}, 144 { scriptCode: 'Sara', scriptName: 'Sarati'}, 145 { scriptCode: 'Sarb', scriptName: 'Old South Arabian'}, 146 { scriptCode: 'Saur', scriptName: 'Saurashtra'}, 147 { scriptCode: 'Sgnw', scriptName: 'SignWriting'}, 148 { scriptCode: 'Shaw', scriptName: 'Shavian'}, 149 { scriptCode: 'Shrd', scriptName: 'Sharada'}, 150 { scriptCode: 'Sind', scriptName: 'Khudawadi'}, 151 { scriptCode: 'Sinh', scriptName: 'Sinhala'}, 152 { scriptCode: 'Sora', scriptName: 'Sora Sompeng'}, 153 { scriptCode: 'Sund', scriptName: 'Sundanese'}, 154 { scriptCode: 'Sylo', scriptName: 'Syloti Nagri'}, 155 { scriptCode: 'Syrc', scriptName: 'Syriac'}, 156 { scriptCode: 'Syre', scriptName: 'Estrangelo Syriac'}, 157 { scriptCode: 'Syrj', scriptName: 'Western Syriac'}, 158 { scriptCode: 'Syrn', scriptName: 'Eastern Syriac'}, 159 { scriptCode: 'Tagb', scriptName: 'Tagbanwa'}, 160 { scriptCode: 'Takr', scriptName: 'Takri'}, 161 { scriptCode: 'Tale', scriptName: 'Tai Le'}, 162 { scriptCode: 'Talu', scriptName: 'New Tai Lue'}, 163 { scriptCode: 'Taml', scriptName: 'Tamil'}, 164 { scriptCode: 'Tang', scriptName: 'Tangut'}, 165 { scriptCode: 'Tavt', scriptName: 'Tai Viet'}, 166 { scriptCode: 'Telu', scriptName: 'Telugu'}, 167 { scriptCode: 'Teng', scriptName: 'Tengwar'}, 168 { scriptCode: 'Tfng', scriptName: 'Tifinagh'}, 169 { scriptCode: 'Tglg', scriptName: 'Tagalog'}, 170 { scriptCode: 'Thaa', scriptName: 'Thaana'}, 171 { scriptCode: 'Thai', scriptName: 'Thai'}, 172 { scriptCode: 'Tibt', scriptName: 'Tibetan'}, 173 { scriptCode: 'Tirh', scriptName: 'Tirhuta'}, 174 { scriptCode: 'Ugar', scriptName: 'Ugaritic'}, 175 { scriptCode: 'Vaii', scriptName: 'Vai'}, 176 { scriptCode: 'Visp', scriptName: 'Visible Speech'}, 177 { scriptCode: 'Wara', scriptName: 'Varang Kshiti'}, 178 { scriptCode: 'Wole', scriptName: 'Woleai'}, 179 { scriptCode: 'Xpeo', scriptName: 'Old Persian'}, 180 { scriptCode: 'Xsux', scriptName: 'Sumero-Akkadian Cuneiform'}, 181 { scriptCode: 'Yiii', scriptName: 'Yi'}, 182 { scriptCode: 'Zmth', scriptName: 'Mathematical Notation'}, 183 { scriptCode: 'Zsym', scriptName: 'Symbols'} 184]; 185 186/** 187 * The generic font families supported by the Font Settings Extension API. 188 * @const 189 */ 190advancedFonts.FAMILIES = 191 ['standard', 'sansserif', 'serif', 'fixed', 'cursive', 'fantasy']; 192 193/** 194 * Sample texts. 195 * @const 196 */ 197advancedFonts.SAMPLE_TEXTS = { 198 // "Cyrllic script". 199 Cyrl: 'Кириллица', 200 Hang: '정 참판 양반댁 규수 큰 교자 타고 혼례 치른 날.', 201 Hans: '床前明月光,疑是地上霜。举头望明月,低头思故乡。', 202 Hant: '床前明月光,疑是地上霜。舉頭望明月,低頭思故鄉。', 203 Jpan: '吾輩は猫である。名前はまだ無い。', 204 // "Khmer language". 205 Khmr: '\u1797\u17B6\u179F\u17B6\u1781\u17D2\u1798\u17C2\u179A', 206 Zyyy: 'The quick brown fox jumps over the lazy dog.' 207}; 208 209/** 210 * Controller of pending changes. 211 * @const 212 */ 213advancedFonts.pendingChanges = new PendingChanges(); 214 215/** 216 * Map from |genericFamily| to UI controls and data for its font setting. 217 */ 218advancedFonts.fontSettings = null; 219 220/** 221 * Map from |fontSizeKey| to UI contols and data for its font size setting. 222 */ 223advancedFonts.fontSizeSettings = null; 224 225/** 226 * Gets the font size used for |fontSizeKey|, including pending changes. Calls 227 * |callback| with the result. 228 * 229 * @param {string} fontSizeKey The font size setting key. See 230 * PendingChanges.getFontSize(). 231 * @param {function(number, boolean)} callback The callback of form 232 * function(size, controllable). |size| is the effective setting, 233 * |controllable| is whether the setting can be set. 234 */ 235advancedFonts.getEffectiveFontSize = function(fontSizeKey, callback) { 236 advancedFonts.fontSizeSettings[fontSizeKey].getter({}, function(details) { 237 var controllable = advancedFonts.isControllableLevel( 238 details.levelOfControl); 239 var size = details.pixelSize; 240 var pendingFontSize = advancedFonts.pendingChanges.getFontSize(fontSizeKey); 241 // If the setting is not controllable, we can have no pending change. 242 if (!controllable) { 243 if (pendingFontSize != null) { 244 advancedFonts.pendingChanges.setFontSize(fontSizeKey, null); 245 $('apply-settings').disabled = advancedFonts.pendingChanges.isEmpty(); 246 pendingFontSize = null; 247 } 248 } 249 250 // If we have a pending change, it overrides the current setting. 251 if (pendingFontSize != null) 252 size = pendingFontSize; 253 callback(size, controllable); 254 }); 255}; 256 257/** 258 * Gets the font used for |script| and |genericFamily|, including pending 259 * changes. Calls |callback| with the result. 260 * 261 * @param {string} script The script code. 262 * @param {string} genericFamily The generic family. 263 * @param {function(string, boolean, string)} callback The callback of form 264 * function(font, controllable, effectiveFont). |font| is the setting 265 * (pending or not), |controllable| is whether the setting can be set, 266 * |effectiveFont| is the font used taking fallback into consideration. 267 */ 268advancedFonts.getEffectiveFont = function(script, genericFamily, callback) { 269 var pendingChanges = advancedFonts.pendingChanges; 270 var details = { script: script, genericFamily: genericFamily }; 271 chrome.fontSettings.getFont(details, function(result) { 272 var setting = {}; 273 setting.font = result.fontId; 274 setting.controllable = 275 advancedFonts.isControllableLevel(result.levelOfControl); 276 var pendingFont = 277 pendingChanges.getFont(details.script, details.genericFamily); 278 // If the setting is not controllable, we can have no pending change. 279 if (!setting.controllable) { 280 if (pendingFont != null) { 281 pendingChanges.setFont(script, genericFamily, null); 282 $('apply-settings').disabled = advancedFonts.pendingChanges.isEmpty(); 283 pendingFont = null; 284 } 285 } 286 287 // If we have a pending change, it overrides the current setting. 288 if (pendingFont != null) 289 setting.font = pendingFont; 290 291 // If we have a font, we're done. 292 if (setting.font) { 293 callback(setting.font, setting.controllable, setting.font); 294 return; 295 } 296 297 // If we're still here, we have to fallback to common script, unless this 298 // already is common script. 299 if (script == advancedFonts.COMMON_SCRIPT) { 300 callback('', setting.controllable, ''); 301 return; 302 } 303 advancedFonts.getEffectiveFont( 304 advancedFonts.COMMON_SCRIPT, 305 genericFamily, 306 callback.bind(null, setting.font, setting.controllable)); 307 }); 308}; 309 310/** 311 * Refreshes the UI controls related to a font setting. 312 * 313 * @param {{fontList: HTMLSelectElement, samples: Array.<HTMLElement>}} 314 * fontSetting The setting object (see advancedFonts.fontSettings). 315 * @param {string} font The value of the font setting. 316 * @param {boolean} controllable Whether the font setting can be controlled 317 * by this extension. 318 * @param {string} effectiveFont The font used, including fallback to Common 319 * script. 320 */ 321advancedFonts.refreshFont = function( 322 fontSetting, font, controllable, effectiveFont) { 323 for (var i = 0; i < fontSetting.samples.length; ++i) 324 fontSetting.samples[i].style.fontFamily = effectiveFont; 325 advancedFonts.setSelectedFont(fontSetting.fontList, font); 326 fontSetting.fontList.disabled = !controllable; 327}; 328 329/** 330 * Refreshes the UI controls related to a font size setting. 331 * 332 * @param {{label: HTMLElement, slider: Slider, samples: Array.<HTMLElement>}} 333 * fontSizeSetting The setting object (see advancedFonts.fontSizeSettings). 334 * @param size The value of the font size setting. 335 * @param controllable Whether the setting can be controlled by this extension. 336 */ 337advancedFonts.refreshFontSize = function(fontSizeSetting, size, controllable) { 338 fontSizeSetting.label.textContent = 'Size: ' + size + 'px'; 339 advancedFonts.setFontSizeSlider(fontSizeSetting.slider, size, controllable); 340 for (var i = 0; i < fontSizeSetting.samples.length; ++i) 341 fontSizeSetting.samples[i].style.fontSize = size + 'px'; 342}; 343 344/** 345 * Refreshes all UI controls to reflect the current settings, including pending 346 * changes. 347 */ 348advancedFonts.refresh = function() { 349 var script = advancedFonts.getSelectedScript(); 350 var sample; 351 if (advancedFonts.SAMPLE_TEXTS[script]) 352 sample = advancedFonts.SAMPLE_TEXTS[script]; 353 else 354 sample = advancedFonts.SAMPLE_TEXTS[advancedFonts.COMMON_SCRIPT]; 355 var sampleTexts = document.querySelectorAll('.sample-text-span'); 356 for (var i = 0; i < sampleTexts.length; i++) 357 sampleTexts[i].textContent = sample; 358 359 var setting; 360 var callback; 361 for (var genericFamily in advancedFonts.fontSettings) { 362 setting = advancedFonts.fontSettings[genericFamily]; 363 callback = advancedFonts.refreshFont.bind(null, setting); 364 advancedFonts.getEffectiveFont(script, genericFamily, callback); 365 } 366 367 for (var fontSizeKey in advancedFonts.fontSizeSettings) { 368 setting = advancedFonts.fontSizeSettings[fontSizeKey]; 369 callback = advancedFonts.refreshFontSize.bind(null, setting); 370 advancedFonts.getEffectiveFontSize(fontSizeKey, callback); 371 } 372 373 $('apply-settings').disabled = advancedFonts.pendingChanges.isEmpty(); 374}; 375 376/** 377 * @return {string} The currently selected script code. 378 */ 379advancedFonts.getSelectedScript = function() { 380 var scriptList = $('scriptList'); 381 return scriptList.options[scriptList.selectedIndex].value; 382}; 383 384/** 385 * @param {HTMLSelectElement} fontList The <select> containing a list of fonts. 386 * @return {string} The currently selected value of |fontList|. 387 */ 388advancedFonts.getSelectedFont = function(fontList) { 389 return fontList.options[fontList.selectedIndex].value; 390}; 391 392/** 393 * Populates the font lists. 394 * @param {Array.<{fontId: string, displayName: string>} fonts The list of 395 * fonts on the system. 396 */ 397advancedFonts.populateFontLists = function(fonts) { 398 for (var genericFamily in advancedFonts.fontSettings) { 399 var list = advancedFonts.fontSettings[genericFamily].fontList; 400 401 // Add a special item to indicate fallback to the non-per-script 402 // font setting. The Font Settings API uses the empty string to indicate 403 // fallback. 404 var defaultItem = document.createElement('option'); 405 defaultItem.value = ''; 406 defaultItem.text = '(Use default)'; 407 list.add(defaultItem); 408 409 for (var i = 0; i < fonts.length; ++i) { 410 var item = document.createElement('option'); 411 item.value = fonts[i].fontId; 412 item.text = fonts[i].displayName; 413 list.add(item); 414 } 415 } 416 advancedFonts.refresh(); 417}; 418 419/** 420 * Handles change events on a <select> element for a font setting. 421 * @param {string} genericFamily The generic family for the font setting. 422 * @param {Event} event The change event. 423 */ 424advancedFonts.handleFontListChange = function(genericFamily, event) { 425 var script = advancedFonts.getSelectedScript(); 426 var font = advancedFonts.getSelectedFont(event.target); 427 428 advancedFonts.pendingChanges.setFont(script, genericFamily, font); 429 advancedFonts.refresh(); 430}; 431 432/** 433 * Sets the selected value of |fontList| to |fontId|. 434 * @param {HTMLSelectElement} fontList The <select> containing a list of fonts. 435 * @param {string} fontId The font to set |fontList|'s selection to. 436 */ 437advancedFonts.setSelectedFont = function(fontList, fontId) { 438 var script = advancedFonts.getSelectedScript(); 439 var i; 440 for (i = 0; i < fontList.length; i++) { 441 if (fontId == fontList.options[i].value) { 442 fontList.selectedIndex = i; 443 break; 444 } 445 } 446 if (i == fontList.length) { 447 console.warn("font '" + fontId + "' for " + fontList.id + ' for ' + 448 script + ' is not on the system'); 449 } 450}; 451 452/** 453 * Handles change events on a font size slider. 454 * @param {string} fontSizeKey The key for the font size setting whose slider 455 * changed. See PendingChanges.getFont. 456 * @param {string} value The new value of the slider. 457 */ 458advancedFonts.handleFontSizeSliderChange = function(fontSizeKey, value) { 459 var pixelSize = parseInt(value); 460 if (!isNaN(pixelSize)) { 461 advancedFonts.pendingChanges.setFontSize(fontSizeKey, pixelSize); 462 advancedFonts.refresh(); 463 } 464}; 465 466/** 467 * @param {string} levelOfControl The level of control string for a setting, 468 * as returned by the Font Settings Extension API. 469 * @return {boolean} True if |levelOfControl| signifies that the extension can 470 * control the setting; otherwise, returns false. 471 */ 472advancedFonts.isControllableLevel = function(levelOfControl) { 473 return levelOfControl == 'controllable_by_this_extension' || 474 levelOfControl == 'controlled_by_this_extension'; 475}; 476 477/* 478 * Updates the specified font size slider's value and enabled property. 479 * @param {Slider} slider The slider for a font size setting. 480 * @param {number} size The value to set the slider to. 481 * @param {boolean} enabled Whether to enable or disable the slider. 482 */ 483advancedFonts.setFontSizeSlider = function(slider, size, enabled) { 484 if (slider.getValue() != size) 485 slider.setValue(size); 486 var inputElement = slider.getInput(); 487 if (enabled) { 488 inputElement.parentNode.classList.remove('disabled'); 489 inputElement.disabled = false; 490 } else { 491 inputElement.parentNode.classList.add('disabled'); 492 inputElement.disabled = true; 493 } 494}; 495 496/** 497 * Initializes the UI control elements related to the font size setting 498 * |fontSizeKey| and registers listeners for the user adjusting its slider and 499 * the setting changing on the browser-side. 500 * @param {string} fontSizeKey The key for font size setting. See 501 * PendingChanges.getFont(). 502 */ 503advancedFonts.initFontSizeSetting = function(fontSizeKey) { 504 var fontSizeSettings = advancedFonts.fontSizeSettings; 505 var setting = fontSizeSettings[fontSizeKey]; 506 var label = setting.label; 507 var samples = setting.samples; 508 509 setting.slider = new Slider( 510 setting.sliderContainer, 511 0, 512 setting.minValue, 513 setting.maxValue, 514 advancedFonts.handleFontSizeSliderChange.bind(null, fontSizeKey) 515 ); 516 517 var slider = setting.slider; 518 setting.getter({}, function(details) { 519 var size = details.pixelSize.toString(); 520 var controllable = advancedFonts.isControllableLevel( 521 details.levelOfControl); 522 advancedFonts.setFontSizeSlider(slider, size, controllable); 523 for (var i = 0; i < samples.length; i++) 524 samples[i].style.fontSize = size + 'px'; 525 }); 526 fontSizeSettings[fontSizeKey].onChanged.addListener(advancedFonts.refresh); 527}; 528 529/** 530 * Clears the font settings for the specified script. 531 * @param {string} script The script code. 532 */ 533advancedFonts.clearSettingsForScript = function(script) { 534 advancedFonts.pendingChanges.clearOneScript(script); 535 for (var i = 0; i < advancedFonts.FAMILIES.length; i++) { 536 chrome.fontSettings.clearFont({ 537 script: script, 538 genericFamily: advancedFonts.FAMILIES[i] 539 }); 540 } 541}; 542 543/** 544 * Clears all font and font size settings. 545 */ 546advancedFonts.clearAllSettings = function() { 547 advancedFonts.pendingChanges.clear(); 548 for (var i = 0; i < advancedFonts.scripts.length; i++) 549 advancedFonts.clearSettingsForScript(advancedFonts.scripts[i].scriptCode); 550 chrome.fontSettings.clearDefaultFixedFontSize(); 551 chrome.fontSettings.clearDefaultFontSize(); 552 chrome.fontSettings.clearMinimumFontSize(); 553}; 554 555/** 556 * Closes the overlay. 557 */ 558advancedFonts.closeOverlay = function() { 559 $('overlay-container').hidden = true; 560}; 561 562/** 563 * Initializes apply and reset buttons. 564 */ 565advancedFonts.initApplyAndResetButtons = function() { 566 var applyButton = $('apply-settings'); 567 applyButton.addEventListener('click', function() { 568 advancedFonts.pendingChanges.apply(); 569 advancedFonts.refresh(); 570 }); 571 572 var overlay = $('overlay-container'); 573 cr.ui.overlay.globalInitialization(); 574 cr.ui.overlay.setupOverlay(overlay); 575 overlay.addEventListener('cancelOverlay', advancedFonts.closeOverlay); 576 577 $('reset-this-script-button').onclick = function(event) { 578 var scriptList = $('scriptList'); 579 var scriptName = scriptList.options[scriptList.selectedIndex].text; 580 $('reset-this-script-overlay-dialog-content').innerText = 581 'Are you sure you want to reset settings for ' + scriptName + 582 ' script?'; 583 584 $('overlay-container').hidden = false; 585 $('reset-this-script-overlay-dialog').hidden = false; 586 $('reset-all-scripts-overlay-dialog').hidden = true; 587 }; 588 $('reset-this-script-ok').onclick = function(event) { 589 advancedFonts.clearSettingsForScript(advancedFonts.getSelectedScript()); 590 advancedFonts.closeOverlay(); 591 advancedFonts.refresh(); 592 }; 593 $('reset-this-script-cancel').onclick = advancedFonts.closeOverlay; 594 595 $('reset-all-button').onclick = function(event) { 596 $('overlay-container').hidden = false; 597 $('reset-all-scripts-overlay-dialog').hidden = false; 598 $('reset-this-script-overlay-dialog').hidden = true; 599 }; 600 $('reset-all-ok').onclick = function(event) { 601 advancedFonts.clearAllSettings(); 602 advancedFonts.closeOverlay(); 603 advancedFonts.refresh(); 604 }; 605 $('reset-all-cancel').onclick = advancedFonts.closeOverlay; 606}; 607 608/** 609 * Best guess for system fonts, taken from the IDS_WEB_FONT_FAMILY strings in 610 * Chrome. 611 * TODO: The font should be localized like Chrome does. 612 * @const 613 */ 614advancedFonts.systemFonts = { 615 cros: 'Noto Sans UI, sans-serif', 616 linux: 'Ubuntu, sans-serif', 617 mac: 'Lucida Grande, sans-serif', 618 win: 'Segoe UI, Tahoma, sans-serif', 619 unknown: 'sans-serif' 620}; 621 622/** 623 * @return {string} The platform this extension is running on. 624 */ 625advancedFonts.getPlatform = function() { 626 var ua = window.navigator.appVersion; 627 if (ua.indexOf('Win') != -1) return 'win'; 628 if (ua.indexOf('Mac') != -1) return 'mac'; 629 if (ua.indexOf('Linux') != -1) return 'linux'; 630 if (ua.indexOf('CrOS') != -1) return 'cros'; 631 return 'unknown'; 632}; 633 634/** 635 * Chrome settings tries to use the system font. So does this extension. 636 */ 637advancedFonts.useSystemFont = function() { 638 document.body.style.fontFamily = 639 advancedFonts.systemFonts[advancedFonts.getPlatform()]; 640}; 641 642/** 643 * Sorts the list of script codes by scriptName. Someday this extension will 644 * have localized script names, so the order will depend on locale. 645 */ 646advancedFonts.sortScripts = function() { 647 var i; 648 var scripts = advancedFonts.scripts; 649 for (i = 0; i < scripts; ++i) { 650 if (scripts[i].scriptCode == advancedFonts.COMMON_SCRIPT) 651 break; 652 } 653 var defaultScript = scripts.splice(i, 1)[0]; 654 655 scripts.sort(function(a, b) { 656 if (a.scriptName > b.scriptName) 657 return 1; 658 if (a.scriptName < b.scriptName) 659 return -1; 660 return 0; 661 }); 662 663 scripts.unshift(defaultScript); 664}; 665 666/** 667 * Initializes UI controls for font settings. 668 */ 669advancedFonts.initFontControls = function() { 670 advancedFonts.fontSettings = { 671 standard: { 672 fontList: $('standardFontList'), 673 samples: [$('standardFontSample'), $('minFontSample')] 674 }, 675 serif: { 676 fontList: $('serifFontList'), 677 samples: [$('serifFontSample')] 678 }, 679 sansserif: { 680 fontList: $('sansSerifFontList'), 681 samples: [$('sansSerifFontSample')] 682 }, 683 fixed: { 684 fontList: $('fixedFontList'), 685 samples: [$('fixedFontSample')] 686 } 687 }; 688 689 for (var genericFamily in advancedFonts.fontSettings) { 690 var list = advancedFonts.fontSettings[genericFamily].fontList; 691 list.addEventListener( 692 'change', advancedFonts.handleFontListChange.bind(list, genericFamily)); 693 } 694 chrome.fontSettings.onFontChanged.addListener(advancedFonts.refresh); 695 chrome.fontSettings.getFontList(advancedFonts.populateFontLists); 696}; 697 698/** 699 * Initializes UI controls for font size settings. 700 */ 701advancedFonts.initFontSizeControls = function() { 702 advancedFonts.fontSizeSettings = { 703 defaultFontSize: { 704 sliderContainer: $('defaultFontSizeSliderContainer'), 705 minValue: 6, 706 maxValue: 50, 707 samples: [ 708 $('standardFontSample'), $('serifFontSample'), $('sansSerifFontSample') 709 ], 710 label: $('defaultFontSizeLabel'), 711 getter: chrome.fontSettings.getDefaultFontSize, 712 onChanged: chrome.fontSettings.onDefaultFontSizeChanged 713 }, 714 defaultFixedFontSize: { 715 sliderContainer: $('defaultFixedFontSizeSliderContainer'), 716 minValue: 6, 717 maxValue: 50, 718 samples: [$('fixedFontSample')], 719 label: $('fixedFontSizeLabel'), 720 getter: chrome.fontSettings.getDefaultFixedFontSize, 721 onChanged: chrome.fontSettings.onDefaultFixedFontSizeChanged 722 }, 723 minFontSize: { 724 sliderContainer: $('minFontSizeSliderContainer'), 725 minValue: 6, 726 maxValue: 24, 727 samples: [$('minFontSample')], 728 label: $('minFontSizeLabel'), 729 getter: chrome.fontSettings.getMinimumFontSize, 730 onChanged: chrome.fontSettings.onMinimumFontSizeChanged 731 } 732 }; 733 734 for (var fontSizeKey in advancedFonts.fontSizeSettings) 735 advancedFonts.initFontSizeSetting(fontSizeKey); 736}; 737 738/** 739 * Initializes the list of scripts. 740 */ 741advancedFonts.initScriptList = function() { 742 var scriptList = $('scriptList'); 743 advancedFonts.sortScripts(); 744 var scripts = advancedFonts.scripts; 745 for (var i = 0; i < scripts.length; i++) { 746 var script = document.createElement('option'); 747 script.value = scripts[i].scriptCode; 748 script.text = scripts[i].scriptName; 749 scriptList.add(script); 750 } 751 scriptList.selectedIndex = 0; 752 scriptList.addEventListener('change', advancedFonts.refresh); 753}; 754 755/** 756 * Initializes the extension. 757 */ 758advancedFonts.init = function() { 759 advancedFonts.useSystemFont(); 760 761 advancedFonts.initFontControls(); 762 advancedFonts.initFontSizeControls(); 763 advancedFonts.initScriptList(); 764 765 advancedFonts.initApplyAndResetButtons(); 766}; 767 768document.addEventListener('DOMContentLoaded', advancedFonts.init); 769