save data via google accounts

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>YouTube Notes App</title>
    <!-- Font Awesome for Icons -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        /* Reset some default styles */
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            margin: 20px;
            background-color: #fafafa;
            color: #333;
        }

        h1, h2 {
            color: #2c3e50;
            margin-bottom: 15px;
        }

        .form-section, .search-section {
            background-color: #fff;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            margin-bottom: 30px;
        }

        .search-section {
            display: flex;
            align-items: center;
            justify-content: space-between;
        }

        input[type="text"], textarea {
            width: 100%;
            padding: 12px 15px;
            margin-top: 8px;
            margin-bottom: 20px;
            border: 1px solid #ccc;
            border-radius: 6px;
            font-size: 16px;
            transition: border-color 0.3s;
        }

        input[type="text"]:focus, textarea:focus {
            border-color: #3498db;
            outline: none;
        }

        label {
            font-weight: 600;
            font-size: 16px;
        }

        button {
            background-color: #3498db;
            color: white;
            padding: 10px 22px;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-size: 16px;
            transition: background-color 0.3s, transform 0.2s;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        button:hover {
            background-color: #2980b9;
            transform: translateY(-2px);
        }

        button:disabled {
            background-color: #95a5a6;
            cursor: not-allowed;
        }

        #videoList {
            list-style-type: none;
            padding: 0;
            display: flex;
            flex-wrap: wrap;
            gap: 20px;
        }

        #videoList li {
            background-color: #fff;
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
            cursor: pointer;
            display: flex;
            flex-direction: column;
            align-items: center;
            width: calc(25% - 20px);
            transition: transform 0.2s, box-shadow 0.2s;
            position: relative;
        }

        #videoList li:hover {
            transform: translateY(-5px);
            box-shadow: 0 4px 8px rgba(0,0,0,0.15);
        }

        .video-thumbnail {
            width: 100%;
            height: auto;
            border-radius: 4px;
            object-fit: cover;
            margin-bottom: 10px;
        }

        .video-title {
            font-size: 14px;
            color: #2c3e50;
            text-align: center;
            flex-grow: 1;
            margin-bottom: 10px;
        }

        .video-actions {
            display: flex;
            gap: 10px;
            width: 100%;
            justify-content: center;
        }

        .video-actions button {
            background-color: #e74c3c;
            padding: 6px 10px;
            font-size: 14px;
            border-radius: 4px;
        }

        .video-actions button.view-btn {
            background-color: #2ecc71;
        }

        .video-actions button:hover {
            opacity: 0.9;
        }

        #videoDisplay {
            display: flex;
            flex-wrap: wrap;
            gap: 30px;
            margin-top: 20px;
        }

        #videoContainer, #notesContainer {
            flex: 1 1 45%;
            background-color: #fff;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }

        iframe {
            width: 100%;
            height: 300px;
            border: none;
            border-radius: 6px;
        }

        .notes-section {
            margin-top: 20px;
        }

        .note-item {
            background-color: #ecf0f1;
            padding: 12px 15px;
            margin-bottom: 15px;
            border-left: 4px solid #3498db;
            border-radius: 4px;
            position: relative;
            transition: background-color 0.3s;
        }

        .note-item:hover {
            background-color: #d0ece7;
        }

        .note-content {
            white-space: pre-wrap;
            font-size: 15px;
            margin-bottom: 10px;
        }

        .note-actions {
            position: absolute;
            top: 10px;
            right: 10px;
            display: flex;
            gap: 8px;
        }

        .note-actions button {
            background-color: transparent;
            border: none;
            color: #2980b9;
            cursor: pointer;
            font-size: 16px;
            transition: color 0.3s;
        }

        .note-actions button:hover {
            color: #1abc9c;
        }

        .add-note-inline {
            margin-top: 20px;
        }

        .add-note-inline textarea {
            resize: vertical;
                height: 80px;
        }

        /* Modal Styles */
        .modal {
            display: none; /* Hidden by default */
            position: fixed; /* Stay in place */
            z-index: 1000; /* Sit on top */
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            overflow: auto; /* Enable scroll if needed */
            background-color: rgba(0,0,0,0.5); /* Black w/ opacity */
            align-items: center;
            justify-content: center;
        }

        .modal-content {
            background-color: #fff;
            padding: 20px 30px;
            border-radius: 8px;
            max-width: 500px;
            width: 90%;
            position: relative;
        }

        .modal-header {
            margin-bottom: 15px;
        }

        .modal-header h2 {
            margin: 0;
            font-size: 20px;
            color: #2c3e50;
        }

        .modal-body {
            margin-bottom: 20px;
        }

        .modal-footer {
            display: flex;
            justify-content: flex-end;
            gap: 10px;
        }

        .close-btn {
            position: absolute;
            top: 15px;
            right: 20px;
            background-color: transparent;
            border: none;
            font-size: 20px;
            cursor: pointer;
            color: #7f8c8d;
        }

        /* Loader Styles */
        .loader {
            border: 4px solid #f3f3f3;
            border-top: 4px solid #3498db;
            border-radius: 50%;
            width: 24px;
            height: 24px;
            animation: spin 1s linear infinite;
            display: inline-block;
            vertical-align: middle;
            margin-left: 10px;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        /* Search Bar */
        .search-bar {
            display: flex;
            align-items: center;
            gap: 10px;
            width: 100%;
        }

        .search-bar input[type="text"] {
            flex-grow: 1;
            padding: 10px 12px;
            font-size: 16px;
        }

        /* Responsive Design */
        @media (max-width: 1200px) {
            #videoList li {
                width: calc(33.333% - 20px);
            }
        }

        @media (max-width: 900px) {
            #videoList li {
                width: calc(50% - 20px);
            }
            #videoContainer, #notesContainer {
                flex: 1 1 100%;
            }
        }

        @media (max-width: 600px) {
            .search-section {
                flex-direction: column;
                align-items: stretch;
                gap: 15px;
            }

            #videoList li {
                width: 100%;
            }
        }

    </style>
