Second pass at adding key files
This commit is contained in:
438
site/forum/editor_image.html
Normal file
438
site/forum/editor_image.html
Normal file
@ -0,0 +1,438 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<%~set page_mode = $in.editor_type%><%if not page_mode or page_mode ne message%><%set page_mode = 'post'%><%endif%>
|
||||
<html>
|
||||
<head>
|
||||
<title>Insert Image</title>
|
||||
<link type="text/css" rel="stylesheet" media="screen" href="<%static_url%>/editor/editor_dialog.css">
|
||||
<script type="text/javascript" src="<%static_url%>/js/utils.js"></script>
|
||||
<script type="text/javascript" src="<%static_url%>/editor/editor_dialog.js"></script>
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var src = {}, activeSource, sourceTypes = ['url', 'file', 'inline'];
|
||||
var inlineImages = [];
|
||||
var inlines = [];
|
||||
if (window.opener.getInlineAttachments)
|
||||
inlines = opener.getInlineAttachments();
|
||||
<%~if attachment_uploaded%>
|
||||
inlines.push(['<%escape_js attachment_uploaded%>', 'temp', '<%attachment_uploaded_id%>']);
|
||||
<%~endif%>
|
||||
|
||||
function checkSource(submitting) {
|
||||
if (activeSource && src[activeSource + '-input'] && src[activeSource + '-input'].value && src[activeSource + '-input'].value.match(/\S/)) {
|
||||
submit.disabled = false;
|
||||
if (submitting) {
|
||||
if (activeSource == 'file')
|
||||
return true;
|
||||
else
|
||||
submitWrapper();
|
||||
}
|
||||
}
|
||||
else
|
||||
submit.disabled = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function changeElement() {
|
||||
activeSource = false;
|
||||
for (var i = 0; i < sourceTypes.length; i++) {
|
||||
if (src[sourceTypes[i]] && src[sourceTypes[i]].checked) {
|
||||
activeSource = sourceTypes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!activeSource) return;
|
||||
|
||||
for (var i = 0; i < sourceTypes.length; i++) {
|
||||
if (src[sourceTypes[i]])
|
||||
src[sourceTypes[i] + '-row'].style.display = activeSource == sourceTypes[i] ? 'block' : 'none';
|
||||
}
|
||||
|
||||
if (document.getElementById('src-file'))
|
||||
{
|
||||
if (document.getElementById('src-file').checked) {
|
||||
document.getElementById('src-file-size-slider').style.display = 'flex';
|
||||
document.getElementById('src-file-quality-slider').style.display = 'flex';
|
||||
document.getElementById('src-file-size-orig').style.display = 'block';
|
||||
document.getElementById('src-file-size-resize').style.display = 'block';
|
||||
}
|
||||
else {
|
||||
document.getElementById('src-file-size-slider').style.display = 'none';
|
||||
document.getElementById('src-file-quality-slider').style.display = 'none';
|
||||
document.getElementById('src-file-size-orig').style.display = 'none';
|
||||
document.getElementById('src-file-size-resize').style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
<%~if attachment_uploaded%>
|
||||
// The upload succeeded: make the parent page refresh the attachment list
|
||||
if (opener.refreshAttachmentList)
|
||||
opener.refreshAttachmentList();
|
||||
|
||||
<%~endif%>
|
||||
|
||||
// We need to copy the temp_id from the parent document.
|
||||
document.getElementById('temp_id').value = opener.document.getElementById('temp_id').value ? opener.document.getElementById('temp_id').value : "foo";
|
||||
|
||||
// Also copy over forum_id, parent_post_id, and post_id, if they exist.
|
||||
var hidden = [opener.document.getElementById('forum_id'), opener.document.getElementById('parent_post_id'), opener.document.getElementById('post_id')];
|
||||
for (var i = 0; i < hidden.length; i++) {
|
||||
if (hidden[i]) {
|
||||
var h = document.createElement('input');
|
||||
h.type = 'hidden';
|
||||
h.name = hidden[i].name;
|
||||
h.value = hidden[i].value;
|
||||
document.getElementById('image_form').appendChild(h);
|
||||
}
|
||||
}
|
||||
|
||||
var eventType = isIE ? 'propertychange' : 'change';
|
||||
for (var i = 0; i < sourceTypes.length; i++) {
|
||||
var sourceType = sourceTypes[i];
|
||||
src[sourceType] = document.getElementById('src-' + sourceType);
|
||||
src[sourceType + '-input'] = document.getElementById('src-' + sourceType + '-input');
|
||||
src[sourceType + '-label'] = document.getElementById('src-' + sourceType + '-label');
|
||||
src[sourceType + '-value'] = document.getElementById('src-' + sourceType + '-value');
|
||||
src[sourceType + '-row'] = document.getElementById('src-' + sourceType + '-row');
|
||||
if (src[sourceType]) registerEvent(src[sourceType], eventType, changeElement);
|
||||
}
|
||||
|
||||
if (inlines.length > 0) {
|
||||
for (i = 0; i < inlines.length; i++) {
|
||||
|
||||
var filename = inlines[i][0];
|
||||
var inlineRadio;
|
||||
// Hack around a weird IE bug where setting the name attribute
|
||||
// below results in the radio button not showing its state properly
|
||||
try {
|
||||
inlineRadio = document.createElement('<input type="radio" name="inline_image" />');
|
||||
}
|
||||
catch (e) {
|
||||
inlineRadio = document.createElement('input');
|
||||
}
|
||||
inlineRadio.type = 'radio';
|
||||
inlineRadio.name = 'inline_image';
|
||||
inlineRadio.id = 'inline_image' + i;
|
||||
inlineRadio.value = filename;
|
||||
<%~if attachment_uploaded%>
|
||||
if (filename == '<%escape_js attachment_uploaded%>') {
|
||||
inlineRadio.checked = true;
|
||||
src['inline-input'].value = filename;
|
||||
}
|
||||
<%~endif%>
|
||||
var inlineLabel = document.createElement('label');
|
||||
inlineLabel.appendChild(document.createTextNode(filename));
|
||||
inlineLabel.htmlFor = 'inline_image' + i;
|
||||
var box = document.createElement('span');
|
||||
box.appendChild(inlineRadio);
|
||||
box.appendChild(inlineLabel);
|
||||
src['inline-value'].appendChild(box);
|
||||
inlineImages[inlineImages.length] = inlineRadio;
|
||||
registerEvent(inlineRadio, 'click', function(radioBox) { return function () { src['inline-input'].value = radioBox.value; }; }(inlineRadio));
|
||||
}
|
||||
}
|
||||
else {
|
||||
src['inline'].parentNode.removeChild(src['inline']);
|
||||
src['inline-label'].parentNode.removeChild(src['inline-label']);
|
||||
src['inline-row'].parentNode.removeChild(src['inline-row']);
|
||||
delete src['inline'];
|
||||
delete src['inline-label'];
|
||||
delete src['inline-row'];
|
||||
}
|
||||
|
||||
if (!opener.canAttach) {
|
||||
src['file'].parentNode.removeChild(src['file']);
|
||||
src['file-label'].parentNode.removeChild(src['file-label']);
|
||||
src['file-row'].parentNode.removeChild(src['file-row']);
|
||||
delete src['file'];
|
||||
delete src['file-label'];
|
||||
delete src['file-row'];
|
||||
}
|
||||
|
||||
submit = document.getElementById('submit');
|
||||
|
||||
changeElement();
|
||||
|
||||
setInterval(function () { checkSource() }, 250);
|
||||
if (src['url'] && src['url'].checked) document.getElementById('src-url-input').focus();
|
||||
|
||||
<%~if error_loop.length%>
|
||||
alert('Error:\n<%loop error_loop%><%escape_js loop_value%><%unless last%>\n<%endunless%><%endloop%>');
|
||||
<%~elsif attachment_uploaded%>
|
||||
if (true || confirm("submit form?")) submitWrapper();
|
||||
<%~endif%>
|
||||
}
|
||||
|
||||
function submitWrapper () {
|
||||
if (activeSource == 'inline') {
|
||||
var att_type = 'temp', att_id = 0;
|
||||
for (var i = 0; i < inlines.length; i++) {
|
||||
if (inlines[i][0] == src['inline-input'].value) {
|
||||
att_type = inlines[i][1];
|
||||
att_id = inlines[i][2];
|
||||
break;
|
||||
}
|
||||
}
|
||||
var url = '<%set url = GForum::SEO::url(params => "do=${page_mode}_attachment")%><%escape_js url%>;' + att_type + 'att_id=' + att_id;
|
||||
if (att_type == 'temp')
|
||||
url += ';<%if page_mode eq "post"%>post_unique<%else%>temp_id<%endif%>=' + document.getElementById('temp_id').value;
|
||||
src['url-input'].value = url;
|
||||
}
|
||||
|
||||
submitForm();
|
||||
}
|
||||
|
||||
registerEvent(window, 'load', init);
|
||||
//]]>
|
||||
</script>
|
||||
</head>
|
||||
<body id="editor_image">
|
||||
<!--form id="image_form" enctype="multipart/form-data" onsubmit="return checkSource(true)"-->
|
||||
<form id="image_form" action="<%GForum::SEO::url()%>" method="post" enctype="multipart/form-data" onsubmit="return checkSource(true)">
|
||||
<%hidden_form%>
|
||||
<input type="hidden" name="do" value="<%page_mode%>_attachment_upload" />
|
||||
<input type="hidden" id="temp_id" name="temp_id" value="" /><%-- populated from parent window by init() --%>
|
||||
<input type="hidden" name="redo" value="<%this_do%>" />
|
||||
<div id="form">
|
||||
<div class="row">
|
||||
<label class="name">Insert Image from:</label>
|
||||
<input type="radio" id="src-url" name="src-type" class="radio"<%unless attachment_uploaded or error_loop.length%> checked<%endunless%>><label id="src-url-label" for="src-url" accesskey="u"><u>U</u>RL</label>
|
||||
<%~unless attachment_uploaded%>
|
||||
<input type="radio" id="src-file" name="src-type" class="radio"<%if error_loop.length%> checked<%endif%>><label id="src-file-label" for="src-file" accesskey="f"><u>F</u>ile</label>
|
||||
<%~endunless%>
|
||||
<input type="radio" id="src-inline" name="src-type" class="radio"<%if attachment_uploaded%> checked<%endunless%>><label id="src-inline-label" for="src-inline" accesskey="i"><u>I</u>nline Attachment</label>
|
||||
</div>
|
||||
<div id="src-url-row" class="row">
|
||||
<label for="src-url-input" class="name" accesskey="s">Image <u>S</u>ource:</label>
|
||||
<input type="text" id="src-url-input" value="" class="text">
|
||||
</div>
|
||||
<%~unless attachment_uploaded%>
|
||||
<div id="src-file-row" class="row">
|
||||
<label for="src-file-input" class="name" accesskey="m">I<u>m</u>age Upload:</label>
|
||||
<input type="file" accept="image/*" id="src-file-input" name="<%if page_mode eq message%>msg<%else%>post<%endif%>_attachment_orig" class="file">
|
||||
<input type="hidden" name="<%if page_mode eq message%>message<%else%>post<%endif%>_attachment_inline" value="1">
|
||||
</div>
|
||||
<div id="src-file-size-slider" style="display: none; align-items: center; gap: 8px;">
|
||||
Size:
|
||||
<input type="range" min="10" max="<%~if in.forum_id eq "1" %>1024<%~else %>256<%~endif%>" value="<%~if in.forum_id eq "1" %>512<%~else %>128<%~endif%>" id="input-size" style="max-width: 200px;"/>
|
||||
<span id="target-size"></span>
|
||||
</div>
|
||||
<div id="src-file-quality-slider" style="display: none; align-items: center; gap: 8px;">
|
||||
Quality:
|
||||
<input type="range" min="10" max="100" value="90" id="input-quality" style="max-width: 200px;"/>
|
||||
</div>
|
||||
<div id="src-file-size-orig" style="display: none;">Original: <span id="original-dimensions"></span> - <span id="original-bytesize"></span></div>
|
||||
<div id="src-file-size-resize" style="display: none;">Resized: <span id="resized-dimensions"></span> - <span id="resized-bytesize"></span></div>
|
||||
<br />
|
||||
<div>
|
||||
<img style="width: 500px; object-fit: scale-down;" id="output-img"></img>
|
||||
</div>
|
||||
<%~endunless%>
|
||||
<div id="src-inline-row" class="row">
|
||||
<input type="hidden" id="src-inline-input" value="">
|
||||
<label class="name">I<u>n</u>line Attachment:</label>
|
||||
<div id="src-inline-value" class="value"><%-- populated by init() --%></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<input type="submit" id="submit" value="OK" class="submit">
|
||||
<input type="button" value="Cancel" class="button" onclick="window.close()">
|
||||
</div>
|
||||
</form>
|
||||
<div id="output-box"></div>
|
||||
</body>
|
||||
|
||||
<%~unless attachment_uploaded%>
|
||||
<script>
|
||||
|
||||
let sourceImg = null
|
||||
let sourceSize = 0
|
||||
let initialized = false
|
||||
|
||||
let inputImg = document.getElementById('src-file-input')
|
||||
let outputImg = document.getElementById('output-img')
|
||||
let inputSize = document.getElementById('input-size')
|
||||
let targetSize = document.getElementById('target-size')
|
||||
let inputQuality = document.getElementById('input-quality')
|
||||
let originalDimensions = document.getElementById('original-dimensions')
|
||||
let resizedDimensions = document.getElementById('resized-dimensions')
|
||||
let originalBytesize = document.getElementById('original-bytesize')
|
||||
let resizedBytesize = document.getElementById('resized-bytesize')
|
||||
|
||||
//action="<%GForum::SEO::url()%>" method="post"
|
||||
const form = document.forms.namedItem("image_form");
|
||||
form.addEventListener(
|
||||
"submit",
|
||||
(event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const output = document.getElementById("output-box");
|
||||
const formData = new FormData(form);
|
||||
|
||||
formData.append("CustomField", "This is some extra data");
|
||||
const imgBlob = imageToBlob(outputImg.src, 'image/jpeg');
|
||||
var imgPath = inputImg.value;
|
||||
const imgName = imgPath.replace(/^.*?([^\\\/]*)$/, '$1');
|
||||
formData.append("post_attachment", imgBlob, imgName);
|
||||
const request = new XMLHttpRequest();
|
||||
request.open("POST", "<%GForum::SEO::url()%>", true);
|
||||
request.onload = (progress) => {
|
||||
output.innerHTML =
|
||||
request.status === 200
|
||||
? "Uploaded!"
|
||||
: `Error ${request.status} occurred when trying to upload your file.<br />`;
|
||||
|
||||
if ( request.status === 200 )
|
||||
{
|
||||
const winUrl = URL.createObjectURL( new Blob([request.responseText], { type: "text/html" }));
|
||||
const win = location.replace(winUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
// error...
|
||||
}
|
||||
};
|
||||
|
||||
request.send(formData);
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
function resizeImage(image, size, quality)
|
||||
{
|
||||
let index = 0
|
||||
let canvases = [document.createElement('canvas'), document.createElement('canvas')]
|
||||
{
|
||||
canvases[index].width = image.naturalWidth
|
||||
canvases[index].height = image.naturalHeight
|
||||
const ctx0 = canvases[index].getContext('2d')
|
||||
ctx0.drawImage(image, 0, 0)
|
||||
}
|
||||
|
||||
let finalWidth = Math.floor(image.naturalWidth * size)
|
||||
let finalHeight = Math.floor(image.naturalHeight * size)
|
||||
let iter = Math.floor(Math.sqrt((1 / size)))
|
||||
|
||||
|
||||
for(let i = 0; i < iter; i++)
|
||||
{
|
||||
let canvasSrc = canvases[index]
|
||||
let canvasDst = canvases[1 - index]
|
||||
const ctxSrc = canvasSrc.getContext('2d')
|
||||
const ctxDst = canvasDst.getContext('2d')
|
||||
|
||||
if(i == (iter - 1))
|
||||
{
|
||||
canvasDst.width = finalWidth
|
||||
canvasDst.height = finalHeight
|
||||
}
|
||||
else
|
||||
{
|
||||
canvasDst.width = canvasSrc.width / 2
|
||||
canvasDst.height = canvasSrc.height / 2
|
||||
}
|
||||
|
||||
ctxDst.drawImage(canvasSrc, 0, 0, canvasSrc.width, canvasSrc.height, 0, 0, canvasDst.width, canvasDst.height)
|
||||
|
||||
index = 1 - index
|
||||
}
|
||||
|
||||
let dataURL = canvases[index].toDataURL('image/jpeg', quality)
|
||||
return dataURL
|
||||
}
|
||||
|
||||
function getDataURLByteSize(d)
|
||||
{
|
||||
let parts = d.split(",")
|
||||
let b64size = parts[1].length
|
||||
let b64padding = parts[1].slice(-4).split('').filter(x => x == '=').length
|
||||
return 3 * ((b64size / 4)) - b64padding
|
||||
}
|
||||
|
||||
function onResizeSourceImg()
|
||||
{
|
||||
if(!sourceImg) return
|
||||
|
||||
targetSize.textContent = `${inputSize.value} KB`
|
||||
|
||||
let dataOriginal = resizeImage(sourceImg, 1, inputQuality.value / 100)
|
||||
let sizeOriginal = getDataURLByteSize(dataOriginal)
|
||||
|
||||
let target = 1
|
||||
if ( !initialized )
|
||||
target = Math.min( sizeOriginal, <%~if in.forum_id eq "1" %>1024000<%~else %>256000<%~endif%> );
|
||||
else
|
||||
target = inputSize.value * 1024;
|
||||
|
||||
let imgRatio = Math.min( sourceImg.naturalWidth / sourceImg.naturalHeight, sourceImg.naturalHeight / sourceImg.naturalWidth );
|
||||
|
||||
let ratio = imgRatio * Math.sqrt( Math.min(1, target / sizeOriginal))
|
||||
|
||||
let dataURL = resizeImage(sourceImg, ratio, inputQuality.value / 100)
|
||||
outputImg.src = dataURL
|
||||
let size = getDataURLByteSize(dataURL)
|
||||
|
||||
resizedBytesize.textContent = `${Math.floor(size / 1024)} KB`
|
||||
if ( !initialized )
|
||||
{
|
||||
targetSize.textContent = `${Math.floor(size / 1024)} KB`
|
||||
inputSize.value = Math.floor(size / 1024)
|
||||
initialized = true
|
||||
}
|
||||
}
|
||||
|
||||
onResizeSourceImg()
|
||||
|
||||
outputImg.onload = () => {
|
||||
if(!sourceImg) return
|
||||
resizedDimensions.textContent = `${outputImg.naturalWidth} x ${outputImg.naturalHeight}`
|
||||
}
|
||||
|
||||
inputSize.addEventListener("input", e => {
|
||||
onResizeSourceImg()
|
||||
})
|
||||
|
||||
inputQuality.addEventListener("input", e => {
|
||||
onResizeSourceImg()
|
||||
})
|
||||
|
||||
inputImg.addEventListener("change", e => {
|
||||
let files = e.target.files
|
||||
if(files.length == 0) return
|
||||
let file = files[0]
|
||||
|
||||
let fr = new FileReader()
|
||||
fr.onload = () => {
|
||||
let image = new Image()
|
||||
image.onload = () => {
|
||||
sourceImg = image
|
||||
sourceSize = file.size
|
||||
|
||||
originalDimensions.textContent = `${image.naturalWidth} x ${image.naturalHeight}`
|
||||
originalBytesize.textContent = `${Math.floor(file.size / 1024)} KB`
|
||||
|
||||
//inputSize.setAttribute('max', image.naturalWidth)
|
||||
//inputSize.value = image.naturalWidth * 0.5
|
||||
onResizeSourceImg()
|
||||
}
|
||||
image.src = fr.result
|
||||
}
|
||||
fr.readAsDataURL(file)
|
||||
})
|
||||
|
||||
function imageToBlob(image, mime)
|
||||
{
|
||||
let byteString = atob(image.split(',')[1]);
|
||||
let ab = new ArrayBuffer(byteString.length);
|
||||
let ia = new Uint8Array(ab);
|
||||
for (var i = 0; i < byteString.length; i++) ia[i] = byteString.charCodeAt(i);
|
||||
let bb = new Blob([ab], { type: mime });
|
||||
return bb
|
||||
}
|
||||
|
||||
</script>
|
||||
<%~endunless%>
|
||||
|
||||
</html>
|
Reference in New Issue
Block a user