Merge pull request #1285 from dpalou/MOBILE-2388

MOBILE-2388 qbehaviour: Implement all behaviour addons
main
Juan Leyva 2018-04-09 08:02:32 +01:00 committed by GitHub
commit f3ea09c7ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1372 additions and 1 deletions

View File

@ -0,0 +1,30 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { NgModule } from '@angular/core';
import { AddonQbehaviourAdaptiveHandler } from './providers/handler';
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
@NgModule({
declarations: [
],
providers: [
AddonQbehaviourAdaptiveHandler
]
})
export class AddonQbehaviourAdaptiveModule {
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourAdaptiveHandler) {
behaviourDelegate.registerHandler(handler);
}
}

View File

@ -0,0 +1,56 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
/**
* Handler to support adaptive question behaviour.
*/
@Injectable()
export class AddonQbehaviourAdaptiveHandler implements CoreQuestionBehaviourHandler {
name = 'AddonQbehaviourAdaptive';
type = 'adaptive';
constructor(private questionHelper: CoreQuestionHelperProvider) {
// Nothing to do.
}
/**
* Handle a question behaviour.
* If the behaviour requires a submit button, it should add it to question.behaviourButtons.
* If the behaviour requires to show some extra data, it should return the components to render it.
*
* @param {any} question The question.
* @return {any[]|Promise<any[]>} Components (or promise resolved with components) to render some extra data in the question
* (e.g. certainty options). Don't return anything if no extra data is required.
*/
handleQuestion(question: any): any[] | Promise<any[]> {
// Just extract the button, it doesn't need any specific component.
this.questionHelper.extractQbehaviourButtons(question);
return;
}
/**
* Whether or not the handler is enabled on a site level.
*
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/
isEnabled(): boolean | Promise<boolean> {
return true;
}
}

View File

@ -0,0 +1,30 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { NgModule } from '@angular/core';
import { AddonQbehaviourAdaptiveNoPenaltyHandler } from './providers/handler';
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
@NgModule({
declarations: [
],
providers: [
AddonQbehaviourAdaptiveNoPenaltyHandler
]
})
export class AddonQbehaviourAdaptiveNoPenaltyModule {
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourAdaptiveNoPenaltyHandler) {
behaviourDelegate.registerHandler(handler);
}
}

View File

@ -0,0 +1,56 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
/**
* Handler to support adaptive no penalty question behaviour.
*/
@Injectable()
export class AddonQbehaviourAdaptiveNoPenaltyHandler implements CoreQuestionBehaviourHandler {
name = 'AddonQbehaviourAdaptiveNoPenalty';
type = 'adaptivenopenalty';
constructor(private questionHelper: CoreQuestionHelperProvider) {
// Nothing to do.
}
/**
* Handle a question behaviour.
* If the behaviour requires a submit button, it should add it to question.behaviourButtons.
* If the behaviour requires to show some extra data, it should return the components to render it.
*
* @param {any} question The question.
* @return {any[]|Promise<any[]>} Components (or promise resolved with components) to render some extra data in the question
* (e.g. certainty options). Don't return anything if no extra data is required.
*/
handleQuestion(question: any): any[] | Promise<any[]> {
// Just extract the button, it doesn't need any specific component.
this.questionHelper.extractQbehaviourButtons(question);
return;
}
/**
* Whether or not the handler is enabled on a site level.
*
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/
isEnabled(): boolean | Promise<boolean> {
return true;
}
}

View File

@ -0,0 +1,6 @@
<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>

View File

@ -0,0 +1,36 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, Input, EventEmitter } from '@angular/core';
/**
* Component to render the deferred CBM in a question.
*/
@Component({
selector: 'addon-qbehaviour-deferredcbm',
templateUrl: 'deferredcbm.html'
})
export class AddonQbehaviourDeferredCBMComponent {
@Input() question: any; // The question.
@Input() component: string; // The component the question belongs to.
@Input() componentId: number; // ID of the component the question belongs to.
@Input() attemptId: number; // Attempt ID.
@Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline.
@Input() buttonClicked: EventEmitter<any>; // Should emit an event when a behaviour button is clicked.
@Input() onAbort: EventEmitter<void>; // Should emit an event if the question should be aborted.
constructor() {
// Nothing to do.
}
}

View File

@ -0,0 +1,46 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { NgModule } from '@angular/core';
import { IonicModule } from 'ionic-angular';
import { TranslateModule } from '@ngx-translate/core';
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
import { CoreDirectivesModule } from '@directives/directives.module';
import { AddonQbehaviourDeferredCBMHandler } from './providers/handler';
import { AddonQbehaviourDeferredCBMComponent } from './component/deferredcbm';
@NgModule({
declarations: [
AddonQbehaviourDeferredCBMComponent
],
imports: [
IonicModule,
TranslateModule.forChild(),
CoreDirectivesModule
],
providers: [
AddonQbehaviourDeferredCBMHandler
],
exports: [
AddonQbehaviourDeferredCBMComponent
],
entryComponents: [
AddonQbehaviourDeferredCBMComponent
]
})
export class AddonQbehaviourDeferredCBMModule {
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourDeferredCBMHandler) {
behaviourDelegate.registerHandler(handler);
}
}

View File

@ -0,0 +1,116 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
import { CoreQuestionDelegate } from '@core/question/providers/delegate';
import { CoreQuestionState } from '@core/question/providers/question';
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
import { AddonQbehaviourDeferredFeedbackHandler } from '@addon/qbehaviour/deferredfeedback/providers/handler';
import { AddonQbehaviourDeferredCBMComponent } from '../component/deferredcbm';
/**
* Handler to support deferred CBM question behaviour.
*/
@Injectable()
export class AddonQbehaviourDeferredCBMHandler implements CoreQuestionBehaviourHandler {
name = 'AddonQbehaviourDeferredCBM';
type = 'deferredcbm';
constructor(private questionDelegate: CoreQuestionDelegate, private questionHelper: CoreQuestionHelperProvider,
private deferredFeedbackHandler: AddonQbehaviourDeferredFeedbackHandler) {
// Nothing to do.
}
/**
* Determine a question new state based on its answer(s).
*
* @param {string} component Component the question belongs to.
* @param {number} attemptId Attempt ID the question belongs to.
* @param {any} question The question.
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {CoreQuestionState|Promise<CoreQuestionState>} New state (or promise resolved with state).
*/
determineNewState(component: string, attemptId: number, question: any, siteId?: string)
: CoreQuestionState | Promise<CoreQuestionState> {
// Depends on deferredfeedback.
return this.deferredFeedbackHandler.determineNewStateDeferred(component, attemptId, question, siteId,
this.isCompleteResponse.bind(this), this.isSameResponse.bind(this));
}
/**
* Handle a question behaviour.
* If the behaviour requires a submit button, it should add it to question.behaviourButtons.
* If the behaviour requires to show some extra data, it should return the components to render it.
*
* @param {any} question The question.
* @return {any[]|Promise<any[]>} Components (or promise resolved with components) to render some extra data in the question
* (e.g. certainty options). Don't return anything if no extra data is required.
*/
handleQuestion(question: any): any[] | Promise<any[]> {
if (this.questionHelper.extractQbehaviourCBM(question)) {
return [AddonQbehaviourDeferredCBMComponent];
}
}
/**
* Check if a response is complete.
*
* @param {any} question The question.
* @param {any} answers Object with the question answers (without prefix).
* @return {number} 1 if complete, 0 if not complete, -1 if cannot determine.
*/
protected isCompleteResponse(question: any, answers: any): number {
// First check if the question answer is complete.
const complete = this.questionDelegate.isCompleteResponse(question, answers);
if (complete > 0) {
// Answer is complete, check the user answered CBM too.
return answers['-certainty'] ? 1 : 0;
}
return complete;
}
/**
* Whether or not the handler is enabled on a site level.
*
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/
isEnabled(): boolean | Promise<boolean> {
return true;
}
/**
* Check if two responses are the same.
*
* @param {any} question Question.
* @param {any} prevAnswers Object with the previous question answers.
* @param {any} prevBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...).
* @param {any} newAnswers Object with the new question answers.
* @param {any} newBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...).
* @return {boolean} Whether they're the same.
*/
protected isSameResponse(question: any, prevAnswers: any, prevBasicAnswers: any, newAnswers: any, newBasicAnswers: any)
: boolean {
// First check if the question answer is the same.
const same = this.questionDelegate.isSameResponse(question, prevBasicAnswers, newBasicAnswers);
if (same) {
// Same response, check the CBM is the same too.
return prevAnswers['-certainty'] == newAnswers['-certainty'];
}
return same;
}
}

View File

@ -0,0 +1,30 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { NgModule } from '@angular/core';
import { AddonQbehaviourDeferredFeedbackHandler } from './providers/handler';
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
@NgModule({
declarations: [
],
providers: [
AddonQbehaviourDeferredFeedbackHandler
]
})
export class AddonQbehaviourDeferredFeedbackModule {
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourDeferredFeedbackHandler) {
behaviourDelegate.registerHandler(handler);
}
}

