Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 1-navigation
  • 15-choses-a-faire-css
  • 2-gitlab-deploy
  • 3-qcu-features
  • 42-mementos-non-pris-en-compte-dans-commentaires-survey
  • add-tat-sample
  • gitlab-ci-debug
  • macao3-export
  • main
  • test
10 results

Target

Select target project
  • macao/macao-hugo
  • dbeniamine/macao-hugo
2 results
Select Git revision
  • 1-navigation
  • 15-choses-a-faire-css
  • 2-gitlab-deploy
  • 3-qcu-features
  • add-tat-sample
  • gitlab-ci-debug
  • macao3-export
  • main
  • test
9 results
Show changes
Commits on Source (143)
Showing
with 1845 additions and 59 deletions
# From template at https://gitlab.com/pages/hugo
# Runner configuration
default:
# The `hugo_extended` image is required because our theme needs SCSS compilation
# (see https://gohugo.io/installation/linux/#editions)
# The image is pinned to an explicit version number to avoid bugs introduced
# by an automatic upgrade
image: "${CI_TEMPLATE_REGISTRY_HOST}/pages/hugo/hugo_extended:0.128.1"
variables:
GIT_SUBMODULE_STRATEGY: recursive # include Hugo themes
GIT_SUBMODULE_FORCE_HTTPS: true # the GitLab runner doesn't have SSH to clone
HUGO_ENV: production
# Jobs configuration
# In this project, the Hugo directory (with `hugo.toml`, `content/` etc...) is
# not the Git root dir, but a subfolder `macao`. This requires a bit of extra
# config for some jobs.
test:
# The `test` job just tries to build the site, runs on all branches except
# the default
script:
- cd macao # move to the hugo dir
- hugo
rules:
- if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
pages:
# The `pages` job builds the site to the `public` dir for Pages to publish,
# runs only on the default branch
script:
- cd macao
- hugo
# Declare an artifact, the job's output
artifacts:
paths:
- macao/public
# Config for Pages: directory to serve (default is 'public')
publish: macao/public
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
environment: production
[submodule "macao/themes/hugo-pure"]
path = macao/themes/hugo-pure
url = https://github.com/undus5/hugo-pure
[submodule "macao/themes/hugo-book"]
path = macao/themes/hugo-book
url = https://github.com/alex-shpak/hugo-book
path = macao/themes/macao-hugo-theme
url = ../macao-hugo-theme.git
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
.idea
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/macao-hugo.iml" filepath="$PROJECT_DIR$/.idea/macao-hugo.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/macao/themes/macao-hugo-theme" vcs="Git" />
</component>
</project>
\ No newline at end of file
This diff is collapsed.
# Macao-Hugo
Expérimentation avec le *static site generator* [Hugo](https://gohugo.io/) pour Macao-New
# Setup
Portage de l'application MACAO (Module d'Aide à la Compréhension de l'Anglais Oral), basé sur le générateur de site statique [Hugo](https://gohugo.io/) et la bibliothèque [SurveyJS](https://surveyjs.io/form-library/documentation/overview)
[![License: GPLv3](https://img.shields.io/badge/License-GPLv3-blue.svg)](LICENSE)
# Démo. avec Gitlab Pages
https://macao.pages.tetras-libre.fr/macao-hugo/
# Installation pour le développent
- [Installer Hugo](https://gohugo.io/installation/) **(version 0.124 minimum)**
- `git clone gitlab@gitlab.tetras-libre.fr:macao/macao-hugo.git`
- `git clone --recurse-submodules gitlab@gitlab.tetras-libre.fr:macao/macao-hugo.git`
- `cd macao-hugo/macao`
Pour lancer en mode développement avec live reload : `hugo server`
Pour build le site statique : `hugo`
Le contenu de `public/` peut être publié avec n'importe quel serveur HTTP statique.
# Licences
Ce projet, à l'exception des répertoires `macao/content` et `static/media`, est distribué sous licence [GNU GPLv3](https://www.gnu.org/licenses/gpl-3.0.html), dont les termes sont décrits dans le fichier [`LICENSE`](./LICENSE).
Le contenu des répertoires `macao/content` et `static/media` utilise une licence séparée, décrite dans le fichier [`macao/content/LICENSE.md`](./macao/content/LICENSE.md)
\ No newline at end of file
public/
resources/_gen/
.hugo_build.lock
# Activity content is generated by the Macao-Legacy converter, will be added to Git when final
content/activities/*
/content/tests/
.idea
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---
body {
// Global theme colors are defined in macao/themes/macao-hugo-theme/assets/_defaults.scss and go to @mixin theme-tetras
--quizz-backgourd-color: #00000;
--feedback-sucess-color: #FFF2CC;
--feedback-error-color: #FFF2CC;
}
// ***********************************
// Global
// ***********************************
* {
line-height: 1.2 !important
}
img.inlineImage {
vertical-align: middle;
height: 1em;
}
img.inlineImage12 {
vertical-align: middle;
height: 1.2em;
}
img.inlineImage14 {
vertical-align: middle;
height: 1.4em;
}
img.inlineImage16 {
vertical-align: middle;
height: 1.6em;
}
img.inlineImage18 {
vertical-align: middle;
height: 1.8em;
}
img.inlineImage2 {
vertical-align: middle;
height: 2em;
}
img.inlineImage22 {
vertical-align: middle;
height: 2.2em;
}
img.inlineImage24 {
vertical-align: middle;
height: 2.4em;
}
img.inlineImage26 {
vertical-align: middle;
height: 2.6em;
}
img.inlineImage28 {
vertical-align: middle;
height: 2.8em;
}
img.inlineImage3 {
vertical-align: middle;
height: 3em;
}
.btn-play {
cursor: pointer;
background-color: var(--macao-primary-color);
border-radius: 5px;
border: none;
padding: 5px 10px;
color: white;
}
.notextplay {
font-size: 0; /* hides text */
color: white;
}
.notextplay i {
font-size: medium; /* keep icon visible */
}
p#gapfill-container {
white-space: pre-wrap;
line-height: 3em;
}
// Tweak the existing SurveyJS styles to have "inline" dropdown menus
// and text fields that don't take too much space
select.sd-dropdown.inline-dropdown, input.sd-input.inline-input {
display: inline-block;
width: fit-content;
padding: 8px;
}
// ***********************************
// User Feedback in quizz
// ***********************************
#correct-indic.incorrect {
background-color: var(--feedback-error-color);
}
#correct-indic.correct {
background-color: var(--feedback-sucess-color);
}
#correct-indic {
border-radius: 8px;
padding: 16px;
p {
margin: 0;
}
}
// ***********************************
// Hide disabled checkboxes in some quizz
// ***********************************
.hide-disabled-checkboxes {
.sd-checkbox--readonly {
span.sd-checkbox__decorator {
display: none;
}
}
}
// ***********************************
// Align one line radio buttons
// ***********************************
.one-line {
display: flex;
align-items: center;
.sd-question__header {
margin-top: 0;
max-width: 150px;
}
}
// ***********************************
// General look and feel
// ***********************************
.book-page > article {
background-color: var(--secondary-background-color);
border-radius: 15px;
}
.book-page > article {
padding: 16px;
margin-top: 16px;
}
.book-search {
border-radius: 10px;
padding: 0px 5px;
background-color: var(--secondary-background-color);
}
a {
color: var(--macao-primary-color);
}
a:visited {
color: var(--macao-primary-color) !important;
}
a.active {
color: var(--macao-secondary-font-color) !important;
background-color: var(--macao-primary-color);
border-radius: 5px;
padding: 5px;
margin-right: 5px;
}
// Hide element when width is less than 900px
@media (min-width: 900px) {
.book-icon {
display: none;
}
}
.sd-btn {
padding: 5px 10px !important;
}
// ***********************************
// Comment on page
// ***********************************
.comment-trigger {
cursor: pointer;
color: var(--macao-primary-color);
text-decoration: underline;
}
em:not(:has(strong)) {
background-color: lightgray;
padding: 3px;
padding-right: 4px;
border-radius: 5px;
}
// ***********************************
// Dropdowns
// ***********************************
// Min width for dropdowns
.sv-dropdown_select-wrapper {
min-width: 150px !important;
}
// ***********************************
// .sv-title-actions__title
// ***********************************
.sv-title-actions__title {
white-space: break-spaces !important;
}
.one-line {
.sd-question__header {
max-width: 60%;
}
.sv-string-viewer {
display: inline-flex;
align-items: center;
gap: 5px;
max-width: 100%;
flex-wrap: wrap;
}
}
@import "plugins/_scrollbars.scss";
console.log('Custom JS loaded');
$(document).ready(function() {
console.log('Custom JS executed');
// Add specific classes to play buttons
$('button:contains("Play")').html('<i class="fa-solid fa-play"></i> Écouter').addClass('btn-play').prop('title','Écouter');
// Add Title on top of the page
$('.book-header').show();
$('.commentaireInfo').hide();
// $('.commentaireInfo').prop('popover', 'auto');
$('.commentaireInfoGroup').show();
// Comment popover
$('[spanid]').addClass('comment-trigger').each(function (){
const spanid = $(this).attr('spanid');
if($("[commentaireid=" + spanid + "]").length !== 0) {
$(this).popover({
trigger: 'click',
html: true,
placement: 'auto',
content: $("[commentaireid=" + spanid + "]")
});
$("[commentaireid=" + spanid + "]").toggle()
}
});
// $(document).on('click', function() {
// $('[spanid]').popover('hide');
// });
// Simplify th in td
$('th').each(function () {
// Store the current content of the <th>
const content = $(this).html();
// Replace <th> with a <td> containing the same content
$(this).replaceWith(`<td>${content}</td>`);
});
// Tricky way to add gap between play button and the next element
// like http://localhost:1313/macao-hugo/activities/mosorgmacao_fusion_ss_barre/mosmod10/mosmod11/mosetp129/pg334/
$(".btn-play").parent().siblings().css("margin-top", "10px")
if ($(window).width() > 900) {
$('.book-icon').hide();
}
});
/**
* Custom SurveyJS question type for a "fill-in-the-gaps" text,
* with an open-ended text input for each gap
*/
export const gapfillOpenWidget = {
name: "gapfill-open",
title: "Gap-Fill Text (Open)",
/**
* This function should return true when the widget and all needed resources
* are loaded
*/
widgetIsLoaded: function () {
return true;
},
/**
* This function should return true if the widget should be applied to the question */
isFit: function (question) {
return question.getType() === this.name;
},
init() {
//Register a new type using the empty question as the base.
Survey.Serializer.addClass(this.name, [], null, "empty");
},
/** Static HTML template rendered by SurveyJS */
htmlTemplate: '<p id="gapfill-container"><template id="template-gap"><input type="text" class="sd-input inline-input"/></template></p>',
/**
* Function called after the HTML template is rendered. This time we actually have the `question` object
* and the `el` element, to build the question according to the JSON
*/
afterRender: function (question, el) {
// The gap-fill text is made of segments, which are either plain pieces
// of text (strings) or "gaps" (objects).
// We append these to build the text, turning strings into <span>s and
// gaps into <input> text fields
let nbGaps = 0;
const segmentElems = new DocumentFragment(); // a bit faster than mutating the DOM all the time
const gapTemplate = document.getElementById("template-gap").content.firstChild;
for (const segment of question.jsonObj.segments) {
let segmentElem;
if (typeof segment === 'string' || segment instanceof String) {
segmentElem = document.createElement("span");
segmentElem.innerText = segment;
} else {
// It's a gap
// Create the <input> element
segmentElem = gapTemplate.cloneNode(true);
// Add the size attribute if present
if (segment.hasOwnProperty("size")) {
segmentElem.setAttribute("size", segment.size);
}
segmentElem.setAttribute("data-index", nbGaps); // The node knows its index
// Add callback to update the question's value when the input value changes
segmentElem.addEventListener("change", (e) => {
// The input node knows its index, therefore is able to update the question value at the correct index
question.value[parseInt(e.target.getAttribute("data-index"))] = e.target.value;
});
// Add callback to update the DOM nodes when the question's value changes
question.valueChangedCallback = () => {
const gaps = question.wrapperElement.querySelectorAll("#gapfill-container input");
if (question.value instanceof Array && question.value.length === gaps.length) {
for (const i in question.value) {
gaps[i].value = question.value[i];
}
} else {
// Unknown value -> reset all fields
for (const gap of gaps) {
gap.value = "";
}
}
}
nbGaps++;
}
// Add segment
segmentElems.appendChild(segmentElem);
}
// Initialize question value array
question.value = new Array(nbGaps);
// Finally add everything to the DOM
el.appendChild(segmentElems);
},
};
\ No newline at end of file
......@@ -51,11 +51,25 @@ export const gapfillSelectWidget = {
optionElem.innerText = opt;
segmentElem.appendChild(optionElem);
}
// Add listener to update the question's value when the selector's value changes
// Add callback to update the question's value when the selector's value changes
segmentElem.addEventListener("change", (e) => {
// The select node knows its index, therefore is able to update the question value at the correct index
question.value[parseInt(e.target.getAttribute("data-index"))] = e.target.value;
});
// Add callback to update the DOM nodes when the question's value changes
question.valueChangedCallback = () => {
const gaps = question.wrapperElement.querySelectorAll("#gapfill-container select");
if (question.value instanceof Array && question.value.length === gaps.length) {
for (const i in question.value) {
gaps[i].value = question.value[i];
}
} else {
// Unknown value -> reset all fields
for (const gap of gaps) {
gap.value = undefined;
}
}
}
nbGaps++;
}
// Add segment
......
function showCorrectIndic(correct) {
let indic = document.getElementById("correct-indic");
if (correct === undefined) {
indic.innerText = "";
} else if (correct) {
indic.innerText = "Correct";
indic.classList.add("correct");
/**
* Checks whether all questions are correctly answered and displays the overall feedback.
*/
function checkAllQuestions(questions, surveyConfig, converter) {
let allCorrect = true;
questions.forEach((question) => {
if (!question.isAnswerCorrect()) {
allCorrect = false;
}
});
if (allCorrect) {
correctIndic.node.className = "correct";
surveyConfig.correctComment = surveyConfig.correctComment.replace(/\n\n/g, "<br>");
correctIndic.commentNode.innerHTML = converter.renderInline(surveyConfig.correctComment) || "All answers are correct!";
} else {
correctIndic.node.className = "incorrect";
surveyConfig.incorrectComment = surveyConfig.incorrectComment.replace(/\n\n/g, "<br>");
correctIndic.commentNode.innerHTML = converter.renderInline(surveyConfig.incorrectComment) || "Some answers are incorrect!";
}
}
/**
* Clears all answers and resets the feedback indicator.
*/
function clearAllQuestions(questions) {
questions.forEach((question) => {
question.value = undefined;
});
correctIndic.node.className = "hidden";
correctIndic.commentNode.innerHTML = "";
}
/**
* Toggles the visibility of the help comment for a question.
*/
function toggleHelpComment(question, htmlElement) {
const helpCommentElement = htmlElement.querySelector('.help-comment');
if (helpCommentElement) {
// If help comment is already shown, remove it
helpCommentElement.remove();
} else {
indic.innerText = "Incorrect";
indic.classList.remove("correct");
// Otherwise, create and show the help comment
const newHelpCommentElement = document.createElement('div');
newHelpCommentElement.className = 'help-comment';
newHelpCommentElement.innerHTML = question.helpComment;
htmlElement.appendChild(newHelpCommentElement);
}
}
let correctIndic;
// The following allows to define complementary attributes in survey JSONS
$(function () {
// Register our custom question types
Survey.CustomWidgetCollection.Instance.add(gapfillSelectWidget, gapfillSelectWidget.name);
correctIndic = {
node: document.getElementById("correct-indic"),
commentNode: document.querySelector("#correct-indic .comment"),
};
Survey.JsonObject.metaData.addProperty("question", {
name: "customFormat",
type: "text",
category: "general",
default: "",
visibleIndex: 0,
});
Survey.JsonObject.metaData.addProperty("survey", {
name: "isSurveySummaryVisible",
type: "boolean",
category: "general",
default: "false",
visibleIndex: 0,
});
Survey.JsonObject.metaData.addProperty("question", {
name: "helpComment",
type: "text",
category: "general",
default: "",
visibleIndex: 0,
});
Survey.JsonObject.metaData.addProperty("question", {
name: "compComment",
type: "text",
category: "general",
default: "",
visibleIndex: 0,
});
Survey.JsonObject.metaData.addProperty("question", {
name: "transcript",
type: "text",
category: "general",
default: "",
visibleIndex: 0,
});
// Load survey model from JSON
const survey = new Survey.Model(jsonStatic);
const questions = survey.getAllQuestions();
if (questions.length === 1) {
console.log("Survey has only one question");
console.log(questions[0]);
const regex = /pg\d\d\d/g;
if (questions[0].title.match(regex)) {
questions[0].titleLocation = "hidden";
}
}
// Instantiate `markdown-it`
const converter = markdownit({
html: true // Support HTML tags in the source (unsafe, see documentation)
});
survey.onTextMarkdown.add((_, options) => {
let str = converter.renderInline(options.text);
options.html = str;
});
survey.onAfterRenderQuestion.add((survey, options) => {
if (options.question.customFormat) {
if (options.question.customFormat === "one_line") {
$(options.htmlElement).addClass("one-line");
}
if (options.question.customFormat === "hide_disabled-checkboxes") {
$(options.htmlElement).addClass("hide-disabled-checkboxes");
}
}
// Display compComment if present
if (options.question.compComment) {
const compCommentElement = document.createElement('div');
compCommentElement.className = 'comp-comment';
compCommentElement.innerHTML = options.question.compComment;
options.htmlElement.appendChild(compCommentElement);
}
// Add "Corrigé" button if helpComment is present
if (options.question.helpComment) {
const helpButton = document.createElement('button');
helpButton.innerHTML = 'Corrigé';
helpButton.className = 'sd-btn sd-btn--action nav-input';
helpButton.onclick = () => toggleHelpComment(options.question, options.htmlElement);
options.htmlElement.appendChild(helpButton);
}
if( options.question.getType() === "dropdown" ) {
// Very ugly fix to avoid sliding dropdown on focus
$('.book-article').css({
'will-change' : 'unset',
'transition': 'none',
});
$('.book-page').css({
'will-change' : 'unset',
'transition': 'none',
});
}
});
// Add "Check answers" button
survey.addNavigationItem({
id: "sv-nav-clear-page",
title: "Check",
id: "sv-nav-check",
title: "Vérifier",
action: () => {
survey.currentPage.questions.forEach((question) => {
console.log("question = ", question);
console.log(" correct = ", question.isAnswerCorrect());
showCorrectIndic(question.isAnswerCorrect());
checkAllQuestions(survey.currentPage.questions, jsonStatic, converter);
},
css: "nav-button",
innerCss: "sd-btn sd-btn--action nav-input"
});
// Add "Clear answers" button
survey.addNavigationItem({
id: "sv-nav-clear",
title: "Effacer",
action: () => {
clearAllQuestions(survey.currentPage.questions);
},
css: "nav-button",
innerCss: "sd-btn nav-input"
});
survey.onComplete.add(function (sender) {
if (sender.isSurveySummaryVisible) {
console.log("Survey summary is visible");
const userData = sender.data;
const summaryDiv = document.createElement("div");
summaryDiv.id = "surveySummary";
let html = "<h3>Your Results:</h3><ul>";
sender.getAllQuestions().forEach(q => {
const userAnswer = userData[q.name];
const correctAnswer = q.correctAnswer;
const isCorrect = userAnswer === correctAnswer;
html += `<li><b>${q.title}</b><br>
Your answer: ${userAnswer} <br>
Correct answer: ${correctAnswer} <br>
<span style="color:${isCorrect ? 'green' : 'red'};">
${isCorrect ? "Correct" : "Incorrect"}
</span>`;
// Check if the transcript property exists and add an image if it does
if (q.transcript) {
html += `<br><img src="${q.transcript}" alt="Transcript Image" class="inlineImage">`;
}
html += `</li><br>`;
});
survey.onValueChanged.add((_, options) => {
console.log(options)
html += "</ul>";
summaryDiv.innerHTML = html;
// Append the summary after the survey container
document.getElementById("surveyContainer").appendChild(summaryDiv);
}
});
// Ugly tricks to avoid creating a new theme
const customTheme = SurveyTheme.DefaultLightPanelless;
const primColor = getComputedStyle(document.documentElement, null).getPropertyValue('--macao-primary-color');
customTheme.cssVariables["--sjs-primary-backcolor"] = primColor;
customTheme.cssVariables["--sjs-primary-backcolor-dark"] = primColor;
// Apply theme
survey.applyTheme(SurveyTheme.DefaultLightPanelless);
console.log("Survey theme applied", SurveyTheme.DefaultLightPanelless);
survey.showCompleteButton = false;
// Inflate the survey in the page
$("#surveyContainer").Survey({ model: survey });
});
$(document).ready(function() {
$('button:contains("Play")').html('<i class="fa-solid fa-play"></i> Écouter').addClass('btn-play').prop('title', 'Écouter');
});
#correct-indic {
color: var(--sd-rating-bad-color);
font-weight: bold;
}
#correct-indic.correct {
color: var(--sd-rating-good-color);
}
p#gapfill-container {
white-space: pre-wrap;
line-height: 3em;
}
select.sd-dropdown.inline-dropdown {
display: inline-block;
width: fit-content;
padding: 8px;
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>full</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
</buildSpec>
<natures>
</natures>
</projectDescription>
+++
title = "MACAO 3"
weight = 40
bookCollapseSection = true
+++
# MACAO 3
{{< section >}}
+++
title = "Assimilation"
weight = 20
bookCollapseSection = true
+++
# Assimilation
{{< section >}}