OXIESEC PANEL
- Current Dir:
/
/
var
/
www
/
3-31-025chanakya
/
Xpress
/
NLEditor
Server IP: 139.59.38.164
Upload:
Create Dir:
Name
Size
Modified
Perms
📁
..
-
03/26/2025 04:21:24 AM
rwxr-xr-x
📄
Edit-arvind-index.php
61.9 KB
03/26/2025 04:16:33 AM
rw-r--r--
📁
NW_images
-
03/26/2025 04:21:28 AM
rwxr-xr-x
📄
aditya_index.php
44.57 KB
03/26/2025 04:16:33 AM
rw-r--r--
📄
arvind-save-template.php
3.75 KB
03/26/2025 04:16:33 AM
rw-r--r--
📄
arvinds-index.php
52.82 KB
03/26/2025 04:16:33 AM
rw-r--r--
📁
assets
-
03/26/2025 04:21:24 AM
rwxr-xr-x
📄
db_gallery.php
285 bytes
03/26/2025 04:16:33 AM
rw-r--r--
📄
delete_image.php
646 bytes
03/26/2025 04:16:33 AM
rw-r--r--
📄
edit_template.php
658 bytes
03/26/2025 04:16:33 AM
rw-r--r--
📄
fetch_data.php
779 bytes
03/26/2025 04:16:33 AM
rw-r--r--
📄
fetch_titles.php
0 bytes
03/26/2025 04:16:33 AM
rw-r--r--
📁
images10
-
03/26/2025 04:20:38 AM
rwxr-xr-x
📁
images11
-
03/26/2025 04:20:38 AM
rwxr-xr-x
📁
images12
-
03/26/2025 04:20:38 AM
rwxr-xr-x
📁
images13
-
03/26/2025 04:20:39 AM
rwxr-xr-x
📁
images14
-
03/26/2025 04:20:40 AM
rwxr-xr-x
📁
images15
-
03/26/2025 04:20:43 AM
rwxr-xr-x
📁
images16
-
03/26/2025 04:20:43 AM
rwxr-xr-x
📁
images17
-
03/26/2025 04:20:44 AM
rwxr-xr-x
📁
images18
-
03/26/2025 04:20:44 AM
rwxr-xr-x
📁
images19
-
03/26/2025 04:20:45 AM
rwxr-xr-x
📁
images2
-
03/26/2025 04:20:47 AM
rwxr-xr-x
📁
images20
-
03/26/2025 04:20:47 AM
rwxr-xr-x
📁
images21
-
03/26/2025 04:20:48 AM
rwxr-xr-x
📁
images3
-
03/26/2025 04:20:52 AM
rwxr-xr-x
📁
images4
-
03/26/2025 04:20:52 AM
rwxr-xr-x
📁
images5
-
03/26/2025 04:20:54 AM
rwxr-xr-x
📁
images6
-
03/26/2025 04:20:54 AM
rwxr-xr-x
📁
images7
-
03/26/2025 04:20:56 AM
rwxr-xr-x
📁
images8
-
03/26/2025 04:20:56 AM
rwxr-xr-x
📁
images9
-
03/26/2025 04:20:57 AM
rwxr-xr-x
📄
index.php
45.3 KB
03/26/2025 04:16:33 AM
rw-r--r--
📄
load_template.php
2.2 KB
03/26/2025 04:16:33 AM
rw-r--r--
📁
newsletter
-
03/26/2025 04:21:25 AM
rwxr-xr-x
📄
nleditor_navbar.php
10.1 KB
03/26/2025 04:16:33 AM
rw-r--r--
📄
save-template.php
2.45 KB
03/26/2025 04:16:33 AM
rw-r--r--
📄
script.js
34.02 KB
03/26/2025 04:16:33 AM
rw-r--r--
📄
styles.css
2.43 KB
03/26/2025 04:16:33 AM
rw-r--r--
📄
template1.jpg
0 bytes
03/26/2025 04:16:33 AM
rw-r--r--
📄
test.php
1.58 KB
03/26/2025 04:16:33 AM
rw-r--r--
📄
test_index.php
46.51 KB
03/26/2025 04:16:33 AM
rw-r--r--
📄
texts.txt
1.7 KB
03/26/2025 04:16:33 AM
rw-r--r--
📄
upload.php
858 bytes
03/26/2025 04:16:33 AM
rw-r--r--
📁
uploads
-
03/26/2025 04:20:57 AM
rwxr-xr-x
Editing: Edit-arvind-index.php
Close
<? include '../assets/php/validate.logged.php'; ?> <?php ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Newsletter Template Editor</title> <script src="../../assets/js/interact.js"></script> <script src="script.js"></script> <!-- <script src="../../assets/js/jquery-3.6.0.min.js"></script> --> <!-- jQuery --> <!-- Font Awesome CSS --> <link rel="stylesheet" href="../../assets/css/bootstrap.min.4.5.0.css" /> <link rel="stylesheet" href="../assets/css/styles.css"> <link rel="stylesheet" href="assets/css/styles.css"> <link rel="stylesheet" href="assets/css/textEdit.css"> <script src="../../assets/js/jquery-3.6.0.min.js"></script> <!-- <script src="../../assets/js/jquery-3.5.1.min.js" ></script> --> <script src="../../assets/js/bootstrap.min.4.5.js" ></script> <script src="../../assets/js/Chart.min.2.8.js" crossorigin="anonymous"></script> <style> .modal { display: none; position: fixed; z-index: 450; right: 0; top: 0; width: 30%; height: 100vh; overflow: auto; background-color: #fafafa; margin-top: 15vh; margin-left: 30px; } </style> </head> <?php include "nleditor_navbar.php" ?> <body> <div class="body"> <div class="sidebar"> <h2>Templates</h2> <div class="template-list"> <div class="template" data-template="template_1" data-category="basic_newsletter"> <span>Basic Newsletter</span> </div> <div class="template" data-template="template_2" data-category="modern_layout"> <span>Modern Layout</span> </div> <div class="template" data-template="template_3" data-category="professional"> <span>Professional</span> </div> <div class="template" data-template="template_4" data-category="minimal_desing"> <span>Minimal Design</span> </div> <div class="template" data-template="template_5" data-category="creative_style"> <span>Creative Style</span> </div> </div> <button onclick="window.location.href='../choose_template.php'">My templates</button> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav"> <li class="nav-item"> <a class="nav-link" href="../choose_template.php">My Templates</a> </li> <li class="nav-item"> <a class="nav-link" href="#" id="addTextNavBtn">Add Text</a> </li> <li class="nav-item"> <a class="nav-link" href="#" id="saveTemplateNavBtn">Save Template</a> </li> </ul> </div> </div> <div id="new-div-container"></div> <div class="main-content"> <div class="canvas-area" id="canvasArea"> </div> </div> <div id="myModal" class="modal" style="z-index: 450;"> <div class="modal-content"> <span class="close">×</span> <h2>Collection Modal</h2> <div id="modalContent"> <!-- Data will be loaded here dynamically --> </div> </div> </div> <script> $(document).ready(function() { $('#addTextNavBtn').on('click', function(e) { e.preventDefault(); if (typeof addNewTextElement === 'function') { addNewTextElement(); } }); $('#saveTemplateNavBtn').on('click', function(e) { e.preventDefault(); if (typeof saveTemplate === 'function') { saveTemplate(); } }); }); </script> <script> $(document).ready(function() { $('.template').each(function() { $(this).on('click', function() { const category = $(this).data('category'); // Create the URL of the PHP endpoint for category-based fetching const templateUrl = `load_template.php?category=${category}`; // Create a new div element for the template content const newDiv = $('<div class="new-div"></div>'); // Show loading message while fetching newDiv.html('<p>Loading templates...</p>'); // Append the new div to the new-div-container $('#new-div-container').empty().append(newDiv); // Clear previous content before appending // Fetch the template data from the server using the PHP endpoint $.ajax({ url: templateUrl, // The PHP endpoint method: 'GET', // HTTP method to use success: function(response) { // Check if response contains templates (using data-template-path) if (response.includes('data-template-path')) { // Parse the response and display the templates const templatesContainer = $(response); // Assuming multiple templates are returned // Insert each template into the new div newDiv.html(templatesContainer); // Add click event for each template newDiv.find('.template').each(function() { $(this).on('click', function() { // Get the template's path and image details const templatePath = $(this).data('template-path'); const imagePath = $(this).find('img').attr('src'); // Handle template selection: Update canvas area const canvasArea = $('#canvasArea'); canvasArea.empty(); // Clear the current content // Fetch and display the template content in the canvas area $.ajax({ url: templatePath, method: 'GET', success: function(templateContent) { // Insert the template content into the canvas area canvasArea.html(templateContent); // Make all text elements editable makeElementsEditable(); // Set up image uploads setupImageUploads(); // Add save button functionality addSaveButton(templatePath); }, error: function(xhr, status, error) { canvasArea.html(`<p>Error loading template: ${error}</p>`); } }); }); }); } else { // Handle error if no templates are returned newDiv.html('<p>No templates found for this category.</p>'); } }, error: function(xhr, status, error) { // If there's an error (e.g., file not found), show an error message newDiv.html(`<p>Error: ${error}</p>`); } }); }); }); }); </script> <script> // Function to show the modal function showModal() { var modal = document.getElementById("myModal"); modal.style.display = "block"; // Show the modal } // Function to close the modal function closeModal() { var modal = document.getElementById("myModal"); modal.style.display = "none"; // Hide the modal } // Get the button that triggers the modal var collectionBtn = document.getElementById("collectionBtn"); // Add click event listener to the "Collection" button collectionBtn.addEventListener("click", function() { fetchData(); // Fetch data when the button is clicked showModal(); // Show the modal }); // Close the modal when the <span> (close button) is clicked document.getElementsByClassName("close")[0].addEventListener("click", closeModal); // Close the modal if the user clicks outside the modal content window.addEventListener("click", function(event) { var modal = document.getElementById("myModal"); if (event.target === modal) { closeModal(); } }); </script> <script> // Function to make elements editable and add action icons function makeElementsEditable() { // Make various elements editable $('#canvasArea p, #canvasArea h1, #canvasArea h2, #canvasArea h3, #canvasArea h4, #canvasArea h5, #canvasArea h6, #canvasArea span, #canvasArea a, #canvasArea li, #canvasArea strong, #canvasArea em, #canvasArea b, #canvasArea i') .attr('contenteditable', 'true') // Make elements editable .addClass('editable-element') // Add a class to identify editable elements .css({ 'position': 'relative' // For positioning the action icons only }) .hover( function() { // Add a dashed border without changing any other styles var originalBorder = $(this).css('border'); $(this).data('original-border', originalBorder); // Remove any existing action icons first to prevent duplicates $('.edit-actions').remove(); // Create action icons container var $actionsContainer = $('<div class="edit-actions"></div>').css({ 'position': 'absolute', 'top': '-30px', 'right': '0', 'background-color': '#f8f9fa', 'border': '1px solid #ddd', 'border-radius': '4px', 'padding': '5px 8px', 'box-shadow': '0 2px 5px rgba(0,0,0,0.1)', 'z-index': '1000', 'display': 'flex', 'gap': '10px' }); // Add edit text icon with tooltip var $editTextBtn = $('<span class="edit-action edit-text-btn" title="Edit Text"></span>').html('<i class="fas fa-edit"></i>') .css({ 'cursor': 'pointer', 'color': '#007bff', 'padding': '2px' }); // Add link icon with tooltip var $addLinkBtn = $('<span class="edit-action add-link-btn" title="Add/Edit Link"></span>').html('<i class="fas fa-link"></i>') .css({ 'cursor': 'pointer', 'color': '#007bff', 'padding': '2px' }); // Add My Collection button with tooltip var $myCollectionBtn = $('<span class="edit-action my-collection-btn" title="My Collection" id="collectionBtn"></span>').html('<i class="fas fa-folder"></i>') .css({ 'cursor': 'pointer', 'color': '#007bff', 'padding': '2px' }); // Append buttons to container $actionsContainer.append($editTextBtn).append($addLinkBtn).append($myCollectionBtn); // Append container to element $(this).append($actionsContainer); }, function() { // Restore original border $(this).css('border', $(this).data('original-border')); // Hide action icons immediately if not hovering over them if (!$('.edit-actions:hover').length) { $('.edit-actions').remove(); } } ) .on('input keyup', function(e) { // Check if the element is empty (considering whitespace) if ($(this).text().trim() === '') { // If element is empty, remove it $(this).remove(); } }); // Add event handler for the action icons so they don't disappear immediately when hovered $(document).on('mouseleave', '.edit-actions', function() { $(this).remove(); }); } $(document).ready(function() { // Add modal HTML to the body $('body').append(` <div id="myModal" style="display: none; position: fixed; z-index: 1050; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.4);"> <div style="background-color: #fefefe; margin: 10% auto; padding: 20px; border: 1px solid #888; width: 80%; border-radius: 5px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);"> <span class="close" style="color: #000; float: right; font-size: 28px; font-weight: bold; cursor: pointer;">×</span> <h2>My Collection</h2> <div id="modalContent" style="margin-top: 15px; max-height: 400px; overflow-y: auto;"> Loading... </div> <div style="margin-top: 15px; text-align: right;"> <button id="insertBtn" style="padding: 8px 16px; color: white; border: none; border-radius: 4px; cursor: pointer;">Insert Selected</button> </div> </div> </div> `); // Initialize the editable elements makeElementsEditable(); // Handle click on edit text button $(document).on('click', '.edit-text-btn', function(e) { e.stopPropagation(); // Get the parent editable element var $element = $(this).closest('.editable-element'); // Hide the action icons immediately $('.edit-actions').remove(); // Focus on the parent editable element $element.focus(); }); // Handle click on add link button $(document).ready(function() { $("#canvasArea").on("click", "a", function(event) { event.preventDefault(); // Prevent default link behavior let currentHref = $(this).attr("href") || ""; // Get current href let newHref = prompt("Enter new link:", currentHref); // Prompt user for new link if (newHref !== null && newHref.trim() !== "") { $(this).attr("href", newHref); // Update the href attribute } }); }); // Handle click on my collection button $(document).on('click', '.my-collection-btn', function(e) { e.stopPropagation(); // Get the parent editable element for later reference var $element = $(this).closest('.editable-element'); // Store the current element for later use when inserting content $('#myModal').data('currentElement', $element); // Hide the action icons immediately $('.edit-actions').remove(); // Run the collection script fetchData(); showModal(); }); // Add Insert Selected button functionality $(document).on('click', '#insertBtn', function() { var selectedIds = []; $('input[name="item"]:checked').each(function() { selectedIds.push($(this).val()); }); if (selectedIds.length > 0) { // Get the current element where we want to insert the content var $currentElement = $('#myModal').data('currentElement'); // For demonstration, we'll just show a message // In a real implementation, you would fetch the content for these IDs // and insert it into the current element showFeedback($currentElement, selectedIds.length + " item(s) will be inserted"); // Close the modal closeModal(); } else { alert("Please select at least one item."); } }); // Function to show feedback tooltip function showFeedback($element, message) { // Create feedback tooltip var $feedback = $('<div class="edit-feedback"></div>').text(message).css({ 'position': 'absolute', 'top': '-30px', 'right': '0', 'background-color': '#28a745', 'color': 'white', 'border-radius': '4px', 'padding': '3px 8px', 'font-size': '12px', 'opacity': '0', 'transition': 'opacity 0.3s ease-in-out' }); // Position relative to element $element.css('position', 'relative').append($feedback); // Show and hide the feedback $feedback.css('opacity', '1'); setTimeout(function() { $feedback.css('opacity', '0'); setTimeout(function() { $feedback.remove(); }, 300); }, 1500); } // Function to show the modal function showModal() { var modal = document.getElementById("myModal"); modal.style.display = "block"; // Show the modal } // Function to close the modal function closeModal() { var modal = document.getElementById("myModal"); modal.style.display = "none"; // Hide the modal } // Modified fetchData function to create clickable items instead of checkboxes function fetchData() { var xhr = new XMLHttpRequest(); xhr.open("GET", "fetch_data.php", true); xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { var data = JSON.parse(xhr.responseText); // Check if there's data if (data.message) { document.getElementById("modalContent").innerHTML = data.message; } else { // Initialize the content for clickable items var content = ""; // Function to limit text to a specific number of words function limitText(text, wordLimit) { var words = text.split(' '); if (words.length > wordLimit) { return words.slice(0, wordLimit).join(' ') + '...'; } else { return text; } } // Function to strip HTML tags function stripHtml(html) { // Create a temporary div element var temp = document.createElement('div'); // Set the HTML content temp.innerHTML = html; // Return just the text content return temp.textContent || temp.innerText || ''; } data.forEach(function(item) { // Strip HTML from title and limit var cleanTitle = stripHtml(item.title); var limitedTitle = limitText(cleanTitle, 10); // Encode content to avoid breaking HTML var encodedTitle = encodeURIComponent(cleanTitle); // Create a clickable item div for the heading content += ` <div class="collection-item" data-content="${encodedTitle}" data-type="heading"> <div class="item-preview"> <strong>Heading:</strong> ${limitedTitle} </div> </div>`; // For images, use a text placeholder instead of the image HTML if (item.image) { content += ` <div class="collection-item" data-content="${encodeURIComponent('[Image: ' + cleanTitle + ']')}" data-type="image"> <div class="item-preview"> <strong>Image:</strong> [Image preview] </div> </div>`; } // Clean and create a clickable item div for the description var cleanDescription = stripHtml(item.description); var limitedDescription = limitText(cleanDescription, 60); content += ` <div class="collection-item" data-content="${encodeURIComponent(cleanDescription)}" data-type="description"> <div class="item-preview"> <strong>Description:</strong> ${limitedDescription} </div> </div>`; // Add clickable items for each paragraph if (item.paragraphs && Array.isArray(item.paragraphs)) { item.paragraphs.forEach(function(paragraph, index) { var cleanParagraph = stripHtml(paragraph); var limitedParagraph = limitText(cleanParagraph, 60); content += ` <div class="collection-item" data-content="${encodeURIComponent(cleanParagraph)}" data-type="paragraph"> <div class="item-preview"> <strong>Paragraph ${index + 1}:</strong> ${limitedParagraph} </div> </div>`; }); } }); // Insert the generated content into the modal document.getElementById("modalContent").innerHTML = content; // Add styling for the collection items var style = document.createElement('style'); style.innerHTML = ` .collection-item { margin: 10px 0; padding: 10px; border: 1px solid #ddd; border-radius: 4px; cursor: pointer; transition: background-color 0.2s; } .collection-item:hover { background-color: #f0f8ff; } .item-preview { overflow: hidden; } `; document.head.appendChild(style); $('.collection-item').on('click', function() { // Retrieve the encoded content and decode it var content = decodeURIComponent($(this).attr('data-content')); var contentType = $(this).attr('data-type'); // Limit the content to 50 words before inserting var limitedContent = limitText(content, 60); // Get the target element where the content should be inserted var $targetElement = $('#myModal').data('currentElement'); // Insert the limited text content $targetElement.text(limitedContent); // Show feedback showFeedback($targetElement, contentType + " inserted!"); // Close the modal closeModal(); }); } } }; xhr.send(); } // Update the modal content to remove the Insert Selected button $(document).ready(function() { // Update modal HTML $('body').find('#myModal').html(` <div style="background-color: #fefefe; margin: 10% auto; padding: 20px; border: 1px solid #888; width: 80%; border-radius: 5px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);"> <span class="close" style="color: #000; float: right; font-size: 28px; font-weight: bold; cursor: pointer;">×</span> <h2>My Collection</h2> <p>Click an item to insert it:</p> <div id="modalContent" style="margin-top: 15px; max-height: 400px; overflow-y: auto;"> Loading... </div> </div> `); }); // Close the modal when the <span> (close button) is clicked $(document).on('click', '.close', closeModal); // Close the modal if the user clicks outside the modal content $(window).on('click', function(event) { var modal = document.getElementById("myModal"); if (event.target === modal) { closeModal(); } }); }); // Add Font Awesome if not already included if ($('link[href*="font-awesome"]').length === 0 && $('script[src*="font-awesome"]').length === 0) { $('head').append('<link rel="stylesheet" href="../../assets/css/all.min.5.15.3.css">'); } function setupImageUploads() { $('#canvasArea img').each(function() { const img = $(this); const uploadBtn = $('<input type="file" accept="image/*" style="display: none;">').insertAfter(img); const changeBtn = $('<button class="image-upload-btn"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M7 7H6a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2v-1"/><path d="M20.385 6.585a2.1 2.1 0 0 0-2.97-2.97L9 12v3h3zM16 5l3 3"/></g></svg></button>').insertAfter(img).css({ 'position': 'absolute', 'background': '#007bff', 'color': 'white', 'border': 'none', 'padding': '5px 10px', 'cursor': 'pointer', 'z-index': '400' // 'right': '50%' // 'display': 'none' }); changeBtn.click(function() { uploadBtn.click(); }); uploadBtn.change(function(e) { const file = e.target.files[0]; if (file) { const formData = new FormData(); formData.append('image', file); $.ajax({ url: 'upload.php', // PHP script to handle file upload type: 'POST', data: formData, contentType: false, processData: false, success: function(response) { if (response.startsWith("uploads/NW_images/")) { let tempSrc = response; // Temporary src while uploading img.attr('src', tempSrc); // After saving, update src by removing "uploads/" // let finalSrc = response.replace("uploads/", ""); // setTimeout(function() { // img.attr('src', finalSrc); // }, 2000); } else { alert('Error: ' + response); } }, error: function() { alert('Image upload failed.'); } }); } }); }); } // Function to add save button to the canvas area function addSaveButton() { // Remove any existing save button first $('#saveTemplate').remove(); // Create a new save button with proper styling const saveBtn = $('<button id="saveTemplate" class="btn btn-primary"></button>') .html('<i class="fas fa-save"></i> Save Template') .css({ 'position': 'fixed', 'top': '94%', 'height': ' fit-content', 'left': '90%', 'z-index': 1000, 'padding': '10px 15px', 'background-color': '#28a745', 'color': 'white', 'border': 'none', 'border-radius': '4px', 'box-shadow': '0 2px 5px rgba(0,0,0,0.2)', 'cursor': 'pointer' }); // Add hover effect saveBtn.hover( function() { $(this).css('background-color', '#218838'); }, function() { $(this).css('background-color', '#28a745'); } ); // Add click event handler saveBtn.on('click', function(e) { e.preventDefault(); saveTemplate(); }); // Add the button to the body $('body').append(saveBtn); console.log('Save button added to editor interface'); } // Helper function to get unique selector for an element $.fn.getPath = function() { if (this.length != 1) return false; var path = this.prop('tagName').toLowerCase(); if (this.attr('id')) { path += '#' + this.attr('id'); } else if (this.attr('class')) { path += '.' + this.attr('class').replace(/\s+/g, '.'); } return path; }; </script> <script> $(document).ready(function() { // Get the template parameter from URL const urlParams = new URLSearchParams(window.location.search); const templateParam = urlParams.get('template'); // If template parameter exists, load the template if (templateParam) { loadTemplate(templateParam); } }); function loadTemplate(templateParam) { // Show loading indicator $("#canvasArea").html('<div style="text-align:center;padding:50px;"><i class="fas fa-spinner fa-spin fa-3x"></i><p>Loading template...</p></div>'); // Perform AJAX request to load the template $.ajax({ url: "edit_template.php", type: "GET", data: { template: templateParam }, success: function(response) { // Insert the response into the canvas area $("#canvasArea").html(response); // Make all text elements editable makeElementsEditable(); // Set up image uploads setupImageUploads(); // Add save button functionality (no parameter needed) addSaveButton(); }, error: function(xhr, status, error) { // Show error message if request fails $("#canvasArea").html('<div class="alert alert-danger">Error loading template: ' + error + '</div>'); console.error("Error loading template:", error); } }); } </script> <script> // Enhanced save template function function saveTemplateEnhanced() { // Create a loading overlay const loadingOverlay = $('<div id="saveLoadingOverlay"></div>') .css({ 'position': 'fixed', 'top': 0, 'left': 0, 'width': '100%', 'height': '100%', 'background-color': 'rgba(0,0,0,0.5)', 'z-index': 9999, 'display': 'flex', 'justify-content': 'center', 'align-items': 'center' }) .append( $('<div></div>') .css({ 'background-color': 'white', 'padding': '20px', 'border-radius': '5px', 'text-align': 'center' }) .append('<i class="fas fa-spinner fa-spin fa-3x"></i>') .append('<p style="margin-top:10px;">Saving template...</p>') ); $('body').append(loadingOverlay); // Prompt for template name const templateName = prompt('Please enter a name for your template:'); // If user cancels the prompt or enters empty string, abort save if (!templateName) { $('#saveLoadingOverlay').remove(); return; } // Create the template filename const templateFileName = `${templateName}.html`; // Create FormData to handle both text content and images const formData = new FormData(); // Get the current state of the template const canvasArea = $('#canvasArea'); const template = canvasArea.clone(); // Clean up the template by removing editing controls template.find('.image-upload-btn, input[type="file"], .edit-actions, #saveTemplate').remove(); // Handle editable elements but preserve their styles template.find('.editable-element').each(function() { const $element = $(this); // Preserve all inline styles const styles = {}; const inlineStyles = $element.attr('style'); if (inlineStyles) { // Convert inline styles to an object const styleArr = inlineStyles.split(';'); for (let i = 0; i < styleArr.length; i++) { const style = styleArr[i].trim(); if (style) { const [property, value] = style.split(':'); if (property && value) { styles[property.trim()] = value.trim(); } } } } // Remove editing-specific attributes but keep the content and styles $element.removeAttr('contenteditable') .removeClass('editable-element') .removeClass('ui-draggable') .removeClass('ui-draggable-handle') .removeClass('selected'); // Make sure position is preserved $element.css({ 'position': 'absolute', 'left': styles.left || '0px', 'top': styles.top || '0px', 'font-family': styles['font-family'] || 'inherit', 'font-size': styles['font-size'] || 'inherit', 'color': styles.color || 'inherit', 'text-align': styles['text-align'] || 'inherit', 'font-weight': styles['font-weight'] || 'inherit', 'font-style': styles['font-style'] || 'inherit', 'text-decoration': styles['text-decoration'] || 'inherit', 'background-color': 'transparent', // Remove background 'border': 'none', // Remove border 'box-shadow': 'none', // Remove box shadow 'cursor': 'auto' // Reset cursor }); }); // Modify image src paths template.find('img').each(function() { let src = $(this).attr('src'); if (src && src.startsWith('uploads/')) { $(this).attr('src', src.replace('uploads/', 'https://knoblycream.com/Xpress/NLEditor/uploads/')); } }); // Get the HTML content const htmlContent = template.html(); // Log what we're about to send (for debugging) console.log({ templateFileName, contentLength: htmlContent.length, contentPreview: htmlContent.substring(0, 100) + '...' }); // Add template information to FormData formData.append('templateFileName', templateFileName); formData.append('template', htmlContent); // Debug log to confirm we're getting to this point console.log('Sending AJAX request to save template'); // Save the template - use the correct path to your PHP file $.ajax({ // Make sure this path is correct relative to your current page url: 'save-template.php', // Update this path to match your file structure method: 'POST', data: formData, processData: false, contentType: false, success: function(response) { $('#saveLoadingOverlay').remove(); console.log('Server response:', response); try { // Parse response as JSON if it's a string const result = typeof response === 'string' ? JSON.parse(response) : response; if (result.success) { alert('Template saved successfully!'); // Redirect to template selection page window.location.href = '../choose_template.php'; } else { // If there is an error from the server, show that error alert('Error saving template: ' + (result.error || 'Unknown error')); } } catch (e) { console.error('Error parsing response:', e); alert('Unexpected response format. Please check the console for details.'); } }, error: function(xhr, status, error) { $('#saveLoadingOverlay').remove(); console.error('Save request failed:', { status, error, responseText: xhr.responseText }); alert('Error saving template: ' + error); } }); } // Replace the original saveTemplate function with the enhanced version $(document).ready(function() { // Save the original function reference const originalSaveTemplate = window.saveTemplate; // Replace with our enhanced version window.saveTemplate = saveTemplateEnhanced; }); // Function to make elements editable and handle the conflict between dragging and editing function makeElementsEditable() { $('.editable-element').each(function() { // Store whether we're in editing mode $(this).data('editing', false); // Handle mousedown on the element $(this).off('mousedown').on('mousedown', function(e) { // If we're not already editing if (!$(this).data('editing')) { // We want to be able to drag it $(this).draggable('enable'); } }); // Handle click for editing $(this).off('click').on('click', function(e) { // Enable editing mode $(this).data('editing', true); // Make sure contenteditable is true $(this).attr('contenteditable', 'true'); // Disable dragging while editing $(this).draggable('disable'); }); // When we're done editing (blur), re-enable dragging $(this).off('blur').on('blur', function() { $(this).data('editing', false); // Small delay before enabling dragging again setTimeout(() => { $(this).draggable('enable'); }, 100); }); }); } // Function to add a new text element to the canvas function addNewTextElement() { // Create a new text element const newTextElement = $('<div class="editable-element" contenteditable="true"></div>') .html('Click to edit text') .css({ 'position': 'absolute', 'left': '50px', 'top': '50px', 'min-width': '100px', 'min-height': '30px', 'padding': '5px', 'cursor': 'move', 'z-index': '100', 'background-color': 'rgba(255, 255, 255, 0.7)', // Semi-transparent background 'border': '1px dashed #ccc', 'font-family': 'Arial, sans-serif', 'font-size': '16px', 'color': '#333' }); // Append the new element to the canvas $('#canvasArea').append(newTextElement); // Make the element draggable newTextElement.draggable({ containment: '#canvasArea', cursor: 'move', stack: '.editable-element', start: function() { // Add a highlighted border when dragging starts $(this).css('border', '1px dashed #007bff'); }, stop: function() { // Restore the border when dragging ends $(this).css('border', '1px dashed #ccc'); } }); // Focus on the new element for immediate editing newTextElement.focus(); // Initially set to editing mode newTextElement.data('editing', true); newTextElement.draggable('disable'); // Apply makeElementsEditable functions to the new element // makeElementsEditable(); } // Create a floating toolbar for text controls function createTextToolbar() { // Remove any existing toolbar $('#textToolbar').remove(); // Create the toolbar const toolbar = $('<div id="textToolbar" class="text-toolbar"></div>') .css({ 'position': 'fixed', 'top': '430px', 'left': '20px', // 'background-color': '#f8f9fa', 'border': '1px solid #ddd', 'border-radius': '4px', 'padding': '10px', // 'box-shadow': '0 2px 5px rgba(0,0,0,0.1)', 'z-index': '1000', 'display': 'flex', 'flex-direction': 'column', 'gap': '10px' }); // Add Text button const addTextBtn = $('<button id="addTextBtn" class="btn btn-sm btn-primary"></button>') .html('<i class="fas fa-font"></i> Add Text') .css({ 'padding': '8px 12px', 'cursor': 'pointer', 'width': '100%' }); // Font controls const fontControls = $('<div class="font-controls"></div>') .css({ 'display': 'flex', 'flex-direction': 'column', 'gap': '5px' }); // Font family dropdown const fontFamilySelect = $('<select id="fontFamily" class="form-control form-control-sm"></select>') .append('<option value="Arial, sans-serif">Arial</option>') .append('<option value="Times New Roman, serif">Times New Roman</option>') .append('<option value="Helvetica, sans-serif">Helvetica</option>') .append('<option value="Georgia, serif">Georgia</option>') .append('<option value="Courier New, monospace">Courier New</option>') .append('<option value="Verdana, sans-serif">Verdana</option>') .css('width', '100%'); // Font size dropdown const fontSizeSelect = $('<select id="fontSize" class="form-control form-control-sm"></select>') .append('<option value="12px">12px</option>') .append('<option value="14px">14px</option>') .append('<option value="16px" selected>16px</option>') .append('<option value="18px">18px</option>') .append('<option value="20px">20px</option>') .append('<option value="24px">24px</option>') .append('<option value="28px">28px</option>') .append('<option value="32px">32px</option>') .append('<option value="36px">36px</option>') .append('<option value="48px">48px</option>') .css('width', '100%'); // Font color picker const fontColorPicker = $('<input type="color" id="fontColor" class="form-control form-control-sm" value="#333333">') .css('width', '100%'); // Text alignment buttons const alignmentBtns = $('<div class="text-alignment"></div>') .css({ 'display': 'flex', 'justify-content': 'space-between', 'gap': '5px' }); const alignLeftBtn = $('<button id="alignLeft" class="btn btn-sm btn-outline-secondary"></button>') .html('<i class="fas fa-align-left"></i>') .css('flex', '1'); const alignCenterBtn = $('<button id="alignCenter" class="btn btn-sm btn-outline-secondary"></button>') .html('<i class="fas fa-align-center"></i>') .css('flex', '1'); const alignRightBtn = $('<button id="alignRight" class="btn btn-sm btn-outline-secondary"></button>') .html('<i class="fas fa-align-right"></i>') .css('flex', '1'); // Style buttons const styleBtns = $('<div class="text-style"></div>') .css({ 'display': 'flex', 'justify-content': 'space-between', 'gap': '5px' }); const boldBtn = $('<button id="boldText" class="btn btn-sm btn-outline-secondary"></button>') .html('<i class="fas fa-bold"></i>') .css('flex', '1'); const italicBtn = $('<button id="italicText" class="btn btn-sm btn-outline-secondary"></button>') .html('<i class="fas fa-italic"></i>') .css('flex', '1'); const underlineBtn = $('<button id="underlineText" class="btn btn-sm btn-outline-secondary"></button>') .html('<i class="fas fa-underline"></i>') .css('flex', '1'); // Append all elements to the toolbar alignmentBtns.append(alignLeftBtn).append(alignCenterBtn).append(alignRightBtn); styleBtns.append(boldBtn).append(italicBtn).append(underlineBtn); fontControls.append('<label for="fontFamily">Font</label>') .append(fontFamilySelect) .append('<label for="fontSize">Size</label>') .append(fontSizeSelect) .append('<label for="fontColor">Color</label>') .append(fontColorPicker) .append('<label>Alignment</label>') .append(alignmentBtns) .append('<label>Style</label>') .append(styleBtns); toolbar.append(addTextBtn).append(fontControls); // Add the toolbar to the body $('body').append(toolbar); // Add event listeners addTextBtn.on('click', addNewTextElement); // Store the currently selected text element let selectedElement = null; // Event listener for selecting text elements $(document).on('click', '.editable-element', function() { selectedElement = $(this); // Update toolbar controls to match the selected element fontFamilySelect.val(selectedElement.css('font-family')); fontSizeSelect.val(selectedElement.css('font-size')); fontColorPicker.val(rgb2hex(selectedElement.css('color'))); // Update alignment buttons alignLeftBtn.removeClass('active'); alignCenterBtn.removeClass('active'); alignRightBtn.removeClass('active'); switch (selectedElement.css('text-align')) { case 'left': alignLeftBtn.addClass('active'); break; case 'center': alignCenterBtn.addClass('active'); break; case 'right': alignRightBtn.addClass('active'); break; } // Update style buttons boldBtn.toggleClass('active', selectedElement.css('font-weight') === 'bold' || parseInt(selectedElement.css('font-weight')) >= 700); italicBtn.toggleClass('active', selectedElement.css('font-style') === 'italic'); underlineBtn.toggleClass('active', selectedElement.css('text-decoration').includes('underline')); }); // Event listeners for controls fontFamilySelect.on('change', function() { if (selectedElement) { selectedElement.css('font-family', $(this).val()); } }); fontSizeSelect.on('change', function() { if (selectedElement) { selectedElement.css('font-size', $(this).val()); } }); fontColorPicker.on('input', function() { if (selectedElement) { selectedElement.css('color', $(this).val()); } }); alignLeftBtn.on('click', function() { if (selectedElement) { selectedElement.css('text-align', 'left'); alignLeftBtn.addClass('active'); alignCenterBtn.removeClass('active'); alignRightBtn.removeClass('active'); } }); alignCenterBtn.on('click', function() { if (selectedElement) { selectedElement.css('text-align', 'center'); alignLeftBtn.removeClass('active'); alignCenterBtn.addClass('active'); alignRightBtn.removeClass('active'); } }); alignRightBtn.on('click', function() { if (selectedElement) { selectedElement.css('text-align', 'right'); alignLeftBtn.removeClass('active'); alignCenterBtn.removeClass('active'); alignRightBtn.addClass('active'); } }); boldBtn.on('click', function() { if (selectedElement) { const isBold = selectedElement.css('font-weight') === 'bold' || parseInt(selectedElement.css('font-weight')) >= 700; selectedElement.css('font-weight', isBold ? 'normal' : 'bold'); boldBtn.toggleClass('active'); } }); italicBtn.on('click', function() { if (selectedElement) { const isItalic = selectedElement.css('font-style') === 'italic'; selectedElement.css('font-style', isItalic ? 'normal' : 'italic'); italicBtn.toggleClass('active'); } }); underlineBtn.on('click', function() { if (selectedElement) { const isUnderlined = selectedElement.css('text-decoration').includes('underline'); selectedElement.css('text-decoration', isUnderlined ? 'none' : 'underline'); underlineBtn.toggleClass('active'); } }); } // Function to convert RGB to HEX function rgb2hex(rgb) { if (!rgb) return '#000000'; if (rgb.search("rgb") == -1) return rgb; rgb = rgb.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))?\)$/); function hex(x) { return ("0" + parseInt(x).toString(16)).slice(-2); } return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]); } // Initialize the text editing toolbar on document ready $(document).ready(function() { // Create the text toolbar createTextToolbar(); // Initialize any existing elements on the page makeElementsEditable(); // Add delete functionality for text elements $(document).on('keydown', '.editable-element', function(e) { // Delete element if it's empty and backspace or delete is pressed if ((e.key === 'Backspace' || e.key === 'Delete') && $(this).text().trim() === '') { $(this).remove(); e.preventDefault(); } }); // Update the saveTemplate function to include the new text elements const originalSaveTemplate = window.saveTemplate; window.saveTemplate = function() { // Call the original saveTemplate function originalSaveTemplate(); }; }); </script> </div> </body> </html>