Browse Source

Implement cancel / redo operations

main
Tissevert 5 months ago
parent
commit
303c4dcf19
14 changed files with 192 additions and 76 deletions
  1. +4
    -0
      css/toolbar.css
  2. +0
    -4
      css/toolbar/zoom.css
  3. +10
    -0
      index.html
  4. +30
    -51
      js/GUI.js
  5. +62
    -0
      js/GUI/Editor.js
  6. +25
    -4
      js/GUI/Keys.js
  7. +2
    -0
      js/Main.js
  8. +6
    -5
      js/Scoria.js
  9. +13
    -4
      js/Toolbar/EditMode.js
  10. +15
    -0
      js/Toolbar/Help.js
  11. +1
    -2
      js/Toolbar/Menu.js
  12. +3
    -1
      js/Toolbar/Page.js
  13. +12
    -3
      js/View.js
  14. +9
    -2
      js/XML/ALTO.js

+ 4
- 0
css/toolbar.css View File

@@ -17,3 +17,7 @@
#wcThreshold {
width: 3.5em;
}

#toolbar button {
padding: 0;
}

+ 0
- 4
css/toolbar/zoom.css View File

@@ -1,7 +1,3 @@
#zoom button {
padding: 0;
}

#zoom input[type="number"] {
width: 3em;
}

+ 10
- 0
index.html View File

@@ -17,6 +17,12 @@
<li id="loadScoria" class="disabled">Importer des scories</li>
<li id="saveScoria" class="disabled">Exporter les scories</li>
</ul>
</div><div id="edit" class="menu">
<span>Édition</span>
<ul>
<li id="editCancel" class="disabled">Annuler</li>
<li id="editRedo" class="disabled">Refaire</li>
</ul>
</div><div id="help" class="menu">
<span>Aide</span>
<ul>
@@ -43,6 +49,10 @@
<option value="blockOrder">Ordre des blocs</option>
</select>
</span>
<span>
<button id="cancel" title="Annuler" disabled>⟲</button>
<button id="redo" title="Refaire" disabled>⟳</button>
</span>
<span id="zoom">
<button id="fitWidth" title="Pleine largeur" >⇔</button>
<input type="number" id="zoomAmount" value="100" step="10" min="30" max="250" title="Zoom"/>


+ 30
- 51
js/GUI.js View File

