var gradebookGrid = function() {
	var grid = this;
	var isdropped = function(taskid, record) {
		return (record.data['droppedTasks'].indexOf(taskid) != -1);
	}
	var gradeRenderer = function(value, metaData, record, rowIndex, colIndex, store) {
		if (!Ext.getCmp('gradebookGrid').loading) {
			var taskid = record.fields.keys[colIndex + 4];
			if (isdropped(taskid, record)) {
				metaData.css = 'notEditable';
			}
			if (value == 'Exempt') {
				metaData.css = 'centerText';
				return '<img src = "images/icon_x.png">';
			}
			return value;
		}
	}
	var calcGrade = function(course, record, grade) {
		var grid = Ext.getCmp('gradebookGrid');
		var calculations = new Object();
		grade.scoreAchieved = 0;
		grade.scoreAvailable = 0;
		//loop through tasks
/*if (record.get('StudentName') != "Foy, Charles") {
			return;
		}*/
		record.fields.each(function(taskid) {
			taskid = taskid.name;
			if (isnum(taskid)) { //an actual task
				var task = record.data[taskid];
				var dropped = isdropped(taskid, record);
				if (!dropped) {
					if (grid.assignments.getById(taskid) === undefined) {
						return;
					}
					var assignment = grid.assignments.getById(taskid).data; //global properties of the current task
					if (course.gradingMethod == 'truepoints') { //points course
						if (task && task != 'Exempt' && !isExam(assignment.category)) {
							grade.scoreAchieved += parseInt(task);
							grade.scoreAvailable += parseInt(assignment.points);
						}
					} else if (course.gradingMethod == 'percent' || course.gradingMethod == 'autocalc') {
						if (task && task != 'Exempt' && !isExam(assignment.category)) {
							var percentGrade = (course.gradingMethod == 'autocalc') ? (parseInt(task) / parseInt(assignment.points) * 100) : parseInt(task);
							if (calculations[assignment.category] === undefined) {
								calculations[assignment.category] = new Object();
							}
							if (calculations[assignment.category].addedWeights === undefined) {
								calculations[assignment.category].addedWeights = 0;
							}
							if (calculations[assignment.category].addedPercents === undefined) {
								calculations[assignment.category].addedPercents = 0;
							}
							calculations[assignment.category].addedWeights += assignment.weight;
							calculations[assignment.category].addedPercents += assignment.weight * percentGrade;
							calculations[assignment.category].categoryWeight = assignment.categoryWeight;
						}
					}
				}
			}
		}, record);
		if (course.gradingMethod == 'percent' || course.gradingMethod == 'autocalc') {
			var catTotals = 0;
			var catWeightTotals = 0;
			for (var category in calculations) {
				var categoryCalcs = calculations[category];
				var catGrade = (categoryCalcs.addedPercents / categoryCalcs.addedWeights);
				if (grid.gradebookData.rounding.roundCat) {
					catGrade = Math.round(catGrade);
				}
				catTotals += catGrade * categoryCalcs.categoryWeight;
				catWeightTotals += categoryCalcs.categoryWeight;
			}
			grade.score = catTotals / catWeightTotals;
			if (isNaN(grade.score)) {
				grade.score = null;
			} else {
				if (grid.gradebookData.rounding.roundCourse) {
					grade.score = Math.round(grade.score);
				} else {
					grade.score = parseInt(grade.score);
				}
			}
		}
		//return resultfalse
		if (course.gradingMethod == 'truepoints') { //points course
			grade.score = parseFloat((grade.scoreAvailable != null ? ((grade.scoreAchieved / grade.scoreAvailable) * 100) : 0)).toFixed(2);
		} else {
			//grade.score = parseFloat((grade.scoreAvailable == null || grade.scoreAchieved == 0) ? null : (grade.scoreAvailable / grade.scoreAchieved)).toFixed(2);
		}
	}
	var calcQtrAvg = function(value, metadata, record, rowIndex, colIndex, store) {
		//record: student and their task grades
		var courseID = Ext.getCmp('gradebookcourseid').getValue();
		if (courseID !== undefined && courseID != '') {
			var course = Ext.getCmp('gradebookcourseid').store.getById(courseID).data;
			var grade = new Object();
			calcGrade(course, record, grade);
			if (isnum(grade.score)) {
				return (course.gradingMethod == 'percent' || course.gradingMethod == 'autocalc') ? grade.score + "%" : grade.score;
			}
			return null;
		}
	}
	var calcPointsOrLetterGrade = function(value, metadata, record, rowIndex, colIndex, store) {
		var grade = new Object();
		var courseCombo = Ext.getCmp('gradebookcourseid');
		var courseID = courseCombo.getValue();
		var course = courseCombo.store.getById(courseID).data;
		if (courseID !== undefined && courseID != '') {
			calcGrade(course, record, grade);
		}
		if (grade.score == null) {
			return null;
		}
		if (course.gradingMethod == 'truepoints') { //points course
			return QTipRenderer(grade.scoreAchieved + '/' + grade.scoreAvailable);
		} else {
			if (course.useGradeScale) { //grade scale table
				var scaleID = record.get('scaleID');
				if (scaleID == 'N/A') {
					return 'N/A';
				}
				for (var i = 0; i < grid.gradescale.data.items.length; i++) {
					var scale = grid.gradescale.data.items[i];
					if (scale.data.scaleid == scaleID && scale.data.lowvalue <= grade.score && grade.score <= scale.data.highvalue) return QTipRenderer(scale.data.lettergrade);
				}
			} else { //letter grade table
				for (var i = 0; i < grid.letterscale.data.items.length; i++) {
					var scale = grid.letterscale.data.items[i];
					if (scale.data.lowvalue <= grade.score && grade.score <= scale.data.highvalue) return QTipRenderer(scale.data.gradeid);
				}
			}
		}
	}
	var cm = new Ext.grid.ColumnModel({
		defaults: {
			menuDisabled: true,
			sortable: false,
			width: 30,
			align: 'right'
		},
		columns: [{
			header: 'Student Name',
			dataIndex: 'StudentName',
			align: 'left',
			width: 150
		},
		{
			header: verticalText("Quarter Grade"),
			dataIndex: 'QtrGrade',
			width: 60,
			renderer: calcPointsOrLetterGrade
		},
		{
			header: verticalText("Quarter Avg"),
			dataIndex: 'QtrAvg',
			width: 45,
			renderer: calcQtrAvg
		}]
	});
	gradebookGrid.superclass.constructor.call(this, {
		id: 'gradebookGrid',
		columnLines: true,
		enableColumnHide: false,
		enableColumnMove: false,
		enableColumnResize: false,
		enableColumnResize: false,
		stripeRows: true,
		trackMouseOver: true,
		store: new Ext.data.Store({
			storeId: 'gradebookGrid_store',
			url: 'teacher_tools/gradebook_data.json.php',
			baseParams: {
				command: 'getGrades'
			},
			reader: new Ext.data.JsonReader({
				root: 'grades',
				idProperty: 'gradebookid'
			}, [{
				name: 'gradebookid'
			},
			{
				name: 'orderno',
				type: 'int'
			},
			{
				name: 'studentid'
			},
			{
				name: 'StudentName'
			},
			{
				name: 'QtrAvg'
			},
			{
				name: 'droppedTasks'
			},
			{
				name: 'scaleID'
			}]),
			autoLoad: false,
			autoSave: true,
			writeWait: false,
			writer: new Ext.data.JsonWriter({}),
			listeners: {
				beforeload: function() {
					this.reader.jsonData = '';
					return true;
				},
				load: function(store, records, options) {
					var hasOrder;
					for (var i = 0, len = store.data.items.length; i < len; i++) {
						var record = store.data.items[i];
						if (record.data.orderno != null) hasOrder = true;
						//console.log(record.data);
					}
					if (this.loading) {
						this.loading = false;
						this.store.sort((hasOrder ? 'orderno' : 'StudentName'), 'ASC');
					}
				}
			}
		}),
		//custom functions and properties
		loadGrid: function() {
			if (!this.store.baseParams.courseid || !this.store.baseParams.quarter) {
				return false;
			}
			this.store.save();
			Ext.getCmp('gb_add').enable();
			Ext.getCmp('gb_course_summary').enable();
			//load the current gradescale
			var courseCombo = Ext.getCmp('gradebookcourseid');
			var courseIndex = courseCombo.store.find('courseid', courseCombo.getValue());
			this.assignments.baseParams.courseid = this.store.baseParams.courseid;
			this.assignments.baseParams.quarter = this.store.baseParams.quarter;
			this.assignments.load();
			this.store.load();
		},
		createColumn: function(defaultValue, quarter, title, dataIndex, index, markDirty) {
			if (typeof dataIndex == 'number') {
				dataIndex = dataIndex.toString();
			}
			if (dataIndex == undefined) {
				var i = 1,
					name = 'newtask';
				while (this.store.fields.containsKey(name + i)) {
					i++;
				}
				dataIndex = name + i;
			}
			var courseCombo = Ext.getCmp('gradebookcourseid');
			var courseValue = courseCombo.getValue();
			var courseIndex = courseCombo.store.find('courseid', courseValue);
			var gradingMethod = courseCombo.store.getAt(courseIndex).get('gradingMethod');
			var myEditor = (gradingMethod == 'percent') ? new Ext.form.NumberField({
				allowBlank: true,
				allowNegative: false,
				maxValue: SchoolDynamics.maxGrade
			}) : new Ext.form.NumberField({
				allowBlank: true,
				allowNegative: false
			});
			var isHidden = (quarter != Ext.getCmp('gradebookquarter').getValue());
			var colConfig = {
				header: verticalText(title),
				dataIndex: dataIndex,
				id: dataIndex,
				editor: myEditor,
				renderer: gradeRenderer,
				hidden: isHidden
			};
			if (index != undefined) {
				//this.addColumn(defaultValue, dataIndex, colConfig, index);
			} else {
				//this.addColumn(defaultValue, dataIndex, colConfig);
				this.addToColumnBuffer(defaultValue, dataIndex, colConfig);
			}
		},
		gradescale: new Ext.data.Store({
			url: 'teacher_tools/gradebook_data.json.php',
			baseParams: {
				command: 'getGradeScale'
			},
			reader: new Ext.data.JsonReader({
				root: 'scale'
			}, [{
				name: 'scaleid'
			},
			{
				name: 'lettergrade'
			},
			{
				name: 'highvalue',
				type: 'float'
			},
			{
				name: 'lowvalue',
				type: 'float'
			}]),
			autoLoad: false
		}),
		letterscale: new Ext.data.Store({
			url: 'teacher_tools/gradebook_data.json.php',
			baseParams: {
				command: 'getLetterScale'
			},
			reader: new Ext.data.JsonReader({
				root: 'scale'
			}, [{
				name: 'scaleid'
			},
			{
				name: 'gradeid'
			},
			{
				name: 'description'
			},
			{
				name: 'highvalue',
				type: 'float'
			},
			{
				name: 'lowvalue',
				type: 'float'
			}]),
			autoLoad: true
		}),
		assignments: new Ext.data.Store({
			url: 'teacher_tools/gradebook_data.json.php',
			baseParams: {
				command: 'getAssignments'
			},
			reader: new Ext.data.JsonReader({
				root: 'assignments',
				idProperty: 'taskid'
			}, [{
				name: 'taskid'
			},
			{
				name: 'task'
			},
			{
				name: 'taskdate'
			},
			{
				name: 'makeup',
				type: 'bool'
			},
			{
				name: 'points',
				type: 'float'
			},
			{
				name: 'weight',
				type: 'float'
			},
			{
				name: 'categoryWeight',
				type: 'float'
			},
			{
				name: 'quarter'
			},
			{
				name: 'category'
			}]),
			autoLoad: false,
			autoSave: false,
			writer: new Ext.data.JsonWriter({
				writeAllFields: true
			}),
			finishLoad: function(records) {
				var grid = Ext.getCmp('gradebookGrid');
				for (var i = 0, len = records.length; i < len; i++) {
					quarter = records[i].get('quarter');
					grid.createColumn("Exempt", quarter, records[i].data.task, records[i].data.taskid);
				}
				grid.colModel.setConfig(grid.colModel.columns.concat(grid.configBuffer));
				grid.configBuffer = [];
				grid.loading = false;
				var lastJsonGrades = grid.getStore().reader.jsonData;
				if (lastJsonGrades != '') {
					grid.store.loadData(lastJsonGrades);
				}
			},
			listeners: {
				beforeload: function() {
					grid.colModel.setConfig(grid.colModel.columns);
					return true;
				},
				load: function(assignments, records, options) {
					var grid = Ext.getCmp('gradebookGrid');
					grid.loading = true;
					var courseCombo = Ext.getCmp('gradebookcourseid');
					var value = courseCombo.getValue();
					var index = courseCombo.store.find('courseid', value);
					var gradingMethod = courseCombo.store.getAt(index).get('gradingMethod');
					if (gradingMethod == 'percent') {
						if (SchoolDynamics.maxGrade === undefined) {
							Ext.Ajax.request({
								url: 'teacher_tools/gradebook_data.json.php',
								params: {
									command: 'getMaximumGrade'
								},
								success: function(r) {
									SchoolDynamics.maxGrade = Ext.decode(r.responseText).maximumGrade;
									this.finishLoad(records);
								},
								scope: this
							});
						} else {
							this.finishLoad(records);
						}
					} else {
						this.finishLoad(records);
					}
				},
				add: function(assignments, records, index) {
					grid.assignments.save();
				},
				write: function(assignments, action, result, res, records) {
					if (action == 'create') {
						var grid = Ext.getCmp('gradebookGrid');
						for (var i = 0, len = result.length; i < len; i++) {
							//console.log(result[i], result[i].taskid);
							quarter = result[i].quarter;
							grid.createColumn("", quarter, result[i].task, result[i].taskid);
						}
						grid.colModel.setConfig(grid.colModel.config.concat(grid.configBuffer));
						grid.configBuffer = [];
						//console.log(records, grid.store);
					}
				},
				update: function(assignments, record, index) {
					//var grid = Ext.getCmp('gradebookGrid');
					var column = grid.colModel.getColumnById(record.id);
					if (column != undefined) {
						column.header = verticalText(record.data.task);
						grid.view.refresh(true);
					}
				},
				remove: function(assignments, record, index) {
					//var grid = Ext.getCmp('gradebookGrid');
					grid.removeColumn(record.id);
				}
			}
		}),
		// end: custom functions and properties
		cm: cm,
		ltrTab: false,
		//tab key scrolls up/down (true=left/right). default=true
		width: 600,
		height: 300,
		title: 'Gradebook',
		frame: false,
		tbar: {
			xtype: 'toolbar',
			items: [{
				xtype: 'label',
				text: 'Course:',
				style: 'margin-left: 5px;'
			},
			new SchoolDynamics.coursesCombo({
				id: 'gradebookcourseid',
				listeners: {
					setValue: function(combo, newvalue, oldvalue) {
						grid.store.baseParams.courseid = SchoolDynamics.currentCourseID = newvalue;
						grid.loadGrid();
						var rec = this.store.getAt(this.store.find('courseid', newvalue));
						if (rec !== undefined) {
							Ext.getCmp('gradingMethodCombo').setValue(rec.get('gradingMethod'), true);
							if (rec.get('calcSemester')) {
								grid.colModel.setColumnHeader(2, verticalText('Semester Avg'));
								grid.colModel.setColumnHeader(1, verticalText('Semester Grade'));
							} else {
								grid.colModel.setColumnHeader(2, verticalText('Quarter Avg'));
								grid.colModel.setColumnHeader(1, verticalText('Quarter Grade'));
							}
						}
					}
				}
			}),
			{
				xtype: 'tbseparator',
				style: 'margin-left: 5px; padding-left: 15px;'
			},
			{
				xtype: 'label',
				text: 'Grade period:',
				style: 'margin-left: 5px;'
			},
			new SchoolDynamics.quarterCombo({
				id: 'gradebookquarter',
				listeners: {
					setValue: function(combo, newvalue, oldvalue) {
						//myGrid = Ext.getCmp('gradebookGrid');
						grid.store.baseParams.quarter = SchoolDynamics.currentQtr = newvalue;
						grid.loadGrid();
						var addTaskMenu = Ext.getCmp('gb_add');
						Ext.iterate(addTaskMenu.menu.items.items, function(btn) {
							if (btn.text == 'Exam1') btn[newvalue != 1 ? 'disable' : 'enable']();
							if (btn.text == 'Exam2') btn[newvalue != 2 ? 'disable' : 'enable']();
							if (btn.text == 'Exam3') btn[newvalue != 3 ? 'disable' : 'enable']();
							if (btn.text == 'Midterm Exam') btn[newvalue != 2 ? 'disable' : 'enable']();
							if (btn.text == 'Final Exam') btn[newvalue != 4 ? 'disable' : 'enable']();
						}, this);
					}
				}
			}),
			{
				xtype: 'tbseparator',
				style: 'margin-left: 5px; padding-left: 15px;'
			},
			{
				xtype: 'splitbutton',
				text: 'Add Task',
				iconCls: 'add',
				id: 'gb_add',
				disabled: true,
				menu: [],
				listeners: {
					click: function(btn) {
						jsLoader.load('teacher_tools/' + modulizerPath + '/gradebook_add_assignment_popup.js');
						jsLoader.loadedCall(
						function() {
							SD.teacherTools.gradebook.addAssignment.content.showWithArgs(true);
						});
					}
				}
			},
			{
				xtype: 'tbseparator',
				style: 'margin-left: 5px;'
			},
			{
				xtype: 'button',
				text: 'Course Summary',
				id: 'gb_course_summary',
				disabled: true,
				iconCls: 'chart_bar',
				style: 'margin-left: 5px;',
				handler: function() {
					jsLoader.load('teacher_tools/' + modulizerPath + '/gradebook_summary.js');
					jsLoader.loadedCall(
					function() {
						var popup = new SD.teacherTools.gradebook.classes.gradebookSummary.window({
							title: 'Course Summary',
							fieldHeader: 'Task',
							limitColumns: [0, 1, 2, 3, 4],
							baseParams: {
								command: 'courseSummary'
							},
							requiredBaseParams: new Array('courseid', 'quarter')
						});
						popup.show();
					});
				}
			}]
		},
		listeners: {
			render: function(tab) {
				var addTaskMenu = Ext.getCmp('gb_add');
				if (!SchoolDynamics.trimester) {
					addTaskMenu.menu.add({
						text: 'Midterm Exam',
						iconCls: 'table_add',
						listeners: {
							click: function(btn) {
								jsLoader.load('teacher_tools/' + modulizerPath + '/gradebook_add_assignment_popup.js');
								jsLoader.loadedCall(
								function() {
									SD.teacherTools.gradebook.addAssignment.content.showWithArgs(true, 'Midterm');
								});
							}
						}
					});
					addTaskMenu.menu.add({
						text: 'Final Exam',
						iconCls: 'table_add',
						listeners: {
							click: function(btn) {
								jsLoader.load('teacher_tools/' + modulizerPath + '/gradebook_add_assignment_popup.js');
								jsLoader.loadedCall(
								function() {
									SD.teacherTools.gradebook.addAssignment.content.showWithArgs(true, 'Final');
								});
							}
						}
					});
				} else {
					for (var i = 1; i <= SchoolDynamics.quartersAvailable; i++) {
						addTaskMenu.menu.add({
							text: 'Exam' + i,
							iconCls: 'table_add',
							listeners: {
								click: function(btn) {
									jsLoader.load('teacher_tools/' + modulizerPath + '/gradebook_add_assignment_popup.js');
									jsLoader.loadedCall(
									function() {
										SD.teacherTools.gradebook.addAssignment.content.showWithArgs(true, this.text);
									}, this);
								}
							}
						});
					}
				}
			},
			activate: function(tab) { //the grid IS the tab
				Ext.getCmp('gradebookcourseid').setValue(SchoolDynamics.currentCourseID);
				Ext.getCmp('gradebookquarter').setValue(SchoolDynamics.currentQtr);
				if (tab.gradescale.totalLength == undefined) tab.gradescale.load();
			},
			cellcontextmenu: function(grid, rowIndex, columnIndex, e) {
				if (columnIndex >= 3) {
					//highlight the field
					var r = grid.view.getRow(rowIndex);
					var cell = Ext.get(r ? r.getElementsByTagName('td')[columnIndex] : false);
					cell.addClass('highlight');
					var task = grid.assignments.getAt(columnIndex - 3);
					var student = grid.store.getAt(rowIndex);
					var taskdropped = (student.data['droppedTasks'].indexOf(task.id) != -1)
					var grade = student.data[task.id];
					this.contextMenu = new Ext.menu.Menu({
						id: 'gradebook_contextmenu',
						items: [{ //task actions
							id: 'task',
							text: 'Task: ' + task.data.task,
							iconCls: 'clipboard_task',
							menu: [{
								text: 'Assign "' + grade + '" to all students',
								iconCls: 'lightning_plus',
								handler: function() {
									var courseStore = Ext.getCmp('gradebookcourseid').store;
									var course = courseStore.getAt(courseStore.find(courseStore.reader.meta.idProperty, SchoolDynamics.currentCourseID));
									grid.store.each(function(record) {
										record.set(task.id, grade);
									}, this);
								}
							},
							{
								text: 'Edit Task',
								iconCls: 'clipboard_task_gear',
								handler: function(btn) {
									jsLoader.load('teacher_tools/' + modulizerPath + '/gradebook_add_assignment_popup.js');
									jsLoader.loadedCall(
									function() {
										SD.teacherTools.gradebook.addAssignment.content.showWithArgs(false, task.get('category'), task);
									});
								}
							},
							{
								text: 'Task Summary',
								iconCls: 'chart_bar',
								handler: function(btn) {
									jsLoader.load('teacher_tools/' + modulizerPath + '/gradebook_summary.js');
									jsLoader.loadedCall(
									function() {
										var popup = new SD.teacherTools.gradebook.classes.gradebookSummary.window({
											title: 'Task Summary',
											fieldHeader: 'Student',
											limitColumns: [0, 5],
											baseParams: {
												command: 'taskSummary',
												taskid: task.id,
												courseid: SchoolDynamics.currentCourseID
											},
											requiredBaseParams: new Array('courseid', 'quarter', 'taskid')
										});
										popup.show();
									});
								}
							}]
						},
						{ //student actions
							id: 'student',
							text: 'Student: ' + student.data.StudentName,
							iconCls: 'user_silhouette',
							menu: [{
								text: (taskdropped ? 'Und' : 'D') + 'rop this grade',
								iconCls: (taskdropped ? 'arrow_return_090' : 'arrow_270'),
								handler: function() {
									Ext.Ajax.request({
										url: 'teacher_tools/gradebook_data.json.php',
										params: {
											command: (taskdropped ? 'undropGrade' : 'dropGrade'),
											taskid: task.id,
											studentid: student.id
										},
										success: function(r) {
											eval("var results = " + r.responseText.trim());
											//student.set(task.id+'_dropped', !taskdropped);
											var newDroppedTasks = (taskdropped) ? student.data['droppedTasks'].replace(new RegExp('~' + task.id, 'ig'), "") : student.data['droppedTasks'] + "~" + task.id;
											student.set('droppedTasks', newDroppedTasks);
											grid.store.commitChanges(); //prevent unwanted post
										}
									});
								}
							},
							{
								text: (student.get(task.id) == "Exempt") ? 'Require task for ' + student.data.StudentName : 'Exempt ' + student.data.StudentName + ' from task',
								iconCls: (student.get(task.id) == "Exempt") ? 'tick' : 'icon_x',
								handler: function() {
									student.set(task.id, (student.get(task.id) == "Exempt") ? '' : 'Exempt');
								}
							},
							{
								text: 'Add memo',
								iconCls: 'quill_plus',
								handler: function(button) {
									var memobox = Ext.MessageBox.getDialog('Memo');
									memobox.saveMemo = function(text) {
										Ext.Ajax.request({
											url: 'teacher_tools/gradebook_data.json.php',
											params: {
												command: 'saveMemo',
												taskid: task.id,
												studentid: student.id,
												memo: Ext.util.JSON.encode(text)
											},
											success: function(r) {
												eval("var results = " + r.responseText.trim());
												if (results.success) Ext.MessageBox.hide();
											}
										});
									};
									var memofield = memobox.body.child('textarea');
									memofield.dom.onkeypress = function(e) { //maxLength: 50
										var maxLength = 50;
										if (e.charCode == 0) {
											if (e.keyCode == 13) {
												memobox.saveMemo(memofield.dom.value);
												return false;
											}
											return true;
										}
										return (parseInt(Ext.get(e.target).dom.textLength) + 1) <= maxLength;
									}
									Ext.Ajax.request({
										url: 'teacher_tools/gradebook_data.json.php',
										params: {
											command: 'getMemo',
											taskid: task.id,
											studentid: student.id
										},
										success: function(r) {
											eval("var results = " + r.responseText.trim());
											Ext.Msg.prompt('Memo', "<b>Student:</b> " + student.data.StudentName + "<br /><b>Assignment:</b> " + task.data.task + "<br /><b>Grade:</b> " + student.data[task.id], function(btn, text) {
												if (btn == 'ok') {
													memobox.saveMemo(text);
												}
											}, grid, true, results[0].memo);
										}
									});
								}
							}]
						}],
						listeners: {
							hide: function(menu) {
								var r = grid.view.getRow(rowIndex);
								var cell = Ext.get(r ? r.getElementsByTagName('td')[columnIndex] : false);
								cell.removeClass('highlight');
								grid.contextMenu.destroy();
							}
						}
					});
					e.stopEvent();
					var xy = e.getXY();
					this.contextMenu.showAt(xy);
				}
			},
			show: function() {
				this.store.proxy.writeWait = false;
			},
			beforeedit: function(e) {
				if (e.value == 'Exempt') {
					return false;
				}
			}
		}
	});
};
Ext.extend(gradebookGrid, Ext.grid.EditorGridPanel);

