source: main/trunk/model-cols-dev/pei-jones/collect/photos/script/image-annotator/js/jquery.annotate2.js@ 27948

Last change on this file since 27948 was 27948, checked in by davidb, 11 years ago

First cut at a collection specificially designed to annotate the photos of the Pei Jones collection

File size: 22.6 KB
Line 
1/// <reference path="jquery-1.2.6-vsdoc.js" />
2(function($) {
3
4 var _posCount = 1;
5 var _page;
6 /**
7 * Creates annotations on the given image.
8 * Images are loaded from the "getUrl" propety
9 * passed into the options.
10 **/
11 $.fn.annotateImage = function(options) {
12
13 var opts = $.extend({}, $.fn.annotateImage.defaults, options);
14 var image = this;
15
16 this.image = this;
17 this.mode = 'view';
18
19 // Assign defaults
20 this.getUrl = opts.getUrl;
21 this.saveUrl = opts.saveUrl;
22 this.deleteUrl = opts.deleteUrl;
23 this.editable = opts.editable;
24 this.useAjax = opts.useAjax;
25 this.notes = opts.notes;
26 _page = opts.page;
27
28 //With paged document images, the width and height are not being read, hence the addition of these if statements.
29 if(this.width() == 0 && opts.width !== undefined)
30 this.width(opts.width);
31 if(this.height() == 0 && opts.width !== undefined)
32 this.height(opts.height);
33
34 //get the last ID number and set _posCount to be the next number.
35 if(this.notes !== null && this.notes !== undefined){
36
37 if(this.notes.length > 0){
38
39 var lastId = this.notes[this.notes.length-1].id;
40
41 if(lastId != null && lastId !== undefined)
42 _posCount = lastId + 1;
43 }
44
45 }
46
47 // Add the canvas
48 this.canvas = $('<div class="image-annotate-canvas"><div class="image-annotate-view"></div><div class="image-annotate-edit"><div class="image-annotate-edit-area"></div></div></div>');
49 this.canvas.children('.image-annotate-edit').hide();
50 this.canvas.children('.image-annotate-view').hide();
51 this.image.after(this.canvas);
52
53 // Give the canvas and the container their size and background
54 this.canvas.height(this.height());
55 this.canvas.width(this.width());
56
57 this.canvas.css('background-image', 'url("' + this.attr('src') + '")');
58 this.canvas.children('.image-annotate-view, .image-annotate-edit').height(this.height());
59 this.canvas.children('.image-annotate-view, .image-annotate-edit').width(this.width());
60
61 // Add the behavior: hide/show the notes when hovering the picture
62 this.canvas.hover(function() {
63 if ($(this).children('.image-annotate-edit').css('display') == 'none') {
64 $(this).children('.image-annotate-view').show();
65 }
66 }, function() {
67 $(this).children('.image-annotate-view').hide();
68 });
69
70 this.canvas.children('.image-annotate-view').hover(function() {
71 $(this).show();
72 }, function() {
73 $(this).hide();
74 });
75
76 // load the notes
77 if (this.useAjax) {
78 $.fn.annotateImage.ajaxLoad(this);
79 } else {
80 $.fn.annotateImage.load(this);
81 }
82
83 // Add the "Add a note" button
84 if (this.editable) {
85
86 this.button = $('<button type="button" id="image-annotate-add">Add Note</button>');
87
88 this.button.click(function() {
89 $.fn.annotateImage.add(image);
90 });
91
92 //this.canvas.after(this.button);
93
94 this.br = $('<br/>');
95
96 this.canvas.before(this.button);
97 this.canvas.before(this.br);
98 }
99
100 // Hide the original image
101 this.hide();
102
103 return this;
104 };
105
106 /**
107 * Plugin Defaults
108 **/
109 $.fn.annotateImage.defaults = {
110 getUrl: 'cgi-bin/metadata-server.pl?a=get-metadata&site=' + gs.xsltParams.site_name + '&c=' + gs.cgiParams.c + '&d=' + gs.cgiParams.d + '&metaname=gsimg.Annotation',
111 saveUrl: 'cgi-bin/metadata-server.pl?a=insert-metadata&site=' + gs.xsltParams.site_name + '&c=' + gs.cgiParams.c + '&d=' + gs.cgiParams.d + '&metaname=gsimg.Annotation&metamode=accumulate&metavalue=',
112 deleteUrl: 'cgi-bin/metadata-server.pl?a=remove-metadata&site=' + gs.xsltParams.site_name + '&c=' + gs.cgiParams.c + '&d=' + gs.cgiParams.d + '&metaname=gsimg.Annotation&metamode=override&metavalue=' + 'testing',
113 editable: true,
114 useAjax: true,
115 notes: new Array(),
116 page: 0 //TODO: use this to annotate correct image when dealing with GSDL paged documents//
117 };
118
119 /**
120 * Clears all existing annotations from the image.
121 **/
122 $.fn.annotateImage.clear = function(image) {
123
124 for (var i = 0; i < image.notes.length; i++) {
125 image.notes[image.notes[i]].destroy();
126 }
127 image.notes = new Array();
128 };
129
130 /**
131 * Loads the annotations from the "getUrl" property passed in on the options object.
132 **/
133 $.fn.annotateImage.ajaxLoad = function(image) {
134
135 $.fn.annotateImage.load(image);
136
137 };
138
139 /**
140 * Loads the annotations from the notes property
141 * passed in on the options object (this method is
142 * only used if "useAjax" is set to false.
143 **/
144 $.fn.annotateImage.load = function(image) {
145
146 if(image.notes === undefined || image.notes === null){
147 console.log("No annotations to load in.");
148 image.notes = new Array();
149 }else{
150
151 if(image.notes.length > 0){
152 for (var i = 0; i < image.notes.length; i++) {
153 image.notes[image.notes[i]] = new $.fn.annotateView(image, image.notes[i]);
154 }
155 }else{
156 console.log("No annotations to load in.");
157 }
158 }
159
160 };
161
162 /**
163 * Gets a count of the ticks for the current date.
164 * This is used to ensure that URLs are always
165 * unique and not cached by the browser.
166 **/
167 $.fn.annotateImage.getTicks = function() {
168
169 var now = new Date();
170 return now.getTime();
171 };
172
173 /**
174 * Adds a note to the image.
175 **/
176 $.fn.annotateImage.add = function(image) {
177
178 console.log("image mode = " + image.mode);
179 if (image.mode == 'view') {
180 image.mode = 'edit';
181
182 // Create/prepare the editable note elements
183 var editable = new $.fn.annotateEdit(image);
184
185 $.fn.annotateImage.createSaveButton(editable, image);
186 $.fn.annotateImage.createCancelButton(editable, image);
187 }
188 };
189
190 /**
191 *Creates an OK button on the editable note.
192 **/
193 $.fn.annotateImage.createSaveButton = function(editable, image, note, prev) {
194
195 var ok = $('<a class="image-annotate-edit-ok">OK</a>');
196
197 ok.click(function() {
198
199 var form = $('#image-annotate-edit-form form');
200 var text = $('#image-annotate-text').val();
201 $.fn.annotateImage.appendPosition(form, editable)
202 image.mode = 'view';
203
204 // Save via AJAX
205 if (image.useAjax) {
206
207 var left_pos = editable.area.position().left;
208 var top_pos = editable.area.position().top;
209 var width = editable.note.width;
210 var height = editable.note.height;
211 var id = editable.note.id;
212 //var page = editable.note.page;
213 console.log(editable.note);
214
215 //var json = '{ "top": ' + top_pos + ', "left": ' + left_pos + ', "width": ' + width + ', "height": ' + height + ', "text": "' + text + '", "id": ' + id + ', "editable": true }';
216
217 var json = '{ "top": ' + top_pos + ', "left": ' + left_pos + ', "width": ' + width + ', "height": ' + height + ', "text": "' + text + '", "id": ' + id + ', "editable": true, "page": ' + _page + ' }';
218
219 var metaname = "gsimg.Annotation";
220
221 var metadata_server_url = "cgi-bin/metadata-server.pl?";
222
223 var saveIndexUrl = metadata_server_url + "a=set-metadata&site=" + gs.xsltParams.site_name + "&c=" + gs.cgiParams.c + "&d=" + gs.cgiParams.d + "&metaname=" + metaname + "&metavalue=" + json;
224
225 var saveArchivesUrl = metadata_server_url + "a=set-archives-metadata&site=" + gs.xsltParams.site_name + "&c=" + gs.cgiParams.c + "&d=" + gs.cgiParams.d + "&metaname=" + metaname + "&metavalue=" + json;
226
227 if(prev !== undefined && prev !== null){
228
229 var prevJson = '{ "top": ' + prev.top + ', "left": ' + prev.left + ', "width": ' + prev.width + ', "height": ' + height + ', "text": "' + prev.text + '", "id": ' + prev.id + ', "editable": true , "page": ' + _page + ' }';
230
231 saveArchivesUrl += "&prevmetavalue=" + prevJson + "&metamode=override" + "&metapos=" + (prev.id-1);
232 }
233
234 var saveImportUrl = metadata_server_url + "a=set-import-metadata&site=" + gs.xsltParams.site_name + "&c=" + gs.cgiParams.c + "&d=" + gs.cgiParams.d + "&metaname=" + metaname + "&metavalue=" + json;
235
236 var saveIndexCallBack = {
237 success: function(response){ console.log("Save index metata successful! Note id: " + id);},
238 failed: function(response){ console.log("Failed to save index metadata of note with id: " + id);}
239 };
240
241 var saveImportCallBack =
242 {
243 sucess: function(response){ console.log("Save import metdata successful! Note id: " + id);},
244 failed: function(response){console.log("Failed in save import metadata of note with id: " + id);}
245 };
246
247 var saveArchivesCallBack = {
248 success: function(response){
249 //apparently should rebuild collection here.
250 console.log("Save archives metadata successful! Note id: " + id);
251 },
252 failed: function(response){
253 console.log("Failed to save archives metadata of note with id: " + id);
254 }
255 };
256
257 YAHOO.util.Connect.asyncRequest("GET",saveIndexUrl,saveIndexCallBack);
258 YAHOO.util.Connect.asyncRequest("GET",saveArchivesUrl,saveArchivesUrl);
259 YAHOO.util.Connect.asyncRequest("GET",saveImportUrl,saveImportCallBack);
260 }
261
262 // Add to canvas
263 if (note) {
264 note.resetPosition(editable, text);
265 } else {
266 editable.note.editable = true;
267 note = new $.fn.annotateView(image, editable.note);
268 note.resetPosition(editable, text);
269 image.notes.push(editable.note);
270 }
271
272 editable.destroy();
273 });
274 editable.form.append(ok);
275 };
276
277 /**
278 * Creates a cancel button on the editable note.
279 **/
280 $.fn.annotateImage.createCancelButton = function(editable, image) {
281
282 var cancel = $('<a class="image-annotate-edit-close">Cancel</a>');
283 cancel.click(function() {
284 editable.destroy();
285 image.mode = 'view';
286 });
287 editable.form.append(cancel);
288 };
289
290 $.fn.annotateImage.saveAsHtml = function(image, target) {
291 var element = $(target);
292 var html = "";
293 for (var i = 0; i < image.notes.length; i++) {
294 html += $.fn.annotateImage.createHiddenField("text_" + i, image.notes[i].text);
295 html += $.fn.annotateImage.createHiddenField("top_" + i, image.notes[i].top);
296 html += $.fn.annotateImage.createHiddenField("left_" + i, image.notes[i].left);
297 html += $.fn.annotateImage.createHiddenField("height_" + i, image.notes[i].height);
298 html += $.fn.annotateImage.createHiddenField("width_" + i, image.notes[i].width);
299 }
300 element.html(html);
301 };
302
303 $.fn.annotateImage.createHiddenField = function(name, value) {
304 return '&lt;input type="hidden" name="' + name + '" value="' + value + '" /&gt;<br />';
305 };
306
307 /**
308 * Defines an editable annotation area.
309 **/
310 $.fn.annotateEdit = function(image, note) {
311
312 this.image = image;
313
314 if (note) {
315 this.note = note;
316 } else {
317 var newNote = new Object();
318
319 newNote.id = _posCount;
320 _posCount++;
321 newNote.top = 30;
322 newNote.left = 30;
323 newNote.width = 30;
324 newNote.height = 30;
325 newNote.text = "";
326 newNote.page = _page;
327 this.note = newNote;
328 }
329
330 // Set area
331 var area = image.canvas.children('.image-annotate-edit').children('.image-annotate-edit-area');
332 this.area = area;
333 this.area.css('height', this.note.height + 'px');
334 this.area.css('width', this.note.width + 'px');
335 this.area.css('left', this.note.left + 'px');
336 this.area.css('top', this.note.top + 'px');
337
338 // Show the edition canvas and hide the view canvas
339 image.canvas.children('.image-annotate-view').hide();
340 image.canvas.children('.image-annotate-edit').show();
341
342 // Add the note (which we'll load with the form afterwards)
343 var form = $('<div id="image-annotate-edit-form"><form><textarea id="image-annotate-text" name="text" rows="3" cols="30">' + this.note.text + '</textarea></form></div>');
344 this.form = form;
345
346 $('body').append(this.form);
347 this.form.css('left', this.area.offset().left + 'px');
348 this.form.css('top', (parseInt(this.area.offset().top) + parseInt(this.area.height()) + 7) + 'px');
349
350 // Set the area as a draggable/resizable element contained in the image canvas.
351 // Would be better to use the containment option for resizable but buggy
352 area.resizable({
353 handles: 'all',
354
355 stop: function(e, ui) {
356 form.css('left', area.offset().left + 'px');
357 form.css('top', (parseInt(area.offset().top) + parseInt(area.height()) + 2) + 'px');
358 }
359 })
360 .draggable({
361 containment: image.canvas,
362 drag: function(e, ui) {
363 form.css('left', area.offset().left + 'px');
364 form.css('top', (parseInt(area.offset().top) + parseInt(area.height()) + 2) + 'px');
365 },
366 stop: function(e, ui) {
367 form.css('left', area.offset().left + 'px');
368 form.css('top', (parseInt(area.offset().top) + parseInt(area.height()) + 2) + 'px');
369 }
370 });
371 return this;
372 };
373
374 /**
375 * Destroys an editable annotation area.
376 **/
377 $.fn.annotateEdit.prototype.destroy = function() {
378
379 this.image.canvas.children('.image-annotate-edit').hide();
380 this.area.resizable('destroy');
381 this.area.draggable('destroy');
382 this.area.css('height', '');
383 this.area.css('width', '');
384 this.area.css('left', '');
385 this.area.css('top', '');
386 this.form.remove();
387 }
388
389 /**
390 * Defines an annotation area.
391 **/
392 $.fn.annotateView = function(image, note) {
393
394 this.image = image;
395
396 this.note = note;
397
398 this.editable = (note.editable && image.editable);
399
400 // Add the area
401 this.area = $('<div class="image-annotate-area' + (this.editable ? ' image-annotate-area-editable' : '') + '"><div></div></div>');
402 image.canvas.children('.image-annotate-view').prepend(this.area);
403
404 // Add the note
405 this.form = $('<div class="image-annotate-note">' + note.text + '</div>');
406 this.form.hide();
407 image.canvas.children('.image-annotate-view').append(this.form);
408 this.form.children('span.actions').hide();
409
410 // Set the position and size of the note
411 this.setPosition();
412
413 // Add the behavior: hide/display the note when hovering the area
414 var annotation = this;
415 this.area.hover(function() {
416 annotation.show();
417 }, function() {
418 annotation.hide();
419 });
420
421 // Edit a note feature
422 if (this.editable) {
423 var form = this;
424 this.area.click(function() {
425 form.edit();
426 });
427 }
428 };
429
430 $.fn.annotateView.prototype.setPosition = function() {
431 /// <summary>
432 /// Sets the position of an annotation.
433 /// </summary>
434 this.area.children('div').height((parseInt(this.note.height) - 2) + 'px');
435 this.area.children('div').width((parseInt(this.note.width) - 2) + 'px');
436 this.area.css('left', (this.note.left) + 'px');
437 this.area.css('top', (this.note.top) + 'px');
438 this.form.css('left', (this.note.left) + 'px');
439 this.form.css('top', (parseInt(this.note.top) + parseInt(this.note.height) + 7) + 'px');
440 };
441
442 $.fn.annotateView.prototype.show = function() {
443 /// <summary>
444 /// Highlights the annotation
445 /// </summary>
446 this.form.fadeIn(250);
447 if (!this.editable) {
448 this.area.addClass('image-annotate-area-hover');
449 } else {
450 this.area.addClass('image-annotate-area-editable-hover');
451 }
452 };
453
454 /**
455 * Removes the highlight from the annotation.
456 **/
457 $.fn.annotateView.prototype.hide = function() {
458
459 this.form.fadeOut(250);
460 this.area.removeClass('image-annotate-area-hover');
461 this.area.removeClass('image-annotate-area-editable-hover');
462 };
463
464 /**
465 * Destroys the annotation.
466 **/
467 $.fn.annotateView.prototype.destroy = function() {
468
469 this.area.remove();
470 this.form.remove();
471 }
472
473 /**
474 * Edits the annotation.
475 **/
476 $.fn.annotateView.prototype.edit = function() {
477
478 if (this.image.mode == 'view') {
479 this.image.mode = 'edit';
480 var annotation = this;
481
482 //Save the previous note (before the edit was made).
483 var storePrevNotes = this.image.notes;
484
485 console.log("this note's id: " + this.note.id);
486
487 var prev = null;
488 for(var i = 0; i < storePrevNotes.length; i++){
489
490 var curr = storePrevNotes[i];
491
492 if(curr.id == this.note.id){
493 prev = curr;
494 }
495 }
496
497 // Create/prepare the editable note elements
498 var editable = new $.fn.annotateEdit(this.image, this.note);
499
500 $.fn.annotateImage.createSaveButton(editable, this.image, annotation,prev);
501
502 // Add the delete button
503 var del = $('<a class="image-annotate-edit-delete">Delete</a>');
504 del.click(function() {
505
506 var form = $('#image-annotate-edit-form form');
507
508 $.fn.annotateImage.appendPosition(form, editable)
509
510 if (annotation.image.useAjax) {
511
512 var left_pos = annotation.note.left;
513 var top_pos = annotation.note.top;
514 var width = annotation.note.width;
515 var height = annotation.note.height;
516 var id = annotation.note.id;
517 var text = annotation.note.text;
518
519 //var page = annotation.note.page;
520
521 var json = '{ "top": ' + top_pos + ', "left": ' + left_pos + ', "width": ' + width + ', "height": ' + height + ', "text": "' + text + '", "id": ' + id + ', "editable": true ' + ', "page": ' + _page + ' }';
522
523 var metaposVal = id-1;
524
525 //make below variables global...
526 var metaname = "gsimg.Annotation";
527
528 var metadata_server_url = "cgi-bin/metadata-server.pl?";
529
530 var removeFromIndexUrl = metadata_server_url + "a=remove-metadata&site=" + gs.xsltParams.site_name + "&c=" + gs.cgiParams.c + "&d=" + gs.cgiParams.d + "&metaname=" + metaname + "&metavalue=" + json + "&metapos=" + metaposVal;
531
532 var removeFromArchivesUrl = metadata_server_url + "a=remove-archives-metadata&site=" + gs.xsltParams.site_name + "&c=" + gs.cgiParams.c + "&d=" + gs.cgiParams.d + "&metaname=" + metaname + "&metavalue=" + json + "&metapos=" + metaposVal;
533
534 var removeFromImportUrl = metadata_server_url + "a=remove-import-metadata&site=" + gs.xsltParams.site_name + "&c=" + gs.cgiParams.c + "&d=" + gs.cgiParams.d + "&metaname=" + metaname + "&metavalue=" + json;
535
536 var indexCallBack = {
537 success: function(response){
538
539 //need to renumber ids so that the correct note is deleted next time
540 for(var i = 0; i < annotation.image.notes.length; i++)
541 {
542 var currNote = annotation.image.notes[i];
543
544 if(currNote.id > id)
545 currNote.id--;
546 }
547 //_posCount--;
548
549 console.log("Index metadata successfully removed. Note id: " + id);
550 },
551 failed: function(){
552 console.log("Failed to delete index metadata of note with id: " + id);
553 }
554 };
555
556 var archiveCallBack = {
557 success: function(response){
558 //need to rebuild collection here.
559 console.log("Metadata successfully removed from archives. Note id: " + id);
560 },
561 failed: function(){
562 console.log("Failed to remove metadata from archives. Note id: " + id);
563 }
564 };
565
566 var importCallBack = {
567 success: function(){},
568 failed: function(){ alert("Failed to delete import metadata of note with id: " + id);}
569 };
570
571 YAHOO.util.Connect.asyncRequest("GET",removeFromIndexUrl,indexCallBack);
572 YAHOO.util.Connect.asyncRequest("GET",removeFromArchivesUrl,archiveCallBack);
573 YAHOO.util.Connect.asyncRequest("GET",removeFromImportUrl,importCallBack);
574
575 }
576
577 annotation.image.mode = 'view';
578 editable.destroy();
579 annotation.destroy();
580
581 });
582 editable.form.append(del);
583
584 $.fn.annotateImage.createCancelButton(editable, this.image);
585 }
586 };
587
588 /**
589 * Appends the annotations coordinates to the given form that is posted to the server.
590 **/
591 $.fn.annotateImage.appendPosition = function(form, editable) {
592
593 var areaFields = $('<input type="hidden" value="' + editable.area.height() + '" name="height"/>' +
594 '<input type="hidden" value="' + editable.area.width() + '" name="width"/>' +
595 '<input type="hidden" value="' + editable.area.position().top + '" name="top"/>' +
596 '<input type="hidden" value="' + editable.area.position().left + '" name="left"/>' +
597 '<input type="hidden" value="' + editable.note.id + '" name="id"/>');
598 form.append(areaFields);
599 }
600
601 /**
602 * Sets the position of an annotation.
603 **/
604 $.fn.annotateView.prototype.resetPosition = function(editable, text) {
605
606 this.form.html(text);
607 this.form.hide();
608
609 // Resize
610 this.area.children('div').height(editable.area.height() + 'px');
611 this.area.children('div').width((editable.area.width() - 2) + 'px');
612 this.area.css('left', (editable.area.position().left) + 'px');
613 this.area.css('top', (editable.area.position().top) + 'px');
614 this.form.css('left', (editable.area.position().left) + 'px');
615 this.form.css('top', (parseInt(editable.area.position().top) + parseInt(editable.area.height()) + 7) + 'px');
616
617 // Save new position to note
618 this.note.top = editable.area.position().top;
619 this.note.left = editable.area.position().left;
620 this.note.height = editable.area.height();
621 this.note.width = editable.area.width();
622 this.note.text = text;
623 this.note.id = editable.note.id;
624 this.editable = true;
625 };
626
627})(jQuery);
Note: See TracBrowser for help on using the repository browser.