Changeset - acc2b5f0aae2
[Not reviewed]
default
0 9 1
Nathan Brink (binki) - 14 years ago 2011-04-18 23:45:54
ohnobinki@ohnopublishing.net
Support umich's concept of a student having to sign up for multiple sections within one course. In slate_permutate, we call these course slots.
10 files changed with 543 insertions and 116 deletions:
0 comments (0 inline, 0 general)
inc/class.course.inc
Show inline comments
 
<?php
 
/*
 
 * Copyright 2010 Nathan Gelderloos, Ethan Zonca, Nathan Phillip Brink
 
 * Copyright 2011 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 <http://www.gnu.org/licenses/>.
 
 */
 

	
 
include_once 'class.section.php';
 
require_once 'class.section.php';
 
require_once 'class.course_slot.inc';
 

	
 
/**
 
 * \brief
 
 *   Represents a Course containing multiple Sections.
 
 *
 
 * A course is associated with a certain subject material. For each
 
 * course, a student has to choose a particular Section to
 
 * take. Courses are not associated with professors or meeting times,
 
 * those are in the realm of the Section and SectionMeeting.
 
 *
 
 * Iterating over this object will return CourseSlot objects, which
 
 * act exactly like most universities' idea of a course. However, some
 
 * universities have one Course and require students to take one
 
 * section from each of different categories of sections within in a
 
 * course. For example, at umich for any course which has a listing of
 
 * Sections of the type 'discussion' the student _must_ take one of
 
 * these 'discussion' sections in addition to, for example, a
 
 * 'lecture' Section.
 
 */
 
class Course implements IteratorAggregate
 
{
 
  private $name;	// String
 
  private $title;       // String
 
  private $sections;	// Array of sections
 
  private $nsections;	// int
 
  private $sections;	// Array of sections, for __wakeup() to convert to CourseSlots.
 
  private $nsections;	// int, for __wakeup() to convert to CourseSlots.
 
  /**
 
   * \brief
 
   *   Other courses that this course depends on.
 
   *
 
   * Example: Many calvin courses depend on lab courses.
 
   */
 
  private $dependencies;
 

	
 
  /**
 
   * \brief
 
   *    Creates a class with the given name.
 
   *
 
   * When updating this function, update the call to ``new Course()''
 
   * in Schedule::findPossibilities(), Schedule::writeoutTables(), and
 
   * Schedule::courses_get().
 
   *
 
   * \param $course_id
 
   *    The identifier of the class. Ex., MATH-101 in
 
   *    MATH-101-A. Retrieved with Course::getName().
 
   * \param $title
 
   *    The human-friendly course title, such as 'Introduction to
 
   *    Algebra', or NULL.
 
   */
 
  function __construct($course_id, $title = NULL)
 
  public function __construct($course_id, $title = NULL)
 
  {
 
    $this->sections = array();
 
    $this->slots = array();
 
    $this->name = $course_id;
 
    $this->title = $title;
 
    $this->nsections = 0;
 
    $this->dependencies = array();
 
  }
 
	
 
  /**
 
   * \brief
 
   *   Adds an already-instantiated section to this class.
 
   *   Adds an already-instantiated Section to this class.
 
   *
 
   * \param $section
 
   *   The Section to append to this Course.
 
   * \param $course_slot_id
 
   *   The string identifer of the CourseSlot to place the given
 
   *   Section into. Most schools will not specify this.
 
   */
 
  public function section_add(Section $section)
 
  public function section_add(Section $section, $course_slot_id = 'default')
 
