<commit_before>// background script
var { storage, runtime, browserAction, pageAction, tabs, windows } = browser;
var { local } = storage;

var bg = {
    isDEV: false,
    VERSION: '4',

    currentData: null,

    init: () => {
        var me = bg;
        me.checkStorageVersion().then( () => {
            me.applyOptions();
            me.onMessage();
            tabs.onUpdated.addListener( tab_id => {
                me.initPageAction({
                    forAll: false,
                    tab_id: tab_id
                });
            });
            me.setupCacheList();
        });
    },

    log_storage: () => bg.isDEV && console.log(bg.currentData),

    _catchErr: e => console.error(e),

    // obj empty: https://stackoverflow.com/questions/679915/how-do-i-test-for-an-empty-javascript-object
    _isEmptyObj: obj => (Object.keys(obj).length === 0 && obj.constructor === Object),

    _getTime: date => date.getFullYear() + '/' + (parseInt(date.getMonth()) + 1) + '/' + date.getDate(),

    default_exceptions: [
        "docs.google.com/spreadsheets",
        "slack.com",
        "messenger.com"
    ],

    checkStorageVersion: () => {
        var me = bg;
        var { default_exceptions, VERSION, _catchErr } = me;
        return local.get().then( db_data => {
            me.currentData = db_data;

            if (parseInt( db_data && db_data.version) == VERSION) {
                return;
            }
            else if (parseInt( db_data && db_data.version) < VERSION) {
                var updateData = {
                    version: VERSION,
                    setting: db_data.setting || {},
                    exceptions: default_exceptions
                };
                local.set(updateData).then( () => {
                    me.currentData = Object.assign( db_data, updateData);
                }).catch(_catchErr);
            }
            else {
                // if just installed || some other strange situations (e.g., db_data.version > VERSION ... etc)
                me.initDatabase();
            }
        }).catch(me._catchErr);
    },

    applyOptions: () => {
        bg.isDEV = bg.currentData && !!(bg.currentData.setting && bg.currentData.setting.debug);
    },

    setOptions: request => {
        var me = bg;
        var { log_storage, isDEV } = me;

        log_storage();
        if (!me.currentData.setting) {
            if (isDEV) console.log('creating setting');
            me.currentData.setting = {};
        }
        me.currentData.setting[request.key] = request.val;
        return local.set(me.currentData).then(log_storage);
    },

    initPageAction: info => {
        var { forAll, tab_id } = info;
        var show_page_action = bg.currentData.setting.pageAction;
        if (forAll) {
            tabs.query({}).then(tab_infos => {
                tab_infos.forEach(tab_info => {
                    if (show_page_action) {
                        pageAction.show(tab_info.id);
                    }
                    else {
                        pageAction.hide(tab_info.id);
                    }
                });
            }).catch(bg._catchErr);
        }
        else {
            if (show_page_action) {
                pageAction.show(tab_id);
            }
            else {
                pageAction.hide(tab_id);
            }
        }
    },

    initDatabase: old_data => local.clear().then( () => {
        var me = bg;
        var resetData = {
            version: bg.VERSION,
            setting: (old_data && old_data.setting) || {},
            exceptions: (old_data && old_data.exceptions)|| bg.default_exceptions
        };
        local.set(resetData).then( () => {
            me.currentData = resetData;
        }).catch(me._catchErr);
    }).catch(bg._catchErr),

    onMessage: () => {
        var me = bg;
        var {log_storage, isDEV} = me;

        runtime.onMessage.addListener( (request, sender, sendBack) => {

            if (isDEV) console.log('bg_get_request', request.behavior);
            switch(request.behavior) {
            case 'get_exceptions':
                if (isDEV) console.log('get_exceptionsv');
                sendBack({ expts: me.currentData.exceptions })
                break;
            case 'set_exceptions':
                if (isDEV) console.log('set_exceptionsv');

                local.set({
                    exceptions: request.val.split('\n').filter(site => site)
                }).then( () => {
                    me.currentData.exceptions = request.val.split('\n').filter(site => site);
                    sendBack({ msg: 'done'});
                }).catch(bg._catchErr);
                break;
            case 'set_options':
                me.setOptions(request).then( () => {
                    me.setupCacheList();
                    me.initPageAction({
                        forAll: true
                    });
                    sendBack({ msg: 'done'});
                });
                break;
            case 'get_options':
                sendBack(me.currentData.setting);
                break;
            case 'save':
                if (isDEV) console.log('bg_save');
                var {title, val, type, id, url, sessionKey} = request;
                var key = `${sessionKey} ${title} ${id}`;

                if (isDEV) console.table({ key: key, val: val, type: type, url: url });

                var tmp = {};
                tmp[key] = {
                    time: sessionKey,
                    type: type,
                    val: val,
                    url: url,
                    last_modified: new Date()
                };

                local.set(tmp).then( () => {
                    me.currentData = Object.assign(me.currentData, tmp);
                }).catch(bg._catchErr);
                log_storage();
                break;
            case 'load':
                if (isDEV) console.log('bg_load');
                sendBack({ data: me.currentData });
                break;
            case 'delete':
                if (isDEV) console.log('bg_delete');
                local.remove(request.id).then( () => {
                    sendBack({ msg: 'done', deleted: request.id, data: me.currentData});
                }).catch(bg._catchErr);
                break;
            case 'clear':
                if (isDEV) console.log('bg_clear');
                me.initDatabase({
                    setting: me.currentData.setting,
                    exceptions: me.currentData.exceptions
                }).then( () => {
                    sendBack({ msg: 'done' });
                });
                break;
            }

            return true;
        });
    },

    _popupListInWindow: () => {
        windows.create({
            url: browser.extension.getURL("list/list.html"),
            type: "detached_panel", // "normal", "popup", "panel", "detached_panel"
            height: 450,
            width: 800
        });
    },

    _popupListInTab: () => {
        tabs.create({
            url: browser.extension.getURL("list/list.html")
        });
    },

    _popupLite: tab => {
        pageAction.setPopup({
            tabId: tab.id,
            popup: browser.extension.getURL("list/lite.html")
        });
        pageAction.openPopup();
    },

    setupCacheList: () => {
        var me = bg;
        var { setting } = me.currentData;

        if (!setting) {
            setting = {
                popupType: "tab",
                pageActionLite: false
            };
        }

        if (setting.popupType == "window") {
            browserAction.onClicked.removeListener(bg._popupListInTab);
            browserAction.onClicked.addListener(bg._popupListInWindow);
        }
        else if (setting.popupType == "tab") {
            browserAction.onClicked.removeListener(bg._popupListInWindow);
            browserAction.onClicked.addListener(bg._popupListInTab);
        }

        if (!setting.pageAction) return;
        var target_function = setting.popupType == "window" ? bg._popupListInWindow : bg._popupListInTab;
        if (setting.pageActionLite) {
            pageAction.onClicked.removeListener(target_function);
            pageAction.onClicked.addListener(bg._popupLite);
        }
        else {
            pageAction.onClicked.removeListener(bg._popupLite);
            pageAction.onClicked.addListener(target_function);
        }
    }
};
bg.init();

<commit_msg>fix for people who let the popupType being default value<commit_after>// background script
var { storage, runtime, browserAction, pageAction, tabs, windows } = browser;
var { local } = storage;

var bg = {
    isDEV: false,
    VERSION: '4',

    currentData: null,

    init: () => {
        var me = bg;
        me.checkStorageVersion().then( () => {
            me.applyOptions();
            me.onMessage();
            tabs.onUpdated.addListener( tab_id => {
                me.initPageAction({
                    forAll: false,
                    tab_id: tab_id
                });
            });
            me.setupCacheList();
        });
    },

    log_storage: () => bg.isDEV && console.log(bg.currentData),

    _catchErr: e => console.error(e),

    // obj empty: https://stackoverflow.com/questions/679915/how-do-i-test-for-an-empty-javascript-object
    _isEmptyObj: obj => (Object.keys(obj).length === 0 && obj.constructor === Object),

    _getTime: date => date.getFullYear() + '/' + (parseInt(date.getMonth()) + 1) + '/' + date.getDate(),

    default_exceptions: [
        "docs.google.com/spreadsheets",
        "slack.com",
        "messenger.com"
    ],

    checkStorageVersion: () => {
        var me = bg;
        var { default_exceptions, VERSION, _catchErr } = me;
        return local.get().then( db_data => {
            me.currentData = db_data;

            if (parseInt( db_data && db_data.version) == VERSION) {
                return;
            }
            else if (parseInt( db_data && db_data.version) < VERSION) {
                var updateData = {
                    version: VERSION,
                    setting: db_data.setting || {},
                    exceptions: default_exceptions
                };
                local.set(updateData).then( () => {
                    me.currentData = Object.assign( db_data, updateData);
                }).catch(_catchErr);
            }
            else {
                // if just installed || some other strange situations (e.g., db_data.version > VERSION ... etc)
                me.initDatabase();
            }
        }).catch(me._catchErr);
    },

    applyOptions: () => {
        bg.isDEV = bg.currentData && !!(bg.currentData.setting && bg.currentData.setting.debug);
    },

    setOptions: request => {
        var me = bg;
        var { log_storage, isDEV } = me;

        log_storage();
        if (!me.currentData.setting) {
            if (isDEV) console.log('creating setting');
            me.currentData.setting = {};
        }
        me.currentData.setting[request.key] = request.val;
        return local.set(me.currentData).then(log_storage);
    },

    initPageAction: info => {
        var { forAll, tab_id } = info;
        var show_page_action = bg.currentData.setting.pageAction;
        if (forAll) {
            tabs.query({}).then(tab_infos => {
                tab_infos.forEach(tab_info => {
                    if (show_page_action) {
                        pageAction.show(tab_info.id);
                    }
                    else {
                        pageAction.hide(tab_info.id);
                    }
                });
            }).catch(bg._catchErr);
        }
        else {
            if (show_page_action) {
                pageAction.show(tab_id);
            }
            else {
                pageAction.hide(tab_id);
            }
        }
    },

    initDatabase: old_data => local.clear().then( () => {
        var me = bg;
        var resetData = {
            version: bg.VERSION,
            setting: (old_data && old_data.setting) || {
                pageActionLite: true,
                popupType: 'tab'
            },
            exceptions: (old_data && old_data.exceptions)|| bg.default_exceptions
        };
        local.set(resetData).then( () => {
            me.currentData = resetData;
        }).catch(me._catchErr);
    }).catch(bg._catchErr),

    onMessage: () => {
        var me = bg;
        var {log_storage, isDEV} = me;

        runtime.onMessage.addListener( (request, sender, sendBack) => {

            if (isDEV) console.log('bg_get_request', request.behavior);
            switch(request.behavior) {
            case 'get_exceptions':
                if (isDEV) console.log('get_exceptionsv');
                sendBack({ expts: me.currentData.exceptions })
                break;
            case 'set_exceptions':
                if (isDEV) console.log('set_exceptionsv');

                local.set({
                    exceptions: request.val.split('\n').filter(site => site)
                }).then( () => {
                    me.currentData.exceptions = request.val.split('\n').filter(site => site);
                    sendBack({ msg: 'done'});
                }).catch(bg._catchErr);
                break;
            case 'set_options':
                me.setOptions(request).then( () => {
                    me.setupCacheList();
                    me.initPageAction({
                        forAll: true
                    });
                    sendBack({ msg: 'done'});
                });
                break;
            case 'get_options':
                sendBack(me.currentData.setting);
                break;
            case 'save':
                if (isDEV) console.log('bg_save');
                var {title, val, type, id, url, sessionKey} = request;
                var key = `${sessionKey} ${title} ${id}`;

                if (isDEV) console.table({ key: key, val: val, type: type, url: url });

                var tmp = {};
                tmp[key] = {
                    time: sessionKey,
                    type: type,
                    val: val,
                    url: url,
                    last_modified: new Date()
                };

                local.set(tmp).then( () => {
                    me.currentData = Object.assign(me.currentData, tmp);
                }).catch(bg._catchErr);
                log_storage();
                break;
            case 'load':
                if (isDEV) console.log('bg_load');
                sendBack({ data: me.currentData });
                break;
            case 'delete':
                if (isDEV) console.log('bg_delete');
                local.remove(request.id).then( () => {
                    sendBack({ msg: 'done', deleted: request.id, data: me.currentData});
                }).catch(bg._catchErr);
                break;
            case 'clear':
                if (isDEV) console.log('bg_clear');
                me.initDatabase({
                    setting: me.currentData.setting,
                    exceptions: me.currentData.exceptions
                }).then( () => {
                    sendBack({ msg: 'done' });
                });
                break;
            }

            return true;
        });
    },

    _popupListInWindow: () => {
        windows.create({
            url: browser.extension.getURL("list/list.html"),
            type: "detached_panel", // "normal", "popup", "panel", "detached_panel"
            height: 450,
            width: 800
        });
    },

    _popupListInTab: () => {
        tabs.create({
            url: browser.extension.getURL("list/list.html")
        });
    },

    _popupLite: tab => {
        pageAction.setPopup({
            tabId: tab.id,
            popup: browser.extension.getURL("list/lite.html")
        });
        pageAction.openPopup();
    },

    setupCacheList: () => {
        var me = bg;
        var { setting } = me.currentData;

        if (!setting) {
            setting = {
                popupType: "tab",
                pageActionLite: false
            };
        }

        if (setting.popupType == "window") {
            browserAction.onClicked.removeListener(bg._popupListInTab);
            browserAction.onClicked.addListener(bg._popupListInWindow);
        }
        else {
            browserAction.onClicked.removeListener(bg._popupListInWindow);
            browserAction.onClicked.addListener(bg._popupListInTab);
        }

        if (!setting.pageAction) return;
        var target_function = setting.popupType == "window" ? bg._popupListInWindow : bg._popupListInTab;
        if (setting.pageActionLite) {
            pageAction.onClicked.removeListener(target_function);
            pageAction.onClicked.addListener(bg._popupLite);
        }
        else {
            pageAction.onClicked.removeListener(bg._popupLite);
            pageAction.onClicked.addListener(target_function);
        }
    }
};
bg.init();

<|endoftext|>


<commit_before>/*
* Yo developer! I'm happy that you are going through this.code :)
* This was developed during a boring sunday in order to avoid mistakes while developing, feel free to edit and improve the code!
*
* Star the project in https://github.com/xunga/leaving-dev-area
*
* Crafted with <3 by Alessio Scarapazzi
* https://github.com/xunga
*
* */

WEBSITE_JSON = 'websites.json';
websites = {};

chrome.tabs.onUpdated.addListener( function (tabId, changeInfo, tab) {
    var i, len, website;
    var topbar = {};

    if(changeInfo.status == 'complete' ){

        for (i = 0, len = websites.length; i < len; i++) {
            website = websites[i];
            if(tab.url.indexOf('://' + website.url  + '/') > -1){
                topbar.name = website.name;
                topbar.color = website.color;
                topbar.delay = website.delay || 0;
                break;
            }
        }

        if (Object.keys(topbar).length) {

            var bar = '
 ' +
                '
 ' +
                '
Hey you are visiting ' + topbar.name + '! Ahoy!
 ' +
                '
 ' +
                '
';

            setTimeout(function() {
                chrome.tabs.executeScript(tab.ib, {
                    code: "document.body.innerHTML += '" + bar + "';"
                    //file: 'inject.js'
                });
            }, topbar.delay);
        }

    }
});

//Loading the webiste list
function loadJSON(callback) {
    var xobj = new XMLHttpRequest();
    xobj.overrideMimeType("application/json");
    xobj.open('GET', WEBSITE_JSON , true);
    xobj.onreadystatechange = function () {
        if (xobj.readyState == 4 && xobj.status == "200") {
            return callback(xobj.responseText);
        }
    };
    xobj.send(null);
}

(function () {
    loadJSON(function(response) {
        websites = JSON.parse(response);
        websites = websites.websites;
    });
})();
<commit_msg>colors in topbar<commit_after>/*
* Yo developer! I'm happy that you are going through this.code :)
* This was developed during a boring sunday in order to avoid mistakes while developing, feel free to edit and improve the code!
*
* Star the project in https://github.com/xunga/leaving-dev-area
*
* Crafted with <3 by Alessio Scarapazzi
* https://github.com/xunga
*
* */

WEBSITE_JSON = 'websites.json';
websites = {};

chrome.tabs.onUpdated.addListener( function (tabId, changeInfo, tab) {

    if(changeInfo.status == 'complete' ){
        var i, len, website;
        var topbar = {};

        for (i = 0, len = websites.length; i < len; i++) {
            website = websites[i];
            if(tab.url.indexOf('://' + website.url  + '/') > -1){
                topbar.name = website.name;
                topbar.color = website.color || 'LightCoral';
                topbar.delay = website.delay || 0;
                break;
            }
        }

        if (Object.keys(topbar).length) {
            var bar = ''+
                '
 ' +
                    '
 ' +
                        '
Hey you are visiting ' + topbar.name + '! Ahoy!
 ' +
                    '
 ' +
                '
';

            setTimeout(function() {
                chrome.tabs.executeScript(tab.ib, {
                    code: "document.body.innerHTML += '" + bar + "';"
                    //file: 'inject.js'
                });
            }, topbar.delay);

        }

    }
});

//Loading the webiste list
function loadJSON(callback) {
    var xobj = new XMLHttpRequest();
    xobj.overrideMimeType("application/json");
    xobj.open('GET', WEBSITE_JSON , true);
    xobj.onreadystatechange = function () {
        if (xobj.readyState == 4 && xobj.status == "200") {
            return callback(xobj.responseText);
        }
    };
    xobj.send(null);
}

(function () {
    loadJSON(function(response) {
        websites = JSON.parse(response);
        websites = websites.websites;
    });
})();

<|endoftext|>


<commit_before>
(function() {

	var calculate = function () {
		$("article.todolist").each(function (index) {

			var done = 0;
			var todo = 0;
			var regExp = /\(([^)]+)\)/;

			$(this).find("ul.todos a:visible").each(function (index) {

				var matches = regExp.exec($(this).text());

				if (matches) {
					matches.forEach(function (entry) {
						if (parseInt(entry)) {
							todo = todo + parseInt(entry);
						}
					});
				}
			});

			$(this).find("ul.completed a").each(function (index) {

				var matches = regExp.exec($(this).text());

				if (matches) {
					matches.forEach(function (entry) {
						if (parseInt(entry)) {
							done = done + parseInt(entry);
						}
					});
				}
			});

			$(this).find("h3 a").append(" (" + todo + "/" + done + ")");
		});
	};

	var init = function()
	{
		calculate(); //run calculate onload..
	};

	$(document).ready(function() {
		init();
	});

})();

<commit_msg>label it.<commit_after>
(function() {

	var calculate = function () {
		$("article.todolist").each(function (index) {

			var done = 0;
			var todo = 0;
			var regExp = /\(([^)]+)\)/;

			$(this).find("ul.todos a:visible").each(function (index) {

				var matches = regExp.exec($(this).text());

				if (matches) {
					matches.forEach(function (entry) {
						if (parseInt(entry)) {
							todo = todo + parseInt(entry);
						}
					});
				}
			});

			$(this).find("ul.completed a").each(function (index) {

				var matches = regExp.exec($(this).text());

				if (matches) {
					matches.forEach(function (entry) {
						if (parseInt(entry)) {
							done = done + parseInt(entry);
						}
					});
				}
			});

			$(this).find("h3 a").append(" (" + todo + "/" + done + ")");
		});
	};

	var labelit = function() {

		var labels = "";

		var appendLabel = function(html)
		{
			var re = /(#(\w+)([-](\w+))*)/g, match, matches = [];

			while (match = re.exec(html)) {
  				matches.push(match[1]);
			}

			for(i=0; i<matches.length; i++)
			{
				labels = labels + matches[i] + " "
			}
			console.log(labels);
		}

		$("div.thread-entry__content").each(function (index){
			//console.log($(this).html());
			appendLabel($(this).html());
		});

		var listUrl = $("a[href*='todolists']").first().attr('href');

		$.get(listUrl, function( data ) {
			var dom = $(data);
			dom.find("div.thread-entry__content").each(function (index){
				//console.log($(this).html());
				appendLabel($(this).html());
				console.log(labels);
			});
		})
		.always(function(){

			console.log("Finished!");

			var html = `

						Labels

`+ labels +`

			`;

			$("section.todo-perma__details").append(html);
		});

	}

	var init = function()
	{
		calculate(); //run calculate onload..
		labelit();
	};

	$(document).ready(function() {
		init();
	});

})();

<|endoftext|>


<commit_before>/*
 * CooCooCookies
 * https://github.com/xadet/CooCooCookies
 *
 * Includes Sizzle.js
 * http://sizzlejs.com/
 *
 * Includes jsuri.js
 * https://code.google.com/p/jsuri/
 *
 * Copyright 2012 Jack Wakefield
 * Released under the MIT license
 */

// a list of generic subdomains to strip from hosts
var genericSubdomains = [ "www.", "www1." ];
var cookieEntries = {};

var xhr = new XMLHttpRequest();

xhr.onreadystatechange = function() {
    if (xhr.readyState == 4) {
        cookieEntries = JSON.parse(xhr.responseText);
        addListeners();
    }
};

xhr.open("GET", chrome.extension.getURL("/entries.json"), false);

