<commit_before>const APP_PORT = 8081;
//var JQUERY_JS = "jquery-2.1.4.min.js";
var JQUERY_JS = "jquery-3.2.1.min.js";
var PAGE_JS = "page.js";
var apiUrl = 'http://localhost:' + APP_PORT + '/';

var pageErrors = [];
var networkStats = [];
var activeFrameId = 0;
var contentScriptCode = '';

chrome.runtime.onMessage.addListener(pageErrorsListener);

getFileContents(JQUERY_JS, function (contents1) {
    getFileContents(PAGE_JS, function (content2) {
        contentScriptCode = contents1 + content2;
        setupSocketClient(apiUrl);
    });
});

function setupSocketClient(apiUrl) {
    console.log('Initializing websocket client ...');

    var socket = io.connect(apiUrl);

    socket.on('cmd', function (data) {
        console.log('cmd: ' + data.cmd);
        data.frameId = activeFrameId;
        runActionInBrowser(socket, data);
    });

    // extension is started and ready - notify the server
    socket.emit('chrome_ready');
}

function getFileContents(filename, callback) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', chrome.extension.getURL(filename), true);
    xhr.onreadystatechange = function () {
        if (xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) {
            callback(xhr.responseText);
        }
    };
    xhr.send();
}

function runActionInBrowser(socket, data) {
    switch (data.cmd) {
        case "get_views_info":
            var retval = [];
            chrome.windows.getAll({}, function (windows) {
                var remainingWindows = windows.length;

                windows.forEach(function (window) {
                    console.log('chrome.windows.getAll - wid=', window.id);

                    chrome.tabs.query({ windowId: window.id }, function (tabs) {
                        remainingWindows--;
                        tabs.forEach(function (tab) {
                            var viewInfo = {
                                id: tab.id,
                                index: tab.index,
                                isActive: tab.active,
                                url: tab.url, title:
                                    tab.title,
                                windowId: window.id,
                                windowType: window.type,
                                top: window.top,
                                left: window.left,
                                width: window.width,
                                height: window.height,
                                state: window.state
                            };
                            retval.push(viewInfo);
                        });

                        if (remainingWindows === 0) {
                            // we are done!
                            data.retVal = retval;
                            socket.emit('cmd_out', data);
                            console.log('--' + data.cmd + ' done:', data);
                        }
                    });
                });
            });

            break;

        case "set_active_view":
            chrome.tabs.update(data.tabId, { active: true }, function (tabs) {

                if (chrome.runtime.lastError) {
                    console.log('--' + data.cmd + ' error');
                    data.error_code = 500;
                    data.error_message = chrome.runtime.lastError.message;
                    socket.emit('cmd_out', data);
                    return;
                }

                socket.emit('cmd_out', data);
                console.log('--' + data.cmd + ' done:', data);
            });
            break;

        case "close_view":
            chrome.tabs.remove(data.tabId, function (tabs) {

                if (chrome.runtime.lastError) {
                    console.log('--' + data.cmd + ' error');
                    data.error_code = 500;
                    data.error_message = chrome.runtime.lastError.message;
                    socket.emit('cmd_out', data);
                    return;
                }

                socket.emit('cmd_out', data);
                console.log('--' + data.cmd + ' done:', data);
            });
            break;

        case "set_views_info":
            chrome.windows.getCurrent(function (wind) {
                var updateInfo = {};
                // data is passed as string, we cannot use || since a top/left position of (0,0) should be supported
                if (data.top)
                    updateInfo.top = parseInt(data.top)
                if (data.left)
                    updateInfo.left = parseInt(data.left)
                if (data.width)
                    updateInfo.width = parseInt(data.width)
                if (data.height)
                    updateInfo.height = parseInt(data.height)
                if (data.state)
                    updateInfo.state = data.state

                chrome.windows.update(wind.id, updateInfo, function (window) {
                    socket.emit('cmd_out', data);
                });
            });
            break;

        case "get_errors":
            data.retVal = pageErrors;
            socket.emit('cmd_out', data);
            console.log('--' + data.cmd + ' done:', data);
            break;

        case "clear_errors":
            pageErrors = [];
            socket.emit('cmd_out', data);
            console.log('--' + data.cmd + ' done:', data);
            break;

        case "pause":
            handlePause(socket, data);
            break;

        default:
            // runs all other actions in the current window & tab
            findActiveTabAndRunAction(socket, data);
    }
}

function handlePause(socket, data) {
    setExtensionIcon(true);
    chrome.browserAction.onClicked.addListener(function (tab) {
        socket.emit('cmd_out', data);
        setExtensionIcon();
    });
}

function findActiveTabAndRunAction(socket, data) {
    chrome.windows.getCurrent({}, function (window) {
        try {
            chrome.tabs.query({ active: true }, function (tabs) {
                tab = tabs[0]; // only work with the active tab - and there is only one active tab!
                runActionInActivePage(socket, tab, data);
            });
        }
        catch (err) {
            console.log('-- ERROR: ' + err.message);
            data.error_code = 500;
            data.error_message = err.message;
            socket.emit('cmd_out', data);
        }
    });
}

function runActionInActivePage(socket, tab, data) {
    if (tab === undefined) {
        console.log('--' + data.cmd + ' error');
        data.error_code = 501;
        data.error_message = 'ERROR: Unable to find the active tab';
        socket.emit('cmd_out', data);
        return;
    }

    switch (data.cmd) {
        case "get_url":
            data.retVal = tab.url;
            socket.emit('cmd_out', data);
            console.log('--' + data.cmd + ' done');
            break;

        case "set_url":
            function tabUpdatedListener(tabId, changeInfo, tab) {
                console.log('--status=' + changeInfo.status + ' url=' + tab.url);
                if (changeInfo.status == 'complete' && tab.url !== 'about:blank') { /* timing issue where about:blank will be 'complete' before socket connection occurs before the about:blank tab is created*/
                    chrome.tabs.onUpdated.removeListener(tabUpdatedListener);
                    socket.emit('cmd_out', data);
                    console.log('--' + data.cmd + ' done');
                }
            }

            chrome.tabs.onUpdated.addListener(tabUpdatedListener);

            // updates the url - response will be sent after the page load is complete - see above
            chrome.tabs.update(tab.id, { url: data.value });
            break;

        case "screenshot":
            chrome.tabs.captureVisibleTab(tab.windowId, { format: "jpeg", quality: 70 }, function (dataUrl) {
                data.retVal = dataUrl;
                socket.emit('cmd_out', data);
                console.log('--' + data.cmd + ' done');
            });
            break;

        case "enable_network_stats":
            parseIncomingNetworkMessage();
            socket.emit('cmd_out', data);
            break;

        case "get_network_stats":
            data.retVal = networkStats;
            socket.emit('cmd_out', data);
            break;

        case "fullpage_screenshot":
            var tabId = tab.id;
            var debuggeeId = { tabId: tabId };

            attachOrReuseDebugger(debuggeeId, function () {
                chrome.debugger.sendCommand(debuggeeId, "Page.getLayoutMetrics", function (metrics) {
                    const widthIncreaseFactor = data.widthIncreaseFactor || 1;
                    const heightIncreaseFactor = data.heightIncreaseFactor || 1;
                    const width = widthIncreaseFactor * Math.ceil(metrics.contentSize.width);
                    const height = heightIncreaseFactor * Math.ceil(metrics.contentSize.height);

                    chrome.debugger.sendCommand(debuggeeId, "Emulation.setDeviceMetricsOverride", {
                        width: width,
                        height: height,
                        deviceScaleFactor: 1,
                        mobile: false,
                        dontSetVisibleSize: false
                    }, function () {
                        /*chrome.debugger.sendCommand(debuggeeId, "Emulation.setVisibleSize", {
                            width: width,
                            height: height,
                        }, function () {
                        */
                        setTimeout(async function () {
                            chrome.debugger.sendCommand(debuggeeId, "Page.captureScreenshot", { format: "png", quality: 100 },
                                function (result) {
                                    data.retVal = result;
                                    socket.emit('cmd_out', data);
                                });
                        }, 1000); /* enough time for chrome to process the changes (even though we are running in a callback!!?!) */
                    });
                });
            });
            break;

        case "switch_frame":
            var urlFragment = data.value;
            chrome.webNavigation.getAllFrames({ tabId: tab.id }, function (framesInfo) {
                for (let { frameId, url } of framesInfo) {
                    if (url.includes(urlFragment)) {
                        activeFrameId = frameId;
                        socket.emit('cmd_out', data);
                        return;
                    }
                }

                data.error_code = 404;
                data.error_message = 'Frame with path "' + urlFragment + '" not found';
                socket.emit('cmd_out', data);
            });
            break;

        case "reset_frame":
            activeFrameId = 0;
            socket.emit('cmd_out', data);
            break;

        case "close_active_view":
            chrome.tabs.remove(tab.id, function (tabs) {

                if (chrome.runtime.lastError) {
                    console.log('--' + data.cmd + ' error');
                    data.error_code = 500;
                    data.error_message = chrome.runtime.lastError.message;
                    socket.emit('cmd_out', data);
                    return;
                }

                socket.emit('cmd_out', data);
            });
            break;

        default:
            sendMessageIntoTab(tab.id, data, function (response) {
                console.log('response:', JSON.stringify(response));

                if (typeof response == "undefined") {
                    setTimeout(function () {
                        console.log(`___ delay restart send message - tabId=${tab.id}, frameId=${data.frameId}`);
                        // try again, case when page moved
                        findActiveTabAndRunAction(socket, data);
                    }, 500);
                }
                else {
                    if (response.data.error_code)
                        console.log('error response received from page: ' + response.data.error_message);

                    socket.emit('cmd_out', response.data);
                    console.log('--' + data.cmd + ' done');
                }
            });
            break;
    }
}

