From aa3e4bdec76266e3ddad388f74694c878ffa7055 Mon Sep 17 00:00:00 2001 From: Jerome Mouneyrac Date: Mon, 28 Oct 2013 13:48:22 +0800 Subject: [PATCH] MDL-42028 EditPDF: replace the loading icon by a progress bar --- mod/assign/feedback/editpdf/ajax_progress.php | 72 ++++++++++ .../editpdf/classes/document_services.php | 58 ++++++-- .../feedback/editpdf/classes/renderer.php | 14 +- .../feedback/editpdf/classes/widget.php | 7 +- .../lang/en/assignfeedback_editpdf.php | 1 + mod/assign/feedback/editpdf/locallib.php | 7 +- mod/assign/feedback/editpdf/styles.css | 128 ++++++++++++++++++ ...dle-assignfeedback_editpdf-editor-debug.js | 80 +++++++++-- ...oodle-assignfeedback_editpdf-editor-min.js | 14 +- .../moodle-assignfeedback_editpdf-editor.js | 80 +++++++++-- .../editpdf/yui/src/editor/js/editor.js | 78 +++++++++-- .../editpdf/yui/src/editor/js/globals.js | 2 + 12 files changed, 496 insertions(+), 45 deletions(-) create mode 100644 mod/assign/feedback/editpdf/ajax_progress.php diff --git a/mod/assign/feedback/editpdf/ajax_progress.php b/mod/assign/feedback/editpdf/ajax_progress.php new file mode 100644 index 00000000000..184a957550c --- /dev/null +++ b/mod/assign/feedback/editpdf/ajax_progress.php @@ -0,0 +1,72 @@ +. + +/** + * Process concurrent ajax request. + * ALL RETURNED INFO IS PUBLIC. + * + * @package assignfeedback_editpdf + * @copyright 2013 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +define('AJAX_SCRIPT', true); +// To be able to process concurrent ajax request with the generate pdf ajax request we can not use cookie. +define('NO_MOODLE_COOKIES', true); + +use \assignfeedback_editpdf\document_services; +require_once('../../../../config.php'); + +try { + $assignmentid = required_param('assignmentid', PARAM_INT); + $userid = required_param('userid', PARAM_INT); + $attemptnumber = required_param('attemptnumber', PARAM_INT); + + // Retrieve the assignments. + require_once($CFG->dirroot . '/mod/assign/locallib.php'); + $cm = get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST); + $context = context_module::instance($cm->id); + $assignment = new assign($context, null, null); + + // Get the generated images from file API call. + $grade = $assignment->get_user_grade($userid, false, $attemptnumber); + + // Check we found a grade. + if (empty($grade)) { + throw new coding_exception('grade not found'); + } + + $component = 'assignfeedback_editpdf'; + $filearea = document_services::PAGE_IMAGE_FILEAREA; + $filepath = '/'; + $fs = get_file_storage(); + $files = $fs->get_directory_files($context->id, $component, $filearea, $grade->id, $filepath); + + // The important security part: we ONLY RETURN the total NUMBER of generated images. + echo $OUTPUT->header(); + echo json_encode(count($files)); + echo $OUTPUT->footer(); +} catch (Exception $e) { + // This should never happened! + // Return a 500 HTTP header so Y.io gets it as a failure. + if (substr(php_sapi_name(), 0, 3) == 'cgi') { + header("Status: 500 Internal Server Error"); + } else { + header('HTTP/1.0 500 Internal Server Error'); + } + throw new moodle_exception('An exception was caught but can not be returned for security purpose. + To easily debug, comment the try catch.'); +} diff --git a/mod/assign/feedback/editpdf/classes/document_services.php b/mod/assign/feedback/editpdf/classes/document_services.php index 9fe930a511b..4acef806563 100644 --- a/mod/assign/feedback/editpdf/classes/document_services.php +++ b/mod/assign/feedback/editpdf/classes/document_services.php @@ -242,6 +242,47 @@ class document_services { return $file; } + /** + * This function will return the number of pages of a pdf. + * @param int|\assign $assignment + * @param int $userid + * @param int $attemptnumber (-1 means latest attempt) + * @return int number of pages + */ + public static function page_number_for_attempt($assignment, $userid, $attemptnumber) { + global $CFG; + + require_once($CFG->libdir . '/pdflib.php'); + + $assignment = self::get_assignment_from_param($assignment); + + if (!$assignment->can_view_submission($userid)) { + \print_error('nopermission'); + } + + // Get a combined pdf file from all submitted pdf files. + $file = self::get_combined_pdf_for_attempt($assignment, $userid, $attemptnumber); + if (!$file) { + \print_error('Could not generate combined pdf.'); + } + + // Store the combined pdf file somewhere to be opened by tcpdf. + $tmpdir = \make_temp_directory('assignfeedback_editpdf/pagetotal/' + . self::hash($assignment, $userid, $attemptnumber)); + $combined = $tmpdir . '/' . self::COMBINED_PDF_FILENAME; + $file->copy_content_to($combined); // Copy the file. + + // Get the total number of pages. + $pdf = new pdf(); + $pagecount = $pdf->set_pdf($combined); + + // Delete temporary folders and files. + @unlink($combined); + @rmdir($tmpdir); + + return $pagecount; + } + /** * This function will generate and return a list of the page images from a pdf. * @param int|\assign $assignment @@ -263,7 +304,7 @@ class document_services { // Need to generate the page images - first get a combined pdf. $file = self::get_combined_pdf_for_attempt($assignment, $userid, $attemptnumber); if (!$file) { - throw \moodle_exception('Could not generate combined pdf.'); + throw new \moodle_exception('Could not generate combined pdf.'); } $tmpdir = \make_temp_directory('assignfeedback_editpdf/pageimages/' . self::hash($assignment, $userid, $attemptnumber)); @@ -275,14 +316,8 @@ class document_services { $pdf->set_image_folder($tmpdir); $pagecount = $pdf->set_pdf($combined); - $i = 0; - $images = array(); - for ($i = 0; $i < $pagecount; $i++) { - $images[$i] = $pdf->get_image($i); - } $grade = $assignment->get_user_grade($userid, true, $attemptnumber); - $files = array(); $record = new \stdClass(); $record->contextid = $assignment->get_context()->id; $record->component = 'assignfeedback_editpdf'; @@ -291,11 +326,14 @@ class document_services { $record->filepath = '/'; $fs = \get_file_storage(); - foreach ($images as $index => $image) { + $files = array(); + for ($i = 0; $i < $pagecount; $i++) { + $image = $pdf->get_image($i); $record->filename = basename($image); - $files[$index] = $fs->create_file_from_pathname($record, $tmpdir . '/' . $image); + $files[$i] = $fs->create_file_from_pathname($record, $tmpdir . '/' . $image); @unlink($tmpdir . '/' . $image); } + @unlink($combined); @rmdir($tmpdir); @@ -407,7 +445,7 @@ class document_services { // Need to generate the page images - first get a combined pdf. $file = self::get_combined_pdf_for_attempt($assignment, $userid, $attemptnumber); if (!$file) { - throw \moodle_exception('Could not generate combined pdf.'); + throw new \moodle_exception('Could not generate combined pdf.'); } $tmpdir = \make_temp_directory('assignfeedback_editpdf/final/' . self::hash($assignment, $userid, $attemptnumber)); diff --git a/mod/assign/feedback/editpdf/classes/renderer.php b/mod/assign/feedback/editpdf/classes/renderer.php index e77bf36ddd4..a5d32e00f79 100644 --- a/mod/assign/feedback/editpdf/classes/renderer.php +++ b/mod/assign/feedback/editpdf/classes/renderer.php @@ -198,7 +198,16 @@ class assignfeedback_editpdf_renderer extends plugin_renderer_base { 'pageheader'); $body = $pageheader; - $loading = $this->pix_icon('i/loading', get_string('loadingeditor', 'assignfeedback_editpdf'), 'moodle', array('class'=>'loading')); + // Loading progress bar. + $progressbar = html_writer::div('', 'bar', array('style' => 'width: 0%')); + $progressbar = html_writer::div($progressbar, 'progress progress-info progress-striped active', + array('title' => get_string('loadingeditor', 'assignfeedback_editpdf'), + 'role'=> 'progressbar', 'aria-valuenow' => 0, 'aria-valuemin' => 0, + 'aria-valuemax' => 100)); + $progressbarlabel = html_writer::div(get_string('generatingpdf', 'assignfeedback_editpdf'), + 'progressbarlabel'); + $loading = html_writer::div($progressbar . $progressbarlabel, 'loading'); + $canvas = html_writer::div($loading, 'drawingcanvas'); $body .= html_writer::div($canvas, 'drawingregion'); @@ -214,7 +223,8 @@ class assignfeedback_editpdf_renderer extends plugin_renderer_base { 'userid'=>$widget->userid, 'attemptnumber'=>$widget->attemptnumber, 'stampfiles'=>$widget->stampfiles, - 'readonly'=>$widget->readonly)); + 'readonly'=>$widget->readonly, + 'pagetotal'=>$widget->pagetotal)); $this->page->requires->yui_module('moodle-assignfeedback_editpdf-editor', 'M.assignfeedback_editpdf.editor.init', diff --git a/mod/assign/feedback/editpdf/classes/widget.php b/mod/assign/feedback/editpdf/classes/widget.php index 5fba7259c09..83b17c706c1 100644 --- a/mod/assign/feedback/editpdf/classes/widget.php +++ b/mod/assign/feedback/editpdf/classes/widget.php @@ -47,6 +47,8 @@ class assignfeedback_editpdf_widget implements renderable { public $stampfiles = array(); /** @var bool $readonly */ public $readonly = true; + /** @var integer $pagetotal */ + public $pagetotal = 0; /** * Constructor @@ -57,8 +59,10 @@ class assignfeedback_editpdf_widget implements renderable { * @param string $downloadfilename - Name of the generated pdf. * @param string[] $stampfiles - The file names of the stamps. * @param bool $readonly - Show the readonly interface (no tools). + * @param integer $pagetotal - The total number of pages. */ - public function __construct($assignment, $userid, $attemptnumber, $downloadurl, $downloadfilename, $stampfiles, $readonly) { + public function __construct($assignment, $userid, $attemptnumber, $downloadurl, + $downloadfilename, $stampfiles, $readonly, $pagetotal) { $this->assignment = $assignment; $this->userid = $userid; $this->attemptnumber = $attemptnumber; @@ -66,5 +70,6 @@ class assignfeedback_editpdf_widget implements renderable { $this->downloadfilename = $downloadfilename; $this->stampfiles = $stampfiles; $this->readonly = $readonly; + $this->pagetotal = $pagetotal; } } diff --git a/mod/assign/feedback/editpdf/lang/en/assignfeedback_editpdf.php b/mod/assign/feedback/editpdf/lang/en/assignfeedback_editpdf.php index ed7ffcc3305..c6f18d33af9 100644 --- a/mod/assign/feedback/editpdf/lang/en/assignfeedback_editpdf.php +++ b/mod/assign/feedback/editpdf/lang/en/assignfeedback_editpdf.php @@ -66,6 +66,7 @@ $string['pagenumber'] = 'Page {$a}'; $string['pagexofy'] = 'Page {$a->page} of {$a->total}'; $string['pen'] = 'Pen'; $string['pluginname'] = 'Annotate PDF'; +$string['generatingpdf'] = 'Generating the PDF...'; $string['rectangle'] = 'Rectangle'; $string['red'] = 'Red'; $string['result'] = 'Result:'; diff --git a/mod/assign/feedback/editpdf/locallib.php b/mod/assign/feedback/editpdf/locallib.php index c7372bfd2f0..1df66f32555 100644 --- a/mod/assign/feedback/editpdf/locallib.php +++ b/mod/assign/feedback/editpdf/locallib.php @@ -131,6 +131,10 @@ class assign_feedback_editpdf extends assign_feedback_plugin { $filename = $feedbackfile->get_filename(); } + // Retrieve total number of pages. + $pagetotal = document_services::page_number_for_attempt($this->assignment->get_instance()->id, + $userid, + $attempt); $widget = new assignfeedback_editpdf_widget($this->assignment->get_instance()->id, $userid, @@ -138,7 +142,8 @@ class assign_feedback_editpdf extends assign_feedback_plugin { $url, $filename, $stampfiles, - $readonly); + $readonly, + $pagetotal); return $widget; } diff --git a/mod/assign/feedback/editpdf/styles.css b/mod/assign/feedback/editpdf/styles.css index 71326eac176..1e5e1228031 100644 --- a/mod/assign/feedback/editpdf/styles.css +++ b/mod/assign/feedback/editpdf/styles.css @@ -240,3 +240,131 @@ ul.assignfeedback_editpdf_menu { padding-left: 20px; padding-right: 4px; } +.assignfeedback_editpdf_widget .drawingcanvas .loading .progressbarlabel { + text-align: center; +} + +/** + * Bootstrap 2.3.2 progress bar css. + * Required for none bootstrap theme. + */ + +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-moz-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-ms-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-o-keyframes progress-bar-stripes { + from { + background-position: 0 0; + } + to { + background-position: 40px 0; + } +} + +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f7f7f7; + background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); + background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9); + background-repeat: repeat-x; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.progress .bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + color: #ffffff; + text-align: center; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e90d2; + background-image: -moz-linear-gradient(top, #149bdf, #0480be); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); + background-image: -webkit-linear-gradient(top, #149bdf, #0480be); + background-image: -o-linear-gradient(top, #149bdf, #0480be); + background-image: linear-gradient(to bottom, #149bdf, #0480be); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: width 0.6s ease; + -moz-transition: width 0.6s ease; + -o-transition: width 0.6s ease; + transition: width 0.6s ease; +} + +.progress .bar + .bar { + -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); +} + +.progress-striped .bar { + background-color: #149bdf; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + -moz-background-size: 40px 40px; + -o-background-size: 40px 40px; + background-size: 40px 40px; +} + +.progress.active .bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -moz-animation: progress-bar-stripes 2s linear infinite; + -ms-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} \ No newline at end of file diff --git a/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-debug.js b/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-debug.js index e5d01dac207..ef4dff08e69 100644 --- a/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-debug.js +++ b/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-debug.js @@ -21,6 +21,7 @@ YUI.add('moodle-assignfeedback_editpdf-editor', function (Y, NAME) { * @module moodle-assignfeedback_editpdf-editor */ var AJAXBASE = M.cfg.wwwroot + '/mod/assign/feedback/editpdf/ajax.php', + AJAXBASEPROGRESS = M.cfg.wwwroot + '/mod/assign/feedback/editpdf/ajax_progress.php', CSS = { DIALOGUE : 'assignfeedback_editpdf_widget' }, @@ -32,6 +33,7 @@ var AJAXBASE = M.cfg.wwwroot + '/mod/assign/feedback/editpdf/ajax.php', SEARCHCOMMENTSLIST : '.assignfeedback_editpdf_commentsearch ul', PAGESELECT : '.' + CSS.DIALOGUE + ' .navigate-page-select', LOADINGICON : '.' + CSS.DIALOGUE + ' .loading', + PROGRESSBARCONTAINER : '.' + CSS.DIALOGUE + ' .progress-info.progress-striped', DRAWINGREGION : '.' + CSS.DIALOGUE + ' .drawingregion', DRAWINGCANVAS : '.' + CSS.DIALOGUE + ' .drawingcanvas', SAVE : '.' + CSS.DIALOGUE + ' .savebutton', @@ -3122,30 +3124,88 @@ EDITOR.prototype = { */ load_all_pages : function() { var ajaxurl = AJAXBASE, - config; + config, + checkconversionstatus, + ajax_error_total; config = { method: 'get', context: this, sync: false, data : { - 'sesskey' : M.cfg.sesskey, - 'action' : 'loadallpages', - 'userid' : this.get('userid'), - 'attemptnumber' : this.get('attemptnumber'), - 'assignmentid' : this.get('assignmentid') + sesskey : M.cfg.sesskey, + action : 'loadallpages', + userid : this.get('userid'), + attemptnumber : this.get('attemptnumber'), + assignmentid : this.get('assignmentid') }, on: { success: function(tid, response) { this.all_pages_loaded(response.responseText); }, failure: function(tid, response) { - return M.core.exception(response.responseText); + return new M.core.exception(response.responseText); } } }; Y.io(ajaxurl, config); + + // If pages are not loaded, check PDF conversion status for the progress bar. + if (this.pagecount <= 0) { + checkconversionstatus = { + method: 'get', + context: this, + sync: false, + data : { + sesskey : M.cfg.sesskey, + action : 'conversionstatus', + userid : this.get('userid'), + attemptnumber : this.get('attemptnumber'), + assignmentid : this.get('assignmentid') + }, + on: { + success: function(tid, response) { + ajax_error_total = 0; + if (this.pagecount === 0) { + var pagetotal = this.get('pagetotal'); + + // Update the progress bar. + var progressbarcontainer = Y.one(SELECTOR.PROGRESSBARCONTAINER); + var progressbar = progressbarcontainer.one('.bar'); + if (progressbar) { + // Calculate progress. + var progress = (response.response / pagetotal) * 100; + progressbar.setStyle('width', progress + '%'); + progressbarcontainer.setAttribute('aria-valuenow', progress); + } + + // New ajax request delayed of a second. + Y.later(1000, this, function () { + Y.io(AJAXBASEPROGRESS, checkconversionstatus); + }); + } + }, + failure: function(tid, response) { + ajax_error_total = ajax_error_total + 1; + // We only continue on error if the all pages were not generated, + // and if the ajax call did not produce 5 errors in the row. + if (this.pagecount === 0 && ajax_error_total < 5) { + Y.later(1000, this, function () { + Y.io(AJAXBASEPROGRESS, checkconversionstatus); + }); + } + return new M.core.exception(response.responseText); + } + } + }; + // We start the AJAX "generated page total number" call a second later to give a chance to + // the AJAX "combined pdf generation" call to clean the previous submission images. + Y.later(1000, this, function () { + ajax_error_total = 0; + Y.io(AJAXBASEPROGRESS, checkconversionstatus); + }); + } }, /** @@ -3612,7 +3672,7 @@ EDITOR.prototype = { } }, failure: function(tid, response) { - return M.core.exception(response.responseText); + return new M.core.exception(response.responseText); } } }; @@ -3807,6 +3867,10 @@ Y.extend(EDITOR, Y.Base, EDITOR.prototype, { stampfiles : { validator : Y.Lang.isArray, value : '' + }, + pagetotal : { + validator : Y.Lang.isInteger, + value : 0 } } }); diff --git a/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-min.js b/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-min.js index 57cfd6d72b3..b2fc17b8bec 100644 --- a/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-min.js +++ b/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-min.js @@ -1,7 +1,7 @@ -YUI.add("moodle-assignfeedback_editpdf-editor",function(e,t){var n=M.cfg.wwwroot+"/mod/assign/feedback/editpdf/ajax.php",r={DIALOGUE:"assignfeedback_editpdf_widget"},s={PREVIOUSBUTTON:"."+r.DIALOGUE+" .navigate-previous-button",NEXTBUTTON:"."+r.DIALOGUE+" .navigate-next-button",SEARCHCOMMENTSBUTTON:"."+r.DIALOGUE+" .searchcommentsbutton",SEARCHFILTER:".assignfeedback_editpdf_commentsearch input",SEARCHCOMMENTSLIST:".assignfeedback_editpdf_commentsearch ul",PAGESELECT:"."+r.DIALOGUE+" .navigate-page-select",LOADINGICON:"."+r.DIALOGUE+" .loading",DRAWINGREGION:"."+r.DIALOGUE+" .drawingregion",DRAWINGCANVAS:"."+r.DIALOGUE+" .drawingcanvas",SAVE:"."+r.DIALOGUE+" .savebutton",COMMENTCOLOURBUTTON:"."+r.DIALOGUE+" .commentcolourbutton",COMMENTMENU:" .commentdrawable a",ANNOTATIONCOLOURBUTTON:"."+r.DIALOGUE+" .annotationcolourbutton",DELETEANNOTATIONBUTTON:"."+r.DIALOGUE+" .deleteannotationbutton",UNSAVEDCHANGESDIV:".assignfeedback_editpdf_unsavedchanges",STAMPSBUTTON:"."+r.DIALOGUE+" .currentstampbutton",DIALOGUE:"."+r.DIALOGUE},o="rgba(200, 200, 255, 0.9)",u="rgba(200, 200, 255, 0.5)",a={white:"rgb(255,255,255)",yellow:"rgb(255,255,176)",red:"rgb(255,176,176)",green:"rgb(176,255,176)",blue:"rgb(208,208,255)",clear:"rgba(255,255,255, 0)"},f={white:"rgb(255,255,255)",yellow:"rgb(255,255,0)",red:"rgb(255,0,0)",green:"rgb(0,255,0)",blue:"rgb(0,0,255)",black:"rgb(0,0,0)"},l=300,c={comment:"."+r.DIALOGUE+" .commentbutton",pen:"."+r.DIALOGUE+" .penbutton",line:"."+r.DIALOGUE+" .linebutton",rectangle:"."+r.DIALOGUE+" .rectanglebutton",oval:"."+r.DIALOGUE+" .ovalbutton",stamp:"."+r.DIALOGUE+" .stampbutton",select:"."+r.DIALOGUE+" .selectbutton",highlight:"."+r.DIALOGUE+" .highlightbutton"},h=4;POINT=function(e,t){this.x=parseInt(e,10),this.y=parseInt(t,10),this.clip=function(e){return this.xe.x+e.width&&(this.x=e.x+e.width),this.ye.y+e.height&&(this.y=e.y+e.height),this}},M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.point=POINT,RECT=function(e,t,n,r){this.x=e,this.y=t,this.width=n,this.height=r,this.bound=function(e){var t=0,n=0,r=0,i=0,s=0,o;for(s=0;sn||s===0)n=o.x;if(o.yi||s===0)i=o.y}return this.x=t,this.y=r,this.width=n-t,this.height=i-r,this}},M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.rect=RECT,EDIT=function(){this.start=!1,this.end=!1,this.starttime=0,this.annotationstart=!1,this.tool="comment",this.commentcolour="yellow",this.annotationcolour="red",this.stamp="",this.path=[]},M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.edit=EDIT,DRAWABLE=function(e){this.editor=e,this.shapes=[],this.nodes=[],this.erase=function(){if(this.shapes)while(this.shapes.length>0)this.editor.graphic.removeShape(this.shapes.pop());if(this.nodes)while(this.nodes.length>0)this.nodes.pop().remove()}},M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.drawable=DRAWABLE,ANNOTATION=function(e){ANNOTATION.superclass.constructor.apply(this,[e])},ANNOTATION.NAME="annotation",ANNOTATION.ATTRS={},e.extend(ANNOTATION,e.Base,{editor:null,gradeid:0,pageno:0,x:0,y:0,endx:0,endy:0,path:"",type:"rect",colour:"red",drawable:!1,initializer:function(e){this.editor=e.editor||null,this.gradeid=parseInt(e.gradeid,10)||0,this.pageno=parseInt(e.pageno,10)||0,this.x=parseInt(e.x,10)||0,this.y=parseInt(e.y,10)||0,this.endx=parseInt(e.endx,10)||0,this.endy=parseInt(e.endy,10)||0,this.path=e.path||"",this.type=e.type||"rect",this.colour=e.colour||"red",this.drawable=!1},clean:function(){return{gradeid:this.gradeid,x:parseInt(this.x,10),y:parseInt(this.y,10),endx:parseInt(this.endx,10),endy:parseInt(this.endy,10),type:this.type,path:this.path,pageno:this.pageno,colour:this.colour}},draw_highlight:function(){var t,n=e.one(s.DRAWINGREGION),r=e.one(s.DRAWINGCANVAS).getXY(),i;if(this.editor.currentannotation===this){t=new M.assignfeedback_editpdf.rect,t.bound([new M.assignfeedback_editpdf.point(this.x,this.y),new M.assignfeedback_editpdf.point(this.endx,this.endy)]),i=this.editor.graphic.addShape({type:e.Rect,width:t.width,height:t.height,stroke:{weight:h,color:o},fill:{color:u},x:t.x,y:t.y}),this.drawable.shapes.push(i);var a=e.Node.create(''),f=e.Node.create('');a.setAttrs({alt:M.util.get_string("deleteannotation","assignfeedback_editpdf")}),a.setStyles({backgroundColor:"white"}),f.addClass("deleteannotationbutton"),f.append(a),n.append(f),f.setData("annotation",this),f.setStyle("zIndex","200"),f.on("click",this.remove,this),f.on("key",this.remove,"space,enter",this),f.setX(r[0]+t.x+t.width-18),f.setY(r[1]+t.y+6),this.drawable.nodes.push(f)}return this.drawable},draw:function(){return this.draw_highlight(),this.drawable},remove:function(){var e;e=this.editor.pages[this.editor.currentpage].annotations;for(i=0;i"),r.setStyles({display:"inline-block",backgroundImage:"url("+this.editor.get_stamp_image_url(this.path)+")",width:this.endx-this.x,height:this.endy-this.y,backgroundSize:"100% 100%",zIndex:50}),n.append(r),r.setX(i.x),r.setY(i.y),r.on("gesturemovestart",this.editor.edit_start,null,this.editor),r.on("gesturemove",this.editor.edit_move,null,this.editor),r.on("gesturemoveend",this.editor.edit_end,null,this.editor),t.nodes.push(r),this.drawable=t,ANNOTATIONSTAMP.superclass.draw.apply(this)},draw_current_edit:function(t){var n=new M.assignfeedback_editpdf.rect,r=new M.assignfeedback_editpdf.drawable(this.editor),i=e.one(s.DRAWINGREGION),o,u;return n.bound([t.start,t.end]),u=this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(n.x,n.y)),o=e.Node.create("
"),o.setStyles({display:"inline-block",backgroundImage:"url("+this.editor.get_stamp_image_url(t.stamp)+")",width:n.width,height:n.height,backgroundSize:"100% 100%",zIndex:50}),i.append(o),o.setX(u.x),o.setY(u.y),r.nodes.push(o),r},init_from_edit:function(e){var t=new M.assignfeedback_editpdf.rect;t.bound([e.start,e.end]),t.width<40&&(t.width=40),t.height<40&&(t.height=40),this.gradeid=this.editor.get("gradeid"),this.pageno=this.editor.currentpage,this.x=t.x,this.y=t.y,this.endx=t.x+t.width,this.endy=t.y+t.height,this.colour=e.annotationcolour,this.path=e.stamp},move:function(e,t){var n=e-this.x,r=t-this.y;this.x+=n,this.y+=r,this.endx+=n,this.endy+=r,this.drawable&&this.drawable.erase(),this.editor.drawables.push(this.draw())}}),M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.annotationstamp=ANNOTATIONSTAMP;var p="Dropdown menu",d;d=function(e){e.draggable=!1,e.centered=!1,e.width="auto",e.lightbox=!1,e.visible=!1,e.zIndex=100,e.footerContent="",d.superclass.constructor.apply(this,[e])},e.extend(d,M.core.dialogue,{initializer:function(t){var n,r,i,s;d.superclass.initializer.call(this,t),s=this.get("boundingBox"),s.addClass("assignfeedback_editpdf_dropdown"),n=this.get("buttonNode"),r=this.bodyNode,i=e.Node.create("

"),i.addClass("accesshide"),i.setHTML(this.get("headerText")),r.prepend(i),r.on("clickoutside",function(e){this.get("visible")&&e.target!==n&&e.target.ancestor()!==n&&(e.preventDefault(),this.hide())},this),n.on("click",this.show,this),n.on("key",this.show,"enter,space",this)},show:function(){var t=this.get("buttonNode");result=d.superclass.show.call(this),this.align(t,[e.WidgetPositionAlign.TL,e.WidgetPositionAlign.BL])}},{NAME:p,ATTRS:{headerText:{value:""},buttonNode:{value:null}}}),M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.dropdown=d;var v="Colourpicker",m;m=function(e){m.superclass.constructor.apply(this,[e])},e.extend(m,M.assignfeedback_editpdf.dropdown,{initializer:function(t){var n=e.Node.create('