mirror of
https://github.com/PretendoNetwork/website.git
synced 2025-04-02 11:11:50 -04:00
474 lines
13 KiB
JavaScript
474 lines
13 KiB
JavaScript
// This file gets automatically bundled with browserify when running the start script. This also means that after any update you're gonna need to restart the server to see any changes.
|
|
|
|
// Prevent the user from reloading or leaving the page
|
|
window.onbeforeunload = function (e) {
|
|
e?.preventDefault();
|
|
e.returnValue = '';
|
|
};
|
|
|
|
// this makes it so the canvas fits in the target element
|
|
function setCanvasScale() {
|
|
let targetX;
|
|
let targetY;
|
|
|
|
if (window.innerWidth <= 1080) {
|
|
const canvasWrapper = document.querySelector('.canvas-wrapper');
|
|
|
|
targetX = canvasWrapper.offsetWidth;
|
|
targetY = canvasWrapper.offsetHeight;
|
|
} else {
|
|
targetX = window.innerWidth * 0.9;
|
|
targetY = window.innerHeight * 0.9;
|
|
}
|
|
|
|
const canvas = document.querySelector('canvas#miiCanvas');
|
|
const XScale = targetX / canvas.width;
|
|
const YScale = targetY / canvas.height;
|
|
canvas.style.transform = `scale(${Math.min(XScale, YScale)})`;
|
|
}
|
|
|
|
setCanvasScale();
|
|
window.addEventListener('resize', () => {
|
|
setCanvasScale();
|
|
});
|
|
|
|
// MII RENDERER
|
|
|
|
const Mii = require('mii-js');
|
|
|
|
// The Mii data is stored in a script tag in the HTML, so we can just grab it and then remove the element
|
|
const encodedUserMiiData = document.querySelector(
|
|
'script#encodedUserMiiData'
|
|
).textContent;
|
|
document.querySelector('script#encodedUserMiiData').remove();
|
|
console.log('encodedMiiData', encodedUserMiiData);
|
|
|
|
// We initialize the Mii object
|
|
const mii = new Mii(Buffer.from(encodedUserMiiData, 'base64'));
|
|
|
|
// We set the img sources for the unedited miis in the save animation
|
|
const miiStudioNeutralUrl = mii.studioUrl({
|
|
width: 512,
|
|
bgColor: '13173300',
|
|
});
|
|
const miiStudioSorrowUrl = mii.studioUrl({
|
|
width: 512,
|
|
bgColor: '13173300',
|
|
expression: 'sorrow',
|
|
});
|
|
document.querySelector('.mii-comparison img.old-mii').src = miiStudioNeutralUrl;
|
|
document.querySelector('.mii-comparison.confirmed img.old-mii').src =
|
|
miiStudioSorrowUrl;
|
|
|
|
// we keeep the images here so we can cache them when we need to change the build/height
|
|
const miiFaceImg = new Image();
|
|
const baldMiiFaceImg = new Image();
|
|
const miiBodyImg = new Image();
|
|
|
|
// Initial mii render
|
|
renderMii();
|
|
// This function renders the Mii on the canvas
|
|
function renderMii(heightOverride, buildOverride) {
|
|
const canvas = document.querySelector('canvas#miiCanvas');
|
|
const ctx = canvas.getContext('2d');
|
|
const height = heightOverride || mii.height;
|
|
const build = buildOverride || mii.build;
|
|
|
|
// if there isn't an override or the images haven't been cached, we load the images
|
|
if ((!heightOverride && !buildOverride) || !miiFaceImg.src || !baldMiiFaceImg.src || !miiBodyImg.src) {
|
|
canvas.style.filter = 'blur(4px) brightness(70%)';
|
|
|
|
// we create a copy of the mii and make it bald
|
|
const baldMii = Object.create(
|
|
Object.getPrototypeOf(mii),
|
|
Object.getOwnPropertyDescriptors(mii)
|
|
);
|
|
baldMii.hairType = 30;
|
|
baldMiiFaceImg.src = baldMii.studioUrl({
|
|
width: 512,
|
|
bgColor: '13173300',
|
|
type: 'face_only',
|
|
});
|
|
miiFaceImg.src = mii.studioUrl({
|
|
width: 512,
|
|
bgColor: '13173300',
|
|
type: 'face_only',
|
|
});
|
|
miiBodyImg.src = mii.studioAssetUrlBody();
|
|
}
|
|
|
|
// misc calculations
|
|
const bodyWidth = (build * 1.7 + 220) * (0.003 * height + 0.6);
|
|
const bodyHeight = height * 3.5 + 227;
|
|
const bodyXPos = (canvas.width - bodyWidth) / 2;
|
|
const bodyYPos = canvas.height - bodyHeight;
|
|
const headYPos = bodyYPos - 408;
|
|
|
|
// we make sure every image is loaded before rendering
|
|
if (miiFaceImg.complete) {
|
|
onMiiFaceImgLoad();
|
|
} else {
|
|
miiFaceImg.onload = () => {
|
|
onMiiFaceImgLoad();
|
|
};
|
|
}
|
|
function onMiiFaceImgLoad() {
|
|
if (miiBodyImg.complete) {
|
|
onBodyImgLoad();
|
|
} else {
|
|
miiBodyImg.onload = () => {
|
|
onBodyImgLoad();
|
|
};
|
|
}
|
|
}
|
|
function onBodyImgLoad() {
|
|
if (baldMiiFaceImg.complete) {
|
|
onBaldMiiFaceImgLoad();
|
|
} else {
|
|
baldMiiFaceImg.onload = () => {
|
|
onBaldMiiFaceImgLoad();
|
|
};
|
|
}
|
|
}
|
|
function onBaldMiiFaceImgLoad() {
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
ctx.drawImage(miiFaceImg, 0, headYPos);
|
|
ctx.drawImage(miiBodyImg, bodyXPos, bodyYPos, bodyWidth, bodyHeight);
|
|
|
|
// we draw a portion of the bald mii on top of the normal mii to hide the mii's neck (see https://i.imgur.com/U0fpkwi.png)
|
|
ctx.drawImage(baldMiiFaceImg, 186, 384, 140, 120, 186, headYPos + 384, 140, 120);
|
|
canvas.style.filter = '';
|
|
}
|
|
|
|
if (!heightOverride && !buildOverride) {
|
|
const faceMiiStudioUrl = mii.studioUrl({
|
|
width: 512,
|
|
bgColor: '13173300',
|
|
});
|
|
|
|
const faceMiiStudioSmileUrl = mii.studioUrl({
|
|
width: 512,
|
|
bgColor: '13173300',
|
|
expression: 'smile'
|
|
});
|
|
|
|
// sets the new mii in the save tab to the new mii
|
|
document.querySelector('.mii-comparison img.new-mii').src = faceMiiStudioUrl;
|
|
document.querySelector('.mii-comparison.confirmed img.new-mii').src = faceMiiStudioSmileUrl;
|
|
}
|
|
}
|
|
|
|
// This function updates a prop of the Mii and rerenders it
|
|
function updateMii(e) {
|
|
const prop = e.target.name;
|
|
let value = e.target.value || e.target.defaultValue;
|
|
|
|
// if the value comes from a checkbox, we use the checked property
|
|
if (value === 'on' || value === 'off') {
|
|
value = e.target.checked;
|
|
}
|
|
|
|
// if the prop is disableSharing, we set the value to the opposite of the current value
|
|
if (prop === 'disableSharing') {
|
|
value = !value;
|
|
}
|
|
|
|
// Handle booleans, on/offs and strings
|
|
if (value === 'true' || value === 'false') {
|
|
mii[prop] = value === 'true';
|
|
} else if (value === 'on' || value === 'off') {
|
|
mii[prop] = value === 'on';
|
|
} else if (isNaN(parseInt(value))) {
|
|
mii[prop] = value;
|
|
} else {
|
|
mii[prop] = parseInt(value);
|
|
}
|
|
|
|
// if the user is editing the height or the build, we render the mii with the correct override, else we do a straight render
|
|
if (prop === 'height') {
|
|
renderMii(value, false);
|
|
} else if (prop === 'build') {
|
|
renderMii(false, value);
|
|
} else {
|
|
renderMii();
|
|
console.log(mii);
|
|
}
|
|
}
|
|
|
|
function handleCalendar(e) {
|
|
const valueArray = e.target.value.split('-');
|
|
const day = valueArray[2];
|
|
const month = valueArray[1];
|
|
|
|
mii.birthDay = parseInt(day);
|
|
mii.birthMonth = parseInt(month);
|
|
}
|
|
|
|
function preventEmpty(e) {
|
|
if (e.target.value !== '') return;
|
|
|
|
e.target.value = e.target.defaultValue;
|
|
}
|
|
|
|
document.querySelectorAll('fieldset').forEach((fieldset) => {
|
|
fieldset.addEventListener('change', updateMii);
|
|
});
|
|
document.querySelectorAll('input[type=\'range\']').forEach((input) => {
|
|
input.addEventListener('input', updateMii);
|
|
});
|
|
document
|
|
.querySelectorAll('input[type=\'text\'], input[type=\'number\']')
|
|
.forEach((input) => {
|
|
input.addEventListener('blur', preventEmpty);
|
|
});
|
|
document
|
|
.querySelector('input[type=\'date\']#birthDate')
|
|
.addEventListener('change', handleCalendar);
|
|
|
|
// FORM
|
|
|
|
// Here we preselect the options corresponding to the Mii's current values
|
|
[
|
|
'faceType',
|
|
'skinColor',
|
|
'makeupType',
|
|
'wrinklesType',
|
|
'hairType',
|
|
'hairColor',
|
|
'eyebrowType',
|
|
'eyebrowColor',
|
|
'eyeType',
|
|
'eyeColor',
|
|
'noseType',
|
|
'mouthType',
|
|
'mouthColor',
|
|
'glassesType',
|
|
'glassesColor',
|
|
'beardType',
|
|
'facialHairColor',
|
|
'mustacheType',
|
|
'moleEnabled',
|
|
'gender',
|
|
'favoriteColor',
|
|
].forEach((prop) => {
|
|
const el = document.querySelector(`#${prop}${mii[prop]}`);
|
|
if (el) {
|
|
el.checked = true;
|
|
}
|
|
console.log(`[info] preselected value for ${prop}`);
|
|
});
|
|
|
|
['favorite', 'allowCopying'].forEach((prop) => {
|
|
const el = document.querySelector(`#${prop}`);
|
|
if (el) {
|
|
el.checked = mii[prop];
|
|
}
|
|
console.log(`[info] preselected value for ${prop}`);
|
|
});
|
|
|
|
document.querySelector('#disableSharing').checked = !mii.disableSharing;
|
|
console.log('[info] preselected value for disableSharing');
|
|
|
|
[
|
|
'eyebrowYPosition',
|
|
'eyebrowSpacing',
|
|
'eyebrowRotation',
|
|
'eyebrowScale',
|
|
'eyebrowVerticalStretch',
|
|
'eyeYPosition',
|
|
'eyeSpacing',
|
|
'eyeRotation',
|
|
'eyeScale',
|
|
'eyeVerticalStretch',
|
|
'noseYPosition',
|
|
'noseScale',
|
|
'mouthYPosition',
|
|
'mouthScale',
|
|
'mouthHorizontalStretch',
|
|
'glassesYPosition',
|
|
'glassesScale',
|
|
'mustacheYPosition',
|
|
'mustacheScale',
|
|
'moleYPosition',
|
|
'moleXPosition',
|
|
'moleScale',
|
|
'height',
|
|
'build',
|
|
'miiName',
|
|
'creatorName',
|
|
].forEach((prop) => {
|
|
document.querySelector(`#${prop}`).value = mii[prop];
|
|
document.querySelector(`#${prop}`).defaultValue = mii[prop];
|
|
console.log(`[info] preselected value for ${prop}`);
|
|
});
|
|
|
|
const paddedBirthDay = mii.birthDay.toString().padStart(2, '0');
|
|
const paddedBirthMonth = mii.birthMonth.toString().padStart(2, '0');
|
|
document.querySelector(
|
|
'input[type=\'date\']#birthDate'
|
|
).value = `2024-${paddedBirthMonth}-${paddedBirthDay}`;
|
|
console.log('[info] preselected value for birthMonth && birthDay');
|
|
|
|
// TABS, SUBTABS, AND ALL THE INHERENT JANK
|
|
|
|
function openTab(e, tabType) {
|
|
e.preventDefault();
|
|
|
|
// Deselect all subpages
|
|
document
|
|
.querySelectorAll('.subtab.has-subpages .subpage.active')
|
|
.forEach((el) => {
|
|
el.classList?.remove('active');
|
|
});
|
|
document.querySelectorAll('.subtab.active').forEach((el) => {
|
|
el.classList?.remove('active');
|
|
});
|
|
|
|
const buttonReplacement =
|
|
tabType.charAt(0).toUpperCase() + tabType.slice(1);
|
|
|
|
document.querySelectorAll(`.${tabType}.active`).forEach((el) => {
|
|
el?.classList?.remove('active');
|
|
});
|
|
document.querySelectorAll(`.${tabType}btn.active`).forEach((el) => {
|
|
el?.classList?.remove('active');
|
|
});
|
|
|
|
const elementID = e.target?.id;
|
|
document.querySelector(`#${elementID}`).classList.add('active');
|
|
const selectedID = elementID
|
|
.replace('SubButton', '')
|
|
.replace('Button', buttonReplacement);
|
|
document.querySelector(`#${selectedID}`).classList.add('active');
|
|
|
|
if (tabType === 'tab') {
|
|
// Click the first subtab button, if there is one
|
|
document.querySelector(`#${selectedID} .subtabbtn`)?.click();
|
|
}
|
|
|
|
setCanvasScale();
|
|
|
|
// We hide all subpages
|
|
document.querySelectorAll('.subpage').forEach((el) => {
|
|
el.classList.remove('active');
|
|
});
|
|
|
|
// Selects the first subpage if there is one
|
|
document.querySelector(`#${selectedID} .subpage`)?.classList?.add('active');
|
|
}
|
|
|
|
// Here we bind all of the functions to the corresponding buttons
|
|
document.querySelectorAll('.tabs button.tabbtn').forEach((el) => {
|
|
el.addEventListener('click', (e) => openTab(e, 'tab'));
|
|
});
|
|
document.querySelectorAll('.subtabs button.subtabbtn').forEach((el) => {
|
|
el.addEventListener('click', (e) => openTab(e, 'subtab'));
|
|
});
|
|
|
|
// SUBPAGES
|
|
|
|
function paginationHandler(e) {
|
|
e.preventDefault();
|
|
|
|
// We hide all subpages
|
|
document.querySelectorAll('.subpage').forEach((el) => {
|
|
el.classList.remove('active');
|
|
});
|
|
|
|
// We get the current subpage
|
|
const currentPageIndex = parseInt(
|
|
e.target.classList[2].replace('index-', '')
|
|
);
|
|
let newPageIndex = currentPageIndex;
|
|
|
|
// We calculate the new subpage
|
|
if (e.target.classList.contains('next')) {
|
|
newPageIndex += 1;
|
|
} else {
|
|
newPageIndex -= 1;
|
|
}
|
|
|
|
// We find the new subpage and activate it
|
|
e.target.parentNode.parentNode.parentNode.children[
|
|
newPageIndex
|
|
].classList.add('active');
|
|
}
|
|
|
|
// This adds 1 to the rendered page indexes to make them start from 1 instead of 0
|
|
document.querySelectorAll('span.current-page-index').forEach((el) => {
|
|
el.textContent = parseInt(el.textContent) + 1;
|
|
});
|
|
|
|
// Here we bind the functions to the corresponding buttons
|
|
document.querySelectorAll('button.page-btn').forEach((el) => {
|
|
el.addEventListener('click', paginationHandler);
|
|
});
|
|
|
|
// mii saving business (animation jank & actual saving)
|
|
document
|
|
.querySelector('#saveTab #saveButton')
|
|
.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
|
|
document
|
|
.querySelector('#saveTab #saveButton')
|
|
.classList.add('inactive', 'fade-out');
|
|
document.querySelector('.tabs').style.pointerEvents = 'none';
|
|
document.querySelector('.mii-comparison.confirmed').style.opacity = 1;
|
|
document
|
|
.querySelector('#saveTab p.save-prompt')
|
|
.classList.add('fade-out');
|
|
|
|
setTimeout(() => {
|
|
document.querySelector(
|
|
'.mii-comparison.unconfirmed'
|
|
).style.opacity = 0;
|
|
}, 500);
|
|
|
|
setTimeout(() => {
|
|
document
|
|
.querySelector('.mii-comparison.confirmed .old-mii')
|
|
.classList.add('fade-out');
|
|
document
|
|
.querySelector('.mii-comparison.confirmed svg')
|
|
.classList.add('fade-out');
|
|
}, 1500);
|
|
|
|
setTimeout(() => {
|
|
document
|
|
.querySelector('.mii-comparison.confirmed .new-mii-wrapper')
|
|
.classList.add('centered-mii-img');
|
|
}, 2000);
|
|
|
|
try {
|
|
const miiData = mii.encode().toString('base64');
|
|
const tokenType = document.cookie.split('; ').find(row => row.startsWith('token_type=')).split('=')[1];
|
|
const accessToken = document.cookie.split('; ').find(row => row.startsWith('access_token=')).split('=')[1];
|
|
|
|
fetch('http://api.pretendo.cc/v1/user', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Accept': 'application/json',
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `${tokenType} ${decodeURIComponent(accessToken)}`
|
|
},
|
|
body: JSON.stringify({
|
|
mii: {
|
|
name: mii.miiName,
|
|
primary: 'Y',
|
|
data: miiData,
|
|
}
|
|
})
|
|
})
|
|
.then(({ status }) => {
|
|
if (status === 200) {
|
|
window.onbeforeunload = null;
|
|
window.location.assign('/account');
|
|
}
|
|
})
|
|
.catch(console.log);
|
|
// CHECK IF MII IS VALID SERVERSIDE
|
|
} catch (error) {
|
|
alert(error);
|
|
}
|
|
});
|