@@ -1,61 +1,40 @@
return {
/* Display */
var windowCatchAll = document.getElementById('windowCatchAll');
var workzone = document.getElementById('workzone');
var page = document.getElementById('page');
var selector = document.getElementById('selector');
windowCatchAll: document.getElementById('windowCatchAll'),
workzone: document.getElementById('workzone'),
page: document.getElementById('page'),
selector: document.getElementById('selector'),

/** Toolbar **/
/* Menus */
var menus = document.getElementsByClassName('menu');
var fileMenu = document.getElementById('file');
var loadALTO = document.getElementById('loadALTO');
var saveALTO = document.getElementById('saveALTO');
var loadScoria = document.getElementById('loadScoria');
var saveScoria = document.getElementById('saveScoria');
var helpMenu = document.getElementById('help');
var documentation = document.getElementById('documentation');
var reposLink = document.getElementById('reposLink');
[documentation, reposLink].forEach(function(elem) {
elem.addEventListener('click', function(e) {
if(e.target.tagName != 'A') {
e.target.children[0].click();
}
});
});

/* Controls */
var fileNumber = document.getElementById('fileNumber');
var fileName = document.getElementById('fileName');
var wcThreshold = document.getElementById('wcThreshold');
var mode = document.getElementById('mode');
var saveScoria = document.getElementById('saveScoria');
menus: document.getElementsByClassName('menu'),
fileMenu: document.getElementById('file'),
loadALTO: document.getElementById('loadALTO'),
saveALTO: document.getElementById('saveALTO'),
loadScoria: document.getElementById('loadScoria'),
saveScoria: document.getElementById('saveScoria'),

/* Zoom */
var zoom = document.getElementById('zoom');
var fitWidth = document.getElementById('fitWidth');
var zoomAmount = document.getElementById('zoomAmount');
var fitHeight = document.getElementById('fitHeight');
editMenu: document.getElementById('edit'),
editCancel: document.getElementById('editCancel'),
editRedo: document.getElementById('editRedo'),

return {
mode: mode,
fileMenu: fileMenu,
fileName: fileName,
fileNumber: fileNumber,
helpMenu: document.getElementById('help'),
documentation: document.getElementById('documentation'),
reposLink: document.getElementById('reposLink'),

loadALTO: loadALTO,
saveALTO: saveALTO,
loadScoria: loadScoria,
saveScoria: saveScoria,
/* Controls */
fileNumber: document.getElementById('fileNumber'),
fileName: document.getElementById('fileName'),
wcThreshold: document.getElementById('wcThreshold'),
mode: document.getElementById('mode'),

menus: menus,
page: page,
selector: selector,
wcThreshold: wcThreshold,
windowCatchAll: windowCatchAll,
workzone: workzone,
/* Cancel / Redo */
cancel: document.getElementById('cancel'),
redo: document.getElementById('redo'),

fitWidth: fitWidth,
zoomAmount: zoomAmount,
fitHeight: fitHeight
/* Zoom */
zoom: document.getElementById('zoom'),
fitWidth: document.getElementById('fitWidth'),
zoomAmount: document.getElementById('zoomAmount'),
fitHeight: document.getElementById('fitHeight')
};

+ 62
- 0
js/GUI/Editor.js View File

@@ -0,0 +1,62 @@
import GUI;
import bind from GUI.Keys;
import toggleWord from Scoria;

var edits = [];
var cursor = -1;

GUI.cancel.addEventListener('click', cancel);
GUI.editCancel.addEventListener('click', cancel);
GUI.redo.addEventListener('click', redo);
GUI.editRedo.addEventListener('click', redo);
bind('Ctrl+z', cancel);
bind('Ctrl+y', redo);

return {
push: push
};

function cancel(e) {
if(cursor >= 0) {
edits[cursor--].wordIds.forEach(function(wordId) {toggleWord(wordId);});
updateButtons();
} else if(e != undefined) {
e.stopPropagation();
}
}

function redo(e) {
if(cursor < edits.length - 1) {
edits[++cursor].wordIds.forEach(function(wordId) {toggleWord(wordId);});
updateButtons();
} else if(e != undefined) {
e.stopPropagation();
}
}

function push(description, wordIds, perform) {
edits.length = ++cursor + 1;
edits[cursor] = {description: description, wordIds: wordIds};
if(perform) {
wordIds.forEach(function(wordId) {toggleWord(wordId);});
}
updateButtons();
}

function setState(action, disabled) {
if(action == 'redo') {
var label = 'Refaire' + (disabled ? '' : ' ' + edits[cursor + 1].description);
} else {
var label = 'Annuler' + (disabled ? '' : ' ' + edits[cursor].description);
}
GUI[action].title = label;
GUI[action].disabled = disabled;
var menuElem = GUI['edit' + action[0].toUpperCase() + action.slice(1)];
menuElem.textContent = label;
menuElem.classList.toggle('disabled', disabled);
}

function updateButtons() {
setState('cancel', cursor < 0);
setState('redo', cursor >= edits.length - 1);
}

+ 25
- 4
js/GUI/Keys.js View File

@@ -1,18 +1,39 @@
var bindings = {};
var bindings = binTree(4);

return {
bind: bind,
init: init
};

function binTree(n) {
if(n == 0) {
return {};
} else {
return {false: binTree(n-1), true: binTree(n-1)};
}
}

function bind(keyCode, f) {
bindings[keyCode] = f;
var components = keyCode.match(/((?:Alt|Ctrl|Meta|Shift)\+)*(.+)$/);
if(components) {
var key = components[2];
var modifiers = components[0].split('+');
bindings
[modifiers.includes('Alt')]
[modifiers.includes('Ctrl')]
[modifiers.includes('Meta')]
[modifiers.includes('Shift')]
[key] = f;
} else {
console.log("Did not add binding for invalid key code '" + keyCode + "'");
}
}

function init() {
window.addEventListener('keydown', function(e) {
if(bindings[e.key] != undefined) {
bindings[e.key]();
var binding = bindings[e.altKey][e.ctrlKey][e.metaKey][e.shiftKey][e.key];
if(binding != undefined) {
binding();
}
});
}

+ 2
- 0
js/Main.js View File

@@ -1,9 +1,11 @@
import * as Keys from GUI.Keys;
import * as EditMode from Toolbar.EditMode;
import * as Help from Toolbar.Help;
import * as Menu from Toolbar.Menu;
import * as Page from Toolbar.Page;

EditMode.sync();
Page.syncNumber();
Help.init();
Menu.init();
Keys.init();

+ 6
- 5
js/Scoria.js View File

@@ -29,10 +29,11 @@ function setScoria(wordId, state) {
}
}

function toggleWord(domElem, forceState) {
if(mode.value == 'edit') {
var state = forceState != undefined ? forceState : !scoriae[domElem.id];
setScoria(domElem.id, state);
domElem.classList.toggle('deleted', state);
function toggleWord(wordId) {
var state = !scoriae[wordId];
setScoria(wordId, state);
var word = document.getElementById(wordId);
if(word != undefined) {
word.classList.toggle('deleted', state);
}
}

+ 13
- 4
js/Toolbar/EditMode.js View File

@@ -1,4 +1,5 @@
import {mode, page, selector, workzone} from GUI;
import GUI.Editor;
import * as Rectangle from Geometry.Rectangle;
import toggleWord from Scoria;

@@ -48,6 +49,7 @@ function moveRectangle(e) {

function endRectangle(e) {
if(mode.value == 'edit') {
recordEdit();
selection = {words: {}, x: {from: null, to: null}, y: {from: null, to: null}};
selector.style.left = selector.style.top = selector.style.width =
selector.style.height = null;
@@ -60,8 +62,8 @@ function updateSelection() {
var rectangle = Rectangle.make(selection);
updateSelected(rectangle, page);
for(var id in selection.words) {
if(!Rectangle.contains(rectangle, selection.words[id].rectangle)) {
toggleWord(selection.words[id].domElem);
if(!Rectangle.contains(rectangle, selection.words[id])) {
toggleWord(id);
delete selection.words[id];
}
}
@@ -82,7 +84,14 @@ function updateSelected(rectangle, domElem) {
function updateWord(rectangle, domElem) {
var elemRectangle = Rectangle.make(domElem);
if(Rectangle.contains(rectangle, elemRectangle) && !selection.words[domElem.id]) {
selection.words[domElem.id] = {rectangle: elemRectangle, domElem: domElem};
toggleWord(domElem);
selection.words[domElem.id] = elemRectangle;
toggleWord(domElem.id);
}
}

function recordEdit() {
var edit = Object.keys(selection.words);
if(edit.length > 0) {
GUI.Editor.push('la sélection rectangulaire de ' + edit.length + ' mots', edit);
}
}

+ 15
- 0
js/Toolbar/Help.js View File

@@ -0,0 +1,15 @@
import {documentation, reposLink} from GUI;

return {
init: init
};

function init() {
[documentation, reposLink].forEach(function(elem) {
elem.addEventListener('click', function(e) {
if(e.target.tagName != 'A') {
e.target.children[0].click();
}
});
});
}

+ 1
- 2
js/Toolbar/Menu.js View File

@@ -41,8 +41,7 @@ function toggleMenu(menu) {
}

function closeMenu(e) {
if(selectedMenu != undefined
&& !(e != undefined && e.target.classList.contains('disabled'))) {
if(selectedMenu != undefined) {
selectedMenu.classList.remove('open');
selectedMenu = null;
windowCatchAll.className = '';


+ 3
- 1
js/Toolbar/Page.js View File

@@ -26,7 +26,7 @@ GUI.loadALTO.addEventListener('click', function() {
);
});

GUI.saveALTO.addEventListener('click', function() {
GUI.saveALTO.addEventListener('click', function(e) {
if(!GUI.saveALTO.classList.contains('disabled')) {
var file = files[GUI.fileNumber.value];
Async.run(
@@ -39,6 +39,8 @@ GUI.saveALTO.addEventListener('click', function() {
})
)
);
} else {
e.stopPropagation();
}
});



+ 12
- 3
js/View.js View File

@@ -1,5 +1,6 @@
import File;
import GUI;
import push from GUI.Editor;
import * as Keys from GUI.Keys;
import Scoria;
import * as Zoom from Toolbar.Zoom;
@@ -53,7 +54,7 @@ function scale(o) {
}
}

function importScoria() {
function importScoria(e) {
if(!GUI.loadScoria.classList.contains('disabled')) {
Async.run(
Async.bind(
@@ -69,21 +70,29 @@ function importScoria() {
Async.map(refresh)
)
);
} else {
e.stopPropagation();
}
}

function setFromFiles(files) {
var edit = [];
files.forEach(function(file) {
file.split('\n').slice(1).forEach(function(line) {
var wordIds = file.split('\n').slice(1);
wordIds.forEach(function(line) {
Scoria.setScoria(line);
});
edit = edit.concat(wordIds);
});
push('le chargement des ' + files.length + ' fichiers de scories', edit);
}

function exportScoria() {
function exportScoria(e) {
if(!GUI.saveScoria.classList.contains('disabled')) {
var column = ['ID'].concat(Scoria.getScoriae());
var data = 'data:text/csv,' + encodeURIComponent(column.join('\n'));
File.save(data, 'scoria.csv');
} else {
e.stopPropagation();
}
}

+ 9
- 2
js/XML/ALTO.js View File

@@ -1,7 +1,8 @@
import * as Element from XML.ALTO.Element;
import {isScoria, toggleWord} from Scoria;
import isScoria from Scoria;
import Quality;
import GUI;
import push from GUI.Editor;
import * as Dom from UnitJS.Dom;
import * as Zoom from Toolbar.Zoom;

@@ -46,7 +47,13 @@ function setAttributes(element, attributes) {
attributes.class = attributes.class
.concat(Quality.isLow(element.get('WC')) ? 'lowQuality' : [])
.concat(isScoria(element.get('ID')) ? 'deleted' : []);
attributes.onClick = function(e) {toggleWord(e.target)};
attributes.onClick = function(e) {
if(GUI.mode.value == 'edit') {
var word = e.target;
var action = isScoria(word.id) ? "l'ajout" : "la suppression";
push(action + ' du mot «' + word.textContent + '»', [word.id], true);
}
};
}

function blockPositionElem() {


Loading…
Cancel
Save