(function(){ var jqScreen = $('#screen_problem'), languageCache = {}, // keyed by language identifier: processed data about languages translationCache = [], // keys are autogenerated in ta(), a value is a dictionary of translations of a translation key for every language problemCache = {}, // problem data cache, 3-level, keyed by: language, problem group, and problem identifier langs, Nlangs, // constants, set on init // ================================================================================ // Hint processing: extract hints from the translations and return them in the // processed form, hint key -> translation language -> value // ================================================================================ defaultHintCondition = function (translation) { // must contain at least one translation var key; if (!translation || typeof translation !== 'object') return false; for (key in translation) { if (!translation.hasOwnProperty(key)) continue; return true; } return false; }, processHints = function (rawTranslations) { var defaultHint = {}, // here we put all the hints with their default translations allHints = {}, // the result allHintKeys = [], tr, key, i, lang, hint, h, j; // find the default hint translations, they will form the basis of default hints tr = chooseDefaultTranslation(rawTranslations, 'hint', defaultHintCondition) || {}; for (key in tr) { // copy the hints if (!tr.hasOwnProperty(key)) continue; defaultHint[key] = tr[key]; allHintKeys.push(key); } // copy any hints not in the default hints to the default hints for (i = langs.length - 1; i >= 0; i--) { lang = langs[i]; tr = rawTranslations[lang]; if (!tr || !defaultHintCondition(tr.hint)) continue; // skip unavailable translations or translations with no hints hint = tr.hint; for (key in hint) { if (!hint.hasOwnProperty(key) || !hint[key]) continue; if (!key in defaultHint) { defaultHint[key] = hint[key]; allHintKeys.push(key); } } } // create all translations for hints for (i = allHintKeys.length - 1; i >= 0; i--) { allHints[allHintKeys[i]] = {}; // create keys with no translations, we'll fill them in the next loop } for (i = langs.length - 1; i >= 0; i--) { lang = langs[i]; tr = rawTranslations[lang]; // set up hints if (!tr || !defaultHintCondition(tr.hint)) { // there's no hint in the current language, copy the default in its entirety for (j = allHintKeys.length - 1; j >= 0; j--) { key = allHintKeys[j]; allHints[key][lang] = defaultHint[key]; } } else { // make a copy of all hints, using the default hint value where a hint value is missing h = tr.hint; for (j = allHintKeys.length - 1; j >= 0; j--) { key = allHintKeys[j]; allHints[key][lang] = h[key] || defaultHint[key]; } } } return allHints; }, // ================================================================================ // Plan processing: extract plans from the translations and return them in the // processed form, hint key -> translation language -> value // ================================================================================ defaultPlanCondition = function (translation) { // default plan must be non-empty if (!translation || !(translation instanceof Array)) return false; return translation.length > 0; }, /** * Structurally converts the plan definition into something that the * hint module can work with. * The input is the translations object from problem.json in the form * {'sl': { ..., 'plan': [hint1_sl, hint2_sl, ...]}, 'en': { ..., 'plan': [hint1_en, hint2_en, ...]}, ...} * The output is a list of hints in the plan, translations are in each hint: * [{'sl': hint1_sl, 'en': hint1_en, ...}, {'sl': hint2_sl, 'en': hint2_en}, ...] */ processPlans = function (rawTranslations) { // find the default plan translation var defaultPlan = chooseDefaultTranslation(rawTranslations, 'plan', defaultPlanCondition) || [], result = [], i, lang, tr, plan, j, fragment; if (defaultPlan.length == 0) return result; // no plan // copy all translations from plan for (i = langs.length - 1; i >= 0; i--) { lang = langs[i]; tr = rawTranslations[lang]; // set up plan if (tr && defaultPlanCondition(tr.plan)) { plan = tr.plan; } else { // there's no plan in the current language, copy the default plan plan = defaultPlan; } if (!(plan instanceof Array)) plan = [plan]; for (j = 0; j < plan.length; j++) { if (j < result.length) fragment = result[j]; else { fragment = {}; result.push(fragment); } fragment[lang] = plan[j]; } } // ensure each plan element has all translations for (j = result.length - 1; j >= 0; j--) { fragment = result[j]; for (i = langs.length - 1; i >= 0; i--) { lang = langs[i]; if (!fragment[lang]) fragment[lang] = 'Missing plan for language ' + lang + ' at index ' + j; } } return result; }, // ================================================================================ // Group + problems directory processing // ================================================================================ chooseTranslation = function (keyword, lang, currentDict, enDict, translations) { var tr = currentDict[keyword], otherLang; if (tr) return tr; // if there is a translation in the current dictionary: return it tr = enDict[keyword]; if (tr) { // if there is a translation in the english dictionary: return it codeq.log.info('Translation for ' + keyword + ' not set for language ' + lang + ', using translation from en'); return tr; } for (otherLang in translations) { if (!translations.hasOwnProperty(otherLang)) continue; tr = (translations[otherLang] || {})[keyword]; if (tr) { // otherwise: return the first available translation in any language codeq.log.info('Translation for ' + keyword + ' not set for language ' + lang + ', using translation from ' + otherLang); return tr; } } return keyword + ' not set for language ' + lang; }, /** * convert the input translations (arg0) for given keys (arg1..argN) * so each keys holds all its translations for every language */ convertTranslations = function () { var translations = arguments[0] || {}, result = {}, enDict = translations['en'] || {}, lang, dict, i, keyword, l; // initialize result: one lang-dict per keyword for (i = arguments.length - 1; i > 0; i--) result[arguments[i]] = {}; // convert translations: one keyword-dict per lang -> one lang-dict per keyword for (l = Nlangs - 1; l >= 0; l--) { lang = langs[l]; dict = translations[lang] || {}; for (i = arguments.length - 1; i > 0; i--) { keyword = arguments[i]; // result[keyword][lang] = dict[keyword] || keyword + ' not set for language ' + lang; result[keyword][lang] = chooseTranslation(keyword, lang, dict, enDict, translations); } } return result; }, /** * Connect the given translations of a key with the DOM, returning * the string of arguments to use with a HTML tag which will contain * a translation chosen from the given dictionary. */ ta = function (trObj) { // an object of the form: {'en': 'english content', 'sl': 'slovenska vsebina'} var result = ['data-dict="directory" data-tkey="', translationCache.length, '"'].join(''); translationCache.push(trObj); return result; }, /** * Assemble the display structure and translations from the server's * language.json for the given language identifier. */ createLanguageData = function (data, languageIdentifier) { // data is the content of language.json var li = languageIdentifier, // a shorthand rawTranslations = data.translations || {}, groups = data.groups || [], Ngroups = groups.length, html = [], problemReferences = [], group, problems, Nproblems, problem, i, j; var langDict = convertTranslations(rawTranslations, 'name', 'description'), // this will be the resulting dictionary: multi-level keys that lead up to the lang-dict groupDict, problemDict; // title: HTML structure for "name" and "desc" html.push('