Click here to Skip to main content
Click here to Skip to main content

Google Chrome Extension - CodeProject Reputation Watcher

, 6 Aug 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
Google Chrome Extension that scrapes your reputation points and graph, and tracks changes between updates.

CodeProject Reputation Extension

Important V2 Changes

Due to the policy change imposed by Google to protect users from harmful code, there were a number of change required to the extension.

 

  • Update of the manifest to new V2 standard.
  • Extension distribution from the Chrome Web Store
  • Removal of inline script blocks from HTML pages.

 

I also took advantage of the changes to upgrade to a new jQuery file and refactor some other code.

Introduction

This article was written as a result of me wanting to try some new topics. In order to try these new topics, it would be better to try and develop a functioning component rather than just play about with code syntax.

This article will explore how to put together a Google Chrome Extension, and also how I utilised jQuery and jQuery UI in building the extension. So what will this extension do? This extension can recover the source of a user's CodeProject profile page and scrape out their reputation points. The extension stores these points locally, and compares the changes between polls to The Code Project, and then presents this information in a nice little table. The extension can also recover the member's reputation graph and display this in the extension.

Background

The information required in building this extension can be found on Google Chrome Extension pages. For information relating to jQuery and jQuery UI, see here and here.

The Chrome Extension

Chrome Extensions are effectively a collection of HTML files with fully embedded JavaScript and CSS, or can link out to other JavaScript, CSS, and image files. In order for Chrome to know what to do with all these files and how to present it to the user, a manifest file in the root of the project defines the various elements. The manifest file must be named 'manifest.json'.

Let's take a look now at the manifest file crafted for this extension:

{
  "manifest_version": 2,
  "name": "CodeProject Reputation Watcher",
  "short_name": "CPRepWatcher",
  "version": "2.0",
  "content_security_policy": "script-src 'self' https://ssl.google-analytics.com 'unsafe-eval'; object-src 'self' ",
  "description": "Extension to retrieve a CodeProject accounts reputation points.",
  "icons": {
      "48":"images/bob48.png",
      "128":"images/bob128.png"
  },
  "browser_action": {
    "default_icon": "images/bob.png",
    "default_popup": "cprepwatch.html"
  },
  "options_page": "options.html",
  "permissions": [
    "http://*.codeproject.com/"
  ]
}

There are some basic attributes like 'name', 'version', and 'description', which are self-explanatory. The 'icons' element defines which images are available for use by the extension; these appear in the Chrome Extensions page when it is installed.

The 'browser_action' element tells Chrome which icon to show in the toolbar and which HTML file is the main popup when the extension icon is clicked.

The 'options_page' element tells Chrome which HTML page will be displayed to allow the user to set any options/user settings used by the extension. This page is accessed from the Options menu displayed when the toolbar button is right-clicked, or from the Options link on the Chrome Extensions page accessed from the Tools menu of Chrome.

The 'permissions' element defines which site the extension wants to access, and is used by the cross site security model.

(Manifest Version 1) The 'update_url' element defined the XML file used by the Auto Update tool to check for newer revisions of the extension. As of Version 2, this is no longer required as the extension is hosted through the Chrome store. Chrome Browser takes care of the version update process. This property was susequently removed.

You can also see the 'manifest_versions' and 'content_security_policy' changes that were required under the new rules. One other thing I noted was that the uploader to the chrome store did not like having comment lines in the manifest, so had to also remove these. This was only discovered by trial and error as the error message wasn't very helpful.

There are several other elements that can be added to the manifest file depending on your requirements; there are details of these on the Chrome Extensions developers page.

Adding jQuery and jQuery UI

To make use of these JavaScript libraries, you need to download them first from the developers site, here, and here. On the jQuery UI site, there are also a number of predefined themes that can be used with the UI elements, and you can also create your own using the website's theme generator. After you have downloaded the zip files with the libraries and themes, unpack these and add them to your project folder. Every page that wants to make use of these libraries must have them linked in the HTML. This is easily achieved using standard markup.

<link type="text/css" 
      href="css/custom-theme/jquery-ui-1.8.2.custom.css" rel="Stylesheet" />
<script type="text/javascript" src="jquery/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="jquery/jquery-ui-1.8.2.custom.min.js"></script>
<script type="text/javascript" src="js/cpjshelper.js"></script>

You will also notice that there is a reference to a 'js/cpjshelper.js' JavaScript file. As the extension was developed, common shared helper functions were moved out to this file, to maximise code re-use and keep the HTML files neater.

Why Use jQuery?