</head>
<body>

    <h1>YouTube Notes App</h1>

    <!-- Section to Add Video URL -->
    <div class="form-section" aria-labelledby="add-video-heading">
        <h2 id="add-video-heading">Add YouTube Video</h2>
        <form id="addVideoForm">
            <label for="videoURL">YouTube Video URL:</label>
            <input type="text" id="videoURL" name="videoURL" placeholder="Enter YouTube URL" required aria-required="true">
            <button type="submit"><i class="fa fa-plus"></i> Add Video</button>
            <span id="addVideoLoader" class="loader" style="display:none;"></span>
        </form>
    </div>

    <!-- Search Section -->
    <div class="search-section" aria-labelledby="search-heading">
        <h2 id="search-heading">Saved Videos</h2>
        <div class="search-bar">
            <input type="text" id="searchInput" placeholder="Search videos..." aria-label="Search videos">
            <button id="clearSearchBtn" title="Clear Search"><i class="fa fa-times"></i></button>
        </div>
    </div>

    <ul id="videoList" aria-live="polite">
        <!-- Video items will be populated here -->
    </ul>

    <div id="videoDisplay">
        <!-- Embedded video and notes will appear here -->
        <div id="videoContainer" style="display:none;" aria-labelledby="video-heading">
            <h2 id="video-heading">Video</h2>
            <div id="playerWrapper">
                <div id="videoPlayer"></div>
            </div>
            <button id="deselectButton" style="margin-top: 15px; display:none;"><i class="fa fa-times-circle"></i> Deselect Video</button>
        </div>
        <div id="notesContainer" style="display:none;" aria-labelledby="notes-heading">
            <h2 id="notes-heading">Notes</h2>
            <div class="notes-section" id="notesSection">
                <!-- Notes will be populated here -->
            </div>
            <!-- Inline Note Input -->
            <div class="add-note-inline">
                <label for="inlineNoteContent">Add a new note:</label>
                <textarea id="inlineNoteContent" name="inlineNoteContent" rows="3" placeholder="Type your note and press Enter..." required aria-required="true"></textarea>
            </div>
        </div>
    </div>

    <!-- Modal for Confirmations and Alerts -->
    <div id="modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="modal-heading">
        <div class="modal-content">
            <button class="close-btn" aria-label="Close Modal">&times;</button>
            <div class="modal-header">
                <h2 id="modal-heading">Modal Title</h2>
            </div>
            <div class="modal-body" id="modal-body">
                <!-- Modal content goes here -->
            </div>
            <div class="modal-footer" id="modal-footer">
                <!-- Modal buttons go here -->
            </div>
        </div>
    </div>

    <script>
        // Function to extract YouTube Video ID from URL
        function getYouTubeID(url) {
            const regex = /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/(?:watch\?(?:.*&)?v=|embed\/)|youtu\.be\/)([^\s&?]+)/;
            const match = url.match(regex);
            return match ? match[1] : null;
        }

        // Retrieve videos from localStorage or initialize empty array
        function getVideos() {
            const videos = localStorage.getItem('videos');
            return videos ? JSON.parse(videos) : [];
        }

        // Save videos array to localStorage
        function saveVideos(videos) {
            localStorage.setItem('videos', JSON.stringify(videos));
        }

        // Function to show modal
        function showModal(title, message, buttons = []) {
            const modal = document.getElementById('modal');
            const modalTitle = document.querySelector('.modal-header h2');
            const modalBody = document.getElementById('modal-body');
            const modalFooter = document.getElementById('modal-footer');

            modalTitle.textContent = title;
            modalBody.innerHTML = message;
            modalFooter.innerHTML = '';

            buttons.forEach(btn => {
                const button = document.createElement('button');
                button.textContent = btn.text;
                button.className = btn.class || '';
                if (btn.icon) {
                    const icon = document.createElement('i');
                    icon.className = btn.icon;
                    button.prepend(icon);
                    button.style.marginRight = '5px';
                }
                button.addEventListener('click', btn.handler);
                modalFooter.appendChild(button);
            });

            modal.style.display = 'flex';
            // Accessibility: Set focus to the first button in the modal
            if (modalFooter.firstChild) {
                modalFooter.firstChild.focus();
            }
        }

        // Function to close modal
        function closeModal() {
            const modal = document.getElementById('modal');
            modal.style.display = 'none';
        }

        // Attach event listener to close modal when clicking outside the content
        window.addEventListener('click', function(event) {
            const modal = document.getElementById('modal');
            if (event.target === modal) {
                closeModal();
            }
        });

        // Attach event listener to close button
        document.querySelector('.close-btn').addEventListener('click', closeModal);

        // Show loading indicator
        function showLoading(show) {
            const loader = document.getElementById('addVideoLoader');
            if (show) {
                loader.style.display = 'inline-block';
            } else {
                loader.style.display = 'none';
            }
        }

        // Add a new video
        async function addVideo(url) {
            const videoID = getYouTubeID(url);
            if (!videoID) {
                showModal('Invalid URL', '<p>Please enter a valid YouTube URL.</p>', [
                    { text: 'OK', handler: closeModal }
                ]);
                return;
            }
            const videos = getVideos();
            // Check for duplicates
            let video = videos.find(video => video.id === videoID);
            if (video) {
                showModal('Duplicate Video', '<p>This video is already added.</p>', [
                    { text: 'OK', handler: closeModal }
                ]);
                return;
            }

            showLoading(true);
            try {
                // Fetch video details using oEmbed
                const response = await fetch(`https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=${videoID}&format=json`);
                if (!response.ok) {
                    throw new Error('Failed to fetch video details. Please ensure the URL is correct.');
                }
                const data = await response.json();
                const title = data.title;
                const thumbnail_url = data.thumbnail_url;

                video = { id: videoID, url: url, title: title, thumbnail_url: thumbnail_url, notes: [] };
                videos.push(video);
                saveVideos(videos);
                renderVideoList();
                document.getElementById('addVideoForm').reset();
                showModal('Success', '<p>Video added successfully!</p>', [
                    { text: 'OK', handler: closeModal }
                ]);
            } catch (error) {
                console.error(error);
                showModal('Error', `<p>Unable to add video. Please check the URL and try again.</p>`, [
                    { text: 'OK', handler: closeModal }
                ]);
            } finally {
                showLoading(false);
            }
        }

        // Add a new note to a video
        function addNote(videoID, content) {
            const videos = getVideos();
            const video = videos.find(v => v.id === videoID);
            if (!video) return;
            if (content.trim() !== "") {
                video.notes.push({ content: content.trim() });
                saveVideos(videos);
                renderVideoList();
                if (currentDisplayedVideo === videoID) {
                    displayVideo(videoID);
                }
                document.getElementById('inlineNoteContent').value = '';
                showModal('Success', '<p>Note added successfully!</p>', [
                    { text: 'OK', handler: closeModal }
                ]);
            }
        }

        // Render the list of saved videos
        function renderVideoList(filter = '') {
            const videos = getVideos();
            const videoList = document.getElementById('videoList');
            videoList.innerHTML = '';
            const filteredVideos = videos.filter(video => video.title.toLowerCase().includes(filter.toLowerCase()));
            if (filteredVideos.length === 0) {
                const noVideosItem = document.createElement('li');
                noVideosItem.textContent = 'No videos found.';
                noVideosItem.style.textAlign = 'center';
                noVideosItem.style.width = '100%';
                noVideosItem.style.color = '#7f8c8d';
                videoList.appendChild(noVideosItem);
                return;
            }
            filteredVideos.forEach(video => {
                const li = document.createElement('li');

                const thumbnail = document.createElement('img');
                thumbnail.src = video.thumbnail_url;
                thumbnail.alt = video.title;
                thumbnail.className = 'video-thumbnail';

                const title = document.createElement('span');
                title.textContent = video.title;
                title.className = 'video-title';

                const actionsDiv = document.createElement('div');
                actionsDiv.className = 'video-actions';

                const viewButton = document.createElement('button');
                viewButton.innerHTML = '<i class="fa fa-eye"></i> View';
                viewButton.setAttribute('data-id', video.id);
                viewButton.className = 'view-btn';
                viewButton.title = 'View Video and Notes';

                const deleteButton = document.createElement('button');
                deleteButton.innerHTML = '<i class="fa fa-trash"></i> Delete';
                deleteButton.setAttribute('data-id', video.id);
                deleteButton.className = 'delete-btn';
                deleteButton.title = 'Delete Video';

                actionsDiv.appendChild(viewButton);
                actionsDiv.appendChild(deleteButton);

                li.appendChild(thumbnail);
                li.appendChild(title);
                li.appendChild(actionsDiv);

                videoList.appendChild(li);
            });

            // Attach event listeners to the newly added buttons
            const viewButtons = document.querySelectorAll('.view-btn');
            viewButtons.forEach(button => {
                button.addEventListener('click', () => displayVideo(button.getAttribute('data-id')));
            });

            const deleteButtons = document.querySelectorAll('.delete-btn');
            deleteButtons.forEach(button => {
                button.addEventListener('click', (e) => {
                    e.stopPropagation();
                    confirmDeleteVideo(button.getAttribute('data-id'));
                });
            });
        }

        // Confirm before deleting a video
        function confirmDeleteVideo(id) {
            showModal('Confirm Delete', '<p>Are you sure you want to delete this video and all its notes?</p>', [
                { text: 'Cancel', handler: closeModal },
                { text: 'Delete', class: 'delete-confirm-btn', handler: () => { deleteVideo(id); closeModal(); } }
            ]);
        }

        // Delete a video
        function deleteVideo(id) {
            let videos = getVideos();
            videos = videos.filter(video => video.id !== id);
            saveVideos(videos);
            renderVideoList(document.getElementById('searchInput').value);
            // Clear video display if deleted video was displayed
            if (currentDisplayedVideo === id) {
                if (player && typeof player.stopVideo === 'function') {
                    player.stopVideo();
                }
                document.getElementById('videoContainer').style.display = 'none';
                document.getElementById('notesContainer').style.display = 'none';
                document.getElementById('deselectButton').style.display = 'none';
                currentDisplayedVideo = null;
            }
            showModal('Deleted', '<p>Video and its notes have been deleted.</p>', [
                { text: 'OK', handler: closeModal }
            ]);
        }

        // Edit a note
        function editNote(videoId, noteIndex) {
            const videos = getVideos();
            const video = videos.find(v => v.id === videoId);
            if (!video) return;
            const note = video.notes[noteIndex];
            const modalContent = `
                <label for="editNoteContent">Edit your note:</label>
                <textarea id="editNoteContent" rows="4" required>${sanitizeHTML(note.content)}</textarea>
            `;
            showModal('Edit Note', modalContent, [
                { text: 'Cancel', handler: closeModal },
                { text: 'Save', handler: () => {
                    const newContent = document.getElementById('editNoteContent').value.trim();
                    if (newContent !== "") {
                        video.notes[noteIndex].content = newContent;
                        saveVideos(videos);
                        renderVideoList(document.getElementById('searchInput').value);
                        if (currentDisplayedVideo === videoId) {
                            displayVideo(videoId);
                        }
                        closeModal();
                        showModal('Success', '<p>Note updated successfully!</p>', [
                            { text: 'OK', handler: closeModal }
                        ]);
                    } else {
                        // Highlight the textarea if empty
                        document.getElementById('editNoteContent').style.borderColor = '#e74c3c';
                    }
                } }
            ]);
        }

        // Confirm before deleting a note
        function confirmDeleteNote(videoId, noteIndex) {
            showModal('Confirm Delete', '<p>Are you sure you want to delete this note?</p>', [
                { text: 'Cancel', handler: closeModal },
                { text: 'Delete', class: 'delete-confirm-btn', handler: () => { deleteNote(videoId, noteIndex); closeModal(); } }
            ]);
        }

        // Delete a note
        function deleteNote(videoId, noteIndex) {
            const videos = getVideos();
            const video = videos.find(v => v.id === videoId);
            if (!video) return;
            video.notes.splice(noteIndex, 1);
            saveVideos(videos);
            renderVideoList(document.getElementById('searchInput').value);
            if (currentDisplayedVideo === videoId) {
                displayVideo(videoId);
            }
            showModal('Deleted', '<p>Note has been deleted.</p>', [
                { text: 'OK', handler: closeModal }
            ]);
        }

        // Simple sanitization to prevent XSS
        function sanitizeHTML(str) {
            const temp = document.createElement('div');
            temp.textContent = str;
            return temp.innerHTML;
        }

        // Handle Add Video form submission
        document.getElementById('addVideoForm').addEventListener('submit', async function(e) {
            e.preventDefault();
            const url = document.getElementById('videoURL').value.trim();
            await addVideo(url);
        });

        // Handle Inline Note addition on Enter key
        document.getElementById('inlineNoteContent').addEventListener('keydown', function(e) {
            if (e.key === 'Enter' && !e.shiftKey) { // Press Enter without Shift
                e.preventDefault();
                const content = e.target.value.trim();
                if (content !== "" && currentDisplayedVideo) {
                    addNote(currentDisplayedVideo, content);
                }
            }
        });

        // Initial render
        renderVideoList();

        // YouTube IFrame API for controlling video playback
        function loadYouTubeAPI() {
            const tag = document.createElement('script');
            tag.src = "https://www.youtube.com/iframe_api";
            const firstScriptTag = document.getElementsByTagName('script')[0];
            firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
        }

        loadYouTubeAPI();

        let player;
        let playerReady = false;

        // Attach onYouTubeIframeAPIReady to the window object
        window.onYouTubeIframeAPIReady = function() {
            player = new YT.Player('videoPlayer', {
                height: '300',
                width: '100%',
                videoId: '', // Start with no video
                events: {
                    'onReady': onPlayerReady
                }
            });
        };

        function onPlayerReady(event) {
            playerReady = true;
            // Dispatch a custom event to notify that the player is ready
            window.dispatchEvent(new Event('playerReady'));
        }

        // Display the selected video and its notes
        let currentDisplayedVideo = null;

        function displayVideo(id) {
            const videos = getVideos();
            const video = videos.find(v => v.id === id);
            if (!video) return;

            currentDisplayedVideo = id;

            const videoContainer = document.getElementById('videoContainer');
            const notesContainer = document.getElementById('notesContainer');
            const notesSection = document.getElementById('notesSection');
            const deselectButton = document.getElementById('deselectButton');

            // Load the video using the YouTube IFrame API
            if (playerReady) {
                player.loadVideoById(video.id);
            } else {
                // If player isn't ready yet, wait until it is
                window.addEventListener('playerReady', () => {
                    player.loadVideoById(video.id);
                }, { once: true });
            }

            // Populate notes
            notesSection.innerHTML = '';

            if (video.notes.length === 0) {
                notesSection.innerHTML = '<p>No notes added yet.</p>';
            } else {
                video.notes.forEach((note, index) => {
                    const noteDiv = document.createElement('div');
                    noteDiv.className = 'note-item';

                    noteDiv.innerHTML = `
                        <div class="note-content">${sanitizeHTML(note.content)}</div>
                        <div class="note-actions">
                            <button aria-label="Edit Note"><i class="fa fa-edit"></i></button>
                            <button aria-label="Delete Note"><i class="fa fa-trash"></i></button>
                        </div>
                    `;
                    // Attach event listeners to buttons
                    const [editBtn, deleteBtn] = noteDiv.querySelectorAll('button');
                    editBtn.addEventListener('click', () => editNote(id, index));
                    deleteBtn.addEventListener('click', () => confirmDeleteNote(id, index));

                    notesSection.appendChild(noteDiv);
                });
            }

            videoContainer.style.display = 'block';
            notesContainer.style.display = 'block';
            deselectButton.style.display = 'inline-block'; // Show the Deselect button

            // Scroll to video display
            videoContainer.scrollIntoView({ behavior: 'smooth' });
        }

        // Deselect the currently selected video
        function deselectVideo() {
            if (player && typeof player.stopVideo === 'function') {
                player.stopVideo();
            }
            document.getElementById('videoContainer').style.display = 'none';
            document.getElementById('notesContainer').style.display = 'none';
            document.getElementById('deselectButton').style.display = 'none';
            currentDisplayedVideo = null;
            // Optionally, scroll back to top or to the video list
            window.scrollTo({ top: 0, behavior: 'smooth' });
        }

        // Handle Deselect button click
        document.getElementById('deselectButton').addEventListener('click', deselectVideo);

        // Search Functionality
        document.getElementById('searchInput').addEventListener('input', function(e) {
            const filter = e.target.value.trim();
            renderVideoList(filter);
        });

        // Clear Search Button
        document.getElementById('clearSearchBtn').addEventListener('click', function() {
            document.getElementById('searchInput').value = '';
            renderVideoList();
            document.getElementById('searchInput').focus();
        });

        // Attach functions to window to make them accessible from inline onclick handlers (if any)
        window.editNote = editNote;
        window.deleteNote = deleteNote;
        window.confirmDeleteVideo = confirmDeleteVideo;
        window.confirmDeleteNote = confirmDeleteNote;
        window.deselectVideo = deselectVideo;

    </script>

</body>
</html>