(function () { "use strict"; const encoder = new TextEncoder(); const decoder = new TextDecoder("utf-8"); const XLSX_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; const crcTable = new Uint32Array(256); for (let n = 0; n < 256; n++) { let value = n; for (let bit = 0; bit < 8; bit++) value = (value & 1) ? (0xedb88320 ^ (value >>> 1)) : (value >>> 1); crcTable[n] = value >>> 0; } function crc32(bytes) { let crc = 0xffffffff; for (const byte of bytes) crc = crcTable[(crc ^ byte) & 0xff] ^ (crc >>> 8); return (crc ^ 0xffffffff) >>> 0; } function write16(view, offset, value) { view.setUint16(offset, value, true); } function write32(view, offset, value) { view.setUint32(offset, value >>> 0, true); } function concat(chunks) { const size = chunks.reduce((sum, chunk) => sum + chunk.length, 0); const result = new Uint8Array(size); let offset = 0; for (const chunk of chunks) { result.set(chunk, offset); offset += chunk.length; } return result; } function zipStore(entries) { const localChunks = [], centralChunks = []; let localOffset = 0; for (const entry of entries) { const name = encoder.encode(entry.name), data = typeof entry.data === "string" ? encoder.encode(entry.data) : entry.data; const crc = crc32(data); const local = new Uint8Array(30 + name.length + data.length), localView = new DataView(local.buffer); write32(localView, 0, 0x04034b50); write16(localView, 4, 20); write16(localView, 6, 0x0800); write16(localView, 8, 0); write16(localView, 10, 0); write16(localView, 12, 0); write32(localView, 14, crc); write32(localView, 18, data.length); write32(localView, 22, data.length); write16(localView, 26, name.length); write16(localView, 28, 0); local.set(name, 30); local.set(data, 30 + name.length); localChunks.push(local); const central = new Uint8Array(46 + name.length), centralView = new DataView(central.buffer); write32(centralView, 0, 0x02014b50); write16(centralView, 4, 20); write16(centralView, 6, 20); write16(centralView, 8, 0x0800); write16(centralView, 10, 0); write16(centralView, 12, 0); write16(centralView, 14, 0); write32(centralView, 16, crc); write32(centralView, 20, data.length); write32(centralView, 24, data.length); write16(centralView, 28, name.length); write16(centralView, 30, 0); write16(centralView, 32, 0); write16(centralView, 34, 0); write16(centralView, 36, 0); write32(centralView, 38, 0); write32(centralView, 42, localOffset); central.set(name, 46); centralChunks.push(central); localOffset += local.length; } const centralDirectory = concat(centralChunks); const end = new Uint8Array(22), endView = new DataView(end.buffer); write32(endView, 0, 0x06054b50); write16(endView, 4, 0); write16(endView, 6, 0); write16(endView, 8, entries.length); write16(endView, 10, entries.length); write32(endView, 12, centralDirectory.length); write32(endView, 16, localOffset); write16(endView, 20, 0); return concat([...localChunks, centralDirectory, end]); } function xml(value) { return String(value ?? "").replace(/[&<>"']/g, character => ({"&":"&","<":"<",">":">",'"':""","'":"'"}[character])); } function columnName(index) { let value = index + 1, result = ""; while (value) { value--; result = String.fromCharCode(65 + value % 26) + result; value = Math.floor(value / 26); } return result; } function safeSheetName(value) { return String(value || "Sayfa1").replace(/[\\/?*\[\]:]/g, " ").slice(0, 31) || "Sayfa1"; } function worksheetXml(rows, options = {}) { const widths = options.widths || []; const columns = widths.length ? `${widths.map((width, index) => ``).join("")}` : ""; const sheetRows = rows.map((row, rowIndex) => { const cells = row.map((value, columnIndex) => { if (value === null || value === undefined || value === "") return ""; const reference = `${columnName(columnIndex)}${rowIndex + 1}`; const style = rowIndex === 0 ? 1 : 0; if (typeof value === "number" && Number.isFinite(value)) return `${value}`; if (typeof value === "boolean") return `${value ? 1 : 0}`; return `${xml(value)}`; }).join(""); return `${cells}`; }).join(""); const maxColumn = columnName(Math.max(0, ...rows.map(row => row.length - 1))); return `${columns}${sheetRows}`; } function createXlsxBlob(sheetName, rows, options = {}) { const title = safeSheetName(sheetName); const entries = [ {name:"[Content_Types].xml",data:``}, {name:"_rels/.rels",data:``}, {name:"docProps/app.xml",data:`Süt ve Yem Takip`}, {name:"docProps/core.xml",data:`${xml(options.title || title)}Süt ve Yem Takip`}, {name:"xl/workbook.xml",data:``}, {name:"xl/_rels/workbook.xml.rels",data:``}, {name:"xl/styles.xml",data:``}, {name:"xl/worksheets/sheet1.xml",data:worksheetXml(rows, options)}, ]; return new Blob([zipStore(entries)], {type:XLSX_TYPE}); } function downloadBlob(filename, blob) { const url = URL.createObjectURL(blob), anchor = document.createElement("a"); anchor.href = url; anchor.download = filename; document.body.append(anchor); anchor.click(); anchor.remove(); setTimeout(() => URL.revokeObjectURL(url), 1200); } function downloadXlsx(filename, sheetName, rows, options = {}) { downloadBlob(filename, createXlsxBlob(sheetName, rows, options)); } async function inflateRaw(bytes) { if (typeof DecompressionStream !== "function") throw new Error("Bu cihaz Excel dosyasını açmayı desteklemiyor. Chrome veya güncel uygulama sürümünü kullanın."); const stream = new Blob([bytes]).stream().pipeThrough(new DecompressionStream("deflate-raw")); return new Uint8Array(await new Response(stream).arrayBuffer()); } async function unzip(buffer) { const bytes = new Uint8Array(buffer), view = new DataView(buffer); let endOffset = bytes.length - 22; while (endOffset >= Math.max(0, bytes.length - 65557) && view.getUint32(endOffset, true) !== 0x06054b50) endOffset--; if (endOffset < 0) throw new Error("Geçerli bir .xlsx dosyası seçin."); const count = view.getUint16(endOffset + 10, true), files = new Map(); let offset = view.getUint32(endOffset + 16, true); for (let index = 0; index < count; index++) { if (view.getUint32(offset, true) !== 0x02014b50) throw new Error("Excel dosyasının içeriği okunamadı."); const method = view.getUint16(offset + 10, true), compressedSize = view.getUint32(offset + 20, true); const nameLength = view.getUint16(offset + 28, true), extraLength = view.getUint16(offset + 30, true), commentLength = view.getUint16(offset + 32, true); const localOffset = view.getUint32(offset + 42, true), name = decoder.decode(bytes.slice(offset + 46, offset + 46 + nameLength)); const localNameLength = view.getUint16(localOffset + 26, true), localExtraLength = view.getUint16(localOffset + 28, true); const dataStart = localOffset + 30 + localNameLength + localExtraLength; const compressed = bytes.slice(dataStart, dataStart + compressedSize); files.set(name, method === 0 ? compressed : method === 8 ? await inflateRaw(compressed) : null); offset += 46 + nameLength + extraLength + commentLength; } return files; } function parseXml(bytes) { const documentXml = new DOMParser().parseFromString(decoder.decode(bytes), "application/xml"); if (documentXml.querySelector("parsererror")) throw new Error("Excel sayfası okunamadı."); return documentXml; } function cellColumn(reference) { const letters = String(reference || "A").match(/[A-Z]+/i)?.[0].toUpperCase() || "A"; let index = 0; for (const letter of letters) index = index * 26 + letter.charCodeAt(0) - 64; return index - 1; } async function readXlsx(file) { const files = await unzip(await file.arrayBuffer()); const sheetBytes = files.get("xl/worksheets/sheet1.xml"); if (!sheetBytes) throw new Error("Excel dosyasında ilk çalışma sayfası bulunamadı."); const sharedBytes = files.get("xl/sharedStrings.xml"); const shared = sharedBytes ? [...parseXml(sharedBytes).querySelectorAll("si")].map(item => [...item.querySelectorAll("t")].map(node => node.textContent || "").join("")) : []; const sheet = parseXml(sheetBytes), result = []; for (const row of sheet.querySelectorAll("sheetData > row")) { const values = []; for (const cell of row.querySelectorAll("c")) { const type = cell.getAttribute("t"), raw = cell.querySelector("v")?.textContent ?? ""; const value = type === "s" ? (shared[Number(raw)] ?? "") : type === "inlineStr" ? [...cell.querySelectorAll("t")].map(node => node.textContent || "").join("") : type === "b" ? raw === "1" : raw === "" ? "" : Number.isFinite(Number(raw)) ? Number(raw) : raw; values[cellColumn(cell.getAttribute("r"))] = value; } result.push(values.map(value => value ?? "")); } return result; } function parseCsv(text) { const delimiter = (text.split(/\r?\n/, 1)[0].match(/;/g) || []).length >= (text.split(/\r?\n/, 1)[0].match(/,/g) || []).length ? ";" : ","; const rows = []; let row = [], value = "", quoted = false; for (let index = 0; index < text.length; index++) { const character = text[index]; if (character === '"') { if (quoted && text[index + 1] === '"') { value += '"'; index++; } else quoted = !quoted; } else if (character === delimiter && !quoted) { row.push(value); value = ""; } else if ((character === "\n" || character === "\r") && !quoted) { if (character === "\r" && text[index + 1] === "\n") index++; row.push(value); if (row.some(cell => String(cell).trim())) rows.push(row); row = []; value = ""; } else value += character; } row.push(value); if (row.some(cell => String(cell).trim())) rows.push(row); return rows; } async function readFirstSheet(file) { const lower = file.name.toLowerCase(); if (lower.endsWith(".csv")) return parseCsv(await file.text()); if (!lower.endsWith(".xlsx")) throw new Error("Yalnızca .xlsx veya .csv dosyası yükleyin."); return readXlsx(file); } window.SutYemSpreadsheet = {createXlsxBlob, downloadXlsx, readFirstSheet, XLSX_TYPE}; })();