H5P.TrueFalse.Answer = (function ($, EventDispatcher) {
'use strict';
var Keys = {
ENTER: 13,
SPACE: 32,
LEFT_ARROW: 37,
UP_ARROW: 38,
RIGHT_ARROW: 39,
DOWN_ARROW: 40
};
/**
* Initialize module.
*
* @class H5P.TrueFalse.Answer
* @extends H5P.EventDispatcher
* @param {String} text Label
* @param {String} correctMessage Message read by readspeaker when correct alternative is chosen
* @param {String} wrongMessage Message read by readspeaker when wrong alternative is chosen
*/
function Answer (text, correctMessage, wrongMessage) {
var self = this;
EventDispatcher.call(self);
var checked = false;
var enabled = true;
var $answer = $('
', {
'class': 'h5p-true-false-answer',
role: 'radio',
'aria-checked': false,
html: text + '',
tabindex: 0, // Tabable by default
click: function (event) {
// Handle left mouse (or tap on touch devices)
if (event.which === 1) {
self.check();
}
},
keydown: function (event) {
if (!enabled) {
return;
}
if ([Keys.SPACE, Keys.ENTER].indexOf(event.keyCode) !== -1) {
event.preventDefault();
self.check();
}
else if ([Keys.LEFT_ARROW, Keys.UP_ARROW, Keys.RIGHT_ARROW, Keys.DOWN_ARROW].indexOf(event.keyCode) !== -1) {
event.preventDefault();
self.uncheck();
self.trigger('invert');
}
},
focus: function () {
self.trigger('focus');
},
blur: function () {
self.trigger('blur');
}
});
var $ariaLabel = $answer.find('.aria-label');
// A bug in Chrome 54 makes the :after icons (V and X) not beeing rendered.
// Doing this in a timeout solves this
// Might be removed when Chrome 56 is out
var chromeBugFixer = function (callback) {
setTimeout(function () {
callback();
}, 0);
};
/**
* Return the dom element representing the alternative
*
* @public
* @method getDomElement
* @return {H5P.jQuery}
*/
self.getDomElement = function () {
return $answer;
};
/**
* Unchecks the alternative
*
* @public
* @method uncheck
* @return {H5P.TrueFalse.Answer}
*/
self.uncheck = function () {
if (enabled) {
$answer.blur();
checked = false;
chromeBugFixer(function () {
$answer.attr('aria-checked', checked);
});
}
return self;
};
/**
* Set tabable or not
* @method tabable
* @param {Boolean} enabled
* @return {H5P.TrueFalse.Answer}
*/
self.tabable = function (enabled) {
$answer.attr('tabIndex', enabled ? 0 : null);
return self;
};
/**
* Checks the alternative
*
* @method check
* @return {H5P.TrueFalse.Answer}
*/
self.check = function () {
if (enabled) {
checked = true;
chromeBugFixer(function () {
$answer.attr('aria-checked', checked);
});
self.trigger('checked');
$answer.focus();
}
return self;
};
/**
* Is this alternative checked?
*
* @method isChecked
* @return {boolean}
*/
self.isChecked = function () {
return checked;
};
/**
* Enable alternative
*
* @method enable
* @return {H5P.TrueFalse.Answer}
*/
self.enable = function () {
$answer.attr({
'aria-disabled': '',
tabIndex: 0
});
enabled = true;
return self;
};
/**
* Disables alternative
*
* @method disable
* @return {H5P.TrueFalse.Answer}
*/
self.disable = function () {
$answer.attr({
'aria-disabled': true,
tabIndex: null
});
enabled = false;
return self;
};
/**
* Reset alternative
*
* @method reset
* @return {H5P.TrueFalse.Answer}
*/
self.reset = function () {
self.enable();
self.uncheck();
self.unmark();
$ariaLabel.html('');
return self;
};
/**
* Marks this alternative as the wrong one
*
* @method markWrong
* @return {H5P.TrueFalse.Answer}
*/
self.markWrong = function () {
chromeBugFixer(function () {
$answer.addClass('wrong');
});
$ariaLabel.html('.' + wrongMessage);
return self;
};
/**
* Marks this alternative as the wrong one
*
* @method markCorrect
* @return {H5P.TrueFalse.Answer}
*/
self.markCorrect = function () {
chromeBugFixer(function () {
$answer.addClass('correct');
});
$ariaLabel.html('.' + correctMessage);
return self;
};
self.unmark = function () {
chromeBugFixer(function () {
$answer.removeClass('wrong correct');
});
return self;
};
}
// Inheritance
Answer.prototype = Object.create(EventDispatcher.prototype);
Answer.prototype.constructor = Answer;
return Answer;
})(H5P.jQuery, H5P.EventDispatcher);