// sends the message into the page - only after having checked that the dependencies are injected
function sendMessageIntoTab(tabId, data, callback) {
    var details = { tabId: tabId, frameId: data.frameId };
    var dependency_key = `${tabId}-${data.frameId}`;

    console.log(`-- sendMessageIntoTab - ${tabId} - ${JSON.stringify(data)}`);
    chrome.webNavigation.getFrame(details, function (info) {
        console.log(`-- getFrame - ${JSON.stringify(info)}`);
        chrome.tabs.sendMessage(
            tabId,
            { data: data },
            { frameId: data.frameId },
            function (response) {
                if (response) {
                    console.log(`-- chrome.tabs.sendMessage-RESPONSE - ${JSON.stringify(response)}`);
                    callback(response);
                }
                else {
                    console.log(`-- chrome.tabs.sendMessage-ELSE-1`);
                    chrome.tabs.executeScript(tabId, { code: contentScriptCode, allFrames: true, frameId: data.frameId }, function (resp) {
                        console.log(`-- chrome.tabs.sendMessage-ELSE-2`);
                        callback(); // after dependencies are injected, this will invoke callback() with no params, which triggers a retry
                    }
                    );
                }
            });
    });
}

function pageErrorsListener(request) {
    if (request.cmd === 'page_error') {
        console.log('errorData:', request.data);
        pageErrors.push(request.data);
    }
}

function setExtensionIcon(isPauseIcon) {
    let iconPath = 'default.png';
    if (isPauseIcon)
        iconPath = 'play4.png';

    chrome.browserAction.setIcon({ path: iconPath });
}

function attachOrReuseDebugger(debuggeeId, callback) {
    var version = "1.0";

    chrome.debugger.getTargets(function (targets) {
        const alreadyAttached = targets.filter(({ attached, tabId }) => attached && tabId === debuggeeId.tabId).length > 0;
        if (alreadyAttached)
            callback();

        else
            chrome.debugger.attach(debuggeeId, version, callback);
    });
}

function parseIncomingNetworkMessage() {
    let pending = new Map(); // map[requestId, {requestData}]

    var tabId = tab.id;
    var debuggeeId = { tabId: tabId };

    let round2 = (nb) => Math.round(nb * 1e2) / 1e2;

    attachOrReuseDebugger(debuggeeId, function () {
        chrome.debugger.sendCommand(debuggeeId, "Network.enable");
        chrome.debugger.onEvent.addListener(function (debuggeeId, message, params) {
            if (message === 'Network.requestWillBeSent') {
                pending.set(params.requestId, {
                    startTime: params.timestamp,
                    method: params.request.method
                });

            } else if (message === 'Network.responseReceived') {
                var requestData = pending.get(params.requestId);
                if (requestData === undefined) /* case we receive events that started before the capturing started */
                    return;

                if (params.response.protocol === "data")
                    // when protocol== data, the url is the data itself (this polutes the logs)
                    requestData.url = "data:[...]"

                else
                    requestData.url = params.response.url;

                var chromeTiming = params.response.timing;
                var timing = {};
                if (chromeTiming) {
                    timing.dns = round2(chromeTiming.dnsEnd - chromeTiming.dnsStart);
                    timing.initialConnection = round2(chromeTiming.connectEnd - chromeTiming.connectStart);
                    timing.ssl = round2(chromeTiming.sslEnd - chromeTiming.sslStart);
                    timing.requestSent = round2(chromeTiming.sendEnd - chromeTiming.sendStart);
                }

                requestData.type = params.response.mimeType;
                requestData.status = params.response.status;
                requestData.timing = timing;
                if (params.response.status !== 200)
                    requestData.statusText = params.response.statusText;

            } else if (message === 'Network.loadingFinished') {
                var requestData = pending.get(params.requestId);
                if (requestData === undefined) /* case we receive events that started before the capturing started */
                    return;

                requestData.length = params.encodedDataLength;
                requestData.totalTime = round2(1000 * (params.timestamp - requestData.startTime));

                delete requestData.startTime;

                networkStats.push(requestData);
                pending.delete(params.requestId);
                //console.log(`[${requestData.url}] ${requestData.totalTime}ms ${requestData.status}`);
            }
        });
    });
}
<commit_msg>- runat document_start<commit_after>const APP_PORT = 8081;
//var JQUERY_JS = "jquery-2.1.4.min.js";
var JQUERY_JS = "jquery-3.2.1.min.js";
var PAGE_JS = "page.js";
var apiUrl = 'http://localhost:' + APP_PORT + '/';

var pageErrors = [];
var networkStats = [];
var activeFrameId = 0;
var contentScriptCode = '';

chrome.runtime.onMessage.addListener(pageErrorsListener);

getFileContents(JQUERY_JS, function (contents1) {
    getFileContents(PAGE_JS, function (content2) {
        contentScriptCode = contents1 + content2;
        setupSocketClient(apiUrl);
    });
});

function setupSocketClient(apiUrl) {
    console.log('Initializing websocket client ...');

    var socket = io.connect(apiUrl);

    socket.on('cmd', function (data) {
        console.log('cmd: ' + data.cmd);
        data.frameId = activeFrameId;
        runActionInBrowser(socket, data);
    });

    // extension is started and ready - notify the server
    socket.emit('chrome_ready');
}

function getFileContents(filename, callback) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', chrome.extension.getURL(filename), true);
    xhr.onreadystatechange = function () {
        if (xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) {
            callback(xhr.responseText);
        }
    };
    xhr.send();
}

function runActionInBrowser(socket, data) {
    switch (data.cmd) {
        case "get_views_info":
            var retval = [];
            chrome.windows.getAll({}, function (windows) {
                var remainingWindows = windows.length;

                windows.forEach(function (window) {
                    console.log('chrome.windows.getAll - wid=', window.id);

                    chrome.tabs.query({ windowId: window.id }, function (tabs) {
                        remainingWindows--;
                        tabs.forEach(function (tab) {
                            var viewInfo = {
                                id: tab.id,
                                index: tab.index,
                                isActive: tab.active,
                                url: tab.url, title:
                                    tab.title,
                                windowId: window.id,
                                windowType: window.type,
                                top: window.top,
                                left: window.left,
                                width: window.width,
                                height: window.height,
                                state: window.state
                            };
                            retval.push(viewInfo);
                        });

                        if (remainingWindows === 0) {
                            // we are done!
                            data.retVal = retval;
                            socket.emit('cmd_out', data);
                            console.log('--' + data.cmd + ' done:', data);
                        }
                    });
                });
            });

            break;

        case "set_active_view":
            chrome.tabs.update(data.tabId, { active: true }, function (tabs) {

                if (chrome.runtime.lastError) {
                    console.log('--' + data.cmd + ' error');
                    data.error_code = 500;
                    data.error_message = chrome.runtime.lastError.message;
                    socket.emit('cmd_out', data);
                    return;
                }

                socket.emit('cmd_out', data);
                console.log('--' + data.cmd + ' done:', data);
            });
            break;

        case "close_view":
            chrome.tabs.remove(data.tabId, function (tabs) {

                if (chrome.runtime.lastError) {
                    console.log('--' + data.cmd + ' error');
                    data.error_code = 500;
                    data.error_message = chrome.runtime.lastError.message;
                    socket.emit('cmd_out', data);
                    return;
                }

                socket.emit('cmd_out', data);
                console.log('--' + data.cmd + ' done:', data);
            });
            break;

        case "set_views_info":
            chrome.windows.getCurrent(function (wind) {
                var updateInfo = {};
                // data is passed as string, we cannot use || since a top/left position of (0,0) should be supported
                if (data.top)
                    updateInfo.top = parseInt(data.top)
                if (data.left)
                    updateInfo.left = parseInt(data.left)
                if (data.width)
                    updateInfo.width = parseInt(data.width)
                if (data.height)
                    updateInfo.height = parseInt(data.height)
                if (data.state)
                    updateInfo.state = data.state

                chrome.windows.update(wind.id, updateInfo, function (window) {
                    socket.emit('cmd_out', data);
                });
            });
            break;

        case "get_errors":
            data.retVal = pageErrors;
            socket.emit('cmd_out', data);
            console.log('--' + data.cmd + ' done:', data);
            break;

        case "clear_errors":
            pageErrors = [];
            socket.emit('cmd_out', data);
            console.log('--' + data.cmd + ' done:', data);
            break;

        case "pause":
            handlePause(socket, data);
            break;

        default:
            // runs all other actions in the current window & tab
            findActiveTabAndRunAction(socket, data);
    }
}

function handlePause(socket, data) {
    setExtensionIcon(true);
    chrome.browserAction.onClicked.addListener(function (tab) {
        socket.emit('cmd_out', data);
        setExtensionIcon();
    });
}

function findActiveTabAndRunAction(socket, data) {
    chrome.windows.getCurrent({}, function (window) {
        try {
            chrome.tabs.query({ active: true }, function (tabs) {
                tab = tabs[0]; // only work with the active tab - and there is only one active tab!
                runActionInActivePage(socket, tab, data);
            });
        }
        catch (err) {
            console.log('-- ERROR: ' + err.message);
            data.error_code = 500;
            data.error_message = err.message;
            socket.emit('cmd_out', data);
        }
    });
}