View File

@ -0,0 +1,154 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
import { CoreQuestionDelegate } from '@core/question/providers/delegate';
import { CoreQuestionProvider, CoreQuestionState } from '@core/question/providers/question';
/**
* Check if a response is complete.
*
* @param {any} question The question.
* @param {any} answers Object with the question answers (without prefix).
* @return {number} 1 if complete, 0 if not complete, -1 if cannot determine.
*/
export type isCompleteResponseFunction = (question: any, answers: any) => number;
/**
* Check if two responses are the same.
*
* @param {any} question Question.
* @param {any} prevAnswers Object with the previous question answers.
* @param {any} prevBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...).
* @param {any} newAnswers Object with the new question answers.
* @param {any} newBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...).
* @return {boolean} Whether they're the same.
*/
export type isSameResponseFunction = (question: any, prevAnswers: any, prevBasicAnswers: any, newAnswers: any,
newBasicAnswers: any) => boolean;
/**
* Handler to support deferred feedback question behaviour.
*/
@Injectable()
export class AddonQbehaviourDeferredFeedbackHandler implements CoreQuestionBehaviourHandler {
name = 'AddonQbehaviourDeferredFeedback';
type = 'deferredfeedback';
constructor(private questionDelegate: CoreQuestionDelegate, private questionProvider: CoreQuestionProvider) {
// Nothing to do.
}
/**
* Determine a question new state based on its answer(s).
*
* @param {string} component Component the question belongs to.
* @param {number} attemptId Attempt ID the question belongs to.
* @param {any} question The question.
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {CoreQuestionState|Promise<CoreQuestionState>} New state (or promise resolved with state).
*/
determineNewState(component: string, attemptId: number, question: any, siteId?: string)
: CoreQuestionState | Promise<CoreQuestionState> {
return this.determineNewStateDeferred(component, attemptId, question, siteId);
}
/**
* Determine a question new state based on its answer(s) for deferred question behaviour.
*
* @param {string} component Component the question belongs to.
* @param {number} attemptId Attempt ID the question belongs to.
* @param {any} question The question.
* @param {string} [siteId] Site ID. If not defined, current site.
* @param {isCompleteResponseFunction} [isCompleteFn] Function to override the default isCompleteResponse check.
* @param {isSameResponseFunction} [isSameFn] Function to override the default isSameResponse check.
* @return {Promise<CoreQuestionState>} Promise resolved with state.
*/
determineNewStateDeferred(component: string, attemptId: number, question: any, siteId?: string,
isCompleteFn?: isCompleteResponseFunction, isSameFn?: isSameResponseFunction): Promise<CoreQuestionState> {
// Check if we have local data for the question.
return this.questionProvider.getQuestion(component, attemptId, question.slot, siteId).catch(() => {
// No entry found, use the original data.
return question;
}).then((dbQuestion) => {
const state = this.questionProvider.getState(dbQuestion.state);
if (state.finished || !state.active) {
// Question is finished, it cannot change.
return state;
}
// We need to check if the answers have changed. Retrieve current stored answers.
return this.questionProvider.getQuestionAnswers(component, attemptId, question.slot, false, siteId)
.then((prevAnswers) => {
const newBasicAnswers = this.questionProvider.getBasicAnswers(question.answers);
prevAnswers = this.questionProvider.convertAnswersArrayToObject(prevAnswers, true);
const prevBasicAnswers = this.questionProvider.getBasicAnswers(prevAnswers);
// If answers haven't changed the state is the same.
if (isSameFn) {
if (isSameFn(question, prevAnswers, prevBasicAnswers, question.answers, newBasicAnswers)) {
return state;
}
} else {
if (this.questionDelegate.isSameResponse(question, prevBasicAnswers, newBasicAnswers)) {
return state;
}
}
// Answers have changed. Now check if the response is complete and calculate the new state.
let complete: number,
newState: string;
if (isCompleteFn) {
// Pass all the answers since some behaviours might need the extra data.
complete = isCompleteFn(question, question.answers);
} else {
// Only pass the basic answers since questions should be independent of extra data.
complete = this.questionDelegate.isCompleteResponse(question, newBasicAnswers);
}
if (complete < 0) {
newState = 'unknown';
} else if (complete > 0) {
newState = 'complete';
} else {
const gradable = this.questionDelegate.isGradableResponse(question, newBasicAnswers);
if (gradable < 0) {
newState = 'unknown';
} else if (gradable > 0) {
newState = 'invalid';
} else {
newState = 'todo';
}
}
return this.questionProvider.getState(newState);
});
});
}
/**
* Whether or not the handler is enabled on a site level.
*
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/
isEnabled(): boolean | Promise<boolean> {
return true;
}
}

View File

@ -0,0 +1,30 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { NgModule } from '@angular/core';
import { AddonQbehaviourImmediateCBMHandler } from './providers/handler';
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
@NgModule({
declarations: [
],
providers: [
AddonQbehaviourImmediateCBMHandler
]
})
export class AddonQbehaviourImmediateCBMModule {
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourImmediateCBMHandler) {
behaviourDelegate.registerHandler(handler);
}
}

View File

@ -0,0 +1,59 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
import { AddonQbehaviourDeferredCBMComponent } from '@addon/qbehaviour/deferredcbm/component/deferredcbm';
/**
* Handler to support immediate CBM question behaviour.
*/
@Injectable()
export class AddonQbehaviourImmediateCBMHandler implements CoreQuestionBehaviourHandler {
name = 'AddonQbehaviourImmediateCBM';
type = 'immediatecbm';
constructor(private questionHelper: CoreQuestionHelperProvider) {
// Nothing to do.
}
/**
* Handle a question behaviour.
* If the behaviour requires a submit button, it should add it to question.behaviourButtons.
* If the behaviour requires to show some extra data, it should return the components to render it.
*
* @param {any} question The question.
* @return {any[]|Promise<any[]>} Components (or promise resolved with components) to render some extra data in the question
* (e.g. certainty options). Don't return anything if no extra data is required.
*/
handleQuestion(question: any): any[] | Promise<any[]> {
// Just extract the button, it doesn't need any specific component.
this.questionHelper.extractQbehaviourButtons(question);
if (this.questionHelper.extractQbehaviourCBM(question)) {
// Depends on deferredcbm.
return [AddonQbehaviourDeferredCBMComponent];
}
}
/**
* Whether or not the handler is enabled on a site level.
*
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/
isEnabled(): boolean | Promise<boolean> {
return true;
}
}

View File

@ -0,0 +1,30 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { NgModule } from '@angular/core';
import { AddonQbehaviourImmediateFeedbackHandler } from './providers/handler';
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
@NgModule({
declarations: [
],
providers: [
AddonQbehaviourImmediateFeedbackHandler
]
})
export class AddonQbehaviourImmediateFeedbackModule {
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourImmediateFeedbackHandler) {
behaviourDelegate.registerHandler(handler);
}
}

View File

@ -0,0 +1,56 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
/**
* Handler to support immediate feedback question behaviour.
*/
@Injectable()
export class AddonQbehaviourImmediateFeedbackHandler implements CoreQuestionBehaviourHandler {
name = 'AddonQbehaviourImmediateFeedback';
type = 'immediatefeedback';
constructor(private questionHelper: CoreQuestionHelperProvider) {
// Nothing to do.
}
/**
* Handle a question behaviour.
* If the behaviour requires a submit button, it should add it to question.behaviourButtons.
* If the behaviour requires to show some extra data, it should return the components to render it.
*
* @param {any} question The question.
* @return {any[]|Promise<any[]>} Components (or promise resolved with components) to render some extra data in the question
* (e.g. certainty options). Don't return anything if no extra data is required.
*/
handleQuestion(question: any): any[] | Promise<any[]> {
// Just extract the button, it doesn't need any specific component.
this.questionHelper.extractQbehaviourButtons(question);
return;
}
/**
* Whether or not the handler is enabled on a site level.
*
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/
isEnabled(): boolean | Promise<boolean> {
return true;
}
}

View File

@ -0,0 +1 @@
<input *ngIf="question.behaviourSeenInput" type="hidden" name="{{question.behaviourSeenInput.name}}" value="{{question.behaviourSeenInput.value}}" >

View File

@ -0,0 +1,36 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, Input, EventEmitter } from '@angular/core';
/**
* Component to render a "seen" hidden input for informationitem question behaviour.
*/
@Component({
selector: 'addon-qbehaviour-informationitem',
templateUrl: 'informationitem.html'
})
export class AddonQbehaviourInformationItemComponent {
@Input() question: any; // The question.
@Input() component: string; // The component the question belongs to.
@Input() componentId: number; // ID of the component the question belongs to.
@Input() attemptId: number; // Attempt ID.
@Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline.
@Input() buttonClicked: EventEmitter<any>; // Should emit an event when a behaviour button is clicked.
@Input() onAbort: EventEmitter<void>; // Should emit an event if the question should be aborted.
constructor() {
// Nothing to do.
}
}

View File

@ -0,0 +1,42 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { NgModule } from '@angular/core';
import { IonicModule } from 'ionic-angular';
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
import { AddonQbehaviourInformationItemHandler } from './providers/handler';
import { AddonQbehaviourInformationItemComponent } from './component/informationitem';
@NgModule({
declarations: [
AddonQbehaviourInformationItemComponent
],
imports: [
IonicModule
],
providers: [
AddonQbehaviourInformationItemHandler
],
exports: [
AddonQbehaviourInformationItemComponent
],
entryComponents: [
AddonQbehaviourInformationItemComponent
]
})
export class AddonQbehaviourInformationItemModule {
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourInformationItemHandler) {
behaviourDelegate.registerHandler(handler);
}
}

View File

@ -0,0 +1,73 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
import { CoreQuestionProvider, CoreQuestionState } from '@core/question/providers/question';
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
import { AddonQbehaviourInformationItemComponent } from '../component/informationitem';
/**
* Handler to support information item question behaviour.
*/
@Injectable()
export class AddonQbehaviourInformationItemHandler implements CoreQuestionBehaviourHandler {
name = 'AddonQbehaviourInformationItem';
type = 'informationitem';
constructor(private questionHelper: CoreQuestionHelperProvider, private questionProvider: CoreQuestionProvider) { }
/**
* Determine a question new state based on its answer(s).
*
* @param {string} component Component the question belongs to.
* @param {number} attemptId Attempt ID the question belongs to.
* @param {any} question The question.
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {CoreQuestionState|Promise<CoreQuestionState>} New state (or promise resolved with state).
*/
determineNewState(component: string, attemptId: number, question: any, siteId?: string)
: CoreQuestionState | Promise<CoreQuestionState> {
if (question.answers['-seen']) {
return this.questionProvider.getState('complete');
}
return this.questionProvider.getState(question.state || 'todo');
}
/**
* Handle a question behaviour.
* If the behaviour requires a submit button, it should add it to question.behaviourButtons.
* If the behaviour requires to show some extra data, it should return the components to render it.
*
* @param {any} question The question.
* @return {any[]|Promise<any[]>} Components (or promise resolved with components) to render some extra data in the question
* (e.g. certainty options). Don't return anything if no extra data is required.
*/
handleQuestion(question: any): any[] | Promise<any[]> {
if (this.questionHelper.extractQbehaviourSeenInput(question)) {
return [AddonQbehaviourInformationItemComponent];
}
}
/**
* Whether or not the handler is enabled on a site level.
*
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/
isEnabled(): boolean | Promise<boolean> {
return true;
}
}

View File

@ -0,0 +1,30 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { NgModule } from '@angular/core';
import { AddonQbehaviourInteractiveHandler } from './providers/handler';
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
@NgModule({
declarations: [
],
providers: [
AddonQbehaviourInteractiveHandler
]
})
export class AddonQbehaviourInteractiveModule {
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourInteractiveHandler) {
behaviourDelegate.registerHandler(handler);
}
}

View File

@ -0,0 +1,56 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
/**
* Handler to support interactive question behaviour.
*/
@Injectable()
export class AddonQbehaviourInteractiveHandler implements CoreQuestionBehaviourHandler {
name = 'AddonQbehaviourInteractive';
type = 'interactive';
constructor(private questionHelper: CoreQuestionHelperProvider) {
// Nothing to do.
}
/**
* Handle a question behaviour.
* If the behaviour requires a submit button, it should add it to question.behaviourButtons.
* If the behaviour requires to show some extra data, it should return the components to render it.
*
* @param {any} question The question.
* @return {any[]|Promise<any[]>} Components (or promise resolved with components) to render some extra data in the question
* (e.g. certainty options). Don't return anything if no extra data is required.
*/
handleQuestion(question: any): any[] | Promise<any[]> {
// Just extract the button, it doesn't need any specific component.
this.questionHelper.extractQbehaviourButtons(question);
return;
}
/**
* Whether or not the handler is enabled on a site level.
*
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/
isEnabled(): boolean | Promise<boolean> {
return true;
}
}

View File

@ -0,0 +1,30 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { NgModule } from '@angular/core';
import { AddonQbehaviourInteractiveCountbackHandler } from './providers/handler';
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
@NgModule({
declarations: [
],
providers: [
AddonQbehaviourInteractiveCountbackHandler
]
})
export class AddonQbehaviourInteractiveCountbackModule {
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourInteractiveCountbackHandler) {
behaviourDelegate.registerHandler(handler);
}
}

View File

@ -0,0 +1,56 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
/**
* Handler to support interactive countback question behaviour.
*/
@Injectable()
export class AddonQbehaviourInteractiveCountbackHandler implements CoreQuestionBehaviourHandler {
name = 'AddonQbehaviourInteractiveCountback';
type = 'interactivecountback';
constructor(private questionHelper: CoreQuestionHelperProvider) {
// Nothing to do.
}
/**
* Handle a question behaviour.
* If the behaviour requires a submit button, it should add it to question.behaviourButtons.
* If the behaviour requires to show some extra data, it should return the components to render it.
*
* @param {any} question The question.
* @return {any[]|Promise<any[]>} Components (or promise resolved with components) to render some extra data in the question
* (e.g. certainty options). Don't return anything if no extra data is required.
*/
handleQuestion(question: any): any[] | Promise<any[]> {
// Just extract the button, it doesn't need any specific component.
this.questionHelper.extractQbehaviourButtons(question);
return;
}
/**
* Whether or not the handler is enabled on a site level.
*
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/
isEnabled(): boolean | Promise<boolean> {
return true;
}
}

View File

@ -0,0 +1,30 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { NgModule } from '@angular/core';
import { AddonQbehaviourManualGradedHandler } from './providers/handler';
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
@NgModule({
declarations: [
],
providers: [
AddonQbehaviourManualGradedHandler
]
})
export class AddonQbehaviourManualGradedModule {
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourManualGradedHandler) {
behaviourDelegate.registerHandler(handler);
}
}

View File

@ -0,0 +1,147 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
import { CoreQuestionDelegate } from '@core/question/providers/delegate';
import { CoreQuestionProvider, CoreQuestionState } from '@core/question/providers/question';
/**
* Check if a response is complete.
*
* @param {any} question The question.
* @param {any} answers Object with the question answers (without prefix).
* @return {number} 1 if complete, 0 if not complete, -1 if cannot determine.
*/
export type isCompleteResponseFunction = (question: any, answers: any) => number;
/**
* Check if two responses are the same.
*
* @param {any} question Question.
* @param {any} prevAnswers Object with the previous question answers.
* @param {any} prevBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...).
* @param {any} newAnswers Object with the new question answers.
* @param {any} newBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...).
* @return {boolean} Whether they're the same.
*/
export type isSameResponseFunction = (question: any, prevAnswers: any, prevBasicAnswers: any, newAnswers: any,
newBasicAnswers: any) => boolean;
/**
* Handler to support manual graded question behaviour.
*/
@Injectable()
export class AddonQbehaviourManualGradedHandler implements CoreQuestionBehaviourHandler {
name = 'AddonQbehaviourManualGraded';
type = 'manualgraded';
constructor(private questionDelegate: CoreQuestionDelegate, private questionProvider: CoreQuestionProvider) {
// Nothing to do.
}
/**
* Determine a question new state based on its answer(s).
*
* @param {string} component Component the question belongs to.
* @param {number} attemptId Attempt ID the question belongs to.
* @param {any} question The question.
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {CoreQuestionState|Promise<CoreQuestionState>} New state (or promise resolved with state).
*/
determineNewState(component: string, attemptId: number, question: any, siteId?: string)
: CoreQuestionState | Promise<CoreQuestionState> {
return this.determineNewStateManualGraded(component, attemptId, question, siteId);
}
/**
* Determine a question new state based on its answer(s) for manual graded question behaviour.
*
* @param {string} component Component the question belongs to.
* @param {number} attemptId Attempt ID the question belongs to.
* @param {any} question The question.
* @param {string} [siteId] Site ID. If not defined, current site.
* @param {isCompleteResponseFunction} [isCompleteFn] Function to override the default isCompleteResponse check.
* @param {isSameResponseFunction} [isSameFn] Function to override the default isSameResponse check.
* @return {Promise<CoreQuestionState>} Promise resolved with state.
*/
determineNewStateManualGraded(component: string, attemptId: number, question: any, siteId?: string,
isCompleteFn?: isCompleteResponseFunction, isSameFn?: isSameResponseFunction): Promise<CoreQuestionState> {
// Check if we have local data for the question.
return this.questionProvider.getQuestion(component, attemptId, question.slot, siteId).catch(() => {
// No entry found, use the original data.
return question;
}).then((dbQuestion) => {
const state = this.questionProvider.getState(dbQuestion.state);
if (state.finished || !state.active) {
// Question is finished, it cannot change.
return state;
}
// We need to check if the answers have changed. Retrieve current stored answers.
return this.questionProvider.getQuestionAnswers(component, attemptId, question.slot, false, siteId)
.then((prevAnswers) => {
const newBasicAnswers = this.questionProvider.getBasicAnswers(question.answers);
prevAnswers = this.questionProvider.convertAnswersArrayToObject(prevAnswers, true);
const prevBasicAnswers = this.questionProvider.getBasicAnswers(prevAnswers);
// If answers haven't changed the state is the same.
if (isSameFn) {
if (isSameFn(question, prevAnswers, prevBasicAnswers, question.answers, newBasicAnswers)) {
return state;
}
} else {
if (this.questionDelegate.isSameResponse(question, prevBasicAnswers, newBasicAnswers)) {
return state;
}
}
// Answers have changed. Now check if the response is complete and calculate the new state.
let complete: number,
newState: string;
if (isCompleteFn) {
// Pass all the answers since some behaviours might need the extra data.
complete = isCompleteFn(question, question.answers);
} else {
// Only pass the basic answers since questions should be independent of extra data.
complete = this.questionDelegate.isCompleteResponse(question, newBasicAnswers);
}
if (complete < 0) {
newState = 'unknown';
} else if (complete > 0) {
newState = 'complete';
} else {
newState = 'todo';
}
return this.questionProvider.getState(newState);
});
});
}
/**
* Whether or not the handler is enabled on a site level.
*
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/
isEnabled(): boolean | Promise<boolean> {
return true;
}
}

View File

@ -0,0 +1,44 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { NgModule } from '@angular/core';
import { AddonQbehaviourAdaptiveModule } from './adaptive/adaptive.module';
import { AddonQbehaviourAdaptiveNoPenaltyModule } from './adaptivenopenalty/adaptivenopenalty.module';
import { AddonQbehaviourDeferredCBMModule } from './deferredcbm/deferredcbm.module';
import { AddonQbehaviourDeferredFeedbackModule } from './deferredfeedback/deferredfeedback.module';
import { AddonQbehaviourImmediateCBMModule } from './immediatecbm/immediatecbm.module';
import { AddonQbehaviourImmediateFeedbackModule } from './immediatefeedback/immediatefeedback.module';
import { AddonQbehaviourInformationItemModule } from './informationitem/informationitem.module';
import { AddonQbehaviourInteractiveModule } from './interactive/interactive.module';
import { AddonQbehaviourInteractiveCountbackModule } from './interactivecountback/interactivecountback.module';
import { AddonQbehaviourManualGradedModule } from './manualgraded/manualgraded.module';
@NgModule({
declarations: [],
imports: [
AddonQbehaviourAdaptiveModule,
AddonQbehaviourAdaptiveNoPenaltyModule,
AddonQbehaviourDeferredCBMModule,
AddonQbehaviourDeferredFeedbackModule,
AddonQbehaviourImmediateCBMModule,
AddonQbehaviourImmediateFeedbackModule,
AddonQbehaviourInformationItemModule,
AddonQbehaviourInteractiveModule,
AddonQbehaviourInteractiveCountbackModule,
AddonQbehaviourManualGradedModule
],
providers: [
],
exports: []
})
export class AddonQbehaviourModule { }

View File

@ -86,6 +86,7 @@ import { AddonMessagesModule } from '@addon/messages/messages.module';
import { AddonNotesModule } from '../addon/notes/notes.module';
import { AddonPushNotificationsModule } from '@addon/pushnotifications/pushnotifications.module';
import { AddonRemoteThemesModule } from '@addon/remotethemes/remotethemes.module';
import { AddonQbehaviourModule } from '@addon/qbehaviour/qbehaviour.module';
// For translate loader. AoT requires an exported function for factories.
export function createTranslateLoader(http: HttpClient): TranslateHttpLoader {
@ -172,7 +173,8 @@ export const CORE_PROVIDERS: any[] = [
AddonMessagesModule,
AddonNotesModule,
AddonPushNotificationsModule,
AddonRemoteThemesModule
AddonRemoteThemesModule,
AddonQbehaviourModule
],
bootstrap: [IonicApp],
entryComponents: [

View File

@ -52,6 +52,68 @@ export class CoreQuestionHelperProvider {
});
}
/**
* Extract question behaviour submit buttons from the question's HTML and add them to "behaviourButtons" property.
* The buttons aren't deleted from the content because all the im-controls block will be removed afterwards.
*
* @param {any} question Question to treat.
* @param {string} [selector] Selector to search the buttons. By default, '.im-controls input[type="submit"]'.
*/
extractQbehaviourButtons(question: any, selector?: string): void {
selector = selector || '.im-controls input[type="submit"]';
this.div.innerHTML = question.html;
// Search the buttons.
const buttons = <HTMLInputElement[]> Array.from(this.div.querySelectorAll(selector));
buttons.forEach((button) => {
this.addBehaviourButton(question, button);
});
question.html = this.div.innerHTML;
}
/**
* Check if the question has CBM and, if so, extract the certainty options and add them to a new
* "behaviourCertaintyOptions" property.
* The value of the selected option is stored in question.behaviourCertaintySelected.
* We don't remove them from HTML because the whole im-controls block will be removed afterwards.
*
* @param {any} question Question to treat.
* @return {boolean} Wether the certainty is found.
*/
extractQbehaviourCBM(question: any): boolean {
this.div.innerHTML = question.html;
const labels = Array.from(this.div.querySelectorAll('.im-controls .certaintychoices label[for*="certainty"]'));
question.behaviourCertaintyOptions = [];
labels.forEach((label) => {
// Search the radio button inside this certainty and add its data to the options array.
const input = <HTMLInputElement> label.querySelector('input[type="radio"]');
if (input) {
question.behaviourCertaintyOptions.push({
id: input.id,
name: input.name,
value: input.value,
text: this.textUtils.cleanTags(label.innerHTML),
disabled: input.disabled
});
if (input.checked) {
question.behaviourCertaintySelected = input.value;
}
}
});
// If we have a certainty value stored in local we'll use that one.
if (question.localAnswers && typeof question.localAnswers['-certainty'] != 'undefined') {
question.behaviourCertaintySelected = question.localAnswers['-certainty'];
}
return labels.length > 0;
}
/**
* Check if the question has a redo button and, if so, add it to "behaviourButtons" property
* and remove it from the HTML.
@ -80,6 +142,33 @@ export class CoreQuestionHelperProvider {
}
}
/**
* Check if the question contains a "seen" input.
* If so, add the name and value to a "behaviourSeenInput" property and remove the input.
*
* @param {any} question Question to treat.
* @return {boolean} Whether the seen input is found.
*/
extractQbehaviourSeenInput(question: any): boolean {
this.div.innerHTML = question.html;
// Search the "seen" input.
const seenInput = <HTMLInputElement> this.div.querySelector('input[type="hidden"][name*=seen]');
if (seenInput) {
// Get the data and remove the input.
question.behaviourSeenInput = {
name: seenInput.name,
value: seenInput.value
};
seenInput.parentElement.removeChild(seenInput);
question.html = this.div.innerHTML;
return true;
}
return false;
}
/**
* Removes the comment from the question HTML code and adds it in a new "commentHtml" property.
*