MOBILE-2348 quiz: Fix question types
parent
d5c3523023
commit
5878d7c3eb
|
@ -12,7 +12,7 @@
|
|||
<form name="itemEdit" (ngSubmit)="addNote()">
|
||||
<ion-item>
|
||||
<ion-label>{{ 'addon.notes.publishstate' | translate }}</ion-label>
|
||||
<ion-select [(ngModel)]="publishState" name="publishState">
|
||||
<ion-select [(ngModel)]="publishState" name="publishState" interface="popover">
|
||||
<ion-option value="personal">{{ 'addon.notes.personalnotes' | translate }}</ion-option>
|
||||
<ion-option value="course">{{ 'addon.notes.coursenotes' | translate }}</ion-option>
|
||||
<ion-option value="site">{{ 'addon.notes.sitenotes' | translate }}</ion-option>
|
||||
|
|
|
@ -1,6 +1,16 @@
|
|||
<ion-item text-wrap class="addon-qbehaviour-deferredcbm-certainty-title" *ngIf="question.behaviourCertaintyOptions && question.behaviourCertaintyOptions.length">
|
||||
<p>{{ 'core.question.certainty' | translate }}</p>
|
||||
</ion-item>
|
||||
<ion-radio *ngFor="let option of question.behaviourCertaintyOptions" id="{{option.id}}" name="{{option.name}}" [ngModel]="question.behaviourCertaintySelected" [value]="option.value" [disabled]="option.disabled">
|
||||
<p><core-format-text [component]="component" [componentId]="componentId" [text]="option.text"></core-format-text></p>
|
||||
</ion-radio>
|
||||
<div *ngIf="question.behaviourCertaintyOptions && question.behaviourCertaintyOptions.length">
|
||||
<ion-item text-wrap class="addon-qbehaviour-deferredcbm-certainty-title" >
|
||||
<p>{{ 'core.question.certainty' | translate }}</p>
|
||||
</ion-item>
|
||||
<div radio-group [(ngModel)]="question.behaviourCertaintySelected" [name]="question.behaviourCertaintyOptions[0].name">
|
||||
<ion-item text-wrap *ngFor="let option of question.behaviourCertaintyOptions">
|
||||
<ion-label>
|
||||
<core-format-text [component]="component" [componentId]="componentId" [text]="option.text"></core-format-text>
|
||||
</ion-label>
|
||||
<ion-radio id="{{option.id}}" [value]="option.value" [disabled]="option.disabled"></ion-radio>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
<!-- ion-radio doesn't use an input. Create a hidden input to hold the selected value. -->
|
||||
<input type="hidden" [ngModel]="question.behaviourCertaintySelected" [attr.name]="question.behaviourCertaintyOptions[0].name">
|
||||
</div>
|
||||
|
|
|
@ -8,24 +8,26 @@
|
|||
<ng-container *ngTemplateOutlet="radioUnits"></ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ion-item text-wrap>
|
||||
<ion-row>
|
||||
<!-- Display unit select before the answer input. -->
|
||||
<ng-container *ngIf="question.select && question.selectFirst">
|
||||
<ng-container *ngTemplateOutlet="selectUnits"></ng-container>
|
||||
</ng-container>
|
||||
<ion-item text-wrap ion-grid>
|
||||
<ion-grid item-content>
|
||||
<ion-row>
|
||||
<!-- Display unit select before the answer input. -->
|
||||
<ng-container *ngIf="question.select && question.selectFirst">
|
||||
<ng-container *ngTemplateOutlet="selectUnits"></ng-container>
|
||||
</ng-container>
|
||||
|
||||
<!-- Input to enter the answer. -->
|
||||
<ion-col>
|
||||
<ion-input type="text" placeholder="{{ 'core.question.answer' | translate }}" [attr.name]="question.input.name" [value]="question.input.value" [disabled]="question.input.readOnly" [ngClass]='{"core-question-answer-correct": question.input.isCorrect === 1, "core-question-answer-incorrect": question.input.isCorrect === 0}' autocorrect="off">
|
||||
</ion-input>
|
||||
</ion-col>
|
||||
<!-- Input to enter the answer. -->
|
||||
<ion-col>
|
||||
<ion-input type="text" placeholder="{{ 'core.question.answer' | translate }}" [attr.name]="question.input.name" [value]="question.input.value" [disabled]="question.input.readOnly" [ngClass]='{"core-question-answer-correct": question.input.isCorrect === 1, "core-question-answer-incorrect": question.input.isCorrect === 0}' autocorrect="off">
|
||||
</ion-input>
|
||||
</ion-col>
|
||||
|
||||
<!-- Display unit select after the answer input. -->
|
||||
<ng-container *ngIf="question.select && !question.selectFirst">
|
||||
<ng-container *ngTemplateOutlet="selectUnits"></ng-container>
|
||||
</ng-container>
|
||||
</ion-row>
|
||||
<!-- Display unit select after the answer input. -->
|
||||
<ng-container *ngIf="question.select && !question.selectFirst">
|
||||
<ng-container *ngTemplateOutlet="selectUnits"></ng-container>
|
||||
</ng-container>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-item>
|
||||
|
||||
<!-- Display unit options after the answer input. -->
|
||||
|
@ -38,18 +40,26 @@
|
|||
<ng-template #selectUnits>
|
||||
<ion-col>
|
||||
<label *ngIf="question.select.accessibilityLabel" class="accesshide" for="{{question.select.id}}">{{ question.select.accessibilityLabel }}</label>
|
||||
<ion-select id="{{question.select.id}}" [name]="question.select.name" [ngModel]="question.select.selected">
|
||||
<ion-select id="{{question.select.id}}" [name]="question.select.name" [(ngModel)]="question.select.selected" interface="popover">
|
||||
<ion-option *ngFor="let option of question.select.options" [value]="option.value">{{option.label}}</ion-option>
|
||||
</ion-select>
|
||||
<!-- @todo: select fix? -->
|
||||
|
||||
<!-- ion-select doesn't use a select. Create a hidden input to hold the selected value. -->
|
||||
<input type="hidden" [ngModel]="question.select.selected" [attr.name]="question.select.name">
|
||||
</ion-col>
|
||||
</ng-template>
|
||||
|
||||
<!-- Template for units entered using radio buttons. -->
|
||||
<ng-template #radioUnits>
|
||||
<div radio-group [ngModel]="question.unit" [name]="question.optionsName">
|
||||
<ion-radio *ngFor="let option of question.options" [value]="option.value" [disabled]="option.disabled">
|
||||
<p>{{option.text}}</p>
|
||||
</ion-radio>
|
||||
<div radio-group [(ngModel)]="question.unit" [name]="question.optionsName">
|
||||
<ion-item text-wrap *ngFor="let option of question.options">
|
||||
<ion-label>
|
||||
<p>{{option.text}}</p>
|
||||
</ion-label>
|
||||
<ion-radio [value]="option.value" [disabled]="option.disabled"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<!-- ion-radio doesn't use an input. Create a hidden input to hold the selected value. -->
|
||||
<input type="hidden" [ngModel]="question.unit" [attr.name]="question.optionsName">
|
||||
</div>
|
||||
</ng-template>
|
||||
|
|
|
@ -45,6 +45,7 @@ export class AddonQtypeDdImageOrTextQuestion {
|
|||
protected topNode: HTMLElement;
|
||||
protected proportion = 1;
|
||||
protected selected: HTMLElement; // Selected element (being "dragged").
|
||||
protected resizeFunction;
|
||||
|
||||
/**
|
||||
* Create the this.
|
||||
|
@ -182,7 +183,10 @@ export class AddonQtypeDdImageOrTextQuestion {
|
|||
*/
|
||||
destroy(): void {
|
||||
this.stopPolling();
|
||||
window.removeEventListener('resize', this.resizeFunction);
|
||||
|
||||
if (this.resizeFunction) {
|
||||
window.removeEventListener('resize', this.resizeFunction);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -192,7 +196,7 @@ export class AddonQtypeDdImageOrTextQuestion {
|
|||
* @return {AddonQtypeDdImageOrTextQuestionDocStructure} The object.
|
||||
*/
|
||||
docStructure(slot: number): AddonQtypeDdImageOrTextQuestionDocStructure {
|
||||
const topNode = <HTMLElement> this.container.querySelector(`#core-question-${slot} .addon-qtype-ddimageortext-container`),
|
||||
const topNode = <HTMLElement> this.container.querySelector('.addon-qtype-ddimageortext-container'),
|
||||
dragItemsArea = <HTMLElement> topNode.querySelector('div.dragitems'),
|
||||
doc: AddonQtypeDdImageOrTextQuestionDocStructure = {};
|
||||
|
||||
|
@ -456,6 +460,7 @@ export class AddonQtypeDdImageOrTextQuestion {
|
|||
this.pollForImageLoad();
|
||||
});
|
||||
|
||||
this.resizeFunction = this.repositionDragsForQuestion.bind(this);
|
||||
window.addEventListener('resize', this.resizeFunction);
|
||||
}
|
||||
|
||||
|
@ -637,13 +642,6 @@ export class AddonQtypeDdImageOrTextQuestion {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to call when the window is resized.
|
||||
*/
|
||||
resizeFunction(): void {
|
||||
this.repositionDragsForQuestion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a draggable element as selected.
|
||||
*
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
<core-loading [hideUntil]="question.loaded"></core-loading>
|
||||
|
||||
<ion-item text-wrap [ngClass]="{invisible: !question.loaded}">
|
||||
<p *ngIf="!question.readOnly" class="core-info-card-icon">
|
||||
<ion-icon name="information" item-start></ion-icon>
|
||||
<p *ngIf="!question.readOnly" class="core-info-card" icon-start>
|
||||
<ion-icon name="information"></ion-icon>
|
||||
{{ 'core.question.howtodraganddrop' | translate }}
|
||||
</p>
|
||||
<p><core-format-text [component]="component" [componentId]="componentId" [text]="question.text"></core-format-text></p>
|
||||
<core-format-text *ngIf="question.ddArea" [adaptImg]="false" [component]="component" [componentId]="componentId" [text]="question.ddArea"></core-format-text>
|
||||
<core-format-text *ngIf="question.ddArea" [adaptImg]="false" [component]="component" [componentId]="componentId" [text]="question.ddArea" (afterRender)="questionRendered()"></core-format-text>
|
||||
</ion-item>
|
||||
</section>
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
// Style ddimageortext content a bit. Almost all these styles are copied from Moodle.
|
||||
addon-qtype-ddimageortext {
|
||||
.qtext {
|
||||
margin-bottom: 0.5em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
div.droparea img {
|
||||
border: 1px solid $gray-darker;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.draghome {
|
||||
vertical-align: top;
|
||||
margin: 5px;
|
||||
visibility : hidden;
|
||||
}
|
||||
.draghome img {
|
||||
display: block;
|
||||
}
|
||||
|
||||
div.draghome {
|
||||
border: 1px solid $gray-darker;
|
||||
cursor: pointer;
|
||||
background-color: #B0C4DE;
|
||||
display:inline-block;
|
||||
height: auto;
|
||||
width: auto;
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
.group1 {
|
||||
background-color: $white;
|
||||
}
|
||||
.group2 {
|
||||
background-color: $blue-light;
|
||||
}
|
||||
.group3 {
|
||||
background-color: #DCDCDC;
|
||||
}
|
||||
.group4 {
|
||||
background-color: #D8BFD8;
|
||||
}
|
||||
.group5 {
|
||||
background-color: #87CEFA;
|
||||
}
|
||||
.group6 {
|
||||
background-color: #DAA520;
|
||||
}
|
||||
.group7 {
|
||||
background-color: #FFD700;
|
||||
}
|
||||
.group8 {
|
||||
background-color: #F0E68C;
|
||||
}
|
||||
.drag {
|
||||
border: 1px solid $gray-darker;
|
||||
cursor: pointer;
|
||||
z-index: 2;
|
||||
}
|
||||
.dragitems.readonly .drag {
|
||||
cursor: auto;
|
||||
}
|
||||
.dragitems>div {
|
||||
clear: both;
|
||||
}
|
||||
.dragitems {
|
||||
cursor: pointer;
|
||||
}
|
||||
.dragitems.readonly {
|
||||
cursor: auto;
|
||||
}
|
||||
.drag img {
|
||||
display: block;
|
||||
}
|
||||
|
||||
div.ddarea {
|
||||
text-align : center;
|
||||
position: relative;
|
||||
}
|
||||
.dropbackground {
|
||||
margin:0 auto;
|
||||
}
|
||||
.dropzone {
|
||||
border: 1px solid $gray-darker;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
.readonly .dropzone {
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
div.dragitems div.draghome, div.dragitems div.drag {
|
||||
font:13px/1.231 arial,helvetica,clean,sans-serif;
|
||||
}
|
||||
.drag.beingdragged {
|
||||
z-index: 3;
|
||||
box-shadow: 3px 3px 4px $gray-darker;
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, OnDestroy, AfterViewInit, Injector, ElementRef } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy, Injector, ElementRef } from '@angular/core';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component';
|
||||
import { AddonQtypeDdImageOrTextQuestion } from '../classes/ddimageortext';
|
||||
|
@ -24,14 +24,15 @@ import { AddonQtypeDdImageOrTextQuestion } from '../classes/ddimageortext';
|
|||
selector: 'addon-qtype-ddimageortext',
|
||||
templateUrl: 'ddimageortext.html'
|
||||
})
|
||||
export class AddonQtypeDdImageOrTextComponent extends CoreQuestionBaseComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
export class AddonQtypeDdImageOrTextComponent extends CoreQuestionBaseComponent implements OnInit, OnDestroy {
|
||||
|
||||
protected element: HTMLElement;
|
||||
protected questionInstance: AddonQtypeDdImageOrTextQuestion;
|
||||
protected drops: any[]; // The drop zones received in the init object of the question.
|
||||
protected destroyed = false;
|
||||
|
||||
constructor(logger: CoreLoggerProvider, injector: Injector, element: ElementRef) {
|
||||
super(logger, 'AddonQtypeDdImageOrTextComponent', injector);
|
||||
constructor(protected loggerProvider: CoreLoggerProvider, injector: Injector, element: ElementRef) {
|
||||
super(loggerProvider, 'AddonQtypeDdImageOrTextComponent', injector);
|
||||
|
||||
this.element = element.nativeElement;
|
||||
}
|
||||
|
@ -76,18 +77,21 @@ export class AddonQtypeDdImageOrTextComponent extends CoreQuestionBaseComponent
|
|||
}
|
||||
|
||||
/**
|
||||
* View has been initialized.
|
||||
* The question has been rendered.
|
||||
*/
|
||||
ngAfterViewInit(): void {
|
||||
// Create the instance.
|
||||
this.questionInstance = new AddonQtypeDdImageOrTextQuestion(this.logger, this.domUtils, this.element,
|
||||
this.question, this.question.readOnly, this.drops);
|
||||
questionRendered(): void {
|
||||
if (!this.destroyed) {
|
||||
// Create the instance.
|
||||
this.questionInstance = new AddonQtypeDdImageOrTextQuestion(this.loggerProvider, this.domUtils, this.element,
|
||||
this.question, this.question.readOnly, this.drops);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.destroyed = true;
|
||||
this.questionInstance && this.questionInstance.destroy();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ export class AddonQtypeDdMarkerQuestion {
|
|||
protected proportion = 1;
|
||||
protected selected: HTMLElement; // Selected element (being "dragged").
|
||||
protected graphics: AddonQtypeDdMarkerGraphicsApi;
|
||||
protected resizeFunction;
|
||||
|
||||
doc: AddonQtypeDdMarkerQuestionDocStructure;
|
||||
shapes = [];
|
||||
|
@ -157,7 +158,9 @@ export class AddonQtypeDdMarkerQuestion {
|
|||
* Function to call when the instance is no longer needed.
|
||||
*/
|
||||
destroy(): void {
|
||||
window.removeEventListener('resize', this.resizeFunction);
|
||||
if (this.resizeFunction) {
|
||||
window.removeEventListener('resize', this.resizeFunction);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -167,7 +170,7 @@ export class AddonQtypeDdMarkerQuestion {
|
|||
* @return {AddonQtypeDdMarkerQuestionDocStructure} The object.
|
||||
*/
|
||||
docStructure(slot: number): AddonQtypeDdMarkerQuestionDocStructure {
|
||||
const topNode = <HTMLElement> this.container.querySelector('#core-question-' + slot + ' .addon-qtype-ddmarker-container'),
|
||||
const topNode = <HTMLElement> this.container.querySelector('.addon-qtype-ddmarker-container'),
|
||||
dragItemsArea = <HTMLElement> topNode.querySelector('div.dragitems');
|
||||
|
||||
return {
|
||||
|
@ -293,9 +296,9 @@ export class AddonQtypeDdMarkerQuestion {
|
|||
const markerTexts = this.doc.markerTexts();
|
||||
// Check if there is already a marker text for this drop zone.
|
||||
if (link) {
|
||||
existingMarkerText = markerTexts.querySelector('span.markertext' + dropZoneNo + ' a');
|
||||
existingMarkerText = <HTMLElement> markerTexts.querySelector('span.markertext' + dropZoneNo + ' a');
|
||||
} else {
|
||||
existingMarkerText = markerTexts.querySelector('span.markertext' + dropZoneNo);
|
||||
existingMarkerText = <HTMLElement> markerTexts.querySelector('span.markertext' + dropZoneNo);
|
||||
}
|
||||
|
||||
if (existingMarkerText) {
|
||||
|
@ -538,7 +541,7 @@ export class AddonQtypeDdMarkerQuestion {
|
|||
dragging = (this.doc.dragItemBeingDragged(choiceNo) !== null),
|
||||
coords: number[][] = [];
|
||||
|
||||
if (fv !== '' && typeof fv != 'undefined') {
|
||||
if (fv !== '' && typeof fv != 'undefined' && fv !== null) {
|
||||
// Get all the coordinates in the input and add them to the coords list.
|
||||
const coordsStrings = fv.split(';');
|
||||
|
||||
|
@ -645,6 +648,7 @@ export class AddonQtypeDdMarkerQuestion {
|
|||
this.pollForImageLoad();
|
||||
});
|
||||
|
||||
this.resizeFunction = this.redrawDragsAndDrops.bind(this);
|
||||
window.addEventListener('resize', this.resizeFunction);
|
||||
}
|
||||
|
||||
|
@ -731,6 +735,9 @@ export class AddonQtypeDdMarkerQuestion {
|
|||
}, 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redraw all draggables and drop zones.
|
||||
*/
|
||||
redrawDragsAndDrops(): void {
|
||||
// Mark all the draggable items as not placed.
|
||||
const drags = this.doc.dragItems();
|
||||
|
@ -789,7 +796,7 @@ export class AddonQtypeDdMarkerQuestion {
|
|||
dropZone = this.dropZones[dropZoneNo],
|
||||
dzNo = Number(dropZoneNo);
|
||||
|
||||
this.drawDropZone(dzNo, dropZone.markerText, dropZone.shape, dropZone.coords, colourForDropZone, true);
|
||||
this.drawDropZone(dzNo, dropZone.markertext, dropZone.shape, dropZone.coords, colourForDropZone, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -803,13 +810,6 @@ export class AddonQtypeDdMarkerQuestion {
|
|||
this.setFormValue(choiceNo, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to call when the window is resized.
|
||||
*/
|
||||
resizeFunction(): void {
|
||||
this.redrawDragsAndDrops();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart the colour index.
|
||||
*/
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
<core-loading [hideUntil]="question.loaded"></core-loading>
|
||||
|
||||
<ion-item text-wrap [ngClass]="{invisible: !question.loaded}">
|
||||
<p *ngIf="!question.readOnly" class="core-info-card-icon">
|
||||
<ion-icon name="information" item-start></ion-icon>
|
||||
<p *ngIf="!question.readOnly" class="core-info-card" icon-start>
|
||||
<ion-icon name="information"></ion-icon>
|
||||
{{ 'core.question.howtodraganddrop' | translate }}
|
||||
</p>
|
||||
<p><core-format-text [component]="component" [componentId]="componentId" [text]="question.text"></core-format-text></p>
|
||||
<core-format-text *ngIf="question.ddArea" [adaptImg]="false" [component]="component" [componentId]="componentId" [text]="question.ddArea"></core-format-text>
|
||||
<core-format-text *ngIf="question.ddArea" [adaptImg]="false" [component]="component" [componentId]="componentId" [text]="question.ddArea" (afterRender)="questionRendered()"></core-format-text>
|
||||
</ion-item>
|
||||
</section>
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
// Style ddmarker content a bit. Almost all these styles are copied from Moodle.
|
||||
addon-qtype-ddmarker {
|
||||
.qtext {
|
||||
margin-bottom: 0.5em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
div.droparea img {
|
||||
border: 1px solid $gray-darker;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.draghome img, .draghome span {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.dragitems .dragitem {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.dropzones {
|
||||
position: absolute;
|
||||
}
|
||||
.dropzones svg {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.dragitem.beingdragged .markertext {
|
||||
z-index: 5;
|
||||
box-shadow: 3px 3px 4px $gray-darker;
|
||||
}
|
||||
.dragitems .draghome {
|
||||
margin: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.dragitems.readonly .dragitem {
|
||||
cursor: auto;
|
||||
}
|
||||
div.ddarea {
|
||||
text-align: center;
|
||||
}
|
||||
div.ddarea .markertexts {
|
||||
min-height: 80px;
|
||||
position: absolute;
|
||||
text-align: left;
|
||||
}
|
||||
.dropbackground {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
div.dragitems div.draghome, div.dragitems div.dragitem,
|
||||
div.draghome, div.drag {
|
||||
font: 13px/1.231 arial,helvetica,clean,sans-serif;
|
||||
}
|
||||
div.dragitems span.markertext,
|
||||
div.markertexts span.markertext {
|
||||
margin: 0 5px;
|
||||
z-index: 2;
|
||||
background-color: $white;
|
||||
border: 2px solid $gray-darker;
|
||||
padding: 5px;
|
||||
display: inline-block;
|
||||
zoom: 1;
|
||||
border-radius: 10px;
|
||||
}
|
||||
div.markertexts span.markertext {
|
||||
z-index: 3;
|
||||
background-color: $yellow-light;
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
border-color: $yellow;
|
||||
position: absolute;
|
||||
}
|
||||
span.wrongpart {
|
||||
background-color: $yellow-light;
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
border-color: $yellow;
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
filter: alpha(opacity=60);
|
||||
opacity: 0.6;
|
||||
margin: 5px;
|
||||
display: inline-block;
|
||||
}
|
||||
div.dragitems img.target {
|
||||
position: absolute;
|
||||
left: -7px; /* This must be half the size of the target image, minus 0.5. */
|
||||
top: -7px; /* In other words, this works for a 15x15 cross-hair. */
|
||||
}
|
||||
div.dragitems div.draghome img.target {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, OnDestroy, AfterViewInit, Injector, ElementRef } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy, Injector, ElementRef } from '@angular/core';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component';
|
||||
import { AddonQtypeDdMarkerQuestion } from '../classes/ddmarker';
|
||||
|
@ -24,14 +24,15 @@ import { AddonQtypeDdMarkerQuestion } from '../classes/ddmarker';
|
|||
selector: 'addon-qtype-ddmarker',
|
||||
templateUrl: 'ddmarker.html'
|
||||
})
|
||||
export class AddonQtypeDdMarkerComponent extends CoreQuestionBaseComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
export class AddonQtypeDdMarkerComponent extends CoreQuestionBaseComponent implements OnInit, OnDestroy {
|
||||
|
||||
protected element: HTMLElement;
|
||||
protected questionInstance: AddonQtypeDdMarkerQuestion;
|
||||
protected dropZones: any[]; // The drop zones received in the init object of the question.
|
||||
protected destroyed = false;
|
||||
|
||||
constructor(logger: CoreLoggerProvider, injector: Injector, element: ElementRef) {
|
||||
super(logger, 'AddonQtypeDdMarkerComponent', injector);
|
||||
constructor(protected loggerProvider: CoreLoggerProvider, injector: Injector, element: ElementRef) {
|
||||
super(loggerProvider, 'AddonQtypeDdMarkerComponent', injector);
|
||||
|
||||
this.element = element.nativeElement;
|
||||
}
|
||||
|
@ -83,18 +84,21 @@ export class AddonQtypeDdMarkerComponent extends CoreQuestionBaseComponent imple
|
|||
}
|
||||
|
||||
/**
|
||||
* View has been initialized.
|
||||
* The question has been rendered.
|
||||
*/
|
||||
ngAfterViewInit(): void {
|
||||
// Create the instance.
|
||||
this.questionInstance = new AddonQtypeDdMarkerQuestion(this.logger, this.domUtils, this.textUtils, this.element,
|
||||
this.question, this.question.readOnly, this.dropZones);
|
||||
questionRendered(): void {
|
||||
if (!this.destroyed) {
|
||||
// Create the instance.
|
||||
this.questionInstance = new AddonQtypeDdMarkerQuestion(this.loggerProvider, this.domUtils, this.textUtils, this.element,
|
||||
this.question, this.question.readOnly, this.dropZones);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.destroyed = true;
|
||||
this.questionInstance && this.questionInstance.destroy();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ export class AddonQtypeDdwtosQuestion {
|
|||
protected selectors: AddonQtypeDdwtosQuestionCSSSelectors; // Result of cssSelectors.
|
||||
protected placed: {[no: number]: number}; // Map that relates drag elements numbers with drop zones numbers.
|
||||
protected selected: HTMLElement; // Selected element (being "dragged").
|
||||
protected resizeFunction;
|
||||
|
||||
/**
|
||||
* Create the instance.
|
||||
|
@ -125,7 +126,7 @@ export class AddonQtypeDdwtosQuestion {
|
|||
* @return {AddonQtypeDdwtosQuestionCSSSelectors} Object with the functions to get the selectors.
|
||||
*/
|
||||
cssSelectors(slot: number): AddonQtypeDdwtosQuestionCSSSelectors {
|
||||
const topNode = '#core-question-' + slot + ' .addon-qtype-ddwtos-container',
|
||||
const topNode = '.addon-qtype-ddwtos-container',
|
||||
selectors: AddonQtypeDdwtosQuestionCSSSelectors = {};
|
||||
|
||||
selectors.topNode = (): string => {
|
||||
|
@ -193,7 +194,9 @@ export class AddonQtypeDdwtosQuestion {
|
|||
* Function to call when the instance is no longer needed.
|
||||
*/
|
||||
destroy(): void {
|
||||
window.removeEventListener('resize', this.resizeFunction);
|
||||
if (this.resizeFunction) {
|
||||
window.removeEventListener('resize', this.resizeFunction);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -285,6 +288,7 @@ export class AddonQtypeDdwtosQuestion {
|
|||
this.positionDragItems();
|
||||
});
|
||||
|
||||
this.resizeFunction = this.positionDragItems.bind(this);
|
||||
window.addEventListener('resize', this.resizeFunction);
|
||||
}
|
||||
|
||||
|
@ -488,13 +492,6 @@ export class AddonQtypeDdwtosQuestion {
|
|||
this.placeDragInDrop(null, drop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to call when the window is resized.
|
||||
*/
|
||||
resizeFunction(): void {
|
||||
this.positionDragItems();
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a certain element as being "dragged".
|
||||
*
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<section ion-list *ngIf="question.text || question.text === ''" class="addon-qtype-ddwtos-container">
|
||||
<ion-item text-wrap>
|
||||
<p *ngIf="!question.readOnly" class="core-info-card-icon">
|
||||
<ion-icon name="information" item-start></ion-icon>
|
||||
<section ion-list *ngIf="question.text || question.text === ''">
|
||||
<ion-item text-wrap class="addon-qtype-ddwtos-container">
|
||||
<p *ngIf="!question.readOnly" class="core-info-card" icon-start>
|
||||
<ion-icon name="information"></ion-icon>
|
||||
{{ 'core.question.howtodraganddrop' | translate }}
|
||||
</p>
|
||||
<p><core-format-text [component]="component" [componentId]="componentId" [text]="question.text"></core-format-text></p>
|
||||
<core-format-text *ngIf="question.answers" [component]="component" [componentId]="componentId" [text]="question.answers"></core-format-text>
|
||||
<core-format-text *ngIf="question.answers" [component]="component" [componentId]="componentId" [text]="question.answers" (afterRender)="questionRendered()"></core-format-text>
|
||||
<div class="drags"></div>
|
||||
</ion-item>
|
||||
</section>
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
// Style ddwtos content a bit. Almost all these styles are copied from Moodle.
|
||||
addon-qtype-ddwtos {
|
||||
.qtext {
|
||||
margin-bottom: 0.5em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.draghome {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.answertext {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.drop {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
border: 1px solid $gray-darker;
|
||||
margin-bottom: 2px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.draghome, .drag {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
}
|
||||
.draghome, .drag.unplaced{
|
||||
border: 1px solid $gray-darker;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.draghome {
|
||||
visibility: hidden;
|
||||
}
|
||||
.drag {
|
||||
z-index: 2;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.drag.selected {
|
||||
z-index: 3;
|
||||
box-shadow: 3px 3px 4px $gray-darker;
|
||||
}
|
||||
|
||||
.drop.selected {
|
||||
border-color: $yellow-light;
|
||||
box-shadow: 0 0 5px 5px $yellow-light;
|
||||
}
|
||||
|
||||
&.notreadonly .drag,
|
||||
&.notreadonly .draghome,
|
||||
&.notreadonly .drop,
|
||||
&.notreadonly .answercontainer {
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
&.readonly .drag,
|
||||
&.readonly .draghome,
|
||||
&.readonly .drop,
|
||||
&.readonly .answercontainer {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
span.incorrect {
|
||||
background-color: $red-light;
|
||||
}
|
||||
span.correct {
|
||||
background-color: $green-light;
|
||||
}
|
||||
|
||||
.group1 {
|
||||
background-color: $white;
|
||||
}
|
||||
.group2 {
|
||||
background-color: #DCDCDC;
|
||||
}
|
||||
.group3 {
|
||||
background-color: $blue-light;
|
||||
}
|
||||
.group4 {
|
||||
background-color: #D8BFD8;
|
||||
}
|
||||
.group5 {
|
||||
background-color: #87CEFA;
|
||||
}
|
||||
.group6 {
|
||||
background-color: #DAA520;
|
||||
}
|
||||
.group7 {
|
||||
background-color: #FFD700;
|
||||
}
|
||||
.group8 {
|
||||
background-color: #F0E68C;
|
||||
}
|
||||
|
||||
sub, sup {
|
||||
font-size: 80%;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
sup {
|
||||
top: -0.4em;
|
||||
}
|
||||
sub {
|
||||
bottom: -0.2em;
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, OnDestroy, AfterViewInit, Injector, ElementRef } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy, Injector, ElementRef } from '@angular/core';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component';
|
||||
import { AddonQtypeDdwtosQuestion } from '../classes/ddwtos';
|
||||
|
@ -24,14 +24,15 @@ import { AddonQtypeDdwtosQuestion } from '../classes/ddwtos';
|
|||
selector: 'addon-qtype-ddwtos',
|
||||
templateUrl: 'ddwtos.html'
|
||||
})
|
||||
export class AddonQtypeDdwtosComponent extends CoreQuestionBaseComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
export class AddonQtypeDdwtosComponent extends CoreQuestionBaseComponent implements OnInit, OnDestroy {
|
||||
|
||||
protected element: HTMLElement;
|
||||
protected questionInstance: AddonQtypeDdwtosQuestion;
|
||||
protected inputIds: string[]; // Ids of the inputs of the question (where the answers will be stored).
|
||||
protected inputIds: string[] = []; // Ids of the inputs of the question (where the answers will be stored).
|
||||
protected destroyed = false;
|
||||
|
||||
constructor(logger: CoreLoggerProvider, injector: Injector, element: ElementRef) {
|
||||
super(logger, 'AddonQtypeDdwtosComponent', injector);
|
||||
constructor(protected loggerProvider: CoreLoggerProvider, injector: Injector, element: ElementRef) {
|
||||
super(loggerProvider, 'AddonQtypeDdwtosComponent', injector);
|
||||
|
||||
this.element = element.nativeElement;
|
||||
}
|
||||
|
@ -74,28 +75,30 @@ export class AddonQtypeDdwtosComponent extends CoreQuestionBaseComponent impleme
|
|||
}
|
||||
|
||||
// Get the inputs where the answers will be stored and add them to the question text.
|
||||
const inputEls = <HTMLElement[]> Array.from(div.querySelectorAll('input[type="hidden"]:not([name*=sequencecheck])')),
|
||||
inputIds = [];
|
||||
const inputEls = <HTMLElement[]> Array.from(div.querySelectorAll('input[type="hidden"]:not([name*=sequencecheck])'));
|
||||
|
||||
inputEls.forEach((inputEl) => {
|
||||
this.question.text += inputEl.outerHTML;
|
||||
inputIds.push(inputEl.getAttribute('id'));
|
||||
this.inputIds.push(inputEl.getAttribute('id'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* View has been initialized.
|
||||
* The question has been rendered.
|
||||
*/
|
||||
ngAfterViewInit(): void {
|
||||
// Create the instance.
|
||||
this.questionInstance = new AddonQtypeDdwtosQuestion(this.logger, this.domUtils, this.element, this.question,
|
||||
this.question.readOnly, this.inputIds);
|
||||
questionRendered(): void {
|
||||
if (!this.destroyed) {
|
||||
// Create the instance.
|
||||
this.questionInstance = new AddonQtypeDdwtosQuestion(this.loggerProvider, this.domUtils, this.element, this.question,
|
||||
this.question.readOnly, this.inputIds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.destroyed = true;
|
||||
this.questionInstance && this.questionInstance.destroy();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
<!-- Textarea. -->
|
||||
<ion-item *ngIf="question.textarea && !question.hasDraftFiles">
|
||||
<!-- "Format" hidden input -->
|
||||
<input *ngIf="question.formatInput" type="hidden" [name]="question.formatInput.name" [value]="question.formatInput.value" >
|
||||
<input item-content *ngIf="question.formatInput" type="hidden" [name]="question.formatInput.name" [value]="question.formatInput.value" >
|
||||
<!-- Plain text textarea. -->
|
||||
<ion-textarea *ngIf="question.isPlainText" class="core-question-textarea" [ngClass]='{"core-monospaced": question.isMonospaced}' placeholder="{{ 'core.question.answer' | translate }}" [attr.name]="question.textarea.name" aria-multiline="true">{{question.textarea.text}}</ion-textarea>
|
||||
<ion-textarea *ngIf="question.isPlainText" class="core-question-textarea" [ngClass]='{"core-monospaced": question.isMonospaced}' placeholder="{{ 'core.question.answer' | translate }}" [attr.name]="question.textarea.name" aria-multiline="true" [ngModel]="question.textarea.text"></ion-textarea>
|
||||
<!-- Rich text editor. -->
|
||||
<core-rich-text-editor *ngIf="!question.isPlainText" placeholder="{{ 'core.question.answer' | translate }}"></core-rich-text-editor>
|
||||
<core-rich-text-editor item-content *ngIf="!question.isPlainText" placeholder="{{ 'core.question.answer' | translate }}" [control]="formControl" [name]="question.textarea.name"></core-rich-text-editor>
|
||||
<!-- @todo: Attributes that were passed to RTE in Ionic 1 but now they aren't supported yet:
|
||||
model="textarea" [name]="textarea.name" [component]="component" [componentId]="componentId" -->
|
||||
[component]="component" [componentId]="componentId" -->
|
||||
</ion-item>
|
||||
|
||||
<!-- Draft files not supported. -->
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
import { Component, OnInit, Injector } from '@angular/core';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component';
|
||||
import { FormControl, FormBuilder } from '@angular/forms';
|
||||
|
||||
/**
|
||||
* Component to render an essay question.
|
||||
|
@ -25,7 +26,9 @@ import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-
|
|||
})
|
||||
export class AddonQtypeEssayComponent extends CoreQuestionBaseComponent implements OnInit {
|
||||
|
||||
constructor(logger: CoreLoggerProvider, injector: Injector) {
|
||||
protected formControl: FormControl;
|
||||
|
||||
constructor(logger: CoreLoggerProvider, injector: Injector, protected fb: FormBuilder) {
|
||||
super(logger, 'AddonQtypeEssayComponent', injector);
|
||||
}
|
||||
|
||||
|
@ -34,5 +37,7 @@ export class AddonQtypeEssayComponent extends CoreQuestionBaseComponent implemen
|
|||
*/
|
||||
ngOnInit(): void {
|
||||
this.initEssayComponent();
|
||||
|
||||
this.formControl = this.fb.control(this.question.textarea && this.question.textarea.text);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
// Style gapselect content a bit. All these styles are copied from Moodle.
|
||||
addon-qtype-gapselect {
|
||||
p {
|
||||
margin: 0 0 .5em;
|
||||
}
|
||||
|
||||
select {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
display: inline-block;
|
||||
border: 1px solid $gray-dark;
|
||||
padding: 4px 6px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
background: $gray-lighter;
|
||||
}
|
||||
}
|
|
@ -3,17 +3,21 @@
|
|||
<p><core-format-text [component]="component" [componentId]="componentId" [text]="question.text"></core-format-text></p>
|
||||
</ion-item>
|
||||
<ion-item text-wrap *ngFor="let row of question.rows">
|
||||
<ion-row>
|
||||
<ion-col>
|
||||
<p><core-format-text id="addon-qtype-match-question-{{row.id}}" [component]="component" [componentId]="componentId" [text]="row.text"></core-format-text></p>
|
||||
</ion-col>
|
||||
<ion-col [ngClass]='{"core-question-answer-correct": row.isCorrect === 1, "core-question-answer-incorrect": row.isCorrect === 0}'>
|
||||
<label class="accesshide" for="{{row.id}}" *ngIf="row.accessibilityLabel">{{ row.accessibilityLabel }}</label>
|
||||
<ion-select id="{{row.id}}" [name]="row.name" [attr.aria-labelledby]="'addon-qtype-match-question-' + row.id" [ngModel]="row.selected">
|
||||
<ion-option *ngFor="let option of row.options" [value]="option.value">{{option.label}}</ion-option>
|
||||
</ion-select>
|
||||
<!-- @todo: select fix? -->
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
<ion-grid item-content>
|
||||
<ion-row>
|
||||
<ion-col>
|
||||
<p><core-format-text id="addon-qtype-match-question-{{row.id}}" [component]="component" [componentId]="componentId" [text]="row.text"></core-format-text></p>
|
||||
</ion-col>
|
||||
<ion-col [ngClass]='{"core-question-answer-correct": row.isCorrect === 1, "core-question-answer-incorrect": row.isCorrect === 0}'>
|
||||
<label class="accesshide" for="{{row.id}}" *ngIf="row.accessibilityLabel">{{ row.accessibilityLabel }}</label>
|
||||
<ion-select id="{{row.id}}" [name]="row.name" [attr.aria-labelledby]="'addon-qtype-match-question-' + row.id" [(ngModel)]="row.selected" interface="popover">
|
||||
<ion-option *ngFor="let option of row.options" [value]="option.value">{{option.label}}</ion-option>
|
||||
</ion-select>
|
||||
|
||||
<!-- ion-select doesn't use a select. Create a hidden input to hold the selected value. -->
|
||||
<input type="hidden" [ngModel]="row.selected" [attr.name]="row.name">
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-item>
|
||||
</section>
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
// Style multianswer content a bit. All these styles are copied from Moodle.
|
||||
addon-qtype-multianswer {
|
||||
p {
|
||||
margin: 0 0 .5em;
|
||||
}
|
||||
|
||||
.answer div.r0, .answer div.r1, .answer td.r0, .answer td.r1 {
|
||||
padding: 0.3em;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
display: table;
|
||||
}
|
||||
|
||||
tr {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
td {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
input, select {
|
||||
display: inline-block;
|
||||
border: 1px solid #ccc;
|
||||
padding: 4px 6px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
select {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
input[type="radio"], input[type="checkbox"] {
|
||||
margin-top: -4px;
|
||||
margin-right: 7px;
|
||||
}
|
||||
}
|
|
@ -7,19 +7,28 @@
|
|||
|
||||
<!-- Checkbox for multiple choice. -->
|
||||
<ng-container *ngIf="question.multi">
|
||||
<ion-checkbox text-wrap [name]="option.name" [ngModel]="option.checked" [disabled]="option.disabled" *ngFor="let option of question.options" [ngClass]="{'core-question-answer-correct': option.isCorrect === 1, 'core-question-answer-incorrect': option.isCorrect === 0}">
|
||||
<p><core-format-text [component]="component" [componentId]="componentId" [text]="option.text"></core-format-text></p>
|
||||
<p *ngIf="option.feedback" class="core-question-feedback-container"><core-format-text [component]="component" [componentId]="componentId" [text]="option.feedback"></core-format-text></p>
|
||||
</ion-checkbox>
|
||||
<ion-item text-wrap *ngFor="let option of question.options" [ngClass]="{'core-question-answer-correct': option.isCorrect === 1, 'core-question-answer-incorrect': option.isCorrect === 0}">
|
||||
<ion-label>
|
||||
<core-format-text [component]="component" [componentId]="componentId" [text]="option.text"></core-format-text>
|
||||
<p *ngIf="option.feedback" class="core-question-feedback-container"><core-format-text [component]="component" [componentId]="componentId" [text]="option.feedback"></core-format-text></p>
|
||||
</ion-label>
|
||||
<ion-checkbox [name]="option.name" [(ngModel)]="option.checked" [disabled]="option.disabled" item-end></ion-checkbox>
|
||||
|
||||
<!-- ion-checkbox doesn't use an input. Create a hidden input to hold the value. -->
|
||||
<input item-content type="hidden" [ngModel]="option.checked" [attr.name]="option.name">
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
|
||||
<!-- Radio buttons for single choice. -->
|
||||
<div *ngIf="!question.multi" radio-group [ngModel]="question.singleChoiceModel" [name]="question.optionsName">
|
||||
<div *ngIf="!question.multi" radio-group [(ngModel)]="question.singleChoiceModel" [name]="question.optionsName">
|
||||
<ion-item text-wrap *ngFor="let option of question.options">
|
||||
<ion-label><core-format-text [component]="component" [componentId]="componentId" [text]="option.text"></core-format-text></ion-label>
|
||||
<ion-radio [value]="option.value" [disabled]="option.disabled" [ngClass]='{"core-question-answer-correct": option.isCorrect === 1, "core-question-answer-incorrect": option.isCorrect === 0}'>
|
||||
<p *ngIf="option.feedback" class="core-question-feedback-container"><core-format-text [component]="component" [componentId]="componentId" [text]="option.feedback"></core-format-text></p>
|
||||
</ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<!-- ion-radio doesn't use an input. Create a hidden input to hold the selected value. -->
|
||||
<input type="hidden" [ngModel]="question.singleChoiceModel" [attr.name]="question.optionsName">
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
.opacity-hide { opacity: 0; }
|
||||
.core-big { font-size: 115%; }
|
||||
.invisible { visibility: hidden; }
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
.core-center-view .scroll-content {
|
||||
|
@ -510,3 +511,13 @@ textarea {
|
|||
color: $color-base;
|
||||
}
|
||||
}
|
||||
|
||||
.accesshide {
|
||||
position: absolute;
|
||||
left: -10000px;
|
||||
font-weight: normal;
|
||||
font-size: 1em; }
|
||||
|
||||
.core-monospaced {
|
||||
font-family: Andale Mono,Monaco,Courier New,DejaVu Sans Mono,monospace;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</div>
|
||||
|
||||
<div [hidden]="rteEnabled">
|
||||
<ion-textarea #textarea class="core-textarea" [placeholder]="placeholder" ngControl="control" (ionChange)="onChange($event)"></ion-textarea>
|
||||
<ion-textarea #textarea class="core-textarea" [placeholder]="placeholder" [attr.name]="name" ngControl="control" (ionChange)="onChange($event)"></ion-textarea>
|
||||
<div class="formatOptions">
|
||||
<button tappable (click)="toggleEditor($event)">Toggle Editor</button>
|
||||
</div>
|
||||
|
|
|
@ -42,6 +42,7 @@ export class CoreRichTextEditorComponent {
|
|||
|
||||
@Input() placeholder = ''; // Placeholder to set in textarea.
|
||||
@Input() control: FormControl; // Form control.
|
||||
@Input() name = 'core-rich-text-editor'; // Name to set to the textarea.
|
||||
@Output() contentChanged: EventEmitter<string>;
|
||||
|
||||
@ViewChild('editor') editor: ElementRef; // WYSIWYG editor.
|
||||
|
@ -109,6 +110,7 @@ export class CoreRichTextEditorComponent {
|
|||
this.clearText();
|
||||
} else {
|
||||
this.control.setValue(this.editorElement.innerHTML);
|
||||
this.textarea.value = this.editorElement.innerHTML;
|
||||
}
|
||||
} else {
|
||||
if (this.isNullOrWhiteSpace(this.textarea.value)) {
|
||||
|
@ -117,6 +119,7 @@ export class CoreRichTextEditorComponent {
|
|||
this.control.setValue(this.textarea.value);
|
||||
}
|
||||
}
|
||||
|
||||
this.contentChanged.emit(this.control.value);
|
||||
}
|
||||
|
||||
|
|
|
@ -83,7 +83,6 @@ export class CoreQuestionBaseComponent {
|
|||
|
||||
if (optionEl.selected) {
|
||||
selectModel.selected = option.value;
|
||||
selectModel.selectedLabel = option.label;
|
||||
}
|
||||
|
||||
selectModel.options.push(option);
|
||||
|
@ -92,7 +91,6 @@ export class CoreQuestionBaseComponent {
|
|||
if (!selectModel.selected) {
|
||||
// No selected option, select the first one.
|
||||
selectModel.selected = selectModel.options[0].value;
|
||||
selectModel.selectedLabel = selectModel.options[0].label;
|
||||
}
|
||||
|
||||
// Get the accessibility label.
|
||||
|
@ -158,7 +156,7 @@ export class CoreQuestionBaseComponent {
|
|||
// Check which one should be displayed first: the options or the input.
|
||||
const input = questionDiv.querySelector('input[type="text"][name*=answer]');
|
||||
this.question.optionsFirst =
|
||||
questionDiv.innerHTML.indexOf(input.outerHTML) > questionDiv.innerHTML.indexOf(options[0].outerHTML);
|
||||
questionDiv.innerHTML.indexOf(input.outerHTML) > questionDiv.innerHTML.indexOf(radios[0].outerHTML);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,7 +311,7 @@ export class CoreQuestionBaseComponent {
|
|||
|
||||
if (questionDiv) {
|
||||
// Find rows.
|
||||
const rows = Array.from(questionDiv.querySelectorAll('tr'));
|
||||
const rows = Array.from(questionDiv.querySelectorAll('table.answer tr'));
|
||||
if (!rows || !rows.length) {
|
||||
this.logger.warn('Aborting because couldn\'t find any row.', this.question.name);
|
||||
|
||||
|
@ -376,7 +374,7 @@ export class CoreQuestionBaseComponent {
|
|||
};
|
||||
|
||||
if (option.selected) {
|
||||
rowModel.selected = option;
|
||||
rowModel.selected = option.value;
|
||||
}
|
||||
|
||||
rowModel.options.push(option);
|
||||
|
@ -404,8 +402,6 @@ export class CoreQuestionBaseComponent {
|
|||
const questionDiv = this.initComponent();
|
||||
|
||||
if (questionDiv) {
|
||||
// Create the model for radio buttons.
|
||||
this.question.singleChoiceModel = {};
|
||||
|
||||
// Get the prompt.
|
||||
this.question.prompt = this.domUtils.getContentsOfElement(questionDiv, '.prompt');
|
||||
|
@ -481,6 +477,11 @@ export class CoreQuestionBaseComponent {
|
|||
|
||||
return this.questionHelper.showComponentError(this.onAbort);
|
||||
}
|
||||
|
||||
if (!this.question.multi && typeof this.question.singleChoiceModel == 'undefined') {
|
||||
// We couldn't find the option to select, select the first one.
|
||||
this.question.singleChoiceModel = options[0].value;
|
||||
}
|
||||
}
|
||||
|
||||
return questionDiv;
|
||||
|
|
|
@ -123,7 +123,7 @@ export class CoreQuestionComponent implements OnInit {
|
|||
|
||||
promise.then(() => {
|
||||
// Handle behaviour.
|
||||
this.behaviourDelegate.handleQuestion(this.question, this.question.preferredBehaviour).then((comps) => {
|
||||
this.behaviourDelegate.handleQuestion(this.question.preferredBehaviour, this.question).then((comps) => {
|
||||
this.behaviourComponents = comps;
|
||||
});
|
||||
this.questionHelper.extractQbehaviourRedoButton(this.question);
|
||||
|
|
|
@ -319,7 +319,7 @@ export class CoreQuestionHelperProvider {
|
|||
elements = Array.from(form.elements);
|
||||
|
||||
elements.forEach((element: HTMLInputElement) => {
|
||||
const name = element.name || '';
|
||||
const name = element.name || element.getAttribute('ng-reflect-name') || '';
|
||||
|
||||
// Ignore flag and submit inputs.
|
||||
if (!name || name.match(/_:flagged$/) || element.type == 'submit' || element.tagName == 'BUTTON') {
|
||||
|
@ -588,13 +588,11 @@ export class CoreQuestionHelperProvider {
|
|||
* @param {string} [error] Error to show.
|
||||
*/
|
||||
showComponentError(onAbort: EventEmitter<void>, error?: string): void {
|
||||
error = error || 'Error processing the question. This could be caused by custom modifications in your site.';
|
||||
|
||||
// Prevent consecutive errors.
|
||||
const now = Date.now();
|
||||
if (now - this.lastErrorShown > 500) {
|
||||
this.lastErrorShown = now;
|
||||
this.domUtils.showErrorModal(error);
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorparsequestions', true);
|
||||
}
|
||||
|
||||
onAbort && onAbort.emit();
|
||||
|
|
Loading…
Reference in New Issue