// TODO make server uri configurable
const serverUri = 'wss://lit-inlet-44494.herokuapp.com/';
const baseUrl = document.createElement('a');
baseUrl.href = document.URL;
let socket;
let nodesByUrl  = {};
let pendingUrls = [];
let observer    = new MutationObserver(mutations => {
    mutations.forEach(mutation => mutation.addedNodes.forEach(processNode));
});

document.body.childNodes.forEach(processNode);
observer.observe(document.body, {childList: true, subtree: true});

/**
 * Check if node is a DOM element and is visible.
 * Try to find image URL in the element and add it to the processing queue.
 * Traverse all node children recursively.
 *
 * @param Node node to process
 */
function processNode(node) {
    if (node.nodeType == Node.ELEMENT_NODE) {
        if (node.nodeName == 'IMG') {
            const imageUrl = node.getAttribute('src') || node.getAttribute('data-src');
            requestImageCompression(imageUrl, node);
        } else {
            const styleUrl = node.hasAttribute('data-style')
                ? node.getAttribute('data-style')
                : window.getComputedStyle(node).backgroundImage;
            const urlMatch = styleUrl.match(/url\((.*)\)/i);
            if (urlMatch && urlMatch.length) {
                const imageUrl = urlMatch[1].replace(/["']/g, '', urlMatch[1]);
                requestImageCompression(imageUrl, node);
            }
        }

        if (node.hasChildNodes()) {
            node.childNodes.forEach(processNode);
        }
    }
}

/**
 * Add image URL to the processing queue and
 * keep reference of all nodes displaying this image,
 * so that we can update those nodes when we receive
 * compressed image back from server.
 *
 * @param String imageUrl original image URL
 * @param Node   node     node displaying the image
 */
function requestImageCompression(imageUrl, node) {
    if (node.hasAttribute('data-original-image')) return;
    imageUrl = normalizeUrl(imageUrl);
    if (!nodesByUrl[imageUrl]) {
        nodesByUrl[imageUrl] = [];
        pendingUrls.push(imageUrl);
    }
    nodesByUrl[imageUrl].push(node);
    processPendingUrls();

    function normalizeUrl(input) {
        if (input.startsWith('//')) {
            return `${baseUrl.protocol}${input}`;
        } else if (input.startsWith('/')) {
            return `${baseUrl.protocol}//${baseUrl.host}${input}`;
        }
        return input;
    }
}

/**
 * Connect to compressing server if it's not connected yet,
 * and send all queued image URLs to the server.
 */
function processPendingUrls() {
    if (!socket || socket.readyState !== WebSocket.OPEN) {
        if (!socket || socket.readyState !== WebSocket.CONNECTING) connect();
        return;
    }

    while (pendingUrls.length > 0) {
        socket.send(pendingUrls.pop());
    }
}

/**
 * Receive response from the server, which should have following format:
 * {
 *   "original": "<original_image_url>",
 *   "compressed": "<compressed_image_url>"
 * }
 *
 * Look up node corresponding to the original image and update it.
 *
 * @param String response JSON as a string
 */
function processResponse(response) {
    const data  = JSON.parse(response.data);
    const nodes = nodesByUrl[data.original];
    if (nodes && nodes.length) {
        while (nodes.length > 0) {
            const node = nodes.pop();
            node.setAttribute('data-original-image', data.original);
            if (node.nodeName == 'IMG') {
                node.src = data.compressed;
            } else {
                node.style.backgroundImage = `url('${data.compressed}')`;
            }
        }
        delete nodesByUrl[data.original];
    }
}

/**
 * Connect to server via WebSocket and assign event handlers.
 */
function connect() {
    socket = new WebSocket(serverUri);

    socket.onopen    = processPendingUrls;
    socket.onmessage = processResponse;
}

@@ -102,16 +102,18 @@
 baseUrl 
+  
 = docume
@@ -148,16 +148,19 @@
 rl.href 
+   
 = docume
@@ -1908,86 +1908,64 @@
 ge')
-) return;%0A    imageUrl = normalizeUrl(imageUrl);%0A    if (!nodesByUrl%5BimageUrl%5D
+ %7C%7C !imageUrl) return;%0A    if (imageUrl.startsWith('//')
 ) %7B%0A
@@ -1972,35 +1972,24 @@
         
-nodesByUrl%5B
 imageUrl
 %5D = %5B%5D;%0A
@@ -1984,49 +1984,41 @@
 eUrl
-%5D
  = 
-%5B%5D;%0A        pending
+%60$%7Bbase
 Url
-s
 .p
-ush(
+rotocol%7D$%7B
 imageUrl
 );%0A 
@@ -2017,133 +2017,35 @@
 eUrl
-)
+%7D%60
 ;%0A    %7D
-%0A    nodesByUrl%5BimageUrl%5D.push(node);%0A    processPendingUrls();%0A%0A    function normalizeUrl(input) %7B%0A        if (input
+ else if (imageUrl
 .sta
@@ -2046,33 +2046,32 @@
 rl.startsWith('/
-/
 ')) %7B%0A          
@@ -2064,34 +2064,34 @@
 ) %7B%0A        
-  
-  return
+imageUrl =
  %60$%7BbaseUrl.
@@ -2099,31 +2099,51 @@
 rotocol%7D
-$%7Binput
+//$%7BbaseUrl.host%7D$%7BimageUrl
 %7D%60;%0A    
     %7D el
@@ -2138,44 +2138,39 @@
     
+%7D%0A
     
-%7D else if (input.startsWith('/')
+if (!nodesByUrl%5BimageUrl%5D
 ) %7B%0A
@@ -2181,104 +2181,139 @@
     
-  
-  return %60$%7BbaseUrl.protocol%7D//$%7BbaseUrl.host%7D$%7Binput%7D%60;%0A        %7D
+nodesByUrl%5BimageUrl%5D = %5B%5D;%0A        pendingUrls.push(imageUrl);
 %0A    
+%7D%0A
     
-return input;%0A    %7D
+nodesByUrl%5BimageUrl%5D.push(node);%0A    processPendingUrls();
 %0A%7D%0A%0A

Fix relative url processing