635 lines
No EOL
18 KiB
JavaScript
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();
|
|
}
|
|
}); |