Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- annotation
- class Annotation < ActiveRecord::Base
- belongs_to :image, :class_name => "Comment:Image"
- include_root_in_json = false
- end
- comment
- class Comment::Image < Asset
- versioned
- has_attached_file :attachment, :styles => { :small => "200x150>", :large => "400x300>" }
- has_many :annotation
- end
- application.js
- $(window).load(function() {
- $(".annotatable").annotateImage({
- getUrl: "/annotations.json",
- saveUrl: "/annotations/create",
- deleteUrl: "/annotations/destroy",
- editable: true
- });
- });
- Comments show
- <% @comment.images.each do |image| %>
- <%= image_tag 'trafalgar-square-annotated.jpg', :width => '600', :height => '398', :class => 'annotatable' %>
- Last edited by <%= image.version %>
- <% end %>
- jquery-image-annotate
- /// <reference path="jquery-1.2.6-vsdoc.js" />
- (function($) {
- $.fn.annotateImage = function(options) {
- /// <summary>
- /// Creates annotations on the given image.
- /// Images are loaded from the "getUrl" propety passed into the options.
- /// </summary>
- var opts = $.extend({}, $.fn.annotateImage.defaults, options);
- var image = this;
- this.image = this;
- this.mode = 'view';
- // Assign defaults
- this.getUrl = opts.getUrl;
- this.saveUrl = opts.saveUrl;
- this.deleteUrl = opts.deleteUrl;
- this.editable = opts.editable;
- this.useAjax = opts.useAjax;
- this.notes = opts.notes;
- // Add the canvas
- 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>');
- this.canvas.children('.image-annotate-edit').hide();
- this.canvas.children('.image-annotate-view').hide();
- this.image.after(this.canvas);
- // Give the canvas and the container their size and background
- this.canvas.height(this.height());
- this.canvas.width(this.width());
- this.canvas.css('background-image', 'url("' + this.attr('src') + '")');
- this.canvas.children('.image-annotate-view, .image-annotate-edit').height(this.height());
- this.canvas.children('.image-annotate-view, .image-annotate-edit').width(this.width());
- // Add the behavior: hide/show the notes when hovering the picture
- this.canvas.hover(function() {
- if ($(this).children('.image-annotate-edit').css('display') == 'none') {
- $(this).children('.image-annotate-view').show();
- }
- }, function() {
- $(this).children('.image-annotate-view').hide();
- });
- this.canvas.children('.image-annotate-view').hover(function() {
- $(this).show();
- }, function() {
- $(this).hide();
- });
- // load the notes
- if (this.useAjax) {
- $.fn.annotateImage.ajaxLoad(this);
- } else {
- $.fn.annotateImage.load(this);
- }
- // Add the "Add a note" button
- if (this.editable) {
- this.button = $('<a class="image-annotate-add" id="image-annotate-add" href="#">Add Note</a>');
- this.button.click(function() {
- $.fn.annotateImage.add(image);
- });
- this.canvas.after(this.button);
- }
- // Hide the original
- this.hide();
- return this;
- };
- /**
- * Plugin Defaults
- **/
- $.fn.annotateImage.defaults = {
- getUrl: 'annotations',
- saveUrl: 'annotations/create',
- deleteUrl: 'annotations/destroy',
- editable: true,
- useAjax: true,
- notes: new Array()
- };
- $.fn.annotateImage.clear = function(image) {
- /// <summary>
- /// Clears all existing annotations from the image.
- /// </summary>
- for (var i = 0; i < image.notes.length; i++) {
- image.notes[image.notes[i]].destroy();
- }
- image.notes = new Array();
- };
- $.fn.annotateImage.ajaxLoad = function(image) {
- /// <summary>
- /// Loads the annotations from the "getUrl" property passed in on the
- /// options object.
- /// </summary>
- $.getJSON(image.getUrl + '?ticks=' + $.fn.annotateImage.getTicks(), function(data) {
- image.notes = data;
- $.fn.annotateImage.load(image);
- });
- };
- $.fn.annotateImage.load = function(image) {
- /// <summary>
- /// Loads the annotations from the notes property passed in on the
- /// options object.
- /// </summary>
- for (var i = 0; i < image.notes.length; i++) {
- image.notes[image.notes[i]] = new $.fn.annotateView(image, image.notes[i]);
- }
- };
- $.fn.annotateImage.getTicks = function() {
- /// <summary>
- /// Gets a count og the ticks for the current date.
- /// This is used to ensure that URLs are always unique and not cached by the browser.
- /// </summary>
- var now = new Date();
- return now.getTime();
- };
- $.fn.annotateImage.add = function(image) {
- /// <summary>
- /// Adds a note to the image.
- /// </summary>
- if (image.mode == 'view') {
- image.mode = 'edit';
- // Create/prepare the editable note elements
- var editable = new $.fn.annotateEdit(image);
- $.fn.annotateImage.createSaveButton(editable, image);
- $.fn.annotateImage.createCancelButton(editable, image);
- }
- };
- $.fn.annotateImage.createSaveButton = function(editable, image, note) {
- /// <summary>
- /// Creates a Save button on the editable note.
- /// </summary>
- var ok = $('<a class="image-annotate-edit-ok">OK</a>');
- ok.click(function() {
- var form = $('#image-annotate-edit-form form');
- var text = $('#image-annotate-text').val();
- $.fn.annotateImage.appendPosition(form, editable)
- image.mode = 'view';
- // Save via AJAX
- if (image.useAjax) {
- $.ajax({
- url: image.saveUrl,
- data: form.serialize(),
- error: function(e) { alert("An error occured saving that note.") },
- success: function(data) {
- if (data.id != undefined) {
- editable.note.id = data.id;
- }
- },
- dataType: "json"
- });
- }
- // Add to canvas
- if (note) {
- note.resetPosition(editable, text);
- } else {
- editable.note.editable = true;
- note = new $.fn.annotateView(image, editable.note)
- note.resetPosition(editable, text);
- image.notes.push(editable.note);
- }
- editable.destroy();
- });
- editable.form.append(ok);
- };
- $.fn.annotateImage.createCancelButton = function(editable, image) {
- /// <summary>
- /// Creates a Cancel button on the editable note.
- /// </summary>
- var cancel = $('<a class="image-annotate-edit-close">Cancel</a>');
- cancel.click(function() {
- editable.destroy();
- image.mode = 'view';
- });
- editable.form.append(cancel);
- };
- $.fn.annotateImage.saveAsHtml = function(image, target) {
- var element = $(target);
- var html = "";
- for (var i = 0; i < image.notes.length; i++) {
- html += $.fn.annotateImage.createHiddenField("text_" + i, image.notes[i].text);
- html += $.fn.annotateImage.createHiddenField("top_" + i, image.notes[i].top);
- html += $.fn.annotateImage.createHiddenField("left_" + i, image.notes[i].left);
- html += $.fn.annotateImage.createHiddenField("height_" + i, image.notes[i].height);
- html += $.fn.annotateImage.createHiddenField("width_" + i, image.notes[i].width);
- }
- element.html(html);
- };
- $.fn.annotateImage.createHiddenField = function(name, value) {
- return '<input type="hidden" name="' + name + '" value="' + value + '" /><br />';
- };
- $.fn.annotateEdit = function(image, note) {
- /// <summary>
- /// Defines an editable annotation area.
- /// </summary>
- this.image = image;
- if (note) {
- this.note = note;
- } else {
- var newNote = new Object();
- newNote.id = "new";
- newNote.top = 30;
- newNote.left = 30;
- newNote.width = 30;
- newNote.height = 30;
- newNote.text = "";
- this.note = newNote;
- }
- // Set area
- var area = image.canvas.children('.image-annotate-edit').children('.image-annotate-edit-area');
- this.area = area;
- this.area.css('height', this.note.height + 'px');
- this.area.css('width', this.note.width + 'px');
- this.area.css('left', this.note.left + 'px');
- this.area.css('top', this.note.top + 'px');
- // Show the edition canvas and hide the view canvas
- image.canvas.children('.image-annotate-view').hide();
- image.canvas.children('.image-annotate-edit').show();
- // Add the note (which we'll load with the form afterwards)
- 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>');
- this.form = form;
- $('body').append(this.form);
- this.form.css('left', this.area.offset().left + 'px');
- this.form.css('top', (parseInt(this.area.offset().top) + parseInt(this.area.height()) + 7) + 'px');
- // Set the area as a draggable/resizable element contained in the image canvas.
- // Would be better to use the containment option for resizable but buggy
- area.resizable({
- handles: 'all',
- stop: function(e, ui) {
- form.css('left', area.offset().left + 'px');
- form.css('top', (parseInt(area.offset().top) + parseInt(area.height()) + 2) + 'px');
- }
- })
- .draggable({
- containment: image.canvas,
- drag: function(e, ui) {
- form.css('left', area.offset().left + 'px');
- form.css('top', (parseInt(area.offset().top) + parseInt(area.height()) + 2) + 'px');
- },
- stop: function(e, ui) {
- form.css('left', area.offset().left + 'px');
- form.css('top', (parseInt(area.offset().top) + parseInt(area.height()) + 2) + 'px');
- }
- });
- return this;
- };
- $.fn.annotateEdit.prototype.destroy = function() {
- /// <summary>
- /// Destroys an editable annotation area.
- /// </summary>
- this.image.canvas.children('.image-annotate-edit').hide();
- this.area.resizable('destroy');
- this.area.draggable('destroy');
- this.area.css('height', '');
- this.area.css('width', '');
- this.area.css('left', '');
- this.area.css('top', '');
- this.form.remove();
- }
- $.fn.annotateView = function(image, note) {
- /// <summary>
- /// Defines a annotation area.
- /// </summary>
- this.image = image;
- this.note = note;
- this.editable = (note.editable && image.editable);
- // Add the area
- this.area = $('<div class="image-annotate-area' + (this.editable ? ' image-annotate-area-editable' : '') + '"><div></div></div>');
- image.canvas.children('.image-annotate-view').prepend(this.area);
- // Add the note
- this.form = $('<div class="image-annotate-note">' + note.text + '</div>');
- this.form.hide();
- image.canvas.children('.image-annotate-view').append(this.form);
- this.form.children('span.actions').hide();
- // Set the position and size of the note
- this.setPosition();
- // Add the behavior: hide/display the note when hovering the area
- var annotation = this;
- this.area.hover(function() {
- annotation.show();
- }, function() {
- annotation.hide();
- });
- // Edit a note feature
- if (this.editable) {
- var form = this;
- this.area.click(function() {
- form.edit();
- });
- }
- };
- $.fn.annotateView.prototype.setPosition = function() {
- /// <summary>
- /// Sets the position of an annotation.
- /// </summary>
- this.area.children('div').height((parseInt(this.note.height) - 2) + 'px');
- this.area.children('div').width((parseInt(this.note.width) - 2) + 'px');
- this.area.css('left', (this.note.left) + 'px');
- this.area.css('top', (this.note.top) + 'px');
- this.form.css('left', (this.note.left) + 'px');
- this.form.css('top', (parseInt(this.note.top) + parseInt(this.note.height) + 7) + 'px');
- };
- $.fn.annotateView.prototype.show = function() {
- /// <summary>
- /// Highlights the annotation
- /// </summary>
- this.form.fadeIn(250);
- if (!this.editable) {
- this.area.addClass('image-annotate-area-hover');
- } else {
- this.area.addClass('image-annotate-area-editable-hover');
- }
- };
- $.fn.annotateView.prototype.hide = function() {
- /// <summary>
- /// Removes the highlight from the annotation.
- /// </summary>
- this.form.fadeOut(250);
- this.area.removeClass('image-annotate-area-hover');
- this.area.removeClass('image-annotate-area-editable-hover');
- };
- $.fn.annotateView.prototype.destroy = function() {
- /// <summary>
- /// Destroys the annotation.
- /// </summary>
- this.area.remove();
- this.form.remove();
- }
- $.fn.annotateView.prototype.edit = function() {
- /// <summary>
- /// Edits the annotation.
- /// </summary>
- if (this.image.mode == 'view') {
- this.image.mode = 'edit';
- var annotation = this;
- // Create/prepare the editable note elements
- var editable = new $.fn.annotateEdit(this.image, this.note);
- $.fn.annotateImage.createSaveButton(editable, this.image, annotation);
- // Add the delete button
- var del = $('<a class="image-annotate-edit-delete">Delete</a>');
- del.click(function() {
- var form = $('#image-annotate-edit-form form');
- $.fn.annotateImage.appendPosition(form, editable)
- if (annotation.image.useAjax) {
- $.ajax({
- url: annotation.image.deleteUrl,
- data: form.serialize(),
- error: function(e) { alert("An error occured deleting that note.") }
- });
- }
- annotation.image.mode = 'view';
- editable.destroy();
- annotation.destroy();
- });
- editable.form.append(del);
- $.fn.annotateImage.createCancelButton(editable, this.image);
- }
- };
- $.fn.annotateImage.appendPosition = function(form, editable) {
- /// <summary>
- /// Appends the annotations coordinates to the given form that is posted to the server.
- /// </summary>
- var areaFields = $('<input type="hidden" value="' + editable.area.height() + '" name="height"/>' +
- '<input type="hidden" value="' + editable.area.width() + '" name="width"/>' +
- '<input type="hidden" value="' + editable.area.position().top + '" name="top"/>' +
- '<input type="hidden" value="' + editable.area.position().left + '" name="left"/>' +
- '<input type="hidden" value="' + editable.note.id + '" name="id"/>');
- form.append(areaFields);
- }
- $.fn.annotateView.prototype.resetPosition = function(editable, text) {
- /// <summary>
- /// Sets the position of an annotation.
- /// </summary>
- this.form.html(text);
- this.form.hide();
- // Resize
- this.area.children('div').height(editable.area.height() + 'px');
- this.area.children('div').width((editable.area.width() - 2) + 'px');
- this.area.css('left', (editable.area.position().left) + 'px');
- this.area.css('top', (editable.area.position().top) + 'px');
- this.form.css('left', (editable.area.position().left) + 'px');
- this.form.css('top', (parseInt(editable.area.position().top) + parseInt(editable.area.height()) + 7) + 'px');
- // Save new position to note
- this.note.top = editable.area.position().top;
- this.note.left = editable.area.position().left;
- this.note.height = editable.area.height();
- this.note.width = editable.area.width();
- this.note.text = text;
- this.note.id = editable.note.id;
- this.editable = true;
- };
- })(jQuery);
Add Comment
Please, Sign In to add comment