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 // The scripts supported by the Font Settings Extension API. 6 var scripts = [ 7 { scriptCode: 'Zyyy', scriptName: 'Default'}, 8 { scriptCode: 'Afak', scriptName: 'Afaka'}, 9 { scriptCode: 'Arab', scriptName: 'Arabic'}, 10 { scriptCode: 'Armi', scriptName: 'Imperial Aramaic'}, 11 { scriptCode: 'Armn', scriptName: 'Armenian'}, 12 { scriptCode: 'Avst', scriptName: 'Avestan'}, 13 { scriptCode: 'Bali', scriptName: 'Balinese'}, 14 { scriptCode: 'Bamu', scriptName: 'Bamum'}, 15 { scriptCode: 'Bass', scriptName: 'Bassa Vah'}, 16 { scriptCode: 'Batk', scriptName: 'Batak'}, 17 { scriptCode: 'Beng', scriptName: 'Bengali'}, 18 { scriptCode: 'Blis', scriptName: 'Blissymbols'}, 19 { scriptCode: 'Bopo', scriptName: 'Bopomofo'}, 20 { scriptCode: 'Brah', scriptName: 'Brahmi'}, 21 { scriptCode: 'Brai', scriptName: 'Braille'}, 22 { scriptCode: 'Bugi', scriptName: 'Buginese'}, 23 { scriptCode: 'Buhd', scriptName: 'Buhid'}, 24 { scriptCode: 'Cakm', scriptName: 'Chakma'}, 25 { scriptCode: 'Cans', scriptName: 'Unified Canadian Aboriginal Syllabics'}, 26 { scriptCode: 'Cari', scriptName: 'Carian'}, 27 { scriptCode: 'Cham', scriptName: 'Cham'}, 28 { scriptCode: 'Cher', scriptName: 'Cherokee'}, 29 { scriptCode: 'Cirt', scriptName: 'Cirth'}, 30 { scriptCode: 'Copt', scriptName: 'Coptic'}, 31 { scriptCode: 'Cprt', scriptName: 'Cypriot'}, 32 { scriptCode: 'Cyrl', scriptName: 'Cyrillic'}, 33 { scriptCode: 'Cyrs', scriptName: 'Old Church Slavonic Cyrillic'}, 34 { scriptCode: 'Deva', scriptName: 'Devanagari'}, 35 { scriptCode: 'Dsrt', scriptName: 'Deseret'}, 36 { scriptCode: 'Dupl', scriptName: 'Duployan shorthand'}, 37 { scriptCode: 'Egyd', scriptName: 'Egyptian demotic'}, 38 { scriptCode: 'Egyh', scriptName: 'Egyptian hieratic'}, 39 { scriptCode: 'Egyp', scriptName: 'Egyptian hieroglyphs'}, 40 { scriptCode: 'Elba', scriptName: 'Elbasan'}, 41 { scriptCode: 'Ethi', scriptName: 'Ethiopic'}, 42 { scriptCode: 'Geok', scriptName: 'Georgian Khutsuri'}, 43 { scriptCode: 'Geor', scriptName: 'Georgian'}, 44 { scriptCode: 'Glag', scriptName: 'Glagolitic'}, 45 { scriptCode: 'Goth', scriptName: 'Gothic'}, 46 { scriptCode: 'Gran', scriptName: 'Grantha'}, 47 { scriptCode: 'Grek', scriptName: 'Greek'}, 48 { scriptCode: 'Gujr', scriptName: 'Gujarati'}, 49 { scriptCode: 'Guru', scriptName: 'Gurmukhi'}, 50 { scriptCode: 'Hang', scriptName: 'Hangul'}, 51 { scriptCode: 'Hani', scriptName: 'Han'}, 52 { scriptCode: 'Hano', scriptName: 'Hanunoo'}, 53 { scriptCode: 'Hans', scriptName: 'Simplified Han'}, 54 { scriptCode: 'Hant', scriptName: 'Traditional Han'}, 55 { scriptCode: 'Hebr', scriptName: 'Hebrew'}, 56 { scriptCode: 'Hluw', scriptName: 'Anatolian Hieroglyphs'}, 57 { scriptCode: 'Hmng', scriptName: 'Pahawh Hmong'}, 58 { scriptCode: 'Hung', scriptName: 'Old Hungarian'}, 59 { scriptCode: 'Inds', scriptName: 'Indus'}, 60 { scriptCode: 'Ital', scriptName: 'Old Italic'}, 61 { scriptCode: 'Java', scriptName: 'Javanese'}, 62 { scriptCode: 'Jpan', scriptName: 'Japanese'}, 63 { scriptCode: 'Jurc', scriptName: 'Jurchen'}, 64 { scriptCode: 'Kali', scriptName: 'Kayah Li'}, 65 { scriptCode: 'Khar', scriptName: 'Kharoshthi'}, 66 { scriptCode: 'Khmr', scriptName: 'Khmer'}, 67 { scriptCode: 'Khoj', scriptName: 'Khojki'}, 68 { scriptCode: 'Knda', scriptName: 'Kannada'}, 69 { scriptCode: 'Kpel', scriptName: 'Kpelle'}, 70 { scriptCode: 'Kthi', scriptName: 'Kaithi'}, 71 { scriptCode: 'Lana', scriptName: 'Lanna'}, 72 { scriptCode: 'Laoo', scriptName: 'Lao'}, 73 { scriptCode: 'Latf', scriptName: 'Fraktur Latin'}, 74 { scriptCode: 'Latg', scriptName: 'Gaelic Latin'}, 75 { scriptCode: 'Latn', scriptName: 'Latin'}, 76 { scriptCode: 'Lepc', scriptName: 'Lepcha'}, 77 { scriptCode: 'Limb', scriptName: 'Limbu'}, 78 { scriptCode: 'Lina', scriptName: 'Linear A'}, 79 { scriptCode: 'Linb', scriptName: 'Linear B'}, 80 { scriptCode: 'Lisu', scriptName: 'Fraser'}, 81 { scriptCode: 'Loma', scriptName: 'Loma'}, 82 { scriptCode: 'Lyci', scriptName: 'Lycian'}, 83 { scriptCode: 'Lydi', scriptName: 'Lydian'}, 84 { scriptCode: 'Mand', scriptName: 'Mandaean'}, 85 { scriptCode: 'Mani', scriptName: 'Manichaean'}, 86 { scriptCode: 'Maya', scriptName: 'Mayan hieroglyphs'}, 87 { scriptCode: 'Mend', scriptName: 'Mende'}, 88 { scriptCode: 'Merc', scriptName: 'Meroitic Cursive'}, 89 { scriptCode: 'Mero', scriptName: 'Meroitic'}, 90 { scriptCode: 'Mlym', scriptName: 'Malayalam'}, 91 { scriptCode: 'Mong', scriptName: 'Mongolian'}, 92 { scriptCode: 'Moon', scriptName: 'Moon'}, 93 { scriptCode: 'Mroo', scriptName: 'Mro'}, 94 { scriptCode: 'Mtei', scriptName: 'Meitei Mayek'}, 95 { scriptCode: 'Mymr', scriptName: 'Myanmar'}, 96 { scriptCode: 'Narb', scriptName: 'Old North Arabian'}, 97 { scriptCode: 'Nbat', scriptName: 'Nabataean'}, 98 { scriptCode: 'Nkgb', scriptName: 'Naxi Geba'}, 99 { scriptCode: 'Nkoo', scriptName: 'NKo'}, 100 { scriptCode: 'Nshu', scriptName: 'Nshu'}, 101 { scriptCode: 'Ogam', scriptName: 'Ogham'}, 102 { scriptCode: 'Olck', scriptName: 'Ol Chiki'}, 103 { scriptCode: 'Orkh', scriptName: 'Orkhon'}, 104 { scriptCode: 'Orya', scriptName: 'Oriya'}, 105 { scriptCode: 'Osma', scriptName: 'Osmanya'}, 106 { scriptCode: 'Palm', scriptName: 'Palmyrene'}, 107 { scriptCode: 'Perm', scriptName: 'Old Permic'}, 108 { scriptCode: 'Phag', scriptName: 'Phags-pa'}, 109 { scriptCode: 'Phli', scriptName: 'Inscriptional Pahlavi'}, 110 { scriptCode: 'Phlp', scriptName: 'Psalter Pahlavi'}, 111 { scriptCode: 'Phlv', scriptName: 'Book Pahlavi'}, 112 { scriptCode: 'Phnx', scriptName: 'Phoenician'}, 113 { scriptCode: 'Plrd', scriptName: 'Pollard Phonetic'}, 114 { scriptCode: 'Prti', scriptName: 'Inscriptional Parthian'}, 115 { scriptCode: 'Rjng', scriptName: 'Rejang'}, 116 { scriptCode: 'Roro', scriptName: 'Rongorongo'}, 117 { scriptCode: 'Runr', scriptName: 'Runic'}, 118 { scriptCode: 'Samr', scriptName: 'Samaritan'}, 119 { scriptCode: 'Sara', scriptName: 'Sarati'}, 120 { scriptCode: 'Sarb', scriptName: 'Old South Arabian'}, 121 { scriptCode: 'Saur', scriptName: 'Saurashtra'}, 122 { scriptCode: 'Sgnw', scriptName: 'SignWriting'}, 123 { scriptCode: 'Shaw', scriptName: 'Shavian'}, 124 { scriptCode: 'Shrd', scriptName: 'Sharada'}, 125 { scriptCode: 'Sind', scriptName: 'Khudawadi'}, 126 { scriptCode: 'Sinh', scriptName: 'Sinhala'}, 127 { scriptCode: 'Sora', scriptName: 'Sora Sompeng'}, 128 { scriptCode: 'Sund', scriptName: 'Sundanese'}, 129 { scriptCode: 'Sylo', scriptName: 'Syloti Nagri'}, 130 { scriptCode: 'Syrc', scriptName: 'Syriac'}, 131 { scriptCode: 'Syre', scriptName: 'Estrangelo Syriac'}, 132 { scriptCode: 'Syrj', scriptName: 'Western Syriac'}, 133 { scriptCode: 'Syrn', scriptName: 'Eastern Syriac'}, 134 { scriptCode: 'Tagb', scriptName: 'Tagbanwa'}, 135 { scriptCode: 'Takr', scriptName: 'Takri'}, 136 { scriptCode: 'Tale', scriptName: 'Tai Le'}, 137 { scriptCode: 'Talu', scriptName: 'New Tai Lue'}, 138 { scriptCode: 'Taml', scriptName: 'Tamil'}, 139 { scriptCode: 'Tang', scriptName: 'Tangut'}, 140 { scriptCode: 'Tavt', scriptName: 'Tai Viet'}, 141 { scriptCode: 'Telu', scriptName: 'Telugu'}, 142 { scriptCode: 'Teng', scriptName: 'Tengwar'}, 143 { scriptCode: 'Tfng', scriptName: 'Tifinagh'}, 144 { scriptCode: 'Tglg', scriptName: 'Tagalog'}, 145 { scriptCode: 'Thaa', scriptName: 'Thaana'}, 146 { scriptCode: 'Thai', scriptName: 'Thai'}, 147 { scriptCode: 'Tibt', scriptName: 'Tibetan'}, 148 { scriptCode: 'Tirh', scriptName: 'Tirhuta'}, 149 { scriptCode: 'Ugar', scriptName: 'Ugaritic'}, 150 { scriptCode: 'Vaii', scriptName: 'Vai'}, 151 { scriptCode: 'Visp', scriptName: 'Visible Speech'}, 152 { scriptCode: 'Wara', scriptName: 'Varang Kshiti'}, 153 { scriptCode: 'Wole', scriptName: 'Woleai'}, 154 { scriptCode: 'Xpeo', scriptName: 'Old Persian'}, 155 { scriptCode: 'Xsux', scriptName: 'Sumero-Akkadian Cuneiform'}, 156 { scriptCode: 'Yiii', scriptName: 'Yi'}, 157 { scriptCode: 'Zmth', scriptName: 'Mathematical Notation'}, 158 { scriptCode: 'Zsym', scriptName: 'Symbols'} 159 ]; 160 161 // The generic font families supported by the Font Settings Extension API. 162 var families = 163 ["standard", "sansserif", "serif", "fixed", "cursive", "fantasy"]; 164 165 // Mapping between font list ids and the generic family setting they 166 // represent. 167 var fontPickers = [ 168 { fontList: 'standardFontList', name: 'standard' }, 169 { fontList: 'serifFontList', name: 'serif' }, 170 { fontList: 'sansSerifFontList', name: 'sansserif' }, 171 { fontList: 'fixedFontList', name: 'fixed' } 172 ]; 173 174 // Ids of elements to contain the sample text. 175 var sampleTextDivIds = [ 176 'standardFontSample', 177 'serifFontSample', 178 'sansSerifFontSample', 179 'fixedFontSample', 180 'minFontSample' 181 ]; 182 183 // Sample texts. 184 var defaultSampleText = 'The quick brown fox jumps over the lazy dog.'; 185 var scriptSpecificSampleText = { 186 // "Cyrllic script". 187 'Cyrl': '', 188 'Hang': ' .', 189 'Hans': '', 190 'Hant': '', 191 'Jpan': '', 192 // "Khmer language". 193 'Khmr': '\u1797\u17B6\u179F\u17B6\u1781\u17D2\u1798\u17C2\u179A', 194 }; 195 196 // Definition for ScriptList. 197 cr.define('fontSettings.ui', function() { 198 const List = cr.ui.List; 199 const ListItem = cr.ui.ListItem; 200 const ListSingleSelectionModel = cr.ui.ListSingleSelectionModel; 201 202 function ScriptListItem(info) { 203 var el = cr.doc.createElement('li'); 204 el.__proto__ = ScriptListItem.prototype; 205 el.info_ = info; 206 el.decorate(); 207 return el; 208 }; 209 210 ScriptListItem.prototype = { 211 __proto__: ListItem.prototype, 212 213 decorate: function() { 214 this.textContent = this.info_.scriptName; 215 if (this.info_.scriptCode == 'Zyyy') { 216 this.style.marginBottom = '1em'; 217 } 218 } 219 }; 220 221 var ScriptList = cr.ui.define('list'); 222 ScriptList.prototype = { 223 __proto__: List.prototype, 224 225 decorate: function() { 226 List.prototype.decorate.call(this); 227 var sm = new ListSingleSelectionModel(); 228 this.selectionModel = sm; 229 this.autoExpands = true; 230 this.dataModel = new cr.ui.ArrayDataModel(scripts); 231 this.style.height = '75vh'; 232 }, 233 234 createItem: function(info) { 235 return new ScriptListItem(info); 236 } 237 }; 238 239 return { 240 ScriptList: ScriptList, 241 ScriptListItem: ScriptListItem 242 }; 243 }); 244 245 function getSelectedScript() { 246 var scriptList = document.getElementById('scriptList'); 247 return scriptList.selectedItem.scriptCode; 248 } 249 250 function getSelectedFont(fontList) { 251 return fontList.options[fontList.selectedIndex].value; 252 } 253 254 // Populates the font lists with the list of system fonts from |fonts|. 255 function populateLists(fonts) { 256 for (var i = 0; i < fontPickers.length; i++) { 257 var list = document.getElementById(fontPickers[i].fontList); 258 259 // Add special item to indicate fallback to the non-per-script 260 // font setting. The Font Settings API uses the empty string to indicate 261 // fallback. 262 var defaultItem = document.createElement('option'); 263 defaultItem.value = ''; 264 defaultItem.text = '(Use default)'; 265 list.add(defaultItem); 266 267 for (var j = 0; j < fonts.length; j++) { 268 var item = document.createElement('option'); 269 item.value = fonts[j].fontId; 270 item.text = fonts[j].displayName; 271 list.add(item); 272 } 273 } 274 275 updateFontListsForScript(); 276 } 277 278 // Returns a function that updates the font setting for |genericFamily| 279 // to match the selected value in |fontList|. It can be used as an event 280 // handler for selection changes in |fontList|. 281 function getFontChangeHandler(fontList, genericFamily) { 282 return function() { 283 var script = getSelectedScript(); 284 var font = getSelectedFont(fontList); 285 286 var details = {}; 287 details.genericFamily = genericFamily; 288 details.fontId = font; 289 details.script = script; 290 291 chrome.fontSettings.setFont(details); 292 }; 293 } 294 295 // Sets the selected value of |fontList| to |fontId|. 296 function setSelectedFont(fontList, fontId) { 297 var script = getSelectedScript(); 298 var i; 299 for (i = 0; i < fontList.length; i++) { 300 if (fontId == fontList.options[i].value) { 301 fontList.selectedIndex = i; 302 break; 303 } 304 } 305 if (i == fontList.length) { 306 console.warn("font '" + fontId + "' for " + fontList.id + ' for ' + 307 script + ' is not on the system'); 308 } 309 } 310 311 // Returns a callback function that sets the selected value of |list| to the 312 // font returned from |chrome.fontSettings.getFont|. 313 function getFontHandler(list) { 314 return function(details) { 315 setSelectedFont(list, details.fontId); 316 list.disabled = !isControllableLevel(details.levelOfControl); 317 }; 318 } 319 320 // Called when the script list selection changes. Sets the selected value of 321 // each font list to the current font setting, and updates the samples' lang 322 // so that they are shown in the current font setting. 323 function updateFontListsForScript() { 324 var script = getSelectedScript(); 325 326 for (var i = 0; i < fontPickers.length; i++) { 327 var list = document.getElementById(fontPickers[i].fontList); 328 var family = fontPickers[i].name; 329 330 var details = {}; 331 details.genericFamily = family; 332 details.script = script; 333 chrome.fontSettings.getFont(details, getFontHandler(list)); 334 } 335 336 if (typeof(scriptSpecificSampleText[script]) != 'undefined') 337 sample = scriptSpecificSampleText[script]; 338 else 339 sample = defaultSampleText; 340 for (var i = 0; i < sampleTextDivIds.length; i++) { 341 var sampleTextDiv = document.getElementById(sampleTextDivIds[i]); 342 // For font selection it's the script code that matters, not language, so 343 // just use en for lang. 344 sampleTextDiv.lang = 'en-' + script; 345 sampleTextDiv.innerText = sample; 346 } 347 } 348 349 // Returns a function to be called when the user changes the font size 350 // input element |elem|. The function calls the Font Settings Extension API 351 // function |setter| to commit the change. 352 function getFontSizeChangedFunc(elem, setter) { 353 return function() { 354 var pixelSize = parseInt(elem.value); 355 if (!isNaN(pixelSize)) { 356 setter({ pixelSize: pixelSize }); 357 } 358 } 359 } 360 361 function isControllableLevel(levelOfControl) { 362 return levelOfControl == 'controllable_by_this_extension' || 363 levelOfControl == 'controlled_by_this_extension'; 364 } 365 366 // Returns a function to be used as a listener for font size setting changed 367 // events from the Font Settings Extension API. The function updates the input 368 // element |elem| and the elements in |sampleTexts| to reflect the change. 369 function getFontSizeChangedOnBrowserFunc(elem, sampleTexts) { 370 return function(details) { 371 var size = details.pixelSize.toString(); 372 elem.value = size; 373 elem.disabled = !isControllableLevel(details.levelOfControl); 374 for (var i = 0; i < sampleTexts.length; i++) 375 document.getElementById(sampleTexts[i]).style.fontSize = size + 'px'; 376 } 377 } 378 379 // Maps the HTML <input> element with |id| to the extension API accessor 380 // functions |getter| and |setter| for a setting and onChange event |apiEvent| 381 // for the setting. Also, maps the element ids in |sampleTexts| to this setting. 382 function initFontSizePref(id, sampleTexts, getter, setter, apiEvent) { 383 var elem = document.getElementById(id); 384 getter({}, function(details) { 385 var size = details.pixelSize.toString(); 386 elem.value = size; 387 elem.disabled = !isControllableLevel(details.levelOfControl); 388 for (var i = 0; i < sampleTexts.length; i++) 389 document.getElementById(sampleTexts[i]).style.fontSize = size + 'px'; 390 }); 391 elem.addEventListener('change', getFontSizeChangedFunc(elem, setter)); 392 apiEvent.addListener(getFontSizeChangedOnBrowserFunc(elem, sampleTexts)); 393 } 394 395 function clearSettingsForScript(script) { 396 for (var i = 0; i < families.length; i++) { 397 chrome.fontSettings.clearFont({ 398 script: script, 399 genericFamily: families[i] 400 }); 401 } 402 } 403 404 function clearAllSettings() { 405 for (var i = 0; i < scripts.length; i++) 406 clearSettingsForScript(scripts[i].scriptCode); 407 408 chrome.fontSettings.clearDefaultFixedFontSize(); 409 chrome.fontSettings.clearDefaultFontSize(); 410 chrome.fontSettings.clearMinimumFontSize(); 411 } 412 413 function closeOverlay() { 414 $('overlay-container').hidden = true; 415 } 416 417 function initResetButtons() { 418 var overlay = $('overlay-container'); 419 cr.ui.overlay.globalInitialization(); 420 cr.ui.overlay.setupOverlay(overlay); 421 overlay.addEventListener('cancelOverlay', closeOverlay); 422 423 $('reset-this-script-button').onclick = function(event) { 424 var scriptName = $('scriptList').selectedItem.scriptName; 425 $('reset-this-script-overlay-dialog-content').innerText = 426 'Are you sure you want to reset settings for ' + scriptName + 427 ' script?'; 428 429 $('overlay-container').hidden = false; 430 $('reset-this-script-overlay-dialog').hidden = false; 431 $('reset-all-scripts-overlay-dialog').hidden = true; 432 } 433 $('reset-this-script-ok').onclick = function(event) { 434 clearSettingsForScript(getSelectedScript()); 435 closeOverlay(); 436 }; 437 $('reset-this-script-cancel').onclick = closeOverlay; 438 439 $('reset-all-button').onclick = function(event) { 440 $('overlay-container').hidden = false; 441 $('reset-all-scripts-overlay-dialog').hidden = false; 442 $('reset-this-script-overlay-dialog').hidden = true; 443 } 444 $('reset-all-ok').onclick = function(event) { 445 clearAllSettings(); 446 closeOverlay(); 447 } 448 $('reset-all-cancel').onclick = closeOverlay; 449 } 450 451 function init() { 452 var scriptList = document.getElementById('scriptList'); 453 fontSettings.ui.ScriptList.decorate(scriptList); 454 scriptList.selectionModel.selectedIndex = 0; 455 scriptList.selectionModel.addEventListener('change', 456 updateFontListsForScript); 457 458 // Populate the font lists. 459 chrome.fontSettings.getFontList(populateLists); 460 461 // Add change handlers to the font lists. 462 for (var i = 0; i < fontPickers.length; i++) { 463 var list = document.getElementById(fontPickers[i].fontList); 464 var handler = getFontChangeHandler(list, fontPickers[i].name); 465 list.addEventListener('change', handler); 466 } 467 468 chrome.fontSettings.onFontChanged.addListener( 469 updateFontListsForScript); 470 471 initFontSizePref( 472 'defaultFontSizeRocker', 473 ['standardFontSample', 'serifFontSample', 'sansSerifFontSample'], 474 chrome.fontSettings.getDefaultFontSize, 475 chrome.fontSettings.setDefaultFontSize, 476 chrome.fontSettings.onDefaultFontSizeChanged); 477 initFontSizePref( 478 'defaultFontSizeRange', 479 ['standardFontSample', 'serifFontSample', 'sansSerifFontSample'], 480 chrome.fontSettings.getDefaultFontSize, 481 chrome.fontSettings.setDefaultFontSize, 482 chrome.fontSettings.onDefaultFontSizeChanged); 483 initFontSizePref( 484 'defaultFixedFontSizeRocker', 485 ['fixedFontSample'], 486 chrome.fontSettings.getDefaultFixedFontSize, 487 chrome.fontSettings.setDefaultFixedFontSize, 488 chrome.fontSettings.onDefaultFixedFontSizeChanged); 489 initFontSizePref( 490 'defaultFixedFontSizeRange', 491 ['fixedFontSample'], 492 chrome.fontSettings.getDefaultFixedFontSize, 493 chrome.fontSettings.setDefaultFixedFontSize, 494 chrome.fontSettings.onDefaultFixedFontSizeChanged); 495 initFontSizePref( 496 'minFontSizeRocker', 497 ['minFontSample'], 498 chrome.fontSettings.getMinimumFontSize, 499 chrome.fontSettings.setMinimumFontSize, 500 chrome.fontSettings.onMinimumFontSizeChanged); 501 initFontSizePref( 502 'minFontSizeRange', 503 ['minFontSample'], 504 chrome.fontSettings.getMinimumFontSize, 505 chrome.fontSettings.setMinimumFontSize, 506 chrome.fontSettings.onMinimumFontSizeChanged); 507 508 initResetButtons(); 509 } 510 511 document.addEventListener('DOMContentLoaded', init); 512