openct-tasks/_common/modules/pemFioi/conceptViewer-1.0-mobileFir...

554 lines
20 KiB
JavaScript

var conceptViewerStrings = {
fr: {
viewerTitle: "Aide",
selectLanguage: "Sélectionnez un langage…",
selectTopic: "Sélectionnez une rubrique…",
reloadFromTask: "Merci d'ouvrir cette documentation directement depuis l'exercice. Vous pouvez fermer cette fenêtre.",
concepts: {
"taskplatform": 'Résolution des exercices',
"language": "Création d'un programme",
"blockly_text_print": 'Afficher du texte',
"blockly_text_print_noend": 'Afficher consécutivement du texte',
"blockly_controls_repeat": 'Boucles de répétition',
"blockly_controls_if": 'Conditions si',
"blockly_controls_if_else": 'Conditions si/sinon',
"blockly_controls_whileUntil": 'Boucles tant que ou jusqu\'à',
"blockly_controls_infiniteloop": 'Boucle infinie',
"blockly_logic_operation": 'Opérateurs logiques',
"extra_nested_repeat": 'Boucles imbriquées',
"extra_variable": 'Variables',
"extra_list": 'Listes',
"extra_function": 'Fonctions',
"robot_commands": 'Commandes du robot',
"arguments": 'Fonctions avec arguments',
}
},
en: {
viewerTitle: "Help",
selectLanguage: "Select a language…",
selectTopic: "Select a topic…",
reloadFromTask: "Please open this documentation from your exercise. You can close this window.", // TODO :: verify
concepts: {
"taskplatform": 'Solving exercises',
"language": "Program creation",
"blockly_text_print": 'Afficher du texte',
"blockly_text_print_noend": 'Afficher consécutivement du texte',
"blockly_controls_repeat": 'Loops: repeat',
"blockly_controls_if": 'if conditions',
"blockly_controls_if_else": 'if/else conditions',
"blockly_controls_whileUntil": 'Loops: while/until',
"blockly_controls_infiniteloop": 'Infinite loop',
"blockly_logic_operation": 'Logic operators',
"extra_nested_repeat": 'Nested loops',
"extra_variable": 'Variables',
"extra_list": 'Lists',
"extra_function": 'Functions',
"robot_commands": 'Robot commands',
"arguments": 'Functions with arguments',
}
},
es: {
viewerTitle: "Ayuda",
selectLanguage: "Seleccione un lenguaje…",
selectTopic: "Seleccione un tema…",
reloadFromTask: "Please open this documentation from your exercise. You can close this window.", // TODO :: Translate
concepts: {
"taskplatform": 'Resolución de ejercicios',
"language": "Creación de un programa",
"blockly_text_print": 'Impresión de texto',
"blockly_text_print_noend": 'Impresión consecutiva de texto',
"blockly_controls_repeat": 'Bucles de repetición',
"blockly_controls_if": 'Condiciones si',
"blockly_controls_if_else": 'Condiciones si/sino',
"blockly_controls_whileUntil": 'Bucles mientras y hasta que',
"blockly_controls_infiniteloop": 'Repetir indefinidamente',
"blockly_logic_operation": 'Operadores lógicos',
"extra_nested_repeat": 'Bucles anidados',
"extra_variable": 'Variables',
"extra_list": 'Listas',
"extra_function": 'Funciones',
"robot_commands": 'Comandos del robot',
"arguments": 'Funciones con argumentos',
}
},
it: {
viewerTitle: "Aiuto online",
selectLanguage: "Seleziona una lingua…",
selectTopic: "Seleziona un argomento…",
reloadFromTask: "Please open this documentation from your exercise. You can close this window.", // TODO :: Translate
concepts: {
"taskplatform": 'Solving exercises',
"language": "Programmazione",
"blockly_text_print": 'Visualizzazione del testo',
"blockly_text_print_noend": 'Visualizzazione del testo sequenziale',
"blockly_controls_repeat": 'Ciclo di ripetizione',
"blockly_controls_if": 'Istruzione if',
"blockly_controls_if_else": 'Istruzione if / else',
"blockly_controls_whileUntil": 'Ripetere fino a quando',
"blockly_controls_infiniteloop": 'Loop infinito',
"blockly_logic_operation": 'Operatori logici (booleani)',
"extra_nested_repeat": 'Loop nidificati',
"extra_variable": 'Variabili',
"extra_list": 'Elenchi',
"extra_function": 'Funzioni',
"robot_commands": 'Robot commands',
"arguments": 'Funzioni conargomenti',
}
}
};
window.stringsLanguage = window.stringsLanguage || "fr";
var conceptViewer = {
concepts: {},
loaded: false,
shownConcept: null,
selectedLanguage: null,
fullScreen: false,
contextTitle: undefined,
allLangs: [
{id: 'blockly', lbl: 'Blockly'},
{id: 'scratch', lbl: 'Scratch'},
{id: 'python', lbl: 'Python'}
],
load: function (fullscreenLoad) {
if (!this.fullScreen)
this.fullScreen = fullscreenLoad;
// Load the conceptViewer into the DOM
if(this.loaded) { return; }
this.strings = conceptViewerStrings[window.stringsLanguage] || conceptViewerStrings.fr;
// TODO :: allow changing list of languages
var navLanguage = '\
<label for="showNavigationLanguage" id="showNavigationLanguageLabel" class="showNavigationLanguage">' + this.strings.selectLanguage + '</label>\
<input type="checkbox" id="showNavigationLanguage" role="button">\
<ul>';
var curLangLbl = null;
for(var i=0; i<this.allLangs.length; i++) {
navLanguage += '<li data-id="'+ this.allLangs[i].id + '"';
if((!this.selectedLanguage && i == 0) || this.allLangs[i].id == this.selectedLanguage) {
navLanguage += ' class="selected"';
curLangLbl = this.allLangs[i].lbl;
}
navLanguage += '><span>' + this.allLangs[i].lbl + '</span>';
navLanguage += '</li>';
}
navLanguage += '</ul>';
$('body').append(''
+ '<div id="conceptViewer"">'
+ ' <div class="content">'
+ ' <div class="panel-heading">'
+ ' <h2 class="sectionTitle"><span class="icon fas fa-list-ul"></span>'
+ (this.fullScreen ? (this.contextTitle ? this.contextTitle + " &ndash; " : "") : "") + this.strings.viewerTitle
+ ' </h2>'
+ ' <div class="section-external-exit">'
+ ' <div class="exit" onclick="conceptViewer.openInNewWidget();"><span class="icon fas fa-external-link-alt"></span></div>'
+ ' <div class="exit" onclick="conceptViewer.hide();"><span class="icon fas fa-times"></span></div>'
+ ' </div>'
+ ' </div>'
+ ' <div class="panel-body">'
+ ' <div class="navigation">'
+ ' <div class="navigationLanguage">'
+ navLanguage
+ ' </div>'
+ ' <div class="navigationContent"></div>'
+ ' </div>'
+ ' <div class="viewer">'
+ ' <iframe class="viewerContent" name="viewerContent"></iframe>'
+ ' </div>'
+ ' </div>'
+ ' </div>'
+ '</div>');
if (!this.fullScreen) {
$('#conceptViewer').hide();
} else {
$('#conceptViewer').addClass('conceptViewer-fullscreen');
}
if(curLangLbl) {
$('#showNavigationLanguageLabel').text(curLangLbl);
}
var that = this;
if (!conceptViewer.fullScreen) {
$('#conceptViewer').on('click', function (event) {
if (!$(event.target).closest('#conceptViewer .content').length) {
that.hide();
}
});
}
this.loaded = true;
$('#conceptViewer .navigationLanguage ul li').click(function(){
conceptViewer.selectedLanguage = $(this).data('id');
$('#conceptViewer .navigationLanguage ul li').removeClass('selected');
$(this).addClass('selected');
conceptViewer.languageChanged();
});
this.loadNavigation();
},
loadNavigation: function () {
var navContent = "\
<label for='showNavigationContent' class='showNavigationContent'>" + this.strings.selectTopic + "</label>\
<input type='checkbox' id='showNavigationContent' role='button'>\
<ul>";
var defaultUrl = null;
for (var i=0; i<this.concepts.length; i++) {
var curConcept = this.concepts[i];
if(curConcept.isDefault) {
defaultUrl = curConcept.url;
}
navContent += '<li data-id="'+curConcept.id+'" onclick="conceptViewer.showConcept(\''+curConcept.id+'\');">'
+ curConcept.name
+ ' </li>';
}
navContent += "</ul>";
$('#conceptViewer .navigationContent').html(navContent);
// Try first to show again the concept we were viewing
if(this.shownConcept && this.showConcept(this.shownConcept, false)) {
return;
} else if (defaultUrl) {
// else show the default concept
this.loadUrl(defaultUrl);
} else {
// else show nothing
this.loadUrl('');
this.shownConcept = null;
}
},
loadConcepts: function (newConcepts) {
// Load new concept information
this.concepts = newConcepts;
if(this.loaded) {
this.loadNavigation();
}
},
selectLanguage: function(lang) {
this.selectedLanguage = lang;
},
show: function (initConcept) {
// Display the conceptViewer
this.load();
$('#conceptViewer').fadeIn(500);
if (this.shownConcept && (initConcept || typeof initConcept == 'undefined')) {
this.showConcept(this.shownConcept);
}
},
hide: function () {
// Hide the conceptViewer
this.load();
$('#conceptViewer').fadeOut(500);
this.loadUrl('');
},
openInNewWidget: function() {
// we use the function to get the base url in order to support http and https.
var url = getConceptViewerBaseUrl() + "display-documentation.html";
// we put the language so we can do some operations faster and not depending on the jschannel
var fullscreenWindow = window.open(url + "?lang=" + window.stringsLanguage);
var channel = Channel.build({window: fullscreenWindow, origin: '*', scope: 'test'});
var that = this;
// The object sent from this page to the fullscreen concept viewer in order to get all the options.
var conceptViewerConfigs = {
concepts: that.concepts,
selectedLang: that.selectedLanguage,
shownConcept: that.shownConcept,
contextTitle: that.contextTitle
};
channel.bind('getConceptViewerConfigs', function() {
return conceptViewerConfigs;
});
},
showConcept: function (concept, show) {
// Show a specific concept
// Either a concept object can be given, either a concept ID can be given
// directly
var conceptUrl = null;
var conceptId = null;
var conceptName = null;
if (concept.url) {
conceptUrl = concept.url;
conceptId = concept.id;
conceptName = concept.name;
} else {
conceptId = concept.id ? concept.id : concept;
for (var i=0; i<this.concepts.length; i++) {
if(this.concepts[i].id == conceptId) {
conceptUrl = this.concepts[i].url;
conceptName = this.concepts[i].name;
}
}
}
if (conceptUrl) {
this.shownConcept = conceptId;
if(show || typeof show == 'undefined') { this.show(false); }
var language = conceptViewer.selectedLanguage;
var urlSplit = conceptUrl.split('#');
if(urlSplit[1]) {
urlSplit[urlSplit.length-1] = language+'-'+urlSplit[urlSplit.length-1];
} else {
urlSplit[1] = language;
}
conceptUrl = urlSplit.join('#');
this.loadUrl(conceptUrl);
$('#conceptViewer .navigationContent ul li').removeClass('selected');
$('#conceptViewer .navigationContent ul li[data-id='+conceptId+']').addClass('selected');
$('#showNavigationContent').prop('checked', false);
if (this.fullScreen) {
document.title = conceptViewerStrings[window.stringsLanguage].viewerTitle + ' - ' + conceptName;
}
return true;
} else {
return false;
}
},
loadUrl: function (url) {
// Load an URL into the iframe
if(window.conceptViewerUrlFunction) {
url = window.conceptViewerUrlFunction(url);
}
$('#conceptViewer .viewerContent').attr('src', url);
},
hasConcept: function (conceptName) {
// Check if a specific concept exists in the list of concepts
for (var i=0; i<this.concepts.length; i++) {
if(this.concepts[i].id == conceptName) {
return true;
}
}
return false;
},
hasPythonConcept: function (pythonCode) {
for (var i=0; i<this.concepts.length; i++) {
var pythonList = this.concepts[i].python;
if(pythonList && pythonList.indexOf(pythonCode) > -1) {
return this.concepts[i].id;
}
}
return false;
},
languageChanged: function () {
$('#showNavigationLanguage').prop('checked', false);
for(var i=0; i<this.allLangs.length; i++) {
if(this.allLangs[i].id == conceptViewer.selectedLanguage) {
$('#showNavigationLanguageLabel').text(this.allLangs[i].lbl);
break;
}
}
this.loadNavigation();
},
unload: function() {
$('#conceptViewer').remove();
this.loaded = false;
}
};
function getConceptViewerBaseUrl() {
// Specific configuration to go through the domain itself if there's a 'p=1'
// argument or we are on concours2.castor-informatique.fr
var baseUrl = '';
baseUrl += (window.location.protocol == 'http:' ? 'http:' : 'https:') + '//';
baseUrl += ((window.location.search.indexOf('p=1') > -1
|| window.location.hostname == 'concours2.castor-informatique.fr')
? window.location.host : 'static4.castor-informatique.fr');
baseUrl += '/help/';
return baseUrl;
}
function getConceptViewerBaseConcepts() {
// Get base concepts in the default help
var baseUrl = getConceptViewerBaseUrl();
if(window.stringsLanguage == 'es' || window.stringsLanguage == 'it') {
baseUrl += 'index_' + window.stringsLanguage + '.html';
} else {
baseUrl += 'index.html';
}
var baseConcepts = [
{id: 'taskplatform', name: 'Résolution des exercices', url: baseUrl+'#taskplatform', language: 'all', order: 100},
{id: 'language', name: "Création d'un programme", url: baseUrl+'#language', order: 101},
{id: 'blockly_text_print', name: 'Afficher du texte', url: baseUrl+'#blockly_text_print', order: 102},
{id: 'blockly_text_print_noend', name: 'Afficher consécutivement du texte', url: baseUrl+'#blockly_text_print_noend', order: 103},
{id: 'blockly_controls_repeat', name: 'Boucles de répétition', url: baseUrl+'#blockly_controls_repeat', order: 104},
{id: 'blockly_controls_if', name: 'Conditions si', url: baseUrl+'#blockly_controls_if', order: 105},
{id: 'blockly_controls_if_else', name: 'Conditions si/sinon', url: baseUrl+'#blockly_controls_if_else', order: 106},
{id: 'blockly_controls_whileUntil', name: 'Boucles tant que ou jusqu\'à', url: baseUrl+'#blockly_controls_whileUntil', order: 107},
{id: 'blockly_controls_infiniteloop', name: 'Boucle infinie', url: baseUrl+'#blockly_controls_infiniteloop', order: 108},
{id: 'blockly_logic_operation', name: 'Opérateurs logiques', url: baseUrl+'#blockly_logic_operation', order: 109},
{id: 'extra_nested_repeat', name: 'Boucles imbriquées', url: baseUrl+'#extra_nested_repeat', order: 110},
{id: 'extra_variable', name: 'Variables', url: baseUrl+'#extra_variable', order: 111},
{id: 'extra_list', name: 'Listes', url: baseUrl+'#extra_list', order: 112},
{id: 'extra_function', name: 'Fonctions', url: baseUrl+'#extra_function', order: 113},
{id: 'robot_commands', name: 'Commandes du robot', url: baseUrl+'#robot_commands', order: 114},
{id: 'arguments', name: 'Fonctions avec arguments', url: baseUrl+'#arguments', order: 115}
];
return baseConcepts;
}
function conceptsFill(baseConcepts, allConcepts) {
var conceptNames = (conceptViewerStrings[window.stringsLanguage] || conceptViewerStrings.fr)[
"concepts"] || conceptViewerStrings.fr.concepts;
var concepts = [];
var baseConceptsById = {};
for(var b=0; b<baseConcepts.length; b++) {
var curConcept = baseConcepts[b];
if(typeof curConcept === 'string') {
baseConceptsById[curConcept] = {id: curConcept};
} else {
baseConceptsById[curConcept.id] = curConcept;
}
}
for(var c=0; c<allConcepts.length; c++) {
var fullConcept = allConcepts[c];
if(baseConceptsById[fullConcept.id]) {
var curConcept = baseConceptsById[fullConcept.id];
// Translate concept name if available
curConcept.name = conceptNames[curConcept.id] || fullConcept.name;
if(!curConcept.url) {
curConcept.url = fullConcept.url;
}
if(!curConcept.order) {
curConcept.order = fullConcept.order;
}
if(!curConcept.python) {
curConcept.python = fullConcept.python;
}
if(!fullConcept.ignore) {
concepts.push(curConcept);
}
delete baseConceptsById[fullConcept.id];
} else if(fullConcept.isBase && baseConceptsById['base']) {
concepts.push(fullConcept);
}
}
for(var leftConcept in baseConceptsById) {
if(leftConcept != 'base') {
concepts.push(baseConceptsById[leftConcept]);
}
}
concepts.sort(function(a,b) {
return !a.order || !b.order ? 0 : a.order - b.order;
});
return concepts;
}
function getConceptsFromBlocks(includeBlocks, allConcepts, context) {
if(!includeBlocks) { return []; }
var concepts = ['language'];
if(includeBlocks.standardBlocks) {
var allConceptsById = {};
for(var c = 0; c<allConcepts.length; c++) {
allConceptsById[allConcepts[c].id] = allConcepts[c];
}
if(includeBlocks.standardBlocks.includeAll) {
for(var c = 0; c<allConcepts.length; c++) {
if(allConcepts[c].id.substr(0, 8) === 'blockly_') {
concepts.push(allConcepts[c]);
}
}
} else if(includeBlocks.standardBlocks.singleBlocks) {
for(var b = 0; b<includeBlocks.standardBlocks.singleBlocks.length; b++) {
var blockName = includeBlocks.standardBlocks.singleBlocks[b];
if(allConceptsById['blockly_'+blockName]) {
concepts.push(allConceptsById['blockly_'+blockName]);
}
}
}
}
if(includeBlocks.generatedBlocks) {
for(var genName in includeBlocks.generatedBlocks) {
// this variable is used in order to make sure that we don't include two
// times a documentation
var includedConceptIds = [];
// We remove all concepts which have no "python" attribute
var filteredConcepts = allConcepts.filter(function(concept) { return concept.python && concept.python != []; });
for (var functionKey in includeBlocks.generatedBlocks[genName]) {
var functionName = includeBlocks.generatedBlocks[genName][functionKey];
var concept = findConceptByFunction(filteredConcepts, functionName);
if (concept) {
// if we does not have the concept already pushed, we push it.
if (includedConceptIds.indexOf(concept.id) == -1) {
includedConceptIds.push(concept.id);
concepts.push(concept);
}
} else {
// here you can print the function name for which the documentation is missing
// for debug:
// console.log("conceptViewer - function getConceptsFromBlocks : the function named: "
// + functionName + " is was not found in the documentation, please consider adding it inside of the "
// + "conceptList.python array.");
}
}
}
}
return concepts;
}
/**
* This function allow us to find a concept by his function name.
* The function name is in the python list of a concept.
* @param filteredConcepts The list of all the concepts which have the "python" attribute
* @param functionName The name of the function we have to look for
* @return A concept if found, false otherwise.
*/
function findConceptByFunction(filteredConcepts, functionName) {
for (var conceptId in filteredConcepts) {
for (var conceptFunctionId in filteredConcepts[conceptId].python) {
if (filteredConcepts[conceptId].python[conceptFunctionId] === functionName) {
return filteredConcepts[conceptId];
}
}
}
return false;
}
function getConceptsFromTask(allConcepts) {
if(typeof taskSettings === 'undefined') { return; }
var baseConcepts = ['taskplatform'];
if(taskSettings.conceptViewer.length) {
baseConcepts = baseConcepts.concat(taskSettings.conceptViewer);
}
if(taskSettings.blocklyOpts && taskSettings.blocklyOpts.includeBlocks) {
baseConcepts = baseConcepts.concat(getConceptsFromBlocks(taskSettings.blocklyOpts.includeBlocks, allConcepts));
}
return conceptsFill(baseConcepts, allConcepts);
}