  {
 
    $this->sections[$this->nsections] = $section;
 
    $this->nsections++;
 
    if (empty($this->slots[$course_slot_id]))
 
      $this->slots[$course_slot_id] = new CourseSlot($course_slot_id);
 
    $this->slots[$course_slot_id]->section_add($section);
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Append a CourseSlot to this Course.
 
   *
 
   * If this course already contains a CourseSlot with the same
 
   * CourseSlot identifier as $course_slot, then the new CourseSlot
 
   * will replace the old one.
 
   *
 
   * \param $course_slot
 
   *   The CourseSlot to append.
 
   */
 
  public function course_slot_add(CourseSlot $course_slot)
 
  {
 
    $this->slots[$course_slot->id_get()] = $course_slot;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Required function to implement the IteratorAggregate interface.
 
   */
 
  public function getIterator()
 
  {
 
    return new ArrayIterator($this->sections);
 
    return new ArrayIterator($this->slots);
 
  }
 

	
 
  /**
 
   * \brief
 
   *    Returns the number of sections in the class.
 
   */
 
  function getnsections()
 
  {
 
    return $this->nsections;
 
  }
 
	
 
  /**
 
   * \brief
 
   *    Returns the desired section for analysis.
 
   * \return
 
   *    The selected section of the course.
 
   */
 
  function getSection($i)
 
  {
 
    $result = $this->sections[$i];
 
    return $result;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Retrieve a section of this class based on its letter.
 
   *   Retrieve a section of this Course based on its letter.
 
   *
 
   * \todo Make this function replace completely the getSection()
 
   * function, have $this->sections be keyed by letter, and have a
 
   * __wakup() convert the old $this->sections format to the new one.
 
   * This function will automatically search CourseSlots for this
 
   * Section. Why? Because even though we support multiple
 
   * CourseSlots, the section_id must _still_ be unique -- no two
 
   * CourseSlots should share a fully-qualified section_id.
 
   *
 
   * \return
 
   *   The requested section or NULL if that section does not yet
 
   *   exist for this class.
 
   */
 
  public function section_get($letter)
 
  {
 
    foreach ($this->sections as $section) {
 
      if ($section->getLetter() == $letter) {
 
	return $section;
 
    foreach ($this->slots as $slot)
 
      {
 
	$section = $slot->section_get($letter);
 
	if (!empty($section))
 
	  return $section;
 
      }
 
    }
 
    return NULL;
 
  }
 

	
 
  /**
 
   * \brief
 
   *    Returns the name of the class.
 
   *    Returns the name of the Course.
 
   * \return
 
   *    The name of the class.
 
   *    The name of the Course.
 
   */
 
  public function getName()
 
  {
 
    return $this->name;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Retrieve the human-friendly course title.
 
   *
 
   * \return
 
   *   A string, the human-friendly course title, or NULL if there is
 
   *   no title.
 
   */
 
  public function title_get()
 
  {
 
    return $this->title;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Add a dependency on another course.
 
   *
 
   * \param $course
 
@@ -159,121 +177,143 @@ class Course implements IteratorAggregat
 
  {
 
    $this->dependencies[] = $course;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Split up a user-friendly course specification into components.
 
   *
 
   * This will only return the 'department' and 'course' components of
 
   * the given course identifier. Otherwise, it acts the same as
 
   * Section::parse.
 
   *
 
   * \see Section::parse()
 
   *
 
   * \param $course_spec
 
   *   A course specifier to parse, such as 'cs262' or 'MATH-156'.
 
   * \return
 
   *   An array with normalized output having keys of 'department' and
 
   *   'course'. If the user's input has less than these two keys of
 
   *   information, the returned array may have zero or one elements.
 
   */
 
  public static function parse($course_spec)
 
  {
 
    $section_parts = Section::parse($course_spec);
 
    if (isset($section_parts['section'])) {
 
    if (isset($section_parts['section']))
 
      unset($section_parts['section']);
 
    }
 

	
 
    return $section_parts;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Represent this class as a string.
 
   *   Represent this Course as a string.
 
   */
 
  public function __toString()
 
  {
 
    return $this->getName();
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Represent this class as an array of sections ready to be JSONized.
 
   *
 
   * \param $recursion_trace
 
   *   Only for internal use. Used to prevent infinite recursion.
 
   */
 
  public function to_json_array(array $recursion_trace = array())
 
  {
 
    if (!empty($recursion_trace[$this->getName()]))
 
      return NULL;
 
    $recursion_trace[$this->getName()] = TRUE;
 

	
 
    $json_array = array(
 
			'course' => $this->getName(),
 
			'title' => $this->title_get(),
 
			'sections' => array(),
 
			'dependencies' => array(),
 
			);
 
    foreach ($this->sections as $section)
 
      {
 
	$section_json_arrays = $section->to_json_arrays();
 
	foreach ($section_json_arrays as $section_json_array)
 
	  $json_array['sections'][] = $section_json_array;
 
      }
 
    foreach ($this->slots as $slot)
 
      foreach ($slot->to_json_arrays() as $slot_json_section_array)
 
        $json_array['sections'][] = $slot_json_section_array;
 

	
 
    foreach ($this->dependencies as $dependency)
 
      {
 
	$dependency_json = $dependency->to_json_array($recursion_trace);
 
	if (!empty($dependency_json))
 
	  $json_array['dependencies'][] = $dependency_json;
 
      }
 

	
 
    return $json_array;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Produce a Course object based on a JSON array compatible with
 
   *   the output of Course::to_json_array().
 
   *
 
   * \param $json
 
   *   The JSON array to parse.
 
   * \return
 
   *   A Course.
 
   */
 
  public static function from_json_array($json)
 
  {
 
    $title = NULL;
 
    if (!empty($json['title']))
 
      $title = $json['title'];
 
    if (!empty($json['class']))
 
      {
 
	$json['course'] = $json['class'];
 
	unset($json['class']);
 
      }
 
    $course = new Course($json['course'], $title);
 

	
 
    if (!empty($json['sections']))
 
      $course->section_add(Section::from_json_arrays($json['sections']));
 
    
 
      {
 
	$json_course_slot_sections = array();
 
	foreach ($json['sections'] as $json_section)
 
	  {
 
	    $slot_id = 'default';
 
	    if (!empty($json_section['slot']))
 
	      $slot_id = $json_section['slot'];
 

	
 
	    if (empty($json_course_slot_sections[$slot_id]))
 
	      $json_course_slot_sections[$slot_id] = array();
 
	    $json_course_slot_sections[$slot_id][] = $json_section;
 
	  }
 

	
 
	foreach ($json_course_slot_sections as $slot_id => $json_course_slot_section)
 
	  $course->section_add(Section::from_json_arrays($json_course_slot_section), $slot_id);
 
      }
 

	
 
    if (!empty($json['dependencies']))
 
      foreach ($json['dependencies'] as $dependency)
 
	$course->dependency_add(Course::from_json_array($dependency));
 

	
 
    return $course;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Upgrade a course class to a newer version of that class.
 
   */
 
  public function __wakeup()
 
  {
 
    if (!isset($this->dependencies))
 
      $this->dependencies = array();
 

	
 
    if (!isset($this->title))
 
      $this->title = NULL;
 

	
 
    if (empty($this->slots) && !empty($this->sections))
 
      {
 
	$this->slots = array();
 

	
 
	foreach ($this->sections as $section)
 
	  $this->section_add($section);
 

	
 
	unset($this->sections);
 
	unset($this->nsections);
 
      }
 
  }
 
}
inc/class.course_slot.inc
Show inline comments
 
new file 100644
 
<?php
 
/*
 
 * Copyright 2011 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 <http://www.gnu.org/licenses/>.
 
 */
 

	
 
require_once 'class.section.php';
 

	
 
/**
 
 * \brief
 
 *   A package of Section objects of which one must be taken to sign
 
 *   up for a particular Course.
 
 *
 
 * For example, some schools like umich have a single Course where one
 
 * must sign up for one Section for every meeting_type of the
 
 * following: 'lecture', 'discussion', and 'lab'. This way they avoid
 
 * creating separate Course objects for labs (which is calvin's
 
 * solution to the problem).
 
 *
 
 * Many schools do not have the CourseSlot paradigm. These will work
 
 * just fine using one default CourseSlot.
 
 *
 
 * Iterating over this object will yield Section objects.
 
 */
 
class CourseSlot implements IteratorAggregate
 
{
 
  /**
 
   * \brief
 
   *   An array of Section objects associated with this CourseSlot.
 
   */
 
  private $sections;
 

	
 
  /**
 
   * \brief
 
   *   An identifier for this slot. Used during crawling when sorting
 
   *   Sections into CourseSlot objects.
 
   */
 
  private $id;
 

	
 
  /**
 
   * \brief
 
   *   Creates a CourseSlot with the given identifier.
 
   */
 
  public function __construct($id)
 
  {
 
    $this->id = $id;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Required function to implement the IteratorAggregate interface.
 
   */
 
  public function getIterator()
 
  {
 
    return new ArrayIterator($this->sections);
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Get the identifier of this slot.
 
   *
 
   * \return
 
   *   The slot's identifier string.
 
   */
 
  public function id_get()
 
  {
 
    return $this->id;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Appends a Section to this CourseSlot.
 
   *
 
   * \param $section
 
   *   The Section to append.
 
   */
 
  public function section_add(Section $section)
 
  {
 
    /*
 
     * This behavior of the Schedule class requires this manner of
 
     * indexing sections because it iterates using for ($count = 0;
 
     * $count < ...) -style loops. Thus we allow PHP's natural
 
     * indexing mechanism to do its job...
 
     */
 
    $this->sections[] = $section;
 
  }
 

	
 
  /**
 
   * \brief
 
   *    Returns the number of sections in the class.
 
   */
 
  function sections_count()
 
  {
 
    return count($this->sections);
 
  }
 

	
 
  /**
 
   * \brief
 
   *    Returns the desired section for analysis.
 
   * \return
 
   *    The selected section of the course.
 
   */
 
  function section_get_i($i)
 
  {
 
    $result = $this->sections[$i];
 
    return $result;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Retrieve a section of this class based on its letter.
 
   *
 
   * \todo Make this function replace completely the getSection()
 
   * function, have $this->sections be keyed by letter, and have a
 
   * __wakup() convert the old $this->sections format to the new one.
 
   *
 
   * \return
 
   *   The requested section or NULL if that section does not yet
 
   *   exist for this class.
 
   */
 
  public function section_get($letter)
 
  {
 
    foreach ($this->sections as $section) {
 
      if ($section->getLetter() == $letter) {
 
	return $section;
 
      }
 
    }
 
    return NULL;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Get the JSON arrays of data specific to each Section, adding
 
   *   slight metadata for this SLOT.
 
   *
 
   * There is no corresponding from_json_arrays() function for this
 
   * class. See Course::from_json_array() which manages the conversion
 
   * of JSON slots to CourseSlot objects.
 
   */
 
  public function to_json_arrays()
 
  {
 
    $slot_section_json_arrays = array();
 
    foreach ($this->sections as $section)
 
      {
 
	$section_json_arrays = $section->to_json_arrays();
 
	foreach ($section_json_arrays as $section_json_array)
 
	  $slot_section_json_arrays[] = $section_json_array + array('slot' => $this->id);
 
      }
 
    return $slot_section_json_arrays;
 
  }
 
}
inc/class.schedule.php
Show inline comments
 
@@ -26,48 +26,63 @@ require_once $incdir . 'school.inc';
 

	
 
/*
 
 * Load a Classes -> Course converter class for the sake of the
 
 * Schedule::__wakeup() magic function.
 
 */
 
require_once $incdir . 'class.classes_convert.inc';
 

	
 
/**
 
 * \brief
 
 *   Finds possible Section combinations for a user's given Courses
 
 *   and stores and displays the results.
 
 *
 
 * Represents a schedule of a week. Stores the classes that are part
 
 * of that week and calculates all the possible permutations.
 
 */
 
class Schedule
 
{
 
  /*
 
   * Variables for upgrading from saved schedules created when there
 
   * was a class called Classes.
 
   */
 
  private $classStorage;			// array of courses
 
  private $nclasses;				// Integer number of classes
 

	
 
  /**
 
   * \brief
 
   *   Provides a mapping to regain the user's original input.
 
   *
 
   * Currently, the Schedule object cannot natively handle CourseSlot
 
   * objects properly. It assumes that each Course has one and only
 
   * one CourseSlot. This array maps each Course object stored in
 
   * $classStorage onto the index of the course it was originally
 
   * from. I.e., if the Course at index 0 had two CourseSlot objects,
 
   * array(0 => 0, 1 => 0, 2 => 1) would map these two CourseSlot
 
   * objects onto the same Course object and have the next CourseSlot
 
   * be mapped into a separate Course object.
 
   */
 
  private $course_slot_mappings;
 

	
 
  /* My member variables. */
 
  private $courses;
 
  private $nPermutations = 0;		// Integer number of real permutations
 
  private $possiblePermutations;	// Integer number of possible permutations
 
  private $scheduleName;			// String name of schedule
 
  private $storage;				// Integer array of valid schedules
 
  /**
 
   * \brief
 
   *   The school_id of the school this schedule was created for.
 
   */
 
  private $school_id;
 
  /**
 
   * \brief
 
   *   The semester this schedule was created for.
 
   *
 
   * The semester array is stored in full in a schedule because some
 
   * schools do not keep a backlog of all semesters for their course
 
   * data. Such a school is calvin. We want to be able to access, for
 
   * example, the friendly name and so on of a semester even one year
 
   * after the semester is created without worrying about having that
 
   * semester be stored in the autocomplete cache.
 
   */
 
  private $semester;
 

	
 
@@ -90,117 +105,125 @@ class Schedule
 
  /**
 
   * \brief
 
   *   Create a schedule with the given name.
 
   *
 
   * \param $name
 
   *   A string, the friendly name the user gave this schedule.
 
   * \param $parent
 
   *   An integer, the id of the schedule from which this schedule is
 
   *   derived. A schedule is considered to be derived of another of
 
   *   the user created this schedule by clicking ``Edit'' for the
 
   *   previous schedule. Or NULL if this schedule stands on its own.
 
   * \param $school
 
   *   The school used for this schedule. The intention of storing
 
   *   this data is that people from different schools may share
 
   *   schedules with eachother. Also, people who bookmark their
 
   *   schedules and want to edit their schedules should not have to
 
   *   go through the school selection dialogue again but should just
 
   *   be set to use the correct school.
 
   * \param $semester
 
   *   The semester used for this schedule.
 
   */
 
  function __construct($name, $parent = NULL, array $school = NULL, array $semester = NULL)
 
  {
 
    $this->courses = array();
 
    $this->course_slot_mappings = array();
 
    $this->scheduleName = $name;
 
    $this->storage = array();
 
    $this->title = "SlatePermutate - Scheduler";
 
    $this->parent_id = $parent;
 

	
 
    if (empty($school))
 
      $school = school_load_guess(FALSE);
 
    $this->school_id = $school['id'];
 

	
 
    if (empty($semester))
 
      {
 
	$semester = school_semester_guess($school);
 
      }
 
    $this->semester = $semester;
 

	
 
    /* mark this as an upgraded Schedule class. See __wakeup() */
 
    $this->nclasses = -1;
 
  }
 

	
 
  //--------------------------------------------------
 
  // Mutators and Accessors
 
  //--------------------------------------------------
 
  public function getName()
 
  {
 
    return $this->scheduleName;
 
  }    
 

	
 
  /**
 
   * \brief
 
   *   Adds a new class to the schedule.
 
   *
 
   * \param $slot
 
   *   Currently, the Schedule class is not smart enough to understand
 
   *   CourseSlots. At a lower level, we split Courses with multiple
 
   *   CourseSlots into multiple Course objects with redundant
 
   *   information.
 
   */
 
  function addCourse($course_id, $title)
 
  {
 
    $this->courses[] = new Course($course_id, $title);
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Adds a section to this semester after finding the class.
 
   *
 
   * \param $instructor
 
   *   The instructor of this section/section_meeting.
 
   *
 
   * \return
 
   *   NULL on success, a string on error which is a message for the
 
   *   user and a valid XHTML fragment.
 
   */
 
  function addSection($course_name, $letter, $time_start, $time_end, $days, $synonym = NULL, $instructor = NULL, $location = NULL, $type = 'lecture')
 
  function addSection($course_name, $letter, $time_start, $time_end, $days, $synonym = NULL, $instructor = NULL, $location = NULL, $type = 'lecture', $slot = 'default')
 
  {
 
    if (empty($letter) && (empty($time_start) || !strcmp($time_start, 'none')) && (empty($time_end) || !strcmp($time_end, 'none')) && empty($days)
 
	&& empty($synonym) && empty($instructor) && empty($location) && (empty($type) || !strcmp($type, 'lecture')))
 
	&& empty($synonym) && empty($instructor) && empty($location) && (empty($type) || !strcmp($type, 'lecture'))
 
	&& (empty($slot) || !strcmp($slot, 'default')))
 
      return;
 

	
 
    /* reject invalid times */
 
    if (!strcmp($time_start, 'none') || !strcmp($time_end, 'none')
 
	|| $time_start > $time_end)
 
      {
 
	return 'Invalid time specifications for ' . htmlentities($course_name) . '-' . htmlentities($letter)
 
	  . '. Start time: <tt>' . htmlentities($time_start) . '</tt>. End time: <tt>' . htmlentities($time_end) . '</tt>.';
 
      }
 

	
 
    foreach ($this->courses as $course)
 
      if (!strcmp($course_name, $course->getName()))
 
	{
 
	  $section = $course->section_get($letter);
 
	  if (!$section)
 
	    {
 
	      $section = new Section($letter, array(), $synonym);
 
	      $course->section_add($section);
 
	      $course->section_add($section, $slot);
 
	    }
 
	  $section->meeting_add(new SectionMeeting($days, $time_start, $time_end, $location, $type, $instructor));
 

	
 
	  return;
 
	}
 

	
 
    error_log('Could not find class when parsing schedule from postData: ' . $course_name);
 
    echo 'Could not find class: ' . $course_name . "<br />\n";
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Get the school associated with this schedule.
 
   *
 
   * \return
 
   *   The school associated with this schedule or some fallback.
 
   */
 
  public function school_get()
 
  {
 
    $school = NULL;
 

	
 
    if (!empty($this->school_id))
 
      /*
 
       * May return NULL, so we don't just return this value right
 
@@ -215,143 +238,187 @@ class Schedule
 
      }
 

	
 
    return $school;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Get the semester associated with this schedule.
 
   *
 
   * \return
 
   *   The schedule's associated semester.
 
   */
 
  public function semester_get()
 
  {
 
    return $this->semester;
 
  }
 

	
 
  //--------------------------------------------------
 
  // Finds all of the possible permutations and stores
 
  // the results in the storage array.
 
  //--------------------------------------------------
 
	function findPossibilities()
 
	{
 
	  /*
 
	   * Split out any Course objects with multiple CourseSlots
 
	   * into multiple Course objects...
 
	   */
 
	  $new_courses = array();
 
	  foreach ($this->courses as $i => $course)
 
	    foreach ($course as $course_slot)
 
	      {
 
		$new_course = new Course($course->getName(), $course->title_get());
 
		$new_course->course_slot_add($course_slot);
 
		$new_courses[] = $new_course;
 

	
 
		$this->course_slot_mappings[count($new_courses) - 1] = $i;
 
	      }
 
	  $this->courses = $new_courses;
 
	  unset($new_courses);
 

	
 
	  /*
 
	   * Clean crud (completely empty courses) out of the
 
	   * schedule. For some crud, it's much easier to detect that
 
	   * it's crud now than during parsing of postData[].
 
	   *
 
	   * Now we may assume that each Course only has one
 
	   * CourseSlot...
 
	   */
 
	  foreach ($this->courses as $i => $course)
 
	    if (!$course->getnsections())
 
	      {
 
		unset($this->courses[$i]);
 
		$this->courses = array_values($this->courses);
 
		return $this->findPossibilities();
 
	      }
 
	    {
 
	      $course_slot = NULL;
 
	      foreach ($course as $course_slot)
 
		break;
 
	      if (empty($course_slot) || !$course_slot->sections_count())
 
		{
 
		  unset($this->courses[$i]);
 
		  $this->courses = array_values($this->courses);
 
		  return $this->findPossibilities();
 
		}
 
	    }
 

	
 
		$this->possiblePermutations = 1;
 
		/* special case: there is nothing entered into the schedule and thus there is one, NULL permutation */
 
		if (!count($this->courses))
 
		{
 
			/* have an empty schedule */
 
			$this->nPermutations = 1;
 
			return;
 
		}
 

	
 
		$position = 0;
 
		$counter = 0;
 

	
 
		$i = 0;
 
		foreach ($this->courses as $course)
 
		{
 
			$this->possiblePermutations = $this->possiblePermutations * $course->getnsections();
 
		  /*
 
		   * Kludge for until we support course_slots natively
 
		   * or find a better solution.
 
		   */
 
		  unset($course_slot);
 
		  foreach ($course as $course_slot)
 
		    break;
 

	
 
			$this->possiblePermutations = $this->possiblePermutations * $course_slot->sections_count();
 
			$cs[$i] = 0;	// Sets the counter array to all zeroes.
 
			$i ++;
 
		}
 
        
 
		// Checks for conflicts in given classes, stores if none found
 
		do
 
		{
 
			$conflict = false;
 
         
 
			// Get first class to compare
 
			for ($upCounter = 0; $upCounter < count($this->courses) && !$conflict; $upCounter ++)
 
			{
 
	    
 
			  for ($downCounter = count($this->courses) - 1; $downCounter > $upCounter && !$conflict; $downCounter --)
 
				{
 
				  if ($this->courses[$upCounter]->getSection($cs[$upCounter])
 
				      ->conflictsWith($this->courses[$downCounter]->getSection($cs[$downCounter])))
 
				  unset($course_slot_up);
 
				  foreach ($this->courses[$upCounter] as $course_slot_up)
 
				    break;
 

	
 
				  unset($course_slot_down);
 
				  foreach ($this->courses[$downCounter] as $course_slot_down)
 
				    break;
 

	
 
				  if ($course_slot_up->section_get_i($cs[$upCounter])->conflictsWith($course_slot_down->section_get_i($cs[$downCounter])))
 
				    {
 
				      $conflict = TRUE;
 
				      break;
 
				    }
 
				}
 
			}
 
	
 

	
 
	// Store to storage if no conflict is found.
 
	if(!$conflict)
 
	  {
 
	    for($i = 0; $i < count($this->courses); $i++)
 
	      {
 
		$this->storage[$this->nPermutations][$i] = $cs[$i];
 
	      }
 
	    $this->nPermutations++;
 
	  }
 
			
 

	
 
	// Increase the counter by one to get the next combination of class sections.
 
	$cs[$position] = $cs[$position] + 1;
 
			
 

	
 
	// Check to make sure the counter is still valid.
 
	$valid = false;
 
	while(!$valid)
 
	  {
 
	    if($cs[$position] == $this->courses[$position]->getnsections())
 
	    unset($course_slot);
 
	    foreach ($this->courses[$position] as $course_slot)
 
	      break;
 

	
 
	    if($cs[$position] == $course_slot->sections_count())
 
	      {
 
		$cs[$position] = 0;
 

	
 
		$position++;
 
					
 
		// This is for the very last permutation. Even 
 
		// though the combination is not actually true
 
		// the larger while loop will end before any 
 
		// new combinations are performed.
 
		if($position == count($this->courses))
 
		  {
 
		    $valid = true;
 
		  } else {
 
		  $cs[$position]++;
 
		}
 
	      } else {
 
	      $valid = true;
 
	      $position = 0;
 
	    }
 
	  }
 
            
 
	$counter++;
 
      } while($counter < $this->possiblePermutations);
 
  }
 
    
 

	
 
  /**
 
   * \brief
 
   *   Prints out the possible permutations in tables.
 
   *
 
   * \param $schedule_store
 
   *   The schedule_store handle with which this schedule was loaded,
 
   *   used to query the parent schedule.
 
   */
 
  //--------------------------------------------------
 
  function writeoutTables(array $schedule_store = NULL)
 
  {
 
    $filled = false;
 
    $time = array(700,730,800,830,900,930,1000,1030,1100,1130,1200,1230,1300,1330,1400,1430,1500,1530,1600,1630,1700,1730,1800,1830,1900,1930,2000,2030,2100,2130, 2200);
 

	
 
    define('SP_PERMUTATIONS_PER_PAGE', 64); /** @TODO: Define this in config.inc */
 

	
 
    $npages = ceil($this->nPermutations / SP_PERMUTATIONS_PER_PAGE);
 
    $page = 0;
 
    if (isset($_REQUEST['page']))
 
      $page = $_REQUEST['page'];
 
    /*
 
     * only display the ``this page doesn't exist'' 404 if there is at
 
     * least one permutation. Otherwise, we give an irrelevant 404 for
 
     * users with no permutations.
 
@@ -437,51 +504,52 @@ class Schedule
 
      echo '          <a class="button" href="input.php">Home</a>' . PHP_EOL
 
	. '        </p>'. PHP_EOL
 
	. '        <p class="centeredtext">Having problems? <a href="feedback.php">Let us know</a>.</p>' . PHP_EOL
 
	. '        <p class="centeredtext graytext"><em>Keyboard Shortcut: Left and right arrow keys switch between schedules</em></p>' . PHP_EOL;
 
    }		
 

	
 
    echo "\n";
 

	
 
    if($this->nPermutations > 0)
 
      {
 
	/*
 
	 * Figure out if we have to deal with Saturday and then deal
 
	 * with it.
 
	 *
 
	 * Also, ensure that our $time array is big enough for all of
 
	 * these courses.
 
	 */
 
	$max_day_plusone = 5;
 
	$have_saturday = FALSE;
 

	
 
	$max_time = (int)max($time);
 
	$min_time = (int)min($time);
 
	$sort_time = FALSE;
 
	foreach ($this->courses as $course)
 
	  foreach ($course as $course_slot)
 
	  {
 
	    for ($si = 0; $si < $course->getnsections(); $si ++)
 
	      foreach ($course->getSection($si)->getMeetings() as $meeting)
 
	    for ($si = 0; $si < $course_slot->sections_count(); $si ++)
 
	      foreach ($course_slot->section_get_i($si)->getMeetings() as $meeting)
 
		{
 
		  /* Saturdayness */
 
		  if ($meeting->getDay(5))
 
		    {
 
		      $max_day_plusone = 6;
 
		      $have_saturday = TRUE;
 
		    }
 

	
 
		  /* very late / very early classes */
 
		  while ((int)ltrim($meeting->getEndTime(), '0') > $max_time)
 
		    {
 
		      $max_time += $max_time + 30;
 
		      while ($max_time % 100 >= 60)
 
			$max_time += 40; /* + 100 - 60 */
 
		      $time[] = $max_time;
 
		    }
 
		  while ((int)ltrim($meeting->getStartTime(), '0') < $min_time)
 
		    {
 
		      $max_time += 30;
 
		      while ($min_time % 100 < 30)
 
			$min_time -= 40; /* + 60 - 100 */
 
		      $min_time -= 30;
 
		      $time[] = $min_time;
 
		      $sort_time = TRUE;
 
@@ -568,52 +636,55 @@ class Schedule
 
	      . "            <td class=\"day\">Wednesday</td>\n"
 
	      . "            <td class=\"day\">Thursday</td>\n"
 
	      . "            <td class=\"day\">Friday</td>\n";
 
	    if ($have_saturday)
 
	      echo "            <td class=\"day\">Saturday</td>\n";
 
	    echo "          </tr>\n";
 

	
 
	    $last_meeting = array();
 
	    $rowspan = array(0, 0, 0, 0, 0, 0);
 
	    for($r = 0; $r < (count($time)-1); $r++)
 
	      {
 

	
 
		echo "          <tr>\n"
 
		  . "            <td class=\"time\">" . $this->prettyTime($time[$r]) . "</td>\n";
 

	
 
		/* currently, 0-5 = monday-saturday */
 
		for($dayLoop = 0; $dayLoop < $max_day_plusone; $dayLoop++)
 
		{
 
		  /* Makes sure there is not a class already in progress */
 
		  if($rowspan[$dayLoop] <= 0)
 
		    {
 
		      for($j = 0; $j < count($this->courses); $j++)
 
			{
 
			  $course = $this->courses[$j];
 
			  $section_index = $this->storage[$i][$j];
 
			  $section = $course->getSection($section_index);
 
				  /* iterate through all of a class's meeting times */
 
				  $meetings = $section->getMeetings();
 
			  foreach ($course as $course_slot)
 
			    {
 
			      $section_index = $this->storage[$i][$j];
 
			      $section = $course_slot->section_get_i($section_index);
 

	
 
			      /* iterate through all of a class's meeting times */
 
			      $meetings = $section->getMeetings();
 

	
 
				  /* find any meeting which are going on at this time */
 
				  $current_meeting = NULL;
 
				  foreach ($meetings as $meeting)
 
				    {
 
				      if ($meeting->getDay($dayLoop)
 
					  && $meeting->getStartTime() >= $time[$r]
 
					  && $meeting->getStartTime() < $time[$r+1])
 
					{
 
					  $current_meeting = $meeting;
 
					}
 
				    }
 
				  
 
				  if ($current_meeting)
 
				    {
 
				      /* calculate how many rows this section should span */
 
				      for ($my_r = $r;
 
					   $my_r < (count($time)-1) && $current_meeting->getEndTime() > $time[$my_r];
 
					   $my_r ++)
 
					;
 
				      $rowspan[$dayLoop] = $my_r - $r;
 

	
 
				      $single_multi = 'single';
 
				      if ($rowspan[$dayLoop] > 1)
 
@@ -623,54 +694,55 @@ class Schedule
 
				      if (empty($title))
 
					$title = '';
 
				      else
 
					$title .= ' ';
 

	
 
				      $carret = '&#013;' . htmlentities("<br />");
 
				      echo '            <td rowspan="' . $rowspan[$dayLoop]
 
					. '" class="qTipCell ' . $single_multi . ' class' . $j
 
					. '" title="' . htmlentities($title, ENT_QUOTES) . $carret
 
					. 'Prof: ' . htmlentities($current_meeting->instructor_get(), ENT_QUOTES) . $carret
 
					. 'Room: ' . htmlentities($current_meeting->getLocation(), ENT_QUOTES) . $carret
 
					. 'Type: ' . htmlentities($current_meeting->type_get(), ENT_QUOTES) . $carret . '">'
 
					. '<span class="course-title block">' . htmlentities($title) . '</span>' . PHP_EOL
 
					. htmlentities($course->getName(), ENT_QUOTES) . '-'
 
					. htmlentities($section->getLetter(), ENT_QUOTES) . "\n"
 
					. '<span class="prof block">' . htmlentities($current_meeting->instructor_get(), ENT_QUOTES) . "</span>\n"
 
					. '<span class="location block">' . htmlentities($current_meeting->getLocation(), ENT_QUOTES) . "</span>\n"
 
					. '<span class="synonym block">' . htmlentities($section->getSynonym(), ENT_QUOTES) . "</span>\n"
 
					. "</td>\n";
 

	
 
				      /* for the ``Registration Codes'' dialogue: */
 
				      if (empty($permutations_courses[$j]))
 
					{
 
					  $singleton_course = new Course($course->getName(), $course->title_get());
 
					  $singleton_course->section_add($section);
 
					  $singleton_course->section_add($section, $course_slot->id_get());
 
					  $permutation_courses[$j] = $singleton_course->to_json_array();
 
					}
 

	
 
				      $filled = TRUE;
 
				    }
 
			    } /* $course_slot */
 
			}
 
		    }
 

	
 
		  if ($rowspan[$dayLoop] > 0)
 
		    {
 
		      $filled = TRUE;
 
		      $rowspan[$dayLoop] --;
 
		    }
 

	
 
		  /* If the cell was not filled, fill it with an empty cell. */
 
			if(!$filled)
 
			{
 
				echo "            <td class=\"none\">&nbsp;</td>\n";
 
			}
 
			$filled = FALSE;
 
		}
 
		
 
		// End of row
 
		echo "          </tr>\n";
 
	      }
 

	
 
	    // End of table
 
	    echo "        </table>\n"
 
              . '         <span class="course-data">'.  htmlentities(json_encode($permutation_courses)) . "</span>\n"
 
@@ -702,57 +774,88 @@ class Schedule
 
  // Make the time "pretty"
 
  //--------------------------------------------------
 
  function prettyTime($t){
 
    if($t >= 1200)
 
      {
 
	if ($t > 1259)
 
	  $t = ($t - 1200);
 
	return substr($t, 0, strlen($t)-2) . ":" . substr($t, strlen($t)-2, strlen($t)) . " PM";
 
      } else {
 
      return substr($t, 0, strlen($t)-2) . ":" . substr($t, strlen($t)-2, strlen($t)) . " AM";
 
    }
 
  }
 

	
 
  /**
 
   * \brief
 
   *   fetch the number of classes
 
   */
 
  function nclasses_get()
 
  {
 
    return count($this->courses);
 
  }
 

	
 
  /**
 
   * \brief
 
   *   fetch a specified class by its key
 
   *   Fetch a specified class by its key.
 
   *
 
   * Use Schedule::courses_get() instead of this function if the code
 
   * you're writing understand CourseSlot objects.
 
   *
 
   * \see Schedule::courses_get().
 
   */
 
  function class_get($class_key)
 
  public function class_get($class_key)
 
  {
 
    return $this->courses[$class_key];
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Get an array of Course objects as originally inputted by the
 
   *   user.
 
   */
 
  public function courses_get()
 
  {
 
    /*
 
     * As Mr. Westra would say, just map them courses back into their
 
     * original forms.
 
     */
 

	
 
    $courses = array();
 
    foreach ($this->courses as $course_i => $course)
 
      {
 
	$mapping = $this->course_slot_mappings[$course_i];
 
	if (empty($courses[$mapping]))
 
	  $courses[$mapping] = new Course($course->getName(), $course->title_get());
 

	
 
	foreach ($course as $course_slot)
 
	  $courses[$mapping]->course_slot_add($course_slot);
 
      }
 

	
 
    return $courses;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Set my global ID.
 
   *
 
   * Only to be called by schedule_store_store().
 
   */
 
  function id_set($id)
 
  { 
 
    $this->id = $id;
 
  }
 

	
 
  /*
 
   * \brief
 
   *   Get my global ID.
 
   */
 
  function id_get()
 
  {
 
    return $this->id;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Write out a relative URL for a particular schedule.
 
   *
 
   * Takes into account the $clean_urls setting.
 
   *
 
@@ -783,58 +886,64 @@ class Schedule
 

	
 
    if ($page)
 
      $url .= 'page=' . (int)$page . '&';
 

	
 
    return $url;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Get the ID of the schedule from which this schedule was
 
   *   derived.
 
   */
 
  public function parent_get()
 
  {
 
    return $this->parent_id;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   A magic function which tries to upgrade old serialized sections
 
   *   to the new format.
 
   */
 
  function __wakeup()
 
  {
 
    if ($this->nclasses == -1)
 
      /* this Schedule doesn't need to be upgraded from Classes to Course */
 
      return;
 
    if ($this->nclasses != -1)
 
      {
 
	/* this Schedule needs to be upgraded from Classes to Course */
 

	
 
    $this->courses = array();
 
    foreach ($this->classStorage as $classes)
 
      {
 
	$this->courses[] = $classes->to_course();
 
	$this->courses = array();
 
	foreach ($this->classStorage as $classes)
 
	  $this->courses[] = $classes->to_course();
 
	$this->nclasses = -1;
 
      }
 
    $this->nclasses = -1;
 

	
 
    if (empty($this->parent_id))
 
      $this->parent_id = NULL;
 

	
 
    if (empty($this->school_id))
 
      {
 
	/* Ensure we have $_SESSION. */
 
	page::session_start();
 
	$school = school_load_guess(FALSE);
 
	$this->school_id = $school['id'];
 
      }
 
    if (empty($this->semester))
 
      {
 
	if (empty($school))
 
	  {
 
	    /* Ensure we have $_SESSION. */
 
	    page::session_start();
 

	
 
	    $school = school_load($this->school_id);
 
	    $this->semester = school_semester_guess($school);
 
	  }
 
      }
 

	
 
    if (empty($this->course_slot_mappings))
 
      {
 
	$this->course_slot_mappings = array();
 
	foreach ($this->courses as $course_i => $course)
 
	  $this->course_slot_mappings[$course_i] = count($this->course_slot_mappings);
 
      }
 
  }
 
}
inc/class.semester.inc
Show inline comments
 
@@ -134,106 +134,115 @@ class Semester
 
  public function department_classes_get($dept)
 
  {
 
    if (!isset($this->departments[$dept]))
 
      throw new ErrorException('I was asked for a department I don\'t own: ' . $dept);
 

	
 
    return array_keys($this->departments[$dept]);
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Utility function to add a section to the semester,
 
   *   automatically creating classes as necessary.
 
   *
 
   * Crawler functions should generally use this instead of
 
   * Semester::class_add().
 
   *
 
   * \param $dept
 
   *   The department this section belongs to.
 
   * \param $class
 
   *   The class this section belongs to.
 
   * \param $section
 
   *   The section itself.
 
   * \param $title
 
   *   The course human-friendly title.
 
   * \param $course_slot_id
 
   *   The slot of the course which this section should be added
 
   *   to. Use 'default' (or don't pass this parameter) if your school
 
   *   does not have the concept of course slots. Ask binki for help
 
   *   figuring this out. Course slots are a sort of
 
   *   inverse/complement to section_meetings.
 
   */
 
  public function section_add($dept, $class, Section $section, $title = NULL)
 
  public function section_add($dept, $class, Section $section, $title = NULL, $course_slot_id = 'default')
 
  {
 
    $dept = strtoupper($dept);
 
    $class = strtoupper($class);
 

	
 
    if (!isset($this->departments[$dept])
 
	|| !isset($this->departments[$dept][$class]))
 
      {
 
	$classobj = new Course($dept . '-' . $class, $title);
 
	$this->class_add($classobj);
 
      }
 
    else
 
      {
 
	$classobj = $this->departments[$dept][$class];
 
      }
 

	
 
    $classobj->section_add($section);
 
    $classobj->section_add($section, $course_slot_id);
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Add a section_meeting, calling Semester::section_add() as
 
   *   necessary.
 
   *
 
   * To be used by crawlers when parsing data which only presents one
 
   * section_meeting at a time. I.e., when they do tabular data right.
 
   *
 
   * \param $dept
 
   *   The department this section_meeting's course belongs to.
 
   * \param $course
 
   *   The course number this section_meeting's section belongs to.
 
   * \param $title
 
   *   The course title of the given course the section_meeting or
 
   *   NULL.
 
   *   belongs to.
 
   * \param $section
 
   *   The letter or numbers which make up the section's name.
 
   * \param $synonym
 
   *   The section synonym or NULL.
 
   * \param $section_meeting
 
   *   The SectionMeeting to be added to a section which may or may
 
   *   not already be in this Semester.
 
   * \param $course_slot_id
 
   *   The name of the new CourseSlot to create if the given section
 
   *   does not yet exist.
 
   */
 
  public function section_meeting_add($dept, $course, $title, $section, $synonym, $section_meeting)
 
  public function section_meeting_add($dept, $course, $title, $section, $synonym, $section_meeting, $course_slot_id = 'default')
 
  {
 
    $dept = strtoupper($dept);
 
    $course = strtoupper($course);
 

	
 
    if (empty($this->departments[$dept][$course]))
 
      $course_obj = NULL;
 
    else
 
      {
 
	$course_obj = $this->departments[$dept][$course];
 
	$section_obj = $course_obj->section_get($section);
 
      }
 
    if (empty($course_obj) || empty($section_obj))
 
      return $this->section_add($dept, $course, new Section($section, array($section_meeting), $synonym), $title);
 
      return $this->section_add($dept, $course, new Section($section, array($section_meeting), $synonym), $title, $course_slot_id);
 

	
 
    $section_obj->meeting_add($section_meeting);
 
    return;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Update the time_end.
 
   *
 
   * The time_end is a unix timestamp roughly estimating the time at
 
   * which a semester starts. It is used when guessing what semester a
 
   * user is interested in.
 
   *
 
   * \param $time_end
 
   *   The new time_end.
 
   */
 
  public function time_end_set($time_end)
 
  {
 
    $this->time_end = $time_end;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Set the time_end only if it would make the semester end later.
input.php
Show inline comments
 
@@ -104,85 +104,85 @@ elseif (!empty($_REQUEST['e']))
 

	
 
    $creating_new_schedule = FALSE;
 
  }
 

	
 
/*
 
 * We cannot initialize the page object nor guess the school before
 
 * figuring loading a saved schedule because we'll default to that
 
 * saved_schedule's school/semester.
 
 */
 
$scripts = array('jQuery', 'jQueryUI', 'qTip2', 'schedInput');
 
$inputPage = page::page_create('Scheduler', $scripts, $inputPage_options);
 
$school = $inputPage->get_school();
 
$semester = $inputPage->semester_get();
 

	
 
$my_hc = 'var slate_permutate_example_course_id = ' . json_encode(school_example_course_id($school)) . ';
 

	
 
jQuery(document).ready(
 
  function()
 
  {
 
    var class_last = 0;
 

	
 
';
 
if ($sch)
 
{
 
  $nclasses = $sch->nclasses_get();
 
  for ($class_key = 0; $class_key < $nclasses; $class_key ++)
 
  foreach ($sch->courses_get() as $course)
 
    {
 
      $my_hc .= input_class_js($sch->class_get($class_key), '    ');
 
      $my_hc .= input_course_js($course, '    ');
 
    }
 
}
 
elseif ($errors_fix)
 
  {
 
    foreach ($_POST['postData'] as $course)
 
      if (is_array($course))
 
	{
 
	  $title = '';
 
	  if (!empty($course['title']))
 
	    $title = $course['title'];
 
	  if (empty($course['name']))
 
	    $my_hc .= '    class_last = add_class();' . PHP_EOL;
 
	  else
 
	    $my_hc .= '    class_last = add_class_n(' . json_encode($course['name']) . ', ' . json_encode($title) . ');' . PHP_EOL;
 
	  foreach ($course as $section)
 
	    if (is_array($section))
 
	      $my_hc .= '    add_section_n(class_last, ' . json_encode($section['letter']) . ', '
 
		. json_encode($section['synonym']) . ', ' . json_encode($section['start']) . ', '
 
		. json_encode($section['end']) . ', '
 
		. json_encode(array('m' => !empty($section['days'][0]), 't' => !empty($section['days'][1]), 'w' => !empty($section['days'][2]),
 
				    'h' => !empty($section['days'][3]), 'f' => !empty($section['days'][4]),
 
				    's' => !empty($section['days'][5])))
 
		. ', ' . json_encode($section['professor']) . ', '
 
		. json_encode($section['location']) . ', '
 
		. json_encode($section['type']) . ');' . PHP_EOL;
 
		. json_encode($section['type']) . ', '
 
		. json_encode($section['slot']) . ');' . PHP_EOL;
 
	  $my_hc .= PHP_EOL;
 
	}
 
  }
 
else
 
  {
 
    $default_courses = school_default_courses($school);
 
    foreach ($default_courses as $default_class)
 
      $my_hc .= input_class_js($default_class, '    ');
 
      $my_hc .= input_course_js($default_class, '    ');
 
  }
 
$my_hc .= '    class_last = add_class();' . PHP_EOL;
 
if ($qtips_always || !isset($_SESSION['saw_qtips']))
 
  {
 
    $my_hc .= '    addTips();' . PHP_EOL;
 
    $_SESSION['saw_qtips'] = TRUE;
 
  }
 
$my_hc .= '  });
 
';
 

	
 
$inputPage->headcode_add('scheduleInput', $inputPage->script_wrap($my_hc), TRUE);
 

	
 
$inputPage->head();
 

	
 
/*
 
 * Force a student to choose a school or declare he's a generic
 
 * student before displaying the input form. To do this, we need
 
 * another variable in $_SESSION: $_SESSION['school_chosen'].
 
 */
 
if (!empty($_REQUEST['school']) && !empty($_SESSION['school']) && !strcmp($_REQUEST['school'], $_SESSION['school']))
 
  $_SESSION['school_chosen'] = TRUE;
 
if (!empty($_REQUEST['selectschool'])
 
    || empty($school) || $school['id'] == 'default' && empty($_SESSION['school_chosen']))
 
  {
 
@@ -309,53 +309,53 @@ if (!empty($_REQUEST['selectsemester']))
 
          <td class="center"></td>
 
          <td class="center"></td>
 
        </tr>
 
      </table>
 
    </td>
 
  </tr>
 
</table>
 

	
 
<div class="paddingtop">
 
  <input class="button olive" type="submit" value="Find a schedule" />
 
</div>
 

	
 
</form>
 

	
 
<?php 
 

	
 
/* Show/hide Advanced Options: <p><span id="showadvanced" style="margin-left: 1em;"><a href="#">Advanced</a></span></p> */ 
 
?>
 
<div id="showInstructions" style="width: 100%; text-align: center;"><a href="#">Detailed Instructions...</a></div>
 

	
 
<?php
 
$inputPage->showSchoolInstructions();
 
$inputPage->foot();
 

	
 
function input_class_js(Course $course, $whitespace = '  ')
 
function input_course_js(Course $course, $whitespace = '  ')
 
{
 
  $title = $course->title_get();
 
  if (empty($title))
 
    $title = '';
 
  $js = $whitespace . 'class_last = add_class_n(' . json_encode($course->getName()) . ', '
 
    . json_encode($title) . ');' . PHP_EOL;
 

	
 
  $nsections  = $course->getnsections();
 
  for ($section_key = $nsections - 1; $section_key >= 0; $section_key --)
 
    {
 
      $section = $course->getSection($section_key);
 
      $meetings = $section->getMeetings();
 
  foreach ($course as $course_slot)
 
    foreach ($course_slot as $section)
 
      {
 
	$meetings = $section->getMeetings();
 
      foreach ($meetings as $meeting)
 
	{
 
	  $js .= $whitespace . 'add_section_n(class_last, ' . json_encode($section->getLetter()) . ', '
 
	    . json_encode($section->getSynonym()) . ', '
 
	    . json_encode($meeting->getStartTime()) . ', '
 
	    . json_encode($meeting->getEndTime()) . ', '
 
	    . json_encode(array('m' => $meeting->getDay(0), 't' => $meeting->getDay(1), 'w' => $meeting->getDay(2), 'h' => $meeting->getDay(3), 'f' => $meeting->getDay(4),
 
				's' => $meeting->getDay(5))) . ', '
 
	    . json_encode($meeting->instructor_get()) . ', '
 
	    . json_encode($meeting->getLocation()) . ','
 
	    . json_encode($meeting->type_get()) . ');' . PHP_EOL;
 
	    . json_encode($meeting->getLocation()) . ', '
 
	    . json_encode($meeting->type_get()) . ', '
 
	    . json_encode($course_slot->id_get()) . ');' . PHP_EOL;
 
	}
 
    }
 

	
 
  return $js;
 
}
process.php
Show inline comments
 
@@ -187,49 +187,52 @@ if(!$DEBUG)
 
	  }
 

	
 
	$allClasses = new Schedule($name, $parent_schedule_id, $school, $semester);
 

	
 
	$errors = array();
 
	foreach($postData as $course)
 
	  {
 
	    /*
 
	     * Only add classes if the user added at least one
 
	     * section to the class. We know that $course['name']
 
	     * is not a section, so count() needs to be > 1 and
 
	     * we need to skip over 'name' in our loop.
 
	     */
 
	    if(is_array($course) && count($course) > 1)
 
	      {
 
		if (empty($course['title']))
 
		  $course['title'] = '';
 

	
 
		$allClasses->addCourse($course['name'], $course['title']);
 

	
 
				foreach($course as $section)
 
				  /* Skip the section name, which isn't a section */
 
					if(is_array($section))
 
					  {
 
					    $error_string = $allClasses->addSection($course['name'], $section['letter'], $section['start'], $section['end'], arrayToDays(empty($section['days']) ? array() : $section['days'], 'alpha'), $section['synonym'], $section['professor'], $section['location'], $section['type']);
 
					    if (empty($section['slot']))
 
					      $section['slot'] = 'default';
 

	
 
					    $error_string = $allClasses->addSection($course['name'], $section['letter'], $section['start'], $section['end'], arrayToDays(empty($section['days']) ? array() : $section['days'], 'alpha'), $section['synonym'], $section['professor'], $section['location'], $section['type'], $section['slot']);
 
					    if ($error_string !== NULL)
 
					      $errors[] = $error_string;
 
					  }
 
			}
 
		}
 

	
 
		/*
 
		 * Tell the user that his input is erroneous and
 
		 * require him to fix it.
 
		 */
 
		if (count($errors))
 
		  {
 
		    $error_page = page::page_create('Process Schedule — Errors', array(), $page_create_options);
 
		    $error_page->head();
 

	
 
		    echo '        <p>' . PHP_EOL
 
		      . '          You have the following errors in your input:' . PHP_EOL
 
		      . '        </p>' . PHP_EOL
 
		      . '        <ul>' . PHP_EOL;
 
		    foreach ($errors as $error)
 
		      echo '          <li>' . $error . '</li>' . PHP_EOL;
 
		    echo '        </ul>' . PHP_EOL
 
		      . '        <h3>Solving Errors</h3>' . PHP_EOL
 
		      . '        <ul>' . PHP_EOL
school.d/default.inc
Show inline comments
 
@@ -61,29 +61,30 @@ EOF;
 
 *
 
 * \see school_registration_html()
 
 */
 
function default_registration_html(Page $page, array $school, array $courses)
 
{
 
  if (!empty($school['registration_url']))
 
    {
 
      $link_url = $school['registration_url'];
 
      $link_text = $school['name'] . '\'s registration system';
 
    }
 
  else
 
    {
 
      $link_url = $school['url'];
 
      $link_text = $school['name'] . '\'s website';
 
    }
 

	
 
  $html = ''
 
    . '  <p>' . PHP_EOL
 
    . '    Enter these codes into ' . htmlentities($school['name']) . '\'s online course registration' . PHP_EOL
 
    . '    system (<a href="' . htmlentities($link_url) . '" target="_blank">' . htmlentities($link_text) . '</a>)' . PHP_EOL
 
    . '    to register for classes:' . PHP_EOL
 
    . '  </p>' . PHP_EOL
 
    . '  <ul class="synonym-list">' . PHP_EOL;
 
  foreach ($courses as $course)
 
    foreach ($course as $section)
 
      $html .= '    <li>' . htmlentities($section->getSynonym()) . '</li>' . PHP_EOL;
 
    foreach ($course as $course_slot)
 
      foreach ($course_slot as $section)
 
        $html .= '    <li>' . htmlentities($section->getSynonym()) . '</li>' . PHP_EOL;
 
  $html .= '  </ul>' . PHP_EOL;
 
  return $html;
 
}
school.d/umich.crawl.inc
Show inline comments
 
@@ -207,55 +207,58 @@ function umich_crawl_csv($school_crawl_l
 
	{
 
	  school_crawl_logf($school_crawl_log, 5, "Unable to parse department string `%s'. Skipping section/course (synonym=%s).",
 
			    $row[$fields['Subject']], $synonym);
 
	  continue;
 
	}
 
      $dept = $matches[1];
 

	
 
      $days = '';
 
      foreach (array('M' => 'm', 'T' => 't', 'W' => 'w', 'TH' => 'h', 'F' => 'f', 'S' => 's')
 
	       as $field => $day)
 
	if (strlen(trim($row[$fields[$field]])))
 
	  $days .= $day;
 

	
 
      if (!preg_match(';^([0-9]+)-([0-9]+)([AP])M$;', $row[$fields['Time']], $matches))
 
	{
 
	  school_crawl_logf($school_crawl_log, 4, "Unable to parse meeting time: `%s'. Skipping section/meeting (synonym=%s).",
 
			    $row[$fields['Time']], $synonym);
 
	  /* ensure that the class is added nonetheless */
 
	  if ($semester->class_get($dept, $course_id) === NULL)
 
	    $semester->class_add(new Course($dept . '-' . $course_id, $row[$fields['Course Title']]));
 
	  continue;
 
	}
 
      $time_end = umich_crawl_time($matches[2], $matches[3]);
 
      $time_start = umich_crawl_time($matches[1], FALSE, $time_end);
 
      /* umich defines course_slots by meeting_type. */
 
      $meeting_type = school_crawl_meeting_type(trim($row[$fields['Component']]));
 

	
 
      $semester->section_meeting_add($dept, $course_id, trim($row[$fields['Course Title']]),
 
				     trim($row[$fields['Section']]), $synonym,
 
				     new SectionMeeting($days, $time_start, $time_end,
 
							trim($row[$fields['Location']]),
 
							school_crawl_meeting_type(trim($row[$fields['Component']])),
 
							trim($row[$fields['Instructor']])));
 
							$meeting_type,
 
							trim($row[$fields['Instructor']])),
 
				     $meeting_type);
 
    }
 
}
 

	
 
/**
 
 * \brief
 
 *   Try to turn a umich-formatted time into something usable.
 
 *
 
 * \param $raw
 
 *   The raw input.
 
 * \param $xm
 
 *   FALSE or, if PM or AM was specified, 'P' for PM and 'A' for AM.
 
 * \param $before
 
 *   A time of day before which this time must be. Used generally for
 
 *   the start time of a class. The end time of a class must be parsed
 
 *   first so that the result of that calculation may be passed as the
 
 *   $before value.
 
 */
 
function umich_crawl_time($raw, $xm = FALSE, $before = '2400')
 
{
 
  $h = $raw;
 
  $m = '00';
 
  if (strlen($raw) > 2)
 
    {
 
      $h = substr($raw, 0, strlen($raw) - 2);
scripts/scheduleInput.js
Show inline comments
 
@@ -41,105 +41,119 @@ var sectionsOfClass = new Array();
 
 * 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
 
 *   A quick hash to prevent there from being two AJAX requests for a
 
 *   given course at one time.
 
 */
 
var course_ajax_requests = [];
 

	
 
/**
 
 * \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;
 

	
 
/**
 
 * \brief
 
 *   Whether or not we should divulge the existence of course slots to
 
 *   the user.
 
 *
 
 * This will automatically be set to true as soon as a course
 
 * utilizing multiple slots is added using autocomplete.
 
 */
 
var show_course_slots = false;
 

	
 
/*
 
 * General Input Functions
 
 */
 

	
 
/**
 
 * Outputs an <option/> 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 '<option value="' + value + '"' + selected + '>' + content + "</option>\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, instructor, location, type)
 
function add_section_n(cnum, name, synonym, stime, etime, days, instructor, location, type, slot)
 
{
 
    var snum = last_section_i ++;
 
    var cssclasses = 'section class' + cnum;
 
    var cssclasses = 'section class' + cnum + ' ' + safe_css_class('slot-' + slot);
 
    var last_tr;
 

	
 
    if(type == 'lab')
 
	cssclasses += ' lab';
 

	
 
    var section_html = '<tr id="tr-section-' + String(snum) + '" class="' + cssclasses + '"><td class="none"></td>' +
 
	'<td class="sectionIdentifier center"><input type="text" size="1" class="required section-letter-entry" name="postData[' + cnum + '][' + snum + '][letter]" /><input class="section-synonym-entry" type="hidden" name="postData[' + cnum + '][' + snum + '][synonym]" /></td>' +
 
	'<td class="sectionIdentifier center"><input type="text" size="1" class="required section-letter-entry" name="postData[' + cnum + '][' + snum + '][letter]" />' + 
 
	'  <input class="section-synonym-entry" type="hidden" name="postData[' + cnum + '][' + snum + '][synonym]" />' +
 
	'  <input class="section-slot-entry" type="hidden" name="postData[' + cnum + '][' + snum + '][slot]" />' +
 
	'</td>' +
 
	'<td class="professor center"><input type="text" size="10" class="profName" name="postData[' + cnum + ']['+ snum + '][professor]" /></td>' +
 
	'<td><select class="selectRequired" name="postData[' + cnum + '][' + snum + '][start]"><option value="none"></option>' +
 
	genOptionHtml("0700", "7:00 am", stime) + genOptionHtml("0730", "7:30 am", stime) +
 
	genOptionHtml("0800", "8:00 am", stime) + genOptionHtml("0830", "8:30 am", stime) +
 
	genOptionHtml("0900", "9:00 am", stime) + genOptionHtml("0930", "9:30 am", stime) +
 
	genOptionHtml("1000", "10:00 am", stime) + genOptionHtml("1030", "10:30 am", stime) +
 
	genOptionHtml("1100", "11:00 am", stime) + genOptionHtml("1130", "11:30 am", stime) +
 
	genOptionHtml("1200", "12:00 pm", stime) + genOptionHtml("1230", "12:30 pm", stime) +
 
	genOptionHtml("1300", "1:00 pm", stime) + genOptionHtml("1330", "1:30 pm", stime) +
 
	genOptionHtml("1400", "2:00 pm", stime) + genOptionHtml("1430", "2:30 pm", stime) +
 
	genOptionHtml("1500", "3:00 pm", stime) + genOptionHtml("1530", "3:30 pm", stime) +
 
	genOptionHtml("1600", "4:00 pm", stime) + genOptionHtml("1630", "4:30 pm", stime) +
 
	genOptionHtml("1700", "5:00 pm", stime) + genOptionHtml("1730", "5:30 pm", stime) +
 
	genOptionHtml("1800", "6:00 pm", stime) + genOptionHtml("1830", "6:30 pm", stime) +
 
	genOptionHtml("1900", "7:00 pm", stime) + genOptionHtml("1930", "7:30 pm", stime) +
 
	genOptionHtml("2000", "8:00 pm", stime) + genOptionHtml("2030", "8:30 pm", stime) +
 
	genOptionHtml("2100", "9:00 pm", stime);
 

	
 
    if (stime.length > 0)
 
    {
 
	var stime_end = stime.substr(2);
 
	var stime_begin = stime.substr(0, 2);
 
	if (stime_end != '00' && stime_end != '30'
 
	   || stime_begin < 7 || stime_begin > 21)
 
@@ -164,125 +178,191 @@ function add_section_n(cnum, name, synon
 
	genOptionHtml("2020", "8:20 pm", etime) + genOptionHtml("2050", "8:50 pm", etime) +
 
	genOptionHtml("2120", "9:20 pm", etime) + genOptionHtml('2150', '9:50 pm', etime);
 

	
 
    if (etime.length > 0)
 
    {
 
	var etime_end = etime.substr(2);
 
	var etime_begin = etime.substr(0, 2);
 
	if (etime_end != '50' && etime_end != '20'
 
	   || etime_begin < 7 || etime_begin > 21)
 
	    section_html = section_html + genOptionHtml(etime, prettyTime(etime), etime);
 
    }
 

	
 
    section_html = section_html + '</select></td>\
 
<td class="cbrow"><input type="checkbox" class="daysRequired" name="postData[' + cnum + '][' + snum + '][days][0]" value="1" ' + (days.m ? 'checked="checked"' : '') + ' /></td>\
 
<td class="cbrow"><input type="checkbox" class="daysRequired" name="postData[' + cnum + '][' + snum + '][days][1]" value="1" ' + (days.t ? 'checked="checked"' : '') + ' /></td>\
 
<td class="cbrow"><input type="checkbox" class="daysRequired" name="postData[' + cnum + '][' + snum + '][days][2]" value="1" ' + (days.w ? 'checked="checked"' : '') + ' /></td>\
 
<td class="cbrow"><input type="checkbox" class="daysRequired" name="postData[' + cnum + '][' + snum + '][days][3]" value="1" ' + (days.h ? 'checked="checked"' : '') + ' /></td>\
 
<td class="cbrow"><input type="checkbox" class="daysRequired" name="postData[' + cnum + '][' + snum + '][days][4]" value="1" ' + (days.f ? 'checked="checked"' : '') + ' /></td>\
 
<td class="cbrow"><input type="checkbox" class="daysRequired" name="postData[' + cnum + '][' + snum + '][days][5]" value="1" ' + (days.s ? 'checked="checked"' : '') + ' /></td>' +
 
	'<td class="removeCell"><div class="deleteSection"><input type="button" value="x" class="gray" /></div></td><td class="emptyCell">' +
 
	'<input class="section-location-entry" type="hidden" name="postData[' + cnum + '][' + snum + '][location]" />' +
 
	'<input class="section-type-entry" type="hidden" name="postData[' + cnum + '][' + snum + '][type]" />' +
 
	'</td></tr>';
 

	
 
    jQuery('tr.class' + cnum + ':last').after(section_html);
 
    /*
 
     * Try to append this section to the last section in its
 
     * associated CourseSlot...
 
     */
 
    last_tr = jQuery('tr.class' + cnum + '.' + safe_css_class('slot-' + slot) + ':last');
 
    if (!last_tr.length)
 
    {
 
	/* Also append a a new ``we are this slot'' row... */
 
	course_add_slot_row(cnum, slot);
 
	last_tr = jQuery('tr.class' + cnum + ':last');
 
    }
 
    last_tr.after(section_html);
 
    sectionsOfClass[cnum] ++;
 

	
 
    var section_tr = jQuery('#tr-section-' + String(snum));
 
    /* store course_i in a place the newly added section will look for it */
 
    section_tr.data({course_i: cnum});
 

	
 
    /*
 
     * Store data into the newly created HTML. With this method we
 
     * have to _avoid_ escaping entities in the text we're setting as
 
     * values because the DOM stuff will escape it for us.
 
     */
 
    section_tr.find('.section-letter-entry').val(name);
 
    section_tr.find('.section-synonym-entry').val(synonym);
 
    section_tr.find('.section-slot-entry').val(slot);
 
    section_tr.find('.profName').val(instructor);
 
    section_tr.find('.section-location-entry').val(location);
 
    section_tr.find('.section-type-entry').val(type);
 

	
 
    /* 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}, '', '', '');
 
    var section_i = add_section_n(cnum, '', '', '', '', {m: false, t: false, w: false, h: false, f: false, s: false}, '', '', '', 'default');
 
    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.title)
 
	jQuery('.pclass' + cnum + ' .course-title-entry').val(data.title);
 

	
 
    /*
 
     * If the user enterred something iffy, correct him. Or do so
 
     * regardless ;-).
 
     */
 
    /* this data['class'] stuff is for the old JSON format we used... */
 
    if (data['class'])
 
	data.course = data['class'];
 
    if (data.course)
 
	jQuery('.className' + cnum).val(data.course);
 

	
 
    if (!data.sections)
 
	return;
 

	
 
    jQuery.each(data.sections, function(i, section)
 
		{
 
		    add_section_n(cnum, section.section, section.synonym, section.time_start, section.time_end, section.days, section.instructor, section.location, section.type);
 
		    if (!section.slot)
 
			section.slot = 'default';
 

	
 
		    add_section_n(cnum, section.section, section.synonym, section.time_start, section.time_end, section.days, section.instructor, section.location, section.type, section.slot);
 
		});
 

	
 
    /*
 
     * Handle course-level interdependencies.
 
     */
 
    if (data.dependencies)
 
	jQuery.each(data.dependencies, function(i, dep)
 
		    {
 
			/* Gracefully deprecate the old crawler's JSON format. */
 
			if (dep['class'])
 
			    dep.course = dep['class'];
 

	
 
			var new_course_num = add_class_n(dep.course, dep['title'] ? dep['title'] : '');
 
			add_sections(new_course_num, dep);
 
		    });
 
}
 

	
 
/**
 
 * \brief
 
 *   Adds an identifier for a course slot.
 
 *
 
 * \param course_i
 
 *   The javascript index of the course to which a slot is being
 
 *   added.
 
 * \param slot_id
 
 *   The idenfifier of the slot.
 
 */
 
function course_add_slot_row(course_i, slot_id)
 
{
 
    var extra_classes = '';
 

	
 
    if (!show_course_slots)
 
    {
 
	var aclass;
 
	/*
 
	 * Then check if this course has multiple slots and we should
 
	 * enable displaying them to the user.
 
	 */
 
	aclass = jQuery('.' + safe_css_class('class' + course_i) + '.section .section-slot-entry');
 
	if (aclass.length && aclass.val() != slot_id)
 
	{
 
	    enable_course_slots();
 
	}
 
    }
 
    if (!show_course_slots)
 
	extra_classes += ' tr-slot-id-hidden';
 

	
 
    jQuery('tr.class' + course_i + ':last').after(
 
	'<tr class="class' + course_i + ' tr-slot-id ' + safe_css_class('slot-' + slot_id) + extra_classes + '">\n' +
 
	    '  <td><span /></td>\n' +
 
	    '  <td colspan="10"><span class="slot-id-text" /></td>\n' +
 
	    '  <td colspan="2"><span /></td>\n' +
 
	    '</tr>\n'
 
    );
 
    jQuery('tr.class' + course_i + ':last .slot-id-text').text('The following are ' + slot_id + ' sections and will be scheduled as a group.');
 
}
 

	
 
/**
 
 * \brief
 
 * Dynamically enable the displification of course slots to the user.
 
 */
 
function enable_course_slots()
 
{
 
    show_course_slots = true;
 

	
 
    jQuery('.tr-slot-id-hidden').removeClass('tr-slot-id-hidden');
 
}
 

	
 
/**
 
 * \brief
 
 *   Adds a new class to the input.
 
 *
 
 * \param course_id
 
 *   The course_id.
 
 * \param title
 
 *   The human-friendly course title.
 
 * \return
 
 *   The javascript-local course entry identifying number.
 
 */
 
function add_class_n(course_id, title)
 
{
 
    /*
 
     * If we're adding a course entry form with preadded
 
     * content, first remove the empty course.
 
     */
 
    if (course_id.length && slate_permutate_course_free != -1)
 
	course_remove(slate_permutate_course_free);
 

	
 
    sectionsOfClass[classNum] = 0; // Initialize at 0
 
    course_ajax_requests[classNum] = false;
 
    jQuery('#jsrows').append('<tr id="tr-course-' + classNum + '" class="class class' + classNum + ' pclass' + classNum + '"><td class="nameTip"><input type="text" id="input-course-' + classNum + '" class="classRequired defText className'+classNum+' className" title="Class Name" name="postData[' + classNum + '][name]" /></td><td colspan="10"><input type="text" name="postData[' + classNum + '][title]" class="inPlace course-title-entry" /></td><td class="tdInput"><div class="deleteClass"><input type="button" value="Remove" class="gray" /></div></td><td class="none"><button type="button" class="addSection gray">+</button></td></tr>');
 

	
 
		/* store classNum as course_i into the <tr />: */
 
    var tr_course = jQuery('#tr-course-' + classNum);
 
@@ -533,48 +613,60 @@ function prettyTime(time_str)
 
    var hour_str;
 
    var m;
 

	
 
    i_hour = time_str.substr(0, 2) * 1;
 
    if (i_hour < 12)
 
	{
 
	    m = 'a';
 
	}
 
    else
 
	{
 
	    m = 'p';
 
	    if (i_hour > 12)
 
		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';
 
}
 

	
 
/**
 
 * \brief
 
 *   Takes any value classname and tries to smooth it out to a valid
 
 *   CSS class name.
 
 *
 
 * \todo STUB
 
 */
 
function safe_css_class(classname)
 
{
 
    return classname;
 
}
 

	
 
//--------------------------------------------------
 
// Items bound to pageload/events
 
//--------------------------------------------------
 
jQuery(document).ready(function() {
 

	
 
	//--------------------------------------------------
 
	// Deletes the selected class from input
 
	//--------------------------------------------------
 
	jQuery('.deleteClass').live('click', function() {
 
	    /* The user is not allowed to interactively delete the one empty course */
 
	    var course_i = jQuery(this).parent().parent().data('course_i');
 
	    if (slate_permutate_course_free == course_i)
 
		return false;
 
	    if(confirm('Delete class and all sections of this class?')) {
 
		/* The one empty course may have became this course in that time */
 
		if (slate_permutate_course_free == course_i)
 
		    return false;
 
		course_remove(course_i);
 
		return false;
 
	    }
 
	    return false;
 
	});
 

	
 
	//--------------------------------------------------
styles/general.css
Show inline comments
 
@@ -365,35 +365,40 @@ a:hover {
 
.smallurl {
 
  font-size: .9em;
 
}
 
.indent {
 
  margin-left: 1em;
 
}
 

	
 
/* qTip2 Styling */
 
.ui-tooltip-dark .ui-tooltip-content{
 
  border-color: #303030;
 
  border-width: 2px;
 
  color: #f3f3f3;
 
  background-color: #505050;
 

	
 
  background: rgba(80,80,80,.9)!important;
 

	
 
  font: normal bold 1.2em sans-serif;
 
}
 

	
 
.course-title-entry
 
{
 
    width: 80%;
 
}
 

	
 
.tr-slot-id-hidden
 
{
 
  display: none;
 
}
 

	
 
.inPlace {
 
  color: #000;
 
  border: none;
 
  background: transparent;
 
}
 

	
 
.warning
 
{
 
  background: #ffffdd;
 
  border: 1pt solid yellow;
 
}
0 comments (0 inline, 0 general)