jQuery provides an excellent method for manipulating and traversing the DOM. It certainly made things very easy once I had got my head round the syntax, which doesn't take too long. Here are some basic examples of what it can do:

//Insert some HTML into a Div element named 'holdingdata'
$("#holdingdata").html("the html markup would go here");
$("#anElement").hide();        //Hide an element
$("#anElement").show();        //Show an element
$("p").each(function(){ $(this).hide();});
//Find all 'p' tags and hide them

The jQuery command below executes a function when the browser document has loaded. The function sets up a timer which calls another function after a period of time. .get() performs an AJAX query using HTTP GET and then passes the returned data onto another function for processing. The AJAX calls can be made synchronous or asynchronous depending on your requirements. The two helper functions are JavaScript functions written to deal with the CodeProject addresses and data.

$(document).ready(function () {
    setTimeout(function () {
        //Start loading the data from Codeproject profile
        $.get(getCPMemberProfile(), function (data) {
            setCPProfileData(data);
        });
    }, 1000);
});

This is just a fraction of the full capabilities of jQuery, and if you pop over to the developer site and explore the documentation, you will soon see what I mean.

Why Use jQuery UI?

jQuery UI provides a comprehensive set of interface widgets that can be plugged into your HTML, and then using the CSS markups, provides a set of rich interface components. In this extension, the components that have been used are the Tabs panel and a basic button.

The Tabs panel was easily created as shown below:

<script type="text/javascript">
                       $(function() {
                           $("#tabs").tabs();
                       });
</script>
<div id="tabs" style="height: 400px;">
    <!-- Tab Pages -->
    <ul>
        <li><a href="#tabs-1">Reputation Points</a></li>
        <li><a href="repgraph.html">Reputation Graph</a></li>
        <li><a href="#tabs-3">Options</a></li>
        <li><a href="about.html">About</a></li>
    </ul>
    <!-- Tab 1 Rep Points -->
    <div id="tabs-1" style="padding-left: 5px">
        <!-- Main Rep Page content -->
    </div>
    <!-- Tab 2 Rep Graph ; Note: This does not require a section 
           as it is a link URL in tab definition above. -->
    <!-- Tab 3 Options -->
    <div id="tabs-3" style="padding-left: 5px">
        <!-- Options Tab Content goes Here -->
    </div>
    <!-- Tab 4 About ; Note: This does not require a section 
            as it is a link URL in tab definition above. -->
</div>

You will also notice that Tab 2 (Rep Graph) and Tab 4 (About) are references to an external document within the extension files. This content is loaded via AJAX, and then injected into the current page's DOM at the relevant tab location.

Scraping the CodeProject Content

In order to retrieve the CodeProject content, a JavaScript function first loads the member profile page by using an AJAX call. The returned content is then placed in a hidden Div block on the page, which can then be accessed, traversed, and parsed using jQuery. There were three main Elements on the CodeProject content which were used to identify the areas to be scraped. These were 'ctl00_MC_Prof_TotalRepBox' for the overall member level, 'ctl00_MC_Prof_TotalRep' for the total reputation points, and 'ctl00_MC_Prof_Status' for all the individual category points.

//The Total Reputation Member Level is Found in
if ($("#ctl00_MC_Prof_TotalRepBox").hasClass("nostatus")) { $("#repStatus0").html(" "); };
if ($("#ctl00_MC_Prof_TotalRepBox").hasClass("bronze")) { $("#repStatus0").html("Bronze"); };
if ($("#ctl00_MC_Prof_TotalRepBox").hasClass("silver")) { $("#repStatus0").html("Silver"); };
if ($("#ctl00_MC_Prof_TotalRepBox").hasClass("gold")) { $("#repStatus0").html("Gold"); };
if ($("#ctl00_MC_Prof_TotalRepBox").hasClass("platinum")) 
              { $("#repStatus0").html("Platinum"); };

//The Total Reputation is found in this node
var thestring = $("#ctl00_MC_Prof_TotalRep");
if (thestring) { newRep[0] = thestring.text(); } else { newRep[0] = "0"; }
$("#loadRepTotal").html(newRep[0]);

//Reputation Points table
holdingdata.innerHTML = $('table.member-rep-list').html();

Once the content had been located and copied into the holding Div, jQuery was then used to extract the text from the inner element. Below is the code that is used to locate each TableRow, and then within each one, find each TableData and locate the Points, Category, and Status level, and then inject them into the DOM for presenting to the user.

