<commit_before>var testEnvironment = false;

//START DEFINING FUNCTIONS

//Playground for context menu

//Opens the options page so that the .js can run on that page. Once that's create, callback function sends a message containing the keyword
//that the options page can grab

function sendKeywordMessage(selectionText) {
  chrome.runtime.sendMessage({"newKeyword":selectionText},function(response){
    if (!response) {
      console.log("Options page hasn't received it. Resending message!");
      sendKeywordMessage(selectionText);
    } else {
      console.log("Keyword successfully received by options page.");
    }
  });
}

function sendKeyword(info,tab) {
  var extId = chrome.i18n.getMessage("@@extension_id"); //Gets the extension ID for opening the options page. Not required for creating the tab, but required for checking to see if the URL is open
  console.log("Word " + info.selectionText + " was clicked.");
  chrome.tabs.create({
      url: "chrome-extension://" + extId + "/options/interests.html",
      active: false
  }, function(){
      chrome.tabs.query({}, function (tab){ //This is necessary to make sure the page fully loads in the browser before the message is sent
        for(var i =0; i < tab.length; i++) {
          if (tab[i].url === "chrome-extension://" + extId + "/options/interests.html") {
            console.log("Looks like the page has loaded. Sending the message!");
            sendKeywordMessage(info.selectionText); //Recursive function that sends the message until response is received
            break;
          }
        }
      });
  });
}

//Creates the context menu, and runs the sendKeyword function once a keyword has been selected
chrome.contextMenus.create({
    title: "Add to DBot Database: %s",
    contexts:["selection"],
    onclick: sendKeyword,
});

//If can find a keyword in the blob of context, push the keyword to one array, and RegEx the context into another array. Pass both of those arrays back.
function extractMatchedKeywords(response, keywords) {
  var matchedKeywords = [];
  var findKeyword;
  for (var i = 0; i < keywords.length; i++) {
    findKeyword = new RegExp('[^a-zA-Z]' + keywords[i] + '[^a-zA-Z]', 'g');
    if (response.search(findKeyword) != -1) {
      matchedKeywords.push(keywords[i]);
    }
  }
  console.log(matchedKeywords);
  return matchedKeywords;
}

function findEssayTitle(keyword, essays){
  console.log(keyword);
  console.log(essays);
  var final;
  var keywordRe = new RegExp(keyword, 'i');

  for (var i = 0; i < essays.length; i++) {
    var essay = essays[i].essay;
    console.log("Now looking through this essay: ", essay);
    if (essay.search(keywordRe) != -1) {
      console.log("Found a match for " + keyword + ": ", + essays[i].name);
      final = '' + essays[i].name + '
';
      return final;
    }
  }
  return "Unknown
";
}

function extractContext(response, keywords, essays) {
  console.log("This is what we're extracting context from: ", response);
  var contextArr = [];
  var findKeyword;
  var essayTitle;
  var final;

  for (var i = 0; i < keywords.length; i++) {
    findKeyword = new RegExp('([^a-zA-Z]|\n|\r|\r\n)' + keywords[i] + '([^a-zA-Z]|\n|\r|\r\n)', 'g');

    if (response.search(findKeyword) != -1) {

      essayTitle = findEssayTitle(keywords[i], essays);
      var contextGrabber = response.match(new RegExp('\S{0,10}(\n|.){0,50}([^a-zA-Z]|\n|\r|\r\n)' + keywords[i] + '([^a-zA-Z]|\n|\r|\r\n)(\n|.){0,50}\S{0,10}', 'g')); //This RegEx finds the keyword, and on either side, adds a space (to capture only the whole word), and then captures all line breaks or characters 50 characters in either direction. Then, extends up to another 10 characters to finish at the nearest whole word

      //console.log("contextGrabber for the keyword " + keywords[i] + ": ", contextGrabber);
      //contextGrabber[0] = contextGrabber[0].replace(/(\r\n|\n|\r)/gm," / "); //Replaces line breaks with a space to prevent code from being broken
      final = essayTitle + contextGrabber[0];
      contextArr.push(final);
    }
  }

  return contextArr;
}

//END DEFINING FUNCTIONS

//START LISTENERS AND DATA PROCESSING
//Set a listener that will capture the getProfile request of the popup and content from the content_script
var okcText = "";
var okcUserName = "";
var okcContext = "";

//Event handler for tab changes. Checks if current tab is the messages page on OKC. If so, initiates inbox scrape
chrome.tabs.onUpdated.addListener(function(tabId,changeInfo,tab){
  console.log("Tab has changed!");
  chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
    console.log(tabs[0]);
    if (tabs[0].url == "http://www.okcupid.com/messages") {
      console.log("This URL is the messages page!");
      var tabId = tabs[0].id;

      //Send a message to the identified tab to scrape for messages
      chrome.tabs.sendMessage(tabId, {action:"scrapeMessages"},function(response){
        console.log("Sent a message off to the content script and receive a response! It was: ", response);
        //chrome.runtime.sendMessage({finalresult:"ready"},function(response){});
      });

    }
  });
});

chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
  if(msg.content) {
    okcText = msg.content[0];
    okcUserName = msg.content[1];
    okcPicture = msg.content[2];
    okcContext = msg.content[3]; //This is an array of objects (essay title and essay)
    console.log(okcContext);
  }

  if(msg.method == "getProfile") {
    if (testEnvironment === false) {
      try {
        var profile = JSON.parse(localStorage["keywords"]);
      }
      catch(e) {
        console.log ("Warning! Error. Unable to parse localStorage['keywords']. Try saving your user settings again");
      }

    } else if (testEnvironment === true) {
      var profile = JSON.parse(localStorage["keywordsTestEnvironment"]);
    }
    var pairs = profile.pairs;

    var desiredKeywords = [];
    var desiredMessage = [];
    var finalKeywords, finalContext, okcResponse;
    var finalMessage = [];
    var finalResult = "";
    var opener = profile.opener;
    var closer = profile.closer;
    var firstTrans = profile.first_transition;
    var secondTrans = profile.second_transition;

    for (var i = 0; i < pairs.length; i++) {
      desiredKeywords.push(pairs[i].keyword);
      desiredMessage.push(pairs[i].message);
    }

    //console.log("opener: ", opener);
    //console.log("closer: ", closer);

    //Now, we create the final JSON object to pass to the popup for processing
    if (okcText) {
      okcResponse = okcText;

      finalKeywords = extractMatchedKeywords(okcResponse,desiredKeywords);
      finalContext = extractContext(okcResponse,desiredKeywords, okcContext);

      for (var i = 0; i < finalKeywords.length; i++) {
        if (desiredKeywords.indexOf(finalKeywords[i]) != -1) {
          var index = desiredKeywords.indexOf(finalKeywords[i]);
          finalMessage.push(desiredMessage[index]);
        }
        console.log("index: ", index);
        console.log(finalKeywords);
        console.log(finalMessage);
      }

      finalResult += '{"user":"' + okcUserName + '",';
      finalResult += '"age": 23,"location": "San Francisco, CA",';
      finalResult += '"pic":"' + okcPicture + '",';
      finalResult += '"opener":"' + opener + ' ",'; //extra space added to this phrase after the variable for proper final sentence structure
      finalResult += '"closer":"' + closer + ' ",'; //extra space added to this phrase after the variable for proper final sentence structure
      finalResult += '"first_transition":"' + firstTrans + ' ",';
      finalResult += '"second_transition":"' + secondTrans + ' ",';
      finalResult += '"matched": [';

      for (var i = 0; i < finalKeywords.length; i++) {
          finalResult += '{"keyword":"' + finalKeywords[i] + '",';
          finalResult += '"context":"' + finalContext[i] + '",';
          finalResult += '"message":"' + finalMessage[i] + '",';

          if (i == 0 || i == 1) {
            finalResult += '"checked":true}';
          } else {
            finalResult += '"checked":false}';
          }

          if (i == (finalKeywords.length - 1)) {
            console.log("The final keyword!");
          } else {
            finalResult += ",";
          } //Adds a comma for the JSON when the keyword is NOT the last keyword in the list
      }

      finalResult += ']}';
    }
    console.log(finalResult);
    sendResponse(finalResult);
  }

  if(msg.method == "triggerScript") {
    chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
      var tabId = tabs[0].id;
      console.log("Was able to get the current tab: ", tabs[0]);

      chrome.tabs.sendMessage(tabId, {action:"scrape"},function(response){
        console.log("Sent a message off to the content script and receive a response! It was: ", response);
        chrome.runtime.sendMessage({finalresult:"ready"},function(response){
            console.log("The deed has been done!");

        });
      });
    });
  }

  if(msg.database) {
      var babeObj = msg.database;
      console.log(babeObj);
      sendResponse("background.js successfully received babeObj!");

      //Old nodeJS CouchDB code
      // $.post("http://localhost:3000/newint", {"interaction": babeObj}, function(result){
      //   console.log("Posted the interaction to the local nodeJS server. The server wrote back: ", result);
      // });

      //TESTING CODE
      // $.post("http://localhost:14080/int", {"interaction": babeObj, "username": "jrrera"}, function(result){
      //   console.log("Posted the interaction to the App Engine server. The server wrote back: ", result);
      // });
      //END TESTING CODE

      $.post("http://dbotapp.appspot.com/int", {"interaction": babeObj, "username": "jrrera"}, function(result){
        console.log("Posted the interaction to the App Engine server. The server wrote back: ", result);
      });
  }

  if (msg.databaseKeywords) {
    var keywordObj = msg.databaseKeywords;
    console.log(keywordObj);

    sendResponse("background.js successfully received keywordObj!");

    //Old nodeJS CouchDB code
    // $.post("http://localhost:3000/keywords", {"keywords": keywordObj}, function(result){
    //   console.log("Posted the keywords to the server. The result: ", result);
    // });

    $.ajax({
        type: 'POST',
        url: 'http://dbotapp.appspot.com/keywords',
        data: JSON.stringify({
          "username":"jrrera",
          "keywords": keywordObj,
         }),
        contentType: 'application/json',
        success: function(data,textStatus, jqXHR) {
            console.log('POST response: ');
            console.log(data);
        }
    });
  }

  if (msg.request == "appengine_keywords") {
    $.get("http://dbotapp.appspot.com/keywords", function(result){
      // console.log("Results of AJAX call");
      // console.log(JSON.parse(result));

      try {
        var aeJson = JSON.parse(result);
        sendResponse(aeJson);
      } catch (e) {
        console.log('Warning! Could not parse App Engine\'s JSON! The error:', e);
        sendResponse('Warning! Could not parse App Engine\'s JSON! The error:', e);
      }
    });
    return true;
  }

  // if (msg.requestAllData) {
  //   $.post("http://localhost:3000/data", {"keywords": keywordObj}, function(result){
  //     console.log("Result of requesting all the data from the database:", result);
  //     sendResponse(result);
  //   });
  //   return true;
  // }

  //This listener is triggered when a list of messages in your inbox is sent from the content script to background.js
  if(msg.messages) {
    var names = msg.messages;
    console.log(msg.messages);

    //Check to see if any messages were found. If yes, send a post request to server.
    if (msg.messages == "Couldn't find any messages") {
      console.log(msg.messages)
    } else {
      // $.post("http://localhost:3000/messages", {"names": names}, function(result){
      //   console.log("Posted the list of names to the server. The result: ", result);
      // });
      $.post("http://dbotapp.appspot.com/messages", {"names": names, "username": "jrrera"}, function(result){
        console.log("Posted the list of names to the server. The result: ", result);
      });
    }
  }

  //Listener to grab current tab to make sure we don't do multiple rescans when one has already been done on the same profile
  if (msg.url) {
    console.log('msg.url listener has been initiated. Begin searching for current tab');
    chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
      console.log('We found the current tab. It is this object:', tabs[0]);
      var okcTabId = tabs[0].id;
      var okcTab = tabs[0].url;
      sendResponse(okcTab);
    });
    return true;
  }

  //Listener for porting the message from the extention into OKCupid's message box
  if (msg.portover) {
    var messageToPort = msg.portover;
    console.log('Time to port over!');
    chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
      var okcTabId = tabs[0].id;
      var okcTab = tabs[0].url;
      chrome.tabs.sendMessage(okcTabId, {'portover':messageToPort},function(response){
        console.log("Sent a message off to the content script to port the message overand receive a response! It was: ", response);
      });
    });
  }

  // //DEPRECATEDThis listener is triggered when it receives the go-ahead from popup.js to scrape the messages page
  // if(msg.method == "triggerMessageScrape") {
  // }
});<commit_msg>Fixes bug where background.js was looking in wrong place for locally stored keywords<commit_after>var testEnvironment = false;

