Commit 768b4714 authored by Robin Tissot's avatar Robin Tissot
Browse files

Merge branch 'develop'

parents 463a25ea d7a2d41e
......@@ -67,9 +67,10 @@ INTERNAL_IPS += ['127.0.0.1',]
it is recomanded to then make an alias for manage.py or set $DJANGO_SETTINGS_MODULE
* if you need to test methods from the queue manager, set USE_CELERY=True and run a celery worker
* To run a basic celery worker listening on everything
> $ celery -A app.escriptorium worker -l INFO
To disable celery you can set `CELERY_TASK_ALWAYS_EAGER = True`
* Create the sql tables
> $ cd app && python manage.py migrate
......
var panels;
var wz = {scale:1, translate: {x:0, y:0}};
var API = {
part: '/api/documents/' + DOCUMENT_ID + '/parts/{part_pk}/'
};
var zoom = new WheelZoom({min_scale: 0.5, max_scale: 10});
$(document).ready(function() {
var show_img = JSON.parse(Cookies.get('img-panel-open') || 'true');
......@@ -42,8 +42,9 @@ $(document).ready(function() {
$('#images-tab-link').attr('href', tabUrl);
if (callback) callback(data);
});
});
}
/* export */
$('button#document-export').click(function(ev) {
ev.preventDefault();
......@@ -53,7 +54,7 @@ $(document).ready(function() {
'$1'+selectedTranscription+'$2');
window.open(new_href);
});
$('.open-panel').on('click', function(ev) {
$(this).toggleClass('btn-primary').toggleClass('btn-secondary');
var panel = $(this).data('target');
......@@ -61,6 +62,7 @@ $(document).ready(function() {
for (var key in panels) {
panels[key].reset();
}
zoom.refresh();
});
$('a#prev-part, a#next-part').click(function(ev) {
......@@ -76,13 +78,20 @@ $(document).ready(function() {
}
});
// propagate zoom & translation
$('.img-container').each(function(i, el) {
el.addEventListener('wheelzoom.update', function(ev) {
wz = ev.detail;
// select all panels but this one
var zc = $(':not(#binar-panel) .img-container .zoom-container');
zc.css({transform: 'translate('+ev.detail.translate.x+'px,'+ev.detail.translate.y+'px) scale('+ev.detail.scale+')'});
});
// zoom slider
$('#zoom-range').attr('min', zoom.min_scale);
$('#zoom-range').attr('max', zoom.max_scale);
$('#zoom-range').val(zoom.scale);
zoom.events.on('wheelzoom.updated', function(data) {
$('#zoom-range').val(zoom.scale);
});
$('#zoom-range').on('change', function(ev) {
zoom.scale = parseFloat($(ev.target).val());
zoom.refresh();
});
$('#zoom-reset').on('click', function(ev) {
zoom.reset();
$('#zoom-range').val(zoom.scale);
});
});
......@@ -3,11 +3,15 @@ class BinarizationPanel {
this.$panel = $panel;
this.opened = opened | false;
this.$container = $('.img-container', this.$panel);
WheelZoom(this.$container, false, 1, 1);
}
load(part) {
this.part = part;
$('.img-container img', this.$panel).on('load', $.proxy(function() {
zoom.register(this.$container);
}, this));
if (this.part.bw_image) {
if (this.part.bw_image.thumbnails) {
$('.img-container img', this.$panel).attr('src', this.part.bw_image.thumbnails.large);
......@@ -18,7 +22,6 @@ class BinarizationPanel {
$('.img-container img', this.$panel).attr('src', '');
}
if (this.opened) this.open();
this.$container.trigger('wheelzoom.refresh');
}
open() {
......@@ -38,7 +41,5 @@ class BinarizationPanel {
else this.open();
}
reset() {
this.$container.trigger('wheelzoom.refresh');
}
reset() {}
}
......@@ -44,8 +44,8 @@ class Box {
}, this),
drag: $.proxy(function(ev, ui) {
var original = ui.originalPosition;
var left = (ev.clientX - this.click.x + original.left - wz.translate.x) / wz.scale;
var top = (ev.clientY - this.click.y + original.top - wz.translate.y) / wz.scale;
var left = (ev.clientX - this.click.x + original.left - zoom.translate.x) / zoom.scale;
var top = (ev.clientY - this.click.y + original.top - zoom.translate.y) / zoom.scale;
ui.position = {
left: left,
top: top
......@@ -75,8 +75,8 @@ class Box {
this.click.y = ev.clientY;
}, this),
resize: $.proxy(function(ev, ui) {
ui.size.width = (ev.clientX - this.click.x) / wz.scale + ui.originalSize.width;
ui.size.height = (ev.clientY - this.click.y) / wz.scale + ui.originalSize.height;
ui.size.width = (ev.clientX - this.click.x) / zoom.scale + ui.originalSize.width;
ui.size.height = (ev.clientY - this.click.y) / zoom.scale + ui.originalSize.height;
var tl = $('#trans-box-line-'+this.pk).data('TranscriptionLine');
if (tl) {
tl.box = this.getBox();
......@@ -147,10 +147,10 @@ class Box {
}
getBox() {
var x1 = parseInt((this.$element.position().left) / wz.scale / this.ratio);
var y1 = parseInt((this.$element.position().top) / wz.scale / this.ratio);
var x2 = parseInt(((this.$element.position().left) / wz.scale + this.$element.width()) / this.ratio);
var y2 = parseInt(((this.$element.position().top) / wz.scale + this.$element.height()) / this.ratio);
var x1 = parseInt((this.$element.position().left) / zoom.scale / this.ratio);
var y1 = parseInt((this.$element.position().top) / zoom.scale / this.ratio);
var x2 = parseInt(((this.$element.position().left) / zoom.scale + this.$element.width()) / this.ratio);
var y2 = parseInt(((this.$element.position().top) / zoom.scale + this.$element.height()) / this.ratio);
return [x1, y1, x2, y2];
}
showOverlay() {
......@@ -263,13 +263,13 @@ class SegmentationPanel {
}
createBoxAtMousePos(ev, mode) {
var top_left_x = Math.max(0, ev.clientX - this.$container.offset().left) / wz.scale / this.ratio;
var top_left_y = Math.max(0, ev.clientY - this.$container.offset().top) / wz.scale / this.ratio;
var top_left_x = Math.max(0, ev.clientX - this.$container.offset().left) / zoom.scale / this.ratio;
var top_left_y = Math.max(0, ev.clientY - this.$container.offset().top) / zoom.scale / this.ratio;
var box = [
parseInt(top_left_x),
parseInt(top_left_y),
parseInt(top_left_x + 200/wz.scale/this.ratio),
parseInt(top_left_y + 40/wz.scale/this.ratio)
parseInt(top_left_x + 200/zoom.scale/this.ratio),
parseInt(top_left_y + 40/zoom.scale/this.ratio)
];
var block = null;
if ($(ev.target).is('.block-box')) {
......@@ -308,6 +308,8 @@ class SegmentationPanel {
this.getRatio();
this.showBlocks();
this.showLines();
zoom.register(this.$container, true);
}
open() {
......
......@@ -3,18 +3,21 @@ class SourcePanel {
this.$panel = $panel;
this.opened = opened | false;
this.$container = $('.img-container', this.$panel);
WheelZoom(this.$container, false, 1, 1);
}
load(part) {
this.part = part;
$('.img-container img', this.$panel).on('load', $.proxy(function() {
zoom.register(this.$container);
}, this));
if (this.part.image.thumbnails) {
$('.img-container img', this.$panel).attr('src', this.part.image.thumbnails.large);
} else {
$('.img-container img', this.$panel).attr('src', this.part.image.url);
}
if (this.opened) this.open();
this.$container.trigger('wheelzoom.refresh');
}
open() {
......@@ -34,7 +37,5 @@ class SourcePanel {
else this.open();
}
reset() {
this.$container.trigger('wheelzoom.refresh');
}
reset() {}
}
......@@ -248,7 +248,8 @@ class TranscriptionPanel{
this.opened = opened;
this.part = null;
this.lines = []; // list of TranscriptionLine != this.part.lines
this.$container = $('.img-container', this.$panel);
$('#document-transcriptions').change($.proxy(function(ev) {
for (var i=0; i<this.lines.length; i++) {
this.lines[i].setText();
......@@ -325,6 +326,7 @@ class TranscriptionPanel{
for (var i=0; i < this.part.lines.length; i++) {
this.addLine(this.part.lines[i]);
}
zoom.register(this.$container, true);
}
open() {
......
......@@ -25,7 +25,6 @@
VPOS="771"
WIDTH="0"
HEIGHT="28">
HEIGHT="28">
<String ID="segment_0"
CONTENT="i"
HPOS="160"
......
......@@ -62,7 +62,6 @@ class InvitationTestCase(TestCase):
self.group = Group.objects.create(name='testgroup')
self.sender.groups.add(self.group)
@override_settings(USE_CELERY=False)
def test_send(self):
self.client.login(username='sender', password='test')
with self.assertNumQueries(3):
......
body {
mind-height: 2000px;
padding: 10px;
padding-top: 70px;
max-width: 1800px;
}
......@@ -94,12 +95,19 @@ body {
}
.card .js-edit {
width: 100%;
/*opacity: 0.8;
position: absolute;
bottom: 0;
left:0; */
opacity: 0.2;
bottom: 5px;;
width: 90%;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
}
.card:hover .js-edit {
opacity: 1;
}
.card .js-part-update-form {
......@@ -208,8 +216,11 @@ body {
.card .proc.pending {
color: var(--secondary);
}
.card .proc {
cursor: pointer;
.card .btn.proc {
padding: 0;
}
.card .btn.proc:hover {
color: var(--info);
}
.card .proc.done {
......@@ -249,7 +260,6 @@ i.panel-icon {
vertical-align: top;
}
.img-container {
position: relative;
overflow: hidden;
......@@ -259,9 +269,11 @@ i.panel-icon {
transform-origin: 0 0;
}
.zoom-container {
transition: transform 0.3s;
transform-origin: 0 0;
}
#zoom-range {
height: 100px;
}
#part-trans, #part-img {
overflow: hidden;
......
'use strict';
function WheelZoom(container, disabled_, initial_scale, min_scale_opt, max_scale_opt) {
var factor = 0.2;
var target = container.children().first();
initial_scale = initial_scale || 1;
var size = {w:target.width() * initial_scale, h:target.height() * initial_scale};
var zoom_target = {x:0, y:0};
var zoom_point = {x:0, y:0};
var previousEvent;
var disabled = disabled_;
target.css({transformOrigin: '0 0', transition: 'transform 0.3s', cursor: 'zoom-in'});
container.on("mousewheel DOMMouseScroll", scrolled);
container.on('mousedown', draggable);
container.on('wheelzoom.reset', reset);
container.on('wheelzoom.refresh', refresh);
container.on('wheelzoom.destroy', destroy);
container.on('wheelzoom.disable', disable);
container.on('wheelzoom.enable', enable);
var api = {
min_scale: min_scale_opt || Math.min(
$(window).width() / target.width() * initial_scale * 0.9,
$(window).height() / target.height() * initial_scale * 0.9),
max_scale: max_scale_opt || 10,
scale: initial_scale,
pos: {x:0, y:0}
};
function scrolled(e){
if (disabled) return;
class WheelZoom {
constructor(options) {
this.options = options || {};
var defaults = {
factor: 0.2,
min_scale: 1,
max_scale: null,
initial_scale: 1,
disabled: false
};
this.factor = options.factor || defaults.factor;
this.min_scale = options.min_scale || defaults.min_scale;
this.max_scale = options.max_scale || defaults.max_scale;
this.initial_scale = options.initial_scale || defaults.initial_scale;
this.disabled = options.disabled || defaults.disabled;
// create a dummy tag for event bindings
this.events = $('<div id="wheelzoom-events-js">');
this.events.appendTo($('body'));
this.targets = []; this.containers = [];
this.previousEvent = null;
this.scale = this.initial_scale;
this.pos = {x:0, y:0};
}
register(container, mirror) {
var target = container.children().first();
this.size = {w:target.width() * this.initial_scale, h:target.height() * this.initial_scale};
target.css({transformOrigin: '0 0', transition: 'transform 0.3s'});
if (mirror !== true) {
target.css({cursor: 'zoom-in'});
container.on("mousewheel DOMMouseScroll", $.proxy(this.scrolled, this));
container.on('mousedown', $.proxy(this.draggable, this));
}
this.events.on('wheelzoom.reset', $.proxy(this.reset, this));
this.events.on('wheelzoom.refresh', $.proxy(this.refresh, this));
this.targets.push(target);
this.containers.push(container);
}
scrolled(e) {
if (this.disabled) return;
e.preventDefault();
var offset = container.offset();
zoom_point.x = e.originalEvent.pageX - offset.left;
zoom_point.y = e.originalEvent.pageY - offset.top;
var offset = $(e.delegateTarget).closest('.img-container').offset();
var zoom_point = {x: e.originalEvent.pageX - offset.left,
y: e.originalEvent.pageY - offset.top};
var delta = e.delta || e.originalEvent.wheelDelta;
if (delta === undefined) {
//we are on firefox
......@@ -41,107 +58,99 @@ function WheelZoom(container, disabled_, initial_scale, min_scale_opt, max_scale
// cap the delta to [-1,1] for cross browser consistency
delta = Math.max(-1, Math.min(1, delta));
// determine the point on where the slide is zoomed in
zoom_target.x = (zoom_point.x - api.pos.x)/ api.scale;
zoom_target.y = (zoom_point.y - api.pos.y)/ api.scale;
var zoom_target = {x: (zoom_point.x - this.pos.x) / this.scale,
y: (zoom_point.y - this.pos.y) / this.scale};
// apply zoom
api.scale += delta * factor * api.scale;
this.scale += delta * this.factor * this.scale;
api.scale = Math.max(api.min_scale, api.scale);
api.scale = Math.min(api.max_scale, api.scale);
if(this.min_scale !== null) this.scale = Math.max(this.min_scale, this.scale);
if(this.max_scale !== null) this.scale = Math.min(this.max_scale, this.scale);
// calculate x and y based on zoom
api.pos.x = Math.round(-zoom_target.x * api.scale + zoom_point.x);
api.pos.y = Math.round(-zoom_target.y * api.scale + zoom_point.y);
this.pos.x = Math.round(-zoom_target.x * this.scale + zoom_point.x);
this.pos.y = Math.round(-zoom_target.y * this.scale + zoom_point.y);
updateStyle();
this.updateStyle();
}
function drag(e) {
if (disabled) return;
drag(e) {
if (this.disabled) return;
e.preventDefault();
api.pos.x += (e.pageX - previousEvent.pageX);
api.pos.y += (e.pageY - previousEvent.pageY);
previousEvent = e;
updateStyle();
this.pos.x += (e.pageX - this.previousEvent.pageX);
this.pos.y += (e.pageY - this.previousEvent.pageY);
this.previousEvent = e;
this.updateStyle();
}
function removeDrag() {
target.removeClass('notransition');
$(document).off('mouseup', removeDrag);
container.off('mousemove', drag);
removeDrag() {
this.targets.forEach(function(e,i) {e.removeClass('notransition');});
$(document).off('mouseup', this.removeDrag);
$(document).off('mousemove', this.drag);
}
function draggable(e) {
if (disabled) return;
draggable(e) {
if (this.disabled) return;
e.preventDefault();
previousEvent = e;
this.previousEvent = e;
// disable transition while dragging
target.addClass('notransition');
container.on('mousemove', drag);
$(document).on('mouseup', removeDrag);
this.targets.forEach(function(e,i) {e.addClass('notransition');});
$(document).on('mousemove', $.proxy(this.drag, this));
$(document).on('mouseup', $.proxy(this.removeDrag, this));
}
function updateStyle() {
updateStyle() {
// Make sure the slide stays in its container area when zooming in/out
if (api.scale > 1) {
if(api.pos.x > 0) { api.pos.x = 0; }
if(api.pos.x+size.w*api.scale < container.width()) { api.pos.x = container.width() - size.w*api.scale; }
if(api.pos.y > 0) { api.pos.y = 0; }
if(api.pos.y+size.h*api.scale < container.height()) { api.pos.y = container.height() - size.h*api.scale; }
if (this.scale > 1) {
if(this.pos.x > 0) { this.pos.x = 0; }
if(this.pos.x+this.size.w*this.scale < this.size.w) { this.pos.x = this.size.w - this.size.w*this.scale; }
if(this.pos.y > 0) { this.pos.y = 0; }
if(this.pos.y+this.size.h*this.scale < this.size.h) { this.pos.y = this.size.h - this.size.h*this.scale; }
} else {
if(api.pos.x < 0) { api.pos.x = 0; }
if(api.pos.x+size.w > container.width()) { api.pos.x = -size.w*(api.scale-1); }
if(api.pos.y < 0) { api.pos.y = 0; }
if(api.pos.y+size.h > container.height()) { api.pos.y = 0; }
if(size.h*api.scale >= container.height() && api.pos.y+size.h < container.height()) { api.pos.y = container.height() - size.h; }
if(size.h*api.scale <= container.height() && api.pos.y+size.h > container.height()) { api.pos.y = container.height() - size.h; }
if(this.pos.x < 0) { this.pos.x = 0; }
if(this.pos.x+this.size.w*this.scale > this.size.w) { this.pos.x = -this.size.w*(this.scale-1); }
if(this.pos.y < 0) { this.pos.y = 0; }
if(this.pos.y+this.size.h*this.scale > this.size.h) { this.pos.y = -this.size.h*(this.scale-1); }
}
// apply scale first for transition effect
target.css('transform','scale('+api.scale+')');
target.css('transform','translate('+(api.pos.x)+'px,'+(api.pos.y)+'px) scale('+api.scale+')');
var event = new CustomEvent('wheelzoom.update', {detail: {
scale: api.scale,
translate: api.pos,
originalWidth: size.w
}});
container.get(0).dispatchEvent(event);
this.targets.forEach($.proxy(function(e, i) {
e.css('transform', 'scale('+this.scale+')')
.css('transform', 'translate('+(this.pos.x)+'px,'+
(this.pos.y)+'px) scale('+this.scale+')'); }, this));
this.events.trigger('wheelzoom.updated');
}
function refresh() {
size = {w: target.width(), h: target.height()};
api.min_scale = min_scale_opt || Math.min(
$(window).width() / (size.w * initial_scale) * 0.9,
$(window).height() / (size.h * initial_scale) * 0.9);
updateStyle();
getVisibleContainer() {
return this.containers.find(function(e) { return e.is(':visible') && e.height() != 0;});
}
function reset() {
api.pos = {x:0, y:0};
api.scale = initial_scale || 1;
size = {w: target.width(), h: target.height()};
updateStyle();
refresh() {
let container = this.getVisibleContainer();
this.size = {w: container.width(), h: container.height()};
this.min_scale = this.options.min_scale || Math.min(
$(window).width() / (this.size.w * this.initial_scale) * 0.9,
$(window).height() / (this.size.h * this.initial_scale) * 0.9);
this.updateStyle();
}
function disable() {
disabled = true;
reset() {
let container = this.getVisibleContainer();
this.pos = {x:0, y:0};
this.scale = this.initial_scale || 1;
this.size = {w: container.width(), h: container.height()};
this.updateStyle();
}
disable() {
this.disabled = true;
}
function enable() {
disabled = false;
enable() {
this.disabled = false;
}
function destroy() {
container.off("mousewheel DOMMouseScroll", scrolled);
container.off('mousedown', draggable);
container.off('wheelzoom.reset', reset);
container.off('wheelzoom.refresh', refresh);
container.off('wheelzoom.destroy', destroy);
container.off('wheelzoom.disable', disable);
container.off('wheelzoom.enable', enable);
destroy() {