Skip to content

Commit

Permalink
Add drag-mode switching buttons (#57)
Browse files Browse the repository at this point in the history
Add radio buttons to toggle between 'crop' and 'move' drag modes,
and a separate 'Transcribe area' button for submitting to crop
the image.

The change to the copy-button is because it's now inside the form.

Bug: https://phabricator.wikimedia.org/T288672
  • Loading branch information
samwilson authored Aug 27, 2021
1 parent 0f63ebc commit 618d8bf
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 65 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ Documentation:
* For contributors: [CONTRIBUTING.md](https://github.com/wikimedia/wikimedia-ocr/blob/main/CONTRIBUTING.md)

![CI](https://github.com/wikimedia/wikimedia-ocr/workflows/CI/badge.svg)

## Licenses

* Wikimedia OCR is GPL 3.0 or later (see the LICENSE file)
* [Crop_-_The_Noun_Project.svg](https://commons.wikimedia.org/wiki/File:Crop_-_The_Noun_Project.svg) is CC0
* [OOjs_UI_icon_move.svg](https://commons.wikimedia.org/wiki/File:OOjs_UI_icon_move.svg) is CC-BY-SA-4.0
103 changes: 64 additions & 39 deletions assets/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ function updateSelect2Options(engine)
}

$(function () {
// Remove nojs class, for styling non-Javascript users.
$('html').removeClass('nojs');

// Initiate Select2, which allows dynamic entry of languages.
$select2.select2({
theme: 'bootstrap',
Expand All @@ -60,51 +63,73 @@ $(function () {
// For the result page. Makes the 'Copy' button copy the transcription to the clipboard.
const $copyButton = $('.copy-button');
if ($copyButton.length) {
$copyButton.on('click', () => {
$copyButton.on('click', e => {
e.preventDefault();
const $textarea = $('#text');
$textarea.select();
document.execCommand('copy');
$copyButton.text($copyButton.data('copied-text'));
});
}

// Cropper.
const img = document.getElementById('source-image'),
x = document.querySelector('[name="crop[x]"]'),
y = document.querySelector('[name="crop[y]"]'),
width = document.querySelector('[name="crop[width]"]'),
height = document.querySelector('[name="crop[height]"]');
new Cropper(img, {
viewMode: 2,
// Only show a crop area if it is defined.
autoCrop: width.value > 0 && height.value > 0,
responsive: true,
ready () {
// Make textarea match height of image.
$('#text').css({
height: this.cropper.getContainerData().height,
});
},
data: {
x: Number.parseFloat(x.value),
y: Number.parseFloat(y.value),
width: Number.parseFloat(width.value),
height: Number.parseFloat(height.value),
},
crop(event) {
x.value = Math.round(event.detail.x);
y.value = Math.round(event.detail.y);
width.value = Math.round(event.detail.width);
height.value = Math.round(event.detail.height);
}
});
var $ocrOutputDiv = $('.ocr-output');
if ($ocrOutputDiv.length) {
// Cropper.
const img = document.getElementById('source-image'),
x = document.querySelector('[name="crop[x]"]'),
y = document.querySelector('[name="crop[y]"]'),
width = document.querySelector('[name="crop[width]"]'),
height = document.querySelector('[name="crop[height]"]'),
$modeButtons = $('.drag-mode');
new Cropper(img, {
viewMode: 2,
dragMode: 'move',
// Remove double-click drag mode toggling, because we've got buttons for that.
toggleDragModeOnDblclick: false,
// Only show a crop area if it is defined.
autoCrop: width.value > 0 && height.value > 0,
responsive: true,
ready () {
// Make textarea match height of image.
$('#text').css({
height: this.cropper.getContainerData().height,
});
// React to changes in the crop-mode buttons.
$modeButtons.on( 'click', event => {
const $button = $(event.currentTarget);
$modeButtons.removeClass('active');
$button.addClass('active');
this.cropper.setDragMode($button.data('drag-mode'));
});
},
data: {
x: Number.parseFloat(x.value),
y: Number.parseFloat(y.value),
width: Number.parseFloat(width.value),
height: Number.parseFloat(height.value),
},
crop(event) {
x.value = Math.round(event.detail.x);
y.value = Math.round(event.detail.y);
width.value = Math.round(event.detail.width);
height.value = Math.round(event.detail.height);
// Enable the cropping buttons. No need to disable them ever because there's no way to remove the crop box.
$('.btn.submit-crop').attr('disabled', false).removeClass('disabled');
$modeButtons.attr('disabled', false);
}
});

// When setting a new image URL, remove the preview and the crop dimensions.
$('[name=image]').on('change', e => {
$('.ocr-output').remove();
x.value = null;
y.value = null;
width.value = null;
height.value = null;
});
// When setting a new image URL, remove the preview and the crop dimensions.
$('[name=image]').on('change', e => {
$ocrOutputDiv.remove();
});

// When submitting the main 'transcribe' button, do not send crop dimensions.
$('.submit-btn .btn').on('click', e => {
x.value = null;
y.value = null;
width.value = null;
height.value = null;
});
}
});
1 change: 1 addition & 0 deletions assets/images/Crop_-_The_Noun_Project.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions assets/images/OOjs_UI_icon_move.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 7 additions & 4 deletions assets/styles/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,11 @@ fieldset,
margin-top: 40px;
}

.copy-button {
position: absolute;
right: 30px;
top: 15px;
.output-buttons {
text-align: right;
margin-bottom: 10px;
}

.nojs .nojs-hide {
display: none;
}
10 changes: 8 additions & 2 deletions i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@
"engine-name-google": "Google Cloud Vision OCR",
"engine-name-tesseract": "Tesseract OCR",
"engine-not-found-warning": "The requested engine '$1' was not found. Using the default engine '$2' instead.",
"engine-invalid-langs-warning": "The following languages are invalid or not supported by the engine and were ignored: $1",
"submit": "Transcribe",
"engine-invalid-langs-warning": "The following languages are invalid or not supported by the engine and were ignored: $1",
"submit": "Transcribe whole page",
"submit-crop": "Transcribe area",
"drag-help": "Select the crop tool and drag a rectangle on the image below to transcribe only one area of the page.",
"drag-mode-move": "Dragging will move the image",
"drag-mode-move-alt": "Icon representing the 'move' action.",
"drag-mode-crop": "Dragging will create a new crop area",
"drag-mode-crop-alt": "Icon representing the 'crop' action.",
"copy-to-clipboard": "Copy to clipboard",
"copied-to-clipboard": "Copied!",
"google-error": "The Google service returned an error: $1",
Expand Down
8 changes: 7 additions & 1 deletion i18n/qqq.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@
"engine-name-tesseract": "Option text for the 'tesseract' engine.",
"engine-not-found-warning": "Error message displayed when an invalid engine is requested.\n\nParameter:\n* $1 – the requested engine.\n* $2 – the default engine that is being used instead.",
"engine-invalid-langs-warning": "Warning message displayed when invalid or unsupported languages are requested.\n\nParameter:\n* $1 – a comma-separated list of invalid languages.",
"submit": "Submit button text.\n{{identical|Go}}",
"submit": "Submit button text for when the whole image is being submitted.",
"submit-crop": "Submit button text for when the image is being cropped.",
"drag-help": "Instructional sentence above the image and cropping buttons.",
"drag-mode-move": "Tooltip text for the 'move' mode radio button.",
"drag-mode-move-alt": "Alt text for the 'move' icon.",
"drag-mode-crop": "Tooltip text for the 'crop' mode radio button.",
"drag-mode-crop-alt": "Alt text for the 'crop' icon.",
"copy-to-clipboard": "Button text for the copy-to-clipboard button.",
"copied-to-clipboard": "Button text shown after the copy-to-clipboard button is clicked.",
"google-error": "Error message prefix for the Google engine.\n\nParameter:\n* $1 – the error message.",
Expand Down
2 changes: 1 addition & 1 deletion templates/base.html.twig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="{{ lang() }}">
<html lang="{{ lang() }}" class="nojs">
<head>
<meta charset="UTF-8">
<title>{% block title %}{{ msg('title') }}{% endblock %}</title>
Expand Down
58 changes: 40 additions & 18 deletions templates/output.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -39,28 +39,50 @@
{% include '_tesseract_options.html.twig' with {engine: engine} %}

<div class="form-group submit-btn">
<input type="hidden" name="crop[x]" value="{% if crop.x is defined %}{{ crop.x }}{% endif %}" />
<input type="hidden" name="crop[y]" value="{% if crop.y is defined %}{{ crop.y }}{% endif %}" />
<input type="hidden" name="crop[width]" value="{% if crop.width is defined %}{{ crop.width }}{% endif %}" />
<input type="hidden" name="crop[height]" value="{% if crop.height is defined %}{{ crop.height }}{% endif %}" />
<input type="submit" value="{{ msg( 'submit' ) }}" class="btn btn-info" />
</div>
</form>

{% if text is defined %}
<div class="ocr-output">
<hr/>
<div class="row">
<div class="col-md-6">
<div><!-- This div is required for Cropper.js -->
<img id="source-image" class="img-responsive" src="{{ app.request.get('image') }}" alt="{{ msg('img-alt-text') }}" />
{% if text is defined %}
<div class="ocr-output">
<hr/>
<p class="nojs-hide">{{ msg('drag-help') }}</p>
<div class="row">
<div class="col-md-6">
<div class="output-buttons nojs-hide">
<div class="btn-group" role="group">
<button type="button" class="btn btn-default drag-mode drag-mode-crop"
title="{{ msg('drag-mode-crop') }}" data-drag-mode="crop">
<img src="{{ asset('build/images/Crop_-_The_Noun_Project.svg') }}" height="20" width="20"
alt="{{ msg('drag-mode-crop-alt') }}" />
</button>
<button type="button" class="btn btn-default drag-mode drag-mode-move active"
title="{{ msg('drag-mode-move') }}" data-drag-mode="move">
<img src="{{ asset('build/images/OOjs_UI_icon_move.svg') }}" height="20" width="20"
alt="{{ msg('drag-mode-move-alt') }}" />
</button>
</div>

<input type="submit" value="{{ msg( 'submit-crop' ) }}" class="submit-crop btn btn-info disabled" disabled />

<input type="hidden" name="crop[x]" value="{% if crop.x is defined %}{{ crop.x }}{% endif %}" />
<input type="hidden" name="crop[y]" value="{% if crop.y is defined %}{{ crop.y }}{% endif %}" />
<input type="hidden" name="crop[width]" value="{% if crop.width is defined %}{{ crop.width }}{% endif %}" />
<input type="hidden" name="crop[height]" value="{% if crop.height is defined %}{{ crop.height }}{% endif %}" />
</div>
<div><!-- This div is required for Cropper.js -->
<img id="source-image" class="img-responsive" src="{{ app.request.get('image') }}" alt="{{ msg('img-alt-text') }}" />
</div>
</div>
<div class="col-md-6">
<div class="output-buttons nojs-hide">
<button class="btn btn-info copy-button" data-copied-text="{{ msg('copied-to-clipboard') }}">{{ msg( 'copy-to-clipboard' ) }}</button>
</div>
<textarea class="form-control" rows="{{ text|textarea_rows }}" id="text">{{ text }}</textarea>
</div>
</div>
<div class="col-md-6">
<button class="btn btn-info copy-button" data-copied-text="{{ msg('copied-to-clipboard') }}">{{ msg( 'copy-to-clipboard' ) }}</button>
<textarea class="form-control" rows="{{ text|textarea_rows }}" id="text">{{ text }}</textarea>
</div>
</div>
</div>
{% endif %}
{% endif %}

</form>

{% endblock %}

0 comments on commit 618d8bf

Please sign in to comment.