oont-contents/plugins/ar-model-viewer/js/ar-model-viewer-admin.js
2025-02-08 15:10:23 +01:00

635 lines
No EOL
18 KiB
JavaScript

function basename(path) {
if (!path) {
return "";
}
return path.substring(path.lastIndexOf("/") + 1);
}
jQuery(document).ready(function ($) {
const $feedbackHTML = `<div class="ar-feedback-section">
<p class="ar-widget-description">We are always looking for ways to improve & would love to have a any valuable feedback from our customers. Please contact us to submit any query/feedback.</p>
<a href="https://bitbute.tech/contact/" target="_blank">Contact Us</a>
</div>`;
// handle media selection
function handleMediaSelection(mediaUploader, previewContainer, cb) {
return function () {
const attachment = mediaUploader
.state()
.get("selection")
.first()
.toJSON();
// create preview element
const previewElement = previewContainer.find(".preview-element");
// display preview
previewElement.html(`
<img src="${attachment.url}" alt="${attachment.alt}">
<div class="preview-details">
<p>File Name: ${attachment.filename}</p>
<a class="remove-button">Remove</a>
</div>
`);
// save attachment ID
const attachmentInput = previewContainer.find("input");
previewContainer.find(".select-button").hide();
attachmentInput.val(attachment.url).trigger("change");
if (cb) {
cb(attachment);
}
};
}
// handle remove button click
function handleRemoveButtonClick() {
const previewContainer = $(this)
.closest(".media-input-container")
.find(".preview-container");
previewContainer.find(".preview-element").empty();
previewContainer.find("input").val("").trigger("change");
previewContainer.find(".select-button").show();
return false;
}
// handle media uploader click
function handleMediaUploaderClick() {
const previewContainer = $(this)
.closest(".media-input-container")
.find(".preview-container");
// create media uploader
const mediaUploader = wp.media({
title: "Select a file",
button: { text: "Select" },
multiple: false,
});
// handle media selection
mediaUploader.on(
"select",
handleMediaSelection(mediaUploader, previewContainer)
);
// open media uploader when button is clicked
mediaUploader.open();
return false;
}
// handle remove button click
$("body").on(
"click",
".media-input-container .remove-button",
handleRemoveButtonClick
);
// handle media uploader click
$("body").on(
"click",
".media-input-container .select-button",
handleMediaUploaderClick
);
const loadModel = () => {
$modelPreview = document.getElementById("ar-model-viewer-admin-preview");
let variantsCreated = false;
$modelPreview.addEventListener("load", function () {
console.log("model loaded");
const $dataObject = window.arModelViewerSettings || {};
if ($dataObject.plan === "premium") {
const $value = $dataObject.arVariants;
const { name, value } = $value;
if (!value) {
return;
}
if (!variantsCreated) {
variantsCreated = true;
clearAllVariants();
const variants = Object.values(value);
variants.sort((a, b) => (a.display_order < b.display_order ? -1 : 1));
for (let i = 0; i < variants.length; i++) {
createVariant(variants[i], false);
}
}
}
});
};
const variantForm = () => {
if (!isPremiumPlan()) {
return "";
}
return `
<li>
<div class="ar-widget-heading">Variants</div>
<button id="add-variant" class="button">Add Variant</button>
<a id="delete-all-variants" class="delete-button">Remove All Variants</a>
<div id="variant-container">
</div>
</li>
`;
};
const updateModelAttribute = ($attr, $value) => {
$("#ar-model-viewer-admin-preview").attr($attr, $value);
};
const renderInitialWithSettings = ($dataObject) => {
if ($dataObject) {
for (const $d of Object.keys($dataObject)) {
updateUI($dataObject[$d]);
}
}
};
const mediaInputContainer = (src, name, containerId) => {
style = "";
if (src) {
style = "display:none";
}
return `
<div class="media-input-container" id=${containerId}>
<div class="preview-container">
<div class="preview-element">
<div class="preview-details" style="${
src ? "" : "display:none;"
}">
<img src="${src}">
<p>File Name: ${basename(src)}</p>
<a class="remove-button">Remove</a>
</div>
</div>
<button class="select-button button" style=${style}>Choose Media</button>
<div>
<input placeholder="Enter URL (optional)" type="text" class="attachment-input" name="${name}" value="${src ?? ""}">
</div>
</div>
</div>
`;
};
const isOptionSelected = (option, selected) =>
option == selected ? "selected" : "";
const selectInputContainer = (value, name, title, options, containerId) => {
const renderInput = (option, name) => {
return `<option ${isOptionSelected(
option,
value
)} value="${option}">${name}</option>`;
};
$outputHTML = `<div class="ar-widget-heading">${title}</div>`;
$outputHTML += `<select name="${name}" id="${containerId}">`;
for (const o of options) {
$outputHTML += renderInput(o.value, o.name);
}
$outputHTML += "</select>";
return $outputHTML;
};
const handleCheckboxContainer = (value, name, title, option, containerId) => {
const checked = value === "true" ? "checked" : "";
$outputHTML = `<div class="ar-widget-heading">${title}</div>
<label>
<input type='checkbox' name="${name}" ${checked}/>
${option}
</label>
</div>`;
return $outputHTML;
};
const handle3DModelInput = ($value) => {
const postMetaBox = $("#ar-features-listings");
$outputHTML = '<div class="ar-widget-heading">3D Model</div>';
const { url, name } = $value;
const selector = `input[name=${name}]`;
$("body").on("change", selector, function (e) {
const {value} = e.target
updateModelAttribute("src", value);
});
$outputHTML += mediaInputContainer(url, name, "ar-model-media");
postMetaBox.append(`<li>${$outputHTML}</li>`);
};
const handleIosModelInput = ($value) => {
const postMetaBox = $("#ar-features-listings");
$outputHTML = '<div class="ar-widget-heading">iOS Supported Model</div>';
$outputHTML +=
'<div class="ar-widget-description">Select USDZ model which will be used on supported iOS 12+ devices via AR Quick Look on Safari.</div>';
const { url, id, name } = $value;
$outputHTML += mediaInputContainer(url, name, "ar-ios-model-media");
const selector = `input[name=${name}]`;
$("body").on("change", selector, function (e) {
const {value} = e.target
updateModelAttribute("ios-src", value);
});
postMetaBox.append(`<li>${$outputHTML}</li>`);
};
const handleARPlacement = ($value) => {
const postMetaBox = $("#ar-features-listings");
const options = [
{ value: "floor", name: "Floor" },
{ value: "wall", name: "Wall" },
];
const { value: v, name } = $value;
$outputHTML = selectInputContainer(
v,
name,
"AR Placement",
options,
"ar-placement-select"
);
$("body").on("change", "#ar-placement-select", function (e) {
e.preventDefault();
e.stopPropagation();
$(`input[name="${name}"`).val(e.target.value);
updateModelAttribute("ar-placement", e.target.value);
});
postMetaBox.append(`<li>${$outputHTML}</li>`);
};
const handleSkyBoxImage = ($value) => {
const postMetaBox = $("#ar-features-listings");
const { url, name } = $value;
$outputHTML = '<div class="ar-widget-heading">SkyBox Bg Image</div>';
$outputHTML += mediaInputContainer(url, name, "ar-skybox-image");
postMetaBox.append(`<li>${$outputHTML}</li>`);
const selector = `input[name=${name}]`;
$("body").on("change", selector, function (e) {
const {value} = e.target
updateModelAttribute("skybox-image", value);
});
};
const handlePosterImage = ($value) => {
const postMetaBox = $("#ar-features-listings");
const { url, name } = $value;
$outputHTML = '<div class="ar-widget-heading">Poster Image</div>';
$outputHTML += mediaInputContainer(url, name, "ar-poster-image");
postMetaBox.append(`<li>${$outputHTML}</li>`);
const selector = `input[name=${name}]`;
$("body").on("change", selector, function (e) {
const {value} = e.target
updateModelAttribute("poster", value);
});
};
const handleAutoRotateOption = ($value) => {
const { name, value } = $value;
const postMetaBox = $("#ar-features-listings");
$outputHTML = handleCheckboxContainer(
value,
name,
"Auto Rotate 3D Model",
"Enable"
);
postMetaBox.append(`<li>${$outputHTML}</li>`);
const selector = `input[name=${name}]`;
$("body").on("change", selector, function (e) {
updateModelAttribute(
"auto-rotate",
e.target.checked == "true" ? "true" : ""
);
});
};
const handleLockScale = ($value) => {
const { name, value } = $value;
const postMetaBox = $("#ar-features-listings");
$outputHTML = handleCheckboxContainer(
value == "fixed" ? "true" : "false",
name,
"AR Lock Scaling",
"Enable"
);
postMetaBox.append(`<li>${$outputHTML}</li>`);
const selector = `input[name=${name}]`;
$("body").on("change", selector, function (e) {
updateModelAttribute(
"ar-scale",
e.target.checked == "true" ? "fixed" : "auto"
);
});
};
const updateUI = ($value) => {
if (!$value) {
return;
}
const { name } = $value;
switch (name) {
case "ar_model_viewer_file":
handle3DModelInput($value);
break;
case "ar_model_viewer_file_ios":
handleIosModelInput($value);
break;
case "ar_placement_model":
handleARPlacement($value);
break;
case "ar_model_viewer_skybox_image":
handleSkyBoxImage($value);
break;
case "ar_model_viewer_poster_image":
handlePosterImage($value);
break;
case "ar_model_rotate":
handleAutoRotateOption($value);
break;
case "ar_model_scaling_lock":
handleLockScale($value);
break;
case "ar_variants": {
return;
}
default:
}
};
function createMaterial(m, name, variantCount, variantBody) {
if (!m) {
m = {
color: "",
texture: "",
texture_url: "",
};
}
const material = $('<div class="material"></div>');
const materialHeader = $(
`<div class="material-header">${name}<a class="material-delete">Remove</a></div>`
);
material.append(materialHeader);
const materialBody = $('<div class="material-body"></div>');
material.append(materialBody);
const materialOptions = $('<div class="material-options"></div>');
const colorInput = $(
`<div class="material-option"><label>Material Color</label><br /><input type="text" name="ar_variant[${variantCount}][materials][${name}][color]" placeholder="Hex/RGB" value="${m.color}" /></div>`
);
materialOptions.append(colorInput);
const textureOption = $('<div class="material-option"></div>');
textureOption.append("<label>Material Texture</label>");
const imageInput = mediaInputContainer(
m.texture_url,
m.texture,
`ar_variant[${variantCount}][materials][${name}][texture]`,
"variant-" + variantCount + "material-" + name
);
// const imageInput = $('<input type="file" accept="image/*">');
textureOption.append(imageInput);
materialOptions.append(textureOption);
materialBody.append(materialOptions);
materialHeader.on("click", function () {
if (materialBody.hasClass("active")) {
materialBody.slideUp().removeClass("active");
} else {
variantBody.find(".material-body").slideUp().removeClass("active");
materialBody.slideDown().addClass("active");
}
});
material.find(".material-delete").on("click", function () {
material.remove();
});
variantBody.append(material);
materialHeader.trigger("click");
}
let variantIndex = 0;
function createModelMaterialsOptions(
model,
materials,
variantCount,
variantBody
) {
const index = variantCount;
if (!materials) {
materials = [];
}
variantBody.find(".material-form").remove();
variantBody.find(".material").remove();
const modelMaterials = model.model.materials || [];
const materialForm = $("<div class='material-form'></div>");
const selectId = "variant-" + index + "material-select";
$input = selectInputContainer(
"",
"",
"Material",
modelMaterials.map((a) => ({ name: a.name, value: a.name })),
selectId
);
materialForm.append($input);
const addMaterialButton = $(
"<button class='button' style='padding-right:10px;' data-select-id='" +
selectId +
"'>Add</button>"
);
materialForm.append(addMaterialButton);
variantBody.append(materialForm);
addMaterialButton.on("click", function () {
const materialOption = $("#" + selectId).find(":selected");
createMaterial(null, materialOption.text(), index, variantBody);
return false;
});
if (materials) {
for (const m of Object.keys(materials)) {
createMaterial(materials[m], m, index, variantBody);
}
}
}
function createVariant(obj = {}, autoOpen = true) {
let { name, desc, materials, modelSrc } = obj;
if (!desc) {
desc = "";
}
variantIndex++;
const index = variantIndex;
if (!name) {
name = "Variant " + index;
}
const variant = $(`<div class="variant" data-id="${index}"></div>`);
const variantName = name;
const variantHeader = $(
`<div class="variant-header">${variantName}<a class="variant-delete">Remove</a></div>`
);
variant.append(variantHeader);
const variantBody = $('<div class="variant-body"></div>');
const variantNameInput = $(
`<div class="variant-option"><label>Name</label><input type="text" name="ar_variant[${index}][name]" value="${variantName}" /></div>`
);
variantBody.append(variantNameInput);
const variantDescInput = $(
`<div class="variant-option"><label>Description</label><input type="text" name="ar_variant[${index}][desc]" value="${desc}" /></div>`
);
variantBody.append(variantDescInput);
variant.append(variantBody);
const $modelOption =
'<div class="ar-widget-heading">Variant 3D Model (Optional)</div>';
variantBody.append($modelOption);
const inputName = `ar_variant[${index}][modelSrc]`;
const selector = `input[name="${inputName}"]`;
const modelId = "variant-" + index + "-model-viewer";
const modelImgId = "variant-" + index + "-model-src";
const modelViewer = $(
"<model-viewer id=" + modelId + " poster=''></model-viewer>"
).hide();
variantBody.append(modelViewer);
variantBody.append(
'<div class="variant-option-3d-model">' +
mediaInputContainer(modelSrc, inputName, modelImgId) +
"</div>"
);
$("body").on("change", selector, function (e) {
console.log("trigger change");
const {value} = e.target
if (value) {
modelViewer.attr("src", value).show();
} else {
modelViewer.attr('src', '').hide();
createModelMaterialsOptions($modelPreview, [], index, variantBody);
}
});
modelViewer.on("load", function () {
console.log("variant model loaded");
if (modelSrc === modelViewer.attr("src")) {
createModelMaterialsOptions(
modelViewer[0],
materials,
index,
variantBody
);
} else {
createModelMaterialsOptions(modelViewer[0], [], index, variantBody);
}
});
if (modelSrc) {
modelViewer.attr("src", modelSrc).show();
} else {
createModelMaterialsOptions($modelPreview, materials, index, variantBody);
}
variantHeader.on("click", function () {
variantBody.slideToggle();
});
variant.find(".variant-delete").on("click", function () {
variant.remove();
});
if (!autoOpen) {
variantBody.slideUp();
}
$("#variant-container").append(variant);
return variant;
}
function clearAllVariants() {
console.log("delete all variants");
$("#variant-container .variant").find(".variant-delete").trigger("click");
}
function isPremiumPlan() {
return window.arModelViewerSettings.plan === "premium";
}
function initVariants() {
$("#variant-container").sortable({
handle: ".variant-header",
axis: "y",
containment: "#variant-container",
tolerance: "pointer",
});
$("#add-variant").on("click", function () {
const variant = createVariant();
return false;
});
$("#delete-all-variants").on("click", function () {
clearAllVariants();
return false;
});
}
function initPostMetaBox() {
const $postMetaBox = $("#ar-model-viewer-post-metabox");
if (!$postMetaBox.length) {
return;
}
$postMetaBox.append(
`<ul id="ar-features-listings"></ul>
${$feedbackHTML}`
);
const $dataObject = window.arModelViewerSettings || {};
renderInitialWithSettings($dataObject);
const listings = $("#ar-features-listings");
listings.append(`${variantForm()}`);
initVariants();
}
initPostMetaBox();
if (window.arModelViewerSettings) {
$modelPreview = document.getElementById(
window.arModelViewerSettings.modelId
);
if (!$modelPreview) {
return;
}
loadModel();
}
});