const { jsPDF } = window.jspdf;
let finalAvatarData = null; // Здесь будет лежать результат кропа
let cropper = null; // Объект кроппера
// 1. Инициализация при загрузке
document.addEventListener('DOMContentLoaded', () => {
setupNavigation();
setupExperience();
setupEducation();
setupCropper();
const grid = document.getElementById('soft-skills-grid');
const counter = document.getElementById('skill-count');
const LIMIT = 5;
grid.addEventListener('change', () => {
const checkedInputs = grid.querySelectorAll('input[name="soft_skill"]:checked');
const uncheckedInputs = grid.querySelectorAll('input[name="soft_skill"]:not(:checked)');
counter.innerText = checkedInputs.length;
if (checkedInputs.length >= LIMIT) {
// Если выбрали 5, отключаем остальные
uncheckedInputs.forEach(input => input.disabled = true);
} else {
// Если меньше 5, включаем все назад
grid.querySelectorAll('input[name="soft_skill"]').forEach(input => input.disabled = false);
}
});
});
// 2. Навигация между шагами
function setupNavigation() {
const steps = document.querySelectorAll('.form-step');
// Для всех кнопок "Далее"
document.querySelectorAll('.next-btn').forEach((btn) => {
btn.onclick = (e) => {
e.preventDefault();
const currentStep = btn.closest('.form-step');
const nextStep = currentStep.nextElementSibling;
if (nextStep && nextStep.classList.contains('form-step')) {
currentStep.classList.remove('active');
nextStep.classList.add('active');
}
};
});
// Для всех кнопок "Назад"
document.querySelectorAll('.prev-btn').forEach((btn) => {
btn.onclick = (e) => {
e.preventDefault();
const currentStep = btn.closest('.form-step');
const prevStep = currentStep.previousElementSibling;
if (prevStep && prevStep.classList.contains('form-step')) {
currentStep.classList.remove('active');
prevStep.classList.add('active');
}
};
});
}
function setupExperience() {
const addBtn = document.getElementById('addExperience');
const list = document.getElementById('experience-list');
if (addBtn) {
addBtn.onclick = () => {
// 1. Проверяем лимит (5 элементов)
const currentItems = list.querySelectorAll('.experience-item').length;
if (currentItems >= 5) {
alert("Максимум 5 мест работы");
return;
}
const item = document.createElement('div');
item.className = 'experience-item';
item.innerHTML = `
`;
// 2. Обработчик удаления с проверкой лимита для кнопки
item.querySelector('.remove-btn').onclick = () => {
item.remove();
// Показываем кнопку назад, если элементов стало меньше 5
if (list.querySelectorAll('.experience-item').length < 5) {
addBtn.style.display = 'block';
}
};
list.appendChild(item);
// 3. Прячем кнопку, если достигли лимита
if (list.querySelectorAll('.experience-item').length >= 5) {
addBtn.style.display = 'none';
}
};
}
}
function setupEducation() {
const addBtn = document.getElementById('addEducation');
const list = document.getElementById('education-list');
if (addBtn) {
addBtn.onclick = () => {
// 1. Проверяем текущее количество добавленных учебных заведений
const currentItems = list.querySelectorAll('.education-item').length;
if (currentItems >= 5) {
alert("Можно добавить не более 5 учебных заведений");
return;
}
const item = document.createElement('div');
item.className = 'education-item';
item.innerHTML = `
`;
// 2. Обработчик удаления
item.querySelector('.remove-btn').onclick = () => {
item.remove();
// Если стало меньше 5, возвращаем кнопку добавления
if (list.querySelectorAll('.education-item').length < 5) {
addBtn.style.display = 'block';
}
};
list.appendChild(item);
// 3. Если достигли лимита, скрываем кнопку
if (list.querySelectorAll('.education-item').length >= 5) {
addBtn.style.display = 'none';
}
};
}
}
function getProfessionalSkills() {
const skills = {};
// Находим все родительские блоки навыков
const skillItems = document.querySelectorAll('.skill-item');
skillItems.forEach(item => {
const name = item.querySelector('.skill-name').innerText;
// Находим внутри этого блока выбранную радиокнопку
const checkedInput = item.querySelector('input[type="radio"]:checked');
skills[name] = checkedInput.value;
});
return skills;
}
function getSoftSkills() {
const softSkills = [];
// Находим все выбранные чекбоксы в сетке soft-skills
const checkedBoxes = document.querySelectorAll('#soft-skills-grid input[type="checkbox"]:checked');
checkedBoxes.forEach(box => {
softSkills.push(box.value);
});
return softSkills;
}
// --- ГЕНЕРАЦИЯ PDF ---
document.getElementById("generateBtn").addEventListener("click", generatePDF);
async function generatePDF() {
const doc = new jsPDF({ orientation: "portrait", unit: "mm", format: "a4" });
const fonts = [
{ url: "ProximaNova-Light.ttf", name: "Proxima-normal", style: "normal" },
{ url: "ProximaNova-Regular.ttf", name: "Proxima-normal", style: "italic" }, // Лайфхак: читайте ниже
{ url: "ProximaNova-Bold.ttf", name: "Proxima-normal", style: "bold" },
];
const backgroundUrl = "background.png";
try {
await loadFont(doc, fonts);
const background = await loadImage(backgroundUrl);
doc.internal.events.subscribe('addPage', () => {
doc.addImage(background, "PNG", 0, 0, 210, 297, undefined, "NONE");
});
doc.addImage(background, "PNG", 0, 0, 210, 297, undefined, "NONE");
const data = getFormData();
// Рендеринг в PDF (основано на вашем коде)
drawName(doc, data.name, 110, 32);
drawDolzh(doc, data.dolzh, 110, 50);
drawZp(doc, data.zp, 170, 55);
await drawAva(doc, data.avatar, 36, 22);
drawCity(doc, data.city, 117, 60);
drawTipZan(doc, data.tipZan, 110, 67);
drawFormRab(doc, data.formRab, 110, 74);
//let leftY = drawKontakt(doc, "КОНТАКТЫ", 20, 70);
//leftY = drawTextLine(doc, `Дата рожд.: ${data.birthDate}`, 20, leftY);
//leftY = drawTextLine(doc, `${data.phone}`, 20, leftY);
//leftY = drawTextLine(doc, `Город: ${data.city}`, 20, leftY);
generateTableLeft(doc, data);
await drawCircles(doc, data);
await generateTableAbout(doc, data.about, 110, 78, data);
let rightY = 110;
//rightY = drawParagraph(doc, `О себе: ${data.about}`, 95, rightY, 100);
// Вывод опыта работы (простой список для примера)
//таблицей будет
// jobs тут массив с опытом работы
doc.save("resume.pdf");
sendGS(data)
} catch (error) {
console.error("Ошибка:", error);
alert("Ошибка при генерации. Проверьте консоль.");
}
}
function phoneNumber(phone){
let output = "";
phone = phone.replace(/\D/g, '');
if(phone[0] == "7"){
output = phone.replace(/(\d{1})(\d{3})(\d{3})(\d{2})(\d{2})/, '+$1 $2 $3-$4-$5');
}//+7 999 999-99-99
else{
output = phone.replace(/(\d{1})(\d{3})(\d{3})(\d{2})(\d{2})/, '$1 $2 $3-$4-$5');
}
return output
}
function smartWrap(text, limit) {
if (!text || text.length <= limit) return text;
const words = text.split(' ');
let currentLine = '';
let result = [];
words.forEach(word => {
// Проверяем: если добавить слово, превысим ли мы лимит?
if ((currentLine + word).length > limit) {
result.push(currentLine.trim()); // Сохраняем готовую строку
currentLine = word + ' '; // Начинаем новую строку с этого слова
} else {
currentLine += word + ' '; // Добавляем слово в текущую строку
}
});
result.push(currentLine.trim()); // Добавляем последний кусок
return result.join('\n'); // Склеиваем всё через перенос строки
}
function generateTableLeft(doc, data){
body = [[{content: `КОНТАКТЫ`, styles: {fontSize: 14, textColor:['#ffffff'], cellPadding: {bottom: 4}}}]];
body.push([{content: `${phoneNumber(data.phone)}`, styles:{fontSize: 12, textColor:['#ffffff'], cellPadding:{bottom: 4, left: 6}}}]);
body.push([{content: `${data.email}`, styles:{fontSize: 12, textColor:['#ffffff'], cellPadding:{bottom: 4, left: 6}}}]);
body.push([{content: `${data.vk}`, styles:{fontSize: 12, textColor:['#ffffff'], cellPadding:{bottom: 6, left: 6}}}]);
body.push([{content: `ПРОФЕССИОНАЛЬНЫЕ НАВЫКИ`, styles:{fontSize: 14, textColor:['#ffffff'], cellPadding:{top:2, bottom: 4}}}]);
for(let i of Object.keys(data.proskills)){
body.push([{content: `${smartWrap(i, 18)}`, styles:{fontSize: 12, textColor:['#ffffff'], cellPadding:{bottom: 2}}}]);
}
body.push([{content: `ГИБКИЕ НАВЫКИ`, styles:{fontSize: 14, textColor:['#ffffff'], cellPadding:{top:6, bottom: 4}}}]);
for(let i of data.softskills){
body.push([{content: `${i}`, styles:{fontSize: 12, textColor:['#ffffff'], cellPadding:{bottom: 2}}}]);
}
doc.autoTable({
startY: 70,
margin: { left: 20},
tableWidth: 75,
body: body,
theme: "plain", //без границ
styles: {
font: "Proxima-normal",
fontStyle:"italic"
}
});
};
async function generateTableAbout(doc, about, x, y, data) {
let currentY = y;
const iconX = 190; // Координата X для иконок (подобрана для правого края)
const iconSize = 12; // Размер иконки в мм
// Вспомогательная внутренняя функция, чтобы не дублировать код
const addSection = async (title, body, iconPath, LinePath) => {
// 1. Сначала загружаем картинки (ждем их)
const iconImg = await loadImage(iconPath);
const lineImg = LinePath ? await loadImage(LinePath) : null;
// 2. РИСУЕМ ГРАФИКУ СРАЗУ (до таблицы!)
// В этот момент currentY — это верхняя точка будущего раздела
if (iconImg) {
let iconYShift = 0;
if (title === "О СЕБЕ") iconYShift = -3;
else if (title === "ОПЫТ РАБОТЫ") iconYShift = -2.8;
else if (title === "ОБРАЗОВАНИЕ") iconYShift = -2.8;
else if (title === "ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ") iconYShift += 1;
else if (title === "ДОСТИЖЕНИЯ") iconYShift = -2.8;
// Рисуем иконку. Она попадет на текущую страницу.
doc.addImage(iconImg, 'PNG', iconX, currentY + iconYShift, iconSize, iconSize);
}
if (lineImg) {
let lineYShift = 0;
let lineWidth = 30;
let lineX = 155;
if (title === "О СЕБЕ") {
lineYShift = 2.75; lineWidth = 52; lineX = 133;
} else if (title === "ОПЫТ РАБОТЫ") {
lineYShift = 3;
} else if (title === "ОБРАЗОВАНИЕ") {
lineYShift = 2.5;
} else if (title === "ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ"){
lineWidth = 15;
lineYShift = 6.5;
lineX = 170;
} else if (title === "ДОСТИЖЕНИЯ"){
lineYShift = 3; lineWidth = 32; lineX = 155;
}
// Рисуем палочку. Она тоже попадет на текущую страницу.
doc.addImage(lineImg, 'PNG', lineX, currentY + lineYShift, lineWidth, 0.3);
}
// 3. ТЕПЕРЬ РИСУЕМ ТАБЛИЦУ
// Она начнется ровно в currentY, поверх или рядом с графикой
doc.autoTable({
startY: currentY,
// margin.top: 45 защитит от наезда на лого ТОЛЬКО на новых листах
margin: { left: x, top: 25 },
theme: "plain",
styles: { font: "Proxima-normal", cellPadding: 0, lineWidth: 0 },
body: body
});
// 4. И только теперь обновляем Y для следующего раздела
// Используем finalY, чтобы знать, где закончился текст
currentY = doc.lastAutoTable.finalY + 10;
};
// 1. ТАБЛИЦА: О СЕБЕ
await addSection(
"О СЕБЕ",
[
[{ content: "О СЕБЕ", styles: { fontSize: 16, textColor: ['#6d5298'], fontStyle: "italic", cellPadding: { bottom: 2 } } }],
[{ content: `Дата рождения: ${data.birthDate}`, styles: { fontSize: 12, textColor: ['#cf794b'] ,cellPadding: { left: 7 }} }],
[{ content: about || "Не указано", styles: { fontSize: 14, cellPadding: { top: 2 } } }]
],
'images/о себе.png',
'images/палочка 1.png'
);
// 2. ТАБЛИЦА: ОПЫТ РАБОТЫ
let expBody = [[{ content: "ОПЫТ РАБОТЫ", styles: { fontSize: 16, textColor: ['#6d5298'], fontStyle: "italic", cellPadding: { bottom: 2 } } }]];
if (data.jobs.length === 0) {
expBody.push([{ content: "Не указано", styles: { fontSize: 14 } }]);
} else {
data.jobs.forEach(job => {
expBody.push([{ content: job.company, styles: { fontSize: 14, fontStyle: "bold", cellPadding: { top: 2 } } }]);
expBody.push([{ content: job.role, styles: { fontSize: 14 } }]);
expBody.push([{ content: `(${job.start} - ${job.end})`, styles: { fontSize: 12, textColor: [100, 100, 100] } }]);
expBody.push([{ content: job.desc, styles: { fontSize: 13, cellPadding: { bottom: 3 } } }]);
});
}
await addSection("ОПЫТ РАБОТЫ", expBody, 'images/опыт.png', 'images/палочка 2.png');
// 3. ТАБЛИЦА: ОБРАЗОВАНИЕ
let eduBody = [[{ content: "ОБРАЗОВАНИЕ", styles: { fontSize: 16, textColor: ['#6d5298'], fontStyle: "italic", cellPadding: { bottom: 2 } } }]];
if (data.education.length > 0) {
data.education.forEach(edu => {
eduBody.push([{ content: `${edu.lvl} - ${edu.school}`, styles: { fontSize: 14, cellPadding: { top: 2 } } }]);
eduBody.push([{ content: `${edu.spec} | ${edu.year} г.`, styles: { fontSize: 12, cellPadding: { bottom: 2 } } }]);
});
} else {
eduBody.push([{ content: "Не указано", styles: { fontSize: 14 } }]);
}
await addSection("ОБРАЗОВАНИЕ", eduBody, 'images/образование.png', 'images/палочка 3.png');
//4.ДОСТИЖЕНИЯ
let achBody = [[{content: "ДОСТИЖЕНИЯ", styles: { fontSize: 16, textColor: ['#6d5298'], fontStyle: "italic", cellPadding: { bottom: 2 } } }]];
await addSection(
"ДОСТИЖЕНИЯ",
[
[{ content: "ДОСТИЖЕНИЯ", styles: { fontSize: 16, textColor: ['#6d5298'], fontStyle: "italic", cellPadding: { bottom: 2 } } }],
[{ content: data.achievements, styles: { fontSize: 14, cellPadding: { top: 1 } } }]
],
'images/достижения.png',
"images/палочка 5.png"
);
// 5. ТАБЛИЦА: ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ
await addSection(
"ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ",
[
[{ content: "ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ", styles: { fontSize: 16, textColor: ['#6d5298'], fontStyle: "italic", cellPadding: { bottom: 2 } } }],
[{ content: `Готовность к переезду: ${data.relocation}`, styles: { fontSize: 12, cellPadding: { top: 1 } } }],
[{ content: `Водительские права: ${data.driverLicense}`, styles: { fontSize: 12 } }]
],
'images/допинфо.png',
"images/палочка 4.png"
);
}
async function drawCircles(doc, data){
//let icon = await loadImage(`images/0 точка.png`);
let x = 67;
let y = 126.5;
let icon = await loadImage(`images/${data.proskills["Деловая переписка"]} точка.png`);
doc.addImage(icon, 'PNG', x, y, 28, 3.5);
icon = await loadImage(`images/${data.proskills["Планирование занятости"]} точка.png`);
doc.addImage(icon, 'PNG', x, y+=10, 28, 3.5);
icon = await loadImage(`images/${data.proskills["Администрирование процессов"]} точка.png`);
doc.addImage(icon, 'PNG', x, y+=12, 28, 3.5);
icon = await loadImage(`images/${data.proskills["Корпоративная культура"]} точка.png`);
doc.addImage(icon, 'PNG', x, y+=12, 28, 3.5);
icon = await loadImage(`images/${data.proskills["Управление проектами"]} точка.png`);
doc.addImage(icon, 'PNG', x, y+=11, 28, 3.5);
//ОТРИСОВКА ПАЛОК НА ФИОЛЕТОВОМ ФОНЕ
icon = await loadImage(`images/линия на фиолетовом фоне.png`);
doc.addImage(icon, 'PNG', 25, 106.5, 62, 0.1);
doc.addImage(icon, 'PNG', 25, 181.5, 62, 0.1);
//ОТРИСОВКА ЗНАЧКОВ В ЛЕВОМ СТОЛБЦЕ
icon = await loadImage(`images/телефон1.png`);
doc.addImage(icon, 'PNG', 17, 78, 7, 7);
icon = await loadImage(`images/почта1.png`);
doc.addImage(icon, 'PNG', 17, 87.5, 7, 7);
//icon = await loadImage(`images/телефон.png`);
//doc.addImage(icon, 'PNG', 16, 77, 9, 9);
//icon = await loadImage(`images/почта.png`);
//doc.addImage(icon, 'PNG', 16, 86.5, 9, 9);
icon = await loadImage(`images/вк.png`);
doc.addImage(icon, 'PNG', 17, 96, 7, 7);
icon = await loadImage(`images/локация.png`);
doc.addImage(icon, 'PNG', 108, 55, 7, 7);
icon = await loadImage(`images/люди.png`);
doc.addImage(icon, 'PNG', 108, 84.5, 7, 7);
}
function drawTipZan(doc, text, x, y){
text = `Тип занятости: ${text}`
doc.setFontSize(12);
doc.setTextColor(0);
doc.text(text, x, y)
}
function drawFormRab(doc, text, x, y){
text = `Форма работы: ${text}`
doc.setFontSize(12);
doc.setTextColor(0);
doc.text(text, x, y)
}
function drawCity(doc, text, x, y){
text = `г. ${text}`
doc.setFontSize(12);
doc.setTextColor('#cf794b');
doc.text(text, x, y)
}
function getFormData() {
const rawName = document.getElementById("fullName").value || "ФАМИЛИЯ ИМЯ ОТЧЕСТВО";
const parts = rawName.split(" ");
// Сбор данных об опыте
const jobs = [];
document.querySelectorAll('.experience-item').forEach(item => {
const isCurrent = item.querySelector('.exp-current').checked;
const endDate = item.querySelector('.exp-end').value;
jobs.push({
company: item.querySelector('.exp-company').value || "Компания",
role: item.querySelector('.exp-role').value || "Должность",
start: item.querySelector('.exp-start').value,
end: isCurrent ? "по н.в." : (endDate || "—"),
current: isCurrent,
desc: item.querySelector('.exp-desc').value
});
});
const education = [];
document.querySelectorAll('.education-item').forEach(item => {
education.push({
lvl: item.querySelector('.edu-lvl').options[item.querySelector('.edu-lvl').selectedIndex].text,
school: item.querySelector('.edu-school').value || "Учебное заведение",
spec: item.querySelector('.edu-spec').value || "Специальность",
year: item.querySelector('.edu-year').value || ""
});
});
const selectedLicenses = [];
document.querySelectorAll('#driver-license-group input:checked').forEach(cb => {
selectedLicenses.push(cb.value);
});
return {
name: [parts[0]?.toUpperCase() || "", `${parts[1] || ""} ${parts[2] || ""}`.trim()],
phone: document.getElementById("phone").value || "Не указано",
email: document.getElementById("email").value || "Не указано",
vk: document.getElementById("vk").value || "Не указано",
dolzh: document.getElementById("dolzh").value || "Не указано",
zp: document.getElementById("zp").value || "Не указано",
avatar: document.getElementById("avatar").files[0] || null,
city: document.getElementById("city").value || "Не указано",
birthDate: document.getElementById("birthDate").value || "Не указано",
about: document.getElementById("about").value || "",
jobs: jobs,
formRab: document.querySelector('.formRab').options[document.querySelector('.formRab').selectedIndex].text,
tipZan: document.querySelector('.tip-Zan').options[document.querySelector('.tip-Zan').selectedIndex].text,
education: education,
relocation: document.getElementById("relocation").value || "Не указано",
driverLicense: selectedLicenses.length > 0 ? selectedLicenses.join(", ") : "Нет",
proskills: getProfessionalSkills(),
softskills: getSoftSkills(),
achievements: document.getElementById("achievements").value || "Не указано"
};
}
function setupCropper() {
const avatarInput = document.getElementById('avatar');
const imageToEdit = document.getElementById('image-to-edit');
const cropperContainer = document.getElementById('cropper-container');
const saveBtn = document.getElementById('save-crop');
avatarInput.addEventListener('change', function(e) {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(event) {
imageToEdit.src = event.target.result;
cropperContainer.style.display = 'block';
if (cropper) cropper.destroy();
// Инициализация Cropper.js
cropper = new Cropper(imageToEdit, {
aspectRatio: 1, // Квадрат
viewMode: 1,
autoCropArea: 1
});
};
reader.readAsDataURL(file);
});
saveBtn.onclick = () => {
const canvas = cropper.getCroppedCanvas({
width: 800,
height: 800
});
// Сохраняем результат в глобальную переменную
finalAvatarData = canvas.toDataURL('image/jpeg');
// Скрываем редактор
cropperContainer.style.display = 'none';
};
}
// --- Вспомогательные функции (остаются без изменений из вашего кода) ---
async function loadFont(doc, fonts) {
for (const font of fonts) {
const response = await fetch(font.url);
const buffer = await response.arrayBuffer();
const base64 = await arrayBufferToBase64(buffer);
const fileName = font.url.split('/').pop();
doc.addFileToVFS(fileName, base64);
doc.addFont(fileName, font.name, font.style);
}
doc.setFont("Proxima-normal", "normal");
}
async function loadImage(url) {
const response = await fetch(url);
if (!response.ok) throw new Error(`Ошибка загрузки: ${response.statusText}`);
const arrayBuffer = await response.arrayBuffer();
// Возвращаем как массив байтов
return new Uint8Array(arrayBuffer);
}
function arrayBufferToBase64(buffer) {
return new Promise(resolve => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result.split(',')[1]);
reader.readAsDataURL(new Blob([buffer]));
});
}
function drawName(doc, nameLines, x, y) {
doc.setFontSize(24);
doc.setTextColor(0);
doc.setFont("Proxima-normal", "italic");
doc.text(nameLines, x, y);
doc.setFont("Proxima-normal", "normal");
}
function drawDolzh(doc, text, x, y) {
doc.setFontSize(18);
doc.text(text, x, y);
}
function drawZp(doc, text, x, y) {
doc.setFontSize(18);
doc.text(`${text.replace(/\D/g, '')} руб.`, x, y);
}
function drawKontakt(doc, text, x, y) {
doc.setFontSize(14);
doc.setTextColor(255);
doc.setFont("Proxima-normal", "italic");
doc.text(text, x, y);
doc.setFont("Proxima-normal", "normal");
return y + 8;
}
function drawSectionTitle(doc, text, x, y) {
doc.setFontSize(14);
doc.setTextColor(0);
doc.text(text, x, y);
return y + 8;
}
function drawTextLine(doc, text, x, y) {
doc.setFontSize(10);
doc.text(text, x, y);
return y + 6;
}
function drawParagraph(doc, text, x, y, width) {
doc.setFontSize(10);
const lines = doc.splitTextToSize(text, width);
doc.text(lines, x, y);
return y + (lines.length * 5) + 5;
}
async function drawAva(doc, file, x, y) {
// Теперь мы игнорируем аргумент file и берем обрезанное фото
if (finalAvatarData) {
doc.addImage(finalAvatarData, "JPEG", x, y, 40, 40);
}
}
async function getCroppedCircleImage(file, size = 200) {
return new Promise((resolve) => {
const img = new Image();
const reader = new FileReader();
reader.onload = (e) => img.src = e.target.result;
img.onload = () => {
const canvas = document.createElement("canvas");
canvas.width = size; canvas.height = size;
const ctx = canvas.getContext("2d");
ctx.beginPath(); ctx.arc(size/2, size/2, size/2, 0, Math.PI*2); ctx.clip();
const minSide = Math.min(img.width, img.height);
ctx.drawImage(img, (img.width-minSide)/2, (img.height-minSide)/2, minSide, minSide, 0, 0, size, size);
resolve(canvas.toDataURL("image/png"));
};
reader.readAsDataURL(file);
});
}
function sendGS(dataa){
const url = 'https://script.google.com/macros/s/AKfycbyXpxzQVBOt1nd-Y2lb2AOzqPnUxC55GkeUmz9NYLpnhsWOqivUCbryyQj1Q15KF7Ma/exec';
const data = {
token: "MySeCrEttttttOkEn",
name: dataa.name,
birthday: dataa.birthday,
phone: dataa.phone,
email : dataa.email,
tg: dataa.vk,
dolzh: dataa.dolzh,
zp: dataa.zp,
tipZan: dataa.tipZan,
formRab: dataa.formRab,
city: dataa.city,
about: dataa.about,
relocation: dataa.relocation,
driverLicense: dataa.driverLicense,
jobs: dataa.jobs,
education: dataa.education,
proskills: dataa.proskills,
softskills: dataa.softskills
};
fetch(url, {
method: 'POST',
mode: 'no-cors', // Важно для Apps Script
body: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' }
})
}