/*
* Copyright 2010 Nathan Gelderloos, Ethan Zonca, Nathan Phillip Brink
*
* This file is part of SlatePermutate.
*
* SlatePermutate is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SlatePermutate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with SlatePermutate. If not, see .
*/
//--------------------------------------------------
// General Notes
//--------------------------------------------------
var classNum = 0;
/**
* \brief
* The number of section entries for a given course.
*
* Key is course_i, value is the current number of sections.
*/
var sectionsOfClass = new Array();
/**
* \brief
* Help to generate a unique section identifier for each section
* added to a given course.
*
* Necessary to support PHP-style post array thingies, like
* classes[0][1][$x] would be all of the data for course_i=0,
* section_i=1, variable $x (ex. day of week, start time, end time,
* teacher). We can't have two sections for a given course using the
* same section_i because those values would override eachother.
*/
var last_section_i = 0;
/**
* \brief
* The course number which contains nothing.
*
* To avoid having a user need to click the ``Add course'' button, we
* keep a course added at the end of the list of courses. If this
* variable is -1, it indicates that no such free course exists. If it
* is zero or greater, that number is the class which is the free one.
*/
var slate_permutate_course_free = -1;
/*
* General Input Functions
*/
/**
* \brief
* Returns the common inputs for each new section.
*/
function genSectionHtml(cnum)
{
genSectionHtml_n(cnum, '', '', '', '', '', '', '');
}
/* @TODO: This should select & set items based on args, if the args != '' */
function genSectionHtml_n(cnum, name, synonym, stime, etime, days, prof, location, type)
{
var snum = last_section_i ++;
var cssclasses = 'section class' + cnum;
if(type == 'lab') {
cssclasses += ' lab';
}
var result = '
';
result = result + '
';
result = result + '
';
result = result + '
\
\
\
\
\
\
\
';
result = result + '
' +
'' +
'' +
'
';
return result;
}
/**
* Outputs an element. It will inlcude selected="selected"
* if the value param equals the test_value param.
*/
function genOptionHtml(value, content, test_value)
{
var selected = ' selected="selected"';
if (value != test_value)
selected = '';
return '\n";
}
/** Add tooltips for user guidance */
function addTips()
{
var tr = jQuery('tr');
var td = tr.eq(tr.length-1);
/* slate_permutate_example_course_id is set globally in input.php. */
jQuery('td:first', td).qtip(
{
content: 'Start typing your class ID (such as ' + slate_permutate_example_course_id + ') and click a suggestion to add sections',
style: {
tip: true,
classes: "ui-tooltip-dark ui-tooltip-shadow ui-tooltip-rounded"
},
show: {
ready: true
},
position:{
my: 'top left',
at: 'bottom right',
}
}
);
}
/**
* \brief
* Add a section to a class.
*/
function add_section_n(cnum, name, synonym, stime, etime, days, prof, location, type)
{
jQuery('.pclass'+cnum).after(genSectionHtml_n(cnum, name, synonym, stime, etime, days, prof, location, type));
sectionsOfClass[cnum] ++;
/* store course_i in a place the newly added section will look for it */
jQuery('.pclass' + cnum).next().data({course_i: cnum});
/* unhide the saturday columns if it's used by autocomplete data */
if (days.s)
jQuery('#jsrows col.saturday').removeClass('collapsed');
return last_section_i - 1;
}
function add_section(cnum)
{
var section_i = add_section_n(cnum, '', '', '', '', {m: false, t: false, w: false, h: false, f: false, s: false}, '', '', '');
if (cnum == slate_permutate_course_free)
course_free_check(cnum);
return section_i;
}
/**
* Add a list of sections gotten via an AJAX call.
*/
function add_sections(cnum, data)
{
var i;
if (!data.sections)
return;
/*
* we get the sections in the correct order. For the user to see
* them in the correct order, we must reverse the add_setion_n()
* calls.
*/
for (i = data.sections.length - 1; i >= 0; i --)
{
section = data.sections[i];
add_section_n(cnum, section.section, section.synonym, section.time_start, section.time_end, section.days, section.prof, section.location, section.type);
}
/*
* Handle course-level interdependencies.
*/
if (data.dependencies)
jQuery.each(data.dependencies, function(i, dep)
{
var new_course_num = add_class_n(dep['class']);
add_sections(new_course_num, dep);
});
}
//--------------------------------------------------
// Adds a new class to the input.
//--------------------------------------------------
function add_class_n(name)
{
/*
* If we're adding a course entry form with preadded
* content, first remove the empty course.
*/
if (name.length && slate_permutate_course_free != -1)
course_remove(slate_permutate_course_free);
sectionsOfClass[classNum] = 0; // Initialize at 0
jQuery('#jsrows').append('
');
/* store classNum as course_i into the
: */
jQuery('#tr-course-' + classNum).data({course_i: classNum});
var class_elem = jQuery('.className' + classNum);
/*
* Don't let the user accidentally submit the form by
* pressing . Instead, select the first
* autocomplete result if possible.
*/
class_elem.bind('keyup keydown', function(e)
{
/* is 13 */
if (e.which == 13)
{
/*
* The user has pressed enter before selecting an autocomplete entry, which means the
* form will be submitted without his expecting it to be. We yet need code
* to figure out what the first autocomplete result is :-/.
*/
return false;
}
return true;
});
class_elem.autocomplete({ source: 'auto.php' });
class_elem.bind('autocompleteselect', {class_num: classNum, class_elem: class_elem},
function(event, ui)
{
if (!ui.item)
return;
if (ui.item.value.indexOf('-') != -1)
{
jQuery.ajax(
{
url: 'auto.php',
data: {'getsections': 1, 'term': ui.item.value},
context: {'class_num': event.data.class_num},
success: function(data, textStatus, reqobj)
{
var new_course_num;
if (data.sections)
{
add_sections(this.class_num, data);
new_course_num = add_class();
/* position the user's cursor the new class's input box */
jQuery('#input-course-' + new_course_num).focus();
}
}
}
);
}
else
{
/*
* The user selected a department, such as CS or MATH.
* Thus, we should append a '-' to the value and do a search for that.
*/
var newval = ui.item.value + '-';
event.data.class_elem.
val(newval).
autocomplete("search", newval);
/* void out the default event since we are setting the value ourselves, with a '-' */
event.preventDefault();
}
});
classNum++;
return (classNum - 1);
}
/**
* \brief
* Ensure that there is an empty course entry and return its
* identifier.
*/
function add_class()
{
/*
* Don't add an empty new course entry if there already is
* one. Otherwise, set this new class to be the ``hot'' one.
*/
if (slate_permutate_course_free == -1)
slate_permutate_course_free = add_class_n('');
return slate_permutate_course_free;
}
/**
* \brief
* Remove a course entry.
*
* Ensures that slate_permutate_course_free is kept consistent.
*
* \param course_i
* The internal JS identifer for the course (not the course_id which
* the PHP cares about).
*/
function course_remove(course_i)
{
jQuery('.class' + course_i).remove();
/*
* Check if the class intended for the user to
* enter information into has been removed.
*/
if (slate_permutate_course_free == course_i)
slate_permutate_course_free = -1;
}
/**
* \brief
* Figure whether or not a given course entry has sections.
*
* \param course_i
* The internal javascript representation of a course entry.
* \return
* true or false.
*/
function course_has_sections(course_i)
{
return sectionsOfClass[course_i] > 0;
}
/**
* \brief
* Figure out whether or not an empty course entry has become filled
* or whether a full course has become emptied and react.
*
* This mainly ensures that there is always exactly one course entry
* spot, eliminating the need of an ``Add class'' button.
*
* \param course_i
* If this is not being called as a 'change' or 'keyup' event
* handler for a , then course_i may refer to
* a the course_i to check.
*/
function course_free_check(course_i)
{
var me;
if (jQuery.type(course_i) == 'number')
me = jQuery('.pclass' + course_i + ' .className');
else
me = jQuery(this);
course_i = me.parent().parent().data('course_i');
if (course_i == slate_permutate_course_free && (me.val().length || course_has_sections(course_i)))
{
/* I am no longer the empty course entry */
slate_permutate_course_free = -1;
add_class();
}
if (course_i != slate_permutate_course_free && !(me.val().length || course_has_sections(course_i)))
{
/* I am now an empty course entry */
/* kill an other empty course entry if it exists... */
if (slate_permutate_course_free != -1)
course_remove(slate_permutate_course_free);
slate_permutate_course_free = course_i;
}
}
/**
* \brief
* Render a slate_permutate-encoded time-of-day.
*
* \param time_str
* A four-character representation of a time of day based on a
* time's 24-hour representation.
* \return
* A string representing the specified time.
*/
function prettyTime(time_str)
{
var i_hour;
var hour_str;
var m;
i_hour = time_str.substr(0, 2) * 1;
if (i_hour <= 12)
{
m = 'a';
}
else
{
m = 'p';
i_hour -= 12;
}
hour_str = new String(i_hour);
/* uncomment to have 08:01 instead of 8:01 */
/*
while (hour_str.length < 2)
hour_str = '0' + hour_str;
*/
return hour_str + ':' + time_str.substr(2) + ' ' + m + 'm';
}
//--------------------------------------------------
// Items bound to pageload/events
//--------------------------------------------------
jQuery(document).ready(function() {
//--------------------------------------------------
// Deletes the selected class from input
//--------------------------------------------------
jQuery('.deleteClass').live('click', function() {
if(confirm('Delete class and all sections of this class?')) {
course_remove(jQuery(this).parent().parent().data('course_i'));
}
});
//--------------------------------------------------
// Deletes the selected section from the input
//--------------------------------------------------
jQuery('.deleteSection').live('click', function() {
// Decreases the total number of classes
var course_i = jQuery(this).parent().parent().data('course_i');
sectionsOfClass[course_i]--;
// Find the ID cell of the row we're in
var row = jQuery(this).parent().parent().find(".sectionIdentifier");
// The first input is the one containing the section ID
var toMatch = jQuery(row).find("input").val();
// This gets the second class of the row, "class#"
var classClass = "." + jQuery(row).parent().attr("class").split(" ")[1];
// Iterate over each section of this class
jQuery(classClass).each( function() {
// If this section has the same course ID as the item clicked, remove it.
if(jQuery(this).find("input").val() == toMatch){
jQuery(this).remove();
}
});
course_free_check(course_i);
});
jQuery('.className').live('change', course_free_check).live('keyup', course_free_check);
//--------------------------------------------------
// Bind the section-adding method
//--------------------------------------------------
jQuery('.addSection').live('click', function() {
var course_i = jQuery(this).parent().parent().data('course_i');
add_section(course_i);
});
//--------------------------------------------------
// Default text
//--------------------------------------------------
jQuery(".defText").focus(function(srcc)
{
if (jQuery(this).val() == jQuery(this)[0].title)
{
jQuery(this).removeClass("defaultTextActive");
jQuery(this).val("");
}
});
jQuery(".defText").blur(function()
{
if (jQuery(this).val().length === 0)
{
jQuery(this).addClass("defaultTextActive");
jQuery(this).val($(this)[0].title);
}
});
jQuery(".defText").blur();
//--------------------------------------------------
// Show/Hide advanced items
//--------------------------------------------------
jQuery('.advanced').hide();
jQuery('#showadvanced').click( function() {
jQuery('#showadvanced').hide();
jQuery('.advanced').slideToggle();
});
//--------------------------------------------------
// Show/Hide instructions
//--------------------------------------------------
jQuery('#schoolInstructionsBox').hide();
jQuery('#showInstructions').click( function() {
jQuery('#showInstructions').hide();
jQuery('#schoolInstructionsBox').slideToggle();
});
//-------------------------------------------------
// Show more saved schedules
//-------------------------------------------------
jQuery('#showMore').click( function() {
jQuery('.hidden').show();
jQuery('#showMore').hide();
jQuery('#showLess').show();
});
jQuery('#showLess').click( function() {
jQuery('.hidden').hide();
jQuery('#showMore').show();
jQuery('#showLess').hide();
});
});