LinkedIn Job Visibility Settings; year, job status highlight

11/13/2024, 5:53:40 PM

// ==UserScript==
// @name        LinkedIn Job Visibility Settings; year, job status highlight
// @namespace   Violentmonkey Scripts
// @match       https://www.linkedin.com/jobs/search/*
// @match       https://www.glassdoor.ca/Job/*
// @match       https://ca.indeed.com/*
// @grant       GM_getValue
// @grant       GM_setValue
// @version     1.0
// @author      LAMSTREAM
// @description 11/13/2024, 5:53:40 PM
// ==/UserScript==

(function() {
    'use strict';

    // Define colors for each status
    const statusColors = { viewed: 'red',
                          applied: 'green',
                          promoted: 'purple',
                          year: 'red' };

    // Initialize settings with stored values or defaults
    const settings = {
        hideViewed: GM_getValue('hideViewed', true),
        hideApplied: GM_getValue('hideApplied', true),
        hidePromoted: GM_getValue('hidePromoted', true),
        panelPosition: GM_getValue('panelPosition', { x: 20, y: 20 }),
        isCollapsed: GM_getValue('isCollapsed', false)
    };

    // Create and style the settings panel
    function createSettingsPanel() {
        const panel = document.createElement('div');
        panel.style.cssText = `
            position: fixed;
            top: ${settings.panelPosition.y}px;
            left: ${settings.panelPosition.x}px;
            background: white;
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            z-index: 9999;
            font-family: system-ui, -apple-system, sans-serif;
            cursor: move;
            min-width: 200px;
            user-select: none;
        `;

        // Header container
        const header = document.createElement('div');
        header.style.cssText = `
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
            cursor: move;
        `;

        const title = document.createElement('h3');
        title.textContent = 'Job Visibility Settings';
        title.style.margin = '0';
        title.style.flex = '1';

        // Collapse/Expand button
        const collapseBtn = document.createElement('button');
        collapseBtn.style.cssText = `
            background: none;
            border: none;
            cursor: pointer;
            font-size: 20px;
            padding: 0 5px;
            margin-left: 10px;
        `;
        collapseBtn.textContent = settings.isCollapsed ? '▼' : '▲';
        collapseBtn.title = settings.isCollapsed ? 'Expand' : 'Collapse';

        header.appendChild(title);
        header.appendChild(collapseBtn);
        panel.appendChild(header);

        // Content container
        const content = document.createElement('div');
        content.style.display = settings.isCollapsed ? 'none' : 'block';

        // Create toggle switches for each setting
        const options = [
            { key: 'hideViewed', label: 'Hide Viewed', color: statusColors.viewed },
            { key: 'hideApplied', label: 'Hide Applied', color: statusColors.applied },
            { key: 'hidePromoted', label: 'Hide Promoted', color: statusColors.promoted }
        ];

        options.forEach(({ key, label, color }) => {
            const container = document.createElement('div');
            container.style.cssText = `
                margin: 10px 0;
                padding: 8px;
                border-radius: 4px;
                background: ${color}15;
                border: 1px solid ${color}40;
            `;

            const toggle = document.createElement('input');
            toggle.type = 'checkbox';
            toggle.id = key;
            toggle.checked = settings[key];
            toggle.style.marginRight = '8px';

            const labelElement = document.createElement('label');
            labelElement.htmlFor = key;
            labelElement.textContent = label;
            labelElement.style.color = color;
            labelElement.style.fontWeight = 'bold';

            toggle.addEventListener('change', (e) => {
                settings[key] = e.target.checked;
                GM_setValue(key, e.target.checked);
                hideListItems();
            });

            container.appendChild(toggle);
            container.appendChild(labelElement);
            content.appendChild(container);
        });

        panel.appendChild(content);

        // Make panel draggable
        let isDragging = false;
        let currentX;
        let currentY;
        let initialX;
        let initialY;

        header.addEventListener('mousedown', dragStart);
        document.addEventListener('mousemove', drag);
        document.addEventListener('mouseup', dragEnd);

        function dragStart(e) {
            initialX = e.clientX - settings.panelPosition.x;
            initialY = e.clientY - settings.panelPosition.y;
            isDragging = true;
        }

        function drag(e) {
            if (!isDragging) return;

            e.preventDefault();
            currentX = e.clientX - initialX;
            currentY = e.clientY - initialY;

            // Update position
            settings.panelPosition = { x: currentX, y: currentY };
            panel.style.left = `${currentX}px`;
            panel.style.top = `${currentY}px`;
        }

        function dragEnd() {
            if (!isDragging) return;

            isDragging = false;
            GM_setValue('panelPosition', settings.panelPosition);
        }

        // Handle collapse/expand
        collapseBtn.addEventListener('click', () => {
            settings.isCollapsed = !settings.isCollapsed;
            content.style.display = settings.isCollapsed ? 'none' : 'block';
            collapseBtn.textContent = settings.isCollapsed ? '▼' : '▲';
            collapseBtn.title = settings.isCollapsed ? 'Expand' : 'Collapse';
            GM_setValue('isCollapsed', settings.isCollapsed);
        });

        document.body.appendChild(panel);
    }

    // Function to hide li elements and color the status text
    const hideListItems = () => {
        const elements = document.querySelectorAll('.job-card-container__footer-item');
        elements.forEach(element => {
            const text = element.innerText;

            // Color the status text based on type
            if (text.includes('Viewed')) {
                element.style.color = statusColors.viewed;
                if (settings.hideViewed) hideFromAncestor(element);
            }
            else if (text.includes('Applied')) {
                element.style.color = statusColors.applied;
                if (settings.hideApplied) hideFromAncestor(element);
            }
            else if (text.includes('Promoted')) {
                element.style.color = statusColors.promoted;
                if (settings.hidePromoted) hideFromAncestor(element);
            }
        });
    };

    // Function to hide the ancestor li element
    const hideFromAncestor = (element) => {
        let liElement = element.parentElement.closest('li');
        if (liElement) {
            liElement.style.display = 'none';
        }
    };

  // Function to highlight the word "year" in text content
const highlightYear = () => {
    const elements = [...document.querySelectorAll('.jobs-search__job-details--wrapper'), ...document.querySelectorAll('[class^="JobDetails_jobDescription"]'), ...document.querySelectorAll('#jobDescriptionText') ]
    observer.disconnect()
    elements.forEach(element => {
        // Get all text nodes within the element, including nested ones
        const walk = document.createTreeWalker(
            element,
            NodeFilter.SHOW_TEXT,
            null,
            false
        );

        const textNodes = [];
        let node;
        while (node = walk.nextNode()) {
            textNodes.push(node);
        }

        // Process each text node
        textNodes.forEach(textNode => {
            const text = textNode.textContent;
            if (/(year|years)/i.test(text)) {
                const span = document.createElement('span');
                span.innerHTML = text.replace(/(years|year)/gi, '<span style="color: red">$1</span>');
                textNode.parentNode.replaceChild(span, textNode);
            }
        });
    });

    // Observe changes in the DOM
    observer.observe(document.body, {
        childList: true,
        subtree: true,
        characterData: true
    });
};

    const highlightCurrentTab = () => {
    observer.disconnect(); // Temporarily stop observing mutations

    const items = document.querySelectorAll('[class*="JobsList_jobListItem"]'); // Get all job list items

    items.forEach(item => {
        const classList = item.className; // Get full class name as string
        if (classList.includes("JobsList_selected")) {
            item.classList.add("highlight"); // Add highlight if selected
        } else {
            item.classList.remove("highlight"); // Remove highlight if not selected
        }
    });

    observer.observe(document.body, { childList: true, subtree: true }); // Resume observing
};



    // Observe changes in the DOM to apply the filtering dynamically
    const observer = new MutationObserver((mutations) => {
        hideListItems();
        highlightYear();
        highlightCurrentTab();
    });

    // Observe changes in the DOM
    observer.observe(document.body, {
        childList: true,
        subtree: true,
        characterData: true
    });

    // Initial setup
    createSettingsPanel();
    hideListItems();
    highlightYear();
    highlightCurrentTab()

    setTimeout(() => {
    const style = document.createElement("style");
    style.textContent = `
        body .highlight {
            position: relative !important;
        }
        body .highlight::before {
            content: "";
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(144, 238, 144, 0.5) !important;
            pointer-events: none;
            z-index: 1000;
        }
    `;
    document.head.appendChild(style);
}, 100);

})();