Files
danbooru/app/javascript/src/javascripts/notes.js
evazion 71e1d7c2a5 notes: fix note mispositioning when image is blacklisted.
If the image is blacklisted on initial page load then unblacklisted
after the image is loaded, then the notes will be mispositioned at the
bottom of the image. This is because we relied on $image.height() to
calculate the note position, but the image height is zero when the image
is hidden.

Potential fix for #4370.
2020-03-28 14:56:38 -05:00

1066 lines
35 KiB
JavaScript

import CurrentUser from './current_user'
import Utility from './utility'
let Note = {
HIDE_DELAY: 250,
NORMALIZE_ATTRIBUTES: ['letter-spacing', 'line-height', 'margin-left', 'margin-right', 'margin-top', 'margin-bottom', 'padding-left', 'padding-right', 'padding-top', 'padding-bottom'],
COPY_ATTRIBUTES: ['background-color', 'border-radius', 'transform'],
permitted_style_values: function(attribute, $attribute_child) {
if ($attribute_child.length === 0) {
return "";
}
let found_attribute = $attribute_child.attr('style').split(';').filter(val => val.match(RegExp(`(^| )${attribute}:`)));
if (found_attribute.length === 0) {
return "";
}
let [, value] = found_attribute[0].trim().split(':').map(val => val.trim());
if (attribute === "background-color") {
const color_code = $attribute_child.css('background-color');
return color_code.startsWith('rgba') ? "" : value;
}
if (attribute === "transform") {
let rotate_match = value.match(/rotate\([^)]+\)/);
return rotate_match ? rotate_match[0] : "";
}
return value;
},
Box: {
create: function(id) {
var $inner_border = $('<div/>');
$inner_border.addClass("note-box-inner-border");
var $note_box = $('<div/>');
$note_box.addClass("note-box");
if (Note.embed) {
$note_box.addClass("embedded");
}
$note_box.data("id", String(id));
$note_box.attr("data-id", String(id));
$note_box.draggable({
containment: $("#image"),
stop: function(e, ui) {
Note.Box.update_data_attributes($note_box);
}
});
$note_box.resizable({
containment: $("#image"),
handles: "se, nw",
stop: function(e, ui) {
Note.Box.update_data_attributes($note_box);
}
});
$note_box.css({position: "absolute"});
$note_box.append($inner_border);
Note.Box.bind_events($note_box);
return $note_box;
},
update_data_attributes: function($note_box) {
var $image = $("#image");
var ratio = $image.width() / parseFloat($image.data("original-width"));
var new_x = parseFloat($note_box.css("left"));
var new_y = parseFloat($note_box.css("top"));
var new_width = parseFloat($note_box.css("width"));
var new_height = parseFloat($note_box.css("height"));
new_x = parseInt(new_x / ratio);
new_y = parseInt(new_y / ratio);
new_width = parseInt(new_width / ratio);
new_height = parseInt(new_height / ratio);
$note_box.data("x", new_x);
$note_box.data("y", new_y);
$note_box.data("width", new_width);
$note_box.data("height", new_height);
},
copy_style_attributes: function($note_box) {
const $attribute_child = $note_box.find('.note-box-attributes');
let has_rotation = false;
Note.COPY_ATTRIBUTES.forEach((attribute)=>{
const attribute_value = Note.permitted_style_values(attribute, $attribute_child);
$note_box.css(attribute, attribute_value);
if (attribute === "transform" && attribute_value.startsWith("rotate")) {
has_rotation = true;
}
});
if (has_rotation) {
const current_left = Math.round(parseFloat($note_box.css("left")));
const current_top = Math.round(parseFloat($note_box.css("top")));
const position = Note.Box.get_min_max_position($note_box);
// Checks for the scenario where the user sets invalid box values through the API
// or by adjusting the box dimensions through the browser's dev console before saving
if (current_left !== position.norm_left || current_top !== position.norm_top) {
$note_box.css({
top: position.percent_top,
left: position.percent_left,
});
$note_box.addClass("out-of-bounds");
} else {
$note_box.removeClass("out-of-bounds");
}
$note_box.data('has_rotation', true);
} else {
$note_box.data('has_rotation', false);
}
},
bind_events: function($note_box) {
$note_box.on(
"dragstart.danbooru resizestart.danbooru",
function(e) {
var $note_box_inner = $(e.currentTarget);
$note_box_inner.addClass("unsaved");
Note.dragging = true;
Note.clear_timeouts();
Note.Body.hide_all();
e.stopPropagation();
}
);
$note_box.on(
"dragstop.danbooru resizestop.danbooru",
function(e) {
Note.dragging = false;
e.stopPropagation();
}
);
$note_box.on(
"mouseover.danbooru mouseout.danbooru",
function(e) {
if (Note.dragging || Utility.test_max_width(660)) {
return;
}
var $this = $(this);
var $note_box_inner = $(e.currentTarget);
const note_id = $note_box_inner.data("id");
if (e.type === "mouseover") {
Note.Body.show(note_id);
if (Note.editing) {
$this.resizable("enable");
$this.draggable("enable");
}
$note_box.addClass("hovering");
} else if (e.type === "mouseout") {
Note.Body.hide(note_id);
if (Note.editing) {
$this.resizable("disable");
$this.draggable("disable");
}
$note_box.removeClass("hovering");
}
e.stopPropagation();
}
);
$note_box.on(
"click.danbooru",
function (event) {
const note_id = $note_box.data("id");
if (!Utility.test_max_width(660)) {
$(".note-box").removeClass("movable");
if (note_id === Note.move_id) {
Note.move_id = null;
} else {
Note.move_id = note_id;
$note_box.addClass("movable");
}
} else if ($note_box.hasClass("viewing")) {
Note.Body.hide(note_id);
$note_box.removeClass("viewing");
} else {
$(".note-box").removeClass("viewing");
Note.Body.show(note_id);
$note_box.addClass("viewing");
}
}
);
$note_box.on("mousedown.danbooru", function(event) {
Note.drag_id = $note_box.data('id');
});
$note_box.on("mouseup.danbooru.drag", Note.Box.drag_stop);
},
find: function(id) {
return $(".note-container div.note-box[data-id=" + id + "]");
},
drag_stop: function(event) {
if (Note.drag_id !== null) {
const $image = $("#image");
const $note_box = Note.Box.find(Note.drag_id);
const dimensions = {
top: (100 * ($note_box.position().top / $image.height())) + '%',
left: (100 * ($note_box.position().left / $image.width())) + '%',
height: (100 * ($note_box.height() / $image.height())) + '%',
width: (100 * ($note_box.width() / $image.width())) + '%',
};
if (Note.embed && $note_box.data('has_rotation')) {
const position = Note.Box.get_min_max_position($note_box);
Object.assign(dimensions, {
top: position.percentage_top,
left: position.percentage_left,
});
}
$note_box.css(dimensions);
Note.drag_id = null;
}
},
key_nudge: function(event) {
if (!Note.move_id) {
return;
}
const $note_box = Note.Box.find(Note.move_id);
if ($note_box.length === 0) {
return;
}
let computed_style = window.getComputedStyle($note_box[0]);
let current_top = Math.round(parseFloat(computed_style.top));
let current_left = Math.round(parseFloat(computed_style.left));
switch (event.originalEvent.key) {
case "ArrowUp":
current_top--;
break;
case "ArrowDown":
current_top++;
break;
case "ArrowLeft":
current_left--;
break;
case "ArrowRight":
current_left++;
break;
default:
// do nothing
}
let position = Note.Box.get_min_max_position($note_box, current_top, current_left);
$note_box.css({
top: position.percent_top,
left: position.percent_left,
});
$note_box.addClass("unsaved");
event.preventDefault();
},
key_resize: function (event) {
if (!Note.move_id) {
return;
}
const $note_box = Note.Box.find(Note.move_id);
if ($note_box.length === 0) {
return;
}
let computed_style = window.getComputedStyle($note_box[0]);
let current_top = Math.round(parseFloat(computed_style.top));
let current_left = Math.round(parseFloat(computed_style.left));
let current_height = $note_box.height();
let current_width = $note_box.width();
switch (event.originalEvent.key) {
case "ArrowUp":
current_height--;
break;
case "ArrowDown":
current_height++;
break;
case "ArrowLeft":
current_width--;
break;
case "ArrowRight":
current_width++;
break;
default:
// do nothing
}
const position = Note.Box.get_min_max_position($note_box, null, null, current_height, current_width);
if (current_top === position.norm_top && current_left === position.norm_left) {
$note_box.css({
height: current_height,
width: current_width,
});
}
$note_box.addClass("unsaved");
event.preventDefault();
},
get_min_max_position: function($note_box, current_top = null, current_left = null, current_height = null, current_width = null) {
const computed_style = window.getComputedStyle($note_box[0]);
current_top = (current_top === null ? parseFloat(computed_style.top) : current_top);
current_left = (current_left === null ? parseFloat(computed_style.left) : current_left);
current_height = current_height || $note_box.height();
current_width = current_width || $note_box.width();
const $image = $("#image");
const image_height = $image.height();
const image_width = $image.width();
const box_data = Note.Box.get_bounding_box($note_box, current_height, current_width);
if (((box_data.max_x - box_data.min_x) <= image_width) && ((box_data.max_y - box_data.min_y) <= image_height)) {
current_top = Math.min(Math.max(current_top, -box_data.min_y, 0), image_height - box_data.max_y - 2, image_height - box_data.min_y - box_data.max_y - 2, image_height);
current_left = Math.min(Math.max(current_left, -box_data.min_x, 0), image_width - box_data.max_x - 2, image_width - box_data.min_x - box_data.max_x - 2, image_width);
} else {
Utility.error("Box too large to be rotated!");
$note_box.css('transform', 'none');
}
return {
norm_top: Math.round(current_top),
norm_left: Math.round(current_left),
percent_top: (100 * (current_top / $image.height())) + '%',
percent_left: (100 * (current_left / $image.width())) + '%',
};
},
get_bounding_box: function($note_box, height = null, width = null) {
height = height || $note_box.height();
width = width || $note_box.width();
let old_coord = [[0, 0], [width, 0], [0, height], [width, height]];
const computed_style = window.getComputedStyle($note_box[0]);
const match = computed_style.transform.match(/matrix\(([-e0-9.]+), ([-e0-9.]+)/);
if (!match) {
return {
min_x: 0,
min_y: 0,
max_x: width,
max_y: height,
norm_coord: old_coord,
degrees: 0,
}
}
const costheta = Math.round(match[1] * 1000) / 1000;
const sintheta = Math.round(match[2] * 1000) / 1000;
let trans_x = width / 2;
let trans_y = height / 2;
let min_x = Infinity;
let max_x = 0;
let min_y = Infinity;
let max_y = 0;
const new_coord = old_coord.map((coord)=>{
let temp_x = coord[0] - trans_x;
let temp_y = coord[1] - trans_y;
let rotated_x = (temp_x * costheta) - (temp_y * sintheta);
let rotated_y = (temp_x * sintheta) + (temp_y * costheta);
let new_x = rotated_x + trans_x;
let new_y = rotated_y + trans_y;
min_x = Math.min(min_x, new_x);
max_x = Math.max(max_x, new_x);
min_y = Math.min(min_y, new_y);
max_y = Math.max(max_y, new_y);
return [new_x, new_y];
});
const norm_coord = new_coord.map((coord)=>{
return [coord[0] - min_x, coord[1] - min_y];
});
const radians_per_degree = 0.017453292519943295;
const degrees = Math.asin(sintheta) / radians_per_degree;
return {
min_x: min_x,
min_y: min_y,
max_x: max_x,
max_y: max_y,
norm_coord: norm_coord,
degrees: degrees,
};
},
show_highlighted: function($note_box) {
var note_id = $note_box.data("id");
Note.Body.show(note_id);
$(".note-box-highlighted").removeClass("note-box-highlighted");
$note_box.addClass("note-box-highlighted");
$note_box[0].scrollIntoView(false);
},
scale: function($note_box) {
var $image = $("#image");
var original_width = $image.data("original-width");
var original_height = $image.data("original-height");
var x_percent = 100 * ($note_box.data('x') / original_width);
var y_percent = 100 * ($note_box.data('y') / original_height);
var height_percent = 100 * ($note_box.data('height') / original_height);
var width_percent = 100 * ($note_box.data('width') / original_width);
$note_box.css({
top: y_percent + '%',
left: x_percent + '%',
width: width_percent + '%',
height: height_percent + '%',
});
},
scale_all: function() {
const $container = $('.note-container');
if ($container.length === 0) {
return;
}
let $image = $("#image");
if (Note.embed) {
let large_width = parseFloat($image.data('large-width'));
let ratio = $image.width() / large_width;
let font_percentage = ratio * 100;
$container.css('font-size', font_percentage + '%');
}
},
toggle_all: function() {
$(".note-container").toggleClass("hide-notes");
}
},
Body: {
create: function(id) {
var $note_body = $('<div></div>');
$note_body.addClass("note-body");
$note_body.data("id", String(id));
$note_body.attr("data-id", String(id));
$note_body.hide();
Note.Body.bind_events($note_body);
return $note_body;
},
initialize: function($note_body) {
var $note_box = Note.Box.find($note_body.data("id"));
if (Note.embed && $note_box.data('has_rotation')) {
const box_data = Note.Box.get_bounding_box($note_box);
// Select the lowest box corner to the farthest left
let selected_corner = box_data.norm_coord.reduce(function (selected, coord) {return (selected[1] > coord[1]) || (selected[1] === coord[1] && selected[0] < coord[0]) ? selected : coord;});
let normalized_degrees = box_data.degrees % 90.0;
// Align to the left or right body corner depending upon the box angle
let body_corner = $note_box.position().left - (normalized_degrees > 0.0 && normalized_degrees <= 45.0 ? $note_body.width() : 0);
$note_body.css({
top: $note_box.position().top + selected_corner[1] + 5,
left: body_corner + selected_corner[0],
});
} else {
$note_body.css({
top: $note_box.position().top + $note_box.height() + 5,
left: $note_box.position().left,
});
}
Note.Body.bound_position($note_body);
},
bound_position: function($note_body) {
var $image = $("#image");
var doc_width = $image.offset().left + $image.width();
if ($note_body.offset().left + $note_body.width() > doc_width) {
$note_body.css({
left: $note_body.position().left - 10 - ($note_body.offset().left + $note_body.width() - doc_width)
});
}
},
show: function(id) {
Note.Body.hide_all();
Note.clear_timeouts();
var $note_body = Note.Body.find(id);
if (!$note_body.data('resized')) {
Note.Body.resize($note_body);
$note_body.data('resized', 'true');
}
$note_body.show();
Note.Body.initialize($note_body);
},
find: function(id) {
return $(".note-container div.note-body[data-id=" + id + "]");
},
hide: function(id) {
var $note_body = Note.Body.find(id);
Note.timeouts.push(setTimeout(() => $note_body.hide(), Note.HIDE_DELAY));
},
hide_all: function() {
$(".note-container div.note-body").hide();
},
resize: function($note_body) {
$note_body.css("min-width", "");
var w = $note_body.width();
var h = $note_body.height();
var golden_ratio = 1.6180339887;
var last = 0;
var x = 0;
var lo = 0;
var hi = 0;
if ((w / h) < golden_ratio) {
lo = 140;
hi = 400;
do {
last = w;
x = (lo + hi) / 2;
$note_body.css("min-width", x);
w = $note_body.width();
h = $note_body.height();
if ((w / h) < golden_ratio) {
lo = x;
} else {
hi = x;
}
} while ((lo < hi) && (w > last));
} else if ($note_body[0].scrollWidth <= $note_body.width()) {
lo = 20;
hi = w;
do {
x = (lo + hi) / 2;
$note_body.css("min-width", x);
if ($note_body.height() > h) {
lo = x
} else {
hi = x;
}
} while ((hi - lo) > 4);
if ($note_body.height() > h) {
$note_body.css("min-width", hi);
}
}
},
set_text: function($note_body, $note_box, text) {
Note.Body.display_text($note_body, text);
if (Note.embed) {
const $note_inner_box = $note_box.find("div.note-box-inner-border");
Note.Body.display_text($note_inner_box, text);
// Reset the font size so that the normalization calculations will be correct
$note_inner_box.css("font-size", Note.base_font_size + "px");
Note.normalize_sizes($note_inner_box.children(), Note.base_font_size);
// Clear the font size so that the fonts will be scaled to the current value
$note_inner_box.css("font-size", "");
Note.Box.copy_style_attributes($note_box);
}
Note.Body.resize($note_body);
Note.Body.bound_position($note_body);
},
display_text: function($note_body, text) {
text = text.replace(/<tn>/g, '<p class="tn">');
text = text.replace(/<\/tn>/g, '</p>');
text = text.replace(/\n/g, '<br>');
$note_body.html(text);
},
bind_events: function($note_body) {
$note_body.on("mouseover.danbooru", function(e) {
var $note_body_inner = $(e.currentTarget);
Note.Body.show($note_body_inner.data("id"));
e.stopPropagation();
});
$note_body.on("mouseout.danbooru", function(e) {
var $note_body_inner = $(e.currentTarget);
Note.Body.hide($note_body_inner.data("id"));
e.stopPropagation();
});
if (CurrentUser.data("is-anonymous") === false) {
$note_body.on("click.danbooru", function(e) {
if (e.target.tagName !== "A") {
var $note_body_inner = $(e.currentTarget);
Note.Edit.show($note_body_inner);
}
e.stopPropagation();
});
} else {
$note_body.on("click.danbooru", function(e) {
if (e.target.tagName !== "A") {
Utility.notice("You must be logged in to edit notes");
}
e.stopPropagation();
});
}
}
},
Edit: {
show: function($note_body) {
var id = $note_body.data("id");
if (Note.editing) {
return;
}
$(".note-box").resizable("disable");
$(".note-box").draggable("disable");
$(".note-box").addClass("editing");
let $textarea = $('<textarea></textarea>');
$textarea.css({
width: "97%",
height: "85%",
resize: "none",
});
if ($note_body.html() !== "<em>Click to edit</em>") {
$textarea.val($note_body.data("original-body"));
}
let $dialog = $('<div></div>');
let note_title = (typeof id === 'string' && id.startsWith('x') ? 'Creating new note' : 'Editing note #' + id);
$dialog.append('<span><b>' + note_title + ' (<a href="/wiki_pages/help:notes">view help</a>)</b></span>');
$dialog.append($textarea);
$dialog.data("id", id);
$dialog.dialog({
width: 360,
height: 240,
position: {
my: "right",
at: "right-20",
of: window
},
classes: {
"ui-dialog": "note-edit-dialog",
},
buttons: {
"Save": Note.Edit.save,
"Preview": Note.Edit.preview,
"Cancel": Note.Edit.cancel,
"Delete": Note.Edit.destroy,
"History": Note.Edit.history
}
});
$dialog.data("uiDialog")._title = function(title) {
title.html(this.options.title); // Allow unescaped html in dialog title
}
$dialog.on("dialogclose.danbooru", function() {
Note.editing = false;
$(".note-box").resizable("enable");
$(".note-box").draggable("enable");
$(".note-box").removeClass("editing");
});
$textarea.selectEnd();
Note.editing = true;
},
parameterize_note: function($note_box, $note_body) {
var $image = $("#image");
var original_width = parseInt($image.data("original-width"));
var ratio = parseInt($image.width()) / original_width;
var hash = {
note: {
x: Math.round($note_box.position().left / ratio),
y: Math.round($note_box.position().top / ratio),
width: Math.round($note_box.width() / ratio),
height: Math.round($note_box.height() / ratio),
body: $note_body.data("original-body"),
}
}
if ($note_box.data("id").match(/x/)) {
hash.note.html_id = $note_box.data("id");
hash.note.post_id = Utility.meta("post-id");
}
return hash;
},
error_handler: function(xhr, status, exception) {
Utility.error("Error: " + (xhr.responseJSON.reason || xhr.responseJSON.reasons.join("; ")));
},
success_handler: function(data, status, xhr) {
var $note_box = null;
if (data.html_id) { // new note
var $note_body = Note.Body.find(data.html_id);
$note_box = Note.Box.find(data.html_id);
$note_body.data("id", String(data.id)).attr("data-id", data.id);
$note_box.data("id", String(data.id)).attr("data-id", data.id);
$note_box.removeClass("unsaved");
} else {
$note_box = Note.Box.find(data.id);
$note_box.removeClass("unsaved");
}
},
save: function() {
var $this = $(this);
var $textarea = $this.find("textarea");
var id = $this.data("id");
var $note_body = Note.Body.find(id);
var $note_box = Note.Box.find(id);
var text = $textarea.val();
$note_body.data("original-body", text);
Note.Body.set_text($note_body, $note_box, "Loading...");
$.get("/note_previews.json", {body: text}).then(function(data) {
Note.Body.set_text($note_body, $note_box, data.body);
Note.Body.initialize($note_body);
$note_body.show();
});
$this.dialog("close");
if (id.match(/\d/)) {
$.ajax("/notes/" + id + ".json", {
type: "PUT",
data: Note.Edit.parameterize_note($note_box, $note_body),
error: Note.Edit.error_handler,
success: Note.Edit.success_handler
});
} else {
$.ajax("/notes.json", {
type: "POST",
data: Note.Edit.parameterize_note($note_box, $note_body),
error: Note.Edit.error_handler,
success: Note.Edit.success_handler
});
}
},
preview: function() {
var $this = $(this);
var $textarea = $this.find("textarea");
var id = $this.data("id");
var $note_body = Note.Body.find(id);
var text = $textarea.val();
var $note_box = Note.Box.find(id);
$note_box.addClass("unsaved");
Note.Body.set_text($note_body, $note_box, "Loading...");
$.get("/note_previews.json", {body: text}).then(function(data) {
Note.Body.set_text($note_body, $note_box, data.body);
Note.Body.initialize($note_body);
$note_body.show();
});
},
cancel: function() {
$(this).dialog("close");
},
destroy: function() {
if (!confirm("Do you really want to delete this note?")) {
return
}
var $this = $(this);
var id = $this.data("id");
if (id.match(/\d/)) {
$.ajax("/notes/" + id + ".json", {
type: "DELETE",
success: function() {
Note.Box.find(id).remove();
Note.Body.find(id).remove();
$this.dialog("close");
}
});
}
},
history: function() {
var $this = $(this);
var id = $this.data("id");
if (id.match(/\d/)) {
window.location.href = "/note_versions?search[note_id]=" + id;
}
$(this).dialog("close");
}
},
TranslationMode: {
active: false,
toggle: function(e) {
if (Note.TranslationMode.active) {
Note.TranslationMode.stop(e);
} else {
Note.TranslationMode.start(e);
}
},
start: function(e) {
e.preventDefault();
if (CurrentUser.data("is-anonymous")) {
Utility.notice("You must be logged in to edit notes");
return;
}
if (Note.TranslationMode.active) {
return;
}
$("#image").css("cursor", "crosshair");
Note.TranslationMode.active = true;
$(document.body).addClass("mode-translation");
$("#image").off("click.danbooru", Note.Box.toggle_all);
$("#image").on("mousedown.danbooru.note", Note.TranslationMode.Drag.start);
$(document).on("mouseup.danbooru.note", Note.TranslationMode.Drag.stop);
$("#mark-as-translated-section").show();
Utility.notice('Translation mode is on. Drag on the image to create notes. <a href="#">Turn translation mode off</a> (shortcut is <span class="key">n</span>).');
$("#notice a:contains(Turn translation mode off)").on("click.danbooru", Note.TranslationMode.stop);
},
stop: function(e) {
e.preventDefault();
Note.TranslationMode.active = false;
$("#image").css("cursor", "auto");
$("#image").on("click.danbooru", Note.Box.toggle_all);
$("#image").off("mousedown.danbooru.note", Note.TranslationMode.Drag.start);
$(document).off("mouseup.danbooru.note", Note.TranslationMode.Drag.stop);
$(document.body).removeClass("mode-translation");
$("#close-notice-link").click();
$("#mark-as-translated-section").hide();
},
create_note: function(e, x, y, w, h) {
if (w > 9 || h > 9) { /* minimum note size: 10px */
if (w <= 9) {
w = 10;
} else if (h <= 9) {
h = 10;
}
Note.create(x, y, w, h);
}
$(".note-container").removeClass("hide-notes");
e.stopPropagation();
e.preventDefault();
},
Drag: {
dragging: false,
dragStartX: 0,
dragStartY: 0,
dragDistanceX: 0,
dragDistanceY: 0,
x: 0,
y: 0,
w: 0,
h: 0,
start: function (e) {
if (e.which !== 1) {
return;
}
e.preventDefault(); /* don't drag the image */
$(document).on("mousemove.danbooru", Note.TranslationMode.Drag.drag);
Note.TranslationMode.Drag.dragStartX = e.pageX;
Note.TranslationMode.Drag.dragStartY = e.pageY;
},
drag: function (e) {
Note.TranslationMode.Drag.dragDistanceX = e.pageX - Note.TranslationMode.Drag.dragStartX;
Note.TranslationMode.Drag.dragDistanceY = e.pageY - Note.TranslationMode.Drag.dragStartY;
var $image = $("#image");
var offset = $image.offset();
var limitX1 = $image.width() - Note.TranslationMode.Drag.dragStartX + offset.left - 1;
var limitX2 = offset.left - Note.TranslationMode.Drag.dragStartX;
var limitY1 = $image.height() - Note.TranslationMode.Drag.dragStartY + offset.top - 1;
var limitY2 = offset.top - Note.TranslationMode.Drag.dragStartY;
if (Note.TranslationMode.Drag.dragDistanceX > limitX1) {
Note.TranslationMode.Drag.dragDistanceX = limitX1;
} else if (Note.TranslationMode.Drag.dragDistanceX < limitX2) {
Note.TranslationMode.Drag.dragDistanceX = limitX2;
}
if (Note.TranslationMode.Drag.dragDistanceY > limitY1) {
Note.TranslationMode.Drag.dragDistanceY = limitY1;
} else if (Note.TranslationMode.Drag.dragDistanceY < limitY2) {
Note.TranslationMode.Drag.dragDistanceY = limitY2;
}
if (Math.abs(Note.TranslationMode.Drag.dragDistanceX) > 9 && Math.abs(Note.TranslationMode.Drag.dragDistanceY) > 9) {
Note.TranslationMode.Drag.dragging = true; /* must drag at least 10pixels (minimum note size) in both dimensions. */
}
if (Note.TranslationMode.Drag.dragging) {
if (Note.TranslationMode.Drag.dragDistanceX >= 0) {
Note.TranslationMode.Drag.x = Note.TranslationMode.Drag.dragStartX - offset.left;
Note.TranslationMode.Drag.w = Note.TranslationMode.Drag.dragDistanceX;
} else {
Note.TranslationMode.Drag.x = Note.TranslationMode.Drag.dragStartX - offset.left + Note.TranslationMode.Drag.dragDistanceX;
Note.TranslationMode.Drag.w = -Note.TranslationMode.Drag.dragDistanceX;
}
if (Note.TranslationMode.Drag.dragDistanceY >= 0) {
Note.TranslationMode.Drag.y = Note.TranslationMode.Drag.dragStartY - offset.top;
Note.TranslationMode.Drag.h = Note.TranslationMode.Drag.dragDistanceY;
} else {
Note.TranslationMode.Drag.y = Note.TranslationMode.Drag.dragStartY - offset.top + Note.TranslationMode.Drag.dragDistanceY;
Note.TranslationMode.Drag.h = -Note.TranslationMode.Drag.dragDistanceY;
}
$('#note-preview').css({
display: 'block',
left: (Note.TranslationMode.Drag.x + 1),
top: (Note.TranslationMode.Drag.y + 1),
width: (Note.TranslationMode.Drag.w - 3),
height: (Note.TranslationMode.Drag.h - 3)
});
}
},
stop: function (e) {
if (e.which !== 1) {
return;
}
if (Note.TranslationMode.Drag.dragStartX === 0) {
return; /* 'stop' is bound to window, don't create note if start wasn't triggered */
}
$(document).off("mousemove.danbooru", Note.TranslationMode.Drag.drag);
if (Note.TranslationMode.Drag.dragging) {
$('#note-preview').css({ display: 'none' });
Note.TranslationMode.create_note(e, Note.TranslationMode.Drag.x, Note.TranslationMode.Drag.y, Note.TranslationMode.Drag.w - 1, Note.TranslationMode.Drag.h - 1);
Note.TranslationMode.Drag.dragging = false; /* border of the note is pixel-perfect on the preview border */
} else { /* no dragging -> toggle display of notes */
Note.Box.toggle_all();
}
Note.TranslationMode.Drag.dragStartX = 0;
Note.TranslationMode.Drag.dragStartY = 0;
}
}
},
id: "x",
dragging: false,
editing: false,
move_id: null,
drag_id: null,
base_font_size: null,
timeouts: [],
pending: {},
add: function(container, id, x, y, w, h, original_body, sanitized_body) {
var $note_box = Note.Box.create(id);
var $note_body = Note.Body.create(id);
$note_box.data('x', x);
$note_box.data('y', y);
$note_box.data('width', w);
$note_box.data('height', h);
container.appendChild($note_box[0]);
container.appendChild($note_body[0]);
$note_body.data("original-body", original_body);
Note.Box.scale($note_box);
Note.Body.display_text($note_body, sanitized_body);
if (Note.embed) {
Note.Body.display_text($note_box.children("div.note-box-inner-border"), sanitized_body);
}
},
create: function(x, y, w, h) {
var $note_box = Note.Box.create(Note.id);
var $note_body = Note.Body.create(Note.id);
$note_box.css({
top: y,
left: x,
width: w,
height: h
});
Note.Box.update_data_attributes($note_box);
$note_box.addClass("unsaved");
$note_body.html("<em>Click to edit</em>");
$(".note-container").append($note_box);
$(".note-container").append($note_body);
Note.id += "x";
},
normalize_sizes: function ($note_elements, parent_font_size) {
if ($note_elements.length === 0) {
return;
}
$note_elements.each(function(i, element) {
const $element = $(element);
const computed_styles = window.getComputedStyle(element);
const font_size = parseFloat(computed_styles.fontSize);
Note.NORMALIZE_ATTRIBUTES.forEach(function(attribute) {
const original_size = parseFloat(computed_styles[attribute]) || 0;
const relative_em = original_size / font_size;
$element.css(attribute, relative_em + "em");
});
const font_percentage = 100 * (font_size / parent_font_size);
$element.css("font-size", font_percentage + "%");
$element.attr("size", "");
Note.normalize_sizes($element.children(), font_size);
});
},
clear_timeouts: function() {
Note.timeouts.forEach(clearTimeout);
Note.timeouts = [];
},
load_all: function() {
var fragment = document.createDocumentFragment();
$.each($("#notes article"), function(i, article) {
var $article = $(article);
Note.add(
fragment,
$article.data("id"),
$article.data("x"),
$article.data("y"),
$article.data("width"),
$article.data("height"),
$article.data("body"),
$article.html()
);
});
const $note_container = $(".note-container");
$note_container.append(fragment);
if (Note.embed) {
Note.base_font_size = parseFloat(window.getComputedStyle($note_container[0]).fontSize);
$.each($(".note-box"), function(i, note_box) {
const $note_box = $(note_box);
Note.normalize_sizes($("div.note-box-inner-border", note_box).children(), Note.base_font_size);
// Accounting for transformation values calculations which aren't correct immediately on page load
setTimeout(()=>{Note.Box.copy_style_attributes($note_box);}, 100);
});
}
Note.Box.scale_all();
},
initialize_all: function() {
if ($("#c-posts #a-show #image").length === 0 || $("video#image").length) {
return;
}
Note.embed = (Utility.meta("post-has-embedded-notes") === "true");
Note.load_all();
this.initialize_shortcuts();
this.initialize_highlight();
$(document).on("hashchange.danbooru.note", this.initialize_highlight);
Utility.keydown("up down left right", "nudge_note", Note.Box.key_nudge);
Utility.keydown("shift+up shift+down shift+left shift+right", "resize_note", Note.Box.key_resize);
$(window).on("resize.danbooru.note_scale", Note.Box.scale_all);
},
initialize_shortcuts: function() {
$("#translate").on("click.danbooru", Note.TranslationMode.toggle);
$("#image").on("click.danbooru", Note.Box.toggle_all);
},
initialize_highlight: function() {
var matches = window.location.hash.match(/^#note-(\d+)$/);
if (matches) {
var $note_box = Note.Box.find(matches[1]);
Note.Box.show_highlighted($note_box);
}
},
}
$(function() {
Note.initialize_all();
});
export default Note