raw => <<'EOT',
<link rel="stylesheet" type="text/css" media="all" href="/word/editor.css" />
<script src="/word/editor.js"></script>
-<script src="https://cdnjs.cloudflare.com/ajax/libs/croppie/2.6.5/croppie.min.js"></script>
-<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/croppie/2.6.5/croppie.min.css" />
EOT
});
printf '<span class=inline>';
print $row->input($col => $attr);
if (my $imgsrc = $attr->{-src}) {
- printf('<img id="%spreview" src="/%s" alt="%s"%s />',
+ printf('<span id="%spreview"><img src="/%s" alt="%s"%s /></span>',
$col, $_, $row->{form}, $col eq 'source' && ' hidden'
) for grep { -e } $imgsrc->($row);
}
let thumbpreview = document.getElementById('convertpreview');
if (thumbpreview && imgpreview) {
thumbpreview.onclick = e => {
+ thumbpreview.onclick = null; // setup once
const cropinput = document.getElementById('crop32');
- let border = { width: 600, height: 400 };
- let crop = new Croppie(thumbpreview, {
- boundary: { width: border.width * 1.3, height: border.height * 1.2 },
- viewport: border,
- update: e => {
- cropinput.value = e.points.map((pos, axis) => {
- Math.round(1000 * pos / (axis % 2 ? border.height : border.width))
- }).join(',');
- },
- });
- crop.bind({
- url: imgpreview.src,
- points: cropinput.value.split(/[^0-9]/).map((rel, axis) => {
- return rel * (axis % 2 ? border.height : border.width) / 1000
- }),
- });
- crop.elements.overlay.addEventListener('dblclick', e => {
- crop.destroy();
- });
+ const imgselect = thumbpreview.children[0];
+ const border = [300,200];
+ let crop = cropinput.value.split(/[^0-9]/).map(pos => pos / 1000);
+ let scale = 1 / (crop[2] - crop[0]) || 1;
+ crop.push(0); // defined y dimension
+ crop.splice(2); // end coordinates applied to zoom
+ crop = crop.map((rel, axis) => rel * border[axis % 2] * scale);
+
+ let drag = false;
+ function applydrag(e) {
+ const pos = [e.pageX, e.pageY];
+ if (drag) {
+ // apply drag delta to crop position
+ crop[0] += drag[0] - pos[0];
+ crop[1] += drag[1] - pos[1];
+ recrop();
+ }
+ drag = pos;
+ }
+ function recrop() {
+ [0, 1].forEach(axis => {
+ if (crop[axis] > border[axis] * (scale - 1)) {
+ crop[axis] = border[axis] * (scale - 1);
+ }
+ if (crop[axis] < 0) {
+ crop[axis] = 0;
+ }
+ });
+ imgselect.style.left = -crop[0]+'px';
+ imgselect.style.top = -crop[1]+'px';
+ imgselect.style.width = (scale * 100)+'%';
+ cropinput.value = [
+ crop[0] / border[0], crop[1] / border[1],
+ crop[0] / border[0] + 1, crop[1] / border[1] + 1,
+ ].map(pos => Math.round(1000 * pos / scale));
+ }
+
+ imgselect.src = imgpreview.children[0].src;
+ imgselect.style.position = 'absolute';
+ recrop();
+
+ imgselect.onmousedown = e => {
+ e.preventDefault();
+ applydrag(e);
+ };
+ imgselect.onmouseup = e => {
+ e.preventDefault();
+ drag = false;
+ };
+ imgselect.onmousemove = e => {
+ if (!drag) return;
+ applydrag(e);
+ };
+ imgselect.onwheel = e => {
+ e.preventDefault();
+ let zoom = e.deltaY * -.05 * scale;
+ if (scale + zoom < 1) {
+ zoom = 1 - scale; // scale = 1
+ }
+ [0, 1].forEach(axis => {
+ // same area center at altered scale
+ crop[axis] += (crop[axis] + border[axis] / 2) / scale * zoom;
+ });
+ scale += zoom;
+ recrop();
+ };
};
}