/*
 * iPlugin provides an 'abstract' (in effect) class which all 
 * plugins can extend from for improved functionality
 * 
 * This should always load first.
 */
	/**
	 * Define a custom error object
	 * Currently this gives no benefit, but it allows us to modify
	 * the error behaviour in the future, hopefully to include
	 * stack traces.
	 */
	function ingotError(message){
		this.message = message;
		if(console !== undefined){
			console.log(this.message);
			console.log(arguments.callee.caller.toString());
		}
	}
	ingotError.prototype = new Error();

(function ($) {
	/**
	 * Store all classes in here
	 */
	ingotClass = {};
	
	/**
	 * Define the error handler
	 */
    
    $.fn.iError = function(){
		
		// Track whether an error is currently being handled.
		// If so then do not go into an error-loop		
		var handlingError = false;
				
		/**
		 * Custom Error Handler
		 */
		function customErrorHandler(desc,page,line,chr){
			try{
				if(handlingError === true)
					throw new ingotError('Already handling an error call');
				handlingError = true;
				
				// To track if the ajax call is successful
				var ajaxSuccess = false;

				// Block the UI to prevent user continuing should
				// there be a problem connecting to the server
				$.iBlockUI($.iMessage['JS_ERROR_PASSING_MESSAGE_TO_SERVER']+'...');

				// Pass to development team
				$.fn.iAjax({
					type: 'POST',
					url: '?r=site/javascripterror',
					data: {
							desc: desc,
							page: page,
							line: line,
							chr: chr
							},
					success: function(){
								ajaxSuccess = true;
							}
//					complete: function(){
//								// Display Error message
//								var message = '<b>'+$.iMessage['JS_UNEXPECTED_ERROR_HAS_OCCURRED']+'</b>';
//								message += '<p>'+$.iMessage['JS_UNEXPECTED_ERROR_HAS_OCCURRED_LONG_MESSAGE']+'<br />';
//								message += '<ul>';
//								message += '<li>'+$.iMessage['JS_SAVE_UNSAVED_WORK_IF_POSSIBLE']+'</li>';
//								message += '<li>'+$.iMessage['JS_ERROR_RELOAD_PAGE']+'</li>';
//								message += '</ul></p>';
//								if(ajaxSuccess === true)
//									message += '<p>'+$.iMessage['JS_ERROR_DETAILS_PASSED_TO_DEV_TEAM']+'</p>';
//								$.fn.iDialog({
//									title: $.iMessage['JS_ERROR_DIALOG_TITLE'],
//									message: message
//								});
//							}
				});
				handlingError = false;
			}
			catch(err){
				alert('An error has occurred, but could not be handled by the system.  Please reloading this page.  If the problem persists then please try contacting The Learning Machine for further support');
			}
			return false;
		}
		
		/**
		 * Set the error handler
		 */
		window.onerror = customErrorHandler;
	}
	
	// Load Custom Error Handler
	$.fn.iError();
	
}(jQuery));
/*
 * iPlugin provides an 'abstract' (in effect) class which all 
 * plugins can extend from for improved functionality
 * 
 * This should always load first.
 */

(function ($) {
	
	ingotClass.ingotObject = function (options, command, parameter) {
		if(options !== undefined && options === 'constructor'){
			return this.RunCommand(command, parameter);
		}
		this.callParentMethod('constructor');
		this.thisObject = null;
		this.setThisObject();
		this.setOptions(options);
		this._setupPassedBehaviours();
	};
	// Inherit from jsInheritableClass
	ingotClass.ingotObject.inheritsFrom(jsInheritableClass);
	
	/**
	 * Handle commands passed after initalisation
	 */
	ingotClass.ingotObject.prototype.RunCommand = function (command, parameter){
	};
	
	/*
	 * Get thisObject for self reference
	 */
	ingotClass.ingotObject.prototype.setThisObject = function(){
		return this.thisObject;
	};
		 
	 /*
	  * Get the default options
	  */
	ingotClass.ingotObject.prototype.getDefaultOptions = function(){
		 return {};
	 };
	 
	 /*
	  * Set the options
	  */
	 ingotClass.ingotObject.prototype.setOptions = function(newOptions){
		 this._options = $.extend(true, {}, this.getDefaultOptions());
		 if($.isPlainObject(newOptions))
			 this._options = $.extend(true, this._options, newOptions);
		this._options.classes = $.fn.iPlugin.standardiseCssClasses(this._options.classes);
	 };
	 
	 /*
	  * Get the options;
	  */
	ingotClass.ingotObject.prototype.getOptions = function(){
		if(this._options === undefined)
			this.setOptions({});
		return this._options;
	 };
	
	/*
	  * Get the behaviours
	  */
	ingotClass.ingotObject.prototype._setupPassedBehaviours= function(){
		for(var i = 0; i < this.getPassedBehaviours().length; i++){
			this.addBehaviour(this.getPassedBehaviours()[i]);
		}
	};

	/*
	 * Get the behaviours
	 */
	ingotClass.ingotObject.prototype.addBehaviour = function(behaviour){
		this.getBehaviours().push(new behaviour(this, this.getOptions()));
	};
	 
	/*
	 * Get the behaviours
	 */
	ingotClass.ingotObject.prototype.getBehaviours = function(){
		if(this.getOptions()._behaviourObjects === undefined){
			this.getOptions()._behaviourObjects = [];
		}
		return this.getOptions()._behaviourObjects;
	};

	/*
	  * Get the behaviours
	  */
	ingotClass.ingotObject.prototype.getPassedBehaviours = function(){
		if(this.getOptions().behaviours === undefined){
			this.getOptions().behaviours = [];
		}
		return this.getOptions().behaviours;
	};
	 
	 /**
	  * Trigger behaviours
	  */
	ingotClass.ingotObject.prototype.TriggerBehaviours = function(method, params){
		for(var i = 0; i < this.getBehaviours().length; i++){
			if (this.getBehaviours()[i][method] !== undefined && $.isFunction(this.getBehaviours()[i][method]) === true){
				this.getBehaviours()[i][method].apply(this.getBehaviours()[i],params);
			}
		}
	};
	
}(jQuery));
/*
 * iPlugin provides an 'abstract' (in effect) class which all 
 * plugins can extend from for improved functionality
 * 
 * This should always load first.
 */

(function ($) {
	
	ingotClass._behaviourBase = function (ownerObject, options) {
		var newOptions = this.removeUnrecognisedOptions(options);
		this.callParentMethod('constructor', [newOptions]);
		this.setOwnerObject(ownerObject);
	};
	// Inherit from ingotObject
	ingotClass._behaviourBase.inheritsFrom(ingotClass.ingotObject);

	ingotClass._behaviourBase.prototype.setOwnerObject = function(object) {
		this._ownerObject = object;
	};
	ingotClass._behaviourBase.prototype.removeUnrecognisedOptions = function(options) {
		var newOptions = {};
		var thisObject = this;
		
		var walkObject = function(obj1, obj2){
			for(var x in obj2){
				if($.isPlainObject(obj2[x])){
					obj1[x] = {};
					walkObject(obj1[x], obj2[x]);
					if(obj1[x].length === 0)
						delete obj1[x];
				}
				else if(x in thisObject.getDefaultOptions()){
					obj1[x] = obj2[x];
				}
			}
		};
		
		walkObject(newOptions, options);
		return newOptions;
	};
	ingotClass._behaviourBase.prototype.getOwnerObject = function() {
		if(this._ownerObject === undefined)
			return {};
		return this._ownerObject;
	};
	ingotClass._behaviourBase.prototype.getOwnerOptions = function() {
		if(this._ownerObject === undefined || this._ownerObject.getOptions === undefined)
			return {};
		return this._ownerObject.getOptions();
	};

}(jQuery));/*
 * iPlugin provides an 'abstract' (in effect) class which all 
 * plugins can extend from for improved functionality
 * 
 * This should always load first.
 */

(function ($) {	
	/**
	 * Helper Classes
	 */
	ingotClass.HelperFunctions = {};
	
	/**
	 * Check if the browser supports file API functionality
	 */
	ingotClass.HelperFunctions.IsFileAPISupported = function(){
		if (window.File && window.FileReader && window.FileList && window.Blob)
			return true;
		return false;
	}
	
	/**
	 * check if canvas is supported
	 * Borrowed from http://stackoverflow.com/questions/2745432/best-way-to-detect-that-html5-canvas-is-not-supported
	 */
    ingotClass.HelperFunctions.isCanvasSupported = function(){
		var elem = document.createElement('canvas');
		return !!(elem.getContext && elem.getContext('2d'));
	}
	
}(jQuery));
/*
 * iPlugin provides an 'abstract' (in effect) class which all 
 * plugins can extend from for improved functionality
 * 
 * This should always load first.
 */

(function ($) {	
	/**
	 * The iPlugin 'abstract' class itself
	 */
	$.fn.iPlugin = {
		/** 
		 * this will either attach a new object of className to the
		 * passed element, remove the object if passed 'remove' as the
		 * options value or pass the data on the a loaded object
		 * as a command
		 */
		handleObjectAttachedToElement: function(element, className, options, parameter){
			var object = element.iGetData(className);

			if(object !== undefined){
				if(options === 'remove')
					this.removeData(pluginName);
				return (object('command', options, parameter))
			}
			
			if(options === undefined)
				options = {};
			options.Root = element;
			var newObject = new ingotClass[className](options, parameter);
			element.iSetData(className, newObject);
		},
		/**
		 * If a custom CSS is passed in a class var then this will generate a new
		 * css entry for that class and set the class var to that css name.
		 * If any classes are blank it will fill in the value as the class var
		 * name to prevent issues with passing blank values to $.removeClass()
		 */
		standardiseCssClasses: function (classes) {
			for(var x in classes){
				if($.isPlainObject(classes[x]) === true){
					var newClassName = 'ingotGeneratedClass_' + this.generateUniqueId();
					var newStyle = $('<style type="text/css" ></style>');
					var newStyleContent = "." + newClassName + '{';
					for(var y in (classes[x])){
						newStyleContent += y + ': ';
						newStyleContent += classes[x][y] + ';';
					}
					newStyleContent += "}";
					newStyle.append(newStyleContent)
					$('html > head').append(newStyle);
					classes[x] = newClassName;
				}
				// Fix any blank classes
				if(typeof classes[x] !== 'string' || classes[x] === '')
					classes[x] = x;
			}
			return classes;
		},
		/**
		 * Generate a unique ID
		 */
		uniqueIds: [],
		generateUniqueId: function (){
			var uniqueId;
			do{
				uniqueId = (Math.random() * 100000000) + 1000000;
				uniqueId = Math.round(uniqueId);
			}while($.inArray(uniqueId) !== -1)
			this.uniqueIds.push(uniqueId);
			return uniqueId;
		}
	};	

	
}(jQuery));
/*
 * Drag and drop files for upload
 */

(function ($) {
	
	$.fn.iSetData = function(pluginName, key, data){
		var pluginData = this.data(pluginName);
		if(pluginData === undefined)
			pluginData = {};
		pluginData[key] = data;
		this.data(pluginName, pluginData);
	};

	$.fn.iGetData = function(pluginName, key){
		return this.data(pluginName);
	};
	
}(jQuery));
/////////////////////////////////////////////////////
//
//    AJAX FEATURES / JQUERY PLUGINS
//
///////////

/*
 *  INTRODUCTION:
 *
 *      This file covers most of the key AJAX features of the certification
 *      site. Every feature in this file is written as a jQuery plugin.
 *      Advantages of writing AJAX features as jQuery plugins:
 *
 *      1) Helps encapsulate code, so one feature cannot conflict with another.
 *      2) Helps make code easy to reuse and more flexible.
 *      3) Helps ensure that all AJAX calls on the site have a similar API
 *         and behave similarly.
 *
 *
 *  BACKGROUND:
 *
 *      You should be familiar with the following concepts:
 *
 *      1)  General knowledge of jQuery.
 *      2)  How to write a jQuery plugin.
 *      3)  Closures. In other words, you should understand this:
 *
 *          (function() {
 *              ... some code here ...
 *          })();
 *
 *unselectable
 *  GUIDE-LINES:
 *
 *      1) Naming:
 *          All plugins follow the "iSomething" naming scheme:
 *
 *          iBlockUI
 *          iUnblockUI
 *          iUploadCSV
 *          iDate
 *          iSchoolActivity
 *          iMarksheet
 *
 *      2) Encapsulation:
 *
 *          Functions and variables should all be private to a closure.
 *          Make sure you declare all variables with "var" otherwise they
 *          will be global variables!!!
 *
 *          Any AJAX feature visible outside this file should be implemented
 *          as a jQuery plugin. And plugin should be designed so they cannot
 *          conflict with one another.
 *
 *      3) JS Lint:
 *
 *          Every once in a while, make sure that all the JS code pases JSLint
 *          with the "Good Parts" option on to the extent possible.
 */

(function ($) {
    /*
	 *  "use strict" is a new feature in ECMAScript 5. To learn about it, see:
	 *  http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
	 */
    "use strict";

    /////////////////////////////////////////////////////
    //
    //    BLOCK/UNBLOCK UI PLUGINS
    //
    ///////////
    var iPhotoCSS = {
        border: 'none',
        padding: '0.5em',
        backgroundColor: '#000',
        '-webkit-border-radius': '10px',
        '-moz-border-radius': '10px',
        opacity: 0.7,
        'font-size': '200%',
        'font-weight': 'bold',
        top: '10px', 
        left: '', 
        right: '10px',
        color: '#fff'
    };

    /*
	 * NOTE:
	 *     I used to change the background colour in iBlockUI, but doing
	 *     so made JS crash on IE7. Now I just use $.blockUI() alone.
	 */
    $.iBlockUI = function (message) {
        // Display message: "Uploading..."
        $.blockUI({
            css: iPhotoCSS,
           // showOverlay: false,
			overlayCSS: {backgroundColor: 'none'},
            message: message,
            centerY:false
        });
    };
    $.iUnblockUI = function (message,timeout) {
        // Flash message: "Done"
        if (message) {
            $.blockUI({
                css: iPhotoCSS,
                showOverlay: false,
                message: message,
                centerY: false
            });
            if (!timeout) timeout = 200;
            setTimeout($.unblockUI, timeout);
        } else {
            $.unblockUI();
        }
    };

    /////////////////////////////////////////////////////
    //
    //    I18N / TRANSLATIONS
    //
    ///////////

    /*
	 * Translations are in the $.iMessages variable which is defined in:
	 *
	 *			protected/views/layouts/main.php
	 */

    /////////////////////////////////////////////////////
    //
    //    UPLOAD CSV PLUGIN
    //
    ///////////

    /*
	 *  Generic plugin for uploading CSV files. Currently used in two places:
	 *
	 *  1) INGOT Markbook (below).
	 *  2) Upload pupils by CSV.
	 *
	 *
	 *  This wrapper simplifies the interface to the ajaxFileUpload()
	 *  plugind and add user feedback ("Uploading..." -> "Done").
	 *
	 *  Learn more: http://www.phpletter.com/Our-Projects/AjaxFileUpload/
	 * 
	 *  NOTE: the AjaxFileUpload plugin is no longer maintained and now 
	 *  causes a crash if an error is thrown within it as handleError() 
	 *  has been depreciated and removed from modern jQuery.  This needs
	 *  to be replaced at some point.
	 */
	 
	$.fn.iUploadCSV2 = function (options){
		options.format = 'iajax';
		return $(this).iUploadCSV(options);
	}


    $.fn.iUploadCSV = function (settings) {
        $(this).click(function() {
            //need to check if file is selected
            if($(this).find("input[name='csvfile']").val()===''){
                $.fn.iDialog("No file is selected! Please select a CSV file to upload!");
                return;
            }

            $.fn.iAjax({
		jFrameFileUpload : true,
                url: settings.action,
                secureuri: false,
                fileElementId: (settings.elementId ? settings.elementId : 'csvfile'),
                dataType: 'xml',
                success: function (data, status) {
                    if (settings.onComplete)
                        settings.onComplete(data);
                    if (settings.success)
                        settings.success(data);
                },
                error: function (data, status, e) {
                    $.fn.iDialog(e);
                }
            });
        });
    };

    /////////////////////////////////////////////////////
    //
    //    DATE PICKER PLUGIN
    //
    ///////////

    /*
	 *  A thin wrapper around the datepicker() plugin.
	 *
	 *  This plugin just changes some default values like the date format.
	 *  This way I can just write $('foo').iDate() everywhere and get the
	 *  date-picker I want.
	 */

    $.fn.iDate = function (options) {
        var defaults = {
            dateFormat: 'dd-mm-yy',
            changeMonth: true,
            changeYear: true,
            maxDate: '+0d'
        };
        var settings = $.extend(defaults, options);
        this.datepicker(settings);
    };
    
    /////////////////////////////////////////////////////
    //
    //		MORE INFO PLUGIN
    //
    ////////////////////////
    
    $.fn.iMoreInfo = function (options){
		
	}

    /////////////////////////////////////////////////////
    //
    //    SCHOOL ACTIVITY PLUGIN
    //
    ///////////

    $.fn.iSchoolActivity = function (data) {
        var Approved = function(req,app) {
            if (req == 0) return "N/A";
            return (app < req ? app : 'All');
        }

        var i, level, block_class, block_classes = [], tbody = '';
        var tot_requested = 0, tot_approved = 0;
        for (i in data) {
            data[i]['requested'] = data[i]['requested'];
            data[i]['approved'] = data[i]['approved'];

            tot_requested += data[i]['requested'];
            tot_approved += data[i]['approved'];

            block_class = 'subject-'+data[i]['subject_id'];
            block_classes.push(block_class);

            tbody +='<tr id="'+block_class+'" class="clickable">'+
            '<td>'+data[i]['category']+'</td>'+
            '<td style="text-align: left">'+data[i]['subject']+'</td>'+
            '<td>'+data[i]['requested']+'</td>'+
            '<td>'+
            Approved(data[i]['requested'],data[i]['approved'])+
            '</td>'+
            '</tr>';
            for (level in data[i]['levels']) {
                tbody +='<tr class="award-details '+block_class+'">'+
                '<td></td>'+
                '<td style="text-align: left">'+level+'</td>'+
                '<td>'+data[i]['levels'][level]['requested']+'</td>'+
                '<td>'+
                Approved(data[i]['levels'][level]['requested'],data[i]['levels'][level]['approved'])+
                '</td>'+
                '</tr>';
            }
        }

        // Set everything up.
        var Root = $(this);
        Root.find('tbody').html(tbody);
        Root.find('tfoot th.tot_requested').html(tot_requested);
        Root.find('tfoot th.tot_approved').html(Approved(tot_requested,tot_approved));

        for (i in block_classes) {
            (function (block_class) {
                Root.find('#'+block_class).click(function () {
                    Root.find('.'+block_class).toggle();
                });
            })(block_classes[i]);
        }
    };
    
    /////////////////////////////////
    //
    //		DISPLAY DIALOG PLUGIN
    //
    //////////
    
    /**
     * This will display a jquery-themed dialog in a page
     * 
     * Typically this will simply display the message passed via
     * the 'message' array key:
     * 
     * $.fn.iDialog({
	 * 		message: 'Display this',
	 * 		// other params
	 * });
     * 
     * If no other dialog parameters need changing, the string can be
     * bassed simply as the first parameter:
     * 
     * $.fn.iDialog('Display this');
     * 
     * Alternatively, an existing html element can be passed using
     * the element key:
     * 
     * $.fn.iDialog({
	 * 		element: '#useThisElementId',
	 * 		// other params
	 * });
	 * 
	 * This latter approach has the benefit of making the dialog contents
	 * persistant between it being opened and closed.  This is especially
	 * useful if you are submitting a form
     * 
     * @param mixed options an array of properties or alternatively
     * a single string with the dialog message.  This accepts all standard
     * jquery dialog options.
     */

	$.fn.iDialog = function (options) { 
		if(options.constructor.toString().match(/object/i) === null){
			options = {message: options};
		}
		// Default dialog settings
		var dialogDefaults = {
			width: 'auto',
			height: 'auto',
			element: null,
			modal: true,
			buttons: {
				Ok: function() {
					$( this ).dialog( "close" );
				}
			},
			autoOpen: false,
			//title: 'A Challenge to Overcome!'
			title: 'Message to the user'
		};
		var dialogOptions = $.extend(dialogDefaults, options);
		// Make Dialog
		if(dialogOptions.element !== null){	// If a passed html element
			var $dialog;
			if(dialogOptions.element.jquery) // if object
				$dialog = dialogOptions.element;
			else 							 // if string
				$dialog = $(dialogOptions.element)
		}
		else{	// if a passed string
			options.message = options.message.replace('\n', "<br/>");
			var $dialog = $('<div class="iDialog"></div>')
				.html(options.message);
		}
		$dialog.dialog(dialogOptions);
		$dialog.parent().wrap('<div class="blueTheme" />');
		$dialog.dialog('open');
		// If 'auto' width is specified, ensure it does not go over 80% 
		// of the window width
		if($dialog.width() > $(window).width() * 0.8 && $dialog.dialog('option','width') == 'auto'){
			$dialog.dialog('option','width', $(window).width() * 0.8);
			$dialog.dialog('option','position', $dialog.dialog('option','position'));
		}
	}
	
	///////////////////////////////////////////////
	//
	//	  CONFIRMATION PLUGIN
	//
	////////
	
	/*
	 * This gives a simple Cancel/OK confirmation box
	 * based oping the above iDialog plugin
	 */

	$.fn.iConfirm = function (message, callback) {
		var defaultOptions = {
			message: '',
			element: null,
			callback: function(){},
			okButton: $.iMessage['SINGLE_WORD_OK'],
			cancelButton: $.iMessage['SINGLE_WORD_CANCEL']
		};
		
		if($.isPlainObject(message)){
			var options = $.extend(defaultOptions, message);
		}
		else{
			var options = defaultOptions;
			options.message = message;
			options.callback = callback;
		}
				
		// Set the buttons
		var buttons = {};
		buttons[options.okButton] = function() {
			var returnValue = options.callback();
			$( this ).dialog( "close" );
			return returnValue;
		};
		buttons[options.cancelButton] = function() {
			$( this ).dialog( "close" );
		};
		
		// Build the dialog
		return $.fn.iDialog({
			message: options.message,
			element: options.element,
			width: $(window).width() * 0.4,
			buttons: buttons
		});
	}

    /////////////////////////////////////////////////////
    //
    //    SAVE FORM PLUGIN
    //
    ///////////

    /*
	 *  A plugin to save form data via AJAX.
	 *
	 *  This plugin reads the form data and sends it to the server as an
	 *  AJAX call. No page refresh. It also adds a "Saving..." message
	 *  and allows the user to specify an "onComplete" action.
	 *
	 *  This plugin is used in many places on the site.
	 * 
	 *  This can use either a simple $.post method or the custom $.fn.iAjax
	 * 	method for sending and receiving the data.  The latter of these
	 *  is preferable, although many functions on the site will need
	 *  updating to come into line with generating the strict json object
	 *  needed to pass back the data.
	 *  
	 *  For now, use $.fn.iSaveForm2 to enforce the use of the new
	 *  type. This will enable us to keep track of the functions which
	 *  use the old method, and will allow us to quickly bring everything
	 *  in to line once the old functions are updated.
	 * 
	 *  @param array options a map of possible parameters:
	 *   - format string 'iajax' forces the new post method
	 * 		or 'old' forces the old.  This should be used internally,
	 * 		instead use $.fn.iSaveForm2 for new method (see above)
	 *   - preSubmit function a function which is run before the form
	 * 		is submitted.  Return true or false from here to continue
	 * 		or prevent submission
	 */
	 
	$.fn.iSaveForm2 = function (options){
		options.format = 'iajax';
		return $(this).iSaveForm(options);
	}

    $.fn.iSaveForm = function (options,callback) {

        // Make this plugin extensible.
        var defaults = {
            form: this.parents('form').get(0),
            format: 'old',
            object: this,
            preSubmit: function(){return true;},
            message: $.iMessage['Saving']+'...',
            onComplete: function (result) {
                if (result === "ok") {
                    if(typeof callback === "function") {                        
                        callback();
                    }
                    $.iUnblockUI($.iMessage['Done']);
                } else {
                    $.iUnblockUI();
                    $.fn.iDialog(result);
                      
                }
            }
        };
        
        var settings = $.extend(defaults, options);
		
		// Ensure Enter will submit this form unless a textarea
		// is currently selected
//		$(settings.form).keydown(function(event){
//			if(event.which == 13) {
//				var selectedtextbox = settings.object.find('textarea:selected');
//				if(selectedtextbox.length > 0)  
//					return;
//				event.preventDefault();
//				settings.object.click();
//			}
//		});

        // set the url for the post action
        if(!settings.actionUrl){
            settings.actionUrl = $(settings.form).attr('action');
        }

        // In the case of an 'onComplete' function, I will un-block the UI automatically.
        // This isn't something the user should have to do.'
        if (options && options.onComplete) {
            settings.onComplete = function (results) {
                $.iUnblockUI($.iMessage['Done']);
                options.onComplete(results);
            }
        }
        

        // Bind the click function
        // Run the preSubmit function first.  If it returns false
        // then do not continue with the ajax
        settings.object.click(function () {
			if(settings.preSubmit() === false)
				return true;
            $.iBlockUI(settings.message);
            if(settings.format === 'iajax'){
				if(settings.data === undefined)
					settings.data = {};
				settings.data.iSaveFormData = $(settings.form).serialize();
				settings.url = settings.actionUrl,
				$.fn.iAjax(settings);
			}
			else{
				$.post(settings.actionUrl, $(settings.form).serialize(),settings.onComplete);
			}
        });
        
        return true;
    };
    
    ///////////////////////////////////////////////////
    //
    //  Simple Box
    //
    /////////////////////////////
    /**
     * Makes a very simple white box witch a close x button
     * 
     * @param options a map of the following:
     *  - html string the html to put in the box
     */
    $.fn.iSimpleBox = function (options){
		
		// Add content Wrapper
		$(this).wrap('<div class="content" />');
		var content = $(this).parent('.content');

		// Add iSimpleBox Wrapper
		content.wrap('<div class="iSimpleBox" />');
		var root = content.parent('.iSimpleBox');
		
		// Permastore elements 
		var sb = {
			Root: root,
			content: content
		};
		
		// Add Header Wrapper
		sb.Root.prepend('<img class="iSimpleBox_CloseButton" src="images/icons/onstage_iconset/Blue-Close.png" style="width: 2.2em; float: right; cursor: pointer;">');
		
		// Add close button event
		sb.Root.find('.iSimpleBox_CloseButton').click(function (){
			sb.Root.hide('slow',function (){
				sb.Root.remove();
			});
		});
		
		// Return
		return sb.Root;
	}
    
    
    //////////////////////////////////////////////////
    //
    //  Output User errors
    //
    //////////////////////////////
    /**
     * This will output user errors into a handy clickable list and will
     * primarily be used in $.fn.iAjax to handle returned user errors.
     * @param array errors - an array of erros in the format:
     *  	errors[x]['shortMessage']
     *  	errors[x]['longMessage']
     */
    $.fn.outputUserErrors = function (errors){
		if(errors === undefined || jQuery.isArray(errors) !== true || errors.length === 0)
			return false
		var outputElement = $('<div></div>');
		var output = '<ul>';
		for(var x in errors){
			output += '<li>';
			output += errors[x]['shortMessage'];
			if(errors[x]['longMessage'] !== undefined && errors[x]['longMessage'] !== null){
				output += '<div class="expandedMessage" style="display:none">';
				output += errors[x]['longMessage'];
				output += '<div>';
			}
			output += '</li>';
		}
		output += '</ul>';
		outputElement.append(output);
		outputElement.find('ul').iExpandableList();
		return outputElement;
	}
    
    //////////////////////////////////////////////////
    //
    //  Ajax plugin
    //
    ////////////////////////
    /**
     * This should be favoured over jQuery's $.get function as
     * the latter caches all get requests from the server, potentially
     * leading to problems.
     * Quite why jQuery chooses to do this be default is beyond me...
     * 
     * If this does not receive a flag from the server to say  that
     * the request was successful then it displays the output in a dialog
     * box.  
     * Currently accepted flags include:
     * 	- 'ok'
     * 
     * This accepts an object of vars as input which match those 
     * parameters available for $.ajax.  Additional parameters accepted
     * by iGet are as follows:
     *  - silent: bool if true this disables UIBlocking (default false)
     *  - blockMessage: string the message whe UI Blocking (default 'Saving')
     *  - handleIAjaxReturn: If another method of ajaxxing has occurred,
     *     for example using the filedrop() function, this will decode
     *     the resulting output and return
     *  - combineResponses: pass a map of responses to combine for an
     * 		overall result
     *  - 
     */
    $.fn.iAjax = function(options){
		
		// Set some default options
		var defaults = {
			ajaxCallId: '',
			type: 'GET',
			silent: false,
			blockMessage: $.iMessage['Saving']+'...',
			cache: false,
			jFrameFileUpload: false,
			handleIAjaxReturn: false,
			displayDialogFromResponses: false,
			combineResponses: false
		};
		var settings = $.extend(defaults, options);
		
		if(settings.success === undefined)
			settings.success = function () {};
		
		settings.userSuccess = settings.success;
				
		/**
		 * Method to check an ajax return for success
		 */
		var checkReturnForSuccess = function (data){
			// Make sure the data is in a JSON object
			try{
				if(data === undefined || data === '' || data === 'ok' || data === null)
					data = {result: 'success'};
				else
					data = jQuery.parseJSON(data);
			}
			catch(err){
				data = {result: 'failure', outputBuffer: data};
			}
			return data;
		}
		
		/**
		 * Combine multiple ajax returns (given from checkReturnForSuccess)
		 * into a single response
		 */
		var combineResponses = function(responses){
			var returnVar = { result: null,
							  outputBuffer: null,
							  userErrors: {}
			};
			for (var x in responses){
				// Calculate result
				if(responses[x].result === 'failure')
					returnVar.result = 'failure';
				else if(returnVar.result !== 'failure')
					returnVar.result = 'success';
				returnVar.outputBuffer
				
				// combine user errors
				$.extend(returnVar.userErrors, responses[x].userErrors);
				
				// combine additional buffered output
				returnVar.outputBuffer += responses[x].outputBuffer
			}
			
			return returnVar;
		}
		
		/**
		 * display a dialog from an input of a response
		 */
		var displayDialog = function(data){
			var displayDialog = false;
			var dialogOutput = $('<div></div>');
			var success = false;
			
			// ensure return data is set
			if(data.data === undefined)
				data.data = {};
			
			// check if the result was successful
			if(data['result'] === 'success'){
				settings.userSuccess(data.data);
				success = true;
				dialogOutput.append('<h4><img src="images/icons/onstage_iconset/Symbol-Check.png" style="height: 2em; margin-bottom: -0.55em">'+$.iMessage['AJAX_SUCCESSFUL']+'<h4/>');
			}
			else{
				success = false;
				displayDialog = true;
				dialogOutput.append('<h4><img src="images/icons/openclipart/warning_button.png" style="height: 2em; margin-bottom: -0.55em">'+$.iMessage['AJAX_FAILURE']+'<h4/>');
			}
			
			// Add any output
			if(data['outputBuffer'] !== undefined && $.trim(data['outputBuffer']) !== ''){
				displayDialog = true
				//dialogOutput.append('<h4>'+$.iMessage['ADDITIONAL_OUTPUT']+':<h4/>');
				dialogOutput.append(data['outputBuffer']);
				// Check for iExpandableLists and apply functioanlity
				dialogOutput.find('ul').iExpandableList();
			}
			
			// Check for user Errors
			var errs = $.fn.outputUserErrors(data['userErrors']);
			if(errs !== false){
				displayDialog = true;
				dialogOutput.append(errs);
			}
			
			// Make Dialog
			if(displayDialog === true){
				var title = undefined;
				if(success === true)
					title = $.iMessage['SUCCESS'] + '!';
				$.fn.iDialog({
					title: title,
					element: dialogOutput
				});
			}			
		}
		
		/**
		 *  Define the success function
		 */
		settings.success = function (data){
			// Check whether this is a success or failure
			data = checkReturnForSuccess(data);
			displayDialog(data);

			// Unblock the UI
			if(settings.silent === false)
				$.iUnblockUI($.iMessage['Done']);
		};
		
		/**
		 * handle alternative uses
		 */
		// If handleIAjaxReturn is set
		if(settings.handleIAjaxReturn !== false)
			return checkReturnForSuccess(settings.handleIAjaxReturn);
		// If combineResponses is set
		if(settings.combineResponses !== false)
			return combineResponses(settings.combineResponses);
		// If combineResponses is set
		if(settings.displayDialogFromResponses !== false)
			return displayDialog(settings.displayDialogFromResponses);
		
		/**
		 * Tidy up and make request
		 */
		
		// Mark this as an ajax request
		if(settings.data === undefined)
			settings.data = {};
		settings.data['isIngotAjaxRequest'] = true;
		
		// Block the UI
		if(settings.silent === false)
			$.iBlockUI(settings.blockMessage);
		
		// Make the request
		if(settings.jFrameFileUpload === true)
			$.ajaxFileUpload(settings);
		else
			$.ajax(settings);
	};
    
    
    /////////////////////////////////////////////////
    //
    //  SEARCH FORM PLUGIN
    //
    /////////////////////////
	
    $.fn.iSearchForm = function (options) {
        var defaults = {
            form: this.parents('form').get(0),
            object: this,
            message: $.iMessage['Searching'] + "...",
            onComplete: function (result) {
                $(options.targetElement).html(result);
            }
        };
        var settings = $.extend(defaults, options);
        $.fn.iSaveForm(settings);

    };
    
    //////////////////////////////////////////////////
    //
    // EXPANDABLE MESSAGE LIST
    //
    /////////////////////////////
    
    /**
     * Apply this to a list and any div with the class "expandedMessage"
     * Will be toggled on and off when its parent 'li' element is
     * clicked.
     */
    $.fn.iExpandableList = function (){
		var el = {
			Root: this,
			emClass: "expandedMessage",
			animation: {
				duration: 400
			}
		};
		
		var liElements = el.Root.find('li');
		var emElements = liElements.find('.'+el.emClass);
		
		// Style the expanded messages
		emElements.css('padding-left','3%');
		emElements.css('padding-right','3%');
		emElements.css('padding-top','1%');
		emElements.css('padding-bottom','2%');
		
		// Add click function
		liElements.click(function(){
			var cell = $(this);
			var em = cell.find("div."+el.emClass);
			if(em.css('display') === 'none')
				em.show(el.animation.duration);
			else
				em.hide(el.animation.duration);
		});
		
		// Show li text as clickable
		liElements.css("cursor", "pointer");
		// And em as not (even though it is)
		emElements.css("cursor", "default");
	}
    
    /////////////////////////////////////////////////////
    //
    //	Floating Box
    //
    ///////////
    
    $.fn.iFloatingBox = function (options){
		/**
		 * Set up basic closures
		 */
		var fb = {
			Root: this
		};
		fb.HTML = {};
		fb.Action = {};
		
		/**
		 * Set Options
		 */
		fb.Options = {
			onClick: function () {}
		};
		fb.Options = $.extend(fb.Options, options);

		/**
		 * Initialise the HTML/CSS
		 */
		 
		fb.HTML.Init = function () {
			fb.Root.addClass('iFloatingBox');
			fb.Root.addClass('unselectable');
		};
		
		/**
		 * Set all Click Actions
		 */
		fb.Action.Click = function () {
			fb.Root.click(fb.Options.onClick);
		}
		
		/**
		 * Begin
		 */
		fb.HTML.Init();
		fb.Action.Click();
	};
	
	/////////////////////////////////////////////////////
	//
	// Load a help box
	//
	////////////////////
	$.fn.iHelpBox = function () {
		var helpFrame = $("<div><iframe frameborder='0' style='height:100%; width: 100%;' src='data/TLM-Survival-Guide-9.pdf'></iframe></div>");
		$.fn.iDialog({
			title: 'TLM Markbook Survival Guide',
			element: helpFrame,
			width: $(window).width() * 0.8,
			height: $(window).height() * 0.8
		});
	};
	
	/////////////////////////////////////////////////////////
	//
	//   iBrowser browser detection
	//
	///////////////////////////////////////////
	
	/**
	 * See http://api.jquery.com/jQuery.browser/
	 * And http://api.jquery.com/jQuery.support/
	 * 
	 * jQuery.browser may be removed in a future version
	 * so this plugin will act as a way for us to detect
	 * browser versions.
	 * 
	 * If $.browser support is removed then we should try
	 * spoofing such things by using .support
	 * 
	 * @param string broserName the browser name, currently
	 * only 'msie' is available
	 * @param mixed version either an int or an array of ints
	 * for the versions to check for.
	 * @return true if any of these browser versions are currently
	 * detected
	 */
	$.fn.iBrowser = function(browserName, version){
		// Sanity checks
		if(browserName === undefined)
			return null;
		if(typeof browserName !== 'string')
			return null;
		if(version !== undefined){
			if($.isArray(version) !== true)
				version = [version];
			for(var x in version){
				if(typeof version[x] !== 'number')
					return null;
			}
		}
		// if msie
		if(browserName === 'msie'){
			if($.browser.msie !== true)
				return false;
			if(version === undefined)
				return true;
			for(var x in version){
				if($.browser.version.substr(0,1) == version[x])
					return true;
			}
			return false;
		}
		// if browser not handled then return null
		return null;
	}
	
	/////////////////////////////////////////////////////////
	//
	// iScroller to scroll elements which  hize horizontally
	//
	///////////////////////////

		/**
		 * Implements a natural scroll to elements so if they
		 * overflow then moving the mouse will always direct
		 * the scroll position neatly.
		 *
		 * This was originally designed to work with selectable columns
		 * so may need some tweaking to use it with other elements
		 *
		 * Must be called on the Container div.  The Container's parent 
		 * is used as the "max-width" element. So ideally a structure
		 * like this:
		 * <div class='parent'>
		 *   <div class='container'>
		 *      ... content
		 *   </div>
		 * </div>
		 */
		$.fn.iScroller = function(initData){
			var sc = {
				Root: this
			};
			sc.Parent = sc.Root.parent();
			sc.options = {
				left: true,
				right: true,
				up: true,
				down: true,
				bufferZoneLeft: 0.2,
				bufferZoneRight: 0.6
			};
			sc.options = $.extend(sc.options, initData);
			
			/**
			 * initialise the function
			 */
			var init = function(){
				sc.Parent.mousemove(mouseMoved);
				return true;	
			}
			
			/**
			 * Check if this element has overflowed
			 * @return bool true or false
			 */
			var checkIfXOverflow = function(){
				var parentWidth = sc.Parent.width();
				var childrenWidth = 0;
				sc.Root.contents().each(function(){
					childrenWidth += $(this).width();
				});
				if(childrenWidth > parentWidth)
					return true;
				return false;
			}
			
			/**
			 * Scroll the element on a mouse move
			 * If the element has not overlfowed then this
			 * will simply return false
			 * The mouse event is set in init()
			 */		
			var mouseMoved = function(event){
				if(checkIfXOverflow() === false)
					return false;
				
				// Get vars	
				var parentOffsetLeft = sc.Parent.offset().left;
				var parentWidth = sc.Parent.width();
				var mousePositionMinusParentOffset = event.pageX - parentOffsetLeft;
				var bufferZoneLeftPx = parentWidth * sc.options.bufferZoneLeft;
				var bufferZoneRightPx = parentWidth * sc.options.bufferZoneRight;

				// Calculate mouse position
				var mousePositionAsPercentageOfParent = (mousePositionMinusParentOffset - bufferZoneLeftPx) / (parentWidth - (bufferZoneRightPx));
				if(mousePositionAsPercentageOfParent < 0)
					mousePositionAsPercentageOfParent = 0;
				else if(mousePositionAsPercentageOfParent > 1)
					mousePositionAsPercentageOfParent = 1;

				// Set new scroll position
				var scrollWidth = sc.Parent.prop('scrollWidth') - parentWidth;
				// alert(scrollWidth);
				var newScrollPosition = scrollWidth * mousePositionAsPercentageOfParent;		
				// alert(newScrollPosition);
				sc.Parent.scrollLeft(newScrollPosition);
				return true;
			}			
			init();
		}
		
		
	//////////////////////////////////////////////////////
	//
	// SELECTABLE COLUMNS
	//
	///////////////////
			
		/**
		 * Make a series of selectable colums which load
		 * via ajax to navigate complex data choices
		 */
		$.fn.iSelectableColumns = function(initData) {			
			var co = {
				Root: this,
				initData: initData,
				container: null,
				columnData: [],
				isAjaxing: false,
				bufferedEvent: null
			};
			co.animation = {
				isSliding: false,
				effectSlideTime: 300,
				flashPercentage: 1
			};
			co.elementMarkers = {
				selectableColumnId: 'selectable_',
				containerClass: '.selectableContainer',
				columnDivClass: '.selectableColumn',
				idAppendedString: 'SelectableColumnId_'
			};
			co.misc = {
				ignoreOnClickValue: '#',
				columnIdSplit: '_'
			};
			
			/**
			 * Instantiate the Helper Closure
			 */
			co.Helper = {};
			
			/**
			 * Get the total width of all columns
			 * @return int the total width
			 */
			co.Helper.GetTotalWidthOfColumns = function(){
				var totalSize = 0;
				for(var x in co.columnData){
					totalSize += co.Action.getColumn(parseInt(x)).width();
				}
				return totalSize;
			}
			
			/**
			 * Instantiate the action closure
			 */
			co.Action = {};
			
			/**
			 * Init
			 */
			co.Action.Init = function(){
				co.Action.setUpDiv();
				co.Action.addColumnData(co.initData.columns);
				co.Action.addColumn();
				co.container.iScroller({up:false, down:false});
			}
			/**
			 * Set up the container div
			 */
			co.Action.setUpDiv = function(){
				co.Root.addClass('selectableRoot');
				co.Root.html("	<div class='selectableContainer'></div><br style='clear:both;'/>");
				co.container = co.Root.find('.selectableContainer');
			}
			/**
			 * add the column data
			 */
			co.Action.addColumnData = function(params){
				var newColumns = Array();
				if(params.constructor != Array)
					newColumns[0] = params;
				else
					newColumns = params;
				for (var c in newColumns){
					var newlength;
					if (co.columnData.length == 0)
						newlength = 1;
					else
						newlength = co.columnData.length;
					co.columnData[newlength] = newColumns[c];
				}
			}
			/**
			 * Get the value for the selected
			 */
			co.Action.getSelectedValueFromColumn = function(columnNo){
				var val;
				var columnElement = co.Action.getColumn(columnNo);
				columnElement.find(".ui-selected").each(function() {
						val = $( this ).attr( 'id' );
				});
				if(val.indexOf(co.elementMarkers.idAppendedString) !== 0){
					throw new ingotError("co.Action.getSelectedValueFromColumn() - value field seems to be in wrong format");
					return false;
				}
				return val.replace(co.elementMarkers.idAppendedString, "");
			}
			/**
			 * Get the index for the selected
			 */
			co.Action.getSelectedIndexFromColumn = function(columnNo){
				var val;
				var columnElement = co.Action.getColumn(columnNo);
				columnElement.find(".ui-selected").each(function() {
						val = columnElement.find('li').index(this);
				});
				return val;
			}
			/**
			 *  Get data for a new column via Ajax and add it to the data array
			 *  @return array an array of the data
			 */
			co.Action.loadColumnDataFromAjax = function(triggerColumnNo){
				var selectedIndex = co.Action.getSelectedIndexFromColumn(triggerColumnNo);
				var onClick = co.columnData[triggerColumnNo].items[selectedIndex].onClick;
				if(onClick == null)
					return false
				var data = {selectableColumnsData: true}
				for(var x in co.columnData)
					data[co.columnData[x].id] = co.Action.getSelectedValueFromColumn(Number(x));

				co.isAjaxing = true;
                $.fn.iAjax({
					type: 'POST',
					url: onClick,
					data: data,
					silent: true,
					success: function(returnedData) {	
								co.isAjaxing = false; 
								if(co.Action.RunBufferedEvent() == true)
									return false;
								co.Action.addColumnData(returnedData);
								co.Action.addColumn();
								return true;
							}
				});
			}
			/**
			 * Add a column
			 */
			co.Action.addColumn = function(){
				var newData = '';
				var title = '';
				var currentCol = co.Action.getNumberOfCols();
				var numOfCols = currentCol + 1;
				// if there are no further columns set in this instance
				// then try to ajax.  
				if(!(numOfCols in co.columnData)){
					co.Action.loadColumnDataFromAjax(currentCol);
					return false;
				}

				// Set the title
				if('title' in co.columnData[numOfCols])
					title = co.columnData[numOfCols].title;
				// Set the Id
				if('id' in co.columnData[numOfCols] && co.columnData[numOfCols].id != null)
					var columnIdValue = co.columnData[numOfCols].id;
				else
					var columnIdValue = title;
				var columnId = co.elementMarkers.selectableColumnId+numOfCols+co.misc.columnIdSplit+columnIdValue;
				// Generate all li items for this column
				for (var x in co.columnData[numOfCols].items){
					// Set ID as passed ID if exists, else set as name
					var itemId;
					if(co.columnData[numOfCols].items[x].id != null)
						itemId = co.columnData[numOfCols].items[x].id;
					else
						itemId = co.columnData[numOfCols].items[x].name;
					// Generate the li element
                                        //Hide Mockand resit unit exam
                                        if (itemId === "6006688X - TLM L2 Open Systems in Enterprise - Mock Exam") continue;
                                        if (itemId === "6006688X - TLM L2 Open Systems in Enterprise - Resit Exam") continue;
                                            
					newData += "<li class='ui-widget-content' id='" + co.elementMarkers.idAppendedString + itemId + "'>" + co.columnData[numOfCols].items[x].name + "</li>";
				}
				// Build the column
				var newstuff = "<div id='"+columnId+"' class='selectableColumn' style=''>";
				newstuff += "<div class='selectableColumnHeader'>"+title+"</div>";
				newstuff += "<ol class='selectable' style='width:100%;'>";
				newstuff += newData;
				newstuff += "</ol>";
				newstuff += "</div>";
				co.container.append(newstuff);
				var newCol = co.Action.getColumn(currentCol+1);
				
				// IE6-only fix to ensure element widths are standardised
				if ($.fn.iBrowser('msie',6)){
					var setElementWidth;
					var elements = newCol.find('li');
					var width = 0;
					elements.each(function(){
						var thiswidth = $(this).width();
						if(thiswidth > width)
							width = thiswidth;
					});
					elements.css("width",width+"px");
				}
				
				//Begin animations
				if(currentCol > 0){
					co.animation.isSliding = true;
					newCol.effect('slide',{},co.animation.effectSlideTime,function(){
						co.animation.isSliding = false;
						if(co.Action.RunBufferedEvent() == true)
							return false;
						co.Action.FlashSelections(newCol);
					});
				}
				else{
					co.Action.FlashSelections(newCol);
				}
				
				// Bind it to the triggers
				co.Action.bindSelectableColumn({column: co.container.find( ".selectable" )});
			}
			/**
			 * Flash selections
			 */
			co.Action.FlashSelections = function (column){
				if(typeof(column)=='number')
					column = co.Action.getColumn(column);
				var elements = column.find('li');
				var animationTime = 90;
				
				var header = column.find(".selectableColumnHeader");
				var headerWidth = header.width();
				header.css("width",headerWidth+"px");

				var width = elements.width();
				var addedWidthLength = co.Root.width() * (co.animation.flashPercentage / 100);
				// Return false if the width of the flashing will exceed the container width
				if(co.Helper.GetTotalWidthOfColumns() + addedWidthLength >= co.Root.width())
					return false;
				// Proceed with animation
				var newWidth = width + addedWidthLength;
				elements.each(function(){
					var eq = $(this).index()+1;
					var beginTime = animationTime * eq;
					$(this).css("width",width+"px");
					$(this).delay(beginTime).animate({ width: newWidth+"px" }, animationTime, function () {
						$(this).animate({ width: width+"px" }, animationTime);
					});
				});
			}
			/**
			 * Get the number of cols
			 */
			co.Action.getNumberOfCols = function (){
				return co.container.find(co.elementMarkers.columnDivClass).size();
			}
			/**
			 * Get a column
			 */
			co.Action.getColumn = function (element){
				if((typeof(element)=='number') && (element.toString().indexOf('.')==-1))
					return co.container.find("div[id^='"+co.elementMarkers.selectableColumnId+element+co.misc.columnIdSplit+"']");
				else
					return element.parents(co.elementMarkers.columnDivClass);
			}
			/**
			 * Get the current column number
			 */
			co.Action.getCurrentColNum = function (element){
				var splits = element.parents(co.elementMarkers.columnDivClass).attr('id').split(co.misc.columnIdSplit);
				return splits[1];
			}
			/**
			 * Remove all columns after x
			 */
			co.Action.removeColumnsAfter = function(currentColNum){
				var position = co.Action.getNumberOfCols();
				var skippedDelete = false;
				while(position in co.columnData){
					if(position == currentColNum)
						break;
					var thisColumn = co.Action.getColumn(position);
					if(co.columnData[position].doNotClose == true){
						break;
					}
					thisColumn.remove();
					if(co.columnData[position].doNotDelete == true || skippedDelete == true){
						skippedDelete = true;
						position--;
						continue;
					}
					co.columnData.splice(position,1);
					position--;
				}
			}
			
			/**
			 * Run any buffered events
			 * @return true or false depending on whether an event existed
			 */
			 co.Action.RunBufferedEvent = function (){
				if(co.bufferedEvent === null)
					return false;
				$(co.bufferedEvent.originalEvent.originalEvent.target).click();
				co.bufferedEvent = null;
				return true;
			 }
			
			/**
			 * Set triggers for the selectables
			 */
			co.Action.bindSelectableColumn = function(params) {
				var options = params;
				options.column.selectable({
					start: function(event, ui) {
						// If Ajaxing or animating then queue
						if(co.isAjaxing === true || co.animation.isSliding === true){
							// Buffer the event
							co.bufferedEvent = event;
							// Stop any ongoing animations
							co.Root.find('li').stop(true,true);
							// remove any extra columns
							var currentColNum = co.Action.getCurrentColNum($(this));
							co.Action.removeColumnsAfter(currentColNum);
							// Clear any effects wrappers
							co.Root.find('.ui-effects-wrapper').remove();
							return false;
						}
					},
					stop: function(event, ui) {
						// Stop any ongoing animations
						co.Root.find('li').stop(true,true);
						// Get data
						var column = $(this);
						var currentNoOfCols = co.Action.getNumberOfCols();
						var currentColNum = co.Action.getCurrentColNum(column);
						var selectedElement = $(this).find( ".ui-selected", this );
						var index = column.find( "li" ).index( selectedElement );
						// Return false if no index is found (eg. user clicked between items)
						if(index === -1)
							return false;
						// Remove columns
						if(currentColNum < currentNoOfCols){
							co.Action.removeColumnsAfter(currentColNum);
							currentNoOfCols = co.Action.getNumberOfCols();
						}						
						// Go to marksheet
						if(currentColNum == currentNoOfCols && co.columnData[currentColNum].items[index].addTab != null){
							var tempData = co.columnData[currentColNum].items[index].addTab;
							window[tempData['tabGroup']].addTab(tempData['link'],tempData['title']);
						}
						// Add column
						else if(currentColNum == currentNoOfCols && co.columnData[currentColNum].items[index].onClick != co.misc.ignoreOnClickValue){
							co.Action.addColumn();
						}
					}
				});
			}
			co.Action.Init();
		}

}(jQuery));
//////////////////////////////////////////////////////
//
// Get an expandable Table for use in a form
//
///////////////////
/**
 * This will produce a table containing form elements
 * which allows for additional rows to be added
 * 
 * NOTE: Currently this is horribly tied into the MODBAC due to time
 * constriants pushing quick-and-dirty programming.  So unusuable for
 * any other purposes without heavy development time.
 * 
 * @param object options a map of all options
 *   - heading: The heading to display for the table
 *   - id: the dom id to identify the table
 *   - initRows: how many rows to begin with
 *   - addRows: true or false to enable or disable the add button
 *   - structure: a map of the structure
 *   - maxRows: a maximum number of rows
 *   - data: a map of any initial data to input
 *   - noOfRows: internal storage of the number of rows currently set (do not change)
 *   - onDeleteRow: a function to be called after a delete row event has occured
 */
(function ($) {
	$.fn.iFormTable = function (options){

		// set some defaults
		var defaults = {
			heading: '',
			id: '',
			initRows: 1,
			addRows: true,
			structure: {},
			maxRows: null,
			data: {},
			noOfRows: 0,
			bind: {},
			onDeleteRow: function () { return true; }
		};
		// combine to get the settings
		var settings = $.extend(defaults, options);

		/**
		 * Add a row
		 */
		var addInputRow = function (element, awData, animate){
			// Do not add if max rows is reached
			if(settings.maxRows !== null && settings.noOfRows == settings.maxRows)
				return false;
			var headerCells = $(element).find('th');
			var noOfRows = $(element).find('tbody tr').size() - 2;
			var tablename = $(element).attr('settings.id');
			var newRow = $('<tr></tr>');
			var i = 0;
			// Make each cell
			headerCells.each(function(){
				var sid = $(this).data('id');
				var abbr = $(this).attr('abbr');
				var cellData = '';
				if(!$.isArray(awData) && !$.isPlainObject(awData))
					cellData = awData;
				else if(awData !== undefined && awData[abbr] !== undefined)
					cellData = awData[abbr];
				// Note: The exta <div> is needed to enable animation as animating table elements directly can break html
				var newInput = $('<td><div style="white-space: nowrap;"><input type="text" name="'+tablename+'_'+abbr+'_'+(noOfRows + 1)+'" value="'+cellData+'"/></div></td>');
				// Set any global bindings 
				for(var x in settings.bind)
					newInput.bind(x,settings.bind[x]);
				// Add any column bindings
				if(settings.structure[sid] !== undefined && settings.structure[sid].bind !== undefined){
					for(var x in settings.structure[sid].bind)
					newInput.bind(x, settings.structure[sid].bind[x]);
				}
				// Append the cell
				newRow.append(newInput);
				i++;
			});
			// Add delete button if add rows option is set
			if(settings.addRows !== false){
				newRow.find('div:last').append('<a href="#"><img class="plusicon" style="height: 1em;" src="images/icons/onstage_iconset/Symbol-Delete.png"></a>');
				// When you click on the delete button, do this
				newRow.find('a').click(function (){
					// Ask a confirmer
					$.fn.iConfirm($.iMessage['CONFIRM_DELETE_ROW'], function () {
						var currentRow = newRow.closest('tr');
						var currentEq = currentRow.index();
						newRow.closest('table').find('tr').each(function (){
							var thisEq = $(this).index();
							if(thisEq > currentEq){
								$(this).find('input').each(function (){
									var currentName = $(this).attr('name');
									var split = currentName.split("_");
									var rowNumber = split.pop();
									rowNumber = Number(rowNumber);
									rowNumber--;
									if(isNaN(rowNumber))
										throw new ingotError('rowNumber must be an integer, but is currently: ' + rowNumber);
									split.push(rowNumber);
									$(this).attr('name', split.join("_"));
								});
							}
						});
						currentRow.remove();
						settings.onDeleteRow(--currentEq);
					});
				});
			}
			// Animate the row dropdown if animate is set
			newRow.find('td div').hide();
			element.find('tbody').append(newRow);
			if(animate === true)
				newRow.find('td div').slideDown();
			else
				newRow.find('td div').show();
			settings.noOfRows++;
		};

		/**
		 * init
		 */
		var buildTable = function () {					
			var returnme = $('<div><h3>'+settings.heading+'</h3></div>');				

			var table = $('<table settings.id="'+settings.id+'"></table>');	
			// Add addable button
			if(settings.addRows !== false){
				returnme.find('h3').append('<a href="#"><img class="plusicon" style="height: 1em;" src="images/icons/onstage_iconset/Symbol%20-%20Add.png"></a>');
				// Click functionality
				returnme.find('a').click(function (){
					addInputRow(table,undefined,true);
					// return false to prevent bubbling and therefore scrolling the page
					return false;
				});
			}

			// Create Body
			var tbody = $('<tbody></tbody>');
			var headers = $('<tr></tr>');
			for(var x in settings.structure){
				var newHeaders = $('<th abbr="'+settings.structure[x].abbr+'" >'+settings.structure[x].title+'</th>').data('id', x);
				headers.append(newHeaders);
			}
			tbody.append(headers);
			table.append(tbody);
			// Decide how many rows are needed
			var totRows = settings.initRows;
			if($.isArray(settings.data) && totRows < settings.data.length)
				totRows = settings.data.length;

			// Add rows
			// Include data if there is any for this learner
			var input;
			for (var i=0; i<totRows; i++){
				if($.isArray(settings.data)){
					if(settings.data[i] !== undefined)
						input = settings.data[i];
					else
						input = '';
				}
				else{
					input = settings.data;
				}
				addInputRow(table, input);
			}

			// Build
			returnme.append(table);

			// Return
			return returnme;
		};

		return buildTable();
	};
}(jQuery));/*
 * Drag and drop files for upload
 */

(function ($) {
	
	/**
	* Drag and drop files plugin
	*/
	$.fn.iMoreInfoToggle = function(options, parameter){
		return $.fn.iPlugin.handleObjectAttachedToElement(this, '_iMoreInfoToggleObject', options, parameter);
	}
			
	/**
	 * Drag and Drop files object
	 * 
	 * options:
	 *  - HideByDefault bool whether to hide the object when not active
	 * 
	 */
	ingotClass._iMoreInfoToggleObject = function(options, command, parameter){
		//If this is a command then handle
		if(options === 'command'){					
			return func.Action.PassedCommand(command, parameter);
		}

		// Set up basic closures
		var cl = {
			Root: options.Root
		}

		// Set default options and merge with options
		cl.DefaultOptions = {
			toggleButtonHoverAnimateTime: 200,
			loadTarget: null
		}
		cl.Options = $.extend(cl.DefaultOptions, options);

		// Set internal Params
		cl.Params = {
			TargetLoaded: false
		}

		// Set Elements
		cl.Elements = {
			HideableElement: null,
			ToggleButton: null,
			Container: null
		}
		cl.ElementLabels = {
			HideableElement: { 
				Search: '#moreinfotarget',
				Label: 'moreinfotarget'
			},
			ToggleButton: {
				Search: '#iMoreInfoToggleButton',
				Label: 'iMoreInfoToggleButton'
			}
		}
		
		/**
		 * Create function closures
		 */
		var func = {};
		func.Action = {};
		
		/**
		 * Initialise the object
		 */
		func.Action.Init = function(){
			// Ensure root element has positioning style;
			if(cl.Root.css('position').toLowerCase() === 'static')
				cl.Root.css('position', 'relative');
			cl.Elements.Container = cl.Root;
			cl.Elements.HideableElement = cl.Root.find(cl.ElementLabels.HideableElement.Search);
			cl.Elements.HideableElement.hide();
			func.Action.CreateToggleButton();
		}
		
		/**
		 * Create the toggle button
		 */
		func.Action.CreateToggleButton = function(){
			var toggleableButton = $("<div />");
			toggleableButton.addClass(cl.ElementLabels.ToggleButton.Label);
			toggleableButton.addClass('unselectable');
			var parentBorderCss = cl.Root.css('border'); // Get parent border css
			toggleableButton.css('border', parentBorderCss); // Set border css to parent border css
			cl.Elements.ToggleButton = toggleableButton;
			func.Action.SetToggleButtonTextMore();
			cl.Elements.Container.append(toggleableButton);
			func.Action.BindToggleHover();
			func.Action.BindToggleClick();
		}
		
		/**
		 * Set the Toggle Button Text
		 */
		func.Action.SetToggleButtonText = function (text){
			cl.Elements.ToggleButton.html(text);
		}
		/**
		 * Set the Toggle Button Text to More Info
		 */
		func.Action.SetToggleButtonTextMore = function (){
			func.Action.SetToggleButtonText('More Info');
		}
		/**
		 * Set the Toggle Button Text to Less Info
		 */
		func.Action.SetToggleButtonTextLess = function (text){
			func.Action.SetToggleButtonText('Less Info');
		}
		
		/**
		 * Toggle the element
		 */
		func.Action.HideableElementToggle = function (){
			if(!cl.Elements.HideableElement.is(":visible")){
				func.Action.LoadTarget(function(){
					cl.Elements.HideableElement.slideDown();
				});
			}
			else{
				cl.Elements.HideableElement.slideUp();
			}
		}
		
		/**
		 * Loads a target if one has been set
		 */
		 func.Action.LoadTarget = function(callback){
			if(cl.Options.loadTarget == null || cl.Options.loadTarget == ''){
				return callback();
			}
			else if(cl.Params.TargetLoaded === true){
				return callback();
			}
			else{
				$.fn.iAjax({
					silent: true,
					url: cl.Options.loadTarget,
					success: function(data){
							cl.Params.TargetLoaded = true;
							cl.Elements.HideableElement.html(data.returnedHtml);
							callback();
						}
				});
			}
		 }
		
		/**
		 * Bind a click function to the Toggle
		 */
		func.Action.BindToggleClick = function (){
			cl.Elements.ToggleButton.click(function (){
				func.Action.HideableElementToggle();
				func.Action.AnimateHoverOutToggleButton();
			});
		}

		/**
		 * Bind a hover function to the Toggle
		 */
		func.Action.BindToggleHover = function (){
			cl.Elements.ToggleButton.hover(
				func.Action.AnimateHoverOverToggleButton,
				func.Action.AnimateHoverOutToggleButton
			);
		}
		
		/**
		 * If a target has been passed to load then load via
		 * Ajax and then call callback, else just call
		
		/**
		 * Animate Toggle Button Hover-over
		 */
		func.Action.AnimateHoverOverToggleButton = function(){
				cl.Elements.ToggleButton.addClass('iMoreInfoToggleButtonHover', cl.Options.toggleButtonHoverAnimateTime);
		}
		/**
		 * Animate Toggle Button Hover-out
		 */
		func.Action.AnimateHoverOutToggleButton = function(){
			cl.Elements.ToggleButton.removeClass('iMoreInfoToggleButtonHover', cl.Options.toggleButtonHoverAnimateTime);
		}
		
		/** Initialise **/
		func.Action.Init();
	}
}(jQuery));
/*
 * Search academy names, input the id into a hidden field for a form
 */

(function ($) {
	
	/*
	* Search for an academy name via ajax
	*/
	$.fn.iSearchAcademyNames = function(options, parameter){
		return $.fn.iPlugin.handleObjectAttachedToElement(this, '_iSearchAcademyNamesObject', options, parameter);
	}
			
	/*
	 * Search academy names via ajax object
	 * 
	 * options:
	 * 
	 */
	ingotClass._iSearchAcademyNamesObject = function(options, command, parameter){
		//If this is a command then handle
		if(options === 'command'){					
			return func.Action.PassedCommand(command, parameter);
		}
		
		cl = {
			Root: options.Root
		};
				
		cl.IdNames = {
			inputId: 'academySearchInput',
			hiddenId: 'academySearchHidden'
		};
		cl.Elements = {
			inputField: null,
			hiddenField: null
		};
		
		/**
		 * Initialise
		 * 
		 */
		var init = function(){
			// Create the basic elements			
			createElements();
			
			// Initialise plugin on element
			cl.Elements.inputField.autocomplete({
				source: ajaxFunction,
				select: select,
				focus: focus,
				delay: 100
			});
		}
		
		/**
		 * Set the inital elements on the page
		 * This is called by init()
		 * 
		 */
		var createElements = function (){
			cl.Root.append('<input id="'+cl.IdNames.inputId+'"></input>');
			cl.Root.append('<input type="hidden" id="'+cl.IdNames.hiddenId+'"></span>');
			
			cl.Elements.inputField = cl.Root.find('#'+cl.IdNames.inputId);
			cl.Elements.hiddenField = cl.Root.find('#'+cl.IdNames.hiddenId);
		}
		
		/**
		 * Create the ajax call function
		 * 
		 */
		var ajaxFunction = function (request, response) {
			var data = {
				input: request.term
			};
			$.fn.iAjax({
				type: 'POST',
				url: '?r=search/ajaxacademynames',
				data: data,
				silent: true,
				success: function(returnedData) {
					var list = [];
					for (var x in returnedData.academyList){
						list.push(returnedData.academyList[x]);
					}
					response(list);
				}
			});
			
		};
		
		/**
		 * The select functionality
		 * 
		 */
		var	select = function( event, ui ) {
			cl.Elements.inputField.val(ui.item.label);
			cl.Elements.hiddenField.html(ui.item.value);
			return false;
		};
		
		/**
		 * The focus functionality
		 * 
		 */
		var focus = function (event, ui){
			cl.Elements.inputField.val(ui.item.label);
			return false;
		}
		
		// Init        
        init();
	};
	
}(jQuery));
    /*
     * iMarksheet provides the editable marksheet for the site
     */

    (function($) {

      /**
       * Marksheet files plugin
       */
      $.fn.iMarksheet = function(options, parameter) {
        return $.fn.iPlugin.handleObjectAttachedToElement(this, '_iMarksheetObject', options, parameter);
      }

      /**
       * The plugin for the ingots markbook
       */
      ingotClass._iMarksheetObject = function(options) {
        // Core varaibles.
        var cl = {
          Root: options.Root,
          SkipCols: 5,
          EditMode: "manual",
          OpenNotes: 0, //Number of open notes rows in the markbook
          UnsavedChanges: false,
          IsScrolling: false, // Mark as true if the window is scrolling
          IsMouseOver: false, // Mark as true if the mouse is over the table
          IsEditingCell: false, // Mark if an ongoing multi-character cell input is being entered
          IsCtrlKeyPressed: false, // Mark if the ctrl key is pressed
          IsShiftKeyPressed: false, // Mark if the shift key is pressed
          MultiSelectedCells: false, // Mark as true if there are multiple cells selected
          SelectedValue: '', // Store the value currently inputted next to the edit mode select
          UniqueId: 'imarkbook-' + Math.floor(Math.random() * 1000),
          CanUserSelectCells: true // set to false if cells should be muted
        };

        // Markbook data.
        cl.Data = {
          head: false,
          body: false,
          crit: false,
          unit: false,
          tgroup: false,
          frameworkID: false,
          frameworkData: false,
          additionalMarkDataFields: null,
          markingScheme: false,
          testGradeThresholds: false,
          test_mark: false,
          suppressHighlightCellXY: false,
          suppressShowCriterion: false,
          validMarks: false,
          suspend_school: 0,
          baseline: 0,
          eds_reject: false,
          dfs_reject: false,
          reg_unit: -1,
          is_reg_unit: false
        };
        cl.Data = $.extend(cl.Data, options);
        cl.Data.unit = parseInt(cl.Data.unit, 10);

        //need to check if test mark column should be added
        //        if(cl.Data.test_mark !== false && cl.Data.unit === 1428){            
        //            cl.Data.head.push($.iMessage['TEST_RESULT']); //add award column to test units
        //
        //        };

        // Set effect options
        cl.EffectOptions = {
          ScrollSpeed: 500
        };

        // Element names
        cl.ElementName = {
          SelectedCell: 'selectedCell',
          ButtonHighlight: 'buttonHighlight', // Highlighting for a button
          ChangedCell: 'changedCell', // Marks the cell as changed and unsaved
          MarkCell: 'markCell', // denotes this cell as storing a mark of some description
          MarkHeader: 'markHeader', // denotes this as a header cell for a column of marks
          MarkRow: 'markRow', // denotes this tr is a row of marks
          NoteRow: 'imarkbook_noterow',
          NoteCell: 'imarkbook_notecell',
          NoteTextArea: 'note_textarea'
        };

        // Class names
        cl.ClassName = {
          HighlightCell: 'highlightCell'
        };

        // various vars for navigation and editing
        cl.Navigation = {
          MultiSelectElements: 'td.editable.criterion', // The elements which can be multiselected
          SingleSelectElements: 'td.' + cl.ElementName.MarkCell // The elements which can be multiselected
        };

        // Useful data for test request plugin
        cl.TestRequests = {
          requestedCellDefault: '',
          madeAvailableCellDefault: ''
        }

        /////////////////////////////////////////////////////
        //
        //    PUPIL STATUS
        //
        ///////////

        cl.Status = function(row) {

          var result, error;

          // Make sure this is a mark row
          if (row.hasClass('markRow') !== true)
            return false;

          // Check for errors
          error = cl.Helper.GetCurrentScheme().Errors(row);
          if (error) {
            row.find('td.imarkbook_status').addClass('imarkbook_error');
            row.find('td.imarkbook_status').html(error.replace(/ /g, '&nbsp;'));
            return false;
          }

          // Make sure this is not frozen
          if (cl.Helper.IsRowFrozen(row)) {
            return false;
          }

          // All good, let's prep
          result = null;
          row.find('td.imarkbook_error').removeClass('imarkbook_error');

          // Get the status
          result = cl.Helper.GetCurrentScheme().Status(row);


          // If no unit has been found
          if (result === null)
            result = "0/0 - " + $.iMessage['Unknown unit'];

          // Finish off
          // IMPORTANT: The first replace can easily wreck any HTML in the status field.
          row.find('td.imarkbook_status').html(
            result.replace(/ /g, '&nbsp;').replace(/<span&nbsp;class/, '<span class')
          );
          return true;
        };

        /////////////////////////////////////////////////////
        //
        //    EFFECTS -- Effects that improve usability.
        //
        //				 SHOW CRITERION, HIGHLIGHT, SORTING
        //
        ///////////

        cl.Effect = {};

        /**
         * Display the criterion for any selected cell
         */
        cl.Effect.ShowCriterion = function(cell) {
          // "this" is a <td> or <th> element.
          var crit, parent, index, desc, type;
          if ($(cell).is('td'))
            type = 'td';
          else if ($(cell).is('th'))
            type = 'th';
          else
            return false;

          parent = $(cell).parent().get(0); // 1. Find parent <tr> element.
          index = $(parent).find(type).index($(cell)); // 2. Find my index inside my parent.

          if (index >= cl.SkipCols) {

            // Update criteria description.
            crit = cl.Data.crit[index - cl.SkipCols];
            cl.Root.find('.imarkbook_description').show();
            if (typeof crit !== 'undefined') {
              desc = crit.type + (crit.count ? ' ' + crit.count : '') + ' - ' + crit.description;
            } else {
              if ($(cell).hasClass('imarkbook_test_markcell')) {
                desc = $.iMessage['TEST_RESULT'];
              } else {
                desc = '';
              }
            }
            cl.Root.find('.imarkbook_description').html('<b>' + $.iMessage['Selected'] + ':</b> ' + desc);
          }
        };
        /**
         * This highlights the x and y column of a cell
         */
        cl.Effect.HighlightCellXY = function(cell) {
          var index, parent, type;
          if (cell.is('td'))
            type = 'td';
          else if (cell.is('th'))
            type = 'th';
          else
            return false;
          parent = $(cell).parent().get(0); // 1. Find parent <tr> element.
          index = $(parent).find(type).index(cell); // 2. Find my index inside my parent.

          // Highlight header and footer.
          cl.Root.find('.imarkbook_table th.' + cl.ClassName.HighlightCell).removeClass(cl.ClassName.HighlightCell)
          cl.Root.find('.imarkbook_table th').eq(index).addClass(cl.ClassName.HighlightCell);

          // Highlight ID column
          $(parent).siblings().find('td:eq(0).' + cl.ClassName.HighlightCell).removeClass(cl.ClassName.HighlightCell);
          $(parent).children("td").eq(0).addClass(cl.ClassName.HighlightCell);
        };
        /**
         * Sorting
         */
        cl.Effect.Sorting = function() {
          var i, sort_headers = {};
          // Only sort the first four columns (Id, Forename, Surname, Status).
          if (cl.Data.head) {
            for (i = 0; i < cl.Data.head.length; i += 1) {
              sort_headers[i + cl.SkipCols] = {
                sorter: false
              };
            }
            cl.Root.find('.imarkbook_table').tablesorter({
              headers: sort_headers
            });
          }
        };

        /////////////////////////////////////////////////////
        //
        //    ACTIONS -- Things that happen many times, based on user action.
        //
        //				 SELECT ALL, EDIT, SAVE, CSV UPLOAD/DOWNLOAD
        //
        ///////////

        cl.Action = {};

        /**
         * Define the actions to perfom when hovering the cursor
         */
        cl.Action.Hover = function() {
          // Track whether the mouse is currently over the table
          cl.Root.find('.imarkbook_table').hover(function() {
            cl.IsMouseOver = true;
          }, function() {
            cl.IsMouseOver = false;
          });
          // Highlight the X and Y co-ordinates
          cl.Root.find('.imarkbook_table tbody td, .imarkbook_table thead th, .imarkbook_table tfoot th').hover(function() {
            if (cl.Data.suppressHighlightCellXY !== true)
              cl.Effect.HighlightCellXY($(this));
            if (cl.Data.suppressShowCriterion !== true)
              cl.Effect.ShowCriterion($(this));
          });

          // Set default actions
          var defaults = {};

          // Get scheme actions and combine with defaults
          var schemeActions = {};
          if (cl.Helper.GetCurrentScheme().Hover !== undefined)
            schemeActions = cl.Helper.GetCurrentScheme().Hover();
          var actions = $.extend(defaults, schemeActions);

          // Set the actions
          for (var x in actions) {
            if (actions[x] !== null)
              cl.Root.find(x).hover(actions[x]);
          }
        };

        /**
         * Define the actions to perfrom on click events
         * 
         * Actions can be modified by adding an optional .Click() method
         * to the marking scheme eg xScheme.MODBAC.Click().
         * 
         * Default settings are:
         *  - 'td.imarkbook_pid'
         *  - 'td.imarkbook_forenamecell'
         *  - 'td.imarkbook_surnamecell'
         *  - 'td.imarkbook_status'
         *  - '.imarkbook_select_all'
         *  - ['.'+cl.ElementName.MarkHeader] (selecting a column header)
         */
        cl.Action.Click = function() {
          // Events to perform when clicking outside the markbook
          $('html').click(function() {
            if (cl.IsMouseOver !== true)
              cl.Action.UnSelectCell();
          });

          // Click a markcell
          cl.Action.ClickCellDefault();

          // Default functions to select row
          var defaultSelectAllToggleColumn = function() {
            cl.Action.SelectAllToggle($(this));
          };
          var defaultSelectAllButton = function() {
            cl.Action.SelectAllToggleButton();
          }

          // Set default actions
          var defaults = {
            //'td.imarkbook_pid': defaultSelectAllToggleRow,
            //'td.imarkbook_forenamecell': defaultSelectAllToggleRow,
            //'td.imarkbook_surnamecell': defaultSelectAllToggleRow,
            //'td.imarkbook_status': defaultSelectAllToggleRow,
            '.imarkbook_select_all': defaultSelectAllButton
          };
          defaults['.' + cl.ElementName.MarkHeader] = defaultSelectAllToggleColumn // When clicking on colum header

          // Get scheme actions and combine with defaults
          var UiActions = {};
          if (cl.Helper.GetCurrentUserInterface().Click !== undefined)
            UiActions = cl.Helper.GetCurrentUserInterface().Click();
          var actions = $.extend(defaults, UiActions);

          // Set the actions
          for (var x in actions) {
            if (actions[x] !== null)
              cl.Root.find(x).click(actions[x]);
          }
        }

        /**
         * Toggle Notes
         */
        cl.Action.ToggleNote = function() {
          cl.Root.find("tr:." + cl.ElementName.NoteRow).each(function() {
            if ($(this).data("note") === undefined) {
              if ($(this).children(':eq(1)').text() !== 'null') {
                $(this).data("note", $(this).children(':eq(1)').text());
              } else {
                $(this).data("note", '');
              }
            }
          })

          cl.Root.find("tr:." + cl.ElementName.NoteRow).hide();

          // Bind the click event
          cl.Root.find("td." + cl.ElementName.NoteCell).click(function() {
            var noterow = $(this).parent().next("tr");
            var pupil_id = $(this).parent().children(':eq(0)').text();

            // Unselect any selected cells to prevent input confusion
            cl.Action.UnSelectCell();

            // Show whether a note exists or not with a *
            var updateNoteCellStatus = function() {
              if ($(noterow).data("note") !== undefined && $(noterow).data("note") !== '' && $(noterow).data("note") !== 'null') {
                $(noterow).prev("tr").find("." + cl.ElementName.NoteCell).html('<center>*</center>');
              } else {
                $(noterow).prev("tr").find("." + cl.ElementName.NoteCell).html('');
              }
            };
            //checking if note textarea has been created for this row
            if ($(this).parent().next("tr").hasClass(cl.ElementName.NoteRow)) { //this row has not been clicked before
              cl.OpenNotes += 1; //increment open notes number
              //read note from first column
              var oldValue = $(noterow).data("note");
              if (oldValue === 'null' || oldValue === undefined) {
                oldValue = '';
              }
              //need to save original value
              //    $(noterow).data("note",oldValue);//no need to attach
              //create textarea for note
              $(noterow).html(
                '<td colspan=40>' +
                '<textarea class="' + cl.ElementName.NoteTextArea + '" rows="5" style="width:100%">' +
                oldValue +
                '</textarea>' +
                '<div>' +
                '<button class="save_button">' +
                'Save note' +
                '</button>' +
                '<button class="cancel_button">' +
                'Undo changes' +
                '</button>' +
                '</div>' +
                '</td>');

              var notetextarea = $(noterow).find('.' + cl.ElementName.NoteTextArea);
              var saveButton = $(noterow).find('.save_button');
              var cancelButton = $(noterow).find('.cancel_button');

              $(saveButton).attr("disabled", "disabled");
              $(cancelButton).attr("disabled", "disabled");
              $(notetextarea).keyup(function() {

                if ($(noterow).data("note") != notetextarea.val()) {

                  $(saveButton).attr("disabled", "");
                  $(cancelButton).attr("disabled", "");
                } else {

                  $(saveButton).attr("disabled", "disabled");
                  $(cancelButton).attr("disabled", "disabled");
                }
              });

              $(saveButton).bind('click', function() {
                if ($(noterow).data("note") !== notetextarea.val()) {
                  $.iBlockUI($.iMessage['Saving'] + '...');
                  $.post("?r=assessor/saveMarkBookNote", {
                      pupilId: pupil_id,
                      unitId: cl.Data.unit,
                      note: $(noterow).find('.' + cl.ElementName.NoteTextArea).val()
                    },
                    function(result) {

                      if (result === "ok") { //successfully saved into database
                        $(noterow).data("note", $(noterow).find('.' + cl.ElementName.NoteTextArea).val()); //updating saved value on client side
                        $.iUnblockUI($.iMessage['Done']);
                        $(saveButton).attr("disabled", "disabled");
                        $(cancelButton).attr("disabled", "disabled");
                        updateNoteCellStatus();

                      } else {
                        $.iUnblockUI();
                        $.fn.iDialog(result);
                      }
                    }
                  );
                }
                updateNoteCellStatus(); //need to update NoteCell status
              });
              $(cancelButton).bind('click', function() {
                if ($(noterow).data("note") != notetextarea.val()) {
                  var undo = confirm("You are going to discard the changes! Press 'OK' to proceed.");
                  if (undo) {
                    notetextarea.val($(noterow).data("note"));
                  }
                }
                updateNoteCellStatus(); //need to update NoteCell status
              });

              $(noterow).removeClass(cl.ElementName.NoteRow);
              $(noterow).addClass(cl.ElementName.NoteTextArea);

              updateNoteCellStatus(); //need to update NoteCell status
              //workaround with IE, otherwise we could use just $(this).next("tr").toggle()
              var elem = $(noterow)[0];
              if (elem.style.display == 'none') $(this).parent().next("tr").show();
              else if (elem.style.display != 'none') $(this).parent().next("tr").hide();

            } else if ($(this).parent().next("tr").hasClass(cl.ElementName.NoteTextArea)) { //the row has been clicked before and textarea for note has already been created
              cl.OpenNotes -= 1;
              //need to check if saved or canceled
              if (!$(this).parent().next("tr").is(':hidden')) {
                if ($(noterow).data("note") != $(noterow).find('.' + cl.ElementName.NoteTextArea).val()) {
                  var save = confirm("The changes have not been saved!. Press 'OK' to save, 'Cancel' to discard it.");
                  var dataToSave = $(noterow).find('.' + cl.ElementName.NoteTextArea).val();
                  if (save) {

                    $.iBlockUI($.iMessage['Saving'] + '...');
                    //alert('pupil_id to save is '+pupil_id);
                    $.post("?r=assessor/saveMarkBookNote", {
                        pupilId: pupil_id,
                        unitId: cl.Data.unit,
                        note: $(noterow).find('.' + cl.ElementName.NoteTextArea).val()
                      },
                      function(result) {
                        if (result === "ok") {
                          //need to update the save value
                          $(noterow).data("note", dataToSave);
                          $.iUnblockUI($.iMessage['Done']);
                          updateNoteCellStatus(); //need to update NoteCell status
                        } else {
                          $.iUnblockUI();
                          $.fn.iDialog(result);
                        }
                      });
                  } else { //user clicked undoChanges, so reverting the changes
                    $(noterow).find('.' + cl.ElementName.NoteTextArea).val($(noterow).data("note"));
                  }
                }
              }

              updateNoteCellStatus(); //need to update NoteCell status
              //$(this).next("tr").toggle();
              var elem1 = $(this).parent().next("tr")[0];
              if (elem1.style.display == 'none') $(this).parent().next("tr").show();
              else if (elem1.style.display != 'none') $(this).parent().next("tr").hide();

              $(this).parent().next("tr").html($(this).parent().html()); //restore previous structure
              //                    $(this).parent().next("tr").children(':eq(0)').text("note row");
              $(this).parent().next("tr").removeClass(cl.ElementName.NoteTextArea);
              $(this).parent().next("tr").addClass(cl.ElementName.NoteRow);

            }
          });
        }

        /**
         * The actions when a cell is clicked on.
         * Binds the edit cell functionality to cells
         * 
         * By default this selects any editable cell with the class
         * markCell.
         * 
         * To change this behaviour simply edit the string 
         * cl.Navigation.SingleSelectElements to contain the required
         * cells, eg 'td.editable.criterion'
         * This can be set in a marking schemes optional Init() method
         */
        cl.Action.ClickCellDefault = function() {
          // Optimization:
          //		Pre-compute all the row elements so I don't have to navigate the DOM
          //		as much when editing marks.
          cl.Root.find('.imarkbook_table tr:not(.' + cl.ElementName.NoteRow + ')').each(function() {
            var row = $(this);
            row.find(cl.Navigation.SingleSelectElements).click(function() {
              var cell = $(this);
              // If Ctrl key is pressed then group select toggle
              // Else if one-click edit then perfrom action
              // Else highlight the cell for standard edit
              if (cl.IsCtrlKeyPressed === true)
                cl.Action.SelectAllToggle(cell);
              else if (cl.EditMode === 'one-click')
                cl.Action.ChangeCellValue(cl.SelectedValue, cell, true);
              else
                cl.Action.SelectCell(cell);
            });
          });
        };
        /**
         * Highlight an indivudual editable cell
         * Note: this is not part of the multi-select functionality
         */
        cl.Action.SelectCell = function(cell) {
          // Check that users can select cells
          if (cl.CanUserSelectCells !== true)
            return false;
          // Reset to show not currently editing a multi-character input
          cl.Helper.IsEditingCell(false);
          // Return if new cell is not a mark cell
          if (!cell.hasClass(cl.ElementName.MarkCell) || cell === undefined)
            return false;
          // First deselect any multiselected cells
          if (cl.MultiSelectedCells === true)
            cl.Action.UnSelectAll();
          // For EDSQ only
          if (cl.Helper.isEDSRegisterCell(cell) && cell.html() == "R") return false;
          if (cl.Helper.isEDSExamMarkCell(cell)) return false;
          
          // Now deselect old cell
          var oldCell = cl.Root.find('td.' + cl.ElementName.SelectedCell);
          oldCell.removeClass(cl.ElementName.SelectedCell);
          // If the new cell is hidden (ie. on a different "page" of columns)
          // Then bring it to the fore
          if (cell.css('display') == 'none') {
            var newIndex = $(cell).closest('tr').find('td').index(cell);
            var firstVisbleCell = $(cell).closest('tr').children('.' + cl.ElementName.MarkCell + ':visible').eq(0);
            var visibleIndex = $(cell).closest('tr').children().index(firstVisbleCell);
            var navElement;
            if (newIndex > visibleIndex)
              navElement = $('.' + cl.UniqueId + '-next-page');
            else
              navElement = $('.' + cl.UniqueId + '-prev-page');
            // Keep clicking until the cell appears
            while (cell.css('display') == 'none') {
              if (navElement.attr('disabled') === 'disabled')
                return false;
              navElement.trigger('click');
            }
          }
          // If new cell is outside the visible port of the window
          // then scroll to find it
          if (!($(window).scrollTop() + $(window).height() > $(cell).offset().top) ||
            !($(window).scrollTop() < $(cell).offset().top)) {
            // Only scroll if not current scrolling (or bad things happen)
            if (cl.IsScrolling == false) {
              cl.IsScrolling = true;
              // Scroll
              $('html,body').animate({
                  scrollTop: $(cell).offset().top - ($(window).height() / 2)
                },
                cl.EffectOptions.ScrollSpeed,
                function() {
                  cl.IsScrolling = false;
                });
            }
          }
          // Mark cell as selected in css
          cell.addClass(cl.ElementName.SelectedCell);
          // Highlight the XY co-ordinates
          cl.Effect.HighlightCellXY(cell);
          // Display the Criterion
          cl.Effect.ShowCriterion(cell);
        };

        /**
         * Unselects all selected Cells
         */
        cl.Action.UnSelectCell = function() {
          cl.Helper.IsEditingCell(false);
          cl.Root.find("." + cl.ElementName.SelectedCell)
            .removeClass(cl.ElementName.SelectedCell);
        };

        /**
         * Changes a cell value
         * 
         * If no JQuery object is provided then it simply updates
         * the currently selected cell
         * It also marks the row to be saved
         * 
         * @param mixed newValue the new value, or false to remove last character
         * of string (if being edited)
         * @param JQueryObject cell (optional) the cell (or cells) to update
         * @param bool fullMarkOnly if passed true then the new value will be
         * treated as a full mark only - so will not accumilate for multiple
         * characters over multiple inputs
         * @return true or false on success
         */
        cl.Action.ChangeCellValue = function(newValue, cells, fullMarkOnly) {
          var originalValue = newValue;
          var originalCells = cells;
          if (fullMarkOnly === undefined)
            fullMarkOnly = false;
          // If no cell given then find what has been selected
          if (cells === undefined)
            var cells = cl.Helper.GetCurrentlySelectedCell();

          // Return false if newValue is null or undefined
          if (newValue === undefined || newValue === null)
            return false;

          // If ForceMarkToUppercase returns true then
          // set the mark to uppercase 
          if (typeof newValue === 'string' && cl.Helper.GetCurrentScheme().ForceMarkToUppercase(cells) === true)
            newValue = newValue.toUpperCase();

          newValue = String(newValue);

          // Remember if this began as a single cell input
          // We cannot rely on cells.length later as it may have
          // been filtered down.
          var singleCell;
          if (cells.length === 1)
            singleCell = true;
          else
            singleCell = false;

          // If we are only editing one cell then check and manipulate
          // the isEditingCell functionality
          if (singleCell == true && fullMarkOnly !== true) {
            // Var to store the current mark
            var currentMark = cells.html();

            // If this cell is being edited with multi-character input
            if (cl.Helper.IsEditingCell() === true) {
              // Delete characters if false passed
              if (newValue === false) {
                newValue = currentMark.substring(0, currentMark.length - 1);
                if (newValue.length === 0) {
                  newValue = cl.Helper.GetCurrentScheme().DefaultMark(cells);
                  cl.Helper.IsEditingCell(false);
                }
              }
              // Else Combine to produce new value
              else
                newValue = currentMark + newValue;
            }
          }

          // Make sure this is a real value
          if (newValue === null || newValue === undefined || newValue === '')
            return false;

          // Remove any highlighting if this is 
          // not a multi select
          if (!cells.hasClass(cl.ElementName.SelectedCell))
            cl.Action.UnSelectAll(cells);

          // Store the possible valid marks
          // Only needed scope if a single cell is selected
          // So we can later check whether to set isEditingCell value
          var possibleValidMarks;

          // Filter out unwanted cells
          cells = cells.filter(function() {
            var thisCell = $(this);
            var currentValue = thisCell.html();

            // Filter out any cells with the same value
            if (currentValue === newValue) return false
            // Filter cells which are not editable
            else if (!$(this).hasClass('editable')) return false

            // Get possible valid marks
            var defaultMark = cl.Helper.GetCurrentScheme().DefaultMark(thisCell);
            if (defaultMark === newValue)
              possibleValidMarks = [defaultMark];
            else
              possibleValidMarks = cl.Helper.PossibleValidMarksForCell(thisCell, newValue);
            // Filter if there are no possible valid marks from the input
            if ($.isArray(possibleValidMarks) === true) {
              if (possibleValidMarks.length < 1)
                return false;
            } else if (typeof possibleValidMarks === 'boolean') {
              if (possibleValidMarks === false)
                return false;
            } else {
              return false;
            }

            // All Ok
            return true;
          });

          // If there are no cells left after filter then...
          if (cells.size() === 0) {
            // ... try again without Editing or...
            if (cl.Helper.IsEditingCell() === true) {
              cl.Helper.IsEditingCell(false);
              cl.Action.ChangeCellValue(originalValue, originalCells);
            }
            // ... return false
            else
              return false;
          }

          // Set the new value
          cells.html(newValue);

          // And set the isEditing class appropriately
          if (singleCell === true &&
            (possibleValidMarks === true ||
              ($.isArray(possibleValidMarks) === true &&
                possibleValidMarks.length > 1))) {
            cl.Helper.IsEditingCell(true);
          } else {
            cl.Helper.IsEditingCell(false);
          }

          // If this is a full mark only then remove editing flag
          if (fullMarkOnly === true)
            cl.Helper.IsEditingCell(false);

          // Mark the cell as changed	
          cl.Helper.MarkCellChanged(cells);

          // Update the row status
          var rows = $.unique(cells.closest("tr"));
          rows.each(function() {
            cl.Status($(this));
          })
          return true;
        };

        /**
         * Monitor for changes to elements
         */
        cl.Action.Changes = function() {
          //Store the current value for edit modes to
          // reduce DOM transversals.
          cl.Root.find(".imarkbook_selected_value").change(function() {
            var newVal = $(this).val().toUpperCase();
            cl.SelectedValue = newVal;
          });
        }

        /**
         * This grabs all keystrokes from the user
         */
        cl.Action.GrabKeystrokes = function() {
          cl.Helper.GetCurrentUserInterface().bindAllEvents();
        };
        /**
         * Change the edit mode
         */
        cl.Action.EditMode = function() {
          cl.Root.find(".imarkbook_edit_mode").click(function() {
            if (cl.Root.find(".imarkbook_std_mode").attr('checked')) {
              cl.EditMode = 'manual';
              cl.Root.find(".imarkbook_selected_value").attr('disabled', 'disabled');
              cl.Root.find(".imarkbook_select_all").show();
            }
            if (cl.Root.find(".imarkbook_clk_mode").attr('checked')) {
              cl.EditMode = 'one-click';
              cl.Root.find(".imarkbook_selected_value").removeAttr('disabled');
              cl.Root.find(".imarkbook_select_all").hide();
              cl.Action.UnSelectAll();
            }
          });
        };

        /**
         * Select and deselect all
         * 
         * The cells highlights will be the children of the passed element
         * If no element is passed then it defaults to the whole table (All)
         * 
         * If Ctrl key is held down then it does not unselect other
         * cells when selecting newcells
         * 
         * By default it selects all "criterion" elements within the range.
         * To change this behaviour simply edit the string set in
         * cl.MultiSelectedCells to be the cells you wish to select
         * eg. 'td.mySpecialCell'
         * This can be set in the marking schemes optional Init() method
         * 
         */
        // Toggle Select All
        cl.Action.SelectAllToggle = function(element) {
          if (element === undefined || element === null)
            var element = cl.Root.find('.imarkbook_table');
          // If this is a table header then get the cells
          if (element.hasClass(cl.ElementName.MarkHeader))
            element = cl.Helper.GetColumn(element);
          // If this is a parent (tr or table) then grab all its markcell td's
          if (element.siblings('td.' + cl.ElementName.MarkCell).size() === 0)
            element = element.find('td');

          if (cl.IsCtrlKeyPressed === false && cl.MultiSelectedCells === true)
            cl.Action.UnSelectAll();

          if (element.filter('.click').size() < element.filter(cl.Navigation.MultiSelectElements).size())
            cl.Action.SelectAll(element);
          else
            cl.Action.UnSelectAll(element);
        };

        // Select All
        cl.Action.SelectAll = function(element) {
          // Do not highlight when in edit-mode
          if (cl.EditMode !== 'manual')
            return false;

          cl.Action.UnSelectCell();
          if (element === undefined || element === null)
            var element = cl.Root.find('.imarkbook_table');

          if (element.siblings('td').size() === 0)
            element = element.find('td');

          element = element.filter(cl.Navigation.MultiSelectElements + ':not(.click)');
          element.addClass('click');
          element.filter('.changedCell').addClass('changedCellClick');

          if (cl.Root.find('.click').size() > 0) {
            cl.Root.find(".imarkbook_select_all").html($.iMessage['Un-select All']);
            cl.Root.find(".imarkbook_select_all").addClass(cl.ElementName.ButtonHighlight);
            cl.MultiSelectedCells = true;
          }
        };

        // Unselect All
        cl.Action.UnSelectAll = function(element) {
          if (element === undefined || element === null)
            var element = cl.Root.find('.imarkbook_table');

          if (element.siblings('td').size() === 0)
            element = element.find('td');

          element = element.filter(cl.Navigation.MultiSelectElements + '.click');
          element.removeClass('click');
          element.filter('.changedCell').removeClass('changedCellClick');

          if (cl.Root.find('.click').size() === 0) {
            cl.Root.find(".imarkbook_select_all").html($.iMessage['Select All']);
            cl.Root.find(".imarkbook_select_all").removeClass(cl.ElementName.ButtonHighlight);
            cl.MultiSelectedCells = false;
          }
        };

        // Toggle select all
        cl.Action.SelectAllToggleButton = function() {
          if (cl.Root.find('.imarkbook_table').find('.click').size() === 0)
            cl.Action.SelectAll();
          else
            cl.Action.UnSelectAll();
        }


        ///////////////////////////////////
        //
        //	HELPER FUNCTIONS
        //
        ////////////


        cl.Helper = {};

        /**
         * Get Cell Type index
         * 
         * Get the index of a cell in a row relative to its specific cell type
         */
        cl.Helper.getCellTypeIndex = function(cellType, cell) {
          var row = cl.Helper.GetRowFromCell(cell);
          var allCells = cl.Helper.GetAllMarkCellsInRow(row);
          var cellsOfType = allCells.filter('.' + cellType);
          var eq = 0;
          var returnVal = null;
          cellsOfType.each(function() {
            if ($(this).is(cell)) {
              returnVal = eq;
              return false;
            }
            eq++;
          });
          return returnVal;
        }

        /**
         * Get Printable Character
         * Pass in an event.which integer and return a printable
         * character, or '' if no printable character.
         * 
         * This is necessary due to discrepencies in returning the correct
         * value for asterisks
         *  
         */
        cl.Helper.GetPrintableCharacterFromEvent = function(event) {
          var charVal, code = event.which;
          charVal = $.trim(String.fromCharCode(code));
          if (event.shiftKey === true)
            charVal = charVal.toUpperCase();
          return charVal;
        }

        /**
         * Get learner's forename from a row
         *  
         */
        cl.Helper.GetPupilForenameFromRow = function(row) {
          return row.find('td.imarkbook_forenamecell').html();
        };

        /**
         * Get learner's surname from a row
         *  
         */
        cl.Helper.GetPupilSurnameFromRow = function(row) {
          return row.find('td.imarkbook_surnamecell').html();
        };

        /**
         * Get learner's name from a row
         *  
         */
        cl.Helper.GetPupilNameFromRow = function(row) {
          var forename = cl.Helper.GetPupilForenameFromRow(row);
          var surname = cl.Helper.GetPupilSurnameFromRow(row);
          return forename + " " + surname;
        };

        /**
         * Is Test Requested for a row
         * @param jqueryObject row the row
         * @return true or false
         */
        cl.Helper.HasTestRequested = function(row) {
          // if (row.indexOf("test_re") >= 0){return true;}
          $marks = row.find("td.examMarks").html();
          if ($marks == "O") {
            return true;
          }
          if ($marks == "P") {
            return true;
          }
          return false;

          //			var requestedCell = row.find("td.test_re")
          //			return requestedCell.html() !== cl.TestRequests.requestedCellDefault;
        };

        cl.Helper.HasTestApproved = function(row) {
          // if (row.indexOf("test_re") >= 0){return true;}
          $marks = row.find("td.test_re").html();
          $result = row.find("td.imarkbook_test_markcell").html();
          if ($marks == "O" || $marks == "P") {
            return true;
          }
          return false;

          //			var requestedCell = row.find("td.test_re")
          //			return requestedCell.html() !== cl.TestRequests.requestedCellDefault;
        };

        cl.Helper.HasTestMade = function(row) {
          $result = row.find("td.finalMark").html();
          $result_r = row.find("td.resitMark").html();
          $result_r2 = row.find("td.resit2Mark").html();
          $result_r3 = row.find("td.resit3Mark").html();
          $result_r4 = row.find("td.resit4Mark").html();
          //                   row.find("td.realExam").each(function(){
          //                        if ($(this).html() !== "-" ) {return true;};
          //                        });
          //                   
          $tt = 0;
          if ($result == "A*") {
            $tt = 1;
          }
          if ($result == "A") {
            $tt = 1;
          }
          if ($result == "B") {
            $tt = 1;
          }
          if ($result == "C") {
            $tt = 1;
          }
          if ($result == "D*") {
            $tt = 1;
          }
          if ($result == "D") {
            $tt = 1;
          }
          if ($result == "M") {
            $tt = 1;
          }
          if ($result == "P") {
            $tt = 1;
          }
          if ($result == "U" && ($result_r == "A*" || $result_r == "A" || $result_r == "B" || $result_r == "C")) {
            $tt = 1;
          }
          if ($result == "U" && ($result_r == "P" || $result_r == "M" || $result_r == "D" || $result_r == "D*")) {
            $tt = 1;
          }
          if ($result == "U" && ($result_r2 == "P" || $result_r2 == "M" || $result_r2 == "D" || $result_r2 == "D*")) {
            $tt = 1;
          }
          if ($result == "U" && ($result_r3 == "P" || $result_r3 == "M" || $result_r3 == "D" || $result_r3 == "D*")) {
            $tt = 1;
          }
          if ($result == "U" && ($result_r4 == "P" || $result_r4 == "M" || $result_r4 == "D" || $result_r4 == "D*")) {
            $tt = 1;
          }

          if ($tt !== 0) {
            return true;
          }

          //if ($result !== "-" ) {return true;}
          return false;
        };

        cl.Helper.HasCourseworkFalse = function(row) {
          if (row.hasClass('coursework_false')) {
            row.addClass("imarkbook_frozen");
            return true;
          }
          return false;
        };
        /**
         * Has the test been made available for a row
         * @param jqueryObject row the row
         * @return true or false
         */
        cl.Helper.HasTestBeenMadeAvailable = function(row) {
          $marks = row.find("td.test_av").html();
          if ($marks == "O" || $marks == "P") {
            return true;
          }
          return false;
          //if (row.search('test_av') != -1){return true;}
          //row.find("td.test_av").each(function(){
          //                            return true;
          //                            
          //                        });
          // return false;
          //var availableCell = row.find("td.imarkbook_test_made_availablecell");
          //			var availableCell = row.find("td.test_av");
          //			return availableCell.html() !== cl.TestRequests.madeAvailableCellDefault;
        };


        /**
         * Returns the test mark for this row
         * 
         * @param jQueryObject row the row
         * @return mixed the value or false if no mark inputted
         */
        cl.Helper.GetTestMarkForRow = function(row) {
          var cell, mark, defaultMark;
          //cell = row.find('td.imarkbook_test_markcell');
          var max_test_value = 0;
          row.find("td.examMarks").each(function() {
            marktemp = $(this).html();
            if (marktemp !== "-") {

              if (marktemp > max_test_value) {
                max_test_value = marktemp;
                defaultMark = cl.Helper.GetCurrentScheme().DefaultMark($(this));
              }
            }

          });
          //mark = cell.html();

          if (max_test_value !== "-")
            mark = max_test_value;
          if (mark === 0 || defaultMark === 0)
            return null;
          if (mark.toString() === defaultMark.toString())
            return false;
          return mark;
        };

        /**
         * Sets the cl.CurrentScheme variable to a scheme object
         * 
         * @param string $name scheme name
         */
        cl.Helper.SetCurrentScheme = function(name) {
          cl.CurrentScheme = ingotClass.iMarkingScheme.getScheme(name, {
            marksheetClosure: cl
          });
        };

        /**
         * Gets the cl.CurrentScheme
         * 
         */
        cl.Helper.GetCurrentScheme = function() {
          if (cl.CurrentScheme === undefined)
            cl.CurrentScheme = {};
          return cl.CurrentScheme;
        };

        /**
         * Sets the cl.CurrentUserInterface variable to a scheme object
         * 
         * @param string $name scheme name
         */
        cl.Helper.SetCurrentUserInterface = function(name) {
          cl.CurrentUserInterface = ingotClass.iMarksheetUserInterface.getUserInterface(name, {
            marksheetClosure: cl
          });
        };

        /**
         * Gets the cl.CurrentUserInterface
         * 
         */
        cl.Helper.GetCurrentUserInterface = function() {
          if (cl.CurrentUserInterface === undefined)
            cl.CurrentUserInterface = {};
          return cl.CurrentUserInterface;
        };

        /**
         * Allows editing of the criteria in the passed row
         * 
         * @param jqueryObject row the row
         */
        cl.Helper.SetRowCriteriaAsEditable = function(row) {
          if (row.hasClass('imarkbook_frozen'))
            return false;
          row.find('td.markCell:not(.editable,.imarkbook_test_markcell,.examMarks)').addClass('editable');
        }

        /**
         * Disables editing of the criteria in the passed row
         * 
         * @param jqueryObject row the row
         */
        cl.Helper.SetRowCriteriaAsUneditable = function(row) {
          row.find('td.markCell.editable:not(.imarkbook_test_markcell)').removeClass('editable');
        }

        /**
         * Allows editing of the Test Mark in the passed row
         * 
         * @param jqueryObject row the row
         */
        cl.Helper.SetRowTestMarkAsEditable = function(row) {
          if (row.hasClass('imarkbook_frozen'))
            return false;
          //row.find('td.imarkbook_test_markcell:not(.editable)').addClass('editable');
          //row.find('td.examMarks:not(.editable)').addClass('editable');
        };

        /**
         * Disables editing of the Test Mark in the passed row
         * 
         * @param jqueryObject row the row
         */
        cl.Helper.SetRowTestMarkAsFrozen = function(row) {
          //row.find('td.imarkbook_test_markcell.editable').removeClass('editable');
          //row.find('td.markcell.disedit').removeClass('editable');
        };

        /**
         * Set whether the currently selected cell is being edited
         * Or return the value
         * 
         * @param boolean value true or false to set the value
         * @return boolean true or false the set value
         */
        cl.Helper.IsEditingCell = function(value) {
          if (value !== undefined) {
            if (typeof value !== 'boolean')
              value = false;
            cl.IsEditingCell = value
            var cell = cl.Helper.GetCurrentlySelectedCell();
            if (cl.Helper.CanEditCell(cell) === true) {
              if (value === true)
                cell.addClass('isEditing');
              else {
                cell.removeClass('isEditing');
                if (cl.Helper.IsMarkValid(cell.html(), cell) !== true)
                  cl.Action.ChangeCellValue(false, cell);
              }
            }
          }
          return cl.IsEditingCell;
        };

        /**
         * Check if this cell can currently be edited
         * 
         * @return bool true or false
         */
        cl.Helper.CanEditCell = function(cell) {
          if (cell === undefined)
            return false;
          if (cell.hasClass('edsexammark'))
            return false;
          if (!cell.hasClass('editable'))
            return false;
          
          return true;
        };

        /**
         * Make a cell editable
         * 
         * @param jqueryObject cell the cell
         */
        cl.Helper.MakeCellEditable = function(cell) {
          cell.addClass('editable');
        };

        /**
         * Make a cell uneditable
         * 
         * @param jqueryObject cell the cell
         */
        cl.Helper.MakeCellUneditable = function(cell) {
          cell.removeClass('editable');
        };


        /**
         * Make a cell editable
         * 
         * @param jqueryObject cell the cell
         */
//         cl.Helper.MakeCellEditable = function(cell) {
//           cell.addClass('editable');
//         };

        /**
         * Select all cells in a column
         */
        cl.Helper.GetColumn = function(element) {
          if (element === undefined || element === null) return false;
          return cl.Root
            .find('tr.' + cl.ElementName.MarkRow)
            .find('td:eq(' + $(element).closest('tr').find('th').index($(element)) + ')');
        };

        /**
         * Select all MarkCells in a row
         */
        cl.Helper.GetAllMarkCellsInRow = function(row) {
          if (!row.hasClass('markRow')) {
            throw new ingotError('cl.Helper.GetAllMarkCellsInRow(): this must be a mark row');
            return null;
          }
          return row.find('td.markCell');
        };

        /**
         * Select all MarkCells
         */
        cl.Helper.GetAllMarkCells = function(row) {
          return cl.Root.find('.imarkbook_table tr.markRow td.markCell');
        };

        /**
         * Get Pid from Row
         */
        cl.Helper.GetPidFromRow = function(row) {
          var pidCell = row.find('.imarkbook_pid');
          if (pidCell.length !== 1)
            throw new ingotError('ingot-iMarksheet::cl.Helper.GetPidFromRow(): There should only be one pid cell returned - returned ' + pidCell.length);
          var pid = pidCell.html();
          return pid;
        };

        /**
         * Get row from cell
         */
        cl.Helper.GetRowFromCell = function(cell) {
          var row = $(cell).closest('tr');
          if (row.length !== 1)
            throw new ingotError('ingot-iMarksheet::cl.Helper.GetRowFromCell(): There should only be one row cell returned - returned ' + row.length);
          return row;
        };

        /**
         * Get row from cell
         */
        cl.Helper.GetPidFromCell = function(cell) {
          var row = cl.Helper.GetRowFromCell(cell);
          var pid = cl.Helper.GetPidFromRow(row);
          return pid;
        };

        /**
         * Get All Rows
         */
        cl.Helper.GetAllMarkRows = function() {
          return cl.Root.find('.markRow');
        };

        /**
         * Select all Mark Cells in a row containing a certain type/class name
         * @param - either a row (jquery object) or pupilId
         * @return the appropriate cells
         */
        cl.Helper.GetAllMarkCellsOfTypeInRow = function(param, type) {
          var row;
          // Sanity Checks
          if (typeof type !== 'string')
            throw new ingotError('ingot-iMarksheet::cl.Helper.GetAllMarkCellsOfTypeInRow(): type must be a string');
          if (param instanceof jQuery)
            row = param;
          else if (typeof param === 'numeric' || typeof param === 'string')
            row = cl.Helper.GetRowFromPid(param);
          else
            throw new ingotError('ingot-iMarksheet::cl.Helper.GetAllMarkCellsOfTypeInRow(): input must be either a row in jquery format or a pid');

          // Get the grade from html table
          var cells = cl.Helper.GetAllMarkCellsInRow(row);
          cells = cells.filter(function() {
            if ($(this).hasClass(type))
              return true;
            return false;
          });
          return cells;
        };

        /**
         * Get a row from a pid
         */
        cl.Helper.GetRowFromPid = function(pid) {
          if (isNaN(pid))
            throw new ingotError('cl.Helper.GetRowFromPid(): pid must be a number.  Currently it is ' + pid);
          var pidCell = cl.Root.find('.imarkbook_pid:contains(' + pid + ')');
          // Filter down to eliminate cells where the supplied pid is only part of the actual pid
          pidCell = pidCell.filter(function() {
            if (!$(this).closest('tr').hasClass('markRow'))
              return false;
            if ($(this).html() === pid)
              return true;
            return false;
          });
          if (pidCell.length === 0)
            return false;
          if (pidCell.length > 1)
            throw new ingotError('cl.Helper.GetRowFromPid(): more than one row matched.  There should only be one row per learner.  Found ' + pidCell.length + 'cells');
          return pidCell.closest('tr');
        };

        /**
         * Marks a cell as changed
         */
        cl.Helper.MarkCellChanged = function(cell) {
          // Mark the Cell itself
          cell.addClass('changedCell');

          // Mark the row to be saved
          if (cell.hasClass('imarkbook_test_markcell'))
            cell.closest("tr").addClass("imarkbook_tosave_testmark");
          else
            cl.Helper.SetRowAsReadyToSave(cell.closest("tr"));

          // Mark Unsaved Changes Globally
          cl.Action.MarkGlobalUnsaved(true);
        };

        /**
         * Unmarks a cell as changed
         */
        cl.Helper.MarkCellNotChanged = function(cell) {
          cell.removeClass('changedCell');
        };

        /**
         * Gets the currently single-selected cell
         */
        cl.Helper.GetCurrentlySelectedCell = function() {
          var cell = cl.Root.find("." + cl.ElementName.SelectedCell);
          if (cell.size() === 0)
            cell = cl.Root.find(".imarkbook_table td.editable.criterion.click");
          return cell;
        };

        /**
         * Check if this cell is a test mark cell
         * @param jqueryObject cell the cell
         * @return true or false
         */
        cl.Helper.isTestMarkCell = function(cell) {
          return cell.hasClass('imarkbook_test_markcell');
        };
        /**
         * Check if this cell is a mark cell fro Exam criterii 
         * @param jqueryObject cell the cell
         * @return true or false
         */
        cl.Helper.isExamMarkCell = function(cell) {
          return cell.hasClass('examMarks');
        };

        /**
         * Set a row as ready for moderation
         */
        cl.Helper.SetRowReadyForModeration = function(row) {
          row.addClass('rowReadyForModeration');
        };

        /**
         * Unset a row as being ready for moderation
         */
        cl.Helper.UnsetRowReadyForModeration = function(row) {
          row.removeClass('rowReadyForModeration');
        };

        /**
         * Check if a row is ready for moderation
         */
        cl.Helper.IsRowReadyForModeration = function(row) {
          return row.hasClass('rowReadyForModeration');
        };
        
        /**
        * EDSQ Resit check if more than one mark is added. For all other QUALs is bypass
        **/
        cl.Helper.IsRowReadyForModerationEDSQResit = function(row) {
          let mpt = 1;
          let abc = 0;
          row.find(".edstestmark-resit").each(function() {
            if (abc == 0) mpt = 0;
            if ($(this).html() != "-") {
              mpt = 1;abc=1;
            }
          });
          row.find(".edsexammark-resit").each(function() {
            if ($(this).html() != "-") {
              mpt = 1;
            }
          });
          if (mpt == 1 ) {return true;}
          return false;
        }

        /**
         * Set a row as frozen
         */
        cl.Helper.SetRowAsFrozen = function(row) {
          cl.Helper.UnsetRowReadyForModeration(row);
          row.addClass('imarkbook_frozen');
        };

        /**
         * Unset a row as frozen
         */
        cl.Helper.UnsetRowAsFrozen = function(row) {
          row.removeClass('imarkbook_frozen');
        };

        /**
         * Check if a row is frozen
         */
        cl.Helper.IsRowFrozen = function(row) {
          return row.hasClass('imarkbook_frozen');
        }

        /**
         * Set a row as ready to save
         */
        cl.Helper.SetRowAsReadyToSave = function(row) {
          if (cl.Helper.IsRowFrozen(row))
            return false;
          row.addClass('imarkbook_tosave');
        };

        /**
         * Unset a row as ready to save
         */
        cl.Helper.UnsetRowAsReadyToSave = function(row) {
          row.removeClass('imarkbook_tosave');
        };

        /**
         * Check if a row is ready to save
         */
        cl.Helper.IsRowReadyToSave = function(row) {
          return row.hasClass('imarkbook_tosave');
        };

        /**
         * Get all possible valid marks for a cell
         * 
         * @param jObject cell the cell
         * @param mixed newValue (optional) a value to test against
         * @return array an array of all possible values or true if unlimited
         */
        cl.Helper.PossibleValidMarksForCell = function(cell, newValue) {
          if (newValue === undefined || newValue === null)
            newValue = '';

          // Get the allowed marks for this cell under the current marking scheme
          var available = cl.Helper.GetCurrentScheme().GetValidMarks(cell);
          if ($.isPlainObject(available) !== true)
            return false;

          var returnValues = [];
          var newValueLength = newValue.length;

          if (newValueLength === 0)
            newValueLength = undefined;

          // If this is a set value 
          if ('setValues' in available) {
            for (var x in available.setValues) {
              if (available.setValues[x].substring(0, newValueLength) == newValue)
                returnValues.push(available.setValues[x]);
            }
          }
          // If this is numeric
          if ('numeric' in available && !isNaN(newValue)) {
            var numMark = Number(newValue)
            // Check this is of a valid numeric type
            if ('type' in available) {
              var okType = false;
              for (var x in available.type) {
                if (available.type[x] === 'int' && numMark % 1 === 0)
                  okType = true;
                else if (available.type[x] === 'float' && numMark % 1 !== 0)
                  okType = true;
              }
              if (okType === false)
                return false;
            }
            // If only one value
            if (available.numeric.length === 1) {
              if (numeric[0] === '*')
                return true;
              else if (numeric[0] === numMark)
                return true;
            }
            // If more than one value
            else {
              // If anything less than second value
              if (available.numeric[0] === '*' && !isNaN(available.numeric[1])) {
                var maxNum = Number(available.numeric[1]);
                if (numMark <= maxNum)
                  return true;
              }
              // If anything greater than first value
              if (available.numeric[1] === '*' && !isNaN(available.numeric[0])) {
                var minNum = Number(available.numeric[0]);
                if (numMark >= minNum)
                  return true;
              }
              // If between two values
              if (!isNaN(available.numeric[0]) && !isNaN(available.numeric[1])) {
                var numAsString = '';
                for (i = available.numeric[0]; i <= available.numeric[1]; i++) {
                  numAsString = i.toString();
                  if (numAsString.substring(0, newValueLength) === newValue)
                    returnValues.push(numAsString);
                }
              }
            }
          }
          if ('anyvalue' in available) {
            return true;
          }
          return returnValues;
        };

        /**
         * Check if a value is valid for a specific cell with the
         * current Marking Scheme
         * 
         * The marking scheme should contain a method called GetValidMarks(cell)
         * It should return an array containing all valid marks.
         * It also should accept a jquery object of the current cell
         * if it needs to perform any special calculations to work out
         * the valid marks.
         * 
         * 
         * @return bool true or false
         * 
         */
        cl.Helper.IsMarkValid = function(mark, cell) {
          if (mark === undefined)
            return false;

          // Get number of possible values
          var result = cl.Helper.PossibleValidMarksForCell(cell, mark);
          // If array
          if ($.isArray(result) === true) {
            if ($.inArray(mark, result) !== -1)
              return true
            return false;
          }
          // If boolean
          if (typeof result === 'boolean')
            return result;

          // If cannot understand result
          return false;
        };

        cl.Helper.IsRowHaveTestRequestAllready = function(row) {
          var haveDate = row.siblings("td.imarkbook_test_requestedcell").html();
          if (haveDate === "")
            return false
          return true;
        };
        
        /**
        * Check if cell is test results for EDSQ
        * @param jqueryObject cell the cell
        * @return true or false
        */
        cl.Helper.isEDSExamMarkCell = function(cell) {
          return cell.hasClass('edsexammark');
        };
        /**
        * Check if cell is test results Resit for EDSQ
        * @param jqueryObject cell the cell
        * @return true or false
        */
        cl.Helper.isEDSExamMarkResitCell = function(cell) {
          return cell.hasClass('edsexammark-resit');
        };
        
        /**
        * Check if cell is request register for EDSQ
        * @param jqueryObject cell the cell
        * @return true or false
        */
        cl.Helper.isEDSRegisterCell = function(cell) {
          return cell.hasClass('edsregister');
        };

        /**
        * Check if cells are Exam marks for FFQ
        * @param jqueryObject cell the cell
        * @return true or false
        */
        cl.Helper.isFFQExamCell = function(style) {

          if (style.includes('fin1230testmark') ||
              style.includes('fin820testmark') ||
              style.includes('finfinaltestmark') ||
              style.includes('fin1530testmark') ||
              style.includes('fin1035testmark') ||
              style.includes('fin2finaltestmark') ) {
            return true;
          }
          return false;
        };

        /**
        * Check if cell is Final Marks for FFQ
        * @param jqueryObject cell the cell
        * @return true or false
        */
        cl.Helper.isFFQFinalCell = function(style) {
          if (style.includes('finfinaltestmark')) {
            return true;
          }
          return false;
        };

        /**
        * Check if cell is Final Marks for FFQ2
        * @param jqueryObject cell the cell
        * @return true or false
        */
        cl.Helper.isFFQ2FinalCell = function(style) {
          if (style.includes('fin2finaltestmark')) {
            return true;
          }
          return false;
        };

        //////////////////////////////////
        //
        // Additional Mark Data Object
        //
        //////////////

        cl.AdditionalMarkData = {
          additionalMarkDataObject: null
        };

        cl.AdditionalMarkData.init = function() {
          cl.AdditionalMarkData.additionalMarkDataObject = new ingotClass.iAdditionalMarkData({
            marksheetClosure: cl,
            frameworkData: cl.Data.frameworkData,
            fields: cl.Data.additionalMarkDataFields
          });
        };

        cl.AdditionalMarkData.getObject = function() {
          if (cl.AdditionalMarkData.additionalMarkDataObject === null)
            this.init();
          return cl.AdditionalMarkData.additionalMarkDataObject;
        };

        ////////////////////////////////////
        //
        // Framework-Specific Data
        //
        //////////////////

        cl.FrameworkSpecificData = {};

        /**
         * Get the Framework-Specific data for a given parameter
         */
        cl.FrameworkSpecificData.GetData = function(param) {
          if (cl.Data.frameworkData[param] === undefined)
            return {};
          return cl.Data.frameworkData[param];
        };

        /**
         * Get the Framework-Specific pupil data
         */
        cl.FrameworkSpecificData.GetAllPupilData = function() {
          return cl.FrameworkSpecificData.GetData('pupilQuals');
        };

        /**
         * Get the Framework-Specific data for a given pupil
         */
        cl.FrameworkSpecificData.GetPupilDataByPupilId = function(pid) {
          if (isNaN(pid))
            return false;
          var allData = cl.FrameworkSpecificData.GetAllPupilData();
          if (allData[pid] === undefined)
            return {};
          return allData[pid];
        };

        /**
         * Set the Framework-Specific data for a given parameter
         */
        cl.FrameworkSpecificData.SetData = function(param, data) {
          if ($.isPlainObject(cl.Data.frameworkData) === false)
            cl.Data.frameworkData = {};
          cl.Data.frameworkData[param] = data;
        };

        /**
         * Set the Framework-Specific data for a given parameter
         */
        cl.FrameworkSpecificData.SetParamByPid = function(pid, param, data) {
          if (pid === undefined)
            throw new ingotError("iMarksheet.FrameworkSpecificData.SetPupilDataParam(): pid must be set");
          if (param === undefined)
            throw new ingotError("iMarksheet.FrameworkSpecificData.SetPupilDataParam(): param must be set");

          var pupilData = cl.FrameworkSpecificData.GetPupilDataByPupilId(pid);
          pupilData[param] = data;
          cl.FrameworkSpecificData.SetPupilDataByPupilId(pid, pupilData);
        };

        /**
         * Set the Framework-Specific data for a given pupil	
         * 
         * Optionally, a jQuery object can be passed for the relevent
         * cell that the data affects.  This will then be marked as
         * changed and ready to be saved.
         */
        cl.FrameworkSpecificData.SetPupilDataByPupilId = function(pid, data, targetCell) {
          if (pid === undefined || isNaN(pid))
            throw new ingotError('cl.FrameworkSpecificData.SetPupilDataByPupilId: Passed pid must be a number, currently it is: ' + pid);
          if (data === undefined)
            throw new ingotError('cl.FrameworkSpecificData.SetPupilDataByPupilId: Passed data is undefined');
          var newData = cl.FrameworkSpecificData.GetAllPupilData();
          newData[pid] = data;
          cl.FrameworkSpecificData.SetData('pupilQuals', newData);

          // Mark ready to save if targetCell data supplied
          if (targetCell !== undefined && targetCell instanceof jQuery)
            cl.Helper.MarkCellChanged(targetCell);
        };


        /////////////////////////////////
        //
        //  Actions
        //
        ///////////////

        /**
         * Set the save button to trigger the save action
         */
        cl.Action.SaveClick = function() {
          cl.Root.find(".imarkbook_save").click(function() {
            if ($(this).hasClass("buttonHighlight")) {
              cl.Action.Save();
            };
          });
        };
        /**
         * Save the marks
         * @param map options a mapt of all options:
         *   - saveAllRows - bool - if true then save every row regardless of changes made
         *   - success - callback to run after save sucessful
         */
        cl.Action.Save = function(options) {

          var defaults = {
            saveAllRows: false,
            success: function() {}
          };

          var settings = $.extend(defaults, options);

          // Check that there are no open Notes
          if (cl.OpenNotes > 0) {
            $.fn.iDialog($.iMessage['Close open notes']);
            return;
          }

          /*
           * Function to set all rows ready to be saved regardless
           * of marks changed
           */
          var setAllRowsReadyToSave = function() {
            cl.Root.find(".imarkbook_table tr").each(function() {
              if (!$(this).hasClass(cl.ElementName.NoteRow)) {
                cl.Helper.SetRowAsReadyToSave($(this));
              }
            });
          };

          // If settings.saveAllRows is true then set all rows to save
          if (settings.saveAllRows === true) {
            setAllRowsReadyToSave();
          }
          // Else check if no rows have been changed and handle
          else {
            if (cl.Root.find("tr.imarkbook_tosave,tr.imarkbook_tosave_testmark").size() === 0) {
              var save = confirm($.iMessage['Ask before saving all marks when no mark changed']);
              if (save) {
                setAllRowsReadyToSave();
              } else {
                return;
              }
            }
          }

          // Set up vars to hold data in csv format
          var csv = "";
          var csv_testmark = '';
          var prevPupilID = "";
          var addNewLine = true;
          var pids = [];

          // Grab the marks and turn into the correct format
          // Exclude test marks
          cl.Root.find("tr.imarkbook_tosave").each(function() {
            addNewLine = true;
            addNewLineExam = false;
            firstTMark = 0;
            $(this).find("td.imarkbook_pid,td." + cl.ElementName.MarkCell + "").each(function() { //:not(.imarkbook_test_markcell)
              if ($(this).hasClass("imarkbook_pid")) { //if this is pupil_id column
                //in case marks been uploaded via csv file, we should not allow change marks after test has been requested
                /*if(cl.Data.test_mark !== false){
                	var okToSave = $(this).siblings("td.imarkbook_test_requestedcell").html(); 
                	if(okToSave !== cl.TestRequests.requestedCellDefault){
                		addNewLine = false;
                		return false;                                
                	}
                }bgvlado*/
                //make sure we do not override last updated marks, so should not allow duplicated pupil_ids
                var currentPid = $(this).text();
                if (currentPid !== prevPupilID) {
                  pids.push(currentPid);
                  csv += "\"" + $(this).text() + "\",";
                  //csv_testmark+="\""+$(this).text()+"\",";
                  prevPupilID = $(this).text();
                } else { //
                  addNewLine = false;
                  addNewLineExam = false;
                  return false;

                }
              } else if ($(this).hasClass("imarkbook_test_markcell")) {
                if (firstTMark === 0) {
                  csv_testmark += "\"" + prevPupilID + "\",";
                }
                csv_testmark += "\"" + $(this).text() + "\",";
                firstTMark = 1;
                addNewLineExam = true;
              } else {
                csv += "\"" + $(this).text() + "\",";
              }
            });

            if (addNewLine) {
              csv += "\n";
            }
            if (addNewLineExam) {
              csv_testmark += "\n";
              firstTMark = 0;
            }
          });


          //			prevPupilID = "";
          //process test marks to save
          //			cl.Root.find("tr.imarkbook_tosave_testmark").each(function(){
          //				addNewLine = true;
          //                                alert ('$(this).text()');
          //				$(this).find("td.imarkbook_pid,td.imarkbook_test_markcell").each(function(){
          //					
          //					if($(this).hasClass("imarkbook_pid")){//if this is pupil_id column
          //						//make sure we do not override last updated marks, so should not allow duplicated pupil_ids
          //						var currentPid = $(this).text();
          //						if(currentPid!==prevPupilID){
          //							pids.push(currentPid);
          //							csv_testmark+="\""+$(this).text()+"\",";
          //							prevPupilID = $(this).text();
          //						}else{//
          //							addNewLine = false;
          //							return false;
          //						}
          //					}else{
          //						csv_testmark+="\""+$(this).text()+"\",";
          //					}
          //                                        
          //				});
          //                                
          //				if (addNewLine){
          //					csv_testmark+="\n";
          //				}
          //			});             

          // Get any additional data needed to be returned by this
          // framework
          var frameworkReturnData = cl.AdditionalMarkData.getObject().getSaveData(pids);

          frameworkReturnData = JSON.stringify(frameworkReturnData);

          // Save.
          //		Do not try to edit the CSV. It is more reliable to remove
          //		unneeded columns at the server.
          //                cl.Root.find(".imarkbook_table").table2csv({
          //                    callback: function (csv) {
          $.iBlockUI($.iMessage['Saving'] + '...');
          $.fn.iAjax({
            type: 'POST',
            url: "?r=assessor/savemarks",
            data: {
              csv: csv,
              unitId: cl.Data.unit,
              csv_testmark: csv_testmark,
              frameworkData: frameworkReturnData,
              tgroup: cl.Data.tgroup
            },
            success: function(result) {
              var savedpids = result.savedPids;
              // find the rows that have been saved
              cl.Root.find("tr.imarkbook_tosave, tr.imarkbook_tosave_testmark").each(function() {
                var pid = $(this).find('td:eq(0)').html();
                if ($.inArray(Number(pid), savedpids) === -1)
                  return true;
                $(this).find('td.' + cl.ElementName.ChangedCell).each(function() {
                  cl.Helper.MarkCellNotChanged($(this));
                });
                cl.Helper.UnsetRowAsReadyToSave($(this));
                $(this).removeClass("imarkbook_tosave_testmark");
                cl.Status($(this));
              });
              // Mark the global indicator of unsaved
              cl.Action.MarkGlobalUnsaved();
              // Run any passed success callbacks
              settings.success();
            }
          });
        };

        /**
         * Check if there is any unsaved changes and mark
         * the global appropriately
         * 
         * @param bool changed set this to true to assume cells changed
         * pass this when changing a cell to save on DOM searching
         */
        cl.Action.MarkGlobalUnsaved = function(changed) {
          // If this is flagged to be true and Global is marked as unsaved then 
          // assume okay and simply return true
          if (changed !== undefined && changed === true && cl.UnsavedChanges === true)
            return true;

          var changedCellNumber = cl.Root.find('td.' + cl.ElementName.ChangedCell).size();

          // Update necessary information
          if (changedCellNumber === 0) {
            cl.UnsavedChanges = false;
            cl.Root.find(".imarkbook_button.imarkbook_save").removeClass(cl.ElementName.ButtonHighlight);
          } else {
            cl.UnsavedChanges = true;
            cl.Root.find(".imarkbook_button.imarkbook_save").addClass(cl.ElementName.ButtonHighlight);
          }

          // Return
          return true;
        };


        cl.Action.CsvDownload = function() {
          cl.Root.find(".imarkbook_csv_down").click(function() {
            // if Units is part of DoY framework
            var doy = 0;
            var markScheme = cl.Data.markingScheme;
            if (markScheme.search(/DUKEOFYORK/i) === 0) doy = 1;

            if (cl.Data.test_mark === false) {
              if ($(".imarkbook_save").hasClass("buttonHighlight")) {
                $.fn.iConfirm($.iMessage['JS_ALL_MARKS_WILL_BE_SAVED_TO_PROCEED'], function() {
                  cl.Action.Save({
                    saveAllRows: true,
                    success: function() {
                      location.href = "index.php?r=assessor/downloadcsv" +
                        "&unit_id=" + cl.Data.unit +
                        "&tgroup=" + cl.Data.tgroup +
                        "&doy=" + doy;
                    }
                  });
                });
              } else {
                location.href = "index.php?r=assessor/downloadcsv" +
                  "&unit_id=" + cl.Data.unit +
                  "&tgroup=" + cl.Data.tgroup +
                  "&doy=" + doy;
              }
            } else {
              location.href = "index.php?r=assessor/downloadcsv" +
                "&unit_id=" + cl.Data.unit +
                "&tgroup=" + cl.Data.tgroup +
                "&exammark=1" +
                "&baseline=" + cl.Data.baseline;
            }
          });
        };

        /**
         * Upload a csv of marks
         */
        cl.Action.CsvUpload = function() {
          var testData = 0;
          if (cl.Data.test_mark !== false) {
            testData = 1; //flag to get test_mark, test_requested and test_made_available fields
          }
          cl.Root.find('.imarkbook_csv_up').iUploadCSV({
            action: 'index.php?r=assessor/uploadcsv&unit=' + cl.Data.unit + '&unitwithtest=' + testData,
            elementId: cl.UniqueId + '-csvfile',
            onComplete: function(data) {
              // If the current framework has a specific function to handle
              // CSVupload on complete then run that
              if (cl.Helper.GetCurrentScheme().CsvUploadOnComplete !== undefined) {
                cl.Helper.GetCurrentScheme().CsvUploadOnComplete(data);
              }
              // Otherwise do this as default
              else {
                for (var i in data.rows) {
                  var row = cl.Helper.GetRowFromPid(i);
                  var markCells = cl.Helper.GetAllMarkCellsInRow(row);
                  for (var x in data.rows[i]) {
                     if (!cl.Helper.isEDSRegisterCell(markCells.eq(x)) && !cl.Helper.isEDSExamMarkCell(markCells.eq(x)) && !cl.Helper.isEDSExamMarkResitCell(markCells.eq(x)) ) // Just for EDSQ bypass changing Registration and Exam mark
                        cl.Action.ChangeCellValue(data.rows[i][x], markCells.eq(x), true);
                  }
                }
              }
            }
          });
        };

        /**
         * Request Awards
         */
        cl.Action.RequestAwards = function() {
          cl.Root.find(".imarkbook_awards").click(function() {
            var pupils = {};

            // Make sure the DB has all the marks saved.
            if (cl.UnsavedChanges) {
              $.fn.iDialog($.iMessage['Save before awards']);
              return;
            }

            // Get a list of all learners that passed.
            var count_pass = 0;
            var edsq_resit = 0;
            cl.Root.find(".imarkbook_table tbody tr.markRow:not(." + cl.ElementName.NoteRow + ")").each(function() {
              if (!cl.Helper.IsRowFrozen($(this))) {
                var row = $(this);
                var pid = '' + row.find("td.imarkbook_pid").html();
                var pass = row.find("span.imarkbook_pass").html();

                // Recalculate the status to make sure
                cl.Status(row);
                // Check if row is ready for moderation
                if (cl.Helper.IsRowReadyForModeration(row) === true) {
                  pupils[pid] = pass;
                  count_pass += 1;
                  // EDSQ Resit
                  if (cl.Helper.IsRowReadyForModerationEDSQResit(row) === true) {
                    edsq_resit +=1;
                  }
                }
                
              }
            });

            // Do we actually have any awards to request?
            if (count_pass === 0) {
              // No. Let's show an error message.'

              //
              // Count now many pupils have awards requested or have not passed.
              //
              var count_frozen = 0;
              var count_not_passed = 0;
              cl.Root.find(".imarkbook_table tbody tr").each(function() {
                var row = $(this);
                if (cl.Helper.IsRowFrozen(row)) {
                  count_frozen += 1;
                }
                var status = row.find("td.imarkbook_status").html();
                if (status) {
                  count_not_passed += 1;
                }
              });

              //
              // Give a useful error message.
              //
              $.fn.iDialog($.iMessage['No awards']);
              return;
            }
            if (edsq_resit != count_pass) //
//               {$.fn.iDialog($.iMessage['Need minimum one resit mark for every learner to request resit moderation']);return;}
             {alert("Need minimum one resit marks to request resit formative sampling " + edsq_resit); return;}
            if (confirm("Please confirm you have no conflicts of interest with any learners in this group")) {
              // Send that list to the server.
              $.fn.iAjax({
                type: 'POST',
                url: "?r=assessor/requestawards",
                blockMessage: $.iMessage['Requesting awards'] + '...',
                data: {
                  pupils: JSON.stringify(pupils),
                  unitId: cl.Data.unit
                },
                success: function(result) {
                  var requestedpids = result.requestedPids;
                  // Prevent further editing of the rows.
                  cl.Root.find(".imarkbook_table tbody tr").each(function() {
                    var row = $(this);
                    var pid = row.find('td:eq(0)').html();
                    if ($.inArray(Number(pid), requestedpids) === -1)
                      return true;
                    /* 1. Update status.
                     * 2. Remove .imarkbook_status class.
                     * 3. Remove .editable class.
                     * 4. Add .imarkbook_frozen class.*/

                    row.find(".imarkbook_status").html('0/0 - ' + $.iMessage['Award requested']);
                    row.find(".imarkbook_status").removeClass('imarkbook_status');
                    row.find(".editable").removeClass('editable');
                    cl.Helper.SetRowAsFrozen(row);
                    //In case of the unit with Test mark, make the test mark uneditable
                    if (cl.Data.test_mark !== false) {
                      row.find("td.imarkbook_test_markcell").removeClass('editable');
                    }
                  });
                }
              });
            }
          });

        };
        /**
         * Calculate Grades For DoYFinalGrade Unit
         */
        cl.Action.CalculateGrades = function() {
          cl.Root.find(".imarkbook_doy").click(function() {
            var pupils = {};
            var pupilsId = "";

            // Make sure the DB has all the marks saved.
            //                if (cl.UnsavedChanges) {
            //                    $.fn.iDialog($.iMessage['Save calculate final grades']);
            //                    return;
            //                }

            // Get a list of all learners that passed.

            cl.Root.find(".imarkbook_table tbody tr.markRow:not(." + cl.ElementName.NoteRow + ")").each(function() {
              if (!cl.Helper.IsRowFrozen($(this))) {
                var row = $(this);
                var pid = '' + row.find("td.imarkbook_pid").html();
                var pass = row.find("span.imarkbook_pass").html();

                // Recalculate the status to make sure
                cl.Status(row);
                pupils[pid] = pass;
                pupilsId = pupilsId + pid + ",";

                
              }
            });



            // Send that list to the server.
            $.fn.iAjax({
              type: 'POST',
              url: "?r=assessor/calculateGrade",
              blockMessage: $.iMessage['Calculate Grades'] + '...',
              data: {
                pupils: JSON.stringify(pupilsId),
                unitId: cl.Data.unit
              },
              success: function(result) {
                for (key in result[0]) {
                  console.log(key + ": " + filters[0][key]);

                }
                for (var i in result.pids) {
                  var row = cl.Helper.GetRowFromPid(i);
                  var markCells = cl.Helper.GetAllMarkCellsInRow(row);
                  for (var x in result.pids[i]) {
                    cl.Action.ChangeCellValue(result.pids[i][x], markCells.eq(x), true);
                  }
                }
                
              }
            });
          });
        };

        

        cl.Action.SetTabHandlers = function() {
          var closeHandler = function(confirmCallback, confirmId) {
            if (cl.UnsavedChanges === true) {
              $.fn.iConfirm({
                message: $.iMessage['JS_MARKSHEET_UNSAVED_CHANGES_CONFIRM_TAB_CLOSE'],
                callback: function() {
                  confirmCallback(true, confirmId);
                },
                okButton: $.iMessage['SINGLE_WORD_YES'],
                cancelButton: $.iMessage['SINGLE_WORD_NO']
              });
            } else {
              confirmCallback(true, confirmId);
            }
          };
          var reloadHandler = function(confirmCallback, confirmId) {
            if (cl.UnsavedChanges === true) {
              $.fn.iConfirm({
                message: $.iMessage['JS_MARKSHEET_UNSAVED_CHANGES_CONFIRM_TAB_RELOAD'],
                callback: function() {
                  confirmCallback(true, confirmId);
                },
                okButton: $.iMessage['SINGLE_WORD_YES'],
                cancelButton: $.iMessage['SINGLE_WORD_NO']
              });
            } else {
              confirmCallback(true, confirmId);
            }
          };
          cl.Root.registerRemoveTabListener(closeHandler);
          cl.Root.registerReloadTabListener(reloadHandler);
        };
        /////////////////////////////////////////////////////
        //
        //    HTML -- Build the core HTML of the markbook.
        //            This is the fist step in making the markbook.
        //
        ///////////

        cl.HTML = {};
        cl.HTML.colStyle = function(n) {
          return cl.UniqueId + '-column-' + n;
        }
        cl.HTML.testcolStyle = function(n) {
          return cl.UniqueId + '-test_markcell-' + n;
        }
        cl.HTML.TableHead = function() {
          // .slice -> Copy the array while poping out the first 4 elements (Id,Note,Forename,Surname).
          var thead = cl.Data.head.slice(4);
          var i, styles, marksHeaders = '';
          //if (cl.Data.test_mark !== false) {requestExamAward='<th>Request Award</th>'};
          for (i = 0; i < thead.length; i++) {
            styles = cl.HTML.colStyle(i) + ' {sorter: false} ' + cl.ElementName.MarkHeader;
            marksHeaders += '<th class="' + styles + '">' + thead[i] + '</th>';
          }

          return '<tr>' +
            '<th>' + $.iMessage['ID'] + '</th>' +
            '<th>' + $.iMessage['Note'] + '</th>' +
            '<th class="imarkbook_name">' + $.iMessage['Forename'] + '</th>' +
            '<th class="imarkbook_name">' + $.iMessage['Surname'] + '</th>' +
            '<th>' + $.iMessage['Status'] + '</th>' +
            marksHeaders +
            '</tr>';
        };

        /**
         * Generate the table body
         */
        cl.HTML.TableBody = function() {
          var i, j, rows, type, note, pupilId, forename, surname, marksCells;
          var requested, approved, statusCell, rowStyle, styles, noteIcon;

          // .slice -> Copy the array.
          rows = cl.Data.body.slice(0);

          function defaultColumnHandler(i, j, req) {
            styles = cl.HTML.colStyle(j);
            styles += ' ' + type;
            //if is exam and cells has not P and O
            if (req !== false) {
              styles += ' editable ';
            }
          }

          function defaultTestColumnHandler(i, j) {
            styles = cl.HTML.testcolStyle(j - 5);
            styles += ' ' + 'imarkbook_test_markcell';
          }

          for (i = 0; i < rows.length; i += 1) {
            pupilId = rows[i].shift();
            note = rows[i].shift();
            forename = rows[i].shift();
            surname = rows[i].shift();
            requested = rows[i].shift();
            approved = rows[i].shift();

            marksCells = '';
            //
            // Ensure data sanity.
            //
            if (cl.Data.test_mark !== false) { //unit with test
              if (!approved) {
                approved = false;
              }
              if (!requested) {
                requested = false;
              }
            } else {
              if (!approved || !approved.match(/20\d\d-\d\d-\d\d/)) {
                approved = false;
              }
              if (!requested || !requested.match(/20\d\d-\d\d-\d\d/)) {
                requested = false;
              }
            }

            /*
             * For each mark
             */
            var rowlength = rows[i].length;

            
            var number_critera = cl.Data.crit.length;
            if (cl.Data.test_mark !== false) {
            
              rowlength = number_critera; //- hidden_col; //1
            }


            //console.log (number_critera);

            // Get the max value from test_marks
            var max_test_value = 0;
            for (j = 0; j < rowlength; j += 1) {
              if (j > 4 && j < 10) {
                if (rows[i][j] > max_test_value) {
                  max_test_value = rows[i][j];
                }
              }
            }
            //alert (rowlength);
            // Begin looping through the cells
            for (j = 0; j < rowlength; j += 1) {
              // if (cl.Data.test_mark !== false && j < number_critera) {continue;};
              styles = "";
              

              if (cl.Data.test_mark === false || (cl.Data.test_mark !== false && j < number_critera)) {
                if (cl.Data.crit[j].type === null || cl.Data.crit[j].type === '') {
                  type = "criterion";
                }
                // Otherwise add the header
                else {
                  type = cl.Data.crit[j].type.toLowerCase().replace(' ', '-');
                } //bgvlado
              }



              // if is Exam Unit
              if (cl.Data.test_mark !== false) {

                // This is a criteria column
                if (j < number_critera) {
                  defaultColumnHandler(i, j, false); // add non-editable class
                }
                // if this is a result column
                else if (j == number_critera) {
                  if (rows[i][j] === null) {
                    rows[i][j] = 'N';
                  }

                  defaultTestColumnHandler(i, j); // add style'imarkbook_test_markcell'; 
                }
                // All other cells if whant to display
                else {
                  defaultColumnHandler(i, j, false);

                }

                if (j < number_critera) {
                  styles += ' examMarks test_re';
                  type = "";
                  if (j == 1) {
                    styles += ' finalMark';
                    type = "criterion";
                  }
                  if (j == 2) {
                    styles += ' resitMark';
                    type = "criterion";
                  }
                  if (j == 3) {
                    styles += ' resit2Mark';
                    type = "criterion";
                  }
                  if (j == 4) {
                    styles += ' resit3Mark';
                    type = "criterion";
                  }
                  if (j == 5) {
                    styles += ' resit4Mark';
                    type = "criterion";
                  }
                  if (j > 0) {
                    styles += ' realExam';
                    type = "criterion";
                  } // this is all except Mock
                }

                // if Cursworks not complete


              } // End Exam Unit
              //regular Unit
              else {
                if (cl.Data.crit[j].type === null || cl.Data.crit[j].type === '') {
                  type = "criterion";
                }
                defaultColumnHandler(i, j, true);
              }

              styles += ' ' + cl.ElementName.MarkCell;


              // Get the mark for this cell

              var currentMark = rows[i][j];
              if (cl.Data.test_mark !== false) {
                currentMark = rows[i][j];
                if (currentMark === null) {
                  currentMark = "-";
                }

              }
              if (currentMark === null)
                currentMark = cl.Helper.GetCurrentScheme().DefaultMark($(this));


              //if (currentMark === "N") {currentMark = "";}

              // Apply the style
              marksCells += '<td class="' + styles + '">' + currentMark + '</td>';
              
              // FFQ remove editable style
              if (cl.Helper.isFFQExamCell(styles)) {
                // console.log(styles);
                marksCells = marksCells.replace("editable  markCell", "");
                // console.log(marksCells);
              }
              var ffqFinalNotEnought = false;
              if (cl.Helper.isFFQFinalCell(styles) && currentMark >= 0 && currentMark < 50) {
                ffqFinalNotEnought = true;
              }
              if (cl.Helper.isFFQ2FinalCell(styles) && currentMark >= 0 && currentMark < 35) {
                ffqFinalNotEnought = true;
              }
            }

            // 5 days after marking row are freezed from request awards
            var coursework = true; // can request

            if (cl.Data.test_mark !== false) {

              if (rows[i][j] === false) {
                coursework = false;
              }

            }


            // Status.
            statusCell = "<td>Error: Could not figure out learner status</td>";
            var freeze = false;
            if (approved) {
              freeze = true;
              statusCell = '<td>0/0 - ' + $.iMessage['Award approved'] + ' ' + approved + '</td>';
            } else if (requested) {
              freeze = true;
              statusCell = '<td>0/0 - ' + $.iMessage['Award requested'] + ' ' + requested + '</td>';
            } else {
              statusCell = '<td class="imarkbook_status">*</td>';
            }
            if (note === '' || note === null) noteIcon = '';
            else noteIcon = '*';
            
            if (cl.Data.eds_reject[i][0] != null || cl.Data.dfs_reject[i][0] != null) {
              var rej_txt = cl.Data.eds_reject[i][0];
              if (cl.Data.dfs_reject[i][0] != null) rej_txt = cl.Data.dfs_reject[i][0];
              //  console.log(cl.Data.eds_reject[i][0]);

              freeze = true;
              statusCell = '<td class="imarkbook_status">Rejected on ' + rej_txt + '</td>';
            }

            // If registration unit exist and is not completed
            if (cl.Data.reg_unit[i] == 0) {
              freeze = true;
              statusCell = '<td class="imarkbook_status">Please register the candidates first by requesting formative sampling for the Registration Unit.</td>';
              marksCells = marksCells.replace(" editable ", "");
            }

            //ffq
            if (ffqFinalNotEnought) {
              freeze = true;
              statusCell = '<td class="imarkbook_status">Not enough marks</td>';
            }

            // Make the row.
            rowStyle = cl.ElementName.MarkRow;
            rowStyle += freeze ? ' imarkbook_frozen' : '';
            rowStyle += coursework ? '' : ' coursework_false';
            rows[i] = '<tr class = "' + rowStyle + ' rowReadyForModeration">' +
              '<td class="imarkbook_pid" width="30">' + pupilId + '</td>' +
              '<td class="' + cl.ElementName.NoteCell + '" width="20">' +
              '<center>' +
              noteIcon +
              '</center>' +
              '</td>' +
              '<td class="imarkbook_forenamecell">' + forename + '</td>' +
              '<td class="imarkbook_surnamecell">' + surname + '</td>' +
              statusCell + marksCells +
              '</tr>' +

              //adding new row to be used for markbook notes
              '<tr class="' + cl.ElementName.NoteRow + '">' +
              '<td class="imarkbook_pid">' + pupilId + '</td>' +
              '<td class="imarkbook_note" style="display:none">' + note + '</td>' +
              '<td>' + forename + '</td>' + '<td>' + surname + '</td>' +
              statusCell + marksCells +
              '</tr>';
          }
          return rows.join('');
        };
        cl.HTML.Init = function() {
          var thead, tbody;
          thead = cl.HTML.TableHead();
          tbody = cl.HTML.TableBody();
          // Add calculate Grades for DoY

          var markScheme = cl.Data.markingScheme;
          var calculatedoygrades = "";
          var disableupload = '';
          var disablerequest = '';
          var savebutton = '<a class="imarkbook_button imarkbook_save">' + $.iMessage['Save'] + '</a>&nbsp;&nbsp;';
          var requestbutton = '<a class="imarkbook_button imarkbook_awards">' + $.iMessage['Request Awards'] + '</a>';
          if (cl.Data.test_mark !== false) {
            requestbutton = '<a class="imarkbook_button imarkbook_awards">' + $.iMessage['Request Results'] + '</a>';
          }
          // For AWS registration
          if (cl.Data.unit == 2203 || cl.Data.is_reg_unit !== false) {
            requestbutton = '<a class="imarkbook_button imarkbook_awards">Request registration</a>';
          }
          if (cl.Data.unit == 2496) {
            requestbutton = '';
          }
          if (cl.Data.baseline !== 0) {
            requestbutton = '';
          }

          if (markScheme.search(/DUKEOFYORK_FINALGRADES/i) === 0) {
            // Add Calculate Grades Button
            calculatedoygrades = '<a class="imarkbook_button imarkbook_doy">Calculate Final Grades</a>&nbsp;&nbsp;';
            // disabble upload marks fromfiles
            disableupload = '<style>#' + cl.UniqueId + '_control .imarkbook_csv_up2{display:none;}</style>';

          }

          if (markScheme.search(/DUKEOFYORK_ALEVELQUALIFICATION/i) === 0 || markScheme.search(/DUKEOFYORK_COMPETENCYFRAMEWORK/i) === 0 || markScheme.search(/DUKEOFYORK_WORKEXPERIENCE/i) === 0 || markScheme.search(/DUKEOFYORK_GCSEQUALIFICATION/i) === 0 || markScheme.search(/DUKEOFYORK_TECHNICALQUALIFICATION/i) === 0) {
            disablerequest = '<style>#' + cl.UniqueId + '_control .imarkbook_awards{display:none;}</style>';
            //savebutton = "";

          }
          //If school is suspended disable save and request awards
          if (cl.Data.suspend_school == 1) {
            savebutton = "";
            requestbutton = "";
          }




          // 1. Insert Base HTML.
          cl.Root.html(
            '<div class="imarkbook_description unselectable"></div>' +
            '<center id="' + cl.UniqueId + '_control">' +
            '  <span style="color: #a19; font-style: italic;" class="imarkbook_csv_up2">' + $.iMessage['CSV Files'] + ': </span>' +
            '  <input id="' + cl.UniqueId + '-csvfile" type="file" name="csvfile" style="border: 1px solid #ccc; padding: 3px;" class="imarkbook_csv_up2"/> ' +
            '  <a class="imarkbook_button imarkbook_csv_up imarkbook_csv_up2">' + $.iMessage['Upload Marks'] + '</a> &nbsp;&nbsp;' +
            '  <a class="imarkbook_button imarkbook_csv_down">' + $.iMessage['Download Marks'] + '</a>' +
            '  <hr/>' +
            '  <table class="imarkbook_edit_table">' +
            '    <tr>' +
            '      <td class="imarkbook_edit_mode">' +
            '<input type="radio" name="' + cl.UniqueId + '-edit-mode" class="imarkbook_std_mode" checked="checked"/>' + $.iMessage['Standard mode'] + '<br/>' +
            '<input type="radio" name="' + cl.UniqueId + '-edit-mode" class="imarkbook_clk_mode"/>' + $.iMessage['One-Click edit'] +
            '      </td>' +
            '      <td style="vertical-align: middle; white-space: nowrap;">' +
            '        <input type="text" class="imarkbook_selected_value" disabled="disabled" size="3"/>' +
            '        <a class="imarkbook_button imarkbook_select_all">' + $.iMessage['Select All'] + '</a> &nbsp;&nbsp;' +
            '         ' + savebutton + calculatedoygrades + requestbutton +
            '      </td>' +
            '    </tr>' +
            '  </table>' +
            '</center>' +
            '<div style="text-align: right; float:right">' +
            '  <button class="' + cl.UniqueId + '-prev-page" disabled="disabled">&lt;&lt; ' + $.iMessage['Prev Page'] + '</button>' +
            '  <button class="' + cl.UniqueId + '-next-page">' + $.iMessage['Next Page'] + ' &gt; &gt;</button>' +
            '</div>' +
            '<div style="width: 100%; padding: 5px">' +
            '  <table id="' + cl.UniqueId + '-table" class="imarkbook_table tablesorter" cellspacing="0">' +
            '    <thead>' + thead + '</thead>' +
            '    <tbody>' + tbody + '</tbody>' +
            '    <tfoot>' + thead + '</tfoot>' +
            '  </table>' +
            '</div>' +
            '<div style="text-align: right">' +
            '  <button class="' + cl.UniqueId + '-prev-page" disabled="disabled">&lt;&lt;' + $.iMessage['Prev Page'] + '</button>' +
            '  <button class="' + cl.UniqueId + '-next-page">' + $.iMessage['Next Page'] + ' &gt; &gt;</button>' +
            '</div>' + disableupload + disablerequest
          );

          // Make all text unselectable    
          cl.Root.find('.imarkbook_table').find('td, th').addClass('unselectable');

          // Fill in all blank values
          cl.Root.find('.imarkbook_table td.' + cl.ElementName.MarkCell).each(function() {
            if ($(this).html() === '') {
              // If these are test mark cells then fill in with test mark data
              if ($(this).hasClass('imarkbook_test_requestedcell'))
                $($(this).html(cl.TestRequests.requestedCellDefault));
              else if ($(this).hasClass('imarkbook_test_made_availablecell'))
                $($(this).html(cl.TestRequests.madeAvailableCellDefault));
              else { // Else get default mark from current marking scheme
                var defaultVal = cl.Helper.GetCurrentScheme().DefaultMark($(this));
                $(this).html(defaultVal);
              }
              
            }
          });

          // 2. Update the status.
          cl.Root.find('.imarkbook_table tr:not(.' + cl.ElementName.NoteRow + ')').each(function() {
            cl.Status($(this));
          });


          //
          //  Horizontal Scrolling.
          //

          // Hide all columns but the first 5.


          var numCols = cl.Data.head.length + 1;
          var i, colsHidden = [];
          var visiblecol = 10;
          if (cl.Data.test_mark !== false) {
            visiblecol = 15;
          }
          for (i = visiblecol + cl.SkipCols; i < numCols; i++) {
            colsHidden.push(i + 1);
          }

          $('#' + cl.UniqueId + '-table').columnManager({
            colsHidden: colsHidden
          });

          // Navigation controls.
          var initialPosition = cl.SkipCols + 1
          var pos = initialPosition;
          $('.' + cl.UniqueId + '-next-page').click(function() {
            // Unselect Cell to prevent bugs
            // Currently if a hidden cell is selected and an arrow key
            // is pressed it can remove the first 5 essential columns
            // It would be nice to rewrite the hide/show columns
            // functionality to allow cell highlights to maintain
            cl.Action.UnSelectCell();
            // Keep 0, 1, 2, 3, 4
            pos += 5;
            $('#' + cl.UniqueId + '-table').hideColumns([pos - 5, pos - 4, pos - 3, pos - 2, pos - 1]);
            $('#' + cl.UniqueId + '-table').showColumns([pos + 5, pos + 6, pos + 7, pos + 8, pos + 9]);

            // Disable and enable buttons.
            $('.' + cl.UniqueId + '-prev-page').removeAttr('disabled');
            if (pos > numCols - visiblecol) {
              $('.' + cl.UniqueId + '-next-page').attr('disabled', 'disabled');
            }
          });
          $('.' + cl.UniqueId + '-prev-page').click(function() {
            // Unselect Cell to prevent bugs
            // See above.
            cl.Action.UnSelectCell();
            // Keep 0, 1, 2, 3, 4
            $('#' + cl.UniqueId + '-table').showColumns([pos - 5, pos - 4, pos - 3, pos - 2, pos - 1]);
            $('#' + cl.UniqueId + '-table').hideColumns([pos + 5, pos + 6, pos + 7, pos + 8, pos + 9]);
            pos -= 5;

            // Disable and enable buttons.
            $('.' + cl.UniqueId + '-next-page').removeAttr('disabled');
            if (pos === initialPosition) {
              $('.' + cl.UniqueId + '-prev-page').attr('disabled', 'disabled');
            }
          });

        };

        /////////////////////////////////////////////////////
        //
        //    MAIN PLUGIN
        //
        ///////////

        /*
         * Functions are divided into groups mainly so I can find them more easily.
         */

        cl.Main = function() {
          // Set the current marking scheme
          cl.Helper.SetCurrentScheme(cl.Data.markingScheme);
          cl.Helper.SetCurrentUserInterface(cl.Helper.GetCurrentScheme().getUserInterfaceName());

          // Build the core HTML.
          cl.HTML.Init();
          // Run the marking scheme's current Init
          if (cl.Helper.GetCurrentScheme().Init !== undefined)
            cl.Helper.GetCurrentScheme().Init();

          // Effects that improve usability.
          cl.Effect.Sorting();
          cl.Effect.ShowCriterion();

          // Things that are done many times, based on user action.
          cl.Action.SaveClick();
          cl.Action.EditMode();
          cl.Action.CsvUpload();
          cl.Action.CsvDownload();
          cl.Action.RequestAwards();

          cl.Action.CalculateGrades();
          cl.Action.ToggleNote();
          /*cl.Action.RequestTest();*/

          // Set the input actions
          cl.Action.GrabKeystrokes();
          cl.Action.Changes();
          cl.Action.Hover();
          cl.Action.Click();
          cl.Action.SetTabHandlers();


        };

        cl.Main();
      };

    }(jQuery));/*
 * iMarksheet provides the editable marksheet for the site
 */

(function ($) {	

	ingotClass.iMarksheetComponent = function(options){
		this.callParentMethod('constructor', [options]);
		this.setMarksheetClosure(options.marksheetClosure);
	};
	
	ingotClass.iMarksheetComponent.inheritsFrom(ingotClass.ingotObject);
	
	/*
	 * Set the marksheet Closure
	 */
	ingotClass.iMarksheetComponent.prototype.setMarksheetClosure = function(closure){
		this.cl = closure;
	};
	
	/*
	 * Get the marksheet closure
	 */
	 ingotClass.iMarksheetComponent.prototype.getMarksheetClosure = function(){
		 return this.cl;
	 };	
}(jQuery));
/**
 * iMarksheetUserInterface provides the editable marksheet for the site
 * 
 */

(function ($) {	

	/**
	 * Constructor
	 */
	ingotClass.iMarksheetUserInterface = function(options){
		this.callParentMethod('constructor', [options]);
		this.setMarksheetClosure(options.marksheetClosure);
		this.actionTypes = [];
		this.addActionType(this.documentKeydown);
		this.addActionType(this.documentKeypress);
		this.addActionType(this.documentKeyup);			
	};
	// Inherit from ingotObject
	ingotClass.iMarksheetUserInterface.inheritsFrom(ingotClass.iMarksheetComponent);
	
	/*
	 * Required Methods
	 */
	ingotClass.iMarksheetUserInterface.prototype.requiredMethods = function(){
		return [
			'documentKeydown'
		];
	};

	/*
	 * get action types
	 */
	ingotClass.iMarksheetUserInterface.prototype.getActionTypes = function(){	
		return this.actionTypes;
	};
	
	/*
	 * setKeystrokes
	 */
	ingotClass.iMarksheetUserInterface.prototype.addActionType = function(actionClass){	
		var newObject = new actionClass({
			marksheetClosure: this.getMarksheetClosure()
		});
		this.actionTypes.push(newObject);
	};
	
	/*
	 * bind all events
	 */
	ingotClass.iMarksheetUserInterface.prototype.bindAllEvents = function(){
		var actionTypes = this.getActionTypes();
		for(var x in actionTypes){
			actionTypes[x].bindEvent();
		}
	};	
	
	/**
	 * ActionType Container
	 */
	ingotClass.iMarksheetUserInterface.ActionTypeContainer = function(options){
		this.callParentMethod('constructor', [options]);
		this.setMarksheetClosure(options.marksheetClosure);
	};
	ingotClass.iMarksheetUserInterface.ActionTypeContainer.inheritsFrom(ingotClass.iMarksheetComponent);
	
	/*
	 * Set Required Methods
	 */
	ingotClass.iMarksheetUserInterface.ActionTypeContainer.prototype.requiredMethods = function(){
		return [
			'getAppliedElements',
			'getEventType',
			'getEventHandler'
		];
	};


	/*
	 * Bind an event
	 * Passes the closure as second paramter
	 */
	ingotClass.iMarksheetUserInterface.ActionTypeContainer.prototype.bindEvent = function(){
		this.getAppliedElements()[this.getEventType()](this.getEventHandler());
	};
		
	/*
	 * Bind an event
	 * Passes the closure as second paramter
	 */
	ingotClass.iMarksheetUserInterface.ActionTypeContainer.prototype.getKeyActionEventHandler = function(){
		var cl = this.getMarksheetClosure();
		var thisObject = this;
		return function(e){
			// Only process if this tab is visible
			if(!cl.Root.is(':visible'))
				return true;
			var result;
			// Check if we can run "beforeKeys"
			if(typeof thisObject.beforeKeys === 'function'){
				result = thisObject.beforeKeys(e, cl, thisObject);
			}
			// Check if we can search for the specific key
			if(result === false)
				return false;

			var calledKey = e.which.toString();	
			if(typeof thisObject[calledKey] === 'function'){
				result = thisObject[calledKey](e, cl, thisObject);
			}

			// Check if we can run "afterKeys
			if(result === false)
				return false;

			if(typeof thisObject.afterKeys === 'function')
				thisObject.afterKeys(e, cl, thisObject);
		};
	};
	
	
	/**
	 * Document Keyup
	 */
	ingotClass.iMarksheetUserInterface.prototype.documentKeyup = function(options){
		this.callParentMethod('constructor', [options]);
		this.setMarksheetClosure(options.marksheetClosure);
	};
	ingotClass.iMarksheetUserInterface.prototype.documentKeyup.inheritsFrom(ingotClass.iMarksheetUserInterface.ActionTypeContainer);
	ingotClass.iMarksheetUserInterface.prototype.documentKeyup.prototype.getEventHandler = function(e){	
		return this.getKeyActionEventHandler(e);
	};
	ingotClass.iMarksheetUserInterface.prototype.documentKeyup.prototype.getAppliedElements = function(){
		return $(document);
	};		
	// Set applied events
	ingotClass.iMarksheetUserInterface.prototype.documentKeyup.prototype.getEventType = function(){
		return 'keyup';
	};
	// Ctrl Key
	ingotClass.iMarksheetUserInterface.prototype.documentKeyup.prototype['17'] = function(e, cl, uiObject){
		cl.IsCtrlKeyPressed = false;
		return false;
	};		
	// Shift Key
	ingotClass.iMarksheetUserInterface.prototype.documentKeyup.prototype['16'] = function(e, cl, uiObject){
		cl.IsShiftKeyPressed = false;
		return false;
	};
	
	/**
	 * Document Keypress
	 */
	ingotClass.iMarksheetUserInterface.prototype.documentKeypress = function(options){
		this.callParentMethod('constructor', [options]);
		this.setMarksheetClosure(options.marksheetClosure);
	};
	ingotClass.iMarksheetUserInterface.prototype.documentKeypress.inheritsFrom(ingotClass.iMarksheetUserInterface.ActionTypeContainer);
	ingotClass.iMarksheetUserInterface.prototype.documentKeypress.prototype.getEventHandler = function(){	
		return this.getKeyActionEventHandler();
	};
	// Set applied events
	ingotClass.iMarksheetUserInterface.prototype.documentKeypress.prototype.getAppliedElements = function(){
		return $(document);
	};		
	// Set applied events
	ingotClass.iMarksheetUserInterface.prototype.documentKeypress.prototype.getEventType = function(){
		return 'keypress';
	};	
	/**
	 * Document Keydown
	 */
	ingotClass.iMarksheetUserInterface.prototype.documentKeydown = function(options){
		this.callParentMethod('constructor', [options]);
		this.setMarksheetClosure(options.marksheetClosure);
	};
	ingotClass.iMarksheetUserInterface.prototype.documentKeydown.inheritsFrom(ingotClass.iMarksheetUserInterface.ActionTypeContainer);
	ingotClass.iMarksheetUserInterface.prototype.documentKeydown.prototype.getEventHandler = function(){	
		return this.getKeyActionEventHandler();
	};
	// Set applied events
	ingotClass.iMarksheetUserInterface.prototype.documentKeydown.prototype.getAppliedElements = function(){
		return $(document);
	};		
	// Set applied events
	ingotClass.iMarksheetUserInterface.prototype.documentKeydown.prototype.getEventType = function(){
		return 'keydown';
	};
	// Ctrl Key
	ingotClass.iMarksheetUserInterface.prototype.documentKeydown.prototype['17'] = function(e, cl, uiObject){
		cl.IsCtrlKeyPressed = true;
	};		
	// Shift Key
	ingotClass.iMarksheetUserInterface.prototype.documentKeydown.prototype['16'] = function(e, cl, uiObject){
		cl.IsShiftKeyPressed = true;
	};				
	
	/**
	 * Load a new Marksheet User Interface
	 * 
	 * @param string type the scheme name
	 * @param map options a map of options to pass to the scheme (ususally the closure of the marksheet)
	 */
	ingotClass.iMarksheetUserInterface.getUserInterface = function (type, params) {
		var constr = type;

		// Throw an error if the marking scheme has not been defined
		var targetType = typeof ingotClass.iMarksheetUserInterface[constr];
		if (targetType === undefined)
			throw new ingotError('The requested Marksheet User Interface - ' + constr + ' - does not exist');
		if (targetType !== 'function')
			throw new ingotError('The requested Marksheet User Interface - ' + constr + ' - must be a function');

		// create a new object using the factory
		var newUserInterface = new ingotClass.iMarksheetUserInterface[constr](params);
		newUserInterface.name = constr;
						
		// Return
		return newUserInterface;
	};
		
}(jQuery));
/*
 * iMarksheet provides the editable marksheet for the site
 */

(function ($) {	

	ingotClass.iMarkingScheme = function(options){
		this.callParentMethod('constructor', [options]);
		this.setMarksheetClosure(options.marksheetClosure);
	}
	
	ingotClass.iMarkingScheme.inheritsFrom(ingotClass.iMarksheetComponent);
	
	/*
	 * Set the marksheet Closure
	 */
	ingotClass.iMarkingScheme.prototype.setMarksheetClosure = function(closure){
		this.cl = closure;
	}
	
	/*
	 * Get the marksheet closure
	 */
	 ingotClass.iMarkingScheme.prototype.getMarksheetClosure = function(){
		 return this.cl;
	 }
	
	ingotClass.iMarkingScheme.prototype.requiredMethods = function(){
		return [
			'DefaultMark', 
			'GetValidMarks',
			'ForceMarkToUppercase',
			'Errors',
			'Status',
			'getUserInterfaceName',
			'NotPassedMessage'
		];
	};
	
	/**
	 * Get the additionalMarkData object
	 */
	ingotClass.iMarkingScheme.prototype.getAdditionalMarkData = function() {
		var cl = this.getMarksheetClosure();
		return cl.AdditionalMarkData.getObject();
	};
	
	/**
	 * Provide a default method to return the default mark if KValidMarks are used to define the marks
	 */
	/*ingotClass.iMarkingScheme.prototype.DefaultMark = function(cell) {
		var validMarks = this.getMarksheetClosure().Data.validMarks;
		if(!$.isPlainObject(validMarks))
			throw ingotError('validMarks is not being populated by KValid marks, or a custom defaultMark method is required');
		var mark = cell.html();
		var cellType = this.GetCellType(cell);
		if(String(this.GetCellType(cell)).toLowercase() === String(cellType).toLowerCase()){
			for(var x in validMarks){
				if(x.toLowerCase() === cellType.toLowerCase()){
					if(validMarks[x]['defaultMark'] !== undefined){
						return validMarks[x]['defaultMark'];
					}
				}
			}
		}
		throw new fatalError('Could not find the default mark');
	};*/	
	
	/**
	 * Return the cell type, override for more cell types
	 */
	/*ingotClass.iMarkingScheme.prototype.GetCellType = function(cell) {
		return 'Criterion';
	};*/
	
	/**
	 * Check if this has passed all criteria (not tests)
	 */
	ingotClass.iMarkingScheme.prototype.IsRowReadyForModeration = function(row) {
		var mark, passed = true, cl = this.getMarksheetClosure();
		row.find('td.markCell:not(.imarkbook_test_markcell)').each(function () {
                        if (cl.Data.test_mark !== false) {
                            if (cl.Helper.GetCurrentScheme().HasTestCellPassed($(this)) !== true){
				passed = false;
				return false;
                            }
                        }
                        else {
                            if (cl.Helper.GetCurrentScheme().HasCellPassed($(this)) !== true){
                                    passed = false;
                                    return false;
                            }
                        }
		});
		return passed;
	};
	
	/**
	 * Return the name of the user interface to use
	 * Override this as necessary
	 */
	ingotClass.iMarkingScheme.prototype.getUserInterfaceName = function(){
		return 'Default';
	}
	
	/**
	 * Calculate the row's status
	 * 
	 * Override this if necessary for unusual frameworks
	 */
	ingotClass.iMarkingScheme.prototype.Status = function(row){
		var numberOfStages = 2, gradeString = '', grade = '', cl = this.getMarksheetClosure();
		// Unset this row for moderation until its new status is calculated
		cl.Helper.UnsetRowReadyForModeration(row);
		// if there is a tesk mark then add one to the number of stages
		if(cl.Data.test_mark !== false)
			numberOfStages = 5;
		// If criteria not complete
		if(this.IsRowReadyForModeration(row) === false){
                
                ////////////////////// Exam Marks

                    if(cl.Data.test_mark !== false) {
                        
                        grade = this.GetTestGrade(row);
                        var mark;
                        mark = this.cl.Helper.GetTestMarkForRow(row);
                        
                        // If this Baseline test
                        if (cl.Data.baseline == 1) {
                            cl.Helper.SetRowCriteriaAsUneditable(row);
                            numberOfStages = 2;
                            if(mark > 0){ 
                                cl.Helper.SetRowCriteriaAsUneditable(row);
                                cl.Helper.SetRowReadyForModeration(row);
                                // Only freeze the test mark if the test has been completed online (otherwise leave it open for editing)
                                if(cl.Helper.HasTestBeenMadeAvailable(row) !== false) 
                                        cl.Helper.SetRowTestMarkAsEditable(row);
                                gradeString = grade;
                                gradeString = '- (' + gradeString + ')';
                                return "<b>"+numberOfStages+"/"+numberOfStages+" - </b> " +$.iMessage['The Baseline Exam is complete'];
                            }
                            else if(cl.Helper.HasTestRequested(row) === true){
                                cl.Helper.SetRowCriteriaAsUneditable(row);
                                return "<b>1/"+numberOfStages + " - </b>Not yet taken the test";
                            }
                            else{
                                cl.Helper.SetRowCriteriaAsUneditable(row);
                                if(!row.hasClass('imarkbook_frozen'))
                                        cl.Helper.SetRowTestMarkAsEditable(row);
                                    return "<b>1/"+numberOfStages + " - </b>Not yet taken the test";
                                //return "<b>1/"+numberOfStages + " - </b> Type <b>O</b> for Online Test.";//$.iMessage['TEST_NOT_YET_PASSED']	
                            }
                            
                        } 
                        // If this Exam test
                        else {
                    
                            numberOfStages = 2;
//                            
                            if(grade !== false){
                                    cl.Helper.SetRowCriteriaAsUneditable(row);
                                    // Only freeze the test mark if the test has been completed online (otherwise leave it open for editing)
                                    if(cl.Helper.HasTestBeenMadeAvailable(row) !== false) 
                                            cl.Helper.SetRowTestMarkAsEditable(row);
                                    gradeString = grade;
                                    gradeString = '- (' + gradeString + ')';                                    

                            }
                            // Else If test not requested
                            else if(cl.Helper.HasCourseworkFalse(row) === true && cl.Helper.HasTestMade(row) === true){
                                return "<b>"+numberOfStages+"/"+numberOfStages+" - </b> Marking in process";
                            }
                            // Else If test has made
                            else if(cl.Helper.HasTestMade(row) === true){
                                cl.Helper.SetRowReadyForModeration(row);
                                // return the ready for moderation status
                                return "<b>"+numberOfStages+"/"+numberOfStages+" -  "+ $.iMessage['Ready to Request Result'] +"</b>"; 
                            }

                            // Else If test not made available
                            //else if(cl.Helper.HasTestBeenMadeAvailable(row) === true){
    //                                    cl.Helper.SetRowTestMarkAsFrozen(row);
    //                                    cl.Helper.SetRowCriteriaAsUneditable(row);
                            //    return "<b>4/"+numberOfStages+" - </b> "+$.iMessage['Test are available'];
                            //}

                            // Else If test Has Approved and ready to take
                            //else if(cl.Helper.HasTestApproved(row) === true){
                                //cl.Helper.SetRowTestMarkAsFrozen(row);
                                //cl.Helper.SetRowCriteriaAsEditable(row);
                             //   return "<b>31/"+numberOfStages+" - </b> "+$.iMessage['Test has been approved'];
                            //}
                            // Else If test not requested
                            //else if(cl.Helper.HasTestRequested(row) === true){
                            //    return "<b>2/"+numberOfStages+" - </b> "+$.iMessage['Test has NOT been requested'];
                            //}
                            

                            // Else If test not yet passed
                            else{
                                cl.Helper.SetRowCriteriaAsUneditable(row);
                                if(!row.hasClass('imarkbook_frozen'))
                                    cl.Helper.SetRowTestMarkAsEditable(row);
                                    return "";
                            }
                            return "" ;
                        }
                        
                
                    }
                    //////// If reqular Marks    
                    else {
                        cl.Helper.SetRowCriteriaAsEditable(row);
                        cl.Helper.SetRowTestMarkAsFrozen(row);
                        return "<b>1/"+numberOfStages+" - </b> " + this.NotPassedMessage();
                    }
                        
		}
		// If ready for moderation then set row as ready
		cl.Helper.SetRowReadyForModeration(row);
		// return the ready for moderation status
                if(cl.Data.test_mark !== false && ((cl.Data.unit !==  1488 || cl.Data.unit !== 1489  || cl.Data.unit !== 1490))) {
                    return "<b>"+numberOfStages+"/"+numberOfStages+" - </b> "+this.getReadyForModerationMessage()+gradeString; 
                }
                
                // if row is market with SSS but not saved
                if (cl.Helper.IsRowReadyToSave(row)) return "<b>"+numberOfStages+"/"+numberOfStages+" - </b> "+ $.iMessage['Ready to Save'] +"</span>"; 
		return "<b>"+numberOfStages+"/"+numberOfStages+" - </b> "+this.getReadyForModerationMessage()+gradeString+"</span>"; 
	}
	
	/*
	 * Check for errors in marks
	 */
	ingotClass.iMarkingScheme.prototype.Errors = function (row) {
		var errors = 0;
		var cl = this.getMarksheetClosure();
		row.find('td.markCell:not(.imarkbook_test_markcell)').each(function () {
			if (cl.Helper.IsMarkValid($(this).html(), $(this)) === false) {
				$(this).addClass('imarkbook_error');
				errors += 1;
			}
		});
		if (errors) {
			return "0/0 - "+$.iMessage['Invalid marks'];
		}

		return false;
	};
	
	/*
	 * Check if this has passed the test
	 * @param jqueryObject row the row to check
	 * @return mixed false if not passed or the grade as a string 
	 */
	ingotClass.iMarkingScheme.prototype.GetTestGrade = function(row) {
		var mark = '', GT;
		mark = this.cl.Helper.GetTestMarkForRow(row);
                
		if(mark === null)
			return false;
		GT = this.cl.Data.testGradeThresholds;
                
		for(var x in GT){
			if (mark >= GT[x].lowValue && mark <= GT[x].highValue){
				if(GT[x].pass == true)
					return GT[x].gradeName;
				//return false;//BESHE commentarnoto
			}
		}
		return false;
	};
	
	/**
	 * Return the ready for moderation message
	 * Override if an alternative message is wanted
	 */
	ingotClass.iMarkingScheme.prototype.getReadyForModerationMessage = function (row){
		return $.iMessage['Ready for moderation'];
	};

	/**
	 * Load a new scheme
	 * 
	 * @param string type the scheme name
	 * @param map options a map of options to pass to the scheme (ususally the closure of the marksheet)
	 */
	ingotClass.iMarkingScheme.getScheme = function (type, params) {
		var constr = type;

		// Throw an error if the marking scheme has not been defined
		var targetType = typeof ingotClass.iMarkingScheme[constr];
		if (targetType === undefined)
			throw new ingotError('The requested Markscheme - ' + constr + ' - does not exist');
		if (targetType !== 'function')
			throw new ingotError('The requested Markscheme - ' + constr + ' - must be a function');

		// create a new object using the factory
		var newScheme = new ingotClass.iMarkingScheme[constr](params);
		newScheme.name = constr;
				
		// Return
		return newScheme;
	};
	
}(jQuery));
/*
 * Drag and drop files for upload
 */

(function ($) {
	
	/**
	* Drag and drop files plugin
	*/
	$.fn.iDragAndDropFiles = function(options, parameter){
		return $.fn.iPlugin.handleObjectAttachedToElement(this, '_iDragAndDropFilesObject', options, parameter);
	};

	/**
	* Drag and drop files plugin
	*/
	$.fn.iDragAndDropFilesImageDropper = function(options, parameter){
		if($.isPlainObject(options)){
			var defaultOptions = {
				HideByDefault: false,
				//PassiveBackgroundMessage: '',
				behaviours: [
					//ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDefaultDragAndDropFilesBox,
					ingotClass._iDragAndDropFilesObject.PresetBehaviours.iShowDragAndDropFilesUploadDialog,
					ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDefaultDragAndDropFilesBackgroundText,
					ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDragAndDropFilesDisplayImage
				],
				classes:{
					iDefaultDragAndDropFilesBoxCss: 'iDefaultDragAndDropFilesBoxCss',
					iDefaultDragAndDropFilesBoxDragEnterStateCss: 'iDefaultDragAndDropFilesBoxDragEnterStateCss',
					iDefaultDragAndDropFilesBoxDefaultStateCss: 'iDefaultDragAndDropFilesBoxDefaultStateCss'
				}
			};
			$.extend(defaultOptions, options);
		}
		else{
			defaultOptions = options;
		}
		return $.fn.iPlugin.handleObjectAttachedToElement(this, '_iDragAndDropFilesObject', defaultOptions, parameter);
	};
			
	/**
	 * Drag and Drop files object
	 * 
	 * options:
	 *  - HideByDefault bool whether to hide the object when not active
	 * 
	 */
	ingotClass._iDragAndDropFilesObject = function(options, command, parameter){
		this.callParentMethod('constructor', [options, command, parameter]);	
		this.Init();
	};
	ingotClass._iDragAndDropFilesObject.inheritsFrom(ingotClass.ingotObject);

	/*
	 * Default Options
	 */
	ingotClass._iDragAndDropFilesObject.prototype.getDefaultOptions = function (){
		return {
			Root: null,
			TargetBox: null,
			BackgroundText: null,
			IsVisible: false,
			IsAnimatingShow: false,
			IsAnimatingHide: false,
			ReceivedFiles: {},
			UploadDialog: null,
			DropCallback: function (){},
			UploadStartedCallback: function(){},
			FileProgressCallback: function(){},
			behaviours: [
				ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDefaultDragAndDropFilesBox,
				ingotClass._iDragAndDropFilesObject.PresetBehaviours.iShowDragAndDropFilesUploadDialog,
				ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDefaultDragAndDropFilesBackgroundText
			],
			classes:{
			},
			height: '100px',
			width: null,
			HideByDefault: true,
			PassiveBackgroundMessage: 'Drag Files Here',
			ActiveBackgroundMessage: 'Release Files to Upload',
			showDroppableBoxDuration: 300,
			hideDroppableBoxDuration: 300,

			/* FileDrop params*/
			fallback_id: 'upload_button',   // an identifier of a standard file input element
			url: '',            // upload handler, handles each file separately
			paramname: 'userfile',          // POST parameter name used on serverside to reference file
			withCredentials: true,          // make a cross-origin request with cookies
			data: '',
			headers: {          // Send additional request headers
				'header': 'value'
			},
			error: function(err, file) {
				switch(err) {
					case 'BrowserNotSupported':
						alert('browser does not support html5 drag and drop');
						break;
					case 'TooManyFiles':
						// user uploaded more than 'maxfiles'
						break;
					case 'FileTooLarge':
						// program encountered a file whose size is greater than 'maxfilesize'
						// FileTooLarge also has access to the file which was too large
						// use file.name to reference the filename of the culprit file
						break;
					case 'FileTypeNotAllowed':
						// The file type is not in the specified list 'allowedfiletypes'
					default:
						break;
				}
			},
			allowedfiletypes: ['image/jpeg','image/png','image/gif'],   // filetypes allowed by Content-Type.  Empty array means no restrictions
			maxfiles: 25,
			maxfilesize: 20    // max file size in MBs
		};
	};
	
	/**
	 * Check if the browser supports drag and drop
	 */
	ingotClass._iDragAndDropFilesObject.IsDragAndDropSupported = function(){
		if('draggable' in document.createElement('span'))
			return true;
		return false;
	};

	/**
	 * Stop propagation and default actions
	 */
	ingotClass._iDragAndDropFilesObject.prototype.NoDefault = function(event){
		event.stopPropagation();
		event.preventDefault();
	};

	/**
	 * Initialise
	 */
	ingotClass._iDragAndDropFilesObject.prototype.Init = function(){
		if(ingotClass._iDragAndDropFilesObject.IsDragAndDropSupported() === true){		
			// Ensure the dataTransfer property is passed
			// with event objects.  Adds overhead.
			// http://api.jquery.com/category/events/event-object/
			jQuery.event.props.push("dataTransfer");


			// Set Root and classes
			this.getOptions().TargetBox = this.getOptions().Root;
			if(this.getOptions().HideByDefault === true)
				this.HideBox();
			this.getOptions().TargetBox.addClass('iDragAndDropFilesBox');
			if(this.getOptions().height !== null){
				this.getOptions().TargetBox.css('height', this.getOptions().height);
			}
			if(this.getOptions().width !== null)
				this.getOptions().TargetBox.css('width', this.getOptions().width);
			//this.getOptions().TargetBox.addClass(this.getOptions().classes.iDefaultDragAndDropFilesBoxCss);
					
			// Trigger Init for behaviours
			this.TriggerBehaviours('Init');


			// Set default state
			this.setDefaultState();


			// Add droppable overlay
			// Bug in chrome means dragleave is triggered when over
			// child elements, and does not populate relatedTarget
			// in the event to check where the mouse has gone.
			// This droppable overlay should resolve
			this.getOptions().TargetBox.append('<div class="iDragAndDropFilesDroppable" style="z-index: 100; position: absolute; top: 0; left: 0; right: 0; bottom: 0;"></div>');
			this.getOptions().DroppableBox = this.getOptions().TargetBox.find('.iDragAndDropFilesDroppable');

			this.InitFileDrop();
		}
		else{
			this.FallBack();
		}
	};

	ingotClass._iDragAndDropFilesObject.prototype.ShowBox = function(){
		if(this.getOptions().isVisible !== true){
			if(this.getOptions().isAnimatingShow === true)
				return false;
			this.getOptions().isAnimatingShow = true;
			this.getOptions().isVisible = true;
			this.getOptions().TargetBox.stop(true,true);
			thisObject = this;
			this.getOptions().TargetBox.show(this.getOptions().showDroppableBoxDuration, function () {
				thisObject.getOptions().isAnimatingShow = false;
			});
		}
	};
	ingotClass._iDragAndDropFilesObject.prototype.HideBox = function(){
		if(this.getOptions().isAnimatingHide === true)
			return false;
		this.getOptions().isVisible = false;
		this.getOptions().TargetBox.stop(true,true);
		if(this.getOptions().TargetBox.is(":visible")){
			this.getOptions().isAnimatingHide = true;
			thisObject = this;
			this.getOptions().TargetBox.hide(this.getOptions().hideDroppableBoxDuration, function (){
				thisObject.getOptions().isAnimatingHide = false;
			});
		}
		else{
			this.getOptions().TargetBox.hide();
		}
	};

	/**
	 * set the default state
	 */
	ingotClass._iDragAndDropFilesObject.prototype.setDefaultState = function(){
		this.setPassiveState();
		if(this.getOptions().HideByDefault === true)
			this.HideBox();
		this.TriggerBehaviours('setDefaultState');
	};
	ingotClass._iDragAndDropFilesObject.prototype.setPassiveState = function(){
		this.TriggerBehaviours('setPassiveState');
	};
	ingotClass._iDragAndDropFilesObject.prototype.setActiveState = function(){
		this.TriggerBehaviours('setActiveState');
	};
	/**
	 * If the browser does not support this plugin then fall back
	 * to a more traditional file selection dialog
	 */
	ingotClass._iDragAndDropFilesObject.prototype.FallBack = function (){
	};
	
	/**
	 * The file drop itself
	 * @returns {undefined}
	 */
	ingotClass._iDragAndDropFilesObject.prototype.InitFileDrop = function (){
		var thisObject = this;
		// Store response from each request here
		var fileResponses = {};

		var fileDropDefaults = {
			dragOver: function() {
				thisObject.TriggerBehaviours('dragOver');
				thisObject.setActiveState();
			},
			dragLeave: function() {
				thisObject.TriggerBehaviours('dragLeave');
				thisObject.setPassiveState();
			},
			docOver: function() {
				thisObject.ShowBox();
				thisObject.TriggerBehaviours('docOver');
			},
			docLeave: function() {
				thisObject.TriggerBehaviours('docLeave');
				thisObject.setDefaultState();
			},
			drop: function() {
				thisObject.setDefaultState();
				thisObject.TriggerBehaviours('drop');
			},
			uploadStarted: function(i, file, len){
				thisObject.getOptions().UploadStartedCallback(i, file, len);
				thisObject.TriggerBehaviours('uploadStarted', [i, file, len]);
				// a file began uploading
				// i = index => 0, 1, 2, 3, 4 etc
				// file is the actual file of the index
				// len = total files user dropped
			},
			uploadFinished: function(i, file, response, time) {
				// response is the data you got back from server in JSON format.
				fileResponses[i] = $.fn.iAjax({handleIAjaxReturn: response});
				thisObject.TriggerBehaviours('uploadFinished', [i, file, response, time]);
			},
			progressUpdated: function(i, file, progress) {
				thisObject.getOptions().FileProgressCallback(i,file,progress);
				thisObject.TriggerBehaviours('progressUpdated', [i,file,progress]);
				// this function is used for large files and updates intermittently
				// progress is the integer value of file being uploaded percentage to completion
			},
			globalProgressUpdated: function(progress) {
				thisObject.TriggerBehaviours('globalProgressUpdated', [progress]);
				// progress for all the files uploaded on the current instance (percentage)
				// ex: $('#progress div').width(progress+"%");
			},
			speedUpdated: function(i, file, speed) {
				thisObject.TriggerBehaviours('speedUpdated', [i, file, speed]);
				// speed in kb/s
			},
			rename: function(name) {
				thisObject.TriggerBehaviours('rename', [name]);
				// name in string format
				// must return alternate name as string
			},
			beforeEach: function(file) {
				thisObject.TriggerBehaviours('beforeEach', [file]);
				// file is a file object
				// return false to cancel upload
			},
			beforeSend: function(file, i, done) {
				var send = true;
				var cancelSend = function (){
					send = false;
				};
				// If any behaviour calls cancelSend() then done() is not called in this method
				thisObject.TriggerBehaviours('beforeSend', [file, i, cancelSend]);
				if(send === true)
					done();
				// file is a file object
				// i is the file index
				// call done() to start the upload
			},
			afterAll: function() {
				// runs after all files have been uploaded or otherwise dealt with
				var combinedResponse = $.fn.iAjax({combineResponses: fileResponses});
				$.fn.iAjax({displayDialogFromResponses: combinedResponse});
				thisObject.TriggerBehaviours('afterAll', [combinedResponse]);
			}
		};
		var fileDropOptions = $.extend(fileDropDefaults, this.getOptions());
		this.getOptions().TargetBox.filedrop(fileDropOptions);
	};
	/**
	 * Handle commands passed after initalisation
	 */
	ingotClass._iDragAndDropFilesObject.prototype.RunCommand = function (command, parameter){
		if(typeof command !== 'string')
			return false;

		switch(command){
			case 'GetTargetBoxElement':
				return this.getOptions().TargetBox;
			break;
			default:
				return false;
		}
	};
}(jQuery));


/**
 * Behaviours
 */
(function ($) {
	
	ingotClass._iDragAndDropFilesObject.PresetBehaviours = {};	

}(jQuery));

	
/** 
 *  Default Droppable Display
 */
(function ($) {
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDefaultDragAndDropFilesBox = function(dragAndDropObject, options){
		this.callParentMethod('constructor', [dragAndDropObject, options]);
	};
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDefaultDragAndDropFilesBox.inheritsFrom(ingotClass._behaviourBase);
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDefaultDragAndDropFilesBox.prototype.getDefaultOptions = function() {
		return {
			classes:{
				iDefaultDragAndDropFilesBoxCss: 'iDefaultDragAndDropFilesBoxCss',
				iDefaultDragAndDropFilesBoxDragEnterStateCss: 'iDefaultDragAndDropFilesBoxDragEnterStateCss',
				iDefaultDragAndDropFilesBoxDefaultStateCss: 'iDefaultDragAndDropFilesBoxDefaultStateCss'
			}
		};
	};
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDefaultDragAndDropFilesBox.prototype.Init = function() {
		this.getOwnerOptions().TargetBox.addClass(this.getOptions().classes.iDefaultDragAndDropFilesBoxCss);
	};
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDefaultDragAndDropFilesBox.prototype.setPassiveState = function(){
		this.getOwnerOptions().TargetBox.removeClass(this.getOptions().classes.iDefaultDragAndDropFilesBoxDragEnterStateCss);	
		this.getOwnerOptions().TargetBox.addClass(this.getOptions().classes.iDefaultDragAndDropFilesBoxDefaultStateCss);
	};
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDefaultDragAndDropFilesBox.prototype.setActiveState = function(){
		this.getOwnerOptions().TargetBox.removeClass(this.getOptions().classes.iDefaultDragAndDropFilesBoxDefaultStateCss);
		this.getOwnerOptions().TargetBox.addClass(this.getOptions().classes.iDefaultDragAndDropFilesBoxDragEnterStateCss);	
	};
}(jQuery));

/**
 * 
 * Image Droppable
 */
(function ($) {
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDragAndDropFilesDisplayImage = function(dragAndDropObject, options){
		this.callParentMethod('constructor', [dragAndDropObject, options]);
	};
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDragAndDropFilesDisplayImage.inheritsFrom(ingotClass._behaviourBase);
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDragAndDropFilesDisplayImage.prototype.getDefaultOptions = function() {
		return {
			classes:{
				iDefaultImageCss: 'iDefaultImage',
				iDefaultDragAndDropFilesBoxDragEnterStateCss: 'iDefaultDragAndDropFilesBoxDragEnterStateCss',
				iDefaultDragAndDropFilesBoxDefaultStateCss: 'iDefaultDragAndDropFilesBoxDefaultStateCss'
			}
		};
	};
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDragAndDropFilesDisplayImage.prototype.Init = function() {
		this.getOwnerOptions().TargetBox.addClass(this.getOptions().classes.iDefaultImageCss);
	};
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDragAndDropFilesDisplayImage.prototype.setPassiveState = function(){
		this.getOwnerOptions().TargetBox.removeClass(this.getOptions().classes.iDefaultDragAndDropFilesBoxDragEnterStateCss);	
		this.getOwnerOptions().TargetBox.addClass(this.getOptions().classes.iDefaultDragAndDropFilesBoxDefaultStateCss);
	};
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDragAndDropFilesDisplayImage.prototype.setActiveState = function(){
		this.getOwnerOptions().TargetBox.removeClass(this.getOptions().classes.iDefaultDragAndDropFilesBoxDefaultStateCss);
		this.getOwnerOptions().TargetBox.addClass(this.getOptions().classes.iDefaultDragAndDropFilesBoxDragEnterStateCss);	
	};
}(jQuery));

/**
 * 
 * Default Background Text
 */
(function ($) {
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDefaultDragAndDropFilesBackgroundText = function(dragAndDropObject, options){
		this.callParentMethod('constructor', [dragAndDropObject, options]);
	};
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDefaultDragAndDropFilesBackgroundText.inheritsFrom(ingotClass._behaviourBase);
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDefaultDragAndDropFilesBackgroundText.prototype.getDefaultOptions = function() {
		return {
			classes:{
				iDragAndDropFilesBoxBackgroundTextCss: 'iDragAndDropFilesBoxBackgroundTextCss',
				iDragAndDropFilesBoxBackgroundTextActiveCss: 'iDragAndDropFilesBoxBackgroundTextActiveCss',
				iDragAndDropFilesBoxBackgroundTextPassiveCss: 'iDragAndDropFilesBoxBackgroundTextPassiveCss'
			},
			BackgroundText: null
		};
	};
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDefaultDragAndDropFilesBackgroundText.prototype.Init = function() {
		// Set Background Text div and classes
		this.getOwnerOptions().TargetBox.append('<div class="iDragAndDropFilesBoxBackgroundText" style="z-index:50">');
		this.getOptions().BackgroundText = this.getOwnerOptions().TargetBox.find('.iDragAndDropFilesBoxBackgroundText');
		this.getOptions().BackgroundText.addClass(this.getOptions().classes.iDragAndDropFilesBoxBackgroundTextCss);

		// Center-align background text
		//$(window).resize(this.CenterBackgroundText());
		//this.getOwnerOptions().TargetBox.resize(this.CenterBackgroundText());
		//$(window).resize();	
	};
	/*ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDefaultDragAndDropFilesBackgroundText.prototype.CenterBackgroundText = function(){
		var thisObject = this;
		return function() {
			var top = (thisObject.getOwnerOptions().TargetBox.height()/2);
			console.log(top);
			thisObject.getOptions().BackgroundText.css({
				position: 'absolute',
				'top': top + 'px',
				'width': thisObject.getOwnerOptions().TargetBox.width() + 'px'
			});
		};
	};*/
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDefaultDragAndDropFilesBackgroundText.prototype.setPassiveState = function(){
		this.getOptions().BackgroundText.removeClass(this.getOptions().classes.iDragAndDropFilesBoxBackgroundTextActiveCss);	
		this.getOptions().BackgroundText.addClass(this.getOptions().classes.iDragAndDropFilesBoxBackgroundTextPassiveCss);
		this.getOptions().BackgroundText.html(this.getOwnerOptions().PassiveBackgroundMessage);
	};
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iDefaultDragAndDropFilesBackgroundText.prototype.setActiveState = function(){
		this.getOptions().BackgroundText.removeClass(this.getOptions().classes.iDragAndDropFilesBoxBackgroundTextPassiveCss);
		this.getOptions().BackgroundText.addClass(this.getOptions().classes.iDragAndDropFilesBoxBackgroundTextActiveCss);
		this.getOptions().BackgroundText.html(this.getOwnerOptions().ActiveBackgroundMessage);
	};
}(jQuery));


/**
 * 
 * Show a standard upload dialog box!
 * 
 */
(function ($) {
	/**
	 * 
	 * Show upload dialog
	 */
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iShowDragAndDropFilesUploadDialog = function(dragAndDropObject, options){
		this.callParentMethod('constructor', [dragAndDropObject, options]);	
	};
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iShowDragAndDropFilesUploadDialog.inheritsFrom(ingotClass.ingotObject);
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iShowDragAndDropFilesUploadDialog.prototype.getDefaultOptions = function() {
		return {
			UploadDialog: null
		};
	};
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iShowDragAndDropFilesUploadDialog.prototype.uploadStarted = function(i, files, len){
		if(this.getOptions().UploadDialog === null){
			this.getOptions().UploadDialog = $('<div>')
					.append('<h3>Uploading Files <span id="currentFileNo">1</span> of '+len+'</h3>')
					.append('<div id="speedDiv" style="visibility:hidden">Speed: <span id="currentSpeed">Unknown</span></div>')
					.append('<div id="fileName"></div>')
					.append('<div id="progress"></div>');
			this.getOptions().UploadDialog.find('#progress').progressbar({
				value: 0
			});
			var thisObject = this;
			$.fn.iDialog({
				element: this.getOptions().UploadDialog,
				title: 'Uploading Files...',
				buttons: this.getCancelButtons()
			});
		}
		else{
			this.getOptions().UploadDialog.find('#currentFileNo').html(i+1);
		}
	};	
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iShowDragAndDropFilesUploadDialog.prototype.getOkButtons = function()  {
		var thisObject = this;
		return{
			Ok: function() {
				$( this ).dialog( "close" );
				thisObject.getOptions().UploadDialog = null;
			}
		};
	};
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iShowDragAndDropFilesUploadDialog.prototype.getCancelButtons = function()  {
		var thisObject = this;
		return{
			Cancel: function() {
				$( this ).dialog( "close" );
				thisObject.getOptions().UploadDialog = null;
			}
		};
	};
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iShowDragAndDropFilesUploadDialog.prototype.progressUpdated = function(i,file,progress){
		if(this.getOptions().UploadDialog !== null){
			this.getOptions().UploadDialog.find('#progress').progressbar({
				value: progress
			});
			this.getOptions().UploadDialog.dialog('option', 'title', 'Uploading ' + file.name);
		}
	};
	ingotClass._iDragAndDropFilesObject.PresetBehaviours.iShowDragAndDropFilesUploadDialog.prototype.speedUpdated = function(i,file,speed){
		if(this.getOptions().UploadDialog !== null){
			var parent = this.getOptions().UploadDialog.find('#speedDiv');
			parent.find('#currentSpeed').html(speed.toFixed(2) + 'kb/s');		
			parent.css('visibility', 'visible');		
		}
	};

}(jQuery));	
	/**
		 * Handle a dropped file
		 * 
		 * @param mixed files either a fileAPI file object or a string
		 * of the path to the file.
		 */
		/*ingotClass._iDragAndDropFilesObject.prototype.HandleFiles = function(files){
			var handledFiles = [];
			// First split the files into a standard format
			// Regardless of whether fileAPI is used or not
			if(ingotClass.HelperFunctions.IsFileAPISupported() === true && typeof files === 'object'){
				for(var i = 0, f; f = files[i]; i++){
					console.log("here");
					console.log(f);
					
					//Get the filetype
					if (f.type.match('image.*'))
						newFileType = 'image';
					else if (f.type.match('text.*'))
						newFileType = 'text';
					else
						newFileType = null;
						
					// Create the closure
					var newFile = function (){
						return {
							filename: f.name,
							fileAPI: true,
							fileAPIObject: f,
							fileType: newFileType,
							dataUrlResult: null,
							TextResult: null,
							result: null,
							loadAsDataUrl: function (callback){
								// If this has already been loaded
								if(this.dataUrlResult === null)
									callback();
								// Else load
								var reader = new FileReader();
								var thisObject = this;
								reader.onload = function(dataUrlFile){
									thisObject.dataUrlResult = dataUrlFile.target.result;
									callback();
								};
								reader.readAsDataURL(this.fileAPIObject);
							},
							loadAsText: function (callback){
								// If this has already been loaded
								if(this.textResult !== null)
									callback();
								// Else load
								var reader = new FileReader();
								var thisObject = this;
								reader.onload = function(dataUrlFile){
									thisObject.textResult = dataUrlFile.target.result;
									callback();
								};
								reader.readAsText(this.fileAPIObject);
							},
							load: function(callback){
								var thisObject = this;
								if(this.fileType === 'image'){ // Image
									this.loadAsDataUrl(function (dataUrlResult){
										thisObject.result = thisObject.dataUrlResult;
										callback();
									});
								}
								else if(this.fileType === 'text'){ // Text
									this.loadAsText(function (){
										thisObject.result = thisObject.textResult;
										callback();
									});
								}
								else{ // Unknown
									return null;
								}
							},
							processImage: function (passedOptions){
								var options = $.extend({
									callback: function () {},
									maxWidth: null,
									maxHeight: null
								}, passedOptions);
								if(this.fileAPI !== true)
									return false;
								var fileObject = this;
								var processNow = function (){
									// Load image
									var img = new Image();
									img.onload = function (){				
										// Get canvas object
										var canvas = $('<canvas></canvas>')[0];

										var MAX_WIDTH = options.maxWidth;
										var MAX_HEIGHT = options.maxHeight;
										var width = img.width;
										var height = img.height;

										if (width > height) {
										  if (width > MAX_WIDTH) {
											height *= MAX_WIDTH / width;
											width = MAX_WIDTH;
										  }
										} else {
										  if (height > MAX_HEIGHT) {
											width *= MAX_HEIGHT / height;
											height = MAX_HEIGHT;
										  }
										}
										
										var canvas = $('<canvas></canvas>')[0];
										canvas.width = width;
										canvas.height = height;

										var ctx = canvas.getContext("2d");
										ctx.drawImage(img, 0, 0, width, height);
										
										fileObject.result = canvas.toDataURL(fileObject.dataType);
										
										options.callback();
									};
									img.src = fileObject.result;
								};
								// I tried to get this to throw an error if the data has not yet been loaded
								// But for some reason the result of this.result seems to change if I perform
								// an IF query on it.  It has baffled me.
								//console.log(this);
								//if(this.result !== null)
									processNow();
								//else
								//	throw new ingotError('progessImage(): the image has not been loaded yet');

							}
						};
					}();
					// Add the created closure obejct to an array
					handledFiles.push(newFile);
				}
			}
			// Else fall back
			else if(typeof files === 'string'){
				handledFiles[0] = files;
			}
			else 
				return false;

			// return the new objects
			return handledFiles;
		};*/
	

/**
 * Default User Interface
 * 
 */
(function ($) {	
	/**
	 * Define the Constructor
	 */
	ingotClass.iMarkDataDialogInput = function(options){
		this.callParentMethod('constructor', [options]);
		
		this.row = null;
		this.pid = null;
		this.awTypePrecursor = '';
		this.dialogTitle = '';
		this.tables = {};
		
		// Set Pid
		if(this.getOptions().pid !== undefined)
			this.setPid(this.getOptions().pid);
		// Set Row
		if(this.getOptions().row !== undefined)
			this.setRow(this.getOptions().row);
		
		// Set AwTypePrecursor
		if(this.getOptions().awTypePrecursor !== undefined)
			this.setAwTypePrecursor(this.getOptions().awTypePrecursor);

		// Set Title
		if(this.getOptions().dialogTitle !== undefined)
			this.setDialogTitle(this.getOptions().dialogTitle);
		
		// Set Tables
		if(this.getOptions().tables !== undefined)
			this.setTables(this.getOptions().tables);
		
	};
	// Inherit from iMarksheetComponent
	ingotClass.iMarkDataDialogInput.inheritsFrom(ingotClass.iMarksheetComponent);

	/* *
	 * Show the dialog
	 */ 
	ingotClass.iMarkDataDialogInput.prototype.show = function(){	
		return this.createDialog();
	};
	/* *
	 * Get Pid
	 */ 
	ingotClass.iMarkDataDialogInput.prototype.getPid = function(){	
		return this.pid;
	};
	/* *
	 * Set Pid
	 */ 
	ingotClass.iMarkDataDialogInput.prototype.setPid = function(value, doNotChangeRow){
		this.pid = value;
		if(doNotChangeRow === undefined || doNotChangeRow !== true){
			throw error('Need to calculate row from pid');
		}
	};
	/* *
	 * Set Pid
	 */ 
	ingotClass.iMarkDataDialogInput.prototype.setAwTypePrecursor = function(value){
		this.awTypePrecursor = value;
	};
	/* *
	 * Get Pid
	 */ 
	ingotClass.iMarkDataDialogInput.prototype.getAwTypePrecursor = function(value){
		return this.awTypePrecursor;
	};
	/* *
	 * Set Pid
	 */ 
	ingotClass.iMarkDataDialogInput.prototype.setDialogTitle = function(value){
		this.dialogTitle = value;
	};
	/* *
	 * Get Pid
	 */ 
	ingotClass.iMarkDataDialogInput.prototype.getDialogTitle = function(value){
		return this.dialogTitle;
	};
	/* *
	 * Get Row
	 */ 
	ingotClass.iMarkDataDialogInput.prototype.getRow = function(){	
		return this.row;
	};
	/* *
	 * Set Row
	 */ 
	ingotClass.iMarkDataDialogInput.prototype.setRow = function(value, doNotChangePid){
		this.row = value;
		if(doNotChangePid === undefined || doNotChangePid !== true){
			var newPid = this.row.find('td.imarkbook_pid').html();
			this.setPid(newPid, true);
		}
	};	
	/* *
	 * Get Row
	 */ 
	ingotClass.iMarkDataDialogInput.prototype.getTables = function(){	
		return this.tables;
	};
	/* *
	 * Set Row
	 */ 
	ingotClass.iMarkDataDialogInput.prototype.setTables = function(value){
		this.tables = value;
	};	

	/**
	 *  Create the dialog
	 */
	ingotClass.iMarkDataDialogInput.prototype.showDialog = function (){
		var cl = this.getMarksheetClosure();
		var schemeObject = cl.Helper.GetCurrentScheme();
		var row = this.getRow();
		var pid = this.getPid();

		// Now set the properties for the individual tables
		var tables = this.getTables();
		
		// Get pupil Name
		var pupilName = cl.Helper.GetPupilNameFromRow(row);

		// Use these vars to store shared data for the focusin,
		// focusout and binds.
		var oldFieldValue = '';
		var inputElement = null;
		var properties = null;
		var fieldValue = null;
		var targetCell = null;

		/*
		 * Delete the honours/skillspassport row
		 */		 
		function deleteRow(rowNo, type) {
			if(type === undefined || typeof type !== 'string')
				throw new Error('deleteRow(): type must be a string');
			if(rowNo === undefined || typeof rowNo !== 'number')
				throw new Error('deleteRow(): rowNo must be an integer');
			schemeObject.DeletePupilQualsData(pid, type, rowNo);
			var row = cl.Helper.GetRowFromPid(pid);
			var cell = row.find('.'+this.getAwTypePrecursor()+type);
			schemeObject.UpdateCell(cell);
			// Ensure the cell is marked as changed
			// Without this it will only be marked if the calculateCellValue
			// changes - so it will not mark when deleting blank rows.
			cl.Helper.MarkCellChanged(cell);
		}

		/*
		 * Save the cell contents
		 */  
		function saveCellContents(){
			// Get the current value
			fieldValue = inputElement.val();
			// If nothing has changed, then don't worry about it
			if(oldFieldValue === fieldValue)
				return false;

			// Record the data
			if(schemeObject.GetCellType(targetCell) === 'grade')
				targetCell.html(fieldValue);
			else
				schemeObject.setPupilQualsData(pid, properties[0], properties[2], properties[1], fieldValue, targetCell);

			// Update the Cell
			//var result = schemeObject.UpdateCell(targetCell);
			oldFieldValue = inputElement.val();
		}

		// A Function to allow the grade to be checked against the
		// allowed grades before altering
		var updateCellIfValidMark = function(targetType){
			return {
				keypress: function (event){
					var value = cl.Helper.GetPrintableCharacterFromEvent(event);
					if(value === '')
						return true;
					combinedValue = inputElement.val() + value;
					if(schemeObject.GetCellType(targetCell) === targetType){
						if(!cl.Action.ChangeCellValue(combinedValue, targetCell, true))
							cl.Action.ChangeCellValue(value, targetCell, true);
						inputElement.val(targetCell.html());
						return false;
					}
					return true;
				}
			};
		};

		// As the row structure is the same for each table then
		// we only need to define it once
		var thisObject = this;
		var standardField = {
			bind: {
				keyup: function (){
					saveCellContents();
				},
				focusin: function () {
					// Store this to check if the value has changed
					// on focusout
					inputElement = $(this).find('input');
					oldFieldValue = inputElement.val();
					properties = inputElement.attr('name').split("_");
					// Get the corresponding cell
					targetCell = row.find('.'+thisObject.getAwTypePrecursor()+properties[0]+':eq('+properties[2]+')');
				},
				focusout: function () {
					saveCellContents();
				}
			}
		};
		
		///////////////////////
		// Build the dialog
		//////////
		
		// Add the title
		var dialogContents = $('<form class="imarkbook_form modbac_input_dialog"></form>');
		dialogContents.append('<h2>' + pupilName + ' - ' + this.getDialogTitle() + '</h2>');

		// Combine structure and individual data then build
		for(var x in tables){
			var newTable = $.extend(true, {}, tables[x]);
			// add binds to fields
			for(var y in newTable.structure){
				newTable.structure[y] = $.extend(true, {}, standardField, newTable.structure[y]);
			}
			
			// Ensure data is set
			if (typeof newTable.data === 'function'){
				newTable.data = newTable.data(pid);
			}
			
			// Ensure we are checking marks are valid
			if(newTable.bind === undefined){
				newTable.bind = updateCellIfValidMark(newTable.id);
			}
			// Create Table and append to dialog
			dialogContents.append($.fn.iFormTable(newTable));		
		}

		// Finally, lets Load the dialog
                var doy_scheme = cl.Data.markingScheme;
                var title = 'Edit Modbac Qualifications for ';
                if (doy_scheme === 'DUKEOFYORK_ALEVELQUALIFICATION') title = 'Edit A-Level Qualifications for ';
                if (doy_scheme === 'DUKEOFYORK_TECHNICALQUALIFICATION') title = 'Edit Techical Qualifications for ';
                if (doy_scheme === 'DUKEOFYORK_GCSEQUALIFICATION') title = 'Edit GCSE Qualifications for ';
		$.fn.iDialog({
                    
			title:  title + pupilName,
			element: dialogContents,
			width: '60%',
			height: $(window).height() * 0.8,
			hide: {effect:"scale", duration: 220}
		});
	};


}(jQuery));



/**
 * iMarksheetUserInterface provides the editable marksheet for the site
 * 
 */

(function ($) {	

	/**
	 * Constructor
	 */
	ingotClass.iAdditionalMarksDataInputElement = function(options){
		this.callParentMethod('constructor', [options]);
		
		this.initComponent();
	};
	// Inherit from ingotObject
	ingotClass.iAdditionalMarksDataInputElement.inheritsFrom(ingotClass.iMarksheetComponent);
	
	/*
	 * Set Required Methods
	 */
	ingotClass.iAdditionalMarksDataInputElement.prototype.requiredMethods = function(){
		return [
			'initComponent'
		];
	};
	/*
	 * Set Required Methods
	 */
	ingotClass.iAdditionalMarksDataInputElement.prototype.getDefaultOptions = function(){
		return this.callParentMethod('requiredMethods', {
			validationCallback: function (){return true},
			setDataCallback: null
		});
	};
	
	/*
	 * ValidateData
	 */
	ingotClass.iAdditionalMarksDataInputElement.prototype.setValidationCallback = function(callback){
		if(!$.isFunction(callback))
			throw new ingotError("iAdditionalMarksDataInputElement::setValidationCallback() : Callback must be a function");
		this.getOptions().validationCallback = callback;
	};

	/*
	 * ValidateData
	 */
	ingotClass.iAdditionalMarksDataInputElement.prototype.getValidationCallback = function(callback){
		return this.getOptions().validationCallback;
	};

	/*
	 * Set setDataCallvack
	 */
	ingotClass.iAdditionalMarksDataInputElement.prototype.getSetDataCallback = function(callback){
		return this.getOptions().setDataCallback;
	};

	/*
	 * ValidateData
	 */
	ingotClass.iAdditionalMarksDataInputElement.prototype.validateData = function(params){
		return this.getOptions().validationCallback(params);
	};

	/*
	 * Set Data
	 * Sets the data in the additional marks object
	 */
	ingotClass.iAdditionalMarksDataInputElement.prototype.setData = function(data){
		if(this.validateData() === false)
			return this.dataIsInvalid();
		var callback = this.getSetDataCallback();
		if(!$.isFunction(callback))
			throw new ingotsError("iAdditionalMarksDataInputElement.setData(): setDataCallback must be set and must be a function");
		return callback(data);
	};

	/*
	 * report the data as invalid
	 * Sets the data in the additional marks object
	 */
	ingotClass.iAdditionalMarksDataInputElement.prototype.dataIsInvalid = function(){
		$.fn.iDialog("Cannot store data as it seems to be invalid");
	};
	
		
}(jQuery));
/**
 * iMarksheetUserInterface provides the editable marksheet for the site
 * 
 */

(function ($) {	

	/**
	 * Constructor
	 */
	ingotClass.iAdditionalMarkData = function(options){
		this.callParentMethod('constructor', [options]);
		
		this._fwkData = {
			pupilQuals: {}
		};
		this._fields = {};
		
		this.setFwkData(options.frameworkData);
		this.setFields(options.fields);
	};
	// Inherit from ingotObject
	ingotClass.iAdditionalMarkData.inheritsFrom(ingotClass.iMarksheetComponent);
	
	/*
	 * Set Required Methods
	 */
	ingotClass.iAdditionalMarkData.prototype.requiredMethods = function(){
		return [
		];
	};
	
	/*
	 * Set Required Methods
	 */
	ingotClass.iAdditionalMarkData.prototype.getDefaultOptions = function(){
		return {
		};
	};

	/*
	 * Get Fwk Data
	 */
	ingotClass.iAdditionalMarkData.prototype.getFwkData = function(){
		return this._fwkData;
	};

	/*
	 * Set Fwk Data
	 */
	ingotClass.iAdditionalMarkData.prototype.setFwkData = function(data){
		if(data === undefined)
			data = {};
		if(data.pupilQuals !== undefined){
			for(var x in data.pupilQuals){
				this.setFwkPupilDataByPid(x, data.pupilQuals[x]);
			}
		}
	};

	/*
	 * Get Fields
	 */
	ingotClass.iAdditionalMarkData.prototype.getFields = function(){
		return this._fields;
	};

	/*
	 * Set Fwk Data
	 */
	ingotClass.iAdditionalMarkData.prototype.setFields = function(data){
		if(data === undefined || $.isArray(data))
			data = {};
		this._fields = data;
	};

	/*
	 * Get All pupilData
	 */
	ingotClass.iAdditionalMarkData.prototype.getAllPupilFwkData = function(data){
		return this._fwkData.pupilQuals;
	};

	/*
	 * Get FwkPupilData
	 */
	ingotClass.iAdditionalMarkData.prototype.getFwkPupilDataByPid = function(pid){
		if(pid === undefined || !$.isNumeric(pid))
			throw new ingotError('iAdditionalMarkData.getFwkPupilDataByPid(): pid must be a number - currently: ' + pid);

		if(this.getFwkData().pupilQuals[pid] === undefined)
			this.createNewPupilData(pid);
		
		return this.getFwkData().pupilQuals[pid];
	};

	/*
	 * Set FwkPupuilData
	 */
	ingotClass.iAdditionalMarkData.prototype.setFwkPupilDataByPid = function(pid, data, targetCell){
		if(pid === undefined || !$.isNumeric(pid))
			throw new ingotError('iAdditionalMarkData.setFwkPupilDataByPid(): pid must be a number - currently: ' + pid);
		this.getFwkData().pupilQuals[pid] = data;
		
		// Mark ready to save if targetCell data supplied
		if(targetCell !== undefined && targetCell instanceof jQuery){
			var cl = this.getMarksheetClosure();
			cl.Helper.MarkCellChanged(targetCell);
		}
	};
	
	/**
	 * Create a data slot for a currently unused pupil
	 */
	ingotClass.iAdditionalMarkData.prototype.createNewPupilData = function(pid){
		if(pid === undefined || !$.isNumeric(pid))
			throw new ingotError('iAdditionalMarkData.createNewPupilData(): pid must be a number - currently: ' + pid);
		
		this.setFwkPupilDataByPid(pid, this.getPupilDataStructure());
	};	

	/**
	 * Create a data slot for a currently unused pupil
	 */
	ingotClass.iAdditionalMarkData.prototype.getPupilDataStructure = function(){
		return {};
	};	
	
	/**
	 * Set the Framework-Specific data for a given parameter
	 */
	ingotClass.iAdditionalMarkData.prototype.setFieldByPid = function (pid, param, data){
		if(pid === undefined)
			throw new ingotError("iAdditionalMarkData.setFieldByPid(): pid must be set");
		if(param === undefined)
			throw new ingotError("iAdditionalMarkData.setFieldByPid(): param must be set");

		if(!this.fieldExists(param, 'pupilFields'))
			throw new ingotError("iAdditionalMarkData.setFieldByPid(): Field '" + param + " does not exist in type 'pupilFields'")

		var pupilData = this.getFwkPupilDataByPid(pid);
		pupilData[param] = data;
		this.setFwkPupilDataByPid(pid, pupilData);
	}

	/**
	 * Set the Framework-Specific data for a given parameter
	 */
	ingotClass.iAdditionalMarkData.prototype.getFieldByPid = function (pid, field){
		if(pid === undefined)
			throw new ingotError("iAdditionalMarkData.getFieldByPid(): pid must be set");
		if(field === undefined)
			throw new ingotError("iAdditionalMarkData.getFieldByPid(): field must be set");

		if(!this.fieldExists(field, 'pupilFields'))
			throw new ingotError("iAdditionalMarkData.getFieldByPid(): Field '" + field + " does not exist in type 'pupilFields'")

		var pupilData = this.getFwkPupilDataByPid(pid);
		if(pupilData[field] === undefined)
			return null;
		return pupilData[field];
	}
	
	/**
	 * Set the Framework-Specific data for a given parameter
	 */
	ingotClass.iAdditionalMarkData.prototype.fieldExists = function (field, type){
		if(this.typeExists(type)){
			for(var x in this.getFields()[type]){
				var fieldProperties = this.getFields()[type][x];
				if(typeof fieldProperties === 'string'){
					if(fieldProperties === field){
						return true;
					}
				}
				else if(fieldProperties.name !== undefined){
					if (fieldProperties.name === field){
						return true;
					}
				}
			}
		}
		return false;
	}

		/**
	 * Set the Framework-Specific data for a given parameter
	 */
	ingotClass.iAdditionalMarkData.prototype.typeExists = function (type){
		if(this.getFields()[type] !== undefined)
			return true;
		return false;
	}

	/**
	 * Set the Framework-Specific data for a given parameter
	 */
	ingotClass.iAdditionalMarkData.prototype.getSaveData = function (pids){
		var returnData = {};
		var cl = this.getMarksheetClosure();
		
		if(cl.Helper.GetCurrentScheme().ReturnData !== undefined)
			returnData = cl.Helper.GetCurrentScheme().ReturnData(pids);
		else{
			returnData = this.getReturnData(pids);
		}

		return returnData;
	}

	/**
	 * Set the Framework-Specific data for a given parameter
	 */
	ingotClass.iAdditionalMarkData.prototype.getReturnData = function (pids){
		var cl = this.getMarksheetClosure();
		var returnvar = {pupilQuals: {}};

		// Add Data
		for(var x in pids){
			var currPid = Number(pids[x]);
			var pupilQualData = this.getFwkPupilDataByPid(currPid);

			returnvar['pupilQuals'][currPid] = $.extend(true,pupilQualData,{});
		}
		
		return returnvar;
	}
	
	
		
}(jQuery));
/**
 * iMarksheetUserInterface provides the editable marksheet for the site
 * 
 */

(function ($) {	
	
	/**
	 * Marksheet files plugin
	 */
	$.fn.iAdditionalMarksDataTextAreaInput = function(options, parameter){
		return $.fn.iPlugin.handleObjectAttachedToElement(this, '_iAdditionalMarksDataTextAreaInput', options, parameter);
	}

	/**
	 * Constructor
	 */
	ingotClass._iAdditionalMarksDataTextAreaInput = function(options){
		this.callParentMethod('constructor', [options]);
	};
	// Inherit from ingotObject
	ingotClass._iAdditionalMarksDataTextAreaInput.inheritsFrom(ingotClass.iAdditionalMarksDataInputElement);
	
	/*
	 * Set Required Methods
	 */
	ingotClass._iAdditionalMarksDataTextAreaInput.prototype.requiredMethods = function(){
		return [
			'initComponent'
		];
	};

	/*
	 * Set Required Methods
	 */
	ingotClass._iAdditionalMarksDataTextAreaInput.prototype.getDefaultOptions = function(){
		return this.callParentMethod('requiredMethods', {
			rows: 4,
			cols: 50,
			textValue: ''
		});
	};
	
	/*
	 * init Componenet
	 */
	ingotClass._iAdditionalMarksDataTextAreaInput.prototype.initComponent = function(){
		var options = this.getOptions();
		var Root = options.Root;
		
		var rows = options.rows;
		var cols = options.cols;
		
		var defaultValue = this.getOptions().textValue;
		
		var params =  "rows='"+rows+"' cols='"+cols+ "'";
		if(options.maxLength !== undefined)
			params += "maxlength='" + options.maxLength + "'";
				
		var textArea = $("<textarea " + params + ">" + defaultValue + "</textarea>");
		var thisObject = this;
		textArea.change(function(){
			var data = textArea.val();
			thisObject.setData(data);
		});
		
		this.getOptions().textArea = textArea;
		
		Root.append(textArea);
	};
	
		
}(jQuery));
/**
 * Not a Marking scheme in itself, this provides a base from which 
 * standard Modbac Marking Schemes can extend a default functionality
 * 
 * Foundation and Upper Cores do not extend from this due to their unique nature
 */

(function ($) {	
	/**
	 * Define the object
	 * 
	 */
	ingotClass.iMarkingScheme.MODBAC_BASE = function(options){
		this.callParentMethod('constructor', [options]);
	}
	// Inherit from iMarkingScheme
	ingotClass.iMarkingScheme.MODBAC_BASE.inheritsFrom(ingotClass.iMarkingScheme);	

	/**
	 * Get last entered cell type
	 */
	ingotClass.iMarkingScheme.MODBAC_BASE.prototype.getPupilStatement = function(pid){		
		var s = this.getAdditionalMarkData().getFieldByPid(pid, 'statement');
		if(s === null)
			return '';
		return s
	};
	
	/**
	 * Get last entered cell type
	 */
	ingotClass.iMarkingScheme.MODBAC_BASE.prototype.setPupilStatement = function(pid, param, statement){		
		this.getAdditionalMarkData().setFieldByPid(pid, 'statement', statement);
		
		var cl = this.getMarksheetClosure();		
		var row = cl.Helper.GetRowFromPid(pid)		
		cl.Helper.SetRowAsReadyToSave(row)
	};

}(jQuery));
/**
 * Not a Marking scheme in itself, this provides a base from which 
 * standard Modbac Marking Schemes can extend a default functionality
 * 
 * Foundation and Upper Cores do not extend from this due to their unique nature
 */

(function ($) {	
	/**
	 * Define the object
	 * 
	 */
	ingotClass.iMarkingScheme.MODBAC_DEFAULT_BASE = function(options){
		this.callParentMethod('constructor', [options]);
	}
	// Inherit from iMarkingScheme
	ingotClass.iMarkingScheme.MODBAC_DEFAULT_BASE.inheritsFrom(ingotClass.iMarkingScheme.MODBAC_BASE);	
	
	ingotClass.iMarkingScheme.MODBAC_DEFAULT_BASE.prototype.requiredMethods = function(){
		return [
			'calculateGradeCell'
		];
	};

	/**
	 * Get the default mark for a cell
	 */
	ingotClass.iMarkingScheme.MODBAC_DEFAULT_BASE.prototype.DefaultMark = function (cell){
		if(this.cl.Helper.isTestMarkCell(cell) === true)
			return '-';
		return 'N';
	};
			
	/**
	 * Force the mark to uppercase
	 */
	ingotClass.iMarkingScheme.MODBAC_DEFAULT_BASE.prototype.ForceMarkToUppercase = function(cell){
		return true;
	};			
	
	/**
	 * Factory method to calculate the row's status
	 */
	ingotClass.iMarkingScheme.MODBAC_DEFAULT_BASE.prototype.getReadyForModerationMessage = function (row){
		return $.iMessage['Ready for moderation'];
	};
	
	/**
	 * @Override
	 * Check if this has passed all criteria (not tests)
	 */
	ingotClass.iMarkingScheme.MODBAC_DEFAULT_BASE.prototype.IsRowReadyForModeration = function(row) {
		var mark, passed = true, cl = this.getMarksheetClosure();
		var gradeCell = row.find('td.modbacgrade:not(.imarkbook_test_markcell)');
		
		// If criterion data has been entered then calculate the grace cell
		// Or if the last eneted cell type was a criterion (set in derived UI class)
		if(this.hasCriterionDataBeenEntered(row) === true || this.getLastEnteredCellType() === 'criterion'){
			cl.Action.ChangeCellValue(this.calculateGradeCell(row), gradeCell);
		}

		// Return
		return cl.Helper.GetCurrentScheme().HasCellPassed(gradeCell);
	};

	/**
	 * Check if criterion data has been entered
	 */
	ingotClass.iMarkingScheme.MODBAC_DEFAULT_BASE.prototype.hasCriterionDataBeenEntered = function(row) {
		var enteredData = false, cl = this.getMarksheetClosure();
		row.find('td.criterion:not(.imarkbook_test_markcell)').each(function () {
			if (cl.Helper.GetCurrentScheme().HasCellPassed($(this)) === true){
				enteredData = true;
				return false;
			}
		});
		return enteredData;
	};
	
	/**
	 * Get the grade Cell from a row
	 */
	ingotClass.iMarkingScheme.MODBAC_DEFAULT_BASE.prototype.getGradeCell = function(row){
		return row.find('td.modbacgrade:not(.imarkbook_test_markcell)');
	}
	
	/**
	 * Check if Final Grade exists
	 */
	ingotClass.iMarkingScheme.MODBAC_DEFAULT_BASE.prototype.doesFinalGradeExist = function(row){
		var cl = this.getMarksheetClosure();
		var gradeCell = this.getGradeCell(row);
		if (cl.Helper.GetCurrentScheme().HasCellPassed(gradeCell) === true)
			return true
		return false;
	};
	
	/**
	 * Get the cell type
	 * 
	 * @param jQueryObject cell the cell
	 * @return string the cell type
	 *  either 'core', 'honours' or 'skills-passport'
	 */
	ingotClass.iMarkingScheme.MODBAC_DEFAULT_BASE.prototype.GetCellType = function (cell){
		if(cell.hasClass('modbacgrade'))
			return 'modbacgrade';
		if(cell.hasClass('criterion'))
			return 'criterion';
		return null;
	};
	
	/**
	 * Return the name of the user interface to use
	 * Override this as necessary
	 */
	ingotClass.iMarkingScheme.MODBAC_DEFAULT_BASE.prototype.getUserInterfaceName = function(){
		return 'MODBAC_DEFAULT';
	};			

	/**
	 * Set last entered cell type
	 */
	ingotClass.iMarkingScheme.MODBAC_DEFAULT_BASE.prototype.setLastEnteredCellType = function(type){
		this.lastEnteredCellType = type;
	};
	
	/**
	 * Get last entered cell type
	 */
	ingotClass.iMarkingScheme.MODBAC_DEFAULT_BASE.prototype.getLastEnteredCellType = function(type){
		return this.lastEnteredCellType;
	};

}(jQuery));
/*
 * iMarksheet provides the editable marksheet for the site
 */

(function ($) {	

	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE = function(options){
		this.callParentMethod('constructor', [options]);
		this.Init();
	}
	// Inherit from iMarkingScheme
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.inheritsFrom(ingotClass.iMarkingScheme.MODBAC_BASE);	
	
	/**
	 * Set the required methods
	 */
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.prototype.requiredMethods = function(){
		return [
			'DefaultMark',
			'GetValidMarks',
			'ForceMarkToUppercase',
			'Status'
		];
	};
	
	/**
	 * Return the name of the user interface to use
	 * Override this as necessary
	 */
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.prototype.getUserInterfaceName = function(){
		return 'MODBAC_FOUNDATIONUPPER_CORES';
	};		
	
	/*
	 * Initialise MODBAC Components						        
	 */
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.prototype.Init = function(){
		var cl = this.getMarksheetClosure();
		
		// Make sure all data is consistant.
		// Alter and display a warning if not
		var changes = false;
		var thisObject = this;
		cl.Helper.GetAllMarkCells().each(function () {
			var result = thisObject.UpdateCell($(this));
			if(result === true)
				changes = true;
		});
		if(changes === true){
			$.fn.iDialog({
				title: 'Information',
				width: '40%',
				message: $.iMessage['JS_ERROR_DATA_INCONSISTANT_BUT_CORRECTED']
			});
		}            
	};
		
		
	/*
	 * set a new field in the pupil quals array
	 */
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.prototype.setPupilQualsData = function (pupilId, awType, awNumber, field, value, targetCell){
		var cl = this.getMarksheetClosure();
		// Sanity Checks
		if(pupilId === undefined || isNaN(pupilId))
			throw new ingotError('this.setPupilQualsData(): passed pid must be a number, currently: ' + pid);
		if(awNumber === undefined || isNaN(awNumber))
			throw new ingotError('this.setPupilQualsData(): passed awNumber must be a number, currently: ' + pid);
		if(awType === undefined)
			throw new ingotError('this.setPupilQualsData(): passed awType must be defined');
		if(field === undefined)
			throw new ingotError('this.setPupilQualsData(): passed field must be defined');
		if(value === undefined)
			throw new ingotError('this.setPupilQualsData(): passed data must be defined');
		
		// Get current data for this pupil
		var newData = this.getAdditionalMarkData().getFwkPupilDataByPid(pupilId);

		// set up any properties if not already set	
		if(newData[awType] === undefined)
			newData[awType] = [];
		if(newData[awType][awNumber] === undefined)
			newData[awType][awNumber] = {};
		if(newData[awType][awNumber][field] !== undefined)
			var oldValue = newData[awType][awNumber][field];
					
		// Return false if the data is unchanged
		if(oldValue !== undefined && (value.toString() === oldValue.toString()))
			return false;
		
		// Set the new data
		newData[awType][awNumber][field] = value;
		
		// Try to work out the targetCell if missing
		if(targetCell === undefined){
			targetCell = this.getModbacCellByType(pupilId, awType, awNumber);
		}
								
		// Set the data
		this.getAdditionalMarkData().setFwkPupilDataByPid(pupilId, newData, targetCell);
		
		// Update the related Cell
		this.UpdateCell(targetCell);
		
		// Return true
		return true;
	};
		
	/*
	 * get Modbac Cell Type
	 */
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.prototype.getModbacCellByType = function(pid, type, number){
		var cl = this.getMarksheetClosure();
		// Get cells for learner
		var row = cl.Helper.GetRowFromPid(pid);
		var markCells = cl.Helper.GetAllMarkCellsInRow(row);
		// Sanity checks
		if(type === 'core' || type === 'modbaccore')
			type = 'modbaccore';
		if(type === 'honours' || type === 'modbachonours')
			type = 'modbachonours';
		if(type === 'skills-passport' || type === 'modbacskills-passport')
			type = 'modbacskills-passport';
		// Filter out by class type
		markCells = markCells.filter(function(){
			if($(this).hasClass(type))
				return true;
			return false;
		});
		// If this is core then select by order number
		if(type === 'modbaccore'){
			markCells = markCells.eq(number);
		}
		return markCells;
	};
		
	/*
	 * Delete an award from the pupil quals array
	 */
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.prototype.DeletePupilQualsData = function (pid, awType, awNumber, targetCell){
		// Sanity Checks
		if(pid === undefined || isNaN(pid))
			throw new ingotError('this.DeletePupilQualsData(): passed pid must be a number, currently: ' + pid);
		if(awNumber === undefined || isNaN(awNumber))
			throw new ingotError('this.DeletePupilQualsData(): passed awNumber must be a number, currently: ' + pid);
		if(awType === undefined)
			throw new ingotError('this.DeletePupilQualsData(): passed awType must be defined');
		// Get current data for this pupil
		var newData = this.getPupilDataByType(pid, awType);
		
		// If this awNumber does not exist then return false
		if(newData[awNumber] === undefined)
			return false;
		
		//  Delete the award
		newData.splice(awNumber, 1);

		// Set the data
		for (var x in newData){
			// alter the type order
			newData[x]['type_order'] = x;
			// re-record each entry
			for(var field in newData[x]){
				this.setPupilQualsData(pid, awType, x, field, newData[x][field], targetCell);
			}
		}
	};
		
	/*
	 * Get pupil quals data by type
	 * 
	 * @param pupilId the pupil Id
	 * @param awType the award type ('core', 'honours', 'skills-passport')
	 * @return object in the format:
	 * 	{
	 * 		'0'=> {
	 * 			'qualcode' => <qualcode>,
	 * 			'qualName' => <qualname>,
	 * 			'grade' => <grade>
	 * 			...
	 * 		}
	 * 		...
	 *  }
	 */
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.prototype.getPupilDataByType = function (pupilId, awType){
		var cl = this.getMarksheetClosure();
		var pupilData = this.getAdditionalMarkData().getFwkPupilDataByPid(pupilId);
		// If grade
		if(awType === 'grade'){
			if(pupilData[awType] !== undefined && pupilData[awType][0] !== undefined && pupilData[awType][0]['finalgrade'] !== undefined)
				return pupilData[awType][0]['finalgrade'];
			else{
				var row = cl.Helper.GetRowFromPid(pupilId);
				var cells = cl.Helper.GetAllMarkCellsOfTypeInRow(pupilId, 'modbacgrade');
				if(cells.length !== 1)
					throw new ingotError('ingot-iMarkingScheme-MODBAC.getPupilDataByType(): we should only have one grade cell! Currently: ' + cells.length + ' for PID: ' + pupilId);
				return cells.eq(0).html();
			}
		}
		// Else
		if(pupilData[awType] !== undefined)
			return pupilData[awType];
		// If nothing found
		return {};
	};
		
	/*
	 * Set the hover behaviours
	 */
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.prototype.Hover = function (){
		var cl = this.getMarksheetClosure();
		var highlightRowFunc = function(){
			cl.Root.find('.imarkbook_table td.'+cl.ClassName.HighlightCell).removeClass(cl.ClassName.HighlightCell);
			$(this)
				.closest('tr')
				.find('.imarkbook_pid, .imarkbook_forenamecell, .imarkbook_surnamecell, .imarkbook_notecell, .imarkbook_status,')
				.addClass(cl.ClassName.HighlightCell);
		};
		var actions = {
			'.imarkbook_table tr td': highlightRowFunc
		};
		return actions;
	};
				
	/**
	 * Calculate a cell value from the stored pupil data
	 * 
	 * @param jQueryObject cell the cell as a jQueryObject
	 * @return mixed the cell value or false if failed;
	 */
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.prototype.CalculateCellValue = function(cell){
		var cl = this.getMarksheetClosure();
		var row = $(cell).closest('tr');
		var pid = row.find('td').html();
		var pupTypeData = this.getAdditionalMarkData().getFwkPupilDataByPid(pid);
		var mark = cell.html();
		
		// get the cell type
		var type = this.GetCellType($(cell));
		if(type === null)
			return false;

		// If this is a grade
		if(type === 'grade'){
			if(cl.Helper.IsMarkValid(mark, cell))
				return mark;
			else
				return this.DefaultMark(cell);
		}
		// If this is a core
		if(type === 'core'){
			var index = row.find('td.modbaccore').index(cell);
			if(pupTypeData['core'] === undefined || pupTypeData['core'][index] === undefined || pupTypeData['core'][index]['grade'] === undefined)
				return this.DefaultMark(cell);
				
			var val = pupTypeData['core'][index]['grade'];
			
			if(val === '')
				val = this.DefaultMark(cell);
			
			return val;
		}
		// Else
		else {										
			return mark;
		}
	};
        
	/**
	 * Update a modbac cell from the stored pupil data
	 * @param jQueryObject cell the cell to update
	 * @return bool true or false on success
	 */
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.prototype.UpdateCell = function (cell){
		var cl = this.getMarksheetClosure();
		var newValue = this.CalculateCellValue(cell);
		if(newValue === false)
			throw new ingotError("Could not calculate the cell value");
		return cl.Action.ChangeCellValue(newValue, cell, true); 
	};
		
		
	/**
	 * Get the cell type
	 * 
	 * @param jQueryObject cell the cell
	 * @return string the cell type
	 *  either 'core', 'honours' or 'skills-passport'
	 */
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.prototype.GetCellType = function (cell){
		if(cell.hasClass('modbacgrade'))
			return 'grade';
		if(cell.hasClass('modbaccore'))
			return 'core';
		if(cell.hasClass('modbachonours'))
			return 'honours';
		if(cell.hasClass('modbacskills-passport'))
			return 'skills-passport';
		return null;
	};
        
	/**
	 * Find errors in the row and return an error message
	 * 
	 * @param jQueryObject row the row to check
	 * @return mixed false if no errors or an error message
	 */
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.prototype.Errors = function (row) {
		var cl = this.getMarksheetClosure();
		var mark, errors = 0;
		var thisObject = this;
		cl.Helper.GetAllMarkCellsInRow(row).each(function () {
			var cell = $(this);
			var currentValue = cell.html();				
							
			if (!$.inArray(currentValue, thisObject.GetValidMarks(cell))) {
				$(this).addClass('imarkbook_error');
				errors += 1;
			}
		});
		if (errors) {
			return "0/0 - "+$.iMessage['Invalid marks'];
		}

		return false;
	};

	/**
	 * Marking Scheme
	 */
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.prototype.IsRowReadyForModeration = function (row) {
		var cl = this.getMarksheetClosure();
		var pupilData = this.GetPupilData(row);
		var allowedQuals = 0;
		var finalGradeEntered = false;
		cl.Helper.UnsetRowReadyForModeration(row);
		// Check how many core we have
		for(var x in pupilData.core){
			if(pupilData.core[x]['qualcode'] !== undefined && pupilData.core[x]['qualcode'] !== ''
				&& pupilData.core[x]['name'] !== undefined && pupilData.core[x]['name'] !== ''
				&& pupilData.core[x]['grade'] !== undefined && pupilData.core[x]['grade'] !== '')
					allowedQuals++;
		}
		// Check final grade is entered
		if(pupilData.grade !== undefined){
			var gradeCell = this.GetModbacCellsFromRow(row, 'grade');
			if(cl.Helper.IsMarkValid(pupilData.grade, gradeCell))
				finalGradeEntered = true;
		}
		
		// Return
		if (finalGradeEntered === true && allowedQuals >= 3){
			return true;
		}
		else
			return false;
	};
        
	/**
	 * Get Modbac Cell Type From Row
	 */
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.prototype.GetModbacCellsFromRow = function(row, type){
		var cl = this.getMarksheetClosure();
		var cells;
		if(type === 'grade'){
			cells = cl.Helper.GetAllMarkCellsOfTypeInRow(row, 'modbacgrade');
			if(cells.length !== 1)
				throw new ingotError('ingot-iMarkingScheme-MODBAC::GetModbacCellFromRow(): There must be only be one grace cell per learner. Currently: ' + cells.length);
		}
		return cells;
	};
        
	/**
	 * Get modbac data from a row or pid
	 * @param param can either be a row jquery object or a pid
	 * @return map of data in the format: {
	 * 		grade: 'string',
	 * 		core: [
	 * 			'<coreNo>': {
	 * 				qualcode: 'string',
	 * 				qualname: 'string',
	 * 				grade: 'string'
	 * 			} 
	 * 		]
	 * }
	 */
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.prototype.GetPupilData = function(pid){
		var cl = this.getMarksheetClosure();
		var returnData = {
			grade: '',
			core: []
		};
		// Make sure we have a pid
		if(pid instanceof jQuery && pid.hasClass('markRow'))
			pid = cl.Helper.GetPidFromRow(pid);

		returnData.grade = this.getPupilDataByType(pid, 'grade');
		returnData.core = this.getPupilDataByType(pid, 'core');
		
		// Return
		return returnData;
	};
        
	/**
	 * Csv Upload OnComplete
	 */
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.prototype.CsvUploadOnComplete = function(data){	
		var cl = this.getMarksheetClosure();
		// Check that we have frameworkData returned
		if(data === undefined || data.frameworkData === undefined || $.isPlainObject(data.frameworkData) === false){
			// The next line is commented out as it is causing the AjaxFileUpload plugin to crash
			// AjaxFileUpload needs to be redeveloped as it uses $.handleError which no longer
			// exists in jQuery
			//throw new ingotError('ingot-iMarkingScheme-Modbac::CsvUploadOnComplete() framework data is not supplied');
			return false;
		}
		// Update the pupilQuals data
		for(var pupilId in data.frameworkData.pupilQuals){
			if(data.frameworkData !== undefined || data.frameworkData.pupilQuals !== undefined){
				for(var modbacType in data.frameworkData.pupilQuals[pupilId]){
					var pupilTypeData = this.getPupilDataByType(pupilId,modbacType);
					for(var orderNo in data.frameworkData.pupilQuals[pupilId][modbacType]){
						if(pupilTypeData[orderNo] !== data.frameworkData.pupilQuals[pupilId][modbacType][orderNo]){
							for(var x in data.frameworkData.pupilQuals[pupilId][modbacType][orderNo]){
								this.setPupilQualsData(pupilId, modbacType, orderNo, x, data.frameworkData.pupilQuals[pupilId][modbacType][orderNo][x]);
							}
						}
					}
				}
			}
			// Update the Final Grade
			var grade = data.rows[pupilId][0];
			var row = cl.Helper.GetRowFromPid(pupilId);
			var cells = cl.Helper.GetAllMarkCellsInRow(row);
			cells.each(function(){
				if($(this).hasClass('modbacgrade')){
					cl.Action.ChangeCellValue(grade, $(this));
					return false;
				}
			});
		}
	};
	
	/**
	 * The message to be displayed when the criteria are not yet passed
	 */
	ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE.prototype.NotPassedMessage = function (){
		return $.iMessage['JS_MODBAC_CORE_HIGHER_NOT_READY_FOR_MODERATION'];
	};
	
			
	
}(jQuery));
/*
 * iMarksheet provides the editable marksheet for the site
 */

(function ($) {	
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE = function(options){
		this.callParentMethod('constructor', [options]);
		this.Init();
	}
	// Inherit from iMarkingScheme
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.inheritsFrom(ingotClass.iMarkingScheme);	
	
	/**
	 * Set the required methods
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.requiredMethods = function(){
		return [
			'DefaultMark',
			'GetValidMarks',
			'ForceMarkToUppercase',
			'Status',
			'getNumberOfFieldRowsForDialog'
		];
	};
	
	/*
	 * Initialise Components						        
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.Init = function(){
		var cl = this.getMarksheetClosure();
		
		// Make sure all data is consistant.
		// Alter and display a warning if not
		var changes = false;
		var thisObject = this;
		cl.Helper.GetAllMarkCells().each(function () {
			var result = thisObject.UpdateCell($(this));
			if(result === true)
				changes = true;
		});
		if(changes === true){
			$.fn.iDialog({
				title: 'Information',
				width: '40%',
				message: $.iMessage['JS_ERROR_DATA_INCONSISTANT_BUT_CORRECTED']
			});
		}            
	};
		
		
	/*
	 * set a new field in the pupil quals array
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.getPupilQualsData = function (pupilId, awType, awNumber, field){
		var cl = this.getMarksheetClosure();
		
		// Get current data for this pupil
		var newData = this.getAdditionalMarkData().getFwkPupilDataByPid(pupilId);
		
		// set up any properties if not already set	
		if(newData[awType] === undefined)
			return null;
		if(newData[awType][awNumber] === undefined)
			return null;
		if(newData[awType][awNumber][field] === undefined)
			return null;

		return newData[awType][awNumber][field];
	};

	/*
	 * set a new field in the pupil quals array
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.setPupilQualsData = function (pupilId, awType, awNumber, field, value, targetCell){
		var cl = this.getMarksheetClosure();
		// Sanity Checks
		if(pupilId === undefined || isNaN(pupilId))
			throw new ingotError('this.setPupilQualsData(): passed pid must be a number, currently: ' + pid);
		if(awNumber === undefined || isNaN(awNumber))
			throw new ingotError('this.setPupilQualsData(): passed awNumber must be a number, currently: ' + pid);
		if(awType === undefined)
			throw new ingotError('this.setPupilQualsData(): passed awType must be defined');
		if(field === undefined)
			throw new ingotError('this.setPupilQualsData(): passed field must be defined');
		if(value === undefined)
			throw new ingotError('this.setPupilQualsData(): passed data must be defined');
		// Get current data for this pupil
		var newData = this.getAdditionalMarkData().getFwkPupilDataByPid(pupilId);
		// set up any properties if not already set	
		if(newData[awType] === undefined)
			newData[awType] = [];
		if(newData[awType][awNumber] === undefined)
			newData[awType][awNumber] = {};
		if(newData[awType][awNumber][field] !== undefined)
			var oldValue = newData[awType][awNumber][field];

            // Return false if the data is unchanged
		if(oldValue !== undefined && (value.toString() === oldValue.toString()))
			return false;

            // Set the new data
		newData[awType][awNumber][field] = value;
		                
		// Try to work out the targetCell if missing
		if(targetCell === undefined){
			targetCell = this.getCellByNumber(pupilId, awNumber);
		}
								
		// Set the data
		this.getAdditionalMarkData().setFwkPupilDataByPid(pupilId, newData, targetCell);
		
		// Update the related Cell
		this.UpdateCell(targetCell);
				
		// Return true
		return true;
	};
		
	/*
	 * get Modbac Cell Type
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.getCellByNumber = function(pid, number){
		var cl = this.getMarksheetClosure();
		// Get cells for learner
		var row = cl.Helper.GetRowFromPid(pid);
		var markCells = cl.Helper.GetAllMarkCellsInRow(row);
		
		return markCells[number];
	};
		
	/*
	 * Delete an award from the pupil quals array
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.DeletePupilQualsData = function (pid, awType, awNumber, targetCell){
		// Sanity Checks
		if(pid === undefined || isNaN(pid))
			throw new ingotError('this.DeletePupilQualsData(): passed pid must be a number, currently: ' + pid);
		if(awNumber === undefined || isNaN(awNumber))
			throw new ingotError('this.DeletePupilQualsData(): passed awNumber must be a number, currently: ' + pid);
		if(awType === undefined)
			throw new ingotError('this.DeletePupilQualsData(): passed awType must be defined');
		// Get current data for this pupil
		var newData = this.getPupilDataByType(pid, awType);
		
		// If this awNumber does not exist then return false
		if(newData[awNumber] === undefined)
			return false;
		
		//  Delete the award
		newData.splice(awNumber, 1);

		// Set the data
		for (var x in newData){
			// alter the type order
			newData[x]['type_order'] = x;
			// re-record each entry
			for(var field in newData[x]){
				this.setPupilQualsData(pid, awType, x, field, newData[x][field], targetCell);
			}
		}
	};
		
	/*
	 * Get pupil quals data by type
	 * 
	 * @param pupilId the pupil Id
	 * @param awType the award type ('core', 'honours', 'skills-passport')
	 * @return object in the format:
	 * 	{
	 * 		'0'=> {
	 * 			'qualcode' => <qualcode>,
	 * 			'qualName' => <qualname>,
	 * 			'grade' => <grade>
	 * 			...
	 * 		}
	 * 		...
	 *  }
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.GetPupilData = function (pupilId){
		var cl = this.getMarksheetClosure();
		var pupilData = this.getAdditionalMarkData().getFwkPupilDataByPid(pupilId);

		// If the pupil data is set		
		if($.isPlainObject(pupilData)){
			var cellType = this.GetCellType();
                        if(pupilData[cellType] !== undefined)
                            return pupilData[cellType];
		}

		// If nothing found
		return {};
	};
		
	/*
	 * Set the hover behaviours
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.Hover = function (){
		var cl = this.getMarksheetClosure();
		var highlightRowFunc = function(){
			cl.Root.find('.imarkbook_table td.'+cl.ClassName.HighlightCell).removeClass(cl.ClassName.HighlightCell);
			$(this)
				.closest('tr')
				.find('.imarkbook_pid, .imarkbook_forenamecell, .imarkbook_surnamecell, .imarkbook_notecell, .imarkbook_status,')
				.addClass(cl.ClassName.HighlightCell);
		};
		var actions = {
			'.imarkbook_table tr td': highlightRowFunc
		};
		return actions;
	};
				
	/**
	 * Calculate a cell value from the stored pupil data
	 * 
	 * @param jQueryObject cell the cell as a jQueryObject
	 * @return mixed the cell value or false if failed;
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.CalculateCellValue = function(cell){
		var cl = this.getMarksheetClosure();

		var pid = cl.Helper.GetPidFromCell(cell);
		var pupTypeData = this.getAdditionalMarkData().getFwkPupilDataByPid(pid);

		var index = cl.Helper.getCellTypeIndex(this.GetCellType(), cell);
		var cellType = this.GetCellType();
		var grade = this.getPupilQualsData(pid, cellType, index, 'grade');

		return grade;
	};
        
	/**
	 * Update a modbac cell from the stored pupil data
	 * @param jQueryObject cell the cell to update
	 * @return bool true or false on success
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.UpdateCell = function (cell){
		cell = $(cell);
		var cl = this.getMarksheetClosure();
		var newValue = this.CalculateCellValue(cell);
		if(newValue === false)
			throw new ingotError("Could not calculate the cell value");
		return cl.Action.ChangeCellValue(newValue, cell, true);
	};
		
        
	/**
	 * Find errors in the row and return an error message
	 * 
	 * @param jQueryObject row the row to check
	 * @return mixed false if no errors or an error message
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.Errors = function (row) {
		var cl = this.getMarksheetClosure();
		var mark, errors = 0;
		var thisObject = this;
		cl.Helper.GetAllMarkCellsInRow(row).each(function () {
			var cell = $(this);
			var currentValue = cell.html();				
							
			if (!$.inArray(currentValue, thisObject.GetValidMarks(cell))) {
				$(this).addClass('imarkbook_error');
				errors += 1;
			}
		});
		if (errors) {
			return "0/0 - "+$.iMessage['Invalid marks'];
		}

		return false;
	};
        
        /**
	 * Get an array of the fields
	 * 
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.getFields = function (){
		return ['minguidedhours','qualname','grade','quallevel'];
	};
        
                /**
	 * Get the cell type
	 * 
	 * @param jQueryObject cell the cell
	 * @return string the cell type
	 *  either 'core', 'honours' or 'skills-passport'
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.GetCellType = function (cell){
		return ['criterion'];
	};
        
	/**
	 * Get Modbac Cell Type From Row
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.GetModbacCellsFromRow = function(row, type){
		var cl = this.getMarksheetClosure();
		var cells;
		if(type === 'grade'){
			cells = cl.Helper.GetAllMarkCellsOfTypeInRow(row, 'modbacgrade');
			if(cells.length !== 1)
				throw new ingotError('ingot-iMarkingScheme-MODBAC::GetModbacCellFromRow(): There must be only be one grace cell per learner. Currently: ' + cells.length);
		}
		return cells;
	};
	
	/**
	 * The message to be displayed when the criteria are not yet passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.NotPassedMessage = function (){
		return $.iMessage['JS_MODBAC_CORE_HIGHER_NOT_READY_FOR_MODERATION'];
	};
	
	/**
	 * Return the name of the user interface to use
	 * Override this as necessary
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.getUserInterfaceName = function(){
		return 'DUKEOFYORK_ADDITIONALINFO';
	};	
	
	/**
	 * Csv Upload OnComplete
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.CsvUploadOnComplete = function(data){	
		var cl = this.getMarksheetClosure();
		// Check that we have frameworkData returned
		if(data === undefined || data.frameworkData === undefined || $.isPlainObject(data.frameworkData) === false){
			// The next line is commented out as it is causing the AjaxFileUpload plugin to crash
			// AjaxFileUpload needs to be redeveloped as it uses $.handleError which no longer
			// exists in jQuery
			//throw new ingotError('ingot-iMarkingScheme-Modbac::CsvUploadOnComplete() framework data is not supplied');
			return false;
		}
		// Update the pupilQuals data
		for(var pupilId in data.frameworkData.pupilQuals){
			if(data.frameworkData !== undefined || data.frameworkData.pupilQuals !== undefined){
				for(var modbacType in data.frameworkData.pupilQuals[pupilId]){
					var pupilTypeData = this.getPupilDataByType(pupilId,modbacType);
					for(var orderNo in data.frameworkData.pupilQuals[pupilId][modbacType]){
						if(pupilTypeData[orderNo] !== data.frameworkData.pupilQuals[pupilId][modbacType][orderNo]){
							for(var x in data.frameworkData.pupilQuals[pupilId][modbacType][orderNo]){
								this.setPupilQualsData(pupilId, modbacType, orderNo, x, data.frameworkData.pupilQuals[pupilId][modbacType][orderNo][x]);
							}
						}
					}
				}
			}
		}
	};
	
	/*
	 * Get pupil quals data by type
	 * 
	 * @param pupilId the pupil Id
	 * @param awType the award type ('core', 'honours', 'skills-passport')
	 * @return object in the format:
	 * 	{
	 * 		'0'=> {
	 * 			'qualcode' => <qualcode>,
	 * 			'qualName' => <qualname>,
	 * 			'grade' => <grade>
	 * 			...
	 * 		}
	 * 		...
	 *  }
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE.prototype.getPupilDataByType = function (pupilId, awType){
		var cl = this.getMarksheetClosure();
		var pupilData = this.getAdditionalMarkData().getFwkPupilDataByPid(pupilId);
		// Else
		if(pupilData[awType] !== undefined)
			return pupilData[awType];
		// If nothing found
		return {};
	};
	
}(jQuery));
/**
 * Default NLSH Marking Scheme
 * 
 */
(function($) {
	/**
	 * Define the object
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_COMPETENCYFRAMEWORK = function(options) {
		this.callParentMethod('constructor', [options]);
	}
	// Inherit from iMarkingScheme
	ingotClass.iMarkingScheme.DUKEOFYORK_COMPETENCYFRAMEWORK.inheritsFrom(ingotClass.iMarkingScheme);

	/**
	 * Get the default mark for a cell
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_COMPETENCYFRAMEWORK.prototype.DefaultMark = function(cell) {
		return '-';
	};

	/**
	 * Force the mark to uppercase
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_COMPETENCYFRAMEWORK.prototype.ForceMarkToUppercase = function(cell) {
		return true;
	};

	/**
	 * return an array of valid marks for a cell
	 * 
	 * Note: This must handle what to return if no cell is passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_COMPETENCYFRAMEWORK.prototype.GetValidMarks = function(cell) {
		return {setValues: ['-', 'B', 'S', 'G']};
	};

	/**
	 * Factory method to calculate the row's status
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_COMPETENCYFRAMEWORK.prototype.getReadyForModerationMessage = function(row) {
		var result = null, cl = this.getMarksheetClosure();

		switch (cl.Data.frameworkID) {
			case "INGOTS_NATIONAL_CURRICULUM":
				result = $.iMessage['Pass'];
				break;
			case "INGOTS_P_SCALES":
				result = $.iMessage['Pass'];
				break;
			case "QCF":
				result = $.iMessage['Ready for moderation'];
				break;
			case "INGOTS_FUNCTIONAL_SKILLS":
				result = $.iMessage['Ready for the test'];
				break;
			default:
				result = $.iMessage['Ready for moderation'];
				break;
		}

		return result;
	};

	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_COMPETENCYFRAMEWORK.prototype.HasCellPassed = function(cell) {
		mark = cell.html();
		if (mark !== 'S' && mark !== 'H')
			return false;
		return true;
	};

	/**
	 * The message to be displayed when the criteria are not yet passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_COMPETENCYFRAMEWORK.prototype.NotPassedMessage = function() {
		return $.iMessage['Need B, S or G'];
	};

}(jQuery));
/**
 * Default NLSH Marking Scheme
 * 
 */
(function($) {
	/**
	 * Define the object
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_FINALGRADES = function(options) {
		this.callParentMethod('constructor', [options]);
	}
	// Inherit from iMarkingScheme
	ingotClass.iMarkingScheme.DUKEOFYORK_FINALGRADES.inheritsFrom(ingotClass.iMarkingScheme);

	/**
	 * Get the default mark for a cell
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_FINALGRADES.prototype.DefaultMark = function(cell) {
		return '-'
	};

	/**
	 * Force the mark to uppercase
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_FINALGRADES.prototype.ForceMarkToUppercase = function(cell) {
		return true;
	};

	/**
	 * return an array of valid marks for a cell
	 * 
	 * Note: This must handle what to return if no cell is passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_FINALGRADES.prototype.GetValidMarks = function(cell) {
		return {setValues: ['-', 'B', 'S', 'G', 'N']};
	};

	/**
	 * Factory method to calculate the row's status
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_FINALGRADES.prototype.getReadyForModerationMessage = function(row) {
		var result = null, cl = this.getMarksheetClosure();

		switch (cl.Data.frameworkID) {
			case "INGOTS_NATIONAL_CURRICULUM":
				result = $.iMessage['Pass'];
				break;
			case "INGOTS_P_SCALES":
				result = $.iMessage['Pass'];
				break;
			case "QCF":
				result = $.iMessage['Ready for moderation'];
				break;
			case "INGOTS_FUNCTIONAL_SKILLS":
				result = $.iMessage['Ready for the test'];
				break;
			default:
				result = $.iMessage['Ready for moderation'];
				break;
		}

		return result;
	};

	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_FINALGRADES.prototype.HasCellPassed = function(cell) {
		var mark = cell.html();
		if($(cell).hasClass('achievedgrade') === true){
			if(mark === 'B' || mark === 'S' || mark === 'G' || mark === 'N')
				return true;
		}
		else if($(cell).hasClass('challengepanel') === true){
			if(mark === 'B' || mark === 'S' || mark === 'G' || mark === '-')
				return true;
		}
		else if($(cell).hasClass('targetgrade') === true){
				return true;
		}
		
		return false;
	};

	/**
	 * The message to be displayed when the criteria are not yet passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_FINALGRADES.prototype.NotPassedMessage = function() {
		return $.iMessage['Need BSG'];
	};

}(jQuery));
/**
 * Default NLSH Marking Scheme
 * 
 */
(function($) {
	/**
	 * Define the object
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_GCSEQUALIFICATION = function(options) {
		this.callParentMethod('constructor', [options]);
	}
	// Inherit from iMarkingScheme
	ingotClass.iMarkingScheme.DUKEOFYORK_GCSEQUALIFICATION.inheritsFrom(ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE);

	/**
	 * Get the defauly mark for a cell
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_GCSEQUALIFICATION.prototype.DefaultMark = function(cell) {
		return '-';
	};

	/**
	 * Force the mark to uppercase
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_GCSEQUALIFICATION.prototype.ForceMarkToUppercase = function(cell) {
		return true;
	};

	/**
	 * return an array of valid marks for a cell
	 * 
	 * Note: This must handle what to return if no cell is passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_GCSEQUALIFICATION.prototype.GetValidMarks = function(cell) {
		var cl = this.getMarksheetClosure();
		if (cell === undefined)
			return null;
		if (cl.Helper.isTestMarkCell(cell) === true) {
			return {
				types: ['int'],
				numeric: ['0', '100'],
				setValues: ['-']
			};
		}
		else {
                    return {anyvalue : true};
			//return {setValues: ['-', 'A*','A','B','C','D','D*','E','F','G','M','P','A*A*','A*A','AA','AB','BB','BC','CC','CD','DD','DE','EE','EF','FF','FG','GG']};
		}
	};

	/**
	 * Factory method to calculate the row's status
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_GCSEQUALIFICATION.prototype.getReadyForModerationMessage = function(row) {
		return $.iMessage['Ready for moderation'];
	};

	/**
	 * Marking Scheme
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_GCSEQUALIFICATION.prototype.IsRowReadyForModeration = function (row) {
		var cl = this.getMarksheetClosure();
                var pid = cl.Helper.GetPidFromRow(row);
		var pupilData = this.GetPupilData(pid);

		cl.Helper.UnsetRowReadyForModeration(row);
		// Check how many core we have

		for(var x = 0; x < pupilData.length; x++){
                    var totalFields = 0;
                    for (var i = 0; i < this.getFields().length; i++){
                        var val = this.getFields()[i];
			if(pupilData[x][val] !== undefined && pupilData[x][val] !== '')
                            totalFields++;
                    }
                    if(totalFields === 4)
                        return true;
		}
		return false;
	};

	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_GCSEQUALIFICATION.prototype.HasCellPassed = function(cell) {
		mark = cell.html();
		if (mark !== 'S' && mark !== 'H')
			return false;
		return true;
	};
	
		/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_GCSEQUALIFICATION.prototype.getTableStructure = function(cell) {
		return {
			'0' : {
				type: 'text',
				title: 'Subject',
				abbr: 'subject'
			},
			'1' : {
				type: 'text',
				title: 'Min learning hours',
				abbr: 'minguidedhours'
			},
			'2' : {
				type: 'text',
				title: 'Grade',
				abbr: 'grade'
			},
			'3' : {
				type: 'text',
				title: 'Qual level',
				abbr: 'quallevel'
			}
		}
	};
	
	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_GCSEQUALIFICATION.prototype.getNumberOfFieldRowsForDialog = function(cell) {
		return 11;
	};

	/**
	 * The message to be displayed when the criteria are not yet passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_GCSEQUALIFICATION.prototype.NotPassedMessage = function() {
		return $.iMessage['Need A* - G'];
	};	

}(jQuery));
/**
 * Default NLSH Marking Scheme
 * 
 */
(function($) {
	/**
	 * Define the object
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_TECHNICALQUALIFICATION = function(options) {
		this.callParentMethod('constructor', [options]);
	}
	// Inherit from iMarkingScheme
	ingotClass.iMarkingScheme.DUKEOFYORK_TECHNICALQUALIFICATION.inheritsFrom(ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE);

	/**
	 * Get the defauly mark for a cell
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_TECHNICALQUALIFICATION.prototype.DefaultMark = function(cell) {
		return '-';
	};

	/**
	 * Force the mark to uppercase
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_TECHNICALQUALIFICATION.prototype.ForceMarkToUppercase = function(cell) {
		return true;
	};

	/**
	 * return an array of valid marks for a cell
	 * 
	 * Note: This must handle what to return if no cell is passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_TECHNICALQUALIFICATION.prototype.GetValidMarks = function(cell) {
		var cl = this.getMarksheetClosure();
		if (cell === undefined)
			return null;
		if (cl.Helper.isTestMarkCell(cell) === true) {
			return {
				types: ['int'],
				numeric: ['0', '100'],
				setValues: ['-']
			};
		}
		else {
			return {anyvalue : true};
			//return {setValues: ['-', 'A', 'B', 'C', 'D', 'E', 'F', 'G']};
		}
	};

	/**
	 * Factory method to calculate the row's status
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_TECHNICALQUALIFICATION.prototype.getReadyForModerationMessage = function(row) {
		return $.iMessage['Ready for moderation'];
	};

	/**
	 * Marking Scheme
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_TECHNICALQUALIFICATION.prototype.IsRowReadyForModeration = function (row) {
		var cl = this.getMarksheetClosure();
                var pid = cl.Helper.GetPidFromRow(row);
		var pupilData = this.GetPupilData(pid);

		cl.Helper.UnsetRowReadyForModeration(row);

		// Check how many core we have
		for(var x = 0; x < pupilData.length; x++){
                    var totalFields = 0;
                    for (var i = 0; i < this.getFields().length; i++){
                        var val = this.getFields()[i];
			if(pupilData[x][val] !== undefined && pupilData[x][val] !== '')
                            totalFields++;
                    }
                    if(totalFields === 5)
                        return true;
		}
		return false;
	};

	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_TECHNICALQUALIFICATION.prototype.HasCellPassed = function(cell) {
		mark = cell.html();
		if (mark !== 'S' && mark !== 'H')
			return false;
		return true;
	};
	
	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_TECHNICALQUALIFICATION.prototype.getTableStructure = function(cell) {
		return {
			'0' : {
				type: 'text',
				title: 'Qual Name',
				abbr: 'qualname'
			},
			'1' : {
				type: 'text',
				title: 'Min learning hours',
				abbr: 'minguidedhours'
			},
			'2' : {
				type: 'text',
				title: 'Grade',
				abbr: 'grade'
			},
			'3' : {
				type: 'text',
				title: 'Qual level',
				abbr: 'quallevel'
			}
		}
	};
	
	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_TECHNICALQUALIFICATION.prototype.HasCellPassed = function(cell) {
		mark = cell.html();
		if (mark !== 'S' && mark !== 'H')
			return false;
		return true;
	};
	
	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_TECHNICALQUALIFICATION.prototype.getNumberOfFieldRowsForDialog = function(cell) {
		return 5;
	};

	/**
	 * The message to be displayed when the criteria are not yet passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_TECHNICALQUALIFICATION.prototype.NotPassedMessage = function() {
		return $.iMessage['Need A* - G'];
	};	

}(jQuery));
/**
 * Default NLSH Marking Scheme
 * 
 */
(function($) {
	/**
	 * Define the object
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_WORKEXPERIENCE = function(options) {
		this.callParentMethod('constructor', [options]);
	}
	// Inherit from iMarkingScheme
	ingotClass.iMarkingScheme.DUKEOFYORK_WORKEXPERIENCE.inheritsFrom(ingotClass.iMarkingScheme);

	/**
	 * Get the default mark for a cell
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_WORKEXPERIENCE.prototype.DefaultMark = function(cell) {
		return '-'
	};

	/**
	 * Force the mark to uppercase
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_WORKEXPERIENCE.prototype.ForceMarkToUppercase = function(cell) {
		return true;
	};

	/**
	 * return an array of valid marks for a cell
	 * 
	 * Note: This must handle what to return if no cell is passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_WORKEXPERIENCE.prototype.GetValidMarks = function(cell) {
		return {
			setValues: [
				'-',
                                'L',
				'L1', 
				'1', 
				'L2',
				'2'
			]
		};
	};

	/**
	 * Factory method to calculate the row's status
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_WORKEXPERIENCE.prototype.getReadyForModerationMessage = function(row) {
		return  $.iMessage['Ready for moderation'];;
	};

	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_WORKEXPERIENCE.prototype.IsRowReadyForModeration = function (row) {
		var cl = this.getMarksheetClosure();
		var cells = cl.Helper.GetAllMarkCellsInRow(row);
                
                if(cells.length !== 1)
                    throw new ingotError('There should only be one cell - found ' + cells.length);
                var mark = $(cells[0]).html();
                if(parseInt(mark) >= 40){
                    return true;
                }
                return false;
	};

	/**
	 * The message to be displayed when the criteria are not yet passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_WORKEXPERIENCE.prototype.NotPassedMessage = function() {
		return $.iMessage['Need 1,2,L,L1,L2'];
	};

}(jQuery));
/**
 * Default NLSH Marking Scheme
 * 
 */
(function($) {
	/**
	 * Define the object
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ALEVELQUALIFICATION = function(options) {
		this.callParentMethod('constructor', [options]);
	}
	// Inherit from iMarkingScheme
	ingotClass.iMarkingScheme.DUKEOFYORK_ALEVELQUALIFICATION.inheritsFrom(ingotClass.iMarkingScheme.DUKEOFYORK_ADDITIONALINFO_BASE);

	/**
	 * Get the defauly mark for a cell
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ALEVELQUALIFICATION.prototype.DefaultMark = function(cell) {
		return '-';
	};

	/**
	 * Force the mark to uppercase
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ALEVELQUALIFICATION.prototype.ForceMarkToUppercase = function(cell) {
		return true;
	};

	/**
	 * return an array of valid marks for a cell
	 * 
	 * Note: This must handle what to return if no cell is passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ALEVELQUALIFICATION.prototype.GetValidMarks = function(cell) {
		var cl = this.getMarksheetClosure();
		if (cell === undefined)
			return null;
		if (cl.Helper.isTestMarkCell(cell) === true) {
			return {
				types: ['int'],
				numeric: ['0', '100'],
				setValues: ['-']
			};
		}
		else {
			return {setValues: ['-', 'A*','A','B','C','D','D*','E','F','G','M','P','A*A*','A*A','AA','AB','BB','BC','CC','CD','DD','DE','EE','EF','FF','FG','GG','U','PP','PM','MM','MD','DD','DD*','D*D*','PPP','PPM','PMM','MMM','MMD','MDD','DDD','DDD*','DD*D*','D*D*D*']};
		}
	};

	/**
	 * Factory method to calculate the row's status
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ALEVELQUALIFICATION.prototype.getReadyForModerationMessage = function(row) {
		return $.iMessage['Ready for moderation'];
	};

	/**
	 * Marking Scheme
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ALEVELQUALIFICATION.prototype.IsRowReadyForModeration = function (row) {
		var cl = this.getMarksheetClosure();
                var pid = cl.Helper.GetPidFromRow(row);
		var pupilData = this.GetPupilData(pid);

		cl.Helper.UnsetRowReadyForModeration(row);
		// Check how many core we have

		for(var x = 0; x < pupilData.length; x++){
                    var totalFields = 0;
                    for (var i = 0; i < this.getFields().length; i++){
                        var val = this.getFields()[i];
			if(pupilData[x][val] !== undefined && pupilData[x][val] !== '')
                            totalFields++;
                    }
                    if(totalFields === 4)
                        return true;
		}
		return false;
	};

	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ALEVELQUALIFICATION.prototype.HasCellPassed = function(cell) {
		mark = cell.html();
		if (mark !== 'S' && mark !== 'H')
			return false;
		return true;
	};
	
		/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ALEVELQUALIFICATION.prototype.getTableStructure = function(cell) {
		return {
			'0' : {
				type: 'text',
				title: 'Subject',
				abbr: 'subject'
			},
			'1' : {
				type: 'text',
				title: 'Min learning hours',
				abbr: 'minguidedhours'
			},
			'2' : {
				type: 'text',
				title: 'Grade',
				abbr: 'grade'
			},
			'3' : {
				type: 'text',
				title: 'Qual level',
				abbr: 'quallevel'
			}
		}
	};
	
	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ALEVELQUALIFICATION.prototype.getNumberOfFieldRowsForDialog = function(cell) {
		return 6;
	};

	/**
	 * The message to be displayed when the criteria are not yet passed
	 */
	ingotClass.iMarkingScheme.DUKEOFYORK_ALEVELQUALIFICATION.prototype.NotPassedMessage = function() {
		return $.iMessage['Need A* - G'];
	};	

}(jQuery));
/*
 * iMarksheet provides the editable marksheet for the site
 */

(function ($) {	
	ingotClass.iMarkingScheme.INGOTS_1 = function(options){
		this.callParentMethod('constructor', [options]);
	}
	// Inherit from iMarkingScheme
	ingotClass.iMarkingScheme.INGOTS_1.inheritsFrom(ingotClass.iMarkingScheme);	
	
	/*
	 * Marking Helpers
	 */
	ingotClass.iMarkingScheme.INGOTS_1.prototype.CountCriteria = function (type) {
		var cl = this.getMarksheetClosure();
		var i, count = 0;
		for (i = 0; i < cl.Data.crit.length; i += 1) {
			if (cl.Data.crit[i].type === type || type === '*') {
				count += 1;
			}
		}
		return count;
	};
	ingotClass.iMarkingScheme.INGOTS_1.prototype.AddMarks = function (row, type) {
		var cl = this.getMarksheetClosure();
		var sum = 0;
		row.find('td.editable').each(function (i) {
			if (cl.Data.crit[i].type === type || type === '*') {
				sum += parseInt($(this).html(), 10);
			}
		});
		return sum;
	};
	ingotClass.iMarkingScheme.INGOTS_1.prototype.MinMark = function (row, type) {
		var cl = this.getMarksheetClosure();
		var mark, min = 999;
		row.find('td.editable').each(function (i) {
			if (cl.Data.crit[i].type === type || type === '*') {
				mark = parseInt($(this).html(), 10);
				min = mark < min ? mark : min;
			}
		});
		return min;
	};
		
	/*
	 *   Helper functions.
	 */
	ingotClass.iMarkingScheme.INGOTS_1.prototype.Pass = function (options) {
		var def = {
			mark: false,
			pass: false,
			merit: false,
			dist: false,
			steps: 0
		};
		var o = $.extend(def, options);

		var steps = "<b>" + o.steps + "/" + o.steps + " -</b>";

		if (o.dist && o.mark >= o.dist) {
			return steps + " <span class='imarkbook_pass'>Pass with Distinction</span>";
		}
		if (o.merit&& o.mark >= o.merit){
			return steps + " <span class='imarkbook_pass'>Pass with Merit</span>";
		}
		if (!o.pass|| o.mark >= o.pass) {
			return steps + " <span class='imarkbook_pass'>Pass</span>";
		}
		return "<b>" + (o.steps-1) + "/" + o.steps + " -</b> Learner needs " + (o.pass - o.mark) + " marks to pass";
	};
	
	ingotClass.iMarkingScheme.INGOTS_1.prototype.Errors = function (row) {
		var cl = this.getMarksheetClosure();
		var mark, errNaN = 0, errBounds = 0;
		row.find('td.editable').removeClass('imarkbook_error'); // Reset.

		// Check for NaN and bounds, but only show one error at a time.
		row.find('td.editable').each(function (index) {
			mark = parseInt($(this).html(), 10);
			if (errBounds === 0 && isNaN(mark)) {
				$(this).addClass('imarkbook_error');
				errNaN += 1;
			}
			if (errNaN === 0 && (mark > cl.Data.crit[index].marks || mark < 0)) {
				$(this).addClass('imarkbook_error');
				errBounds += 1;
			}
		});
		if (errNaN) {
			return "0/0 - Error: Not a number.";
		}
		if (errBounds) {
			return "0/0 - Error: Mark out of bounds.";
		}

		return false;
	};
        
	ingotClass.iMarkingScheme.INGOTS_1.prototype.ScaledMark = function (row) {
		return 5 - 2 * this.CountCriteria('Criterion') + this.AddMarks(row, 'Criterion');
	};
	
	/*
	 * Factory method to find the row's status
	 */
	ingotClass.iMarkingScheme.INGOTS_1.prototype.Status = function (row) {
		var cl = this.getMarksheetClosure();
		var result = null;
		
		switch (cl.Data.unit) {
			case 1:
				result = this.E1U1(row);
				break; // Entry Level 1
			case 2:
				result = this.E2U1(row);
				break; // Entry Level 2
			case 4:
				result = this.E3U2(row);
				break; // Entry Level 3 Unit 2

			case 6:
				result = this.SimpleUnit(row, 22, 26, 32);
				break; // Level 1 Unit 2
			case 9:
				result = this.SimpleUnit(row, 18, 21, 25);
				break; // Level 1 Unit 5 (Enterprise)
			case 12:
				result = this.SimpleUnit(row, 22, 27, 32);
				break; // Level 2 Unit 3
			case 14:
				result = this.SimpleUnit(row, 28, 34, 40);
				break; // Level 2 Unit 5 (Enterprise)

			case 3:
				result = this.ScaledUnit(row, 'Test', 15, 18, 21);
				break; // E3U1
			case 5:
				result = this.ScaledUnit(row, 'Test', 18, 21, 25);
				break; // L1U1
			case 7:
				result = this.ScaledUnit(row, 'Task', 18, 21, 25);
				break; // L1U3
			case 8:
				result = this.ScaledUnit(row, 'Test', 18, 21, 25);
				break; // L1U4 (Enterprise)
			case 10:
				result = this.ScaledUnit(row, 'Test', 18, 21, 25);
				break; // L2U1
			case 11:
				result = this.ScaledUnit(row, 'Task', 18, 21, 25);
				break; // L2U2
			case 13:
				result = this.ScaledUnit(row, 'Test', 18, 21, 25);
				break; // L2U4 (Enterprise)
		}
		
		return result;
	};
		
	/*
	 * Get the defauly mark for a cell
	 */
	ingotClass.iMarkingScheme.INGOTS_1.prototype.DefaultMark = function (cell){
		return '0';
	}
		
	/*
	 * If this returns true then always force the
	 * mark to uppercase when applying to a cell
	 * or checking if the mark is valid
	 */
	ingotClass.iMarkingScheme.INGOTS_1.prototype.ForceMarkToUppercase = function(cell){
		return true;
	}
	
	/*
	 * return an array of valid marks for a cell
	 * 
	 * This method is resposible for handling what to
	 * return if no cell is passed
	 */
	ingotClass.iMarkingScheme.INGOTS_1.prototype.GetValidMarks = function (cell){
		if($(cell).hasClass('test'))
			return {type: ['int'], numeric: [0,12]};
		else
			return {setValues : ['0','1','2']};
	};

	/*
	 *   Marking scheme.
	 */
	ingotClass.iMarkingScheme.INGOTS_1.prototype.SimpleUnit = function (row, P, M, D) {
		return this.Pass({
			mark: this.AddMarks(row, '*'),
			pass: P,
			merit: M,
			dist: D,
			steps: 2
		});
	};
	ingotClass.iMarkingScheme.INGOTS_1.prototype.ScaledUnit = function (row, type, P, M, D) {
		var scaled, sum;
		scaled = this.ScaledMark(row);
		sum = this.AddMarks(row, type);
		if (this.MinMark(row, 'Criterion') === 0) {
			return "<b>1/5 -</b> Learner needs 1 mark in each criterion";
		}
		if (scaled < 1) {
			return "<b>2/5 -</b> Learner needs " + (1 - scaled) + " more criteria marks.";
		}
		if (sum === 0) {
			return "<b>3/5 -</b> Learner is ready for the " + (type === 'Test' ? 'test' : 'tasks') +".";
		}
		return this.Pass({
			mark: scaled + sum,
			pass: P,
			merit: M,
			dist: D,
			steps: 5
		});
	};
	ingotClass.iMarkingScheme.INGOTS_1.prototype.E1U1 = function (row) {
		var cl = this.getMarksheetClosure();
		if (this.MinMark(row, 'Criterion') === 0) {
			return "<b>1/3 -</b> Learner needs 1 mark in each criterion";
		}
		var count = 0;
		row.find('td.editable').each(function (i) {
			if (cl.Data.crit[i].type === 'Criterion') {
				if (parseInt($(this).html(), 10) === 2) {
					count += 1;
				}
			}
		});
		if (count < 28) {
			return "<b>2/3 -</b> Learner needs 2 marks in at least 28 criteria.";
		}
		return this.Pass({
			steps:3
		});
	};
	ingotClass.iMarkingScheme.INGOTS_1.prototype.E2U1 = function (row) {
		var sum, min;
		if (this.MinMark(row, 'Criterion') === 0) {
			return "<b>1/5 -</b> Learner needs 1 mark in each criterion";
		}
		sum = this.AddMarks(row, 'Criterion');
		min = this.MinMark(row, 'Task');
		if (sum < 90) {
			return "<b>2/5 -</b> Learner needs " + (90 - sum) + " more criteria marks.";
		}
		if (min === 0) {
			return "<b>3/5 -</b> Learner is ready for the tasks.";
		}
		if (min  <  8) {
			return "<b>4/5 -</b> Learner needs 8 marks in each task.";
		}
		return this.Pass({
			steps:5
		});
	};
	ingotClass.iMarkingScheme.INGOTS_1.prototype.E3U2 = function (row) {
		var total, sumCrit, sumTask;
		if (this.MinMark(row, 'Criterion') === 0) {
			return "<b>1/7 -</b> Learner needs 1 mark in each criterion";
		}
		sumCrit = this.AddMarks(row, 'Criterion');
		sumTask = this.AddMarks(row, 'Task');
		if (sumCrit < 16) {
			return "<b>2/7 -</b> Learner has " + sumCrit + " criteria marks. Minimum is 16.";
		}
		if (this.MinMark(row, 'Cooperation') === 0) {
			return "<b>3/7 -</b> Learner must pass the Cooperation criterion.";
		}
		if (sumTask === 0) {
			return "<b>4/7 -</b> Learner is ready for the tasks.";
		}
		if (sumTask < 11) {
			return "<b>5/7 -</b> Learner has " + sumTask + " task marks. Minimum is 11.";
		}
		total = sumCrit + sumTask;
		return this.Pass({
			mark: total,
			pass: 27,
			merit: 31,
			dist: 36,
			steps: 7
		});
	};

}(jQuery));
/*
 * iMarksheet provides the editable marksheet for the site
 */

(function ($) {	
	ingotClass.iMarkingScheme.MODBAC_FOUNDATION_CORE = function(options){
		this.callParentMethod('constructor', [options]);
		this.Init();
	};
	// Inherit from iMarkingScheme
	ingotClass.iMarkingScheme.MODBAC_FOUNDATION_CORE.inheritsFrom(ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE);	
		
	/*
	 * return an array of valid marks for a cell
	 * if no passed varaible then return the modbaccore marks
	 * 
	 * @param jQueryObject cell the cell to get the marks for
	 * @return array an array of the available marks
	 */
	ingotClass.iMarkingScheme.MODBAC_FOUNDATION_CORE.prototype.GetValidMarks = function (cell){
		if(this.GetCellType($(cell)) === 'grade')
			return {setValues : ['P','M','D']};
		else{
			return {anyvalue : true};
		}
	};
        
	/**
	 * Return the default mark of a cell
	 * 
	 * @param jQueryObject cell the cell to get the default mark for
	 * @return string the default mark
	 */
	ingotClass.iMarkingScheme.MODBAC_FOUNDATION_CORE.prototype.DefaultMark = function (cell){
		return '-';
	};
		
	/**
	 * Whether to force this mark to uppercase
	 */
	ingotClass.iMarkingScheme.MODBAC_FOUNDATION_CORE.prototype.ForceMarkToUppercase = function(cell){
		if(this.GetCellType(cell) === 'grade')
			return true;
		return false;
	};
                
	/**
	 * The message to be displayed when the criteria are not yet passed
	 */
	ingotClass.iMarkingScheme.MODBAC_FOUNDATION_CORE.prototype.NotPassedMessage = function (){
		return $.iMessage['JS_MODBAC_CORE_HIGHER_NOT_READY_FOR_MODERATION'];
	};
	
	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.MODBAC_FOUNDATION_CORE.prototype.HasCellPassed = function(cell){
		mark = cell.html();
		if(this.GetCellType(cell) === 'modbacgrade'){
			if($.inArray(mark, ['P', 'M', 'D']))
				return true;
		}
		return false;
	};
        	
}(jQuery));
/*
 * iMarksheet provides the editable marksheet for the site
 */

(function ($) {	
	ingotClass.iMarkingScheme.MODBAC_HIGHER_CORE = function(options){
		this.callParentMethod('constructor', [options]);
		this.Init();
	};
	// Inherit from iMarkingScheme
	ingotClass.iMarkingScheme.MODBAC_HIGHER_CORE.inheritsFrom(ingotClass.iMarkingScheme.MODBAC_UPPERCORES_BASE);
		
	/*
	 * return an array of valid marks for a cell
	 * if no passed varaible then return the modbaccore marks
	 * 
	 * @param jQueryObject cell the cell to get the marks for
	 * @return array an array of the available marks
	 */
	ingotClass.iMarkingScheme.MODBAC_HIGHER_CORE.prototype.GetValidMarks = function (cell){
		if(this.GetCellType($(cell)) === 'grade')
			return {setValues : ['P','M','D','D*']};
		else{
			return {anyvalue : true};
		}
	};
        
	/**
	 * Return the default mark of a cell
	 * 
	 * @param jQueryObject cell the cell to get the default mark for
	 * @return string the default mark
	 */
	ingotClass.iMarkingScheme.MODBAC_HIGHER_CORE.prototype.DefaultMark = function (cell){
		return '-';
	};
		
	/**
	 * Whether to force this mark to uppercase
	 */
	ingotClass.iMarkingScheme.MODBAC_HIGHER_CORE.prototype.ForceMarkToUppercase = function(cell){
		if(this.GetCellType(cell) === 'grade')
			return true;
		return false;
	};
	
	/**
	 * The message to be displayed when the criteria are not yet passed
	 */
	ingotClass.iMarkingScheme.MODBAC_HIGHER_CORE.prototype.NotPassedMessage = function (){
		return $.iMessage['JS_MODBAC_CORE_HIGHER_NOT_READY_FOR_MODERATION'];
	};
	
	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.MODBAC_HIGHER_CORE.prototype.HasCellPassed = function(cell){
		mark = cell.html();
		if(this.GetCellType(cell) === 'modbacgrade'){
			if($.inArray(mark, ['P', 'M', 'D', 'D*']))
				return true;
		}
		else{
			return true;
		}
		
		return false;
	};
	
}(jQuery));
/**
 * This provides the marking scheem for MODBAC_HONOURS_BASE
 */

(function ($) {	
	/**
	 * Define the object
	 * 
	 */
	ingotClass.iMarkingScheme.MODBAC_HONOURS_BASE = function(options){
		this.callParentMethod('constructor', [options]);
	};
	// Inherit from MODBAC_DEFAULT_BASE
	ingotClass.iMarkingScheme.MODBAC_HONOURS_BASE.inheritsFrom(ingotClass.iMarkingScheme.MODBAC_DEFAULT_BASE);	
	
	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.MODBAC_HONOURS_BASE.prototype.HasCellPassed = function(cell){
		mark = cell.html();
		if(this.GetCellType(cell) === 'modbacgrade'){
			if(mark === 'P' || mark === 'M' || mark === 'D')
				return true;
		}
		else if (mark === 'P'){
			return true;
		}
		return false;
	};

	/**
	 * Calculate the gradeCell for a row
	 * 
	 * If NC subject marks have been entered then automatically
	 * calculate the gradecell value
	 */
	ingotClass.iMarkingScheme.MODBAC_HONOURS_BASE.prototype.calculateGradeCell = function (row){
		var N = 0; P = 0;
		row.find('td.criterion:not(.imarkbook_test_markcell)').each(function () {
			switch($(this).html()){
				case 'N':
					N++;
					break;
				case 'P':
					P++;
					break;				
			}
		});
		// If 5 for more P then grade is D
		if(P >= 5)
			return 'D';
		// If 4 P then grade is D
		if(P == 4)
			return 'M';
		// If 3 P then grade is P
		if(P == 3)
			return 'P';
		// Otherwise grade is N	
		return 'N';

	};			

}(jQuery));
/**
 * This provides the marking scheem for MODBAC_HONOURS_HIGHER
 */

(function ($) {	
	/**
	 * Define the object
	 * 
	 */
	ingotClass.iMarkingScheme.MODBAC_HONOURS_HIGHER = function(options){
		this.callParentMethod('constructor', [options]);
	};
	// Inherit from MODBAC_DEFAULT_BASE
	ingotClass.iMarkingScheme.MODBAC_HONOURS_HIGHER.inheritsFrom(ingotClass.iMarkingScheme.MODBAC_HONOURS_BASE);	

	/**
	 * return an array of valid marks for a cell
	 * 
	 * Note: This must handle what to return if no cell is passed
	 */
	ingotClass.iMarkingScheme.MODBAC_HONOURS_HIGHER.prototype.GetValidMarks = function (cell){
		if(cell === undefined)
			return null;
		if(this.cl.Helper.isTestMarkCell(cell) === true){
			return {
				types: ['int'], 
				numeric : ['0', '100'],
				setValues : ['-']
			};
		}
		else if(this.GetCellType(cell) === 'modbacgrade'){
			return {setValues : ['N','P', 'M', 'D', 'D*']};
		}
		else{
			return {setValues : ['N','L', 'S','H']};
		}
	};
	
	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.MODBAC_HONOURS_HIGHER.prototype.HasCellPassed = function(cell){
		mark = cell.html();
		if(this.GetCellType(cell) === 'modbacgrade'){
			if(mark === 'P' || mark === 'M' || mark === 'D' || mark === 'D*')
				return true;
		}
		else if (mark === 'S', mark === 'H'){
			return true;
		}
		return false;
	};

	/**
	 * Calculate the gradeCell for a row
	 * 
	 * If NC subject marks have been entered then automatically
	 * calculate the gradecell value
	 */
	ingotClass.iMarkingScheme.MODBAC_HONOURS_HIGHER.prototype.calculateGradeCell = function (row){
		var N = 0, S = 0, H = 0, totalCells = 0; 
		row.find('td.criterion:not(.imarkbook_test_markcell)').each(function () {
			totalCells++;
			switch($(this).html()){
				case 'N':
					N++;
					break;
				case 'S':
					S++;
					break;				
				case 'H':
					H++;
					break;				
			}
		});
		// If at least 4 H and 5 passed in total
		if(H === totalCells)
			return 'D*';
		// If at least 4 H and 5 passed in total
		if(H >= 4 && S+H >= totalCells)
			return 'D';
		// If at least 2 H and 4 passed in total
		if(H >= 2 && S+H >= 4)
			return 'M';
		// If at least 4 passed in total
		if(S+H >= 4)
			return 'P';
		// Otherwise grade is N	
		return 'N';
	};			
	
	/**
	 * The message to be displayed when the criteria are not yet passed
	 */
	ingotClass.iMarkingScheme.MODBAC_HONOURS_HIGHER.prototype.NotPassedMessage = function (){
		return $.iMessage['Need S or H'];
	};

}(jQuery));
/**
 * This provides the marking scheem for MODBAC_HONOURS_LOWER
 * This includes the Midbac and Minibac
 */

(function ($) {	
	/**
	 * Define the object
	 * 
	 */
	ingotClass.iMarkingScheme.MODBAC_HONOURS_LOWER = function(options){
		this.callParentMethod('constructor', [options]);
	}
	// Inherit from MODBAC_DEFAULT_BASE
	ingotClass.iMarkingScheme.MODBAC_HONOURS_LOWER.inheritsFrom(ingotClass.iMarkingScheme.MODBAC_HONOURS_BASE);	

	/**
	 * return an array of valid marks for a cell
	 * 
	 * Note: This must handle what to return if no cell is passed
	 */
	ingotClass.iMarkingScheme.MODBAC_HONOURS_LOWER.prototype.GetValidMarks = function (cell){
		if(cell === undefined)
			return null;
		if(this.cl.Helper.isTestMarkCell(cell) === true){
			return {
				types: ['int'], 
				numeric : ['0', '100'],
				setValues : ['-']
			};
		}
		else if(this.GetCellType(cell) === 'modbacgrade'){
			return {setValues : ['N','P', 'M', 'D']};
		}
		else{
			return {setValues : ['N','P']};
		}
	};
	
	/**
	 * The message to be displayed when the criteria are not yet passed
	 */
	ingotClass.iMarkingScheme.MODBAC_HONOURS_LOWER.prototype.NotPassedMessage = function (){
		return $.iMessage['JS_MODBAC_HONOURS_LOWER_NOT_READY_FOR_MODERATION'];
	};

}(jQuery));
/**
 * This provides the marking scheem for MODBAC_HONOURS_MIDDLE
 */

(function ($) {	
	/**
	 * Define the object
	 * 
	 */
	ingotClass.iMarkingScheme.MODBAC_HONOURS_MIDDLE = function(options){
		this.callParentMethod('constructor', [options]);
	};
	// Inherit from MODBAC_DEFAULT_BASE
	ingotClass.iMarkingScheme.MODBAC_HONOURS_MIDDLE.inheritsFrom(ingotClass.iMarkingScheme.MODBAC_HONOURS_BASE);	

	/**
	 * return an array of valid marks for a cell
	 * 
	 * Note: This must handle what to return if no cell is passed
	 */
	ingotClass.iMarkingScheme.MODBAC_HONOURS_MIDDLE.prototype.GetValidMarks = function (cell){
		if(cell === undefined)
			return null;
		if(this.cl.Helper.isTestMarkCell(cell) === true){
			return {
				types: ['int'], 
				numeric : ['0', '100'],
				setValues : ['-']
			};
		}
		else if(this.GetCellType(cell) === 'modbacgrade'){
			return {setValues : ['N','P', 'M', 'D']};
		}
		else{
			return {setValues : ['N','L','S','H']};
		}
	};
	
	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.MODBAC_HONOURS_MIDDLE.prototype.HasCellPassed = function(cell){
		mark = cell.html();
		if(this.GetCellType(cell) === 'modbacgrade'){
			if(mark === 'P' || mark === 'M' || mark === 'D')
				return true;
		}
		else if (mark === 'S' || mark === 'H'){
			return true;
		}
		return false;
	};

	/**
	 * Calculate the gradeCell for a row
	 * 
	 * If NC subject marks have been entered then automatically
	 * calculate the gradecell value
	 */
	ingotClass.iMarkingScheme.MODBAC_HONOURS_MIDDLE.prototype.calculateGradeCell = function (row){
		var N = 0, S = 0, H = 0; 
		row.find('td.criterion:not(.imarkbook_test_markcell)').each(function () {
			switch($(this).html()){
				case 'N':
					N++;
					break;
				case 'S':
					S++;
					break;				
				case 'H':
					H++;
					break;				
			}
		});
		// If at least 4 H and 5 passed in total
		if(H >= 4 && S+H >= 5)
			return 'D';
		// If at least 2 H and 4 passed in total
		if(H >= 2 && S+H >= 4)
			return 'M';
		// If at least 4 passed in total
		if(S+H >= 4)
			return 'P';
		// Otherwise grade is N	
		return 'N';

	};			
	
	/**
	 * The message to be displayed when the criteria are not yet passed
	 */
	ingotClass.iMarkingScheme.MODBAC_HONOURS_MIDDLE.prototype.NotPassedMessage = function (){
		return $.iMessage['Need S or H'];
	};

}(jQuery));
/**
 * This provides the marking scheem for MODBAC_LOWER_CORE
 */

(function ($) {	
	/**
	 * Define the object
	 * 
	 */
	ingotClass.iMarkingScheme.MODBAC_LOWER_CORE = function(options){
		this.callParentMethod('constructor', [options]);
	}
	// Inherit from MODBAC_DEFAULT_BASE
	ingotClass.iMarkingScheme.MODBAC_LOWER_CORE.inheritsFrom(ingotClass.iMarkingScheme.MODBAC_DEFAULT_BASE);	

	/**
	 * return an array of valid marks for a cell
	 * 
	 * Note: This must handle what to return if no cell is passed
	 */
	ingotClass.iMarkingScheme.MODBAC_LOWER_CORE.prototype.GetValidMarks = function (cell){
		if(cell === undefined)
			return null;
		if(this.cl.Helper.isTestMarkCell(cell) === true){
			return {
				types: ['int'], 
				numeric : ['0', '100'],
				setValues : ['-']
			};
		}
		else{
			return {setValues : ['N','P','M','D','W']};
		}
	};
	
	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.MODBAC_LOWER_CORE.prototype.HasCellPassed = function(cell){
		mark = cell.html();
		if (mark !== 'P' && mark !== 'M' && mark !== 'D' && mark !== 'W')
			return false;
		return true;
	};

	/**
	 * Calculate the gradeCell for a row
	 * 
	 * If NC subject marks have been entered then automatically
	 * calculate the gradecell value
	 */
	ingotClass.iMarkingScheme.MODBAC_LOWER_CORE.prototype.calculateGradeCell = function (row){
		var P = 0, M = 0, D = 0, passed = true;
		row.find('td.criterion:not(.imarkbook_test_markcell)').each(function () {
			switch($(this).html()){
				case 'N':
					passed = false
					return false;
					break;
				case 'P':
					P++;
					break;
				case 'M':
					M++;
					break;
				case 'D':
					D++;
					break;
				
			}
		});
		// If any were N then false was returned
		if(passed === false)
			return 'N';
		// If 8 or more Ds then grade is D
		if(D >= 8)
			return 'D';
		// if 8 or more M or D then grade is M
		if((M+D) >= 8)
			return 'M';
		// Otherwise grade is P
		return 'P';
	};
	
	/**
	 * The message to be displayed when the criteria are not yet passed
	 */
	ingotClass.iMarkingScheme.MODBAC_LOWER_CORE.prototype.NotPassedMessage = function (){
		return $.iMessage['JS_MODBAC_CORE_LOWER_NOT_READY_FOR_MODERATION'];
	};

}(jQuery));
/**
 * This provides the marking scheem for MODBAC_SKILLSPASSPORT
 */

(function ($) {	
	/**
	 * Define the object
	 * 
	 */
	ingotClass.iMarkingScheme.MODBAC_SKILLSPASSPORT = function(options){
		this.callParentMethod('constructor', [options]);
	}
	// Inherit from MODBAC_DEFAULT_BASE
	ingotClass.iMarkingScheme.MODBAC_SKILLSPASSPORT.inheritsFrom(ingotClass.iMarkingScheme.MODBAC_DEFAULT_BASE);	

	/**
	 * return an array of valid marks for a cell
	 * 
	 * Note: This must handle what to return if no cell is passed
	 */
	ingotClass.iMarkingScheme.MODBAC_SKILLSPASSPORT.prototype.GetValidMarks = function (cell){
		if(cell === undefined)
			return null;
		if(this.cl.Helper.isTestMarkCell(cell) === true){
			return {
				types: ['int'], 
				numeric : ['0', '100'],
				setValues : ['-']
			};
		}
		else if(this.GetCellType(cell) === 'modbacgrade'){
			return {setValues : ['N','P','W']};
		}
		else{
			return {setValues : ['N','P']};
		}
	};
	
	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.MODBAC_SKILLSPASSPORT.prototype.HasCellPassed = function(cell){
		mark = cell.html();
		if(this.GetCellType(cell) === 'modbacgrade'){
			if(mark === 'P' || mark === 'W')
				return true;
		}
		else if (mark === 'P'){
			return true;
		}
		return false;
	};

	/**
	 * Calculate the gradeCell for a row
	 * 
	 * If NC subject marks have been entered then automatically
	 * calculate the gradecell value
	 */
	ingotClass.iMarkingScheme.MODBAC_SKILLSPASSPORT.prototype.calculateGradeCell = function (row){
		var N = 0, P = 0, totalCells = 0;
		row.find('td.criterion:not(.imarkbook_test_markcell)').each(function () {
			totalCells++;
			switch($(this).html()){
				case 'N':
					N++;
					break;
				case 'P':
					P++;
					break;				
			}
		});
		// All cells are P then grade is P
		if(P == totalCells)
			return 'P';
		// Otherwise grade is N	
		return 'N';

	};			

	/**
	 * The message to be displayed when the criteria are not yet passed
	 */
	ingotClass.iMarkingScheme.MODBAC_SKILLSPASSPORT.prototype.NotPassedMessage = function (){
		return $.iMessage['JS_MODBAC_SKILLSPASSPORT_NOT_READY_FOR_MODERATION'];
	};

}(jQuery));
/**
 * Default NLSH Marking Scheme
 * 
 */
(function ($) {	
	/**
	 * Define the object
	 */
	ingotClass.iMarkingScheme.NLSH_Default = function(options){
		this.callParentMethod('constructor', [options]);
	}				
	// Inherit from iMarkingScheme
	ingotClass.iMarkingScheme.NLSH_Default.inheritsFrom(ingotClass.iMarkingScheme);	
	
	/**
	 * Get the defauly mark for a cell
	 */
	ingotClass.iMarkingScheme.NLSH_Default.prototype.DefaultMark = function (cell){
		var cl = this.getMarksheetClosure();
		if (cl.Helper.isTestMarkCell(cell) === true)
			return '-';
        if (cl.Helper.isExamMarkCell(cell) === true)
            return 'N';
		if (cell.hasClass('fin1230testmark') || cell.hasClass('fin820testmark') || cell.hasClass('finfinaltestmark')) { 
			return '-';
		}
		return 'N';
	};
			
	/**
	 * Force the mark to uppercase
	 */
	ingotClass.iMarkingScheme.NLSH_Default.prototype.ForceMarkToUppercase = function(cell){
		return true;
	};
			
	/**
	 * return an array of valid marks for a cell
	 * 
	 * Note: This must handle what to return if no cell is passed
	 */
	ingotClass.iMarkingScheme.NLSH_Default.prototype.GetValidMarks = function (cell){
		var cl = this.getMarksheetClosure();
		if(cell === undefined)
			return null;
		if (cl.Helper.isTestMarkCell(cell) === true) {
			return {
				types: ['int'], 
				numeric : ['0', '100'],
				setValues : ['-']
			};
		} 
		else if (cell.hasClass('fin1230testmark')) { 
			return {
				types: ['int'], 
				numeric : ['0', '100'],
				setValues : ['-']
			};
		}
		else if (cell.hasClass('fin1530testmark')) { 
			return {
				types: ['int'], 
				numeric : ['0', '100'],
				setValues : ['-']
			};
		}
		else if (cell.hasClass('fin1035testmark')) { 
			return {
				types: ['int'], 
				numeric : ['0', '100'],
				setValues : ['-']
			};
		}
		else if (cell.hasClass('fin820testmark')) { 
			return {
				types: ['int'], 
				numeric : ['0', '100'],
				setValues : ['-']
			};
		}
		else if (cell.hasClass('finfinaltestmark')) { 
			return {
				types: ['int'], 
				numeric : ['0', '100'],
				setValues : ['-']
			};
		}
		else {
            if (cl.Helper.isExamMarkCell(cell) === true)
				return {setValues : ['D*','M','D','P','N','O','P','A*','A','B','C','U','-','1','0','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31','32','33','34','35','36','37','38','39','40','41','42','43','44','45','46','47','48','49','50','51','52','53','54','55','56','57','58','59','60','61','62','63','64','65','66','67','68','69','70','71','72','73','74','75','76','77','78','79','80','81','82','83','84','85','86','87','88','89','90','91','92','93','94','95','96','97','98','99','100']};
            if (cl.Helper.isExamMarkCell(cell) !== true)
				return {setValues : ['N','L','S','H']};
		}
	};

	/**
	 * Factory method to calculate the row's status
	 */
	ingotClass.iMarkingScheme.NLSH_Default.prototype.getReadyForModerationMessage = function (row){
		var result = null, cl = this.getMarksheetClosure();
    
		switch(cl.Data.frameworkID) {
			case "INGOTS_NATIONAL_CURRICULUM":
				result = $.iMessage['Pass'];
				break;
			case "INGOTS_P_SCALES":
				result = $.iMessage['Pass'];
				break;
			case "QCF":
				result = $.iMessage['Ready for moderation'];
				break;
			case "INGOTS_FUNCTIONAL_SKILLS":
				result = $.iMessage['Ready for the test'];
				break;
			default:
				result = $.iMessage['Ready for moderation'];
				break;
		}
    // AWS registration unit
		if (cl.Data.unit == 2203 || cl.Data.is_reg_unit !== false) result = "<b>Ready to Request Registration.</b>";
		return result;	
	};
			
	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.NLSH_Default.prototype.HasCellPassed = function(cell){
		mark = cell.html();
		if (cell.hasClass('fin1230testmark')) { 
			mark = cell.html();
			if(parseInt(mark, 10) >= 12)
				return true;
			return false;
		}
		if (cell.hasClass('fin1530testmark')) { 
			mark = cell.html();
			if(parseInt(mark, 10) >= 15)
				return true;
			return false;
		}
		if (cell.hasClass('fin1035testmark')) { 
			mark = cell.html();
			if(parseInt(mark, 10) >= 10)
				return true;
			return false;
		}
		if (cell.hasClass('fin820testmark')) { 
			mark = cell.html();
			if(parseInt(mark, 10) >= 8)
				return true;
			return false;
		}
		if (cell.hasClass('finfinaltestmark')) { 
			mark = cell.html();
			if(parseInt(mark, 10) >= 0)
				return true;
			return false;
		}
		if (mark !== 'S' && mark !== 'H')
			return false;
		return true;
	};
			
	/**
	 * The message to be displayed when the criteria are not yet passed
	 */
	ingotClass.iMarkingScheme.NLSH_Default.prototype.NotPassedMessage = function (){
		var cl = this.getMarksheetClosure();
		if (cl.Data.unit == 2203) return "Please use S in the 'Registration' column and then 'Save' and 'Request registration'";
		if (cl.Data.unit == 2486 || cl.Data.unit == 2490 || cl.Data.unit == 2618) return "Waiting for exam results";
		if (cl.Data.unit == 2496) return "Type L to register learner for exam.";
			return $.iMessage['Need S or H']; 
			// return $.iMessage['Need S or H'];
		};
        /**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.NLSH_Default.prototype.HasTestCellPassed = function(cell){
		mark = cell.html();
		if (mark !== 'O' && mark !== 'P')
			return false;
		return true;
	};
        /**
	 * The message to be displayed when the Test are not yet passed
	 */
	ingotClass.iMarkingScheme.NLSH_Default.prototype.NotTestPassedMessage = function (){
		return $.iMessage['Need Y or N'];
	};

}(jQuery));
/**
 * EDS Marking Scheme
 * 
 * 
 */

(function ($) {	
	/**
	 * Define the object
	 */
	ingotClass.iMarkingScheme.NLSH_EDS = function (options){
		this.callParentMethod('constructor', [options]);
	}
	// Inherit from NLSH_Default
	ingotClass.iMarkingScheme.NLSH_EDS.inheritsFrom(ingotClass.iMarkingScheme.NLSH_Default);	
			
	/**
	 * @Override
	 * Get the default mark for a cell
	 */
	ingotClass.iMarkingScheme.NLSH_EDS.prototype.DefaultMark = function (cell){
		if(cell.hasClass('edstestmark') || cell.hasClass('edstestmark-resit') || cell.hasClass('edsexammark') || cell.hasClass('edsexammark-resit')) // If this is the EDS practicle task or exam mark
			return '-';
    if(cell.hasClass('edsregister') ) // If this is the EDS Registration
			return 'N';
		return this.callParentMethod('DefaultMark', [cell]);
	};
	
	/**
	 * return an array of valid marks for a cell
	 * 
	 * Note: This must handle what to return if no cell is passed
	 */
	ingotClass.iMarkingScheme.NLSH_EDS.prototype.GetValidMarks = function (cell){
		if(cell === undefined)
			return null;
		if(cell.hasClass('edstestmark') || cell.hasClass('edstestmark-resit') || cell.hasClass('edsexammark') || cell.hasClass('edsexammark-resit')){ // If this is the EDS test mark
			return {
				types: ['int'], 
				numeric : ['0', '45'],
				setValues : ['-']
			};   
    } else if (cell.hasClass('edsregister')) {
        return {
				setValues : ['N','R']
			};       
    }
		else{ // Standard NLSH marks
			return this.callParentMethod('GetValidMarks', [cell]);
		}
	};
	
	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.NLSH_EDS.prototype.HasCellPassed = function(cell){
    let mark = cell.html();
		if(cell.hasClass('edstestmark') || cell.hasClass('edsexammark')){ // If this is the EDS test mark
			if(parseInt(mark, 10) >= 0)
				return true;
			return false;
		} else if ( cell.hasClass('edstestmark-resit') || cell.hasClass('edsexammark-resit')) {
      if(parseInt(mark, 10) >= 0)
				return true;
			return false;         
}  else if(cell.hasClass('edsregister') ) {
      if (mark == "R") return true; //mark == "N" || 
      return false;
    }
		return this.callParentMethod('HasCellPassed', [cell]);
	};
	
	/**
	 * Factory method to calculate the row's status
	 */
	ingotClass.iMarkingScheme.NLSH_EDS.prototype.getReadyForModerationMessage = function (row){
		return $.iMessage['Ready for moderation'];
	};

	/**
	 * The message to be displayed when not passed
	 */
	ingotClass.iMarkingScheme.NLSH_EDS.prototype.NotPassedMessage = function (){
		  return 'Please use R in the "Registration" column and then enter the mark for the Practical Assessments.';
// 		  return $.iMessage['JS_EDS_NOT_PASSED_MESSAGE'];
	};
	
}(jQuery));
/**
 * Food Hygiene Marking Scheme
 * 
 * 
 */

(function ($) {	
	/**
	 * Define the object
	 */
	ingotClass.iMarkingScheme.NLSH_FoodHygiene = function (options){
		this.callParentMethod('constructor', [options]);
	}
	// Inherit from NLSH_Default
	ingotClass.iMarkingScheme.NLSH_FoodHygiene.inheritsFrom(ingotClass.iMarkingScheme.NLSH_Default);	
			
	/**
	 * @Override
	 * Get the default mark for a cell
	 */
	ingotClass.iMarkingScheme.NLSH_FoodHygiene.prototype.DefaultMark = function (cell){
		if(cell.hasClass('foodhygienetestmark')) // If this is the Food Hygiene test mark
			return '-';
		return this.callParentMethod('DefaultMark', [cell]);
	};
	
	/**
	 * return an array of valid marks for a cell
	 * 
	 * Note: This must handle what to return if no cell is passed
	 */
	ingotClass.iMarkingScheme.NLSH_FoodHygiene.prototype.GetValidMarks = function (cell){
		if(cell === undefined)
			return null;
		if(cell.hasClass('foodhygienetestmark')){ // If this is the Food Hygiene test mark
			return {
				types: ['int'], 
				numeric : ['0', '30'],
				setValues : ['-']
			};
		}
		else{ // Standard NLSH marks
			return this.callParentMethod('GetValidMarks', [cell]);
		}
	};
	
	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.NLSH_FoodHygiene.prototype.HasCellPassed = function(cell){
		if(cell.hasClass('foodhygienetestmark')){ // If this is the Food Hygiene test mark
			mark = cell.html();
			if(parseInt(mark, 10) >= 20)
				return true;
			return false;
		}
		return this.callParentMethod('HasCellPassed', [cell]);
	};
	
	/**
	 * Factory method to calculate the row's status
	 */
	ingotClass.iMarkingScheme.NLSH_FoodHygiene.prototype.getReadyForModerationMessage = function (row){
		return $.iMessage['Ready for moderation'];
	};

	/**
	 * The message to be displayed when not passed
	 */
	ingotClass.iMarkingScheme.NLSH_FoodHygiene.prototype.NotPassedMessage = function (){
		return $.iMessage['JS_FOODHYGIENE_NOT_PASSED_MESSAGE'];
	};
	
}(jQuery));
/**
 * OME Drums Marking Scheme
 * 
 * 
 */

(function ($) {	
	/**
	 * Define the object
	 */
	ingotClass.iMarkingScheme.NLSH_OmeDrums = function (options){
		this.callParentMethod('constructor', [options]);
	}
	// Inherit from NLSH_Default
	ingotClass.iMarkingScheme.NLSH_OmeDrums.inheritsFrom(ingotClass.iMarkingScheme.NLSH_Default);	
			
	/**
	 * @Override
	 * Get the default mark for a cell
	 */
	ingotClass.iMarkingScheme.NLSH_OmeDrums.prototype.DefaultMark = function (cell){
		if(cell.hasClass('omedrumstestmark')) // If this is the OME Drums test mark
			return '-';
		return this.callParentMethod('DefaultMark', [cell]);
	};
	
	/**
	 * return an array of valid marks for a cell
	 * 
	 * Note: This must handle what to return if no cell is passed
	 */
	ingotClass.iMarkingScheme.NLSH_OmeDrums.prototype.GetValidMarks = function (cell){
		if(cell === undefined)
			return null;
		if(cell.hasClass('omedrumstestmark')){ // If this is the OME Drums test mark
			return {
				types: ['int'], 
				numeric : ['0', '100'],
				setValues : ['-']
			};
		}
		else{ // Standard NLSH marks
			return this.callParentMethod('GetValidMarks', [cell]);
		}
	};
	
	/**
	 * Check an individual cell to see if it has passed
	 */
	ingotClass.iMarkingScheme.NLSH_OmeDrums.prototype.HasCellPassed = function(cell){
		if(cell.hasClass('omedrumstestmark')){ // If this is the OME Drums test mark
			mark = cell.html();
			if(parseInt(mark, 10) >= 55)
				return true;
			return false;
		}
		return this.callParentMethod('HasCellPassed', [cell]);
	};
	
	/**
	 * Factory method to calculate the row's status
	 */
	ingotClass.iMarkingScheme.NLSH_OmeDrums.prototype.getReadyForModerationMessage = function (row){
		return $.iMessage['Ready for moderation'];
	};

	/**
	 * The message to be displayed when not passed
	 */
	ingotClass.iMarkingScheme.NLSH_OmeDrums.prototype.NotPassedMessage = function (){
		return "Learner needs S in each criterion and 55 or more in the Exam";
	};
	
}(jQuery));
/**
 * Default User Interface
 * 
 */
(function ($) {	
	/**
	 * Define the Constructor
	 */
	ingotClass.iMarksheetUserInterface.DUKEOFYORK_ADDITIONALINFO = function(options){
		this.callParentMethod('constructor', [options]);
		this.addActionType(this.loadDataDialog);	
				
		this.Init();
	};
	// Inherit from iMarkingScheme
	ingotClass.iMarksheetUserInterface.DUKEOFYORK_ADDITIONALINFO.inheritsFrom(ingotClass.iMarksheetUserInterface);

	/**
	 * Init
	 */
	ingotClass.iMarksheetUserInterface.DUKEOFYORK_ADDITIONALINFO.prototype.Init = function(){
		var cl = this.getMarksheetClosure();
		// Nothing should be multiselected, but next line left in as an example
		//cl.Navigation.MultiSelectElements = 'td.editable.modbaccore';
		cl.Navigation.MultiSelectElements = '';
		// Nothing should be selected by single-click either
		cl.Navigation.SingleSelectElements = '';
		// Suppress highlighting the cell XY co-ordinates
		cl.Data.suppressHighlightCellXY = true;
		// Do not suppress showing the criterion
		cl.Data.suppressShowCriterion = false;
	};

	/**
	 * Load Data Dialog Action Type
	 */
	ingotClass.iMarksheetUserInterface.DUKEOFYORK_ADDITIONALINFO.prototype.loadDataDialog = function(options){
		this.callParentMethod('constructor', [options]);
		this.setMarksheetClosure(options.marksheetClosure);
	};
	// Inherit from ActionTypeContainer
	ingotClass.iMarksheetUserInterface.DUKEOFYORK_ADDITIONALINFO.prototype.loadDataDialog.inheritsFrom(ingotClass.iMarksheetUserInterface.ActionTypeContainer);
	ingotClass.iMarksheetUserInterface.DUKEOFYORK_ADDITIONALINFO.prototype.loadDataDialog.prototype.getEventHandler = function(){
		return this.eventHandler();
	};
	// Set applied events
	ingotClass.iMarksheetUserInterface.DUKEOFYORK_ADDITIONALINFO.prototype.loadDataDialog.prototype.getAppliedElements = function(){
		return $('.imarkbook_table tr td:not(.imarkbook_notecell)');
	};
	// Set applied events
	ingotClass.iMarksheetUserInterface.DUKEOFYORK_ADDITIONALINFO.prototype.loadDataDialog.prototype.getEventType = function(){
		return 'click';
	};
	// Set applied events
	ingotClass.iMarksheetUserInterface.DUKEOFYORK_ADDITIONALINFO.prototype.loadDataDialog.prototype.getTables = function(){
		var schemeObject = this.getMarksheetClosure().Helper.GetCurrentScheme();
		var tableStructure = schemeObject.getTableStructure();
		return {
			FIELDS: {
				heading: 'Fields',
				id: schemeObject.GetCellType(),
				initRows: schemeObject.getNumberOfFieldRowsForDialog(),
				addRows: false,
				data: function(pid){
					return schemeObject.GetPupilData(pid);
				},
				structure: tableStructure
			}
		};
	};
	// Bind this to the click event
	ingotClass.iMarksheetUserInterface.DUKEOFYORK_ADDITIONALINFO.prototype.loadDataDialog.prototype.eventHandler = function(){
		var cl = this.getMarksheetClosure();
		var tables = this.getTables();
		return function(){
			var dialogObject = new ingotClass.iMarkDataDialogInput({
				marksheetClosure: cl,
				row: $(this).closest('tr'),
				tables: tables,
				dialogTitle: 'Duke Of York'
			});
			dialogObject.showDialog();
		};
	};

}(jQuery));
/**
 * Default User Interface
 * 
 */
(function ($) {	
	/**
	 * Define the Constructor
	 */
	ingotClass.iMarksheetUserInterface.Default = function(options){
		this.callParentMethod('constructor', [options]);
		this.addActionType(this.clickToggleRow);	
	}
	// Inherit from iMarkingScheme
	ingotClass.iMarksheetUserInterface.Default.inheritsFrom(ingotClass.iMarksheetUserInterface);
	
	/**
	 * DocumentKeypress
	 */
	ingotClass.iMarksheetUserInterface.Default.prototype.documentKeypress = function(options){
		this.callParentMethod('constructor', [options]);
		this.setMarksheetClosure(options.marksheetClosure);
	};
	ingotClass.iMarksheetUserInterface.Default.prototype.documentKeypress.inheritsFrom(ingotClass.iMarksheetUserInterface.prototype.documentKeypress);
	// beforeKeys
	ingotClass.iMarksheetUserInterface.Default.prototype.documentKeypress.prototype.beforeKeys = function(e, cl, uiObject){
		// If alphanumeric input then add to selected cell
		var value = cl.Helper.GetPrintableCharacterFromEvent(e);
		if (value !== '') 
			cl.Action.ChangeCellValue(value);		
	};
	
	/**
	 * Document Keydown
	 */
	ingotClass.iMarksheetUserInterface.Default.prototype.documentKeydown = function(options){
		this.callParentMethod('constructor', [options]);
		this.setMarksheetClosure(options.marksheetClosure);
	};
	ingotClass.iMarksheetUserInterface.Default.prototype.documentKeydown.inheritsFrom(ingotClass.iMarksheetUserInterface.prototype.documentKeydown);
	// Left Arrow
	ingotClass.iMarksheetUserInterface.Default.prototype.documentKeydown.prototype['37'] = function(e, cl, uiObject){
		var currentCell = cl.Root.find('td.'+cl.ElementName.SelectedCell);
		cl.Action.SelectCell(currentCell.prev());
		return false;
	};
	// Right Arrow
	ingotClass.iMarksheetUserInterface.Default.prototype.documentKeydown.prototype['39'] = function(e, cl, uiObject){
		var currentCell = cl.Root.find('td.'+cl.ElementName.SelectedCell);
		cl.Action.SelectCell(currentCell.next());
		return false;
	};
	// Down Arrow
	ingotClass.iMarksheetUserInterface.Default.prototype.documentKeydown.prototype['40'] = function(e, cl, uiObject){
		var currentCell = cl.Root.find('td.'+cl.ElementName.SelectedCell);
		var cellIndex = currentCell.parent("tr").children().index(currentCell);
		var newCell = currentCell.closest("tr")
								 .nextAll("tr:not(."+cl.ElementName.NoteRow+")").eq(0)
								 .find("td").eq(cellIndex);
		cl.Action.SelectCell(newCell);
		return false;
	};
	// Up Arrow
	ingotClass.iMarksheetUserInterface.Default.prototype.documentKeydown.prototype['38'] = function(e, cl, uiObject){
		var currentCell = cl.Root.find('td.'+cl.ElementName.SelectedCell);
		var cellIndex = currentCell.parent("tr").children().index(currentCell);
		var newCell = currentCell.closest("tr")
								 .prevAll("tr:not(."+cl.ElementName.NoteRow+")").eq(0)
								 .find("td").eq(cellIndex);
		cl.Action.SelectCell(newCell);
		return false;
	};
	// Backspace
	ingotClass.iMarksheetUserInterface.Default.prototype.documentKeydown.prototype['8'] = function(e, cl, uiObject){
		cl.Action.ChangeCellValue(false);
	};
	// Enter
	ingotClass.iMarksheetUserInterface.Default.prototype.documentKeydown.prototype['13'] = function(e, cl, uiObject){
		cl.Helper.IsEditingCell(false);
	};	
	
/**
 * Toggle Row by clicking
 */
	/**
	 * Load Data Dialog Action Type
	 */
	ingotClass.iMarksheetUserInterface.Default.prototype.clickToggleRow = function(options){
		this.callParentMethod('constructor', [options]);
		this.setMarksheetClosure(options.marksheetClosure);
	};
	// Inherit from ActionTypeContainer
	ingotClass.iMarksheetUserInterface.Default.prototype.clickToggleRow.inheritsFrom(ingotClass.iMarksheetUserInterface.ActionTypeContainer);
	// Set applied events
	ingotClass.iMarksheetUserInterface.Default.prototype.clickToggleRow.prototype.getAppliedElements = function(){
		return $('td.imarkbook_pid, td.imarkbook_forenamecell, td.imarkbook_surnamecell, td.imarkbook_status');
	};
	// Set applied events
	ingotClass.iMarksheetUserInterface.Default.prototype.clickToggleRow.prototype.getEventType = function(){
		return 'click';
	};
	ingotClass.iMarksheetUserInterface.Default.prototype.clickToggleRow.prototype.getEventHandler = function(){
		var cl = this.getMarksheetClosure();
		return function(){
			cl.Action.SelectAllToggle($(this).closest('tr'));
			return false;
		}
	};
}(jQuery));
/**
 * Default User Interface
 * 
 */
(function ($) {	
	/**
	 * Define the Constructor
	 */
	ingotClass.iMarksheetUserInterface.MODBAC_DEFAULT = function(options){
		this.callParentMethod('constructor', [options]);
		this.addActionType(this.loadStatementDialog);	
	}				
	// Inherit from iMarkingScheme
	ingotClass.iMarksheetUserInterface.MODBAC_DEFAULT.inheritsFrom(ingotClass.iMarksheetUserInterface.Default);
	
	/**
	 * Document Keypress
	 */
	ingotClass.iMarksheetUserInterface.MODBAC_DEFAULT.prototype.documentKeypress = function(options){
		this.callParentMethod('constructor', [options]);
		this.setMarksheetClosure(options.marksheetClosure);
	};
	ingotClass.iMarksheetUserInterface.MODBAC_DEFAULT.prototype.documentKeypress.inheritsFrom(ingotClass.iMarksheetUserInterface.Default.prototype.documentKeypress);
	
	/* 
	 * beforeKeys
	 * Only allow grade to be changed if no marks are in criteria cells
	 * Do not allow grade cell to be changed if marks are in criteria cells
	 */ 
	ingotClass.iMarksheetUserInterface.MODBAC_DEFAULT.prototype.documentKeypress.prototype.beforeKeys = function(e, cl, uiObject){
		// If alphanumeric input then add to selected cell
		var markingScheme = cl.Helper.GetCurrentScheme();
		
		// Get the selected Cell
		var selectedCell = cl.Helper.GetCurrentlySelectedCell();
		
		// Get row
		var row = $.unique(selectedCell.closest("tr"));
		
		// If entering criterion cell
		var cellType = markingScheme.GetCellType(selectedCell);
		if(cellType === 'criterion'){
			if(markingScheme.doesFinalGradeExist(row) === true){
				if(markingScheme.hasCriterionDataBeenEntered(row) !== true){
					return false;
				}
			}
		}
		// If entering grade cell
		if(cellType === 'modbacgrade'){
			if(markingScheme.hasCriterionDataBeenEntered(row) === true){
				return false;
			}
		}
		markingScheme.setLastEnteredCellType(cellType);
		this.callParentMethod('beforeKeys', [e, cl, uiObject]);	
		markingScheme.setLastEnteredCellType('');
	};
	
	/**
	 * Override which cells trigger toggle row
	 * @returns {unresolved}
	 */
	ingotClass.iMarksheetUserInterface.MODBAC_DEFAULT.prototype.clickToggleRow.prototype.getAppliedElements = function(){
		return $('td.imarkbook_pid, td.imarkbook_status');
	};

/**
 * Load a text box 
 */
	/**
	 * Load Data Dialog Action Type
	 */
	ingotClass.iMarksheetUserInterface.MODBAC_DEFAULT.prototype.loadStatementDialog = function(options){
		this.callParentMethod('constructor', [options]);
		this.setMarksheetClosure(options.marksheetClosure);
	};
	// Inherit from ActionTypeContainer
	ingotClass.iMarksheetUserInterface.MODBAC_DEFAULT.prototype.loadStatementDialog.inheritsFrom(ingotClass.iMarksheetUserInterface.ActionTypeContainer);
	ingotClass.iMarksheetUserInterface.MODBAC_DEFAULT.prototype.loadStatementDialog.prototype.getEventHandler = function(){
		return this.eventHandler();
	};
	// Set applied events
	ingotClass.iMarksheetUserInterface.MODBAC_DEFAULT.prototype.loadStatementDialog.prototype.getAppliedElements = function(){
		return $('.imarkbook_forenamecell, .imarkbook_surnamecell');
	};
	// Set applied events
	ingotClass.iMarksheetUserInterface.MODBAC_DEFAULT.prototype.loadStatementDialog.prototype.getEventType = function(){
		return 'click';
	};
	// Bind this to the click event
	ingotClass.iMarksheetUserInterface.MODBAC_DEFAULT.prototype.loadStatementDialog.prototype.eventHandler = function(){
		var cl = this.getMarksheetClosure();
		var scheme = this.getMarksheetClosure().Helper.GetCurrentScheme();
		return function(){
			var cell = $(this);
			var row = cl.Helper.GetRowFromCell(cell);
			var pid = cl.Helper.GetPidFromRow(row);

			var name = cl.Helper.GetPupilNameFromRow(row);
			
			var currentStatement = scheme.getPupilStatement(pid);

			var textArea = $("<div>");
			textArea.iAdditionalMarksDataTextAreaInput({
				setDataCallback: function(data){
					scheme.setPupilStatement(pid, 'statement', data);
				},
				validationCallback: function(data){
					return true;
				},
				textValue: currentStatement,
				maxLength: 200,
				rows: 5,
				cols: 50
			});

			var dialogContents = $("<div>");
			dialogContents.append(name);
			dialogContents.append(textArea);
			
			cl.CanUserSelectCells = false;
			cl.Action.UnSelectAll();
			$.fn.iDialog({element: dialogContents});
		};
	};

}(jQuery));
/**
 * Default User Interface
 * 
 */
(function ($) {	
	/**
	 * Define the Constructor
	 */
	ingotClass.iMarksheetUserInterface.MODBAC_FOUNDATIONUPPER_CORES = function(options){
		this.callParentMethod('constructor', [options]);
		this.addActionType(this.loadDataDialog);
		this.addActionType(this.loadStatementDialog);

		
		this.Init();
	};
	// Inherit from iMarkingScheme
	ingotClass.iMarksheetUserInterface.MODBAC_FOUNDATIONUPPER_CORES.inheritsFrom(ingotClass.iMarksheetUserInterface);

	/**
	 * Init
	 */
	ingotClass.iMarksheetUserInterface.MODBAC_FOUNDATIONUPPER_CORES.prototype.Init = function(){
		var cl = this.getMarksheetClosure();
		// Nothing should be multiselected, but next line left in as an example
		//cl.Navigation.MultiSelectElements = 'td.editable.modbaccore';
		cl.Navigation.MultiSelectElements = '';
		// Nothing should be selected by single-click either
		cl.Navigation.SingleSelectElements = '';
		// Suppress highlighting the cell XY co-ordinates
		cl.Data.suppressHighlightCellXY = true;
		// Do not suppress showing the criterion
		cl.Data.suppressShowCriterion = false;
	};

	/**
	 * Load Data Dialog Action Type
	 */
	ingotClass.iMarksheetUserInterface.MODBAC_FOUNDATIONUPPER_CORES.prototype.loadDataDialog = function(options){
		this.callParentMethod('constructor', [options]);
		this.setMarksheetClosure(options.marksheetClosure);
	};
	// Inherit from ActionTypeContainer
	ingotClass.iMarksheetUserInterface.MODBAC_FOUNDATIONUPPER_CORES.prototype.loadDataDialog.inheritsFrom(ingotClass.iMarksheetUserInterface.ActionTypeContainer);
	ingotClass.iMarksheetUserInterface.MODBAC_FOUNDATIONUPPER_CORES.prototype.loadDataDialog.prototype.getEventHandler = function(){
		return this.eventHandler();
	};
	// Set applied events
	ingotClass.iMarksheetUserInterface.MODBAC_FOUNDATIONUPPER_CORES.prototype.loadDataDialog.prototype.getAppliedElements = function(){
		return $('.imarkbook_table tr td:not(.imarkbook_notecell, .imarkbook_forenamecell, .imarkbook_surnamecell)');
	};
	// Set applied events
	ingotClass.iMarksheetUserInterface.MODBAC_FOUNDATIONUPPER_CORES.prototype.loadDataDialog.prototype.getEventType = function(){
		return 'click';
	};
	// Set applied events
	ingotClass.iMarksheetUserInterface.MODBAC_FOUNDATIONUPPER_CORES.prototype.loadDataDialog.prototype.getTables = function(){
		var schemeObject = this.getMarksheetClosure().Helper.GetCurrentScheme();
		return {
			FinalGrade: {				
				heading: 'Final Grade',
				id: 'grade',
				initRows: 1,
				addRows: false,
				data: function(pid){
					return schemeObject.getPupilDataByType(pid, 'grade');
				},				
				structure: {
					'0' : {
						type: 'text',
						title: 'Final Grade',
						abbr: 'finalgrade'
					}
				}
			},
			Cores: {
				heading: 'Cores',
				id: 'core',
				initRows: 8,
				addRows: false,
				awTypePrecursor: 'modbac',
				data: function(pid){
					return schemeObject.getPupilDataByType(pid, 'core');
				},
				structure: {
					'0' : {
						type: 'text',
						title: 'QualCode',
						abbr: 'qualcode'
					},
					'1' : {
						type: 'text',
						title: 'Qual Name',
						abbr: 'name'
					},
					'2' : {
						type: 'text',
						title: 'Grade',
						abbr: 'grade'
					}
				}
			}
		};
	};
	// Bind this to the click event
	ingotClass.iMarksheetUserInterface.MODBAC_FOUNDATIONUPPER_CORES.prototype.loadDataDialog.prototype.eventHandler = function(){
		var cl = this.getMarksheetClosure();
		var tables = this.getTables();
		return function(){
			var dialogObject = new ingotClass.iMarkDataDialogInput({
				marksheetClosure: cl,
				row: $(this).closest('tr'),
				tables: tables,
				awTypePrecursor: 'modbac',
				dialogTitle: 'Modbac Core Data'
			});
			dialogObject.showDialog();
		};
	};

/**
 * Load a text box 
 * 
 * FIXME: COPIED AND PASTED DUE TO TIME CONSTRAINTS!!  We need States... STATES!
 */
	/**
	 * Load Data Dialog Action Type
	 */
	ingotClass.iMarksheetUserInterface.MODBAC_FOUNDATIONUPPER_CORES.prototype.loadStatementDialog = function(options){
		this.callParentMethod('constructor', [options]);
		this.setMarksheetClosure(options.marksheetClosure);
	};
	// Inherit from ActionTypeContainer
	ingotClass.iMarksheetUserInterface.MODBAC_FOUNDATIONUPPER_CORES.prototype.loadStatementDialog.inheritsFrom(ingotClass.iMarksheetUserInterface.ActionTypeContainer);
	ingotClass.iMarksheetUserInterface.MODBAC_FOUNDATIONUPPER_CORES.prototype.loadStatementDialog.prototype.getEventHandler = function(){
		return this.eventHandler();
	};
	// Set applied events
	ingotClass.iMarksheetUserInterface.MODBAC_FOUNDATIONUPPER_CORES.prototype.loadStatementDialog.prototype.getAppliedElements = function(){
		return $('.imarkbook_forenamecell, .imarkbook_surnamecell');
	};
	// Set applied events
	ingotClass.iMarksheetUserInterface.MODBAC_FOUNDATIONUPPER_CORES.prototype.loadStatementDialog.prototype.getEventType = function(){
		return 'click';
	};
	// Bind this to the click event
	ingotClass.iMarksheetUserInterface.MODBAC_FOUNDATIONUPPER_CORES.prototype.loadStatementDialog.prototype.eventHandler = function(){
		var cl = this.getMarksheetClosure();
		var scheme = this.getMarksheetClosure().Helper.GetCurrentScheme();
		return function(){
			var cell = $(this);
			var row = cl.Helper.GetRowFromCell(cell);
			var pid = cl.Helper.GetPidFromRow(row);

			var name = cl.Helper.GetPupilNameFromRow(row);
			
			var currentStatement = scheme.getPupilStatement(pid);

			var textArea = $("<div>");
			textArea.iAdditionalMarksDataTextAreaInput({
				setDataCallback: function(data){
					scheme.setPupilStatement(pid, 'statement', data);
				},
				validationCallback: function(data){
					return true;
				},
				textValue: currentStatement,
				maxLength: 200,
				rows: 5,
				cols: 50
			});

			var dialogContents = $("<div>");
			dialogContents.append(name);
			dialogContents.append(textArea);
			
			cl.CanUserSelectCells = false;
			cl.Action.UnSelectAll();
			$.fn.iDialog({element: dialogContents});
		};
	};

}(jQuery));
