(function () {
'use strict';
var FEEDBACK = {
INCORRECT: 'Sorry. Perhaps you should go back to review the activity.',
CORRECT: 'Press the Next button and move forward.'
};
var ARIATEXTS = {
INTRO_END: "You can change the location of the label only while this label is selected.",
DONE_DRAG: "You have completed the Table with all answers. Press Reset button to start a new try or press Check answer button to check this try.",
COMPLETE: "You have set all the labels correctly. Good job! "
};
var KEYCODES = {
SHIFT: 16
};
var DnDSlide = function (section) {
var self = this;
this.attempt = 0;
this.section = section;
this.section.jq.addClass('dnd_test');
this.section.jq.attr('role', 'application');
this.section.points_possible = 1;
this.section.points_earned = 0;
this.section.section_completed = false;
var correct_fb = this.section.jq.find('.correct_feedback').text();
var incorrect_fb = this.section.jq.find('.incorrect_feedback').text();
this.incorrect_feedback = incorrect_fb || FEEDBACK.INCORRECT;
this.correct_feedback = correct_fb || FEEDBACK.CORRECT;
this.dragItems = [];
this.dropItems = [];
this.ariaDragItems = [];
this.numPlacedItems = 0;
this.numCorrectItems = 0;
this.dragItemsNodes = this.section.jq.find('.drag-item');
this.dropAreas = this.section.jq.find('.drop-area');
var mainAria;
var reviewText = this.section.jq.find('.review_items').text();
this.dropAreas.each(function (i) {
i = i + 1;
$(this).append($('
' + i + '
'));
if (i === self.dropAreas.length) {
self.section.instructionsAria = 'This page contains a matching activity. You need to match a ' +
'label with a definition. Choose the label using TAB button. Then enter ' +
'one of the following numbers to select the corresponding answer.';
mainAria = mainAria || self.section.title + ". " + reviewText + self.section.instructionsAria;
mainAria += " " + ARIATEXTS.INTRO_END;
self.section.jq.attr("aria-label", mainAria);
}
self.dropItems.push($(this));
});
this.dragItemsNodes.each(function (i) {
var $item = $(this).data('drag-item', i);
var ariaLabel = $item.text();
$item.attr({
'tabindex': '0',
'aria-label': ariaLabel
});
$item.keydown(onKeyHandler.bind(self, i));
$item.keyup(onKeyHandlerUp.bind(self, i));
$item.focus(onFocus.bind(self, i));
$item.blur(onBlur.bind(self, i));
self.dragItems.push({
targets: $item.data('targets').split(','),
correct: false,
userTarget: null,
dropped: false
});
});
addButtons.call(this);
addHelperDragItems.call(this);
initDraggableItems.call(this);
initDropAreas.call(this);
initLiveRegion.call(this);
this.updateButtons();
};
var initLiveRegion = function () {
this.liveRegion = $('');
this.section.jq.append(this.liveRegion);
};
var getAriaText = function ($item, ind) {
var text = $item.data('text') || $item.text();
var ariaText = 'You chose ' + text + '. ' +
' Label ' + ( ind + 1 ) + ' of ' + this.dragItemsNodes.length + '.' +
' Enter one of the following numbers to select the corresponding answer: ';
this.dropAreas.each(function (i) {
var isDisabled = $(this).droppable("option", "disabled");
if (!isDisabled) {
ariaText += ( i + 1 ) + '. ' + $(this).data('definition-begin') + ' ' + text + ' ' + $(this).data('definition-end') + '.';
}
});
return ariaText;
};
var onFocus = function (i) {
var item = this.dragItemsNodes[i];
var $item = $(item);
var aria = getAriaText.call(this, $item, i);
$item.attr('aria-label', aria);
this.section.jq.addClass('grabbed');
};
var onBlur = function () {
this.section.jq.removeClass('grabbed');
};
var key;
var onKeyHandlerUp = function (i, e) {
e = e || window.event;
var keyCode = e.keyCode || e.which;
// when the user releases the button
if (keyCode === KEYCODES.SHIFT) {
key = parseInt(key, 10);
key -= 1;
setDropItem.call(this, i);
}
};
var onKeyHandler = function (i, e) {
e = e || window.event;
var keyCode = e.keyCode || e.which;
// If the shift button is pressed, we need to remember what kind of
if (e.shiftKey && keyCode !== KEYCODES.SHIFT) {
key = key || "";
key += String.fromCharCode(keyCode);
} else {
key = String.fromCharCode(e.keyCode);
key = parseInt(key, 10);
key -= 1;
setDropItem.call(this, i);
}
};
var setDropItem = function (i) {
if (!isNaN(key) && key >= 0 && key < this.dragItems.length) { // key 0 -- this.dragItems.length - 1
var $dropArea = $(this.dropAreas[key]);
var $item = $(this.dragItemsNodes[i]);
var item = this.dragItems[i];
if (item.correct) return;
if (!item.originalPosition) {
item.originalPosition = findPosition($item);
}
var isDisabled = $dropArea.droppable('option', 'disabled');
if (!isDisabled) {
this.dragItemsNodes[i].setAttribute('tabindex', "-1");
dropItem.call(this, item, $item, $dropArea);
}
}
key = "";
};
var initDropAreas = function () {
var self = this;
this.dropAreas.droppable({
drop: function (event, ui) {
var $dropArea = $(this);
var $dragItem = $(ui.draggable[0]);
var dragItem = self.dragItems[$dragItem.data('drag-item')];
dropItem.call(self, dragItem, $dragItem, $dropArea);
},
tolerance: 'intersect',
accept: '.drag-item'
});
};
var dropItem = function (dragItem, $dragItem, $dropArea) {
var position = $dropArea.position();
position = {
top: position.top / player._scaleFactor,
left: position.left / player._scaleFactor
};
if (dragItem.userTarget) {
dragItem.userTarget.droppable('enable');
}
else {
this.numPlacedItems++;
this.updateButtons.call(this);
}
dragItem.userTarget = $dropArea;
dragItem.dropped = true;
$dragItem.css(position);
$dropArea.droppable('disable');
this.helper.css(position);
};
var initDraggableItems = function () {
var self = this;
this.dragItemsNodes.draggable({
start: function () {
var $item = $(this);
var item = self.dragItems[$item.data('drag-item')];
item.dropped = false;
resetItem.call(self, item);
self.helper.css({
width: $item.width(),
height: $item.height()
});
if (!item.originalPosition) {
item.originalPosition = findPosition($item);
}
},
drag: function (event, ui) {
ui.position = {
top: Math.round(ui.position.top / player._scaleFactor),
left: Math.round(ui.position.left / player._scaleFactor)
};
self.helper.css(ui.position);
},
stop: function (event, ui) {
var $item = $(this);
var item = self.dragItems[$item.data('drag-item')];
self.helper2.css({
width: $item.width(),
height: $item.height()
});
self.helper2.css(ui.position);
// Revert item with animation
if (!item.dropped) {
revertItem.call(self, item, $item, self.helper2);
}
},
scroll: false
});
};
var findPosition = function ($item) {
var position = $item.position();
return {
top: Math.round(position.top / player._scaleFactor),
left: Math.round(position.left / player._scaleFactor)
};
};
DnDSlide.prototype.updateButtons = function () {
var disable = ( this.numPlacedItems === this.dropItems.length ) ? '' : 'disabled';
this.btnCheck.attr('disabled', disable);
this.btnReset.attr('disabled', disable);
if (this.numPlacedItems === this.dropItems.length) {
this.liveRegion.text(ARIATEXTS.DONE_DRAG);
onBlur.call(this);
}
};
DnDSlide.prototype.checkSingleLabel = function (item, id) {
var result = false;
item.targets.forEach(function (target) {
if (target === id) {
result = true;
}
});
return result;
};
var addHelperDragItems = function () {
this.helper = $('');
this.helper2 = $('');
this.dragItemsNodes.parent().prepend(this.helper, this.helper2);
};
var addButtons = function () {
this.btnReset = $('');
this.btnCheck = $('');
var instruction = $('
Enter one of the following numbers to select the corresponding answer
');
this.btnCheck.bind('click', checkAnswer.bind(this));
this.btnReset.bind('click', resetAllItems.bind(this));
var btnContainer = $('');
btnContainer.append(instruction, this.btnReset, this.btnCheck);
this.section.jq.append(btnContainer);
};
var disableButtons = function () {
this.btnCheck.attr('disabled', 'disabled');
this.btnReset.attr('disabled', 'disabled');
};
var resetAllItems = function () {
var self = this;
if (self.numCorrectItems === self.dropItems.length) {
self.numCorrectItems = 0;
self.section.jq.focus();
this.dragItemsNodes.each(function () {
var $item = $(this);
var item = self.dragItems[$item.data('drag-item')];
resetItem.call(self, item, $item);
if (item.originalPosition) {
$item.css(item.originalPosition);
}
$item.attr('tabindex', '0');
$item.draggable('enable');
$item.removeClass('incorrect');
$item.removeClass('correct');
});
return;
}
this.dragItemsNodes.each(function () {
var $item = $(this);
var item = self.dragItems[$item.data('drag-item')];
if (!item.correct) {
resetItem.call(self, item);
if (item.originalPosition) {
$item.css(item.originalPosition);
}
$item.attr('tabindex', '0');
$item.draggable('enable');
$item.removeClass('incorrect');
$item.removeClass('correct');
}
});
this.updateButtons.call(this);
};
var resetItem = function (item, $item) {
if (item.userTarget) {
item.userTarget.droppable('enable');
item.userTarget = null;
item.correct = false;
item.dropped = false;
this.numPlacedItems--;
}
};
var revertItem = function (item, $item, helper) {
resetItem.call(this, item);
this.updateButtons.call(this);
$item.addClass('revert');
$item.animate(item.originalPosition, 400, function () {
$item.removeClass('revert');
});
helper.animate(item.originalPosition, 400);
};
var checkAnswer = function () {
var self = this;
disableButtons.call(this);
this.numCorrectItems = 0;
var fb;
var correctAnswers = 0;
this.dragItemsNodes.each(function (i) {
var $item = $(this);
var item = self.dragItems[$item.data('drag-item')];
if (item.userTarget) {
item.correct = self.checkSingleLabel.call(self, item, item.userTarget.data('drop-area'));
if (item.correct) {
$item.attr('tabindex', '-1');
correctAnswers++;
}
fb = 'You have set ' + correctAnswers + ' of ' + self.dragItemsNodes.length + ' labels correctly. ';
var className = item.correct ? 'correct' : 'incorrect';
$item.addClass(className);
$item.draggable('disable');
self.numCorrectItems += item.correct;
}
});
var html;
if (this.numCorrectItems === this.dropItems.length) {
finishSlide.call(this);
html = '