try {
    xhr.send();
} catch(e) {
    console.error("Unable to load entries.json");
}

// populate the url filter array of hosts from the cookie elements
// to ensure unneeded onCompleted events are not fired
// for the hosts we are not monitoring
function createUrlFilter(triggerType) {
    var urlFilter = [];

    for (var host in cookieEntries) {
        var cookieEntry = cookieEntries[host];
        var trigger = typeof cookieEntry.trigger !== "undefined" ? cookieEntry.trigger : "ready";

        if (trigger == triggerType) {
            urlFilter.push({
                hostSuffix: host
            });
        }
    }

    return urlFilter;
}

// execute a script on the specified tab to simulate
// a click the first element found to be matching the given path
function clickElement(tabId, element) {
    chrome.tabs.executeScript(tabId, {
        allFrames: true,
        code: "" +
        "var matchingElements = Sizzle('" + element + "');" +
        "if (matchingElements.length > 0) {" +
        "   var element = matchingElements[0];" +
        "   element.click();" +
        "}"
    });
}

// execute a script on the specified tab to remove
// the first element found to be matching the given path
function removeElement(tabId, element) {
    chrome.tabs.executeScript(tabId, {
        allFrames: true,
        code: "" +
        "var matchingElements = Sizzle('" + element + "');" +
        "if (matchingElements.length > 0) {" +
        "   var element = matchingElements[0];" +
        "   if (typeof element.parentNode !== 'undefined') {" +
        "       element.parentNode.removeChild(element);" +
        "   }" +
        "}"
    });
}

function handleEvent(details) {
    var uri = new Uri(details.url);
    var host = uri.host();

    // strip out the lits of generic subdomains from the host
    for (var i = 0; i < genericSubdomains.length; i++) {
        var subdomain = genericSubdomains[i];

        if (host.indexOf(subdomain) == 0) {
            host = host.substring(subdomain.length);
        }
    }

    // ensure the determined host exists in the entries array
    // perhaps the url passed the filter but the host determined
    // is incorrect, most likely due to a subdomain that hasn't
    // been stripped out
    if (host in cookieEntries) {
        var tabId = details.tabId;

        var cookieEntry = cookieEntries[host];
        var element = cookieEntry.element;
        var action = typeof cookieEntry.action !== "undefined" ? cookieEntry.action : "click";
        var delay = typeof cookieEntry.delay !== "undefined" ? cookieEntry.delay : false;

        // inject the sizzle.js script into the page
        // once the script has loaded, determine which action
        // to take
        chrome.tabs.executeScript(tabId, {
            allFrames: true,
            file: "libs/sizzle.js"
        }, function() {
            if (action == "click") { // simulate a click on the element
                if (!delay) {
                    // immediately click the element as no delay has been specified
                    clickElement(tabId, element);
                } else {
                    // click the element after the specified amount of milliseconds have passed
                    setTimeout(function() {
                        clickElement(tabId, element);
                    }, delay);
                }
            } else if (action == "remove") { // remove the element from the parent
                if (!delay) {
                    // immediately remove the element as no delay has been specified
                    removeElement(tabId, element);
                } else {
                    // remove the element after the specified amount of milliseconds have passed
                    setTimeout(function() {
                        removeElement(tabId, element);
                    }, delay);
                }
            }
        });
    }
}

function addListeners() {
    chrome.webNavigation.onDOMContentLoaded.addListener(function(details) {
        handleEvent(details);
    }, {
        url: createUrlFilter("ready")
    });

    chrome.webNavigation.onCompleted.addListener(function(details) {
        handleEvent(details);
    }, {
        url: createUrlFilter("loaded")
    });
}
<commit_msg>Tidied comments<commit_after>/*
 * CooCooCookies
 * https://github.com/xadet/CooCooCookies
 *
 * Includes Sizzle.js
 * http://sizzlejs.com/
 *
 * Includes jsuri.js
 * https://code.google.com/p/jsuri/
 *
 * Includes json3.js
 * http://bestiejs.github.com/json3/
 *
 * Copyright 2012 Jack Wakefield
 * Released under the MIT license
 */

// a list of generic subdomains to strip from hosts
var genericSubdomains = [ "www.", "www1." ];
var cookieEntries = {};

// create an AJAX request to parse the cookie entries
var xhr = new XMLHttpRequest();

xhr.onreadystatechange = function() {
    if (xhr.readyState == 4) {
        cookieEntries = JSON.parse(xhr.responseText);
        addListeners();
    }
};

xhr.open("GET", chrome.extension.getURL("/entries.json"), false);

try {
    xhr.send();
} catch(e) {
    console.error("Unable to load entries.json");
}

// populate the url filter array of hosts from the
// cookie elements to ensure unneeded events are not
// fired for the hosts we are not monitoring
function createUrlFilter(triggerType) {
    var urlFilter = [];

    for (var host in cookieEntries) {
        var cookieEntry = cookieEntries[host];
        var trigger = typeof cookieEntry.trigger !== "undefined" ? cookieEntry.trigger : "ready";

        if (trigger == triggerType) {
            urlFilter.push({
                hostSuffix: host
            });
        }
    }

    return urlFilter;
}

// execute a script on the specified tab to simulate
// a click the first element found to be matching the given path
function clickElement(tabId, element) {
    chrome.tabs.executeScript(tabId, {
        allFrames: true,
        code: "" +
        "var matchingElements = Sizzle('" + element + "');" +
        "if (matchingElements.length > 0) {" +
        "   var element = matchingElements[0];" +
        "   element.click();" +
        "}"
    });
}

// execute a script on the specified tab to remove
// the first element found to be matching the given path
function removeElement(tabId, element) {
    chrome.tabs.executeScript(tabId, {
        allFrames: true,
        code: "" +
        "var matchingElements = Sizzle('" + element + "');" +
        "if (matchingElements.length > 0) {" +
        "   var element = matchingElements[0];" +
        "   if (typeof element.parentNode !== 'undefined') {" +
        "       element.parentNode.removeChild(element);" +
        "   }" +
        "}"
    });
}

// handles the event for the loaded page ensuring
// a cookie entry is found for the host and processes
// the action specified in the entry
function handleEvent(details) {
    var uri = new Uri(details.url);
    var host = uri.host();

    // strip out the lits of generic subdomains from the host
    for (var i = 0; i < genericSubdomains.length; i++) {
        var subdomain = genericSubdomains[i];

        if (host.indexOf(subdomain) == 0) {
            host = host.substring(subdomain.length);
        }
    }

    // ensure the determined host exists in the entries array
    // perhaps the url passed the filter but the host determined
    // is incorrect, most likely due to a subdomain that hasn't
    // been stripped out
    if (host in cookieEntries) {
        var tabId = details.tabId;

        var cookieEntry = cookieEntries[host];
        var element = cookieEntry.element;
        var action = typeof cookieEntry.action !== "undefined" ? cookieEntry.action : "click";
        var delay = typeof cookieEntry.delay !== "undefined" ? cookieEntry.delay : false;

        // inject the sizzle.js script into the page
        // once the script has loaded, determine which action
        // to take
        chrome.tabs.executeScript(tabId, {
            allFrames: true,
            file: "libs/sizzle.js"
        }, function() {
            if (action == "click") { // simulate a click on the element
                if (!delay) {
                    // immediately click the element as no delay has been specified
                    clickElement(tabId, element);
                } else {
                    // click the element after the specified amount of milliseconds have passed
                    setTimeout(function() {
                        clickElement(tabId, element);
                    }, delay);
                }
            } else if (action == "remove") { // remove the element from the parent
                if (!delay) {
                    // immediately remove the element as no delay has been specified
                    removeElement(tabId, element);
                } else {
                    // remove the element after the specified amount of milliseconds have passed
                    setTimeout(function() {
                        removeElement(tabId, element);
                    }, delay);
                }
            }
        });
    }
}

// adds the listener callbacks for the events needed
function addListeners() {
    chrome.webNavigation.onDOMContentLoaded.addListener(function(details) {
        handleEvent(details);
    }, {
        url: createUrlFilter("ready")
    });

    chrome.webNavigation.onCompleted.addListener(function(details) {
        handleEvent(details);
    }, {
        url: createUrlFilter("loaded")
    });
}
<|endoftext|>


<commit_before>// Define root folder for searches
const FOLDER_NAME = "Searches";
const ILLEGAL_BOOKMARK_PROTOCOLS = ["chrome", "javascript", "data", "file", "about"];
const ILLEGAL_CONTENTSCRIPT_PROTOCOLS = ["view-source", "about", "moz-extension"];
const ILLEGAL_CONTENTSCRIPT_DOMAINS = ["accounts-static.cdn.mozilla.net", "accounts.firefox.com", "addons.cdn.mozilla.net",
                                     "addons.mozilla.org", "api.accounts.firefox.com", "content.cdn.mozilla.net", "content.cdn.mozilla.net",
                                     "discovery.addons.mozilla.org", "input.mozilla.org", "install.mozilla.org", "oauth.accounts.firefox.com",
                                     "profile.accounts.firefox.com", "support.mozilla.org", "sync.services.mozilla.com", "testpilot.firefox.com"];
const HELP_LINK = browser.extension.getURL("/help.html");

var rootFolderId = "";
var fallbackMode = false;
var query = "";
var activeTabId = 0;
var showMultiOption = false;
var showFavicons = false;

function parsePlatformInfo(info) {
  os = info.os;
}

// Check to see if the current tab supports content scripts. If not, use the
// fallback mode where only selected text can be used.
// When switching from a tab that require fallback mode to a tab that doesn't
// (and vice versa), the context menu must be rebuilt so that queries aren't
// carried over. Menu rebuilding is only done when necessary, since it is
// supposedly expensive.
function parseTabUrl(tabId) {
  let gettingTabId = browser.tabs.get(tabId);
  gettingTabId.then((response) => {
    let tabProtocol = getUrlProtocol(response.url);
    let tabHostname = getUrlHostname(response.url);

    // All new tabs start out as about:blank. By ignoring those, this code
    // isn't run unnecessarily, since onUpdated will use this function twice.
    if (response.url != "about:blank") {
      let previousFallbackMode = fallbackMode;
      fallbackMode = false;

      if (ILLEGAL_CONTENTSCRIPT_PROTOCOLS.includes(tabProtocol)) {
        fallbackMode = true;
      }

      if (!tabHostname || ILLEGAL_CONTENTSCRIPT_DOMAINS.includes(tabHostname)) {
        fallbackMode = true;
      }

      if (fallbackMode != previousFallbackMode) {
        rebuildMenu();
      }
    }
  });
}