//START DEFINING FUNCTIONS

//Playground for context menu

//Opens the options page so that the .js can run on that page. Once that's create, callback function sends a message containing the keyword
//that the options page can grab

function sendKeywordMessage(selectionText) {
  chrome.runtime.sendMessage({"newKeyword":selectionText},function(response){
    if (!response) {
      console.log("Options page hasn't received it. Resending message!");
      sendKeywordMessage(selectionText);
    } else {
      console.log("Keyword successfully received by options page.");
    }
  });
}

function sendKeyword(info,tab) {
  var extId = chrome.i18n.getMessage("@@extension_id"); //Gets the extension ID for opening the options page. Not required for creating the tab, but required for checking to see if the URL is open
  console.log("Word " + info.selectionText + " was clicked.");
  chrome.tabs.create({
      url: "chrome-extension://" + extId + "/options/interests.html",
      active: false
  }, function(){
      chrome.tabs.query({}, function (tab){ //This is necessary to make sure the page fully loads in the browser before the message is sent
        for(var i =0; i < tab.length; i++) {
          if (tab[i].url === "chrome-extension://" + extId + "/options/interests.html") {
            console.log("Looks like the page has loaded. Sending the message!");
            sendKeywordMessage(info.selectionText); //Recursive function that sends the message until response is received
            break;
          }
        }
      });
  });
}

//Creates the context menu, and runs the sendKeyword function once a keyword has been selected
chrome.contextMenus.create({
    title: "Add to DBot Database: %s",
    contexts:["selection"],
    onclick: sendKeyword,
});

//If can find a keyword in the blob of context, push the keyword to one array, and RegEx the context into another array. Pass both of those arrays back.
function extractMatchedKeywords(response, keywords) {
  var matchedKeywords = [];
  var findKeyword;
  for (var i = 0; i < keywords.length; i++) {
    findKeyword = new RegExp('[^a-zA-Z]' + keywords[i] + '[^a-zA-Z]', 'g');
    if (response.search(findKeyword) != -1) {
      matchedKeywords.push(keywords[i]);
    }
  }
  console.log(matchedKeywords);
  return matchedKeywords;
}

function findEssayTitle(keyword, essays){
  console.log(keyword);
  console.log(essays);
  var final;
  var keywordRe = new RegExp(keyword, 'i');

  for (var i = 0; i < essays.length; i++) {
    var essay = essays[i].essay;
    console.log("Now looking through this essay: ", essay);
    if (essay.search(keywordRe) != -1) {
      console.log("Found a match for " + keyword + ": ", + essays[i].name);
      final = '' + essays[i].name + '
';
      return final;
    }
  }
  return "Unknown
";
}

function extractContext(response, keywords, essays) {
  console.log("This is what we're extracting context from: ", response);
  var contextArr = [];
  var findKeyword;
  var essayTitle;
  var final;

  for (var i = 0; i < keywords.length; i++) {
    findKeyword = new RegExp('([^a-zA-Z]|\n|\r|\r\n)' + keywords[i] + '([^a-zA-Z]|\n|\r|\r\n)', 'g');

    if (response.search(findKeyword) != -1) {

      essayTitle = findEssayTitle(keywords[i], essays);
      var contextGrabber = response.match(new RegExp('\S{0,10}(\n|.){0,50}([^a-zA-Z]|\n|\r|\r\n)' + keywords[i] + '([^a-zA-Z]|\n|\r|\r\n)(\n|.){0,50}\S{0,10}', 'g')); //This RegEx finds the keyword, and on either side, adds a space (to capture only the whole word), and then captures all line breaks or characters 50 characters in either direction. Then, extends up to another 10 characters to finish at the nearest whole word

      //console.log("contextGrabber for the keyword " + keywords[i] + ": ", contextGrabber);
      //contextGrabber[0] = contextGrabber[0].replace(/(\r\n|\n|\r)/gm," / "); //Replaces line breaks with a space to prevent code from being broken
      final = essayTitle + contextGrabber[0];
      contextArr.push(final);
    }
  }

  return contextArr;
}

//END DEFINING FUNCTIONS

//START LISTENERS AND DATA PROCESSING
//Set a listener that will capture the getProfile request of the popup and content from the content_script
var okcText = "";
var okcUserName = "";
var okcContext = "";

//Event handler for tab changes. Checks if current tab is the messages page on OKC. If so, initiates inbox scrape
chrome.tabs.onUpdated.addListener(function(tabId,changeInfo,tab){
  console.log("Tab has changed!");
  chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
    console.log(tabs[0]);
    if (tabs[0].url == "http://www.okcupid.com/messages") {
      console.log("This URL is the messages page!");
      var tabId = tabs[0].id;

      //Send a message to the identified tab to scrape for messages
      chrome.tabs.sendMessage(tabId, {action:"scrapeMessages"},function(response){
        console.log("Sent a message off to the content script and receive a response! It was: ", response);
        //chrome.runtime.sendMessage({finalresult:"ready"},function(response){});
      });

    }
  });
});

chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
  if(msg.content) {
    okcText = msg.content[0];
    okcUserName = msg.content[1];
    okcPicture = msg.content[2];
    okcContext = msg.content[3]; //This is an array of objects (essay title and essay)
    console.log(okcContext);
  }

  if(msg.method == "getProfile") {
    if (testEnvironment === false) {
      try {
        var profile = JSON.parse(localStorage["dbotKeywords"]);
      }
      catch(e) {
        console.log ("Warning! Error. Unable to parse localStorage['keywords']. Try saving your user settings again");
      }

    } else if (testEnvironment === true) {
      var profile = JSON.parse(localStorage["keywordsTestEnvironment"]);
    }
    var pairs = profile.pairs;

    var desiredKeywords = [];
    var desiredMessage = [];
    var finalKeywords, finalContext, okcResponse;
    var finalMessage = [];
    var finalResult = "";
    var opener = profile.opener;
    var closer = profile.closer;
    var firstTrans = profile.first_transition;
    var secondTrans = profile.second_transition;

    for (var i = 0; i < pairs.length; i++) {
      desiredKeywords.push(pairs[i].keyword);
      desiredMessage.push(pairs[i].message);
    }

    //console.log("opener: ", opener);
    //console.log("closer: ", closer);

    //Now, we create the final JSON object to pass to the popup for processing
    if (okcText) {
      okcResponse = okcText;

      finalKeywords = extractMatchedKeywords(okcResponse,desiredKeywords);
      finalContext = extractContext(okcResponse,desiredKeywords, okcContext);

      for (var i = 0; i < finalKeywords.length; i++) {
        if (desiredKeywords.indexOf(finalKeywords[i]) != -1) {
          var index = desiredKeywords.indexOf(finalKeywords[i]);
          finalMessage.push(desiredMessage[index]);
        }
        console.log("index: ", index);
        console.log(finalKeywords);
        console.log(finalMessage);
      }

      finalResult += '{"user":"' + okcUserName + '",';
      finalResult += '"age": 23,"location": "San Francisco, CA",';
      finalResult += '"pic":"' + okcPicture + '",';
      finalResult += '"opener":"' + opener + ' ",'; //extra space added to this phrase after the variable for proper final sentence structure
      finalResult += '"closer":"' + closer + ' ",'; //extra space added to this phrase after the variable for proper final sentence structure
      finalResult += '"first_transition":"' + firstTrans + ' ",';
      finalResult += '"second_transition":"' + secondTrans + ' ",';
      finalResult += '"matched": [';

      for (var i = 0; i < finalKeywords.length; i++) {
          finalResult += '{"keyword":"' + finalKeywords[i] + '",';
          finalResult += '"context":"' + finalContext[i] + '",';
          finalResult += '"message":"' + finalMessage[i] + '",';

          if (i == 0 || i == 1) {
            finalResult += '"checked":true}';
          } else {
            finalResult += '"checked":false}';
          }

          if (i == (finalKeywords.length - 1)) {
            console.log("The final keyword!");
          } else {
            finalResult += ",";
          } //Adds a comma for the JSON when the keyword is NOT the last keyword in the list
      }

      finalResult += ']}';
    }
    console.log(finalResult);
    sendResponse(finalResult);
  }

  if(msg.method == "triggerScript") {
    chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
      var tabId = tabs[0].id;
      console.log("Was able to get the current tab: ", tabs[0]);

      chrome.tabs.sendMessage(tabId, {action:"scrape"},function(response){
        console.log("Sent a message off to the content script and receive a response! It was: ", response);
        chrome.runtime.sendMessage({finalresult:"ready"},function(response){
            console.log("The deed has been done!");

        });
      });
    });
  }

  if(msg.database) {
      var babeObj = msg.database;
      console.log(babeObj);
      sendResponse("background.js successfully received babeObj!");

      //Old nodeJS CouchDB code
      // $.post("http://localhost:3000/newint", {"interaction": babeObj}, function(result){
      //   console.log("Posted the interaction to the local nodeJS server. The server wrote back: ", result);
      // });

      //TESTING CODE
      // $.post("http://localhost:14080/int", {"interaction": babeObj, "username": "jrrera"}, function(result){
      //   console.log("Posted the interaction to the App Engine server. The server wrote back: ", result);
      // });
      //END TESTING CODE

      $.post("http://dbotapp.appspot.com/int", {"interaction": babeObj, "username": "jrrera"}, function(result){
        console.log("Posted the interaction to the App Engine server. The server wrote back: ", result);
      });
  }

  if (msg.databaseKeywords) {
    var keywordObj = msg.databaseKeywords;
    console.log(keywordObj);

    sendResponse("background.js successfully received keywordObj!");

    //Old nodeJS CouchDB code
    // $.post("http://localhost:3000/keywords", {"keywords": keywordObj}, function(result){
    //   console.log("Posted the keywords to the server. The result: ", result);
    // });

    $.ajax({
        type: 'POST',
        url: 'http://dbotapp.appspot.com/keywords',
        data: JSON.stringify({
          "username":"jrrera",
          "keywords": keywordObj,
         }),
        contentType: 'application/json',
        success: function(data,textStatus, jqXHR) {
            console.log('POST response: ');
            console.log(data);
        }
    });
  }

  if (msg.request == "appengine_keywords") {
    $.get("http://dbotapp.appspot.com/keywords", function(result){
      // console.log("Results of AJAX call");
      // console.log(JSON.parse(result));

      try {
        var aeJson = JSON.parse(result);
        sendResponse(aeJson);
      } catch (e) {
        console.log('Warning! Could not parse App Engine\'s JSON! The error:', e);
        sendResponse('Warning! Could not parse App Engine\'s JSON! The error:', e);
      }
    });
    return true;
  }

  // if (msg.requestAllData) {
  //   $.post("http://localhost:3000/data", {"keywords": keywordObj}, function(result){
  //     console.log("Result of requesting all the data from the database:", result);
  //     sendResponse(result);
  //   });
  //   return true;
  // }

  //This listener is triggered when a list of messages in your inbox is sent from the content script to background.js
  if(msg.messages) {
    var names = msg.messages;
    console.log(msg.messages);

    //Check to see if any messages were found. If yes, send a post request to server.
    if (msg.messages == "Couldn't find any messages") {
      console.log(msg.messages)
    } else {
      // $.post("http://localhost:3000/messages", {"names": names}, function(result){
      //   console.log("Posted the list of names to the server. The result: ", result);
      // });
      $.post("http://dbotapp.appspot.com/messages", {"names": names, "username": "jrrera"}, function(result){
        console.log("Posted the list of names to the server. The result: ", result);
      });
    }
  }

  //Listener to grab current tab to make sure we don't do multiple rescans when one has already been done on the same profile
  if (msg.url) {
    console.log('msg.url listener has been initiated. Begin searching for current tab');
    chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
      console.log('We found the current tab. It is this object:', tabs[0]);
      var okcTabId = tabs[0].id;
      var okcTab = tabs[0].url;
      sendResponse(okcTab);
    });
    return true;
  }

  //Listener for porting the message from the extention into OKCupid's message box
  if (msg.portover) {
    var messageToPort = msg.portover;
    console.log('Time to port over!');
    chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
      var okcTabId = tabs[0].id;
      var okcTab = tabs[0].url;
      chrome.tabs.sendMessage(okcTabId, {'portover':messageToPort},function(response){
        console.log("Sent a message off to the content script to port the message overand receive a response! It was: ", response);
      });
    });
  }

  // //DEPRECATEDThis listener is triggered when it receives the go-ahead from popup.js to scrape the messages page
  // if(msg.method == "triggerMessageScrape") {
  // }
});<|endoftext|>


<commit_before>var currentNotification;

var CLIPBOARD_DATA_KEY = 'clipboard-data';

chrome.browserAction.onClicked.addListener(function() {
  var clipboardData = getClipboardData();
  getClientInfo(function(clientInfo) {
    var storageData = {};
    storageData[CLIPBOARD_DATA_KEY] = {
      clientInfo: clientInfo,
      value: clipboardData,
      // We add the timestamp to the stored data to make sure that copying the
      // same thing as what was last copying still generates a change
      // notification that gets synced.
      timestamp: Date.now()
    };

    // TODO(mihaip): check for rate limiting.
    chrome.storage.sync.set(storageData);
    chrome.browserAction.setIcon({path: 'icon19-uploading.png'});

    // We're going to reset the icon when the data is copied, but if it never
    // is (or if we somehow miss that notification), then make sure that it does
    // eventually go back to normal.
    // TODO(mihaip): use alarm API when switching to event pages.
    setTimeout(function() {
      chrome.browserAction.setIcon({path: 'icon19.png'});
    }, 30 * 1000);
  });
});

chrome.storage.onChanged.addListener(function(changes, storageNamespace) {
  if (storageNamespace != 'sync'|| !CLIPBOARD_DATA_KEY in changes) {
    return;
  }

  var data = changes[CLIPBOARD_DATA_KEY].newValue;

  // Another client copied the data, therefore we can reset the icon and dismiss
  // our notification (if any).
  if (!data) {
    chrome.browserAction.setIcon({path: 'icon19.png'});
    if (currentNotification) {
      currentNotification.close();
      currentNotification = undefined;
    }
    return;
  }

  var sourceClientInfo = data.clientInfo;
  var clipboardData = data.value;

  getClientInfo(function(clientInfo) {
    if (clientInfo.id == sourceClientInfo.id) {
      return;
    }

    var clipboardDataSnippet = clipboardData;
    if (clipboardDataSnippet.length > 20) {
      clipboardDataSnippet = clipboardDataSnippet.substring(0, 20) + '…';
    }

    chrome.browserAction.setIcon({path: 'icon19-downloading.png'});

    // We use the legacy notifications API since it supports icon URLs. The
    // new one doesn't (as implemented in WebKit).
    currentNotification = webkitNotifications.createNotification(
        chrome.extension.getURL('icon32.png'),
        'Clipboard pushed from ' + sourceClientInfo.name,
        'Click to copy "' + clipboardDataSnippet + '"');
    currentNotification.show();
    currentNotification.onclick = function() {
      setClipboardData(clipboardData);
      currentNotification.close();
      currentNotification = undefined;

      chrome.browserAction.setIcon({path: 'icon19.png'});

      // Remove the synced data as a way of notifying the other clients that
      // they can dismiss their notifications.
      chrome.storage.sync.remove(CLIPBOARD_DATA_KEY);
    }
  });
});

function getClipboardData() {
  var bufferNode = document.getElementById('buffer');
  bufferNode.value = '';
  bufferNode.focus();
  bufferNode.selectionStart = bufferNode.selectionEnd = 0;
  if (!document.execCommand('paste')) {
    // TODO(mihaip): error handling
    alert('Couldn\'t paste into buffer');
    return;
  }
  return bufferNode.value;
}

function setClipboardData(clipboardData) {
  var bufferNode = document.getElementById('buffer');
  bufferNode.value = clipboardData;
  bufferNode.focus();
  bufferNode.selectionStart = 0;
  bufferNode.selectionEnd = clipboardData.length;
  if (!document.execCommand('copy')) {
    // TODO(mihaip): error handling
    alert('Couldn\'t copy from buffer');
    return;
  }
}
<commit_msg>Don't show more than one notification.<commit_after>var currentNotification;

var CLIPBOARD_DATA_KEY = 'clipboard-data';

chrome.browserAction.onClicked.addListener(function() {
  var clipboardData = getClipboardData();
  getClientInfo(function(clientInfo) {
    var storageData = {};
    storageData[CLIPBOARD_DATA_KEY] = {
      clientInfo: clientInfo,
      value: clipboardData,
      // We add the timestamp to the stored data to make sure that copying the
      // same thing as what was last copying still generates a change
      // notification that gets synced.
      timestamp: Date.now()
    };

    // TODO(mihaip): check for rate limiting.
    chrome.storage.sync.set(storageData);
    chrome.browserAction.setIcon({path: 'icon19-uploading.png'});

    // We're going to reset the icon when the data is copied, but if it never
    // is (or if we somehow miss that notification), then make sure that it does
    // eventually go back to normal.
    // TODO(mihaip): use alarm API when switching to event pages.
    setTimeout(function() {
      chrome.browserAction.setIcon({path: 'icon19.png'});
    }, 30 * 1000);
  });
});

chrome.storage.onChanged.addListener(function(changes, storageNamespace) {
  if (storageNamespace != 'sync'|| !CLIPBOARD_DATA_KEY in changes) {
    return;
  }

  var data = changes[CLIPBOARD_DATA_KEY].newValue;

  // Another client copied the data, therefore we can reset the icon and dismiss
  // our notification (if any).
  if (!data) {
    chrome.browserAction.setIcon({path: 'icon19.png'});
    if (currentNotification) {
      currentNotification.close();
      currentNotification = undefined;
    }
    return;
  }

  var sourceClientInfo = data.clientInfo;
  var clipboardData = data.value;

  getClientInfo(function(clientInfo) {
    if (clientInfo.id == sourceClientInfo.id) {
      return;
    }

    var clipboardDataSnippet = clipboardData;
    if (clipboardDataSnippet.length > 20) {
      clipboardDataSnippet = clipboardDataSnippet.substring(0, 20) + '…';
    }

    chrome.browserAction.setIcon({path: 'icon19-downloading.png'});

    // We use the legacy notifications API since it supports icon URLs. The
    // new one doesn't (as implemented in WebKit).
    currentNotification = webkitNotifications.createNotification(
        chrome.extension.getURL('icon32.png'),
        'Clipboard pushed from ' + sourceClientInfo.name,
        'Click to copy "' + clipboardDataSnippet + '"');
    currentNotification.replaceId = CLIPBOARD_DATA_KEY;
    currentNotification.show();
    currentNotification.onclick = function() {
      setClipboardData(clipboardData);
      currentNotification.close();
      currentNotification = undefined;

      chrome.browserAction.setIcon({path: 'icon19.png'});

      // Remove the synced data as a way of notifying the other clients that
      // they can dismiss their notifications.
      chrome.storage.sync.remove(CLIPBOARD_DATA_KEY);
    }
  });
});

function getClipboardData() {
  var bufferNode = document.getElementById('buffer');
  bufferNode.value = '';
  bufferNode.focus();
  bufferNode.selectionStart = bufferNode.selectionEnd = 0;
  if (!document.execCommand('paste')) {
    // TODO(mihaip): error handling
    alert('Couldn\'t paste into buffer');
    return;
  }
  return bufferNode.value;
}

