Guest User

Untitled

a guest
Jun 18th, 2018
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.74 KB | None | 0 0
  1. annotation
  2. class Annotation < ActiveRecord::Base
  3. belongs_to :image, :class_name => "Comment:Image"
  4. include_root_in_json = false
  5. end
  6.  
  7. comment
  8. class Comment::Image < Asset
  9. versioned
  10. has_attached_file :attachment, :styles => { :small => "200x150>", :large => "400x300>" }
  11.  
  12. has_many :annotation
  13. end
  14.  
  15. application.js
  16. $(window).load(function() {
  17. $(".annotatable").annotateImage({
  18. getUrl: "/annotations.json",
  19. saveUrl: "/annotations/create",
  20. deleteUrl: "/annotations/destroy",
  21. editable: true
  22. });
  23.  
  24. });
  25.  
  26. Comments show
  27.  
  28. <% @comment.images.each do |image| %>
  29. <%= image_tag 'trafalgar-square-annotated.jpg', :width => '600', :height => '398', :class => 'annotatable' %>
  30. Last edited by <%= image.version %>
  31. <% end %>
  32.  
  33. jquery-image-annotate
  34. /// <reference path="jquery-1.2.6-vsdoc.js" />
  35. (function($) {
  36.  
  37. $.fn.annotateImage = function(options) {
  38. /// <summary>
  39. /// Creates annotations on the given image.
  40. /// Images are loaded from the "getUrl" propety passed into the options.
  41. /// </summary>
  42. var opts = $.extend({}, $.fn.annotateImage.defaults, options);
  43. var image = this;
  44.  
  45. this.image = this;
  46. this.mode = 'view';
  47.  
  48. // Assign defaults
  49. this.getUrl = opts.getUrl;
  50. this.saveUrl = opts.saveUrl;
  51. this.deleteUrl = opts.deleteUrl;
  52. this.editable = opts.editable;
  53. this.useAjax = opts.useAjax;
  54. this.notes = opts.notes;
  55.  
  56. // Add the canvas
  57. 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>');
  58. this.canvas.children('.image-annotate-edit').hide();
  59. this.canvas.children('.image-annotate-view').hide();
  60. this.image.after(this.canvas);
  61.  
  62. // Give the canvas and the container their size and background
  63. this.canvas.height(this.height());
  64. this.canvas.width(this.width());
  65. this.canvas.css('background-image', 'url("' + this.attr('src') + '")');
  66. this.canvas.children('.image-annotate-view, .image-annotate-edit').height(this.height());
  67. this.canvas.children('.image-annotate-view, .image-annotate-edit').width(this.width());
  68.  
  69. // Add the behavior: hide/show the notes when hovering the picture
  70. this.canvas.hover(function() {
  71. if ($(this).children('.image-annotate-edit').css('display') == 'none') {
  72. $(this).children('.image-annotate-view').show();
  73. }
  74. }, function() {
  75. $(this).children('.image-annotate-view').hide();
  76. });
  77.  
  78. this.canvas.children('.image-annotate-view').hover(function() {
  79. $(this).show();
  80. }, function() {
  81. $(this).hide();
  82. });
  83.  
  84. // load the notes
  85. if (this.useAjax) {
  86. $.fn.annotateImage.ajaxLoad(this);
  87. } else {
  88. $.fn.annotateImage.load(this);
  89. }
  90.  
  91. // Add the "Add a note" button
  92. if (this.editable) {
  93. this.button = $('<a class="image-annotate-add" id="image-annotate-add" href="#">Add Note</a>');
  94. this.button.click(function() {
  95. $.fn.annotateImage.add(image);
  96. });
  97. this.canvas.after(this.button);
  98. }
  99.  
  100. // Hide the original
  101. this.hide();
  102.  
  103. return this;
  104. };
  105.  
  106. /**
  107. * Plugin Defaults
  108. **/
  109. $.fn.annotateImage.defaults = {
  110. getUrl: 'annotations',
  111. saveUrl: 'annotations/create',
  112. deleteUrl: 'annotations/destroy',
  113. editable: true,
  114. useAjax: true,
  115. notes: new Array()
  116. };
  117.  
  118. $.fn.annotateImage.clear = function(image) {
  119. /// <summary>
  120. /// Clears all existing annotations from the image.
  121. /// </summary>
  122. for (var i = 0; i < image.notes.length; i++) {
  123. image.notes[image.notes[i]].destroy();
  124. }
  125. image.notes = new Array();
  126. };
  127.  
  128. $.fn.annotateImage.ajaxLoad = function(image) {
  129. /// <summary>
  130. /// Loads the annotations from the "getUrl" property passed in on the
  131. /// options object.
  132. /// </summary>
  133. $.getJSON(image.getUrl + '?ticks=' + $.fn.annotateImage.getTicks(), function(data) {
  134. image.notes = data;
  135. $.fn.annotateImage.load(image);
  136. });
  137. };
  138.  
  139. $.fn.annotateImage.load = function(image) {
  140. /// <summary>
  141. /// Loads the annotations from the notes property passed in on the
  142. /// options object.
  143. /// </summary>
  144. for (var i = 0; i < image.notes.length; i++) {
  145. image.notes[image.notes[i]] = new $.fn.annotateView(image, image.notes[i]);
  146. }
  147. };
  148.  
  149. $.fn.annotateImage.getTicks = function() {
  150. /// <summary>
  151. /// Gets a count og the ticks for the current date.
  152. /// This is used to ensure that URLs are always unique and not cached by the browser.
  153. /// </summary>
  154. var now = new Date();
  155. return now.getTime();
  156. };
  157.  
  158. $.fn.annotateImage.add = function(image) {
  159. /// <summary>
  160. /// Adds a note to the image.
  161. /// </summary>
  162. if (image.mode == 'view') {
  163. image.mode = 'edit';
  164.  
  165. // Create/prepare the editable note elements
  166. var editable = new $.fn.annotateEdit(image);
  167.  
  168. $.fn.annotateImage.createSaveButton(editable, image);
  169. $.fn.annotateImage.createCancelButton(editable, image);
  170. }
  171. };
  172.  
  173. $.fn.annotateImage.createSaveButton = function(editable, image, note) {
  174. /// <summary>
  175. /// Creates a Save button on the editable note.
  176. /// </summary>
  177. var ok = $('<a class="image-annotate-edit-ok">OK</a>');
  178.  
  179. ok.click(function() {
  180. var form = $('#image-annotate-edit-form form');
  181. var text = $('#image-annotate-text').val();
  182. $.fn.annotateImage.appendPosition(form, editable)
  183. image.mode = 'view';
  184.  
  185. // Save via AJAX
  186. if (image.useAjax) {
  187. $.ajax({
  188. url: image.saveUrl,
  189. data: form.serialize(),
  190. error: function(e) { alert("An error occured saving that note.") },
  191. success: function(data) {
  192. if (data.id != undefined) {
  193. editable.note.id = data.id;
  194. }
  195. },
  196. dataType: "json"
  197. });
  198. }
  199.  
  200. // Add to canvas
  201. if (note) {
  202. note.resetPosition(editable, text);
  203. } else {
  204. editable.note.editable = true;
  205. note = new $.fn.annotateView(image, editable.note)
  206. note.resetPosition(editable, text);
  207. image.notes.push(editable.note);
  208. }
  209.  
  210. editable.destroy();
  211. });
  212. editable.form.append(ok);
  213. };
  214.  
  215. $.fn.annotateImage.createCancelButton = function(editable, image) {
  216. /// <summary>
  217. /// Creates a Cancel button on the editable note.
  218. /// </summary>
  219. var cancel = $('<a class="image-annotate-edit-close">Cancel</a>');
  220. cancel.click(function() {
  221. editable.destroy();
  222. image.mode = 'view';
  223. });
  224. editable.form.append(cancel);
  225. };
  226.  
  227. $.fn.annotateImage.saveAsHtml = function(image, target) {
  228. var element = $(target);
  229. var html = "";
  230. for (var i = 0; i < image.notes.length; i++) {
  231. html += $.fn.annotateImage.createHiddenField("text_" + i, image.notes[i].text);
  232. html += $.fn.annotateImage.createHiddenField("top_" + i, image.notes[i].top);
  233. html += $.fn.annotateImage.createHiddenField("left_" + i, image.notes[i].left);
  234. html += $.fn.annotateImage.createHiddenField("height_" + i, image.notes[i].height);
  235. html += $.fn.annotateImage.createHiddenField("width_" + i, image.notes[i].width);
  236. }
  237. element.html(html);
  238. };
  239.  
  240. $.fn.annotateImage.createHiddenField = function(name, value) {
  241. return '<input type="hidden" name="' + name + '" value="' + value + '" /><br />';
  242. };
  243.  
  244. $.fn.annotateEdit = function(image, note) {
  245. /// <summary>
  246. /// Defines an editable annotation area.
  247. /// </summary>
  248. this.image = image;
  249.  
  250. if (note) {
  251. this.note = note;
  252. } else {
  253. var newNote = new Object();
  254. newNote.id = "new";
  255. newNote.top = 30;
  256. newNote.left = 30;
  257. newNote.width = 30;
  258. newNote.height = 30;
  259. newNote.text = "";
  260. this.note = newNote;
  261. }
  262.  
  263. // Set area
  264. var area = image.canvas.children('.image-annotate-edit').children('.image-annotate-edit-area');
  265. this.area = area;
  266. this.area.css('height', this.note.height + 'px');
  267. this.area.css('width', this.note.width + 'px');
  268. this.area.css('left', this.note.left + 'px');
  269. this.area.css('top', this.note.top + 'px');
  270.  
  271. // Show the edition canvas and hide the view canvas
  272. image.canvas.children('.image-annotate-view').hide();
  273. image.canvas.children('.image-annotate-edit').show();
  274.  
  275. // Add the note (which we'll load with the form afterwards)
  276. 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>');
  277. this.form = form;
  278.  
  279. $('body').append(this.form);
  280. this.form.css('left', this.area.offset().left + 'px');
  281. this.form.css('top', (parseInt(this.area.offset().top) + parseInt(this.area.height()) + 7) + 'px');
  282.  
  283. // Set the area as a draggable/resizable element contained in the image canvas.
  284. // Would be better to use the containment option for resizable but buggy
  285. area.resizable({
  286. handles: 'all',
  287.  
  288. stop: function(e, ui) {
  289. form.css('left', area.offset().left + 'px');
  290. form.css('top', (parseInt(area.offset().top) + parseInt(area.height()) + 2) + 'px');
  291. }
  292. })
  293. .draggable({
  294. containment: image.canvas,
  295. drag: function(e, ui) {
  296. form.css('left', area.offset().left + 'px');
  297. form.css('top', (parseInt(area.offset().top) + parseInt(area.height()) + 2) + 'px');
  298. },
  299. stop: function(e, ui) {
  300. form.css('left', area.offset().left + 'px');
  301. form.css('top', (parseInt(area.offset().top) + parseInt(area.height()) + 2) + 'px');
  302. }
  303. });
  304. return this;
  305. };
  306.  
  307. $.fn.annotateEdit.prototype.destroy = function() {
  308. /// <summary>
  309. /// Destroys an editable annotation area.
  310. /// </summary>
  311. this.image.canvas.children('.image-annotate-edit').hide();
  312. this.area.resizable('destroy');
  313. this.area.draggable('destroy');
  314. this.area.css('height', '');
  315. this.area.css('width', '');
  316. this.area.css('left', '');
  317. this.area.css('top', '');
  318. this.form.remove();
  319. }
  320.  
  321. $.fn.annotateView = function(image, note) {
  322. /// <summary>
  323. /// Defines a annotation area.
  324. /// </summary>
  325. this.image = image;
  326.  
  327. this.note = note;
  328.  
  329. this.editable = (note.editable && image.editable);
  330.  
  331. // Add the area
  332. this.area = $('<div class="image-annotate-area' + (this.editable ? ' image-annotate-area-editable' : '') + '"><div></div></div>');
  333. image.canvas.children('.image-annotate-view').prepend(this.area);
  334.  
  335. // Add the note
  336. this.form = $('<div class="image-annotate-note">' + note.text + '</div>');
  337. this.form.hide();
  338. image.canvas.children('.image-annotate-view').append(this.form);
  339. this.form.children('span.actions').hide();
  340.  
  341. // Set the position and size of the note
  342. this.setPosition();
  343.  
  344. // Add the behavior: hide/display the note when hovering the area
  345. var annotation = this;
  346. this.area.hover(function() {
  347. annotation.show();
  348. }, function() {
  349. annotation.hide();
  350. });
  351.  
  352. // Edit a note feature
  353. if (this.editable) {
  354. var form = this;
  355. this.area.click(function() {
  356. form.edit();
  357. });
  358. }
  359. };
  360.  
  361. $.fn.annotateView.prototype.setPosition = function() {
  362. /// <summary>
  363. /// Sets the position of an annotation.
  364. /// </summary>
  365. this.area.children('div').height((parseInt(this.note.height) - 2) + 'px');
  366. this.area.children('div').width((parseInt(this.note.width) - 2) + 'px');
  367. this.area.css('left', (this.note.left) + 'px');
  368. this.area.css('top', (this.note.top) + 'px');
  369. this.form.css('left', (this.note.left) + 'px');
  370. this.form.css('top', (parseInt(this.note.top) + parseInt(this.note.height) + 7) + 'px');
  371. };
  372.  
  373. $.fn.annotateView.prototype.show = function() {
  374. /// <summary>
  375. /// Highlights the annotation
  376. /// </summary>
  377. this.form.fadeIn(250);
  378. if (!this.editable) {
  379. this.area.addClass('image-annotate-area-hover');
  380. } else {
  381. this.area.addClass('image-annotate-area-editable-hover');
  382. }
  383. };
  384.  
  385. $.fn.annotateView.prototype.hide = function() {
  386. /// <summary>
  387. /// Removes the highlight from the annotation.
  388. /// </summary>
  389. this.form.fadeOut(250);
  390. this.area.removeClass('image-annotate-area-hover');
  391. this.area.removeClass('image-annotate-area-editable-hover');
  392. };
  393.  
  394. $.fn.annotateView.prototype.destroy = function() {
  395. /// <summary>
  396. /// Destroys the annotation.
  397. /// </summary>
  398. this.area.remove();
  399. this.form.remove();
  400. }
  401.  
  402. $.fn.annotateView.prototype.edit = function() {
  403. /// <summary>
  404. /// Edits the annotation.
  405. /// </summary>
  406. if (this.image.mode == 'view') {
  407. this.image.mode = 'edit';
  408. var annotation = this;
  409.  
  410. // Create/prepare the editable note elements
  411. var editable = new $.fn.annotateEdit(this.image, this.note);
  412.  
  413. $.fn.annotateImage.createSaveButton(editable, this.image, annotation);
  414.  
  415. // Add the delete button
  416. var del = $('<a class="image-annotate-edit-delete">Delete</a>');
  417. del.click(function() {
  418. var form = $('#image-annotate-edit-form form');
  419.  
  420. $.fn.annotateImage.appendPosition(form, editable)
  421.  
  422. if (annotation.image.useAjax) {
  423. $.ajax({
  424. url: annotation.image.deleteUrl,
  425. data: form.serialize(),
  426. error: function(e) { alert("An error occured deleting that note.") }
  427. });
  428. }
  429.  
  430. annotation.image.mode = 'view';
  431. editable.destroy();
  432. annotation.destroy();
  433. });
  434. editable.form.append(del);
  435.  
  436. $.fn.annotateImage.createCancelButton(editable, this.image);
  437. }
  438. };
  439.  
  440. $.fn.annotateImage.appendPosition = function(form, editable) {
  441. /// <summary>
  442. /// Appends the annotations coordinates to the given form that is posted to the server.
  443. /// </summary>
  444. var areaFields = $('<input type="hidden" value="' + editable.area.height() + '" name="height"/>' +
  445. '<input type="hidden" value="' + editable.area.width() + '" name="width"/>' +
  446. '<input type="hidden" value="' + editable.area.position().top + '" name="top"/>' +
  447. '<input type="hidden" value="' + editable.area.position().left + '" name="left"/>' +
  448. '<input type="hidden" value="' + editable.note.id + '" name="id"/>');
  449. form.append(areaFields);
  450. }
  451.  
  452. $.fn.annotateView.prototype.resetPosition = function(editable, text) {
  453. /// <summary>
  454. /// Sets the position of an annotation.
  455. /// </summary>
  456. this.form.html(text);
  457. this.form.hide();
  458.  
  459. // Resize
  460. this.area.children('div').height(editable.area.height() + 'px');
  461. this.area.children('div').width((editable.area.width() - 2) + 'px');
  462. this.area.css('left', (editable.area.position().left) + 'px');
  463. this.area.css('top', (editable.area.position().top) + 'px');
  464. this.form.css('left', (editable.area.position().left) + 'px');
  465. this.form.css('top', (parseInt(editable.area.position().top) + parseInt(editable.area.height()) + 7) + 'px');
  466.  
  467. // Save new position to note
  468. this.note.top = editable.area.position().top;
  469. this.note.left = editable.area.position().left;
  470. this.note.height = editable.area.height();
  471. this.note.width = editable.area.width();
  472. this.note.text = text;
  473. this.note.id = editable.note.id;
  474. this.editable = true;
  475. };
  476.  
  477. })(jQuery);
Add Comment
Please, Sign In to add comment