// Get user prefs. If changed since last load, run rebuildMenu()
function getOptions() {
  let gettingItem = browser.storage.local.get();
  gettingItem.then((response) => {
    let previousShowMultiOption = showMultiOption;
    let previousShowFavicons = showFavicons;

    showMultiOption = response.showMultiOption;
    showFavicons = response.showFavicons;

    if (showMultiOption == undefined) {
      showMultiOption = true;
    }

    if (showFavicons == undefined) {
      showFavicons = true;
    }

    if (showMultiOption != previousShowMultiOption || showFavicons != previousShowFavicons) {
      rebuildMenu();
    }
  });
}

// Ampersands (&) are used as modifiers for access keys in menus.
// As this is not desirable in CSO, these must be escaped as &&
function encodeAmpersand(str) {
  return str.replace(/&/g, "&&")
}

// Extract the protocol part of a URL
function getUrlProtocol(url) {
  if (url.indexOf(":") > -1) {
    return url.split(":")[0];
  }
}

// Extract the hostname part of a URL
function getUrlHostname(url) {
  if (url.indexOf("://") > -1) {
    return url.split("/")[2];
  }
}

// Set the "contexts" parameter in browser.menus.create
// If in fallback mode, only selection is allowed,
// else, all applicable contexts are allowed.
function getAllowedContexts() {
  if (!fallbackMode) {
    return ["selection", "link", "image"];
  }
  else {
    return ["selection"];
  }
}

// Error logging
function onSuccess(n) {
  if (browser.runtime.lastError) {
    console.log(`Error: ${browser.runtime.lastError}`);
  }
}

function onError(error) {
  console.log(`Error: ${error}`);
}

// Define how long a string is allowed to be before truncated.
// Used in root menu item. But only working if in fallback mode.
function truncate(val) {
  if (val.length > 20) {
    return val.substr(0, 20) + browser.i18n.getMessage("ellipsis");
  }
  else {
    return val;
  }
}

// Get ID of FOLDER_NAME and the object and pass everything through listBookmarksInTree.
// If no root folder found: Show "Getting Started" help link
function main() {
  let gettingRootFolder = browser.bookmarks.search({title: FOLDER_NAME});
  gettingRootFolder.then((bookmarks) => {
    if (bookmarks.length > 0) {
      rootFolderId = bookmarks[0].id;

      let gettingSubTree = browser.bookmarks.getSubTree(rootFolderId);
      gettingSubTree.then((bookmarkItems) => {
        if (bookmarkItems[0].children.length > 0) {
          listBookmarksInTree(bookmarkItems[0], rootFolderId);
        }
        else {
          createHelpLink();
        }
      });
    }
    else {
      createHelpLink();
    }
  });
}

// Parse through all bookmarks in tree.
// "n" adds 1 for each bookmark in node. If all bookmarks have been created,
// call on createMultiOption
function listBookmarksInTree(bookmarkItem, rootFolderId) {
  populateContextMenu(bookmarkItem.id, bookmarkItem.title, bookmarkItem.url, bookmarkItem.parentId, bookmarkItem.type, rootFolderId);

  if (bookmarkItem.children) {
    let n = 0;
    for (child of bookmarkItem.children) {
      listBookmarksInTree(child, rootFolderId);
      n ++;
    }
    if (n = bookmarkItem.children.length) {
      createMultiOption(bookmarkItem.id);
    }
  }
}

// Some URLs cannot be used as search engines. Check all bookmark URLs
// in the Searches folder if they are valid.
function checkValid(url) {
  let isValidProtocol = false, isValidWildcard = false, isValid = false;

  // Check that URL is not privileged according to
  // https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs/create
  let protocol = getUrlProtocol(url);
  if (protocol) {
    isValidProtocol = !ILLEGAL_BOOKMARK_PROTOCOLS.includes(protocol);
  }

  // Check that URL is a keyword search (i.e. containing "%s")
  if (url.indexOf("%s") > -1) {
    isValidWildcard = true;
  }

  if (isValidProtocol && isValidWildcard) {
    isValid = true;
  }
  else {
    console.warn(`Non-conforming url: ${url}. Illegal protocol or missing \"%s\".`);
  }

  return isValid;
}

function makeFavicon(url, showFavicons) {
  if (showFavicons) {
    let faviconUrl = "";

    if (url == "FOLDER") {
      faviconUrl = "icons/folder.svg"
    }
    else if (url.indexOf("://") > -1) {
      faviconUrl = "https://www.google.com/s2/favicons?domain=" + getUrlProtocol(url) + "://" + getUrlHostname(url);
    }
    return {16: faviconUrl};
  }
  else {
    return undefined;
  }
}

// Show a "Getting Started" link in the context menu if not set up properly
function createHelpLink() {
  browser.menus.create({
    id: HELP_LINK,
    title: "Context Search – " + browser.i18n.getMessage("help_linkText"),
    contexts: ["all"],
    onclick: createTab
  }, onSuccess());
}

// Make the context menu
function populateContextMenu(id, title, url, parent, type, rootFolderId) {
  if (id == rootFolderId) {
    //This is the root folder, make the title what is searched for
    browser.menus.create({
      id: rootFolderId,
      title: browser.i18n.getMessage("menu_rootLabelText", "%s"),
      contexts: getAllowedContexts()
    }, onSuccess());
  }
  else {

    if (!url) {
      // These are the folders
      browser.menus.create({
        parentId: parent,
        id: id,
        title: encodeAmpersand(title),
        icons: makeFavicon("FOLDER", showFavicons)
      }, onSuccess());
    }

    else {
      if (type == "separator") {
        // These are the separators
        browser.menus.create({
          parentId: parent,
          id: id,
          type: "separator"
        }, onSuccess());
      }

      if (url && title) {
        // These are the bookmarks
        browser.menus.create({
          parentId: parent,
          id: id + ";" + url,
          title: encodeAmpersand(title),
          icons: makeFavicon(url, showFavicons),
          enabled: checkValid(url),
          onclick: createTab
        }, onSuccess());
      }
    }

  }
}

// Create a separator and a new option to open all bookmarks at once for all folders
function createMultiOption(folderId) {
  if (showMultiOption) {
    browser.menus.create({
      parentId: folderId,
      type: "separator"
    });
    browser.menus.create({
      parentId: folderId,
      id: "folder" + folderId,
      title: browser.i18n.getMessage("menu_openMulti"),
      onclick: createMultiTab
    });
  }
}

function createMultiTab(info, parentTab) {
  let parentDir = info.parentMenuItemId;

  if (query == "%s" || fallbackMode) {
    query = info.selectionText;
  }

  let gettingSubTree = browser.bookmarks.getSubTree(parentDir);
  gettingSubTree.then((bookmarkItems) => {
    for (child of bookmarkItems[0].children) {
      if (child.type == "bookmark" && checkValid(child.url)) {
        let url = child.url.replace("%s", encodeURIComponent(query));
        goTo(url, parentTab.id, false, true);
      }
    }
  });
}

// Create the new tab with the search result.
function createTab(info, parentTab) {
  // If in fallback mode, content scripts cannot be utilized.
  // If so, make sure the selected text is used for search.
  if (query == "%s" || fallbackMode) {
    query = info.selectionText;
  }
  let bookmarkId = info.menuItemId.split(";")[0];
  let url = info.menuItemId.split(";")[1].replace("%s", encodeURIComponent(query));

  // Check which mouse button was clicked and if any modifier keys was held down.
  // button 0 = left, 1 = middle, 2 = right.
  // This code tries to mimic the default behaviors as seen on http://mzl.la/1xKrRYF

  let mouseButton = info.button;
  let modifiers = info.modifiers;

  let invertNewCurrentTab = false;
  let invertForegroundBackgroundTab = false;

  if (mouseButton == 1) {
    invertNewCurrentTab = true;
  }

  if (os = "mac") {
    if (modifiers.includes("Command")) {
      invertNewCurrentTab = true;
    }
  else
    if (modifiers.includes("Ctrl")) {
      invertNewCurrentTab = true;
    }
  }

  if (modifiers.includes("Shift")) {
    invertForegroundBackgroundTab = true;
  }

  // Check options if tab should open as active or in background
  // Replace the browser standard %s for keyword searches with
  // the selected text on the page and make a tab
  let gettingItem = browser.storage.local.get();
  gettingItem.then((response) => {

    // Get default user settings, if none set, use for default behavior
    let openInNewTab = response.openInNewTab;
    let makeTabActive = response.makeTabActive;

    if (openInNewTab == undefined) {
      openInNewTab = true;
    }
    if (makeTabActive == undefined) {
      makeTabActive = true;
    }

    // If modifiers or alternative mouse button clicked, invert settings temporarily
    if (invertNewCurrentTab) {
      openInNewTab = !openInNewTab;
    }

    if (invertForegroundBackgroundTab) {
      makeTabActive = !makeTabActive;
    }

    goTo(url, parentTab.id, makeTabActive, openInNewTab);
  });
}

function goTo(url, openerTabId, active, openInNewTab) {
  if (openInNewTab) {
    // Using openerTabId makes new tabs open as expected. If in a window with
    // toolbars hidden, trying to set an openerTabId will throw an error.
    // If an error is thrown, the new tab is created without openerTabId.
    // For usability reasons the new tab will always get active.
    let creatingTab = browser.tabs.create({
      url: url,
      active: active,
      openerTabId: openerTabId
    });
    creatingTab.catch((e) => {
      browser.tabs.create({
        url: url,
        active: true
      })
    });
  }
  else {
    browser.tabs.update({
      url: url
    });
  }
}

// Rebuild the entire menu.
function rebuildMenu() {
  browser.menus.remove(rootFolderId);
  browser.menus.remove(HELP_LINK);
  browser.menus.refresh();

  main();
}

// Take care of the message sent from query.js. When in fallback mode
// content scripts cannot be utilized. Thus this function should be ignored.
function handleQuery(response) {
  if (!fallbackMode) {
    query = response.query;
    elementType = response.elementType;

    if (!query && (elementType == "IMG" || elementType == "A")) {
      // Remove contextmenu when query is empty but
      // browser.menus will want to show it because of its contexts.
      // The menu must then be rebuilt to recieve future input
      rebuildMenu();
    }
    else if (elementType == "IMG") {
      browser.menus.update(rootFolderId, {
        title: browser.i18n.getMessage("menu_rootLabelImage")
      });
      browser.menus.refresh();
    }
    else {
      browser.menus.update(rootFolderId, {
        title: browser.i18n.getMessage("menu_rootLabelText", encodeAmpersand(truncate(query)))
      });
      browser.menus.refresh();
    }
  }
}

// Add some listeners for when bookmarks in the Searches folder is edited
browser.bookmarks.onCreated.addListener(rebuildMenu);
browser.bookmarks.onRemoved.addListener(rebuildMenu);
browser.bookmarks.onChanged.addListener(rebuildMenu);
browser.bookmarks.onMoved.addListener(rebuildMenu);

browser.tabs.onActivated.addListener(function(info) {
  activeTabId = info.tabId;
  parseTabUrl(info.tabId);
  getOptions();
});

browser.tabs.onUpdated.addListener(function(tabId, changeInfo, tabInfo) {
  // Only run this code on the active tab.
  if (changeInfo.status == "loading" && tabId == activeTabId) {
    parseTabUrl(tabId);
    getOptions();
  }
});

// Recieve messages from query.js
browser.runtime.onMessage.addListener(handleQuery);

// Parse platform info, used for modifier keys
browser.runtime.getPlatformInfo().then(parsePlatformInfo);

main();
<commit_msg>prevent rebuildMenu() from running too soon<commit_after>// Define root folder for searches
const FOLDER_NAME = "Searches";
const ILLEGAL_BOOKMARK_PROTOCOLS = ["chrome", "javascript", "data", "file", "about"];
const ILLEGAL_CONTENTSCRIPT_PROTOCOLS = ["view-source", "about", "moz-extension"];
const ILLEGAL_CONTENTSCRIPT_DOMAINS = ["accounts-static.cdn.mozilla.net", "accounts.firefox.com", "addons.cdn.mozilla.net",
                                     "addons.mozilla.org", "api.accounts.firefox.com", "content.cdn.mozilla.net", "content.cdn.mozilla.net",
                                     "discovery.addons.mozilla.org", "input.mozilla.org", "install.mozilla.org", "oauth.accounts.firefox.com",
                                     "profile.accounts.firefox.com", "support.mozilla.org", "sync.services.mozilla.com", "testpilot.firefox.com"];
const HELP_LINK = browser.extension.getURL("/help.html");

var rootFolderId = "";
var fallbackMode = false;
var query = "";
var activeTabId = 0;
var showMultiOption = false;
var showFavicons = false;
var menuRebuilt = false;

function parsePlatformInfo(info) {
  os = info.os;
}

// Check to see if the current tab supports content scripts. If not, use the
// fallback mode where only selected text can be used.
// When switching from a tab that require fallback mode to a tab that doesn't
// (and vice versa), the context menu must be rebuilt so that queries aren't
// carried over. Menu rebuilding is only done when necessary, since it is
// supposedly expensive.
function parseTabUrl(tabId) {
  let gettingTabId = browser.tabs.get(tabId);
  gettingTabId.then((response) => {
    let tabProtocol = getUrlProtocol(response.url);
    let tabHostname = getUrlHostname(response.url);

    // All new tabs start out as about:blank. By ignoring those, this code
    // isn't run unnecessarily, since onUpdated will use this function twice.
    if (response.url != "about:blank") {
      let prevFallbackMode = fallbackMode;
      fallbackMode = false;

      if (ILLEGAL_CONTENTSCRIPT_PROTOCOLS.includes(tabProtocol)) {
        fallbackMode = true;
      }

      if (!tabHostname || ILLEGAL_CONTENTSCRIPT_DOMAINS.includes(tabHostname)) {
        fallbackMode = true;
      }

      if (fallbackMode != prevFallbackMode) {
        rebuildMenu();
      }
    }
  });
}

// Get user prefs. If changed since last load, run rebuildMenu()
function getOptions() {
  let gettingItem = browser.storage.local.get();
  gettingItem.then((response) => {
    let prevShowMultiOption = showMultiOption;
    let prevShowFavicons = showFavicons;

    showMultiOption = response.showMultiOption;
    showFavicons = response.showFavicons;

    if (showMultiOption == undefined) {
      showMultiOption = true;
    }

    if (showFavicons == undefined) {
      showFavicons = true;
    }

    if (showMultiOption != prevShowMultiOption || showFavicons != prevShowFavicons) {
      rebuildMenu();
    }
  });
}

// Ampersands (&) are used as modifiers for access keys in menus.
// As this is not desirable in CSO, these must be escaped as &&
function encodeAmpersand(str) {
  return str.replace(/&/g, "&&")
}

// Extract the protocol part of a URL
function getUrlProtocol(url) {
  if (url.indexOf(":") > -1) {
    return url.split(":")[0];
  }
}

// Extract the hostname part of a URL
function getUrlHostname(url) {
  if (url.indexOf("://") > -1) {
    return url.split("/")[2];
  }
}

// Set the "contexts" parameter in browser.menus.create
// If in fallback mode, only selection is allowed,
// else, all applicable contexts are allowed.
function getAllowedContexts() {
  if (!fallbackMode) {
    return ["selection", "link", "image"];
  }
  else {
    return ["selection"];
  }
}

// Error logging
function onSuccess(n) {
  if (browser.runtime.lastError) {
    console.log(`Error: ${browser.runtime.lastError}`);
  }
}

function onError(error) {
  console.log(`Error: ${error}`);
}

// Define how long a string is allowed to be before truncated.
// Used in root menu item. But only working if in fallback mode.
function truncate(val) {
  if (val.length > 20) {
    return val.substr(0, 20) + browser.i18n.getMessage("ellipsis");
  }
  else {
    return val;
  }
}

// Get ID of FOLDER_NAME and the object and pass everything through listBookmarksInTree.
// If no root folder found: Show "Getting Started" help link
function main() {
  let gettingRootFolder = browser.bookmarks.search({title: FOLDER_NAME});
  gettingRootFolder.then((bookmarks) => {
    if (bookmarks.length > 0) {
      rootFolderId = bookmarks[0].id;

      let gettingSubTree = browser.bookmarks.getSubTree(rootFolderId);
      gettingSubTree.then((bookmarkItems) => {
        if (bookmarkItems[0].children.length > 0) {
          listBookmarksInTree(bookmarkItems[0], rootFolderId);
        }
        else {
          createHelpLink();
        }
      });
    }
    else {
      createHelpLink();
    }
  });
}

// Parse through all bookmarks in tree.
// "n" adds 1 for each bookmark in node. If all bookmarks have been created,
// call on createMultiOption
function listBookmarksInTree(bookmarkItem, rootFolderId) {
  populateContextMenu(bookmarkItem.id, bookmarkItem.title, bookmarkItem.url, bookmarkItem.parentId, bookmarkItem.type, rootFolderId);

  if (bookmarkItem.children) {
    let n = 0;
    for (child of bookmarkItem.children) {
      listBookmarksInTree(child, rootFolderId);
      n ++;
    }
    if (n = bookmarkItem.children.length) {
      createMultiOption(bookmarkItem.id);
    }
  }
}

// Some URLs cannot be used as search engines. Check all bookmark URLs
// in the Searches folder if they are valid.
function checkValid(url) {
  let isValidProtocol = false, isValidWildcard = false, isValid = false;

  // Check that URL is not privileged according to
  // https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs/create
  let protocol = getUrlProtocol(url);
  if (protocol) {
    isValidProtocol = !ILLEGAL_BOOKMARK_PROTOCOLS.includes(protocol);
  }

  // Check that URL is a keyword search (i.e. containing "%s")
  if (url.indexOf("%s") > -1) {
    isValidWildcard = true;
  }

  if (isValidProtocol && isValidWildcard) {
    isValid = true;
  }
  else {
    console.warn(`Non-conforming url: ${url}. Illegal protocol or missing \"%s\".`);
  }

  return isValid;
}

function makeFavicon(url, showFavicons) {
  if (showFavicons) {
    let faviconUrl = "";

    if (url == "FOLDER") {
      faviconUrl = "icons/folder.svg"
    }
    else if (url.indexOf("://") > -1) {
      faviconUrl = "https://www.google.com/s2/favicons?domain=" + getUrlProtocol(url) + "://" + getUrlHostname(url);
    }
    return {16: faviconUrl};
  }
  else {
    return undefined;
  }
}

// Show a "Getting Started" link in the context menu if not set up properly
function createHelpLink() {
  browser.menus.create({
    id: HELP_LINK,
    title: "Context Search – " + browser.i18n.getMessage("help_linkText"),
    contexts: ["all"],
    onclick: createTab
  }, onSuccess());
}

// Make the context menu
function populateContextMenu(id, title, url, parent, type, rootFolderId) {
  if (id == rootFolderId) {
    //This is the root folder, make the title what is searched for
    browser.menus.create({
      id: rootFolderId,
      title: browser.i18n.getMessage("menu_rootLabelText", "%s"),
      contexts: getAllowedContexts()
    }, onSuccess());
  }
  else {

    if (!url) {
      // These are the folders
      browser.menus.create({
        parentId: parent,
        id: id,
        title: encodeAmpersand(title),
        icons: makeFavicon("FOLDER", showFavicons)
      }, onSuccess());
    }

    else {
      if (type == "separator") {
        // These are the separators
        browser.menus.create({
          parentId: parent,
          id: id,
          type: "separator"
        }, onSuccess());
      }

      if (url && title) {
        // These are the bookmarks
        browser.menus.create({
          parentId: parent,
          id: id + ";" + url,
          title: encodeAmpersand(title),
          icons: makeFavicon(url, showFavicons),
          enabled: checkValid(url),
          onclick: createTab
        }, onSuccess());
      }
    }

  }
}

// Create a separator and a new option to open all bookmarks at once for all folders
function createMultiOption(folderId) {
  if (showMultiOption) {
    browser.menus.create({
      parentId: folderId,
      type: "separator"
    });
    browser.menus.create({
      parentId: folderId,
      id: "folder" + folderId,
      title: browser.i18n.getMessage("menu_openMulti"),
      onclick: createMultiTab
    });
  }
}

function createMultiTab(info, parentTab) {
  let parentDir = info.parentMenuItemId;

  if (query == "%s" || fallbackMode) {
    query = info.selectionText;
  }

  let gettingSubTree = browser.bookmarks.getSubTree(parentDir);
  gettingSubTree.then((bookmarkItems) => {
    for (child of bookmarkItems[0].children) {
      if (child.type == "bookmark" && checkValid(child.url)) {
        let url = child.url.replace("%s", encodeURIComponent(query));
        goTo(url, parentTab.id, false, true);
      }
    }
  });
}

