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
Post a Comment

Labels

Java (159) Lucene-Solr (112) Interview (61) All (58) J2SE (53) Algorithm (45) Soft Skills (38) Eclipse (33) Code Example (31) Linux (25) JavaScript (23) Spring (22) Windows (22) Web Development (20) Tools (19) Nutch2 (18) Bugs (17) Debug (16) Defects (14) Text Mining (14) J2EE (13) Network (13) Troubleshooting (13) PowerShell (11) Chrome (9) Design (9) How to (9) Learning code (9) Performance (9) Problem Solving (9) UIMA (9) html (9) Http Client (8) Maven (8) Security (8) bat (8) blogger (8) Big Data (7) Continuous Integration (7) Google (7) Guava (7) JSON (7) Shell (7) ANT (6) Coding Skills (6) Database (6) Lesson Learned (6) Programmer Skills (6) Scala (6) Tips (6) css (6) Algorithm Series (5) Cache (5) Dynamic Languages (5) IDE (5) System Design (5) adsense (5) xml (5) AIX (4) Code Quality (4) GAE (4) Git (4) Good Programming Practices (4) Jackson (4) Memory Usage (4) Miscs (4) OpenNLP (4) Project Managment (4) Spark (4) Testing (4) ads (4) regular-expression (4) Android (3) Apache Spark (3) Become a Better You (3) Concurrency (3) Eclipse RCP (3) English (3) Happy Hacking (3) IBM (3) J2SE Knowledge Series (3) JAX-RS (3) Jetty (3) Restful Web Service (3) Script (3) regex (3) seo (3) .Net (2) Android Studio (2) Apache (2) Apache Procrun (2) Architecture (2) Batch (2) Bit Operation (2) Build (2) Building Scalable Web Sites (2) C# (2) C/C++ (2) CSV (2) Career (2) Cassandra (2) Distributed (2) Fiddler (2) Firefox (2) Google Drive (2) Gson (2) How to Interview (2) Html Parser (2) Http (2) Image Tools (2) JQuery (2) Jersey (2) LDAP (2) Life (2) Logging (2) Python (2) Software Issues (2) Storage (2) Text Search (2) xml parser (2) AOP (1) Application Design (1) AspectJ (1) Chrome DevTools (1) Cloud (1) Codility (1) Data Mining (1) Data Structure (1) ExceptionUtils (1) Exif (1) Feature Request (1) FindBugs (1) Greasemonkey (1) HTML5 (1) Httpd (1) I18N (1) IBM Java Thread Dump Analyzer (1) JDK Source Code (1) JDK8 (1) JMX (1) Lazy Developer (1) Mac (1) Machine Learning (1) Mobile (1) My Plan for 2010 (1) Netbeans (1) Notes (1) Operating System (1) Perl (1) Problems (1) Product Architecture (1) Programming Life (1) Quality (1) Redhat (1) Redis (1) Review (1) RxJava (1) Solutions logs (1) Team Management (1) Thread Dump Analyzer (1) Visualization (1) boilerpipe (1) htm (1) ongoing (1) procrun (1) rss (1)

Popular Posts