1 <?xml version="1.0" encoding="UTF-8"?>
2 <?xml-stylesheet href="xhtml-default.css" type="text/css" media="screen, aural, print" ?>
3 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN"
4 "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">
5 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" >
7 <link rel="shortcut icon" href="sgc.png" />
8 <link rel="icon" sizes="192x192" href="sgc.png" />
9 <link rel="manifest" href="manifest.json" />
10 <meta name="viewport" content="width=device-width" />
11 <meta name="mobile-web-app-capable" content="yes" />
12 <meta http-equiv="Content-Language" content="en" />
13 <title lang="en" xml:lang="en" dir="ltr">Settings - SpeakGoodChinese</title>
16 background-image: url("Background.png");
17 background-color: rgb(250,250,250);
18 background-repeat: no-repeat;
19 background-position: center center;
20 background-attachment: fixed;}
27 text-overflow: clip clip;
30 cursor:pointer; /*forces the cursor to change to a hand when the button is hovered*/
32 font: bold 3vmin "Helvetica";
33 background-color: rgb(220,220,220);
35 -webkit-box-shadow: inset 0px 1px 0px #3e9cbf, 0px 5px 0px 0px #205c73, 0px 10px 5px #999;
36 -moz-box-shadow: inset 0px 1px 0px #3e9cbf, 0px 5px 0px 0px #205c73, 0px 10px 5px #999;
37 box-shadow: inset 0px 1px 0px #3e9cbf, 0px 5px 0px 0px #205c73, 0px 10px 5px #999;
39 /*give the corners a small curve*/
40 -moz-border-radius: 7px;
41 -webkit-border-radius: 7px;
47 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
49 <script type="text/javascript" src="internationalization_tables.js" ></script>
50 <script type="text/javascript" src="wordlists.js" ></script>
51 <script type="text/javascript" src="wordlists_plus.js" ></script>
52 <script type="text/javascript" src="audioProcessing.js" ></script>
53 <script type="text/javascript" src="toneprot.js" ></script>
54 <script type="text/javascript" src="jszip.min.js" defer="true" ></script>
56 <body onfocus="if(localStorage.sgc3_reload == '1'){location.reload();}else{load_SGC3_settings ()}; " onblur="store_SGC3_settings ();" onunload="store_SGC3_settings (); localStorage.removeItem('sgc3_settingsWindow'); if(creditsWindow)creditsWindow.close(); " >
59 Copyright (C) 2016 R.J.J.H. van Son (r.j.j.h.vanson@gmail.com)
61 This program is free software; you can redistribute it and/or
62 modify it under the terms of the GNU General Public License
63 as published by the Free Software Foundation; either version 2
64 of the License, or (at your option) any later version.
66 This program is distributed in the hope that it will be useful,
67 but WITHOUT ANY WARRANTY; without even the implied warranty of
68 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
69 GNU General Public License for more details.
71 You can find a copy of the GNU General Public License at
72 http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
74 <div id='headertitle' style='position: center; font-size: 3vmin; '>
75 <h2 style='text-align: center' id="settingsTitle">
76 SpeakGoodChinese 3 <br />
77 Shuō hǎo Zhōngwén <br />
81 <div style="position: fixed; color: black; bottom: 70%; left: 5%; height: 8%; width: 17%; font: 'Helvetica'; font-size: 3vmin; text-align: center " id="LanguageCaption">---</div>
82 <select id="Language" style="position: fixed; color: black; bottom: 65%; left: 5%; height: 8%; width: 17%; font: 'Helvetica'; font-size: 3vmin ; background-color: rgb(220,220,220); " onchange="sgc3_settings.language = change_configLanguage(); store_SGC3_settings (); " >
83 <option value="---" ><span id="LanguageCaption2" title="" >---</span></option>
86 <a href="" id="HiddenDownloadLink" name="Download" style="display:none" title="Download" ></a>
87 <div style="position: fixed; color: black; bottom: 70%; left: 25%; height: 8%; width: 17%; font: 'Helvetica'; font-size: 3vmin; text-align: center " id="RegisterCaption">---</div>
88 <select id="Register" style="position: fixed; color: black; bottom: 65%; left: 25%; height: 8%; width: 17%; font: 'Helvetica'; font-size: 3vmin; background-color: rgb(220,220,220); " onchange="sgc3_settings.register = getRegister(); store_SGC3_settings (); " >
89 <option value="---" ><span id="RegisterCaption2" title="" >---</span></option>
92 <div title=""><button type="button" style="color: black; bottom: 65%; left: 45%; height: 8%;" id="LocalSynthesis" ><span id="Synthesis_eSpeak" >Synthesis</span></button></div>
94 <div style="background-color: rgb(220,220,220); position: fixed; color: black; bottom: 54%; left: 25%; height: 8%; width: 17%; font-size: 3vmin; text-align: center; " id="LocalStrict" title="">
95 <span id="Recognition" style="vertical-align: middle; " >---</span><br />
96 <span id="StrictPost" style="vertical-align: middle; " >---</span>
97 <select id="Strict" style="font-size: 2vmin; vertical-align: middle; background-color: rgb(220,220,220);" onchange="sgc3_settings.strict = getStrict(); store_SGC3_settings (); ">
98 <option value="0">0</option><option value="1">1</option><option value="2">2</option><option value="3">3</option>
102 <div title=""><div style="background-color: rgb(220,220,220); position: fixed; color: black; bottom: 54%; left: 45%; height: 8%; width: 17%; font-size: 3vmin; text-align: center; " id="LocalRecordingTime" title="">
103 <span id="RecordingTimeCaption" style="vertical-align: middle; " >---</span><br />
104 <select id="RecordingTime" style="font-size: 2vmin; vertical-align: middle; background-color: rgb(220,220,220);" onchange="sgc3_settings.recSecs = getRecordingTime(); store_SGC3_settings (); ">
105 <option value="2">2</option><option value="3">3</option><option value="4">4</option><option value="5">5</option><option value="10">10</option>
107 <span id="RecordingTimePost" style="vertical-align: middle; " >---</span>
110 <div title=""><button type="button" style="color: black; bottom: 65%; left: 65%; height: 8%" id="ShuffleListsButton" ><span id="ShuffleLists" >Synthesis</span></button></div>
111 <div title=""><button type="button" style="color: black; bottom: 55%; left: 65%; height: 8%" id="DisplayNumbersButton" ><span id="DisplayNumbers" >---</span></button></div>
112 <div title=""><button type="button" style="color: black; bottom: 45%; left: 65%; height: 8%" id="DisplayPinyinButton" ><span id="DisplayPinyin" >---</span></button></div>
113 <div title=""><button type="button" style="color: black; bottom: 35%; left: 65%; height: 8%" id="DisplayCharButton" ><span id="DisplayChar" >---</span></button></div>
114 <div title=""><button type="button" style="color: black; bottom: 25%; left: 65%; height: 8%" id="DisplayTransButton" ><span id="DisplayTrans" >---</span></button></div>
116 <div title=""><button type="button" style="color: black; bottom: 62%; left: 85%; height: 14%; width: 10%" id="CreditsButton" onclick="creditsWindow = window.open('SpeakGoodChinese3_Credits.xml', '_blank');" ><span id="Credits" >---</span></button></div>
118 <input type="file" id="HiddenOpenWordlist" name="OpenWordlist" style="display:none" onchange='openWordlist (this.files); this.value="";' title="OpenWord" />
119 <div title=""><button type="button" style="color: black; bottom: 5%; left: 5%; height: 8%;" id="LocalOpenWordlist" ><span id="OpenWordlist" >---</span></button></div>
120 <div style="position: fixed; color: gray; bottom: 13%; left: 20%; height: 5%; width: 27%; font: 'Helvetica'; font-size: 3vmin; text-align: center " id="CurrentWordlist">---</div>
121 <div title=""><button type="button" style="color: black; bottom: 5%; left: 25%; height: 8%;" id="DeleteWordlistButton" ><span id="DeleteWordlist" >---</span></button></div>
123 <input type="file" id="HiddenImportAudio" name="ImportAudio" style="display:none" onchange='importAudio (this.files); this.value="";' title="OpenWord" />
124 <div title=""><button type="button" style="color: black; bottom: 5%; right: 43%; height: 8%; width: 8% " id="LocalDeleteAudio" ><span id="DeleteAudio" style="font-size: 2vmin;" >Delete</span></button></div>
125 <div title=""><button type="button" style="color: black; bottom: 5%; right: 33%; height: 8%; width: 8% " id="LocalImportAudio" ><span id="ImportAudio" style="font-size: 2vmin;" >Import</span></button></div>
126 <div title=""><button type="button" style="color: black; bottom: 5%; right: 23%; height: 8%; width: 8% " id="ExportAudioButton" ><span id="ExportAudio" style="font-size: 2vmin;" >Export</span></button></div>
128 <div title=""><button type="button" style="color: black; bottom: 40%; right: 5%; height: 8%; width: 10%; vertical-align: top; " id="ListPerfButton" ><span id="ListPerf" style="font-size: 2vmin;" >---</span></button></div>
129 <div title=""><button type="button" style="color: black; bottom: 25%; right: 5%; height: 14%; width: 10%; vertical-align: top; " id="SaveAudioButton" >
130 <div style="color: gray; font-size: 6vmin; text-align: center; vertical-align: top; " id="SaveAudioLight">●</div>
131 <span id="SaveAudio" >---</span></button>
134 <div style="position: fixed; color: black; bottom: 10%; right: 5%; height: 8%; width: 17%; font: 'Helvetica'; font-size: 4vmin; text-align: center " id="AudioNameCaption">---</div>
135 <select id="AudioName" style="position: fixed; color: black; bottom: 5%; right: 5%; height: 8%; width: 17%; font: 'Helvetica'; font-size: 3vmin; background-color: rgb(220,220,220); " onchange="changeCollection(this.selectedIndex); store_SGC3_settings (); " >
136 <option value="NewCollection" ><span id="NewCollection" title="" >New</span></option>
139 <script type="text/javascript">
141 // Install the service worker for offline use
142 if ('serviceWorker' in navigator) {
143 navigator.serviceWorker.register('sw.js').then(function(registration) {
144 // Registration was successful
145 console.log('ServiceWorker registration successful with scope: ', registration.scope);
146 }).catch(function(err) {
147 // registration failed :(
148 console.log('ServiceWorker registration failed: ', err);
153 var mainWindow = localStorage.sgc3_mainWindow;
156 var localSynthesis = document.getElementById('LocalSynthesis');
157 var localRecordingTime = document.getElementById('RecordingTime');
158 var localStrict = document.getElementById('Strict');
160 var shuffleListsButton = document.getElementById('ShuffleListsButton');
161 var displayNumbersButton = document.getElementById('DisplayNumbersButton');
162 var displayPinyinButton = document.getElementById('DisplayPinyinButton');
163 var displayCharButton = document.getElementById('DisplayCharButton');
164 var displayTransButton = document.getElementById('DisplayTransButton');
165 var hiddenDownloadLink = document.getElementById('HiddenDownloadLink');
166 var exportAudioButton = document.getElementById('ExportAudioButton');
167 var saveAudioButton = document.getElementById('SaveAudioButton');
168 var listPerfButton = document.getElementById('ListPerfButton');
169 var openWordlistButton = document.getElementById('LocalOpenWordlist');
170 var openAudioButton = document.getElementById('LocalOpenAudio');
171 var deleteAudioButton = document.getElementById('LocalDeleteAudio');
172 var hiddenImportAudio = document.getElementById('HiddenImportAudio');
173 var importAudioButton = document.getElementById('LocalImportAudio');
174 importAudioButton.onclick = function () {
175 document.getElementById('HiddenImportAudio').click();
177 // Remove collection after confirm
178 deleteAudioButton.onclick = function () {
179 var confirmText = config_tables[sgc3_settings.language].DeleteWordlistConfirm[1];
180 var canBeDeleted = confirm(confirmText);
182 // Note that the currentCollection is reset AFTER the removal
183 removeCollection (sgc3_settings.currentCollection, function () {
184 localStorage.sgc3_currentCollection = JSON.stringify("SGC3");
185 load_SGC3_settings ();
187 sgc3_settings.currentCollection = "SGC3";
188 load_SGC3_settings ();
191 var hiddenOpenWordlist = document.getElementById('HiddenOpenWordlist');
192 openWordlistButton.onclick = function () {
193 document.getElementById('HiddenOpenWordlist').click();
195 var deleteWordlistButton = document.getElementById('DeleteWordlistButton');
196 var currentWordlistName = document.getElementById('CurrentWordlist');
199 localStorage.removeItem("sgc3_reload");
200 var sgc3_settings = {
206 register: 249, // Must match Register_249 Id
207 wordList: "20 basic tone combinations",
209 synthesis_eSpeak: false,
211 personalWordlists: [],
212 displayNumbers: false,
217 examplesDatabaseName: "SGC3 examples",
218 audioDatabaseName: "SGC3 audio",
219 currentCollection: "SGC3",
226 function store_SGC3_settings () {
227 sgc3_settings.settingsRead = true;
229 for (x in sgc3_settings) {
230 localStorage["sgc3_"+x] = JSON.stringify(sgc3_settings[x]);
234 function load_SGC3_settings () {
235 for (x in sgc3_settings) {
236 // For some reason, parsing the language goes wrong
237 if (localStorage["sgc3_"+x]) {
238 sgc3_settings[x] = JSON.parse(localStorage["sgc3_"+x]);
242 set_configLanguage (sgc3_settings.language);
243 setRegister (sgc3_settings.register);
244 setRecordingTime (sgc3_settings.recSecs);
245 setStrict (sgc3_settings.strict);
247 localRecordingTime.value = sgc3_settings.recSecs;
248 localStrict.value = sgc3_settings.strict;
249 sgc3_settings.settingsRead = true;
250 wordlists = combineWordlistLists(global_wordlists, sgc3_settings.personalWordlists);
251 get_wordlist (sgc3_settings.wordList);
252 currentWordlistName.textContent = sgc3_settings.wordList;
253 currentWordlistName.style.color = "gray";
254 document.getElementById('SaveAudioLight').style.color = sgc3_settings.saveAudio ? 'blue' : 'gray';
255 document.getElementById('SaveAudio').style.color = sgc3_settings.saveAudio ? 'blue' : 'black';
256 deleteWordlistButton.style.color = "gray";
257 deleteWordlistButton.disabled = true;
258 if (wordlistExist (sgc3_settings.personalWordlists, sgc3_settings.wordList)) {
259 currentWordlistName.style.color = "blue";
260 deleteWordlistButton.style.color = "black";
261 deleteWordlistButton.disabled = false;
263 examplesDatabaseName = sgc3_settings.examplesDatabaseName;
264 audioDatabaseName = sgc3_settings.audioDatabaseName;
265 add_collections_names_to_select ();
267 // Reset the language
268 if (!sgc3_settings.language) {
269 sgc3_settings.language = (userLanguage) ? userLanguage : "EN";
273 // Initialize to stored settings
274 load_SGC3_settings ();
276 // Set language (make that selectable)
277 if (!sgc3_settings.language) {
278 sgc3_settings.language = (userLanguage) ? userLanguage : "EN";
280 set_configLanguage (sgc3_settings.language);
283 if (sgc3_settings.shuffleLists) {
284 shuffleListsButton.style.color = "red";
285 shuffleListsButton.background = "rgb(256,245,245)";
288 shuffleListsButton.onclick = function () {
289 if (! sgc3_settings.shuffleLists) {
290 sgc3_settings.shuffleLists = true;
291 shuffleListsButton.style.color = "red";
292 shuffleListsButton.background = "rgb(232,210,210)";
294 sgc3_settings.shuffleLists = false;
295 shuffleListsButton.style.color = "black";
296 shuffleListsButton.background = "rgb(220,220,220)";
298 store_SGC3_settings ();
302 if (sgc3_settings.displayNumbers) {
303 displayNumbersButton.style.color = "red";
304 displayNumbersButton.background = "rgb(256,245,245)";
307 displayNumbersButton.onclick = function () {
308 if (! sgc3_settings.displayNumbers) {
309 sgc3_settings.displayNumbers = true;
310 displayNumbersButton.style.color = "red";
311 displayNumbersButton.background = "rgb(232,210,210)";
313 sgc3_settings.displayNumbers = false;
314 displayNumbersButton.style.color = "black";
315 displayNumbersButton.background = "rgb(220,220,220)";
317 store_SGC3_settings ();
320 if (sgc3_settings.displayPinyin) {
321 displayPinyinButton.style.color = "red";
322 displayPinyinButton.background = "rgb(256,245,245)";
325 displayPinyinButton.onclick = function () {
326 if (! sgc3_settings.displayPinyin) {
327 sgc3_settings.displayPinyin = true;
328 displayPinyinButton.style.color = "red";
329 displayPinyinButton.background = "rgb(232,210,210)";
331 sgc3_settings.displayPinyin = false;
332 displayPinyinButton.style.color = "black";
333 displayPinyinButton.background = "rgb(220,220,220)";
335 store_SGC3_settings ();
338 if (sgc3_settings.displayChar) {
339 displayCharButton.style.color = "red";
340 displayCharButton.background = "rgb(256,245,245)";
343 displayCharButton.onclick = function () {
344 if (! sgc3_settings.displayChar) {
345 sgc3_settings.displayChar = true;
346 displayCharButton.style.color = "red";
347 displayCharButton.background = "rgb(232,210,210)";
349 sgc3_settings.displayChar = false;
350 displayCharButton.style.color = "black";
351 displayCharButton.background = "rgb(220,220,220)";
353 store_SGC3_settings ();
356 if (sgc3_settings.displayTrans) {
357 displayTransButton.style.color = "red";
358 displayTransButton.background = "rgb(256,245,245)";
361 displayTransButton.onclick = function () {
362 if (! sgc3_settings.displayTrans) {
363 sgc3_settings.displayTrans = true;
364 displayTransButton.style.color = "red";
365 displayTransButton.background = "rgb(232,210,210)";
367 sgc3_settings.displayTrans = false;
368 displayTransButton.style.color = "black";
369 displayTransButton.background = "rgb(220,220,220)";
371 store_SGC3_settings ();
374 function openWordlist (files) {
376 for (var i=0; i < files.length; ++i) {
377 if (files[i].name.match(/\.(tsv|Table|csv|sgc)$/i)) {
378 wordlistFile = files[i];
379 if (wordlistFile.name.match(/\.sgc$/ig)) {
380 importExamples (wordlistFile);
382 readWordlist (wordlistFile);
388 deleteWordlistButton.onclick = function () {
389 var wordlistName = sgc3_settings.wordList;
390 var canBeDeleted = wordlistExist (sgc3_settings.personalWordlists, wordlistName);
391 var confirmText = config_tables[sgc3_settings.language].DeleteWordlistConfirm[1];
392 canBeDeleted = confirm(confirmText);
394 for(var x = sgc3_settings.personalWordlists.length - 1; x >= 0; --x) {
395 if (sgc3_settings.personalWordlists[x][0] == wordlistName) {
396 sgc3_settings.personalWordlists.splice(x, 1);
401 // Remove example audio
402 removeExamples (sgc3_settings.wordList, function () {});
405 wordlists = combineWordlistLists(global_wordlists, sgc3_settings.personalWordlists);
406 wordlistNumber = (wordlistNumber < wordlists.length) ? wordlistNumber : 0;
407 sgc3_settings.wordList = wordlists[wordlistNumber][0];
408 localStorage.sgc3_currentWord = JSON.stringify(0);
409 store_SGC3_settings ();
410 load_SGC3_settings ();
415 if (sgc3_settings.synthesis_eSpeak) {
416 localSynthesis.style.color = "red";
417 localSynthesis.background = "rgb(256,245,245)";
420 localSynthesis.onclick = function () {
421 if (!sgc3_settings.synthesis_eSpeak) {
422 sgc3_settings.synthesis_eSpeak = true;
423 localSynthesis.style.color = "red";
424 localSynthesis.background = "rgb(232,210,210)";
426 sgc3_settings.synthesis_eSpeak = false;
427 localSynthesis.style.color = "black";
428 localSynthesis.background = "rgb(220,220,220)";
430 store_SGC3_settings ();
434 saveAudioButton.onclick = function () {
435 sgc3_settings.saveAudio = ! sgc3_settings.saveAudio;
436 document.getElementById('SaveAudioLight').style.color = sgc3_settings.saveAudio ? 'blue' : 'gray';
437 document.getElementById('SaveAudio').style.color = sgc3_settings.saveAudio ? 'blue' : 'black';
438 store_SGC3_settings ();
440 // Get performance data
441 getCurrentMetaData (sgc3_settings.currentCollection, false);
444 listPerfButton.onclick = function () {
445 // Get performance data
446 getCurrentMetaData (sgc3_settings.currentCollection, function (list){var performanceRecord = objectList2performanceRecord(list); displayPerformanceRecord(performanceRecord);});
450 function displayPerformanceRecord (performanceRecord) {
452 var date = new Date().toLocaleString();
453 page += "<h1 align=CENTER>"+config_tables[sgc3_settings.language].ListPerf[0]+": "+sgc3_settings.currentCollection+"</h1>\n";
454 page += "<h4 align=CENTER>"+date+"</h4>\n";
456 if (performanceRecord) {
457 var wordLists = Object.getOwnPropertyNames(performanceRecord);
458 for (var i=0; i<wordLists.length; ++i) {
459 page += "<CENTER><table CELLPADDING='5'><tr><td colspan='9'><h3>"+wordLists[i]+"</h3></td></tr>\n";
460 var table = performanceRecord[wordLists[i]];
461 var pinyin = Object.getOwnPropertyNames(table).sort();
463 // Write out header line
465 page += "<td><h4>"+evaluation_tables[sgc3_settings.language].Pinyin+"</h4></td>";
466 page += "<td><h4>"+evaluation_tables[sgc3_settings.language].Character+"</h4></td>";
467 page += "<td><h4>"+evaluation_tables[sgc3_settings.language].Grade+"</h4></td>";
468 page += "<td><h4>"+evaluation_tables[sgc3_settings.language].Correct+"</h4></td>";
469 page += "<td><h4>"+evaluation_tables[sgc3_settings.language].Wrong+"</h4></td>";
470 page += "<td><h4>"+evaluation_tables[sgc3_settings.language].High+"</h4></td>";
471 page += "<td><h4>"+evaluation_tables[sgc3_settings.language].Low+"</h4></td>";
472 page += "<td><h4>"+evaluation_tables[sgc3_settings.language].Wide+"</h4></td>";
473 page += "<td><h4>"+evaluation_tables[sgc3_settings.language].Narrow+"</h4></td>";
474 page += "<td><h4>"+evaluation_tables[sgc3_settings.language].Level+"</h4></td>";
475 page += "<td><h4>"+evaluation_tables[sgc3_settings.language].Time+"</h4></td>";
477 // Write body of table
478 for (var p=0; p<pinyin.length; ++p){
479 page += "<td><strong>"+table[pinyin[p]].Mark+"</strong></td>";
480 page += "<td style='text-align: center' >"+table[pinyin[p]].Character+"</td>";
481 var grade = table[pinyin[p]].Grade == "-1" ? "-" : table[pinyin[p]].Grade
482 page += "<td style='text-align: center' ><em>"+grade+"</em></td>";
483 page += "<td style='text-align: center' >"+table[pinyin[p]].Correct+"</td>";
484 page += "<td style='text-align: center' >"+table[pinyin[p]].Wrong+"</td>";
485 page += "<td style='text-align: center' >"+table[pinyin[p]].High+"</td>";
486 page += "<td style='text-align: center' >"+table[pinyin[p]].Low+"</td>";
487 page += "<td style='text-align: center' >"+table[pinyin[p]].Wide+"</td>";
488 page += "<td style='text-align: center' >"+table[pinyin[p]].Narrow+"</td>";
489 page += "<td style='text-align: center' >"+table[pinyin[p]].Proficiency+"</td>";
490 page += "<td style='text-align: center; font-size: x-small' >"+table[pinyin[p]].Date+"</td>";
493 page += "</table></CENTER>\n";
495 page += "\n \n \n<Center><button type='button' onclick='window.print()' style='font: bold 3vmin \"Helvetica\";background-color: rgb(220,220,220);' >"+evaluation_tables[sgc3_settings.language].Print+"</button></Center>\n\n";
497 var windowObjectReference;
498 var strWindowFeatures = "menubar=yes,location=yes,resizable=yes,scrollbars=yes,status=yes";
499 windowObjectReference = window.open("", "", strWindowFeatures);
500 windowObjectReference.document.write(page);
501 windowObjectReference.document.title = config_tables[sgc3_settings.language].ListPerf[0]+": "+sgc3_settings.currentCollection;
504 function getTTSvoice(){
506 if (window.speechSynthesis) {
507 var voices = window.speechSynthesis.getVoices();
508 if (! voices) voices = window.speechSynthesis.getVoices();
509 for (x = 0; x < voices.length; ++x) {
510 if (voices[x].lang == "zh-CN") {
512 selectedVoice = voices[x];
516 return selectedVoice;
520 function getRecordingTime () {
521 var index = document.getElementById("RecordingTime").selectedIndex;
522 var value = document.getElementById("RecordingTime").options[index].value;
523 if (index < 0) value = 3;
527 function setRecordingTime (value) {
528 for(var x = 0; x < document.getElementById("RecordingTime").options.length; ++ x) {
529 if (document.getElementById("RecordingTime").options[x].value == value) {
530 document.getElementById("RecordingTime").selectedIndex = x;
535 function getStrict () {
536 var index = document.getElementById("Strict").selectedIndex;
537 var value = document.getElementById("Strict").options[index].value;
540 document.getElementById("Strict").selectedIndex = 1;
545 function setStrict (value) {
546 if (value < 0 || value > 3) value = 0;
547 for(var x = 0; x < document.getElementById("Strict").options.length; ++ x) {
548 if (document.getElementById("Strict").options[x].value == value) {
549 document.getElementById("Strict").selectedIndex = x;
555 function changeCollection(index) {
556 var selector = document.getElementById('AudioName');
557 var value = selector.options[index].value;
559 var promptText = config_tables[sgc3_settings.language]["NewCollection"][1];
560 var collectionName = prompt(promptText, sgc3_settings.currentCollection);
561 if (collectionName && collectionName.length > 0) sgc3_settings.currentCollection = collectionName;
563 sgc3_settings.currentCollection = value;
565 getCurrentMetaData (sgc3_settings.currentCollection, false);
568 // Construct a list of Collections
569 function addCollectionsToSelect (records) {
570 var collectionList = [];
571 if(sgc3_settings.currentCollection && sgc3_settings.currentCollection.length > 0) collectionList.push(sgc3_settings.currentCollection);
572 for (var i = 0; i<records.length; ++ i) {
573 var collection = records[i].collection;
574 if (collectionList.indexOf(collection) < 0) collectionList.push(collection);
576 collectionList.sort();
578 // First, remove old entries
579 var selector = document.getElementById('AudioName');
580 var numOptions = selector.options.length
581 for(var i = numOptions - 1; i > 0; --i) {
585 var selectedIndex = 1;
586 for(var i=0; i < collectionList.length; ++i) {
587 var lastOption = selector.options.length - 1;
588 var collectionTitle = collectionList[i];
589 var newOption = selector.options[0].cloneNode(true);
590 newOption.value = collectionTitle;
591 newOption.text = collectionTitle;
592 selector.add(newOption);
593 if (collectionTitle == sgc3_settings.currentCollection) selectedIndex = selector.options.length - 1;
595 selector.selectedIndex = selectedIndex;
598 function add_collections_names_to_select () {
600 sgc3_settings.currentCollection = JSON.parse(localStorage.sgc3_currentCollection);
601 // Get the names of the collections
602 getAllRecords (undefined, addCollectionsToSelect);
606 exportAudioButton.onclick = function (){
607 if (hiddenDownloadLink.href != "") URL.revokeObjectURL(hiddenDownloadLink.href);
608 getAllRecords (sgc3_settings.currentCollection, zipAudio);
611 var zipAudio = function (records) {
612 var zip = new JSZip();
613 // Create a file listing (also to force the whole path into the zip archive)
615 for (var i = 0; i<records.length; ++ i) {
616 text += records[i].collection+"/"+records[i].map+"/"+records[i].name+"\t"+records[i].audio.size+"\t"+records[i].date+"\n";
618 zip.file("README.txt", text, {text: true});
620 for(var i=0; i < records.length; ++i) {
621 if (records[i].collection == sgc3_settings.currentCollection) {
622 var filename = records[i].collection+"/"+records[i].map+"/"+records[i].name;
623 var blob = records[i].audio;
624 zip.file(filename, blob, {binary: true});
627 zip.generateAsync({type:"blob"})
628 .then(function(content) {
629 hiddenDownloadLink.download = sgc3_settings.currentCollection+".zip";
630 hiddenDownloadLink.href = URL.createObjectURL(content);
631 hiddenDownloadLink.click();
636 function importAudio (files) {
638 var zipReader = new FileReader();
639 zipReader.onload = function(){
640 var zip = new JSZip();
641 zip.loadAsync(zipReader.result).then(unZipAudio);
643 zipReader.onerror = function(err) {
644 console.log("Open zip file",err);
646 zipReader.readAsArrayBuffer(files[0]);
649 var unZipAudio = function (zip) {
650 var fileList = zip.folder("\/$").files;
651 var defaultCollection = Object.getOwnPropertyNames(fileList).find(function(name){return name.match(/^[^\/]+\/$/g)});
652 defaultCollection = defaultCollection.replace(/\/$/g, "");
655 // Ask whether to override the collection of the import.
656 var collectionName = defaultCollection;
658 /* Check whether collection name already "exists", ie, is in the list */
659 var selector = document.getElementById('AudioName');
660 // Note: The selector option list might not have been updated with the currentCollection yet!
661 var collectionList = [sgc3_settings.currentCollection];
662 if(selector && selector.options) {
663 var numOptions = selector.options.length;
664 for(var i=0; i < numOptions; ++i) {
665 collectionList.push(selector.options[i].text);
668 while (collectionList.indexOf(collectionName) >= 0) {
670 if(collectionName.match(/\[\d+\]$/)) {
671 num = collectionName.match(/\[\d+\]$/)[0];
672 num = num.replace(/[^0-9]/ig, "");
674 collectionName = collectionName.replace(/ \[([0-9]+)\]$/g, " ["+num+"]");
676 collectionName += " [1]";
681 * This is not accepted here anymore. But moving it out of this microtask is a problem.
682 var promptText = config_tables[sgc3_settings.language]["NewCollection"][1];
683 var newCollectionName = prompt(promptText, defaultCollection);
684 if (newCollectionName && newCollectionName.length > 0 && newCollectionName != defaultCollection) collectionName = newCollectionName;
687 // Set new current collection
688 sgc3_settings.currentCollection = collectionName;
689 localStorage.sgc3_currentCollection = JSON.stringify(sgc3_settings.currentCollection);
691 zip.forEach(function (relativePath, file){
692 if(relativePath.match(/\.(wav|flac|mp3|spx|ogg|tsv)$/ig)) {
693 var collection = collectionName;
695 var name = "Audio.wav";
696 var path = relativePath.split("/");
697 name = path[path.length - 1];
698 if (path.length > 2) {
699 map = path[path.length - 2];
700 origCollection = path[path.length - 3];
701 } else if (path.length == 2) {
702 origCollection = path[path.length - 2];
704 // Change the name of the TSV file
705 if (origCollection != collection && name == origCollection+".tsv") name = collection+".tsv";
707 var type = mimeTypes[name.match(/[^\.]+$/g)[0]];
708 zip.file(relativePath).async("arraybuffer").then(function (data) {
709 var blob = new Blob([data], {type : type});
710 addAudioBlob(collection, map, name, blob);
711 }, function(event){console.log("Error", event)});
715 // Update selection list
716 add_collections_names_to_select ();
720 function importExamples (file) {
722 var zipReader = new FileReader();
723 zipReader.onload = function(){
724 var zip = new JSZip();
725 zip.loadAsync(zipReader.result).then(unZipExamples);
727 zipReader.onerror = function(err) {
728 console.log("Open zip file",err);
730 zipReader.readAsArrayBuffer(file);
733 var unZipExamples = function (zip) {
734 var fileList = zip.folder("\/$").files;
735 var collectionName = Object.getOwnPropertyNames(fileList).find(function(name){return name.match(/^[^\/]+\/$/g)});
736 collectionName = collectionName.replace(/\/$/g, "");
738 zip.forEach(function (relativePath, file){
739 if(!relativePath.match(/\/\./ig) && relativePath.match(/\.(wav|flac|mp3|spx|ogg|tsv|csv|txt|Table)$/ig)) {
740 var wordlist = collectionName;
742 var name = "Audio.wav";
743 var path = relativePath.split("/");
744 name = path[path.length - 1];
745 wordlist = path[path.length - 2];
746 var type = mimeTypes[name.match(/[^\.]+$/g)[0]];
747 zip.file(relativePath).async("arraybuffer").then(function (data) {
748 var blob = new Blob([data], {type : type});
749 var itemName = name.replace(/\.(wav|flac|mp3|spx|ogg)$/ig, "");
750 itemName = add_missing_neutral_tones (itemName)
751 addExamplesBlob(wordlist, itemName, blob);
753 if(name.match(/^wordlist\.(tsv|csv|txt|Table)$/ig)) {
754 wordlistURL = URL.createObjectURL(blob);
756 readWordlist (wordlistURL, wordlist+(name.match(/\.[^\.]+$/g)[0]));
758 }, function(event){console.log("Error", event)});
766 setRegister (sgc3_settings.register);