]> git.mjollnir.org Git - moodle.git/commitdiff
Automatic grading Google Summer of Code project (Epaile assignment module) by Arkaitz...
authornicolasconnault <nicolasconnault>
Wed, 18 Jul 2007 21:13:53 +0000 (21:13 +0000)
committernicolasconnault <nicolasconnault>
Wed, 18 Jul 2007 21:13:53 +0000 (21:13 +0000)
33 files changed:
lang/en_utf8/assignment.php
mod/assignment/config.html
mod/assignment/type/program/assignment.class.php [new file with mode: 0644]
mod/assignment/type/program/db/mysql.sql [new file with mode: 0644]
mod/assignment/type/program/js/tests.js [new file with mode: 0644]
mod/assignment/type/program/languages/bash.sh [new file with mode: 0644]
mod/assignment/type/program/languages/c.sh [new file with mode: 0644]
mod/assignment/type/program/languages/cpp.sh [new file with mode: 0644]
mod/assignment/type/program/languages/haskell.sh [new file with mode: 0644]
mod/assignment/type/program/languages/java.sh [new file with mode: 0644]
mod/assignment/type/program/languages/pascal.sh [new file with mode: 0644]
mod/assignment/type/program/languages/perl.sh [new file with mode: 0644]
mod/assignment/type/program/mod.html [new file with mode: 0644]
mod/assignment/type/program/source.html [new file with mode: 0644]
mod/assignment/type/program/source.php [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Scripts/clipboard.swf [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushCSharp.js [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushCpp.js [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushCss.js [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushDelphi.js [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushJScript.js [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushJava.js [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushPhp.js [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushPython.js [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushRuby.js [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushSql.js [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushVb.js [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushXml.js [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Scripts/shCore.js [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Scripts/shCore.uncompressed.js [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Styles/SyntaxHighlighter.css [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Styles/TestPages.css [new file with mode: 0644]
mod/assignment/type/program/syntaxhighlighter/Templates/Test.dwt [new file with mode: 0644]

index 8af0a8c405a8d00c5c85b7a01353b7470325d831..c68d79217a33bf2f7214c261fcfb1821cd53ad5e 100644 (file)
@@ -1,7 +1,7 @@
 <?PHP // $Id$ 
       // assignment.php - created with Moodle 1.7 beta + (2006101003)
 
-
+$string['addtest'] = 'Add new test';
 $string['allowdeleting'] = 'Allow deleting';
 $string['allowmaxfiles'] = 'Maximum number of uploaded files';
 $string['allownotes'] = 'Allow notes';
@@ -10,6 +10,7 @@ $string['assignment:grade'] = 'Grade assignment';
 $string['assignment:submit'] = 'Submit assignment';
 $string['assignment:view'] = 'View assignment';
 $string['assignmentdetails'] = 'Assignment details';
+$string['assignmentlangs'] = 'Programming language';
 $string['assignmentmail'] = '$a->teacher has posted some feedback on your
 assignment submission for \'$a->assignment\'
 
@@ -24,9 +25,13 @@ $string['assignmenttype'] = 'Assignment type';
 $string['availabledate'] = 'Available from';
 $string['comment'] = 'Comment';
 $string['commentinline'] = 'Comment inline';
+$string['compileerrors'] = 'Compile errors';
 $string['configitemstocount'] = 'Nature of items to be counted for student submissions in online assignments.';
 $string['configmaxbytes'] = 'Default maximum assignment size for all assignments on the site (subject to course limits and other local settings)';
+$string['configmaxcpu'] = 'Default maximum assignment cpu time for all assignments on the site (subject to other local settings)';
+$string['configmaxmem'] = 'Default maximum assignment memory usage for all assignments on the site (subject to other local settings)';
 $string['confirmdeletefile'] = 'Are you absolutely sure you want to delete this file?<br /><strong>$a</strong>';
+$string['crondate'] = 'Cron date';
 $string['deletefilefailed'] = 'Deleting of file failed.';
 $string['description'] = 'Description';
 $string['draft'] = 'Draft';
@@ -46,6 +51,7 @@ for <i>\'$a->assignment\'</i><br /><br />
 It is <a href=\"$a->url\">available on the web site</a>.';
 $string['emailteachers'] = 'Email alerts to teachers';
 $string['emptysubmission'] = 'You have not submitted anything yet';
+$string['exactouput'] = 'Exact output';
 $string['existingfiledeleted'] = 'Existing file has been deleted: $a';
 $string['failedupdatefeedback'] = 'Failed to update submission feedback for user $a';
 $string['feedback'] = 'Feedback';
@@ -73,9 +79,23 @@ $string['helpuploadsingle'] = '<p>This type of assignment allows each participan
    single file, of any type.</p> <p>This might be a Word processor document, an image, 
    a zipped web site, or anything you ask them to submit.</p>';
 $string['hideintro'] = 'Hide description before available date';
+$string['ignorespace'] = 'Ignore spaces';
+$string['ignorecase'] = 'Ignore Upcase / Lowcase';
+$string['input'] = 'Input';
+$string['langbash'] = 'Bash';
+$string['langc'] = 'C';
+$string['langcpp'] = 'C++';
+$string['langhaskell'] = 'Haskell';
+$string['langjava'] = 'Java';
+$string['langpascal'] = 'Pascal';
+$string['langperl'] = 'Perl';
 $string['late'] = '$a late';
+$string['loading'] = 'Grade process in progress... please wait';
+$string['maximumcpu'] = 'Maximum CPU time (seconds)';
 $string['maximumgrade'] = 'Maximum grade';
+$string['maximummem'] = 'Maximum memory usage';
 $string['maximumsize'] = 'Maximum size';
+$string['maximumfilesize'] = 'Maximum source file size';
 $string['modulename'] = 'Assignment';
 $string['modulenameplural'] = 'Assignments';
 $string['newsubmissions'] = 'Assignments submitted';
@@ -90,12 +110,15 @@ $string['notesupdateerror'] = 'Error when updating notes';
 $string['notgradedyet'] = 'Not graded yet';
 $string['notsubmittedyet'] = 'Not submitted yet';
 $string['onceassignmentsent'] = 'Once the assignment is sent for marking, you will no longer be able to delete or attach file(s). Do you want to continue?';
+$string['output'] = 'Output';
+$string['outputfilter'] = 'Output filter';
 $string['overwritewarning'] = 'Warning: uploading again will REPLACE your current submission';
 $string['pagesize'] = 'Submissions shown per page';
 $string['preventlate'] = 'Prevent late submissions';
 $string['quickgrade'] = 'Allow quick grading';
 $string['responsefiles'] = 'Response files';
 $string['reviewed'] = 'Reviewed';
+$string['runtimeerrors'] = 'Runtime errors';
 $string['saveallfeedback'] = 'Save all my feedback';
 $string['sendformarking'] = 'Send for marking';
 $string['submission'] = 'Submission';
@@ -109,8 +132,10 @@ $string['submitedformarking'] = 'Assignment was already submitted for marking an
 $string['submitformarking'] = 'Final submission for assignment marking';
 $string['submitted'] = 'Submitted';
 $string['submittedfiles'] = 'Submitted files';
+$string['tests'] = 'Program tests';
 $string['typeoffline'] = 'Offline activity';
 $string['typeonline'] = 'Online text';
+$string['typeprogram'] = 'Epaile';
 $string['typeupload'] = 'Advanced uploading of files';
 $string['typeuploadsingle'] = 'Upload a single file';
 $string['unfinalize'] = 'Revert to draft';
index 6eedcdff2d050449e4346fd7e59572d9ca78f158..0f55ba3b7fcd95977c66dcb090fbd8f42779b750 100644 (file)
     </td>
 </tr>
 
+<tr>
+    <td align="right">assignment_maxcpu:</td>
+    <td>
+    <?php
+        /**
+         * TODO: create a function in moodlelib.php.
+         * Actually present in /mod/assignment/type/program/assignment.class.php -> assignment_program_get_max_cpu_times
+         */
+        require_once $CFG->dirroot.'/mod/assignment/type/program/assignment.class.php';
+        DEFINE('MAX_CPU',60);
+        
+        unset($choices);
+        $choices = assignment_program_get_max_cpu_times(MAX_CPU);
+        choose_from_menu($choices, "assignment_maxcpu", $CFG->assignment_maxcpu, "");
+    ?>
+    </td>
+    <td>
+        <?php print_string("configmaxcpu", "assignment") ?>
+    </td>
+</tr>
+
+<tr>
+    <td align="right">assignment_maxmem:</td>
+    <td>
+    <?php
+        /**
+         * TODO: create a function in moodlelib.php.
+         * Actually present in /mod/assignment/type/program/assignment.class.php -> assignment_program_get_max_mem_usages
+         */
+        DEFINE('MAX_MEM',10485760);
+         
+        unset($choices);
+        $choices = assignment_program_get_max_memory_usages(MAX_MEM);
+        choose_from_menu($choices, "assignment_maxmem", $CFG->assignment_maxmem, "");
+    ?>
+    </td>
+    <td>
+        <?php print_string("configmaxmem", "assignment") ?>
+    </td>
+</tr>
+
 <tr>
     <td colspan="3" align="center">
     <input type="submit" value="<?php print_string("savechanges") ?>" /></td>
diff --git a/mod/assignment/type/program/assignment.class.php b/mod/assignment/type/program/assignment.class.php
new file mode 100644 (file)
index 0000000..1682fec
--- /dev/null
@@ -0,0 +1,1127 @@
+<?php
+// $Id$
+
+define('ASSIGNMENT_STATUS_SUBMITTED', 'submitted');
+define('NUMTESTS', 4); // Default number of tests
+
+/**
+ * Extends the base assignment class
+ *
+ * @author Arkaitz Garro
+ * @version 0.1
+ */
+class assignment_program extends assignment_base {
+
+    function assignment_program($cmid = 0) {
+        parent::    assignment_base($cmid);
+    }
+
+    function setup_elements(& $mform) {
+        global $CFG, $COURSE;
+
+        $add           = optional_param('add', '', PARAM_ALPHA);
+        $update        = optional_param('update', 0, PARAM_INT);
+
+        $ynoptions = array(0 => get_string('no'), 1 => get_string('yes'));
+
+        // Programming languages
+        $choices = assignment_program_languages();
+        $mform->addElement('select', 'lang', get_string("assignmentlangs", "assignment"), $choices);
+        $mform->setHelpButton('lang', array('lang',get_string('assignmentlangs','assignment'),'assignment'));
+        $mform->setDefault('lang', 'java');
+
+        // Cron date
+        $mform->addElement('date_time_selector', 'var1', get_string('crondate', 'assignment'), array('optional' => true));
+        $mform->setHelpButton('var1', array('timecron',get_string('crondate','assignment'), 'assignment'));
+        $mform->disabledIf('var1', 'var1', 'eq', 0);
+        $mform->setDefault('var1', time() + 7 * 24 * 3600);
+
+        // Max. CPU time
+        unset($choices);
+        $choices = $this->get_max_cpu_times($CFG->assignment_maxcpu);
+        $mform->addElement('select', 'var2', get_string('maximumcpu', 'assignment'), $choices);
+        $mform->setHelpButton('var2', array('maximumcpu',get_string('maximumcpu','assignment'), 'assignment'));
+        $mform->setDefault('var2', $CFG->assignment_maxcpu);
+
+        // Max. memory usage
+        unset($choices);
+        $choices = $this->get_max_memory_usages($CFG->assignment_maxmem);
+        $mform->addElement('select', 'var3', get_string('maximummem', 'assignment'), $choices);
+        $mform->setHelpButton('var3', array('maximummem',get_string('maximummem','assignment'), 'assignment'));
+        $mform->setDefault('var3', $CFG->assignment_maxmem);
+
+        // Allow resubmit
+        $mform->addElement('select', 'resubmit', get_string("allowresubmit", "assignment"), $ynoptions);
+        $mform->setHelpButton('resubmit', array('resubmit',get_string('allowresubmit','assignment'), 'assignment'));
+        $mform->setDefault('resubmit', 0);
+
+        // Email teachers
+        $mform->addElement('select', 'emailteachers', get_string("emailteachers", "assignment"), $ynoptions);
+        $mform->setHelpButton('emailteachers', array('emailteachers',get_string('emailteachers','assignment'), 'assignment'));
+        $mform->setDefault('emailteachers', 0);
+
+        // Submission max bytes
+        $choices = get_max_upload_sizes($CFG->maxbytes, $COURSE->maxbytes);
+        $choices[1] = get_string('uploadnotallowed');
+        $choices[0] = get_string('courseuploadlimit') . ' (' . display_size($COURSE->maxbytes) . ')';
+        $mform->addElement('select', 'maxbytes', get_string('maximumfilesize', 'assignment'), $choices);
+        $mform->setDefault('maxbytes', $CFG->assignment_maxbytes);
+
+        // Tests form
+        $mform->addElement('header', 'tests', get_string('tests', 'assignment'));
+
+        // Output filter
+        unset ($choices);
+        $choices[1] = get_string('ignorespace', 'assignment');
+        $choices[2] = get_string('ignorecase', 'assignment');
+        $choices[3] = get_string('exactouput', 'assignment');
+        $mform->addElement('select', 'var4', get_string('outputfilter', 'assignment'), $choices);
+        $mform->setHelpButton('var4', array('outputfilter',get_string('outputfilter','assignment'), 'assignment'));
+
+        // Get course module instance
+        $cm = new Object();
+        if (!empty($update)) {
+            $cm = get_record("course_modules", "id", $update);
+        }
+
+        // Get tests data
+        $tests = array ();
+        $numtests = $this->get_tests($cm, $tests);
+        if ($tests) {
+            // Tests allready defined (update assignment)
+            $i = 1;
+            foreach ($tests as $tstObj => $tstValue) {
+                $mform->addElement('text', "input[$i]", get_string('input', 'assignment') . $i);
+                $mform->setDefault("input[$i]",$tstValue->input);
+                $mform->addElement('text', "output[$i]", get_string('output', 'assignment') . $i);
+                $mform->setDefault("output[$i]",$tstValue->output);
+
+                $i++;
+            }
+        } else {
+            // New assignment
+            for ($i = 1; $i <= $numtests; $i++) {
+                $mform->addElement('text', "input[$i]", get_string('input', 'assignment') . $i);
+                $mform->addElement('text', "output[$i]", get_string('output', 'assignment') . $i);
+            }
+        }
+    }
+
+    /**
+     * Create a new program type assignment activity
+     *
+     * Given an object containing all the necessary data,
+     * (defined by the form in mod.html) this function
+     * will create a new instance and return the id number
+     * of the new instance.
+     * The due data is added to the calendar
+     * Tests are added to assignment_epaile_tests table
+     *
+     * @param $assignment object The data from the form on mod.html
+     * @return int The id of the assignment
+     */
+    function add_instance($assignment) {
+        // Add assignment instance
+        $assignment->id = parent::add_instance($assignment);
+        if ($assignment->id) {
+            $this->after_add_update($assignment);
+        }
+
+        return $assignment->id;
+    }
+
+    /**
+     * Updates a program assignment activity
+     *
+     * Given an object containing all the necessary data,
+     * (defined by the form in mod.html) this function
+     * will update the assignment instance and return the id number
+     * The due date is updated in the calendar
+     *
+     * @param $assignment object The data from the form on mod.html
+     * @return int The assignment id
+     */
+
+    function update_instance($assignment) {
+        // Add assignment instance
+        $returnid = parent::update_instance($assignment);
+        if ($returnid) {
+            $this->after_add_update($assignment);
+        }
+
+        return $returnid;
+    }
+
+    /**
+     * Deletes a program assignment activity
+     *
+     * Deletes all database records, files and calendar events for this assignment.
+     * @param $assignment object The assignment to be deleted
+     * @return boolean False indicates error
+     */
+    function delete_instance($assignment) {
+        global $CFG;
+
+        // DELETE submissions results
+        $sql = 'test IN (SELECT id FROM '.$CFG->prefix.'assignment_epaile_tests WHERE assignment='.$assignment->id.')';
+        if (!delete_records_select('assignment_epaile_results', $sql)) {
+            return false;
+        }
+
+        // DELETE submissions
+        $sql = 'submission IN (SELECT id FROM '.$CFG->prefix.'assignment_submissions WHERE assignment='.$assignment->id.')';
+        if (!delete_records_select('assignment_epaile_submissions', $sql)) {
+            return false;
+        }
+
+        // DELETE tests
+        if (!delete_records('assignment_epaile_tests', 'assignment', $assignment->id)) {
+            return false;
+        }
+
+        $result = parent::delete_instance($assignment);
+
+        return $result;
+    }
+
+    /**
+    * This function is called at the end of add_instance
+    * and update_instance, to add or update tests
+    *
+    * @param object $assignment the epaile object.
+    */
+    function after_add_update($assignment) {
+        // Count real input/output (not empty tests)
+        $assignment->numtests = count($assignment->input);
+
+        // Delete actual tests
+        delete_records('assignment_epaile_tests', 'assignment', $assignment->id);
+
+        // Insert new tests
+        for ($i = 0; $i < $assignment->numtests; $i++) {
+            // Check if tests is not empty
+            if(!empty($assignment->input[$i+1]) && !empty($assignment->output[$i+1])) {
+                $test = new Object();
+                $test->assignment = $assignment->id;
+                $test->input = $assignment->input[$i+1];
+                $test->output = $assignment->output[$i+1];
+
+                if (!insert_record('assignment_epaile_tests', $test)) {
+                    return get_string('notestinsert', 'assignment');
+                }
+
+                unset ($test);
+            }
+        }
+    }
+
+    /**
+     * Get tests data for current assignment
+     *
+     * @param $instanceid int Instance ID
+     * @param $tests object The object used to fill the tests
+     *
+     * @return $numtests Number of tests
+     */
+    function get_tests($cm, & $tests) {
+        if (isset ($cm->instance))
+            $tests = get_records('assignment_epaile_tests', 'assignment', $cm->instance, 'id ASC');
+
+        if (empty ($tests)) {
+            $numtests = NUMTESTS;
+        } else {
+            $numtests = count($tests);
+        }
+        return $numtests;
+    }
+
+    /**
+     * Get compile time / errors
+     *
+     * @param $submissionid int Submission id
+     *
+     * @return $comp Array
+     */
+    function get_compile($submissionid) {
+        $comp = array ();
+        if ($submissionid)
+            $comp = get_record('assignment_epaile_submissions', 'submission', $submissionid);
+
+        return $comp;
+    }
+
+    /**
+     * Get tests results for current submission
+     *
+     * @param $submissionid int Submission id
+     *
+     * @return $res Array
+     */
+    function get_results($submissionid) {
+        $res = array ();
+        if ($submissionid)
+            $res = get_records('assignment_epaile_results', 'submission', $submissionid);
+
+        return $res;
+    }
+
+    /**
+     * Get number of errors in tests
+     *
+     * @param $submissionid int Submission id
+     *
+     * @return $num int
+     */
+    function get_errors_number($submissionid) {
+        global $CFG;
+
+        $num = 0;
+        if($submissionid) {
+            $sql  = "SELECT COUNT(id) AS num FROM ".$CFG->prefix."assignment_epaile_results";
+            $sql .= " WHERE submission=$submissionid AND error<>''";
+
+            if (($res = get_record_sql($sql)) !== false) {
+                $num = $res->num;
+            }
+        }
+        return $num;
+    }
+
+    /**
+     * View assignment details.
+     */
+    function view() {
+        global $USER;
+
+        $context = get_context_instance(CONTEXT_MODULE, $this->cm->id);
+
+        require_capability('mod/assignment:view', $context);
+        add_to_log($this->course->id, "assignment", "view", "view.php?id={$this->cm->id}", $this->assignment->id, $this->cm->id);
+
+        $this->view_header();
+        $this->view_intro();
+        $this->view_lang();
+        $this->view_dates();
+        $filecount = $this->count_user_files($USER->id);
+
+        if ($submission = $this->get_submission()) {
+            if ($submission->timemarked) {
+                $this->view_feedback();
+            }
+            if ($filecount) {
+                print_simple_box($this->print_user_files($USER->id, true), 'center');
+            }
+        }
+        if (has_capability('mod/assignment:submit', $context) && $this->isopen() && (!$filecount || $this->assignment->resubmit || !$submission->timemarked)) {
+            $this->view_upload_form();
+        }
+        $this->view_footer();
+    }
+
+    /**
+     * Upload file
+     */
+    function upload() {
+        global $CFG, $USER;
+        require_capability('mod/assignment:submit', get_context_instance(CONTEXT_MODULE, $this->cm->id));
+        $this->view_header(get_string('upload'));
+        $filecount = $this->count_user_files($USER->id);
+        $submission = $this->get_submission($USER->id);
+        if ($this->isopen() && (!$filecount || $this->assignment->resubmit || !$submission->timemarked)) {
+            if ($submission = $this->get_submission($USER->id)) {
+                if (($submission->grade >= 0) and !$this->assignment->resubmit) {
+                    notify(get_string('alreadygraded', 'assignment'));
+                }
+            }
+            $dir = $this->file_area_name($USER->id);
+            require_once ($CFG->dirroot . '/lib/uploadlib.php');
+            $um = new upload_manager('newfile', true, false, $this->course, false, $this->assignment->maxbytes);
+            if ($um->process_file_uploads($dir)) {
+                $newfile_name = $um->get_new_filename();
+                if ($submission) {
+                    $submission->timemodified = time();
+                    $submission->numfiles = 1;
+                    $submission->submissioncomment = addslashes($submission->submissioncomment);
+                    unset ($submission->data1); // Don't need to update this.
+                    unset ($submission->data2); // Don't need to update this.
+                    if (update_record("assignment_submissions", $submission)) {
+                        add_to_log($this->course->id, 'assignment', 'upload', 'view.php?a='
+                            . $this->assignment->id, $this->assignment->id, $this->cm->id);
+                        $this->email_teachers($submission);
+                        print_heading(get_string('uploadedfile'));
+                    } else {
+                        notify(get_string("uploadfailnoupdate", "assignment"));
+                    }
+                    print_continue('view.php?id=' . $this->cm->id);
+                } else {
+                    $newsubmission = $this->prepare_new_submission($USER->id);
+                    $newsubmission->timemodified = time();
+                    $newsubmission->numfiles = 1;
+                    if (insert_record('assignment_submissions', $newsubmission)) {
+                        add_to_log($this->course->id, 'assignment', 'upload', 'view.php?a='
+                            . $this->assignment->id, $this->assignment->id, $this->cm->id);
+                        $this->email_teachers($newsubmission);
+                        print_heading(get_string('uploadedfile'));
+                        /*******************************************************
+                        **  Automated grade test                              **
+                        ********************************************************/
+                        $this->print_loading();
+                        $submission = $this->get_submission($USER->id);
+                        chdir($CFG->dataroot . '/' . $dir);
+                        $cmd = "javac " . $um->get_new_filename() . " > cerrors 2>&1";
+                        exec($cmd, $exit, $retval);
+                        // Compile errors
+                        if ($retval) {
+                            // Read compile errors
+                            $gestor = fopen($CFG->dataroot . '/' . $dir . '/cerrors', 'r');
+                            $errors = fread($gestor, filesize($CFG->dataroot . '/' . $dir . '/cerrors'));
+                            fclose($gestor);
+                            // Delete compile errors file
+                            unlink($CFG->dataroot . '/' . $dir . '/cerrors');
+                            // Update submission
+                            $submission = $this->get_submission($USER->id);
+                            $submission->compileerrors = addslashes($errors);
+                        }
+                        $submission->submission = $submission->id;
+                        $submission->runtime = 10;
+                        if (!insert_record("assignment_epaile_submissions", $submission))
+                            error(get_string('gradeerror', 'assignment'));
+                        #print_continue('view.php?id='.$this->cm->id);
+                    } else {
+                        notify(get_string("uploadnotregistered", "assignment", $newfile_name));
+                        print_continue('view.php?id=' . $this->cm->id);
+                    }
+                }
+            }
+        } else {
+            notify(get_string("uploaderror", "assignment")); //submitting not allowed!
+            print_continue('view.php?id=' . $this->cm->id);
+        }
+        $this->view_footer();
+    }
+
+    function view_upload_form() {
+        global $CFG;
+        $struploadafile = get_string("uploadafile");
+        $strmaxsize = get_string("maxsize", "", display_size($this->assignment->maxbytes));
+        echo '<center>';
+        echo '<form enctype="multipart/form-data" method="post" ' .        "action=\"$CFG->wwwroot/mod/assignment/upload.php\">";
+        echo "<p>$struploadafile ($strmaxsize)</p>";
+        echo '<input type="hidden" name="id" value="' . $this->cm->id . '" />';
+        require_once ($CFG->libdir . '/uploadlib.php');
+        upload_print_form_fragment(1, array (
+            'newfile'
+        ), false, null, 0, $this->assignment->maxbytes, false);
+        echo '<input type="submit" name="save" value="' . get_string('uploadthisfile') . '" />';
+        echo '</form>';
+        echo '</center>';
+    }
+
+    function print_student_answer($userid, $return = false) {
+        global $CFG, $USER;
+        $filearea = $this->file_area_name($userid);
+        $output = '';
+        if ($basedir = $this->file_area($userid)) {
+            if ($files = get_directory_list($basedir)) {
+                foreach ($files as $key => $file) {
+                    require_once ($CFG->libdir . '/filelib.php');
+                    $icon = mimeinfo('icon', $file);
+                    if ($CFG->slasharguments) {
+                        $ffurl = "$CFG->wwwroot/file.php/$filearea/$file";
+                    } else {
+                        $ffurl = "$CFG->wwwroot/file.php?file=/$filearea/$file";
+                    }
+                    //died right here
+                    //require_once($ffurl);
+                    #$output = '<img align="middle" src="'.$CFG->pixpath.'/f/'.$icon.'" height="16" width="16" alt="'.$icon.'" />'.
+                    #        '<a href="'.$ffurl.'" >'.$file.'</a><br />';
+                    $output = link_to_popup_window('/mod/assignment/type/program/source.php?id=' . $this->cm->id . '&amp;userid=' . $userid . '&amp;file=' . $file, $file . ' source code', $file, 710, 780, $file, 'none', true, 'button' . $userid);
+                }
+            }
+        }
+        $output = '<div class="files">' . $output . '</div>';
+        return $output;
+    }
+
+    /**
+     * Produces a list of links to the files uploaded by a user
+     *
+     * @param $userid int optional id of the user. If 0 then $USER->id is used.
+     * @param $return boolean optional defaults to false. If true the list is returned rather than printed
+     * @return string optional
+     */
+    function print_user_files($userid = 0, $return = false) {
+        global $CFG, $USER;
+        if (!$userid) {
+            if (!isloggedin()) {
+                return '';
+            }
+            $userid = $USER->id;
+        }
+        $filearea = $this->file_area_name($userid);
+        $output = '';
+        if ($basedir = $this->file_area($userid)) {
+            if ($files = get_directory_list($basedir)) {
+                require_once ($CFG->libdir . '/filelib.php');
+                foreach ($files as $key => $file) {
+                    $icon = mimeinfo('icon', $file);
+                    if ($CFG->slasharguments) {
+                        $ffurl = "$CFG->wwwroot/file.php/$filearea/$file";
+                    } else {
+                        $ffurl = "$CFG->wwwroot/file.php?file=/$filearea/$file";
+                    }
+                    #$output .= '<img align="middle" src="'.$CFG->pixpath.'/f/'.$icon.'" height="16" width="16" alt="'.$icon.'" />'.
+                    #        '<a href="'.$ffurl.'" >'.$file.'</a><br />';
+                    // Syntax Highlighert source code
+                    $output = link_to_popup_window('/mod/assignment/type/program/source.php?id=' . $this->cm->id . '&amp;userid=' . $userid . '&amp;file=' . $file, $file . ' source code', $file, 710, 780, $file, 'none', true, 'button' . $userid);
+                }
+            }
+        }
+        $output = '<div class="files">' . $output . '</div>';
+        if ($return) {
+            return $output;
+        }
+        echo $output;
+    }
+
+    /**
+     * Return the programming language of this instance
+     *
+     * @return string Programming language
+     */
+    function get_language() {
+        $lang = '';
+        if ($ass = get_record('assignment', 'id', $this->cm->instance))
+            $lang = $ass->lang;
+        return $lang;
+    }
+
+    /**
+     * Display programming language for this assignment
+     */
+    function view_lang() {
+        $lang = $this->get_language();
+        if (!empty ($lang)) {
+            print_simple_box_start('center', '', '', 0, 'generalbox', 'dates');
+            $formatoptions = new stdClass;
+            $formatoptions->noclean = true;
+            echo format_text('<strong>Programing language:</strong> ' . get_string('lang' . $lang, 'assignment'), $this->assignment->format, $formatoptions);
+            print_simple_box_end();
+        }
+    }
+
+    /**
+     * Print grade progress
+     */
+    function print_loading() {
+        global $CFG;
+        print_simple_box_start('center', '30%', '', '', 'generalbox', 'intro');
+        echo '<p align="center">' . get_string('loading', 'assignment') . '</p>';
+        echo '<p align="center"><img src="' . $CFG->pixpath . '/t/loading.gif" alt="Loading" width="20" /></p>';
+        print_simple_box_end();
+    }
+
+    /**
+     *  Display all the submissions ready for grading
+     */
+    function display_submissions() {
+        global $CFG, $db, $USER;
+
+        /* first we check to see if the form has just been submitted
+         * to request user_preference updates
+         */
+        if (isset ($_POST['updatepref'])) {
+            $perpage = optional_param('perpage', 10, PARAM_INT);
+            $perpage = ($perpage <= 0) ? 10 : $perpage;
+            set_user_preference('assignment_perpage', $perpage);
+            set_user_preference('assignment_quickgrade', optional_param('quickgrade', 0, PARAM_BOOL));
+        }
+
+        /* next we get perpage and quickgrade (allow quick grade) params
+         * from database
+         */
+        $perpage = get_user_preferences('assignment_perpage', 10);
+        $quickgrade = get_user_preferences('assignment_quickgrade', 0);
+        $teacherattempts = true; /// Temporary measure
+        $page = optional_param('page', 0, PARAM_INT);
+        $strsaveallfeedback = get_string('saveallfeedback', 'assignment');
+
+        // Some shortcuts to make the code read better
+        $course = $this->course;
+        $assignment = $this->assignment;
+        $cm = $this->cm;
+        $tabindex = 1; //tabindex for quick grading tabbing; Not working for dropdowns yet
+
+        add_to_log($course->id, 'assignment', 'view submission', 'submissions.php?id=' . $this->assignment->id, $this->assignment->id, $this->cm->id);
+
+        print_header_simple(format_string($this->assignment->name, true), "", '<a href="index.php?id=' . $course->id . '">' . $this->strassignments . '</a> -> <a href="view.php?a=' . $this->assignment->id . '">' . format_string($this->assignment->name, true) . '</a> -> ' . $this->strsubmissions, '', '', true, update_module_button($cm->id, $course->id, $this->strassignment), navmenu($course, $cm));
+
+        // Position swapped
+        if ($groupmode = groupmode($course, $cm)) { // Groups are being used
+            $currentgroup = setup_and_print_groups($course, $groupmode, 'submissions.php?id=' . $this->cm->id);
+        } else {
+            $currentgroup = false;
+        }
+
+        // Get all teachers and students
+        if ($currentgroup) {
+            $users = get_group_users($currentgroup);
+        } else {
+            $context = get_context_instance(CONTEXT_MODULE, $cm->id);
+            $users = get_users_by_capability($context, 'mod/assignment:submit'); // everyone with this capability set to non-prohibit
+        }
+
+        $tablecolumns = array (
+            'picture',
+            'fullname',
+            'grade',
+            'compileerrors',
+            'runtimeerrors',
+            'submissioncomment',
+            'timemodified',
+            'timemarked',
+            'status'
+        );
+
+        $tableheaders = array (
+            '',
+            get_string('fullname'
+        ), get_string('grade'), get_string('compileerrors', 'assignment'), get_string('runtimeerrors', 'assignment'), get_string('comment', 'assignment'), get_string('lastmodified') . ' (' . $course->student . ')', get_string('lastmodified') . ' (' . $course->teacher . ')', get_string('status'));
+
+        require_once ($CFG->libdir . '/tablelib.php');
+        $table = new flexible_table('mod-assignment-submissions');
+        $table->define_columns($tablecolumns);
+        $table->define_headers($tableheaders);
+        $table->define_baseurl($CFG->wwwroot . '/mod/assignment/submissions.php?id=' . $this->cm->id . '&amp;currentgroup=' . $currentgroup);
+        $table->sortable(true, 'lastname'); //sorted by lastname by default
+        $table->collapsible(true);
+        $table->initialbars(true);
+        $table->column_suppress('picture');
+        $table->column_suppress('fullname');
+        $table->column_class('picture', 'picture');
+        $table->column_class('fullname', 'fullname');
+        $table->column_class('grade', 'grade');
+        $table->column_class('compileerrors', 'comment');
+        $table->column_class('runtimeerrors', 'comment');
+        $table->column_class('submissioncomment', 'comment');
+        $table->column_class('timemodified', 'timemodified');
+        $table->column_class('timemarked', 'timemarked');
+        $table->column_class('status', 'status');
+        $table->set_attribute('cellspacing', '0');
+        $table->set_attribute('id', 'attempts');
+        $table->set_attribute('class', 'submissions');
+        $table->set_attribute('width', '90%');
+        $table->set_attribute('align', 'center');
+
+        // Start working -- this is necessary as soon as the niceties are over
+        $table->setup();
+
+        /// Check to see if groups are being used in this assignment
+        if (!$teacherattempts) {
+            $teachers = get_course_teachers($course->id);
+            if (!empty ($teachers)) {
+                $keys = array_keys($teachers);
+            }
+            foreach ($keys as $key) {
+                unset ($users[$key]);
+            }
+        }
+
+        if (empty ($users)) {
+            print_heading(get_string('noattempts', 'assignment'));
+            return true;
+        }
+
+        /// Construct the SQL
+        if ($where = $table->get_sql_where()) {
+            $where .= ' AND ';
+        }
+
+        if ($sort = $table->get_sql_sort()) {
+            $sort = ' ORDER BY ' . $sort;
+        }
+
+        $select = 'SELECT u.id, u.firstname, u.lastname, u.picture,
+                                  s.id AS submissionid, s.grade, s.submissioncomment, se.compileerrors,
+                                  s.timemodified, s.timemarked ';
+        $sql = 'FROM ' . $CFG->prefix . 'user u ' .        'LEFT JOIN ' . $CFG->prefix . 'assignment_submissions s ON u.id = s.userid
+                                                                          AND s.assignment = ' . $this->assignment->id . ' ' .        'LEFT JOIN ' . $CFG->prefix . 'assignment_epaile_submissions se ON s.id = se.submission ' .        'WHERE ' . $where . 'u.id IN (' . implode(',', array_keys($users)) . ') ';
+        $table->pagesize($perpage, count($users));
+
+        ///offset used to calculate index of student in that particular query, needed for the pop up to know who's next
+        $offset = $page * $perpage;
+        $strupdate = get_string('update');
+        $strgrade = get_string('grade');
+        $grademenu = make_grades_menu($this->assignment->grade);
+
+        if (($ausers = get_records_sql($select . $sql . $sort, $table->get_page_start(), $table->get_page_size())) !== false) {
+            foreach ($ausers as $auser) {
+                /// Calculate user status
+                $auser->status = ($auser->timemarked > 0) && ($auser->timemarked >= $auser->timemodified);
+                $picture = print_user_picture($auser->id, $course->id, $auser->picture, false, true);
+
+                if (empty ($auser->submissionid)) {
+                    $auser->grade = -1; //no submission yet
+                }
+
+                if (!empty ($auser->submissionid)) {
+                    ///Prints student answer and student modified date
+                    ///attach file or print link to student answer, depending on the type of the assignment.
+                    ///Refer to print_student_answer in inherited classes.
+                    if ($auser->timemodified > 0) {
+                        $studentmodified = '<div id="ts' . $auser->id . '">' . $this->print_student_answer($auser->id) . userdate($auser->timemodified) . '</div>';
+                    } else {
+                        $studentmodified = '<div id="ts' . $auser->id . '">&nbsp;</div>';
+                    }
+
+                    ///Print grade, dropdown or text
+                    if ($auser->timemarked > 0) {
+                        $teachermodified = '<div id="tt' . $auser->id . '">' . userdate($auser->timemarked) . '</div>';
+                        if ($quickgrade) {
+                            $grade = '<div id="g' . $auser->id . '">' . choose_from_menu(make_grades_menu($this->assignment->grade), 'menu[' .                            $auser->id . ']', $auser->grade, get_string('nograde'), '', -1, true, false, $tabindex++) . '</div>';
+                        } else {
+                            $grade = '<div id="g' . $auser->id . '">' . $this->display_grade($auser->grade) . '</div>';
+                        }
+                    } else {
+                        $teachermodified = '<div id="tt' . $auser->id . '">&nbsp;</div>';
+                        if ($quickgrade) {
+                            $grade = '<div id="g' . $auser->id . '">' . choose_from_menu(make_grades_menu($this->assignment->grade), 'menu[' .                            $auser->id . ']', $auser->grade, get_string('nograde'), '', -1, true, false, $tabindex++) . '</div>';
+                        } else {
+                            $grade = '<div id="g' . $auser->id . '">' . $this->display_grade($auser->grade) . '</div>';
+                        }
+                    }
+
+                    ///Print Comment
+                    if ($quickgrade) {
+                        $comment = '<div id="com' . $auser->id . '"><textarea tabindex="' . $tabindex++ . '" name="submissioncomment[' . $auser->id . ']" id="submissioncomment[' . $auser->id . ']">' . ($auser->submissioncomment) . '</textarea></div>';
+                    } else {
+                        $comment = '<div id="com' . $auser->id . '">' . shorten_text(strip_tags($auser->submissioncomment), 15) . '</div>';
+                    }
+                } else {
+                    $studentmodified = '<div id="ts' . $auser->id . '">&nbsp;</div>';
+                    $teachermodified = '<div id="tt' . $auser->id . '">&nbsp;</div>';
+                    $status = '<div id="st' . $auser->id . '">&nbsp;</div>';
+
+                    if ($quickgrade) { // allow editing
+                        $grade = '<div id="g' . $auser->id . '">' . choose_from_menu(make_grades_menu($this->assignment->grade), 'menu[' .                        $auser->id . ']', $auser->grade, get_string('nograde'), '', -1, true, false, $tabindex++) . '</div>';
+                    } else {
+                        $grade = '<div id="g' . $auser->id . '">-</div>';
+                    }
+
+                    if ($quickgrade) {
+                        $comment = '<div id="com' . $auser->id . '"><textarea tabindex="' . $tabindex++ . '" name="submissioncomment[' . $auser->id . ']" id="submissioncomment[' . $auser->id . ']">' . ($auser->submissioncomment) . '</textarea></div>';
+                    } else {
+                        $comment = '<div id="com' . $auser->id . '">&nbsp;</div>';
+                    }
+                }
+
+                ///Print Compile errors
+                $compileerrors = ($auser->compileerrors) ? 'Yes' : 'No';
+                $compileerrors = '<div id="ce' . $auser->id . '">' . $compileerrors . '</div>';
+
+                ///Print Runtime errors
+                $runtimeerrors = ($this->get_errors_number($auser->submissionid)>0) ? 'Yes' : 'No';
+                $runtimeerrors = '<div id="re' . $auser->id . '">' . $runtimeerrors . '</div>';
+
+                if (empty ($auser->status)) { /// Confirm we have exclusively 0 or 1
+                    $auser->status = 0;
+                } else {
+                    $auser->status = 1;
+                }
+
+                $buttontext = ($auser->status == 1) ? $strupdate : $strgrade;
+
+                ///No more buttons, we use popups ;-).
+                $button = link_to_popup_window('/mod/assignment/submissions.php?id=' . $this->cm->id . '&amp;userid=' . $auser->id . '&amp;mode=single' . '&amp;offset=' . $offset++, 'grade' .                $auser->id, $buttontext, 500, 780, $buttontext, 'none', true, 'button' . $auser->id);
+                $status = '<div id="up' . $auser->id . '" class="s' . $auser->status . '">' . $button . '</div>';
+                $row = array (
+                    $picture,
+                    fullname($auser
+                ), $grade, $compileerrors, $runtimeerrors, $comment, $studentmodified, $teachermodified, $status);
+                $table->add_data($row);
+            }
+        }
+
+        /// Print quickgrade form around the table
+        if ($quickgrade) {
+            echo '<form action="submissions.php" name="fastg" method="post">';
+            echo '<input type="hidden" name="id" value="' . $this->cm->id . '">';
+            echo '<input type="hidden" name="mode" value="fastgrade">';
+            echo '<input type="hidden" name="page" value="' . $page . '">';
+            echo '<p align="center"><input type="submit" name="fastg" value="' . get_string('saveallfeedback', 'assignment') . '" /></p>';
+        }
+
+        $table->print_html(); /// Print the whole table
+
+        if ($quickgrade) {
+            echo '<p align="center"><input type="submit" name="fastg" value="' . get_string('saveallfeedback', 'assignment') . '" /></p>';
+            echo '</form>';
+        }
+
+        /// End of fast grading form
+        /// Mini form for setting user preference
+        echo '<br />';
+        echo '<form name="options" action="submissions.php?id=' . $this->cm->id . '" method="post">';
+        echo '<input type="hidden" id="updatepref" name="updatepref" value="1" />';
+        echo '<table id="optiontable" align="center">';
+        echo '<tr align="right"><td>';
+        echo '<label for="perpage">' . get_string('pagesize', 'assignment') . '</label>';
+        echo ':</td>';
+        echo '<td align="left">';
+        echo '<input type="text" id="perpage" name="perpage" size="1" value="' . $perpage . '" />';
+        helpbutton('pagesize', get_string('pagesize', 'assignment'), 'assignment');
+        echo '</td></tr>';
+        echo '<tr align="right">';
+        echo '<td>';
+        print_string('quickgrade', 'assignment');
+        echo ':</td>';
+        echo '<td align="left">';
+
+        if ($quickgrade) {
+            echo '<input type="checkbox" name="quickgrade" value="1" checked="checked" />';
+        } else {
+            echo '<input type="checkbox" name="quickgrade" value="1" />';
+
+        }
+
+        helpbutton('quickgrade', get_string('quickgrade', 'assignment'), 'assignment') . '</p></div>';
+        echo '</td></tr>';
+        echo '<tr>';
+        echo '<td colspan="2" align="right">';
+        echo '<input type="submit" value="' . get_string('savepreferences') . '" />';
+        echo '</td></tr></table>';
+        echo '</form>';
+
+        ///End of mini form
+        print_footer($this->course);
+    }
+
+    /**
+     *  Display a single submission, ready for grading on a popup window
+     *
+     * This default method prints the teacher info and submissioncomment box at the top and
+     * the student info and submission at the bottom.
+     * This method also fetches the necessary data in order to be able to
+     * provide a "Next submission" button.
+     * Calls preprocess_submission() to give assignment type plug-ins a chance
+     * to process submissions before they are graded
+     * This method gets its arguments from the page parameters userid and offset
+     */
+    function display_submission($extra_javascript = '') {
+        global $CFG;
+
+        $userid = required_param('userid', PARAM_INT);
+        $offset = required_param('offset', PARAM_INT); //offset for where to start looking for student.
+
+        if (!$user = get_record('user', 'id', $userid)) {
+            error('No such user!');
+        }
+
+        if (!$submission = $this->get_submission($user->id)) {
+            $submission = $this->prepare_new_submission($userid);
+        }
+
+        if (isset($submission->id)) {
+            $comp = $this->get_compile($submission->id);
+        }
+
+        if ($submission->timemodified > $submission->timemarked) {
+            $subtype = 'assignmentnew';
+        } else {
+            $subtype = 'assignmentold';
+        }
+
+        /// construct SQL, using current offset to find the data of the next student
+        $course = $this->course;
+        $assignment = $this->assignment;
+        $cm = $this->cm;
+
+        /// Get all teachers and students
+        $currentgroup = get_current_group($course->id);
+        if ($currentgroup) {
+            $users = get_group_users($currentgroup);
+        } else {
+            $users = get_course_users($course->id);
+        }
+
+        $select = 'SELECT u.id, u.firstname, u.lastname, u.picture,
+                                  s.id AS submissionid, s.grade, s.submissioncomment, se.compileerrors,
+                                  s.timemodified, s.timemarked ';
+        $sql = 'FROM ' . $CFG->prefix . 'user u ' .        'LEFT JOIN ' . $CFG->prefix . 'assignment_submissions s ON u.id = s.userid
+                                                                          AND s.assignment = ' . $this->assignment->id . ' ' .        'LEFT JOIN ' . $CFG->prefix . 'assignment_epaile_submissions se ON s.id = se.submission ' .        'WHERE u.id IN (' . implode(',', array_keys($users)) . ') ';
+
+        require_once ($CFG->libdir . '/tablelib.php');
+        if ($sort = flexible_table :: get_sql_sort('mod-assignment-submissions')) {
+            $sort = 'ORDER BY ' . $sort . ' ';
+        }
+
+        $nextid = 0;
+        if (($auser = get_records_sql($select . $sql . $sort, $offset +1, 1)) !== false) {
+            $nextuser = array_shift($auser);
+            /// Calculate user status
+            $nextuser->status = ($nextuser->timemarked > 0) && ($nextuser->timemarked >= $nextuser->timemodified);
+            $nextid = $nextuser->id;
+        }
+
+        print_header(get_string('feedback', 'assignment') . ':' . fullname($user, true) . ':' . format_string($this->assignment->name));
+
+        /// Print any extra javascript needed for saveandnext
+        echo $extra_javascript;
+
+        ///Some javascript to help with setting up >.>
+        echo '<script type="text/javascript">' . "\n";
+        echo 'function setNext(){' . "\n";
+        echo 'document.submitform.mode.value=\'next\';' . "\n";
+        echo 'document.submitform.userid.value="' . $nextid . '";' . "\n";
+        echo '}' . "\n";
+        echo 'function saveNext(){' . "\n";
+        echo 'document.submitform.mode.value=\'saveandnext\';' . "\n";
+        echo 'document.submitform.userid.value="' . $nextid . '";' . "\n";
+        echo 'document.submitform.saveuserid.value="' . $userid . '";' . "\n";
+        echo 'document.submitform.menuindex.value = document.submitform.grade.selectedIndex;' . "\n";
+        echo '}' . "\n";
+        echo '</script>' . "\n";
+        echo '<table cellspacing="0" class="feedback ' . $subtype . '" >';
+
+        ///Start of student info row
+        echo '<tr>';
+        echo '<td width="35" valign="top" class="picture teacher">';
+        print_user_picture($user->id, $this->course->id, $user->picture);
+        echo '</td>';
+        echo '<td class="topic">';
+        echo '<div class="from">';
+        echo '<div class="fullname">' . fullname($user, true) . '</div>';
+
+        if ($submission->timemodified) {
+            echo '<div class="time">' . userdate($submission->timemodified) .            $this->display_lateness($submission->timemodified) . '</div>';
+        }
+        echo '</div>';
+
+        $this->print_user_files($user->id);
+        if(isset($comp))
+            $this->print_user_errors($user->id,$comp->compileerrors, '');
+        echo '</td>';
+        echo '</tr>';
+        ///End of student info row
+
+        ///Start of teacher info row
+        echo '<tr>';
+        echo '<td width="35" valign="top" class="picture teacher">';
+        if ($submission->teacher) {
+            $teacher = get_record('user', 'id', $submission->teacher);
+        } else {
+            global $USER;
+            $teacher = $USER;
+        }
+        print_user_picture($teacher->id, $this->course->id, $teacher->picture);
+        echo '<td class="content">';
+        echo '<form name="submitform" action="submissions.php" method="post">';
+        echo '<input type="hidden" name="offset" value="' . ++ $offset . '">';
+        echo '<input type="hidden" name="userid" value="' . $userid . '" />';
+        echo '<input type="hidden" name="id" value="' . $this->cm->id . '" />';
+        echo '<input type="hidden" name="mode" value="grade" />';
+        echo '<input type="hidden" name="menuindex" value="0" />'; //selected menu index
+        //new hidden field, initialized to -1.
+        echo '<input type="hidden" name="saveuserid" value="-1" />';
+        if ($submission->timemarked) {
+            echo '<div class="from">';
+            echo '<div class="fullname">' . fullname($teacher, true) . '</div>';
+            echo '<div class="time">' . userdate($submission->timemarked) . '</div>';
+            echo '</div>';
+        }
+        echo '<div class="grade">' . get_string('grade') . ':';
+        choose_from_menu(make_grades_menu($this->assignment->grade), 'grade', $submission->grade, get_string('nograde'), '', -1);
+        echo '</div>';
+        echo '<div class="clearer"></div>';
+        $this->preprocess_submission($submission);
+        echo '<br />';
+        print_textarea($this->usehtmleditor, 14, 58, 0, 0, 'submissioncomment', $submission->submissioncomment, $this->course->id);
+        if ($this->usehtmleditor) {
+            echo '<input type="hidden" name="format" value="' . FORMAT_HTML . '" />';
+        } else {
+            echo '<div align="right" class="format">';
+            choose_from_menu(format_text_menu(), "format", $submission->format, "");
+            helpbutton("textformat", get_string("helpformatting"));
+            echo '</div>';
+        }
+        ///End of teacher info row
+        ///Print Buttons in Single View
+        echo '<div class="buttons" align="center">';
+        echo '<input type="submit" name="submit" value="' . get_string('savechanges') . '" onclick = "document.submitform.menuindex.value = document.submitform.grade.selectedIndex" />';
+        echo '<input type="submit" name="cancel" value="' . get_string('cancel') . '" />';
+        //if there are more to be graded.
+        if ($nextid) {
+            echo '<input type="submit" name="saveandnext" value="' . get_string('saveandnext') . '" onclick="saveNext()" />';
+            echo '<input type="submit" name="next" value="' . get_string('next') . '" onclick="setNext();" />';
+        }
+        echo '</div>';
+        echo '</form>';
+        $customfeedback = $this->custom_feedbackform($submission, true);
+        if (!empty ($customfeedback)) {
+            echo $customfeedback;
+        }
+        echo '</td></tr>';
+        echo '</table>';
+        if ($this->usehtmleditor) {
+            use_html_editor();
+        }
+        print_footer('none');
+    }
+
+    /**
+     * Show compile or runtime errors
+     *
+     * @param $userid int optional id of the user. If 0 then $USER->id is used.
+     * @param $return boolean optional defaults to false. If true the list is returned rather than printed
+     * @return string optional
+     */
+    function print_user_errors($userid = 0, $compileerrors = '', $runtimeerrors = '', $return = false) {
+        global $CFG, $USER;
+
+        if (!$userid) {
+            if (!isloggedin()) {
+                return '';
+            }
+            $userid = $USER->id;
+        }
+
+        $output = '';
+        $clear = '<div class="clearer"></div>';
+        if ($compileerrors) {
+            $output .= '<br />Compile errors:<br />';
+            $output .= '<div class="generaltable epaileerror"><pre>' . $compileerrors . '</pre></div>';
+        }
+
+        if ($runtimeerrors) {
+            $output .= '<br />';
+            $output .= 'Runtime output:<br />';
+            $output .= '<div class="generaltable"><pre>' . $runtimeerrors . '<pre></div>';
+        }
+
+        $output = '<div class="errors">' . $output . '</div>';
+
+        if ($return) {
+            return $clear . $output;
+        }
+
+        echo $clear . $output;
+    }
+
+    /**
+     * This function returns an
+     * array of possible memory sizes in an array, translated to the
+     * local language.
+     *
+     * @uses SORT_NUMERIC
+     * @param int $sizebytes Moodle site $CGF->assignment_maxmem
+     * @return array
+     */
+    function get_max_memory_usages($sitebytes=0) {
+        global $CFG;
+
+        // Get max size
+        $maxsize = $sitebytes;
+
+        $memusage[$maxsize] = display_size($maxsize);
+
+        $sizelist = array(10240, 51200, 102400, 512000, 1048576, 2097152,
+                          5242880, 10485760);
+
+        // Allow maxbytes to be selected if it falls outside the above boundaries
+        if( isset($CFG->maxmem) && !in_array($CFG->maxmem, $sizelist) ){
+                $sizelist[] = $CFG->maxbytes;
+        }
+
+        foreach ($sizelist as $sizebytes) {
+           if ($sizebytes < $maxsize) {
+               $memusage[$sizebytes] = display_size($sizebytes);
+           }
+        }
+
+        krsort($memusage, SORT_NUMERIC);
+
+        return $memusage;
+    }
+
+    /**
+     * This function returns an
+     * array of possible CPU time (in seconds) in an array
+     *
+     * @uses SORT_NUMERIC
+     * @param int $time Moodle site $CGF->assignment_maxcpu
+     * @return array
+     */
+    function get_max_cpu_times($time=0) {
+        global $CFG;
+
+        // Get max size
+        $maxtime = $time;
+
+        $cputime[$maxtime] = $maxtime.' secs';
+
+        $timelist = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 20, 25, 30, 40, 50, 60);
+
+        // Allow maxtime to be selected if it falls outside the above boundaries
+        if( isset($CFG->maxcpu) && !in_array($CFG->maxcpu, $timelist) ){
+                $cputime[] = $CFG->maxbytes;
+        }
+
+        foreach ($timelist as $timesecs) {
+           if ($timesecs < $maxtime) {
+               $cputime[$timesecs] = $timesecs.' secs';
+           }
+        }
+
+        krsort($cputime, SORT_NUMERIC);
+
+        return $cputime;
+    }
+}
+
+/**
+ * OTHER GENERAL FUNCTIONS FOR PROGRAM ASSIGNMENTS
+ */
+
+/**
+ * Returns an array of installed programming languages indexed and sorted by name
+ *
+ * @return array The index is the name of the assignment type, the value its full name from the language strings
+ */
+function assignment_program_languages() {
+    global $CFG;
+    $lang = array ();
+    $dir = $CFG->dirroot . '/mod/assignment/type/program/languages/';
+    $files = get_directory_list($dir);
+    $names = preg_replace('/\.(\w+)/', '', $files); // Replace file extension with nothing
+    foreach ($names as $name) {
+        $lang[$name] = get_string('lang' . $name, 'assignment');
+    }
+    asort($lang);
+    return $lang;
+}
+
+/**
+ * This function returns an
+ * array of possible CPU time (in seconds) in an array
+ *
+ * This is done by calling the get_max_cpu_times() method of the assignment type class
+ */
+function assignment_program_get_max_cpu_times($time=0) {
+    $ass = new assignment_program();
+
+    return $ass->get_max_cpu_times($time);
+}
+
+/**
+ * This function returns an
+ * array of possible memory sizes in an array, translated to the
+ * local language.
+ *
+ * This is done by calling the get_max_memory_usages() method of the assignment type class
+ */
+function assignment_program_get_max_memory_usages($sitebytes=0) {
+    $ass = new assignment_program();
+
+    return $ass->get_max_memory_usages($sitebytes);
+}
+?>
diff --git a/mod/assignment/type/program/db/mysql.sql b/mod/assignment/type/program/db/mysql.sql
new file mode 100644 (file)
index 0000000..f24ae7c
--- /dev/null
@@ -0,0 +1,56 @@
+-- 
+-- Table structure for table `mdl_assignment_epaile_results`
+-- 
+
+CREATE TABLE `prefix_assignment_epaile_results` (
+  `id` bigint(10) NOT NULL auto_increment,
+  `submission` bigint(10) NOT NULL,
+  `test` bigint(10) NOT NULL,
+  `runtime` bigint(10) NOT NULL,
+  `status` varchar(50) collate utf8_unicode_ci NOT NULL COMMENT 'Possible values: Accepted, Wrong answer, Internal error',
+  `output` varchar(255) collate utf8_unicode_ci default NULL COMMENT 'Program output',
+  `error` text collate utf8_unicode_ci COMMENT 'Runtime error',
+  PRIMARY KEY  (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Test results' AUTO_INCREMENT=1 ;
+
+-- --------------------------------------------------------
+
+-- 
+-- Table structure for table `mdl_assignment_epaile_submissions`
+-- 
+
+CREATE TABLE `prefix_assignment_epaile_submissions` (
+  `id` bigint(10) unsigned NOT NULL auto_increment,
+  `submission` bigint(10) unsigned NOT NULL default '0',
+  `compileerrors` text collate utf8_unicode_ci,
+  `compiletime` bigint(10) NOT NULL default '0',
+  PRIMARY KEY  (`id`),
+  KEY `prefix_epaisubm_epa_ix` (`submission`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Info about submitted assignments' AUTO_INCREMENT=1 ;
+
+-- --------------------------------------------------------
+
+-- 
+-- Table structure for table `mdl_assignment_epaile_tests`
+-- 
+
+CREATE TABLE `prefix_assignment_epaile_tests ` (
+  `id` bigint(10) unsigned NOT NULL auto_increment,
+  `assignment` bigint(10) unsigned NOT NULL default '0',
+  `input` varchar(255) collate utf8_unicode_ci NOT NULL COMMENT 'Program input',
+  `output` varchar(255) collate utf8_unicode_ci NOT NULL COMMENT 'Expected output',
+  PRIMARY KEY  (`id`),
+  KEY `prefix_epaitest_epa_ix` (`assignment`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Info about assignment tests' AUTO_INCREMENT=1 ;
+
+-- --------------------------------------------------------
+
+--
+-- Add new field to `assignment` table
+--
+ALTER TABLE `prefix_assignment` ADD `lang` VARCHAR( 50 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL AFTER `var5` ;
+
+--
+-- Some values for `config` table
+--
+ INSERT INTO `prefix_config` (`name`,`value`) VALUES ('assignment_maxmem', '512000'),('assignment_maxcpu', '15');
\ No newline at end of file
diff --git a/mod/assignment/type/program/js/tests.js b/mod/assignment/type/program/js/tests.js
new file mode 100644 (file)
index 0000000..5ffe5ff
--- /dev/null
@@ -0,0 +1,105 @@
+<!--
+    /* tests.js (v1.0 - 2007/06/18)
+     * ********************************************************************* *
+     * by Arkaitz Garro, June 2007                                           *
+     * Copyright (c) 2007 Arkaitz Garro. All Rights Reserved.                *
+     *                                                                       *
+     * This code is free software; you can redistribute it and/or modify     *
+     * it under the terms of the GNU General Public License as published by  *
+     * the Free Software Foundation; either version 2 of the License, or     *
+     * (at your option) any later version.                                   *
+     *                                                                       *
+     * This program 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 General Public License for more details:                          *
+     *                                                                       *
+     *          http://www.gnu.org/copyleft/gpl.html                         *
+     * ********************************************************************* *
+     * This JavaScript add / delete tests dynamicaly                         *
+     *                                                                       *
+     * @author Arkaitz Garro                                                 *
+     * @package epaile                                                       *
+     * ********************************************************************* *
+     */
+
+    // Add a new test
+    function addTest() {
+        divTest = document.createElement("tbody");               // New Test container
+        divTest.setAttribute("id","test"+id);
+        divTest.appendChild(createTitle());                      // New title row
+        divTest.appendChild(createBoxes());                      // New boxes row
+        document.getElementById("tests").appendChild(divTest);
+        id++;
+    }
+
+    // Delete an existing test
+    function delTest(testId) {
+        var divTest = document.getElementById("test"+testId);
+        document.getElementById("tests").removeChild(divTest);
+        id--;
+    }
+
+    // Create test title (Test+id)
+    function createTitle() {
+        
+        trTitle = document.createElement("tr");         // New title row
+        tdTitle = document.createElement("td");         // New title cell       
+        aDel = document.createElement("a");             // New link 'Delete test'
+        imgDel = document.createElement("img");         // New image 'Delete test'
+        txtTest = document.createElement("strong")      // New Test text
+
+        
+        trTitle.setAttribute("valign","top");                                   // Set attributes to title row
+        trTitle.setAttribute("style","border-bottom: 1px solid #BBBBBB;");      // Set attributes to title row
+        tdTitle.setAttribute("colspan","2");                                    // Set attributes to title cell
+        aDel.setAttribute("href","#");                                          // Set attributes to link
+        aDel.setAttribute("onclick","Javascript:delTest("+id+");");                 // Set attributes to link
+        imgDel.setAttribute("src",pixpath+"/t/switch_minus.gif");    // Set attributes to image
+
+        // Title structure
+        txtTest.appendChild(document.createTextNode(" Test"));
+        aDel.appendChild(imgDel);
+        tdTitle.appendChild(aDel);
+        tdTitle.appendChild(txtTest);
+        trTitle.appendChild(tdTitle);
+
+        return trTitle;
+    }
+
+    // Create input / output boxes
+    function createBoxes() {
+        
+        tr = document.createElement("tr");          // New row
+        tdIn = document.createElement("td");        // New 'input' cell 
+        tdOut = document.createElement("td");       // New 'output' cell
+        tbIn = document.createElement("input");     // New 'input' textBox
+        tbOut = document.createElement("input");    // New 'output' textBox
+        txtIn = document.createElement("strong")    // New input text
+        txtOut = document.createElement("strong")   // New output text
+        
+        tdIn.setAttribute("align","right");         // Set attributes to cell
+        
+        tbIn.setAttribute("type","text");           // Set attributes to textBox 'input'
+        tbIn.setAttribute("name","input[]");        // Set attributes to textBox 'input'
+        tbIn.setAttribute("size","30");             // Set attributes to textBox 'input'
+        
+        tbOut.setAttribute("type","text");          // Set attributes to textBox 'output'
+        tbOut.setAttribute("name","output[]");      // Set attributes to textBox 'output'
+        tbOut.setAttribute("size","30");            // Set attributes to textBox 'output'
+        tbIn.focus();
+        
+        
+        // Row structure
+        txtIn.appendChild(document.createTextNode("Input: "));
+        txtOut.appendChild(document.createTextNode("Output: "));
+        tdIn.appendChild(txtIn);
+        tdIn.appendChild(tbIn);
+        tdOut.appendChild(txtOut);
+        tdOut.appendChild(tbOut);
+        tr.appendChild(tdIn);
+        tr.appendChild(tdOut);
+
+        return tr;
+    }
+//-->
\ No newline at end of file
diff --git a/mod/assignment/type/program/languages/bash.sh b/mod/assignment/type/program/languages/bash.sh
new file mode 100644 (file)
index 0000000..e7b6a13
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# Bash compile wrapper-script for 'test_solution.sh'.
+# See that script for syntax and more info.
+#
+# This script does not actually "compile" the source, but writes a
+# shell script that will function as the executable: when called, it
+# will execute the source with the correct interpreter syntax, thus
+# allowing this interpreted source to be used transparantly as if it
+# was compiled to a standalone binary.
+#
+# NOTICE: this compiler script cannot be used with the USE_CHROOT
+# configuration option turned on! (Unless proper preconfiguration of
+# the chroot environment has been taken care of.)
+
+SOURCE="$1"
+DEST="$2"
+
+RUNOPTIONS="--noprofile --norc -r -p"
+
+# Check for '#!' interpreter line: don't allow it to prevent teams
+# from passing options to the interpreter.
+if grep '^#!' $SOURCE &>/dev/null ; then
+       echo "Error: interpreter statement(s) found:"
+       grep -n '^#!' $SOURCE
+       exit 1
+fi
+
+# Check bash syntax:
+bash $RUNOPTIONS -n $SOURCE
+EXITCODE=$?
+[ "$EXITCODE" -ne 0 ] && exit $EXITCODE
+
+# Write executing script:
+cat > $DEST <<EOF
+#!/bin/sh
+# Generated shell-script to execute bash interpreter on source.
+
+exec bash $RUNOPTIONS $SOURCE
+EOF
+
+chmod a+x $DEST
+
+exit 0
diff --git a/mod/assignment/type/program/languages/c.sh b/mod/assignment/type/program/languages/c.sh
new file mode 100644 (file)
index 0000000..879a421
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# C compile wrapper-script for 'test_solution.sh'.
+# See that script for syntax and more info.
+
+SOURCE="$1"
+DEST="$2"
+
+# -Wall:       Report all warnings
+# -O2:         Level 2 optimizations (default for speed)
+# -static:     Static link with all libraries
+# -lm:         Link with math-library (has to be last argument!)
+gcc -Wall -O2 -static -o $DEST $SOURCE -lm
+exit $?
diff --git a/mod/assignment/type/program/languages/cpp.sh b/mod/assignment/type/program/languages/cpp.sh
new file mode 100644 (file)
index 0000000..3cbe6da
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+# C++ compile wrapper-script for 'test_solution.sh'.
+# See that script for syntax and more info.
+
+SOURCE="$1"
+DEST="$2"
+
+# -Wall:       Report all warnings
+# -O2:         Level 2 optimizations (default for speed)
+# -static:     Static link with all libraries
+g++ -Wall -O2 -static -o $DEST $SOURCE
+exit $?
diff --git a/mod/assignment/type/program/languages/haskell.sh b/mod/assignment/type/program/languages/haskell.sh
new file mode 100644 (file)
index 0000000..5a35d65
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+# Haskell compile wrapper-script for 'test_solution.sh'.
+# See that script for syntax and more info.
+
+SOURCE="$1"
+DEST="$2"
+
+# -Wall:       Report all warnings
+# -O:          Optimize
+# -static:     Static link Haskell libraries
+# -optl-static:        Pass '-static' option to the linker
+ghc -Wall -O -static -optl-static -o $DEST $SOURCE
+exitcode=$?
+
+# clean created files:
+rm -f $DEST.o Main.hi
+
+exit $exitcode
diff --git a/mod/assignment/type/program/languages/java.sh b/mod/assignment/type/program/languages/java.sh
new file mode 100644 (file)
index 0000000..56a7367
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+# Java compile wrapper-script for 'test_solution.sh'.
+# See that script for syntax and more info.
+#
+# This script byte-compiles with the Sun javac compiler and generates
+# a shell script to run it with the java interpreter later.
+#
+# NOTICE: this compiler script cannot be used with the USE_CHROOT
+# configuration option turned on, unless proper preconfiguration of
+# the chroot environment has been taken care of!
+
+SOURCE="$1"
+DEST="$2"
+MEMLIMIT="$3"
+
+# Sun java needs filename to match main class:
+MAINCLASS=Main
+
+SOURCEDIR=${SOURCE%/*}
+[ "$SOURCEDIR" = "$SOURCE" ] && SOURCEDIR='.'
+TMP="$SOURCEDIR/$MAINCLASS"
+
+cp $SOURCE $TMP.java
+
+# Byte-compile:
+javac $TMP.java
+EXITCODE=$?
+[ "$EXITCODE" -ne 0 ] && exit $EXITCODE
+
+# Check for class file:
+if [ ! -f "$TMP.class" ]; then
+       echo "Error: byte-compiled class file '$TMP.class' not found."
+       exit 1
+fi
+
+# Calculate Java program memlimit as MEMLIMIT - max. JVM memory usage:
+MEMLIMITJAVA=$(($MEMLIMIT - 204800))
+
+# Write executing script:
+# Executes java byte-code interpreter with following options
+# -Xmx: maximum size of memory allocation pool
+# -Xrs: reduces usage signals by java, because that generates debug
+#       output when program is terminated on timelimit exceeded.
+cat > $DEST <<EOF
+#!/bin/sh
+# Generated shell-script to execute java interpreter on source.
+
+cd $SOURCEDIR
+exec java -Xrs -Xmx${MEMLIMITJAVA}k $MAINCLASS
+EOF
+
+chmod a+x $DEST
+
+exit 0
diff --git a/mod/assignment/type/program/languages/pascal.sh b/mod/assignment/type/program/languages/pascal.sh
new file mode 100644 (file)
index 0000000..53e670b
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+# Psacal compile wrapper-script for 'test_solution.sh'.
+# See that script for syntax and more info.
+
+SOURCE="$1"
+DEST="$2"
+
+# -viwn:       Verbose warnings, notes and informational messages
+# -02:         Level 2 optimizations (default for speed)
+# -Sg:         Support label and goto commands (for those who need it ;-)
+# -XS:         Static link with all libraries
+fpc -viwn -O2 -Sg -XS -o$DEST $SOURCE
+exitcode=$?
+
+# clean created object files:
+rm -f $DEST.o
+
+exit $exitcode
diff --git a/mod/assignment/type/program/languages/perl.sh b/mod/assignment/type/program/languages/perl.sh
new file mode 100644 (file)
index 0000000..4774a44
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# Perl compile wrapper-script for 'test_solution.sh'.
+# See that script for syntax and more info.
+#
+# This script does not actually "compile" the source, but writes a
+# shell script that will function as the executable: when called, it
+# will execute the source with the correct interpreter syntax, thus
+# allowing this interpreted source to be used transparantly as if it
+# was compiled to a standalone binary.
+#
+# NOTICE: this compiler script cannot be used with the USE_CHROOT
+# configuration option turned on! (Unless proper preconfiguration of
+# the chroot environment has been taken care of.)
+
+SOURCE="$1"
+DEST="$2"
+
+# Check for '#!' interpreter line: don't allow it to prevent teams
+# from passing options to the interpreter.
+if grep '^#!' $SOURCE &>/dev/null ; then
+       echo "Error: interpreter statement(s) found:"
+       grep -n '^#!' $SOURCE
+       exit 1
+fi
+
+# Check perl syntax:
+perl -c -W $SOURCE
+EXITCODE=$?
+[ "$EXITCODE" -ne 0 ] && exit $EXITCODE
+
+# Write executing script:
+cat > $DEST <<EOF
+#!/bin/sh
+# Generated shell-script to execute perl interpreter on source.
+
+exec perl $SOURCE
+EOF
+
+chmod a+x $DEST
+
+exit 0
diff --git a/mod/assignment/type/program/mod.html b/mod/assignment/type/program/mod.html
new file mode 100644 (file)
index 0000000..5c7ff9c
--- /dev/null
@@ -0,0 +1,62 @@
+<!-- Javascript functions -->
+<script src="../mod/assignment/type/program/js/tests.js" type="text/javascript"></script>
+<script language="JavaScript" type="text/javascript">
+    // Global variable id: Number of test + 1
+    var id = <?php echo $numtests+1 ?>;
+    
+    var pixpath = '<?php echo $CFG->pixpath ?>';
+</script>
+<!-- End -->
+
+<!-- Program tests -->
+<table align="center" cellpadding="5" cellspacing="0" id="tests">
+<?php
+    if($tests) {
+        // Tests allready defined (update assignment)
+        $i = 1;
+        foreach($tests as $tstObj => $tstValue) {
+?>
+<tbody id="test<?php echo $i ?>">
+    <tr valign="top" style="border-bottom: 1px solid #BBBBBB;">
+        <td colspan="2">
+            <a href="#" onclick="Javascript:delTest(<?php echo $i ?>);">
+            <img src="<?php echo $CFG->pixpath ?>/t/switch_minus.gif" alt="Delete" title="<?php print_string('deletetest', 'epaile') ?>" /></a>&nbsp;<strong>Test</strong>
+        </td>
+    </tr>
+    <tr>
+        <td align="right">
+            <strong><?php print_string('input', 'assignment') ?>:</strong> <input type="text" name="input[]" size="30" value="<?php p($tstValue->input) ?>">
+        </td>
+        <td>
+            <strong><?php print_string('output', 'assignment') ?>:</strong> <input type="text" name="output[]" size="30" value="<?php p($tstValue->output) ?>">
+        </td>
+    </tr>
+</tbody>
+<?php
+            $i++;
+        }
+    } else {
+        // New assignment
+        for($i=1; $i<=$numtests; $i++) {
+?>
+<tbody id="test<?php echo $i ?>">
+    <tr valign="top" style="border-bottom: 1px solid #BBBBBB;">
+        <td colspan="2"><a href="#" onclick="Javascript:delTest(<?php echo $i ?>);"><img src="<?php echo $CFG->pixpath ?>/t/switch_minus.gif" alt="Delete" title="Delete test" /></a>&nbsp;<strong>Test</strong></td>
+    </tr>
+
+    <tr>
+        <td align="right"><strong><?php print_string('input', 'assignment') ?>:</strong> <input type="text" name="input[]" size="30" value=""></td>
+        <td><strong><?php print_string('output', 'assignment') ?>:</strong> <input type="text" name="output[]" size="30" value=""></td>
+    </tr>
+</tbody>
+<?php
+        }
+    }
+?>
+</table>
+<table align="center" cellpadding="5" cellspacing="0">
+<tr>
+    <td align="left" colspan="2"><a href="Javascript:addTest();"><img src="<?php echo $CFG->pixpath ?>/t/switch_plus.gif" alt="Add" title="<?php print_string('addtest', 'assignment') ?>" /> <?php print_string('addtest', 'assignment') ?></a></td>
+</tr>
+</table>
+<!-- End program tests -->
diff --git a/mod/assignment/type/program/source.html b/mod/assignment/type/program/source.html
new file mode 100644 (file)
index 0000000..6353977
--- /dev/null
@@ -0,0 +1,25 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+    <head>
+        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+        <title><?php echo $file ?></title>
+        
+    </head>
+    <body>
+        <pre name="code" class="<?php echo $lang ?>"><?php echo $code ?></pre>
+        <p align="center">
+            <?php
+                close_window_button();
+            ?>
+        </p>
+
+        <link type="text/css" rel="stylesheet" href="syntaxhighlighter/Styles/SyntaxHighlighter.css"></link>
+        <script class="javascript" src="syntaxhighlighter/Scripts/shCore.js"></script>
+        <script class="javascript" src="syntaxhighlighter/Scripts/shBrushJava.js"></script>
+        <script language="javascript">
+            dp.SyntaxHighlighter.ClipboardSwf = '/syntaxhighlighter/flash/clipboard.swf';
+            dp.SyntaxHighlighter.HighlightAll('code');
+        </script>
+    </body>
+</html>
\ No newline at end of file
diff --git a/mod/assignment/type/program/source.php b/mod/assignment/type/program/source.php
new file mode 100644 (file)
index 0000000..4799590
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+    /* source.php (v1.0 - 2007/06/26)
+     * ********************************************************************* *
+     * by Arkaitz Garro, July 2007                                           *
+     * Copyright (c) 2007 Arkaitz Garro. All Rights Reserved.                *
+     * This code is based in actual assigment module.                        *
+     *                                                                       *
+     * This code is free software; you can redistribute it and/or modify     *
+     * it under the terms of the GNU General Public License as published by  *
+     * the Free Software Foundation; either version 2 of the License, or     *
+     * (at your option) any later version.                                   *
+     *                                                                       *
+     * This program 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 General Public License for more details:                          *
+     *                                                                       *
+     *          http://www.gnu.org/copyleft/gpl.html                         *
+     * ********************************************************************* *
+     * Use of SyntaxHighlighter                                              *
+     * Free syntax highlighter written in Javascript                         *
+     * http://code.google.com/p/syntaxhighlighter/                           *
+     * ********************************************************************* *
+     */
+
+
+    require_once('../../../../config.php');
+    require_once('../../lib.php');
+    
+    global $CFG, $USER;
+    
+    $id = optional_param('id', 0, PARAM_INT);  // Course Module ID
+    $e  = optional_param('a', 0, PARAM_INT);   // Epaile ID
+    $userid = optional_param('userid', 0, PARAM_INT);   // User ID
+    $file = optional_param('file', NULL, PARAM_CLEANHTML); // File to show
+    
+    if ($id) {
+        if (!$cm = get_coursemodule_from_id('assignment', $id)) {
+            error(get_string('error_invalidcoursemodule','assignment'));
+        }
+
+        if (!$assignment = get_record('assignment', 'id', $cm->instance)) {
+            error(get_string('error_invalidepaileid','assignment'));
+        }
+
+        if (!$course = get_record('course', 'id', $assignment->course)) {
+            error(get_string('error_misconfiguredcourse','assignment'));
+        }
+    } else {
+        if (!$assignment = get_record('assignment', 'id', $e)) {
+            error(get_string('error_incorrectmodule','assignment'));
+        }
+        if (!$course = get_record('course', 'id', $assignment->course)) {
+            error(get_string('error_misconfiguredcourse','assignment'));
+        }
+        if (!$cm = get_coursemodule_from_instance('assignment', $assignment->id, $course->id)) {
+            error(get_string('error_invalidcoursemodule','assignment'));
+        }
+    }
+
+    if(!$userid)
+        error(get_string('nouser','assignment'));
+
+    if(!$file)
+        error(get_string('nosuchfile','assignment'));
+
+    require_login($course->id, false, $cm);
+
+    // Load up the required assignment code
+    require('assignment.class.php');
+    $assignmentinstance = new assignment_program($cm->id, $assignment, $cm, $course);
+
+    $filearea = $assignmentinstance->file_area_name($userid);
+
+    if ($basedir = $assignmentinstance->file_area($userid)) {
+        require_once($CFG->libdir.'/filelib.php');
+
+        if ($CFG->slasharguments) {
+            $ffurl = "$CFG->dataroot/$filearea/$file";
+        } else {
+            $ffurl = "$CFG->dataroot/$filearea/$file";
+        }
+    }
+
+    if($gestor = fopen($ffurl,'r')) {
+        $code = fread($gestor, filesize($ffurl));
+        fclose($gestor);
+    } else {
+        error(get_string('filereaderror','assignment'));   
+    }
+
+    $lang = $assignmentinstance->get_language();
+    
+    include('source.html');
+?>
\ No newline at end of file
diff --git a/mod/assignment/type/program/syntaxhighlighter/Scripts/clipboard.swf b/mod/assignment/type/program/syntaxhighlighter/Scripts/clipboard.swf
new file mode 100644 (file)
index 0000000..2cfe371
Binary files /dev/null and b/mod/assignment/type/program/syntaxhighlighter/Scripts/clipboard.swf differ
diff --git a/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushCSharp.js b/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushCSharp.js
new file mode 100644 (file)
index 0000000..b8beffb
--- /dev/null
@@ -0,0 +1,32 @@
+dp.sh.Brushes.CSharp = function()
+{
+       var keywords =  'abstract as base bool break byte case catch char checked class const ' +
+                                       'continue decimal default delegate do double else enum event explicit ' +
+                                       'extern false finally fixed float for foreach get goto if implicit in int ' +
+                                       'interface internal is lock long namespace new null object operator out ' +
+                                       'override params private protected public readonly ref return sbyte sealed set ' +
+                                       'short sizeof stackalloc static string struct switch this throw true try ' +
+                                       'typeof uint ulong unchecked unsafe ushort using virtual void while';
+
+       this.regexList = [
+               // There's a slight problem with matching single line comments and figuring out
+               // a difference between // and ///. Using lookahead and lookbehind solves the
+               // problem, unfortunately JavaScript doesn't support lookbehind. So I'm at a 
+               // loss how to translate that regular expression to JavaScript compatible one.
+//             { regex: new RegExp('(?<!/)//(?!/).*$|(?<!/)////(?!/).*$|/\\*[^\\*]*(.)*?\\*/', 'gm'),  css: 'comment' },                       // one line comments starting with anything BUT '///' and multiline comments
+//             { regex: new RegExp('(?<!/)///(?!/).*$', 'gm'),                                                                                 css: 'comments' },              // XML comments starting with ///
+
+               { regex: dp.sh.RegexLib.SingleLineCComments,                            css: 'comment' },                       // one line comments
+               { regex: dp.sh.RegexLib.MultiLineCComments,                                     css: 'comment' },                       // multiline comments
+               { regex: dp.sh.RegexLib.DoubleQuotedString,                                     css: 'string' },                        // strings
+               { regex: dp.sh.RegexLib.SingleQuotedString,                                     css: 'string' },                        // strings
+               { regex: new RegExp('^\\s*#.*', 'gm'),                                          css: 'preprocessor' },          // preprocessor tags like #region and #endregion
+               { regex: new RegExp(this.GetKeywords(keywords), 'gm'),          css: 'keyword' }                        // c# keyword
+               ];
+
+       this.CssClass = 'dp-c';
+       this.Style = '.dp-c .vars { color: #d00; }';
+}
+
+dp.sh.Brushes.CSharp.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.CSharp.Aliases   = ['c#', 'c-sharp', 'csharp'];
diff --git a/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushCpp.js b/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushCpp.js
new file mode 100644 (file)
index 0000000..025311a
--- /dev/null
@@ -0,0 +1,73 @@
+/**
+ * Code Syntax Highlighter for C++(Windows Platform).
+ * Version 0.0.2
+ * Copyright (C) 2006 Shin, YoungJin.
+ * http://www.jiniya.net/lecture/techbox/test.html
+ * 
+ * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General 
+ * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) 
+ * any later version.
+ *
+ * This library 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 Lesser General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to 
+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
+ */
+
+dp.sh.Brushes.Cpp = function()
+{
+       var datatypes = 
+       'ATOM BOOL BOOLEAN BYTE CHAR COLORREF DWORD DWORDLONG DWORD_PTR ' +
+       'DWORD32 DWORD64 FLOAT HACCEL HALF_PTR HANDLE HBITMAP HBRUSH ' + 
+       'HCOLORSPACE HCONV HCONVLIST HCURSOR HDC HDDEDATA HDESK HDROP HDWP ' +
+       'HENHMETAFILE HFILE HFONT HGDIOBJ HGLOBAL HHOOK HICON HINSTANCE HKEY ' +
+       'HKL HLOCAL HMENU HMETAFILE HMODULE HMONITOR HPALETTE HPEN HRESULT ' +
+       'HRGN HRSRC HSZ HWINSTA HWND INT INT_PTR INT32 INT64 LANGID LCID LCTYPE ' +
+       'LGRPID LONG LONGLONG LONG_PTR LONG32 LONG64 LPARAM LPBOOL LPBYTE LPCOLORREF ' +
+       'LPCSTR LPCTSTR LPCVOID LPCWSTR LPDWORD LPHANDLE LPINT LPLONG LPSTR LPTSTR ' +
+       'LPVOID LPWORD LPWSTR LRESULT PBOOL PBOOLEAN PBYTE PCHAR PCSTR PCTSTR PCWSTR ' +
+       'PDWORDLONG PDWORD_PTR PDWORD32 PDWORD64 PFLOAT PHALF_PTR PHANDLE PHKEY PINT ' +
+       'PINT_PTR PINT32 PINT64 PLCID PLONG PLONGLONG PLONG_PTR PLONG32 PLONG64 POINTER_32 ' +
+       'POINTER_64 PSHORT PSIZE_T PSSIZE_T PSTR PTBYTE PTCHAR PTSTR PUCHAR PUHALF_PTR ' +
+       'PUINT PUINT_PTR PUINT32 PUINT64 PULONG PULONGLONG PULONG_PTR PULONG32 PULONG64 ' +
+       'PUSHORT PVOID PWCHAR PWORD PWSTR SC_HANDLE SC_LOCK SERVICE_STATUS_HANDLE SHORT ' + 
+       'SIZE_T SSIZE_T TBYTE TCHAR UCHAR UHALF_PTR UINT UINT_PTR UINT32 UINT64 ULONG ' +
+       'ULONGLONG ULONG_PTR ULONG32 ULONG64 USHORT USN VOID WCHAR WORD WPARAM WPARAM WPARAM ' +
+       'char bool short int __int32 __int64 __int8 __int16 long float double __wchar_t ' +
+       'clock_t _complex _dev_t _diskfree_t div_t ldiv_t _exception _EXCEPTION_POINTERS ' +
+       'FILE _finddata_t _finddatai64_t _wfinddata_t _wfinddatai64_t __finddata64_t ' +
+       '__wfinddata64_t _FPIEEE_RECORD fpos_t _HEAPINFO _HFILE lconv intptr_t ' +
+       'jmp_buf mbstate_t _off_t _onexit_t _PNH ptrdiff_t _purecall_handler ' +
+       'sig_atomic_t size_t _stat __stat64 _stati64 terminate_function ' +
+       'time_t __time64_t _timeb __timeb64 tm uintptr_t _utimbuf ' +
+       'va_list wchar_t wctrans_t wctype_t wint_t signed';
+
+       var keywords = 
+       'break case catch class const __finally __exception __try ' +
+       'const_cast continue private public protected __declspec ' + 
+       'default delete deprecated dllexport dllimport do dynamic_cast ' + 
+       'else enum explicit extern if for friend goto inline ' + 
+       'mutable naked namespace new noinline noreturn nothrow ' + 
+       'register reinterpret_cast return selectany ' + 
+       'sizeof static static_cast struct switch template this ' + 
+       'thread throw true false try typedef typeid typename union ' + 
+       'using uuid virtual void volatile whcar_t while';
+
+       this.regexList = [
+               { regex: dp.sh.RegexLib.SingleLineCComments,                            css: 'comment' },                       // one line comments
+               { regex: dp.sh.RegexLib.MultiLineCComments,                                     css: 'comment' },                       // multiline comments
+               { regex: dp.sh.RegexLib.DoubleQuotedString,                                     css: 'string' },                        // strings
+               { regex: dp.sh.RegexLib.SingleQuotedString,                                     css: 'string' },                        // strings
+               { regex: new RegExp('^ *#.*', 'gm'),                                            css: 'preprocessor' },
+               { regex: new RegExp(this.GetKeywords(datatypes), 'gm'),         css: 'datatypes' },
+               { regex: new RegExp(this.GetKeywords(keywords), 'gm'),          css: 'keyword' }
+               ];
+
+       this.CssClass = 'dp-cpp';
+       this.Style =    '.dp-cpp .datatypes { color: #2E8B57; font-weight: bold; }';
+}
+
+dp.sh.Brushes.Cpp.prototype    = new dp.sh.Highlighter();
+dp.sh.Brushes.Cpp.Aliases      = ['cpp', 'c', 'c++'];
diff --git a/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushCss.js b/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushCss.js
new file mode 100644 (file)
index 0000000..cd5a2cb
--- /dev/null
@@ -0,0 +1,52 @@
+dp.sh.Brushes.CSS = function()
+{
+       var keywords =  'ascent azimuth background-attachment background-color background-image background-position ' +
+                                       'background-repeat background baseline bbox border-collapse border-color border-spacing border-style border-top ' +
+                                       'border-right border-bottom border-left border-top-color border-right-color border-bottom-color border-left-color ' +
+                                       'border-top-style border-right-style border-bottom-style border-left-style border-top-width border-right-width ' +
+                                       'border-bottom-width border-left-width border-width border bottom cap-height caption-side centerline clear clip color ' +
+                                       'content counter-increment counter-reset cue-after cue-before cue cursor definition-src descent direction display ' +
+                                       'elevation empty-cells float font-size-adjust font-family font-size font-stretch font-style font-variant font-weight font ' +
+                                       'height letter-spacing line-height list-style-image list-style-position list-style-type list-style margin-top ' +
+                                       'margin-right margin-bottom margin-left margin marker-offset marks mathline max-height max-width min-height min-width orphans ' +
+                                       'outline-color outline-style outline-width outline overflow padding-top padding-right padding-bottom padding-left padding page ' +
+                                       'page-break-after page-break-before page-break-inside pause pause-after pause-before pitch pitch-range play-during position ' +
+                                       'quotes richness right size slope src speak-header speak-numeral speak-punctuation speak speech-rate stemh stemv stress ' +
+                                       'table-layout text-align text-decoration text-indent text-shadow text-transform unicode-bidi unicode-range units-per-em ' +
+                                       'vertical-align visibility voice-family volume white-space widows width widths word-spacing x-height z-index important';
+
+       var values =    'above absolute all always aqua armenian attr aural auto avoid baseline behind below bidi-override black blink block blue bold bolder '+
+                                       'both bottom braille capitalize caption center center-left center-right circle close-quote code collapse compact condensed '+
+                                       'continuous counter counters crop cross crosshair cursive dashed decimal decimal-leading-zero default digits disc dotted double '+
+                                       'embed embossed e-resize expanded extra-condensed extra-expanded fantasy far-left far-right fast faster fixed format fuchsia '+
+                                       'gray green groove handheld hebrew help hidden hide high higher icon inline-table inline inset inside invert italic '+
+                                       'justify landscape large larger left-side left leftwards level lighter lime line-through list-item local loud lower-alpha '+
+                                       'lowercase lower-greek lower-latin lower-roman lower low ltr marker maroon medium message-box middle mix move narrower '+
+                                       'navy ne-resize no-close-quote none no-open-quote no-repeat normal nowrap n-resize nw-resize oblique olive once open-quote outset '+
+                                       'outside overline pointer portrait pre print projection purple red relative repeat repeat-x repeat-y rgb ridge right right-side '+
+                                       'rightwards rtl run-in screen scroll semi-condensed semi-expanded separate se-resize show silent silver slower slow '+
+                                       'small small-caps small-caption smaller soft solid speech spell-out square s-resize static status-bar sub super sw-resize '+
+                                       'table-caption table-cell table-column table-column-group table-footer-group table-header-group table-row table-row-group teal '+
+                                       'text-bottom text-top thick thin top transparent tty tv ultra-condensed ultra-expanded underline upper-alpha uppercase upper-latin '+
+                                       'upper-roman url visible wait white wider w-resize x-fast x-high x-large x-loud x-low x-slow x-small x-soft xx-large xx-small yellow';
+       
+       var fonts =             '[mM]onospace [tT]ahoma [vV]erdana [aA]rial [hH]elvetica [sS]ans-serif [sS]erif';
+
+       this.regexList = [
+               { regex: dp.sh.RegexLib.MultiLineCComments,                                     css: 'comment' },       // multiline comments
+               { regex: dp.sh.RegexLib.DoubleQuotedString,                                     css: 'string' },        // double quoted strings
+               { regex: dp.sh.RegexLib.SingleQuotedString,                                     css: 'string' },        // single quoted strings
+               { regex: new RegExp('\\#[a-zA-Z0-9]{3,6}', 'g'),                        css: 'colors' },        // html colors
+               { regex: new RegExp('(\\d+)(px|pt|\:)', 'g'),                           css: 'string' },        // size specifications
+               { regex: new RegExp(this.GetKeywords(keywords), 'gm'),          css: 'keyword' },       // keywords
+               { regex: new RegExp(this.GetKeywords(values), 'g'),                     css: 'string' },        // values
+               { regex: new RegExp(this.GetKeywords(fonts), 'g'),                      css: 'string' }         // fonts
+               ];
+
+       this.CssClass = 'dp-css';
+       this.Style =    '.dp-css .colors { color: darkred; }' +
+                                       '.dp-css .vars { color: #d00; }';
+}
+
+dp.sh.Brushes.CSS.prototype    = new dp.sh.Highlighter();
+dp.sh.Brushes.CSS.Aliases      = ['css'];
diff --git a/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushDelphi.js b/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushDelphi.js
new file mode 100644 (file)
index 0000000..9f71174
--- /dev/null
@@ -0,0 +1,34 @@
+/* Delphi brush is contributed by Eddie Shipman */
+dp.sh.Brushes.Delphi = function()
+{
+       var keywords =  'abs addr and ansichar ansistring array as asm begin boolean byte cardinal ' +
+                                       'case char class comp const constructor currency destructor div do double ' +
+                                       'downto else end except exports extended false file finalization finally ' +
+                                       'for function goto if implementation in inherited int64 initialization ' +
+                                       'integer interface is label library longint longword mod nil not object ' +
+                                       'of on or packed pansichar pansistring pchar pcurrency pdatetime pextended ' + 
+                                       'pint64 pointer private procedure program property pshortstring pstring ' + 
+                                       'pvariant pwidechar pwidestring protected public published raise real real48 ' +
+                                       'record repeat set shl shortint shortstring shr single smallint string then ' +
+                                       'threadvar to true try type unit until uses val var varirnt while widechar ' +
+                                       'widestring with word write writeln xor';
+
+       this.regexList = [
+               { regex: new RegExp('\\(\\*[\\s\\S]*?\\*\\)', 'gm'),            css: 'comment' },                       // multiline comments (* *)
+               { regex: new RegExp('{(?!\\$)[\\s\\S]*?}', 'gm'),                       css: 'comment' },                       // multiline comments { }
+               { regex: dp.sh.RegexLib.SingleLineCComments,                            css: 'comment' },                       // one line
+               { regex: dp.sh.RegexLib.SingleQuotedString,                                     css: 'string' },                        // strings
+               { regex: new RegExp('\\{\\$[a-zA-Z]+ .+\\}', 'g'),                      css: 'directive' },                     // Compiler Directives and Region tags
+               { regex: new RegExp('\\b[\\d\\.]+\\b', 'g'),                            css: 'number' },                        // numbers 12345
+               { regex: new RegExp('\\$[a-zA-Z0-9]+\\b', 'g'),                         css: 'number' },                        // numbers $F5D3
+               { regex: new RegExp(this.GetKeywords(keywords), 'gm'),          css: 'keyword' }                        // keyword
+               ];
+
+       this.CssClass = 'dp-delphi';
+       this.Style =    '.dp-delphi .number { color: blue; }' +
+                                       '.dp-delphi .directive { color: #008284; }' +
+                                       '.dp-delphi .vars { color: #000; }';
+}
+
+dp.sh.Brushes.Delphi.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Delphi.Aliases   = ['delphi', 'pascal'];
diff --git a/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushJScript.js b/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushJScript.js
new file mode 100644 (file)
index 0000000..673d001
--- /dev/null
@@ -0,0 +1,22 @@
+dp.sh.Brushes.JScript = function()
+{
+       var keywords =  'abstract boolean break byte case catch char class const continue debugger ' +
+                                       'default delete do double else enum export extends false final finally float ' +
+                                       'for function goto if implements import in instanceof int interface long native ' +
+                                       'new null package private protected public return short static super switch ' +
+                                       'synchronized this throw throws transient true try typeof var void volatile while with';
+
+       this.regexList = [
+               { regex: dp.sh.RegexLib.SingleLineCComments,                            css: 'comment' },                       // one line comments
+               { regex: dp.sh.RegexLib.MultiLineCComments,                                     css: 'comment' },                       // multiline comments
+               { regex: dp.sh.RegexLib.DoubleQuotedString,                                     css: 'string' },                        // double quoted strings
+               { regex: dp.sh.RegexLib.SingleQuotedString,                                     css: 'string' },                        // single quoted strings
+               { regex: new RegExp('^\\s*#.*', 'gm'),                                          css: 'preprocessor' },          // preprocessor tags like #region and #endregion
+               { regex: new RegExp(this.GetKeywords(keywords), 'gm'),          css: 'keyword' }                        // keywords
+               ];
+
+       this.CssClass = 'dp-c';
+}
+
+dp.sh.Brushes.JScript.prototype        = new dp.sh.Highlighter();
+dp.sh.Brushes.JScript.Aliases  = ['js', 'jscript', 'javascript'];
diff --git a/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushJava.js b/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushJava.js
new file mode 100644 (file)
index 0000000..9b1648b
--- /dev/null
@@ -0,0 +1,28 @@
+dp.sh.Brushes.Java = function()
+{
+       var keywords =  'abstract assert boolean break byte case catch char class const ' +
+                       'continue default do double else enum extends ' +
+                       'false final finally float for goto if implements import ' +
+                       'instanceof int interface long native new null ' +
+                       'package private protected public return ' +
+                       'short static strictfp super switch synchronized this throw throws true ' +
+                       'transient try void volatile while';
+
+       this.regexList = [
+               { regex: dp.sh.RegexLib.SingleLineCComments,                                                    css: 'comment' },               // one line comments
+               { regex: dp.sh.RegexLib.MultiLineCComments,                                                             css: 'comment' },               // multiline comments
+               { regex: dp.sh.RegexLib.DoubleQuotedString,                                                             css: 'string' },                // strings
+               { regex: dp.sh.RegexLib.SingleQuotedString,                                                             css: 'string' },                // strings
+               { regex: new RegExp('\\b([\\d]+(\\.[\\d]+)?|0x[a-f0-9]+)\\b', 'gi'),    css: 'number' },                // numbers
+               { regex: new RegExp('(?!\\@interface\\b)\\@[\\$\\w]+\\b', 'g'),                 css: 'annotation' },    // annotation @anno
+               { regex: new RegExp('\\@interface\\b', 'g'),                                                    css: 'keyword' },               // @interface keyword
+               { regex: new RegExp(this.GetKeywords(keywords), 'gm'),                                  css: 'keyword' }                // java keyword
+               ];
+
+       this.CssClass = 'dp-j';
+       this.Style =    '.dp-j .annotation { color: #646464; }' +
+                                       '.dp-j .number { color: #C00000; }';
+}
+
+dp.sh.Brushes.Java.prototype   = new dp.sh.Highlighter();
+dp.sh.Brushes.Java.Aliases     = ['java'];
diff --git a/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushPhp.js b/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushPhp.js
new file mode 100644 (file)
index 0000000..0add7cc
--- /dev/null
@@ -0,0 +1,60 @@
+dp.sh.Brushes.Php = function()
+{
+       var funcs       =       'abs acos acosh addcslashes addslashes ' +
+                                       'array_change_key_case array_chunk array_combine array_count_values array_diff '+
+                                       'array_diff_assoc array_diff_key array_diff_uassoc array_diff_ukey array_fill '+
+                                       'array_filter array_flip array_intersect array_intersect_assoc array_intersect_key '+
+                                       'array_intersect_uassoc array_intersect_ukey array_key_exists array_keys array_map '+
+                                       'array_merge array_merge_recursive array_multisort array_pad array_pop array_product '+
+                                       'array_push array_rand array_reduce array_reverse array_search array_shift '+
+                                       'array_slice array_splice array_sum array_udiff array_udiff_assoc '+
+                                       'array_udiff_uassoc array_uintersect array_uintersect_assoc '+
+                                       'array_uintersect_uassoc array_unique array_unshift array_values array_walk '+
+                                       'array_walk_recursive atan atan2 atanh base64_decode base64_encode base_convert '+
+                                       'basename bcadd bccomp bcdiv bcmod bcmul bindec bindtextdomain bzclose bzcompress '+
+                                       'bzdecompress bzerrno bzerror bzerrstr bzflush bzopen bzread bzwrite ceil chdir '+
+                                       'checkdate checkdnsrr chgrp chmod chop chown chr chroot chunk_split class_exists '+
+                                       'closedir closelog copy cos cosh count count_chars date decbin dechex decoct '+
+                                       'deg2rad delete ebcdic2ascii echo empty end ereg ereg_replace eregi eregi_replace error_log '+
+                                       'error_reporting escapeshellarg escapeshellcmd eval exec exit exp explode extension_loaded '+
+                                       'feof fflush fgetc fgetcsv fgets fgetss file_exists file_get_contents file_put_contents '+
+                                       'fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype '+
+                                       'floatval flock floor flush fmod fnmatch fopen fpassthru fprintf fputcsv fputs fread fscanf '+
+                                       'fseek fsockopen fstat ftell ftok getallheaders getcwd getdate getenv gethostbyaddr gethostbyname '+
+                                       'gethostbynamel getimagesize getlastmod getmxrr getmygid getmyinode getmypid getmyuid getopt '+
+                                       'getprotobyname getprotobynumber getrandmax getrusage getservbyname getservbyport gettext '+
+                                       'gettimeofday gettype glob gmdate gmmktime ini_alter ini_get ini_get_all ini_restore ini_set '+
+                                       'interface_exists intval ip2long is_a is_array is_bool is_callable is_dir is_double '+
+                                       'is_executable is_file is_finite is_float is_infinite is_int is_integer is_link is_long '+
+                                       'is_nan is_null is_numeric is_object is_readable is_real is_resource is_scalar is_soap_fault '+
+                                       'is_string is_subclass_of is_uploaded_file is_writable is_writeable mkdir mktime nl2br '+
+                                       'parse_ini_file parse_str parse_url passthru pathinfo readlink realpath rewind rewinddir rmdir '+
+                                       'round str_ireplace str_pad str_repeat str_replace str_rot13 str_shuffle str_split '+
+                                       'str_word_count strcasecmp strchr strcmp strcoll strcspn strftime strip_tags stripcslashes '+
+                                       'stripos stripslashes stristr strlen strnatcasecmp strnatcmp strncasecmp strncmp strpbrk '+
+                                       'strpos strptime strrchr strrev strripos strrpos strspn strstr strtok strtolower strtotime '+
+                                       'strtoupper strtr strval substr substr_compare';
+
+       var keywords =  'and or xor __FILE__ __LINE__ array as break case ' +
+                                       'cfunction class const continue declare default die do else ' +
+                                       'elseif empty enddeclare endfor endforeach endif endswitch endwhile ' +
+                                       'extends for foreach function include include_once global if ' +
+                                       'new old_function return static switch use require require_once ' +
+                                       'var while __FUNCTION__ __CLASS__ ' +
+                                       '__METHOD__ abstract interface public implements extends private protected throw';
+
+       this.regexList = [
+               { regex: dp.sh.RegexLib.SingleLineCComments,                            css: 'comment' },                       // one line comments
+               { regex: dp.sh.RegexLib.MultiLineCComments,                                     css: 'comment' },                       // multiline comments
+               { regex: dp.sh.RegexLib.DoubleQuotedString,                                     css: 'string' },                        // double quoted strings
+               { regex: dp.sh.RegexLib.SingleQuotedString,                                     css: 'string' },                        // single quoted strings
+               { regex: new RegExp('\\$\\w+', 'g'),                                            css: 'vars' },                          // variables
+               { regex: new RegExp(this.GetKeywords(funcs), 'gmi'),            css: 'func' },                          // functions
+               { regex: new RegExp(this.GetKeywords(keywords), 'gm'),          css: 'keyword' }                        // keyword
+               ];
+
+       this.CssClass = 'dp-c';
+}
+
+dp.sh.Brushes.Php.prototype    = new dp.sh.Highlighter();
+dp.sh.Brushes.Php.Aliases      = ['php'];
diff --git a/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushPython.js b/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushPython.js
new file mode 100644 (file)
index 0000000..d3af47a
--- /dev/null
@@ -0,0 +1,30 @@
+/* Python 2.3 syntax contributed by Gheorghe Milas */
+dp.sh.Brushes.Python = function()
+{
+    var keywords =  'and assert break class continue def del elif else ' +
+                    'except exec finally for from global if import in is ' +
+                    'lambda not or pass print raise return try yield while';
+
+    var special =  'None True False self cls class_'
+
+    this.regexList = [
+        { regex: dp.sh.RegexLib.SingleLinePerlComments, css: 'comment' },
+        { regex: new RegExp("^\\s*@\\w+", 'gm'), css: 'decorator' },
+        { regex: new RegExp("(['\"]{3})([^\\1])*?\\1", 'gm'), css: 'comment' },
+        { regex: new RegExp('"(?!")(?:\\.|\\\\\\"|[^\\""\\n\\r])*"', 'gm'), css: 'string' },
+        { regex: new RegExp("'(?!')*(?:\\.|(\\\\\\')|[^\\''\\n\\r])*'", 'gm'), css: 'string' },
+        { regex: new RegExp("\\b\\d+\\.?\\w*", 'g'), css: 'number' },
+        { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' },
+        { regex: new RegExp(this.GetKeywords(special), 'gm'), css: 'special' },
+        ];
+
+    this.CssClass = 'dp-py';
+       this.Style =    '.dp-py .builtins { color: #ff1493; }' +
+                                       '.dp-py .magicmethods { color: #808080; }' +
+                                       '.dp-py .exceptions { color: brown; }' +
+                                       '.dp-py .types { color: brown; font-style: italic; }' +
+                                       '.dp-py .commonlibs { color: #8A2BE2; font-style: italic; }';
+}
+
+dp.sh.Brushes.Python.prototype  = new dp.sh.Highlighter();
+dp.sh.Brushes.Python.Aliases    = ['py', 'python'];
diff --git a/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushRuby.js b/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushRuby.js
new file mode 100644 (file)
index 0000000..47768af
--- /dev/null
@@ -0,0 +1,28 @@
+/* Ruby 1.8.4 syntax contributed by Erik Peterson */
+dp.sh.Brushes.Ruby = function()
+{
+  var keywords =       'alias and BEGIN begin break case class def define_method defined do each else elsif ' +
+                                       'END end ensure false for if in module new next nil not or raise redo rescue retry return ' +
+                                       'self super then throw true undef unless until when while yield';
+
+  var builtins =       'Array Bignum Binding Class Continuation Dir Exception FalseClass File::Stat File Fixnum Fload ' +
+                                       'Hash Integer IO MatchData Method Module NilClass Numeric Object Proc Range Regexp String Struct::TMS Symbol ' +
+                                       'ThreadGroup Thread Time TrueClass'
+
+       this.regexList = [
+               { regex: dp.sh.RegexLib.SingleLinePerlComments,                 css: 'comment' },       // one line comments
+               { regex: dp.sh.RegexLib.DoubleQuotedString,                             css: 'string' },        // double quoted strings
+               { regex: dp.sh.RegexLib.SingleQuotedString,                             css: 'string' },        // single quoted strings
+               { regex: new RegExp(':[a-z][A-Za-z0-9_]*', 'g'),                css: 'symbol' },        // symbols
+               { regex: new RegExp('(\\$|@@|@)\\w+', 'g'),                             css: 'variable' },      // $global, @instance, and @@class variables
+               { regex: new RegExp(this.GetKeywords(keywords), 'gm'),  css: 'keyword' },       // keywords
+               { regex: new RegExp(this.GetKeywords(builtins), 'gm'),  css: 'builtin' }        // builtins
+               ];
+
+       this.CssClass = 'dp-rb';
+       this.Style =    '.dp-rb .symbol { color: #a70; }' +
+                                       '.dp-rb .variable { color: #a70; font-weight: bold; }';
+}
+
+dp.sh.Brushes.Ruby.prototype = new dp.sh.Highlighter();
+dp.sh.Brushes.Ruby.Aliases = ['ruby', 'rails', 'ror'];
diff --git a/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushSql.js b/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushSql.js
new file mode 100644 (file)
index 0000000..d171260
--- /dev/null
@@ -0,0 +1,42 @@
+dp.sh.Brushes.Sql = function()
+{
+       var funcs       =       'abs avg case cast coalesce convert count current_timestamp ' +
+                                       'current_user day isnull left lower month nullif replace right ' +
+                                       'session_user space substring sum system_user upper user year';
+
+       var keywords =  'absolute action add after alter as asc at authorization begin bigint ' +
+                                       'binary bit by cascade char character check checkpoint close collate ' +
+                                       'column commit committed connect connection constraint contains continue ' +
+                                       'create cube current current_date current_time cursor database date ' +
+                                       'deallocate dec decimal declare default delete desc distinct double drop ' +
+                                       'dynamic else end end-exec escape except exec execute false fetch first ' +
+                                       'float for force foreign forward free from full function global goto grant ' +
+                                       'group grouping having hour ignore index inner insensitive insert instead ' +
+                                       'int integer intersect into is isolation key last level load local max min ' +
+                                       'minute modify move name national nchar next no numeric of off on only ' +
+                                       'open option order out output partial password precision prepare primary ' +
+                                       'prior privileges procedure public read real references relative repeatable ' +
+                                       'restrict return returns revoke rollback rollup rows rule schema scroll ' +
+                                       'second section select sequence serializable set size smallint static ' +
+                                       'statistics table temp temporary then time timestamp to top transaction ' +
+                                       'translation trigger true truncate uncommitted union unique update values ' +
+                                       'varchar varying view when where with work';
+
+       var operators = 'all and any between cross in join like not null or outer some';
+
+       this.regexList = [
+               { regex: new RegExp('--(.*)$', 'gm'),                                           css: 'comment' },                       // one line and multiline comments
+               { regex: dp.sh.RegexLib.DoubleQuotedString,                                     css: 'string' },                        // double quoted strings
+               { regex: dp.sh.RegexLib.SingleQuotedString,                                     css: 'string' },                        // single quoted strings
+               { regex: new RegExp(this.GetKeywords(funcs), 'gmi'),            css: 'func' },                          // functions
+               { regex: new RegExp(this.GetKeywords(operators), 'gmi'),        css: 'op' },                            // operators and such
+               { regex: new RegExp(this.GetKeywords(keywords), 'gmi'),         css: 'keyword' }                        // keyword
+               ];
+
+       this.CssClass = 'dp-sql';
+       this.Style =    '.dp-sql .func { color: #ff1493; }' +
+                                       '.dp-sql .op { color: #808080; }';
+}
+
+dp.sh.Brushes.Sql.prototype    = new dp.sh.Highlighter();
+dp.sh.Brushes.Sql.Aliases      = ['sql'];
diff --git a/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushVb.js b/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushVb.js
new file mode 100644 (file)
index 0000000..a9c376f
--- /dev/null
@@ -0,0 +1,29 @@
+dp.sh.Brushes.Vb = function()
+{
+       var keywords =  'AddHandler AddressOf AndAlso Alias And Ansi As Assembly Auto ' +
+                                       'Boolean ByRef Byte ByVal Call Case Catch CBool CByte CChar CDate ' +
+                                       'CDec CDbl Char CInt Class CLng CObj Const CShort CSng CStr CType ' +
+                                       'Date Decimal Declare Default Delegate Dim DirectCast Do Double Each ' +
+                                       'Else ElseIf End Enum Erase Error Event Exit False Finally For Friend ' +
+                                       'Function Get GetType GoSub GoTo Handles If Implements Imports In ' +
+                                       'Inherits Integer Interface Is Let Lib Like Long Loop Me Mod Module ' +
+                                       'MustInherit MustOverride MyBase MyClass Namespace New Next Not Nothing ' +
+                                       'NotInheritable NotOverridable Object On Option Optional Or OrElse ' +
+                                       'Overloads Overridable Overrides ParamArray Preserve Private Property ' +
+                                       'Protected Public RaiseEvent ReadOnly ReDim REM RemoveHandler Resume ' +
+                                       'Return Select Set Shadows Shared Short Single Static Step Stop String ' +
+                                       'Structure Sub SyncLock Then Throw To True Try TypeOf Unicode Until ' +
+                                       'Variant When While With WithEvents WriteOnly Xor';
+
+       this.regexList = [
+               { regex: new RegExp('\'.*$', 'gm'),                                                     css: 'comment' },                       // one line comments
+               { regex: dp.sh.RegexLib.DoubleQuotedString,                                     css: 'string' },                        // strings
+               { regex: new RegExp('^\\s*#.*', 'gm'),                                          css: 'preprocessor' },          // preprocessor tags like #region and #endregion
+               { regex: new RegExp(this.GetKeywords(keywords), 'gm'),          css: 'keyword' }                        // c# keyword
+               ];
+
+       this.CssClass = 'dp-vb';
+}
+
+dp.sh.Brushes.Vb.prototype     = new dp.sh.Highlighter();
+dp.sh.Brushes.Vb.Aliases       = ['vb', 'vb.net'];
diff --git a/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushXml.js b/mod/assignment/type/program/syntaxhighlighter/Scripts/shBrushXml.js
new file mode 100644 (file)
index 0000000..d0b360f
--- /dev/null
@@ -0,0 +1,70 @@
+dp.sh.Brushes.Xml = function()
+{
+       this.CssClass = 'dp-xml';
+       this.Style =    '.dp-xml .cdata { color: #ff1493; }' +
+                                       '.dp-xml .tag, .dp-xml .tag-name { color: #069; font-weight: bold; }' +
+                                       '.dp-xml .attribute { color: red; }' +
+                                       '.dp-xml .attribute-value { color: blue; }';
+}
+
+dp.sh.Brushes.Xml.prototype    = new dp.sh.Highlighter();
+dp.sh.Brushes.Xml.Aliases      = ['xml', 'xhtml', 'xslt', 'html', 'xhtml'];
+
+dp.sh.Brushes.Xml.prototype.ProcessRegexList = function()
+{
+       function push(array, value)
+       {
+               array[array.length] = value;
+       }
+       
+       /* If only there was a way to get index of a group within a match, the whole XML
+          could be matched with the expression looking something like that:
+       
+          (<!\[CDATA\[\s*.*\s*\]\]>)
+          | (<!--\s*.*\s*?-->)
+          | (<)*(\w+)*\s*(\w+)\s*=\s*(".*?"|'.*?'|\w+)(/*>)*
+          | (</?)(.*?)(/?>)
+       */
+       var index       = 0;
+       var match       = null;
+       var regex       = null;
+
+       // Match CDATA in the following format <![ ... [ ... ]]>
+       // (\&lt;|<)\!\[[\w\s]*?\[(.|\s)*?\]\](\&gt;|>)
+       this.GetMatches(new RegExp('(\&lt;|<)\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\](\&gt;|>)', 'gm'), 'cdata');
+       
+       // Match comments
+       // (\&lt;|<)!--\s*.*\s*?--(\&gt;|>)
+       this.GetMatches(new RegExp('(\&lt;|<)!--\\s*.*\\s*?--(\&gt;|>)', 'gm'), 'comments');
+
+       // Match attributes and their values
+       // (:|\w+)\s*=\s*(".*?"|\'.*?\'|\w+)*
+       regex = new RegExp('([:\\w-\.]+)\\s*=\\s*(".*?"|\'.*?\'|\\w+)*|(\\w+)', 'gm'); // Thanks to Tomi Blinnikka of Yahoo! for fixing namespaces in attributes
+       while((match = regex.exec(this.code)) != null)
+       {
+               if(match[1] == null)
+               {
+                       continue;
+               }
+                       
+               push(this.matches, new dp.sh.Match(match[1], match.index, 'attribute'));
+       
+               // if xml is invalid and attribute has no property value, ignore it     
+               if(match[2] != undefined)
+               {
+                       push(this.matches, new dp.sh.Match(match[2], match.index + match[0].indexOf(match[2]), 'attribute-value'));
+               }
+       }
+
+       // Match opening and closing tag brackets
+       // (\&lt;|<)/*\?*(?!\!)|/*\?*(\&gt;|>)
+       this.GetMatches(new RegExp('(\&lt;|<)/*\\?*(?!\\!)|/*\\?*(\&gt;|>)', 'gm'), 'tag');
+
+       // Match tag names
+       // (\&lt;|<)/*\?*\s*(\w+)
+       regex = new RegExp('(?:\&lt;|<)/*\\?*\\s*([:\\w-\.]+)', 'gm');
+       while((match = regex.exec(this.code)) != null)
+       {
+               push(this.matches, new dp.sh.Match(match[1], match.index + match[0].indexOf(match[1]), 'tag-name'));
+       }
+}
diff --git a/mod/assignment/type/program/syntaxhighlighter/Scripts/shCore.js b/mod/assignment/type/program/syntaxhighlighter/Scripts/shCore.js
new file mode 100644 (file)
index 0000000..e3ee84b
--- /dev/null
@@ -0,0 +1,414 @@
+var dp={sh:{Toolbar:{},Utils:{},RegexLib:{},Brushes:{},Strings:{AboutDialog:"<html><head><title>About...</title></head><body class=\"dp-about\"><table cellspacing=\"0\"><tr><td class=\"copy\"><p class=\"title\">dp.SyntaxHighlighter</div><div class=\"para\">Version: {V}</p><p><a href=\"http://www.dreamprojections.com/syntaxhighlighter/?ref=about\" target=\"_blank\">http://www.dreamprojections.com/syntaxhighlighter</a></p>&copy;2004-2007 Alex Gorbatchev.</td></tr><tr><td class=\"footer\"><input type=\"button\" class=\"close\" value=\"OK\" onClick=\"window.close()\"/></td></tr></table></body></html>"},ClipboardSwf:null,Version:"1.5"}};
+dp.SyntaxHighlighter=dp.sh;
+dp.sh.Toolbar.Commands={ExpandSource:{label:"+ expand source",check:function(_1){
+return _1.collapse;
+},func:function(_2,_3){
+_2.parentNode.removeChild(_2);
+_3.div.className=_3.div.className.replace("collapsed","");
+}},ViewSource:{label:"view plain",func:function(_4,_5){
+var _6=_5.originalCode.replace(/</g,"&lt;");
+var _7=window.open("","_blank","width=750, height=400, location=0, resizable=1, menubar=0, scrollbars=0");
+_7.document.write("<textarea style=\"width:99%;height:99%\">"+_6+"</textarea>");
+_7.document.close();
+}},CopyToClipboard:{label:"copy to clipboard",check:function(){
+return window.clipboardData!=null||dp.sh.ClipboardSwf!=null;
+},func:function(_8,_9){
+var _a=_9.originalCode;
+if(window.clipboardData){
+window.clipboardData.setData("text",_a);
+}else{
+if(dp.sh.ClipboardSwf!=null){
+var _b=_9.flashCopier;
+if(_b==null){
+_b=document.createElement("div");
+_9.flashCopier=_b;
+_9.div.appendChild(_b);
+}
+_b.innerHTML="<embed src=\""+dp.sh.ClipboardSwf+"\" FlashVars=\"clipboard="+encodeURIComponent(_a)+"\" width=\"0\" height=\"0\" type=\"application/x-shockwave-flash\"></embed>";
+}
+}
+alert("The code is in your clipboard now");
+}},PrintSource:{label:"print",func:function(_c,_d){
+var _e=document.createElement("IFRAME");
+var _f=null;
+_e.style.cssText="position:absolute;width:0px;height:0px;left:-500px;top:-500px;";
+document.body.appendChild(_e);
+_f=_e.contentWindow.document;
+dp.sh.Utils.CopyStyles(_f,window.document);
+_f.write("<div class=\""+_d.div.className.replace("collapsed","")+" printing\">"+_d.div.innerHTML+"</div>");
+_f.close();
+_e.contentWindow.focus();
+_e.contentWindow.print();
+alert("Printing...");
+document.body.removeChild(_e);
+}},About:{label:"?",func:function(_10){
+var wnd=window.open("","_blank","dialog,width=300,height=150,scrollbars=0");
+var doc=wnd.document;
+dp.sh.Utils.CopyStyles(doc,window.document);
+doc.write(dp.sh.Strings.AboutDialog.replace("{V}",dp.sh.Version));
+doc.close();
+wnd.focus();
+}}};
+dp.sh.Toolbar.Create=function(_13){
+var div=document.createElement("DIV");
+div.className="tools";
+for(var _15 in dp.sh.Toolbar.Commands){
+var cmd=dp.sh.Toolbar.Commands[_15];
+if(cmd.check!=null&&!cmd.check(_13)){
+continue;
+}
+div.innerHTML+="<a href=\"#\" onclick=\"dp.sh.Toolbar.Command('"+_15+"',this);return false;\">"+cmd.label+"</a>";
+}
+return div;
+};
+dp.sh.Toolbar.Command=function(_17,_18){
+var n=_18;
+while(n!=null&&n.className.indexOf("dp-highlighter")==-1){
+n=n.parentNode;
+}
+if(n!=null){
+dp.sh.Toolbar.Commands[_17].func(_18,n.highlighter);
+}
+};
+dp.sh.Utils.CopyStyles=function(_1a,_1b){
+var _1c=_1b.getElementsByTagName("link");
+for(var i=0;i<_1c.length;i++){
+if(_1c[i].rel.toLowerCase()=="stylesheet"){
+_1a.write("<link type=\"text/css\" rel=\"stylesheet\" href=\""+_1c[i].href+"\"></link>");
+}
+}
+};
+dp.sh.RegexLib={MultiLineCComments:new RegExp("/\\*[\\s\\S]*?\\*/","gm"),SingleLineCComments:new RegExp("//.*$","gm"),SingleLinePerlComments:new RegExp("#.*$","gm"),DoubleQuotedString:new RegExp("\"(?:\\.|(\\\\\\\")|[^\\\"\"])*\"","g"),SingleQuotedString:new RegExp("'(?:\\.|(\\\\\\')|[^\\''])*'","g")};
+dp.sh.Match=function(_1e,_1f,css){
+this.value=_1e;
+this.index=_1f;
+this.length=_1e.length;
+this.css=css;
+};
+dp.sh.Highlighter=function(){
+this.noGutter=false;
+this.addControls=true;
+this.collapse=false;
+this.tabsToSpaces=true;
+this.wrapColumn=80;
+this.showColumns=true;
+};
+dp.sh.Highlighter.SortCallback=function(m1,m2){
+if(m1.index<m2.index){
+return -1;
+}else{
+if(m1.index>m2.index){
+return 1;
+}else{
+if(m1.length<m2.length){
+return -1;
+}else{
+if(m1.length>m2.length){
+return 1;
+}
+}
+}
+}
+return 0;
+};
+dp.sh.Highlighter.prototype.CreateElement=function(_23){
+var _24=document.createElement(_23);
+_24.highlighter=this;
+return _24;
+};
+dp.sh.Highlighter.prototype.GetMatches=function(_25,css){
+var _27=0;
+var _28=null;
+while((_28=_25.exec(this.code))!=null){
+this.matches[this.matches.length]=new dp.sh.Match(_28[0],_28.index,css);
+}
+};
+dp.sh.Highlighter.prototype.AddBit=function(str,css){
+if(str==null||str.length==0){
+return;
+}
+var _2b=this.CreateElement("SPAN");
+str=str.replace(/ /g,"&nbsp;");
+str=str.replace(/</g,"&lt;");
+str=str.replace(/\n/gm,"&nbsp;<br>");
+if(css!=null){
+if((/br/gi).test(str)){
+var _2c=str.split("&nbsp;<br>");
+for(var i=0;i<_2c.length;i++){
+_2b=this.CreateElement("SPAN");
+_2b.className=css;
+_2b.innerHTML=_2c[i];
+this.div.appendChild(_2b);
+if(i+1<_2c.length){
+this.div.appendChild(this.CreateElement("BR"));
+}
+}
+}else{
+_2b.className=css;
+_2b.innerHTML=str;
+this.div.appendChild(_2b);
+}
+}else{
+_2b.innerHTML=str;
+this.div.appendChild(_2b);
+}
+};
+dp.sh.Highlighter.prototype.IsInside=function(_2e){
+if(_2e==null||_2e.length==0){
+return false;
+}
+for(var i=0;i<this.matches.length;i++){
+var c=this.matches[i];
+if(c==null){
+continue;
+}
+if((_2e.index>c.index)&&(_2e.index<c.index+c.length)){
+return true;
+}
+}
+return false;
+};
+dp.sh.Highlighter.prototype.ProcessRegexList=function(){
+for(var i=0;i<this.regexList.length;i++){
+this.GetMatches(this.regexList[i].regex,this.regexList[i].css);
+}
+};
+dp.sh.Highlighter.prototype.ProcessSmartTabs=function(_32){
+var _33=_32.split("\n");
+var _34="";
+var _35=4;
+var tab="\t";
+function InsertSpaces(_37,pos,_39){
+var _3a=_37.substr(0,pos);
+var _3b=_37.substr(pos+1,_37.length);
+var _3c="";
+for(var i=0;i<_39;i++){
+_3c+=" ";
+}
+return _3a+_3c+_3b;
+}
+function ProcessLine(_3e,_3f){
+if(_3e.indexOf(tab)==-1){
+return _3e;
+}
+var pos=0;
+while((pos=_3e.indexOf(tab))!=-1){
+var _41=_3f-pos%_3f;
+_3e=InsertSpaces(_3e,pos,_41);
+}
+return _3e;
+}
+for(var i=0;i<_33.length;i++){
+_34+=ProcessLine(_33[i],_35)+"\n";
+}
+return _34;
+};
+dp.sh.Highlighter.prototype.SwitchToList=function(){
+var _43=this.div.innerHTML.replace(/<(br)\/?>/gi,"\n");
+var _44=_43.split("\n");
+if(this.addControls==true){
+this.bar.appendChild(dp.sh.Toolbar.Create(this));
+}
+if(this.showColumns){
+var div=this.CreateElement("div");
+var _46=this.CreateElement("div");
+var _47=10;
+var i=1;
+while(i<=150){
+if(i%_47==0){
+div.innerHTML+=i;
+i+=(i+"").length;
+}else{
+div.innerHTML+="&middot;";
+i++;
+}
+}
+_46.className="columns";
+_46.appendChild(div);
+this.bar.appendChild(_46);
+}
+for(var i=0,lineIndex=this.firstLine;i<_44.length-1;i++,lineIndex++){
+var li=this.CreateElement("LI");
+var _4b=this.CreateElement("SPAN");
+li.className=(i%2==0)?"alt":"";
+_4b.innerHTML=_44[i]+"&nbsp;";
+li.appendChild(_4b);
+this.ol.appendChild(li);
+}
+this.div.innerHTML="";
+};
+dp.sh.Highlighter.prototype.Highlight=function(_4c){
+function Trim(str){
+return str.replace(/^\s*(.*?)[\s\n]*$/g,"$1");
+}
+function Chop(str){
+return str.replace(/\n*$/,"").replace(/^\n*/,"");
+}
+function Unindent(str){
+var _50=str.split("\n");
+var _51=new Array();
+var _52=new RegExp("^\\s*","g");
+var min=1000;
+for(var i=0;i<_50.length&&min>0;i++){
+if(Trim(_50[i]).length==0){
+continue;
+}
+var _55=_52.exec(_50[i]);
+if(_55!=null&&_55.length>0){
+min=Math.min(_55[0].length,min);
+}
+}
+if(min>0){
+for(var i=0;i<_50.length;i++){
+_50[i]=_50[i].substr(min);
+}
+}
+return _50.join("\n");
+}
+function Copy(_57,_58,_59){
+return _57.substr(_58,_59-_58);
+}
+var pos=0;
+if(_4c==null){
+_4c="";
+}
+this.originalCode=_4c;
+this.code=Chop(Unindent(_4c));
+this.div=this.CreateElement("DIV");
+this.bar=this.CreateElement("DIV");
+this.ol=this.CreateElement("OL");
+this.matches=new Array();
+this.div.className="dp-highlighter";
+this.div.highlighter=this;
+this.bar.className="bar";
+this.ol.start=this.firstLine;
+if(this.CssClass!=null){
+this.ol.className=this.CssClass;
+}
+if(this.collapse){
+this.div.className+=" collapsed";
+}
+if(this.noGutter){
+this.div.className+=" nogutter";
+}
+if(this.tabsToSpaces==true){
+this.code=this.ProcessSmartTabs(this.code);
+}
+this.ProcessRegexList();
+if(this.matches.length==0){
+this.AddBit(this.code,null);
+this.SwitchToList();
+this.div.appendChild(this.ol);
+return;
+}
+this.matches=this.matches.sort(dp.sh.Highlighter.SortCallback);
+for(var i=0;i<this.matches.length;i++){
+if(this.IsInside(this.matches[i])){
+this.matches[i]=null;
+}
+}
+for(var i=0;i<this.matches.length;i++){
+var _5d=this.matches[i];
+if(_5d==null||_5d.length==0){
+continue;
+}
+this.AddBit(Copy(this.code,pos,_5d.index),null);
+this.AddBit(_5d.value,_5d.css);
+pos=_5d.index+_5d.length;
+}
+this.AddBit(this.code.substr(pos),null);
+this.SwitchToList();
+this.div.appendChild(this.bar);
+this.div.appendChild(this.ol);
+};
+dp.sh.Highlighter.prototype.GetKeywords=function(str){
+return "\\b"+str.replace(/ /g,"\\b|\\b")+"\\b";
+};
+dp.sh.HighlightAll=function(_5f,_60,_61,_62,_63,_64){
+function FindValue(){
+var a=arguments;
+for(var i=0;i<a.length;i++){
+if(a[i]==null){
+continue;
+}
+if(typeof (a[i])=="string"&&a[i]!=""){
+return a[i]+"";
+}
+if(typeof (a[i])=="object"&&a[i].value!=""){
+return a[i].value+"";
+}
+}
+return null;
+}
+function IsOptionSet(_67,_68){
+for(var i=0;i<_68.length;i++){
+if(_68[i]==_67){
+return true;
+}
+}
+return false;
+}
+function GetOptionValue(_6a,_6b,_6c){
+var _6d=new RegExp("^"+_6a+"\\[(\\w+)\\]$","gi");
+var _6e=null;
+for(var i=0;i<_6b.length;i++){
+if((_6e=_6d.exec(_6b[i]))!=null){
+return _6e[1];
+}
+}
+return _6c;
+}
+function FindTagsByName(_70,_71,_72){
+var _73=document.getElementsByTagName(_72);
+for(var i=0;i<_73.length;i++){
+if(_73[i].getAttribute("name")==_71){
+_70.push(_73[i]);
+}
+}
+}
+var _75=[];
+var _76=null;
+var _77={};
+var _78="innerHTML";
+FindTagsByName(_75,_5f,"pre");
+FindTagsByName(_75,_5f,"textarea");
+if(_75.length==0){
+return;
+}
+for(var _79 in dp.sh.Brushes){
+var _7a=dp.sh.Brushes[_79].Aliases;
+if(_7a==null){
+continue;
+}
+for(var i=0;i<_7a.length;i++){
+_77[_7a[i]]=_79;
+}
+}
+for(var i=0;i<_75.length;i++){
+var _7d=_75[i];
+var _7e=FindValue(_7d.attributes["class"],_7d.className,_7d.attributes["language"],_7d.language);
+var _7f="";
+if(_7e==null){
+continue;
+}
+_7e=_7e.split(":");
+_7f=_7e[0].toLowerCase();
+if(_77[_7f]==null){
+continue;
+}
+_76=new dp.sh.Brushes[_77[_7f]]();
+_7d.style.display="none";
+_76.noGutter=(_60==null)?IsOptionSet("nogutter",_7e):!_60;
+_76.addControls=(_61==null)?!IsOptionSet("nocontrols",_7e):_61;
+_76.collapse=(_62==null)?IsOptionSet("collapse",_7e):_62;
+_76.showColumns=(_64==null)?IsOptionSet("showcolumns",_7e):_64;
+if(_76.Style){
+document.write("<style>"+_76.Style+"</style>");
+}
+_76.firstLine=(_63==null)?parseInt(GetOptionValue("firstline",_7e,1)):_63;
+_76.Highlight(_7d[_78]);
+_76.source=_7d;
+_7d.parentNode.insertBefore(_76.div,_7d);
+}
+};
+
diff --git a/mod/assignment/type/program/syntaxhighlighter/Scripts/shCore.uncompressed.js b/mod/assignment/type/program/syntaxhighlighter/Scripts/shCore.uncompressed.js
new file mode 100644 (file)
index 0000000..d79c009
--- /dev/null
@@ -0,0 +1,674 @@
+/**
+ * Code Syntax Highlighter.
+ * Version 1.5
+ * Copyright (C) 2004-2007 Alex Gorbatchev.
+ * http://www.dreamprojections.com/syntaxhighlighter/
+ * 
+ * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General 
+ * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) 
+ * any later version.
+ *
+ * This library 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 Lesser General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to 
+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
+ */
+
+//
+// create namespaces
+//
+var dp = {
+       sh :
+       {
+               Toolbar : {},
+               Utils   : {},
+               RegexLib: {},
+               Brushes : {},
+               Strings : {
+                       AboutDialog : '<html><head><title>About...</title></head><body class="dp-about"><table cellspacing="0"><tr><td class="copy"><p class="title">dp.SyntaxHighlighter</div><div class="para">Version: {V}</p><p><a href="http://www.dreamprojections.com/syntaxhighlighter/?ref=about" target="_blank">http://www.dreamprojections.com/syntaxhighlighter</a></p>&copy;2004-2007 Alex Gorbatchev.</td></tr><tr><td class="footer"><input type="button" class="close" value="OK" onClick="window.close()"/></td></tr></table></body></html>'
+               },
+               ClipboardSwf : null,
+               Version : '1.5'
+       }
+};
+
+// make an alias
+dp.SyntaxHighlighter = dp.sh;
+
+//
+// Toolbar functions
+//
+
+dp.sh.Toolbar.Commands = {
+       ExpandSource: {
+               label: '+ expand source',
+               check: function(highlighter) { return highlighter.collapse; },
+               func: function(sender, highlighter)
+               {
+                       sender.parentNode.removeChild(sender);
+                       highlighter.div.className = highlighter.div.className.replace('collapsed', '');
+               }
+       },
+       
+       // opens a new windows and puts the original unformatted source code inside.
+       ViewSource: {
+               label: 'view plain',
+               func: function(sender, highlighter)
+               {
+                       var code = highlighter.originalCode.replace(/</g, '&lt;');
+                       var wnd = window.open('', '_blank', 'width=750, height=400, location=0, resizable=1, menubar=0, scrollbars=0');
+                       wnd.document.write('<textarea style="width:99%;height:99%">' + code + '</textarea>');
+                       wnd.document.close();
+               }
+       },
+       
+       // Copies the original source code in to the clipboard. Uses either IE only method or Flash object if ClipboardSwf is set
+       CopyToClipboard: {
+               label: 'copy to clipboard',
+               check: function() { return window.clipboardData != null || dp.sh.ClipboardSwf != null; },
+               func: function(sender, highlighter)
+               {
+                       var code = highlighter.originalCode;
+                       
+                       if(window.clipboardData)
+                       {
+                               window.clipboardData.setData('text', code);
+                       }
+                       else if(dp.sh.ClipboardSwf != null)
+                       {
+                               var flashcopier = highlighter.flashCopier;
+                               
+                               if(flashcopier == null)
+                               {
+                                       flashcopier = document.createElement('div');
+                                       highlighter.flashCopier = flashcopier;
+                                       highlighter.div.appendChild(flashcopier);
+                               }
+                               
+                               flashcopier.innerHTML = '<embed src="' + dp.sh.ClipboardSwf + '" FlashVars="clipboard='+encodeURIComponent(code)+'" width="0" height="0" type="application/x-shockwave-flash"></embed>';
+                       }
+                       
+                       alert('The code is in your clipboard now');
+               }
+       },
+       
+       // creates an invisible iframe, puts the original source code inside and prints it
+       PrintSource: {
+               label: 'print',
+               func: function(sender, highlighter)
+               {
+                       var iframe = document.createElement('IFRAME');
+                       var doc = null;
+
+                       // this hides the iframe
+                       iframe.style.cssText = 'position:absolute;width:0px;height:0px;left:-500px;top:-500px;';
+                       
+                       document.body.appendChild(iframe);
+                       doc = iframe.contentWindow.document;
+
+                       dp.sh.Utils.CopyStyles(doc, window.document);
+                       doc.write('<div class="' + highlighter.div.className.replace('collapsed', '') + ' printing">' + highlighter.div.innerHTML + '</div>');
+                       doc.close();
+
+                       iframe.contentWindow.focus();
+                       iframe.contentWindow.print();
+                       
+                       alert('Printing...');
+                       
+                       document.body.removeChild(iframe);
+               }
+       },
+       
+       About: {
+               label: '?',
+               func: function(highlighter)
+               {
+                       var wnd = window.open('', '_blank', 'dialog,width=300,height=150,scrollbars=0');
+                       var doc = wnd.document;
+
+                       dp.sh.Utils.CopyStyles(doc, window.document);
+                       
+                       doc.write(dp.sh.Strings.AboutDialog.replace('{V}', dp.sh.Version));
+                       doc.close();
+                       wnd.focus();
+               }
+       }
+};
+
+// creates a <div /> with all toolbar links
+dp.sh.Toolbar.Create = function(highlighter)
+{
+       var div = document.createElement('DIV');
+       
+       div.className = 'tools';
+       
+       for(var name in dp.sh.Toolbar.Commands)
+       {
+               var cmd = dp.sh.Toolbar.Commands[name];
+               
+               if(cmd.check != null && !cmd.check(highlighter))
+                       continue;
+               
+               div.innerHTML += '<a href="#" onclick="dp.sh.Toolbar.Command(\'' + name + '\',this);return false;">' + cmd.label + '</a>';
+       }
+       
+       return div;
+}
+
+// executes toolbar command by name
+dp.sh.Toolbar.Command = function(name, sender)
+{
+       var n = sender;
+       
+       while(n != null && n.className.indexOf('dp-highlighter') == -1)
+               n = n.parentNode;
+       
+       if(n != null)
+               dp.sh.Toolbar.Commands[name].func(sender, n.highlighter);
+}
+
+// copies all <link rel="stylesheet" /> from 'target' window to 'dest'
+dp.sh.Utils.CopyStyles = function(destDoc, sourceDoc)
+{
+       var links = sourceDoc.getElementsByTagName('link');
+
+       for(var i = 0; i < links.length; i++)
+               if(links[i].rel.toLowerCase() == 'stylesheet')
+                       destDoc.write('<link type="text/css" rel="stylesheet" href="' + links[i].href + '"></link>');
+}
+
+//
+// Common reusable regular expressions
+//
+dp.sh.RegexLib = {
+       MultiLineCComments : new RegExp('/\\*[\\s\\S]*?\\*/', 'gm'),
+       SingleLineCComments : new RegExp('//.*$', 'gm'),
+       SingleLinePerlComments : new RegExp('#.*$', 'gm'),
+       DoubleQuotedString : new RegExp('"(?:\\.|(\\\\\\")|[^\\""])*"','g'),
+       SingleQuotedString : new RegExp("'(?:\\.|(\\\\\\')|[^\\''])*'", 'g')
+};
+
+//
+// Match object
+//
+dp.sh.Match = function(value, index, css)
+{
+       this.value = value;
+       this.index = index;
+       this.length = value.length;
+       this.css = css;
+}
+
+//
+// Highlighter object
+//
+dp.sh.Highlighter = function()
+{
+       this.noGutter = false;
+       this.addControls = true;
+       this.collapse = false;
+       this.tabsToSpaces = true;
+       this.wrapColumn = 80;
+       this.showColumns = true;
+}
+
+// static callback for the match sorting
+dp.sh.Highlighter.SortCallback = function(m1, m2)
+{
+       // sort matches by index first
+       if(m1.index < m2.index)
+               return -1;
+       else if(m1.index > m2.index)
+               return 1;
+       else
+       {
+               // if index is the same, sort by length
+               if(m1.length < m2.length)
+                       return -1;
+               else if(m1.length > m2.length)
+                       return 1;
+       }
+       return 0;
+}
+
+dp.sh.Highlighter.prototype.CreateElement = function(name)
+{
+       var result = document.createElement(name);
+       result.highlighter = this;
+       return result;
+}
+
+// gets a list of all matches for a given regular expression
+dp.sh.Highlighter.prototype.GetMatches = function(regex, css)
+{
+       var index = 0;
+       var match = null;
+
+       while((match = regex.exec(this.code)) != null)
+               this.matches[this.matches.length] = new dp.sh.Match(match[0], match.index, css);
+}
+
+dp.sh.Highlighter.prototype.AddBit = function(str, css)
+{
+       if(str == null || str.length == 0)
+               return;
+
+       var span = this.CreateElement('SPAN');
+       
+//     str = str.replace(/&/g, '&amp;');
+       str = str.replace(/ /g, '&nbsp;');
+       str = str.replace(/</g, '&lt;');
+//     str = str.replace(/&lt;/g, '<');
+//     str = str.replace(/>/g, '&gt;');
+       str = str.replace(/\n/gm, '&nbsp;<br>');
+
+       // when adding a piece of code, check to see if it has line breaks in it 
+       // and if it does, wrap individual line breaks with span tags
+       if(css != null)
+       {
+               if((/br/gi).test(str))
+               {
+                       var lines = str.split('&nbsp;<br>');
+                       
+                       for(var i = 0; i < lines.length; i++)
+                       {
+                               span = this.CreateElement('SPAN');
+                               span.className = css;
+                               span.innerHTML = lines[i];
+                               
+                               this.div.appendChild(span);
+                               
+                               // don't add a <BR> for the last line
+                               if(i + 1 < lines.length)
+                                       this.div.appendChild(this.CreateElement('BR'));
+                       }
+               }
+               else
+               {
+                       span.className = css;
+                       span.innerHTML = str;
+                       this.div.appendChild(span);
+               }
+       }
+       else
+       {
+               span.innerHTML = str;
+               this.div.appendChild(span);
+       }
+}
+
+// checks if one match is inside any other match
+dp.sh.Highlighter.prototype.IsInside = function(match)
+{
+       if(match == null || match.length == 0)
+               return false;
+       
+       for(var i = 0; i < this.matches.length; i++)
+       {
+               var c = this.matches[i];
+               
+               if(c == null)
+                       continue;
+
+               if((match.index > c.index) && (match.index < c.index + c.length))
+                       return true;
+       }
+       
+       return false;
+}
+
+dp.sh.Highlighter.prototype.ProcessRegexList = function()
+{
+       for(var i = 0; i < this.regexList.length; i++)
+               this.GetMatches(this.regexList[i].regex, this.regexList[i].css);
+}
+
+dp.sh.Highlighter.prototype.ProcessSmartTabs = function(code)
+{
+       var lines       = code.split('\n');
+       var result      = '';
+       var tabSize     = 4;
+       var tab         = '\t';
+
+       // This function inserts specified amount of spaces in the string
+       // where a tab is while removing that given tab. 
+       function InsertSpaces(line, pos, count)
+       {
+               var left        = line.substr(0, pos);
+               var right       = line.substr(pos + 1, line.length);    // pos + 1 will get rid of the tab
+               var spaces      = '';
+               
+               for(var i = 0; i < count; i++)
+                       spaces += ' ';
+               
+               return left + spaces + right;
+       }
+
+       // This function process one line for 'smart tabs'
+       function ProcessLine(line, tabSize)
+       {
+               if(line.indexOf(tab) == -1)
+                       return line;
+
+               var pos = 0;
+
+               while((pos = line.indexOf(tab)) != -1)
+               {
+                       // This is pretty much all there is to the 'smart tabs' logic.
+                       // Based on the position within the line and size of a tab, 
+                       // calculate the amount of spaces we need to insert.
+                       var spaces = tabSize - pos % tabSize;
+                       
+                       line = InsertSpaces(line, pos, spaces);
+               }
+               
+               return line;
+       }
+
+       // Go through all the lines and do the 'smart tabs' magic.
+       for(var i = 0; i < lines.length; i++)
+               result += ProcessLine(lines[i], tabSize) + '\n';
+       
+       return result;
+}
+
+dp.sh.Highlighter.prototype.SwitchToList = function()
+{
+       // thanks to Lachlan Donald from SitePoint.com for this <br/> tag fix.
+       var html = this.div.innerHTML.replace(/<(br)\/?>/gi, '\n');
+       var lines = html.split('\n');
+       
+       if(this.addControls == true)
+               this.bar.appendChild(dp.sh.Toolbar.Create(this));
+
+       // add columns ruler
+       if(this.showColumns)
+       {
+               var div = this.CreateElement('div');
+               var columns = this.CreateElement('div');
+               var showEvery = 10;
+               var i = 1;
+               
+               while(i <= 150)
+               {
+                       if(i % showEvery == 0)
+                       {
+                               div.innerHTML += i;
+                               i += (i + '').length;
+                       }
+                       else
+                       {
+                               div.innerHTML += '&middot;';
+                               i++;
+                       }
+               }
+               
+               columns.className = 'columns';
+               columns.appendChild(div);
+               this.bar.appendChild(columns);
+       }
+
+       for(var i = 0, lineIndex = this.firstLine; i < lines.length - 1; i++, lineIndex++)
+       {
+               var li = this.CreateElement('LI');
+               var span = this.CreateElement('SPAN');
+               
+               // uses .line1 and .line2 css styles for alternating lines
+               li.className = (i % 2 == 0) ? 'alt' : '';
+               span.innerHTML = lines[i] + '&nbsp;';
+
+               li.appendChild(span);
+               this.ol.appendChild(li);
+       }
+       
+       this.div.innerHTML      = '';
+}
+
+dp.sh.Highlighter.prototype.Highlight = function(code)
+{
+       function Trim(str)
+       {
+               return str.replace(/^\s*(.*?)[\s\n]*$/g, '$1');
+       }
+       
+       function Chop(str)
+       {
+               return str.replace(/\n*$/, '').replace(/^\n*/, '');
+       }
+
+       function Unindent(str)
+       {
+               var lines = str.split('\n');
+               var indents = new Array();
+               var regex = new RegExp('^\\s*', 'g');
+               var min = 1000;
+
+               // go through every line and check for common number of indents
+               for(var i = 0; i < lines.length && min > 0; i++)
+               {
+                       if(Trim(lines[i]).length == 0)
+                               continue;
+                               
+                       var matches = regex.exec(lines[i]);
+
+                       if(matches != null && matches.length > 0)
+                               min = Math.min(matches[0].length, min);
+               }
+
+               // trim minimum common number of white space from the begining of every line
+               if(min > 0)
+                       for(var i = 0; i < lines.length; i++)
+                               lines[i] = lines[i].substr(min);
+
+               return lines.join('\n');
+       }
+       
+       // This function returns a portions of the string from pos1 to pos2 inclusive
+       function Copy(string, pos1, pos2)
+       {
+               return string.substr(pos1, pos2 - pos1);
+       }
+
+       var pos = 0;
+       
+       if(code == null)
+               code = '';
+       
+       this.originalCode = code;
+       this.code = Chop(Unindent(code));
+       this.div = this.CreateElement('DIV');
+       this.bar = this.CreateElement('DIV');
+       this.ol = this.CreateElement('OL');
+       this.matches = new Array();
+
+       this.div.className = 'dp-highlighter';
+       this.div.highlighter = this;
+       
+       this.bar.className = 'bar';
+       
+       // set the first line
+       this.ol.start = this.firstLine;
+
+       if(this.CssClass != null)
+               this.ol.className = this.CssClass;
+
+       if(this.collapse)
+               this.div.className += ' collapsed';
+       
+       if(this.noGutter)
+               this.div.className += ' nogutter';
+
+       // replace tabs with spaces
+       if(this.tabsToSpaces == true)
+               this.code = this.ProcessSmartTabs(this.code);
+
+       this.ProcessRegexList();        
+
+       // if no matches found, add entire code as plain text
+       if(this.matches.length == 0)
+       {
+               this.AddBit(this.code, null);
+               this.SwitchToList();
+               this.div.appendChild(this.ol);
+               return;
+       }
+
+       // sort the matches
+       this.matches = this.matches.sort(dp.sh.Highlighter.SortCallback);
+
+       // The following loop checks to see if any of the matches are inside
+       // of other matches. This process would get rid of highligted strings
+       // inside comments, keywords inside strings and so on.
+       for(var i = 0; i < this.matches.length; i++)
+               if(this.IsInside(this.matches[i]))
+                       this.matches[i] = null;
+
+       // Finally, go through the final list of matches and pull the all
+       // together adding everything in between that isn't a match.
+       for(var i = 0; i < this.matches.length; i++)
+       {
+               var match = this.matches[i];
+
+               if(match == null || match.length == 0)
+                       continue;
+
+               this.AddBit(Copy(this.code, pos, match.index), null);
+               this.AddBit(match.value, match.css);
+
+               pos = match.index + match.length;
+       }
+       
+       this.AddBit(this.code.substr(pos), null);
+
+       this.SwitchToList();
+       this.div.appendChild(this.bar);
+       this.div.appendChild(this.ol);
+}
+
+dp.sh.Highlighter.prototype.GetKeywords = function(str) 
+{
+       return '\\b' + str.replace(/ /g, '\\b|\\b') + '\\b';
+}
+
+// highlightes all elements identified by name and gets source code from specified property
+dp.sh.HighlightAll = function(name, showGutter /* optional */, showControls /* optional */, collapseAll /* optional */, firstLine /* optional */, showColumns /* optional */)
+{
+       function FindValue()
+       {
+               var a = arguments;
+               
+               for(var i = 0; i < a.length; i++)
+               {
+                       if(a[i] == null)
+                               continue;
+                               
+                       if(typeof(a[i]) == 'string' && a[i] != '')
+                               return a[i] + '';
+               
+                       if(typeof(a[i]) == 'object' && a[i].value != '')
+                               return a[i].value + '';
+               }
+               
+               return null;
+       }
+       
+       function IsOptionSet(value, list)
+       {
+               for(var i = 0; i < list.length; i++)
+                       if(list[i] == value)
+                               return true;
+               
+               return false;
+       }
+       
+       function GetOptionValue(name, list, defaultValue)
+       {
+               var regex = new RegExp('^' + name + '\\[(\\w+)\\]$', 'gi');
+               var matches = null;
+
+               for(var i = 0; i < list.length; i++)
+                       if((matches = regex.exec(list[i])) != null)
+                               return matches[1];
+               
+               return defaultValue;
+       }
+       
+       function FindTagsByName(list, name, tagName)
+       {
+               var tags = document.getElementsByTagName(tagName);
+
+               for(var i = 0; i < tags.length; i++)
+                       if(tags[i].getAttribute('name') == name)
+                               list.push(tags[i]);
+       }
+
+       var elements = [];
+       var highlighter = null;
+       var registered = {};
+       var propertyName = 'innerHTML';
+
+       // for some reason IE doesn't find <pre/> by name, however it does see them just fine by tag name... 
+       FindTagsByName(elements, name, 'pre');
+       FindTagsByName(elements, name, 'textarea');
+
+       if(elements.length == 0)
+               return;
+
+       // register all brushes
+       for(var brush in dp.sh.Brushes)
+       {
+               var aliases = dp.sh.Brushes[brush].Aliases;
+
+               if(aliases == null)
+                       continue;
+               
+               for(var i = 0; i < aliases.length; i++)
+                       registered[aliases[i]] = brush;
+       }
+
+       for(var i = 0; i < elements.length; i++)
+       {
+               var element = elements[i];
+               var options = FindValue(
+                               element.attributes['class'], element.className, 
+                               element.attributes['language'], element.language
+                               );
+               var language = '';
+               
+               if(options == null)
+                       continue;
+               
+               options = options.split(':');
+               
+               language = options[0].toLowerCase();
+
+               if(registered[language] == null)
+                       continue;
+               
+               // instantiate a brush
+               highlighter = new dp.sh.Brushes[registered[language]]();
+               
+               // hide the original element
+               element.style.display = 'none';
+
+               highlighter.noGutter = (showGutter == null) ? IsOptionSet('nogutter', options) : !showGutter;
+               highlighter.addControls = (showControls == null) ? !IsOptionSet('nocontrols', options) : showControls;
+               highlighter.collapse = (collapseAll == null) ? IsOptionSet('collapse', options) : collapseAll;
+               highlighter.showColumns = (showColumns == null) ? IsOptionSet('showcolumns', options) : showColumns;
+
+               // write out custom brush style
+               if(highlighter.Style)
+                       document.write('<style>' + highlighter.Style + '</style>');
+               
+               // first line idea comes from Andrew Collington, thanks!
+               highlighter.firstLine = (firstLine == null) ? parseInt(GetOptionValue('firstline', options, 1)) : firstLine;
+
+               highlighter.Highlight(element[propertyName]);
+               
+               highlighter.source = element;
+
+               element.parentNode.insertBefore(highlighter.div, element);
+       }       
+}
diff --git a/mod/assignment/type/program/syntaxhighlighter/Styles/SyntaxHighlighter.css b/mod/assignment/type/program/syntaxhighlighter/Styles/SyntaxHighlighter.css
new file mode 100644 (file)
index 0000000..709058d
--- /dev/null
@@ -0,0 +1,158 @@
+.dp-highlighter
+{
+       font-family: "Consolas", "Courier New", Courier, mono;
+       font-size: 12px;
+       background-color: #E7E5DC;
+       width: 99%;
+       overflow: auto;
+       margin: 18px 0px 18px 0px;
+       padding-top: 1px; /* adds a little border on top when controls are hidden */
+}
+
+.dp-highlighter .bar
+{
+       padding-left: 45px;
+}
+
+.dp-highlighter.collapsed .bar, 
+.dp-highlighter.nogutter .bar 
+{
+       padding-left: 0px;
+}
+
+.dp-highlighter ol 
+{
+       list-style: decimal; /* for ie */
+       list-style: decimal-leading-zero; /* better look for others */
+       background-color: #fff;
+       margin: 0px 0px 1px 45px; /* 1px bottom margin seems to fix occasional Firefox scrolling */
+       padding: 0px;
+       color: #5C5C5C;
+}
+
+.dp-highlighter.nogutter ol
+{
+       list-style-type: none !important; 
+       margin-left: 0px; 
+}
+
+.dp-highlighter ol li,
+.dp-highlighter .columns div
+{
+       border-left: 3px solid #6CE26C;
+       background-color: #f8f8f8;
+       padding-left: 10px;
+       line-height: 14px;
+}
+
+.dp-highlighter.nogutter ol li,
+.dp-highlighter.nogutter .columns div
+{
+       border: 0;
+}
+
+.dp-highlighter .columns 
+{
+       color: gray;
+       overflow: hidden;
+       width: 100%;
+}
+
+.dp-highlighter .columns div 
+{
+       padding-bottom: 5px;
+}
+
+.dp-highlighter ol li.alt 
+{
+       background-color: #fff; 
+}
+
+.dp-highlighter ol li span 
+{
+       color: Black;
+}
+
+/* Adjust some properties when collapsed */
+
+.dp-highlighter.collapsed ol 
+{
+       margin: 0px;
+}
+
+.dp-highlighter.collapsed ol li 
+{ 
+       display: none;
+}
+
+/* Additional modifications when in print-view */
+
+.dp-highlighter.printing 
+{
+       border: none;
+}
+
+.dp-highlighter.printing .tools 
+{
+       display: none !important;
+}
+
+.dp-highlighter.printing li 
+{
+       display: list-item !important;
+}
+
+/* Styles for the tools */
+
+.dp-highlighter .tools 
+{
+       padding: 3px 8px 3px 10px;
+       font: 9px Verdana, Geneva, Arial, Helvetica, sans-serif;
+       color: silver;
+       background-color: #f8f8f8;
+       text-align1: right;
+       padding-bottom: 10px;
+       border-left: 3px solid #6CE26C;
+}
+
+.dp-highlighter.nogutter .tools
+{
+       border-left: 0;
+}
+
+.dp-highlighter.collapsed .tools
+{
+       border-bottom: 0;
+}
+
+.dp-highlighter .tools a
+{
+       font-size: 9px;
+       color: #a0a0a0;
+       text-decoration: none;
+       margin-right: 10px;
+}
+
+.dp-highlighter .tools a:hover
+{
+       color: red;
+       text-decoration: underline;
+}
+
+/* About dialog styles */
+
+.dp-about { background-color: #fff; margin: 0px; padding: 0px; }
+.dp-about table { width: 100%; height: 100%; font-size: 11px; font-family: Tahoma, Verdana, Arial, sans-serif !important; }
+.dp-about td { padding: 10px; vertical-align: top; }
+.dp-about .copy { border-bottom: 1px solid #ACA899; height: 95%; }
+.dp-about .title { color: red; font-weight: bold; }
+.dp-about .para { margin: 0 0 4px 0; }
+.dp-about .footer { background-color: #ECEADB; border-top: 1px solid #fff; text-align: right; }
+.dp-about .close { font-size: 11px; font-family: Tahoma, Verdana, Arial, sans-serif !important; background-color: #ECEADB; width: 60px; height: 22px; }
+
+/* Language specific styles */
+
+.dp-highlighter .comment, .dp-highlighter .comments { color: #008200; }
+.dp-highlighter .string { color: blue; }
+.dp-highlighter .keyword { color: #069; font-weight: bold; }
+.dp-highlighter .preprocessor { color: gray; }
diff --git a/mod/assignment/type/program/syntaxhighlighter/Styles/TestPages.css b/mod/assignment/type/program/syntaxhighlighter/Styles/TestPages.css
new file mode 100644 (file)
index 0000000..ec8f851
--- /dev/null
@@ -0,0 +1,63 @@
+@import url(undohtml.css);
+
+body
+{
+       background-color: #fff;
+       font-family: Verdana, Arial, Helvetica, sans-serif;
+       font-size: 12px;
+       padding: 20px;
+}
+
+h1, h2, h3 { font-weight: normal; margin: 0px 0px 10px 0px; }
+h1 { font-size: 200%; border-bottom: 5px solid #f0f0f0; padding-bottom: 5px; font-family: "Trebuchet MS", Trebuchet, Verdana, sans-serif; }
+h2 { font-size: 150%; margin-left: 20%; }
+h3 { font-size: 120%; }
+
+li a
+{
+       color: blue;
+       text-decoration: none;
+}
+
+li a:hover
+{
+       background-color: #FFFF00 !important;
+}
+
+li a:visited
+{
+       background-color: #ececec;
+}
+
+.layout
+{
+       position: relative;
+}
+
+.column1
+{
+       width: 15%;
+       border-right: 15px solid silver;
+       background-color: #f0f0f0;
+       padding-top: 10px;
+       padding-bottom: 10px;
+}
+
+.column1 h3
+{
+       margin-left: 10px;
+}
+
+.column2
+{
+       position: absolute;
+       left: 20%;
+       top: 0px;
+       width: 75%;
+}
+
+.footer
+{
+       margin-top: 20px;
+       width: 15%;
+}
\ No newline at end of file
diff --git a/mod/assignment/type/program/syntaxhighlighter/Templates/Test.dwt b/mod/assignment/type/program/syntaxhighlighter/Templates/Test.dwt
new file mode 100644 (file)
index 0000000..a041ff9
--- /dev/null
@@ -0,0 +1,80 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+<title>dp.SyntaxHighlighter Tests</title>
+<link type="text/css" rel="stylesheet" href="../Styles/SyntaxHighlighter.css"></link>
+<link href="../Styles/TestPages.css" rel="stylesheet" type="text/css">
+</head>
+
+<body>
+
+<h1>dp.SyntaxHighlighter 1.4.1 Tests and Samples (<a href="http://www.dreamprojections.com/syntaxhighlighter/">dreamprojections.com/syntaxhighlighter</a>)</h1>
+
+<h2><!-- TemplateBeginEditable name="Title" -->Title<!-- TemplateEndEditable --></h2>
+
+<div class="layout">
+
+<div class="column1">
+       <h3>Languages:</h3>
+       <ol>
+               <li><a href="../CSharp.html">C#</a></li>
+               <li><a href="../CSS.html">CSS</a></li>
+               <li><a href="../Cpp.html">C++</a></li>
+               <li><a href="../Delphi.html">Delphi</a></li>
+               <li><a href="../Java.html">Java</a></li>
+               <li><a href="../JavaScript.html">JavaScript</a></li>
+               <li><a href="../PHP.html">PHP</a></li>
+               <li><a href="../Python.html">Python</a></li>
+               <li><a href="../Ruby.html">Ruby</a></li>
+               <li><a href="../SQL.html">SQL</a></li>
+               <li><a href="../VB.html">Visual Basic</a></li>
+               <li><a href="../XML.html">XML / HTML</a></li>
+       </ol>
+       <h3>Features:</h3>
+       <ol>
+               <li><a href="../SmartTabs.html">Smart tabs</a></li>
+               <li><a href="../FirstLine.html">First line</a> </li>
+               <li><a href="../CollapseCode.html">Expand code</a></li>
+           <li><a href="../ShowColumns.html">Show columns</a></li>
+           <li><a href="../NoGutter.html">No gutter</a></li>
+           <li><a href="../NoControls.html">No controls</a></li>
+       </ol>
+</div>
+
+<div class="column2">
+Text body before.
+<hr/>
+<!-- TemplateBeginEditable name="Code" -->
+<pre name="code" class="">
+</pre>
+<!-- TemplateEndEditable -->
+<hr/>
+Text body after.
+</div>
+</div>
+
+<div class="footer">
+Copyright 2004-2007 Alex Gorbatchev.<br/>
+</div>
+
+<script class="javascript" src="../Scripts/shCore.js"></script>
+<script class="javascript" src="../Scripts/shBrushCSharp.js"></script>
+<script class="javascript" src="../Scripts/shBrushPhp.js"></script>
+<script class="javascript" src="../Scripts/shBrushJScript.js"></script>
+<script class="javascript" src="../Scripts/shBrushJava.js"></script>
+<script class="javascript" src="../Scripts/shBrushVb.js"></script>
+<script class="javascript" src="../Scripts/shBrushSql.js"></script>
+<script class="javascript" src="../Scripts/shBrushXml.js"></script>
+<script class="javascript" src="../Scripts/shBrushDelphi.js"></script>
+<script class="javascript" src="../Scripts/shBrushPython.js"></script>
+<script class="javascript" src="../Scripts/shBrushRuby.js"></script>
+<script class="javascript" src="../Scripts/shBrushCss.js"></script>
+<script class="javascript" src="../Scripts/shBrushCpp.js"></script>
+<script class="javascript">
+dp.SyntaxHighlighter.ClipboardSwf = 'Scripts/clipboard.swf';
+dp.SyntaxHighlighter.HighlightAll('code');
+</script>
+
+</body>
+</html>