// ES6 Compliant Syntax
// GitHub Copilot, Claude Sonnet 4 (Preview), October 16, 2025
// File: showcase-demos/pdf-page-manipulation-api/ui-elements.js
class UIElements {
static init(instance) {
this.createUIElements(instance);
}
static createUIElements(instance) {
const element = document.getElementById('viewer');
let newDoc = null; // document to merge into the current document
// Check if controls container already exists
let controlsContainer = document.getElementById('controls-container');
if (controlsContainer) {
console.log('Controls container already exists, skipping creation');
return; // Exit early if container already exists
}
// UI section
// Create a container and insert the controls in it
controlsContainer = document.createElement('div');
const commandsList = document.createElement('select');
controlsContainer.appendChild(commandsList);
const uploadButton = document.createElement('input');
controlsContainer.appendChild(uploadButton);
const pagesRange = document.createElement('input');
controlsContainer.appendChild(pagesRange);
const rotateAngle = document.createElement('select');
window.rotateAngle = rotateAngle; // expose to global scope for function access
controlsContainer.appendChild(rotateAngle);
const topLabel = document.createElement('label');
topLabel.textContent = 'Top';
controlsContainer.appendChild(topLabel);
const cropTop = document.createElement('input');
controlsContainer.appendChild(cropTop);
const bottomLabel = document.createElement('label');
bottomLabel.textContent = 'Bottom';
controlsContainer.appendChild(bottomLabel);
const cropBottom = document.createElement('input');
controlsContainer.appendChild(cropBottom);
const leftLabel = document.createElement('label');
leftLabel.textContent = 'Left';
controlsContainer.appendChild(leftLabel);
const cropLeft = document.createElement('input');
controlsContainer.appendChild(cropLeft);
const rightLabel = document.createElement('label');
rightLabel.textContent = 'Right';
controlsContainer.appendChild(rightLabel);
const cropRight = document.createElement('input');
controlsContainer.appendChild(cropRight);
cropTop.type = cropBottom.type = cropLeft.type = cropRight.type = 'number';
cropTop.className = cropBottom.className = cropLeft.className = cropRight.className = 'elements-style';
cropTop.value = cropBottom.value = cropLeft.value = cropRight.value = 50;
const pageInput = document.createElement('input');
controlsContainer.appendChild(pageInput);
const locationInput = document.createElement('input');
controlsContainer.appendChild(locationInput);
const runCommandButton = document.createElement('button');
controlsContainer.appendChild(runCommandButton);
const br = document.createElement('br');
controlsContainer.appendChild(br);
const promptLabel = document.createElement('label');
controlsContainer.appendChild(promptLabel);
controlsContainer.className = 'control-container';
uploadButton.type = 'file';
uploadButton.accept = '.pdf,.jpg,.jpeg,.png';
uploadButton.onchange = async (e) => {
const file = e.target.files[0];
const split = file.name.split('.');
const ext = split[split.length - 1];
newDoc = await instance.Core.createDocument(file, {
extension: ext,
filename: file.name,
});
// Update the global reference so it's available for merge operations
window.newDoc = newDoc;
console.log('File uploaded and newDoc updated:', file.name);
}
rotateAngle.className = commandsList.className = 'list-style';
pagesRange.className = pageInput.className = locationInput.className = 'elements-style';
uploadButton.className = runCommandButton.className = 'btn';
pagesRange.type = 'text';
pagesRange.placeholder = '1,2,3-5';
[90, 180, 270].forEach(deg => { // create rotate angle UI elements
const option = document.createElement('option');
//option.value will be set after WebViewer is initialized because values are in instance.Core.PageRotation
option.textContent = deg.toString()+'\u00b0'; // degree symbol °
rotateAngle.appendChild(option);
});
pageInput.type = 'text';
pageInput.placeholder = 'Page #';
locationInput.type = 'text';
locationInput.placeholder = 'to page #';
runCommandButton.textContent = 'Run Command';
runCommandButton.onclick = () => commandsArray[commandsList.selectedIndex].executeCommand();
const commandsArray = [
{ value: 'Select Command', executeCommand: () => alert('Select a command from the list'), visibleItems: [],
text: 'Select a command from the list' },
{ value: 'Rotate pages', executeCommand: () => window.rotatePages(), visibleItems: [pagesRange, rotateAngle],
text: 'Enter a comma or dash separated list of page numbers to rotate (leave empty for all pages)' },
{ value: 'Delete pages', executeCommand: () => window.deletePages(), visibleItems: [pagesRange],
text: 'Enter a comma or dash separated list of page numbers to delete' },
{ value: 'Move page', executeCommand: () => window.movePage(), visibleItems: [pageInput, locationInput],
text: 'Enter page number to move then location to move it to' },
{ value: 'Insert blank page', executeCommand: () => window.insertBlankPage(), visibleItems: [pageInput],
text: 'Enter a page number to add a blank page at (-1 to append at end)' },
{ value: 'Split / extract pages', executeCommand: () => window.extractPages(), visibleItems: [pagesRange],
text: 'Enter a comma or dash separated list of page numbers to extract (leave empty for all pages)' },
{ value: 'Crop pages', executeCommand: () => window.cropPage(), visibleItems: [pageInput, cropTop, cropBottom, cropLeft, cropRight, topLabel, bottomLabel, leftLabel, rightLabel],
text: 'Enter amount of pixels to crop, and then a page number (cropping cannot be undone)' },
{ value: 'Merge documents', executeCommand: () => window.mergeDocuments(), visibleItems: [uploadButton, pagesRange, locationInput],
text: 'Upload document to merge then enter list of pages to insert (leave empty for all pages) and position to insert the pages at' },
];
commandsArray.forEach(cmd => { // create a UI element in the list for each command
const option = document.createElement('option');
option.value = cmd.value;
option.textContent = cmd.value;
commandsList.appendChild(option);
});
const hideAllItems = () => [uploadButton, pagesRange, rotateAngle, pageInput, locationInput, cropTop, cropBottom, cropLeft, cropRight, topLabel, bottomLabel, leftLabel, rightLabel].forEach(item => item.style.display = 'none');
commandsList.onchange = (e) => {
const selectedIndex = e.target.selectedIndex;
promptLabel.textContent = commandsArray[selectedIndex].text;
hideAllItems();
commandsArray[selectedIndex].visibleItems.forEach(item => item.style.display = '');
}
commandsList.selectedIndex = 0;
promptLabel.textContent = commandsArray[0].text;
hideAllItems();
controlsContainer.id = 'controls-container';// set the id for CSS styling
element.insertBefore(controlsContainer, element.firstChild);
// Store elements globally for function access
window.pagesRange = pagesRange;
window.rotateAngle = rotateAngle;
window.pageInput = pageInput;
window.locationInput = locationInput;
window.cropTop = cropTop;
window.cropBottom = cropBottom;
window.cropLeft = cropLeft;
window.cropRight = cropRight;
window.newDoc = newDoc;
}
}
window.UIElements = UIElements; // Expose the class to the global scope