// ==UserScript==
// @name Đăng Nhanh TTV v2
// @namespace http://tampermonkey.net/
// @version 1.9
// @description Đăng truyện nhanh TTV với nhiều tính năng mới và tự động tách chương - phiên bản tối ưu
// @author HA
// @match https://tangthuvien.net/dang-chuong/story/*
// @match https://tangthuvien.net/danh-sach-chuong/story/*
// @grant none
// @required https://code.jquery.com/jquery-3.2.1.min.js
// @icon https://tangthuvien.net/images/icon-favico.png
// @copyright 2021-2023, by HA
// @license AGPL-3.0-only
// @collaborator HA
// ==/UserScript==
(function() {
'use strict';
const HEADER_SIGN = "";
const FOOTER_SIGN = "";
const dăngnhanhTTV = {
CONFIG: {
YEAR_ALIVE: 2021,
MAX_CHAPTER_POST: 10,
MIN_CHARACTERS: 3000,
MAX_CHARACTERS: 20000,
SAFE_CHARACTERS: 15000
},
STATE: {
CHAP_NUMBER: 1,
CHAP_STT: 1,
CHAP_SERIAL: 1,
CHAP_NUMBER_ORIGINAL: 1,
CHAP_STT_ORIGINAL: 1,
CHAP_SERIAL_ORIGINAL: 1
},
ELEMENTS: {
qpContent: null,
qpButtonSubmit: null,
qpButtonRemoveEmpty: null,
qpn: null,
characterCount: null
},
init: function() {
try {
const me = this;
if (this.handleRedirects()) {
return;
}
this.initializeChapterValues();
this.createInterface();
this.addStyles();
this.cacheElements();
this.registerEvents();
this.createListAddChapter(true);
jQuery('#qpNumberOfChapter').attr('max', me.CONFIG.MAX_CHAPTER_POST);
this.ELEMENTS.qpContent.focus();
} catch (e) {
console.error("Lỗi khởi tạo:", e);
}
},
handleRedirects: function() {
if (window.location.href.includes('tangthuvien.net/message/successchapter')) {
setTimeout(function() {
const previousUrl = document.referrer;
if (previousUrl.includes('/dang-chuong/story/')) {
const matches = previousUrl.match(/\/dang-chuong\/story\/(\d+)/);
if (matches && matches[1]) {
window.location.href = 'https://tangthuvien.net/dang-chuong/story/' + matches[1];
}
}
}, 3000);
return true;
} else if (window.location.href.includes('/story/') && window.location.href.includes('/danh-sach-chuong')) {
setTimeout(function() {
const matches = window.location.href.match(/\/story\/(\d+)/);
if (matches && matches[1]) {
window.location.href = 'https://tangthuvien.net/dang-chuong/story/' + matches[1];
}
}, 3000);
return true;
}
return false;
},
cacheElements: function() {
this.ELEMENTS.qpContent = jQuery("#qpContent");
this.ELEMENTS.qpButtonSubmit = jQuery("#qpButtonSubmit");
this.ELEMENTS.qpButtonRemoveEmpty = jQuery("#qpButtonRemoveEmpty");
this.ELEMENTS.qpn = jQuery("#qpn");
this.ELEMENTS.characterCount = jQuery(".character-count");
},
initializeChapterValues: function() {
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());
}
this.STATE.CHAP_NUMBER = this.STATE.CHAP_NUMBER_ORIGINAL = chap_number;
this.STATE.CHAP_STT = this.STATE.CHAP_STT_ORIGINAL = chap_stt;
this.STATE.CHAP_SERIAL = this.STATE.CHAP_SERIAL_ORIGINAL = chap_serial;
},
addNewChapter: function() {
const me = this;
if ((me.STATE.CHAP_NUMBER + 1) <= me.CONFIG.MAX_CHAPTER_POST) {
me.updateChapNumber(true);
const chap_vol = parseInt(jQuery('.chap_vol').val());
const chap_vol_name = jQuery('.chap_vol_name').val();
const html = `
<div data-gen="MK_GEN" id="COUNT_CHAP_${me.STATE.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[${me.STATE.CHAP_NUMBER}]" value="${me.STATE.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="${me.STATE.CHAP_SERIAL}" required class="form-control" name="chap_number[${me.STATE.CHAP_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[${me.STATE.CHAP_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[${me.STATE.CHAP_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[${me.STATE.CHAP_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" name="introduce[${me.STATE.CHAP_NUMBER}]" rows="20" placeholder="Nội dung" type="text"></textarea>
</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[${me.STATE.CHAP_NUMBER}]" placeholder="Quảng cáo" type="text"></textarea>
</div>
</div>
</div>`;
jQuery('#add-chap').before(html);
} else {
const chapterLeft = Math.max(0, me.CONFIG.MAX_CHAPTER_POST - me.STATE.CHAP_NUMBER);
alert(`Bạn nên đăng tối đa ${me.CONFIG.MAX_CHAPTER_POST} chương một lần, số chương đã tạo ${me.STATE.CHAP_NUMBER} chương, bạn có thể đăng thêm ${chapterLeft} chương nữa.`);
}
},
createListAddChapter: function(isSilent) {
const me = this;
const chapterAdd = parseInt(jQuery("#qpNumberOfChapter").val());
if ((me.STATE.CHAP_NUMBER + chapterAdd) <= me.CONFIG.MAX_CHAPTER_POST) {
for (let i = 0; i < chapterAdd; i++) {
me.addNewChapter();
}
if (!isSilent) {
alert(`Đã tạo thêm ${chapterAdd} chương, hãy copy và dán nội dung cần đăng.`);
}
} else {
alert(`Bạn nên đăng tối đa ${me.CONFIG.MAX_CHAPTER_POST} chương một lần, số chương đã tạo ${me.STATE.CHAP_NUMBER} chương, bạn có thể đăng thêm ${me.CONFIG.MAX_CHAPTER_POST - me.STATE.CHAP_NUMBER} chương nữa.`);
}
},
removeLastedPost: function() {
const me = this;
jQuery(`#COUNT_CHAP_${me.STATE.CHAP_NUMBER}_MK`).remove();
me.updateChapNumber();
},
updateChapNumber: function(isAdd) {
const me = this;
try {
if (isAdd) {
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());
}
me.STATE.CHAP_STT = chap_stt;
me.STATE.CHAP_SERIAL = chap_serial;
me.STATE.CHAP_NUMBER++;
me.STATE.CHAP_STT++;
me.STATE.CHAP_SERIAL++;
} else {
if (me.STATE.CHAP_NUMBER > me.STATE.CHAP_NUMBER_ORIGINAL) {
me.STATE.CHAP_NUMBER--;
}
if (me.STATE.CHAP_STT > me.STATE.CHAP_STT_ORIGINAL) {
me.STATE.CHAP_STT--;
}
if (me.STATE.CHAP_SERIAL > me.STATE.CHAP_SERIAL_ORIGINAL) {
me.STATE.CHAP_SERIAL--;
}
}
jQuery('#chap_number').val(me.STATE.CHAP_NUMBER);
jQuery('#chap_stt').val(me.STATE.CHAP_STT);
jQuery('#chap_serial').val(me.STATE.CHAP_SERIAL);
jQuery('#countNumberPost').text(me.STATE.CHAP_NUMBER);
} catch (e) {
console.error("Lỗi cập nhật số chương:", e);
}
},
createInterface: function() {
const html = `
<div id="HA">
<div class="form-group"></div>
<center>
<h3 style="color: #4285f4; margin-bottom: 15px; font-weight: 600;">📝 CÔNG CỤ ĐĂNG NHANH</h3>
</center>
<center>
<h4><span id="short-chapter-warning" style="display:none; color:#ea4335; padding: 8px; background-color: #ffebee; border-radius: 6px;"></span></h4>
</center>
<div class="form-group">
<textarea placeholder="Nội dung truyện (Dán vào đây để tự động tách chương)" id="qpContent" class="form-control" rows="5"></textarea>
<div class="character-count" style="text-align: right; font-size: 11px; color: #666; margin-top: 3px;">Số ký tự: 0</div>
<div class="form-group" id="qpAdv" class="form-control" rows="2"></div>
<div class="form-group" style="display: flex; justify-content: space-between; margin-top: 15px;">
<div>
<button type="button" id="qpButtonSubmit" class="btn btn-success">📤 Đăng chương</button>
</div>
<div>
<button type="button" id="qpButtonRemoveEmpty" class="btn btn-danger" style="display: none;">Xóa chương trống</button>
<button type="button" id="qpButtonAddEmpty" class="btn btn-outline">Xóa chương trống</button>
</div>
</div>
<div class="form-group">
<span id="qpn" style="margin:0px 15px;font-weight:500"></span>
</div>
</div>
<div class="hidden-config-section" style="display:none;height:0;overflow:hidden;visibility:hidden;">
<h4></h4>
<div class="form-group">
<input type="text" id="qpSplitValue" class="form-control" value="/[c|C]hương\\s?\\d+\\s?:?\\s?/">
</div>
<div class="form-group" id="qpSplitValueReplace"></div>
<div class="form-group">
<input type="number" placeholder="Thêm" value="9" id="qpNumberOfChapter" class="form-control">
</div>
</div>
</div>`;
jQuery(".list-in-user").before(html);
},
addStyles: function() {
const style = document.createElement('style');
style.textContent = `
#HA {
background-color: #ffffff !important;
padding: 20px;
color: #333333 !important;
border-radius: 12px;
margin-bottom: 15px;
position: fixed;
right: 20px;
top: 50%;
transform: translateY(-50%);
max-width: 400px;
z-index: 9999;
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
font-family: "Segoe UI", Roboto, Arial, sans-serif;
transition: all 0.3s ease;
}
#HA > .form-group > div {
font-size: 14px;
color: #333333 !important;
}
#HA > p {
font-size: 14px;
color: #333333 !important;
text-align: right;
}
#HA .HA-option {
padding: 10px;
border: 1px solid #e0e0e0;
border-radius: 8px;
margin-bottom: 15px;
background-color: #f8f9fa;
}
#HA .HA-option-label {
width: 100%;
background-color: #4285f4;
color: white;
padding: 10px;
border-radius: 6px;
margin: 0 0 10px 0;
font-weight: 500;
}
#qpn {
max-height: 300px;
overflow-y: auto;
margin-top: 15px;
font-size: 13px;
padding: 10px;
background-color: #f5f8ff;
border-radius: 8px;
}
.hidden-element, .hidden-config-section {
display: none !important;
height: 0 !important;
overflow: hidden !important;
visibility: hidden !important;
padding: 0 !important;
margin: 0 !important;
position: absolute !important;
left: -9999px !important;
}
@keyframes blink-red {
0% { box-shadow: 0 0 5px rgba(255, 0, 0, 0.5); }
50% { box-shadow: 0 0 15px rgba(255, 0, 0, 0.8); }
100% { box-shadow: 0 0 5px rgba(255, 0, 0, 0.5); }
}
textarea[style*='border: 3px solid red'] {
animation: blink-red 2s infinite;
border: 2px solid #ea4335 !important;
border-radius: 8px !important;
background-color: rgba(255, 235, 238, 0.2) !important;
}
#short-chapter-warning {
animation: blink-red 2s infinite;
display: inline-block;
padding: 6px 10px;
border-radius: 4px;
font-size: 12px;
}
.form-control {
border: 1px solid #e0e0e0;
border-radius: 6px;
padding: 10px;
font-size: 14px;
transition: all 0.3s ease;
}
.form-control:focus {
border-color: #4285f4;
box-shadow: 0 0 5px rgba(66, 133, 244, 0.3);
outline: none;
}
.character-count, .chapter-character-count {
text-align: right;
font-size: 14px;
color: #666;
margin-top: 5px;
padding-right: 8px;
font-family: "Segoe UI", Roboto, Arial, sans-serif;
font-weight: 600;
}
.btn {
padding: 8px 16px;
border-radius: 6px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
border: none;
font-size: 14px;
margin: 5px;
}
.btn-success {
background-color: #4285f4;
color: white;
}
.btn-success:hover {
background-color: #3367d6;
}
.btn-danger {
background-color: #f5f5f5;
color: #333333;
border: 1px solid #ccc;
font-weight: normal;
}
.btn-danger:hover {
background-color: #e9e9e9;
color: #333333;
border: 1px solid #bbb;
}
.btn-default {
background-color: #f1f3f4;
color: #333333;
}
.btn-default:hover {
background-color: #e8eaed;
}
/* Styles for summary sections */
.summary-section {
padding: 6px 8px;
margin-bottom: 5px;
border-radius: 4px;
background-color: #ffffff;
border-left: 3px solid #4285f4;
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
font-size: 12px;
}
.summary-section.warning {
border-left-color: #fbbc05;
background-color: #fffde7;
}
.summary-section.error {
border-left-color: #ea4335;
background-color: #fef2f2;
}
.summary-label {
font-weight: 600;
color: #202124;
display: inline-block;
min-width: 110px;
font-size: 12px;
}
.summary-value {
color: #3c4043;
font-weight: 500;
font-size: 12px;
}
.summary-details {
margin: 2px 0 2px 8px;
padding: 2px 0 2px 6px;
border-left: 2px solid #e0e0e0;
font-size: 11px;
}
.detail-item {
padding: 1px 0;
color: #5f6368;
font-size: 11px;
}
#qpn {
max-height: 250px;
overflow-y: auto;
margin-top: 10px;
font-size: 12px;
padding: 8px;
background-color: #f5f8ff;
border-radius: 6px;
}
`;
document.head.appendChild(style);
setTimeout(function() {
const elementsToHide = [
document.querySelector('.hidden-config-section'),
document.getElementById('qpSplitValue'),
document.getElementById('qpSplitValueReplace'),
document.getElementById('qpNumberOfChapter')
];
elementsToHide.forEach(function(el) {
if (el) {
el.style.display = 'none';
el.style.visibility = 'hidden';
el.style.height = '0px';
el.style.overflow = 'hidden';
el.style.position = 'absolute';
el.style.left = '-9999px';
el.setAttribute('aria-hidden', 'true');
}
});
}, 100);
},
registerEvents: function() {
const me = this;
this.ELEMENTS.qpContent.on("input", function() {
me.updateCharacterCount(jQuery(this).val().length, me.ELEMENTS.characterCount);
});
jQuery(document).on("input", "[name^=introduce]", function() {
const text = jQuery(this).val().replace(HEADER_SIGN, '').replace(FOOTER_SIGN, '').trim();
const charCount = text.length;
let charCountElement = jQuery(this).next('.chapter-character-count');
if (charCountElement.length === 0) {
charCountElement = jQuery('<div class="chapter-character-count" style="text-align: right; font-size: 11px; margin-top: 3px; padding-right: 5px;"></div>');
jQuery(this).after(charCountElement);
}
me.updateCharacterCount(charCount, charCountElement);
});
this.ELEMENTS.qpContent.on("paste", function(e) {
jQuery(this).val("");
me.showProcessingNotification();
setTimeout(function() {
const text = me.ELEMENTS.qpContent.val();
me.updateCharacterCount(text.length, me.ELEMENTS.characterCount);
}, 100);
setTimeout(function() {
me.splitChapters();
jQuery("#processing-notification").fadeOut(300, function() {
jQuery(this).remove();
});
me.showCompletionNotification();
}, 500);
});
jQuery('#qpButtonRemoveEmpty').on('click', function(e) {
e.preventDefault();
me.removeEmptyList();
});
jQuery('#qpButtonAddEmpty').on('click', function(e) {
e.preventDefault();
me.addNewChapter();
});
this.ELEMENTS.qpButtonSubmit.on('click', function(e) {
e.preventDefault();
const titles = jQuery("[name^=chap_name]");
const contents = jQuery("[name^=introduce]");
const advs = jQuery("[name^=adv]");
const advContent = jQuery("#qpAdv").val();
const st = jQuery('#qpSplitValueReplace').val().trim();
me.fillEmptyForms(titles, contents, advs, advContent, st);
jQuery('form button[type=submit]')[0].click();
});
},
showProcessingNotification: function() {
const processingMsg = jQuery("<div>", {
id: "processing-notification",
text: "Đang xử lý tách chương...",
css: {
position: "fixed",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
backgroundColor: "rgba(0, 0, 0, 0.8)",
color: "white",
padding: "15px 25px",
borderRadius: "8px",
zIndex: "10000",
fontSize: "16px",
fontWeight: "500",
boxShadow: "0 4px 15px rgba(0,0,0,0.2)"
}
});
jQuery("body").append(processingMsg);
},
showCompletionNotification: function() {
const completeMsg = jQuery("<div>", {
id: "complete-notification",
html: "<span style='font-size:18px;margin-right:8px;'>✅</span> Đã tách chương tự động!",
css: {
position: "fixed",
top: "20px",
right: "20px",
backgroundColor: "#4CAF50",
color: "white",
padding: "12px 20px",
borderRadius: "8px",
zIndex: "10000",
fontSize: "14px",
fontWeight: "500",
boxShadow: "0 4px 10px rgba(0,0,0,0.15)",
display: "flex",
alignItems: "center"
}
});
jQuery("body").append(completeMsg);
completeMsg.fadeIn(300).delay(2000).fadeOut(500, function() {
jQuery(this).remove();
});
},
updateCharacterCount: function(charCount, element) {
if (element === this.ELEMENTS.characterCount) {
element.html("<strong style='font-size: 14px;'>Số ký tự: " + charCount.toLocaleString('vi-VN') + "</strong>");
} else {
element.html("<strong style='font-size: 14px;'>Số ký tự: " + charCount.toLocaleString('vi-VN') + "/20.000</strong>");
}
if (charCount < this.CONFIG.MIN_CHARACTERS) {
element.css("color", "#ea4335");
} else if (charCount > this.CONFIG.MAX_CHARACTERS) {
element.css("color", "#fbbc05");
} else {
element.css("color", "#34a853");
}
},
ucFirst: function(str) {
if (str && typeof str === 'string' && str.length > 0) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
return str;
},
copyToClipboard: function(text) {
navigator.clipboard.writeText(text).catch(err => {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.setAttribute('readonly', '');
textarea.style.position = 'absolute';
textarea.style.left = '-9999px';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
});
},
removeEmptyList: function() {
try {
const me = this;
const titles = jQuery("[name^=chap_name]");
const contents = jQuery("[name^=introduce]");
let count = 0;
this.ELEMENTS.qpn.html('');
if (titles && titles.length) {
for (let i = 0; i < titles.length; i++) {
const t = titles[i];
const c = contents[i];
if (t && (!t.value || (c && !c.value.trim().replace(HEADER_SIGN, '').replace(FOOTER_SIGN, '').trim()))) {
if (t.parentElement.parentElement.parentElement.tagName != 'FORM') {
t.parentElement.parentElement.parentElement.remove();
}
me.updateChapNumber();
count++;
}
}
}
alert('Đã loại bỏ ' + count + ' chương trống. Đã có thể nhấn Đăng chương');
const updatedTitles = jQuery("[name^=chap_name]");
const updatedContents = jQuery("[name^=introduce]");
const updatedAdvs = jQuery("[name^=adv]");
const advContent = jQuery("#qpAdv").val();
const st = jQuery('#qpSplitValueReplace').val().trim();
let stillHasEmptyChapters = false;
for (let i = 0; i < updatedTitles.length; i++) {
const t = updatedTitles[i];
const c = updatedContents[i];
if (t && (!t.value || (c && !c.value.trim().replace(HEADER_SIGN, '').replace(FOOTER_SIGN, '').trim()))) {
stillHasEmptyChapters = true;
break;
}
}
if (stillHasEmptyChapters) {
this.ELEMENTS.qpButtonRemoveEmpty.show();
jQuery('#qpButtonAddEmpty').hide();
} else {
this.ELEMENTS.qpButtonRemoveEmpty.hide();
jQuery('#qpButtonAddEmpty').show();
}
me.fillEmptyForms(updatedTitles, updatedContents, updatedAdvs, advContent, st);
} catch (e) {
console.error("Lỗi khi xóa chương trống:", e);
}
},
fillEmptyForms: function(titles, contents, advs, advContent, st) {
try {
const maxIterations = 3;
let iteration = 0;
let hasEmptyForms = true;
while (hasEmptyForms && iteration < maxIterations) {
hasEmptyForms = false;
for (let i = 0; i < titles.length; i++) {
const currentTitle = titles[i];
const currentContent = contents[i];
if (currentTitle && (!currentTitle.value || (currentContent && !currentContent.value.trim().replace(HEADER_SIGN, '').replace(FOOTER_SIGN, '').trim()))) {
hasEmptyForms = true;
let foundNextValid = false;
for (let j = i + 1; j < titles.length; j++) {
const nextTitle = titles[j];
const nextContent = contents[j];
if (nextTitle && nextTitle.value && nextContent && nextContent.value.trim().replace(HEADER_SIGN, '').replace(FOOTER_SIGN, '').trim()) {
currentTitle.value = nextTitle.value;
currentContent.value = nextContent.value;
if (advs && advs[i]) {
advs[i].value = advs[j] ? advs[j].value : advContent;
}
nextTitle.value = "";
nextContent.value = "";
if (advs && advs[j]) {
advs[j].value = "";
}
foundNextValid = true;
break;
}
}
}
}
iteration++;
}
let emptyFormCount = 0;
for (let i = 0; i < titles.length; i++) {
const currentTitle = titles[i];
const currentContent = contents[i];
if (currentTitle && (!currentTitle.value || (currentContent && !currentContent.value.trim().replace(HEADER_SIGN, '').replace(FOOTER_SIGN, '').trim()))) {
emptyFormCount++;
}
}
if (emptyFormCount > 0) {
this.ELEMENTS.qpn.html(this.ELEMENTS.qpn.html() + `<br><b style='color:red'>Cảnh báo: Vẫn còn ${emptyFormCount} chương trống.</b>`);
this.ELEMENTS.qpButtonRemoveEmpty.show();
jQuery('#qpButtonAddEmpty').hide();
} else {
this.ELEMENTS.qpButtonRemoveEmpty.hide();
jQuery('#qpButtonAddEmpty').show();
}
} catch (e) {
console.error("Lỗi khi đẩy chương lên form trống:", e);
}
},
splitChapters: function() {
try {
const me = this;
window.shortChapterNotified = false;
const warningEl = document.getElementById('short-chapter-warning');
if (warningEl) {
warningEl.style.display = 'none';
}
const mainContentText = this.ELEMENTS.qpContent.val();
const mainContentLength = mainContentText.length;
this.updateCharacterCount(mainContentLength, this.ELEMENTS.characterCount);
const content = this.ELEMENTS.qpContent.val();
const st = jQuery('#qpSplitValueReplace').val().trim();
const advContent = jQuery("#qpAdv").val();
const lines = content.split('\n');
const chapters = [];
let currentChapter = [];
const regex = /^\s*[Cc]hương\s*\d+\s*:/;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line.match(regex)) {
if (currentChapter.length > 0) {
chapters.push(currentChapter.join('\n'));
currentChapter = [];
}
currentChapter.push(line);
} else {
currentChapter.push(line);
}
}
if (currentChapter.length > 0) {
chapters.push(currentChapter.join('\n'));
}
const filteredChapters = chapters.filter(function(chapter) {
const lines = chapter.split('\n');
return lines.some(function(line, index) {
return index > 0 && line.trim().length > 0;
});
});
let i = 0;
const processedChapters = [];
while (i < filteredChapters.length) {
if (filteredChapters[i].length > this.CONFIG.MAX_CHARACTERS) {
const largeChapter = filteredChapters[i];
const lines = largeChapter.split('\n');
const chapterTitle = lines.shift();
const contentToSplit = lines.join('\n');
const contentLength = contentToSplit.length;
const numSubChapters = Math.ceil(contentLength / this.CONFIG.SAFE_CHARACTERS);
const chunkSize = Math.floor(contentLength / numSubChapters);
let startPos = 0;
for (let j = 0; j < numSubChapters; j++) {
let endPos = (j === numSubChapters - 1) ? contentLength : startPos + chunkSize;
if (j < numSubChapters - 1) {
const lookAhead = Math.min(200, contentLength - endPos);
const segment = contentToSplit.substring(endPos, endPos + lookAhead);
const sentenceEnd = segment.search(/[.!?][^.!?]*$/);
if (sentenceEnd !== -1) {
endPos += sentenceEnd + 1;
}
}
const subContent = contentToSplit.substring(startPos, endPos);
const subTitle = `${chapterTitle.trim()} (Phần ${j + 1}/${numSubChapters})`;
processedChapters.push(subTitle + "\n" + subContent);
startPos = endPos;
}
} else {
processedChapters.push(filteredChapters[i]);
}
i++;
}
const titles = jQuery("[name^=chap_name]");
const contents = jQuery("[name^=introduce]");
const advs = jQuery("[name^=adv]");
let validChapters = 0;
const shortChapters = [];
const splitChapterGroups = [];
const duplicateTitles = [];
const titleMap = {};
for (let i = 0; i < Math.min(processedChapters.length, titles.length); i++) {
const chapterLines = processedChapters[i].split('\n');
const title = chapterLines.shift().trim();
const content = chapterLines.join('\n');
if (content) {
validChapters++;
let processedTitle = title;
if (title.includes(':')) {
processedTitle = title.split(':').slice(1).join(':').trim();
}
processedTitle = this.ucFirst(processedTitle);
if (processedTitle.toLowerCase().startsWith(st.toLowerCase())) {
titles[i].value = processedTitle;
} else {
titles[i].value = st + " " + processedTitle;
}
contents[i].value = HEADER_SIGN + "\r\n" + content + "\r\n" + FOOTER_SIGN;
advs[i].value = advContent;
const contentWithoutEmpty = content.split('\n')
.filter(line => line.trim().length > 0)
.join('\n');
const charCount = contentWithoutEmpty.length;
let charCountElement = jQuery(contents[i]).next('.chapter-character-count');
if (charCountElement.length === 0) {
charCountElement = jQuery('<div class="chapter-character-count" style="text-align: right; font-size: 11px; margin-top: 3px; padding-right: 5px;"></div>');
jQuery(contents[i]).after(charCountElement);
}
this.updateCharacterCount(charCount, charCountElement);
if (charCount < this.CONFIG.MIN_CHARACTERS) {
contents[i].style.border = '3px solid red';
shortChapters.push(title);
}
const titleText = titles[i].value.toLowerCase();
if (titleMap[titleText]) {
duplicateTitles.push(titles[i].value);
} else {
titleMap[titleText] = true;
}
if (title.includes(" (Phần ")) {
const parts = title.match(/\(Phần (\d+)\/(\d+)\)/);
if (parts && parts[2] && parts[1] === "1") {
const chapterName = title.replace(/\s*\(Phần \d+\/\d+\)/, "");
splitChapterGroups.push({
name: chapterName,
totalParts: parseInt(parts[2])
});
}
}
}
}
const totalProcessedChapters = processedChapters.length;
let splitChaptersCount = 0;
let largeChapterCount = 0;
let totalParts = 0;
for (let i = 0; i < processedChapters.length; i++) {
const firstLine = processedChapters[i].split('\n')[0];
if (firstLine && firstLine.includes(" (Phần ")) {
splitChaptersCount++;
const parts = firstLine.match(/\(Phần (\d+)\/(\d+)\)/);
if (parts && parts[2]) {
const totalPartsInChapter = parseInt(parts[2]);
if (parts[1] === "1") {
largeChapterCount++;
totalParts += totalPartsInChapter;
}
}
}
}
if (processedChapters.length > titles.length) {
const remainingContent = processedChapters.slice(titles.length).join("\n\n");
this.copyToClipboard(remainingContent);
}
let detailedSummary = "";
detailedSummary += `<div class='summary-section' style='font-size:12px;padding:6px 8px;margin-bottom:5px'><span class='summary-label'>Tổng số chương:</span> <span class='summary-value'>${totalProcessedChapters}</span></div>`;
detailedSummary += `<div class='summary-section' style='font-size:12px;padding:6px 8px;margin-bottom:5px'><span class='summary-label'>Đã xử lý:</span> <span class='summary-value'>${Math.min(processedChapters.length, titles.length)} chương</span></div>`;
if (processedChapters.length > titles.length) {
detailedSummary += `<div class='summary-section' style='font-size:12px;padding:6px 8px;margin-bottom:5px'><span class='summary-label'>Đã lưu vào Clipboard:</span> <span class='summary-value'>${processedChapters.length - titles.length} chương</span></div>`;
}
if (largeChapterCount > 0) {
detailedSummary += `<div class='summary-section warning' style='font-size:12px;padding:6px 8px;margin-bottom:5px'><span class='summary-label'>Chương dài:</span> <span class='summary-value'>${largeChapterCount} chia thành ${totalParts} phần</span></div>`;
if (splitChapterGroups.length > 0 && splitChapterGroups.length <= 3) {
detailedSummary += `<div class='summary-details' style='font-size:11px;margin:2px 0 2px 8px;padding:2px 0 2px 6px'>`;
for (let i = 0; i < splitChapterGroups.length; i++) {
const chapterMatch = splitChapterGroups[i].name.match(/[Cc]hương\s*(\d+):/);
const chapterNum = chapterMatch ? chapterMatch[0] : "Chương";
detailedSummary += `<div class='detail-item' style='padding:1px 0'>${chapterNum} chia thành ${splitChapterGroups[i].totalParts} phần</div>`;
}
detailedSummary += `</div>`;
} else if (splitChapterGroups.length > 3) {
detailedSummary += `<div class='summary-details' style='font-size:11px;margin:2px 0 2px 8px;padding:2px 0 2px 6px'>`;
for (let i = 0; i < 3; i++) {
const chapterMatch = splitChapterGroups[i].name.match(/[Cc]hương\s*(\d+):/);
const chapterNum = chapterMatch ? chapterMatch[0] : "Chương";
detailedSummary += `<div class='detail-item' style='padding:1px 0'>${chapterNum} chia thành ${splitChapterGroups[i].totalParts} phần</div>`;
}
detailedSummary += `<div class='detail-item' style='padding:1px 0'>... và ${splitChapterGroups.length - 3} chương khác</div>`;
detailedSummary += `</div>`;
}
}
if (duplicateTitles.length > 0) {
detailedSummary += `<div class='summary-section error' style='font-size:12px;padding:6px 8px;margin-bottom:5px'><span class='summary-label'>Chương trùng lặp:</span> <span class='summary-value'>${duplicateTitles.length} chương</span></div>`;
if (duplicateTitles.length <= 3) {
detailedSummary += `<div class='summary-details' style='font-size:11px;margin:2px 0 2px 8px;padding:2px 0 2px 6px'>`;
for (let i = 0; i < duplicateTitles.length; i++) {
detailedSummary += `<div class='detail-item' style='padding:1px 0'>${duplicateTitles[i]}</div>`;
}
detailedSummary += `</div>`;
} else {
detailedSummary += `<div class='summary-details' style='font-size:11px;margin:2px 0 2px 8px;padding:2px 0 2px 6px'>`;
for (let i = 0; i < 3; i++) {
detailedSummary += `<div class='detail-item' style='padding:1px 0'>${duplicateTitles[i]}</div>`;
}
detailedSummary += `<div class='detail-item' style='padding:1px 0'>... và ${duplicateTitles.length - 3} chương khác</div>`;
detailedSummary += `</div>`;
}
}
if (shortChapters.length > 0) {
detailedSummary += `<div class='summary-section error' style='font-size:12px;padding:6px 8px;margin-bottom:5px'><span class='summary-label'>Chương ngắn:</span> <span class='summary-value'>${shortChapters.length} chương</span></div>`;
if (shortChapters.length <= 3) {
detailedSummary += `<div class='summary-details' style='font-size:11px;margin:2px 0 2px 8px;padding:2px 0 2px 6px'>`;
for (let i = 0; i < shortChapters.length; i++) {
detailedSummary += `<div class='detail-item' style='padding:1px 0'>${shortChapters[i]}</div>`;
}
detailedSummary += `</div>`;
} else {
detailedSummary += `<div class='summary-details' style='font-size:11px;margin:2px 0 2px 8px;padding:2px 0 2px 6px'>`;
for (let i = 0; i < 3; i++) {
detailedSummary += `<div class='detail-item' style='padding:1px 0'>${shortChapters[i]}</div>`;
}
detailedSummary += `<div class='detail-item' style='padding:1px 0'>... và ${shortChapters.length - 3} chương khác</div>`;
detailedSummary += `</div>`;
}
}
const mainSummary = `Đã xử lý ${totalProcessedChapters} chương\nĐã lấy ${Math.min(processedChapters.length, titles.length)} chương`
+ (processedChapters.length > titles.length ? `\nĐã lưu ${processedChapters.length - titles.length} chương vào Clipboard` : '');
this.ELEMENTS.qpContent.val(mainSummary);
this.ELEMENTS.qpn.html(detailedSummary);
this.ELEMENTS.qpButtonSubmit.removeClass("btn-disable").addClass("btn-success");
let emptyChaptersExist = false;
for (let i = 0; i < titles.length; i++) {
const currentTitle = titles[i];
const currentContent = contents[i];
if (currentTitle && (!currentTitle.value || (currentContent && !currentContent.value.trim().replace(HEADER_SIGN, '').replace(FOOTER_SIGN, '').trim()))) {
emptyChaptersExist = true;
break;
}
}
let hasEmptyChapters = false;
for (let i = 0; i < titles.length; i++) {
const t = titles[i];
const c = contents[i];
if (t && (!t.value || (c && !c.value.trim().replace(HEADER_SIGN, '').replace(FOOTER_SIGN, '').trim()))) {
hasEmptyChapters = true;
break;
}
}
if (hasEmptyChapters) {
me.ELEMENTS.qpButtonRemoveEmpty.show();
jQuery('#qpButtonAddEmpty').hide();
} else {
me.ELEMENTS.qpButtonRemoveEmpty.hide();
jQuery('#qpButtonAddEmpty').show();
}
} catch (e) {
console.error("Lỗi khi tách chương:", e);
this.ELEMENTS.qpn.html(`<div class='summary-section error'><span class='summary-label'>Lỗi:</span> <span class='summary-value'>${e}</span></div>`).addClass("text-danger");
}
}
};
dăngnhanhTTV.init();
})();