Facebook Notifications Highlighter

Highlights your interesting notifications.

目前為 2017-03-29 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Facebook Notifications Highlighter
// @namespace    http://www.JamesKoss.com/
// @version      2.0.0
// @description  Highlights your interesting notifications.
// @author       James Koss
// @match        https://www.facebook.com/*
// @supportURL   https://greasyfork.org/en/scripts/27189-facebook-your-notifications-highlighter/feedback
// ==/UserScript==

(function() {
    'use strict';
    
    var first = true;
    var hidden = false;
    var updater = null;
    
    var fbNotificationsFlyout = null;
    var ulElement = null;
    var firstNotification = null;
    var lastNotification = null;
    
    // Wait for page to load.
    window.addEventListener('load', function() {
        startScript();
    }, false);
    
    function startScript() {
        fbNotificationsFlyout = document.getElementById('fbNotificationsFlyout');
        
        // Listen to a click on the notifications button on FB.
        var fbNotificationsJewel = document.getElementById("fbNotificationsJewel");
        fbNotificationsJewel.addEventListener("click", onclickJewel);
        
        // Auto updater.
        updater = setInterval(updateView, 500);
    }
    
    // Clicking the notifications button on FB.
    function onclickJewel(e) {
        if (e.which === 1) {
            updateNotifications();
        }
    }
    
    // Check if notifications need updating while displayed.
    function updateView() {
        // Notifications must be visible and ready.
        if (fbNotificationsFlyout.offsetParent === null) return;
        if (ulElement === null) return;
        
        // Request more notifications.
        if (hidden === true) {
            // Scrollbar hidden.
            var sb = fbNotificationsFlyout.querySelector('div.uiScrollableAreaGripper.hidden_elem');
            // Or scrolled to bottom.
            var wrap = fbNotificationsFlyout.querySelector('div.uiScrollableAreaWrap');
            var bottom = wrap.offsetHeight + wrap.scrollTop >= wrap.scrollHeight - 10;
            // And loading jewel must exist, otherwise finished all loadings.
            var jewel = fbNotificationsFlyout.querySelector('div.uiScrollableAreaContent span.jewelLoading');
            
            if (jewel && (sb !== null || bottom === true)) {
                // Fake a scroll event for loader.
                var e = new Event('scroll');
                fbNotificationsFlyout.querySelector('div.uiScrollableArea').dispatchEvent(e);
            }
        }
        
        var curFirstNotification = ulElement.firstChild;
        var curLastNotification = ulElement.lastChild;
        // Update view, if new notifications, or loaded old ones.
        if (curLastNotification !== lastNotification ||
           curFirstNotification !== firstNotification) {
            updateNotifications();
            updateVisibility();
        }
        
        firstNotification = curFirstNotification;
        lastNotification = curLastNotification;
    }
    
    // After clicking the notification, remove highlight color and eventListener.
    function removeHighlight(e) {
        e.currentTarget.removeEventListener("click", removeHighlight, false);
        
        e.currentTarget.style.backgroundColor = "";
        if (e.currentTarget.paramA !== null) {
            e.currentTarget.paramA.style.backgroundColor = "";
        }
        
        // Remove highlight class.
        e.currentTarget.classList.remove("highlightedNotification");
        updateVisibility();
    }
    
    // Update notifications visibility.
    function updateVisibility() {
        var notificationsOld = ulElement.querySelectorAll('li:not(.highlightedNotification), li.highlightedReact');
        
        for (var i=0; i < notificationsOld.length; i++) {
            var displayMode = null;
            if (hidden === true) displayMode = 'none';

            notificationsOld[i].style.display = displayMode;
        }
    }
    
    // Update relevant notifications with highlight.
    function updateNotifications() {
        if (ulElement === null) ulElement = fbNotificationsFlyout.querySelector('ul');
        
        // Delay until notifications elements are available.
        if (ulElement === null) {
            setTimeout(function(){ updateNotifications(); }, 500);
            return;
        }
        
        // On first viewing.
        if (first === true) {
            first = false;
            // Hide/Show option.
            var headerActions = fbNotificationsFlyout.querySelector('div.uiHeaderActions');
            var toggleOption = document.createElement("a");
            toggleOption.style = "font-weight: bold;";
            toggleOption.title = "Hide unimportant notifications.";
            toggleOption.innerHTML = 'Hide Unimportant';
            
            toggleOption.onclick = function(e) {
                e.stopPropagation();
                e.preventDefault();
                
                if (hidden === true) {
                    hidden = false;
                    toggleOption.title = "Hide unimportant notifications.";
                    toggleOption.innerHTML = 'Hide Unimportant';
                } else {
                    hidden = true;
                    toggleOption.title = "Show unimportant notifications.";
                    toggleOption.innerHTML = 'Show Unimportant';
                }
                
                updateVisibility();
            };
            
            var spacer = document.createElement("span");
            spacer.setAttribute("role", "presentation");
            spacer.setAttribute("aria-hidden", "true");
            spacer.innerHTML = ' · ';
            
            headerActions.insertBefore(toggleOption, headerActions.firstChild);
            headerActions.insertBefore(spacer, toggleOption.nextSibling);
        }
        
        // Only check new notifications.
        var notificationsNew = ulElement.querySelectorAll('li.jewelItemNew');

        for (var i = 0; i < notificationsNew.length; i++) {
            var current = notificationsNew[i];
            
            var notificationParent = current.querySelector('div[class="_4l_v"]');
            var notificationYour = notificationParent.querySelectorAll('span');
            
            // match 1 for interest highlight, 2 for "Like" highlight.
            var match = false;
            for (var j=0; j < notificationYour.length; j++) {
                var cur = notificationYour[j];
                var t = cur.textContent;
                
                // Relevant text inside notification element.
                if (t.indexOf("replied to your") !== -1 ||
                    t.indexOf("commented on your") !== -1 ||
                    t.indexOf("shared your") !== -1 ||
                    t.indexOf("mentioned you") !== -1 ||
                    t.indexOf("tagged you") !== -1 ||
                    t.indexOf("you're tagged in") !== -1 ||
                    t.indexOf("made you") !== -1 ||
                    t.indexOf("also replied") !== -1 ||
                    (t.indexOf("replied to") !== -1 && t.indexOf("on your") !== -1)) {
                    match = 1;
                    break;
                } else if (t.indexOf("likes your") !== -1 ||
                          t.indexOf("like your") !== -1 ||
                          t.indexOf("liked your") !== -1 ||
                          t.indexOf("reacted to") !== -1) {
                    match = 2;
                    break;
                } else if (t.indexOf("approved your") !== -1 ||
                          t.indexOf("changed the name of the") !== -1 ||
                          t.indexOf("changed the type of the") !== -1) {
                    match = 3;
                    break;
                } else if (t.indexOf("needs review") !== -1 ||
                           t.indexOf("your Timeline") !== -1 ||
                           t.indexOf("flagged as possible spam") !== -1) {
                    match = 4;
                    break;
                }
            }
            
            // No match.
            if (match === false) {
                continue;
            }
            
            // Remember highlight.
            current.classList.add("highlightedNotification");
            
            // Select color by matching value.
            var color;
            switch(match) {
                case 1:
                    color = "#c3d4ef"; // darker blue
                    break;
                case 2:
                    color = "#faedf8"; // light pink
                    current.classList.add("highlightedReact");
                    break;
                case 3:
                    color = "#d7f4d7"; // light green
                    break;
                case 4:
                    color = "#dec3ef"; // darker purple
                    break;
            }
            
            // Update the li & a elements backgrounds.
            var a = current.querySelector('a[class="_33e _1_0e"]');
            if (a !== null) {
                a.style.backgroundColor = color;
            }
            current.style.backgroundColor = color;
            
            current.paramA = a; // Pass to object, for highlight removal after clicked.
            current.addEventListener("click", removeHighlight, false);
        }
    }
})();