Building Chrome Extension to Find All On Sale Products in Google Express


The Problem
Google express does have a page to show on-sale products from one store, like this one https://www.google.com/express/u/0/search?oc=on_sale&m=9090995, but for unknown reason, it only lists a very small part of all on-sale products.

I like on-sale products, so I build one chrome extension that lists all on-sale products from one store.

How to Use
We go to one store, then run "Find on-sale products in Express" -> "Find in all pages", or choose one category(such as grocery), then choose "Find in all pages".

How it Works
In Google Express, when we go to the end of the page, it will automatically load more data in current page. So the extension continues to scroll to end of page until there is no more data to load, then remove all not on-sale products.

How to Build it We use extensionizr to create one chrome extension, then change the manifest.json as below:
{
  "name": "Find on-sales products in Express",
  "version": "0.0.1",
  "manifest_version": 2,
  "homepage_url": "http://lifelongprogrammer.blogspot.com",
  "default_locale": "en",
  "background": {
    "page": "src/bg/background.html",
    "persistent": true
  },
  "permissions": [
    "contextMenus",
    "tabs",
    "activeTab",    
    "https://www.google.com/express/*"
  ],
  "content_scripts": [
    {
      "matches": [
        "https://www.google.com/express/*",
      ],
      "js": [
        "src/inject/inject.js"
      ],
    }
  ]
}
background.js
We create context menu in background.js. When user clicks one content menu, it uses message passing to call function defined in content scripts.

Here we use "documentUrlPatterns":showForPages, to only create the context menus in google express websites.
'use strict';
var showForPages = ["https://www.google.com/express/*"];
function findOnSalesProducts(info, tab, pageCount) {
    console.log("pageCount: " + pageCount);
    chrome.tabs.query({
        "active": true,
        "currentWindow": true
    }, function (tabs) {
        chrome.tabs.sendMessage(tabs[0].id, {
            "functiontoInvoke": "findOnSalesProducts",
            "pageCount":  pageCount
        });
    });
}

function findAllSales(info, tab) {
    chrome.tabs.query({
        "active": true,
        "currentWindow": true
    }, function (tabs) {
        chrome.tabs.sendMessage(tabs[0].id, {
            "functiontoInvoke": "findAllSales"
        });
    });
}

chrome.contextMenus.create(
{
"title": "Find in all pages",
"contexts" : ["all"],
"documentUrlPatterns":showForPages,
"onclick": function(info, tab) {
    findAllSales(info, tab)
  }
});
createSeperatorMenu();

chrome.contextMenus.create(
{
"title": "Find in current page",
"contexts" : ["all"],
"documentUrlPatterns":showForPages,
"onclick": function(info, tab) {
    findOnSalesProducts(info, tab, 0)
  }
});

createSeperatorMenu();

var pageCounts = ["5", "10", "20", "40", "60"];
for (let pageCount of pageCounts) {
  chrome.contextMenus.create(
  {
  "title": "Find in next " + pageCount + " pages",
  "contexts" : ["all"],
  "documentUrlPatterns":showForPages,
  "onclick": function(info, tab) {
      findOnSalesProducts(info, tab, pageCount)
    }
  });
}

function createSeperatorMenu()
{
  chrome.contextMenus.create(
   {
     "title": "-------------------------------------",
     "contexts" : ["all"],
     "documentUrlPatterns":showForPages     
   }
  ); 
}
content_scripts.js
Main function is defined in content_scripts.js.
It uses setInterval to scroll to end of page multiple times, and use css selector to find not-on-sale products, and then remove them.
'use strict';
chrome.extension.onMessage.addListener(function (message, sender, callback) {
  console.log("message: " + JSON.stringify(message))
  if(message.functiontoInvoke =="findOnSalesProducts")
  {    
    findOnSalesProducts(message.pageCount);
  } else if(message.functiontoInvoke == "findAllSales")
  {
    findAllSales();
  }
  return;
});
function findOnSalesProducts(times)
{
  if(times == 0)
  {
    removeAllNonSales();
    return;
  }
  let timesRun = 0;
  var interval = setInterval(function(){
      timesRun += 1;
      if(timesRun > times){
          console.log("clearInterval and removeAllNonSales");
          clearInterval(interval);
          removeAllNonSales();
      }
      console.log("scroll")
      window.scrollTo(0,document.body.scrollHeight);
  }, 500); 
}

function findAllSales()
{
  let lastProductsCount = 0, currentProductsCount = 0;
  console.log(lastProductsCount +" last1, current: "+currentProductsCount );  
  var interval = setInterval(function(){
      currentProductsCount = getProductsCount();    
      console.log(lastProductsCount +" last2, current: "+currentProductsCount );  
      if(currentProductsCount == lastProductsCount){
          console.log("clearInterval and removeAllNonSales");
          clearInterval(interval);
          removeAllNonSales();
          return;
      }
      console.log("scroll")
      window.scrollTo(0,document.body.scrollHeight);
      lastProductsCount = currentProductsCount;
      console.log(lastProductsCount +" last3, current: "+currentProductsCount );
  }, 1500); 
}

function getProductsCount()
{
  return document.querySelectorAll("body div.contentArea>div.right ul>li").length;
}

function removeAllNonSales()
{
  var allProducts = document.querySelectorAll("body div.contentArea>div.right ul>li")
  Array.prototype.forEach.call( allProducts, function( node ) {
      if(node.querySelectorAll("span.priceSale").length==0)
      {
        node.parentNode.removeChild( node );    
      }    
  });
}
Resources
Chrome Extension Overview

Labels

adsense (5) Algorithm (69) Algorithm Series (35) Android (7) ANT (6) bat (8) Big Data (7) Blogger (14) Bugs (6) Cache (5) Chrome (19) Code Example (29) Code Quality (7) Coding Skills (5) Database (7) Debug (16) Design (5) Dev Tips (63) Eclipse (32) Git (5) Google (33) Guava (7) How to (9) Http Client (8) IDE (7) Interview (88) J2EE (13) J2SE (49) Java (186) JavaScript (27) JSON (7) Learning code (9) Lesson Learned (6) Linux (26) Lucene-Solr (112) Mac (10) Maven (8) Network (9) Nutch2 (18) Performance (9) PowerShell (11) Problem Solving (11) Programmer Skills (6) regex (5) Scala (6) Security (9) Soft Skills (38) Spring (22) System Design (11) Testing (7) Text Mining (14) Tips (17) Tools (24) Troubleshooting (29) UIMA (9) Web Development (19) Windows (21) xml (5)