function setClipboardData(clipboardData) {
  var bufferNode = document.getElementById('buffer');
  bufferNode.value = clipboardData;
  bufferNode.focus();
  bufferNode.selectionStart = 0;
  bufferNode.selectionEnd = clipboardData.length;
  if (!document.execCommand('copy')) {
    // TODO(mihaip): error handling
    alert('Couldn\'t copy from buffer');
    return;
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Boris Smus. 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.
 */

var registeredTabs = [];
var registerTab = function (tabId) {
    if (registeredTabs.indexOf(tabId) == -1) {
        registeredTabs.push(tabId);

        chrome.pageAction.show(tabId);
        chrome.pageAction.setIcon({
            tabId: tabId,
            path: {
                '19': 'icons/icon19.png',
                '38': 'icons/icon38.png'
            }
        });
        chrome.pageAction.setTitle({
            tabId: tabId,
            title: 'Click to disable media keys for this tab'
        });
    }
};
var unregisterTab = function(tabId) {
    var index = registeredTabs.indexOf(tabId);
    if (index > -1) {
        registeredTabs.splice(index, 1);

        chrome.pageAction.setIcon({
            tabId: tabId,
            path: {
                '19': 'icons/icon19-inactive.png',
                '38': 'icons/icon38-inactive.png'
            }
        });
        chrome.pageAction.setTitle({
            tabId: tabId,
            title: 'Click to enable media keys for this tab'
        });
    }
};

chrome.commands.onCommand.addListener(function(command) {
    console.log('Command:', command);

    for (var i = 0; i < registeredTabs.length; i++) {
        chrome.tabs.sendMessage(registeredTabs[i], {command: command});
    }
});

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {
        console.log('Received tab message: ', request);

        if (request.command == 'registerTab' && sender.tab) {
            registerTab(sender.tab.id);
        } else if (request.command == 'unregisterTab' && sender.tab) {
            unregisterTab(sender.tab.id);
        }
    }
);

chrome.tabs.onRemoved.addListener(unregisterTab);

chrome.pageAction.onClicked.addListener(function(tab) {
    var index = registeredTabs.indexOf(tab.id);
    if (index < 0) {
        registerTab(tab.id);
    } else {
        unregisterTab(tab.id);
    }
});

chrome.contextMenus.create({id: "keySocketMediaKeys-group", title: "Key Socket Media Keys"});

chrome.contextMenus.create({parentId: "keySocketMediaKeys-group", id: "keySocketMediaKeys-disableThisTab", title: "Disable for this tab", onclick: function(a, tab){
    unregisterTab(tab.id);
}});
chrome.contextMenus.create({parentId: "keySocketMediaKeys-group", id: "keySocketMediaKeys-enableThisTab", title: "Enable for this tab", onclick: function(a, tab){
    registerTab(tab.id);
}});

chrome.contextMenus.create({parentId: "keySocketMediaKeys-group", id: "keySocketMediaKeys-separator1", type: "separator"});

chrome.contextMenus.create({parentId: "keySocketMediaKeys-group", id: "keySocketMediaKeys-disableAllTabs", title: "Disable for all tabs", onclick: function(a, tab){
    chrome.tabs.getAllInWindow(null, function(tabs){
        for (var i = 0; i < tabs.length; i++) {
            unregisterTab(tabs[i].id);
        }
    });
}});
chrome.contextMenus.create({parentId: "keySocketMediaKeys-group", id: "keySocketMediaKeys-enableAllTabs", title: "Enable for all tabs", onclick: function(a, tab){
    chrome.tabs.getAllInWindow(null, function(tabs){
        for (var i = 0; i < tabs.length; i++) {
            registerTab(tabs[i].id);
        }
    });
}});<commit_msg>Enable/disable all but this tab choice<commit_after>/*
 * Copyright 2015 Boris Smus. 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.
 */

var registeredTabs = [];
var registerTab = function (tabId) {
    if (registeredTabs.indexOf(tabId) == -1) {
        registeredTabs.push(tabId);

        chrome.pageAction.show(tabId);
        chrome.pageAction.setIcon({
            tabId: tabId,
            path: {
                '19': 'icons/icon19.png',
                '38': 'icons/icon38.png'
            }
        });
        chrome.pageAction.setTitle({
            tabId: tabId,
            title: 'Click to disable media keys for this tab'
        });
    }
};
var unregisterTab = function(tabId) {
    var index = registeredTabs.indexOf(tabId);
    if (index > -1) {
        registeredTabs.splice(index, 1);

        chrome.pageAction.setIcon({
            tabId: tabId,
            path: {
                '19': 'icons/icon19-inactive.png',
                '38': 'icons/icon38-inactive.png'
            }
        });
        chrome.pageAction.setTitle({
            tabId: tabId,
            title: 'Click to enable media keys for this tab'
        });
    }
};

chrome.commands.onCommand.addListener(function(command) {
    console.log('Command:', command);

    for (var i = 0; i < registeredTabs.length; i++) {
        chrome.tabs.sendMessage(registeredTabs[i], {command: command});
    }
});

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {
        console.log('Received tab message: ', request);

        if (request.command == 'registerTab' && sender.tab) {
            registerTab(sender.tab.id);
        } else if (request.command == 'unregisterTab' && sender.tab) {
            unregisterTab(sender.tab.id);
        }
    }
);

chrome.tabs.onRemoved.addListener(unregisterTab);

chrome.pageAction.onClicked.addListener(function(tab) {
    var index = registeredTabs.indexOf(tab.id);
    if (index < 0) {
        registerTab(tab.id);
    } else {
        unregisterTab(tab.id);
    }
});

chrome.contextMenus.create({id: "keySocketMediaKeys-group", title: "Key Socket Media Keys"});

chrome.contextMenus.create({parentId: "keySocketMediaKeys-group", id: "keySocketMediaKeys-disableThisTab", title: "Disable this tab", onclick: function(a, tab){
    unregisterTab(tab.id);
}});
chrome.contextMenus.create({parentId: "keySocketMediaKeys-group", id: "keySocketMediaKeys-enableThisTab", title: "Enable this tab", onclick: function(a, tab){
    registerTab(tab.id);
}});

chrome.contextMenus.create({parentId: "keySocketMediaKeys-group", id: "keySocketMediaKeys-separator1", type: "separator"});

chrome.contextMenus.create({parentId: "keySocketMediaKeys-group", id: "keySocketMediaKeys-disableAllTabs", title: "Disable all tabs", onclick: function(a, tab){
    chrome.tabs.getAllInWindow(null, function(tabs){
        for (var i = 0; i < tabs.length; i++) {
            unregisterTab(tabs[i].id);
        }
    });
}});
chrome.contextMenus.create({parentId: "keySocketMediaKeys-group", id: "keySocketMediaKeys-enableAllTabs", title: "Enable all tabs", onclick: function(a, tab){
    chrome.tabs.getAllInWindow(null, function(tabs){
        for (var i = 0; i < tabs.length; i++) {
            registerTab(tabs[i].id);
        }
    });
}});

chrome.contextMenus.create({parentId: "keySocketMediaKeys-group", id: "keySocketMediaKeys-separator2", type: "separator"});

chrome.contextMenus.create({parentId: "keySocketMediaKeys-group", id: "keySocketMediaKeys-disableAllBut", title: "Disable all but this tab", onclick: function(a, tab){
    chrome.tabs.getAllInWindow(null, function(tabs){
        for (var i = 0; i < tabs.length; i++) {
            if(tab.id !== tabs[i].id) {
                unregisterTab(tabs[i].id);
            }
        }
    });

    registerTab(tab.id);
}});

chrome.contextMenus.create({parentId: "keySocketMediaKeys-group", id: "keySocketMediaKeys-enableAllBut", title: "Enable all but this tab", onclick: function(a, tab){
    unregisterTab(tab.id);

    chrome.tabs.getAllInWindow(null, function(tabs){
        for (var i = 0; i < tabs.length; i++) {
            if(tab.id !== tabs[i].id) {
                registerTab(tabs[i].id);
            }
        }
    });
}});<|endoftext|>


<commit_before>const __bind = function(fn, me) {
  return function() {
    return fn.apply(me, arguments);
  };
};

class SomaPlayerBackground {
  constructor() {
    console.debug('initializing SomaPlayer background script');
    this.onTrack = __bind(this.onTrack, this);
    this.lastfm = SomaPlayerUtil.getLastfmConnection();
    this.createAudioTag();
    this.createTitleEl();
    this.createArtistEl();
  }

  createAudioTag() {
    this.audioTag = document.querySelector('audio');
    if (!this.audioTag) {
      console.debug('adding new audio tag');
      this.audioTag = document.createElement('audio');
      this.audioTag.setAttribute('autoplay', 'true');
      this.audioTag.setAttribute('data-station', '');
      document.body.appendChild(this.audioTag);
    }
  }

  createTitleEl() {
    this.titleEl = document.getElementById('title');
    if (!this.titleEl) {
      this.titleEl = document.createElement('div');
      this.titleEl.id = 'title';
      document.body.appendChild(this.titleEl);
    }
  }

  createArtistEl() {
    this.artistEl = document.getElementById('artist');
    if (!this.artistEl) {
      this.artistEl = document.createElement('div');
      this.artistEl.id = 'artist';
      document.body.appendChild(this.artistEl);
    }
  }

  play(station) {
    console.debug('playing station', station);
    this.resetTrackInfoIfNecessary(station);
    this.subscribe(station);
    this.socket.on('track', this.onTrack);
    this.audioTag.src = SomaPlayerConfig.somafm_station_url + station;
    this.audioTag.setAttribute('data-station', station);
    this.audioTag.removeAttribute('data-paused');
  }

  resetTrackInfoIfNecessary(station) {
    if (this.audioTag.getAttribute('data-station') === station) {
      return;
    }
    console.debug('changed station from',
                  this.audioTag.getAttribute('data-station'), 'to', station,
                  'clearing current track info');
    this.titleEl.textContent = '';
    this.artistEl.textContent = '';
  }

  displayCurrentTrack(station) {
    return SomaPlayerUtil.getCurrentTrackInfo(station).then(track => {
      this.titleEl.textContent = track.title;
      this.artistEl.textContent = track.artist;
    });
  }

  subscribe(station) {
    const emitSubscribe = () => {
      this.socket.emit('subscribe', station, response => {
        if (response.subscribed) {
          console.debug('subscribed to', station);
          this.displayCurrentTrack(station);
        } else {
          console.error('failed to subscribe to', station, response);
        }
      });
    };
    if (typeof this.socket === 'undefined') {
      console.debug('connecting to socket', SomaPlayerConfig.scrobbler_api_url);
      this.socket = io.connect(SomaPlayerConfig.scrobbler_api_url);
    }
    if (this.socket.connected) {
      emitSubscribe();
    } else {
      this.socket.on('connect', () => {
        emitSubscribe();
      });
    }
  }

  onTrack(track) {
    console.debug('new track:', track);
    this.titleEl.textContent = track.title;
    this.artistEl.textContent = track.artist;
    SomaPlayerUtil.getOptions().then(opts => {
      this.notifyOfTrack(track, opts);
      this.waitToScrobbleTrack(track, opts);
    });
  }

  waitToScrobbleTrack(track, opts) {
    let delay = 60000; // 60 seconds
    if (this.trackHasDuration(track)) {
      delay = track.duration / 2; // half the length of the song
    }
    console.debug('waiting', (delay / 1000), 'seconds to scrobble track');
    setTimeout(() => {
      this.scrobbleTrack(track, opts);
    }, delay);
  }

  notifyOfTrack(track, opts) {
    // Default to showing notifications, so if user has not saved preferences,
    // assume they want notifications.
    if (opts.notifications === false) {
      return;
    }
    const notificationOpts = {
      type: 'basic',
      title: track.artist,
      message: track.title,
      iconUrl: 'icon48.png'
    };
    const delay = 15000; // 15 seconds
    setTimeout(() => {
      chrome.notifications.create('', notificationOpts, () => {});
    }, delay);
  }

  trackHasDuration(track) {
    return typeof track.duration === 'number' && track.duration > 0;
  }

