(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};
})();