<!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 = window.open( winUrl, "_self" ); win.opener = opener; } }; 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>