Get all text content from page
// ==UserScript==
// @name Extract Page Text
// @namespace https://sodaubai.hcm.edu.vn/*
// @version 1.2
// @description Get all text content from page
// @match https://sodaubai.hcm.edu.vn/*
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
/* ---------------------------
UI CONTAINER
--------------------------- */
const container = document.createElement("div");
container.style.position = "fixed";
container.style.bottom = "20px";
container.style.right = "20px";
container.style.zIndex = "999999";
container.style.fontFamily = "Arial";
/* ---------------------------
TOGGLE BUTTON
--------------------------- */
const toggleBtn = document.createElement("button");
toggleBtn.innerText = "📄 Extract";
toggleBtn.style.padding = "10px 16px";
toggleBtn.style.borderRadius = "25px";
toggleBtn.style.border = "none";
toggleBtn.style.background = "#007bff";
toggleBtn.style.color = "#fff";
toggleBtn.style.cursor = "pointer";
toggleBtn.style.boxShadow = "0 4px 12px rgba(0,0,0,0.25)";
/* ---------------------------
PANEL
--------------------------- */
const panel = document.createElement("div");
panel.style.width = "500px";
panel.style.height = "350px";
panel.style.background = "#ffffff";
panel.style.borderRadius = "10px";
panel.style.boxShadow = "0 5px 20px rgba(0,0,0,0.3)";
panel.style.padding = "10px";
panel.style.marginBottom = "10px";
panel.style.display = "none";
panel.style.flexDirection = "column";
panel.style.resize = "both";
panel.style.overflow = "auto";
panel.style.minWidth = "300px";
panel.style.minHeight = "200px";
panel.style.resize = "both";
/* ---------------------------
HEADER
--------------------------- */
const header = document.createElement("div");
header.style.display = "flex";
header.style.justifyContent = "space-between";
header.style.alignItems = "center";
header.style.marginBottom = "10px";
header.style.fontWeight = "bold";
const title = document.createElement("span");
title.innerText = "Smart Extractor";
const closeBtn = document.createElement("button");
closeBtn.innerText = "✖";
closeBtn.style.border = "none";
closeBtn.style.background = "transparent";
closeBtn.style.cursor = "pointer";
closeBtn.onclick = () => panel.style.display = "none";
header.appendChild(title);
header.appendChild(closeBtn);
/* ---------------------------
BUTTON BAR
--------------------------- */
const btnBar = document.createElement("div");
btnBar.style.display = "flex";
btnBar.style.gap = "6px";
btnBar.style.marginBottom = "10px";
const extractBtn = document.createElement("button");
extractBtn.innerText = "Lấy dữ liệu";
const copyBtn = document.createElement("button");
copyBtn.innerText = "Copy JSON";
const downloadBtn = document.createElement("button");
downloadBtn.innerText = "Download JSON";
const excelBtn = document.createElement("button");
excelBtn.innerText = "Download Excel";
[extractBtn, copyBtn, downloadBtn, excelBtn].forEach(b => {
b.style.padding = "6px 10px";
b.style.cursor = "pointer";
});
btnBar.appendChild(extractBtn);
btnBar.appendChild(copyBtn);
btnBar.appendChild(downloadBtn);
btnBar.appendChild(excelBtn);
/* ---------------------------
OUTPUT AREA
--------------------------- */
const output = document.createElement("div");
output.style.flex = "1";
output.style.overflow = "auto";
output.style.border = "1px solid #ddd";
output.style.padding = "6px";
output.style.fontSize = "12px";
output.style.background = "#fafafa";
panel.appendChild(header);
panel.appendChild(btnBar);
panel.appendChild(output);
container.appendChild(panel);
container.appendChild(toggleBtn);
document.body.appendChild(container);
toggleBtn.onclick = () => {
panel.style.display = panel.style.display === "none" ? "flex" : "none";
};
/* ---------------------------
WIJMO GRID EXTRACTOR
--------------------------- */
function extractWijmoGrid(grid) {
const headers = [];
const rows = [];
const headerCells = grid.querySelectorAll(".wj-colheaders .wj-cell");
headerCells.forEach(cell => {
const text = cell.innerText.trim();
if (text) headers.push(text);
});
const rowElements = grid.querySelectorAll(".wj-cells .wj-row");
rowElements.forEach(row => {
const cells = Array.from(row.querySelectorAll(".wj-cell"))
.map(c => c.innerText.trim());
if (cells.length) rows.push(cells);
});
return { headers, rows };
}
/* ---------------------------
TABLE RENDERER
--------------------------- */
function renderTable(data) {
const table = document.createElement("table");
table.style.width = "100%";
table.style.borderCollapse = "collapse";
const thead = document.createElement("thead");
const tr = document.createElement("tr");
data.headers.forEach(h => {
const th = document.createElement("th");
th.innerText = h;
th.style.border = "1px solid #ccc";
th.style.padding = "4px";
th.style.background = "#333";
th.style.color = "white";
tr.appendChild(th);
});
thead.appendChild(tr);
table.appendChild(thead);
const tbody = document.createElement("tbody");
data.rows.forEach(r => {
const tr = document.createElement("tr");
r.forEach(c => {
const td = document.createElement("td");
td.innerText = c;
td.style.border = "1px solid #ccc";
td.style.padding = "4px";
tr.appendChild(td);
});
tbody.appendChild(tr);
});
table.appendChild(tbody);
output.innerHTML = "";
output.appendChild(table);
}
/* ---------------------------
BUTTON LOGIC
--------------------------- */
let extractedData = null;
extractBtn.onclick = () => {
const grids = document.querySelectorAll(".wj-flexgrid");
if (grids.length === 0) {
output.innerText = "No Wijmo grid found on this page.";
return;
}
output.innerHTML = "";
grids.forEach((grid, index) => {
const btn = document.createElement("button");
btn.innerText = `Extract Grid ${index + 1}`;
btn.style.margin = "4px";
btn.onclick = () => {
extractedData = extractWijmoGrid(grid);
renderTable(extractedData);
};
output.appendChild(btn);
});
};
/* ---------------------------
COPY JSON
--------------------------- */
copyBtn.onclick = () => {
if (!extractedData) return;
navigator.clipboard.writeText(JSON.stringify(extractedData, null, 2));
};
/* ---------------------------
DOWNLOAD JSON
--------------------------- */
downloadBtn.onclick = () => {
if (!extractedData) return;
const blob = new Blob(
[JSON.stringify(extractedData, null, 2)],
{ type: "application/json" }
);
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "extracted_data.json";
a.click();
};
/* ---------------------------
DOWNLOAD EXCEL (CSV)
--------------------------- */
excelBtn.onclick = () => {
if (!extractedData || !extractedData.headers) return;
let csv = "";
csv += extractedData.headers.join(",") + "\n";
extractedData.rows.forEach(row => {
csv += row.map(v => `"${v.replace(/"/g, '""')}"`).join(",") + "\n";
});
const BOM = "\uFEFF"; // Fix Vietnamese encoding for Excel
const blob = new Blob(
[BOM + csv],
{ type: "text/csv;charset=utf-8;" }
);
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "grid_data.csv";
a.click();
};
})();