$("#holdingdata").find("tr").each(function (iTR) {
    $(this).find("td").each(function (iTD) {
        var MemberLevel = "";
        if ($(this).hasClass("nostatus")) { MemberLevel = "" };
        if ($(this).hasClass("bronze")) { MemberLevel = "Bronze" };
        if ($(this).hasClass("silver")) { MemberLevel = "Silver" };
        if ($(this).hasClass("gold")) { MemberLevel = "Gold" };
        if ($(this).hasClass("platinum")) { MemberLevel = "Platinum" };
         
         
         //Version 1.6 21st March 2011, Additional second link in each category
         //iterate all the links determining a Category/Value pair
         var thevalue = $(this).find("div.medium-text").text();
         if (!thevalue) { thevalue = ""; 
         $(this).find("a").each( function (iA) {
            var category = $(this).text();             
            var category = $(this).find("a").text();

            if (!category) { category = "No Data"; };
            category = category.toUpperCase(category);            
            switch (category) {
                case ("AUTHOR"):
                    newRep[1] = thevalue;
                    $("#loadRepAuthor").html(newRep[1]);
                    $("#repStatus1").html(MemberLevel);
                    break;
                case ("AUTHORITY"):
                    newRep[2] = thevalue;
                    $("#loadRepAuthority").html(newRep[2]);
                    $("#repStatus2").html(MemberLevel);
                    break;
                // Other case statements
                    for the other categories continue...........
            }
        });
    });
});

Testing Code

In order to test the code, first load the unpackaged extension into Chrome. To do this, you select 'Tools', then the 'Extensions' menu, expand the developer panel, and then select 'Load unpacked extension...', and navigate to the folder which contains all the files used in the extension. The extension will be loaded, shown as unpacked, and the icons, etc., specified in the manifest should appear in the toolbar and also to the left of the extension.

To see what is going on under the hood, you can right click the toolbar and select 'Inspect Pop-up' and look for errors, see what is being stored in the local storage, etc. As you revise the codebase/HTML files, simply click 'Reload' in extensions to update it.

To aid testing, you can place some JavaScript code in your extension to output to the Chrome debugger; this is as simple as:

//Output the contents of a variable to the debugger
console.log(variablename);
//Output a string to the debugger
console.log("Some text.");

Local Storage of Data/Settings

Objects can be stored locally, and in this extension, the CodeProject member ID as well as the reputation points for the last data poll are stored. Persisting the data is easy, and as you can see below, it is also easy to recall the data when required.

// Put data in the local store
var sometext = "Hello";
localStorage["AnyKeyName"] = sometext;

// Get data from the local store
var sometext;
sometext = localStorage["AnyKeyName"];

// Test the data does exist and was read
if (!sometext)
{
    // The data is not present in the local store
}

Packaging and Deployment/Hosting

There are two methods of distribution of the extension:

  1. Unpackaged method, which although works, is really suited to testing and development scenarios
  2. Google hosting via the Chrome Web Store.

The unpackaged extension is available to download from this article and the extension has also been published to the Chrome Web Store.

Packaged Extensions are no longer permitted. These will be blocked by the browser and disabled.

For more information on Packing, Deployment, and Google Hosting, see Extension Packaging.

How Can I Use This Extension?

You can either download and install the unpackaged extension, or click the link above to install from the web store.

NOTE: Installing the unpacked extension (in developer mode) from the Tools | Extension menu will allow you to install from the source. The browser will however keep reminding you and asking if you want to disable this mode.

Once you have installed by either method, then all you need to do is from the Options menu item, Extensions Options link, or from the Options tab on the actual extension, set this to your CodeProject member ID. Your member ID can be found on your profile page for your account.

Every time you click the Extensions Toolbar button, it will display the page shown at the top of the article and update the table with your latest points.

The other tabs on the extension can be seen below: Rep Graph, Options, and About.

Extension Removal

After you install an extension, it's just a case of hitting the uninstall link and deleting the source folder where you unzipped the unpackaged extension.

What Next

The codebase isn't probably the most optimised, so could do with a tidy up, but as usual, once it is working, I was reluctant to change anything. If I get time, I might look into this and possibly add some other features, like auto refresh.

Tracking Usage and User Interaction

It is possible to track usage of the extension by adding Google Analytics code to the files. From this, you can track which pages are being viewed, which buttons get clicked etc. More information on Analytics integration can be found here.

Points of Interest

As this was my first adventure in Chrome Extensions, jQuery, and jQuery UI, it was certainly a challenge, which has proved very rewarding.

Known Issues

There appears to be a weird cache related problem in Chrome, which is only an issue if you enter an invalid CodeProject member ID: e.g., navigate to your member profile, then navigate to an invalid profile, navigate to someone else's profile, navigate to the same invalid profile, and you will see your member profile entered at the start appears... I can't explain this, and for normal use of the extension, it doesn't matter anyway. As long as you use a valid member ID: things will be fine.

History

(Version numbers relate to code changes, not article revisions.)

  • V2.0 18th June 2014 - Updated to the new Chrome requirements, jQuery updated to V1.11.1
  • 23 October 2013 - Update installation instructions due to Chrome changes
  • V1.6, 21 March 2011 - Updated for CodeProject DOM/Member Profile changes
  • V1.5, 13 December 2010 - Updated for CodeProject DOM changes
  • V1.4, 20 October 2010 - Added Overall Member Level
  • V1.3, 10 September 2010 - Resolved some Google Analytics issues
  • V1.2, 9 September 2010 - Fixed some JS errors, moved the About content to a standalone file and called via AJAX, added Google Analytics tracking. Also introduced Package & Deployment to private web server details to the article
  • V1.1, 4 August 2010 - Fixed to handle new CSS class IDs for member status; removed redundant CSS theme from zip file
  • V1.0, 1 August 2010 - First version of article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

DaveAuld
Engineer
Scotland Scotland
I have been working in the Oil & Gas Industry for over 25 years now.
 
Core Discipline is Instrumentation and Control Systems.
 
Completed Bsc Honours Degree (B29 in Computing) with the Open University in 2012.
 
Currently, Offshore Installation Manager for the Beryl Bravo platform, which is located ~180 miles NE of Aberdeen, Scotland in the Northern North Sea.
Formely on the Forties Charlie platform, which is located ~110Miles NE of Aberdeen.
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
GeneralMy vote of 5 PinprofessionalBrian A Stephens25-Jun-14 9:32 
GeneralMy vote of 5 PinmemberKarstenK23-Jun-14 0:43 
QuestionMy vote of 5! [modified] PinprofessionalAgent__00718-Jun-14 19:42 
QuestionGoogle's facsist regime is killing your app :-( PinprofessionalSander Rossel7-Jun-14 1:38 
AnswerRe: Google's facsist regime is killing your app :-( PinprotectorDaveAuld7-Jun-14 11:09 
GeneralRe: Google's facsist regime is killing your app :-( PinprofessionalSander Rossel7-Jun-14 12:11 
GeneralRe: Google's facsist regime is killing your app :-( PinprotectorDaveAuld8-Jun-14 5:19 
GeneralRe: Google's facsist regime is killing your app :-( PinprofessionalSander Rossel8-Jun-14 12:53 
AnswerRe: Google's facsist regime is killing your app :-( PinprotectorDaveAuld18-Jun-14 10:16 
GeneralRe: Google's facsist regime is killing your app :-( PinprofessionalSander Rossel18-Jun-14 12:29 
GeneralRe: Google's facsist regime is killing your app :-( PinprotectorDaveAuld18-Jun-14 12:37 
GeneralRe: Google's facsist regime is killing your app :-( PinprofessionalSander Rossel18-Jun-14 12:56 
GeneralMy vote of 5 PinprofessionalSunasara Imdadhusen21-May-14 2:58 
GeneralMy vote of 5 PinmemberPablo Aliskevicius24-Oct-13 1:06 
GeneralMy vote of 5 PinmemberVitorHugoGarcia5-Mar-13 5:11 
QuestionExtension installation link - is broken Pinmemberjibesh18-Jan-13 9:20 
AnswerRe: Extension installation link - is broken PinprotectorDaveAuld18-May-13 6:54 
GeneralMy vote of 5 PinmemberAamer Alduais16-Jul-12 20:26 
GeneralMy vote of 5 PinmemberTim Corey21-May-12 4:01 
GeneralMy vote of 5 PinmemberPravin Patil, Mumbai2-Apr-12 3:52 
GeneralMy vote of 5 PinmemberPranit Kothari16-Dec-11 6:18 
Thx!
GeneralMy vote of 5 PinmemberNagy Vilmos22-Sep-11 6:32 
QuestionMessage Automatically Removed Pinmemberaasser1-Sep-11 17:51 
GeneralMy vote of 5 PinmemberMika Wendelius23-Mar-11 8:05 
GeneralError in Code Above, PinmemberDaveAuld22-Mar-11 6:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 6 Aug 2014
Article Copyright 2010 by DaveAuld
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid