From 0b2485f393bcc47dcf895044c410b2c8784af432 Mon Sep 17 00:00:00 2001 From: Timotej Lazar Date: Tue, 6 Oct 2015 18:32:42 +0200 Subject: Rename state 'problem' to problem_list Remove the Language element from the navbar and have "CodeQ" (logo TBD) send user to the language-selection page. --- js/codeq/language.js | 10 +- js/codeq/navigation.js | 6 +- js/codeq/problem.js | 385 ----------------------------------------------- js/codeq/problem_list.js | 383 ++++++++++++++++++++++++++++++++++++++++++++++ js/codeq/prolog.js | 6 +- js/codeq/python.js | 6 +- js/codeq/robot.js | 6 +- 7 files changed, 395 insertions(+), 407 deletions(-) delete mode 100644 js/codeq/problem.js create mode 100644 js/codeq/problem_list.js (limited to 'js/codeq') diff --git a/js/codeq/language.js b/js/codeq/language.js index 0893e33..b861560 100644 --- a/js/codeq/language.js +++ b/js/codeq/language.js @@ -7,14 +7,12 @@ jqProlog = $('#choose-prolog'), jqPython = $('#choose-python'), jqRobot = $('#choose-robot'), - chooseProlog = function () {codeq.globalStateMachine.transition('problem', 'prolog');}, - choosePython = function () {codeq.globalStateMachine.transition('problem', 'python');}, - chooseRobot = function () {codeq.globalStateMachine.transition('problem', 'robot');}; + chooseProlog = function () {codeq.globalStateMachine.transition('problem_list', 'prolog');}, + choosePython = function () {codeq.globalStateMachine.transition('problem_list', 'python');}, + chooseRobot = function () {codeq.globalStateMachine.transition('problem_list', 'robot');}; codeq.globalStateMachine.register('language',{ 'enter': function(){ - $("#navigation-language").addClass("active").css('display', ''); - jqScreen.css('display', ''); jqProlog.on('click', chooseProlog); jqPython.on('click', choosePython); @@ -25,8 +23,6 @@ jqPython.off(); jqRobot.off(); jqScreen.css('display', 'none'); - - $('#navigation-language').css('display', 'none').removeClass("active"); } }); })(); diff --git a/js/codeq/navigation.js b/js/codeq/navigation.js index a1b8f3d..eafa5cc 100644 --- a/js/codeq/navigation.js +++ b/js/codeq/navigation.js @@ -81,12 +81,12 @@ //setup all the buttons in the banner - $('#navigation-language').on('click', function(e){ + $('#navigation-home').on('click', function(e){ codeq.globalStateMachine.transition('language'); e.preventDefault(); }); - $('#navigation-problem').on('click', function(e){ - codeq.globalStateMachine.transition('problem'); + $('#navigation-problem_list').on('click', function(e){ + codeq.globalStateMachine.transition('problem_list'); e.preventDefault(); }); $('#navigation-python').on('click', function(e){ diff --git a/js/codeq/problem.js b/js/codeq/problem.js deleted file mode 100644 index 4387344..0000000 --- a/js/codeq/problem.js +++ /dev/null @@ -1,385 +0,0 @@ -(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('


'); - html.push('
'); - // content: problem directory - html.push(''); - - return { - 'language': languageIdentifier, // 'prolog', 'python', ... - 'html': html.join(''), // the DOM structure (without textual content), as HTML text - 'refs': problemReferences, // array of problem info {g: group, p: problem, id: problem_id}, referenced from DOM elements - 'commonDef': { - 'hint': processHints(rawTranslations), // hint translations: keyword -> lang -> value - 'hint_type': data.hint_type || {} - } - }; - }, - - // ================================================================================ - // DOM instantiation. - // The transition to problem solving takes place here, after the user clicks on a - // problem and all the required data is loaded. - // ================================================================================ - /** - * Instantiates the screen from the given processed data. - */ - createDom = function (data) { // data is the (cached) result of createLanguageData() - var language = data.language; - jqScreen.html(data.html); - codeq.tr.translateDom(jqScreen); - jqScreen.find('a').on('click', function () { - var index = +$(this).attr('class').split(' ')[0].split('-')[1], - ref = data.refs[index]; - if (!ref) { - codeq.log.error('Clicked on a problem link having erroneous index: ' + index); - return; - } - codeq.wait( - Q.all([ - codeq.comms.getProblem(language, ref.g, ref.p), // TODO: use ref.id instead // the current solution - getProblemData(language, ref.g, ref.p) // the (cached) result of processProblemData() - ]) - .spread(function (userProblemData, generalProblemData) { - if (userProblemData.code !== 0) throw new Error('Failed to obtain user problem data, code: ' + userProblemData.code + ', message: ' + userProblemData.message); - if (!generalProblemData) throw new Error('General problem data is not defined'); - codeq.globalStateMachine.transition(language, generalProblemData, data.commonDef, userProblemData.data.solution); - }) - ) - .fail(function (reason) { - codeq.log.error('Failed to obtain the problem definition: ' + reason, reason); - alert('Failed to obtain the problem definition: ' + reason); - }) - .done(); - }); - }, - - // ================================================================================ - // Problem definition processing - // ================================================================================ - - chooseDefaultTranslation = function (rawTranslations, translationKey, condition) { - var tr = rawTranslations.en, // try English as the default - lang; - if (typeof condition !== 'function') condition = function (x) { return !!x; }; - if (tr && condition(tr[translationKey])) return tr[translationKey]; - for (lang in rawTranslations) { // find a translation with content - if (!rawTranslations.hasOwnProperty(lang)) continue; - tr = rawTranslations[lang]; - if (tr && condition(tr[translationKey])) return tr[translationKey]; - } - return null; // default must be chosen by the caller - }, - - processProblemData = function (rawData, language, group, problem) { - var rawTranslations = rawData.translations || {}; - return { - 'language': language, - 'group': group, - 'problem': problem, - 'id': rawData.id, - 'translations': convertTranslations(rawTranslations, 'title', 'name', 'slug', 'description'), // GUI translations: keyword -> lang -> value - 'hint': processHints(rawTranslations), // hint translations: keyword -> lang -> value - 'plan': processPlans(rawTranslations), // plan translations: keyword -> lang -> value - 'hint_type': rawData.hint_type || {} - }; - }, - - getProblemData = function (language, group, problem) { - var langCache = problemCache[language], - groupCache, cachedProblem, promise; - if (langCache) { - groupCache = langCache[group]; - if (!groupCache) { - groupCache = {}; - langCache[group] = groupCache; - } - } - else { - langCache = {}; - problemCache[language] = langCache; - groupCache = {}; - langCache[group] = groupCache; - } - cachedProblem = groupCache[problem]; - if (cachedProblem) return Q(cachedProblem); - return codeq.comms.getProblemDef(language, group, problem).then(function (rawData) { - var data = processProblemData(rawData, language, group, problem); - groupCache[problem] = data; - return data; - }); - }, - - currentLanguage; // the currently active language - - // ================================================================================ - // Initialization, invoked from the boot sequence - // ================================================================================ - - codeq.on('init', function () { - codeq.tr.registerDictionary('directory', translationCache); - langs = codeq.availableLangs; // cache for easier access - Nlangs = langs.length; - }); - - // ================================================================================ - // Register with the state machine - // ================================================================================ - - codeq.globalStateMachine.register('problem', { - 'enter': function(language){ - var data = null; // language data - - $('#navigation-language').css('display', ''); - $("#navigation-problem").addClass("active").css('display', ''); - - if (!language) language = currentLanguage; // This happens when we hit this with the back button - - if (currentLanguage !== language) { - jqScreen.empty(); - currentLanguage = language; - data = languageCache[language]; - if (data) { - createDom(data); - } - else { - codeq.wait(codeq.comms.getLanguageDef(language).then(function (rawData) { - var data = createLanguageData(rawData, language); - languageCache[language] = data; - createDom(data); - })).done(); - } - } - - jqScreen.css('display', ''); - }, - 'exit' : function(){ - jqScreen.css('display', 'none'); - $('#navigation-language').css('display', 'none'); - $('#navigation-problem').css('display', 'none').removeClass("active"); - } - }); -})(); diff --git a/js/codeq/problem_list.js b/js/codeq/problem_list.js new file mode 100644 index 0000000..1b20fe2 --- /dev/null +++ b/js/codeq/problem_list.js @@ -0,0 +1,383 @@ +(function(){ + var jqScreen = $('#screen_problem_list'), + 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('


