您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Tự động điền form đăng chương trên tangthuvien.net với tính năng nâng cao
当前为
- // ==UserScript==
- // @name TTV Auto Upload
- // @namespace http://tampermonkey.net/
- // @version 3.7
- // @description Tự động điền form đăng chương trên tangthuvien.net với tính năng nâng cao
- // @author HA
- // @match https://tangthuvien.net/dang-chuong/story/*
- // @grant none
- // ==/UserScript==
- (function() {
- 'use strict';
- // CSS cho thông báo và control panel
- const style = document.createElement('style');
- style.textContent = `
- .ttv-notification {
- position: fixed;
- top: 20px;
- right: 20px;
- padding: 10px 20px;
- background: #4CAF50;
- color: white;
- border-radius: 4px;
- z-index: 9999;
- display: none;
- }
- .ttv-error {
- background: #f44336;
- }
- .ttv-control-panel {
- position: fixed;
- top: 50px;
- right: 20px;
- background: white;
- padding: 25px;
- border-radius: 12px;
- box-shadow: 0 4px 20px rgba(0,0,0,0.15);
- z-index: 9998;
- width: 500px;
- margin-bottom: 20px;
- transition: all 0.3s ease;
- }
- .ttv-control-panel.minimized {
- width: auto;
- height: auto;
- padding: 10px;
- opacity: 0.8;
- transform: translateX(calc(100% - 40px));
- transition: all 0.3s ease;
- }
- .ttv-control-panel.minimized:hover {
- opacity: 1;
- transform: translateX(0);
- }
- .ttv-control-panel.minimized .ttv-button-group,
- .ttv-control-panel.minimized .ttv-header {
- display: none;
- }
- .ttv-button-group {
- display: flex;
- flex-direction: column;
- gap: 15px;
- }
- .ttv-content-editor {
- width: 100%;
- height: 100px;
- margin: 6px 0;
- padding: 8px;
- border: 1px solid #ddd;
- border-radius: 6px;
- font-size: 13px;
- line-height: 1.4;
- resize: vertical;
- transition: all 0.2s ease;
- }
- .ttv-content-editor:focus {
- border-color: #5bc0de;
- outline: none;
- box-shadow: 0 0 8px rgba(91,192,222,0.2);
- }
- .ttv-preview {
- display: none;
- width: 100%;
- height: 100px;
- margin: 6px 0;
- padding: 8px;
- border: 1px solid #ddd;
- border-radius: 6px;
- font-size: 13px;
- line-height: 1.4;
- overflow-y: auto;
- background: #f9f9f9;
- transition: all 0.2s ease;
- }
- .ttv-heading {
- font-size: 15px;
- color: #555;
- margin: 12px 0;
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
- .ttv-word-count {
- font-size: 12px;
- color: #888;
- padding: 3px 8px;
- background: #f5f5f5;
- border-radius: 4px;
- transition: all 0.2s ease;
- }
- .ttv-chapter-list {
- width: 100%;
- margin: 10px 0;
- max-height: 200px;
- overflow-y: auto;
- border: 1px solid #eee;
- border-radius: 6px;
- padding: 8px;
- }
- .ttv-chapter-item {
- padding: 8px;
- border-bottom: 1px solid #eee;
- cursor: pointer;
- transition: all 0.2s ease;
- line-height: 1.4;
- font-size: 12px;
- color: #666;
- }
- .ttv-chapter-item .chapter-title {
- font-weight: bold;
- margin-bottom: 4px;
- }
- .ttv-chapter-item .chapter-name {
- color: #888;
- padding-left: 10px;
- border-left: 2px solid #ddd;
- margin: 4px 0;
- }
- .ttv-chapter-item .chapter-stats {
- font-size: 11px;
- color: #999;
- }
- .ttv-chapter-item:last-child {
- border-bottom: none;
- }
- .ttv-chapter-item.selected {
- background: #f0f8ff;
- border-left: 2px solid #5bc0de;
- }
- .ttv-control-panel.fullscreen {
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- width: 100%;
- height: 100%;
- border-radius: 0;
- z-index: 9999;
- padding: 15px;
- display: flex;
- flex-direction: column;
- }
- .ttv-control-panel.fullscreen .ttv-content-editor,
- .ttv-control-panel.fullscreen .ttv-preview {
- height: calc(100vh - 250px);
- margin: 15px 0;
- font-size: 16px;
- }
- .ttv-header {
- margin-bottom: 20px;
- padding-bottom: 15px;
- border-bottom: 2px solid #eee;
- font-weight: bold;
- font-size: 16px;
- color: #444;
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
- button.btn-warning {
- background: #f0ad4e;
- color: white;
- border: none;
- padding: 12px;
- border-radius: 6px;
- width: 100%;
- font-size: 14px;
- transition: all 0.2s ease;
- }
- button.btn-warning:hover {
- background: #ec971f;
- transform: translateY(-1px);
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
- }
- button.ttv-minimize {
- padding: 2px 8px;
- background: none;
- border: none;
- cursor: pointer;
- font-size: 16px;
- color: #666;
- transition: all 0.2s ease;
- }
- button.ttv-minimize:hover {
- color: #333;
- }
- `;
- document.head.appendChild(style);
- // Tạo div thông báo
- const notification = document.createElement('div');
- notification.className = 'ttv-notification';
- document.body.appendChild(notification);
- // Hiển thị thông báo
- function showNotification(message, isError = false) {
- notification.textContent = message;
- notification.className = 'ttv-notification' + (isError ? ' ttv-error' : '');
- notification.style.display = 'block';
- setTimeout(() => {
- notification.style.display = 'none';
- }, 3000);
- }
- // Parse chapters function update
- function parseChapters(content) {
- const lines = content.split('\n');
- const chapters = [];
- let currentChapter = {
- title: '',
- name: '',
- content: []
- };
- const chapterPattern = /^\s*Chương\s+\d+:/;
- let previousChapterTitle = '';
- // Find chapters with clear markers
- for (let i = 0; i < lines.length; i++) {
- const line = lines[i].trim();
- if (chapterPattern.test(line)) {
- // Check if it's a duplicate header
- if (line !== previousChapterTitle) {
- // Log for debug
- console.log(`Tìm thấy chương mới: ${line}`);
- // If there was a previous chapter, save it
- if (currentChapter.title && currentChapter.content.length > 0) {
- chapters.push({...currentChapter});
- console.log(`Đã lưu chương: ${currentChapter.title}\nTên: ${currentChapter.name}\nSố dòng: ${currentChapter.content.length}`);
- currentChapter = {title: '', name: '', content: []};
- }
- // Save current chapter title
- currentChapter.title = line;
- // Get chapter name after :
- let name = line.split(':')[1]?.trim() || '';
- // Remove punctuation from start and end of chapter name
- name = name.replace(/^[.,;'"]+|[.,;'"]+$/g, '').trim();
- currentChapter.name = name;
- previousChapterTitle = line;
- console.log(`Đang xử lý chương mới:\nTiêu đề: ${line}\nTên chương: ${name}`);
- } else {
- console.log(`Bỏ qua tiêu đề trùng lặp: ${line}`);
- }
- } else {
- // Save all non-chapter-title content
- if (line) { // Only add non-empty lines
- if (currentChapter.title) {
- currentChapter.content.push(line);
- }
- }
- }
- }
- // Add the last chapter if exists
- if (currentChapter.title && currentChapter.content.length > 0) {
- chapters.push({...currentChapter});
- }
- // Display chapters and auto fill forms
- if (chapters.length > 0) {
- displayChapters(chapters);
- // Auto fill forms
- setTimeout(() => {
- try {
- transferContent();
- } catch (error) {
- console.error('Lỗi khi tự động điền form:', error);
- showNotification('Có lỗi xảy ra khi tự động điền form!', true);
- }
- }, 1000);
- }
- console.log(`Tổng số chương tìm thấy: ${chapters.length}`);
- return chapters;
- }
- // Thêm function createChapterHTML
- function createChapterHTML(chapNum) {
- const chap_vol = parseInt(jQuery('.chap_vol').val()) || 1;
- const chap_vol_name = jQuery('.chap_vol_name').val() || '';
- return `
- <div data-gen="MK_GEN" id="COUNT_CHAP_${chapNum}_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[${chapNum}]" value="${dăngnhanhTTV.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="${dăngnhanhTTV.STATE.CHAP_SERIAL}" required class="form-control" name="chap_number[${chapNum}]" 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[${chapNum}]" 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[${chapNum}]" 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[${chapNum}]" 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[${chapNum}]" rows="20" placeholder="Nội dung" type="text"></textarea>
- <div class="chapter-character-count"></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[${chapNum}]" placeholder="Quảng cáo" type="text"></textarea>
- </div>
- </div>
- </div>`;
- }
- // Add state management
- const dăngnhanhTTV = {
- STATE: {
- CHAP_NUMBER: 1,
- CHAP_STT: 1,
- CHAP_SERIAL: 1,
- CHAP_NUMBER_ORIGINAL: 1,
- CHAP_STT_ORIGINAL: 1,
- CHAP_SERIAL_ORIGINAL: 1
- },
- initializeChapterValues: function() {
- 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());
- }
- this.STATE.CHAP_NUMBER = this.STATE.CHAP_NUMBER_ORIGINAL = chap_number || 1;
- this.STATE.CHAP_STT = this.STATE.CHAP_STT_ORIGINAL = chap_stt || 1;
- this.STATE.CHAP_SERIAL = this.STATE.CHAP_SERIAL_ORIGINAL = chap_serial || 1;
- } catch (e) {
- console.error("Error initializing chapter values:", e);
- }
- }
- };
- // Update transferContent function to use createChapterHTML
- async function transferContent() {
- try {
- console.log("Bắt đầu quá trình chuyển nội dung");
- // Get all chapters
- const chapterItems = document.querySelectorAll('.ttv-chapter-item');
- if (!chapterItems.length) {
- throw new Error('Không tìm thấy chương nào để điền vào form');
- }
- console.log(`Tìm thấy ${chapterItems.length} chương để điền vào form`);
- // Find the form container
- const form = document.querySelector('form[name="postChapForm"]');
- if (!form) {
- throw new Error('Không tìm thấy form đăng chương');
- }
- console.log("Tìm thấy form đăng chương");
- // Get and update state values
- let chap_vol = 1;
- let chap_vol_name = '';
- try {
- chap_vol = parseInt(jQuery('.chap_vol').val()) || 1;
- chap_vol_name = jQuery('.chap_vol_name').val() || '';
- console.log(`Đã lấy thông tin quyển: số ${chap_vol}, tên: ${chap_vol_name}`);
- } catch (e) {
- console.warn("Không thể lấy thông tin quyển, sử dụng giá trị mặc định", e);
- }
- // Tìm STT chương và số thứ tự cao nhất trong form hiện tại
- let maxChapStt = 0;
- let maxChapSerial = 0;
- try {
- // Tìm tất cả các form chương hiện có
- const existingForms = form.querySelectorAll('[id^="COUNT_CHAP_"]');
- console.log(`Tìm thấy ${existingForms.length} form chương hiện có`);
- // Tìm STT và số thứ tự cao nhất
- existingForms.forEach(formElem => {
- try {
- // Lấy index từ ID của form, ví dụ: COUNT_CHAP_1_MK -> 1
- const formIdMatch = formElem.id.match(/COUNT_CHAP_(\d+)_MK/);
- if (formIdMatch && formIdMatch[1]) {
- const formIndex = parseInt(formIdMatch[1]);
- // Lấy giá trị STT
- const sttInput = formElem.querySelector(`input[name="chap_stt[${formIndex}]"]`);
- if (sttInput && sttInput.value && !isNaN(parseInt(sttInput.value))) {
- const sttVal = parseInt(sttInput.value);
- if (sttVal > maxChapStt) {
- maxChapStt = sttVal;
- }
- }
- // Lấy giá trị số thứ tự
- const serialInput = formElem.querySelector(`input[name="chap_number[${formIndex}]"]`);
- if (serialInput && serialInput.value && !isNaN(parseInt(serialInput.value))) {
- const serialVal = parseInt(serialInput.value);
- if (serialVal > maxChapSerial) {
- maxChapSerial = serialVal;
- }
- }
- }
- } catch (formError) {
- console.warn("Lỗi khi đọc giá trị từ form:", formError);
- }
- });
- console.log(`STT chương cao nhất tìm thấy: ${maxChapStt}, Số thứ tự cao nhất: ${maxChapSerial}`);
- // Cập nhật state với giá trị cao nhất tìm được
- if (maxChapStt > 0) {
- dăngnhanhTTV.STATE.CHAP_STT = maxChapStt;
- }
- if (maxChapSerial > 0) {
- dăngnhanhTTV.STATE.CHAP_SERIAL = maxChapSerial;
- }
- } catch (e) {
- console.warn("Không thể tìm STT và số thứ tự cao nhất, sử dụng giá trị mặc định", e);
- }
- // Đếm số form chương hiện có để xác định index bắt đầu
- const existingFormCount = form.querySelectorAll('[id^="COUNT_CHAP_"]').length;
- console.log(`Số form chương hiện có: ${existingFormCount}`);
- console.log(`Bắt đầu tạo ${chapterItems.length} form chương mới từ index ${existingFormCount + 1}`);
- // Tạo form cho mỗi chương
- for (let i = 0; i < chapterItems.length; i++) {
- const formIndex = existingFormCount + i + 1;
- console.log(`Đang tạo form cho chương ${formIndex}`);
- try {
- // Tăng các giá trị STT và số thứ tự cho chương mới
- dăngnhanhTTV.STATE.CHAP_STT++;
- dăngnhanhTTV.STATE.CHAP_SERIAL++;
- console.log(`Sử dụng STT: ${dăngnhanhTTV.STATE.CHAP_STT}, Số thứ tự: ${dăngnhanhTTV.STATE.CHAP_SERIAL}`);
- // Tạo HTML cho form chương mới
- const chapterHTML = createChapterHTML(formIndex);
- const tempDiv = document.createElement('div');
- tempDiv.innerHTML = chapterHTML;
- const newFormElement = tempDiv.firstElementChild;
- if (!newFormElement) {
- throw new Error(`Không thể tạo element form cho chương ${formIndex}`);
- }
- form.appendChild(newFormElement);
- console.log(`Đã thêm form HTML cho chương ${formIndex}`);
- // Lấy thông tin chương
- const chapterItem = chapterItems[i];
- if (!chapterItem) {
- throw new Error(`Không tìm thấy dữ liệu cho chương ${formIndex}`);
- }
- const titleElement = chapterItem.querySelector('.chapter-title');
- const nameElement = chapterItem.querySelector('.chapter-name');
- if (!titleElement || !nameElement) {
- throw new Error(`Thiếu thông tin tiêu đề hoặc tên cho chương ${formIndex}`);
- }
- const chapterTitle = titleElement.textContent;
- const chapterName = nameElement.textContent.replace('Tên chương: ', '');
- const chapterNumberMatch = chapterTitle.match(/Chương\s+(\d+)/);
- const chapterNumber = chapterNumberMatch ? chapterNumberMatch[1] : dăngnhanhTTV.STATE.CHAP_SERIAL.toString();
- console.log(`Thông tin chương ${formIndex}: Tiêu đề: ${chapterTitle}, Tên: ${chapterName}, Số: ${chapterNumber}`);
- // Lấy các trường form
- const formFields = {
- chapterName: form.querySelector(`input[name="chap_name[${formIndex}]"]`),
- content: form.querySelector(`textarea[name="introduce[${formIndex}]"]`),
- chapterNumber: form.querySelector(`input[name="chap_number[${formIndex}]"]`),
- chapterOrder: form.querySelector(`input[name="chap_stt[${formIndex}]"]`),
- volume: form.querySelector(`input[name="vol[${formIndex}]"]`),
- volumeName: form.querySelector(`input[name="vol_name[${formIndex}]"]`),
- advertisement: form.querySelector(`textarea[name="adv[${formIndex}]"]`)
- };
- // Kiểm tra xem có thiếu trường nào không
- const missingFields = [];
- for (const [fieldName, element] of Object.entries(formFields)) {
- if (!element) {
- missingFields.push(fieldName);
- }
- }
- if (missingFields.length > 0) {
- throw new Error(`Thiếu các trường ${missingFields.join(', ')} trong form ${formIndex}`);
- }
- // Điền dữ liệu vào form
- formFields.chapterName.value = chapterName;
- formFields.chapterNumber.value = dăngnhanhTTV.STATE.CHAP_SERIAL.toString();
- formFields.chapterOrder.value = dăngnhanhTTV.STATE.CHAP_STT.toString();
- formFields.volume.value = chap_vol;
- formFields.volumeName.value = chap_vol_name;
- // Kiểm tra và gán nội dung
- if (chapterItem._content) {
- formFields.content.value = chapterItem._content;
- } else {
- console.warn(`Chương ${formIndex} không có nội dung`);
- formFields.content.value = '';
- }
- formFields.advertisement.value = '';
- console.log(`Đã điền form cho chương ${formIndex}: Tên: ${chapterName}, STT: ${dăngnhanhTTV.STATE.CHAP_STT}, Số thứ tự: ${dăngnhanhTTV.STATE.CHAP_SERIAL}, Độ dài nội dung: ${formFields.content.value.length} ký tự`);
- // Kích hoạt sự kiện input để cập nhật số đếm ký tự
- try {
- const inputEvent = new Event('input', { bubbles: true });
- formFields.content.dispatchEvent(inputEvent);
- } catch (inputError) {
- console.warn(`Không thể kích hoạt sự kiện input cho chương ${formIndex}:`, inputError);
- }
- } catch (chapterError) {
- console.error(`Lỗi khi xử lý chương ${formIndex}:`, chapterError);
- showNotification(`Lỗi ở chương ${formIndex}: ${chapterError.message}`, true);
- // Tiếp tục với chương tiếp theo thay vì dừng toàn bộ quá trình
- }
- }
- showNotification(`Đã tự động điền ${chapterItems.length} chương vào form!`);
- console.log(`Đã hoàn thành việc điền ${chapterItems.length} chương vào form`);
- } catch (error) {
- console.error('Lỗi khi điền form:', error);
- showNotification('Có lỗi xảy ra khi điền form: ' + error.message, true);
- }
- }
- // Chọn chương để hiển thị
- function selectChapter(chapter, index) {
- const contentEditor = document.querySelector('.ttv-content-editor');
- contentEditor.value = chapter.title + '\n' + chapter.content.join('\n');
- // Cập nhật số từ
- const wordCountSpan = document.querySelector('.ttv-word-count');
- if (wordCountSpan) {
- wordCountSpan.textContent = updateWordCount(contentEditor.value);
- }
- // Highlight selected chapter
- const chapterItems = document.querySelectorAll('.ttv-chapter-item');
- chapterItems.forEach(item => item.classList.remove('selected'));
- chapterItems[index]?.classList.add('selected');
- // Tự động điền form khi chọn chương
- try {
- transferContent();
- } catch (error) {
- console.error('Lỗi khi tự động điền form:', error);
- showNotification('Có lỗi xảy ra khi tự động điền form!', true);
- }
- }
- // Hiển thị danh sách chương
- function displayChapters(chapters) {
- const chapterList = document.createElement('div');
- chapterList.className = 'ttv-chapter-list';
- chapters.forEach((chapter, index) => {
- const chapterItem = document.createElement('div');
- chapterItem.className = 'ttv-chapter-item';
- // Store chapter content
- chapterItem._content = chapter.title + '\n' + chapter.content.join('\n');
- // Tạo các phần tử con với định dạng riêng
- const titleDiv = document.createElement('div');
- titleDiv.className = 'chapter-title';
- titleDiv.textContent = chapter.title;
- const nameDiv = document.createElement('div');
- nameDiv.className = 'chapter-name';
- nameDiv.textContent = `Tên chương: ${chapter.name}`;
- const statsDiv = document.createElement('div');
- statsDiv.className = 'chapter-stats';
- statsDiv.textContent = `${chapter.content.length} dòng`;
- // Thêm các phần tử vào item
- chapterItem.appendChild(titleDiv);
- chapterItem.appendChild(nameDiv);
- chapterItem.appendChild(statsDiv);
- chapterItem.onclick = () => selectChapter(chapter, index);
- chapterList.appendChild(chapterItem);
- // Select first chapter by default
- if (index === 0) {
- chapterItem.classList.add('selected');
- }
- });
- const existingList = document.querySelector('.ttv-chapter-list');
- if (existingList) {
- existingList.remove();
- }
- const contentEditor = document.querySelector('.ttv-content-editor');
- contentEditor.parentNode.insertBefore(chapterList, contentEditor);
- // Hiển thị thông báo về số chương tìm thấy
- showNotification(`Đã tìm thấy ${chapters.length} chương và tự động điền vào form.`);
- // Tự động chọn chương đầu tiên
- if (chapters.length > 0) {
- selectChapter(chapters[0], 0);
- }
- }
- // Đếm số từ và ký tự
- function updateWordCount(content) {
- const wordCount = content.trim().split(/\s+/).length;
- const charCount = content.length;
- return `${wordCount} từ | ${charCount} ký tự`;
- }
- // Chuyển đổi giữa chế độ soạn thảo và xem trước
- function togglePreview() {
- const contentEditor = document.querySelector('.ttv-content-editor');
- const preview = document.querySelector('.ttv-preview');
- const previewBtn = document.querySelector('.ttv-preview-btn');
- if (contentEditor.style.display !== 'none') {
- contentEditor.style.display = 'none';
- preview.style.display = 'block';
- preview.innerHTML = contentEditor.value.replace(/\n/g, '<br>');
- previewBtn.textContent = 'Soạn thảo';
- } else {
- contentEditor.style.display = 'block';
- preview.style.display = 'none';
- previewBtn.textContent = 'Xem trước';
- }
- }
- // Chuyển đổi chế độ toàn màn hình
- function toggleFullscreen() {
- const panel = document.querySelector('.ttv-control-panel');
- const fullscreenBtn = document.querySelector('.ttv-fullscreen-btn');
- panel.classList.toggle('fullscreen');
- fullscreenBtn.textContent = panel.classList.contains('fullscreen') ? 'Thu nhỏ' : 'Toàn màn hình';
- }
- // Thêm panel điều khiển
- function addControlPanel() {
- // Tạo panel
- const panel = document.createElement('div');
- panel.className = 'ttv-control-panel';
- // Thêm header
- const header = document.createElement('div');
- header.className = 'ttv-header';
- header.innerHTML = `
- <div>Soạn Thảo Nội Dung</div>
- <div class="ttv-toolbar">
- <button class="ttv-preview-btn" onclick="togglePreview()">Xem trước</button>
- <button class="ttv-fullscreen-btn" onclick="toggleFullscreen()">Toàn màn hình</button>
- <button class="ttv-minimize">−</button>
- </div>
- `;
- const buttonGroup = document.createElement('div');
- buttonGroup.className = 'ttv-button-group';
- // Khung soạn thảo nội dung
- const contentEditorLabel = document.createElement('div');
- contentEditorLabel.className = 'ttv-heading';
- contentEditorLabel.innerHTML = `
- Nội dung chương:
- <span class="ttv-word-count">0 từ | 0 ký tự</span>
- `;
- const contentEditor = document.createElement('textarea');
- contentEditor.className = 'ttv-content-editor';
- contentEditor.placeholder = 'Nhập hoặc dán nội dung chương vào đây...';
- // Khung xem trước
- const preview = document.createElement('div');
- preview.className = 'ttv-preview';
- // Cập nhật số từ khi nhập nội dung
- contentEditor.oninput = () => {
- const wordCountSpan = document.querySelector('.ttv-word-count');
- if (wordCountSpan) {
- wordCountSpan.textContent = updateWordCount(contentEditor.value);
- }
- };
- // Xử lý khi paste nội dung
- contentEditor.onpaste = (e) => {
- // Cho phép paste hoàn tất
- setTimeout(() => {
- const content = contentEditor.value;
- const chapters = parseChapters(content);
- if (chapters.length > 0) {
- displayChapters(chapters);
- // Tự động điền form sau khi hiển thị danh sách chương
- setTimeout(() => {
- try {
- transferContent();
- } catch (error) {
- console.error('Lỗi khi tự động điền form:', error);
- showNotification('Có lỗi xảy ra khi tự động điền form!', true);
- }
- }, 1000); // Đợi 1s để đảm bảo UI đã được cập nhật hoàn toàn
- }
- }, 0);
- };
- // Nút chuyển nội dung
- const transferBtn = document.createElement('button');
- transferBtn.type = 'button';
- transferBtn.className = 'btn btn-warning';
- transferBtn.innerHTML = '<span>Chuyển nội dung sang form</span>';
- transferBtn.onclick = transferContent;
- // Thêm các phần tử vào panel
- panel.appendChild(header);
- buttonGroup.appendChild(contentEditorLabel);
- buttonGroup.appendChild(contentEditor);
- buttonGroup.appendChild(preview);
- buttonGroup.appendChild(transferBtn);
- panel.appendChild(buttonGroup);
- document.body.appendChild(panel);
- // Thêm xử lý sự kiện cho các nút trong toolbar
- const minimizeBtn = panel.querySelector('.ttv-minimize');
- minimizeBtn.onclick = () => {
- panel.classList.toggle('minimized');
- minimizeBtn.innerHTML = panel.classList.contains('minimized') ? '+' : '−';
- };
- window.togglePreview = togglePreview;
- window.toggleFullscreen = toggleFullscreen;
- }
- // Initialize chapter values when page loads
- window.addEventListener('load', function() {
- dăngnhanhTTV.initializeChapterValues();
- addControlPanel();
- });
- })();