Commit 0abad13c authored by Curtis Adam's avatar Curtis Adam

Add a button to learn flashcards backwards

parent 51c5e5aa
......@@ -1226,6 +1226,11 @@
"showHelp": "",
"arsnovaApp": "Befrage dein Publikum mit unserem Audience-Response-System ARSnova auf arsnova.thm.de",
"arsnovaClick": "Befrage dein Publikum mit arsnova.click, der spielbasierten Variante von ARSnova",
"swapQuestionAnswer": {
"normal": "Zeige die Antwort zuerst",
"shuffled": "Zeige die Antwort zuerst für die Karteitypen: __cardTypes__",
"listSeparator": ", "
},
"markdeepEditor": {
"help": "Zeige alle Formatierungsoptionen von Markdeep",
"center": "Zentriere den Karteninhalt vertikal",
......@@ -1240,6 +1245,17 @@
}
}
},
"swapQuestionAnswer": {
"modal": {
"title": "Karten rückwärts lernen?",
"normal": "Möchtest du die Kartei rückwärts lernen?",
"shuffled": "Möchtest du die Karten folgender Karteitypen rückwärts lernen?<br> <b>__cardTypes__</b>",
"button": {
"forward": "Vorwärts lernen",
"backward": "Rückwärts lernen"
}
}
},
"courseIteration": {
"name": "Repetitorium",
"targetAudience": "Erstellt für…",
......
......@@ -8,9 +8,11 @@ import {CardIndex} from "./cardIndex";
import {Cards} from "./cards";
import {Cardsets} from "./cardsets";
import {SweetAlertMessages} from "./sweetAlert";
import {CardType} from "./cardTypes";
let keyEventsUnlocked = true;
let lastActiveCardString = "lastActiveCard";
let isReset = false;
export let CardNavigation = class CardNavigation {
......@@ -35,19 +37,23 @@ export let CardNavigation = class CardNavigation {
$(".cardNavigation > li:nth-child(" + index + ") a").removeClass('btn-default').addClass('btn-primary').addClass('card-navigation-active');
}
static filterNavigation (cubeSides, mode) {
static filterNavigation (cubeSides, mode = undefined) {
if (cubeSides === undefined) {
return [""];
}
if (mode === false) {
mode = undefined;
}
let filteredSides = [];
let index = 0;
for (let i = 0; i < cubeSides.length; i++) {
if (cubeSides[i].isAnswer === mode) {
cubeSides[i].index = index++;
filteredSides.push(cubeSides[i]);
if (Session.get('swapAnswerQuestion') && CardType.isCardTypesWithSwapAnswerQuestionButton(Session.get('cardType'))) {
if (cubeSides[i].isAnswerFocus !== undefined && cubeSides[i].isAnswerFocus === true) {
cubeSides[i].index = index++;
filteredSides.push(cubeSides[i]);
}
} else {
if (cubeSides[i].isAnswer === mode) {
cubeSides[i].index = index++;
filteredSides.push(cubeSides[i]);
}
}
}
return filteredSides;
......@@ -199,6 +205,18 @@ export let CardNavigation = class CardNavigation {
}
}
static resetNavigation () {
isReset = true;
this.toggleVisibility(false);
}
static checkIfReset () {
if (isReset) {
isReset = false;
this.toggleVisibility(true);
}
}
static skipAnswer (scrollRight = true) {
if (scrollRight) {
$('.scrollRight').addClass('pressed');
......@@ -291,7 +309,7 @@ export let CardNavigation = class CardNavigation {
let keyCodes = [];
CardVisuals.toggleZoomContainer(true);
if (!$('#input-search').is(":focus") && !$('#lightbox').is(":visible") && keyEventsUnlocked) {
if (!$('#input-search').is(":focus") && !$('#lightbox').is(":visible") && !$('#cardSettingsModal').is(":visible") && keyEventsUnlocked) {
keyEventsUnlocked = false;
if (Route.isCardset() || Route.isBox() || Route.isMemo()) {
keyCodes = [9];
......
import {Session} from "meteor/session";
import {Cardsets} from "./cardsets.js";
//0: Lernkartei / Flashcard
//1: Vokabelkartei / Vocabulary
......@@ -25,6 +26,7 @@ let cardTypesWithPresentationMode = [0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14,
let cardTypesWithContrastButton = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15];
let cardTypesWithNotesForDifficultyLevel = [2];
let cardTypesWithCardsetTitleNavigation = [14];
let cardTypesWithSwapAnswerQuestionButton = [1, 3, 6];
let cardTypesOrder = [{cardType: 2}, {cardType: 0}, {cardType: 15}, {cardType: 3}, {cardType: 6}, {cardType: 13}, {cardType: 12}, {cardType: 11}, {cardType: 5}, {cardType: 1}, {cardType: 10}, {cardType: 7}, {cardType: 4}, {cardType: 8}, {cardType: 9}, {cardType: 14}];
//0: left
......@@ -33,6 +35,7 @@ let cardTypesOrder = [{cardType: 2}, {cardType: 0}, {cardType: 15}, {cardType: 3
//3: justify
let defaultTextAlign = 0;
let defaultCentered = true;
let swapAnserQuestionCardTypeResult = [];
let cardTypeCubeSides = [
//0: Lernkartei / Flashcard
......@@ -359,6 +362,37 @@ export let CardType = class CardType {
return cardTypesWithDifficultyLevel;
}
static isCardTypesWithSwapAnswerQuestionButton (cardType) {
return cardTypesWithSwapAnswerQuestionButton.includes(cardType);
}
/**
*
* @param cubeSides = The cube sides of the card
* @param cardType = The card type
* @param type = 0 = return the content ID, 1 = return the style
* @returns {*}
*/
static getActiveSideData (cubeSides, cardType, type = 0) {
if (Session.get('swapAnswerQuestion') && this.isCardTypesWithSwapAnswerQuestionButton(cardType)) {
for (let i = 0, cubeSidesLength = cubeSides.length; i < cubeSidesLength; i++) {
if (cubeSides[i].isAnswerFocus) {
if (type) {
return cubeSides[i].defaultStyle;
} else {
return cubeSides[i].contentId;
}
}
}
} else {
if (type) {
return cubeSides[0].defaultStyle;
} else {
return cubeSides[0].contentId;
}
}
}
static getSortQuery (cardType) {
let sortQuery = {};
sortQuery.subject = 1;
......@@ -388,7 +422,7 @@ export let CardType = class CardType {
static contentWithLearningGoalPlaceholder (contentId, cardType) {
let cubeSides = this.getCardTypeCubeSides(cardType);
for (let i = 0; i < cubeSides.length; i++) {
for (let i = 0, cubeSidesLength = cubeSides.length; i < cubeSidesLength; i++) {
if (cubeSides[i].contentId === contentId) {
return cubeSides[i].gotLearningGoalPlaceholder;
}
......@@ -407,6 +441,55 @@ export let CardType = class CardType {
return cardTypesWithCardsetTitleNavigation.includes(cardType);
}
static gotCardTypesWithSwapAnswerQuestionButton (cardset_id) {
let cardset = Cardsets.findOne({_id: cardset_id}, {fields: {shuffled: 1, cardGroups: 1, cardType: 1}});
if (cardset !== undefined) {
swapAnserQuestionCardTypeResult = [];
let foundCardset = false;
if (cardset.shuffled) {
for (let i = 0, cardGroupsLength = cardset.cardGroups.length; i < cardGroupsLength; i++) {
let cardType = Cardsets.findOne({_id: cardset.cardGroups[i]}).cardType;
if (cardTypesWithSwapAnswerQuestionButton.includes(cardType)) {
if (!swapAnserQuestionCardTypeResult.includes(cardType)) {
swapAnserQuestionCardTypeResult.push(cardType);
}
foundCardset = true;
}
}
} else {
foundCardset = cardTypesWithSwapAnswerQuestionButton.includes(cardset.cardType);
if (foundCardset) {
swapAnserQuestionCardTypeResult.push(cardset.cardType);
}
}
return foundCardset;
}
}
/**
* Returns a description of all card types that can swap their answer and question
* @param sortMode: 0 = Sort by name, 1 = Sort by card Type Order
* @returns {string}
*/
static getCardTypesWithSwapAnswerQuestionTooltip (sortMode = 0) {
let array = [];
if (sortMode === 0) {
for (let i = 0, cardTypeLength = swapAnserQuestionCardTypeResult.length; i < cardTypeLength; i++) {
array.push(TAPi18n.__('card.cardType' + swapAnserQuestionCardTypeResult[i] + '.name'));
}
array.sort();
} else {
for (let i = 0, cardTypesOrderLength = cardTypesOrder.length; i < cardTypesOrderLength; i++) {
for (let k = 0, cardTypeLength = swapAnserQuestionCardTypeResult.length; k < cardTypeLength; k++) {
if (cardTypesOrder[i].cardType === swapAnserQuestionCardTypeResult[k]) {
array.push(TAPi18n.__('card.cardType' + swapAnserQuestionCardTypeResult[k] + '.name'));
}
}
}
}
return array.join(TAPi18n.__('card.tooltip.swapQuestionAnswer.listSeparator'));
}
static gotLearningModes (cardType) {
return cardTypesWithLearningModes.includes(cardType);
}
......@@ -439,8 +522,8 @@ export let CardType = class CardType {
let centerTextElement = Array(6).fill(defaultCentered);
let textAlignType = Array(6).fill(defaultTextAlign);
let cubeSides = this.getCardTypeCubeSides(cardType);
for (let i = 0; i < centerTextElement.length; i++) {
for (let l = 0; l < cubeSides.length; l++) {
for (let i = 0, centerTextElementLength = centerTextElement.length; i < centerTextElementLength; i++) {
for (let l = 0, cubeSidesLength = cubeSides.length; l < cubeSidesLength; l++) {
if (cubeSides[l].contentId === (i + 1)) {
if (cubeSides[l].defaultCentered !== undefined) {
centerTextElement[i] = cubeSides[l].defaultCentered;
......
......@@ -203,7 +203,7 @@ export let CardVisuals = class CardVisuals {
style = Session.get('activeCardStyle');
} else {
let cubeSides = CardType.getCardTypeCubeSides(cardType);
style = cubeSides[0].defaultStyle;
style = CardType.getActiveSideData(cubeSides, cardType, 1);
}
if (style === "default") {
if (!CardType.gotDifficultyLevel(cardType)) {
......
<template name="flashcards">
{{> copyCard}}
{{> deleteCardForm}}
{{> cardSettingsModal}}
<div class="flip-container"
id="set-details-region">
<div>
......
......@@ -15,6 +15,7 @@ import '/client/hammer.js';
import './header/header.js';
import './content/content.js';
import './navigation/navigation.js';
import './modal/settings.js';
import {BertAlertVisuals} from "../../api/bertAlertVisuals";
/*
......@@ -32,7 +33,6 @@ Template.flashcards.onCreated(function () {
} else {
Session.set('activeCardset', Cardsets.findOne({"_id": Router.current().params._id}));
}
Session.set('reverseViewOrder', false);
Session.set('selectedHint', undefined);
Session.set('isQuestionSide', true);
if (Session.get('activeCard') === undefined) {
......@@ -98,8 +98,8 @@ Template.flashcards.helpers({
if (resetData) {
let cubeSides = CardType.getCardTypeCubeSides(this.cardType);
Session.set('cardType', this.cardType);
Session.set('activeCardContentId', cubeSides[0].contentId);
Session.set('activeCardStyle', cubeSides[0].defaultStyle);
Session.set('activeCardContentId', CardType.getActiveSideData(cubeSides, this.cardType));
Session.set('activeCardStyle', CardType.getActiveSideData(cubeSides, this.cardType, 1));
}
return true;
}
......@@ -173,7 +173,6 @@ Template.flashcardsEmpty.onCreated(function () {
if (Session.get('fullscreen')) {
CardVisuals.toggleFullscreen();
}
Session.set('reverseViewOrder', false);
});
Template.flashcardsEmpty.helpers({
......
......@@ -200,18 +200,18 @@ Template.cardContentActive.events({
Template.cardContentInactive.helpers({
isCentered: function () {
let cubeSides = CardType.getCardTypeCubeSides(this.cardType);
return CardVisuals.isCentered(cubeSides[0].contentId, this.centerTextElement);
return CardVisuals.isCentered((CardType.getActiveSideData(cubeSides, this.cardType)), this.centerTextElement);
},
isLeftAlign: function () {
let cubeSides = CardType.getCardTypeCubeSides(this.cardType);
return CardVisuals.isLeftAlign(cubeSides[0].contentId, this.alignType);
return CardVisuals.isLeftAlign((CardType.getActiveSideData(cubeSides, this.cardType)), this.alignType);
},
gotContent: function () {
if (!Route.isCardset()) {
return true;
} else {
let cubeSides = CardType.getCardTypeCubeSides(this.cardType);
switch (cubeSides[0].contentId) {
switch (CardType.getActiveSideData(cubeSides, this.cardType)) {
case 1:
return this.front !== '' && this.front !== undefined;
case 2:
......@@ -229,7 +229,7 @@ Template.cardContentInactive.helpers({
},
getContent: function () {
let cubeSides = CardType.getCardTypeCubeSides(this.cardType);
switch (cubeSides[0].contentId) {
switch (CardType.getActiveSideData(cubeSides, this.cardType)) {
case 1:
return this.front;
case 2:
......@@ -246,6 +246,6 @@ Template.cardContentInactive.helpers({
},
getPlaceholder: function () {
let cubeSides = CardType.getCardTypeCubeSides(this.cardType);
return CardType.getPlaceholderText(cubeSides[0].contentId, this.cardType, this.learningGoalLevel);
return CardType.getPlaceholderText((CardType.getActiveSideData(cubeSides, this.cardType)), this.cardType, this.learningGoalLevel);
}
});
<template name="cardSettingsModal">
<div class="modal fade" id="cardSettingsModal" tabindex="-1" role="dialog" aria-labelledby="cardSettingsModalTitle">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 id="cardSettings" class="modal-title">{{_ "swapQuestionAnswer.modal.title"}}
</h4>
</div>
<div class="modal-body">
{{{getText}}}
</div>
<div class="modal-footer">
<button class="btn btn-raised btn-success"
id="cardSettingsForward">{{_ "swapQuestionAnswer.modal.button.forward"}}</button>
<button class="btn btn-raised btn-success"
id="cardSettingsBackward">{{_ "swapQuestionAnswer.modal.button.backward"}}</button>
</div>
</div>
</div>
</div>
</template>
import {Template} from "meteor/templating";
import {Session} from "meteor/session";
import {Route} from "../../../api/route";
import {CardType} from "../../../api/cardTypes";
import {Cardsets} from "../../../api/cardsets";
import {CardNavigation} from "../../../api/cardNavigation";
import "./settings.html";
/*
* ############################################################################
* cardSettingsModal
* ############################################################################
*/
Template.cardSettingsModal.helpers({
getText: function () {
if (Route.isDemo() || Cardsets.findOne({_id: Router.current().params._id}).shuffled) {
if (Route.isDemo()) {
CardType.gotCardTypesWithSwapAnswerQuestionButton(Cardsets.findOne({name: "DemoCardset", shuffled: true})._id);
} else {
CardType.gotCardTypesWithSwapAnswerQuestionButton(Router.current().params._id);
}
return TAPi18n.__('swapQuestionAnswer.modal.shuffled', {cardTypes: CardType.getCardTypesWithSwapAnswerQuestionTooltip()});
} else {
return TAPi18n.__('swapQuestionAnswer.modal.normal');
}
}
});
Template.cardSettingsModal.events({
"click #cardSettingsForward": function () {
$('#cardSettingsModal').modal('hide');
},
"click #cardSettingsBackward": function () {
Session.set('swapAnswerQuestion', 1);
CardNavigation.resetNavigation();
$('#cardSettingsModal').modal('hide');
}
});
......@@ -39,14 +39,15 @@
</template>
<template name="cardNavigationItem">
<li>
<a class="btn {{#if isFirstButton this.index}}btn-primary card-navigation-active{{else}}btn-default{{/if}} btn-raised switchCardSide"
type="button"
data-content-id="{{this.contentId}}"
data-navigation-id="{{this.index}}" data-side="{{this.side}}"
data-style="{{this.defaultStyle}}"
tabindex="{{getTabIndex this.index}}">{{getTitle}}</a>
</li>
<li>
<a class="btn {{#if isFirstButton
this.index}}btn-primary card-navigation-active{{else}}btn-default{{/if}} btn-raised switchCardSide"
type="button"
data-content-id="{{this.contentId}}"
data-navigation-id="{{this.index}}" data-side="{{this.side}}"
data-style="{{this.defaultStyle}}"
tabindex="{{getTabIndex this.index}}">{{getTitle}}</a>
</li>
</template>
<template name="cardArrowNavigation">
......
......@@ -28,7 +28,6 @@ Template.cardNavigation.onCreated(function () {
if (Session.get('fullscreen') && !Route.isPresentationOrDemo()) {
CardVisuals.toggleFullscreen();
}
Session.set('reverseViewOrder', false);
CardNavigation.toggleVisibility(true);
});
......@@ -51,7 +50,7 @@ Template.cardNavigationEnabled.helpers({
});
Template.cardNavigationEnabled.onRendered(function () {
CardNavigation.selectButton(1);
CardNavigation.selectButton();
CardVisuals.setSidebarPosition();
});
......@@ -74,7 +73,11 @@ Template.cardNavigationEnabledAnswer.helpers({
});
Template.cardNavigationEnabledAnswer.onRendered(function () {
CardNavigation.selectButton(Session.get('answerFocus'));
if (Session.get('swapAnswerQuestion') && CardType.isCardTypesWithSwapAnswerQuestionButton(Session.get('cardType'))) {
CardNavigation.selectButton();
} else {
CardNavigation.selectButton(Session.get('answerFocus'));
}
});
/*
......@@ -91,12 +94,16 @@ Template.cardNavigationEnabledQuestion.events({
Template.cardNavigationEnabledQuestion.helpers({
getCardTypeSides: function () {
return CardNavigation.filterNavigation(CardType.getCardTypeCubeSides(Session.get('cardType')), false);
if (Session.get('swapAnswerQuestion') && CardType.isCardTypesWithSwapAnswerQuestionButton(Session.get('cardType'))) {
return CardNavigation.filterNavigation(CardType.getCardTypeCubeSides(Session.get('cardType')), true);
} else {
return CardNavigation.filterNavigation(CardType.getCardTypeCubeSides(Session.get('cardType')));
}
}
});
Template.cardNavigationEnabledQuestion.onRendered(function () {
CardNavigation.selectButton(1);
CardNavigation.selectButton();
});
/*
......@@ -149,3 +156,13 @@ Template.cardArrowNavigation.events({
CardNavigation.switchCard();
}
});
/*
* ############################################################################
* cardNavigationDisabled
* ############################################################################
*/
Template.cardNavigationDisabled.onCreated(function () {
CardNavigation.checkIfReset();
});
<template name="cardSidebarItemSwapQuestionAnswer">
{{#if gotCardTypesWithSwapAnswerQuestionButton}}
<i class="card-button cardHeaderItem fa swapQuestionAnswer fa fa-exchange {{#if
questionAnswerSwapped}}pressed{{/if}}"
data-type="cardNavigation"
title="{{getTooltip}}"></i>
{{/if}}
</template>
import {Template} from "meteor/templating";
import {CardType} from "../../../../api/cardTypes";
import {Cardsets} from "../../../../api/cardsets";
import {Route} from "../../../../api/route";
import {Session} from "meteor/session";
import {CardNavigation} from "../../../../api/cardNavigation";
import "./swapQuestionAnswer.html";
Session.setDefault('swapAnswerQuestion', 0);
/*
* ############################################################################
* cardSidebarItemSwapQuestionAnswer
* ############################################################################
*/
Template.cardSidebarItemSwapQuestionAnswer.onCreated(function () {
Session.set('swapAnswerQuestion', 0);
CardNavigation.resetNavigation();
});
Template.cardSidebarItemSwapQuestionAnswer.onDestroyed(function () {
Session.set('swapAnswerQuestion', 0);
});
Template.cardSidebarItemSwapQuestionAnswer.onRendered(function () {
if (!Route.isDemo() && CardType.gotCardTypesWithSwapAnswerQuestionButton(Router.current().params._id)) {
$('#cardSettingsModal').modal('show');
$('.carousel-inner .item .cardContent').addClass('blurHideAnswerQuestion');
$('#cardSettingsModal').on('hidden.bs.modal', function () {
$('.carousel-inner .item .cardContent').removeClass('blurHideAnswerQuestion');
});
}
});
Template.cardSidebarItemSwapQuestionAnswer.helpers({
gotCardTypesWithSwapAnswerQuestionButton: function () {
if (Route.isDemo()) {
return CardType.gotCardTypesWithSwapAnswerQuestionButton(Cardsets.findOne({
name: "DemoCardset",
kind: "demo",
shuffled: true
})._id);
} else {
return CardType.gotCardTypesWithSwapAnswerQuestionButton(Router.current().params._id);
}
},
questionAnswerSwapped: function () {
return Session.get('swapAnswerQuestion');
},
getTooltip: function () {
if (Route.isDemo() || Cardsets.findOne({_id: Router.current().params._id}).shuffled) {
return TAPi18n.__('card.tooltip.swapQuestionAnswer.shuffled', {cardTypes: CardType.getCardTypesWithSwapAnswerQuestionTooltip()});
} else {
return TAPi18n.__('card.tooltip.swapQuestionAnswer.normal');
}
}
});
Template.cardSidebarItemSwapQuestionAnswer.events({
"click .swapQuestionAnswer": function () {
if (Session.get('swapAnswerQuestion')) {
Session.set('swapAnswerQuestion', 0);
} else {
Session.set('swapAnswerQuestion', 1);
}
CardNavigation.resetNavigation();
}
});
......@@ -138,6 +138,7 @@
</template>
<template name="flashcardSidebarLeitnerLeft">
{{> cardSidebarItemSwapQuestionAnswer}}
{{> flashcardSidebarDictionary}}
</template>
......@@ -148,6 +149,7 @@
</template>
<template name="flashcardSidebarWozniakLeft">
{{> cardSidebarItemSwapQuestionAnswer}}
{{> flashcardSidebarDictionary}}
</template>
......
......@@ -3,6 +3,7 @@
import {Template} from "meteor/templating";
import {Session} from "meteor/session";
import {Route} from "../../../api/route";
import {CardNavigation} from "../../../api/cardNavigation";
import "./sidebar.html";
import "./item/arsnovaClick.js";
import "./item/arsnovaApp.js";
......@@ -18,8 +19,7 @@ import "./item/delete.js";
import "./item/edit.js";
import "./item/pomodoroButton.js";
import "./item/help.js";
import {CardNavigation} from "../../../api/cardNavigation";
import "./item/swapQuestionAnswer.js";
/*
......
......@@ -66,3 +66,7 @@
.showArsnovaApp, .showArsnovaClick {
padding: 0 4px !important;
}
#cardSettingsForward {
float: left;
}
......@@ -175,6 +175,10 @@ $zoom_slider_handle_offset: -25px;
//loading screen
$loading_screen_size: 150px;
//blur
$blur_hide_answer_question: 16px;
html {
position: relative;
min-height: 100%;
......@@ -968,3 +972,15 @@ th {
#showLicenseModal {
z-index: 1060;
}
#cardSettingsModal {
z-index: 9000;
}
.blurHideAnswerQuestion {
-webkit-filter: blur($blur_hide_answer_question);
-moz-filter: blur($blur_hide_answer_question);
-o-filter: blur($blur_hide_answer_question);
-ms-filter: blur($blur_hide_answer_question);
filter: blur($blur_hide_answer_question);
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment