Warning, /education/gcompris/src/core/Wordlist.qml is written in an unsupported language. File is not indexed.
0001 /* GCompris - Wordlist.qml
0002 *
0003 * SPDX-FileCopyrightText: 2014 Holger Kaelberer <holger.k@elberer.de>
0004 *
0005 * Authors:
0006 * Holger Kaelberer <holger.k@elberer.de>
0007 *
0008 * SPDX-License-Identifier: GPL-3.0-or-later
0009 */
0010 import QtQuick 2.12
0011 import GCompris 1.0
0012 import "core.js" as Core
0013
0014 /**
0015 * A Wordlist component loads and maintains GCompris wordlists.
0016 * @ingroup components
0017 *
0018 * It loads wordlists from json-files, validates its content and exposes
0019 * wordlists and levels to activities.
0020 *
0021 * It expects and returns the following wordlist format (UTF8 encoded):
0022 *
0023 * @code
0024 * {
0025 * "name":"default-gd",
0026 * "description":"GĂ idhlig",
0027 * "locale":"gd",
0028 * "levels":[ { "level":1,
0029 * "speed":150, <-- optional
0030 * "fallspeed":7000, <-- optional
0031 * "sublevels":10, <-- optional
0032 * "words":["a","jim", "beam", ... ]}, <-- mandatory
0033 * { "level":2, ... }
0034 * ]
0035 * }
0036 * @endcode
0037 *
0038 * @inherit QtQuick.Item
0039 * @sa JsonParser
0040 */
0041 Item {
0042 id: wordlist
0043
0044 /**
0045 * type:string
0046 * Default filename to be used if the language specific wordlist file could
0047 * not be loaded.
0048 * Default is emtpy.
0049 */
0050 property string defaultFilename: ""
0051
0052 /**
0053 * type:bool
0054 * Whether to automatically fallback to the default filename if the
0055 * language specific wordlist file could not be loaded.
0056 * Default is true.
0057 */
0058 property bool useDefault: true
0059
0060 /**
0061 * type:string
0062 * Name of the file to load the language specific wordlist from.
0063 * Default is empty. Can also be passed directly in loadFromFile().
0064 * If set in the QML definition, the wordlist is autoloaded onCompleted.
0065 */
0066 property string filename: ""
0067
0068 /**
0069 * type:object
0070 * Complete Wordlist content loaded. You probably want to use one of the
0071 * convenience accessors like getLevelWordList().
0072 * Default is empty.
0073 */
0074 property var wordList: ({})
0075
0076 /// @cond INTERNAL_DOCS
0077
0078 property var randomWordList: []
0079 property int maxLevel: 0
0080
0081 /// @endcond
0082
0083 /**
0084 * Emitted if an error occurs.
0085 * @param msg Error message.
0086 */
0087 signal error(string msg);
0088
0089 /**
0090 * Load Wordlist from JSON Object.
0091 *
0092 * @param type:object levels to load wordlist from.
0093 */
0094 function loadFromJSON(levels) {
0095 wordList = {levels: levels};
0096 maxLevel = wordList.levels.length;
0097 return wordList;
0098 }
0099
0100 /**
0101 * Load Wordlist from file @p fname.
0102 *
0103 * @param type:string fname Filename to load wordlist from.
0104 */
0105 function loadFromFile(fname) {
0106 filename = fname;
0107 var from;
0108 maxLevel = 0
0109 wordList = parser.parseFromUrl(filename, validateWordlist);
0110 if (wordList == null) {
0111 error("Wordlist: Invalid wordlist file " + fname);
0112 if (useDefault) {
0113 // fallback to default file:
0114 wordList = parser.parseFromUrl(defaultFilename, validateWordlist);
0115 if (wordList == null) {
0116 error("Wordlist: Invalid wordlist file " + defaultFilename);
0117 return;
0118 }
0119 from = "default-file " + defaultFilename;
0120 }
0121 else {
0122 error("Wordlist: do not use default list, no list loaded");
0123 return;
0124 }
0125 } else {
0126 from = "file " + fname;
0127 }
0128 // at this point we have valid levels
0129 maxLevel = wordList.levels.length;
0130 console.log("Wordlist: loaded " + maxLevel + " levels from " + from);
0131 return wordList;
0132 }
0133
0134 /**
0135 * Get wordlist data for @p level
0136 *
0137 * @param type:int level Level.
0138 * @returns type:object wordlist data.
0139 */
0140 function getLevelWordList(level) {
0141 if (level > maxLevel)
0142 return null;
0143 return wordList.levels[level - 1];
0144 }
0145
0146 /**
0147 * Get number of sub-levels in @p level.
0148 *
0149 * @param type:int level Level.
0150 * @returns type:int Number of sublevels.
0151 */
0152 function getMaxSubLevel(level) {
0153 if (level > maxLevel || level === 0) {
0154 console.log("ERROR: Wordlist.getMaxSubLevel out of range, requested level", level,
0155 "out of expected range", 1, "-", maxLevel)
0156 return null;
0157 }
0158 return wordList.levels[level - 1].sublevels !== undefined ?
0159 wordList.levels[level - 1].sublevels : 0;
0160 }
0161
0162 /**
0163 * Build a random word list for @p level.
0164 *
0165 * We don't want to propose several time the same word. First call
0166 * initRandomWord(level) to create the initial shuffled list of words.
0167 * Then call getRandomWord() to get the words one at a time.
0168 * If a word was not found by the child, add it again to the list
0169 * with appendRandomWord(word)
0170 *
0171 * @param type:int level Level.
0172 */
0173 function initRandomWord(level) {
0174 randomWordList = Core.shuffle(wordList.levels[level - 1].words).slice(0)
0175 }
0176
0177 /**
0178 * Re-add a random word to a shuffled word list.
0179 *
0180 * @param type:string word Word to append.
0181 * @sa initRandomWord
0182 */
0183 function appendRandomWord(word) {
0184 randomWordList.unshift(word)
0185 }
0186
0187 /**
0188 * Returns the next random word from a shuffled wordlist.
0189 *
0190 * @sa initRandomWord
0191 */
0192 function getRandomWord() {
0193 return randomWordList.pop()
0194 }
0195
0196 /// @cond INTERNAL_DOCS
0197
0198 function validateWordlist(doc)
0199 {
0200 // minimal syntax check:
0201 var i;
0202 if (undefined === doc.levels)
0203 return false;
0204 for (i = 0; i < doc.levels.length; i++) {
0205 // check mandatory level properties only (speed, fallspeed and sublevels are optional)
0206 if (doc.levels[i].words.length < 1)
0207 return false;
0208 }
0209 if (i < 1)
0210 return false;
0211 return true;
0212 }
0213
0214 /// @endcond
0215
0216 JsonParser {
0217 id: parser
0218
0219 onError: wordlist.error(msg);
0220 }
0221
0222 Component.onCompleted: {
0223 if (filename != "")
0224 loadFromFile(filename);
0225 }
0226 }