  getScrobbleData(track, opts) {
    // http://www.last.fm/api/show/track.scrobble
    const data = {
      artist: (track.artist || '').replace(/"/g, "'"),
      track: (track.title || '').replace(/"/g, "'"),
      user: opts.lastfm_user,
      chosenByUser: 0,
      timestamp: Math.round((new Date()).getTime() / 1000)
    };
    if (typeof track.trackMBID === 'string') {
      data.mbid = track.trackMBID;
    }
    if (this.trackHasDuration(track)) {
      const milliseconds = track.duration;
      data.duration = milliseconds / 1000;
    }
    if (typeof track.album === 'string') {
      data.album = track.album;
    }
    return data;
  }

  scrobbleTrack(track, opts) {
    if (!(opts.lastfm_session_key && opts.lastfm_user && opts.scrobbling)) {
      return;
    }
    console.debug('scrobbling track for Last.fm user', opts.lastfm_user);
    const data = this.getScrobbleData(track, opts);
    const auth = { key: opts.lastfm_session_key };
    return this.lastfm.track.scrobble(data, auth, {
      success() {
        let e;
        try {
          const iframeWin = document.querySelector('iframe').contentWindow;
          iframeWin.document.querySelector('form').submit();
          console.debug('scrobbled track', track.title, track.artist);
        } catch (_error) {
          // Mysterious second submit after scrobble form has already POSTed
          // to Last.fm and the iframe has its origin changed to
          // ws.audioscrobbler.com, which can't be touched by the extension.
          e = _error;
          if (e.name !== 'SecurityError') {
            throw e;
          }
        }
      },
      error(err) {
        console.error('failed to scrobble track', track, 'response:', err);
      }
    });
  }

  pause(station) {
    console.debug('pausing station', station);
    this.unsubscribe(station);
    this.audioTag.pause();
    this.audioTag.currentTime = 0;
    this.audioTag.setAttribute('data-paused', 'true');
  }

  clear() {
    const info = this.getInfo();
    this.unsubscribe(info.station);
    this.audioTag.pause();
    this.audioTag.currentTime = 0;
    this.audioTag.setAttribute('data-station', '');
    this.audioTag.removeAttribute('data-paused');
  }

  unsubscribe(station) {
    if (!(typeof station === 'string' && station.length > 0)) {
      return;
    }
    if (typeof this.socket === 'undefined') {
      return;
    }
    console.debug('unsubscribing from', station, '...');
    this.socket.emit('unsubscribe', station, response => {
      if (response.unsubscribed) {
        console.debug('unsubscribed from', station);
      } else {
        console.error('failed to unsubscribe from', station);
      }
    });
    console.debug('removing track listener');
    this.socket.removeListener('track', this.onTrack);
  }

  getInfo() {
    let station = '';
    if (this.audioTag) {
      station = this.audioTag.getAttribute('data-station') || '';
    }
    return {
      station,
      artist: this.artistEl.textContent,
      title: this.titleEl.textContent,
      paused: this.audioTag.hasAttribute('data-paused') || station === ''
    };
  }

  setStations(stations) {
    console.debug('set stations', stations);
    SomaPlayerUtil.getOptions().then(opts => {
      opts.stations = stations;
      SomaPlayerUtil.setOptions(opts);
    });
  }

  getStations() {
    return new Promise(resolve => {
      SomaPlayerUtil.getOptions().then(opts => {
        resolve(opts.stations);
      });
    });
  }

  fetchStations() {
    const url = `${SomaPlayerConfig.somafm_api_url}channels.json`;
    console.debug(`fetching channels list from ${url}`);
    return new Promise((resolve, reject) => {
      SomaPlayerUtil.getJSON(url).then(data => {
        console.debug('fetched stations list', data);
        const simpleStations = this.extractStations(data);
        this.setStations(simpleStations);
        resolve(simpleStations);
      }).catch((xhr, status, error) => {
        console.error('failed to fetch stations list', error);
        reject(error);
      });
    });
  }

  extractStations(data) {
    const stations = data.channels.map(station => {
      return { id: station.id, title: station.title };
    });
    stations.sort(this.stationCompare);
    return stations;
  }

  stationCompare(a, b) {
    return a.title.toLowerCase().localeCompare(b.title.toLowerCase());
  }
}

let somaPlayerBG;

document.addEventListener('DOMContentLoaded', () => {
  somaPlayerBG = new SomaPlayerBackground();
});

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (typeof somaPlayerBG === 'undefined') {
    return;
  }
  console.debug('received:', request.action, request);
  if (request.action === 'play') {
    somaPlayerBG.play(request.station);
    sendResponse();
    return true;
  }
  if (request.action === 'pause') {
    somaPlayerBG.pause(request.station);
    sendResponse();
    return true;
  }
  if (request.action === 'info') {
    const info = somaPlayerBG.getInfo();
    console.debug('info:', info);
    sendResponse(info);
    return true;
  }
  if (request.action === 'clear') {
    somaPlayerBG.clear();
    sendResponse();
    return true;
  }
  if (request.action === 'fetch_stations') {
    somaPlayerBG.fetchStations().then(stations => {
      sendResponse(stations);
    }).catch(error => {
      sendResponse(null, error);
    });
    return true;
  }
  if (request.action === 'get_stations') {
    somaPlayerBG.getStations().then(stations => {
      console.debug('got saved list of stations:', stations);
      sendResponse(stations);
    });
    return true;
  }
});
<commit_msg>Clear other notification timers before notifying<commit_after>const __bind = function(fn, me) {
  return function() {
    return fn.apply(me, arguments);
  };
};

class SomaPlayerBackground {
  constructor() {
    console.debug('initializing SomaPlayer background script');
    this.onTrack = __bind(this.onTrack, this);
    this.lastfm = SomaPlayerUtil.getLastfmConnection();
    this.createAudioTag();
    this.createTitleEl();
    this.createArtistEl();
  }

  createAudioTag() {
    this.audioTag = document.querySelector('audio');
    if (!this.audioTag) {
      console.debug('adding new audio tag');
      this.audioTag = document.createElement('audio');
      this.audioTag.setAttribute('autoplay', 'true');
      this.audioTag.setAttribute('data-station', '');
      document.body.appendChild(this.audioTag);
    }
  }

  createTitleEl() {
    this.titleEl = document.getElementById('title');
    if (!this.titleEl) {
      this.titleEl = document.createElement('div');
      this.titleEl.id = 'title';
      document.body.appendChild(this.titleEl);
    }
  }

  createArtistEl() {
    this.artistEl = document.getElementById('artist');
    if (!this.artistEl) {
      this.artistEl = document.createElement('div');
      this.artistEl.id = 'artist';
      document.body.appendChild(this.artistEl);
    }
  }

  play(station) {
    console.debug('playing station', station);
    this.resetTrackInfoIfNecessary(station);
    this.subscribe(station);
    this.socket.on('track', this.onTrack);
    this.audioTag.src = SomaPlayerConfig.somafm_station_url + station;
    this.audioTag.setAttribute('data-station', station);
    this.audioTag.removeAttribute('data-paused');
  }

  resetTrackInfoIfNecessary(station) {
    if (this.audioTag.getAttribute('data-station') === station) {
      return;
    }
    console.debug('changed station from',
                  this.audioTag.getAttribute('data-station'), 'to', station,
                  'clearing current track info');
    this.titleEl.textContent = '';
    this.artistEl.textContent = '';
  }

  displayCurrentTrack(station) {
    return SomaPlayerUtil.getCurrentTrackInfo(station).then(track => {
      this.titleEl.textContent = track.title;
      this.artistEl.textContent = track.artist;
    });
  }

  subscribe(station) {
    const emitSubscribe = () => {
      this.socket.emit('subscribe', station, response => {
        if (response.subscribed) {
          console.debug('subscribed to', station);
          this.displayCurrentTrack(station);
        } else {
          console.error('failed to subscribe to', station, response);
        }
      });
    };
    if (typeof this.socket === 'undefined') {
      console.debug('connecting to socket', SomaPlayerConfig.scrobbler_api_url);
      this.socket = io.connect(SomaPlayerConfig.scrobbler_api_url);
    }
    if (this.socket.connected) {
      emitSubscribe();
    } else {
      this.socket.on('connect', () => {
        emitSubscribe();
      });
    }
  }

  onTrack(track) {
    console.debug('new track:', track);
    this.titleEl.textContent = track.title;
    this.artistEl.textContent = track.artist;
    SomaPlayerUtil.getOptions().then(opts => {
      this.notifyOfTrack(track, opts);
      this.waitToScrobbleTrack(track, opts);
    });
  }

  waitToScrobbleTrack(track, opts) {
    let delay = 60000; // 60 seconds
    if (this.trackHasDuration(track)) {
      delay = track.duration / 2; // half the length of the song
    }
    console.debug('waiting', (delay / 1000), 'seconds to scrobble track');
    setTimeout(() => {
      this.scrobbleTrack(track, opts);
    }, delay);
  }

  notifyOfTrack(track, opts) {
    if (typeof this.notifyTimer !== 'undefined') {
      clearTimeout(this.notifyTimer);
    }
    // Default to showing notifications, so if user has not saved preferences,
    // assume they want notifications.
    if (opts.notifications === false) {
      return;
    }
    const notification = {
      type: 'basic',
      title: track.artist,
      message: track.title,
      iconUrl: 'icon48.png'
    };
    const delay = 15000; // 15 seconds
    console.debug('notifying in', (delay / 1000), 'seconds', notification);
    this.notifyTimer = setTimeout(() => {
      chrome.notifications.create('', notification, () => {});
    }, delay);
  }

  trackHasDuration(track) {
    return typeof track.duration === 'number' && track.duration > 0;
  }

  getScrobbleData(track, opts) {
    // http://www.last.fm/api/show/track.scrobble
    const data = {
      artist: (track.artist || '').replace(/"/g, "'"),
      track: (track.title || '').replace(/"/g, "'"),
      user: opts.lastfm_user,
      chosenByUser: 0,
      timestamp: Math.round((new Date()).getTime() / 1000)
    };
    if (typeof track.trackMBID === 'string') {
      data.mbid = track.trackMBID;
    }
    if (this.trackHasDuration(track)) {
      const milliseconds = track.duration;
      data.duration = milliseconds / 1000;
    }
    if (typeof track.album === 'string') {
      data.album = track.album;
    }
    return data;
  }

  scrobbleTrack(track, opts) {
    if (!(opts.lastfm_session_key && opts.lastfm_user && opts.scrobbling)) {
      return;
    }
    console.debug('scrobbling track for Last.fm user', opts.lastfm_user);
    const data = this.getScrobbleData(track, opts);
    const auth = { key: opts.lastfm_session_key };
    return this.lastfm.track.scrobble(data, auth, {
      success() {
        let e;
        try {
          const iframeWin = document.querySelector('iframe').contentWindow;
          iframeWin.document.querySelector('form').submit();
          console.debug('scrobbled track', track.title, track.artist);
        } catch (_error) {
          // Mysterious second submit after scrobble form has already POSTed
          // to Last.fm and the iframe has its origin changed to
          // ws.audioscrobbler.com, which can't be touched by the extension.
          e = _error;
          if (e.name !== 'SecurityError') {
            throw e;
          }
        }
      },
      error(err) {
        console.error('failed to scrobble track', track, 'response:', err);
      }
    });
  }

  pause(station) {
    console.debug('pausing station', station);
    this.unsubscribe(station);
    this.audioTag.pause();
    this.audioTag.currentTime = 0;
    this.audioTag.setAttribute('data-paused', 'true');
  }

  clear() {
    const info = this.getInfo();
    this.unsubscribe(info.station);
    this.audioTag.pause();
    this.audioTag.currentTime = 0;
    this.audioTag.setAttribute('data-station', '');
    this.audioTag.removeAttribute('data-paused');
  }

  unsubscribe(station) {
    if (!(typeof station === 'string' && station.length > 0)) {
      return;
    }
    if (typeof this.socket === 'undefined') {
      return;
    }
    console.debug('unsubscribing from', station, '...');
    this.socket.emit('unsubscribe', station, response => {
      if (response.unsubscribed) {
        console.debug('unsubscribed from', station);
      } else {
        console.error('failed to unsubscribe from', station);
      }
    });
    console.debug('removing track listener');
    this.socket.removeListener('track', this.onTrack);
  }

  getInfo() {
    let station = '';
    if (this.audioTag) {
      station = this.audioTag.getAttribute('data-station') || '';
    }
    return {
      station,
      artist: this.artistEl.textContent,
      title: this.titleEl.textContent,
      paused: this.audioTag.hasAttribute('data-paused') || station === ''
    };
  }

  setStations(stations) {
    console.debug('set stations', stations);
    SomaPlayerUtil.getOptions().then(opts => {
      opts.stations = stations;
      SomaPlayerUtil.setOptions(opts);
    });
  }

  getStations() {
    return new Promise(resolve => {
      SomaPlayerUtil.getOptions().then(opts => {
        resolve(opts.stations);
      });
    });
  }

  fetchStations() {
    const url = `${SomaPlayerConfig.somafm_api_url}channels.json`;
    console.debug(`fetching channels list from ${url}`);
    return new Promise((resolve, reject) => {
      SomaPlayerUtil.getJSON(url).then(data => {
        console.debug('fetched stations list', data);
        const simpleStations = this.extractStations(data);
        this.setStations(simpleStations);
        resolve(simpleStations);
      }).catch((xhr, status, error) => {
        console.error('failed to fetch stations list', error);
        reject(error);
      });
    });
  }

  extractStations(data) {
    const stations = data.channels.map(station => {
      return { id: station.id, title: station.title };
    });
    stations.sort(this.stationCompare);
    return stations;
  }

  stationCompare(a, b) {
    return a.title.toLowerCase().localeCompare(b.title.toLowerCase());
  }
}

let somaPlayerBG;

document.addEventListener('DOMContentLoaded', () => {
  somaPlayerBG = new SomaPlayerBackground();
});

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (typeof somaPlayerBG === 'undefined') {
    return;
  }
  console.debug('received:', request.action, request);
  if (request.action === 'play') {
    somaPlayerBG.play(request.station);
    sendResponse();
    return true;
  }
  if (request.action === 'pause') {
    somaPlayerBG.pause(request.station);
    sendResponse();
    return true;
  }
  if (request.action === 'info') {
    const info = somaPlayerBG.getInfo();
    console.debug('info:', info);
    sendResponse(info);
    return true;
  }
  if (request.action === 'clear') {
    somaPlayerBG.clear();
    sendResponse();
    return true;
  }
  if (request.action === 'fetch_stations') {
    somaPlayerBG.fetchStations().then(stations => {
      sendResponse(stations);
    }).catch(error => {
      sendResponse(null, error);
    });
    return true;
  }
  if (request.action === 'get_stations') {
    somaPlayerBG.getStations().then(stations => {
      console.debug('got saved list of stations:', stations);
      sendResponse(stations);
    });
    return true;
  }
});
<|endoftext|>


<commit_before>var loki    = require('lokijs');
var irc     = require('tmi.js');
var uuid    = require('uuid');
var express = require('express');

const util = require('util');
const digraph = require('../src/digraph');
const entity  = require('../src/entity');

var DirectedGraph = digraph.DirectedGraph;
var Entity        = entity.Entity;
//var Relationship  = entity.Relationship;

var twitchEvent = require('./twitch-event');
var JoinEvent   = twitchEvent.JoinEvent;
var PartEvent   = twitchEvent.PartEvent;
var HostEvent   = twitchEvent.HostEvent;
var ChatEvent   = twitchEvent.ChatEvent;
var ActionEvent = twitchEvent.ActionEvent;

console.dir(JoinEvent);

var User = (function(superclass) {
  util.inherits(User, superclass);
  function User(username) {
    return superclass.call(this, username, {username: username});
  }
  return User;
})(Entity);

var Channel = (function(superclass) {
  util.inherits(Channel, superclass);
  function Channel(username) {
    return superclass.call(this, username, {name: username});
  }
  return Channel;
})(Entity);

module.exports = function(emitter, username, secret, config) {

  var started = new Date();

  var lokiConfig = {
    autosave: true,
    autosaveInterval: 1000
  };

  // TODO: encapsulate collection configuration and init in twitch-collection.js

  var channeldb = new loki('twitch-channel.json', lokiConfig);
  var channels  = channeldb.addCollection('channels');

  var socialdb = new loki('twitch-' + username + '-social-graph.json');
  var entities = socialdb.addCollection('entities');
  var relationships = socialdb.addCollection('relationships');
  var socialGraph = new DirectedGraph("twitch-" + username + "-social");

  var userdb = new loki('twitch-' + username + '.json', lokiConfig);
  var users      = userdb.addCollection('users');
  var hosting    = userdb.addCollection('hosting');
  var follows    = userdb.addCollection('follow');
  var events     = userdb.addCollection('event');

  this.client = new irc.client(config);

  this.cacheChannel = function(name) {
    //var channel = channels.findObject({"name": {"$eq": name}});
    if (!channel) { // always cache channels for now
      client.api({
        url: "https://api.twitch.tv/kraken/channels/" + name
      }, function(err, res, body) {
        if (err) return console.error(err);
        var tchannel = JSON.parse(body);
        var channel = new Channel(name);
        channels.insert(channel);
        vres = socialGraph.addVertex(channel.toVertex());
        if (vres.error) return console.error(vres.error);
        //entities.insert(vres.result);
      });
      cacheChannelHosting(name);
      cacheChannelFollows(name);
    }
  }

  this.cacheChannelHosting = function(name) {

  }

  this.cacheChannelFollows = function(name) {

  }

  this.cacheUser = function(name) {
    var user = users.findObject({"username": {"$eq": name}});
    if (!user) {
      this.client.api({
        url: "https://api.twitch.tv/kraken/users/" + name
      }, function(err, res, body) {
        if (err) return console.error(err);
        var tuser = JSON.parse(body);
        var user = new User(tuser.username);
        users.insert(user);
        vres = socialGraph.addVertex(user.toVertex());
        if (vres.error) return console.error(vres.error);
        //entities.insert(vres.result);
      });
    }
  }

  function onConnected(address, port) {
    //var event = new Event({address: address, port: port});
    //even.eventType = "server-connected"; // FIXME: refactor Event constructor
    //console.dir(event);
  }

  function onJoin(channel, user) {
    var event = new JoinEvent(channel, user);
    events.insert(event);
    var user = users.findObject({"username": {"$eq": username}});
    cacheUser(user);
  }

  function onPart(channel, username) {
    var event = new PartEvent(channel, username);
    events.insert(event);
  };

  function onHosted(channel, user, viewers) {
    var ts = new Date();
    console.log("%s [%s] <%s> host %s", ts.toUTCString(), channel, user, viewers);
    var event = new HostEvent(channel, user, viewers);
    events.insert(event);
    cacheUser(user);
  }

  function onChat(channel, user, message, self) {
    var ts = new Date();
    console.log("%s [%s] <%s> %s", ts.toUTCString(), channel, user.username, message);
    var event = new ChatEvent(channel, user.username, message);
    events.insert(event);
    cacheUser(user.username);
    var commandPattern = new RegExp("^!\\w+");
    if (commandPattern.test(message)) {
      var command = commandPattern.exec(message);
      emitter.emit("command-dispatch", command, user.username, message);
    }
  }

  function onAction(channel, user, message, self) {
    var ts = new Date();
    console.log("%s [%s] <*%s> %s", ts.toUTCString(), channel, user.username, message);
    var event = new ActionEvent(channel, user.username, message);
    events.insert(event);
    cacheUser(user.username);
    var commandPattern = new RegExp("^!\\w+");
    if (commandPattern.test(message)) {
      var command = commandPattern.exec(message);
      emitter.emit("command-dispatch", command, user.username, message);
    }
  };

  this.say = function(channel, message) {
    this.client.say(channel, message);
  }

  this.client.on("connected", onConnected);
  this.client.on("join", onJoin);
  this.client.on("part", onPart);
  this.client.on("hosted", onHosted);
  this.client.on("chat", onChat);
  this.client.on("action", onAction);

  // still needed?
  emitter.on('cache-user', this.cacheUser);
  emitter.on('cache-channel', this.cacheChannel);
  emitter.on('cache-channel-hosting', this.cacheChannelHosting);
  emitter.on('cache-channel-follows', this.cacheChannelFollows);

  this.router = express.Router();

  /*
  this.router.get('/channel/:name', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(channels.find({"name": req.params.name}));
  });
  this.router.get('/channel/:name/hosting', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(hosting.find());
  });
  this.router.get('/channel/:name/follows', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(follows.find());
  });
*/
  // register new router for every new user identified so they get their own base loki file registered once

  this.connect = function() {
    this.client.connect();
  };

  return this;
};
<commit_msg>test import of Relationship and remove debug statement<commit_after>var loki    = require('lokijs');
var irc     = require('tmi.js');
var uuid    = require('uuid');
var express = require('express');

const util = require('util');
const digraph = require('../src/digraph');
const entity  = require('../src/entity');

var DirectedGraph = digraph.DirectedGraph;
var Entity        = entity.Entity;
var Relationship  = entity.Relationship;
//var Relationship  = entity.Relationship;

var twitchEvent = require('./twitch-event');
var JoinEvent   = twitchEvent.JoinEvent;
var PartEvent   = twitchEvent.PartEvent;
var HostEvent   = twitchEvent.HostEvent;
var ChatEvent   = twitchEvent.ChatEvent;
var ActionEvent = twitchEvent.ActionEvent;

var User = (function(superclass) {
  util.inherits(User, superclass);
  function User(username) {
    return superclass.call(this, username, {username: username});
  }
  return User;
})(Entity);

var Channel = (function(superclass) {
  util.inherits(Channel, superclass);
  function Channel(username) {
    return superclass.call(this, username, {name: username});
  }
  return Channel;
})(Entity);