function runActionInActivePage(socket, tab, data) {
    if (tab === undefined) {
        console.log('--' + data.cmd + ' error');
        data.error_code = 501;
        data.error_message = 'ERROR: Unable to find the active tab';
        socket.emit('cmd_out', data);
        return;
    }

    switch (data.cmd) {
        case "get_url":
            data.retVal = tab.url;
            socket.emit('cmd_out', data);
            console.log('--' + data.cmd + ' done');
            break;

        case "set_url":
            function tabUpdatedListener(tabId, changeInfo, tab) {
                console.log('--status=' + changeInfo.status + ' url=' + tab.url);
                if (changeInfo.status == 'complete' && tab.url !== 'about:blank') { /* timing issue where about:blank will be 'complete' before socket connection occurs before the about:blank tab is created*/
                    chrome.tabs.onUpdated.removeListener(tabUpdatedListener);
                    socket.emit('cmd_out', data);
                    console.log('--' + data.cmd + ' done');
                }
            }

            chrome.tabs.onUpdated.addListener(tabUpdatedListener);

            // updates the url - response will be sent after the page load is complete - see above
            chrome.tabs.update(tab.id, { url: data.value });
            break;

        case "screenshot":
            chrome.tabs.captureVisibleTab(tab.windowId, { format: "jpeg", quality: 70 }, function (dataUrl) {
                data.retVal = dataUrl;
                socket.emit('cmd_out', data);
                console.log('--' + data.cmd + ' done');
            });
            break;

        case "enable_network_stats":
            parseIncomingNetworkMessage();
            socket.emit('cmd_out', data);
            break;

        case "get_network_stats":
            data.retVal = networkStats;
            socket.emit('cmd_out', data);
            break;

        case "fullpage_screenshot":
            var tabId = tab.id;
            var debuggeeId = { tabId: tabId };

            attachOrReuseDebugger(debuggeeId, function () {
                chrome.debugger.sendCommand(debuggeeId, "Page.getLayoutMetrics", function (metrics) {
                    const widthIncreaseFactor = data.widthIncreaseFactor || 1;
                    const heightIncreaseFactor = data.heightIncreaseFactor || 1;
                    const width = widthIncreaseFactor * Math.ceil(metrics.contentSize.width);
                    const height = heightIncreaseFactor * Math.ceil(metrics.contentSize.height);

                    chrome.debugger.sendCommand(debuggeeId, "Emulation.setDeviceMetricsOverride", {
                        width: width,
                        height: height,
                        deviceScaleFactor: 1,
                        mobile: false,
                        dontSetVisibleSize: false
                    }, function () {
                        /*chrome.debugger.sendCommand(debuggeeId, "Emulation.setVisibleSize", {
                            width: width,
                            height: height,
                        }, function () {
                        */
                        setTimeout(async function () {
                            chrome.debugger.sendCommand(debuggeeId, "Page.captureScreenshot", { format: "png", quality: 100 },
                                function (result) {
                                    data.retVal = result;
                                    socket.emit('cmd_out', data);
                                });
                        }, 1000); /* enough time for chrome to process the changes (even though we are running in a callback!!?!) */
                    });
                });
            });
            break;

        case "switch_frame":
            var urlFragment = data.value;
            chrome.webNavigation.getAllFrames({ tabId: tab.id }, function (framesInfo) {
                for (let { frameId, url } of framesInfo) {
                    if (url.includes(urlFragment)) {
                        activeFrameId = frameId;
                        socket.emit('cmd_out', data);
                        return;
                    }
                }

                data.error_code = 404;
                data.error_message = 'Frame with path "' + urlFragment + '" not found';
                socket.emit('cmd_out', data);
            });
            break;

        case "reset_frame":
            activeFrameId = 0;
            socket.emit('cmd_out', data);
            break;

        case "close_active_view":
            chrome.tabs.remove(tab.id, function (tabs) {

                if (chrome.runtime.lastError) {
                    console.log('--' + data.cmd + ' error');
                    data.error_code = 500;
                    data.error_message = chrome.runtime.lastError.message;
                    socket.emit('cmd_out', data);
                    return;
                }

                socket.emit('cmd_out', data);
            });
            break;

        default:
            sendMessageIntoTab(tab.id, data, function (response) {
                console.log('response:', JSON.stringify(response));

                if (typeof response == "undefined") {
                    setTimeout(function () {
                        console.log(`___ delay restart send message - tabId=${tab.id}, frameId=${data.frameId}`);
                        // try again, case when page moved
                        findActiveTabAndRunAction(socket, data);
                    }, 500);
                }
                else {
                    if (response.data.error_code)
                        console.log('error response received from page: ' + response.data.error_message);

                    socket.emit('cmd_out', response.data);
                    console.log('--' + data.cmd + ' done');
                }
            });
            break;
    }
}

// sends the message into the page - only after having checked that the dependencies are injected
function sendMessageIntoTab(tabId, data, callback) {
    var details = { tabId: tabId, frameId: data.frameId };
    var dependency_key = `${tabId}-${data.frameId}`;

    console.log(`-- sendMessageIntoTab - ${tabId} - ${JSON.stringify(data)}`);
    chrome.webNavigation.getFrame(details, function (info) {
        console.log(`-- getFrame - ${JSON.stringify(info)}`);
        chrome.tabs.sendMessage(
            tabId,
            { data: data },
            { frameId: data.frameId },
            function (response) {
                if (response) {
                    console.log(`-- chrome.tabs.sendMessage-RESPONSE - ${JSON.stringify(response)}`);
                    callback(response);
                }
                else {
                    console.log(`-- chrome.tabs.sendMessage-ELSE-1`);
                    chrome.tabs.executeScript(tabId, {
                        code: contentScriptCode,
                        allFrames: true,
                        frameId: data.frameId,
                        runAt: 'document_start'
                    }, function (resp) {
                        console.log(`-- chrome.tabs.sendMessage-ELSE-2`);
                        callback(); // after dependencies are injected, this will invoke callback() with no params, which triggers a retry
                    }
                    );
                }
            });
    });
}

function pageErrorsListener(request) {
    if (request.cmd === 'page_error') {
        console.log('errorData:', request.data);
        pageErrors.push(request.data);
    }
}

function setExtensionIcon(isPauseIcon) {
    let iconPath = 'default.png';
    if (isPauseIcon)
        iconPath = 'play4.png';

    chrome.browserAction.setIcon({ path: iconPath });
}

function attachOrReuseDebugger(debuggeeId, callback) {
    var version = "1.0";

    chrome.debugger.getTargets(function (targets) {
        const alreadyAttached = targets.filter(({ attached, tabId }) => attached && tabId === debuggeeId.tabId).length > 0;
        if (alreadyAttached)
            callback();

        else
            chrome.debugger.attach(debuggeeId, version, callback);
    });
}

function parseIncomingNetworkMessage() {
    let pending = new Map(); // map[requestId, {requestData}]

    var tabId = tab.id;
    var debuggeeId = { tabId: tabId };

    let round2 = (nb) => Math.round(nb * 1e2) / 1e2;

    attachOrReuseDebugger(debuggeeId, function () {
        chrome.debugger.sendCommand(debuggeeId, "Network.enable");
        chrome.debugger.onEvent.addListener(function (debuggeeId, message, params) {
            if (message === 'Network.requestWillBeSent') {
                pending.set(params.requestId, {
                    startTime: params.timestamp,
                    method: params.request.method
                });

            } else if (message === 'Network.responseReceived') {
                var requestData = pending.get(params.requestId);
                if (requestData === undefined) /* case we receive events that started before the capturing started */
                    return;

                if (params.response.protocol === "data")
                    // when protocol== data, the url is the data itself (this polutes the logs)
                    requestData.url = "data:[...]"

                else
                    requestData.url = params.response.url;

                var chromeTiming = params.response.timing;
                var timing = {};
                if (chromeTiming) {
                    timing.dns = round2(chromeTiming.dnsEnd - chromeTiming.dnsStart);
                    timing.initialConnection = round2(chromeTiming.connectEnd - chromeTiming.connectStart);
                    timing.ssl = round2(chromeTiming.sslEnd - chromeTiming.sslStart);
                    timing.requestSent = round2(chromeTiming.sendEnd - chromeTiming.sendStart);
                }

                requestData.type = params.response.mimeType;
                requestData.status = params.response.status;
                requestData.timing = timing;
                if (params.response.status !== 200)
                    requestData.statusText = params.response.statusText;

            } else if (message === 'Network.loadingFinished') {
                var requestData = pending.get(params.requestId);
                if (requestData === undefined) /* case we receive events that started before the capturing started */
                    return;

                requestData.length = params.encodedDataLength;
                requestData.totalTime = round2(1000 * (params.timestamp - requestData.startTime));

                delete requestData.startTime;

                networkStats.push(requestData);
                pending.delete(params.requestId);
                //console.log(`[${requestData.url}] ${requestData.totalTime}ms ${requestData.status}`);
            }
        });
    });
}
<|endoftext|>


<commit_before>

/*!
 * Engine - Master
 * Copyright(c) 2010 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var Worker = require('./worker')
  , EventEmitter = require('events').EventEmitter
  , binding = process.binding('net')
  , os = require('os');

/**
 * Start a new `Master` with the given `server`.
 *
 * @param {http.Server} server
 * @return {Master}
 * @api public
 */

var Master = module.exports = function Master(server) {
  this.server = server;
  this.plugins = [];
  this.children = [];
  this.options = {};
  this.env = process.env.NODE_ENV || 'development';
  this.isWorker = !! process.env.ENGINE_MASTER_PID;
};

/**
 * Interit from `EventEmitter.prototype`.
 */

Master.prototype.__proto__ = EventEmitter.prototype;

/**
 * Defer `http.Server#listen()` call.
 *
 * @return {Master} for chaining
 * @api public
 */

Master.prototype.listen = function(){
  (this.isWorker ? new Worker(this) : this).start();
  return this;
};

/**
 * Set the number of works to `n`.
 *
 * @param {Number} n
 * @return {Master} for chaining
 * @api public
 */

Master.prototype.workers = function(n){
  this.options.workers = n;
  return this;
};

/**
 * Check if `option` has been set.
 *
 * @param {String} option
 * @return {Boolean}
 * @api public
 */

Master.prototype.has = function(option){
  return !! this.options[option];
};

/**
 * Use the given `plugin`.
 *
 * @param {Function} plugin
 * @return {Master} for chaining
 * @api public
 */

Master.prototype.use = function(plugin){
  this.plugins.push(plugin);
  if (!this.isWorker) plugin(this);
  return this;
};

/**
 * Start master process.
 *
 *   - set __ENGINE_MASTER_PID__
 *   - defaults workers to the number of CPUs available
 *   - creates listening socket
 *
 * @api private
 */

Master.prototype.start = function(){
  this.pid = process.env.ENGINE_MASTER_PID = process.pid;
  if (!this.has('workers')) this.workers(os.cpus().length);
  this.cmd = process.argv.slice(1);
  this.sock = binding.socket('tcp4'); // TODO: ipv6
  this.emit('start');
  this.spawn(this.options.workers);
  binding.bind(this.sock, 3000, '127.0.0.1'); // TODO: dynamic ... clean up
  binding.listen(this.sock, 128);
};

/**
 * Spawn `n` workers.
 *
 * @param {Number} n
 * @api private
 */

Master.prototype.spawn = function(n){
  while (n--) this.spawnWorker();
};

/**
 * Spawn a worker.
 *
 * @return {Worker}
 * @api public
 */

Master.prototype.spawnWorker = function(){
  var fds = binding.socketpair()
    , worker = new Worker(this).spawn(fds);
  worker.sock.write('test', 'ascii', this.sock);
  var id = this.children.push(worker);
  worker.id = id;
  this.emit('worker', worker);
  return worker;
};
<commit_msg>Moved worker event emission to Master#spawn()<commit_after>

/*!
 * Engine - Master
 * Copyright(c) 2010 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var Worker = require('./worker')
  , EventEmitter = require('events').EventEmitter
  , binding = process.binding('net')
  , os = require('os');

/**
 * Start a new `Master` with the given `server`.
 *
 * @param {http.Server} server
 * @return {Master}
 * @api public
 */

var Master = module.exports = function Master(server) {
  this.server = server;
  this.plugins = [];
  this.children = [];
  this.options = {};
  this.env = process.env.NODE_ENV || 'development';
  this.isWorker = !! process.env.ENGINE_MASTER_PID;
};

/**
 * Interit from `EventEmitter.prototype`.
 */

Master.prototype.__proto__ = EventEmitter.prototype;

/**
 * Defer `http.Server#listen()` call.
 *
 * @return {Master} for chaining
 * @api public
 */

Master.prototype.listen = function(){
  (this.isWorker ? new Worker(this) : this).start();
  return this;
};

/**
 * Set the number of works to `n`.
 *
 * @param {Number} n
 * @return {Master} for chaining
 * @api public
 */

Master.prototype.workers = function(n){
  this.options.workers = n;
  return this;
};

/**
 * Check if `option` has been set.
 *
 * @param {String} option
 * @return {Boolean}
 * @api public
 */

Master.prototype.has = function(option){
  return !! this.options[option];
};

/**
 * Use the given `plugin`.
 *
 * @param {Function} plugin
 * @return {Master} for chaining
 * @api public
 */

Master.prototype.use = function(plugin){
  this.plugins.push(plugin);
  if (!this.isWorker) plugin(this);
  return this;
};

/**
 * Start master process.
 *
 *   - set __ENGINE_MASTER_PID__
 *   - defaults workers to the number of CPUs available
 *   - creates listening socket
 *
 * @api private
 */

Master.prototype.start = function(){
  this.pid = process.env.ENGINE_MASTER_PID = process.pid;
  if (!this.has('workers')) this.workers(os.cpus().length);
  this.cmd = process.argv.slice(1);
  this.sock = binding.socket('tcp4'); // TODO: ipv6
  this.emit('start');
  this.spawn(this.options.workers);
  binding.bind(this.sock, 3000, '127.0.0.1'); // TODO: dynamic ... clean up
  binding.listen(this.sock, 128);
};

/**
 * Spawn `n` workers.
 *
 * @param {Number} n
 * @api private
 */

Master.prototype.spawn = function(n){
  while (n--) this.emit('worker', this.spawnWorker());
};

/**
 * Spawn a worker.
 *
 * @return {Worker}
 * @api public
 */

Master.prototype.spawnWorker = function(){
  var fds = binding.socketpair()
    , worker = new Worker(this).spawn(fds);
  worker.sock.write('test', 'ascii', this.sock);
  var id = this.children.push(worker);
  worker.id = id;
  return worker;
};
<|endoftext|>


<commit_before>/**
 *
 *  Web Starter Kit
 *  Copyright 2014 Google Inc. All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License
 *
 */

'use strict';

// Include Gulp & Tools We'll Use
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var del = require('del');
var runSequence = require('run-sequence');
var browserSync = require('browser-sync');
var pagespeed = require('psi');
var reload = browserSync.reload;

var AUTOPREFIXER_BROWSERS = [
  'ie >= 8',
  'ie_mob >= 10',
  'ff >= 30',
  'chrome >= 34',
  'safari >= 7',
  'opera >= 23',
  'ios >= 7',
  'android >= 4.4',
  'bb >= 10'
];

// Lint JavaScript
gulp.task('jshint', function () {
  return gulp.src('app/scripts/**/*.js')
    .pipe(reload({stream: true, once: true}))
    .pipe($.jshint())
    .pipe($.jshint.reporter('jshint-stylish'))
    .pipe($.if(!browserSync.active, $.jshint.reporter('fail')));
});

// Optimize Images
gulp.task('images', function () {
  return gulp.src('app/images/**/*')
    .pipe($.cache($.imagemin({
      progressive: true,
      interlaced: true
    })))
    .pipe(gulp.dest('dist/images'))
    .pipe($.size({title: 'images'}));
});

// Copy All Files At The Root Level (app)
gulp.task('copy', function () {
  return gulp.src([
    '.tmp/*',
    '!.tmp/*.html',
    'node_modules/apache-server-configs/dist/.htaccess'
  ], {
    dot: true
  }).pipe(gulp.dest('dist'))
    .pipe($.size({title: 'copy'}));
});

// Copy Web Fonts To Dist
gulp.task('fonts', function () {
  return gulp.src(['app/fonts/**'])
    .pipe(gulp.dest('dist/fonts'))
    .pipe($.size({title: 'fonts'}));
});

// Compile and Automatically Prefix Stylesheets
gulp.task('styles', function () {
  // For best performance, don't add Sass partials to `gulp.src`
  return gulp.src([
      'app/styles/*.scss',
      'app/styles/**/*.css'
    ])
    .pipe($.plumber())
    .pipe($.changed('styles', {extension: '.scss'}))
    .pipe($.rubySass({
        style: 'expanded',
        precision: 10,
        compass: true
      })
      .on('error', console.error.bind(console))
    )
    .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
    .pipe(gulp.dest('.tmp/styles'))
    .pipe($.size({title: 'styles'}));
});

gulp.task('cmq', function () {
  gulp.src('./dist/styles/*.css')
    .pipe($.combineMediaQueries({
          log: true
    }))
    // Concatenate And Minify Styles
    .pipe($.csso())
    .pipe(gulp.dest('dist/styles'))
    .pipe($.size({title: 'Media Queries'}));
});

// Scan Your HTML For Assets & Optimize Them

gulp.task('html' ,function () {
  var assets = $.useref.assets({searchPath: '.tmp'});

  return gulp.src('.tmp/**/*.html')
    .pipe(assets)
    // Concatenate And Minify JavaScript
    .pipe($.if('*.js', $.uglify({preserveComments: 'some'})))
    // Remove Any Unused CSS
    // Note: If not using the Style Guide, you can delete it from
    // the next line to only include styles your project uses.
    .pipe($.if('*.css', $.uncss({
      html: [
        '.tmp/index.html',
        '.tmp/sign-up/index.html',
        '.tmp/privacy-policy/index.html',
        '.tmp/plans-pricing/index.html',
        '.tmp/terms-of-service/index.html'
      ],
      // CSS Selectors for UnCSS to ignore
      ignore: [
        /.navdrawer-container.open/,
        /.app-bar.open/,
        /.ibutton-*/,
        /#header/
      ]
    })))
    // Concatenate And Minify Styles
    // In case you are still using useref build blocks
    .pipe($.if('*.css', $.csso()))
    .pipe(assets.restore())
    .pipe($.useref())
    // Update Production Style Guide Paths
    //.pipe($.replace('components/components.css', 'components/main.min.css'))
    // Minify Any HTML
    .pipe($.if('*.html', $.minifyHtml()))
    // Output Files
    .pipe(gulp.dest('dist'))
    .pipe($.size({title: 'html'}));
});

gulp.task('html:inline', function () {
    return gulp.src('./.tmp/**/*.html')
        .pipe($.inlineSource('./app'))
        .pipe(gulp.dest('./dist'));
});

gulp.task('html:jekyll', function () {
    gulp.src(['./app/index.html', './app/_layouts/*.html', './app/_posts/*.{markdown,md}', './app/_customers/*.{markdown,md}'])
        .pipe($.plumber())
        .pipe($.jekyll({
            source: './app',
            destination: './.tmp/',
            config: '_config.yml',
            bundleExec: true
        }))
        .on('error', console.error.bind(console))
        .pipe(gulp.dest('./dist/'));
});

// Clean Output Directory
gulp.task('clean', del.bind(null, ['.tmp', 'dist/*', '!dist/.git']));

gulp.task('serve', function () {
  browserSync({
    notify: false,
    // Run as an https by uncommenting 'https: true'
    // Note: this uses an unsigned certificate which on first access
    //       will present a certificate warning in the browser.
    // https: true,
    server: ['.tmp', 'app']
  });
});

// Watch Files For Changes & Reload
gulp.task('watch', ['serve','html:jekyll','styles'], function () {

  gulp.watch(['app/**/*.html'], ['html:jekyll', 'styles']);
  gulp.watch(['app/**/*.{html,md,markdown}'], ['html:jekyll', 'styles']);
  gulp.watch(['app/styles/**/*.{scss,css}'], ['styles']);
  gulp.watch(['app/styles/**/*.{scss,css}'], ['hologram']);
  gulp.watch(['app/scripts/**/*.js'], ['jshint']);
  gulp.watch(['app/images/**/*'], reload);
  gulp.watch(['.tmp/**'], reload);
});

// Build and serve the output from the dist build
gulp.task('serve:dist', ['default'], function () {
  browserSync({
    notify: false,
    // Run as an https by uncommenting 'https: true'
    // Note: this uses an unsigned certificate which on first access
    //       will present a certificate warning in the browser.
    // https: true,
    server: 'dist'
  });
});

// Build Production Files, the Default Task
gulp.task('default', ['clean'], function (cb) {
  runSequence('html:jekyll','styles', 'hologram',['html', 'jshint', 'images', 'fonts', 'copy'], 'html:inline', 'cmq', cb);
});

gulp.task('build', function (cb) {
  runSequence('hologram',['html', 'jshint', 'images', 'fonts', 'copy'], 'html:inline', 'cmq', cb);
});

// Run PageSpeed Insights
// Update `url` below to the public URL for your site
gulp.task('pagespeed', pagespeed.bind(null, {
  // By default, we use the PageSpeed Insights
  // free (no API key) tier. You can use a Google
  // Developer API key if you have one. See
  // http://goo.gl/RkN0vE for info key: 'YOUR_API_KEY'
  url: 'http://getroadmap.github.io/roadmap-site/',
  strategy: 'mobile'
}));

gulp.task('bump:major', function () {
  gulp.src(['./package.json'])
    .pipe($.bump({type:'major'}))
    .pipe(gulp.dest('./'));
});

gulp.task('bump:minor', function () {
  gulp.src(['./bower.json', './package.json'])
    .pipe($.bump({type:'minor'}))
    .pipe(gulp.dest('./'));
});

gulp.task('bump:patch', function () {
  gulp.src(['./bower.json', './package.json'])
    .pipe($.bump({type:'patch'}))
    .pipe(gulp.dest('./'));
});

gulp.task('hologram', function () {
  gulp.src('hologram/hologram_config.yml')
    .pipe($.hologram({bundler:true, logging:true}));
});

gulp.task('watch:hologram', ['serve', 'styles', 'hologram'], function () {
  gulp.watch(['app/styles/**/*.{scss,css}'], ['styles']);
  gulp.watch(['.tmp/styles/**/*.{scss,css}'], ['hologram']);
});

// Load custom tasks from the `tasks` directory
try { require('require-dir')('tasks'); } catch (err) {}
<commit_msg>format gulpfile<commit_after>/**
 *
 *  Web Starter Kit
 *  Copyright 2014 Google Inc. All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License
 *
 */

'use strict';

/* ==========================================================================
   § Gulp Tools
   ========================================================================== */

// Include Gulp & Tools We'll Use
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var del = require('del');
var runSequence = require('run-sequence');
var browserSync = require('browser-sync');
var pagespeed = require('psi');
var reload = browserSync.reload;
var path = require('path');

/* ==========================================================================
   § Gulp Variables
   ========================================================================== */

var AUTOPREFIXER_BROWSERS = [
 'ie >= 8',
 'ie_mob >= 10',
 'ff >= 30',
 'chrome >= 34',
 'safari >= 7',
 'opera >= 23',
 'ios >= 7',
 'android >= 4.4',
 'bb >= 10'
];

/* ==========================================================================
   § Assets
   ========================================================================== */

gulp.task('assets' ,function () {
  var assets = $.useref.assets({searchPath: '.tmp'});

  return gulp.src('.tmp/**/*.html')
    .pipe(assets)
    // Concatenate And Minify JavaScript
    .pipe($.if('*.js', $.uglify({preserveComments: 'some'})))
    // Remove Any Unused CSS
    // Note: If not using the Style Guide, you can delete it from
    // the next line to only include styles your project uses.
    .pipe($.if('*.css', $.uncss({
      html: [
        '.tmp/index.html',
        '.tmp/sign-up/index.html',
        '.tmp/privacy-policy/index.html',
        '.tmp/plans-pricing/index.html',
        '.tmp/terms-of-service/index.html'
      ],
      // CSS Selectors for UnCSS to ignore
      ignore: [
        // for mobile
        /video, embed, object, img, picture/,
        /img/,
        // application styles
        /.app-bar.open/,
        /.ibutton-*/,
        /#header/,

      ]
    })))
    // Concatenate And Minify Styles
    // In case you are still using useref build blocks
    .pipe($.if('*.css', $.csso()))
    .pipe(assets.restore())
    .pipe($.useref())
    // Update Production Style Guide Paths
    //.pipe($.replace('components/components.css', 'components/main.min.css'))
    // Minify Any HTML
    // Output Files
    .pipe(gulp.dest('.tmp'))
    .pipe($.size({title: 'html'}));
});

/*
   §§ Assets - Inline
   ========================================================================== */

gulp.task('assets:inline', function () {
    return gulp.src('./.tmp/**/*.html')
        .pipe($.inlineSource('./app'))
        .pipe(gulp.dest('./dist'));
});

/* ==========================================================================
   § Bump Versions
   ========================================================================== */

/*
   §§ Bump Major
   ========================================================================== */

gulp.task('bump:major', function () {
  gulp.src(['./package.json'])
    .pipe($.bump({type:'major'}))
    .pipe(gulp.dest('./'));
});

/*
   §§ Bump Minor
   ========================================================================== */

gulp.task('bump:minor', function () {
  gulp.src(['./bower.json', './package.json'])
    .pipe($.bump({type:'minor'}))
    .pipe(gulp.dest('./'));
});

/*
   §§ Bump Patch
   ========================================================================== */

gulp.task('bump:patch', function () {
  gulp.src(['./bower.json', './package.json'])
    .pipe($.bump({type:'patch'}))
    .pipe(gulp.dest('./'));
});

/* ==========================================================================
   § Clean
   ========================================================================== */

gulp.task('clean', del.bind(null, ['.tmp', 'dist/*', '!dist/.git']));

/* ==========================================================================
   § Copy
   ========================================================================== */

gulp.task('copy', function () {
  return gulp.src([
    '.tmp/*',
    '!.tmp/*.html',
    'node_modules/apache-server-configs/dist/.htaccess'
  ], {
    dot: true
  }).pipe(gulp.dest('dist'))
    .pipe($.size({title: 'copy'}));
});

/* ==========================================================================
   § Fonts
   ========================================================================== */

gulp.task('fonts', function () {
  return gulp.src(['app/fonts/**'])
    .pipe(gulp.dest('dist/fonts'))
    .pipe($.size({title: 'fonts'}));
});

/* ==========================================================================
   § Hologram
   ========================================================================== */

gulp.task('hologram', function () {
  gulp.src('hologram/hologram_config.yml')
    .pipe($.hologram({bundler:true, logging:true}));
});

/* ==========================================================================
   § Images
   ========================================================================== */

gulp.task('images', function () {
  return gulp.src('app/images/**/*')
    .pipe($.cache($.imagemin({
      progressive: true,
      interlaced: true
    })))
    .pipe(gulp.dest('.tmp/images'))
    .pipe($.size({title: 'images'}));
});

/* ==========================================================================
   § Jekyll
   ========================================================================== */

gulp.task('jekyll', function () {
  gulp.src(['./app/index.html', './app/_layouts/*.html', './app/_posts/*.{markdown,md}', './app/_customers/*.{markdown,md}'])
    .pipe($.plumber())
    .pipe($.jekyll({
        source: './app',
        destination: './.tmp/',
        config: '_config.yml',
        bundleExec: true
    }))
    .on('error', console.error.bind(console))
});

/* ==========================================================================
   § JSHINT
   ========================================================================== */

gulp.task('jshint', function () {
  return gulp.src('app/scripts/**/*.js')
    .pipe(reload({stream: true, once: true}))
    .pipe($.jshint())
    .pipe($.jshint.reporter('jshint-stylish'))
    .pipe($.if(!browserSync.active, $.jshint.reporter('fail')));
});

/* ==========================================================================
   § Pagespeed
     Run PageSpeed Insights
     Update `url` below to the public URL for your site
   ========================================================================== */

gulp.task('pagespeed', pagespeed.bind(null, {
  // By default, we use the PageSpeed Insights
  // free (no API key) tier. You can use a Google
  // Developer API key if you have one. See
  // http://goo.gl/RkN0vE for info key: 'YOUR_API_KEY'
  url: 'http://getroadmap.github.io/roadmap-site/',
  strategy: 'mobile'
}));

/* ==========================================================================
   § Rev
   ========================================================================== */

gulp.task('rev', function () {
  return gulp.src(['.tmp/**', ])
    .pipe($.revAll({
      base: '.tmp/',
      ignore: [
        /^\/favicon.ico$/g,
        '.html',
        '.php'
      ],
    }))
    .pipe(gulp.dest('dist'));
});

gulp.task('rev:gh-pages', function () {
  return gulp.src(['.tmp/**', ])
    .pipe($.revAll({
        ignore: [
          /^\/favicon.ico$/g,
          '.html',
          '.php'
        ],
        transformFilename: function (file, hash) {
          var ext = path.extname(file.path);
          return path.basename(file.path, ext) + ext; // no transform
        },
        prefix: '/roadmap-site/'
    }))
    .pipe(gulp.dest('gh-pages'));
});

/* ==========================================================================
   § Styles
   ========================================================================== */

gulp.task('styles', function () {
  // For best performance, don't add Sass partials to `gulp.src`
  return gulp.src([
      'app/styles/*.scss',
      'app/styles/**/*.css'
    ])
    .pipe($.plumber())
    .pipe($.changed('styles', {extension: '.scss'}))
    .pipe($.rubySass({
        style: 'expanded',
        precision: 10,
        compass: true
      })
      .on('error', console.error.bind(console))
    )
    .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
    .pipe(gulp.dest('.tmp/styles'))
    .pipe($.size({title: 'styles'}));
});

/*
   §§ Styles - Combine Media Queries
   ========================================================================== */

gulp.task('styles:cmq', function () {
  gulp.src('./dist/styles/*.css')
    .pipe($.combineMediaQueries({
          log: true
    }))
    // Concatenate And Minify Styles
    .pipe($.csso())
    .pipe(gulp.dest('dist/styles'))
    .pipe($.size({title: 'Media Queries'}));
});

/* ==========================================================================
   § Serve
   ========================================================================== */

/*
   §§ Serve - Default
   ========================================================================== */
gulp.task('serve', ['jekyll', 'styles'], function () {
  browserSync({
    notify: false,
    // Run as an https by uncommenting 'https: true'
    // Note: this uses an unsigned certificate which on first access
    //       will present a certificate warning in the browser.
    // https: true,
    server: ['.tmp', 'app']
  });
});

/*
   §§ Serve - Dist
   ========================================================================== */
gulp.task('serve:dist', ['build'], function () {
  browserSync({
    notify: false,
    // Run as an https by uncommenting 'https: true'
    // Note: this uses an unsigned certificate which on first access
    //       will present a certificate warning in the browser.
    // https: true,
    server: ['dist']
  });
});

/* ==========================================================================
   § Watch
   ========================================================================== */

/*
   §§ Watch - Default
   ========================================================================== */
gulp.task('watch', ['serve','jekyll','styles'], function () {
  gulp.watch(['app/**/*.html'], ['jekyll', 'styles']);
  gulp.watch(['app/**/*.{html,md,markdown}'], ['jekyll', 'styles']);
  gulp.watch(['app/styles/**/*.{scss,css}'], ['styles']);
  gulp.watch(['app/styles/**/*.{scss,css}'], ['hologram']);
  gulp.watch(['app/scripts/**/*.js'], ['jshint']);
  gulp.watch(['app/images/**/*'], reload);
  gulp.watch(['.tmp/**'], reload);
});

/*
   §§ Watch - Hologram
   ========================================================================== */

gulp.task('watch:hologram', ['serve', 'styles', 'hologram'], function () {
  gulp.watch(['app/styles/**/*.{scss,css}'], ['styles']);
  gulp.watch(['.tmp/styles/**/*.{scss,css}'], ['hologram']);
});

/* ==========================================================================
   § Build
   ========================================================================== */

/*
   §§ Build - Default
   ========================================================================== */
gulp.task('build', ['clean'], function (cb) {
  runSequence('jekyll','styles', 'styles:cmq', 'hologram', ['jshint', 'images', 'fonts', 'copy'], 'assets', 'assets:inline', 'rev', cb);
});

/*
   §§ Build - GH-Pages
   ========================================================================== */
gulp.task('build:gh-pages', ['clean'], function (cb) {
  runSequence('jekyll','styles', 's', 'rev:gh-pages');
});

/* ==========================================================================
   § Default
   ========================================================================== */

gulp.task('default', ['clean'], function () {
  gulp.start('watch');
});

// Load custom tasks from the `tasks` directory
try { require('require-dir')('tasks'); } catch (err) {}
<|endoftext|>


<commit_before>module.exports = function portal(bot, info)
{
  const express = require('express');
  const app = express();
  const request = require('request');
  const fs = require('fs');
  const Handlebars = require('handlebars');
  const session = require('express-session');
  const bodyParser = require('body-parser');
  const perms = require('./permissions')();
  const https = require('https');
  const helmet = require('helmet');
  //const multer = require('multer');
  //const upload = multer();
  const db = info.db;
  //keeps track of the guilds that sessions have access to
  const guildsWithPerms = {};
  //holds the tokens, keys are session IDs
  const tokens = {};
  //config for testing purposes
  const config = info.config;
  let privateKey = fs.readFileSync(__dirname + '/web/privkey1.pem');
  let certificate = fs.readFileSync(__dirname + '/web/fullchain1.pem');

  //use bodyParser to parse post shtuff\
  app.use(bodyParser.json());
  app.use(bodyParser.urlencoded({extended: false}));
  //security shtuff
  app.use(helmet());
  app.use(helmet.referrerPolicy({ policy: 'no-referrer'}));
  app.use(helmet.contentSecurityPolicy({
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", 'https://fonts.googleapis.com', "'unsafe-inline'"],
      imgSrc: ["'self'", 'https://cdn.discordapp.com', 'https://www.google-analytics.com'],
      scriptSrc: ["'self'", 'https://www.google-analytics.com', "'unsafe-inline'"],
      fontSrc: ["'self'", 'https://fonts.googleapis.com', 'https://fonts.gstatic.com', 'https://fonts.googleapis.com']
    }
  }));
  //Actually use session info.
  app.use(session({
    secret: config.web.client_secret,
    resave: false,
    saveUninitialized: true
  }));
  //get info from the users and guilds endpoints
  //Promise that resolves an object with user and guilds as keys.
  function getAllInfo (sessionId)
  {
    return new Promise((resolve, reject) =>
    {
      let token = tokens[sessionId];
      let userUrl = `https://discordapp.com/api/users/@me`;
      let guildsUrl = 'https://discordapp.com/api/users/@me/guilds';
      if(Date.now() - token.time >= 604800)
      {
        refreshToken(token).then((newToken) =>
        {
          token = newToken;
        }).then((err) =>
        {
          console.log(err);
        });
      }
      //console.log(tokens[sessionId]);
      let info = {};
      let userPromise = requestPage(userUrl, {json: true, headers: {Authorization: `Bearer ${token.token}`}});
      let guildsPromise = requestPage(guildsUrl, {json: true, headers: {Authorization: `Bearer ${token.token}`}});
      Promise.all([userPromise, guildsPromise]).then((values) =>
      {
        info.user = values[0];
        info.guilds = values[1];
        resolve(info);
      });
    });
  };
  //gets info for the servers a user is connected to
  //promise resolves an array of guild objects
  function getServerInfo(sessionId)
  {
    let token = tokens[sessionId];
    if(Date.now() - token.time >= 604800)
    {
      refreshToken(token).then((newToken) =>
      {
        token = newToken;
      });
    }
    let url = 'https://discordapp.com/api/users/@me/guilds';
    return requestPage(url, {json: true, headers: {Authorization: `Bearer ${tokens[sessionId].token}`}});
  }
  //gets info for a user (the user that is authenticated via Oauth)
  //promise resolves user obj
  function getUserInfo(sessionId)
  {
    return new Promise((resolve, reject) =>
    {
      let token = tokens[sessionId];
      if(Date.now() - token.time >= 604800)
      {
        refreshToken(token);
        token = tokens[sessionId];
      }
      let url = 'https://discordapp.com/api/users/@me';
      requestPage(url, {json: true, headers: {Authorization: `Bearer ${token.token}`}}).then((user) =>
      {
        resolve(user);
      });
    });
  }
  //the keys are sessionId's, which could be used in guildsWithPerms as well
  //what we'll do here is find any sessions that are older than 2 days and
  //clear them out of tokens and guildsWithPerms if they are present
  setInterval(() =>
  {
    Object.keys(tokens).forEach((key) =>
    {
      if(Date.now() - tokens[key].time >= 172800000)
      {
        delete tokens[key];
        if(guildsWithPerms[key] != undefined)
        {
          delete guildsWithPerms[key];
        }
      }
    });
  }, 172800000);
  //requests a url with the selected options
  //promise resolves the body of the url, or rejects with the error
  function requestPage(url, options)
  {
    options.url = url;
    return new Promise((resolve, reject) =>
    {
      request(options, (err, response, body) =>
      {
        if(!err && response.statusCode === 200)
        {
          //console.log(body);
          resolve(body);
        }
        else
        {
          reject(err);
        }
      });
    });
  }
  function filterSearch(guilds)
  {
    return new Promise((resolve, reject) =>
    {
      filterGuilds(guilds).then((filtered) =>
      {
        //console.log(filtered);
        searchServers(filtered).then((searched) =>
        {
          resolve(searched);
        });
      });
    });

  }
  //filters the guilds that you don't have access to
  //promise resolves the guilds that you have Manage Server perms
  function filterGuilds(guilds)
  {
    return new Promise((resolve, reject) =>
    {
      let filteredGuilds = guilds.filter((guild) =>
      {
        return perms.hasPermission(perms.permissionBits['GENERAL_MANAGE_GUILD'], guild.permissions);
      });
      resolve(filteredGuilds);
    });
  }
  //gets the prefix, nsfw setting and possibly add buttons to add HawkBot to the server
  function searchServers(guilds)
  {
    return new Promise((resolve, reject) =>
    {
      //console.log(`In searchServers: ${JSON.stringify(guilds)}`);
      let len = Object.keys(guilds).length -1;
      let counter = 0;
      Object.keys(guilds).forEach((key) =>
      {
        //console.log(guilds[key].id);
        db.getSettings(guilds[key].id).then((settings) =>
        {
          //console.log(settings);
          if(settings != null)
          {
            guilds[key].settings = settings;
          }
          if(counter == len)
          {
            resolve(guilds);
          }
          counter++;
        });
      });
    });

  }
  //Hopefully don't need this bs
  function getServerContext()
  {
    return new Promise((resolve, reject) =>
    {
      if(guildsWithPerms[req.sessionID])
      {
        if(guildsWithPerms[req.sessionID].indexOf(serverID) > -1)
        {
          resolve(serverID);
        }
        else
        {
          resolve(null);
        }
      }
    });
  }
  //handles the refreshing of tokens
  function refreshToken(token)
  {
    return new Promise((resolve, reject) =>
    {
      let data = {
        refresh_token: token.refresh,
        grant_type: 'refresh_token'
      };

      let base64 = new Buffer(`${config.web.client_id}:${config.web.client_secret}`).toString('base64');
      request.post({
        url: 'https://discordapp.com/api/oauth2/token',
        headers: { Authorization: `Basic ${base64}` },
        form: data
      }, (err, httpResponse, body) =>
      {

        if(err)
        {
          reject(err);
        }
        else
        {
          body = JSON.parse(body);
          tokens[token.session] = {
            token: body.access_token,
            session: token.session,
            time: Date.now(),
            refresh: body.refresh_token
          };
          resolve(tokens[token.session]);
        }
      });
    });

  }
  app.use(express.static(__dirname+'/web'));
  //handle the root of the site
  app.get('/', (req, res, next) =>
  {
    let context = {};
    if(tokens[req.sessionID])
    {
      getUserInfo(req.sessionID).then((info) =>
      {
        context.user = info;
        //console.log(info);
        let userPage = fs.readFileSync('./lib/web/index.hbs').toString();
        let renderedUserPage = Handlebars.compile(userPage);
        res.send(renderedUserPage(context));
      }).catch((err) =>
      {
        console.log(err);
      });
    }
    else
    {
      let userPage = fs.readFileSync('./lib/web/index.hbs').toString();
      let renderedUserPage = Handlebars.compile(userPage);
      res.send(renderedUserPage(context));
      //console.log(req.sessionID);
    }
  });
  //handles login
  app.get('/login', (req, res, next) =>
  {
    let data = {
      client_id: config.web.client_id,
      client_secret: config.web.client_secret,
      code: req.query.code,
      redirect_uri: config.web.redirect_uri,
      grant_type: 'authorization_code'
    };
    request.post({
      url: 'https://discordapp.com/api/oauth2/token',
      form: data
    }, (err, httpResponse, body) =>
    {
      if(err)
      {
        console.log(err);
      }
      else
      {
        body = JSON.parse(body);
        tokens[req.sessionID] = {
          token: body.access_token,
          session: req.sessionID,
          time: Date.now(),
          refresh: body.refresh_token
        };
        //console.log(tokens);
        //console.log(body);
      }
      res.redirect('https://bot.cyberhawk.co/');
    });
  });
  app.get('/servers', (req, res, next) =>
  {
    let context = {};
    if(tokens[req.sessionID])
    {
      getAllInfo(req.sessionID).then((info) =>
      {
        context.user = info.user;
        let guilds = info.guilds;

        return filterSearch(guilds);
      }).then((guilds) =>
      {
        context.guilds = guilds;
        //database query for guild info (parameter and nsfw flag)
        //console.log(guilds);
        guildsWithPerms[req.sessionID] = guilds.map((guild) =>
        {
          return guild.id;
        });
        //console.log(guildsWithPerms[req.sessionID]);
        let userPage = fs.readFileSync('./lib/web/servers.hbs').toString();
        let renderedUserPage = Handlebars.compile(userPage);
        res.send(renderedUserPage(context));
      }).catch((err) =>
      {
        console.log(err);
      });
    }
    else
    {
      let userPage = fs.readFileSync('./lib/web/servers.hbs').toString();
      let renderedUserPage = Handlebars.compile(userPage);
      res.send(renderedUserPage(context));
    }
  });
  app.get('/server', (req, res, next) =>
  {
    let context = {};
    if(tokens[req.sessionID])
    {
      getUserInfo(req.sessionID).then((info) =>
      {
        if(req.query.serverID != undefined)
        {
          //console.log('query is defined');
          let serverID = req.query.serverID;
          if(guildsWithPerms[req.sessionID])
          {
            if(guildsWithPerms[req.sessionID].indexOf(serverID) > -1)
            {
              context.user = info;
              //query db for document(nedb lingo)
              return db.findServer(serverID);
            }
          }
        }
        context.user = info;

      }).then((server) =>
      {
        if(server != null)
        {
          context.server = server;
          try{
            context.serverName = bot.servers[server._id].name;
          }
          catch(err)
          {
            console.log(err);
          }
        }

        //console.log(server);
        let userPage = fs.readFileSync('./lib/web/server.hbs').toString();
        let renderedUserPage = Handlebars.compile(userPage);
        res.send(renderedUserPage(context));
      }).catch((err) =>
      {
        console.log(err);
      });
    }
    else
    {
      let userPage = fs.readFileSync('./lib/web/index.hbs').toString();
      let renderedUserPage = Handlebars.compile(userPage);
      res.send(renderedUserPage(context));
      //console.log(req.sessionID);
    }
  });
  app.post('/form',(req, res, next) =>
  {
    if(req.body.id)
    {
      if(tokens[req.sessionID])
      {
        if(guildsWithPerms[req.sessionID])
        {
          if(guildsWithPerms[req.sessionID].indexOf(req.body.id) > -1)
          {
            let postData = req.body;
            //you can do stuff cuz you have perms.
            //if it has id, it will have at least prefix as well
            //console.log("You actually have perms to do this, nice");
            db.findServer(postData.id).then((oldInfo) =>
            {
              if(oldInfo !== null)
              {
                let done = oldInfo.commands.length - 1;
                let count = 0;
                for(let i = 0; i < oldInfo.commands.length; i ++)
                {
                  if(postData[oldInfo.commands[i].key])
                  {
                    //this means that the checkmard was on, so it'll be enabled
                    //console.log(`${postData[oldInfo.commands[i].key]} exists, going to enabled it`);
                    oldInfo.commands[i].enabled = true;
                  }
                  else
                  {
                    //this means that the command wasn't checked, so it's disabled
                    //console.log(`${postData[oldInfo.commands[i].key]} doesn't exist, going to disable it`);
                    oldInfo.commands[i].enabled = false;
                  }
                  if(count === done)
                  {
                    let newData = {};
                    newData.prefix = postData.prefix;
                    if(postData.nsfw)
                    {
                      newData.nsfw = true;
                    }
                    else
                    {
                      newData.nsfw = false;
                    }
                    newData.commands = oldInfo.commands;
                    //console.log(newData);
                    db.updateServer(postData.id, newData);
                  }
                  count++;
                }
              }
            });
          }
        }
      }
    }
    //console.log(req.body);
    res.json(req.body);
  });
  app.get('/logout', (req, res, next) =>
  {
    if(tokens[req.sessionID])
    {
      delete tokens[req.sessionID];
      delete guildsWithPerms[req.sessionID];
    }
    res.redirect("http://hawkbot.cyberhawk.co");
  });
  https.createServer({
    key: privateKey,
    cert: certificate
  }, app).listen(443);
  // Redirect from http port 80 to https
  const http = require('http');
  http.createServer(function (req, res)
  {
    res.writeHead(301, { 'Location': 'https://' + req.headers['host'] + req.url });
    res.end();
  }).listen(80);
};