// Create the new tab with the search result.
function createTab(info, parentTab) {
  // If in fallback mode, content scripts cannot be utilized.
  // If so, make sure the selected text is used for search.
  if (query == "%s" || fallbackMode) {
    query = info.selectionText;
  }
  let bookmarkId = info.menuItemId.split(";")[0];
  let url = info.menuItemId.split(";")[1].replace("%s", encodeURIComponent(query));

  // Check which mouse button was clicked and if any modifier keys was held down.
  // button 0 = left, 1 = middle, 2 = right.
  // This code tries to mimic the default behaviors as seen on http://mzl.la/1xKrRYF

  let mouseButton = info.button;
  let modifiers = info.modifiers;

  let invertNewCurrentTab = false;
  let invertForegroundBackgroundTab = false;

  if (mouseButton == 1) {
    invertNewCurrentTab = true;
  }

  if (os = "mac") {
    if (modifiers.includes("Command")) {
      invertNewCurrentTab = true;
    }
  else
    if (modifiers.includes("Ctrl")) {
      invertNewCurrentTab = true;
    }
  }

  if (modifiers.includes("Shift")) {
    invertForegroundBackgroundTab = true;
  }

  // Check options if tab should open as active or in background
  // Replace the browser standard %s for keyword searches with
  // the selected text on the page and make a tab
  let gettingItem = browser.storage.local.get();
  gettingItem.then((response) => {

    // Get default user settings, if none set, use for default behavior
    let openInNewTab = response.openInNewTab;
    let makeTabActive = response.makeTabActive;

    if (openInNewTab == undefined) {
      openInNewTab = true;
    }
    if (makeTabActive == undefined) {
      makeTabActive = true;
    }

    // If modifiers or alternative mouse button clicked, invert settings temporarily
    if (invertNewCurrentTab) {
      openInNewTab = !openInNewTab;
    }

    if (invertForegroundBackgroundTab) {
      makeTabActive = !makeTabActive;
    }

    goTo(url, parentTab.id, makeTabActive, openInNewTab);
  });
}

function goTo(url, openerTabId, active, openInNewTab) {
  if (openInNewTab) {
    // Using openerTabId makes new tabs open as expected. If in a window with
    // toolbars hidden, trying to set an openerTabId will throw an error.
    // If an error is thrown, the new tab is created without openerTabId.
    // For usability reasons the new tab will always get active.
    let creatingTab = browser.tabs.create({
      url: url,
      active: active,
      openerTabId: openerTabId
    });
    creatingTab.catch((e) => {
      browser.tabs.create({
        url: url,
        active: true
      })
    });
  }
  else {
    browser.tabs.update({
      url: url
    });
  }
}