module.exports = function(emitter, username, secret, config) {

  var started = new Date();

  var lokiConfig = {
    autosave: true,
    autosaveInterval: 1000
  };

  // TODO: encapsulate collection configuration and init in twitch-collection.js

  var channeldb = new loki('twitch-channel.json', lokiConfig);
  var channels  = channeldb.addCollection('channels');

  var socialdb = new loki('twitch-' + username + '-social-graph.json');
  var entities = socialdb.addCollection('entities');
  var relationships = socialdb.addCollection('relationships');
  var socialGraph = new DirectedGraph("twitch-" + username + "-social");

  var userdb = new loki('twitch-' + username + '.json', lokiConfig);
  var users      = userdb.addCollection('users');
  var hosting    = userdb.addCollection('hosting');
  var follows    = userdb.addCollection('follow');
  var events     = userdb.addCollection('event');

  this.client = new irc.client(config);

  this.cacheChannel = function(name) {
    //var channel = channels.findObject({"name": {"$eq": name}});
    if (!channel) { // always cache channels for now
      client.api({
        url: "https://api.twitch.tv/kraken/channels/" + name
      }, function(err, res, body) {
        if (err) return console.error(err);
        var tchannel = JSON.parse(body);
        var channel = new Channel(name);
        channels.insert(channel);
        vres = socialGraph.addVertex(channel.toVertex());
        if (vres.error) return console.error(vres.error);
        //entities.insert(vres.result);
      });
      cacheChannelHosting(name);
      cacheChannelFollows(name);
    }
  }

  this.cacheChannelHosting = function(name) {

  }

  this.cacheChannelFollows = function(name) {

  }

  this.cacheUser = function(name) {
    var user = users.findObject({"username": {"$eq": name}});
    if (!user) {
      this.client.api({
        url: "https://api.twitch.tv/kraken/users/" + name
      }, function(err, res, body) {
        if (err) return console.error(err);
        var tuser = JSON.parse(body);
        var user = new User(tuser.username);
        users.insert(user);
        vres = socialGraph.addVertex(user.toVertex());
        if (vres.error) return console.error(vres.error);
        //entities.insert(vres.result);
      });
    }
  }

  function onConnected(address, port) {
    //var event = new Event({address: address, port: port});
    //even.eventType = "server-connected"; // FIXME: refactor Event constructor
    //console.dir(event);
  }

  function onJoin(channel, user) {
    var event = new JoinEvent(channel, user);
    events.insert(event);
    var user = users.findObject({"username": {"$eq": username}});
    cacheUser(user);
  }

  function onPart(channel, username) {
    var event = new PartEvent(channel, username);
    events.insert(event);
  };

  function onHosted(channel, user, viewers) {
    var ts = new Date();
    console.log("%s [%s] <%s> host %s", ts.toUTCString(), channel, user, viewers);
    var event = new HostEvent(channel, user, viewers);
    events.insert(event);
    cacheUser(user);
  }

  function onChat(channel, user, message, self) {
    var ts = new Date();
    console.log("%s [%s] <%s> %s", ts.toUTCString(), channel, user.username, message);
    var event = new ChatEvent(channel, user.username, message);
    events.insert(event);
    cacheUser(user.username);
    var commandPattern = new RegExp("^!\\w+");
    if (commandPattern.test(message)) {
      var command = commandPattern.exec(message);
      emitter.emit("command-dispatch", command, user.username, message);
    }
  }

  function onAction(channel, user, message, self) {
    var ts = new Date();
    console.log("%s [%s] <*%s> %s", ts.toUTCString(), channel, user.username, message);
    var event = new ActionEvent(channel, user.username, message);
    events.insert(event);
    cacheUser(user.username);
    var commandPattern = new RegExp("^!\\w+");
    if (commandPattern.test(message)) {
      var command = commandPattern.exec(message);
      emitter.emit("command-dispatch", command, user.username, message);
    }
  };

  this.say = function(channel, message) {
    this.client.say(channel, message);
  }

  this.client.on("connected", onConnected);
  this.client.on("join", onJoin);
  this.client.on("part", onPart);
  this.client.on("hosted", onHosted);
  this.client.on("chat", onChat);
  this.client.on("action", onAction);

  // still needed?
  emitter.on('cache-user', this.cacheUser);
  emitter.on('cache-channel', this.cacheChannel);
  emitter.on('cache-channel-hosting', this.cacheChannelHosting);
  emitter.on('cache-channel-follows', this.cacheChannelFollows);

  this.router = express.Router();

  /*
  this.router.get('/channel/:name', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(channels.find({"name": req.params.name}));
  });
  this.router.get('/channel/:name/hosting', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(hosting.find());
  });
  this.router.get('/channel/:name/follows', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(follows.find());
  });
*/
  // register new router for every new user identified so they get their own base loki file registered once

  this.connect = function() {
    this.client.connect();
  };

  return this;
};
<|endoftext|>


<commit_before>(function() {

    "use strict";

    var seedrandom = require('seedrandom');
    var randomSeed = (new Date()).getTime();

    // must be first
    seedrandom(randomSeed, {
        global: true
    });

    var chalk = require('chalk');
    var math = require('mathjs');
    var jpeg = require('jpeg-js');
    var R = require('ramda');
    var Rp = require('./plus-fp/plus-fp');

    var clr = require('./color');

    var path = require('path');
    var promise = require("bluebird");
    var fs = promise.promisifyAll(require('fs'));

    var isDev = /Apple_Terminal|iterm\.app/i.test(process.env.TERM_PROGRAM);

    // load jpeg

    function loadJpeg(filename) {

        var jpegData = fs.readFileSync(filename);

        var rawImageData = jpeg.decode(jpegData);

        var data = R.times(() => [], rawImageData.height);

        var rawData = rawImageData.data;
        var w = rawImageData.width;
        var h = rawImageData.height;

        for (var d = 0; d < rawImageData.width * rawImageData.height * 4; d += 4) {
            let rgb = {};

            rgb.r = rawData[d];
            rgb.g = rawData[d + 1];
            rgb.b = rawData[d + 2];

            let d1 = d / 4;

            let x = math.floor(d1 / w);
            let y = d1 - x * w;

            data[y][x] = {
                rgb: rgb
            };
        }

        return {
            data: data,
            width: w,
            height: h
        };
    }

    function num2hex(d) {
        return (d < 16 ? "0" : "") + d.toString(16);
    }

    function randomColor(params, maxLightness) {
      let k = math.randomInt(params.xy.length);
      let yy = params.xy[k].y;
      let xx = params.xy[k].x;

      let x = xx + params.inputWidth / 2 - params.outputWidth / 2;
      let y = yy + params.inputHeight / 2 - params.outputHeight / 2;
      let rgb = params.data[x][y].rgb;

      let color = `0x${num2hex(math.floor(rgb.r * maxLightness))}${num2hex(math.floor(rgb.g * maxLightness))}${num2hex(math.floor(rgb.b * maxLightness))}`;
      // console.log('color ' , color);

      return color;
    }

    // ------------ output to html+three.js

    var create3d = function(filename) {

        return new promise(function(resolve) {

            console.log(chalk.yellow(filename));

            var jpeg = loadJpeg(filename);

            let data = jpeg.data;
            let inputWidth = jpeg.width;
            let inputHeight = jpeg.height;

            let outputFilename = filename.replace(/\.jpg/, '.html');

            let outputWidth = inputWidth;
            let outputHeight = inputHeight;

            let maxz = 100 + Rp.bandomInt(outputHeight - 100, 3);
            console.log('maxz ', maxz);

            let maxRadius = 10 + Rp.bandomInt(128 - 10, 2);
            console.log('maxRadius ' , maxRadius);

            try {
                var outf = fs.createWriteStream(`${outputFilename}`);

                outf.write(`<!DOCTYPE html>
                <html lang='en'>
                <head>
                    <meta charset='UTF-8'>
                    <title>${outputFilename}</title>
                    <script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/r73/three.min.js'></script>
                </head>
                <body style='margin:0; padding:0; overflow: hidden'>\n`);

                // add line
                outf.write(`<script>
                function ln(scene, x3, y3, z3, color) {
                  var geometry = new THREE.Geometry();

                  geometry.vertices.push(
                    new THREE.Vector3( x3, y3, 0 ),
                    new THREE.Vector3( x3, y3, z3 )
                  );

                  var material = new THREE.LineBasicMaterial({
                    color: color
                  });

                  var line = new THREE.Line( geometry, material );

                  scene.add(line);
                }
                </script>\n`);

                // add cylinder
                // todo: open/closed ends. vary radius.

                outf.write(`<script>
                function cyl(scene, options) {
                  var geometry = new THREE.CylinderGeometry(options.radius, options.radius, options.z, 64);
                  var material = new THREE.MeshPhongMaterial( {color: options.color} );

                  var cylinder = new THREE.Mesh( geometry, material );

                  cylinder.position.x = options.x;
                  cylinder.position.y = options.y;
                  cylinder.position.z = 0;

                  cylinder.castShadow = true;
                  cylinder.receiveShadow = true;

                  scene.add(cylinder);
                }
                </script>\n`);

                outf.write(`<script>
                  draw();

                  function draw() {

                      var width = ${outputWidth};
                      var height = ${outputHeight};

                      var scene = new THREE.Scene();
                      var s = scene;
                \n`);

                let xy = [];

                for (let y = 0; y < outputHeight; y++) {

                    for (let x = 0; x < outputWidth; x++) {
                        xy.push({
                            x: x,
                            y: y
                        });
                    }
                }

                // use a small % of points otherwise too big to render

                // 0.4 for lines

                let sample = (isDev ? 1 : 1);

                sample = sample / (maxRadius * maxRadius);

                xy = R.sortBy(() => math.random(), xy);
                xy = R.take(xy.length * sample, xy);

                let params = {
                  xy: xy,
                  data: data,
                  inputWidth: inputWidth,
                  inputHeight: inputHeight,
                  outputWidth: outputWidth,
                  outputHeight: outputHeight
                };

                let backgroundColor = '0x000000';

                outf.write(`
                  var renderer = new THREE.WebGLRenderer();

                  var camera = new THREE.PerspectiveCamera( 75, width / height, 0.1, 1000 );

                  camera.position.x = ${Rp.bandomInt(outputWidth / 2, 2) * Rp.randomSign()};
                  camera.position.y = ${Rp.bandomInt(outputHeight / 2, 2) * Rp.randomSign()};
                  camera.position.z = ${ Rp.bandomInt(1000, -2)};

                  camera.lookAt(s.position);

                  var spotLight = new THREE.SpotLight(${randomColor(params, 1)});
                  spotLight.position.set(${Rp.bandomInt(outputWidth / 2, 2) * Rp.randomSign()}, ${Rp.bandomInt(outputHeight / 2, 2) * Rp.randomSign()}, ${ Rp.bandomInt(1000, -2)});
                  spotLight.castShadow = true;
                  scene.add(spotLight);

                  var ambientLight = new THREE.AmbientLight(${randomColor(params, 0.25)});
                  scene.add(ambientLight);

                  renderer.setClearColor(new THREE.Color(${backgroundColor}));

                  renderer.setSize( width, height );

                  renderer.shadowMapEnabled = true;

                  scene.fog = new THREE.FogExp2(0xffffff, 0.00025);

                  document.body.appendChild( renderer.domElement );
                \n`);

                for (let k = 0; k < xy.length; k++) {

                    let yy = xy[k].y;
                    let xx = xy[k].x;

                    let x = xx + inputWidth / 2 - outputWidth / 2;
                    let y = yy + inputHeight / 2 - outputHeight / 2;
                    let rgb = data[x][y].rgb;

                    let hsl = clr.rgb2hsl(rgb);

                    // 3d coords
                    let x3 = (x / inputWidth) * outputWidth - outputWidth / 2;
                    let y3 = -((y / inputHeight) * outputHeight - outputHeight / 2);

                    let z3 = math.round(hsl.l * maxz, 0); /// outputHeight;

                    let radius = maxRadius;

                    outf.write(`cyl(s, {
                      x: ${x3},
                      y: ${y3},
                      z: ${z3},
                      color: 0x${num2hex(rgb.r)}${num2hex(rgb.g)}${num2hex(rgb.b)},
                      radius: ${radius}
                    });
                    \n`);
                }

                // end

                outf.write(`
                  renderer.render( scene, camera );
                \n`);

                outf.write(`}
                      </script>
                  </body>
                  </html>
                  \n`);

                data = null;
                jpeg.data = null;
                jpeg = null;
                xy = null;

                outf.end(function() {
                    outf = null;
                    resolve();
                });
            } catch (ex) {
                console.log(chalk.red('error'), ex);

                resolve();
            }

        });
    };

    // process file

    var processFiles = function() {

        if (process.argv.length > 2) {

            var files = R.tail(R.tail(process.argv));

            files = R.filter((f) => path.extname(f) === '.jpg', files);

            // console.log(files);

            promise.each(files, create3d).then(function() {});
        }
        else {
            console.log('usage: node 3d-image path ...');
        }

    };

    // kick off

    processFiles();

}());
<commit_msg>defog<commit_after>(function() {

    "use strict";

    var seedrandom = require('seedrandom');
    var randomSeed = (new Date()).getTime();

    // must be first
    seedrandom(randomSeed, {
        global: true
    });

    var chalk = require('chalk');
    var math = require('mathjs');
    var jpeg = require('jpeg-js');
    var R = require('ramda');
    var Rp = require('./plus-fp/plus-fp');

    var clr = require('./color');

    var path = require('path');
    var promise = require("bluebird");
    var fs = promise.promisifyAll(require('fs'));

    var isDev = /Apple_Terminal|iterm\.app/i.test(process.env.TERM_PROGRAM);

    // load jpeg

    function loadJpeg(filename) {

        var jpegData = fs.readFileSync(filename);

        var rawImageData = jpeg.decode(jpegData);

        var data = R.times(() => [], rawImageData.height);

        var rawData = rawImageData.data;
        var w = rawImageData.width;
        var h = rawImageData.height;

        for (var d = 0; d < rawImageData.width * rawImageData.height * 4; d += 4) {
            let rgb = {};

            rgb.r = rawData[d];
            rgb.g = rawData[d + 1];
            rgb.b = rawData[d + 2];

            let d1 = d / 4;

            let x = math.floor(d1 / w);
            let y = d1 - x * w;

            data[y][x] = {
                rgb: rgb
            };
        }

        return {
            data: data,
            width: w,
            height: h
        };
    }

    function num2hex(d) {
        return (d < 16 ? "0" : "") + d.toString(16);
    }

    function randomColor(params, maxLightness) {
      let k = math.randomInt(params.xy.length);
      let yy = params.xy[k].y;
      let xx = params.xy[k].x;

      let x = xx + params.inputWidth / 2 - params.outputWidth / 2;
      let y = yy + params.inputHeight / 2 - params.outputHeight / 2;
      let rgb = params.data[x][y].rgb;

      let color = `0x${num2hex(math.floor(rgb.r * maxLightness))}${num2hex(math.floor(rgb.g * maxLightness))}${num2hex(math.floor(rgb.b * maxLightness))}`;
      // console.log('color ' , color);

      return color;
    }

    // ------------ output to html+three.js

    var create3d = function(filename) {

        return new promise(function(resolve) {

            console.log(chalk.yellow(filename));

            var jpeg = loadJpeg(filename);

            let data = jpeg.data;
            let inputWidth = jpeg.width;
            let inputHeight = jpeg.height;

            let outputFilename = filename.replace(/\.jpg/, '.html');

            let outputWidth = inputWidth;
            let outputHeight = inputHeight;

            let maxz = 100 + Rp.bandomInt(outputHeight - 100, 3);
            console.log('maxz ', maxz);

            let maxRadius = 10 + Rp.bandomInt(128 - 10, 2);
            console.log('maxRadius ' , maxRadius);

            try {
                var outf = fs.createWriteStream(`${outputFilename}`);

                outf.write(`<!DOCTYPE html>
                <html lang='en'>
                <head>
                    <meta charset='UTF-8'>
                    <title>${outputFilename}</title>
                    <script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/r73/three.min.js'></script>
                </head>
                <body style='margin:0; padding:0; overflow: hidden'>\n`);

                // add line
                outf.write(`<script>
                function ln(scene, x3, y3, z3, color) {
                  var geometry = new THREE.Geometry();

                  geometry.vertices.push(
                    new THREE.Vector3( x3, y3, 0 ),
                    new THREE.Vector3( x3, y3, z3 )
                  );

                  var material = new THREE.LineBasicMaterial({
                    color: color
                  });

                  var line = new THREE.Line( geometry, material );

                  scene.add(line);
                }
                </script>\n`);

                // add cylinder
                // todo: open/closed ends. vary radius.

                outf.write(`<script>
                function cyl(scene, options) {
                  var geometry = new THREE.CylinderGeometry(options.radius, options.radius, options.z, 64);
                  var material = new THREE.MeshPhongMaterial( {color: options.color} );

                  var cylinder = new THREE.Mesh( geometry, material );

                  cylinder.position.x = options.x;
                  cylinder.position.y = options.y;
                  cylinder.position.z = 0;

                  cylinder.castShadow = true;
                  cylinder.receiveShadow = true;

                  scene.add(cylinder);
                }
                </script>\n`);

                outf.write(`<script>
                  draw();

                  function draw() {

                      var width = ${outputWidth};
                      var height = ${outputHeight};

                      var scene = new THREE.Scene();
                      var s = scene;
                \n`);

                let xy = [];

                for (let y = 0; y < outputHeight; y++) {

                    for (let x = 0; x < outputWidth; x++) {
                        xy.push({
                            x: x,
                            y: y
                        });
                    }
                }

                // use a small % of points otherwise too big to render

                // 0.4 for lines

                let sample = (isDev ? 1 : 1);

                sample = sample / (maxRadius * maxRadius);

                xy = R.sortBy(() => math.random(), xy);
                xy = R.take(xy.length * sample, xy);

                let params = {
                  xy: xy,
                  data: data,
                  inputWidth: inputWidth,
                  inputHeight: inputHeight,
                  outputWidth: outputWidth,
                  outputHeight: outputHeight
                };

                let backgroundColor = '0x000000';

                outf.write(`
                  var renderer = new THREE.WebGLRenderer();

                  var camera = new THREE.PerspectiveCamera( 75, width / height, 0.1, 1000 );

                  camera.position.x = ${Rp.bandomInt(outputWidth / 2, 2) * Rp.randomSign()};
                  camera.position.y = ${Rp.bandomInt(outputHeight / 2, 2) * Rp.randomSign()};
                  camera.position.z = ${ Rp.bandomInt(1000, -2)};

                  camera.lookAt(s.position);

                  var spotLight = new THREE.SpotLight(${randomColor(params, 1)});
                  spotLight.position.set(${Rp.bandomInt(outputWidth / 2, 2) * Rp.randomSign()}, ${Rp.bandomInt(outputHeight / 2, 2) * Rp.randomSign()}, ${ Rp.bandomInt(1000, -2)});
                  spotLight.castShadow = true;
                  scene.add(spotLight);

                  var ambientLight = new THREE.AmbientLight(${randomColor(params, 0.25)});
                  scene.add(ambientLight);

                  renderer.setClearColor(new THREE.Color(${backgroundColor}));

                  renderer.setSize( width, height );

                  renderer.shadowMapEnabled = true;

                  document.body.appendChild( renderer.domElement );
                \n`);

                for (let k = 0; k < xy.length; k++) {

                    let yy = xy[k].y;
                    let xx = xy[k].x;

                    let x = xx + inputWidth / 2 - outputWidth / 2;
                    let y = yy + inputHeight / 2 - outputHeight / 2;
                    let rgb = data[x][y].rgb;

                    let hsl = clr.rgb2hsl(rgb);

                    // 3d coords
                    let x3 = (x / inputWidth) * outputWidth - outputWidth / 2;
                    let y3 = -((y / inputHeight) * outputHeight - outputHeight / 2);

                    let z3 = math.round(hsl.l * maxz, 0); /// outputHeight;

                    let radius = maxRadius;

                    outf.write(`cyl(s, {
                      x: ${x3},
                      y: ${y3},
                      z: ${z3},
                      color: 0x${num2hex(rgb.r)}${num2hex(rgb.g)}${num2hex(rgb.b)},
                      radius: ${radius}
                    });
                    \n`);
                }

                // end

                outf.write(`
                  renderer.render( scene, camera );
                \n`);

                outf.write(`}
                      </script>
                  </body>
                  </html>
                  \n`);

                data = null;
                jpeg.data = null;
                jpeg = null;
                xy = null;

                outf.end(function() {
                    outf = null;
                    resolve();
                });
            } catch (ex) {
                console.log(chalk.red('error'), ex);

                resolve();
            }

        });
    };

    // process file

    var processFiles = function() {

        if (process.argv.length > 2) {

            var files = R.tail(R.tail(process.argv));

            files = R.filter((f) => path.extname(f) === '.jpg', files);

            // console.log(files);

            promise.each(files, create3d).then(function() {});
        }
        else {
            console.log('usage: node 3d-image path ...');
        }

    };

    // kick off

    processFiles();

}());
<|endoftext|>