'); + html.push('
'); + // content: problem directory + html.push('
'); + + return { + 'language': languageIdentifier, // 'prolog', 'python', ... + 'html': html.join(''), // the DOM structure (without textual content), as HTML text + 'refs': problemReferences, // array of problem info {g: group, p: problem, id: problem_id}, referenced from DOM elements + 'commonDef': { + 'hint': processHints(rawTranslations), // hint translations: keyword -> lang -> value + 'hint_type': data.hint_type || {} + } + }; + }, + + // ================================================================================ + // DOM instantiation. + // The transition to problem solving takes place here, after the user clicks on a + // problem and all the required data is loaded. + // ================================================================================ + /** + * Instantiates the screen from the given processed data. + */ + createDom = function (data) { // data is the (cached) result of createLanguageData() + var language = data.language; + jqScreen.html(data.html); + codeq.tr.translateDom(jqScreen); + jqScreen.find('a').on('click', function () { + var index = +$(this).attr('class').split(' ')[0].split('-')[1], + ref = data.refs[index]; + if (!ref) { + codeq.log.error('Clicked on a problem link having erroneous index: ' + index); + return; + } + codeq.wait( + Q.all([ + codeq.comms.getProblem(language, ref.g, ref.p), // TODO: use ref.id instead // the current solution + getProblemData(language, ref.g, ref.p) // the (cached) result of processProblemData() + ]) + .spread(function (userProblemData, generalProblemData) { + if (userProblemData.code !== 0) throw new Error('Failed to obtain user problem data, code: ' + userProblemData.code + ', message: ' + userProblemData.message); + if (!generalProblemData) throw new Error('General problem data is not defined'); + codeq.globalStateMachine.transition(language, generalProblemData, data.commonDef, userProblemData.data.solution); + }) + ) + .fail(function (reason) { + codeq.log.error('Failed to obtain the problem definition: ' + reason, reason); + alert('Failed to obtain the problem definition: ' + reason); + }) + .done(); + }); + }, + + // ================================================================================ + // Problem definition processing + // ================================================================================ + + chooseDefaultTranslation = function (rawTranslations, translationKey, condition) { + var tr = rawTranslations.en, // try English as the default + lang; + if (typeof condition !== 'function') condition = function (x) { return !!x; }; + if (tr && condition(tr[translationKey])) return tr[translationKey]; + for (lang in rawTranslations) { // find a translation with content + if (!rawTranslations.hasOwnProperty(lang)) continue; + tr = rawTranslations[lang]; + if (tr && condition(tr[translationKey])) return tr[translationKey]; + } + return null; // default must be chosen by the caller + }, + + processProblemData = function (rawData, language, group, problem) { + var rawTranslations = rawData.translations || {}; + return { + 'language': language, + 'group': group, + 'problem': problem, + 'id': rawData.id, + 'translations': convertTranslations(rawTranslations, 'title', 'name', 'slug', 'description'), // GUI translations: keyword -> lang -> value + 'hint': processHints(rawTranslations), // hint translations: keyword -> lang -> value + 'plan': processPlans(rawTranslations), // plan translations: keyword -> lang -> value + 'hint_type': rawData.hint_type || {} + }; + }, + + getProblemData = function (language, group, problem) { + var langCache = problemCache[language], + groupCache, cachedProblem, promise; + if (langCache) { + groupCache = langCache[group]; + if (!groupCache) { + groupCache = {}; + langCache[group] = groupCache; + } + } + else { + langCache = {}; + problemCache[language] = langCache; + groupCache = {}; + langCache[group] = groupCache; + } + cachedProblem = groupCache[problem]; + if (cachedProblem) return Q(cachedProblem); + return codeq.comms.getProblemDef(language, group, problem).then(function (rawData) { + var data = processProblemData(rawData, language, group, problem); + groupCache[problem] = data; + return data; + }); + }, + + currentLanguage; // the currently active language + + // ================================================================================ + // Initialization, invoked from the boot sequence + // ================================================================================ + + codeq.on('init', function () { + codeq.tr.registerDictionary('directory', translationCache); + langs = codeq.availableLangs; // cache for easier access + Nlangs = langs.length; + }); + + // ================================================================================ + // Register with the state machine + // ================================================================================ + + codeq.globalStateMachine.register('problem_list', { + 'enter': function(language){ + var data = null; // language data + + $("#navigation-problem_list").addClass("active").css('display', ''); + + if (!language) language = currentLanguage; // This happens when we hit this with the back button + + if (currentLanguage !== language) { + jqScreen.empty(); + currentLanguage = language; + data = languageCache[language]; + if (data) { + createDom(data); + } + else { + codeq.wait(codeq.comms.getLanguageDef(language).then(function (rawData) { + var data = createLanguageData(rawData, language); + languageCache[language] = data; + createDom(data); + })).done(); + } + } + + jqScreen.css('display', ''); + }, + 'exit' : function(){ + jqScreen.css('display', 'none'); + $('#navigation-problem_list').css('display', 'none').removeClass("active"); + } + }); +})(); diff --git a/js/codeq/prolog.js b/js/codeq/prolog.js index ce1865a..b73d037 100644 --- a/js/codeq/prolog.js +++ b/js/codeq/prolog.js @@ -67,8 +67,7 @@ var prologHandler; //created when we enter the prolog state and destroyed once we leave it codeq.globalStateMachine.register('prolog', { 'enter': function (problemDef, commonDef, currentSolution) { - $('#navigation-language').css('display', ''); - $('#navigation-problem').css('display', ''); + $('#navigation-problem_list').css('display', ''); $("#navigation-prolog").addClass("active"); $('#navigation-prolog').css('display', ''); @@ -98,8 +97,7 @@ subScreens = null; jqScreen.addClass('block1'); - $('#navigation-language').css('display', 'none'); - $('#navigation-problem').css('display', 'none'); + $('#navigation-problem_list').css('display', 'none'); $("#navigation-prolog").removeClass("active"); $('#navigation-prolog').css('display', 'none'); } diff --git a/js/codeq/python.js b/js/codeq/python.js index c3a76b9..35c4be8 100644 --- a/js/codeq/python.js +++ b/js/codeq/python.js @@ -72,8 +72,7 @@ var pythonHandler; //created when we enter the python state and destroyed once we leave it codeq.globalStateMachine.register('python', { 'enter': function (problemDef, commonDef, currentSolution) { - $('#navigation-language').css('display', ''); - $('#navigation-problem').css('display', ''); + $('#navigation-problem_list').css('display', ''); $("#navigation-python").addClass("active"); $('#navigation-python').css('display', ''); @@ -108,8 +107,7 @@ subScreens = null; jqScreen.addClass('block1'); - $('#navigation-language').css('display', 'none'); - $('#navigation-problem').css('display', 'none'); + $('#navigation-problem_list').css('display', 'none'); $("#navigation-python").removeClass("active"); $('#navigation-python').css('display', 'none'); } diff --git a/js/codeq/robot.js b/js/codeq/robot.js index 5d86559..1188c7f 100644 --- a/js/codeq/robot.js +++ b/js/codeq/robot.js @@ -67,8 +67,7 @@ var robotHandler; //created when we enter the robot state and destroyed once we leave it codeq.globalStateMachine.register('robot', { 'enter': function (problemDef, commonDef, currentSolution) { - $('#navigation-language').css('display', ''); - $('#navigation-problem').css('display', ''); + $('#navigation-problem_list').css('display', ''); $("#navigation-robot").addClass("active"); $('#navigation-robot').css('display', ''); @@ -103,8 +102,7 @@ subScreens = null; jqScreen.addClass('block1'); - $('#navigation-language').css('display', 'none'); - $('#navigation-problem').css('display', 'none'); + $('#navigation-problem_list').css('display', 'none'); $("#navigation-robot").removeClass("active"); $('#navigation-robot').css('display', 'none'); } -- cgit v1.2.1