~ubuntu-branches/ubuntu/utopic/moodle/utopic

« back to all changes in this revision

Viewing changes to mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor.js

  • Committer: Package Import Robot
  • Author(s): Thijs Kinkhorst
  • Date: 2014-05-12 16:10:38 UTC
  • mfrom: (36.1.3 sid)
  • Revision ID: package-import@ubuntu.com-20140512161038-puyqf65k4e0s8ytz
Tags: 2.6.3-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
YUI.add('moodle-assignfeedback_editpdf-editor', function (Y, NAME) {
 
2
 
 
3
// This file is part of Moodle - http://moodle.org/
 
4
//
 
5
// Moodle is free software: you can redistribute it and/or modify
 
6
// it under the terms of the GNU General Public License as published by
 
7
// the Free Software Foundation, either version 3 of the License, or
 
8
// (at your option) any later version.
 
9
//
 
10
// Moodle is distributed in the hope that it will be useful,
 
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
// GNU General Public License for more details.
 
14
//
 
15
// You should have received a copy of the GNU General Public License
 
16
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
17
 
 
18
/**
 
19
 * A list of globals used by this module.
 
20
 *
 
21
 * @module moodle-assignfeedback_editpdf-editor
 
22
 */
 
23
var AJAXBASE = M.cfg.wwwroot + '/mod/assign/feedback/editpdf/ajax.php',
 
24
    AJAXBASEPROGRESS = M.cfg.wwwroot + '/mod/assign/feedback/editpdf/ajax_progress.php',
 
25
    CSS = {
 
26
        DIALOGUE : 'assignfeedback_editpdf_widget'
 
27
    },
 
28
    SELECTOR = {
 
29
        PREVIOUSBUTTON : '.' + CSS.DIALOGUE + ' .navigate-previous-button',
 
30
        NEXTBUTTON : '.' + CSS.DIALOGUE + ' .navigate-next-button',
 
31
        SEARCHCOMMENTSBUTTON : '.' + CSS.DIALOGUE + ' .searchcommentsbutton',
 
32
        SEARCHFILTER : '.assignfeedback_editpdf_commentsearch input',
 
33
        SEARCHCOMMENTSLIST : '.assignfeedback_editpdf_commentsearch ul',
 
34
        PAGESELECT : '.' + CSS.DIALOGUE + ' .navigate-page-select',
 
35
        LOADINGICON : '.' + CSS.DIALOGUE + ' .loading',
 
36
        PROGRESSBARCONTAINER : '.' + CSS.DIALOGUE + ' .progress-info.progress-striped',
 
37
        DRAWINGREGION : '.' + CSS.DIALOGUE + ' .drawingregion',
 
38
        DRAWINGCANVAS : '.' + CSS.DIALOGUE + ' .drawingcanvas',
 
39
        SAVE : '.' + CSS.DIALOGUE + ' .savebutton',
 
40
        COMMENTCOLOURBUTTON : '.' + CSS.DIALOGUE + ' .commentcolourbutton',
 
41
        COMMENTMENU : ' .commentdrawable a',
 
42
        ANNOTATIONCOLOURBUTTON : '.' + CSS.DIALOGUE + ' .annotationcolourbutton',
 
43
        DELETEANNOTATIONBUTTON : '.' + CSS.DIALOGUE + ' .deleteannotationbutton',
 
44
        UNSAVEDCHANGESDIV : '.assignfeedback_editpdf_unsavedchanges',
 
45
        STAMPSBUTTON : '.' + CSS.DIALOGUE + ' .currentstampbutton',
 
46
        DIALOGUE : '.' + CSS.DIALOGUE
 
47
    },
 
48
    SELECTEDBORDERCOLOUR = 'rgba(200, 200, 255, 0.9)',
 
49
    SELECTEDFILLCOLOUR = 'rgba(200, 200, 255, 0.5)',
 
50
    COMMENTTEXTCOLOUR = 'rgb(51, 51, 51)',
 
51
    COMMENTCOLOUR = {
 
52
        'white' : 'rgb(255,255,255)',
 
53
        'yellow' : 'rgb(255,236,174)',
 
54
        'red' : 'rgb(249,181,179)',
 
55
        'green' : 'rgb(214,234,178)',
 
56
        'blue' : 'rgb(203,217,237)',
 
57
        'clear' : 'rgba(255,255,255, 0)'
 
58
    },
 
59
    ANNOTATIONCOLOUR = {
 
60
        'white' : 'rgb(255,255,255)',
 
61
        'yellow' : 'rgb(255,207,53)',
 
62
        'red' : 'rgb(239,69,64)',
 
63
        'green' : 'rgb(152,202,62)',
 
64
        'blue' : 'rgb(125,159,211)',
 
65
        'black' : 'rgb(51,51,51)'
 
66
    },
 
67
    CLICKTIMEOUT = 300,
 
68
    TOOLSELECTOR = {
 
69
        'comment': '.' + CSS.DIALOGUE + ' .commentbutton',
 
70
        'pen': '.' + CSS.DIALOGUE + ' .penbutton',
 
71
        'line': '.' + CSS.DIALOGUE + ' .linebutton',
 
72
        'rectangle': '.' + CSS.DIALOGUE + ' .rectanglebutton',
 
73
        'oval': '.' + CSS.DIALOGUE + ' .ovalbutton',
 
74
        'stamp': '.' + CSS.DIALOGUE + ' .stampbutton',
 
75
        'select': '.' + CSS.DIALOGUE + ' .selectbutton',
 
76
        'highlight': '.' + CSS.DIALOGUE + ' .highlightbutton'
 
77
    },
 
78
    STROKEWEIGHT = 4;
 
79
// This file is part of Moodle - http://moodle.org/
 
80
//
 
81
// Moodle is free software: you can redistribute it and/or modify
 
82
// it under the terms of the GNU General Public License as published by
 
83
// the Free Software Foundation, either version 3 of the License, or
 
84
// (at your option) any later version.
 
85
//
 
86
// Moodle is distributed in the hope that it will be useful,
 
87
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
88
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
89
// GNU General Public License for more details.
 
90
//
 
91
// You should have received a copy of the GNU General Public License
 
92
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
93
 
 
94
/**
 
95
 * Class representing a 2d point.
 
96
 *
 
97
 * @module moodle-assignfeedback_editpdf-editor
 
98
 */
 
99
 
 
100
/**
 
101
 * POINT
 
102
 *
 
103
 * @namespace M.assignfeedback_editpdf
 
104
 * @param int x
 
105
 * @param int y
 
106
 * @class point
 
107
 */
 
108
POINT = function(x, y) {
 
109
 
 
110
    /**
 
111
     * X coordinate.
 
112
     * @property x
 
113
     * @type int
 
114
     * @public
 
115
     */
 
116
    this.x = parseInt(x, 10);
 
117
 
 
118
    /**
 
119
     * Y coordinate.
 
120
     * @property y
 
121
     * @type int
 
122
     * @public
 
123
     */
 
124
    this.y = parseInt(y, 10);
 
125
 
 
126
    /**
 
127
     * Clip this point to the rect
 
128
     * @method clip
 
129
     * @param M.assignfeedback_editpdf.point
 
130
     * @public
 
131
     */
 
132
    this.clip = function(bounds) {
 
133
        if (this.x < bounds.x) {
 
134
            this.x = bounds.x;
 
135
        }
 
136
        if (this.x > (bounds.x + bounds.width)) {
 
137
            this.x = bounds.x + bounds.width;
 
138
        }
 
139
        if (this.y < bounds.y) {
 
140
            this.y = bounds.y;
 
141
        }
 
142
        if (this.y > (bounds.y + bounds.height)) {
 
143
            this.y = bounds.y + bounds.height;
 
144
        }
 
145
        // For chaining.
 
146
        return this;
 
147
    };
 
148
};
 
149
 
 
150
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
151
M.assignfeedback_editpdf.point = POINT;
 
152
// This file is part of Moodle - http://moodle.org/
 
153
//
 
154
// Moodle is free software: you can redistribute it and/or modify
 
155
// it under the terms of the GNU General Public License as published by
 
156
// the Free Software Foundation, either version 3 of the License, or
 
157
// (at your option) any later version.
 
158
//
 
159
// Moodle is distributed in the hope that it will be useful,
 
160
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
161
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
162
// GNU General Public License for more details.
 
163
//
 
164
// You should have received a copy of the GNU General Public License
 
165
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
166
 
 
167
/**
 
168
 * Class representing a 2d rect.
 
169
 *
 
170
 * @module moodle-assignfeedback_editpdf-editor
 
171
 */
 
172
 
 
173
/**
 
174
 * RECT
 
175
 *
 
176
 * @namespace M.assignfeedback_editpdf
 
177
 * @param int x
 
178
 * @param int y
 
179
 * @param int width
 
180
 * @param int height
 
181
 * @class rect
 
182
 */
 
183
RECT = function(x, y, width, height) {
 
184
 
 
185
    /**
 
186
     * X coordinate.
 
187
     * @property x
 
188
     * @type int
 
189
     * @public
 
190
     */
 
191
    this.x = x;
 
192
 
 
193
    /**
 
194
     * Y coordinate.
 
195
     * @property y
 
196
     * @type int
 
197
     * @public
 
198
     */
 
199
    this.y = y;
 
200
 
 
201
    /**
 
202
     * Width
 
203
     * @property width
 
204
     * @type int
 
205
     * @public
 
206
     */
 
207
    this.width = width;
 
208
 
 
209
    /**
 
210
     * Height
 
211
     * @property height
 
212
     * @type int
 
213
     * @public
 
214
     */
 
215
    this.height = height;
 
216
 
 
217
    /**
 
218
     * Set this rect to represent the smallest possible rectangle containing this list of points.
 
219
     * @method bounds
 
220
     * @param M.assignfeedback_editpdf.point[]
 
221
     * @public
 
222
     */
 
223
    this.bound = function(points) {
 
224
        var minx = 0,
 
225
            maxx = 0,
 
226
            miny = 0,
 
227
            maxy = 0,
 
228
            i = 0,
 
229
            point;
 
230
 
 
231
        for (i = 0; i < points.length; i++) {
 
232
            point = points[i];
 
233
            if (point.x < minx || i === 0) {
 
234
                minx = point.x;
 
235
            }
 
236
            if (point.x > maxx || i === 0) {
 
237
                maxx = point.x;
 
238
            }
 
239
            if (point.y < miny || i === 0) {
 
240
                miny = point.y;
 
241
            }
 
242
            if (point.y > maxy || i === 0) {
 
243
                maxy = point.y;
 
244
            }
 
245
        }
 
246
        this.x = minx;
 
247
        this.y = miny;
 
248
        this.width = maxx - minx;
 
249
        this.height = maxy - miny;
 
250
        // Allow chaining.
 
251
        return this;
 
252
    };
 
253
 
 
254
    /**
 
255
     * Checks if rect has min width.
 
256
     * @method has_min_width
 
257
     * @return bool true if width is more than 5px.
 
258
     * @public
 
259
     */
 
260
    this.has_min_width = function() {
 
261
        return (this.width >= 5);
 
262
    };
 
263
 
 
264
    /**
 
265
     * Checks if rect has min height.
 
266
     * @method has_min_height
 
267
     * @return bool true if height is more than 5px.
 
268
     * @public
 
269
     */
 
270
    this.has_min_height = function() {
 
271
        return (this.height >= 5);
 
272
    };
 
273
 
 
274
    /**
 
275
     * Set min. width of annotation bound.
 
276
     * @method set_min_width
 
277
     * @public
 
278
     */
 
279
    this.set_min_width = function() {
 
280
        this.width = 5;
 
281
    };
 
282
 
 
283
    /**
 
284
     * Set min. height of annotation bound.
 
285
     * @method set_min_height
 
286
     * @public
 
287
     */
 
288
    this.set_min_height = function() {
 
289
        this.height = 5;
 
290
    };
 
291
};
 
292
 
 
293
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
294
M.assignfeedback_editpdf.rect = RECT;
 
295
// This file is part of Moodle - http://moodle.org/
 
296
//
 
297
// Moodle is free software: you can redistribute it and/or modify
 
298
// it under the terms of the GNU General Public License as published by
 
299
// the Free Software Foundation, either version 3 of the License, or
 
300
// (at your option) any later version.
 
301
//
 
302
// Moodle is distributed in the hope that it will be useful,
 
303
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
304
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
305
// GNU General Public License for more details.
 
306
//
 
307
// You should have received a copy of the GNU General Public License
 
308
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
309
 
 
310
/**
 
311
 * Class representing a partially completed edit operation.
 
312
 *
 
313
 * @module moodle-assignfeedback_editpdf-editor
 
314
 */
 
315
 
 
316
/**
 
317
 * EDIT
 
318
 *
 
319
 * @namespace M.assignfeedback_editpdf
 
320
 * @class edit
 
321
 */
 
322
EDIT = function() {
 
323
 
 
324
    /**
 
325
     * Starting point for the edit.
 
326
     * @property start
 
327
     * @type M.assignfeedback_editpdf.point|false
 
328
     * @public
 
329
     */
 
330
    this.start = false;
 
331
 
 
332
    /**
 
333
     * Finishing point for the edit.
 
334
     * @property end
 
335
     * @type M.assignfeedback_editpdf.point|false
 
336
     * @public
 
337
     */
 
338
    this.end = false;
 
339
 
 
340
    /**
 
341
     * Starting time for the edit.
 
342
     * @property starttime
 
343
     * @type int
 
344
     * @public
 
345
     */
 
346
    this.starttime = 0;
 
347
 
 
348
    /**
 
349
     * Starting point for the currently selected annotation.
 
350
     * @property annotationstart
 
351
     * @type M.assignfeedback_editpdf.point|false
 
352
     * @public
 
353
     */
 
354
    this.annotationstart = false;
 
355
 
 
356
    /**
 
357
     * The currently selected tool
 
358
     * @property tool
 
359
     * @type String
 
360
     * @public
 
361
     */
 
362
    this.tool = "comment";
 
363
 
 
364
    /**
 
365
     * The currently comment colour
 
366
     * @property commentcolour
 
367
     * @type String
 
368
     * @public
 
369
     */
 
370
    this.commentcolour = 'yellow';
 
371
 
 
372
    /**
 
373
     * The currently annotation colour
 
374
     * @property annotationcolour
 
375
     * @type String
 
376
     * @public
 
377
     */
 
378
    this.annotationcolour = 'red';
 
379
 
 
380
    /**
 
381
     * The current stamp image.
 
382
     * @property stamp
 
383
     * @type String
 
384
     * @public
 
385
     */
 
386
    this.stamp = '';
 
387
 
 
388
    /**
 
389
     * List of points the the current drawing path.
 
390
     * @property path
 
391
     * @type M.assignfeedback_editpdf.point[]
 
392
     * @public
 
393
     */
 
394
    this.path = [];
 
395
};
 
396
 
 
397
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
398
M.assignfeedback_editpdf.edit = EDIT;
 
399
// This file is part of Moodle - http://moodle.org/
 
400
//
 
401
// Moodle is free software: you can redistribute it and/or modify
 
402
// it under the terms of the GNU General Public License as published by
 
403
// the Free Software Foundation, either version 3 of the License, or
 
404
// (at your option) any later version.
 
405
//
 
406
// Moodle is distributed in the hope that it will be useful,
 
407
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
408
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
409
// GNU General Public License for more details.
 
410
//
 
411
// You should have received a copy of the GNU General Public License
 
412
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
413
 
 
414
/**
 
415
 * Class representing a drawable thing which contains both
 
416
 * Y.Nodes, and Y.Shapes.
 
417
 *
 
418
 * @module moodle-assignfeedback_editpdf-editor
 
419
 */
 
420
 
 
421
/**
 
422
 * DRAWABLE
 
423
 *
 
424
 * @namespace M.assignfeedback_editpdf
 
425
 * @param M.assignfeedback_editpdf.editor editor
 
426
 * @class drawable
 
427
 */
 
428
DRAWABLE = function(editor) {
 
429
 
 
430
    /**
 
431
     * Reference to M.assignfeedback_editpdf.editor.
 
432
     * @property editor
 
433
     * @type M.assignfeedback_editpdf.editor
 
434
     * @public
 
435
     */
 
436
    this.editor = editor;
 
437
 
 
438
    /**
 
439
     * Array of Y.Shape
 
440
     * @property shapes
 
441
     * @type Y.Shape[]
 
442
     * @public
 
443
     */
 
444
    this.shapes = [];
 
445
 
 
446
    /**
 
447
     * Array of Y.Node
 
448
     * @property nodes
 
449
     * @type Y.Node[]
 
450
     * @public
 
451
     */
 
452
    this.nodes = [];
 
453
 
 
454
    /**
 
455
     * Delete the shapes from the drawable.
 
456
     * @protected
 
457
     * @method erase_drawable
 
458
     */
 
459
    this.erase = function() {
 
460
        if (this.shapes) {
 
461
            while (this.shapes.length > 0) {
 
462
                this.editor.graphic.removeShape(this.shapes.pop());
 
463
            }
 
464
        }
 
465
        if (this.nodes) {
 
466
            while (this.nodes.length > 0) {
 
467
                this.nodes.pop().remove();
 
468
            }
 
469
        }
 
470
    };
 
471
 
 
472
};
 
473
 
 
474
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
475
M.assignfeedback_editpdf.drawable = DRAWABLE;
 
476
// This file is part of Moodle - http://moodle.org/
 
477
//
 
478
// Moodle is free software: you can redistribute it and/or modify
 
479
// it under the terms of the GNU General Public License as published by
 
480
// the Free Software Foundation, either version 3 of the License, or
 
481
// (at your option) any later version.
 
482
//
 
483
// Moodle is distributed in the hope that it will be useful,
 
484
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
485
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
486
// GNU General Public License for more details.
 
487
//
 
488
// You should have received a copy of the GNU General Public License
 
489
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
490
 
 
491
/**
 
492
 * Class representing a list of annotations.
 
493
 *
 
494
 * @module moodle-assignfeedback_editpdf-editor
 
495
 */
 
496
ANNOTATION = function(config) {
 
497
    ANNOTATION.superclass.constructor.apply(this, [config]);
 
498
};
 
499
 
 
500
ANNOTATION.NAME = "annotation";
 
501
ANNOTATION.ATTRS = {};
 
502
 
 
503
Y.extend(ANNOTATION, Y.Base, {
 
504
    /**
 
505
     * Reference to M.assignfeedback_editpdf.editor.
 
506
     * @property editor
 
507
     * @type M.assignfeedback_editpdf.editor
 
508
     * @public
 
509
     */
 
510
    editor : null,
 
511
 
 
512
    /**
 
513
     * Grade id
 
514
     * @property gradeid
 
515
     * @type Int
 
516
     * @public
 
517
     */
 
518
    gradeid : 0,
 
519
 
 
520
    /**
 
521
     * Comment page number
 
522
     * @property pageno
 
523
     * @type Int
 
524
     * @public
 
525
     */
 
526
    pageno : 0,
 
527
 
 
528
    /**
 
529
     * X position
 
530
     * @property x
 
531
     * @type Int
 
532
     * @public
 
533
     */
 
534
    x : 0,
 
535
 
 
536
    /**
 
537
     * Y position
 
538
     * @property y
 
539
     * @type Int
 
540
     * @public
 
541
     */
 
542
    y : 0,
 
543
 
 
544
    /**
 
545
     * Ending x position
 
546
     * @property endx
 
547
     * @type Int
 
548
     * @public
 
549
     */
 
550
    endx : 0,
 
551
 
 
552
    /**
 
553
     * Ending y position
 
554
     * @property endy
 
555
     * @type Int
 
556
     * @public
 
557
     */
 
558
    endy : 0,
 
559
 
 
560
    /**
 
561
     * Path
 
562
     * @property path
 
563
     * @type String - list of points like x1,y1:x2,y2
 
564
     * @public
 
565
     */
 
566
    path : '',
 
567
 
 
568
    /**
 
569
     * Tool.
 
570
     * @property type
 
571
     * @type String
 
572
     * @public
 
573
     */
 
574
    type : 'rect',
 
575
 
 
576
    /**
 
577
     * Annotation colour.
 
578
     * @property colour
 
579
     * @type String
 
580
     * @public
 
581
     */
 
582
    colour : 'red',
 
583
 
 
584
    /**
 
585
     * Reference to M.assignfeedback_editpdf.drawable
 
586
     * @property drawable
 
587
     * @type M.assignfeedback_editpdf.drawable
 
588
     * @public
 
589
     */
 
590
    drawable : false,
 
591
 
 
592
    /**
 
593
     * Initialise the annotation.
 
594
     *
 
595
     * @method initializer
 
596
     * @return void
 
597
     */
 
598
    initializer : function(config) {
 
599
        this.editor = config.editor || null;
 
600
        this.gradeid = parseInt(config.gradeid, 10) || 0;
 
601
        this.pageno = parseInt(config.pageno, 10) || 0;
 
602
        this.x = parseInt(config.x, 10) || 0;
 
603
        this.y = parseInt(config.y, 10) || 0;
 
604
        this.endx = parseInt(config.endx, 10) || 0;
 
605
        this.endy = parseInt(config.endy, 10) || 0;
 
606
        this.path = config.path || '';
 
607
        this.type = config.type || 'rect';
 
608
        this.colour = config.colour || 'red';
 
609
        this.drawable = false;
 
610
    },
 
611
 
 
612
    /**
 
613
     * Clean a comment record, returning an oject with only fields that are valid.
 
614
     * @public
 
615
     * @method clean
 
616
     * @return {}
 
617
     */
 
618
    clean : function() {
 
619
        return {
 
620
            gradeid : this.gradeid,
 
621
            x : parseInt(this.x, 10),
 
622
            y : parseInt(this.y, 10),
 
623
            endx : parseInt(this.endx, 10),
 
624
            endy : parseInt(this.endy, 10),
 
625
            type : this.type,
 
626
            path : this.path,
 
627
            pageno : this.pageno,
 
628
            colour : this.colour
 
629
        };
 
630
    },
 
631
 
 
632
    /**
 
633
     * Draw a selection around this annotation if it is selected.
 
634
     * @public
 
635
     * @method draw_highlight
 
636
     * @return M.assignfeedback_editpdf.drawable
 
637
     */
 
638
    draw_highlight : function() {
 
639
        var bounds,
 
640
            drawingregion = Y.one(SELECTOR.DRAWINGREGION),
 
641
            offsetcanvas = Y.one(SELECTOR.DRAWINGCANVAS).getXY(),
 
642
            shape;
 
643
 
 
644
        if (this.editor.currentannotation === this) {
 
645
            // Draw a highlight around the annotation.
 
646
            bounds = new M.assignfeedback_editpdf.rect();
 
647
            bounds.bound([new M.assignfeedback_editpdf.point(this.x, this.y),
 
648
                          new M.assignfeedback_editpdf.point(this.endx, this.endy)]);
 
649
 
 
650
            shape = this.editor.graphic.addShape({
 
651
                type: Y.Rect,
 
652
                width: bounds.width,
 
653
                height: bounds.height,
 
654
                stroke: {
 
655
                   weight: STROKEWEIGHT,
 
656
                   color: SELECTEDBORDERCOLOUR
 
657
                },
 
658
                fill: {
 
659
                   color: SELECTEDFILLCOLOUR
 
660
                },
 
661
                x: bounds.x,
 
662
                y: bounds.y
 
663
            });
 
664
            this.drawable.shapes.push(shape);
 
665
 
 
666
            // Add a delete X to the annotation.
 
667
            var deleteicon = Y.Node.create('<img src="' + M.util.image_url('trash', 'assignfeedback_editpdf') + '"/>'),
 
668
                deletelink = Y.Node.create('<a href="#" role="button"></a>');
 
669
 
 
670
            deleteicon.setAttrs({
 
671
                'alt': M.util.get_string('deleteannotation', 'assignfeedback_editpdf')
 
672
            });
 
673
            deleteicon.setStyles({
 
674
                'backgroundColor' : 'white'
 
675
            });
 
676
            deletelink.addClass('deleteannotationbutton');
 
677
            deletelink.append(deleteicon);
 
678
 
 
679
            drawingregion.append(deletelink);
 
680
            deletelink.setData('annotation', this);
 
681
            deletelink.setStyle('zIndex', '200');
 
682
 
 
683
            deletelink.on('click', this.remove, this);
 
684
            deletelink.on('key', this.remove, 'space,enter', this);
 
685
 
 
686
            deletelink.setX(offsetcanvas[0] + bounds.x + bounds.width - 18);
 
687
            deletelink.setY(offsetcanvas[1] + bounds.y + 6);
 
688
            this.drawable.nodes.push(deletelink);
 
689
        }
 
690
        return this.drawable;
 
691
    },
 
692
 
 
693
    /**
 
694
     * Draw an annotation
 
695
     * @public
 
696
     * @method draw
 
697
     * @return M.assignfeedback_editpdf.drawable|false
 
698
     */
 
699
    draw : function() {
 
700
        // Should be overridden by the subclass.
 
701
        this.draw_highlight();
 
702
        return this.drawable;
 
703
    },
 
704
 
 
705
    /**
 
706
     * Delete an annotation
 
707
     * @protected
 
708
     * @method remove
 
709
     * @param event
 
710
     */
 
711
    remove : function(e) {
 
712
        var annotations;
 
713
 
 
714
        e.preventDefault();
 
715
 
 
716
        annotations = this.editor.pages[this.editor.currentpage].annotations;
 
717
        for (i = 0; i < annotations.length; i++) {
 
718
            if (annotations[i] === this) {
 
719
                annotations.splice(i, 1);
 
720
                if (this.drawable) {
 
721
                    this.drawable.erase();
 
722
                }
 
723
                this.editor.currentannotation = false;
 
724
                this.editor.save_current_page();
 
725
                return;
 
726
            }
 
727
        }
 
728
    },
 
729
 
 
730
    /**
 
731
     * Move an annotation to a new location.
 
732
     * @public
 
733
     * @param int newx
 
734
     * @param int newy
 
735
     * @method move_annotation
 
736
     */
 
737
    move : function(newx, newy) {
 
738
        var diffx = newx - this.x,
 
739
            diffy = newy - this.y,
 
740
            newpath, oldpath, xy,
 
741
            x, y;
 
742
 
 
743
        this.x += diffx;
 
744
        this.y += diffy;
 
745
        this.endx += diffx;
 
746
        this.endy += diffy;
 
747
 
 
748
        if (this.path) {
 
749
            newpath = [];
 
750
            oldpath = this.path.split(':');
 
751
            Y.each(oldpath, function(position) {
 
752
                xy = position.split(',');
 
753
                x = parseInt(xy[0], 10);
 
754
                y = parseInt(xy[1], 10);
 
755
                newpath.push((x + diffx) + ',' + (y + diffy));
 
756
            });
 
757
 
 
758
            this.path = newpath.join(':');
 
759
 
 
760
        }
 
761
        if (this.drawable) {
 
762
            this.drawable.erase();
 
763
        }
 
764
        this.editor.drawables.push(this.draw());
 
765
    },
 
766
 
 
767
    /**
 
768
     * Draw the in progress edit.
 
769
     *
 
770
     * @public
 
771
     * @method draw_current_edit
 
772
     * @param M.assignfeedback_editpdf.edit edit
 
773
     */
 
774
    draw_current_edit : function(edit) {
 
775
        var noop = edit && false;
 
776
        // Override me please.
 
777
        return noop;
 
778
    },
 
779
 
 
780
    /**
 
781
     * Promote the current edit to a real annotation.
 
782
     *
 
783
     * @public
 
784
     * @method init_from_edit
 
785
     * @param M.assignfeedback_editpdf.edit edit
 
786
     * @return bool if width/height is more than min. required.
 
787
     */
 
788
    init_from_edit : function(edit) {
 
789
        var bounds = new M.assignfeedback_editpdf.rect();
 
790
        bounds.bound([edit.start, edit.end]);
 
791
 
 
792
        this.gradeid = this.editor.get('gradeid');
 
793
        this.pageno = this.editor.currentpage;
 
794
        this.x = bounds.x;
 
795
        this.y = bounds.y;
 
796
        this.endx = bounds.x + bounds.width;
 
797
        this.endy = bounds.y + bounds.height;
 
798
        this.colour = edit.annotationcolour;
 
799
        this.path = '';
 
800
        return (bounds.has_min_width() && bounds.has_min_height());
 
801
    }
 
802
 
 
803
});
 
804
 
 
805
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
806
M.assignfeedback_editpdf.annotation = ANNOTATION;
 
807
// This file is part of Moodle - http://moodle.org/
 
808
//
 
809
// Moodle is free software: you can redistribute it and/or modify
 
810
// it under the terms of the GNU General Public License as published by
 
811
// the Free Software Foundation, either version 3 of the License, or
 
812
// (at your option) any later version.
 
813
//
 
814
// Moodle is distributed in the hope that it will be useful,
 
815
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
816
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
817
// GNU General Public License for more details.
 
818
//
 
819
// You should have received a copy of the GNU General Public License
 
820
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
821
 
 
822
/**
 
823
 * Class representing a line.
 
824
 *
 
825
 * @namespace M.assignfeedback_editpdf
 
826
 * @class annotationline
 
827
 * @extends annotation
 
828
 * @module moodle-assignfeedback_editpdf-editor
 
829
 */
 
830
ANNOTATIONLINE = function(config) {
 
831
    ANNOTATIONLINE.superclass.constructor.apply(this, [config]);
 
832
};
 
833
 
 
834
ANNOTATIONLINE.NAME = "annotationline";
 
835
ANNOTATIONLINE.ATTRS = {};
 
836
 
 
837
Y.extend(ANNOTATIONLINE, M.assignfeedback_editpdf.annotation, {
 
838
    /**
 
839
     * Draw a line annotation
 
840
     * @protected
 
841
     * @method draw
 
842
     * @return M.assignfeedback_editpdf.drawable
 
843
     */
 
844
    draw : function() {
 
845
        var drawable,
 
846
            shape;
 
847
 
 
848
        drawable = new M.assignfeedback_editpdf.drawable(this.editor);
 
849
 
 
850
        shape = this.editor.graphic.addShape({
 
851
        type: Y.Path,
 
852
            fill: false,
 
853
            stroke: {
 
854
                weight: STROKEWEIGHT,
 
855
                color: ANNOTATIONCOLOUR[this.colour]
 
856
            }
 
857
        });
 
858
 
 
859
        shape.moveTo(this.x, this.y);
 
860
        shape.lineTo(this.endx, this.endy);
 
861
        shape.end();
 
862
        drawable.shapes.push(shape);
 
863
        this.drawable = drawable;
 
864
 
 
865
        return ANNOTATIONLINE.superclass.draw.apply(this);
 
866
    },
 
867
 
 
868
    /**
 
869
     * Draw the in progress edit.
 
870
     *
 
871
     * @public
 
872
     * @method draw_current_edit
 
873
     * @param M.assignfeedback_editpdf.edit edit
 
874
     */
 
875
    draw_current_edit : function(edit) {
 
876
        var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
 
877
            shape;
 
878
 
 
879
        shape = this.editor.graphic.addShape({
 
880
           type: Y.Path,
 
881
            fill: false,
 
882
            stroke: {
 
883
                weight: STROKEWEIGHT,
 
884
                color: ANNOTATIONCOLOUR[edit.annotationcolour]
 
885
            }
 
886
        });
 
887
 
 
888
        shape.moveTo(edit.start.x, edit.start.y);
 
889
        shape.lineTo(edit.end.x, edit.end.y);
 
890
        shape.end();
 
891
 
 
892
        drawable.shapes.push(shape);
 
893
 
 
894
        return drawable;
 
895
    },
 
896
 
 
897
    /**
 
898
     * Promote the current edit to a real annotation.
 
899
     *
 
900
     * @public
 
901
     * @method init_from_edit
 
902
     * @param M.assignfeedback_editpdf.edit edit
 
903
     * @return bool true if line bound is more than min width/height, else false.
 
904
     */
 
905
    init_from_edit : function(edit) {
 
906
        this.gradeid = this.editor.get('gradeid');
 
907
        this.pageno = this.editor.currentpage;
 
908
        this.x = edit.start.x;
 
909
        this.y = edit.start.y;
 
910
        this.endx = edit.end.x;
 
911
        this.endy = edit.end.y;
 
912
        this.colour = edit.annotationcolour;
 
913
        this.path = '';
 
914
 
 
915
        return !(((this.endx - this.x) === 0) && ((this.endy - this.y) === 0));
 
916
    }
 
917
 
 
918
});
 
919
 
 
920
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
921
M.assignfeedback_editpdf.annotationline = ANNOTATIONLINE;
 
922
// This file is part of Moodle - http://moodle.org/
 
923
//
 
924
// Moodle is free software: you can redistribute it and/or modify
 
925
// it under the terms of the GNU General Public License as published by
 
926
// the Free Software Foundation, either version 3 of the License, or
 
927
// (at your option) any later version.
 
928
//
 
929
// Moodle is distributed in the hope that it will be useful,
 
930
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
931
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
932
// GNU General Public License for more details.
 
933
//
 
934
// You should have received a copy of the GNU General Public License
 
935
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
936
 
 
937
/**
 
938
 * Class representing a rectangle.
 
939
 *
 
940
 * @namespace M.assignfeedback_editpdf
 
941
 * @class annotationrectangle
 
942
 * @extends annotation
 
943
 * @module moodle-assignfeedback_editpdf-editor
 
944
 */
 
945
ANNOTATIONRECTANGLE = function(config) {
 
946
    ANNOTATIONRECTANGLE.superclass.constructor.apply(this, [config]);
 
947
};
 
948
 
 
949
ANNOTATIONRECTANGLE.NAME = "annotationrectangle";
 
950
ANNOTATIONRECTANGLE.ATTRS = {};
 
951
 
 
952
Y.extend(ANNOTATIONRECTANGLE, M.assignfeedback_editpdf.annotation, {
 
953
    /**
 
954
     * Draw a rectangle annotation
 
955
     * @protected
 
956
     * @method draw
 
957
     * @return M.assignfeedback_editpdf.drawable
 
958
     */
 
959
    draw : function() {
 
960
        var drawable,
 
961
            shape;
 
962
 
 
963
        drawable = new M.assignfeedback_editpdf.drawable(this.editor);
 
964
 
 
965
        bounds = new M.assignfeedback_editpdf.rect();
 
966
        bounds.bound([new M.assignfeedback_editpdf.point(this.x, this.y),
 
967
                      new M.assignfeedback_editpdf.point(this.endx, this.endy)]);
 
968
 
 
969
        shape = this.editor.graphic.addShape({
 
970
            type: Y.Rect,
 
971
            width: bounds.width,
 
972
            height: bounds.height,
 
973
            stroke: {
 
974
               weight: STROKEWEIGHT,
 
975
               color: ANNOTATIONCOLOUR[this.colour]
 
976
            },
 
977
            x: bounds.x,
 
978
            y: bounds.y
 
979
        });
 
980
        drawable.shapes.push(shape);
 
981
        this.drawable = drawable;
 
982
 
 
983
        return ANNOTATIONRECTANGLE.superclass.draw.apply(this);
 
984
    },
 
985
 
 
986
    /**
 
987
     * Draw the in progress edit.
 
988
     *
 
989
     * @public
 
990
     * @method draw_current_edit
 
991
     * @param M.assignfeedback_editpdf.edit edit
 
992
     */
 
993
    draw_current_edit : function(edit) {
 
994
        var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
 
995
            shape,
 
996
            bounds;
 
997
 
 
998
        bounds = new M.assignfeedback_editpdf.rect();
 
999
        bounds.bound([new M.assignfeedback_editpdf.point(edit.start.x, edit.start.y),
 
1000
                      new M.assignfeedback_editpdf.point(edit.end.x, edit.end.y)]);
 
1001
 
 
1002
        // Set min. width and height of rectangle.
 
1003
        if (!bounds.has_min_width()) {
 
1004
            bounds.set_min_width();
 
1005
        }
 
1006
        if (!bounds.has_min_height()) {
 
1007
            bounds.set_min_height();
 
1008
        }
 
1009
 
 
1010
        shape = this.editor.graphic.addShape({
 
1011
            type: Y.Rect,
 
1012
            width: bounds.width,
 
1013
            height: bounds.height,
 
1014
            stroke: {
 
1015
               weight: STROKEWEIGHT,
 
1016
               color: ANNOTATIONCOLOUR[edit.annotationcolour]
 
1017
            },
 
1018
            x: bounds.x,
 
1019
            y: bounds.y
 
1020
        });
 
1021
 
 
1022
        drawable.shapes.push(shape);
 
1023
 
 
1024
        return drawable;
 
1025
    }
 
1026
});
 
1027
 
 
1028
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
1029
M.assignfeedback_editpdf.annotationrectangle = ANNOTATIONRECTANGLE;
 
1030
// This file is part of Moodle - http://moodle.org/
 
1031
//
 
1032
// Moodle is free software: you can redistribute it and/or modify
 
1033
// it under the terms of the GNU General Public License as published by
 
1034
// the Free Software Foundation, either version 3 of the License, or
 
1035
// (at your option) any later version.
 
1036
//
 
1037
// Moodle is distributed in the hope that it will be useful,
 
1038
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
1039
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
1040
// GNU General Public License for more details.
 
1041
//
 
1042
// You should have received a copy of the GNU General Public License
 
1043
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
1044
 
 
1045
/**
 
1046
 * Class representing a oval.
 
1047
 *
 
1048
 * @namespace M.assignfeedback_editpdf
 
1049
 * @class annotationoval
 
1050
 * @extends annotation
 
1051
 * @module moodle-assignfeedback_editpdf-editor
 
1052
 */
 
1053
ANNOTATIONOVAL = function(config) {
 
1054
    ANNOTATIONOVAL.superclass.constructor.apply(this, [config]);
 
1055
};
 
1056
 
 
1057
ANNOTATIONOVAL.NAME = "annotationoval";
 
1058
ANNOTATIONOVAL.ATTRS = {};
 
1059
 
 
1060
Y.extend(ANNOTATIONOVAL, M.assignfeedback_editpdf.annotation, {
 
1061
    /**
 
1062
     * Draw a oval annotation
 
1063
     * @protected
 
1064
     * @method draw
 
1065
     * @return M.assignfeedback_editpdf.drawable
 
1066
     */
 
1067
    draw : function() {
 
1068
        var drawable,
 
1069
            shape;
 
1070
 
 
1071
        drawable = new M.assignfeedback_editpdf.drawable(this.editor);
 
1072
 
 
1073
        bounds = new M.assignfeedback_editpdf.rect();
 
1074
        bounds.bound([new M.assignfeedback_editpdf.point(this.x, this.y),
 
1075
                      new M.assignfeedback_editpdf.point(this.endx, this.endy)]);
 
1076
 
 
1077
        shape = this.editor.graphic.addShape({
 
1078
            type: Y.Ellipse,
 
1079
            width: bounds.width,
 
1080
            height: bounds.height,
 
1081
            stroke: {
 
1082
               weight: STROKEWEIGHT,
 
1083
               color: ANNOTATIONCOLOUR[this.colour]
 
1084
            },
 
1085
            x: bounds.x,
 
1086
            y: bounds.y
 
1087
        });
 
1088
        drawable.shapes.push(shape);
 
1089
        this.drawable = drawable;
 
1090
 
 
1091
        return ANNOTATIONOVAL.superclass.draw.apply(this);
 
1092
    },
 
1093
 
 
1094
    /**
 
1095
     * Draw the in progress edit.
 
1096
     *
 
1097
     * @public
 
1098
     * @method draw_current_edit
 
1099
     * @param M.assignfeedback_editpdf.edit edit
 
1100
     */
 
1101
    draw_current_edit : function(edit) {
 
1102
        var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
 
1103
            shape,
 
1104
            bounds;
 
1105
 
 
1106
        bounds = new M.assignfeedback_editpdf.rect();
 
1107
        bounds.bound([new M.assignfeedback_editpdf.point(edit.start.x, edit.start.y),
 
1108
                      new M.assignfeedback_editpdf.point(edit.end.x, edit.end.y)]);
 
1109
 
 
1110
        // Set min. width and height of oval.
 
1111
        if (!bounds.has_min_width()) {
 
1112
            bounds.set_min_width();
 
1113
        }
 
1114
        if (!bounds.has_min_height()) {
 
1115
            bounds.set_min_height();
 
1116
        }
 
1117
 
 
1118
        shape = this.editor.graphic.addShape({
 
1119
            type: Y.Ellipse,
 
1120
            width: bounds.width,
 
1121
            height: bounds.height,
 
1122
            stroke: {
 
1123
               weight: STROKEWEIGHT,
 
1124
               color: ANNOTATIONCOLOUR[edit.annotationcolour]
 
1125
            },
 
1126
            x: bounds.x,
 
1127
            y: bounds.y
 
1128
        });
 
1129
 
 
1130
        drawable.shapes.push(shape);
 
1131
 
 
1132
        return drawable;
 
1133
    }
 
1134
});
 
1135
 
 
1136
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
1137
M.assignfeedback_editpdf.annotationoval = ANNOTATIONOVAL;
 
1138
// This file is part of Moodle - http://moodle.org/
 
1139
//
 
1140
// Moodle is free software: you can redistribute it and/or modify
 
1141
// it under the terms of the GNU General Public License as published by
 
1142
// the Free Software Foundation, either version 3 of the License, or
 
1143
// (at your option) any later version.
 
1144
//
 
1145
// Moodle is distributed in the hope that it will be useful,
 
1146
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
1147
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
1148
// GNU General Public License for more details.
 
1149
//
 
1150
// You should have received a copy of the GNU General Public License
 
1151
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
1152
 
 
1153
/**
 
1154
 * Class representing a pen.
 
1155
 *
 
1156
 * @namespace M.assignfeedback_editpdf
 
1157
 * @class annotationpen
 
1158
 * @extends annotation
 
1159
 * @module moodle-assignfeedback_editpdf-editor
 
1160
 */
 
1161
ANNOTATIONPEN = function(config) {
 
1162
    ANNOTATIONPEN.superclass.constructor.apply(this, [config]);
 
1163
};
 
1164
 
 
1165
ANNOTATIONPEN.NAME = "annotationpen";
 
1166
ANNOTATIONPEN.ATTRS = {};
 
1167
 
 
1168
Y.extend(ANNOTATIONPEN, M.assignfeedback_editpdf.annotation, {
 
1169
    /**
 
1170
     * Draw a pen annotation
 
1171
     * @protected
 
1172
     * @method draw
 
1173
     * @return M.assignfeedback_editpdf.drawable
 
1174
     */
 
1175
    draw : function() {
 
1176
        var drawable,
 
1177
            shape,
 
1178
            first,
 
1179
            positions,
 
1180
            xy;
 
1181
 
 
1182
        drawable = new M.assignfeedback_editpdf.drawable(this.editor);
 
1183
 
 
1184
        shape = this.editor.graphic.addShape({
 
1185
           type: Y.Path,
 
1186
            fill: false,
 
1187
            stroke: {
 
1188
                weight: STROKEWEIGHT,
 
1189
                color: ANNOTATIONCOLOUR[this.colour]
 
1190
            }
 
1191
        });
 
1192
 
 
1193
        first = true;
 
1194
        // Recreate the pen path array.
 
1195
        positions = this.path.split(':');
 
1196
        // Redraw all the lines.
 
1197
        Y.each(positions, function(position) {
 
1198
            xy = position.split(',');
 
1199
            if (first) {
 
1200
                shape.moveTo(xy[0], xy[1]);
 
1201
                first = false;
 
1202
            } else {
 
1203
                shape.lineTo(xy[0], xy[1]);
 
1204
            }
 
1205
        }, this);
 
1206
 
 
1207
        shape.end();
 
1208
 
 
1209
        drawable.shapes.push(shape);
 
1210
        this.drawable = drawable;
 
1211
 
 
1212
        return ANNOTATIONPEN.superclass.draw.apply(this);
 
1213
    },
 
1214
 
 
1215
    /**
 
1216
     * Draw the in progress edit.
 
1217
     *
 
1218
     * @public
 
1219
     * @method draw_current_edit
 
1220
     * @param M.assignfeedback_editpdf.edit edit
 
1221
     */
 
1222
    draw_current_edit : function(edit) {
 
1223
        var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
 
1224
            shape,
 
1225
            first;
 
1226
 
 
1227
        shape = this.editor.graphic.addShape({
 
1228
           type: Y.Path,
 
1229
            fill: false,
 
1230
            stroke: {
 
1231
                weight: STROKEWEIGHT,
 
1232
                color: ANNOTATIONCOLOUR[edit.annotationcolour]
 
1233
            }
 
1234
        });
 
1235
 
 
1236
        first = true;
 
1237
        // Recreate the pen path array.
 
1238
        // Redraw all the lines.
 
1239
        Y.each(edit.path, function(position) {
 
1240
            if (first) {
 
1241
                shape.moveTo(position.x, position.y);
 
1242
                first = false;
 
1243
            } else {
 
1244
                shape.lineTo(position.x, position.y);
 
1245
            }
 
1246
        }, this);
 
1247
 
 
1248
        shape.end();
 
1249
 
 
1250
        drawable.shapes.push(shape);
 
1251
 
 
1252
        return drawable;
 
1253
    },
 
1254
 
 
1255
 
 
1256
    /**
 
1257
     * Promote the current edit to a real annotation.
 
1258
     *
 
1259
     * @public
 
1260
     * @method init_from_edit
 
1261
     * @param M.assignfeedback_editpdf.edit edit
 
1262
     * @return bool true if pen bound is more than min width/height, else false.
 
1263
     */
 
1264
    init_from_edit : function(edit) {
 
1265
        var bounds = new M.assignfeedback_editpdf.rect(),
 
1266
            pathlist = [],
 
1267
            i = 0;
 
1268
 
 
1269
        // This will get the boundaries of all points in the path.
 
1270
        bounds.bound(edit.path);
 
1271
 
 
1272
        for (i = 0; i < edit.path.length; i++) {
 
1273
            pathlist.push(parseInt(edit.path[i].x, 10) + ',' + parseInt(edit.path[i].y, 10));
 
1274
        }
 
1275
 
 
1276
        this.gradeid = this.editor.get('gradeid');
 
1277
        this.pageno = this.editor.currentpage;
 
1278
        this.x = bounds.x;
 
1279
        this.y = bounds.y;
 
1280
        this.endx = bounds.x + bounds.width;
 
1281
        this.endy = bounds.y + bounds.height;
 
1282
        this.colour = edit.annotationcolour;
 
1283
        this.path = pathlist.join(':');
 
1284
 
 
1285
        return (bounds.has_min_width() || bounds.has_min_height());
 
1286
    }
 
1287
 
 
1288
 
 
1289
});
 
1290
 
 
1291
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
1292
M.assignfeedback_editpdf.annotationpen = ANNOTATIONPEN;
 
1293
// This file is part of Moodle - http://moodle.org/
 
1294
//
 
1295
// Moodle is free software: you can redistribute it and/or modify
 
1296
// it under the terms of the GNU General Public License as published by
 
1297
// the Free Software Foundation, either version 3 of the License, or
 
1298
// (at your option) any later version.
 
1299
//
 
1300
// Moodle is distributed in the hope that it will be useful,
 
1301
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
1302
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
1303
// GNU General Public License for more details.
 
1304
//
 
1305
// You should have received a copy of the GNU General Public License
 
1306
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
1307
 
 
1308
/**
 
1309
 * Class representing a highlight.
 
1310
 *
 
1311
 * @namespace M.assignfeedback_editpdf
 
1312
 * @class annotationhighlight
 
1313
 * @extends annotation
 
1314
 * @module moodle-assignfeedback_editpdf-editor
 
1315
 */
 
1316
ANNOTATIONHIGHLIGHT = function(config) {
 
1317
    ANNOTATIONHIGHLIGHT.superclass.constructor.apply(this, [config]);
 
1318
};
 
1319
 
 
1320
ANNOTATIONHIGHLIGHT.NAME = "annotationhighlight";
 
1321
ANNOTATIONHIGHLIGHT.ATTRS = {};
 
1322
 
 
1323
Y.extend(ANNOTATIONHIGHLIGHT, M.assignfeedback_editpdf.annotation, {
 
1324
    /**
 
1325
     * Draw a highlight annotation
 
1326
     * @protected
 
1327
     * @method draw
 
1328
     * @return M.assignfeedback_editpdf.drawable
 
1329
     */
 
1330
    draw : function() {
 
1331
        var drawable,
 
1332
            shape,
 
1333
            bounds,
 
1334
            highlightcolour;
 
1335
 
 
1336
        drawable = new M.assignfeedback_editpdf.drawable(this.editor);
 
1337
        bounds = new M.assignfeedback_editpdf.rect();
 
1338
        bounds.bound([new M.assignfeedback_editpdf.point(this.x, this.y),
 
1339
                      new M.assignfeedback_editpdf.point(this.endx, this.endy)]);
 
1340
 
 
1341
        highlightcolour = ANNOTATIONCOLOUR[this.colour];
 
1342
 
 
1343
        // Add an alpha channel to the rgb colour.
 
1344
 
 
1345
        highlightcolour = highlightcolour.replace('rgb', 'rgba');
 
1346
        highlightcolour = highlightcolour.replace(')', ',0.5)');
 
1347
 
 
1348
        shape = this.editor.graphic.addShape({
 
1349
            type: Y.Rect,
 
1350
            width: bounds.width,
 
1351
            height: bounds.height,
 
1352
            stroke: false,
 
1353
            fill: {
 
1354
                color: highlightcolour
 
1355
            },
 
1356
            x: bounds.x,
 
1357
            y: bounds.y
 
1358
        });
 
1359
 
 
1360
        drawable.shapes.push(shape);
 
1361
        this.drawable = drawable;
 
1362
 
 
1363
        return ANNOTATIONHIGHLIGHT.superclass.draw.apply(this);
 
1364
    },
 
1365
 
 
1366
    /**
 
1367
     * Draw the in progress edit.
 
1368
     *
 
1369
     * @public
 
1370
     * @method draw_current_edit
 
1371
     * @param M.assignfeedback_editpdf.edit edit
 
1372
     */
 
1373
    draw_current_edit : function(edit) {
 
1374
        var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
 
1375
            shape,
 
1376
            bounds,
 
1377
            highlightcolour;
 
1378
 
 
1379
        bounds = new M.assignfeedback_editpdf.rect();
 
1380
        bounds.bound([new M.assignfeedback_editpdf.point(edit.start.x, edit.start.y),
 
1381
                      new M.assignfeedback_editpdf.point(edit.end.x, edit.end.y)]);
 
1382
 
 
1383
        // Set min. width of highlight.
 
1384
        if (!bounds.has_min_width()) {
 
1385
            bounds.set_min_width();
 
1386
        }
 
1387
 
 
1388
        highlightcolour = ANNOTATIONCOLOUR[edit.annotationcolour];
 
1389
        // Add an alpha channel to the rgb colour.
 
1390
 
 
1391
        highlightcolour = highlightcolour.replace('rgb', 'rgba');
 
1392
        highlightcolour = highlightcolour.replace(')', ',0.5)');
 
1393
 
 
1394
        // We will draw a box with the current background colour.
 
1395
        shape = this.editor.graphic.addShape({
 
1396
            type: Y.Rect,
 
1397
            width: bounds.width,
 
1398
            height: 16,
 
1399
            stroke: false,
 
1400
            fill: {
 
1401
               color: highlightcolour
 
1402
            },
 
1403
            x: bounds.x,
 
1404
            y: edit.start.y
 
1405
        });
 
1406
 
 
1407
        drawable.shapes.push(shape);
 
1408
 
 
1409
        return drawable;
 
1410
    },
 
1411
 
 
1412
    /**
 
1413
     * Promote the current edit to a real annotation.
 
1414
     *
 
1415
     * @public
 
1416
     * @method init_from_edit
 
1417
     * @param M.assignfeedback_editpdf.edit edit
 
1418
     * @return bool true if highlight bound is more than min width/height, else false.
 
1419
     */
 
1420
    init_from_edit : function(edit) {
 
1421
        var bounds = new M.assignfeedback_editpdf.rect();
 
1422
        bounds.bound([edit.start, edit.end]);
 
1423
 
 
1424
        this.gradeid = this.editor.get('gradeid');
 
1425
        this.pageno = this.editor.currentpage;
 
1426
        this.x = bounds.x;
 
1427
        this.y = edit.start.y;
 
1428
        this.endx = bounds.x + bounds.width;
 
1429
        this.endy = edit.start.y + 16;
 
1430
        this.colour = edit.annotationcolour;
 
1431
        this.page = '';
 
1432
 
 
1433
        return (bounds.has_min_width());
 
1434
    }
 
1435
 
 
1436
});
 
1437
 
 
1438
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
1439
M.assignfeedback_editpdf.annotationhighlight = ANNOTATIONHIGHLIGHT;
 
1440
// This file is part of Moodle - http://moodle.org/
 
1441
//
 
1442
// Moodle is free software: you can redistribute it and/or modify
 
1443
// it under the terms of the GNU General Public License as published by
 
1444
// the Free Software Foundation, either version 3 of the License, or
 
1445
// (at your option) any later version.
 
1446
//
 
1447
// Moodle is distributed in the hope that it will be useful,
 
1448
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
1449
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
1450
// GNU General Public License for more details.
 
1451
//
 
1452
// You should have received a copy of the GNU General Public License
 
1453
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
1454
 
 
1455
/**
 
1456
 * Class representing a stamp.
 
1457
 *
 
1458
 * @namespace M.assignfeedback_editpdf
 
1459
 * @class annotationstamp
 
1460
 * @extends annotation
 
1461
 * @module moodle-assignfeedback_editpdf-editor
 
1462
 */
 
1463
ANNOTATIONSTAMP = function(config) {
 
1464
    ANNOTATIONSTAMP.superclass.constructor.apply(this, [config]);
 
1465
};
 
1466
 
 
1467
ANNOTATIONSTAMP.NAME = "annotationstamp";
 
1468
ANNOTATIONSTAMP.ATTRS = {};
 
1469
 
 
1470
Y.extend(ANNOTATIONSTAMP, M.assignfeedback_editpdf.annotation, {
 
1471
    /**
 
1472
     * Draw a stamp annotation
 
1473
     * @protected
 
1474
     * @method draw
 
1475
     * @return M.assignfeedback_editpdf.drawable
 
1476
     */
 
1477
    draw : function() {
 
1478
        var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
 
1479
            drawingregion = Y.one(SELECTOR.DRAWINGREGION),
 
1480
            node,
 
1481
            position;
 
1482
 
 
1483
        position = this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(this.x, this.y));
 
1484
        node = Y.Node.create('<div/>');
 
1485
        node.setStyles({
 
1486
            'position': 'absolute',
 
1487
            'display': 'inline-block',
 
1488
            'backgroundImage': 'url(' + this.editor.get_stamp_image_url(this.path) + ')',
 
1489
            'width': (this.endx - this.x),
 
1490
            'height': (this.endy - this.y),
 
1491
            'backgroundSize': '100% 100%',
 
1492
            'zIndex': 50
 
1493
        });
 
1494
 
 
1495
        drawingregion.append(node);
 
1496
        node.setX(position.x);
 
1497
        node.setY(position.y);
 
1498
 
 
1499
        // Pass throught the event handlers on the div.
 
1500
        node.on('gesturemovestart', this.editor.edit_start, null, this.editor);
 
1501
        node.on('gesturemove', this.editor.edit_move, null, this.editor);
 
1502
        node.on('gesturemoveend', this.editor.edit_end, null, this.editor);
 
1503
 
 
1504
        drawable.nodes.push(node);
 
1505
 
 
1506
        this.drawable = drawable;
 
1507
        return ANNOTATIONSTAMP.superclass.draw.apply(this);
 
1508
    },
 
1509
 
 
1510
    /**
 
1511
     * Draw the in progress edit.
 
1512
     *
 
1513
     * @public
 
1514
     * @method draw_current_edit
 
1515
     * @param M.assignfeedback_editpdf.edit edit
 
1516
     */
 
1517
    draw_current_edit : function(edit) {
 
1518
        var bounds = new M.assignfeedback_editpdf.rect(),
 
1519
            drawable = new M.assignfeedback_editpdf.drawable(this.editor),
 
1520
            drawingregion = Y.one(SELECTOR.DRAWINGREGION),
 
1521
            node,
 
1522
            position;
 
1523
 
 
1524
        bounds.bound([edit.start, edit.end]);
 
1525
        position = this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(bounds.x, bounds.y));
 
1526
 
 
1527
        node = Y.Node.create('<div/>');
 
1528
        node.setStyles({
 
1529
            'position': 'absolute',
 
1530
            'display': 'inline-block',
 
1531
            'backgroundImage': 'url(' + this.editor.get_stamp_image_url(edit.stamp) + ')',
 
1532
            'width': bounds.width,
 
1533
            'height': bounds.height,
 
1534
            'backgroundSize': '100% 100%',
 
1535
            'zIndex': 50
 
1536
        });
 
1537
 
 
1538
        drawingregion.append(node);
 
1539
        node.setX(position.x);
 
1540
        node.setY(position.y);
 
1541
 
 
1542
        drawable.nodes.push(node);
 
1543
 
 
1544
        return drawable;
 
1545
    },
 
1546
 
 
1547
    /**
 
1548
     * Promote the current edit to a real annotation.
 
1549
     *
 
1550
     * @public
 
1551
     * @method init_from_edit
 
1552
     * @param M.assignfeedback_editpdf.edit edit
 
1553
     * @return bool if width/height is more than min. required.
 
1554
     */
 
1555
    init_from_edit : function(edit) {
 
1556
        var bounds = new M.assignfeedback_editpdf.rect();
 
1557
        bounds.bound([edit.start, edit.end]);
 
1558
 
 
1559
        if (bounds.width < 40) {
 
1560
            bounds.width = 40;
 
1561
        }
 
1562
        if (bounds.height < 40) {
 
1563
            bounds.height = 40;
 
1564
        }
 
1565
        this.gradeid = this.editor.get('gradeid');
 
1566
        this.pageno = this.editor.currentpage;
 
1567
        this.x = bounds.x;
 
1568
        this.y = bounds.y;
 
1569
        this.endx = bounds.x + bounds.width;
 
1570
        this.endy = bounds.y + bounds.height;
 
1571
        this.colour = edit.annotationcolour;
 
1572
        this.path = edit.stamp;
 
1573
 
 
1574
        // Min width and height is always more than 40px.
 
1575
        return true;
 
1576
    },
 
1577
 
 
1578
    /**
 
1579
     * Move an annotation to a new location.
 
1580
     * @public
 
1581
     * @param int newx
 
1582
     * @param int newy
 
1583
     * @method move_annotation
 
1584
     */
 
1585
    move : function(newx, newy) {
 
1586
        var diffx = newx - this.x,
 
1587
            diffy = newy - this.y;
 
1588
 
 
1589
        this.x += diffx;
 
1590
        this.y += diffy;
 
1591
        this.endx += diffx;
 
1592
        this.endy += diffy;
 
1593
 
 
1594
        if (this.drawable) {
 
1595
            this.drawable.erase();
 
1596
        }
 
1597
        this.editor.drawables.push(this.draw());
 
1598
    }
 
1599
 
 
1600
});
 
1601
 
 
1602
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
1603
M.assignfeedback_editpdf.annotationstamp = ANNOTATIONSTAMP;
 
1604
var DROPDOWN_NAME = "Dropdown menu",
 
1605
    DROPDOWN;
 
1606
 
 
1607
/**
 
1608
 * DROPDOWN
 
1609
 * This is a drop down list of buttons triggered (and aligned to) a button.
 
1610
 *
 
1611
 * @namespace M.assignfeedback_editpdf.widget.dropdown
 
1612
 * @class dropdown
 
1613
 * @constructor
 
1614
 * @extends Y.Base
 
1615
 */
 
1616
DROPDOWN = function(config) {
 
1617
    config.draggable = false;
 
1618
    config.centered = false;
 
1619
    config.width = 'auto';
 
1620
    config.lightbox = false;
 
1621
    config.visible = false;
 
1622
    config.footerContent = '';
 
1623
    DROPDOWN.superclass.constructor.apply(this, [config]);
 
1624
};
 
1625
 
 
1626
Y.extend(DROPDOWN, M.core.dialogue, {
 
1627
    /**
 
1628
     * Initialise the menu.
 
1629
     *
 
1630
     * @method initializer
 
1631
     * @return void
 
1632
     */
 
1633
    initializer : function(config) {
 
1634
        var button, body, headertext, bb;
 
1635
        DROPDOWN.superclass.initializer.call(this, config);
 
1636
 
 
1637
        bb = this.get('boundingBox');
 
1638
        bb.addClass('assignfeedback_editpdf_dropdown');
 
1639
 
 
1640
        // Align the menu to the button that opens it.
 
1641
        button = this.get('buttonNode');
 
1642
 
 
1643
        // Close the menu when clicked outside (excluding the button that opened the menu).
 
1644
        body = this.bodyNode;
 
1645
 
 
1646
        headertext = Y.Node.create('<h3/>');
 
1647
        headertext.addClass('accesshide');
 
1648
        headertext.setHTML(this.get('headerText'));
 
1649
        body.prepend(headertext);
 
1650
 
 
1651
        body.on('clickoutside', function(e) {
 
1652
            if (this.get('visible')) {
 
1653
                // Note: we need to compare ids because for some reason - sometimes button is an Object, not a Y.Node.
 
1654
                if (e.target.get('id') !== button.get('id') && e.target.ancestor().get('id') !== button.get('id')) {
 
1655
                    e.preventDefault();
 
1656
                    this.hide();
 
1657
                }
 
1658
            }
 
1659
        }, this);
 
1660
 
 
1661
        button.on('click', function(e) {e.preventDefault(); this.show();}, this);
 
1662
        button.on('key', this.show, 'enter,space', this);
 
1663
    },
 
1664
 
 
1665
    /**
 
1666
     * Override the show method to align to the button.
 
1667
     *
 
1668
     * @method show
 
1669
     * @return void
 
1670
     */
 
1671
    show : function() {
 
1672
        var button = this.get('buttonNode');
 
1673
 
 
1674
        result = DROPDOWN.superclass.show.call(this);
 
1675
        this.align(button, [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL]);
 
1676
    }
 
1677
}, {
 
1678
    NAME : DROPDOWN_NAME,
 
1679
    ATTRS : {
 
1680
        /**
 
1681
         * The header for the drop down (only accessible to screen readers).
 
1682
         *
 
1683
         * @attribute headerText
 
1684
         * @type String
 
1685
         * @default ''
 
1686
         */
 
1687
        headerText : {
 
1688
            value : ''
 
1689
        },
 
1690
 
 
1691
        /**
 
1692
         * The button used to show/hide this drop down menu.
 
1693
         *
 
1694
         * @attribute buttonNode
 
1695
         * @type Y.Node
 
1696
         * @default null
 
1697
         */
 
1698
        buttonNode : {
 
1699
            value : null
 
1700
        }
 
1701
    }
 
1702
});
 
1703
 
 
1704
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
1705
M.assignfeedback_editpdf.dropdown = DROPDOWN;
 
1706
var COLOURPICKER_NAME = "Colourpicker",
 
1707
    COLOURPICKER;
 
1708
 
 
1709
/**
 
1710
 * COLOURPICKER
 
1711
 * This is a drop down list of colours.
 
1712
 *
 
1713
 * @namespace M.assignfeedback_editpdf.colourpicker
 
1714
 * @class dropdown
 
1715
 * @constructor
 
1716
 * @extends Y.Base
 
1717
 */
 
1718
COLOURPICKER = function(config) {
 
1719
    COLOURPICKER.superclass.constructor.apply(this, [config]);
 
1720
};
 
1721
 
 
1722
Y.extend(COLOURPICKER, M.assignfeedback_editpdf.dropdown, {
 
1723
 
 
1724
    /**
 
1725
     * Initialise the menu.
 
1726
     *
 
1727
     * @method initializer
 
1728
     * @return void
 
1729
     */
 
1730
    initializer : function(config) {
 
1731
        var colourlist = Y.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>'),
 
1732
            body;
 
1733
 
 
1734
        // Build a list of coloured buttons.
 
1735
        Y.each(this.get('colours'), function(rgb, colour) {
 
1736
            var button, listitem, title, img, iconname;
 
1737
 
 
1738
            title = M.util.get_string(colour, 'assignfeedback_editpdf');
 
1739
            iconname = this.get('iconprefix') + colour;
 
1740
            img = M.util.image_url(iconname, 'assignfeedback_editpdf');
 
1741
            button = Y.Node.create('<button><img alt="' + title + '" src="' + img + '"/></button>');
 
1742
            button.setAttribute('data-colour', colour);
 
1743
            button.setAttribute('data-rgb', rgb);
 
1744
            button.setStyle('backgroundImage', 'none');
 
1745
            listitem = Y.Node.create('<li/>');
 
1746
            listitem.append(button);
 
1747
            colourlist.append(listitem);
 
1748
        }, this);
 
1749
 
 
1750
        body = Y.Node.create('<div/>');
 
1751
 
 
1752
        // Set the call back.
 
1753
        colourlist.delegate('click', this.callback_handler, 'button', this);
 
1754
        colourlist.delegate('key', this.callback_handler, 'down:13', 'button', this);
 
1755
 
 
1756
        // Set the accessible header text.
 
1757
        this.set('headerText', M.util.get_string('colourpicker', 'assignfeedback_editpdf'));
 
1758
 
 
1759
        // Set the body content.
 
1760
        body.append(colourlist);
 
1761
        this.set('bodyContent', body);
 
1762
 
 
1763
        COLOURPICKER.superclass.initializer.call(this, config);
 
1764
    },
 
1765
    callback_handler : function(e) {
 
1766
        e.preventDefault();
 
1767
 
 
1768
        var callback = this.get('callback'),
 
1769
            callbackcontext = this.get('context'),
 
1770
            bind;
 
1771
 
 
1772
        this.hide();
 
1773
 
 
1774
        // Call the callback with the specified context.
 
1775
        bind = Y.bind(callback, callbackcontext, e);
 
1776
 
 
1777
        bind();
 
1778
    }
 
1779
}, {
 
1780
    NAME : COLOURPICKER_NAME,
 
1781
    ATTRS : {
 
1782
        /**
 
1783
         * The list of colours this colour picker supports.
 
1784
         *
 
1785
         * @attribute colours
 
1786
         * @type {String: String} (The keys of the array are the colour names and the values are localized strings)
 
1787
         * @default {}
 
1788
         */
 
1789
        colours : {
 
1790
            value : {}
 
1791
        },
 
1792
 
 
1793
        /**
 
1794
         * The function called when a new colour is chosen.
 
1795
         *
 
1796
         * @attribute callback
 
1797
         * @type function
 
1798
         * @default null
 
1799
         */
 
1800
        callback : {
 
1801
            value : null
 
1802
        },
 
1803
 
 
1804
        /**
 
1805
         * The context passed to the callback when a colour is chosen.
 
1806
         *
 
1807
         * @attribute context
 
1808
         * @type Y.Node
 
1809
         * @default null
 
1810
         */
 
1811
        context : {
 
1812
            value : null
 
1813
        },
 
1814
 
 
1815
        /**
 
1816
         * The prefix for the icon image names.
 
1817
         *
 
1818
         * @attribute iconprefix
 
1819
         * @type String
 
1820
         * @default 'colour_'
 
1821
         */
 
1822
        iconprefix : {
 
1823
            value : 'colour_'
 
1824
        }
 
1825
    }
 
1826
});
 
1827
 
 
1828
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
1829
M.assignfeedback_editpdf.colourpicker = COLOURPICKER;
 
1830
var STAMPPICKER_NAME = "Colourpicker",
 
1831
    STAMPPICKER;
 
1832
 
 
1833
/**
 
1834
 * STAMPPICKER
 
1835
 * This is a drop down list of stamps.
 
1836
 *
 
1837
 * @namespace M.assignfeedback_editpdf.stamppicker
 
1838
 * @class dropdown
 
1839
 * @constructor
 
1840
 * @extends Y.Base
 
1841
 */
 
1842
STAMPPICKER = function(config) {
 
1843
    STAMPPICKER.superclass.constructor.apply(this, [config]);
 
1844
};
 
1845
 
 
1846
Y.extend(STAMPPICKER, M.assignfeedback_editpdf.dropdown, {
 
1847
 
 
1848
    /**
 
1849
     * Initialise the menu.
 
1850
     *
 
1851
     * @method initializer
 
1852
     * @return void
 
1853
     */
 
1854
    initializer : function(config) {
 
1855
        var stamplist = Y.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>');
 
1856
 
 
1857
        // Build a list of stamped buttons.
 
1858
        Y.each(this.get('stamps'), function(stamp) {
 
1859
            var button, listitem, title;
 
1860
 
 
1861
            title = M.util.get_string('stamp', 'assignfeedback_editpdf');
 
1862
            button = Y.Node.create('<button><img height="16" width="16" alt="' + title + '" src="' + stamp + '"/></button>');
 
1863
            button.setAttribute('data-stamp', stamp);
 
1864
            button.setStyle('backgroundImage', 'none');
 
1865
            listitem = Y.Node.create('<li/>');
 
1866
            listitem.append(button);
 
1867
            stamplist.append(listitem);
 
1868
        }, this);
 
1869
 
 
1870
 
 
1871
        // Set the call back.
 
1872
        stamplist.delegate('click', this.callback_handler, 'button', this);
 
1873
        stamplist.delegate('key', this.callback_handler, 'down:13', 'button', this);
 
1874
 
 
1875
        // Set the accessible header text.
 
1876
        this.set('headerText', M.util.get_string('stamppicker', 'assignfeedback_editpdf'));
 
1877
 
 
1878
        // Set the body content.
 
1879
        this.set('bodyContent', stamplist);
 
1880
 
 
1881
        STAMPPICKER.superclass.initializer.call(this, config);
 
1882
    },
 
1883
    callback_handler : function(e) {
 
1884
        e.preventDefault();
 
1885
        var callback = this.get('callback'),
 
1886
            callbackcontext = this.get('context'),
 
1887
            bind;
 
1888
 
 
1889
        this.hide();
 
1890
 
 
1891
        // Call the callback with the specified context.
 
1892
        bind = Y.bind(callback, callbackcontext, e);
 
1893
 
 
1894
        bind();
 
1895
    }
 
1896
}, {
 
1897
    NAME : STAMPPICKER_NAME,
 
1898
    ATTRS : {
 
1899
        /**
 
1900
         * The list of stamps this stamp picker supports.
 
1901
         *
 
1902
         * @attribute stamps
 
1903
         * @type String[] - the stamp filenames.
 
1904
         * @default {}
 
1905
         */
 
1906
        stamps : {
 
1907
            value : []
 
1908
        },
 
1909
 
 
1910
        /**
 
1911
         * The function called when a new stamp is chosen.
 
1912
         *
 
1913
         * @attribute callback
 
1914
         * @type function
 
1915
         * @default null
 
1916
         */
 
1917
        callback : {
 
1918
            value : null
 
1919
        },
 
1920
 
 
1921
        /**
 
1922
         * The context passed to the callback when a stamp is chosen.
 
1923
         *
 
1924
         * @attribute context
 
1925
         * @type Y.Node
 
1926
         * @default null
 
1927
         */
 
1928
        context : {
 
1929
            value : null
 
1930
        }
 
1931
    }
 
1932
});
 
1933
 
 
1934
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
1935
M.assignfeedback_editpdf.stamppicker = STAMPPICKER;
 
1936
var COMMENTMENUNAME = "Commentmenu",
 
1937
    COMMENTMENU;
 
1938
 
 
1939
/**
 
1940
 * COMMENTMENU
 
1941
 * This is a drop down list of comment context functions.
 
1942
 *
 
1943
 * @namespace M.assignfeedback_editpdf.editor
 
1944
 * @class commentmenu
 
1945
 * @constructor
 
1946
 * @extends Y.Base
 
1947
 */
 
1948
COMMENTMENU = function(config) {
 
1949
    COMMENTMENU.superclass.constructor.apply(this, [config]);
 
1950
};
 
1951
 
 
1952
Y.extend(COMMENTMENU, M.assignfeedback_editpdf.dropdown, {
 
1953
 
 
1954
    /**
 
1955
     * Initialise the menu.
 
1956
     *
 
1957
     * @method initializer
 
1958
     * @return void
 
1959
     */
 
1960
    initializer : function(config) {
 
1961
        var commentlinks,
 
1962
            link,
 
1963
            body,
 
1964
            comment;
 
1965
 
 
1966
        comment = this.get('comment');
 
1967
        // Build the list of menu items.
 
1968
        commentlinks = Y.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>');
 
1969
 
 
1970
        link = Y.Node.create('<li><a tabindex="-1" href="#">' + M.util.get_string('addtoquicklist', 'assignfeedback_editpdf') + '</a></li>');
 
1971
        link.on('click', comment.add_to_quicklist, comment);
 
1972
        link.on('key', comment.add_to_quicklist, 'enter,space', comment);
 
1973
 
 
1974
        commentlinks.append(link);
 
1975
 
 
1976
        link = Y.Node.create('<li><a tabindex="-1" href="#">' + M.util.get_string('deletecomment', 'assignfeedback_editpdf') + '</a></li>');
 
1977
        link.on('click', function(e) { e.preventDefault(); this.menu.hide(); this.remove(); }, comment);
 
1978
        link.on('key', function() { comment.menu.hide(); comment.remove(); }, 'enter,space', comment);
 
1979
 
 
1980
        commentlinks.append(link);
 
1981
 
 
1982
        link = Y.Node.create('<li><hr/></li>');
 
1983
        commentlinks.append(link);
 
1984
 
 
1985
        // Set the accessible header text.
 
1986
        this.set('headerText', M.util.get_string('commentcontextmenu', 'assignfeedback_editpdf'));
 
1987
 
 
1988
        body = Y.Node.create('<div/>');
 
1989
 
 
1990
        // Set the body content.
 
1991
        body.append(commentlinks);
 
1992
        this.set('bodyContent', body);
 
1993
 
 
1994
        COMMENTMENU.superclass.initializer.call(this, config);
 
1995
    },
 
1996
 
 
1997
    /**
 
1998
     * Show the menu.
 
1999
     *
 
2000
     * @method show
 
2001
     * @return void
 
2002
     */
 
2003
    show : function() {
 
2004
        var commentlinks = this.get('boundingBox').one('ul');
 
2005
            commentlinks.all('.quicklist_comment').remove(true),
 
2006
            comment = this.get('comment');
 
2007
 
 
2008
        comment.deleteme = false; // Cancel the deleting of blank comments.
 
2009
 
 
2010
        // Now build the list of quicklist comments.
 
2011
        Y.each(comment.editor.quicklist.comments, function(quickcomment) {
 
2012
            var listitem = Y.Node.create('<li class="quicklist_comment"></li>'),
 
2013
                linkitem = Y.Node.create('<a href="#" tabindex="-1">' + quickcomment.rawtext + '</a>'),
 
2014
                deletelinkitem = Y.Node.create('<a href="#" tabindex="-1" class="delete_quicklist_comment">' +
 
2015
                                               '<img src="' + M.util.image_url('t/delete', 'core') + '" ' +
 
2016
                                               'alt="' + M.util.get_string('deletecomment', 'assignfeedback_editpdf') + '"/>' +
 
2017
                                               '</a>');
 
2018
            listitem.append(linkitem);
 
2019
            listitem.append(deletelinkitem);
 
2020
 
 
2021
            commentlinks.append(listitem);
 
2022
 
 
2023
            linkitem.on('click', comment.set_from_quick_comment, comment, quickcomment);
 
2024
            linkitem.on('key', comment.set_from_quick_comment, 'space,enter', comment, quickcomment);
 
2025
 
 
2026
            deletelinkitem.on('click', comment.remove_from_quicklist, comment, quickcomment);
 
2027
            deletelinkitem.on('key', comment.remove_from_quicklist, 'space,enter', comment, quickcomment);
 
2028
        }, this);
 
2029
 
 
2030
        COMMENTMENU.superclass.show.call(this);
 
2031
    }
 
2032
}, {
 
2033
    NAME : COMMENTMENUNAME,
 
2034
    ATTRS : {
 
2035
        /**
 
2036
         * The comment this menu is attached to.
 
2037
         *
 
2038
         * @attribute comment
 
2039
         * @type M.assignfeedback_editpdf.comment
 
2040
         * @default null
 
2041
         */
 
2042
        comment : {
 
2043
            value : null
 
2044
        }
 
2045
 
 
2046
    }
 
2047
});
 
2048
 
 
2049
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
2050
M.assignfeedback_editpdf.commentmenu = COMMENTMENU;
 
2051
var COMMENTSEARCHNAME = "commentsearch",
 
2052
    COMMENTSEARCH;
 
2053
 
 
2054
/**
 
2055
 * COMMENTSEARCH
 
2056
 * This is a searchable dialogue of comments.
 
2057
 *
 
2058
 * @namespace M.assignfeedback_editpdf.editor
 
2059
 * @class commentsearch
 
2060
 * @constructor
 
2061
 * @extends Y.Base
 
2062
 */
 
2063
COMMENTSEARCH = function(config) {
 
2064
    config.draggable = false;
 
2065
    config.centered = true;
 
2066
    config.width = '400px';
 
2067
    config.lightbox = true;
 
2068
    config.visible = false;
 
2069
    config.headerContent = M.util.get_string('searchcomments', 'assignfeedback_editpdf');
 
2070
    config.footerContent = '';
 
2071
    COMMENTSEARCH.superclass.constructor.apply(this, [config]);
 
2072
};
 
2073
 
 
2074
Y.extend(COMMENTSEARCH, M.core.dialogue, {
 
2075
    /**
 
2076
     * Initialise the menu.
 
2077
     *
 
2078
     * @method initializer
 
2079
     * @return void
 
2080
     */
 
2081
    initializer : function(config) {
 
2082
        var editor,
 
2083
            container,
 
2084
            placeholder,
 
2085
            commentfilter,
 
2086
            commentlist,
 
2087
            bb;
 
2088
 
 
2089
        bb = this.get('boundingBox');
 
2090
        bb.addClass('assignfeedback_editpdf_commentsearch');
 
2091
 
 
2092
        editor = this.get('editor');
 
2093
        container = Y.Node.create('<div/>');
 
2094
 
 
2095
        placeholder = M.util.get_string('filter', 'assignfeedback_editpdf');
 
2096
        commentfilter = Y.Node.create('<input type="text" size="20" placeholder="' + placeholder + '"/>');
 
2097
        container.append(commentfilter);
 
2098
        commentlist = Y.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>');
 
2099
        container.append(commentlist);
 
2100
 
 
2101
        commentfilter.on('keyup', this.filter_search_comments, null, this);
 
2102
        commentlist.delegate('click', this.focus_on_comment, 'a', this);
 
2103
        commentlist.delegate('key', this.focus_on_comment, 'enter,space', 'a', this);
 
2104
 
 
2105
        // Set the body content.
 
2106
        this.set('bodyContent', container);
 
2107
 
 
2108
        COMMENTSEARCH.superclass.initializer.call(this, config);
 
2109
    },
 
2110
 
 
2111
    /**
 
2112
     * Event handler to filter the list of comments.
 
2113
     *
 
2114
     * @protected
 
2115
     * @method filter_search_comments
 
2116
     */
 
2117
    filter_search_comments : function() {
 
2118
        var filternode,
 
2119
            commentslist,
 
2120
            filtertext;
 
2121
 
 
2122
        filternode = Y.one(SELECTOR.SEARCHFILTER);
 
2123
        commentslist = Y.one(SELECTOR.SEARCHCOMMENTSLIST);
 
2124
 
 
2125
        filtertext = filternode.get('value');
 
2126
 
 
2127
        commentslist.all('li').each(function (node) {
 
2128
            if (node.get('text').indexOf(filtertext) !== -1) {
 
2129
                node.show();
 
2130
            } else {
 
2131
                node.hide();
 
2132
            }
 
2133
        });
 
2134
    },
 
2135
 
 
2136
    /**
 
2137
     * Event handler to focus on a selected comment.
 
2138
     *
 
2139
     * @param Event e
 
2140
     * @protected
 
2141
     * @method focus_on_comment
 
2142
     */
 
2143
    focus_on_comment : function(e) {
 
2144
        e.preventDefault();
 
2145
        var target = e.target.ancestor('li'),
 
2146
            comment = target.getData('comment'),
 
2147
            editor = this.get('editor');
 
2148
 
 
2149
        this.hide();
 
2150
 
 
2151
        if (comment.pageno === editor.currentpage) {
 
2152
            comment.drawable.nodes[0].one('textarea').focus();
 
2153
        } else {
 
2154
            // Comment is on a different page.
 
2155
            editor.currentpage = comment.pageno;
 
2156
            editor.change_page();
 
2157
            comment.drawable.nodes[0].one('textarea').focus();
 
2158
        }
 
2159
    },
 
2160
 
 
2161
    /**
 
2162
     * Show the menu.
 
2163
     *
 
2164
     * @method show
 
2165
     * @return void
 
2166
     */
 
2167
    show : function() {
 
2168
        var commentlist = this.get('boundingBox').one('ul'),
 
2169
            editor = this.get('editor');
 
2170
 
 
2171
        commentlist.all('li').remove(true);
 
2172
 
 
2173
        // Rebuild the latest list of comments.
 
2174
        Y.each(editor.pages, function(page) {
 
2175
            Y.each(page.comments, function(comment) {
 
2176
                var commentnode = Y.Node.create('<li><a href="#" tabindex="-1"><pre>' + comment.rawtext + '</pre></a></li>');
 
2177
                commentlist.append(commentnode);
 
2178
                commentnode.setData('comment', comment);
 
2179
            }, this);
 
2180
        }, this);
 
2181
 
 
2182
        this.centerDialogue();
 
2183
        COMMENTSEARCH.superclass.show.call(this);
 
2184
    }
 
2185
}, {
 
2186
    NAME : COMMENTSEARCHNAME,
 
2187
    ATTRS : {
 
2188
        /**
 
2189
         * The editor this search window is attached to.
 
2190
         *
 
2191
         * @attribute editor
 
2192
         * @type M.assignfeedback_editpdf.editor
 
2193
         * @default null
 
2194
         */
 
2195
        editor : {
 
2196
            value : null
 
2197
        }
 
2198
 
 
2199
    }
 
2200
});
 
2201
 
 
2202
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
2203
M.assignfeedback_editpdf.commentsearch = COMMENTSEARCH;
 
2204
// This file is part of Moodle - http://moodle.org/
 
2205
//
 
2206
// Moodle is free software: you can redistribute it and/or modify
 
2207
// it under the terms of the GNU General Public License as published by
 
2208
// the Free Software Foundation, either version 3 of the License, or
 
2209
// (at your option) any later version.
 
2210
//
 
2211
// Moodle is distributed in the hope that it will be useful,
 
2212
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
2213
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
2214
// GNU General Public License for more details.
 
2215
//
 
2216
// You should have received a copy of the GNU General Public License
 
2217
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
2218
 
 
2219
/**
 
2220
 * Class representing a list of comments.
 
2221
 *
 
2222
 * @module moodle-assignfeedback_editpdf-editor
 
2223
 */
 
2224
 
 
2225
/**
 
2226
 * COMMENT
 
2227
 *
 
2228
 * @namespace M.assignfeedback_editpdf
 
2229
 * @class comment
 
2230
 * @param M.assignfeedback_editpdf.editor editor
 
2231
 * @param Int gradeid
 
2232
 * @param Int pageno
 
2233
 * @param Int x
 
2234
 * @param Int y
 
2235
 * @param Int width
 
2236
 * @param String colour
 
2237
 * @param String rawtext
 
2238
 */
 
2239
COMMENT = function(editor, gradeid, pageno, x, y, width, colour, rawtext) {
 
2240
 
 
2241
    /**
 
2242
     * Reference to M.assignfeedback_editpdf.editor.
 
2243
     * @property editor
 
2244
     * @type M.assignfeedback_editpdf.editor
 
2245
     * @public
 
2246
     */
 
2247
    this.editor = editor;
 
2248
 
 
2249
    /**
 
2250
     * Grade id
 
2251
     * @property gradeid
 
2252
     * @type Int
 
2253
     * @public
 
2254
     */
 
2255
    this.gradeid = gradeid || 0;
 
2256
 
 
2257
    /**
 
2258
     * X position
 
2259
     * @property x
 
2260
     * @type Int
 
2261
     * @public
 
2262
     */
 
2263
    this.x = parseInt(x, 10) || 0;
 
2264
 
 
2265
    /**
 
2266
     * Y position
 
2267
     * @property y
 
2268
     * @type Int
 
2269
     * @public
 
2270
     */
 
2271
    this.y = parseInt(y, 10) || 0;
 
2272
 
 
2273
    /**
 
2274
     * Comment width
 
2275
     * @property width
 
2276
     * @type Int
 
2277
     * @public
 
2278
     */
 
2279
    this.width = parseInt(width, 10) || 0;
 
2280
 
 
2281
    /**
 
2282
     * Comment rawtext
 
2283
     * @property rawtext
 
2284
     * @type String
 
2285
     * @public
 
2286
     */
 
2287
    this.rawtext = rawtext || '';
 
2288
 
 
2289
    /**
 
2290
     * Comment page number
 
2291
     * @property pageno
 
2292
     * @type Int
 
2293
     * @public
 
2294
     */
 
2295
    this.pageno = pageno || 0;
 
2296
 
 
2297
    /**
 
2298
     * Comment background colour.
 
2299
     * @property colour
 
2300
     * @type String
 
2301
     * @public
 
2302
     */
 
2303
    this.colour = colour || 'yellow';
 
2304
 
 
2305
    /**
 
2306
     * Reference to M.assignfeedback_editpdf.drawable
 
2307
     * @property drawable
 
2308
     * @type M.assignfeedback_editpdf.drawable
 
2309
     * @public
 
2310
     */
 
2311
    this.drawable = false;
 
2312
 
 
2313
    /**
 
2314
     * Boolean used by a timeout to delete empty comments after a short delay.
 
2315
     * @property deleteme
 
2316
     * @type Boolean
 
2317
     * @public
 
2318
     */
 
2319
    this.deleteme = false;
 
2320
 
 
2321
    /**
 
2322
     * Reference to the link that opens the menu.
 
2323
     * @property menulink
 
2324
     * @type Y.Node
 
2325
     * @public
 
2326
     */
 
2327
    this.menulink = null;
 
2328
 
 
2329
    /**
 
2330
     * Reference to the dialogue that is the context menu.
 
2331
     * @property menu
 
2332
     * @type M.assignfeedback_editpdf.dropdown
 
2333
     * @public
 
2334
     */
 
2335
    this.menu = null;
 
2336
 
 
2337
    /**
 
2338
     * Clean a comment record, returning an oject with only fields that are valid.
 
2339
     * @public
 
2340
     * @method clean
 
2341
     * @return {}
 
2342
     */
 
2343
    this.clean = function() {
 
2344
        return {
 
2345
            gradeid : this.gradeid,
 
2346
            x : parseInt(this.x, 10),
 
2347
            y : parseInt(this.y, 10),
 
2348
            width : parseInt(this.width, 10),
 
2349
            rawtext : this.rawtext,
 
2350
            pageno : this.currentpage,
 
2351
            colour : this.colour
 
2352
        };
 
2353
    };
 
2354
 
 
2355
    /**
 
2356
     * Draw a comment.
 
2357
     * @public
 
2358
     * @method draw_comment
 
2359
     * @param boolean focus - Set the keyboard focus to the new comment if true
 
2360
     * @return M.assignfeedback_editpdf.drawable
 
2361
     */
 
2362
    this.draw = function(focus) {
 
2363
        var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
 
2364
            node,
 
2365
            drawingregion = Y.one(SELECTOR.DRAWINGREGION),
 
2366
            container,
 
2367
            menu,
 
2368
            position,
 
2369
            scrollheight;
 
2370
 
 
2371
        // Lets add a contenteditable div.
 
2372
        node = Y.Node.create('<textarea/>');
 
2373
        container = Y.Node.create('<div class="commentdrawable"/>');
 
2374
        menu = Y.Node.create('<a href="#"><img src="' + M.util.image_url('t/contextmenu', 'core') + '"/></a>');
 
2375
 
 
2376
        this.menulink = menu;
 
2377
        container.append(node);
 
2378
 
 
2379
        if (!this.editor.get('readonly')) {
 
2380
            container.append(menu);
 
2381
        } else {
 
2382
            node.setAttribute('readonly', 'readonly');
 
2383
        }
 
2384
        if (this.width < 100) {
 
2385
            this.width = 100;
 
2386
        }
 
2387
 
 
2388
        position = this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(this.x, this.y));
 
2389
        node.setStyles({
 
2390
            width: this.width + 'px',
 
2391
            backgroundColor: COMMENTCOLOUR[this.colour],
 
2392
            color: COMMENTTEXTCOLOUR
 
2393
        });
 
2394
 
 
2395
        drawingregion.append(container);
 
2396
        container.setStyle('position', 'absolute');
 
2397
        container.setX(position.x);
 
2398
        container.setY(position.y);
 
2399
        drawable.nodes.push(container);
 
2400
        node.set('value', this.rawtext);
 
2401
        scrollheight = node.get('scrollHeight'),
 
2402
        node.setStyles({
 
2403
            'height' : scrollheight + 'px',
 
2404
            'overflow': 'hidden'
 
2405
        });
 
2406
        if (!this.editor.get('readonly')) {
 
2407
            this.attach_events(node, menu);
 
2408
        }
 
2409
        if (focus) {
 
2410
            node.focus();
 
2411
        }
 
2412
        this.drawable = drawable;
 
2413
 
 
2414
 
 
2415
        return drawable;
 
2416
    };
 
2417
 
 
2418
    /**
 
2419
     * Delete an empty comment if it's menu hasn't been opened in time.
 
2420
     * @method delete_comment_later
 
2421
     */
 
2422
    this.delete_comment_later = function() {
 
2423
        if (this.deleteme) {
 
2424
            this.remove();
 
2425
        }
 
2426
    };
 
2427
 
 
2428
    /**
 
2429
     * Comment nodes have a bunch of event handlers attached to them directly.
 
2430
     * This is all done here for neatness.
 
2431
     *
 
2432
     * @protected
 
2433
     * @method attach_comment_events
 
2434
     * @param node - The Y.Node representing the comment.
 
2435
     * @param menu - The Y.Node representing the menu.
 
2436
     */
 
2437
    this.attach_events = function(node, menu) {
 
2438
        // Save the text on blur.
 
2439
        node.on('blur', function() {
 
2440
            // Save the changes back to the comment.
 
2441
            this.rawtext = node.get('value');
 
2442
            this.width = parseInt(node.getStyle('width'), 10);
 
2443
 
 
2444
            // Trim.
 
2445
            if (this.rawtext.replace(/^\s+|\s+$/g, "") === '') {
 
2446
                // Delete empty comments.
 
2447
                this.deleteme = true;
 
2448
                Y.later(400, this, this.delete_comment_later);
 
2449
            }
 
2450
            this.editor.save_current_page();
 
2451
            this.editor.editingcomment = false;
 
2452
        }, this);
 
2453
 
 
2454
        // For delegated event handler.
 
2455
        menu.setData('comment', this);
 
2456
 
 
2457
        node.on('keyup', function() {
 
2458
            var scrollheight = node.get('scrollHeight'),
 
2459
                height = parseInt(node.getStyle('height'), 10);
 
2460
 
 
2461
            // Webkit scrollheight fix.
 
2462
            if (scrollheight === height + 8) {
 
2463
                scrollheight -= 8;
 
2464
            }
 
2465
            node.setStyle('height', scrollheight + 'px');
 
2466
 
 
2467
        });
 
2468
 
 
2469
        node.on('gesturemovestart', function(e) {
 
2470
            node.setData('dragging', true);
 
2471
            node.setData('offsetx', e.clientX - node.getX());
 
2472
            node.setData('offsety', e.clientY - node.getY());
 
2473
        });
 
2474
        node.on('gesturemoveend', function() {
 
2475
            node.setData('dragging', false);
 
2476
            this.editor.save_current_page();
 
2477
        }, null, this);
 
2478
        node.on('gesturemove', function(e) {
 
2479
            var x = e.clientX - node.getData('offsetx'),
 
2480
                y = e.clientY - node.getData('offsety'),
 
2481
                nodewidth,
 
2482
                nodeheight,
 
2483
                newlocation,
 
2484
                windowlocation,
 
2485
                bounds;
 
2486
 
 
2487
            nodewidth = parseInt(node.getStyle('width'), 10);
 
2488
            nodeheight = parseInt(node.getStyle('height'), 10);
 
2489
 
 
2490
            newlocation = this.editor.get_canvas_coordinates(new M.assignfeedback_editpdf.point(x, y));
 
2491
            bounds = this.editor.get_canvas_bounds(true);
 
2492
            bounds.x = 0;
 
2493
            bounds.y = 0;
 
2494
 
 
2495
            bounds.width -= nodewidth + 42;
 
2496
            bounds.height -= nodeheight + 8;
 
2497
            // Clip to the window size - the comment size.
 
2498
            newlocation.clip(bounds);
 
2499
 
 
2500
            this.x = newlocation.x;
 
2501
            this.y = newlocation.y;
 
2502
 
 
2503
            windowlocation = this.editor.get_window_coordinates(newlocation);
 
2504
            node.ancestor().setX(windowlocation.x);
 
2505
            node.ancestor().setY(windowlocation.y);
 
2506
        }, null, this);
 
2507
 
 
2508
        this.menu = new M.assignfeedback_editpdf.commentmenu({
 
2509
            buttonNode: this.menulink,
 
2510
            comment: this
 
2511
        });
 
2512
    };
 
2513
 
 
2514
    /**
 
2515
     * Delete a comment.
 
2516
     * @method remove
 
2517
     */
 
2518
    this.remove = function() {
 
2519
        var i = 0, comments;
 
2520
 
 
2521
        comments = this.editor.pages[this.editor.currentpage].comments;
 
2522
        for (i = 0; i < comments.length; i++) {
 
2523
            if (comments[i] === this) {
 
2524
                comments.splice(i, 1);
 
2525
                this.drawable.erase();
 
2526
                this.editor.save_current_page();
 
2527
                return;
 
2528
            }
 
2529
        }
 
2530
    };
 
2531
 
 
2532
    /**
 
2533
     * Event handler to remove a comment from the users quicklist.
 
2534
     *
 
2535
     * @protected
 
2536
     * @method remove_from_quicklist
 
2537
     */
 
2538
    this.remove_from_quicklist = function(e, quickcomment) {
 
2539
        e.preventDefault();
 
2540
 
 
2541
        this.menu.hide();
 
2542
 
 
2543
        this.editor.quicklist.remove(quickcomment);
 
2544
    };
 
2545
 
 
2546
    /**
 
2547
     * A quick comment was selected in the list, update the active comment and redraw the page.
 
2548
     *
 
2549
     * @param Event e
 
2550
     * @protected
 
2551
     * @method set_from_quick_comment
 
2552
     */
 
2553
    this.set_from_quick_comment = function(e, quickcomment) {
 
2554
        e.preventDefault();
 
2555
 
 
2556
        this.menu.hide();
 
2557
 
 
2558
        this.rawtext = quickcomment.rawtext;
 
2559
        this.width = quickcomment.width;
 
2560
        this.colour = quickcomment.colour;
 
2561
 
 
2562
        this.editor.save_current_page();
 
2563
 
 
2564
        this.editor.redraw();
 
2565
    };
 
2566
 
 
2567
    /**
 
2568
     * Event handler to add a comment to the users quicklist.
 
2569
     *
 
2570
     * @protected
 
2571
     * @method add_to_quicklist
 
2572
     */
 
2573
    this.add_to_quicklist = function(e) {
 
2574
        e.preventDefault();
 
2575
        this.menu.hide();
 
2576
        this.editor.quicklist.add(this);
 
2577
    };
 
2578
 
 
2579
    /**
 
2580
     * Draw the in progress edit.
 
2581
     *
 
2582
     * @public
 
2583
     * @method draw_current_edit
 
2584
     * @param M.assignfeedback_editpdf.edit edit
 
2585
     */
 
2586
    this.draw_current_edit = function(edit) {
 
2587
        var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
 
2588
            shape,
 
2589
            bounds;
 
2590
 
 
2591
        bounds = new M.assignfeedback_editpdf.rect();
 
2592
        bounds.bound([edit.start, edit.end]);
 
2593
 
 
2594
        // We will draw a box with the current background colour.
 
2595
        shape = this.editor.graphic.addShape({
 
2596
            type: Y.Rect,
 
2597
            width: bounds.width,
 
2598
            height: bounds.height,
 
2599
            fill: {
 
2600
               color: COMMENTCOLOUR[edit.commentcolour]
 
2601
            },
 
2602
            x: bounds.x,
 
2603
            y: bounds.y
 
2604
        });
 
2605
 
 
2606
        drawable.shapes.push(shape);
 
2607
 
 
2608
        return drawable;
 
2609
    };
 
2610
 
 
2611
    /**
 
2612
     * Promote the current edit to a real comment.
 
2613
     *
 
2614
     * @public
 
2615
     * @method init_from_edit
 
2616
     * @param M.assignfeedback_editpdf.edit edit
 
2617
     * @return bool true if comment bound is more than min width/height, else false.
 
2618
     */
 
2619
    this.init_from_edit = function(edit) {
 
2620
        var bounds = new M.assignfeedback_editpdf.rect();
 
2621
        bounds.bound([edit.start, edit.end]);
 
2622
 
 
2623
        // Minimum comment width.
 
2624
        if (bounds.width < 100) {
 
2625
            bounds.width = 100;
 
2626
        }
 
2627
 
 
2628
        // Save the current edit to the server and the current page list.
 
2629
 
 
2630
        this.gradeid = this.editor.get('gradeid');
 
2631
        this.pageno = this.editor.currentpage;
 
2632
        this.x = bounds.x;
 
2633
        this.y = bounds.y;
 
2634
        this.width = bounds.width;
 
2635
        this.colour = edit.commentcolour;
 
2636
        this.rawtext = '';
 
2637
 
 
2638
        return (bounds.has_min_width() && bounds.has_min_height());
 
2639
    };
 
2640
 
 
2641
};
 
2642
 
 
2643
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
2644
M.assignfeedback_editpdf.comment = COMMENT;
 
2645
// This file is part of Moodle - http://moodle.org/
 
2646
//
 
2647
// Moodle is free software: you can redistribute it and/or modify
 
2648
// it under the terms of the GNU General Public License as published by
 
2649
// the Free Software Foundation, either version 3 of the License, or
 
2650
// (at your option) any later version.
 
2651
//
 
2652
// Moodle is distributed in the hope that it will be useful,
 
2653
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
2654
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
2655
// GNU General Public License for more details.
 
2656
//
 
2657
// You should have received a copy of the GNU General Public License
 
2658
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
2659
 
 
2660
/**
 
2661
 * Class representing a users quick comment.
 
2662
 *
 
2663
 * @module moodle-assignfeedback_editpdf-editor
 
2664
 */
 
2665
 
 
2666
/**
 
2667
 * QUICKCOMMENT
 
2668
 *
 
2669
 * @namespace M.assignfeedback_editpdf
 
2670
 * @class quickcomment
 
2671
 */
 
2672
QUICKCOMMENT = function(id, rawtext, width, colour) {
 
2673
 
 
2674
    /**
 
2675
     * Quick comment text.
 
2676
     * @property rawtext
 
2677
     * @type String
 
2678
     * @public
 
2679
     */
 
2680
    this.rawtext = rawtext || '';
 
2681
 
 
2682
    /**
 
2683
     * ID of the comment
 
2684
     * @property id
 
2685
     * @type Int
 
2686
     * @public
 
2687
     */
 
2688
    this.id = id || 0;
 
2689
 
 
2690
    /**
 
2691
     * Width of the comment
 
2692
     * @property width
 
2693
     * @type Int
 
2694
     * @public
 
2695
     */
 
2696
    this.width = width || 100;
 
2697
 
 
2698
    /**
 
2699
     * Colour of the comment.
 
2700
     * @property colour
 
2701
     * @type String
 
2702
     * @public
 
2703
     */
 
2704
    this.colour = colour || "yellow";
 
2705
};
 
2706
 
 
2707
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
2708
M.assignfeedback_editpdf.quickcomment = QUICKCOMMENT;
 
2709
// This file is part of Moodle - http://moodle.org/
 
2710
//
 
2711
// Moodle is free software: you can redistribute it and/or modify
 
2712
// it under the terms of the GNU General Public License as published by
 
2713
// the Free Software Foundation, either version 3 of the License, or
 
2714
// (at your option) any later version.
 
2715
//
 
2716
// Moodle is distributed in the hope that it will be useful,
 
2717
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
2718
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
2719
// GNU General Public License for more details.
 
2720
//
 
2721
// You should have received a copy of the GNU General Public License
 
2722
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
2723
 
 
2724
/**
 
2725
 * Class representing a users list of quick comments.
 
2726
 *
 
2727
 * @module moodle-assignfeedback_editpdf-editor
 
2728
 */
 
2729
 
 
2730
/**
 
2731
 * QUICKLIST
 
2732
 *
 
2733
 * @namespace M.assignfeedback_editpdf
 
2734
 * @class quickcommentlist
 
2735
 */
 
2736
QUICKCOMMENTLIST = function(editor) {
 
2737
 
 
2738
    /**
 
2739
     * Reference to M.assignfeedback_editpdf.editor.
 
2740
     * @property editor
 
2741
     * @type M.assignfeedback_editpdf.editor
 
2742
     * @public
 
2743
     */
 
2744
    this.editor = editor;
 
2745
 
 
2746
    /**
 
2747
     * Array of Comments
 
2748
     * @property shapes
 
2749
     * @type M.assignfeedback_editpdf.quickcomment[]
 
2750
     * @public
 
2751
     */
 
2752
    this.comments = [];
 
2753
 
 
2754
    /**
 
2755
     * Add a comment to the users quicklist.
 
2756
     *
 
2757
     * @protected
 
2758
     * @method add
 
2759
     */
 
2760
    this.add = function(comment) {
 
2761
        var ajaxurl = AJAXBASE,
 
2762
            config;
 
2763
 
 
2764
        // Do not save empty comments.
 
2765
        if (comment.rawtext === '') {
 
2766
            return;
 
2767
        }
 
2768
 
 
2769
        config = {
 
2770
            method: 'post',
 
2771
            context: this,
 
2772
            sync: false,
 
2773
            data : {
 
2774
                'sesskey' : M.cfg.sesskey,
 
2775
                'action' : 'addtoquicklist',
 
2776
                'userid' : this.editor.get('userid'),
 
2777
                'commenttext' : comment.rawtext,
 
2778
                'width' : comment.width,
 
2779
                'colour' : comment.colour,
 
2780
                'attemptnumber' : this.editor.get('attemptnumber'),
 
2781
                'assignmentid' : this.editor.get('assignmentid')
 
2782
            },
 
2783
            on: {
 
2784
                success: function(tid, response) {
 
2785
                    var jsondata, quickcomment;
 
2786
                    try {
 
2787
                        jsondata = Y.JSON.parse(response.responseText);
 
2788
                        if (jsondata.error) {
 
2789
                            return new M.core.ajaxException(jsondata);
 
2790
                        } else {
 
2791
                            quickcomment = new M.assignfeedback_editpdf.quickcomment(jsondata.id,
 
2792
                                                                                     jsondata.rawtext,
 
2793
                                                                                     jsondata.width,
 
2794
                                                                                     jsondata.colour);
 
2795
                            this.comments.push(quickcomment);
 
2796
                        }
 
2797
                    } catch (e) {
 
2798
                        return new M.core.exception(e);
 
2799
                    }
 
2800
                },
 
2801
                failure: function(tid, response) {
 
2802
                    return M.core.exception(response.responseText);
 
2803
                }
 
2804
            }
 
2805
        };
 
2806
 
 
2807
        Y.io(ajaxurl, config);
 
2808
    };
 
2809
 
 
2810
    /**
 
2811
     * Remove a comment from the users quicklist.
 
2812
     *
 
2813
     * @public
 
2814
     * @method remove
 
2815
     */
 
2816
    this.remove = function(comment) {
 
2817
        var ajaxurl = AJAXBASE,
 
2818
            config;
 
2819
 
 
2820
        // Should not happen.
 
2821
        if (!comment) {
 
2822
            return;
 
2823
        }
 
2824
 
 
2825
        config = {
 
2826
            method: 'post',
 
2827
            context: this,
 
2828
            sync: false,
 
2829
            data : {
 
2830
                'sesskey' : M.cfg.sesskey,
 
2831
                'action' : 'removefromquicklist',
 
2832
                'userid' : this.editor.get('userid'),
 
2833
                'commentid' : comment.id,
 
2834
                'attemptnumber' : this.editor.get('attemptnumber'),
 
2835
                'assignmentid' : this.editor.get('assignmentid')
 
2836
            },
 
2837
            on: {
 
2838
                success: function() {
 
2839
                    var i;
 
2840
 
 
2841
                    // Find and remove the comment from the quicklist.
 
2842
                    i = this.comments.indexOf(comment);
 
2843
                    if (i >= 0) {
 
2844
                        this.comments.splice(i, 1);
 
2845
                    }
 
2846
                },
 
2847
                failure: function(tid, response) {
 
2848
                    return M.core.exception(response.responseText);
 
2849
                }
 
2850
            }
 
2851
        };
 
2852
 
 
2853
        Y.io(ajaxurl, config);
 
2854
    };
 
2855
 
 
2856
    /**
 
2857
     * Load the users quick comments list.
 
2858
     *
 
2859
     * @protected
 
2860
     * @method load_quicklist
 
2861
     */
 
2862
    this.load = function() {
 
2863
        var ajaxurl = AJAXBASE,
 
2864
            config;
 
2865
 
 
2866
        config = {
 
2867
            method: 'get',
 
2868
            context: this,
 
2869
            sync: false,
 
2870
            data : {
 
2871
                'sesskey' : M.cfg.sesskey,
 
2872
                'action' : 'loadquicklist',
 
2873
                'userid' : this.editor.get('userid'),
 
2874
                'attemptnumber' : this.editor.get('attemptnumber'),
 
2875
                'assignmentid' : this.editor.get('assignmentid')
 
2876
            },
 
2877
            on: {
 
2878
                success: function(tid, response) {
 
2879
                    var jsondata;
 
2880
                    try {
 
2881
                        jsondata = Y.JSON.parse(response.responseText);
 
2882
                        if (jsondata.error) {
 
2883
                            return new M.core.ajaxException(jsondata);
 
2884
                        } else {
 
2885
                            Y.each(jsondata, function(comment) {
 
2886
                                var quickcomment = new M.assignfeedback_editpdf.quickcomment(comment.id,
 
2887
                                                                                             comment.rawtext,
 
2888
                                                                                             comment.width,
 
2889
                                                                                             comment.colour);
 
2890
                                this.comments.push(quickcomment);
 
2891
                            }, this);
 
2892
                        }
 
2893
                    } catch (e) {
 
2894
                        return new M.core.exception(e);
 
2895
                    }
 
2896
                },
 
2897
                failure: function(tid, response) {
 
2898
                    return M.core.exception(response.responseText);
 
2899
                }
 
2900
            }
 
2901
        };
 
2902
 
 
2903
        Y.io(ajaxurl, config);
 
2904
    };
 
2905
};
 
2906
 
 
2907
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
2908
M.assignfeedback_editpdf.quickcommentlist = QUICKCOMMENTLIST;
 
2909
// This file is part of Moodle - http://moodle.org/
 
2910
//
 
2911
// Moodle is free software: you can redistribute it and/or modify
 
2912
// it under the terms of the GNU General Public License as published by
 
2913
// the Free Software Foundation, either version 3 of the License, or
 
2914
// (at your option) any later version.
 
2915
//
 
2916
// Moodle is distributed in the hope that it will be useful,
 
2917
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
2918
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
2919
// GNU General Public License for more details.
 
2920
//
 
2921
// You should have received a copy of the GNU General Public License
 
2922
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
2923
 
 
2924
/**
 
2925
 * Provides an in browser PDF editor.
 
2926
 *
 
2927
 * @module moodle-assignfeedback_editpdf-editor
 
2928
 */
 
2929
 
 
2930
/**
 
2931
 * EDITOR
 
2932
 * This is an in browser PDF editor.
 
2933
 *
 
2934
 * @namespace M.assignfeedback_editpdf.editor
 
2935
 * @class Editor
 
2936
 * @constructor
 
2937
 * @extends Y.Base
 
2938
 */
 
2939
EDITOR = function() {
 
2940
    EDITOR.superclass.constructor.apply(this, arguments);
 
2941
};
 
2942
EDITOR.prototype = {
 
2943
 
 
2944
    // Instance variables.
 
2945
    /**
 
2946
     * The dialogue used for all action menu displays.
 
2947
     * @property type
 
2948
     * @type M.core.dialogue
 
2949
     * @protected
 
2950
     */
 
2951
    dialogue : null,
 
2952
 
 
2953
    /**
 
2954
     * The number of pages in the pdf.
 
2955
     * @property type
 
2956
     * @type int
 
2957
     * @protected
 
2958
     */
 
2959
    pagecount : 0,
 
2960
 
 
2961
    /**
 
2962
     * The active page in the editor.
 
2963
     * @property type
 
2964
     * @type int
 
2965
     * @protected
 
2966
     */
 
2967
    currentpage : 0,
 
2968
 
 
2969
    /**
 
2970
     * A list of page objects. Each page has a list of comments and annotations.
 
2971
     * @property type
 
2972
     * @type array
 
2973
     * @protected
 
2974
     */
 
2975
    pages : [],
 
2976
 
 
2977
    /**
 
2978
     * The yui node for the loading icon.
 
2979
     * @property type
 
2980
     * @type Y.Node
 
2981
     * @protected
 
2982
     */
 
2983
    loadingicon : null,
 
2984
 
 
2985
    /**
 
2986
     * Image object of the current page image.
 
2987
     * @property type
 
2988
     * @type Image
 
2989
     * @protected
 
2990
     */
 
2991
    pageimage : null,
 
2992
 
 
2993
    /**
 
2994
     * YUI Graphic class for drawing shapes.
 
2995
     * @property type
 
2996
     * @type Y.Graphic
 
2997
     * @protected
 
2998
     */
 
2999
    graphic : null,
 
3000
 
 
3001
    /**
 
3002
     * Info about the current edit operation.
 
3003
     * @property currentedit
 
3004
     * @type M.assignfeedback_editpdf.edit
 
3005
     * @protected
 
3006
     */
 
3007
    currentedit : new M.assignfeedback_editpdf.edit(),
 
3008
 
 
3009
    /**
 
3010
     * Current drawable.
 
3011
     * @property currentdrawable
 
3012
     * @type M.assignfeedback_editpdf.drawable (or false)
 
3013
     * @protected
 
3014
     */
 
3015
    currentdrawable : false,
 
3016
 
 
3017
    /**
 
3018
     * Current drawables.
 
3019
     * @property drawables
 
3020
     * @type array(M.assignfeedback_editpdf.drawable)
 
3021
     * @protected
 
3022
     */
 
3023
    drawables : [],
 
3024
 
 
3025
    /**
 
3026
     * Current comment when the comment menu is open.
 
3027
     * @property currentcomment
 
3028
     * @type M.assignfeedback_editpdf.comment
 
3029
     * @protected
 
3030
     */
 
3031
    currentcomment : null,
 
3032
 
 
3033
    /**
 
3034
     * Current annotation when the select tool is used.
 
3035
     * @property currentannotation
 
3036
     * @type M.assignfeedback_editpdf.annotation
 
3037
     * @protected
 
3038
     */
 
3039
    currentannotation : null,
 
3040
 
 
3041
    /**
 
3042
     * Last selected annotation tool
 
3043
     * @property lastannotationtool
 
3044
     * @type String
 
3045
     * @protected
 
3046
     */
 
3047
    lastanntationtool : "pen",
 
3048
 
 
3049
    /**
 
3050
     * The users comments quick list
 
3051
     * @property quicklist
 
3052
     * @type M.assignfeedback_editpdf.quickcommentlist
 
3053
     * @protected
 
3054
     */
 
3055
    quicklist : null,
 
3056
 
 
3057
    /**
 
3058
     * The search comments window.
 
3059
     * @property searchcommentswindow
 
3060
     * @type M.core.dialogue
 
3061
     * @protected
 
3062
     */
 
3063
    searchcommentswindow : null,
 
3064
 
 
3065
 
 
3066
    /**
 
3067
     * The selected stamp picture.
 
3068
     * @property currentstamp
 
3069
     * @type String
 
3070
     * @protected
 
3071
     */
 
3072
    currentstamp : null,
 
3073
 
 
3074
    /**
 
3075
     * The stamps.
 
3076
     * @property stamps
 
3077
     * @type Array
 
3078
     * @protected
 
3079
     */
 
3080
    stamps : [],
 
3081
 
 
3082
    /**
 
3083
     * Prevent new comments from appearing
 
3084
     * immediately after clicking off a current
 
3085
     * comment
 
3086
     * @property editingcomment
 
3087
     * @type Boolean
 
3088
     * @public
 
3089
     */
 
3090
    editingcomment : false,
 
3091
 
 
3092
    /**
 
3093
     * Called during the initialisation process of the object.
 
3094
     * @method initializer
 
3095
     */
 
3096
    initializer : function() {
 
3097
        var link;
 
3098
 
 
3099
        link = Y.one('#' + this.get('linkid'));
 
3100
 
 
3101
        if (link) {
 
3102
            link.on('click', this.link_handler, this);
 
3103
            link.on('key', this.link_handler, 'down:13', this);
 
3104
 
 
3105
            this.currentedit.start = false;
 
3106
            this.currentedit.end = false;
 
3107
            if (!this.get('readonly')) {
 
3108
                this.quicklist = new M.assignfeedback_editpdf.quickcommentlist(this);
 
3109
            }
 
3110
        }
 
3111
    },
 
3112
 
 
3113
    /**
 
3114
     * Called to show/hide buttons and set the current colours/stamps.
 
3115
     * @method refresh_button_state
 
3116
     */
 
3117
    refresh_button_state : function() {
 
3118
        var button, currenttoolnode, imgurl;
 
3119
 
 
3120
        // Initalise the colour buttons.
 
3121
        button = Y.one(SELECTOR.COMMENTCOLOURBUTTON);
 
3122
 
 
3123
        imgurl = M.util.image_url('background_colour_' + this.currentedit.commentcolour, 'assignfeedback_editpdf');
 
3124
        button.one('img').setAttribute('src', imgurl);
 
3125
 
 
3126
        if (this.currentedit.commentcolour === 'clear') {
 
3127
            button.one('img').setStyle('borderStyle', 'dashed');
 
3128
        } else {
 
3129
            button.one('img').setStyle('borderStyle', 'solid');
 
3130
        }
 
3131
 
 
3132
        button = Y.one(SELECTOR.ANNOTATIONCOLOURBUTTON);
 
3133
        imgurl = M.util.image_url('colour_' + this.currentedit.annotationcolour, 'assignfeedback_editpdf');
 
3134
        button.one('img').setAttribute('src', imgurl);
 
3135
 
 
3136
        currenttoolnode = Y.one(TOOLSELECTOR[this.currentedit.tool]);
 
3137
        currenttoolnode.addClass('assignfeedback_editpdf_selectedbutton');
 
3138
        currenttoolnode.setAttribute('aria-pressed', 'true');
 
3139
 
 
3140
        button = Y.one(SELECTOR.STAMPSBUTTON);
 
3141
        button.one('img').setAttrs({'src': this.get_stamp_image_url(this.currentedit.stamp),
 
3142
                                    'height': '16',
 
3143
                                    'width': '16'});
 
3144
    },
 
3145
 
 
3146
    /**
 
3147
     * Called to get the bounds of the drawing region.
 
3148
     * @method get_canvas_bounds
 
3149
     */
 
3150
    get_canvas_bounds : function() {
 
3151
        var canvas = Y.one(SELECTOR.DRAWINGCANVAS),
 
3152
            offsetcanvas = canvas.getXY(),
 
3153
            offsetleft = offsetcanvas[0],
 
3154
            offsettop = offsetcanvas[1],
 
3155
            width = parseInt(canvas.getStyle('width'), 10),
 
3156
            height = parseInt(canvas.getStyle('height'), 10);
 
3157
 
 
3158
        return new M.assignfeedback_editpdf.rect(offsetleft, offsettop, width, height);
 
3159
    },
 
3160
 
 
3161
    /**
 
3162
     * Called to translate from window coordinates to canvas coordinates.
 
3163
     * @method get_canvas_coordinates
 
3164
     * @param M.assignfeedback_editpdf.point point in window coordinats.
 
3165
     */
 
3166
    get_canvas_coordinates : function(point) {
 
3167
        var bounds = this.get_canvas_bounds(),
 
3168
            newpoint = new M.assignfeedback_editpdf.point(point.x - bounds.x, point.y - bounds.y);
 
3169
 
 
3170
        bounds.x = bounds.y = 0;
 
3171
 
 
3172
        newpoint.clip(bounds);
 
3173
        return newpoint;
 
3174
    },
 
3175
 
 
3176
    /**
 
3177
     * Called to translate from canvas coordinates to window coordinates.
 
3178
     * @method get_window_coordinates
 
3179
     * @param M.assignfeedback_editpdf.point point in window coordinats.
 
3180
     */
 
3181
    get_window_coordinates : function(point) {
 
3182
        var bounds = this.get_canvas_bounds(),
 
3183
            newpoint = new M.assignfeedback_editpdf.point(point.x + bounds.x, point.y + bounds.y);
 
3184
 
 
3185
        return newpoint;
 
3186
    },
 
3187
 
 
3188
    /**
 
3189
     * Called to open the pdf editing dialogue.
 
3190
     * @method link_handler
 
3191
     */
 
3192
    link_handler : function(e) {
 
3193
        var drawingcanvas;
 
3194
        e.preventDefault();
 
3195
 
 
3196
        if (!this.dialogue) {
 
3197
            this.dialogue = new M.core.dialogue({
 
3198
                headerContent: this.get('header'),
 
3199
                bodyContent: this.get('body'),
 
3200
                footerContent: this.get('footer'),
 
3201
                modal: true,
 
3202
                width: '840px',
 
3203
                visible: false,
 
3204
                draggable: true
 
3205
            });
 
3206
 
 
3207
            // Add custom class for styling.
 
3208
            this.dialogue.get('boundingBox').addClass(CSS.DIALOGUE);
 
3209
 
 
3210
            this.loadingicon = Y.one(SELECTOR.LOADINGICON);
 
3211
 
 
3212
            drawingcanvas = Y.one(SELECTOR.DRAWINGCANVAS);
 
3213
            this.graphic = new Y.Graphic({render : SELECTOR.DRAWINGCANVAS});
 
3214
 
 
3215
            if (!this.get('readonly')) {
 
3216
                drawingcanvas.on('gesturemovestart', this.edit_start, null, this);
 
3217
                drawingcanvas.on('gesturemove', this.edit_move, null, this);
 
3218
                drawingcanvas.on('gesturemoveend', this.edit_end, null, this);
 
3219
 
 
3220
                this.refresh_button_state();
 
3221
            }
 
3222
 
 
3223
            this.load_all_pages();
 
3224
        }
 
3225
        this.dialogue.centerDialogue();
 
3226
        this.dialogue.show();
 
3227
    },
 
3228
 
 
3229
    /**
 
3230
     * Called to load the information and annotations for all pages.
 
3231
     * @method load_all_pages
 
3232
     */
 
3233
    load_all_pages : function() {
 
3234
        var ajaxurl = AJAXBASE,
 
3235
            config,
 
3236
            checkconversionstatus,
 
3237
            ajax_error_total;
 
3238
 
 
3239
        config = {
 
3240
            method: 'get',
 
3241
            context: this,
 
3242
            sync: false,
 
3243
            data : {
 
3244
                sesskey : M.cfg.sesskey,
 
3245
                action : 'loadallpages',
 
3246
                userid : this.get('userid'),
 
3247
                attemptnumber : this.get('attemptnumber'),
 
3248
                assignmentid : this.get('assignmentid')
 
3249
            },
 
3250
            on: {
 
3251
                success: function(tid, response) {
 
3252
                    this.all_pages_loaded(response.responseText);
 
3253
                },
 
3254
                failure: function(tid, response) {
 
3255
                    return new M.core.exception(response.responseText);
 
3256
                }
 
3257
            }
 
3258
        };
 
3259
 
 
3260
        Y.io(ajaxurl, config);
 
3261
 
 
3262
        // If pages are not loaded, check PDF conversion status for the progress bar.
 
3263
        if (this.pagecount <= 0) {
 
3264
            checkconversionstatus = {
 
3265
                method: 'get',
 
3266
                context: this,
 
3267
                sync: false,
 
3268
                data : {
 
3269
                    sesskey : M.cfg.sesskey,
 
3270
                    action : 'conversionstatus',
 
3271
                    userid : this.get('userid'),
 
3272
                    attemptnumber : this.get('attemptnumber'),
 
3273
                    assignmentid : this.get('assignmentid')
 
3274
                },
 
3275
                on: {
 
3276
                    success: function(tid, response) {
 
3277
                        ajax_error_total = 0;
 
3278
                        if (this.pagecount === 0) {
 
3279
                            var pagetotal = this.get('pagetotal');
 
3280
 
 
3281
                            // Update the progress bar.
 
3282
                            var progressbarcontainer = Y.one(SELECTOR.PROGRESSBARCONTAINER);
 
3283
                            var progressbar = progressbarcontainer.one('.bar');
 
3284
                            if (progressbar) {
 
3285
                                // Calculate progress.
 
3286
                                var progress = (response.response / pagetotal) * 100;
 
3287
                                progressbar.setStyle('width', progress + '%');
 
3288
                                progressbarcontainer.setAttribute('aria-valuenow', progress);
 
3289
                            }
 
3290
 
 
3291
                            // New ajax request delayed of a second.
 
3292
                            Y.later(1000, this, function () {
 
3293
                                Y.io(AJAXBASEPROGRESS, checkconversionstatus);
 
3294
                            });
 
3295
                        }
 
3296
                    },
 
3297
                    failure: function(tid, response) {
 
3298
                        ajax_error_total = ajax_error_total + 1;
 
3299
                        // We only continue on error if the all pages were not generated,
 
3300
                        // and if the ajax call did not produce 5 errors in the row.
 
3301
                        if (this.pagecount === 0 && ajax_error_total < 5) {
 
3302
                            Y.later(1000, this, function () {
 
3303
                                Y.io(AJAXBASEPROGRESS, checkconversionstatus);
 
3304
                            });
 
3305
                        }
 
3306
                        return new M.core.exception(response.responseText);
 
3307
                    }
 
3308
                }
 
3309
            };
 
3310
            // We start the AJAX "generated page total number" call a second later to give a chance to
 
3311
            // the AJAX "combined pdf generation" call to clean the previous submission images.
 
3312
            Y.later(1000, this, function () {
 
3313
                ajax_error_total = 0;
 
3314
                Y.io(AJAXBASEPROGRESS, checkconversionstatus);
 
3315
            });
 
3316
        }
 
3317
    },
 
3318
 
 
3319
    /**
 
3320
     * The info about all pages in the pdf has been returned.
 
3321
     * @param string The ajax response as text.
 
3322
     * @protected
 
3323
     * @method all_pages_loaded
 
3324
     */
 
3325
    all_pages_loaded : function(responsetext) {
 
3326
        var data, i, j, comment, error;
 
3327
        try {
 
3328
            data = Y.JSON.parse(responsetext);
 
3329
            if (data.error || !data.pagecount) {
 
3330
                this.dialogue.hide();
 
3331
                // Display alert dialogue.
 
3332
                error = new M.core.alert({ message: M.util.get_string('cannotopenpdf', 'assignfeedback_editpdf') });
 
3333
                error.show();
 
3334
                return;
 
3335
            }
 
3336
        } catch (e) {
 
3337
            this.dialogue.hide();
 
3338
            // Display alert dialogue.
 
3339
            error = new M.core.alert({ title: M.util.get_string('cannotopenpdf', 'assignfeedback_editpdf')});
 
3340
            error.show();
 
3341
            return;
 
3342
        }
 
3343
 
 
3344
        this.pagecount = data.pagecount;
 
3345
        this.pages = data.pages;
 
3346
 
 
3347
        for (i = 0; i < this.pages.length; i++) {
 
3348
            for (j = 0; j < this.pages[i].comments.length; j++) {
 
3349
                comment = this.pages[i].comments[j];
 
3350
                this.pages[i].comments[j] = new M.assignfeedback_editpdf.comment(this,
 
3351
                                                                                 comment.gradeid,
 
3352
                                                                                 comment.pageno,
 
3353
                                                                                 comment.x,
 
3354
                                                                                 comment.y,
 
3355
                                                                                 comment.width,
 
3356
                                                                                 comment.colour,
 
3357
                                                                                 comment.rawtext);
 
3358
            }
 
3359
            for (j = 0; j < this.pages[i].annotations.length; j++) {
 
3360
                data = this.pages[i].annotations[j];
 
3361
                this.pages[i].annotations[j] = this.create_annotation(data.type, data);
 
3362
            }
 
3363
        }
 
3364
 
 
3365
        // Update the ui.
 
3366
        if (this.quicklist) {
 
3367
            this.quicklist.load();
 
3368
        }
 
3369
        this.setup_navigation();
 
3370
        this.setup_toolbar();
 
3371
        this.change_page();
 
3372
    },
 
3373
 
 
3374
    /**
 
3375
     * Get the full pluginfile url for an image file - just given the filename.
 
3376
     *
 
3377
     * @public
 
3378
     * @method get_stamp_image_url
 
3379
     * @param string filename
 
3380
     */
 
3381
    get_stamp_image_url : function(filename) {
 
3382
        var urls = this.get('stampfiles'),
 
3383
            fullurl = '';
 
3384
 
 
3385
        Y.Array.each(urls, function(url) {
 
3386
            if (url.indexOf(filename) > 0) {
 
3387
                fullurl = url;
 
3388
            }
 
3389
        }, this);
 
3390
 
 
3391
        return fullurl;
 
3392
    },
 
3393
 
 
3394
    /**
 
3395
     * Attach listeners and enable the color picker buttons.
 
3396
     * @protected
 
3397
     * @method setup_toolbar
 
3398
     */
 
3399
    setup_toolbar : function() {
 
3400
        var toolnode,
 
3401
            commentcolourbutton,
 
3402
            annotationcolourbutton,
 
3403
            searchcommentsbutton,
 
3404
            currentstampbutton,
 
3405
            stampfiles,
 
3406
            picker,
 
3407
            filename;
 
3408
 
 
3409
        searchcommentsbutton = Y.one(SELECTOR.SEARCHCOMMENTSBUTTON);
 
3410
        searchcommentsbutton.on('click', this.open_search_comments, this);
 
3411
        searchcommentsbutton.on('key', this.open_search_comments, 'down:13', this);
 
3412
 
 
3413
        if (this.get('readonly')) {
 
3414
            return;
 
3415
        }
 
3416
        // Setup the tool buttons.
 
3417
        Y.each(TOOLSELECTOR, function(selector, tool) {
 
3418
            toolnode = Y.one(selector);
 
3419
            toolnode.on('click', this.handle_tool_button, this, tool);
 
3420
            toolnode.on('key', this.handle_tool_button, 'down:13', this, tool);
 
3421
            toolnode.setAttribute('aria-pressed', 'false');
 
3422
        }, this);
 
3423
 
 
3424
        // Set the default tool.
 
3425
 
 
3426
        commentcolourbutton = Y.one(SELECTOR.COMMENTCOLOURBUTTON);
 
3427
        picker = new M.assignfeedback_editpdf.colourpicker({
 
3428
            buttonNode: commentcolourbutton,
 
3429
            colours: COMMENTCOLOUR,
 
3430
            iconprefix: 'background_colour_',
 
3431
            callback: function (e) {
 
3432
                var colour = e.target.getAttribute('data-colour');
 
3433
                if (!colour) {
 
3434
                    colour = e.target.ancestor().getAttribute('data-colour');
 
3435
                }
 
3436
                this.currentedit.commentcolour = colour;
 
3437
                this.handle_tool_button(e, "comment");
 
3438
            },
 
3439
            context: this
 
3440
        });
 
3441
 
 
3442
        annotationcolourbutton = Y.one(SELECTOR.ANNOTATIONCOLOURBUTTON);
 
3443
        picker = new M.assignfeedback_editpdf.colourpicker({
 
3444
            buttonNode: annotationcolourbutton,
 
3445
            iconprefix: 'colour_',
 
3446
            colours: ANNOTATIONCOLOUR,
 
3447
            callback: function (e) {
 
3448
                var colour = e.target.getAttribute('data-colour');
 
3449
                if (!colour) {
 
3450
                    colour = e.target.ancestor().getAttribute('data-colour');
 
3451
                }
 
3452
                this.currentedit.annotationcolour = colour;
 
3453
                if (this.lastannotationtool) {
 
3454
                    this.handle_tool_button(e, this.lastannotationtool);
 
3455
                } else {
 
3456
                    this.handle_tool_button(e, "pen");
 
3457
                }
 
3458
            },
 
3459
            context: this
 
3460
        });
 
3461
 
 
3462
        stampfiles = this.get('stampfiles');
 
3463
        if (stampfiles.length <= 0) {
 
3464
            Y.one(TOOLSELECTOR.stamp).ancestor().hide();
 
3465
        } else {
 
3466
            filename = stampfiles[0].substr(stampfiles[0].lastIndexOf('/') + 1);
 
3467
            this.currentedit.stamp = filename;
 
3468
            currentstampbutton = Y.one(SELECTOR.STAMPSBUTTON);
 
3469
 
 
3470
            picker = new M.assignfeedback_editpdf.stamppicker({
 
3471
                buttonNode: currentstampbutton,
 
3472
                stamps: stampfiles,
 
3473
                callback: function(e) {
 
3474
                    var stamp = e.target.getAttribute('data-stamp'),
 
3475
                        filename;
 
3476
 
 
3477
                    if (!stamp) {
 
3478
                        stamp = e.target.ancestor().getAttribute('data-stamp');
 
3479
                    }
 
3480
                    filename = stamp.substr(stamp.lastIndexOf('/'));
 
3481
                    this.currentedit.stamp = filename;
 
3482
                    this.handle_tool_button(e, "stamp");
 
3483
                },
 
3484
                context: this
 
3485
            });
 
3486
            this.refresh_button_state();
 
3487
        }
 
3488
    },
 
3489
 
 
3490
    /**
 
3491
     * Change the current tool.
 
3492
     * @protected
 
3493
     * @method handle_tool_button
 
3494
     */
 
3495
    handle_tool_button : function(e, tool) {
 
3496
        var currenttoolnode;
 
3497
 
 
3498
        e.preventDefault();
 
3499
 
 
3500
        // Change style of the pressed button.
 
3501
        currenttoolnode = Y.one(TOOLSELECTOR[this.currentedit.tool]);
 
3502
        currenttoolnode.removeClass('assignfeedback_editpdf_selectedbutton');
 
3503
        currenttoolnode.setAttribute('aria-pressed', 'false');
 
3504
        this.currentedit.tool = tool;
 
3505
        if (tool !== "comment" && tool !== "select" && tool !== "stamp") {
 
3506
            this.lastannotationtool = tool;
 
3507
        }
 
3508
        this.refresh_button_state();
 
3509
    },
 
3510
 
 
3511
    /**
 
3512
     * JSON encode the current page data - stripping out drawable references which cannot be encoded.
 
3513
     * @protected
 
3514
     * @method stringify_current_page
 
3515
     * @return string
 
3516
     */
 
3517
    stringify_current_page : function() {
 
3518
        var comments = [],
 
3519
            annotations = [],
 
3520
            page,
 
3521
            i = 0;
 
3522
 
 
3523
        for (i = 0; i < this.pages[this.currentpage].comments.length; i++) {
 
3524
            comments[i] = this.pages[this.currentpage].comments[i].clean();
 
3525
        }
 
3526
        for (i = 0; i < this.pages[this.currentpage].annotations.length; i++) {
 
3527
            annotations[i] = this.pages[this.currentpage].annotations[i].clean();
 
3528
        }
 
3529
 
 
3530
        page = { comments : comments, annotations : annotations };
 
3531
 
 
3532
        return Y.JSON.stringify(page);
 
3533
    },
 
3534
 
 
3535
    /**
 
3536
     * Generate a drawable from the current in progress edit.
 
3537
     * @protected
 
3538
     * @method get_current_drawable
 
3539
     */
 
3540
    get_current_drawable : function() {
 
3541
        var comment,
 
3542
            annotation,
 
3543
            drawable = false;
 
3544
 
 
3545
        if (!this.currentedit.start || !this.currentedit.end) {
 
3546
            return false;
 
3547
        }
 
3548
 
 
3549
        if (this.currentedit.tool === 'comment') {
 
3550
            comment = new M.assignfeedback_editpdf.comment(this);
 
3551
            drawable = comment.draw_current_edit(this.currentedit);
 
3552
        } else {
 
3553
            annotation = this.create_annotation(this.currentedit.tool, {});
 
3554
            if (annotation) {
 
3555
                drawable = annotation.draw_current_edit(this.currentedit);
 
3556
            }
 
3557
        }
 
3558
 
 
3559
        return drawable;
 
3560
    },
 
3561
 
 
3562
    /**
 
3563
     * Redraw the active edit.
 
3564
     * @protected
 
3565
     * @method redraw_active_edit
 
3566
     */
 
3567
    redraw_current_edit : function() {
 
3568
        if (this.currentdrawable) {
 
3569
            this.currentdrawable.erase();
 
3570
        }
 
3571
        this.currentdrawable = this.get_current_drawable();
 
3572
    },
 
3573
 
 
3574
    /**
 
3575
     * Event handler for mousedown or touchstart.
 
3576
     * @protected
 
3577
     * @param Event
 
3578
     * @method edit_start
 
3579
     */
 
3580
    edit_start : function(e) {
 
3581
        var canvas = Y.one(SELECTOR.DRAWINGCANVAS),
 
3582
            offset = canvas.getXY(),
 
3583
            scrolltop = canvas.get('docScrollY'),
 
3584
            scrollleft = canvas.get('docScrollX'),
 
3585
            point = {x : e.clientX - offset[0] + scrollleft,
 
3586
                     y : e.clientY - offset[1] + scrolltop},
 
3587
            selected = false,
 
3588
            lastannotation;
 
3589
 
 
3590
        // Ignore right mouse click.
 
3591
        if (e.button === 3) {
 
3592
            return;
 
3593
        }
 
3594
 
 
3595
        if (this.currentedit.starttime) {
 
3596
            return;
 
3597
        }
 
3598
 
 
3599
        if (this.editingcomment) {
 
3600
            return;
 
3601
        }
 
3602
 
 
3603
        this.currentedit.starttime = new Date().getTime();
 
3604
        this.currentedit.start = point;
 
3605
        this.currentedit.end = {x : point.x, y : point.y};
 
3606
 
 
3607
        if (this.currentedit.tool === 'select') {
 
3608
            x = this.currentedit.end.x;
 
3609
            y = this.currentedit.end.y;
 
3610
            annotations = this.pages[this.currentpage].annotations;
 
3611
            // Find the first annotation whose bounds encompass the click.
 
3612
            Y.each(annotations, function(annotation) {
 
3613
                if (((x - annotation.x) * (x - annotation.endx)) <= 0 &&
 
3614
                    ((y - annotation.y) * (y - annotation.endy)) <= 0) {
 
3615
                    selected = annotation;
 
3616
                }
 
3617
            });
 
3618
 
 
3619
            if (selected) {
 
3620
                lastannotation = this.currentannotation;
 
3621
                this.currentannotation = selected;
 
3622
                if (lastannotation && lastannotation !== selected) {
 
3623
                    // Redraw the last selected annotation to remove the highlight.
 
3624
                    if (lastannotation.drawable) {
 
3625
                        lastannotation.drawable.erase();
 
3626
                        this.drawables.push(lastannotation.draw());
 
3627
                    }
 
3628
                }
 
3629
                // Redraw the newly selected annotation to show the highlight.
 
3630
                if (this.currentannotation.drawable) {
 
3631
                    this.currentannotation.drawable.erase();
 
3632
                }
 
3633
                this.drawables.push(this.currentannotation.draw());
 
3634
            }
 
3635
        }
 
3636
        if (this.currentannotation) {
 
3637
            // Used to calculate drag offset.
 
3638
            this.currentedit.annotationstart = { x : this.currentannotation.x,
 
3639
                                                 y : this.currentannotation.y };
 
3640
        }
 
3641
    },
 
3642
 
 
3643
    /**
 
3644
     * Event handler for mousemove.
 
3645
     * @protected
 
3646
     * @param Event
 
3647
     * @method edit_move
 
3648
     */
 
3649
    edit_move : function(e) {
 
3650
        var bounds = this.get_canvas_bounds(),
 
3651
            canvas = Y.one(SELECTOR.DRAWINGCANVAS),
 
3652
            clientpoint = new M.assignfeedback_editpdf.point(e.clientX + canvas.get('docScrollX'),
 
3653
                                                             e.clientY + canvas.get('docScrollY')),
 
3654
            point = this.get_canvas_coordinates(clientpoint);
 
3655
 
 
3656
        // Ignore events out of the canvas area.
 
3657
        if (point.x < 0 || point.x > bounds.width || point.y < 0 || point.y > bounds.height) {
 
3658
            return;
 
3659
        }
 
3660
 
 
3661
        if (this.currentedit.tool === 'pen') {
 
3662
            this.currentedit.path.push(point);
 
3663
        }
 
3664
 
 
3665
        if (this.currentedit.tool === 'select') {
 
3666
            if (this.currentannotation && this.currentedit) {
 
3667
                this.currentannotation.move( this.currentedit.annotationstart.x + point.x - this.currentedit.start.x,
 
3668
                                             this.currentedit.annotationstart.y + point.y - this.currentedit.start.y);
 
3669
            }
 
3670
        } else {
 
3671
            if (this.currentedit.start) {
 
3672
                this.currentedit.end = point;
 
3673
                this.redraw_current_edit();
 
3674
            }
 
3675
        }
 
3676
    },
 
3677
 
 
3678
    /**
 
3679
     * Event handler for mouseup or touchend.
 
3680
     * @protected
 
3681
     * @param Event
 
3682
     * @method edit_end
 
3683
     */
 
3684
    edit_end : function() {
 
3685
        var duration,
 
3686
            comment,
 
3687
            annotation;
 
3688
 
 
3689
        duration = new Date().getTime() - this.currentedit.start;
 
3690
 
 
3691
        if (duration < CLICKTIMEOUT || this.currentedit.start === false) {
 
3692
            return;
 
3693
        }
 
3694
 
 
3695
        if (this.currentedit.tool === 'comment') {
 
3696
            if (this.currentdrawable) {
 
3697
                this.currentdrawable.erase();
 
3698
            }
 
3699
            this.currentdrawable = false;
 
3700
            comment = new M.assignfeedback_editpdf.comment(this);
 
3701
            if (comment.init_from_edit(this.currentedit)) {
 
3702
                this.pages[this.currentpage].comments.push(comment);
 
3703
                this.drawables.push(comment.draw(true));
 
3704
                this.editingcomment = true;
 
3705
            }
 
3706
        } else {
 
3707
            annotation = this.create_annotation(this.currentedit.tool, {});
 
3708
            if (annotation) {
 
3709
                if (this.currentdrawable) {
 
3710
                    this.currentdrawable.erase();
 
3711
                }
 
3712
                this.currentdrawable = false;
 
3713
                if (annotation.init_from_edit(this.currentedit)) {
 
3714
                    this.pages[this.currentpage].annotations.push(annotation);
 
3715
                    this.drawables.push(annotation.draw());
 
3716
                }
 
3717
            }
 
3718
        }
 
3719
 
 
3720
        // Save the changes.
 
3721
        this.save_current_page();
 
3722
 
 
3723
        // Reset the current edit.
 
3724
        this.currentedit.starttime = 0;
 
3725
        this.currentedit.start = false;
 
3726
        this.currentedit.end = false;
 
3727
        this.currentedit.path = [];
 
3728
    },
 
3729
 
 
3730
    /**
 
3731
     * Factory method for creating annotations of the correct subclass.
 
3732
     * @public
 
3733
     * @method create_annotation
 
3734
     */
 
3735
    create_annotation : function(type, data) {
 
3736
        data.type = type;
 
3737
        data.editor = this;
 
3738
        if (type === "line") {
 
3739
            return new M.assignfeedback_editpdf.annotationline(data);
 
3740
        } else if (type === "rectangle") {
 
3741
            return new M.assignfeedback_editpdf.annotationrectangle(data);
 
3742
        } else if (type === "oval") {
 
3743
            return new M.assignfeedback_editpdf.annotationoval(data);
 
3744
        } else if (type === "pen") {
 
3745
            return new M.assignfeedback_editpdf.annotationpen(data);
 
3746
        } else if (type === "highlight") {
 
3747
            return new M.assignfeedback_editpdf.annotationhighlight(data);
 
3748
        } else if (type === "stamp") {
 
3749
            return new M.assignfeedback_editpdf.annotationstamp(data);
 
3750
        }
 
3751
        return false;
 
3752
    },
 
3753
 
 
3754
    /**
 
3755
     * Save all the annotations and comments for the current page.
 
3756
     * @protected
 
3757
     * @method save_current_page
 
3758
     */
 
3759
    save_current_page : function() {
 
3760
        var ajaxurl = AJAXBASE,
 
3761
            config;
 
3762
 
 
3763
        config = {
 
3764
            method: 'post',
 
3765
            context: this,
 
3766
            sync: false,
 
3767
            data : {
 
3768
                'sesskey' : M.cfg.sesskey,
 
3769
                'action' : 'savepage',
 
3770
                'index' : this.currentpage,
 
3771
                'userid' : this.get('userid'),
 
3772
                'attemptnumber' : this.get('attemptnumber'),
 
3773
                'assignmentid' : this.get('assignmentid'),
 
3774
                'page' : this.stringify_current_page()
 
3775
            },
 
3776
            on: {
 
3777
                success: function(tid, response) {
 
3778
                    var jsondata;
 
3779
                    try {
 
3780
                        jsondata = Y.JSON.parse(response.responseText);
 
3781
                        if (jsondata.error) {
 
3782
                            return new M.core.ajaxException(jsondata);
 
3783
                        }
 
3784
                        Y.one(SELECTOR.UNSAVEDCHANGESDIV).addClass('haschanges');
 
3785
                    } catch (e) {
 
3786
                        return new M.core.exception(e);
 
3787
                    }
 
3788
                },
 
3789
                failure: function(tid, response) {
 
3790
                    return new M.core.exception(response.responseText);
 
3791
                }
 
3792
            }
 
3793
        };
 
3794
 
 
3795
        Y.io(ajaxurl, config);
 
3796
 
 
3797
    },
 
3798
 
 
3799
    /**
 
3800
     * Event handler to open the comment search interface.
 
3801
     *
 
3802
     * @param Event e
 
3803
     * @protected
 
3804
     * @method open_search_comments
 
3805
     */
 
3806
    open_search_comments : function(e) {
 
3807
        if (!this.searchcommentswindow) {
 
3808
            this.searchcommentswindow = new M.assignfeedback_editpdf.commentsearch({
 
3809
                editor : this
 
3810
            });
 
3811
        }
 
3812
 
 
3813
        this.searchcommentswindow.show();
 
3814
        e.preventDefault();
 
3815
    },
 
3816
 
 
3817
    /**
 
3818
     * Redraw all the comments and annotations.
 
3819
     * @protected
 
3820
     * @method redraw
 
3821
     */
 
3822
    redraw : function() {
 
3823
        var i,
 
3824
            page;
 
3825
 
 
3826
        page = this.pages[this.currentpage];
 
3827
        while (this.drawables.length > 0) {
 
3828
            this.drawables.pop().erase();
 
3829
        }
 
3830
 
 
3831
        for (i = 0; i < page.annotations.length; i++) {
 
3832
            this.drawables.push(page.annotations[i].draw());
 
3833
        }
 
3834
        for (i = 0; i < page.comments.length; i++) {
 
3835
            this.drawables.push(page.comments[i].draw(false));
 
3836
        }
 
3837
    },
 
3838
 
 
3839
    /**
 
3840
     * Load the image for this pdf page and remove the loading icon (if there).
 
3841
     * @protected
 
3842
     * @method change_page
 
3843
     */
 
3844
    change_page : function() {
 
3845
        var drawingcanvas = Y.one(SELECTOR.DRAWINGCANVAS),
 
3846
            page,
 
3847
            previousbutton,
 
3848
            nextbutton;
 
3849
 
 
3850
        previousbutton = Y.one(SELECTOR.PREVIOUSBUTTON);
 
3851
        nextbutton = Y.one(SELECTOR.NEXTBUTTON);
 
3852
 
 
3853
        if (this.currentpage > 0) {
 
3854
            previousbutton.removeAttribute('disabled');
 
3855
        } else {
 
3856
            previousbutton.setAttribute('disabled', 'true');
 
3857
        }
 
3858
        if (this.currentpage < (this.pagecount - 1)) {
 
3859
            nextbutton.removeAttribute('disabled');
 
3860
        } else {
 
3861
            nextbutton.setAttribute('disabled', 'true');
 
3862
        }
 
3863
 
 
3864
        page = this.pages[this.currentpage];
 
3865
        this.loadingicon.hide();
 
3866
        drawingcanvas.setStyle('backgroundImage', 'url("' + page.url + '")');
 
3867
 
 
3868
        // Update page select.
 
3869
        Y.one(SELECTOR.PAGESELECT).set('value', this.currentpage);
 
3870
 
 
3871
        this.redraw();
 
3872
    },
 
3873
 
 
3874
    /**
 
3875
     * Now we know how many pages there are,
 
3876
     * we can enable the navigation controls.
 
3877
     * @protected
 
3878
     * @method setup_navigation
 
3879
     */
 
3880
    setup_navigation : function() {
 
3881
        var pageselect,
 
3882
            i,
 
3883
            option,
 
3884
            previousbutton,
 
3885
            nextbutton;
 
3886
 
 
3887
        pageselect = Y.one(SELECTOR.PAGESELECT);
 
3888
 
 
3889
        options = pageselect.all('option');
 
3890
        if (options.size() <= 1) {
 
3891
            for (i = 0; i < this.pages.length; i++) {
 
3892
                option = Y.Node.create('<option/>');
 
3893
                option.setAttribute('value', i);
 
3894
                option.setHTML(M.util.get_string('pagenumber', 'assignfeedback_editpdf', i+1));
 
3895
                pageselect.append(option);
 
3896
            }
 
3897
        }
 
3898
        pageselect.removeAttribute('disabled');
 
3899
        pageselect.on('change', function() {
 
3900
            this.currentpage = pageselect.get('value');
 
3901
            this.change_page();
 
3902
        }, this);
 
3903
 
 
3904
        previousbutton = Y.one(SELECTOR.PREVIOUSBUTTON);
 
3905
        nextbutton = Y.one(SELECTOR.NEXTBUTTON);
 
3906
 
 
3907
        previousbutton.on('click', this.previous_page, this);
 
3908
        previousbutton.on('key', this.previous_page, 'down:13', this);
 
3909
        nextbutton.on('click', this.next_page, this);
 
3910
        nextbutton.on('key', this.next_page, 'down:13', this);
 
3911
    },
 
3912
 
 
3913
    /**
 
3914
     * Navigate to the previous page.
 
3915
     * @protected
 
3916
     * @method previous_page
 
3917
     */
 
3918
    previous_page : function(e) {
 
3919
        e.preventDefault();
 
3920
        this.currentpage--;
 
3921
        if (this.currentpage < 0) {
 
3922
            this.currentpage = 0;
 
3923
        }
 
3924
        this.change_page();
 
3925
    },
 
3926
 
 
3927
    /**
 
3928
     * Navigate to the next page.
 
3929
     * @protected
 
3930
     * @method next_page
 
3931
     */
 
3932
    next_page : function(e) {
 
3933
        e.preventDefault();
 
3934
        this.currentpage++;
 
3935
        if (this.currentpage >= this.pages.length) {
 
3936
            this.currentpage = this.pages.length - 1;
 
3937
        }
 
3938
        this.change_page();
 
3939
    }
 
3940
 
 
3941
 
 
3942
 
 
3943
};
 
3944
 
 
3945
Y.extend(EDITOR, Y.Base, EDITOR.prototype, {
 
3946
    NAME : 'moodle-assignfeedback_editpdf-editor',
 
3947
    ATTRS : {
 
3948
        userid : {
 
3949
            validator : Y.Lang.isInteger,
 
3950
            value : 0
 
3951
        },
 
3952
        assignmentid : {
 
3953
            validator : Y.Lang.isInteger,
 
3954
            value : 0
 
3955
        },
 
3956
        attemptnumber : {
 
3957
            validator : Y.Lang.isInteger,
 
3958
            value : 0
 
3959
        },
 
3960
        header : {
 
3961
            validator : Y.Lang.isString,
 
3962
            value : ''
 
3963
        },
 
3964
        body : {
 
3965
            validator : Y.Lang.isString,
 
3966
            value : ''
 
3967
        },
 
3968
        footer : {
 
3969
            validator : Y.Lang.isString,
 
3970
            value : ''
 
3971
        },
 
3972
        linkid : {
 
3973
            validator : Y.Lang.isString,
 
3974
            value : ''
 
3975
        },
 
3976
        deletelinkid : {
 
3977
            validator : Y.Lang.isString,
 
3978
            value : ''
 
3979
        },
 
3980
        readonly : {
 
3981
            validator : Y.Lang.isBoolean,
 
3982
            value : true
 
3983
        },
 
3984
        stampfiles : {
 
3985
            validator : Y.Lang.isArray,
 
3986
            value : ''
 
3987
        },
 
3988
        pagetotal : {
 
3989
            validator : Y.Lang.isInteger,
 
3990
            value : 0
 
3991
        }
 
3992
    }
 
3993
});
 
3994
 
 
3995
/**
 
3996
 * Assignfeedback edit pdf namespace.
 
3997
 * @static
 
3998
 * @class assignfeedback_editpdf
 
3999
 */
 
4000
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
 
4001
 
 
4002
/**
 
4003
 * Editor namespace
 
4004
 * @namespace M.assignfeedback_editpdf.editor
 
4005
 * @class editor
 
4006
 * @static
 
4007
 */
 
4008
M.assignfeedback_editpdf.editor = M.assignfeedback_editpdf.editor || {};
 
4009
 
 
4010
/**
 
4011
 * Init function - will create a new instance every time.
 
4012
 * @method init
 
4013
 * @static
 
4014
 * @param {Object} params
 
4015
 */
 
4016
M.assignfeedback_editpdf.editor.init = M.assignfeedback_editpdf.editor.init || function(params) {
 
4017
    return new EDITOR(params);
 
4018
};
 
4019
 
 
4020
 
 
4021
}, '@VERSION@', {
 
4022
    "requires": [
 
4023
        "base",
 
4024
        "event",
 
4025
        "node",
 
4026
        "io",
 
4027
        "graphics",
 
4028
        "json",
 
4029
        "event-move",
 
4030
        "querystring-stringify-simple",
 
4031
        "moodle-core-notification-dialog",
 
4032
        "moodle-core-notification-exception",
 
4033
        "moodle-core-notification-ajaxexception"
 
4034
    ]
 
4035
});