- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / docs / examples / api / fontSettings / options.js
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
11 function $(id) {
12   return document.getElementById(id);
13 }
14
15 /**
16  * @namespace
17  */
18 var 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  */
25 advancedFonts.COMMON_SCRIPT = 'Zyyy';
26
27 /**
28  * The scripts supported by the Font Settings Extension API.
29  * @const
30  */
31 advancedFonts.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  */
190 advancedFonts.FAMILIES =
191     ['standard', 'sansserif', 'serif', 'fixed', 'cursive', 'fantasy'];
192
193 /**
194  * Sample texts.
195  * @const
196  */
197 advancedFonts.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  */
213 advancedFonts.pendingChanges = new PendingChanges();
214
215 /**
216  * Map from |genericFamily| to UI controls and data for its font setting.
217  */
218 advancedFonts.fontSettings = null;
219
220 /**
221  * Map from |fontSizeKey| to UI contols and data for its font size setting.
222  */
223 advancedFonts.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  */
235 advancedFonts.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  */
268 advancedFonts.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  */
321 advancedFonts.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  */
337 advancedFonts.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  */
348 advancedFonts.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  */
379 advancedFonts.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  */
388 advancedFonts.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  */
397 advancedFonts.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  */
424 advancedFonts.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  */
437 advancedFonts.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  */
458 advancedFonts.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  */
472 advancedFonts.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  */
483 advancedFonts.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  */
503 advancedFonts.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  */
533 advancedFonts.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  */
546 advancedFonts.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  */
558 advancedFonts.closeOverlay = function() {
559   $('overlay-container').hidden = true;
560 };
561
562 /**
563  * Initializes apply and reset buttons.
564  */
565 advancedFonts.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  */
614 advancedFonts.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  */
625 advancedFonts.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  */
637 advancedFonts.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  */
646 advancedFonts.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  */
669 advancedFonts.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  */
701 advancedFonts.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  */
741 advancedFonts.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  */
758 advancedFonts.init = function() {
759   advancedFonts.useSystemFont();
760
761   advancedFonts.initFontControls();
762   advancedFonts.initFontSizeControls();
763   advancedFonts.initScriptList();
764
765   advancedFonts.initApplyAndResetButtons();
766 };
767
768 document.addEventListener('DOMContentLoaded', advancedFonts.init);