mirror of
https://github.com/stashapp/CommunityScripts.git
synced 2026-04-17 09:33:01 -05:00
Merge pull request #191 from feederbox826/main
Add CropperJS and Scene Cropper as JS plugins
This commit is contained in:
11
plugins/4. CropperJS/CropperJS.yml
Normal file
11
plugins/4. CropperJS/CropperJS.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
name: Cropper.JS
|
||||
description: Exports cropper.js functionality for JS/Userscripts
|
||||
version: 1.6.1
|
||||
ui:
|
||||
css:
|
||||
- cropper.css
|
||||
javascript:
|
||||
- cropper.js
|
||||
|
||||
# note - not minimized for more transparency around updates & diffs against source code
|
||||
# https://github.com/fengyuanchen/cropperjs/tree/main/dist
|
||||
308
plugins/4. CropperJS/cropper.css
Normal file
308
plugins/4. CropperJS/cropper.css
Normal file
@@ -0,0 +1,308 @@
|
||||
/*!
|
||||
* Cropper.js v1.6.1
|
||||
* https://fengyuanchen.github.io/cropperjs
|
||||
*
|
||||
* Copyright 2015-present Chen Fengyuan
|
||||
* Released under the MIT license
|
||||
*
|
||||
* Date: 2023-09-17T03:44:17.565Z
|
||||
*/
|
||||
|
||||
.cropper-container {
|
||||
direction: ltr;
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.cropper-container img {
|
||||
backface-visibility: hidden;
|
||||
display: block;
|
||||
height: 100%;
|
||||
image-orientation: 0deg;
|
||||
max-height: none !important;
|
||||
max-width: none !important;
|
||||
min-height: 0 !important;
|
||||
min-width: 0 !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cropper-wrap-box,
|
||||
.cropper-canvas,
|
||||
.cropper-drag-box,
|
||||
.cropper-crop-box,
|
||||
.cropper-modal {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.cropper-wrap-box,
|
||||
.cropper-canvas {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cropper-drag-box {
|
||||
background-color: #fff;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.cropper-modal {
|
||||
background-color: #000;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.cropper-view-box {
|
||||
display: block;
|
||||
height: 100%;
|
||||
outline: 1px solid #39f;
|
||||
outline-color: rgba(51, 153, 255, 0.75);
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cropper-dashed {
|
||||
border: 0 dashed #eee;
|
||||
display: block;
|
||||
opacity: 0.5;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.cropper-dashed.dashed-h {
|
||||
border-bottom-width: 1px;
|
||||
border-top-width: 1px;
|
||||
height: calc(100% / 3);
|
||||
left: 0;
|
||||
top: calc(100% / 3);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cropper-dashed.dashed-v {
|
||||
border-left-width: 1px;
|
||||
border-right-width: 1px;
|
||||
height: 100%;
|
||||
left: calc(100% / 3);
|
||||
top: 0;
|
||||
width: calc(100% / 3);
|
||||
}
|
||||
|
||||
.cropper-center {
|
||||
display: block;
|
||||
height: 0;
|
||||
left: 50%;
|
||||
opacity: 0.75;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.cropper-center::before,
|
||||
.cropper-center::after {
|
||||
background-color: #eee;
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.cropper-center::before {
|
||||
height: 1px;
|
||||
left: -3px;
|
||||
top: 0;
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
.cropper-center::after {
|
||||
height: 7px;
|
||||
left: 0;
|
||||
top: -3px;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.cropper-face,
|
||||
.cropper-line,
|
||||
.cropper-point {
|
||||
display: block;
|
||||
height: 100%;
|
||||
opacity: 0.1;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cropper-face {
|
||||
background-color: #fff;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.cropper-line {
|
||||
background-color: #39f;
|
||||
}
|
||||
|
||||
.cropper-line.line-e {
|
||||
cursor: ew-resize;
|
||||
right: -3px;
|
||||
top: 0;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.cropper-line.line-n {
|
||||
cursor: ns-resize;
|
||||
height: 5px;
|
||||
left: 0;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
.cropper-line.line-w {
|
||||
cursor: ew-resize;
|
||||
left: -3px;
|
||||
top: 0;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.cropper-line.line-s {
|
||||
bottom: -3px;
|
||||
cursor: ns-resize;
|
||||
height: 5px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.cropper-point {
|
||||
background-color: #39f;
|
||||
height: 5px;
|
||||
opacity: 0.75;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.cropper-point.point-e {
|
||||
cursor: ew-resize;
|
||||
margin-top: -3px;
|
||||
right: -3px;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.cropper-point.point-n {
|
||||
cursor: ns-resize;
|
||||
left: 50%;
|
||||
margin-left: -3px;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
.cropper-point.point-w {
|
||||
cursor: ew-resize;
|
||||
left: -3px;
|
||||
margin-top: -3px;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.cropper-point.point-s {
|
||||
bottom: -3px;
|
||||
cursor: s-resize;
|
||||
left: 50%;
|
||||
margin-left: -3px;
|
||||
}
|
||||
|
||||
.cropper-point.point-ne {
|
||||
cursor: nesw-resize;
|
||||
right: -3px;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
.cropper-point.point-nw {
|
||||
cursor: nwse-resize;
|
||||
left: -3px;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
.cropper-point.point-sw {
|
||||
bottom: -3px;
|
||||
cursor: nesw-resize;
|
||||
left: -3px;
|
||||
}
|
||||
|
||||
.cropper-point.point-se {
|
||||
bottom: -3px;
|
||||
cursor: nwse-resize;
|
||||
height: 20px;
|
||||
opacity: 1;
|
||||
right: -3px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
|
||||
.cropper-point.point-se {
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
|
||||
.cropper-point.point-se {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
|
||||
.cropper-point.point-se {
|
||||
height: 5px;
|
||||
opacity: 0.75;
|
||||
width: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.cropper-point.point-se::before {
|
||||
background-color: #39f;
|
||||
bottom: -50%;
|
||||
content: ' ';
|
||||
display: block;
|
||||
height: 200%;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: -50%;
|
||||
width: 200%;
|
||||
}
|
||||
|
||||
.cropper-invisible {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.cropper-bg {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC');
|
||||
}
|
||||
|
||||
.cropper-hide {
|
||||
display: block;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.cropper-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.cropper-move {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.cropper-crop {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.cropper-disabled .cropper-drag-box,
|
||||
.cropper-disabled .cropper-face,
|
||||
.cropper-disabled .cropper-line,
|
||||
.cropper-disabled .cropper-point {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
3274
plugins/4. CropperJS/cropper.js
Normal file
3274
plugins/4. CropperJS/cropper.js
Normal file
File diff suppressed because it is too large
Load Diff
145
plugins/sceneCoverCropper/sceneCoverCropper.js
Normal file
145
plugins/sceneCoverCropper/sceneCoverCropper.js
Normal file
@@ -0,0 +1,145 @@
|
||||
// By ScruffyNerf
|
||||
// Ported by feederbox826
|
||||
|
||||
(function () {
|
||||
let cropping = false;
|
||||
let cropper = null;
|
||||
|
||||
try {
|
||||
const img = document.createElement('img');
|
||||
new Cropper(img)
|
||||
} catch (e) {
|
||||
console.error("Cropper not loaded - please install 4. CropperJS from CommunityScripts")
|
||||
}
|
||||
try {
|
||||
stash.getVersion()
|
||||
} catch (e) {
|
||||
console.error("Stash not loaded - please install 1. stashUserscriptLibrary from CommunityScripts")
|
||||
}
|
||||
|
||||
function setupCropper() {
|
||||
const cropBtnContainerId = "crop-btn-container";
|
||||
if (document.getElementById(cropBtnContainerId)) return
|
||||
const sceneId = window.location.pathname.replace('/scenes/', '').split('/')[0];
|
||||
const sceneImage = document.querySelector("img.scene-cover")
|
||||
|
||||
var cropperModal = document.createElement("dialog");
|
||||
cropperModal.style.width = "90%";
|
||||
cropperModal.style.border = "none";
|
||||
cropperModal.classList.add('bg-dark');
|
||||
document.body.appendChild(cropperModal);
|
||||
|
||||
var cropperContainer = document.createElement("div");
|
||||
cropperContainer.style.width = "100%";
|
||||
cropperContainer.style.height = "auto";
|
||||
cropperContainer.style.margin = "auto";
|
||||
cropperModal.appendChild(cropperContainer);
|
||||
|
||||
var image = sceneImage.cloneNode();
|
||||
image.style.display = "block";
|
||||
image.style.maxWidth = "100%";
|
||||
cropperContainer.appendChild(image);
|
||||
|
||||
var cropBtnContainer = document.createElement('div');
|
||||
cropBtnContainer.setAttribute("id", cropBtnContainerId);
|
||||
cropBtnContainer.classList.add('d-flex','flex-row','justify-content-center','align-items-center');
|
||||
cropBtnContainer.style.gap = "10px";
|
||||
cropperModal.appendChild(cropBtnContainer);
|
||||
|
||||
|
||||
sceneImage.parentElement.parentElement.style.flexFlow = 'column';
|
||||
|
||||
const cropInfo = document.createElement('p');
|
||||
cropInfo.style.all = "revert";
|
||||
cropInfo.classList.add('text-white');
|
||||
|
||||
const cropStart = document.createElement('button');
|
||||
cropStart.setAttribute("id", "crop-start");
|
||||
cropStart.classList.add('btn', 'btn-primary');
|
||||
cropStart.innerText = 'Crop Image';
|
||||
cropStart.addEventListener('click', evt => {
|
||||
cropping = true;
|
||||
cropStart.style.display = 'none';
|
||||
cropCancel.style.display = 'inline-block';
|
||||
|
||||
//const isVertical = image.naturalHeight > image.naturalWidth;
|
||||
//const aspectRatio = isVertical ? 3/2 : NaN
|
||||
const aspectRatio = NaN
|
||||
|
||||
cropper = new Cropper(image, {
|
||||
viewMode: 1,
|
||||
initialAspectRatio: aspectRatio,
|
||||
movable: false,
|
||||
rotatable: false,
|
||||
scalable: false,
|
||||
zoomable: false,
|
||||
zoomOnTouch: false,
|
||||
zoomOnWheel: false,
|
||||
ready() {
|
||||
cropAccept.style.display = 'inline-block';
|
||||
},
|
||||
crop(e) {
|
||||
cropInfo.innerText = `X: ${Math.round(e.detail.x)}, Y: ${Math.round(e.detail.y)}, Width: ${Math.round(e.detail.width)}px, Height: ${Math.round(e.detail.height)}px`;
|
||||
}
|
||||
});
|
||||
cropperModal.showModal();
|
||||
});
|
||||
sceneImage.parentElement.appendChild(cropStart);
|
||||
|
||||
const cropAccept = document.createElement('button');
|
||||
cropAccept.setAttribute("id", "crop-accept");
|
||||
cropAccept.classList.add('btn', 'btn-success', 'mr-2');
|
||||
cropAccept.innerText = 'OK';
|
||||
cropAccept.addEventListener('click', async evt => {
|
||||
cropping = false;
|
||||
cropStart.style.display = 'inline-block';
|
||||
cropAccept.style.display = 'none';
|
||||
cropCancel.style.display = 'none';
|
||||
cropInfo.innerText = '';
|
||||
|
||||
const reqData = {
|
||||
"operationName": "SceneUpdate",
|
||||
"variables": {
|
||||
"input": {
|
||||
"cover_image": cropper.getCroppedCanvas().toDataURL(),
|
||||
"id": sceneId
|
||||
}
|
||||
},
|
||||
"query": `mutation SceneUpdate($input: SceneUpdateInput!) {
|
||||
sceneUpdate(input: $input) {
|
||||
id
|
||||
}
|
||||
}`
|
||||
}
|
||||
await stash.callGQL(reqData);
|
||||
reloadImg(image.src);
|
||||
cropper.destroy();
|
||||
cropperModal.close("cropAccept");
|
||||
});
|
||||
cropBtnContainer.appendChild(cropAccept);
|
||||
|
||||
const cropCancel = document.createElement('button');
|
||||
cropCancel.setAttribute("id", "crop-accept");
|
||||
cropCancel.classList.add('btn', 'btn-danger');
|
||||
cropCancel.innerText = 'Cancel';
|
||||
cropCancel.addEventListener('click', evt => {
|
||||
cropping = false;
|
||||
cropStart.style.display = 'inline-block';
|
||||
cropAccept.style.display = 'none';
|
||||
cropCancel.style.display = 'none';
|
||||
cropInfo.innerText = '';
|
||||
|
||||
cropper.destroy();
|
||||
cropperModal.close("cropCancel");
|
||||
});
|
||||
cropBtnContainer.appendChild(cropCancel);
|
||||
cropAccept.style.display = 'none';
|
||||
cropCancel.style.display = 'none';
|
||||
|
||||
cropBtnContainer.appendChild(cropInfo);
|
||||
}
|
||||
|
||||
stash.addEventListener('page:scene', function () {
|
||||
waitForElementId('scene-edit-details', setupCropper);
|
||||
});
|
||||
})();
|
||||
7
plugins/sceneCoverCropper/sceneCoverCropper.yml
Normal file
7
plugins/sceneCoverCropper/sceneCoverCropper.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
name: Scene Cover Cropper
|
||||
description: Crop Scene Cover Images
|
||||
version: 1.0
|
||||
ui:
|
||||
css:
|
||||
javascript:
|
||||
- sceneCoverCropper.js
|
||||
Reference in New Issue
Block a user