// Rebuild the entire menu.
// In certain cases, like creating a new folder in the "Searches" folder,
// this function is called twice in short time (once for onCreated and once for onMoved).
// If main() isn't done before this function is called again, weird stuff happen.
// Therefore, this function isn't allowed to run again for 1000 ms.
function rebuildMenu() {
  if (!menuRebuilt) {
    menuRebuilt = true;
    browser.menus.remove(rootFolderId);
    browser.menus.remove(HELP_LINK);
    browser.menus.refresh();
    main();
  }
  resetMenuRebuilt();
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function resetMenuRebuilt() {
  await sleep(1000);
  menuRebuilt = false;
}

// Take care of the message sent from query.js. When in fallback mode
// content scripts cannot be utilized. Thus this function should be ignored.
function handleQuery(response) {
  if (!fallbackMode) {
    query = response.query;
    elementType = response.elementType;

    if (!query && (elementType == "IMG" || elementType == "A")) {
      // Remove contextmenu when query is empty but
      // browser.menus will want to show it because of its contexts.
      // The menu must then be rebuilt to recieve future input
      rebuildMenu();
    }
    else if (elementType == "IMG") {
      browser.menus.update(rootFolderId, {
        title: browser.i18n.getMessage("menu_rootLabelImage")
      });
      browser.menus.refresh();
    }
    else {
      browser.menus.update(rootFolderId, {
        title: browser.i18n.getMessage("menu_rootLabelText", encodeAmpersand(truncate(query)))
      });
      browser.menus.refresh();
    }
  }
}

// Add some listeners for when bookmarks in the Searches folder is edited
browser.bookmarks.onCreated.addListener(rebuildMenu);
browser.bookmarks.onRemoved.addListener(rebuildMenu);
browser.bookmarks.onChanged.addListener(rebuildMenu);
browser.bookmarks.onMoved.addListener(rebuildMenu);

browser.tabs.onActivated.addListener(function(info) {
  activeTabId = info.tabId;
  parseTabUrl(info.tabId);
  getOptions();
});

browser.tabs.onUpdated.addListener(function(tabId, changeInfo, tabInfo) {
  // Only run this code on the active tab.
  if (changeInfo.status == "loading" && tabId == activeTabId) {
    parseTabUrl(tabId);
    getOptions();
  }
});

// Recieve messages from query.js
browser.runtime.onMessage.addListener(handleQuery);

// Parse platform info, used for modifier keys
browser.runtime.getPlatformInfo().then(parsePlatformInfo);

main();
<|endoftext|>


<commit_before>'use strict';

var debug = true;

function log() {
  console.log.apply(console, arguments);
}

function debug() {
  if (debug) {
    console.debug.apply(console, arguments);
  }
}

function promiseChain(opts) {
  return new Promise(function(resolve, reject) {
    resolve(opts);
  })
}

function urlParser(url) {
  var el = document.createElement('a');
  el.href = url;

  return el;
}

function getTab() {
  return new Promise(function(resolve, reject) {
    chrome.tabs.getSelected(null, function(tab) {
      resolve(tab);
    });
  });
}

function getRoomFromTab(tab) {
  return getTab().then(function(tab) {
    var parser = urlParser(tab.url);

    return {
      owner: parser.pathname.split('/')[1] || null,
      name:  parser.pathname.split('/')[2] || null,
      tabId: tab.id
    };
  });
}

function roomCheckSuccess(request) {
  // TODO: Improve status checks once API provides additional data
  //
  // request.exists   - (Boolean) Does the room exist?
  // request.public   - (Boolean) Is the room publicly accessible?
  // request.joinable - (Boolean) Can the current user join the room?
  //                      false - user has perms || room is public
  //                      false - user doesn't have perms && room is private
  // request.makable  - (Boolean) Can the current user create the room?
  //                      true  - room doesn't exist && user has perms
  //                      false - room already exists || user doesn't have perms

  // 200 = room is public and exists (request.exists == true)
  // 302 = room is private           (request.isPrivate == true)
  // 401 = room does not exist       (request.exists == false)
  if(request.status === 200) {

    chrome.pageAction.setIcon({
      tabId: request.tabId,
      path: 'icon.png'
    });
    chrome.pageAction.show(request.tabId);
    // chrome.pageAction.setPopup({
    //   tabId: request.tabId,
    //   popup: 'page_action/popup.html'
    // })
  }
}

function roomCheckError(error) {
  console.error(error);
}

// room (Object)
// room.name  - name of the room (e.g. gitter)
// room.owner - User or organization that owns the room (e.g. gitterHQ)
function roomCheck(room) {
  return new Promise(function(resolve, reject) {
    var req = new XMLHttpRequest();
    req.addEventListener('load', function(){
      console.info('API resp', room.owner + room.name)
      req.tabId = room.tabId;
      resolve(req);
    }, false);
    req.addEventListener('error', reject, false);

    room.name = room.name ? '/' + room.name : '';
    var host = 'http://localhost:3000/room/';
    var url = host + room.owner + room.name;
    req.open('GET', url, true);
    req.setRequestHeader('Content-Type', 'application/json');
    console.info('API req', room.owner + room.name)
    req.send();
  });
}

function updatePageAction(tabId, tab) {
  var parser = urlParser(tab.url);

  if(parser.hostname === 'github.com' || parser.hostname === 'www.github.com') {
    // Found a GitHub page! That means there may be a room here ...

    getRoomFromTab()
      .then(roomCheck)
      .then(
        roomCheckSuccess,
        roomCheckError
      );
  } else {
    // noop - we don't care about this URL
  }
}

// -- Event handlers -----------------------------------------------------------
// The following event handlers normalize contexts in which the page action
// will need to get checked.
function onActivated(activeInfo) {
  var tabId = activeInfo.tabId;
  chrome.tabs.get(tabId, function(tab) {
    console.info('enter onActivated', tab.url);
    updatePageAction(tabId, tab);
  });
}

function onCreated(tab) {
  console.info('enter onCreated', tab.url);
  updatePageAction(tab.id, tab);
}

function onUpdated(tabId, changeInfo, tab) {
  console.info('enter onUpdate', tab.url);
  updatePageAction(tabId, tab);
}

function actionClicked() {
  // TODO: Rather than create a new tab every time, switch to the appropriate tab
  chrome.tabs.create({
    active: true,
    url: 'https://gitter.im/' + getRoomPath()
  }, function(){ /* ... */});
}

// -- Register tab event handlers ----------------------------------------------
// Re-check URL whenever the user navigates
chrome.tabs.onUpdated.addListener(onUpdated);

// Whenever the user switches to another tab, check the new tab
chrome.tabs.onActivated.addListener(onActivated);

// Cover cases where a home page is set to a Github page(Overkill?)
chrome.tabs.onCreated.addListener(onCreated);

// -- Register action event handlers -------------------------------------------
chrome.pageAction.onClicked.addListener(actionClicked);

<commit_msg>Tweak logging<commit_after>'use strict';

var enableDebug = true;

function log() {
  console.log.apply(console, arguments);
}

function debug() {
  if (enableDebug) {
    console.debug.apply(console, arguments);
  }
}

function chain(opts) {
  return new Promise(function(resolve, reject) {
    resolve(opts);
  })
}

function urlParser(url) {
  var el = document.createElement('a');
  el.href = url;

  return el;
}

function getTab() {
  return new Promise(function(resolve, reject) {
    chrome.tabs.getSelected(null, function(tab) {
      resolve(tab);
    });
  });
}

function getRoomFromTab(tab) {
  return getTab().then(function(tab) {
    var parser = urlParser(tab.url);

    return {
      owner: parser.pathname.split('/')[1] || null,
      name:  parser.pathname.split('/')[2] || null,
      tabId: tab.id
    };
  });
}

function roomCheckSuccess(request) {
  // TODO: Improve status checks once API provides additional data
  //
  // request.exists   - (Boolean) Does the room exist?
  // request.public   - (Boolean) Is the room publicly accessible?
  // request.joinable - (Boolean) Can the current user join the room?
  //                      false - user has perms || room is public
  //                      false - user doesn't have perms && room is private
  // request.makable  - (Boolean) Can the current user create the room?
  //                      true  - room doesn't exist && user has perms
  //                      false - room already exists || user doesn't have perms

  // 200 = room is public and exists (request.exists == true)
  // 302 = room is private           (request.isPrivate == true)
  // 401 = room does not exist       (request.exists == false)
  if(request.status === 200) {

    chrome.pageAction.setIcon({
      tabId: request.tabId,
      path: 'icon.png'
    });
    chrome.pageAction.show(request.tabId);
    // chrome.pageAction.setPopup({
    //   tabId: request.tabId,
    //   popup: 'page_action/popup.html'
    // })
  }
}

function roomCheckError(error) {
  console.error(error);
}

// room (Object)
// room.name  - name of the room (e.g. gitter)
// room.owner - User or organization that owns the room (e.g. gitterHQ)
function roomCheck(room) {
  return new Promise(function(resolve, reject) {
    var req = new XMLHttpRequest();
    req.addEventListener('load', function(){
      debug('API resp', room.owner + room.name)
      req.tabId = room.tabId;
      resolve(req);
    }, false);
    req.addEventListener('error', reject, false);

    room.name = room.name ? '/' + room.name : '';
    var host = 'http://localhost:3000/room/';
    var url = host + room.owner + room.name;

    req.open('GET', url, true);
    req.setRequestHeader('Content-Type', 'application/json');
    debug('API req', room.owner + room.name);
    req.send();
  });
}

function updatePageAction(tabId, tab) {
  var parser = urlParser(tab.url);

  if(parser.hostname === 'github.com' || parser.hostname === 'www.github.com') {
    // Found a GitHub page! That means there may be a room here ...

    getRoomFromTab()
      .then(roomCheck)
      .then(
        roomCheckSuccess,
        roomCheckError
      );
  } else {
    debug('END: Dropping', parser.href);
    // noop - we don't care about this URL
  }
}

// -- Event handlers -----------------------------------------------------------
// The following event handlers normalize contexts in which the page action
// will need to get checked.
function onActivated(activeInfo) {
  var tabId = activeInfo.tabId;
  chrome.tabs.get(tabId, function(tab) {
    debug('START: onActivated', tab.url);
    updatePageAction(tabId, tab);
  });
}

function onCreated(tab) {
  debug('START: onCreated', tab.url);
  updatePageAction(tab.id, tab);
}

function onUpdated(tabId, changeInfo, tab) {
  debug('START: onUpdate', tab.url);
  updatePageAction(tabId, tab);
}

function actionClicked() {
  // TODO: Rather than create a new tab every time, switch to the appropriate tab
  chrome.tabs.create({
    active: true,
    url: 'https://gitter.im/' + getRoomPath()
  }, function(){ /* ... */});
}

// -- Register tab event handlers ----------------------------------------------
// Re-check URL whenever the user navigates
chrome.tabs.onUpdated.addListener(onUpdated);

// Whenever the user switches to another tab, check the new tab
chrome.tabs.onActivated.addListener(onActivated);

// Cover cases where a home page is set to a Github page(Overkill?)
chrome.tabs.onCreated.addListener(onCreated);

// -- Register action event handlers -------------------------------------------
chrome.pageAction.onClicked.addListener(actionClicked);

<|endoftext|>


<commit_before>/* global browser */

"use strict";

function parseReceivedHeader(headerStr, regexp) {
    const captured = headerStr.match(new RegExp(regexp));
    if (captured === null) return null;
    captured.shift();
    // If the capturing group is optional ("* "or "?" quantifier) and didn’t match, its value is set to undefined.
    const filtered = captured.filter(function (element) {
        return typeof element !== "undefined";
    });
    if (!filtered.length) return null;
    return filtered;
}

function parseReceivedHeaders(headers, regexp) {
    const received = [];
    headers.forEach(function (header) {
        const parsed = parseReceivedHeader(header, regexp);
        if (parsed !== null)
            received.push(parsed);
    });
    return received;
}

browser.storage.local.get(["headerNumbers", "regexp"]).then(({headerNumbers, regexp}) => {
    if (!headerNumbers) browser.storage.local.set({headerNumbers: ""});
    if (!regexp) browser.storage.local.set({regexp: "(.*)"});
});

function displayReceivedHeader(tabId, messageId) {
    browser.messages.getFull(messageId).then((messagepart) => {
        browser.storage.local.get(["headerNumbers", "regexp"]).then(({headerNumbers, regexp}) => {
            const headers = [];
            headerNumbers.split(",").map((item) => parseInt(item, 10)).forEach((i) => {
                const header = messagepart.headers.received[i];
                if (typeof header !== "undefined") headers.push(header);
            });
            if (headers.length) {
                const parsed = parseReceivedHeaders(headers, regexp);
                browser.displayReceivedHeader.setReceivedHeaderValue(tabId, parsed);
                browser.displayReceivedHeader.setReceivedHeaderHidden(tabId, !parsed.length);
            } else {
                browser.displayReceivedHeader.setReceivedHeaderHidden(tabId, true);
            }
        });
    });
}

browser.windows.getAll({populate: true, windowTypes: ["normal", "messageDisplay"]}).then((windows) => {
    windows.forEach(function (window) {
        browser.displayReceivedHeader.addHeadersToWindowById(window.id);
        window.tabs.filter((tab) => tab.active)
            .forEach(function (tab) {
                browser.messageDisplay.getDisplayedMessage(tab.id).then((message) => {
                    if (!message) return;
                    displayReceivedHeader(tab.id, message.id);
                });
            });
    });
});

browser.windows.onCreated.addListener((window) => {
    // Skip popup, devtools, etc.
    if (window.type !== "normal" && window.type !== "messageDisplay") return;
    browser.displayReceivedHeader.addHeadersToWindowById(window.id);
});

browser.messageDisplay.onMessageDisplayed.addListener((tab, message) => {
    displayReceivedHeader(tab.id, message.id);
});
<commit_msg>Parse all headers if header number list is empty<commit_after>/* global browser */

"use strict";

function parseReceivedHeader(headerStr, regexp) {
    const captured = headerStr.match(new RegExp(regexp));
    if (captured === null) return null;
    captured.shift();
    // If the capturing group is optional ("* "or "?" quantifier) and didn’t match, its value is set to undefined.
    const filtered = captured.filter(function (element) {
        return typeof element !== "undefined";
    });
    if (!filtered.length) return null;
    return filtered;
}

function parseReceivedHeaders(headers, regexp) {
    const received = [];
    headers.forEach(function (header) {
        const parsed = parseReceivedHeader(header, regexp);
        if (parsed !== null)
            received.push(parsed);
    });
    return received;
}

browser.storage.local.get(["headerNumbers", "regexp"]).then(({headerNumbers, regexp}) => {
    if (!headerNumbers) browser.storage.local.set({headerNumbers: ""});
    if (!regexp) browser.storage.local.set({regexp: "(.*)"});
});

function displayReceivedHeader(tabId, messageId) {
    browser.messages.getFull(messageId).then((messagepart) => {
        browser.storage.local.get(["headerNumbers", "regexp"]).then(({headerNumbers, regexp}) => {
            let headers = [];
            let numberFound = false;
            headerNumbers.split(",").map((item) => parseInt(item, 10)).forEach((i) => {
                if (!isNaN(i)) numberFound = true;
                const header = messagepart.headers.received[i];
                if (typeof header !== "undefined") headers.push(header);
            });
            if (!numberFound) headers = messagepart.headers.received;
            if (headers.length) {
                const parsed = parseReceivedHeaders(headers, regexp);
                browser.displayReceivedHeader.setReceivedHeaderValue(tabId, parsed);
                browser.displayReceivedHeader.setReceivedHeaderHidden(tabId, !parsed.length);
            } else {
                browser.displayReceivedHeader.setReceivedHeaderHidden(tabId, true);
            }
        });
    });
}

browser.windows.getAll({populate: true, windowTypes: ["normal", "messageDisplay"]}).then((windows) => {
    windows.forEach(function (window) {
        browser.displayReceivedHeader.addHeadersToWindowById(window.id);
        window.tabs.filter((tab) => tab.active)
            .forEach(function (tab) {
                browser.messageDisplay.getDisplayedMessage(tab.id).then((message) => {
                    if (!message) return;
                    displayReceivedHeader(tab.id, message.id);
                });
            });
    });
});

browser.windows.onCreated.addListener((window) => {
    // Skip popup, devtools, etc.
    if (window.type !== "normal" && window.type !== "messageDisplay") return;
    browser.displayReceivedHeader.addHeadersToWindowById(window.id);
});

browser.messageDisplay.onMessageDisplayed.addListener((tab, message) => {
    displayReceivedHeader(tab.id, message.id);
});
<|endoftext|>


<commit_before>
define([
     "dojo/_base/declare",
     "dojo/topic",
     "dojo/aspect",
     "hotplate/hotDojoStores/stores",
     "hotplate/hotMessages/messages",

     "dojo/domReady!",
   ], function(
   declare
   , topic
   , aspect
   , stores
   , messages
 ){

  // CHANGES WITHIN
  //
  // For each "put" and "remove" in the store, publish a message locally
  // about the store being changed
  // Basically, after a put() or a remove(), it will publish a topic (storeUpdate, storeCreate or storeErase )
  // so that other parts of the application will be able to listen up.
  // NOTE: The _tabId is added to each request, so that the server will know not to broadcast the change
  //       here (since it was a local one, no point in knowing about it twice).
  //
  console.log("I AM HEREEEEEEEEEEEEEEEE");
  topic.subscribe( 'hotplate/hotDojoStores/newStore', function( storeName, store ){

    console.log("Enhancing " + storeName );
    // Before anything else, create _tabId so that the JsonRest store will receive
    // each time, and will know which tab NOT to notify of the change
    aspect.before( store, 'put', function( object, options ){
      console.log("ADDING TAB ID:");
      console.log(object);
      console.log(messages);
      console.log(messages.tabId);
      object._tabId = messages.tabId;
      OO = object;
      console.log(object);
      return [ object, options ];
    });

    // But before storing it in the local cache, delete it
    aspect.before( store.memCache, 'put', function( object, options ){
      delete object._tabId;
      return [ object, options ];
    });

    // After making a change to the cache, well, the main store has definitely
    // worked, so it's time for an app-wide message
    aspect.after( store.memCache, 'put', function( object, options ){
      var eventName;
      var identity = store.idProperty;
      eventName = object[identity] ? 'storeUpdate' : 'storeCreate';

      if( ! options || ( options && ! options.dontPublish ) ) {
        topic.publish(eventName, null, { storeName: storeName, objectId: object[identity], object: object, remote: false }  );
      }
    }, true );

    // After making a change to the cache, well, the main store has definitely
    // worked, so it's time for an app-wide message
    aspect.after( store.memCache, 'add', function( object, options ){
      var eventName;
      eventName = 'storeCreate';

      if( ! options || ( options && ! options.dontPublish ) ) {
        topic.publish(eventName, null, { storeName: storeName, object: object, remote: false }  );
      }
    }, true );

    aspect.after( store.memCache, 'remove', function( objectId, options ){
      var identity = store.idProperty;
      if( ! options || ( options && ! options.dontPublish ) ) {
        topic.publish('storeRemove', null, { storeName: storeName, objectId: objectId, remote: false }  );
      }
    }, true );
  });

  // CHANGES FROM THE OUTSIDE
  //
  // Update local stores according to Comet messages
  // NOTE: The comet message will come from the server after a put() (that is, the server will queue it),
  //       at which point the local cache will need to be updated with the new values.
  //       New topics (which will guarantee that the cache is in a good state at time emission) wll also
  //       be published

  topic.subscribe('storeUpdate', function( from, message){
    // A Comet changed a remote store. Refresh the Something came from a Comet.
    if( message.remote ){

      // Inject the data into the store's cache, run a notify on the main store,
      // publish an afterCache event
      var store = stores(message.storeName);
      store.memCache.put( message.object, { dontPublish: true } );
      store.notify( message.object, message.object[store.idProperty]);
      topic.publish('storeUpdate/afterCache', from, message);
    }
  });

  topic.subscribe('storeCreate', function( from, message ){
    // A Comet changed a remote store. Refresh the Something came from a Comet.
    if( message.remote ){

      // Inject the data into the store's cache, run a notify on the main store,
      // publish an afterCache event
      var store = stores(message.storeName);
      store.memCache.put( message.object, { dontPublish: true } );
      store.noCache.notify( message.object, null );
      topic.publish('storeCreate/afterCache', from, message);
    }
  });

  topic.subscribe('storeRemove', function( from, message){
    // A Comet changed a remote store. Refresh the Something came from a Comet.
    if( message.remote ){

      // Inject the data into the store's cache, run a notify on the main store,
      // publish an afterCache event
      var store = stores(message.storeName);
      store.memCache.remove( message.objectId, { dontPublish: true } );
      store.noCache.notify( null, message.objectId );
      topic.publish('storeRemove/afterCache', from, message);
    }
  });

  return {};

});

<commit_msg>Deleted silly debug messages from AppStoreNotify<commit_after>
define([
     "dojo/_base/declare",
     "dojo/topic",
     "dojo/aspect",
     "hotplate/hotDojoStores/stores",
     "hotplate/hotMessages/messages",

     "dojo/domReady!",
   ], function(
   declare
   , topic
   , aspect
   , stores
   , messages
 ){

  // CHANGES WITHIN
  //
  // For each "put" and "remove" in the store, publish a message locally
  // about the store being changed
  // Basically, after a put() or a remove(), it will publish a topic (storeUpdate, storeCreate or storeErase )
  // so that other parts of the application will be able to listen up.
  // NOTE: The _tabId is added to each request, so that the server will know not to broadcast the change
  //       here (since it was a local one, no point in knowing about it twice).
  //

  topic.subscribe( 'hotplate/hotDojoStores/newStore', function( storeName, store ){

    // Before anything else, create _tabId so that the JsonRest store will receive
    // each time, and will know which tab NOT to notify of the change
    aspect.before( store, 'put', function( object, options ){
      object._tabId = messages.tabId;
      return [ object, options ];
    });

    // But before storing it in the local cache, delete it
    aspect.before( store.memCache, 'put', function( object, options ){
      delete object._tabId;
      return [ object, options ];
    });

    // After making a change to the cache, well, the main store has definitely
    // worked, so it's time for an app-wide message
    aspect.after( store.memCache, 'put', function( object, options ){
      var eventName;
      var identity = store.idProperty;
      eventName = object[identity] ? 'storeUpdate' : 'storeCreate';

      if( ! options || ( options && ! options.dontPublish ) ) {
        topic.publish(eventName, null, { storeName: storeName, objectId: object[identity], object: object, remote: false }  );
      }
    }, true );

    // After making a change to the cache, well, the main store has definitely
    // worked, so it's time for an app-wide message
    aspect.after( store.memCache, 'add', function( object, options ){
      var eventName;
      eventName = 'storeCreate';

      if( ! options || ( options && ! options.dontPublish ) ) {
        topic.publish(eventName, null, { storeName: storeName, object: object, remote: false }  );
      }
    }, true );

    aspect.after( store.memCache, 'remove', function( objectId, options ){
      var identity = store.idProperty;
      if( ! options || ( options && ! options.dontPublish ) ) {
        topic.publish('storeRemove', null, { storeName: storeName, objectId: objectId, remote: false }  );
      }
    }, true );
  });

  // CHANGES FROM THE OUTSIDE
  //
  // Update local stores according to Comet messages
  // NOTE: The comet message will come from the server after a put() (that is, the server will queue it),
  //       at which point the local cache will need to be updated with the new values.
  //       New topics (which will guarantee that the cache is in a good state at time emission) wll also
  //       be published

  topic.subscribe('storeUpdate', function( from, message){
    // A Comet changed a remote store. Refresh the Something came from a Comet.
    if( message.remote ){

      // Inject the data into the store's cache, run a notify on the main store,
      // publish an afterCache event
      var store = stores(message.storeName);
      store.memCache.put( message.object, { dontPublish: true } );
      store.notify( message.object, message.object[store.idProperty]);
      topic.publish('storeUpdate/afterCache', from, message);
    }
  });

  topic.subscribe('storeCreate', function( from, message ){
    // A Comet changed a remote store. Refresh the Something came from a Comet.
    if( message.remote ){

      // Inject the data into the store's cache, run a notify on the main store,
      // publish an afterCache event
      var store = stores(message.storeName);
      store.memCache.put( message.object, { dontPublish: true } );
      store.noCache.notify( message.object, null );
      topic.publish('storeCreate/afterCache', from, message);
    }
  });

  topic.subscribe('storeRemove', function( from, message){
    // A Comet changed a remote store. Refresh the Something came from a Comet.
    if( message.remote ){

      // Inject the data into the store's cache, run a notify on the main store,
      // publish an afterCache event
      var store = stores(message.storeName);
      store.memCache.remove( message.objectId, { dontPublish: true } );
      store.noCache.notify( null, message.objectId );
      topic.publish('storeRemove/afterCache', from, message);
    }
  });

  return {};

});

<|endoftext|>


<commit_before>var Delegator = require('dom-delegator')

module.exports = BaseEvent

function BaseEvent(lambda) {
    return EventHandler;

    function EventHandler(fn, data, opts) {
        var handler = {
            fn: fn,
            data: data || {},
            opts: opts || {},
            handleEvent: handleEvent
        }

        if (fn && fn.type === 'dom-delegator-handle') {
            return Delegator.transformHandle(fn,
                handleLambda.bind(handler))
        }

        return handler;
    }

    function handleLambda(ev, broadcast) {
        if (this.opts.startPropagation && ev.startPropagation) {
            ev.startPropagation();
        }

        return lambda.call(this, ev, broadcast)
    }

    function handleEvent(ev) {
        var self = this

        if (self.opts.startPropagation && ev.startPropagation) {
            ev.startPropagation()
        }

        lambda.call(self, ev, broadcast)

        function broadcast(value) {
            if (typeof self.fn === 'function') {
                self.fn(value)
            } else {
                self.fn.write(value)
            }
        }
    }
}
<commit_msg>Ensure that when data is provided "falsey" data values are passed through<commit_after>var Delegator = require('dom-delegator')

module.exports = BaseEvent

function BaseEvent(lambda) {
    return EventHandler;

    function EventHandler(fn, data, opts) {
        var handler = {
            fn: fn,
            data: typeof data != 'undefined' ? data : {},
            opts: opts || {},
            handleEvent: handleEvent
        }

        if (fn && fn.type === 'dom-delegator-handle') {
            return Delegator.transformHandle(fn,
                handleLambda.bind(handler))
        }

        return handler;
    }

    function handleLambda(ev, broadcast) {
        if (this.opts.startPropagation && ev.startPropagation) {
            ev.startPropagation();
        }

        return lambda.call(this, ev, broadcast)
    }

    function handleEvent(ev) {
        var self = this

        if (self.opts.startPropagation && ev.startPropagation) {
            ev.startPropagation()
        }

        lambda.call(self, ev, broadcast)

        function broadcast(value) {
            if (typeof self.fn === 'function') {
                self.fn(value)
            } else {
                self.fn.write(value)
            }
        }
    }
}
<|endoftext|>


<commit_before>let main = function() {
  const nickname = ['Drumpf', 'Agent Orange', 'Pumpkin', 'Putin Lackey', 'Tiny Fingers'];
  const adverbs = ['yugely', 'bigly', 'tremendously', 'covfefe'];
  let drumpf = /Trump/g, adverb = /\b[A-Za-z]*ly\b/g;
  let length1 = nickname.length, length2 = adverbs.length;
  let isDrumpfed = false;
  let corrections = 0;
  let elements = document.getElementsByTagName('*');

  for(let i = elements.length; i--;) {
    let element = elements[i];
    for(let j = element.childNodes.length; j--;) {
      let node = element.childNodes[j];
      if (node.nodeType === 3) {
        let text = node.nodeValue;
        if(isDrumpfed || text.match(drumpf)) {
          let replacedText = text
            .replace(drumpf, () => {
              corrections++;
              return nickname[Math.floor(Math.random() * length1)];
            })
            .replace(adverb, () => {
              corrections++;
              return adverbs[Math.floor(Math.random() * length2)];
            });
          if(replacedText !== text) {
            element.replaceChild(document.createTextNode(replacedText), node);
            if(!isDrumpfed) {
              isDrumpfed = true;
            }
          }
        }
      }
    }
  }
  chrome.runtime.sendMessage({corrections: corrections}, () => {});
};

setTimeout(main, 200);
<commit_msg>changed duration<commit_after>let main = function() {
  const nickname = ['Drumpf', 'Agent Orange', 'Pumpkin', 'Putin Lackey', 'Tiny Fingers'];
  const adverbs = ['yugely', 'bigly', 'tremendously', 'covfefe'];
  let drumpf = /Trump/g, adverb = /\b[A-Za-z]*ly\b/g;
  let length1 = nickname.length, length2 = adverbs.length;
  let isDrumpfed = false;
  let corrections = 0;
  let elements = document.getElementsByTagName('*');

  for(let i = elements.length; i--;) {
    let element = elements[i];
    for(let j = element.childNodes.length; j--;) {
      let node = element.childNodes[j];
      if (node.nodeType === 3) {
        let text = node.nodeValue;
        if(isDrumpfed || text.match(drumpf)) {
          let replacedText = text
            .replace(drumpf, () => {
              corrections++;
              return nickname[Math.floor(Math.random() * length1)];
            })
            .replace(adverb, () => {
              corrections++;
              return adverbs[Math.floor(Math.random() * length2)];
            });
          if(replacedText !== text) {
            element.replaceChild(document.createTextNode(replacedText), node);
            if(!isDrumpfed) {
              isDrumpfed = true;
            }
          }
        }
      }
    }
  }
  chrome.runtime.sendMessage({corrections: corrections}, () => {});
};

//add delay, in case content is rendered later by scripts - I'm talking to you CNN!
setTimeout(main, 500);
<|endoftext|>