<commit_before>#!/usr/bin/env node
var ok = require('./oK'); process.stdout.write('oK v' + ok.version + '\n');
var fs = require('fs');
var readline = require('readline');
var conv = require('./convert');

var env = ok.baseEnv();
var rl = readline.createInterface({
	input:  process.stdin,
	output: process.stdout,
	completer: function (line) {
		var m = /[a-z][a-z\d]*$/i.exec(line);
		var prefix = m ? m[0] : '';
		var names = [];
		for (var e = env; e; e = e.p) { // iterate over ancestor environments
			for (var name in e.d) {
				if (name.slice(0, prefix.length) === prefix && names.indexOf(name) < 0) {
					names.push(name);
				}
			}
		}
		return [names, prefix];
	}
});
rl.on('line', function (line) {
	if (line === '\\\\') { process.exit(0); }
	try {
		line.trim() && process.stdout.write(ok.format(ok.run(ok.parse(line), env)) + '\n');
	} catch (err) {
		process.stdout.write(err.message + '\n');
	}
	rl.prompt();
});
rl.on('close', function () { process.stdout.write('\n'); process.exit(0); });
function read(x) {
	// todo
	return conv.tok('');
}
function write(x, y) {
	// todo
	return y;
}
ok.setIO('0:', 2, write);
ok.setIO('0:', 4, write);
ok.setIO('0:', 0, read);
rl.setPrompt(' '); rl.prompt();
<commit_msg>implemented some cases of 0:<commit_after>#!/usr/bin/env node
var ok = require('./oK'); process.stdout.write('oK v' + ok.version + '\n');
var fs = require('fs');
var path = require('path');
var readline = require('readline');
var conv = require('./convert');

var env = ok.baseEnv();
var rl = readline.createInterface({
	input:  process.stdin,
	output: process.stdout,
	completer: function (line) {
		var m = /[a-z][a-z\d]*$/i.exec(line);
		var prefix = m ? m[0] : '';
		var names = [];
		for (var e = env; e; e = e.p) { // iterate over ancestor environments
			for (var name in e.d) {
				if (name.slice(0, prefix.length) === prefix && names.indexOf(name) < 0) {
					names.push(name);
				}
			}
		}
		return [names, prefix];
	}
});
rl.on('line', function (line) {
	if (line === '\\\\') { process.exit(0); }
	try {
		line.trim() && process.stdout.write(ok.format(ok.run(ok.parse(line), env)) + '\n');
	} catch (err) {
		process.stdout.write(err.message + '\n');
	}
	rl.prompt();
});
rl.on('close', function () { process.stdout.write('\n'); process.exit(0); });
function str(x) { // convert a k string or symbol to a js string
	var s = x.t === 2 ? x.v : conv.tojs(x);
	if (typeof s !== 'string') { throw Error("ERROR: type"); }
	return s;
}
function read(x) {
	var f = path.resolve(process.cwd(), str(x));
	return conv.tok(fs.statSync(f).isDirectory() ? fs.readdirSync(f) : fs.readFileSync(f, 'utf8').split(/\r?\n/));
}
function write(x, y) {
	var f = path.resolve(process.cwd(), str(x));
	var s = y.t === 2 ? y.v : conv.tojs(y);
	if (Array.isArray(s)) { s = s.map(str).join('\n') + '\n'; }
	if (typeof s !== 'string') { throw Error("ERROR: type"); }
	fs.writeFileSync(f, s);
	return x;
}
for (var i = 0; i < 2; i++) { ok.setIO('0:', i, read ); }
for (var i = 2; i < 6; i++) { ok.setIO('0:', i, write); }
rl.setPrompt(' '); rl.prompt();
<|endoftext|>