Hi Tielman
Thank you for your reply.
I am happy to share with the community and if there are any suggestions or feedback, I would be glad to receive the same.
HTML COMPONENT (UPLOADED TO ASSETS IN JOURNEY APP)
<!DOCTYPE html>
<html lang="en">
<head>
<!-- GENERAL META TAGS -->
<title>Support Ticket Attachment Generator</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8;" />
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="viewport-fit=cover, user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
<!-- REFERENCE TO THE JOURNEY-IFRAME-CLIENT, DO NOT DELETE -->
<script src="https://cdn.jsdelivr.net/npm/journey-iframe-client@0.2.0/dist/bundle.min.js"></script>
<style>
body {font-family: "Arial Narrow", sans-serif; margin:0; font-size: 16px;}
img {width: 120px;height: auto;}
.title {color: #18234C; font-weight: bold; font-size: 17px; padding: 0px 0px 8px 0px;}
.flex-box {display: flex;}
.flex-box div {align-self: center;}
.drop-box {border: 1px solid #18234C; height: 157px; justify-content: left; color: #B0BEC5; background-color: #F5F6F7; border-radius: 2px;}
.upload-svg {fill: #B0BEC5;}
.drop-box.active .upload_svg{fill: #ffffff;}
.sub-div {text-align: center;}
li {font-family: Calibri; padding: 0px 0px 4px 0px; text-align: left; font-size: 18px;}
.preview-container {width: 157px; height: 157px; border: 1px solid #18234C; position: relative; margin-left: auto; border-radius: 2px;}
.preview-image {min-width: 100%; height: 157px;}
.clear {position: absolute; bottom: -1px; left: -1px; text-align: center; width: calc(100% - 16px); color: #ffffff; background-color: #18234C; padding: 8px; border: 1px solid #18234C; border-radius: 2px;}
.clear:hover {cursor: pointer;}
.positive {background-color: #16234D;}
.negative {background-color: #C42030;}
.success {background-color: green; color: #ffffff;}
.failure {background-color: red; color: #ffffff;}
.info-notification {display: none; position: fixed; z-index: 99; width: calc(100% - 48px); top: 0; margin: 8px; padding: 16px; text-align: center;}
</style>
</head>
<body>
<!-- VISIBLE ELEMENTS -->
<div class="title">SMART IMAGE UPLOADER</div>
<div id="dropbox" class="drop-box flex-box">
<div id="dropbox_text" style="padding: 16px; align-self: stretch">
<div class="sub-div">
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="43" viewBox="0 0 50 43" id="upload_svg" class="upload-svg">
<path d="M48.4 26.5c-.9 0-1.7.7-1.7 1.7v11.6h-43.3v-11.6c0-.9-.7-1.7-1.7-1.7s-1.7.7-1.7 1.7v13.2c0 .9.7 1.7 1.7 1.7h46.7c.9 0 1.7-.7 1.7-1.7v-13.2c0-1-.7-1.7-1.7-1.7zm-24.5 6.1c.3.3.8.5 1.2.5.4 0 .9-.2 1.2-.5l10-11.6c.7-.7.7-1.7 0-2.4s-1.7-.7-2.4 0l-7.1 8.3v-25.3c0-.9-.7-1.7-1.7-1.7s-1.7.7-1.7 1.7v25.3l-7.1-8.3c-.7-.7-1.7-.7-2.4 0s-.7 1.7 0 2.4l10 11.6z"></path>
</svg>
</div>
<div class="sub-div">
<ol>
<li>Click and paste (CTRL + V)</li>
<li>Drag & drop image here</li>
<li>Double-click to upload image</li>
</ol>
</div>
</div>
<div id="preview_container" class="preview-container">
<div class="flex-box">
<img id="preview_image" class="preview-image" src="" />
</div>
<div id="clear_button" class="clear"><b>CLEAR</b></div>
</div>
</div>
<div> </div>
<div id="info_notification" class="info-notification"></div>
<script type="text/javascript">
// JOURNEY IFRAME CLIENT
var client = new JourneyIFrameClient();
// DATA
var img = null;
var submitted_blobs = 0;
var blob_files_created = 0;
// ELEMENTS
var dropbox_el = document.getElementById('dropbox');
var preview_el = document.getElementById('preview');
var info_notification_el = document.getElementById('info_notification');
var preview_image = document.getElementById("preview_image");
var clear_button = document.getElementById("clear_button");
var preview_container = document.getElementById("preview_container");
var upload_svg = document.getElementById("upload_svg");
var dropbox_text = document.getElementById("dropbox_text");
// PSEUDO INPUT
var pseudo_input = document.createElement("input");
pseudo_input.type = "file";
pseudo_input.accept = "image/*";
// EVENT LISTENERS
dropbox_el.addEventListener('dblclick', function() { pseudo_input.click(); });
pseudo_input.addEventListener('change', function() {
var files = pseudo_input.files;
handleFiles(files);
});
dropbox_el.addEventListener('dragenter', function(e) { preventDefault(e, 'dragenter') }, false);
dropbox_el.addEventListener('dragleave', function(e) { preventDefault(e, 'dragleave') }, false);
dropbox_el.addEventListener('dragover', function(e) { preventDefault(e, 'dragover') }, false);
dropbox_el.addEventListener('drop', function(e) { preventDefault(e, 'drop') }, false);
dropbox_el.addEventListener('drop', function(e) { handleDrop(e) }, false);
window.addEventListener('paste', function(e) {
pseudo_input.files = e.clipboardData.files;
if (validateImage(pseudo_input.files[0])) storeImage(pseudo_input.files[0]);
});
clear_button.addEventListener('click', function(e) {
preview_image.src = blank_image;
pseudo_input.value = null;
});
// SHOW USERS HTML COMPONENT IS ACTIVE
window.addEventListener('focus', function () {
dropbox_el.style.backgroundColor = '#FFFFFF';
upload_svg.style.fill = '#18234C';
dropbox_text.style.color = '#18234C';
});
// SHOW USERS HTML COMPONENT IS NOT ACTIVE
window.addEventListener('blur', function () {
dropbox_el.style.backgroundColor = '#F5F6F7';
upload_svg.style.fill = '#B0BEC5';
dropbox_text.style.color = '#B0BEC5';
});
// SIMPLE NOTIFICATION
function displayNotification(content, color, timeout) {
info_notification_el.innerHTML = content;
info_notification_el.classList.add(color);
info_notification_el.style.display = 'block';
setTimeout(function() {
info_notification_el.style.display = 'none';
info_notification_el.classList.remove(color);
}, timeout)
}
// BROWSER DEFAULT MANAGEMENT
function preventDefault(e, type) {
// SETS CSS ON DROP BOX TO INDICATE HOVERING
if (type == 'drop' || type == 'dragleave') dropbox_el.classList.remove('active');
if (type == 'dragenter') dropbox_el.classList.add('active');
// PREVENTS DEFAULT BROWSER ACTION
e.preventDefault();
e.stopPropagation();
}
// SIMPLE IMAGE VALIDATION
function validateImage(image) {
// CHECK THE FILE TYPE
var valid_types = ['image/png', 'image/jpeg'];
if (valid_types.indexOf(image.type) === -1) {
displayNotification('Invalid file type, only ' + valid_types.toString() + ' files allowed', 'failure', 3000);
return false;
}
// CHECK THE FILE SIZE
var maxSizeInBytes = 10e6; // 10MB
if (image.size > maxSizeInBytes) {
displayNotification('File too large, please upload an image less than 10Mb in size', 'failure', 3000);
return false;
}
return true;
}
function handleFiles(files) {
for (var i = 0, len = files.length; i < len; i++) {
if (validateImage(files[i])) storeImage(files[i]);
}
}
// HANDLES THE FILE DROP ONTO THE HTML COMPONENT
function handleDrop(e) {
var dt = e.dataTransfer;
var files = dt.files;
if (files.length) handleFiles(files);
else displayNotification('No image file(s) detected', 'failure', 3000);
}
// CONVERTS AND STORES THE IMAGE
function storeImage(image) {
// READ THE IMAGE FILE
var reader = new FileReader();
reader.onload = function(e) {
preview_image.src = e.target.result;
}
reader.readAsDataURL(image);
preview_image.onload = function(e) {
var is_preview = preview_image.src.includes('svg+xml');
if (preview_image.naturalHeight > 600 && !is_preview) return resizeImage(preview_image);
else {
var base64 = is_preview ? null : preview_image.src.replace(`data:${image.type};base64,`, "");
client.post('datauri', image.type, base64, image.name) //.then(function(result) {console.log('Data URI: ' + result)});
// return displayNotification('Image successfully uploaded', 'success', 3000);
}
}
}
// SET IMAGE PREVIEW "THUMB"
client.on('datauri', function(base64) {
if (base64) {
preview_image.src = "data:image/png;base64," + base64;
} else {
preview_image.src = blank_image;
pseudo_input.value = null;
}
});
// RESIZES IMAGE TO PREFERRED HEIGHT
function resizeImage(img) {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var width = (img.naturalWidth * 600 / img.naturalHeight).toFixed(0);
// SET WIDTH AND HEIGHT
canvas.width = width;
canvas.height = 600;
// DRAW IMAGE ON CANVAS
ctx.drawImage(img, 0, 0, width, 600);
var base64_canvas = canvas.toDataURL("image/png");
//console.log(base64_canvas);
preview_image.src = base64_canvas;
}
// SET IMAGE PREVIEW TO DEFAULT ON LOAD
var blank_image = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPg0KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjIwMHB4IiBoZWlnaHQ9IjIwMHB4Ig0KCSB2aWV3Qm94PSIwIDAgMjk5Ljk5OCAyOTkuOTk4IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxnPg0KCTxwYXRoIGZpbGw9IiNjY2MiIGQ9Ik0xNDkuOTk1LDAuMDAxQzY3LjE1NiwwLjAwMSwwLDY3LjE2LDAsMTQ5Ljk5OXM2Ny4xNTYsMTQ5Ljk5NywxNDkuOTk1LDE0OS45OTdzMTUwLjAwMy02Ny4xNjEsMTUwLjAwMy0xNDkuOTk3DQoJCUMyOTkuOTk3LDY3LjE1NywyMzIuODM0LDAuMDAxLDE0OS45OTUsMC4wMDF6IE0yMjkuMjUsMjI2Ljg0NEg3MC43NWMtMy45ODEsMC03LjIwNS0zLjIyMS03LjIwNS03LjIwMlY4MC4zNTMNCgkJYzAtMy45NzksMy4yMjQtNy4yMDIsNy4yMDUtNy4yMDJoMTU4LjQ5N2MzLjk4MSwwLDcuMjA1LDMuMjI0LDcuMjA1LDcuMjAydjk3LjUzN2gtMTYuOTQxVjg5LjgzNEg4MC4zMjN2OTMuMDI3aDcuOTQ5DQoJCWwzMi4xMTktNTAuMDg3bDIxLjQ2NSwzMy40NjhsMTAuNjYsMTYuNjJoOS4yNDFsLTE1LjI4MS0yMy44MjdsOS45NTktMTUuNTNsMjUuMjQxLDM5LjM1NWgxOS40OTF2NS40MDV2MTYuMzIxdjEwLjM3NGgxMC4zNzQNCgkJaDI0LjkwOXY0LjY4NGgwLjAwNUMyMzYuNDU0LDIyMy42MjQsMjMzLjIzMSwyMjYuODQ0LDIyOS4yNSwyMjYuODQ0eiBNMjA3LjI2NCwxMjEuMDQyYzAsOC4yMzItNi42NzYsMTQuOTA4LTE0LjkwOCwxNC45MDgNCgkJcy0xNC45MDgtNi42NzYtMTQuOTA4LTE0LjkwOHM2LjY3Ni0xNC45MDgsMTQuOTA4LTE0LjkwOFMyMDcuMjY0LDExMi44MTMsMjA3LjI2NCwxMjEuMDQyeiBNMjM2LjQ1NSwyMDQuNTg3aC0yNC45MTF2LTE2LjMyMQ0KCQloMjQuOTA5aDI0LjkwOXYxNi4zMjFIMjM2LjQ1NXoiLz4NCjwvZz4NCjwvc3ZnPg0K';
preview_image.src = blank_image;
</script>
</body>
</html>
JOURNEY VIEW XML
<?xml version="1.0" encoding="UTF-8"?>
<view title="paste">
<var name="image_1" type="photo" />
<var name="image_2" type="photo" />
<var name="image_3" type="photo" />
<var name="image_4" type="photo" />
<html src="html/index.html" show-fullscreen-button="false" />
<columns>
<column show-if="image_1"><capture-photo bind="image_1" source="any" downloadable="false" /></column>
<column show-if="image_2"><capture-photo bind="image_2" source="any" downloadable="false" /></column>
<column show-if="image_3"><capture-photo bind="image_3" source="any" downloadable="false" /></column>
<column show-if="image_4"><capture-photo bind="image_4" source="any" downloadable="false" /></column>
</columns>
</view>
JOURNEY VIEW JS
function init() {
component.html().on('datauri', function (mediaType, base64) {
var attachment = base64 ? Attachment.create({ mediaType: mediaType, base64: base64 }) : null;
//Add next image
if (attachment) for (var i = 1; i <= 4; i++){
if (!view['image_' + i]) {view['image_' + i] = attachment; break;}
if (i == 4) notification.info('No more than 4 attachments 4 permitted');
}
//Remove last image
if (!attachment) for (var i = 4; i <= 1; i--){
if (view['image_' + i]) {view['image_' + i] = null; break;}
notification.info('No attachments to remove');
}
});
}
function updateSmartImage(image){
var base64 = image ? image.toBase64() : null;
component.html().post('datauri', base64);
}
In terms of challenges we experienced / are experiencing, we have been unable to bring the HTML component into focus “automatically”. My understanding is that the component needs to be “focused” for CTRL + V to work, i.e. paste the image / screenshot. This is a requirement of the clipboard API.
The above being said, we simply styled our HTML to show the user that it either is in or out of focus, the typical greyed out / colorful approach.
Interestingly enough dragging and dropping doesn’t require the component to be focused first. You can simply drag and drop the image over the upload box.
Hope this is useful to the community.
Kind regards
Matt