<commit_msg>Issue with users not owning any servers<commit_after>module.exports = function portal(bot, info)
{
  const express = require('express');
  const app = express();
  const request = require('request');
  const fs = require('fs');
  const Handlebars = require('handlebars');
  const session = require('express-session');
  const bodyParser = require('body-parser');
  const perms = require('./permissions')();
  const https = require('https');
  const helmet = require('helmet');
  //const multer = require('multer');
  //const upload = multer();
  const db = info.db;
  //keeps track of the guilds that sessions have access to
  const guildsWithPerms = {};
  //holds the tokens, keys are session IDs
  const tokens = {};
  //config for testing purposes
  const config = info.config;
  let privateKey = fs.readFileSync(__dirname + '/web/privkey1.pem');
  let certificate = fs.readFileSync(__dirname + '/web/fullchain1.pem');

  //use bodyParser to parse post shtuff\
  app.use(bodyParser.json());
  app.use(bodyParser.urlencoded({extended: false}));
  //security shtuff
  app.use(helmet());
  app.use(helmet.referrerPolicy({ policy: 'no-referrer'}));
  app.use(helmet.contentSecurityPolicy({
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", 'https://fonts.googleapis.com', "'unsafe-inline'"],
      imgSrc: ["'self'", 'https://cdn.discordapp.com', 'https://www.google-analytics.com'],
      scriptSrc: ["'self'", 'https://www.google-analytics.com', "'unsafe-inline'"],
      fontSrc: ["'self'", 'https://fonts.googleapis.com', 'https://fonts.gstatic.com', 'https://fonts.googleapis.com']
    }
  }));
  //Actually use session info.
  app.use(session({
    secret: config.web.client_secret,
    resave: false,
    saveUninitialized: true
  }));
  //get info from the users and guilds endpoints
  //Promise that resolves an object with user and guilds as keys.
  function getAllInfo (sessionId)
  {
    return new Promise((resolve, reject) =>
    {
      let token = tokens[sessionId];
      let userUrl = `https://discordapp.com/api/users/@me`;
      let guildsUrl = 'https://discordapp.com/api/users/@me/guilds';
      if(Date.now() - token.time >= 604800)
      {
        refreshToken(token).then((newToken) =>
        {
          token = newToken;
        }).then((err) =>
        {
          console.log(err);
        });
      }
      //console.log(tokens[sessionId]);
      let info = {};
      let userPromise = requestPage(userUrl, {json: true, headers: {Authorization: `Bearer ${token.token}`}});
      let guildsPromise = requestPage(guildsUrl, {json: true, headers: {Authorization: `Bearer ${token.token}`}});
      Promise.all([userPromise, guildsPromise]).then((values) =>
      {
        info.user = values[0];
        info.guilds = values[1];
        resolve(info);
      });
    });
  };
  //gets info for the servers a user is connected to
  //promise resolves an array of guild objects
  function getServerInfo(sessionId)
  {
    let token = tokens[sessionId];
    if(Date.now() - token.time >= 604800)
    {
      refreshToken(token).then((newToken) =>
      {
        token = newToken;
      });
    }
    let url = 'https://discordapp.com/api/users/@me/guilds';
    return requestPage(url, {json: true, headers: {Authorization: `Bearer ${tokens[sessionId].token}`}});
  }
  //gets info for a user (the user that is authenticated via Oauth)
  //promise resolves user obj
  function getUserInfo(sessionId)
  {
    return new Promise((resolve, reject) =>
    {
      let token = tokens[sessionId];
      if(Date.now() - token.time >= 604800)
      {
        refreshToken(token);
        token = tokens[sessionId];
      }
      let url = 'https://discordapp.com/api/users/@me';
      requestPage(url, {json: true, headers: {Authorization: `Bearer ${token.token}`}}).then((user) =>
      {
        resolve(user);
      });
    });
  }
  //the keys are sessionId's, which could be used in guildsWithPerms as well
  //what we'll do here is find any sessions that are older than 2 days and
  //clear them out of tokens and guildsWithPerms if they are present
  setInterval(() =>
  {
    Object.keys(tokens).forEach((key) =>
    {
      if(Date.now() - tokens[key].time >= 172800000)
      {
        delete tokens[key];
        if(guildsWithPerms[key] != undefined)
        {
          delete guildsWithPerms[key];
        }
      }
    });
  }, 172800000);
  //requests a url with the selected options
  //promise resolves the body of the url, or rejects with the error
  function requestPage(url, options)
  {
    options.url = url;
    return new Promise((resolve, reject) =>
    {
      request(options, (err, response, body) =>
      {
        if(!err && response.statusCode === 200)
        {
          //console.log(body);
          resolve(body);
        }
        else
        {
          reject(err);
        }
      });
    });
  }
  function filterSearch(guilds)
  {
    return new Promise((resolve, reject) =>
    {
      filterGuilds(guilds).then((filtered) =>
      {
        //console.log(filtered);
        searchServers(filtered).then((searched) =>
        {
          resolve(searched);
        });
      });
    });

  }
  //filters the guilds that you don't have access to
  //promise resolves the guilds that you have Manage Server perms
  function filterGuilds(guilds)
  {
    return new Promise((resolve, reject) =>
    {
      if(guilds == null)
      {
        resolve(null);
      }
      let filteredGuilds = guilds.filter((guild) =>
      {
        return perms.hasPermission(perms.permissionBits['GENERAL_MANAGE_GUILD'], guild.permissions);
      });
      resolve(filteredGuilds);
    });
  }
  //gets the prefix, nsfw setting and possibly add buttons to add HawkBot to the server
  function searchServers(guilds)
  {
    return new Promise((resolve, reject) =>
    {
      //console.log(`In searchServers: ${JSON.stringify(guilds)}`);
      let len = Object.keys(guilds).length -1;
      let counter = 0;
      Object.keys(guilds).forEach((key) =>
      {
        //console.log(guilds[key].id);
        db.getSettings(guilds[key].id).then((settings) =>
        {
          //console.log(settings);
          if(settings != null)
          {
            guilds[key].settings = settings;
          }
          if(counter == len)
          {
            resolve(guilds);
          }
          counter++;
        });
      });
    });

  }
  //Hopefully don't need this bs
  function getServerContext()
  {
    return new Promise((resolve, reject) =>
    {
      if(guildsWithPerms[req.sessionID])
      {
        if(guildsWithPerms[req.sessionID].indexOf(serverID) > -1)
        {
          resolve(serverID);
        }
        else
        {
          resolve(null);
        }
      }
    });
  }
  //handles the refreshing of tokens
  function refreshToken(token)
  {
    return new Promise((resolve, reject) =>
    {
      let data = {
        refresh_token: token.refresh,
        grant_type: 'refresh_token'
      };

      let base64 = new Buffer(`${config.web.client_id}:${config.web.client_secret}`).toString('base64');
      request.post({
        url: 'https://discordapp.com/api/oauth2/token',
        headers: { Authorization: `Basic ${base64}` },
        form: data
      }, (err, httpResponse, body) =>
      {

        if(err)
        {
          reject(err);
        }
        else
        {
          body = JSON.parse(body);
          tokens[token.session] = {
            token: body.access_token,
            session: token.session,
            time: Date.now(),
            refresh: body.refresh_token
          };
          resolve(tokens[token.session]);
        }
      });
    });

  }
  app.use(express.static(__dirname+'/web'));
  //handle the root of the site
  app.get('/', (req, res, next) =>
  {
    let context = {};
    if(tokens[req.sessionID])
    {
      getUserInfo(req.sessionID).then((info) =>
      {
        context.user = info;
        //console.log(info);
        let userPage = fs.readFileSync('./lib/web/index.hbs').toString();
        let renderedUserPage = Handlebars.compile(userPage);
        res.send(renderedUserPage(context));
      }).catch((err) =>
      {
        console.log(err);
      });
    }
    else
    {
      let userPage = fs.readFileSync('./lib/web/index.hbs').toString();
      let renderedUserPage = Handlebars.compile(userPage);
      res.send(renderedUserPage(context));
      //console.log(req.sessionID);
    }
  });
  //handles login
  app.get('/login', (req, res, next) =>
  {
    let data = {
      client_id: config.web.client_id,
      client_secret: config.web.client_secret,
      code: req.query.code,
      redirect_uri: config.web.redirect_uri,
      grant_type: 'authorization_code'
    };
    request.post({
      url: 'https://discordapp.com/api/oauth2/token',
      form: data
    }, (err, httpResponse, body) =>
    {
      if(err)
      {
        console.log(err);
      }
      else
      {
        body = JSON.parse(body);
        tokens[req.sessionID] = {
          token: body.access_token,
          session: req.sessionID,
          time: Date.now(),
          refresh: body.refresh_token
        };
        //console.log(tokens);
        //console.log(body);
      }
      res.redirect('https://bot.cyberhawk.co/');
    });
  });
  app.get('/servers', (req, res, next) =>
  {
    let context = {};
    if(tokens[req.sessionID])
    {
      getAllInfo(req.sessionID).then((info) =>
      {
        context.user = info.user;
        let guilds = info.guilds;

        return filterSearch(guilds);
      }).catch((err) =>
      {
        console.log(err);
      }).then((guilds) =>
      {
        context.guilds = guilds;
        //database query for guild info (parameter and nsfw flag)
        //console.log(guilds);
        if(guilds != null)
        {
          guildsWithPerms[req.sessionID] = guilds.map((guild) =>
          {
            return guild.id;
          });
        }

        //console.log(guildsWithPerms[req.sessionID]);
        let userPage = fs.readFileSync('./lib/web/servers.hbs').toString();
        let renderedUserPage = Handlebars.compile(userPage);
        res.send(renderedUserPage(context));
      }).catch((err) =>
      {
        console.log(err);
      });
    }
    else
    {
      let userPage = fs.readFileSync('./lib/web/servers.hbs').toString();
      let renderedUserPage = Handlebars.compile(userPage);
      res.send(renderedUserPage(context));
    }
  });
  app.get('/server', (req, res, next) =>
  {
    let context = {};
    if(tokens[req.sessionID])
    {
      getUserInfo(req.sessionID).then((info) =>
      {
        if(req.query.serverID != undefined)
        {
          //console.log('query is defined');
          let serverID = req.query.serverID;
          if(guildsWithPerms[req.sessionID])
          {
            if(guildsWithPerms[req.sessionID].indexOf(serverID) > -1)
            {
              context.user = info;
              //query db for document(nedb lingo)
              return db.findServer(serverID);
            }
          }
        }
        context.user = info;

      }).then((server) =>
      {
        if(server != null)
        {
          context.server = server;
          try{
            context.serverName = bot.servers[server._id].name;
          }
          catch(err)
          {
            console.log(err);
          }
        }

        //console.log(server);
        let userPage = fs.readFileSync('./lib/web/server.hbs').toString();
        let renderedUserPage = Handlebars.compile(userPage);
        res.send(renderedUserPage(context));
      }).catch((err) =>
      {
        console.log(err);
      });
    }
    else
    {
      let userPage = fs.readFileSync('./lib/web/index.hbs').toString();
      let renderedUserPage = Handlebars.compile(userPage);
      res.send(renderedUserPage(context));
      //console.log(req.sessionID);
    }
  });
  app.post('/form',(req, res, next) =>
  {
    if(req.body.id)
    {
      if(tokens[req.sessionID])
      {
        if(guildsWithPerms[req.sessionID])
        {
          if(guildsWithPerms[req.sessionID].indexOf(req.body.id) > -1)
          {
            let postData = req.body;
            //you can do stuff cuz you have perms.
            //if it has id, it will have at least prefix as well
            //console.log("You actually have perms to do this, nice");
            db.findServer(postData.id).then((oldInfo) =>
            {
              if(oldInfo !== null)
              {
                let done = oldInfo.commands.length - 1;
                let count = 0;
                for(let i = 0; i < oldInfo.commands.length; i ++)
                {
                  if(postData[oldInfo.commands[i].key])
                  {
                    //this means that the checkmard was on, so it'll be enabled
                    //console.log(`${postData[oldInfo.commands[i].key]} exists, going to enabled it`);
                    oldInfo.commands[i].enabled = true;
                  }
                  else
                  {
                    //this means that the command wasn't checked, so it's disabled
                    //console.log(`${postData[oldInfo.commands[i].key]} doesn't exist, going to disable it`);
                    oldInfo.commands[i].enabled = false;
                  }
                  if(count === done)
                  {
                    let newData = {};
                    newData.prefix = postData.prefix;
                    if(postData.nsfw)
                    {
                      newData.nsfw = true;
                    }
                    else
                    {
                      newData.nsfw = false;
                    }
                    newData.commands = oldInfo.commands;
                    //console.log(newData);
                    db.updateServer(postData.id, newData);
                  }
                  count++;
                }
              }
            });
          }
        }
      }
    }
    //console.log(req.body);
    res.json(req.body);
  });
  app.get('/logout', (req, res, next) =>
  {
    if(tokens[req.sessionID])
    {
      delete tokens[req.sessionID];
      delete guildsWithPerms[req.sessionID];
    }
    res.redirect("http://hawkbot.cyberhawk.co");
  });
  https.createServer({
    key: privateKey,
    cert: certificate
  }, app).listen(443);
  // Redirect from http port 80 to https
  const http = require('http');
  http.createServer(function (req, res)
  {
    res.writeHead(301, { 'Location': 'https://' + req.headers['host'] + req.url });
    res.end();
  }).listen(80);
};

<|endoftext|>