<commit_before>class NotificationsPopup {
    /**
     * Initialize instance, load notifications from storage and watch future data updates.
     * @param remotePontoon
     */
    constructor(remotePontoon) {
        this._remotePontoon = remotePontoon;

        this._watchStorageChanges();
        this._loadNotificationsFromStorage();
    }

    /**
     * Update notifications popup when notification data change in storage.
     * @private
     */
    _watchStorageChanges() {
        chrome.storage.onChanged.addListener((changes, areaName) => {
            const dataKey = 'notificationsData';
            if (changes[dataKey] !== undefined) {
                this._displayNotifications(changes[dataKey].newValue);
            }
        });
    }

    /**
     * Load notifications data from storage and update the popup.
     * @private
     */
    _loadNotificationsFromStorage() {
        const dataKey = 'notificationsData';
        chrome.storage.local.get(dataKey, (item) => this._displayNotifications(item[dataKey]));
    }

    /**
     * Create notification popup list item from data object.
     * @param notification data object
     * @returns {Element} notification list item
     * @private
     */
    _createNotificationListItem(notification) {
        const listItem = document.createElement('li');
        if (notification.actor) {
            const actorLink = document.createElement('a');
            actorLink.textContent = notification.actor.text;
            actorLink.setAttribute('href', this._remotePontoon.getTeamProjectUrl(notification.actor.link));
            listItem.appendChild(actorLink);
        }
        if (notification.verb) {
            const verb = document.createElement('span');
            verb.textContent = ` ${notification.verb}`;
            listItem.appendChild(verb);
        }
        if (notification.target) {
            const targetLink = document.createElement('a');
            targetLink.textContent = ` ${notification.target.text}`;
            targetLink.setAttribute('href', this._remotePontoon.getTeamProjectUrl(notification.target.link));
            listItem.appendChild(targetLink);
        }
        if (notification.timeago) {
            const timeago = document.createElement('div');
            timeago.textContent = notification.timeago;
            timeago.classList.add('timeago');
            listItem.appendChild(timeago);
        }
        if (notification.message) {
            const message = document.createElement('div');
            message.textContent = notification.message;
            message.classList.add('message');
            listItem.appendChild(message);
        }
        if (!notification.target) {
            listItem.querySelectorAll('*').forEach((child) => {
                child.addEventListener('click', (e) => chrome.tabs.create({url: this._remotePontoon.getTeamProjectUrl(notification.actor.link)}));
            });
            listItem.addEventListener('click', (e) => chrome.tabs.create({url: this._remotePontoon.getTeamProjectUrl(notification.actor.link)}));
            listItem.classList.add('pointer');
        }
        return listItem;
    }

    /**
     * Display notifications from data from storage.
     * @param notificationsData from storage
     * @private
     */
    _displayNotifications(notificationsData) {
        const notificationsList = document.getElementById('notification-list');
        const fullList = document.getElementById('full-list');
        const emptyList = document.getElementById('empty-list');
        const error = document.getElementById('error');

        if (notificationsData !== undefined && Object.keys(notificationsData).length > 0) {
            while (notificationsList.lastChild) {
                notificationsList.removeChild(notificationsList.lastChild);
            }
            Object.keys(notificationsData).sort().reverse()<commit_msg>Fix notification click action opening multiple tabs, #28<commit_after>class NotificationsPopup {
    /**
     * Initialize instance, load notifications from storage and watch future data updates.
     * @param remotePontoon
     */
    constructor(remotePontoon) {
        this._remotePontoon = remotePontoon;

        this._watchStorageChanges();
        this._loadNotificationsFromStorage();
    }

    /**
     * Update notifications popup when notification data change in storage.
     * @private
     */
    _watchStorageChanges() {
        chrome.storage.onChanged.addListener((changes, areaName) => {
            const dataKey = 'notificationsData';
            if (changes[dataKey] !== undefined) {
                this._displayNotifications(changes[dataKey].newValue);
            }
        });
    }

    /**
     * Load notifications data from storage and update the popup.
     * @private
     */
    _loadNotificationsFromStorage() {
        const dataKey = 'notificationsData';
        chrome.storage.local.get(dataKey, (item) => this._displayNotifications(item[dataKey]));
    }

    /**
     * Create notification popup list item from data object.
     * @param notification data object
     * @returns {Element} notification list item
     * @private
     */
    _createNotificationListItem(notification) {
        const listItem = document.createElement('li');
        if (notification.actor) {
            const actorLink = document.createElement('a');
            actorLink.textContent = notification.actor.text;
            actorLink.setAttribute('href', this._remotePontoon.getTeamProjectUrl(notification.actor.link));
            listItem.appendChild(actorLink);
        }
        if (notification.verb) {
            const verb = document.createElement('span');
            verb.textContent = ` ${notification.verb}`;
            listItem.appendChild(verb);
        }
        if (notification.target) {
            const targetLink = document.createElement('a');
            targetLink.textContent = ` ${notification.target.text}`;
            targetLink.setAttribute('href', this._remotePontoon.getTeamProjectUrl(notification.target.link));
            listItem.appendChild(targetLink);
        }
        if (notification.timeago) {
            const timeago = document.createElement('div');
            timeago.textContent = notification.timeago;
            timeago.classList.add('timeago');
            listItem.appendChild(timeago);
        }
        if (notification.message) {
            const message = document.createElement('div');
            message.textContent = notification.message;
            message.classList.add('message');
            listItem.appendChild(message);
        }
        if (listItem.querySelectorAll('a').length === 1) {
            const linkUrl  = listItem.querySelector('a').href;
            listItem.querySelectorAll('*').forEach((child) => {
                child.addEventListener('click', (e) => {
                    e.preventDefault();
                    e.stopPropagation();
                    chrome.tabs.create({url: linkUrl});
                });
            });
            listItem.addEventListener('click', (e) => {
                e.preventDefault();
                e.stopPropagation();
                chrome.tabs.create({url: linkUrl});
            });
            listItem.classList.add('pointer');
        }
        return listItem;
    }

    /**
     * Display notifications from data from storage.
     * @param notificationsData from storage
     * @private
     */
    _displayNotifications(notificationsData) {
        const notificationsList = document.getElementById('notification-list');
        const fullList = document.getElementById('full-list');
        const emptyList = document.getElementById('empty-list');
        const error = document.getElementById('error');

        if (notificationsData !== undefined && Object.keys(notificationsData).length > 0) {
            while (notificationsList.lastChild) {
                notificationsList.removeChild(notificationsList.lastChild);
            }
            Object.keys(notificationsData).sort().reverse()
{'fraction_non_alphanumeric': 0.11268129416139828, 'fraction_numerical': 0.0006198090987975703, 'mean_word_length': 2.367278797996661, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4485071', 'n_tokens_mistral': 1968, 'n_tokens_neox': 1935, 'n_words': 358}