﻿(function ($) {

	//Common start

	SurveyQuestionType = {
		text: 1,
		paragraphText: 2,
		checkBoxes: 3,
		radioButtons: 4,
		dropDown: 5,
		scale: 6
	};

	ListType = {
		checkBoxes: 1,
		radioButtons: 2,
		dropDown: 3
	};

	Common = {
		createDelegate: function (instance, method) {
			return function () { return method.apply(instance, arguments); };
		},

		settings: null,

		resources: null,

		defaultSettings: {
			aspNetSaveButtonId: '',
			aspNetCancelButtonId: '',
			navigationControlIds: [],
			isFullPreview: false,
			buttonsHeaderAreaId: '',

			modifierId: 0,
			saveUrl: '/SurveyHandler.ashx',
			cancelUrl: '',
			actionResultMessageDuration: 5000,
			actionResultMessageHideDuration: 500,
			actionResultScrollingDuration: 500,
			scaleMinBoundaryMinValue: 0,
			scaleMinBoundaryMaxValue: 1,
			scaleMaxBoundaryMinValue: 2,
			scaleMaxBoundaryMaxValue: 10
		},

		defaultResources: {
			questionTypeTitles: {
				text: 'Text',
				paragraphText: 'Paragraph Text',
				checkBoxes: 'Check Boxes',
				radioButtons: 'Radio Buttons',
				dropDown: 'Drop Down',
				scale: 'Scale'
			},

			questionSampleText: 'Sample question text',

			surveyNameLabel: 'Short Name: ',
			surveyTitleLabel: 'Title: ',
			surveyDescriptionLabel: 'Description: ',
			surveyMaxDurationLabel: 'Max Duration, days: ',
			requiredQuestionsDisclaimer: '* Required',

			fullPreviewButtonText: 'Full Preview',
			fullEditButtonText: 'Full Edit',
			addQuestionButtonText: 'Add Question',
			saveSurveyButtonText: 'Save',
			cancelSurveyButtonText: 'Cancel',

			questionTextLabel: 'Question Text: ',
			questionDescriptionLabel: 'Help Text: ',
			questionIsRequiredLabel: 'Is Required: ',
			questionTypeLabel: 'Question Type: ',

			previewQuestionButtonText: 'Preview',
			editQuestionButtonText: 'Edit',
			deleteQuestionButtonText: 'Delete',

			sampleTextAnswer: 'Their answer',
			sampleParagraphTextAnswer: 'Their longer answer',
			sampleOptionText: 'Sample option',
			lastListOptionText: 'Click here to add answer',
			defaultDropDownOption: 'Please select...',

			deleteAnswerButtonText: 'Delete',

			minBoundaryLabel: 'Min Boundary: ',
			maxBoundaryLabel: 'Max Boundary: ',
			minBoundaryTitleLabel: 'Min Label: ',
			maxBoundaryTitleLabel: 'Max Label: '
		},

		prepareUi: function (element) {
			$('input[type=text], select', element).addClass('input-field');

			$('input[type=button]', element).button().addClass('survey-button');

			$('input, textarea', element).keydown(function (e) {
				if (e.keyCode == 13) {
					e.cancelBubble = true;
					e.returnValue = false;
					return false;
				}
			});
		},

		//Content protection is copied from WebExamShell.
		//It is needed for sample text area and input text elements on question forms,
		//as simple disabling breaks sorting.
		protectContent: function (element, disableClickEvent) {
			var disableOptionEvents = $.browser.webkit ? "contextmenu selectstart select mouseup beforecut cut beforecopy copy dragstart dragover" : "contextmenu selectstart select mouseup mousedown beforecut cut beforecopy copy dragstart dragover";
			element.bind(disableOptionEvents, Common.disableOption);

			var hideStatusEvents = "mouseup mouseover mouseout mousedown";
			if (disableClickEvent) {
				hideStatusEvents = 'click ' + hideStatusEvents;
			}
			element.bind(hideStatusEvents, Common.hideStatus);

			var processKeysEvents = "keydown keypress";
			element.bind(processKeysEvents, Common.processKeys);
		},

		processKeys: function (ev) {
			return false;
		},

		disableOption: function () {
			return false;
		},

		hideStatus: function () {
			window.status = " ";
			return true;
		}
	};

	//Common end

	//ASP.NET survey form start

	//Represents a wrapper over the survey form, so that it initiates asp.net button clicks
	//on survey form events and simulates native asp.net button clicks.
	AspNetSurveyForm = function (containerArea, initialSurvey) {
		$('#' + Common.settings.aspNetSaveButtonId).hide();
		$('#' + Common.settings.aspNetCancelButtonId).hide();

		var handlers = {
			handleSurveySaveSuccess: Common.createDelegate(this, this.handleSurveySaveSuccess),
			handleCancelClick: Common.createDelegate(this, this.handleCancelClick),
			handleSurveySaveError: Common.createDelegate(this, this.handleSurveySaveError)
		};

		this.surveyForm = new SurveyForm(containerArea, initialSurvey, handlers);

		var navigationControlsCount = Common.settings.navigationControlIds.length;
		for (var i = 0; i < navigationControlsCount; i++) {
			var id = Common.settings.navigationControlIds[i];
			$('#' + id).click(Common.createDelegate(this, this.handleNavigationClick));
		}
	};

	AspNetSurveyForm.prototype = {
		handleNavigationClick: function (eventData) {
			this.surveyForm.updateSurvey();
			var surveyString = JSON.stringify(this.surveyForm.survey);
			$.post(Common.settings.saveUrl, { survey: surveyString, action: 'cache' });

			return true;
		},

		handleSurveySaveSuccess: function () {
			$('#' + Common.settings.aspNetSaveButtonId).click();
		},

		handleCancelClick: function () {
			$('#' + Common.settings.aspNetCancelButtonId).click();
		},

		handleSurveySaveError: function () {

		}
	};

	//ASP.NET survey form end

	//Survey form start

	SurveyForm = function (containerArea, initialSurvey, handlers) {
		this.containerArea = $(containerArea);
		this.questionsArea = null;
		this.handlers = handlers;

		//Common.settings.isFullPreview determines current state of the editor,
		//and was introduced to avoid passing it everywhere (to question and answer forms).
		//editingDisabled determines whether editing is supported at all.
		this.editingDisabled = Common.settings.isFullPreview;

		this.questionFormsByQuestionIds = {};
		this.maxQuestionId = 0;

		if (initialSurvey == null) {
			this.survey = this.createEmptySurvey();
		}
		else {
			this.survey = initialSurvey;

			//modify date is serialized badly on the server, and will be replaced on the server
			this.survey.modifyDate = '';
			this.assignQuestionIds();
		}

		this.initialize();
	};

	SurveyForm.prototype = {
		createEmptySurvey: function () {
			var survey =
			{ id: 0, accountId: Common.settings.modifierId, shortName: '', title: '', description: '', maxAssignmentDuration: null, groups: [
				{ id: 0, title: '', position: 0, description: '', questions: [
					//id is the database id, client id is a number, unique for the current form
					{id: 0,
					clientId: this.getNewQuestionId(),
					text: Common.resources.questionSampleText,
					description: '',
					position: 0,
					isRequired: false,
					type: SurveyQuestionType.text,
					supportsCustomAnswer: false
				},
					{ id: 0,
						clientId: this.getNewQuestionId(),
						text: Common.resources.questionSampleText,
						description: '',
						position: 1,
						isRequired: false,
						type: SurveyQuestionType.text,
						supportsCustomAnswer: false
					}]
			}]
		};

		return survey;
	},

	assignQuestionIds: function () {
		var questionsCount = this.survey.groups[0].questions.length;
		for (var i = 0; i < questionsCount; i++) {
			this.survey.groups[0].questions[i].clientId = this.getNewQuestionId();
		}
	},

	initialize: function () {
		this.containerArea.empty();

		if (Common.settings.isFullPreview) {
			this.containerArea.addClass('survey-full-preview');
		}
		else {
			this.containerArea.removeClass('survey-full-preview');
		}

		this.addHeaderArea();
		this.addQuestionsArea();
		this.addButtonsArea();
	},

	addHeaderArea: function () {
		var headerArea = null;

		if (!Common.settings.isFullPreview) {
			headerArea = this.getHeaderEditorArea();
		}
		else {
			headerArea = this.getHeaderPreviewArea();
		}

		this.containerArea.append(headerArea);
	},

	getHeaderEditorArea: function () {
		var actionResultArea = $('<div/>', { 'class': 'action-result', id: 'actionResult' });

		var nameArea = $('<div/>', { 'class': 'header-row' })
				.append($('<span/>', { text: Common.resources.surveyNameLabel, 'class': 'mandatory' }))
				.append($('<input/>', { id: 'surveyName', name: 'surveyName', type: 'text', value: this.survey.shortName }));

		var titleArea = $('<div/>', { 'class': 'header-row' })
				.append($('<span/>', { text: Common.resources.surveyTitleLabel, 'class': 'mandatory' }))
				.append($('<input/>', { id: 'surveyTitle', name: 'surveyTitle', type: 'text', value: this.survey.title }));

		var descriptionArea = $('<div/>', { 'class': 'header-row' })
				.append($('<span/>', { text: Common.resources.surveyDescriptionLabel }))
				.append($('<input/>', { id: 'surveyDescription', name: 'surveyDescription', type: 'text', value: this.survey.description }));

		var maxDuration = '';
		if (this.survey.maxAssignmentDuration) {
			maxDuration = this.survey.maxAssignmentDuration;
		}

		var maxDurationArea = $('<div/>', { 'class': 'header-row' })
				.append($('<span/>', { text: Common.resources.surveyMaxDurationLabel }))
				.append($('<input/>', { id: 'maxDuration', name: 'maxDuration', type: 'text', value: maxDuration }).numeric());

		var headerArea = $('<div/>', { 'class': 'header-area' })
                .append(actionResultArea).append(nameArea).append(titleArea).append(descriptionArea).append(maxDurationArea);
		Common.prepareUi(headerArea);

		return headerArea;
	},

	getHeaderPreviewArea: function () {
		var actionResultArea = $('<div/>', { 'class': 'action-result', id: 'actionResult' });

		var titleArea = $('<div/>', { 'class': 'survey-preview-title', text: this.survey.title });
		var descriptionArea = $('<div/>', { 'class': 'survey-preview-description', text: this.survey.description });
		var requiredQuestionDisclaimer = $('<div/>', { 'class': 'required-questions-disclaimer', text: Common.resources.requiredQuestionsDisclaimer });

		var headerArea = $('<div/>', { 'class': 'survey-preview-header' })
				.append(actionResultArea).append(titleArea);

		if (this.survey.description && this.survey.description.length > 0) {
			headerArea.append(descriptionArea);
		}

		if (this.hasRequiredQuestions()) {
			headerArea.append(requiredQuestionDisclaimer);
		}

		return headerArea;
	},

	hasRequiredQuestions: function () {
		var questionsCount = this.survey.groups[0].questions.length;
		for (var i = 0; i < questionsCount; i++) {
			var question = this.survey.groups[0].questions[i];
			if (question.isRequired) {
				return true;
			}
		}

		return false;
	},

	addQuestionsArea: function () {
		var questionsCount = this.survey.groups[0].questions.length;

		this.questionsArea = $('<div/>', { 'class': 'questions-area' }).appendTo(this.containerArea);

		for (var i = 0; i < questionsCount; i++) {
			var question = this.survey.groups[0].questions[i];
			this.addQuestionForm(question);
		}

		if (!Common.settings.isFullPreview) {
			this.questionsArea.sortable({ start: function (event, ui) { ui.item.addClass('question-sorted'); },
				stop: function (event, ui) { ui.item.removeClass('question-sorted'); }
			});
		}
	},

	addQuestionForm: function (question) {
		var handlers = { handleQuestionDelete: Common.createDelegate(this, this.deleteQuestion) };

		var questionFormDiv = $('<div/>', { id: 'question' + question.clientId, 'class': 'question' }).appendTo(this.questionsArea);
		var questionForm = new QuestionForm(questionFormDiv, question, handlers);

		this.questionFormsByQuestionIds[question.clientId] = questionForm;
	},

	addButtonsArea: function () {
		//Add buttons to the bottom area
		var buttonsContainer = $('<div />', { 'class': 'buttons-area' });
		var buttonsBottomArea = $('<div />').appendTo(buttonsContainer);

		var bottomButtonsList = this.getButtonsList();
		buttonsBottomArea.append(bottomButtonsList);
		this.containerArea.append(buttonsContainer);

		//add buttons to the top area
		if (!Common.settings.buttonsHeaderAreaId) {
			return;
		}

		var topButtonsList = this.getButtonsList();

		var buttonsTopArea = $('#' + Common.settings.buttonsHeaderAreaId);
		buttonsTopArea.empty().append(topButtonsList);
	},

	getButtonsList: function () {
		var addButton = this.createButton('addQuestion', Common.resources.addQuestionButtonText, 'ui-icon-plusthick')
				.addClass('add-question-button').click(Common.createDelegate(this, this.addQuestion));

		var saveButton = this.createButton('saveSurvey', Common.resources.saveSurveyButtonText, 'ui-icon-big-check')
				.addClass('save-survey-button').click(Common.createDelegate(this, this.saveSurvey));

		var cancelButton = this.createButton('cancelSurvey', Common.resources.cancelSurveyButtonText, 'ui-icon-closethick')
				.addClass('cancel-survey-button').click(Common.createDelegate(this, this.cancelSurvey));

		var switchModeText = Common.settings.isFullPreview ? Common.resources.fullEditButtonText : Common.resources.fullPreviewButtonText;
		//ui-icon-arrowthick-2-n-s ui-icon-wrench ui-icon-refresh ui-icon-search
		var switchModeClass = Common.settings.isFullPreview ? 'ui-icon-wrench' : 'ui-icon-search';
		var switchModeButton = this.createButton('switchMode', switchModeText, switchModeClass)
				.addClass('switch-survey-mode-button').click(Common.createDelegate(this, this.switchMode));

		var buttonsList = [];
		if (!this.editingDisabled) {
			buttonsList.push(switchModeButton[0]);

			if (!Common.settings.isFullPreview) {
				buttonsList.push(addButton[0]);
			}

			buttonsList.push(saveButton[0]);
		}
		buttonsList.push(cancelButton[0]);

		//jQuery ctor accepts an array of dom elements, returns a jQuery object
		return $(buttonsList);
	},

	createButton: function (buttonId, buttonText, iconClass) {
		var textSpan = $('<span/>', { 'class': 'ui-button-text', text: buttonText });
		var iconSpan = $('<span/>', { 'class': 'es-icon-small-right ui-icon' }).addClass(iconClass);

		var button = $('<a/>', { 'class': 'es-icon-right es-icon-right-margin', id: buttonId })
			.append(textSpan)
			.append(iconSpan)
			.button();

		return button;
	},

	addQuestion: function () {
		var question = { id: 0, clientId: this.getNewQuestionId(), text: Common.resources.questionSampleText, description: '', position: 0, type: SurveyQuestionType.text, supportsCustomAnswer: false };
		this.survey.groups[0].questions.push(question);

		this.addQuestionForm(question);

		return false;
	},

	saveSurvey: function () {
		this.updateSurvey();
		var surveyString = JSON.stringify(this.survey);
		$.post(Common.settings.saveUrl, { survey: surveyString, action: 'save' }, Common.createDelegate(this, this.handleSaveResponse));

		return false;
	},

	updateSurvey: function () {
		if (Common.settings.isFullPreview) {
			return;
		}

		this.survey.shortName = $('#surveyName', this.containerArea).val();
		this.survey.title = $('#surveyTitle', this.containerArea).val();
		this.survey.description = $('#surveyDescription', this.containerArea).val();

		var maxDurationString = $('#maxDuration', this.containerArea).val();
		this.survey.maxAssignmentDuration = parseInt(maxDurationString);

		this.survey.groups[0].questions = this.getSurveyQuestions();
	},

	getSurveyQuestions: function () {
		var currentQuestions = [];

		var questionElementIds = this.questionsArea.sortable('toArray');

		var questionElementsCount = questionElementIds.length;

		for (var i = 0; i < questionElementsCount; i++) {
			var elementId = questionElementIds[i];

			var questionIdString = elementId.replace('question', '');
			var questionId = parseInt(questionIdString);

			var questionForm = this.questionFormsByQuestionIds[questionId];

			var question = questionForm.getQuestion();
			question.position = i;

			currentQuestions.push(question);
		}

		return currentQuestions;
	},

	cancelSurvey: function () {
		this.handlers.handleCancelClick();

		return false;
	},

	switchMode: function () {
		this.updateSurvey();

		Common.settings.isFullPreview = !Common.settings.isFullPreview;
		this.initialize();
	},

	handleSaveResponse: function (actionResult) {

		var actionResultArea = $('#actionResult', this.containerArea);

		if (actionResult.success) {
			//Assign saved survey id (necessary when updating existing survey).
			//In general, it is necessary to update groups, questions and answers ids,
			//but the survey service simply inserts these entities even when the survey is updated, so 
			//these ids are not needed.
			//TODO: implement updating entities ids, if necessary.
			this.survey.id = actionResult.surveyId;

			actionResultArea
					.removeClass('action-result-error')
					.addClass('action-result-success')
					.html(actionResult.message)
					.show()
					.delay(Common.settings.actionResultMessageDuration)
					.hide(Common.settings.actionResultMessageHideDuration);

			this.handlers.handleSurveySaveSuccess();
		}
		else {
			actionResultArea
					.removeClass('action-result-success')
					.addClass('action-result-error')
					.html(actionResult.message);

			if (actionResult.validationErrors != null && actionResult.validationErrors.length > 0) {
				var validationErrorsArea = $('<ul/>', { 'class': 'validation-errors' });

				var errorsCount = actionResult.validationErrors.length;
				for (var i = 0; i < errorsCount; i++) {
					var errorItem = $('<li/>', { text: actionResult.validationErrors[i] });
					validationErrorsArea.append(errorItem);
				}

				actionResultArea.append(validationErrorsArea);
			}

			actionResultArea.show();
			this.handlers.handleSurveySaveError();
		}

		$('body').scrollTo(0, Common.settings.actionResultScrollingDuration);
	},

	deleteQuestion: function (questionForm) {
		questionForm.getQuestionArea().remove();

		var questionId = questionForm.getQuestion().clientId;

		delete this.questionFormsByQuestionIds[questionId];

		//do not need to update survey object, as it will be updated before saving
	},

	getNewQuestionId: function () {
		this.maxQuestionId++;
		return this.maxQuestionId;
	}
};

//Survey form end

//Question form start

QuestionForm = function (questionArea, question, handlers) {
	this.questionArea = $(questionArea);
	this.question = question;

	this.initialHandlers = handlers;

	this.handlers = {
		handleQuestionDelete: Common.createDelegate(this, this.handleQuestionDelete),
		handleEditorPreview: Common.createDelegate(this, this.handleEditorPreview),
		handlePreviewEdit: Common.createDelegate(this, this.handlePreviewEdit)
	};

	if (!Common.settings.isFullPreview) {
		this.isEdited = true;
		this.editor = new QuestionEditor(questionArea, question, this.handlers);
		this.preview = null;
	}
	else {
		this.isEdited = false;
		this.preview = new QuestionPreview(questionArea, question, this.handlers);
		this.editor = null;
	}
};

QuestionForm.prototype = {
	handleEditorPreview: function () {
		this.isEdited = false;
		this.question = this.editor.getQuestion();
		delete this.editor;

		this.questionArea.empty();
		this.preview = new QuestionPreview(this.questionArea, this.question, this.handlers);
	},

	handlePreviewEdit: function () {
		this.isEdited = true;
		delete this.preview;

		this.questionArea.empty();
		this.editor = new QuestionEditor(this.questionArea, this.question, this.handlers);
	},

	handleQuestionDelete: function () {
		this.initialHandlers.handleQuestionDelete(this);

		delete this.preview;
		delete this.editor;
	},

	getQuestion: function () {
		if (this.isEdited) {
			return this.editor.getQuestion();
		} else {
			return this.question;
		}
	},

	getQuestionArea: function () {
		return this.questionArea;
	}
};

//Question form end

//Question editor start

//handlers is the object of different function handlers. It is a simplified observer pattern.
QuestionEditor = function (questionArea, question, handlers) {
	this.questionArea = $(questionArea);
	this.answersArea = null;
	this.burronsArea = null;

	this.answerEditor = null;

	this.question = question;
	this.handlers = handlers;

	this.initialize();
};

QuestionEditor.prototype = {

	initialize: function () {
		this.addHeaderArea();

		this.answersArea = $('<div/>', { 'class': 'answers-area' }).appendTo(this.questionArea);
		this.addAnswersEditor(this.question.type);

		this.addButtonsArea();

		Common.prepareUi(this.questionArea);
	},

	addHeaderArea: function () {
		var textId = 'questionText' + this.question.clientId;
		var textArea = $('<div/>', { 'class': 'question-header-row ui-helper-reset' })
					.append($('<span/>', { text: Common.resources.questionTextLabel, 'class': 'mandatory' }))
					.append($('<input/>', { id: textId, name: textId, type: 'text', value: this.question.text }));

		var descriptionId = 'questionDescription' + this.question.clientId;
		var descriptionArea = $('<div/>', { 'class': 'question-header-row ui-helper-reset' })
					.append($('<span/>', { text: Common.resources.questionDescriptionLabel }))
					.append($('<input/>', { id: descriptionId, name: descriptionId, type: 'text', value: this.question.description }));

		var isRequiredId = 'questionIsRequired' + this.question.clientId;
		var isRequiredInput = $('<input/>',
				{ id: isRequiredId, name: isRequiredId, type: 'checkbox', 'class': 'question-required-input' });

		var isRequiredArea = $('<div/>', { 'class': 'question-header-row ui-helper-reset' })
					.append($('<span/>', { text: Common.resources.questionIsRequiredLabel }))
					.append(isRequiredInput);

		var textTypeArea = $('<div/>', { 'class': 'question-header-row ui-helper-reset' })
					.append($('<span/>', { text: Common.resources.questionTypeLabel }))
					.append(this.getQuestionTypeDropDown());

		var questionHeaderArea = $('<div/>', { 'class': 'question-header-area' })
					.append(textArea).append(descriptionArea).append(isRequiredArea).append(textTypeArea);

		//checked: this.question.isRequired in jQuery attributes object does not work in IE.
		//Also, if this line is placed earlier, it doesn't work in IE.
		isRequiredInput[0].checked = this.question.isRequired;

		this.questionArea.append(questionHeaderArea);
	},

	addButtonsArea: function () {
		var previewButtonId = 'questionPreview' + this.question.clientId;
		var previewButton = $('<input/>', { id: previewButtonId, name: previewButtonId, type: 'button', value: Common.resources.previewQuestionButtonText, 'class': 'preview-question-button' })
				.click(this.handlers.handleEditorPreview);

		var deleteButtonId = 'questionDelete' + this.question.clientId;
		var deleteButton = $('<input/>', { id: deleteButtonId, name: deleteButtonId, type: 'button', value: Common.resources.deleteQuestionButtonText, 'class': 'delete-question-button' })
				.click(this.handlers.handleQuestionDelete);

		this.burronsArea = $('<div/>', { 'class': 'question-buttons-area' }).append(previewButton).append(deleteButton); //.hide();
		this.questionArea.append(this.burronsArea);
	},

	getQuestionTypeDropDown: function () {
		var dropDownId = 'questionType' + this.question.clientId;
		var dropDown = $('<select/>', { id: dropDownId, name: dropDownId }).change(Common.createDelegate(this, this.handleQuestionTypeChange));

		for (var questionType in SurveyQuestionType) {
			var questionTypeValue = SurveyQuestionType[questionType];
			var questionTypeText = Common.resources.questionTypeTitles[questionType];
			dropDown.append($('<option/>', { value: questionTypeValue, text: questionTypeText }));
		}

		dropDown.val(this.question.type);

		return dropDown;
	},

	addAnswersEditor: function (questionType) {
		switch (questionType) {
			case SurveyQuestionType.text:
				this.answerEditor = new TextAnswerForm(this.answersArea);
				break;
			case SurveyQuestionType.paragraphText:
				this.answerEditor = new ParagraphTextAnswerForm(this.answersArea);
				break;
			case SurveyQuestionType.checkBoxes:
				this.answerEditor = new ListAnswerEditor(this.answersArea, this.question, ListType.checkBoxes);
				break;
			case SurveyQuestionType.radioButtons:
				this.answerEditor = new ListAnswerEditor(this.answersArea, this.question, ListType.radioButtons);
				break;
			case SurveyQuestionType.dropDown:
				this.answerEditor = new ListAnswerEditor(this.answersArea, this.question, ListType.dropDown);
				break;
			case SurveyQuestionType.scale:
				this.answerEditor = new ScaleAnswerEditor(this.answersArea, this.question);
				break;
			default:
				return;
		}
	},

	handleQuestionTypeChange: function () {
		var oldQuestionType = this.question.type;

		this.question = this.getQuestion();

		var oldTypeIsList = oldQuestionType == SurveyQuestionType.checkBoxes ||
							oldQuestionType == SurveyQuestionType.radioButtons ||
							oldQuestionType == SurveyQuestionType.dropDown;

		var newQuestionType = this.question.type;
		var newTypeIsList = newQuestionType == SurveyQuestionType.checkBoxes ||
							newQuestionType == SurveyQuestionType.radioButtons ||
							newQuestionType == SurveyQuestionType.dropDown;

		//Answers are preserved just between list questions
		if (!(oldTypeIsList && newTypeIsList)) {
			this.question.answers = null;
		}

		this.answersArea.empty();
		delete this.answerEditor;

		this.addAnswersEditor(this.getQuestionType());
		Common.prepareUi(this.answersArea);
	},

	handleMouseOver: function () {
		this.burronsArea.show();
	},

	handleMouseOut: function () {
		this.burronsArea.hide();
	},

	getQuestionType: function () {
		var questionTypeString = $('#questionType' + this.question.clientId, this.questionArea).val();
		var questionType = parseInt(questionTypeString);

		return questionType;
	},

	//Pop data flow model is used in surveys (i.e. this function is called when question update is needed)
	//instead of the push model (i.e. when events are fired at each question update).
	//Currently, question is requested before saving survey, previewing question, changing question type.
	getQuestion: function () {
		var question = {
			id: this.question.id,
			clientId: this.question.clientId,
			text: $('#questionText' + this.question.clientId, this.questionArea).val(),
			description: $('#questionDescription' + this.question.clientId, this.questionArea).val(),
			isRequired: $('#questionIsRequired' + this.question.clientId, this.questionArea).is(':checked'),
			position: 0,
			type: this.getQuestionType(),
			supportsCustomAnswer: false,
			answers: this.answerEditor.getAnswers()
		};

		//Assign previous database ids. 
		//It's not required for now, as groups, questions and answers currently are completely rewritten.
		//TODO: Think about it!
		if (question.answers != null && this.question.answers != null) {
			var minAnswerId = question.answers.length;
			if (this.question.answers.length < minAnswerId) {
				minAnswerId = this.question.answers.length;
			}

			for (var i = 0; i < minAnswerId; i++) {
				question.answers[i].id = this.question.answers[i].id;
			}
		}

		return question;
	}
};

//Question editor end

//Question preview start

QuestionPreview = function (questionArea, question, handlers) {
	this.questionArea = $(questionArea);
	this.question = question;
	this.handlers = handlers;

	this.initialize();
};

QuestionPreview.prototype = {

	initialize: function () {

		this.addHeaderArea();

		this.answersArea = $('<div/>', { 'class': 'preview-answers-area' }).appendTo(this.questionArea);
		this.addAnswersPreview(this.question.type);

		if (!Common.settings.isFullPreview) {
			this.addButtonsArea();
		}

		Common.prepareUi(this.questionArea);
	},

	addHeaderArea: function () {
		var textArea = $('<div/>', { 'class': 'question-preview-text', text: this.question.text });
		if (this.question.isRequired) {
			textArea.addClass('mandatory');
			textArea.append($('<span/>', { text: '*', 'class': 'mandatory-star' }));
		}

		var descriptionArea = $('<div/>', { 'class': 'question-preview-description', text: this.question.description });

		var questionHeaderArea = $('<div/>', { 'class': 'question-preview-header-area' })
				.append(textArea).append(descriptionArea);
		this.questionArea.append(questionHeaderArea);
	},

	addButtonsArea: function () {
		var editButtonId = 'questionSave' + this.question.clientId;
		var editButton = $('<input/>', { id: editButtonId, name: editButtonId, type: 'button', value: Common.resources.editQuestionButtonText, 'class': 'edit-question-button' })
				.click(this.handlers.handlePreviewEdit);

		var deleteButtonId = 'questionDelete' + this.question.clientId;
		var deleteButton = $('<input/>', { id: deleteButtonId, name: deleteButtonId, type: 'button', value: Common.resources.deleteQuestionButtonText, 'class': 'delete-question-button' })
				.click(this.handlers.handleQuestionDelete);

		var buttonsArea = $('<div/>', { 'class': 'question-preview-buttons-area' }).append(editButton).append(deleteButton);
		this.questionArea.append(buttonsArea);
	},

	addAnswersPreview: function (questionType) {
		switch (questionType) {
			case SurveyQuestionType.text:
				this.answerEditor = new TextAnswerForm(this.answersArea);
				break;
			case SurveyQuestionType.paragraphText:
				this.answerEditor = new ParagraphTextAnswerForm(this.answersArea);
				break;
			case SurveyQuestionType.checkBoxes:
				this.answerEditor = new ListAnswerPreview(this.answersArea, this.question, ListType.checkBoxes);
				break;
			case SurveyQuestionType.radioButtons:
				this.answerEditor = new ListAnswerPreview(this.answersArea, this.question, ListType.radioButtons);
				break;
			case SurveyQuestionType.dropDown:
				this.answerEditor = new DropDownAnswerPreview(this.answersArea, this.question);
				break;
			case SurveyQuestionType.scale:
				this.answerEditor = new ScaleAnswerPreview(this.answersArea, this.question);
				break;
			default:
				return;
		}
	}
};

//Question preview end


//Answer editors start

//Text answer editor start

TextAnswerForm = function (answersArea) {
	this.answersArea = $(answersArea);

	this.initialize();
};

TextAnswerForm.prototype = {
	initialize: function () {
		//We do not simply use disabled option, but handle key down events,
		//as disabled elements fire mouseout for parent elements, which in some cases
		//is inappropriate.
		var input = $('<input/>', { type: 'text', value: Common.resources.sampleTextAnswer, 'class': 'text-answer' })
			.appendTo(this.answersArea);

		Common.protectContent(input, true);
	},

	getAnswers: function () {
		return [];
	}
};

//Text answer editor end

//Paragraph text answer editor start

ParagraphTextAnswerForm = function (answersArea) {
	this.answersArea = $(answersArea);

	this.initialize();
};

ParagraphTextAnswerForm.prototype = {
	initialize: function () {
		var textArea = $('<textarea/>', { value: Common.resources.sampleParagraphTextAnswer, 'class': 'paragraph-text-answer' })
			.appendTo(this.answersArea);

		Common.protectContent(textArea, true);
	},

	getAnswers: function () {
		return [];
	}
};

//Paragraph text answer editor end

//List answer editor start

ListAnswerEditor = function (answersArea, question, listType) {
	this.answersArea = $(answersArea);
	this.question = question;
	this.listType = listType;

	if (this.question.answers == null || this.question.answers.length == 0) {
		this.question.answers = [{ id: 0, text: Common.resources.sampleOptionText, position: 0 }, { id: 0, text: Common.resources.sampleOptionText, position: 1}];
	}

	//TODO: else sort answers by position!

	this.initialize();
};

ListAnswerEditor.prototype = {
	initialize: function () {
		var answerOptionsList = $('<ul/>', { id: 'answerOptions' + this.question.clientId, 'class': 'answer-options-list-editor' }).appendTo(this.answersArea);

		var answersCount = this.question.answers.length;
		for (var i = 0; i < answersCount; i++) {
			var listItem = this.createAnswerOption(this.question.answers[i].text);
			listItem.appendTo(answerOptionsList);
		}

		this.addLastOption(answerOptionsList);
	},

	createAnswerOption: function (answerText) {
		var checkBox = this.getInputElement();
		var answerInput = $('<input/>', { type: 'text', value: answerText, 'class': 'answer-options-value-editor' });
		var deleteButton = $('<input/>', { type: 'button', value: Common.resources.deleteAnswerButtonText, 'class': 'delete-answer-option-button' }).click(Common.createDelegate(this, this.deleteAnswerOption));

		var listItem = $('<li/>', { 'class': 'answer-options-item-editor ui-helper-reset' }).append(checkBox).append(answerInput).append(deleteButton);

		return listItem;
	},

	addLastOption: function (answerOptionsList) {
		var listItem = $('<li/>', { 'class': 'answer-options-item-editor ui-helper-reset' }).appendTo(answerOptionsList);

		var checkBox = this.getInputElement();
		var answerText = $('<input/>', { type: 'text', value: Common.resources.lastListOptionText, 'class': 'last-editor-option' }).click(Common.createDelegate(this, this.addNewAnswerOption));
		Common.protectContent(answerText, false);

		listItem.append(checkBox).append(answerText);
	},

	getInputElement: function () {

		//radiobuttons should have equal names to belong to the same group
		switch (this.listType) {
			case ListType.checkBoxes:
				return $('<span/>').append($('<input/>', { type: 'checkbox', name: 'answerOption' + this.question.clientId }));
			case ListType.radioButtons:
				return $('<span/>').append($('<input/>', { type: 'radio', name: 'answerOption' + this.question.clientId }));
			case ListType.dropDown:
				return $('<span>&nbsp;</span>');
			default:
				return $('<span/>');
		}
	},

	deleteAnswerOption: function (eventData) {
		var listItem = $(eventData.target).parent();
		listItem.remove();
	},

	addNewAnswerOption: function (eventData) {
		var lastListItem = $(eventData.target).parent();

		var listItem = this.createAnswerOption(Common.resources.sampleOptionText);
		listItem.insertBefore(lastListItem);

		Common.prepareUi(listItem);

		$('input[type = text]', listItem).select();
	},

	getAnswers: function () {
		var answerTextInputs = $('input[type = text]', this.answersArea);
		var answersCount = answerTextInputs.length - 1; //the last input is the 'new answer' text input

		var answers = [];
		for (var i = 0; i < answersCount; i++) {
			var answerText = $(answerTextInputs[i]).val();

			answers.push({ text: answerText, position: i });
		}

		return answers;
	}
};

//List answer editor end

//Scale answer editor start

ScaleAnswerEditor = function (answersArea, question) {
	this.answersArea = $(answersArea);
	this.question = question;

	if (this.question.answers == null || this.question.answers.length == 0) {
		this.question.answers = [];
		for (var i = Common.settings.scaleMinBoundaryMinValue; i <= Common.settings.scaleMaxBoundaryMinValue; i++) {
			this.question.answers.push({ id: 0, text: '', position: i });
		}

		this.question.answers[0].text = '';
		this.question.answers[this.question.answers.length - 1].text = '';
	}

	this.initialize();
};

ScaleAnswerEditor.prototype = {
	initialize: function () {
		var minBoundaryArea = this.getMinBoundaryArea();
		var maxBoundaryArea = this.getMaxBoundaryArea();
		var minBoundaryTitleArea = this.getMinBoundaryTitleArea();
		var maxBoundaryTitleArea = this.getMaxBoundaryTitleArea();

		this.answersArea.append(minBoundaryArea).append(maxBoundaryArea).append(minBoundaryTitleArea).append(maxBoundaryTitleArea);
	},

	getMinBoundaryArea: function () {
		var minBoundaryArea = $('<div/>', { 'class': 'scale-settings-row ui-helper-reset' });

		var minBounadryLabel = $('<span/>', { text: Common.resources.minBoundaryLabel });

		var minBoundary = $('<select/>', { id: 'minBoundaryValue' + this.question.clientId });
		for (var i = Common.settings.scaleMinBoundaryMinValue; i <= Common.settings.scaleMinBoundaryMaxValue; i++) {
			$('<option/>', { value: i, text: i }).appendTo(minBoundary);
		}

		minBoundary.val(this.question.answers[0].position);

		minBoundaryArea.append(minBounadryLabel).append(minBoundary);

		return minBoundaryArea;
	},

	getMaxBoundaryArea: function () {
		var maxBoundaryArea = $('<div/>', { 'class': 'scale-settings-row ui-helper-reset' });
		var maxBounadryLabel = $('<span/>', { text: Common.resources.maxBoundaryLabel });
		var maxBoundary = $('<select/>', { id: 'maxBoundaryValue' + this.question.clientId });

		for (var i = Common.settings.scaleMaxBoundaryMinValue; i <= Common.settings.scaleMaxBoundaryMaxValue; i++) {
			$('<option/>', { value: i, text: i }).appendTo(maxBoundary);
		}

		maxBoundary.val(this.question.answers[this.question.answers.length - 1].position);

		maxBoundaryArea.append(maxBounadryLabel).append(maxBoundary);

		return maxBoundaryArea;
	},

	getMinBoundaryTitleArea: function () {
		var title = this.question.answers[0].text;
		var minBoundaryTitle = $('<input/>', { id: 'minBoundaryTitle' + this.question.clientId, value: title, type: 'text', 'class': 'scale-title-input' });
		var minBoundaryTitleArea = $('<div/>', { 'class': 'scale-settings-row ui-helper-reset' })
				.append($('<span/>', { text: Common.resources.minBoundaryTitleLabel }))
				.append(minBoundaryTitle);

		return minBoundaryTitleArea;
	},

	getMaxBoundaryTitleArea: function () {
		var title = this.question.answers[this.question.answers.length - 1].text;
		var maxBoundaryTitle = $('<input/>', { id: 'maxBoundaryTitle' + this.question.clientId, value: title, type: 'text', 'class': 'scale-title-input' });
		var maxBoundaryTitleArea = $('<div/>', { 'class': 'scale-settings-row ui-helper-reset' })
				.append($('<span/>', { text: Common.resources.maxBoundaryTitleLabel }))
				.append(maxBoundaryTitle);

		return maxBoundaryTitleArea;
	},

	getAnswers: function () {
		var minBoundaryString = $('#minBoundaryValue' + this.question.clientId, this.answersArea).val();
		var minBoundaryValue = parseInt(minBoundaryString);

		var maxBoundaryString = $('#maxBoundaryValue' + this.question.clientId, this.answersArea).val();
		var maxBoundaryValue = parseInt(maxBoundaryString);

		var minBoundaryTitle = $('#minBoundaryTitle' + this.question.clientId, this.answersArea).val();
		var maxBoundaryTitle = $('#maxBoundaryTitle' + this.question.clientId, this.answersArea).val();

		var answers = [];
		for (var i = minBoundaryValue; i <= maxBoundaryValue; i++) {
			answers.push({ text: '', position: i });
		}

		answers[0].text = minBoundaryTitle;
		answers[maxBoundaryValue - minBoundaryValue].text = maxBoundaryTitle;

		return answers;
	}
};

//Scale answer editor end

//Answer editors end

//Answer previews start

//List answer preview start

ListAnswerPreview = function (answersArea, question, listType) {
	this.answersArea = $(answersArea);
	this.question = question;
	this.listType = listType;

	if (this.question.answers == null || this.question.answers.length == 0) {
		return;
	}

	//TODO: else sort answers by position!

	this.initialize();
};

ListAnswerPreview.prototype = {
	initialize: function () {
		var answerOptionsList = $('<ul/>', { id: 'answerOptions' + this.question.clientId, 'class': 'answer-options-list' }).appendTo(this.answersArea);

		var answersCount = this.question.answers.length;
		for (var i = 0; i < answersCount; i++) {
			this.addAnswerOption(answerOptionsList, this.question.answers[i].text);
		}
	},

	addAnswerOption: function (answerOptionsList, answerText) {
		var input = this.getInputElement();
		var answerInput = $('<span/>', { text: answerText, 'class': 'answer-options-value' });

		var listItem = $('<li/>', { 'class': 'answer-options-item' }).append(input).append(answerInput);

		answerOptionsList.append(listItem);
	},

	getInputElement: function () {

		//radiobuttons should have equal names to belong to the same group
		switch (this.listType) {
			case ListType.checkBoxes:
				return $('<input/>', { type: 'checkbox', name: 'answerOption' + this.question.clientId });
			case ListType.radioButtons:
				return $('<input/>', { type: 'radio', name: 'answerOption' + this.question.clientId });
			default:
				return $('<span/>');
		}
	}
};

//List answer preview end

//Drop down answer preview start

DropDownAnswerPreview = function (answersArea, question) {
	this.answersArea = $(answersArea);
	this.question = question;

	if (this.question.answers == null || this.question.answers.length == 0) {
		return;
	}

	//TODO: sort by position

	this.initialize();
};

DropDownAnswerPreview.prototype = {
	initialize: function () {
		var answerOptionsList = $('<select/>').appendTo(this.answersArea);
		var defaultOption = $('<option/>', { value: -1, text: Common.resources.defaultDropDownOption }).appendTo(answerOptionsList);

		var answersCount = this.question.answers.length;
		for (var i = 0; i < answersCount; i++) {
			var option = $('<option/>', { value: i, text: this.question.answers[i].text }).appendTo(answerOptionsList);
		}
	}
};

//Drop down answer preview end

//Scale answer preview start

ScaleAnswerPreview = function (answersArea, question) {
	this.answersArea = $(answersArea);
	this.question = question;

	if (this.question.answers == null || this.question.answers.length == 0) {
		return;
	}

	//TODO: Sort answers by position (for preventing programming). But now answers are sorted anyway.

	this.initialize();
};

ScaleAnswerPreview.prototype = {
	initialize: function () {

		var valuesRow = this.getValuesRow();
		var inputsRow = this.getInputsRow();

		var table = $('<table/>', {'class': 'scale-preview-table'}).append(valuesRow).append(inputsRow);
		this.answersArea.append(table);
	},

	getValuesRow: function () {
		var answersCount = this.question.answers.length;

		var valuesRow = $('<tr/>', { 'class': 'scale-preview-values-row' });

		valuesRow.append($('<td/>'));
		for (var i = 0; i < answersCount; i++) {
			var position = this.question.answers[i].position;
			valuesRow.append($('<td/>', { text: position }));
		}
		valuesRow.append($('<td/>'));

		return valuesRow;
	},

	getInputsRow: function () {
		var answersCount = this.question.answers.length;

		var minValueTitle = this.question.answers[0].text;
		var maxValueTitle = this.question.answers[answersCount - 1].text;

		var inputsRow = $('<tr/>', { 'class': 'scale-preview-inputs-row' });

		inputsRow.append($('<td/>', { text: minValueTitle, 'class': 'scale-preview-label' }));
		for (var i = 0; i < answersCount; i++) {
			var position = this.question.answers[i].position;
			var option = $('<input/>', { value: position, name: 'answerOptions' + this.question.clientId, type: 'radio', 'class': 'scale-preview-input' });
			var cell = $('<td/>').append(option);

			inputsRow.append(cell);
		}
		inputsRow.append($('<td/>', { text: maxValueTitle, 'class': 'scale-preview-label' }));

		return inputsRow;
	}
};

//Scale answer preview end

//Answer previews end

$.fn.surveyForm = function (settings, resources, initialSurvey) {
	Common.settings = $.extend({}, Common.defaultSettings, settings);
	Common.resources = $.extend({}, Common.defaultResources, resources);

	//Here 'this' is the jQuery selection set, over which the surveyForm function was called.
	return this.each(function () {
		//Here 'this' is the DOM child element of the selection set, over which the surveyForm function was called.
		var surveyForm = new AspNetSurveyForm(this, initialSurvey);
	});
};

})(jQuery);
