﻿/* ===================================================
 * main.js
 * ===================================================
 * Copyright AS "Datorzinību centrs", 2018
 * =================================================== */
(function($) {
    $.support.touch = 'ontouchend' in document;
    if (!$.support.touch) {
        return;
    }
    var mouseProto = $.ui.mouse.prototype,
            _mouseInit = mouseProto._mouseInit,
            touchHandled;
    function simulateMouseEvent(event, simulatedType) {
        if (event.originalEvent.touches.length > 1) {
            return;
        }
        event.preventDefault();
        var touch = event.originalEvent.changedTouches[0],
                simulatedEvent = document.createEvent('MouseEvents');
        simulatedEvent.initMouseEvent(
                simulatedType,
                true,
                true,
                window,
                1,
                touch.screenX,
                touch.screenY,
                touch.clientX,
                touch.clientY,
                false,
                false,
                false,
                false,
                0,
                null
                );
        event.target.dispatchEvent(simulatedEvent);
    }
    mouseProto._touchStart = function(event) {
        var self = this;
        if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
            return;
        }
        touchHandled = true;
        self._touchMoved = false;
        simulateMouseEvent(event, 'mouseover');
        simulateMouseEvent(event, 'mousemove');
        simulateMouseEvent(event, 'mousedown');
    };
    mouseProto._touchMove = function(event) {
        if (!touchHandled) {
            return;
        }
        this._touchMoved = true;
        simulateMouseEvent(event, 'mousemove');
    };
    mouseProto._touchEnd = function(event) {
        if (!touchHandled) {
            return;
        }
        simulateMouseEvent(event, 'mouseup');
        simulateMouseEvent(event, 'mouseout');
        if (!this._touchMoved) {
            simulateMouseEvent(event, 'click');
        }
        touchHandled = false;
    };
    mouseProto._mouseInit = function() {
        var self = this;
        self.element
                .on('touchstart', $.proxy(self, '_touchStart'))
                .on('touchmove', $.proxy(self, '_touchMove'))
                .on('touchend', $.proxy(self, '_touchEnd'));
        _mouseInit.call(self);
    };
})(jQuery);
function shortenText() {
	if ($('#section-1').length) {
		$('#section-1').addClass('in');
		
		var pHtml = $('#section-1').html();
		if (pHtml.length && pHtml.indexOf('|') !== -1) {
			pHtml = pHtml.replace('|', '</p><p class="delimited">');
			$('#section-1').html(pHtml);
			shortenTextDelimited();
		}
		else {
			shortenTextHeight();
		}
	}
}
function shortenTextDelimited() {
	$('#section-1').children().each(function() {
		if ($(this).hasClass('delimited')) {
			$(this).prev().addClass('ellipsis');
			
			appendMore();
			
			return false;
		}
		else {
			$(this).addClass('shortenedText');
		}
	});
}
function shortenTextHeight() {
	var requiredHeight = parseFloat($('#section-1 p').eq(0).css('line-height')) * 6 + 32, /* 188.6 */
		currentHeight = 0;
	
	$('#section-1').children().each(function(i) {
		currentHeight += $(this).outerHeight(true);
		$(this).addClass('shortenedText');
		
		if (currentHeight > requiredHeight && i !== 0) {
			if ((currentHeight - requiredHeight) > (requiredHeight - (currentHeight - $(this).outerHeight(true)))) {
				$(this).removeClass('shortenedText');
				$(this).prev().addClass('ellipsis');
			}
			else if ($('#section-1').children().not('.shortenedText').length) {
				$(this).addClass('ellipsis');
			}
			
			if ($('#section-1').children().not('.shortenedText').length) {
				if ($('#section-1').children().not('.shortenedText').length === 1 && $('#section-1 .ellipsis').next().outerHeight(true) <= parseFloat($('#section-1 p').eq(0).css('line-height')) * 2 + 32) { /* 58.1 */
					$('#section-1 .ellipsis').next().addClass('shortenedText');
					$('#section-1').children().removeClass('ellipsis');
				}
				else {
					appendMore();
				}
			}
			
			return false;
		}
	});
}
function appendMore() {
	if (!$('#section-1 .more').length) {
		$('#section-1')
			.append(
				$('<div></div>', {
					class: 'more'
				})
				.append(
					$('<div></div>', {
						class: 'more-button'
					})
					.on('click', function(e) {
						$('#section-1').children().removeClass('ellipsis');
						$('div.more').hide();
						
						$('.moreText')
							.slideDown(200, 'linear', function() {
								if ($('.delimited').length) {
									var pHtml = $('.delimited').html();
									$('.more').prev().append(pHtml);
									$('.delimited').remove();
								}
								
								if ($(this).find('.img-wrapper').length) {
									triggerLoad($(this).find('.img-wrapper'));
								}
								
								setupScrolling();
							});
					})
				)
			)
			.append(
				$('<div></div>', {
					class: 'moreText'
				})
				.append(
					$('#section-1').children().not('.shortenedText, div.more')
				)
				.hide()
			);
	}
}
function emphasizeText() {
	if ($('.Atceries').length) {
		$('.Atceries')
			.before(
				$('<p></p>', {
					class: 'Atceries-title'
				})
					.text('Atceries!')
			);
	}
	
	if ($('#accordion').length) {
		setupAccordions();
	}
}
function setupAccordions() {
	var n = 0;
	
	$('.panel-group').each(function(i) {
		$(this)
			.attr('id', 'accordion-' + (i + 1));
		
		$(this).children().each(function() {
			$(this).find('.panel-heading a')
				.attr('data-parent', '#accordion-' + (i + 1))
				.attr('href', '#collapse-' + (++n));
			
			$(this).find('.panel-collapse')
				.attr('id', 'collapse-' + n);
		});
	});
}
function setupImg() {
	var i,
		pWords,
		$caption,
		$span,
		$spanHtml;
	
	// Setup wrapper
	$('.article-section-content > div > img').parent().addClass('img-wrapper');
	$('.article-section-content > .moreText > div > img').parent().addClass('img-wrapper');
	$('.article-section-content > .exe > div > img').parent().addClass('img-wrapper');
	$('.article-section-content .zoom-td-img td > div > img').parent().addClass('img-wrapper');
	
	// Setup images' captions
	$('.img-wrapper').each(function(j) {
		if ($(this).next('p').length) {
			pWords = cleanText($(this).next('p').html());
			if (pWords[0] === "Attēls.") {
				$caption = $(this).next('p');
				
				for (i = 0; i < $caption.children().length; i += 1) {
					$span = $caption.children().eq(i);
					$spanHtml = $span.html();
					if ($span.hasClass('emoStyleBold') || $spanHtml.indexOf("Attēls") != -1 || $spanHtml.indexOf(".") != -1) {
						$span
							.addClass('span-remove');
						
						if ($spanHtml.indexOf(".") != -1) {
							break;
						}
					}
					else {
						break;
					}
				}
				$caption.children('.span-remove')
					.remove();
				
				$caption
					.wrap($('<div></div>', {
							class: 'img-caption'
						})
					)
					.wrap($('<div></div>'));
				
				$caption.parent().parent()
					.append($('<div></div>', {
							class: 'helper-middle'
						})
					);
				
				$(this)
					.addClass('withCaption');
			}
		}
	});
	
	if ($('.img-wrapper:visible').length) {
		triggerLoad($('.img-wrapper:visible'));
	}
}
function triggerLoad(that) {
	that.imagesLoaded()
		.progress(function(instance, image) {
			var $image = $(image.img);
			if (image.isLoaded) {
				var $wrapper = $image.closest('.img-wrapper');
				if ($wrapper.length) {
					setupZooming($wrapper);
				}
				else {
					console.log('Cannot find image wrapper:', $image);
				}
			}
			else {
				console.log('Image is broken:', $image);
			}
		});
}
function setupZooming(that) {
	var zoom = false;
	
	if (that.not('.vaks').find('img').prop('naturalWidth') > that.find('img').width()) {
		zoom = true;
	}
	if (that.not('.vaks').find('img').prop('naturalHeight') > parseInt(that.find('img').css('max-height'))) {
		zoom = true;
	}
	
	if (zoom) {
		that
			.addClass('zoom');
		
		setupModal(that);
	}
}
function setupModal(that) {
	var modalID = $('.modal').length + 1;
	
	that
		.append($('<div></div>', {
				role: 'dialog',
				class: 'modal fade',
				id: 'img-modal-' + modalID
				
			})
			.append($('<div></div>', {
					class: 'modal-dialog'
				})
				.append($('<div></div>', {
						class: 'modal-content'
					})
					.append($('<div></div>', {
							class: 'modal-header'
						})
						.append('<button type="button" class="close">&times;</button>')
					)
					.append($('<div></div>', {
							class: 'modal-body'
						})
					)
					.append($('<div></div>', {
							class: 'modal-footer'
						})
					)
				)
			)
		);
	
	if (that.next('.img-caption').length) {
		that.next('.img-caption')
			.clone()
			.appendTo(that.find('.modal-footer'));
	}
	
	that.children('img')
		.clone()
		.appendTo(that.find('.modal-body'));
	
	that
		.attr('data-toggle', 'modal')
		.attr('data-target', '#img-modal-' + modalID)
		.attr('data-backdrop', 'static')
		.attr('data-keyboard', 'true');
}
function cleanTextInput(pHtml) {
	var pWords = [];
	
	pWords = pHtml.split(' ');
	pWords = jQuery.grep(pWords, function(v) {
		return v !== '';
	});
	
	return pWords;
}
function cleanText(pHtml) {
	var pText,
		pWords = [];
	
	pText = pHtml;
	pText = pText.replace(/<.*?>/g, '');
	pText = pText.replace(/[\n\r]/g, ' ');
	pText = pText.replace('Attēls.&nbsp;', 'Attēls. ');
	
	pWords = pText.split(' ');
	pWords = jQuery.grep(pWords, function(v) {
		return v !== '';
	});
	
	return pWords;
}
function checkColspans() {
	var table_heading_html,
		start;
	
	$('.article-panel').find('td.table_heading').each(function() {
		table_heading_html = $(this).html();
		start = table_heading_html.indexOf('#');
		
		if (start !== -1) {
			table_heading_html = table_heading_html.replace('#', '');
			$(this).html(table_heading_html);
			
			$(this).attr('colspan', 2);
		}
	});
	
	if ($('.vertical-text').length) {
		setVerticalText();
	}
	
	formatPIEMERS();
}
function formatPIEMERS() {
	$('.article-section').each(function() {
		if (!$(this).hasClass('Testi')) {
			$(this).find('table.txt').each(function() {
				var table_heading_html = $(this).find('tr').eq(0).find('td').eq(0).html();
				
				if ((table_heading_html.indexOf('PIEMĒRS') !== -1) || (table_heading_html.indexOf('PIEMĒRI') !== -1)) {
					$(this).addClass('PIEMERS');
				}
			});
		}
	});
}
function setVerticalText() {
	$('.vertical-text').parent().addClass('vertical-text-td');
	$('.vertical-text').parent().parent().parent().parent().addClass('vertical-text-table');
}
function scrollTop() {
    $('.collapse')
		.on('shown.bs.collapse', function() {
			setupScrolling();
		})
		.one('shown.bs.collapse', function() {
			if ($(this).not('#section-1').find('.img-wrapper').length) {
				triggerLoad($(this).find('.img-wrapper'));
			}
		})
		.on('hidden.bs.collapse', function() {
			//$(this).removeClass('in');
			setupScrolling();
		});
	
    $('a.scrollTop')
		.on('click', function(e) {
			$('html, body').stop().animate({
				scrollTop: $($(this).attr('href')).offset().top
			}, 500, 'easeInOutExpo');
			
			e.preventDefault();
		});
}
function setupScrolling() {
	if ($('.article-container').height() > $(window).height()) {
		$('a.scrollTop').show();
	}
	else {
		$('a.scrollTop').hide();
	}
}
function linksTarget() {
	$('.article-content a').attr('target', '_blank');
	$('a.scrollTop').attr('target', '_self');
}
function navigate() {
	$(document.body).on('keyup', function(e) {
		var code = e.keyCode ? e.keyCode : e.which;
		
		if (!$('textarea:focus').length && !$('input[type="text"]:focus').length) {
			if (code === 33 || code === 37) {
				window.location.href = $('.prev').attr('href');
			}
			if (code === 34 || code === 39) {
				window.location.href = $('.next').attr('href');
			}
			
			e.preventDefault();
		}
	});
}
function Exercise(options) {
    $.extend(this, {
		context: null,
		exeIndex: null,
		controls: [],
		textInputContext: true,
		visibleAnswers: false
    }, options);
	
	this.find = function(selector) {
		return $(selector, this.context);
	};
	
	this.getControlsByType = function(type) {
		var returnArray = [];
		
		$.each(this.controls, function(k, v) {
			if (this.type == type) {
				returnArray.push(v);
			}
		});
		return returnArray;
	};
	
	this.getControlsByName = function(name) {
		var returnArray = [];
		
		$.each(this.controls, function(k, v) {
			if (this.name == name) {
				returnArray.push(v);
			}
		});
		return returnArray;
	};
	
	this.addControls();
}
function Control(options) {
	this.attach = {};
	
    $.extend(this, {
		exe: null,
		context: null,
		type: null,
		tabindex: null,
		contextIndex: null,
		object: undefined,
		name: null,
		id: null,
        answer: null,
		contextAnswerUsed: false,
		initialVal: null,
		currentVal: null,
		correct: false
    }, options);
	
	var contextControls = this.context.data('controls') || [];
	contextControls.push(this);
	this.context.data('controls', contextControls);
	
	this.find = function(selector) {
		return $(selector, this.context);
	};
	
	this.checkAnswer = function() {
		if (!this.object.hasClass('initial')) {
			if (this.currentVal === this.answer) {
				this.object
					.addClass('correct');
			}
			else if (this.currentVal !== null) {
				this.object
					.addClass('wrong');
			}
		}
	};
	
	this.showAnswer = function() {
		if (this.type === 'textMarkup') {
			this.object
				.removeClass('highlight')
				.addClass('disabled');
			
			if (this.answer) {
				this.object
					.addClass('highlight');
			}
		}
		else if (this.type === 'radioInput' || this.type === 'checkboxInput') {
			this.object
				.prop('checked', this.answer);
		}
		else {
			this.object
				.val(this.answer);
		}
		
		this.object
			.attr('disabled', true)
			.removeClass('correct wrong');
	};
	
	this.hideAnswer = function() {
		if (this.type === 'textMarkup') {
			this.object
				.removeClass('disabled highlight');
			
			if (this.currentVal) {
				this.object
					.addClass('highlight');
			}
		}
		else if (this.type === 'radioInput' || this.type === 'checkboxInput') {
			this.object
				.prop('checked', this.currentVal);
		}
		else {
			this.object
				.val(this.currentVal);
		}
		
		this.object
			.attr('disabled', false);
	};
	
	this.setupObject = $.noop;
	
	if (!this.context || !this.type) {
		return;
	}
	
	if (this.types[this.type]) {
		this.types[this.type].call(this);
	}
	
	$.each(this.attach, function(k, v) {
		v.call(this);
	});
}
Control.prototype.types = {
	'textMarkup': function() {
		var that = this;
		
		this.changeCurrentVal = function() {
			if (!that.object.hasClass('disabled')) {
				that.object
					.removeClass('correct wrong')
					.toggleClass('initial highlight');
				
				that.exe.find('.exe-title').removeClass('completed');
				that.exe.find('.button.Parbaudit').removeClass('completed');
				
				that.currentVal = !that.currentVal;
			}
		};
		
		this.attach.setupObject = function() {
			that.object = that.find('.control').eq(that.contextIndex);
			
			if (that.object.hasClass('not')) {
				that.answer = false;
			}
			else {
				that.answer = true;
			}
		};
		
		this.attach.onMousedown = function() {
			that.object.on('mousedown', that.changeCurrentVal);
		};
	},
	'textArea': function() {
		var that = this;
		
		this.changeCurrentVal = function() {
			that.object.removeClass('correct wrong');
			
			that.exe.find('.exe-title').removeClass('completed');
			that.exe.find('.button.Parbaudit').removeClass('completed');
			
			that.currentVal = that.object.val();
			
			that.object.outerHeight(1);
			that.object.outerHeight(this.scrollHeight);
		};
		
		this.attach.setupObject = function() {
			that.object = that.find('textarea').eq(that.contextIndex);
			that.object.attr('tabindex', that.tabindex);
		};
		
		this.attach.onInput = function() {
			that.object.on('input', that.changeCurrentVal);
		};
	},
	'textInput': function() {
		var that = this;
		
		this.changeCurrentVal = function() {
			var contextControls = that.context.data('controls');
			
			$.each(contextControls, function() {
				this.object.removeClass('correct wrong');
			});
			
			that.exe.find('.exe-title').removeClass('completed');
			that.exe.find('.button.Parbaudit').removeClass('completed');
			
			that.currentVal = that.object.val();
			
			if (that.currentVal === that.answer) {
				if (that.tabindex === that.exe.getControlsByType('textInput').length + that.exe.exeIndex * 100) {
					that.exe.find('.Parbaudit').focus();
				}
				else {
					that.exe.find('[type="text"].control').eq(that.tabindex - that.exe.exeIndex * 100).focus();
				}
			}
		};
		
		this.attach.setupObject = function() {
			that.object = that.find('[type="text"].control').eq(that.contextIndex);
			that.object.attr('tabindex', that.tabindex);
		};
		
		this.attach.onKeyup = function() {
			that.object.on('keyup', that.changeCurrentVal);
		};
	},	
	'radioInput': function() {
		var that = this;
		
		this.changeCurrentVal = function() {
			$.each(that.exe.getControlsByName(that.name), function() {
				this.object.removeClass('correct wrong');
				
				that.exe.find('.exe-title').removeClass('completed');
				that.exe.find('.button.Parbaudit').removeClass('completed');
				
				this.currentVal = null;
			});
			
			that.currentVal = 1;
		};
		
		this.attach.setupObject = function() {
			that.object = that.context.find('[type="radio"].control').eq(that.contextIndex);
			
			that.name = that.exe.context.attr('id').concat('-', that.name);
			that.object.attr('name', that.name);
			
			var id = $('input[name="' + that.name + '"]:radio').index(that.object) + 1;
			that.id = that.name.concat('-', id);
			that.object.attr('id', that.id);
			that.object.next('label').attr('for', that.id);
			
			if (that.object.next('label').html().length <= 4) {
				that.object.next('label').parent().addClass('center');
			}
		};
		
		this.attach.onChange = function() {
			that.object.on('change', that.changeCurrentVal);
		};
	},
	'checkboxInput': function() {
		var that = this;
		
		this.changeCurrentVal = function() {
			that.object.removeClass('correct wrong');
			that.exe.find('.exe-title').removeClass('completed');
			that.exe.find('.button.Parbaudit').removeClass('completed');
			
			if (that.object.prop('checked')) {
				that.currentVal = 1;
			}
			else {
				that.currentVal = 0;
			}
		};
		
		this.attach.setupObject = function() {
			that.object = that.context.find('[type="checkbox"].control').eq(that.contextIndex);
			
			that.name = that.exe.context.parent().attr('id').concat('-', that.name);
			that.object.attr('name', that.name);
			
			var id = $('input[name="' + that.name + '"]:checkbox').index(that.object) + 1;
			that.id = that.name.concat('-', id);
			that.object.attr('id', that.id);
			that.object.next('label').attr('for', that.id);
			
			if (that.object.next('label').html().length <= 4) {
				that.object.next('label').parent().addClass('center');
			}
		};
		
		this.attach.onChange = function() {
			that.object.on('change', that.changeCurrentVal);
		};
	},
	'select': function() {
		var that = this;
		
		this.changeCurrentVal = function() {
			that.object.removeClass('correct wrong');
			that.exe.find('.exe-title').removeClass('completed');
			that.exe.find('.button.Parbaudit').removeClass('completed');
			
			that.currentVal = parseInt(that.object.val());
			if (that.currentVal === 0) {
				that.currentVal = null;
			}
		};
		
		this.openDropdown = function() {
			that.object.removeClass('correct wrong');
		};
		
		this.attach.setupObject = function() {
			that.object = that.context.find('select').eq(that.contextIndex);
		};
		
		this.attach.onChange = function() {
			that.object.on('change', that.changeCurrentVal);
		};
		
		this.attach.onFocus = function() {
			that.object.on('focus', that.openDropdown);
		};
	},
	'sortable': function() {
		var that = this;
		
		this.changeCurrentVal = function() {
			that.currentVal = that.object.index() + 1;
			
			that.exe.find('.exe-title').removeClass('completed');
			that.exe.find('.button.Parbaudit').removeClass('completed');
		};
		
		this.attach.setupObject = function() {
			that.object = that.find('.sortable').eq(that.contextIndex);
		};
		
		this.attach.onUpdatePosition = function() {
			that.object.on('updateposition', that.changeCurrentVal);
		};
	},
	'draggable': function() {
		var that = this;
		
		this.changeCurrentVal = function() {
			that.exe.find('.exe-title').removeClass('completed');
			that.exe.find('.button.Parbaudit').removeClass('completed');
			
			that.currentVal = that.object.parent().attr('name');
		};
		
		this.attach.setupObject = function() {
			that.object = that.context.find('.draggable').eq(that.contextIndex);
		};
		
		this.attach.onUpdatePosition = function() {
			that.object.on('updateposition', that.changeCurrentVal);
		};
	}
};
Exercise.prototype.addControls = function() {
	var that = this,
		pHtml,
		pHtml1,
		pHtml2,
		pSpans = [],
		pWords = [],
		control,
		tabindex = this.exeIndex * 100,
		i;
	
	this.context.find('p').each(function() {
		pHtml = $(this).html();
		
		if (pHtml.indexOf('</media>') === -1 && pHtml.indexOf('</bibliography>') === -1 && pHtml.indexOf('</term>') === -1 && pHtml.indexOf('conent-link') === -1) {
			removeSpanTags($(this));
			
			pHtml = $(this).html();
			
			if ($(this).find('mark').length) {
				setupTextMarkup($(this));
			}
			else if (pHtml.indexOf('~~') !== -1) {
				setupTextArea($(this));
			}
			else if (pHtml.indexOf('~') !== -1) {
				setupTextInput($(this));
			}
			else if (pHtml.indexOf('@') !== -1 && that.context.find('.checkboxes').length) {
				setupCheckboxInput($(this));
			}
			else if (pHtml.indexOf('@') !== -1) {
				setupRadioInput($(this));
			}
			else if (pHtml.indexOf('|') !== -1) {
				setupSelect($(this));
			}
			else if (pHtml.indexOf('#') !== -1) {
				setupSortable($(this));
			}
			else if (pHtml.indexOf('$') !== -1) {
				setupDraggable($(this));
			}
		}
	});
	
	if (this.getControlsByType('textInput').length) {
		this.setupTextInputWidth();
		
		if ($('#exe-' + this.exeIndex).children('.exe-title').html().indexOf('alfab') !== -1) {
			this.textInputContext = false;
		}
	}
	if (this.getControlsByType('sortable').length) {
		if (!this.find('.sortable.inline').length) {
			this.find('.sortable').unwrap();
			
			this.find('.sortable').eq(0)
				.before(
					$('<ul></ul>', {
						class: 'sortable-list'
					})
				);
			
			this.find('.sortable-list').append(this.find('.sortable'));
			
			var sortableControls = this.getControlsByType('sortable');
			$.each(this.getControlsByType('sortable'), function(k, v) {
				v.initialVal = k + 1;
				v.currentVal = v.initialVal;
			});
		}
		
		this.find('.sortable-list')
			.sortable({
				start: function(e, ui) {
					that.find('.sortable').removeClass('correct wrong');
				},
				update: function(e, ui) {
					$(ui.item).removeClass('initial');
					that.find('.sortable').trigger('updateposition');
				}
			})
			.disableSelection()
			.children('.sortable').addClass('initial');
	}
	if (this.getControlsByType('draggable').length) {
		if (!this.find('.draggable.inline').length) {
			this.find('.draggable').unwrap();
			
			this.find('.draggable').eq(0)
				.before(
					$('<ul></ul>', {
						class: 'draggable-list clearfix'
					})
				);
			
			this.find('.draggable-list').append(this.find('.draggable'));
		}
		
		this.context
			.append(
				$('<div></div>', {
					class: 'containment-draggable'
				})
					.append(
						this.find('.draggable-list')
					)
					.append(
						this.find('table.txt')
					)
			);
		
		this.find('table.txt td').not('.table_heading').each(function(i) {
			$(this)
				.append(
					$('<ul></ul>', {
						class: 'droppable clearfix',
						name: i + 1
					})
				);			
		});
		
		this.find('.draggable')
			.draggable({
				containment: '.containment-draggable',
				stack: that.find('.draggable'),
				snap: that.find('.droppable'),
				snapMode: 'inner',
				snapTolerance: 20,
				revert: 'invalid',
				start: function(e, ui) {
					$(e.target).removeClass('correct wrong');
				}
			})
			.disableSelection()
			.addClass('initial');
		
		this.find('.droppable')
			.droppable({
				accept: that.find('.draggable'),
				tolerance: 'pointer',
				drop: function(e, ui) {				
					$(ui.draggable)
						.appendTo(e.target)
						.css({left: '0px', top: '0px'})
						.removeClass('initial')
						.trigger('updateposition');
					
					if ($(e.target).children().length === 1 || ($(ui.draggable).outerWidth() + 4) > $(e.target).width()) {
						$(e.target).parent().width(($(ui.draggable).outerWidth() + 4) + 'px');
					}
					if (!that.context.find('.grouping-1').length) {
						that.find('.droppable').each(function() {
							$(this).height((($(this).children().length + 1) * 36) + 'px');
						});
					}
				}
			})
			.disableSelection();
	}
	
	if (this.controls.length || that.context.find('.Atbildes-block').length) {
		this.addButtons();
	}
	
	function removeSpanTags($p) {
		pHtml = $p.html();
		
		pHtml = removeOuterSpanTags(pHtml);
		
		pSpans = pHtml.split('</span>');
		pHtml = '';
		
		$.each(pSpans, function(k, v) {
			if (v.indexOf('<span>') !== -1) {
				v = v.replace('<span>', '');
			}
			else if (v.indexOf('<span class="emoStyleBold">') !== -1) {
				v = v.replace('<span class="emoStyleBold">', '<b>');
				v = v.concat('</b>');
			}
			else if (v.indexOf('<span class="emoStyleItalic">') !== -1) {
				v = v.replace('<span class="emoStyleItalic">', '<i>');
				v = v.concat('</i>');
			}
			else if (v.indexOf('<span class="emoStyleBold emoStyleItalic">') !== -1) {
				v = v.replace('<span class="emoStyleBold emoStyleItalic">', '<em>');
				v = v.concat('</em>');
			}
			else if (v.indexOf('<span class="emoStyleItalic emoStyleBold">') !== -1) {
				v = v.replace('<span class="emoStyleItalic emoStyleBold">', '<em>');
				v = v.concat('</em>');
			}
			else if (v.indexOf('<span class="emoStyleUnderline">') !== -1) {
				v = v.replace('<span class="emoStyleUnderline">', '<mark>');
				v = v.concat('</mark>');
			}
			
			pHtml = pHtml.concat(v);
		});
		
		$p.html(pHtml);
	}
	function removeOuterSpanTags(pHtml) {
		var spanStartTag = pHtml.indexOf('<span>'),
			pHtmlInsideSpan,
			spanEndTag;
		
		if (spanStartTag !== -1) {
			pHtmlInsideSpan = pHtml.substr(spanStartTag + 6);
			spanStartTag = pHtmlInsideSpan.indexOf('<span');
			spanEndTag = pHtmlInsideSpan.indexOf('</span>');
			if (spanStartTag !== -1 && spanStartTag < spanEndTag) {
				if (spanEndTag !== pHtmlInsideSpan.lastIndexOf('</span>')) {
					spanEndTag = pHtmlInsideSpan.lastIndexOf('</span>');
					pHtmlInsideSpan = pHtmlInsideSpan.substring(0, spanEndTag);
					spanStartTag = pHtmlInsideSpan.lastIndexOf('<span');
					spanEndTag = pHtmlInsideSpan.lastIndexOf('</span>');
					if (spanStartTag !== -1 && spanStartTag < spanEndTag) {
						pHtml = pHtmlInsideSpan;
					}
				}
			}
		}
		
		return pHtml;
	}
	function setupTextMarkup($p) {
		var prefix = ['„', '('],
			suffix = [',', '.', '!', '?', ';', ':', '”', ')'],
			fix,
			tag,
			part1,
			part2,
			part3;
		
		pHtml = $p.html();
		pWords = pHtml.split(' ');
		pWords = jQuery.grep(pWords, function(v) {
			return v !== '';
		});
		pHtml = '';
		
		$.each(pWords, function(k, v) {			
			if (v.indexOf('<mark>') === -1) {
				v = '<mark class="not">'.concat(v, '</mark>');
			}
			
			for (i = 0; i < prefix.length; i += 1) {
				fix = v.indexOf(prefix[i]);
				tag = v.indexOf('<');
				
				if (fix > tag) {
					part1 = v.substring(0, tag);
					part2 = v.substring(tag, fix);
					part3 = v.substr(fix + 1);
					
					v = part1.concat(prefix[i], part2, part3);
				}
			}
			
			for (i = 0; i < suffix.length; i += 1) {
				fix = v.indexOf(suffix[i]);
				tag = v.lastIndexOf('>');
				
				if (fix !== -1 && fix < tag) {
					part1 = v.substring(0, fix);
					part2 = v.substring(fix + 1, tag + 1);
					part3 = v.substr(tag + 1);
					
					v = part1.concat(part2, part3, suffix[i]);
				}
			}
			
			pHtml = pHtml.concat(v, ' ');
		});
		
		$p.html(pHtml);
		$p.find('mark').addClass('control initial');	
		
		for (i = 0; i < that.find('.control').length; i += 1) {
			control = new Control({
				exe: that,
				context: $p,
				type: 'textMarkup',
				contextIndex: i,
				currentVal: false
			});
			
			that.controls.push(control);
		}
	}
	function setupTextArea($p) {
		var startInput,
			endInput,
			inputText = [],
			delimiter,
			initialText = [];
		
		pHtml = $p.html();
		i = -1;
		
		startInput = pHtml.indexOf('~~');
		while (startInput !== -1) {
			endInput = pHtml.indexOf('`', startInput + 2);
			if (endInput !== -1) {
				inputText[++i] = pHtml.substring(startInput + 2, endInput);
				inputText[i] = inputText[i].replace(/<.*?>/g, '');
				inputText[i] = inputText[i].replace(/[\n\r]/g, ' ');
				
				pHtml1 = pHtml.substring(0, startInput + 2);
				pHtml2 = pHtml.substring(endInput + 1);
				pHtml = pHtml1.concat(pHtml2);
				
				startInput = pHtml.indexOf('~~', startInput + 2);
				
				delimiter = inputText[i].indexOf('/');
				if (delimiter !== -1) {
					initialText[i] = inputText[i].substr(delimiter + 1);
					inputText[i] = inputText[i].substring(0, delimiter);
				}
				else {
					initialText[i] = '';
				}
			}
			else return;
		}
		
		pHtml = pHtml.replace(/~~/g, '<textarea rows="1">' + initialText[i] + '</textarea>');
		$p.html(pHtml);
		
		for (i = 0; i < inputText.length; i += 1) {
			control = new Control({
				exe: that,
				context: $p,
				type: 'textArea',
				tabindex: ++tabindex,
				contextIndex: i,
				answer: inputText[i],
				initialVal: initialText[i],
				currentVal: initialText[i]
			});
			
			that.controls.push(control);
		}
		/*
		if ($p.find('textarea').length) {
			var content = $p.find('textarea').val();
			
			content = content.replace(/\n/g, '<br>');
			
			$p.append($('<div></div>', {
						class: 'hiddenDiv'
					})
				);
			
			$p.find('.hiddenDiv').html(content + '<br class="lbr">');
		}*/
	}
	function setupTextInput($p) {
		var startInput,
			endInput,
			inputText = [],
			delimiter,
			initialText = [],
			pWords = [];
		
		pHtml = $p.html();
		i = -1;
		
		startInput = pHtml.indexOf('~');
		while (startInput !== -1) {
			endInput = pHtml.indexOf('`', startInput + 1);
			if (endInput !== -1) {
				inputText[++i] = pHtml.substring(startInput + 1, endInput);
				inputText[i] = inputText[i].replace(/<.*?>/g, '');
				inputText[i] = inputText[i].replace(/[\n\r]/g, ' ');
				
				pHtml1 = pHtml.substring(0, startInput + 1);
				pHtml2 = pHtml.substring(endInput + 1);
				pHtml = pHtml1.concat(pHtml2);
				
				startInput = pHtml.indexOf('~', startInput + 1);
				
				delimiter = inputText[i].indexOf('/');
				if (delimiter !== -1) {
					initialText[i] = inputText[i].substr(delimiter + 1);
					inputText[i] = inputText[i].substring(0, delimiter);
				}
				else {
					initialText[i] = '';
				}
			}
			else return;
		}
		
		pWords = cleanTextInput(pHtml);
		pHtml = '';
		
		$.each(pWords, function(k, v) {
			if (v.indexOf('~') !== -1) {
				if (v.indexOf('~') !== 0 || v.indexOf('~') !== (v.length - 1)) {
					v = '<span class="no-wrap">'.concat(v, '</span>');
				}
			}
			
			pHtml = pHtml.concat(v, ' ');
		});
		
		pHtml = pHtml.replace(/~/g, '<input type="text" class="control initial" value="' + initialText[i] + '">');
		$p.html(pHtml);
		
		for (i = 0; i < inputText.length; i += 1) {
			control = new Control({
				exe: that,
				context: $p,
				type: 'textInput',
				tabindex: ++tabindex,
				contextIndex: i,
				answer: inputText[i],
				initialVal: initialText[i],
				currentVal: initialText[i]
			});
			
			that.controls.push(control);
		}
	}	
	function setupRadioInput($p) {
		var startInput,
			inputParameters;
		
		pHtml = $p.html();
		
		startInput = pHtml.indexOf('@');
		inputParameters = pHtml.substring(startInput + 1, startInput + 3);
		inputParameters = inputParameters.replace(/<.*?>/g, '');
		inputParameters = inputParameters.replace(/[\n\r]/g, ' ');
		
		pHtml1 = pHtml.substring(0, startInput + 1);
		pHtml2 = pHtml.substring(startInput + 3);
		pHtml = pHtml1.concat('<label>', pHtml2, '</label>');
		
		pHtml = pHtml.replace(/@/g, '<input type="radio" class="control">');
		$p.html(pHtml);
		
		control = new Control({
			exe: that,
			context: $p,
			type: 'radioInput',
			contextIndex: 0,
			name: 'radio-'.concat(inputParameters.substr(0, 1)),
			answer: parseInt(inputParameters.substr(1, 1))
		});
		
		that.controls.push(control);
	}
	function setupCheckboxInput($p) {
		var startInput,
			inputParameters;
		
		pHtml = $p.html();
		
		startInput = pHtml.indexOf('@');
		inputParameters = pHtml.substring(startInput + 1, startInput + 3);
		inputParameters = inputParameters.replace(/<.*?>/g, '');
		inputParameters = inputParameters.replace(/[\n\r]/g, ' ');
		
		pHtml1 = pHtml.substring(0, startInput + 1);
		pHtml2 = pHtml.substring(startInput + 3);
		pHtml = pHtml1.concat('<label>', pHtml2, '</label>');
		
		pHtml = pHtml.replace(/@/g, '<input type="checkbox" class="control">');
		$p
			.html(pHtml)
			.addClass('radio-p');
		
		control = new Control({
			exe: that,
			context: $p,
			type: 'checkboxInput',
			contextIndex: 0,
			name: 'radio-'.concat(inputParameters.substr(0, 1)),
			answer: parseInt(inputParameters.substr(1, 1)),
			currentVal: 0
		});
		
		that.controls.push(control);
	}
	function setupSelect($p) {
		var startInput,
			endInput,
			answers = [],
			optionsText,
			options = [],
			optionsTags;
		
		pHtml = $p.html();
		i = -1;
		
		startInput = pHtml.indexOf('|');
		while (startInput !== -1) {
			endInput = pHtml.indexOf('`', startInput + 1);
			if (endInput !== -1) {
				answers[++i] = pHtml.substring(startInput + 1, startInput + 2);
				answers[i] = answers[i].replace(/<.*?>/g, '');
				answers[i] = answers[i].replace(/[\n\r]/g, ' ');
				
				optionsText = pHtml.substring(startInput + 2, endInput);
				options = optionsText.split(';');
				optionsTags = '';
				$.each(options, function(k, v) {
					v = '<option value="' + (k + 1) + '">'.concat(v, '</option>');
					optionsTags += v;
				});
				
				pHtml1 = pHtml.substring(0, startInput);
				pHtml2 = pHtml.substring(endInput + 1);
				pHtml = pHtml1.concat('<select class="control"><option value="0"></option>', optionsTags, '</select>', pHtml2);
				
				startInput = pHtml.indexOf('|', startInput + 1);
			}
			else return;
		}
		
		$p.html(pHtml);
		
		for (i = 0; i < $p.find('select').length; i += 1) {
			control = new Control({
				exe: that,
				context: $p,
				type: 'select',
				contextIndex: i,
				answer: parseInt(answers[i])
			});
			
			that.controls.push(control);
		}
	}
	function setupSortable($p) {
		var startInput,
			endInput,
			sortableNo = [],
			inline = false;
		
		pHtml = $p.html();
		i = -1;
		
		startInput = pHtml.indexOf('#');
		while (startInput !== -1) {
			endInput = pHtml.indexOf('`', startInput + 1);
			if (endInput !== -1) {
				sortableNo[++i] = pHtml.substring(startInput + 1, startInput + 3);
				sortableNo[i] = sortableNo[i].replace(/<.*?>/g, '');
				sortableNo[i] = sortableNo[i].replace(/[\n\r]/g, ' ');
				
				pHtml1 = pHtml.substring(0, startInput + 1);
				pHtml2 = pHtml.substring(startInput + 3);
				pHtml = pHtml1.concat(pHtml2);
				
				startInput = pHtml.indexOf('#', startInput + 1);
				if (startInput !== -1) {
					inline = true;
				}
			}
			else return;
		}
		
		pHtml = pHtml.replace(/#/g, '<li class="control sortable">');
		pHtml = pHtml.replace(/`/g, '</li>');
		
		if (inline) {
			pHtml = '<ul class="sortable-list clearfix">'.concat(pHtml, '</ul>');
		}
		
		$p.html(pHtml);
		
		if (inline) {
			$p.find('.sortable').addClass('inline');
		}
		
		for (i = 0; i < $p.find('.sortable').length; i += 1) {
			control = new Control({
				exe: that,
				context: $p,
				type: 'sortable',
				contextIndex: i,
				answer: parseInt(sortableNo[i]),
				initialVal: i + 1,
				currentVal: i + 1
			});
			
			that.controls.push(control);
		}
	}
	function setupDraggable($p) {
		var startInput,
			endInput,
			draggableNo = [],
			inline = false;
		
		pHtml = $p.html();
		i = -1;
		
		startInput = pHtml.indexOf('$');
		while (startInput !== -1) {
			endInput = pHtml.indexOf('`', startInput + 1);
			if (endInput !== -1) {
				draggableNo[++i] = pHtml.substring(startInput + 1, startInput + 2);
				draggableNo[i] = draggableNo[i].replace(/<.*?>/g, '');
				draggableNo[i] = draggableNo[i].replace(/[\n\r]/g, ' ');
				
				pHtml1 = pHtml.substring(0, startInput + 1);
				pHtml2 = pHtml.substring(startInput + 2);
				pHtml = pHtml1.concat(pHtml2);
				
				startInput = pHtml.indexOf('$', startInput + 1);
				if (startInput !== -1) {
					inline = true;
				}
			}
			else return;
		}
		
		while (pHtml.indexOf('$') !== -1) {
			pHtml = pHtml.replace('$', '<li class="control draggable">');
		}
		pHtml = pHtml.replace(/`/g, '</li>');
		
		if (inline) {
			pHtml = '<ul class="draggable-list clearfix">'.concat(pHtml, '</ul>');
		}
		
		$p.html(pHtml);
		
		if (inline) {
			$p.find('.draggable').addClass('inline');
			that.find('.draggable-list').unwrap();
		}
		
		for (i = 0; i < that.find('.draggable').length; i += 1) {
			control = new Control({
				exe: that,
				context: that.find('.draggable-list'),
				type: 'draggable',
				contextIndex: i,
				answer: draggableNo[i]
			});
			
			that.controls.push(control);
		}
	}
};
Exercise.prototype.addButtons = function() {
	var that = this;
	
	this.context
		.append(
			$('<div></div>', {
				class: 'buttons'
			})
		);
	
	if (this.controls.length) {
		this.context.find('.buttons')
			.append(
				$('<div></div>', {
					class: 'button Parbaudit'
				})
					.text("Pārbaudīt")
					.attr('tabindex', this.exeIndex * 100 + this.controls.length + 1)
			);
		
		this.context.find('.Parbaudit').off();
		this.context.find('.Parbaudit').on('mouseup', function() {
			that.checkAnswers();
		});
		this.context.find('.Parbaudit').on('keyup', function(e) {
			var code = e.keyCode ? e.keyCode : e.which;
			
			if (code === 13 && $(this).is(':focus')) {
				that.checkAnswers();
			}
			
			e.preventDefault();
		});
	}
	
	this.context.find('.buttons')
		.append(
			$('<div></div>', {
				class: 'button Atbildes'
			})
				.text("Rādīt atbildes")
		)
		.append(
			$('<div></div>', {
				class: 'button Atjaunot'
			})
				.text("Mēģināt vēlreiz")
		);
	
	if (this.context.find('.Atbildes-block').length) {
		this.context.find('.Atbildes-block')
			.attr('id', 'atbilde-' + this.exeIndex)
		
		this.context.find('.Atbildes').addClass('collapsed');
		this.context.find('.Atbildes')
			.attr('data-toggle', 'collapse')
			.attr('data-target', '#atbilde-' + this.exeIndex);
	}
	
	this.context.find('.Atbildes').on('click', function(e) {
		that.visibleAnswers = !that.visibleAnswers;
		
		if (that.visibleAnswers) {
			that.showAnswers();
			$(this).text("Paslēpt atbildes");
		}
		else {
			that.hideAnswers();
			$(this).text("Rādīt atbildes");
		}
	});
	
	this.context.find('.Atjaunot').on('mouseup', function() {
		that.restore();
	});
	
	if (this.getControlsByType('textArea').length) {
		$('.Testi .collapse').on('shown.bs.collapse', function() {
			that.setupTextAreaAutoHeight();
		});
	}
};
Exercise.prototype.checkAnswers = function() {
	var textMarkupControls = this.getControlsByType('textMarkup'),
		textAreaControls = this.getControlsByType('textArea'),
		textInputControls = this.getControlsByType('textInput'),
		radioInputControls = this.getControlsByType('radioInput'),
		checkboxInputControls = this.getControlsByType('checkboxInput'),
		sortableControls = this.getControlsByType('sortable'),
		completed = true;
	
	if (sortableControls.length) {
		$.each(sortableControls, function() {
			if (this.currentVal !== this.answer) {
				completed = false;
			}
		});
		
		if (completed) {
			this.find('.sortable').addClass('correct');
		}
	}
	
	if ((!sortableControls.length || (sortableControls.length && !completed)) && !textAreaControls.length && !textInputControls.length) {
		$.each(this.controls, function() {
			this.checkAnswer();
		});
	}
	else if (textAreaControls.length) {
		$.each(textAreaControls, function() {
			var $textarea = this.object,
				parentHeight = $textarea.parent().height();
			
			if (this.currentVal !== null) {
				var textAreaWords = [],
					answerWords = [],
					wordClass,
					objectVal = this.currentVal;
				
				while (objectVal.indexOf('  ') !== -1) {
					objectVal = objectVal.replace(/  /g, ' ');
				}
				if (objectVal.indexOf(' ') === 0) {
					objectVal = objectVal.substring(1, objectVal.length);
				}
				if (objectVal.lastIndexOf(' ') === objectVal.length - 1) {
					objectVal = objectVal.substring(0, objectVal.length - 1);
				}
				this.object.val(objectVal);
				this.currentVal = objectVal;
				
				textAreaWords = this.currentVal.split(' ');
				textAreaWords = jQuery.grep(textAreaWords, function(v) {
					return v !== '';
				});
				
				answerWords = this.answer.split(' ');
				answerWords = jQuery.grep(answerWords, function(v) {
					return v !== '';
				});
				
				if (!$textarea.parent().find('.textAreaChecked').length) {
					$textarea.parent()
						.append(
							$('<div></div>', {
								class: 'textAreaChecked'
							})
							.on('click', function(e) {
								$textarea.parent().find('.textAreaChecked').remove();
								$textarea.parent().css('height', 'auto');
								$textarea
									.show()
									.focus();
							})
						);
					
					$textarea.parent().find('.textAreaChecked')
						.outerHeight($textarea.prop('scrollHeight'));
					
					$textarea.parent().height(parentHeight);
					
					$textarea.hide();
					
					$.each(textAreaWords, function(k, v) {
						if (k < answerWords.length && v === answerWords[k]) {
							wordClass = 'correct';
						}
						else {
							wordClass = 'wrong';
						}
						
						$textarea.parent().find('.textAreaChecked')
							.append(
								$('<span></span>', {
									class: wordClass
								})
								.text(v + ' ')
							);
					});
				}
				
				if ($textarea.parent().find('.textAreaChecked').children('.correct').length !== answerWords.length) {
					completed = false;
				}
			}
			if (this.currentVal === null) {
				completed = false;
			}
		});
	}
	else if (textInputControls.length) {
		var that,
			contextControls,
			objectVal;
		
		$.each(textInputControls, function() {
			this.correct = false;
			this.contextAnswerUsed = false;
			
			if (this.currentVal !== null) {
				objectVal = this.currentVal;
				
				while (objectVal.indexOf('  ') !== -1) {
					objectVal = objectVal.replace(/  /g, ' ');
				}
				if (objectVal.indexOf(' ') === 0) {
					objectVal = objectVal.substring(1, objectVal.length);
				}
				if (objectVal.lastIndexOf(' ') === objectVal.length - 1) {
					objectVal = objectVal.substring(0, objectVal.length - 1);
				}
				this.object.val(objectVal);
				this.currentVal = objectVal;
				
				if (this.currentVal.toLowerCase() === this.answer.toLowerCase()) {
					this.correct = true;
					this.contextAnswerUsed = true;
				}
			}
		});
		
		if (this.find('.letters-grid').length) {
			$.each(textInputControls, function() {
				if (this.exe.textInputContext && this.currentVal !== "" && !this.correct) {
					that = this;
					
					$.each(textInputControls, function(k, v) {
						if (that.currentVal.toLowerCase() === v.answer.toLowerCase() && !v.contextAnswerUsed) {
							that.correct = true;
							v.contextAnswerUsed = true;
							
							return false;
						}
					});
				}
				
				this.object
					.removeClass('correct wrong');
				
				if (this.correct) {
					this.object
						.addClass('correct');
				}
				else if (this.currentVal !== "") {
					this.object
						.addClass('wrong');
				}
			});		
		}
		else {
			$.each(textInputControls, function() {
				if (this.exe.textInputContext && this.currentVal !== null && !this.correct) {
					contextControls = this.context.data('controls');
					that = this;
					
					$.each(contextControls, function(k, v) {
						if (that.currentVal.toLowerCase() === v.answer.toLowerCase() && !v.contextAnswerUsed) {
							that.correct = true;
							v.contextAnswerUsed = true;
							
							return false;
						}
					});
				}
				
				if (this.correct) {
					this.object
						.addClass('correct');
				}
				else if (this.currentVal !== null) {
					this.object
						.addClass('wrong');
				}
			});
		}
		
		$.each(textMarkupControls, function() {
			this.checkAnswer();
		});
		
		$.each(radioInputControls, function() {
			this.checkAnswer();
		});
		
		$.each(checkboxInputControls, function() {
			this.checkAnswer();
		});
	}
	
	if (textMarkupControls.length) {
		if (this.find('mark.control').length !== this.find('mark.control.correct').length + this.find('mark.not.control').not('mark.not.control.wrong').length) {
			completed = false;
		}
		
		if (this.find('[type="text"].control').length !== this.find('[type="text"].control.correct').length) {
			completed = false;
		}
	}
	else if (radioInputControls.length) {
		var groupName = this.find('[type="radio"].control').eq(0).attr('name'),
			groupLength = $('input[name="' + groupName + '"]:radio').length;
		
		if (this.find('[type="radio"].control').length !== this.find('[type="radio"].control.correct').length * groupLength) {
			completed = false;
		}
		
		if (this.find('[type="text"].control').length !== this.find('[type="text"].control.correct').length) {
			completed = false;
		}
	}
	else {
		if (this.find('.control').length !== this.find('.control.correct').length) {
			completed = false;
		}
	}
	
	if (completed) {
		this.find('.exe-title').addClass('completed');
		this.find('.button.Parbaudit').addClass('completed');
		$('#Pareizi').get(0).play();
	}
	else {
		$('#Nepareizi').get(0).play();
	}
};
Exercise.prototype.showAnswers = function() {
	var textAreaControls = this.getControlsByType('textArea'),
		sortableControls = this.getControlsByType('sortable'),
		draggableControls = this.getControlsByType('draggable');
	
	if (textAreaControls.length) {
		$.each(textAreaControls, function() {			
			if (this.object.parent().find('.textAreaChecked').length) {
				this.object.parent().find('.textAreaChecked').remove();
				this.object.parent().css('height', 'auto');
				this.object.show();
			}
			
			this.showAnswer();
			this.object
				.outerHeight(this.object.prop('scrollHeight'));
		});
	}
	else if (sortableControls.length) {
		sortableControls
			.sort(function(a, b) {
				return a.answer - b.answer;
			});
		
		$.each(sortableControls, function() {
			this.object
				.appendTo(this.exe.find('.sortable-list'))
				.removeClass('correct wrong');
		});
		
		this.find('.sortable-list').sortable('option', 'disabled', true);
	}
	else if (draggableControls.length) {
		$.each(draggableControls, function() {
			this.object
				.appendTo(this.exe.find('.droppable[name="' + this.answer + '"]'))
				.removeClass('correct wrong')
				.draggable('option', 'disabled', true);
		});
	}
	else {
		$.each(this.controls, function() {
			this.showAnswer();
		});
	}
	
	this.find('.Parbaudit').addClass('disabled');
};
Exercise.prototype.hideAnswers = function() {
	var sortableControls = this.getControlsByType('sortable'),
		draggableControls = this.getControlsByType('draggable');
	
	if (sortableControls.length) {
		sortableControls
			.sort(function(a, b) {
				return a.currentVal - b.currentVal;
			});
		
		$.each(sortableControls, function() {
			this.object
				.appendTo(this.exe.find('.sortable-list'));
		});
		
		this.find('.sortable-list').sortable('option', 'disabled', false);
	}
	else if (draggableControls.length) {
		$.each(draggableControls, function() {
			this.object
				.appendTo(this.exe.find('.draggable-list'))
				.draggable('option', 'disabled', false);
			
			if (this.currentVal !== null) {
				this.object
					.appendTo(this.exe.find('.droppable[name="' + this.currentVal + '"]'));
			}
		});
	}
	else {
		$.each(this.controls, function() {
			this.hideAnswer();
		});
	}
	
	this.find('.Parbaudit').removeClass('disabled');
};
Exercise.prototype.restore = function() {
	var textMarkupControls = this.getControlsByType('textMarkup'),
		textAreaControls = this.getControlsByType('textArea'),
		textInputControls = this.getControlsByType('textInput'),
		textRadioControls = this.getControlsByType('radioInput'),
		sortableControls = this.getControlsByType('sortable'),
		draggableControls = this.getControlsByType('draggable');
	
	if (textMarkupControls.length) {
		$.each(textMarkupControls, function() {
			this.object
				.removeClass('disabled highlight')
				.addClass('initial');
		});
	}
	if (textAreaControls.length) {
		$.each(textAreaControls, function() {
			this.object.val(this.initialVal);
			this.currentVal = this.initialVal;
			
			if (this.object.parent().find('.textAreaChecked').length) {
				this.object.parent().find('.textAreaChecked').remove();
				this.object.show();
			}
		});
		
		this.setupTextAreaAutoHeight();
	}
	if (textInputControls.length) {
		$.each(textInputControls, function() {
			this.object.val(this.initialVal);
			this.currentVal = this.initialVal;
		});
	}
	if (textRadioControls.length) {
		$.each(textRadioControls, function() {
			this.object.attr('checked', false);
		});
	}
	if (sortableControls.length) {
		sortableControls
			.sort(function(a, b) {
				return a.initialVal - b.initialVal;
			});
		
		$.each(sortableControls, function() {
			this.object
				.appendTo(this.exe.find('.sortable-list'))
				.addClass('initial');
			
			this.currentVal = this.initialVal;
		});
		
		this.find('.sortable-list').sortable('option', 'disabled', false);
	}
	if (draggableControls.length) {
		$.each(draggableControls, function() {
			this.object
				.appendTo(this.exe.find('.draggable-list'))
				.addClass('initial')
				.draggable('option', 'disabled', false);
		});
		
		$.each(this.find('.droppable'), function() {
			$(this).parent().css('width', 'auto');
			$(this).css('height', 'auto');
		});
	}
	
	$.each(this.controls, function() {
		this.object
			.attr('disabled', false)
			.removeClass('correct wrong');
		
		contextAnswerUsed = false;
		if (this.type !== 'textArea' && this.type !== 'textInput' && this.type !== 'sortable') {
			this.currentVal = null;
		}
		this.correct = false;
	});
	
	this.find('.exe-title').removeClass('completed');
	this.find('.Parbaudit').removeClass('disabled');
	this.find('.Atbildes').text("Rādīt atbildes");
	this.visibleAnswers = false;
};
Exercise.prototype.setupTextAreaAutoHeight = function() {
	var textAreaControls = this.getControlsByType('textArea');
	
	if (textAreaControls.length) {
		$.each(textAreaControls, function() {
			this.object.trigger('input');
		});
	}
};
Exercise.prototype.setupTextInputWidth = function() {
	var maxLength = 1,
		maxWidth = 20,
		maxMCount = 0,
		thisLength,
		thisWidth,
		thisMCount;
	
	$.each(this.getControlsByType('textInput'), function() {
		thisLength = this.answer.length;
		thisWidth = Math.ceil(thisLength * 7.3);
		thisMCount = charOccurances(this.answer, 'm')
		
		if (thisLength > maxLength) {
			maxLength = thisLength;
		}
		
		if (thisWidth > maxWidth) {
			maxWidth = thisWidth;
		}
		
		if (thisMCount > maxMCount) {
			maxMCount = thisMCount;
		}
	});
	
	this.find('[type="text"].control')
		.attr('maxlength', maxLength)
		.width(maxWidth + maxMCount * 3);
	
	if (maxLength === 1) {
		this.find('[type="text"].control').width(12);
	}
	if (maxLength === 2) {
		this.find('[type="text"].control').width(16);
	}
	
	if (maxLength < 3) {
		this.find('[type="text"].control').addClass('center');
	}
	
	function charOccurances(str, char) {
		var c;
		
		for (c = 0, i = 0, len = str.length; i < len; i += 1) {
			if (str[i] == char) {
				++c;
			}
		}
		return c;
	}
};

/*!
 * imagesLoaded PACKAGED v4.1.4
 * JavaScript is all like "You images are done yet or what?"
 * MIT License
 */

/**
 * EvEmitter v1.1.0
 * Lil' event emitter
 * MIT License
 */

/* jshint unused: true, undef: true, strict: true */

( function( global, factory ) {
  // universal module definition
  /* jshint strict: false */ /* globals define, module, window */
  if ( typeof define == 'function' && define.amd ) {
    // AMD - RequireJS
    define( 'ev-emitter/ev-emitter',factory );
  } else if ( typeof module == 'object' && module.exports ) {
    // CommonJS - Browserify, Webpack
    module.exports = factory();
  } else {
    // Browser globals
    global.EvEmitter = factory();
  }

}( typeof window != 'undefined' ? window : this, function() {



function EvEmitter() {}

var proto = EvEmitter.prototype;

proto.on = function( eventName, listener ) {
  if ( !eventName || !listener ) {
    return;
  }
  // set events hash
  var events = this._events = this._events || {};
  // set listeners array
  var listeners = events[ eventName ] = events[ eventName ] || [];
  // only add once
  if ( listeners.indexOf( listener ) == -1 ) {
    listeners.push( listener );
  }

  return this;
};

proto.once = function( eventName, listener ) {
  if ( !eventName || !listener ) {
    return;
  }
  // add event
  this.on( eventName, listener );
  // set once flag
  // set onceEvents hash
  var onceEvents = this._onceEvents = this._onceEvents || {};
  // set onceListeners object
  var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
  // set flag
  onceListeners[ listener ] = true;

  return this;
};

proto.off = function( eventName, listener ) {
  var listeners = this._events && this._events[ eventName ];
  if ( !listeners || !listeners.length ) {
    return;
  }
  var index = listeners.indexOf( listener );
  if ( index != -1 ) {
    listeners.splice( index, 1 );
  }

  return this;
};

proto.emitEvent = function( eventName, args ) {
  var listeners = this._events && this._events[ eventName ];
  if ( !listeners || !listeners.length ) {
    return;
  }
  // copy over to avoid interference if .off() in listener
  listeners = listeners.slice(0);
  args = args || [];
  // once stuff
  var onceListeners = this._onceEvents && this._onceEvents[ eventName ];

  for ( var i=0; i < listeners.length; i++ ) {
    var listener = listeners[i]
    var isOnce = onceListeners && onceListeners[ listener ];
    if ( isOnce ) {
      // remove listener
      // remove before trigger to prevent recursion
      this.off( eventName, listener );
      // unset once flag
      delete onceListeners[ listener ];
    }
    // trigger listener
    listener.apply( this, args );
  }

  return this;
};

proto.allOff = function() {
  delete this._events;
  delete this._onceEvents;
};

return EvEmitter;

}));

/*!
 * imagesLoaded v4.1.4
 * JavaScript is all like "You images are done yet or what?"
 * MIT License
 */

( function( window, factory ) { 'use strict';
  // universal module definition

  /*global define: false, module: false, require: false */

  if ( typeof define == 'function' && define.amd ) {
    // AMD
    define( [
      'ev-emitter/ev-emitter'
    ], function( EvEmitter ) {
      return factory( window, EvEmitter );
    });
  } else if ( typeof module == 'object' && module.exports ) {
    // CommonJS
    module.exports = factory(
      window,
      require('ev-emitter')
    );
  } else {
    // browser global
    window.imagesLoaded = factory(
      window,
      window.EvEmitter
    );
  }

})( typeof window !== 'undefined' ? window : this,

// --------------------------  factory -------------------------- //

function factory( window, EvEmitter ) {



var $ = window.jQuery;
var console = window.console;

// -------------------------- helpers -------------------------- //

// extend objects
function extend( a, b ) {
  for ( var prop in b ) {
    a[ prop ] = b[ prop ];
  }
  return a;
}

var arraySlice = Array.prototype.slice;

// turn element or nodeList into an array
function makeArray( obj ) {
  if ( Array.isArray( obj ) ) {
    // use object if already an array
    return obj;
  }

  var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';
  if ( isArrayLike ) {
    // convert nodeList to array
    return arraySlice.call( obj );
  }

  // array of single index
  return [ obj ];
}



// -------------------------- imagesLoaded -------------------------- //

/**
 * @param {Array, Element, NodeList, String} elem
 * @param {Object or Function} options - if function, use as callback
 * @param {Function} onAlways - callback function
 */
function ImagesLoaded( elem, options, onAlways ) {
  // coerce ImagesLoaded() without new, to be new ImagesLoaded()
  if ( !( this instanceof ImagesLoaded ) ) {
    return new ImagesLoaded( elem, options, onAlways );
  }
  // use elem as selector string
  var queryElem = elem;
  if ( typeof elem == 'string' ) {
    queryElem = document.querySelectorAll( elem );
  }
  // bail if bad element
  if ( !queryElem ) {
    console.error( 'Bad element for imagesLoaded ' + ( queryElem || elem ) );
    return;
  }

  this.elements = makeArray( queryElem );
  this.options = extend( {}, this.options );
  // shift arguments if no options set
  if ( typeof options == 'function' ) {
    onAlways = options;
  } else {
    extend( this.options, options );
  }

  if ( onAlways ) {
    this.on( 'always', onAlways );
  }

  this.getImages();

  if ( $ ) {
    // add jQuery Deferred object
    this.jqDeferred = new $.Deferred();
  }

  // HACK check async to allow time to bind listeners
  setTimeout( this.check.bind( this ) );
}

ImagesLoaded.prototype = Object.create( EvEmitter.prototype );

ImagesLoaded.prototype.options = {};

ImagesLoaded.prototype.getImages = function() {
  this.images = [];

  // filter & find items if we have an item selector
  this.elements.forEach( this.addElementImages, this );
};

/**
 * @param {Node} element
 */
ImagesLoaded.prototype.addElementImages = function( elem ) {
  // filter siblings
  if ( elem.nodeName == 'IMG' ) {
    this.addImage( elem );
  }
  // get background image on element
  if ( this.options.background === true ) {
    this.addElementBackgroundImages( elem );
  }

  // find children
  // no non-element nodes, #143
  var nodeType = elem.nodeType;
  if ( !nodeType || !elementNodeTypes[ nodeType ] ) {
    return;
  }
  var childImgs = elem.querySelectorAll('img');
  // concat childElems to filterFound array
  for ( var i=0; i < childImgs.length; i++ ) {
    var img = childImgs[i];
    this.addImage( img );
  }

  // get child background images
  if ( typeof this.options.background == 'string' ) {
    var children = elem.querySelectorAll( this.options.background );
    for ( i=0; i < children.length; i++ ) {
      var child = children[i];
      this.addElementBackgroundImages( child );
    }
  }
};

var elementNodeTypes = {
  1: true,
  9: true,
  11: true
};

ImagesLoaded.prototype.addElementBackgroundImages = function( elem ) {
  var style = getComputedStyle( elem );
  if ( !style ) {
    // Firefox returns null if in a hidden iframe https://bugzil.la/548397
    return;
  }
  // get url inside url("...")
  var reURL = /url\((['"])?(.*?)\1\)/gi;
  var matches = reURL.exec( style.backgroundImage );
  while ( matches !== null ) {
    var url = matches && matches[2];
    if ( url ) {
      this.addBackground( url, elem );
    }
    matches = reURL.exec( style.backgroundImage );
  }
};

/**
 * @param {Image} img
 */
ImagesLoaded.prototype.addImage = function( img ) {
  var loadingImage = new LoadingImage( img );
  this.images.push( loadingImage );
};

ImagesLoaded.prototype.addBackground = function( url, elem ) {
  var background = new Background( url, elem );
  this.images.push( background );
};

ImagesLoaded.prototype.check = function() {
  var _this = this;
  this.progressedCount = 0;
  this.hasAnyBroken = false;
  // complete if no images
  if ( !this.images.length ) {
    this.complete();
    return;
  }

  function onProgress( image, elem, message ) {
    // HACK - Chrome triggers event before object properties have changed. #83
    setTimeout( function() {
      _this.progress( image, elem, message );
    });
  }

  this.images.forEach( function( loadingImage ) {
    loadingImage.once( 'progress', onProgress );
    loadingImage.check();
  });
};

ImagesLoaded.prototype.progress = function( image, elem, message ) {
  this.progressedCount++;
  this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded;
  // progress event
  this.emitEvent( 'progress', [ this, image, elem ] );
  if ( this.jqDeferred && this.jqDeferred.notify ) {
    this.jqDeferred.notify( this, image );
  }
  // check if completed
  if ( this.progressedCount == this.images.length ) {
    this.complete();
  }

  if ( this.options.debug && console ) {
    console.log( 'progress: ' + message, image, elem );
  }
};

ImagesLoaded.prototype.complete = function() {
  var eventName = this.hasAnyBroken ? 'fail' : 'done';
  this.isComplete = true;
  this.emitEvent( eventName, [ this ] );
  this.emitEvent( 'always', [ this ] );
  if ( this.jqDeferred ) {
    var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve';
    this.jqDeferred[ jqMethod ]( this );
  }
};

// --------------------------  -------------------------- //

function LoadingImage( img ) {
  this.img = img;
}

LoadingImage.prototype = Object.create( EvEmitter.prototype );

LoadingImage.prototype.check = function() {
  // If complete is true and browser supports natural sizes,
  // try to check for image status manually.
  var isComplete = this.getIsImageComplete();
  if ( isComplete ) {
    // report based on naturalWidth
    this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
    return;
  }

  // If none of the checks above matched, simulate loading on detached element.
  this.proxyImage = new Image();
  this.proxyImage.addEventListener( 'load', this );
  this.proxyImage.addEventListener( 'error', this );
  // bind to image as well for Firefox. #191
  this.img.addEventListener( 'load', this );
  this.img.addEventListener( 'error', this );
  this.proxyImage.src = this.img.src;
};

LoadingImage.prototype.getIsImageComplete = function() {
  // check for non-zero, non-undefined naturalWidth
  // fixes Safari+InfiniteScroll+Masonry bug infinite-scroll#671
  return this.img.complete && this.img.naturalWidth;
};

LoadingImage.prototype.confirm = function( isLoaded, message ) {
  this.isLoaded = isLoaded;
  this.emitEvent( 'progress', [ this, this.img, message ] );
};

// ----- events ----- //

// trigger specified handler for event type
LoadingImage.prototype.handleEvent = function( event ) {
  var method = 'on' + event.type;
  if ( this[ method ] ) {
    this[ method ]( event );
  }
};

LoadingImage.prototype.onload = function() {
  this.confirm( true, 'onload' );
  this.unbindEvents();
};

LoadingImage.prototype.onerror = function() {
  this.confirm( false, 'onerror' );
  this.unbindEvents();
};

LoadingImage.prototype.unbindEvents = function() {
  this.proxyImage.removeEventListener( 'load', this );
  this.proxyImage.removeEventListener( 'error', this );
  this.img.removeEventListener( 'load', this );
  this.img.removeEventListener( 'error', this );
};

// -------------------------- Background -------------------------- //

function Background( url, element ) {
  this.url = url;
  this.element = element;
  this.img = new Image();
}

// inherit LoadingImage prototype
Background.prototype = Object.create( LoadingImage.prototype );

Background.prototype.check = function() {
  this.img.addEventListener( 'load', this );
  this.img.addEventListener( 'error', this );
  this.img.src = this.url;
  // check if image is already complete
  var isComplete = this.getIsImageComplete();
  if ( isComplete ) {
    this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
    this.unbindEvents();
  }
};

Background.prototype.unbindEvents = function() {
  this.img.removeEventListener( 'load', this );
  this.img.removeEventListener( 'error', this );
};

Background.prototype.confirm = function( isLoaded, message ) {
  this.isLoaded = isLoaded;
  this.emitEvent( 'progress', [ this, this.element, message ] );
};

// -------------------------- jQuery -------------------------- //

ImagesLoaded.makeJQueryPlugin = function( jQuery ) {
  jQuery = jQuery || window.jQuery;
  if ( !jQuery ) {
    return;
  }
  // set local variable
  $ = jQuery;
  // $().imagesLoaded()
  $.fn.imagesLoaded = function( options, callback ) {
    var instance = new ImagesLoaded( this, options, callback );
    return instance.jqDeferred.promise( $(this) );
  };
};
// try making plugin
ImagesLoaded.makeJQueryPlugin();

// --------------------------  -------------------------- //

return ImagesLoaded;

});