// ==UserScript==
// @name TTV
// @namespace http://tampermonkey.net/
// @version 7.2
// @description Công cụ đăng chương hiện đại cho Tàng Thư Viện với UI/UX được tối ưu
// @author HA
// @match https://tangthuvien.net/dang-chuong/story/*
// @match https://tangthuvien.net/danh-sach-chuong/story/*
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @required https://code.jquery.com/jquery-3.2.1.min.js
// ==/UserScript==
(function() {
'use strict';
const headerSign = "";
const footerSign = "";
const MAX_CHAPTER_POST = 10;
let CHAP_NUMBER = 1;
let CHAP_STT = 1;
let CHAP_SERIAL = 1;
let CHAP_NUMBER_ORIGINAL = 1;
let CHAP_STT_ORIGINAL = 1;
let CHAP_SERIAL_ORIGINAL = 1;
// CSS tùy chỉnh
GM_addStyle(`
#modern-uploader {
position: fixed;
top: 10px;
right: 10px;
width: 400px;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
z-index: 9999;
}
#notification-area {
padding: 0.5rem;
border-radius: 0.25rem;
font-weight: bold;
}
.notification-success { color: #22c55e; }
.notification-error { color: #ef4444; }
.notification-warning { color: #f59e0b; }
.notification-info { color: #3b82f6; }
.loading-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #3b82f6;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
#debug-output {
font-family: monospace;
white-space: pre;
font-size: 12px;
max-height: 200px;
overflow: auto;
margin-top: 10px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
background: #f5f5f5;
}
.character-counter {
text-align: right;
margin-top: 5px;
font-size: 12px;
color: #666;
}
`);
// Tạo HTML cho chương mới
function createChapterHTML(number) {
const chap_vol = parseInt(jQuery('.chap_vol').val());
const chap_vol_name = jQuery('.chap_vol_name').val();
return `
<div data-gen="MK_GEN" id="COUNT_CHAP_${number}_MK">
<div class="col-xs-12 form-group"></div>
<div class="form-group">
<label class="col-sm-2" for="chap_stt">STT</label>
<div class="col-sm-8">
<input class="form-control" required name="chap_stt[${number}]" value="${CHAP_STT}"
placeholder="Số thứ tự của chương" type="text"/>
</div>
</div>
<div class="form-group">
<label class="col-sm-2" for="chap_number">Chương thứ..</label>
<div class="col-sm-8">
<input value="${CHAP_SERIAL}" required class="form-control" name="chap_number[${number}]"
placeholder="Chương thứ.. (1,2,3..)" type="text"/>
</div>
</div>
<div class="form-group">
<label class="col-sm-2" for="chap_name">Quyển số</label>
<div class="col-sm-8">
<input class="form-control" name="vol[${number}]"
placeholder="Quyển số" type="number" value="${chap_vol}" required/>
</div>
</div>
<div class="form-group">
<label class="col-sm-2" for="chap_name">Tên quyển</label>
<div class="col-sm-8">
<input class="form-control chap_vol_name" name="vol_name[${number}]"
placeholder="Tên quyển" type="text" value="${chap_vol_name}" />
</div>
</div>
<div class="form-group">
<label class="col-sm-2" for="chap_name">Tên chương</label>
<div class="col-sm-8">
<input required class="form-control" name="chap_name[${number}]"
placeholder="Tên chương" type="text"/>
</div>
</div>
<div class="form-group">
<label class="col-sm-2" for="introduce">Nội dung</label>
<div class="col-sm-8">
<textarea maxlength="75000" style="color:#000;font-weight: 400;" required class="form-control chapter-content"
name="introduce[${number}]" rows="20" placeholder="Nội dung" type="text"></textarea>
<div class="character-counter" style="text-align: right; margin-top: 5px; font-size: 12px; color: #666;">
<span class="current-count">0</span>/20.000 ký tự
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-2" for="adv">Quảng cáo</label>
<div class="col-sm-8">
<textarea maxlength="1000" class="form-control" name="adv[${number}]"
placeholder="Quảng cáo" type="text"></textarea>
</div>
</div>
</div>`;
}
// Thêm chương mới
function addNewChapter() {
if ((CHAP_NUMBER + 1) <= MAX_CHAPTER_POST) {
updateChapNumber(true);
const html = createChapterHTML(CHAP_NUMBER);
jQuery('#add-chap').before(html);
setupCharacterCounter(); // Add this line to initialize counter for new chapters
} else {
showNotification(`Chỉ có thể đăng tối đa ${MAX_CHAPTER_POST} chương một lần`, 'warning');
}
}
// Cập nhật số chương
function updateChapNumber(isAdd) {
try {
if (isAdd) {
let maxStt = 0;
let maxSerial = 0;
// Tìm giá trị lớn nhất trong tất cả các input hiện có
jQuery('input[name^="chap_stt"]').each(function() {
const val = parseInt(jQuery(this).val()) || 0;
maxStt = Math.max(maxStt, val);
});
jQuery('input[name^="chap_number"]').each(function() {
const val = parseInt(jQuery(this).val()) || 0;
maxSerial = Math.max(maxSerial, val);
});
// So sánh với giá trị từ DOM
const chapStt = parseInt(jQuery('.chap_stt1').val()) || 0;
const chapSerial = parseInt(jQuery('.chap_serial').val()) || 0;
maxStt = Math.max(maxStt, chapStt);
maxSerial = Math.max(maxSerial, chapSerial);
// Cập nhật biến đếm
CHAP_STT = maxStt + 1;
CHAP_SERIAL = maxSerial + 1;
CHAP_NUMBER++;
} else {
if (CHAP_NUMBER > CHAP_NUMBER_ORIGINAL) {
CHAP_NUMBER--;
}
if (CHAP_STT > CHAP_STT_ORIGINAL) {
CHAP_STT--;
}
if (CHAP_SERIAL > CHAP_SERIAL_ORIGINAL) {
CHAP_SERIAL--;
}
}
jQuery('#chap_number').val(CHAP_NUMBER);
jQuery('#chap_stt').val(CHAP_STT);
jQuery('#chap_serial').val(CHAP_SERIAL);
jQuery('#countNumberPost').text(CHAP_NUMBER);
} catch (e) {
console.log("Lỗi: " + e);
}
}
// Tạo giao diện chính
function createInterface() {
const container = document.createElement('div');
container.id = 'modern-uploader';
container.innerHTML = `
<div class="text-center mb-4">
<h3 class="text-xl font-bold">CÔNG CỤ ĐĂNG NHANH</h3>
<p id="short-chapter-warning" class="text-red-500 mt-2 hidden"></p>
</div>
<div class="form-group">
<textarea id="qpContent" class="form-control" rows="5"
placeholder="Dán nội dung truyện vào đây để tự động tách chương"></textarea>
</div>
<div class="debug-info" style="display: none;">
<pre id="debug-output"></pre>
</div>
<div class="flex justify-between">
<button class="btn btn-outline" id="qpButtonRemoveEmpty">Xóa chương trống</button>
<button class="btn btn-primary" id="qpButtonSubmit">Đăng chương</button>
</div>
<div id="notification-area" class="mt-3 text-center"></div>
`;
jQuery(".list-in-user").before(container);
setupEventListeners();
}
// Khởi tạo các chương
function initializeChapters() {
showLoading();
try {
const chap_number = parseInt(jQuery('#chap_number').val());
let chap_stt = parseInt(jQuery('.chap_stt1').val());
let chap_serial = parseInt(jQuery('.chap_serial').val());
if (parseInt(jQuery('#chap_stt').val()) > chap_stt) {
chap_stt = parseInt(jQuery('#chap_stt').val());
}
if (parseInt(jQuery('#chap_serial').val()) > chap_serial) {
chap_serial = parseInt(jQuery('#chap_serial').val());
}
CHAP_NUMBER = CHAP_NUMBER_ORIGINAL = chap_number;
CHAP_STT = CHAP_STT_ORIGINAL = chap_stt;
CHAP_SERIAL = CHAP_SERIAL_ORIGINAL = chap_serial;
// Thêm 9 chương mới
for(let i = 0; i < 9; i++) {
addNewChapter();
}
hideLoading();
showNotification('Đã tạo đủ 10 chương');
} catch (e) {
console.log("Lỗi: " + e);
hideLoading();
showNotification('Có lỗi khi tạo chương', 'error');
}
}
// Khởi tạo các event listener
function setupEventListeners() {
// Xử lý paste nội dung
jQuery("#qpContent").on("paste", function (e) {
e.preventDefault();
jQuery(this).val("");
showLoading();
const pastedText = e.originalEvent.clipboardData.getData('text');
jQuery(this).val(pastedText);
setTimeout(function() {
const chapters = performAction();
hideLoading();
showNotification(`Đã nhập ${chapters} chương`, 'success');
}, 100);
});
jQuery('#qpButtonRemoveEmpty').on('click', removeEmptyChapters);
jQuery('#qpButtonSubmit').on('click', submitChapters);
}
// Xử lý paste và tách chương
function performAction() {
try {
console.log("Starting performAction");
var text = jQuery("#qpContent").val();
if (!text) {
showNotification('Không có nội dung để tách chương', 'error');
return 0;
}
var debugOutput = [];
var chapters = [];
var lines = text.split('\n');
var currentChapter = [];
var lastTitle = null;
debugOutput.push("=== Processing Text ===");
debugOutput.push(`Total lines: ${lines.length}`);
debugOutput.push("=== Line Analysis ===");
// Helper function to show exact whitespace
function visualizeWhitespace(str) {
return str.split('').map(c => {
if (c === '\t') return '\\t';
if (c === ' ') return '·';
if (c === '\n') return '\\n';
return c;
}).join('');
}
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
let isChapterTitle = /^\t[Cc]hương\s*\d+\s*:/.test(line) || /^\s{4,}[Cc]hương\s*\d+\s*:/.test(line);
// Debug log with exact whitespace
debugOutput.push(`Line ${i}: ${visualizeWhitespace(line.substring(0, 50))}${line.length > 50 ? '...' : ''}`);
debugOutput.push(` Is chapter: ${isChapterTitle}`);
if (isChapterTitle) {
if (currentChapter.length > 0) {
if (line !== lastTitle) {
chapters.push(currentChapter.join('\n'));
currentChapter = [line];
lastTitle = line;
debugOutput.push(` -> New chapter started`);
} else {
debugOutput.push(` -> Skipped duplicate title`);
}
} else {
currentChapter = [line];
lastTitle = line;
debugOutput.push(` -> First chapter started`);
}
} else if (currentChapter.length > 0) {
currentChapter.push(line);
}
}
if (currentChapter.length > 0) {
chapters.push(currentChapter.join('\n'));
debugOutput.push("-> Added final chapter");
}
debugOutput.push("=== Results ===");
debugOutput.push(`Found ${chapters.length} chapters`);
// Phân tách chương dài
const processedChapters = [];
for (let i = 0; i < chapters.length; i++) {
const chapterLines = chapters[i].split('\n');
const title = chapterLines.shift().trim();
const chapterText = chapterLines.join('\n');
// Đếm số ký tự của chương
const charCount = chapterText.length;
debugOutput.push(`Chapter ${i+1} character count: ${charCount}`);
// Nếu chương có kích thước lớn hơn 20000 ký tự, chia nhỏ
if (charCount > 20000) {
// Số chương cần tách
const parts = Math.ceil(charCount / 20000);
debugOutput.push(`Splitting into ${parts} parts`);
// Tính số ký tự cho mỗi phần
const charsPerPart = Math.ceil(charCount / parts);
debugOutput.push(`Characters per part: ~${charsPerPart}`);
// Chia chương thành các phần đều nhau dựa trên số ký tự
let currentText = chapterText;
let totalProcessed = 0;
for (let part = 0; part < parts; part++) {
// Xác định kích thước phần này
const isLastPart = part === parts - 1;
const targetSize = isLastPart ? currentText.length : charsPerPart;
// Tìm điểm kết thúc phù hợp (kết thúc câu hoặc đoạn văn)
let endPos = Math.min(targetSize, currentText.length);
// Tìm điểm kết thúc là cuối đoạn văn hoặc cuối câu gần với vị trí mục tiêu
if (!isLastPart && endPos < currentText.length) {
// Tìm vị trí xuống dòng gần nhất
const nextParagraph = currentText.indexOf('\n\n', endPos - 500);
if (nextParagraph !== -1 && nextParagraph < endPos + 500) {
endPos = nextParagraph + 2; // +2 để bao gồm cả ký tự xuống dòng
} else {
// Nếu không tìm thấy đoạn văn, tìm dấu kết thúc câu
const sentenceEnd = Math.max(
currentText.lastIndexOf('. ', endPos),
currentText.lastIndexOf('! ', endPos),
currentText.lastIndexOf('? ', endPos)
);
if (sentenceEnd !== -1 && sentenceEnd > endPos - 500) {
endPos = sentenceEnd + 2; // +2 để bao gồm dấu kết thúc câu và khoảng trắng
}
}
}
// Lấy nội dung của phần này
const partContent = currentText.substring(0, endPos);
totalProcessed += partContent.length;
// Cập nhật text còn lại cho phần tiếp theo
currentText = currentText.substring(endPos);
// Tạo tiêu đề phần: chỉ lấy tiêu đề gốc và thêm phần chia
let chapterTitle = title;
if (title.includes(':')) {
chapterTitle = title.substring(title.indexOf(':') + 1).trim();
}
// Thêm thông tin phần vào tiêu đề gốc
let newTitle = `${title} (Phần ${part+1}/${parts})`;
processedChapters.push(newTitle + '\n' + partContent);
debugOutput.push(`Part ${part+1}: ${partContent.length} chars`);
}
debugOutput.push(`Total processed: ${totalProcessed}/${charCount} chars`);
} else {
// Giữ nguyên chương nếu không cần chia
processedChapters.push(chapters[i]);
}
}
debugOutput.push(`After processing: ${processedChapters.length} chapters`);
// Giới hạn chỉ lấy 10 chương đầu tiên vào form, phần còn lại sẽ copy vào clipboard
const chaptersToFill = processedChapters.slice(0, MAX_CHAPTER_POST);
const remainingChapters = processedChapters.slice(MAX_CHAPTER_POST);
// Fill chapters into forms
var titles = jQuery("input[name^='chap_name']");
var contents = jQuery("textarea[name^='introduce']");
var advs = jQuery("textarea[name^='adv']");
debugOutput.push(`Forms found: ${titles.length}`);
if (processedChapters.length === 0) {
showNotification('Không tìm thấy chương nào', 'error');
jQuery('#debug-output').text(debugOutput.join('\n'));
return;
}
// Nếu có chương còn lại, sao chép vào clipboard
if (remainingChapters.length > 0) {
debugOutput.push(`${remainingChapters.length} chapters will be copied to clipboard`);
}
// Tạo thêm form nếu số chương cần điền nhiều hơn số form hiện có
const neededForms = chaptersToFill.length - titles.length;
if (neededForms > 0 && titles.length < MAX_CHAPTER_POST) {
debugOutput.push(`Need to add ${neededForms} more forms`);
for (let i = 0; i < neededForms && (titles.length + i) < MAX_CHAPTER_POST; i++) {
addNewChapter();
}
// Cập nhật lại danh sách các form sau khi thêm
titles = jQuery("input[name^='chap_name']");
contents = jQuery("textarea[name^='introduce']");
advs = jQuery("textarea[name^='adv']");
}
debugOutput.push(`Filling ${chaptersToFill.length} chapters into forms`);
// Điền nội dung vào form
jQuery.each(titles, function(k, v) {
if (k < chaptersToFill.length) {
var content = chaptersToFill[k].split('\n');
var title = content.shift().trim();
//Preserve the "Chương X:" prefix
var originalTitle = title;
debugOutput.push(`\nFilling chapter ${k + 1}:`);
debugOutput.push(`Original title: ${title}`);
debugOutput.push(`Extracted title: ${originalTitle}`);
debugOutput.push(`Content length: ${content.length} lines`);
titles[k].value = originalTitle;
contents[k].value = headerSign + "\r\n" + content.join('\n') + "\r\n" + footerSign;
if (advs[k]) advs[k].value = "";
// Trigger character counter update
jQuery(contents[k]).trigger('input');
}
});
// Sao chép các chương còn lại vào clipboard nếu có
if (remainingChapters.length > 0) {
try {
// Tạo nội dung clipboard với định dạng rõ ràng để dễ phân biệt các chương
const clipboardContent = remainingChapters.map(chap => chap.trim()).join('\n\n---CHAPTER_SEPARATOR---\n\n');
// Thử trước với Clipboard API (hiện đại)
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(clipboardContent)
.then(() => {
debugOutput.push(`Đã sao chép ${remainingChapters.length} chương vào clipboard (Clipboard API)`);
showNotification(`Đã sao chép ${remainingChapters.length} chương vào clipboard`, 'success');
})
.catch(err => {
throw err; // Chuyển đến phương pháp dự phòng
});
} else {
// Phương pháp dự phòng với execCommand (cũ)
const tempTextarea = document.createElement('textarea');
tempTextarea.style.position = 'fixed';
tempTextarea.style.top = '0';
tempTextarea.style.left = '0';
tempTextarea.style.width = '2em';
tempTextarea.style.height = '2em';
tempTextarea.style.opacity = '0';
tempTextarea.style.pointerEvents = 'none';
tempTextarea.value = clipboardContent;
document.body.appendChild(tempTextarea);
tempTextarea.focus();
tempTextarea.select();
const successful = document.execCommand('copy');
document.body.removeChild(tempTextarea);
if (!successful) {
throw new Error('Không thể sao chép vào clipboard');
}
debugOutput.push(`Đã sao chép ${remainingChapters.length} chương vào clipboard (execCommand)`);
showNotification(`Đã sao chép ${remainingChapters.length} chương vào clipboard`, 'success');
}
} catch (err) {
console.error('Lỗi khi sao chép vào clipboard:', err);
debugOutput.push(`Lỗi khi sao chép vào clipboard: ${err.message}`);
showNotification('Không thể sao chép vào clipboard. Vui lòng thử lại.', 'error');
// Hiển thị textarea để copy thủ công nếu tự động không thành công
const manualCopyArea = document.createElement('div');
manualCopyArea.style.position = 'fixed';
manualCopyArea.style.top = '50%';
manualCopyArea.style.left = '50%';
manualCopyArea.style.transform = 'translate(-50%, -50%)';
manualCopyArea.style.backgroundColor = 'white';
manualCopyArea.style.padding = '20px';
manualCopyArea.style.borderRadius = '8px';
manualCopyArea.style.boxShadow = '0 4px 6px -1px rgba(0, 0, 0, 0.1)';
manualCopyArea.style.zIndex = '10000';
manualCopyArea.style.maxWidth = '80%';
manualCopyArea.style.maxHeight = '80%';
manualCopyArea.style.overflow = 'auto';
manualCopyArea.innerHTML = `
<h3 style="margin-top: 0;">Sao chép thủ công</h3>
<p>Không thể sao chép tự động. Vui lòng chọn toàn bộ nội dung bên dưới và sao chép (Ctrl+C):</p>
<textarea style="width: 100%; height: 300px; padding: 10px;">${clipboardContent}</textarea>
<div style="text-align: right; margin-top: 10px;">
<button id="closeManualCopy" style="padding: 8px 16px; background: #3b82f6; color: white; border: none; border-radius: 4px; cursor: pointer;">Đóng</button>
</div>
`;
document.body.appendChild(manualCopyArea);
document.getElementById('closeManualCopy').addEventListener('click', () => {
document.body.removeChild(manualCopyArea);
});
}
}
jQuery("#qpButtonSubmit").removeClass("btn-disable").addClass("btn-success");
jQuery("#qpContent").val("Đã thực hiện xong");
// Hiển thị thông báo sau khi xử lý xong
if (remainingChapters.length > 0) {
let message = `Đã sử lý ${chapters.length} chương\n`;
message += `Đã nhập ${MAX_CHAPTER_POST} chương\n`;
message += `Đã coppy ${remainingChapters.length} chương`;
// Đếm số chương được chia
let splitChapters = 0;
for (let i = 0; i < chapters.length; i++) {
const chapterText = chapters[i].split('\n').slice(1).join('\n');
if (chapterText.length > 20000) {
splitChapters++;
}
}
if (processedChapters.length > chapters.length) {
message += `\nCó ${splitChapters} chương chia thành ${processedChapters.length - (chapters.length - splitChapters)} chương`;
}
showNotification(message, 'success');
} else if (processedChapters.length > chapters.length) {
// Đếm số chương được chia
let splitChapters = 0;
for (let i = 0; i < chapters.length; i++) {
const chapterText = chapters[i].split('\n').slice(1).join('\n');
if (chapterText.length > 20000) {
splitChapters++;
}
}
let message = `Đã sử lý ${chapters.length} chương\n`;
message += `Đã nhập ${processedChapters.length} chương\n`;
message += `Có ${splitChapters} chương chia thành ${processedChapters.length - (chapters.length - splitChapters)} chương`;
showNotification(message, 'success');
} else {
showNotification(`Đã sử lý ${chapters.length} chương\nĐã nhập ${processedChapters.length} chương vào form`, 'success');
}
// Show debug output
jQuery('#debug-output').text(debugOutput.join('\n'));
return processedChapters.length;
} catch (e) {
console.error("Error in performAction:", e);
showNotification('Có lỗi khi tách chương', 'error');
return 0;
}
}
// Xóa chương trống
function removeEmptyChapters() {
const forms = document.querySelectorAll('[data-gen="MK_GEN"]');
let removed = 0;
forms.forEach(form => {
const content = form.querySelector('textarea[name^="introduce"]').value.trim();
if (!content) {
form.remove();
removed++;
updateChapNumber(false);
}
});
showNotification(`Đã xử lý ${forms.length} chương`, 'info');
}
// Đăng chương
function submitChapters() {
const forms = document.querySelectorAll('[data-gen="MK_GEN"]');
let hasError = false;
forms.forEach(form => {
const contentTextarea = form.querySelector('textarea[name^="introduce"]');
const content = contentTextarea.value;
if (content.length < 3000) {
// Đánh dấu form với màu đỏ rõ ràng hơn
contentTextarea.style.border = '3px solid #ff0000';
contentTextarea.style.backgroundColor = 'rgba(255, 0, 0, 0.08)';
// Đổi màu chữ để dễ đọc hơn trên nền đỏ
contentTextarea.style.color = '#cc0000';
// Thêm đường viền ngoài cho parent để làm nổi bật toàn bộ form
contentTextarea.parentNode.style.border = '1px solid #ff0000';
contentTextarea.parentNode.style.padding = '5px';
contentTextarea.parentNode.style.borderRadius = '5px';
contentTextarea.parentNode.style.backgroundColor = 'rgba(255, 0, 0, 0.03)';
hasError = true;
// Thêm thông báo lỗi bên dưới textarea nổi bật hơn
let errorMsg = form.querySelector('.chapter-length-error');
if (!errorMsg) {
errorMsg = document.createElement('div');
errorMsg.className = 'chapter-length-error';
errorMsg.style.color = '#ff0000';
errorMsg.style.marginTop = '5px';
errorMsg.style.fontSize = '14px';
errorMsg.style.fontWeight = 'bold';
errorMsg.style.padding = '5px 10px';
errorMsg.style.backgroundColor = '#fff0f0';
errorMsg.style.border = '1px solid #ff0000';
errorMsg.style.borderRadius = '3px';
contentTextarea.parentNode.appendChild(errorMsg);
}
errorMsg.textContent = `⚠️ Chương quá ngắn: ${content.length}/3000 ký tự cần thiết`;
} else {
// Reset tất cả các style khi chương đủ dài
contentTextarea.style.border = '';
contentTextarea.style.backgroundColor = '';
contentTextarea.style.color = '';
contentTextarea.parentNode.style.border = '';
contentTextarea.parentNode.style.padding = '';
contentTextarea.parentNode.style.borderRadius = '';
contentTextarea.parentNode.style.backgroundColor = '';
// Xóa thông báo lỗi nếu có
const errorMsg = form.querySelector('.chapter-length-error');
if (errorMsg) {
errorMsg.remove();
}
}
});
if (hasError) {
showNotification('Có chương ngắn hơn 3000 ký tự, vui lòng kiểm tra các ô màu đỏ', 'error');
return;
}
showLoading();
// Submit form gốc
document.querySelector('form[name="postChapForm"] button[type="submit"]').click();
setTimeout(hideLoading, 2000);
}
// Hiển thị thông báo
function showNotification(message, type = 'info') {
const notificationArea = document.getElementById('notification-area');
// Chuyển đổi xuống dòng thành thẻ <br> để hiển thị đúng trong HTML
notificationArea.innerHTML = message.replace(/\n/g, '<br>');
notificationArea.className = `mt-3 text-center notification-${type}`;
// Thêm padding để thông báo đẹp hơn khi hiển thị nhiều dòng
notificationArea.style.padding = '10px';
}
// Hiển thị loading
function showLoading() {
const loading = document.createElement('div');
loading.className = 'loading-overlay';
loading.innerHTML = `
<div class="loading-spinner"></div>
`;
document.body.appendChild(loading);
}
function hideLoading() {
const loading = document.querySelector('.loading-overlay');
if (loading) {
loading.remove();
}
}
//Setup character counter
function setupCharacterCounter() {
jQuery('.chapter-content').on('input', function() {
const textarea = jQuery(this);
const currentCount = textarea.val().length;
textarea.next('.character-counter').find('.current-count').text(currentCount);
// Hiển thị số ký tự
const counterElement = textarea.next('.character-counter');
// Cảnh báo nếu chương quá dài
if (currentCount > 20000) {
counterElement.css('color', '#ff6600');
if (counterElement.find('.char-warning').length === 0) {
counterElement.append(`<div class="char-warning" style="color: #ff6600; font-weight: bold;">Chương này dài (>2000 ký tự), sẽ được tự động chia khi đăng</div>`);
}
} else {
counterElement.css('color', '#666');
counterElement.find('.word-warning').remove();
}
});
// Trigger input event để cập nhật counters cho các textarea đã có nội dung
jQuery('.chapter-content').trigger('input');
}
// Khởi tạo script
function init() {
createInterface();
initializeChapters();
setupCharacterCounter(); // Initialize character counters on page load
}
init();
})();