IE Context Menu Extension

by Gong Liu January 31, 2009 05:11

Years ago I stumbled upon the concept of IE Context Menu Extension. I created a menu extension to help me to lookup an online dictionary for any highlighted word. Here is how I use it. Wheneve I encounter an unfamiliar word while browsing the web, I just highlight the word, right click on it, and select a custom context menu item "Dictionary Lookup". This will launch a new browser window and take me straight to the online dictionary with the word already looked up. Without the extension I would have to do all this menually - highlight the word, right click, copy it, launch a new browser window, type in the URL of the online dictionary, paste the word, and press a Lookup button. So by comparison my little extension came in really handy. I have been using it ever since.

Over the years I added a few more items that I thought were also very useful. These include:

  • Google highlighted word or words
  • Map highlighted address
  • Lookup highlighted word in Wikipedia
  • Translate highlighted word in a different language
  • Share a web page on Facebook, Digg, etc.   

Now let's take a look at how to implement these extensions.

Google Highlighted Word or Words

We first need to create a HTML page and save it as, say, C:\MyScripts\IE_MenuExt\goto_google.htm. The HTML page contains just this javscript:

<script language="javascript" defer>
    var doc = external.menuArguments.document;
    var text = doc.selection.createRange().text;
    var url = "http://www.google.com/search?hl=en&q=" + escape(text);
    var w = window.open(url, "newwin");
    w.focus();
</script>

This script simply gets the highlighted text, creates a Google search URL out of it, and opens the URL in a new browser window. Another version of the script is to quote the highlighted text, which instructs the Google search engine to search the exact phrase:

<script language="javascript" defer>
    var doc = external.menuArguments.document;
    var text = doc.selection.createRange().text;
    var url = "http://www.google.com/search?hl=en&q=\"" + escape(trim(text)) + "\"";
    var w = window.open(url, "newwin");
    w.focus();
</script>

<script language="javascript">
//source: http://blog.stevenlevithan.com/archives/faster-trim-javascript
function trim(str) {
    var str = str.replace(/^\s\s*/, ''),
    ws = /\s/,
    i = str.length;
    while (ws.test(str.charAt(--i)));
        return str.slice(0, i + 1);
}
</script>

The only difference in this script from the first one is that the highlighted text is trimmed and quoted. The trim function is taken from Flagrant Badassery's blog.

The second step is to create the following registry key and save it to a .reg file, such as C:\MyScripts\IE_MenuExt\install.reg:

[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Google It]
@="file://C:\\MyScripts\\IE_MenuExt\\goto_google.htm"
"Contexts"=dword:00000010

The "Contexts" value 0x00000010 indicates that the context menu item "Google It" appears when you right click a highlighted (or selected) text in IE browser.

Map Highlighted Address

The script (C:\MyScripts\IE_MenuExt\goto_google_maps.htm):

<script language="javascript" defer>
    var doc = external.menuArguments.document;
    var text = doc.selection.createRange().text;
    text = trim(text).replace(/\n/, ", ");
    var url = "http://maps.google.com/maps?f=q&source=s_q&hl=en&q=" + escape(text);
    var w = window.open(url, "newwin");
    w.focus();
</script>

Since the highlighted address may be in multiple lines, the above script will replace any new line characters with commas to make it a single liner, and then feed it to Google Maps.

The registry key:

[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Map It]
@="file://C:\\MyScripts\\IE_MenuExt\\goto_google_maps.htm"
"Contexts"=dword:00000010

Note: when you use the "Map It" menu extension, make sure you only highlight the address part, not anything more. Otherwise, you may confuse the Goggle Maps geocoder.

Lookup Highlighted Word in Wikipedia

The script (C:\MyScripts\IE_MenuExt\goto_wikipedia.htm):

<script language="javascript" defer>
    var doc = external.menuArguments.document;
    var text = doc.selection.createRange().text;
    text = trim(text).replace(/ /, "_");
    var url = "http://en.wikipedia.org/wiki/" + text;
    var w = window.open(url, "newwin");
    w.focus();
</script>

The key point here is that if multiple words are highlighted, they need to be concatenated with an underscore "_". On Wikipedia site if an article is not found with the extact words, you will be presented with a link to search the words. So you don't need a separate menu item to search Wikipedia.  

The registry key:

[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Go Wikipedia]
@="file://C:\\MyScripts\\IE_MenuExt\\goto_wikipedia.htm"
"Contexts"=dword:00000010

Translate Highlighted Word in a Different Language

I have been teaching myself Spanish recently, and I find this Spanish online dictionary is very useful. It translates both ways, Spanish -> English and English -> Spanish. Now it becomes a permanent part of my menu extension collection.

The script (C:\MyScripts\IE_MenuExt\goto_spanish.htm):

<script language="javascript" defer>
    var doc = external.menuArguments.document;
    var theWord = doc.selection.createRange().text;
    var url = "http://www.spanishdict.com/translate/" + theWord;
    var w = window.open(url, "newwin");
    w.focus();
</script>

The registry key:

[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Spanish Lookup]
@="file://C:\\MyScripts\\IE_MenuExt\\goto_spanish.htm"
"Contexts"=dword:00000010

Share a Web Page on Facebook

If you find a web page interesting and want to share it on Facebook, Digg, Reddit, or whatever online community you belong to, you only need to click an approprate sharing icon (similar to those found at the end of this post) - if the page offers one. However, if the page does not have a sharing icon, you can use the following menu extension to do the trick.  

The script (C:\MyScripts\IE_MenuExt\goto_facebook.htm):

<script language="javascript" defer>
    var doc = external.menuArguments.document;
    var u = doc.URL;
    var t = doc.title;
    var url= "http://www.facebook.com/sharer.php?u=" + escape(u) + "&amp;t=" + escape(t);
    var w = window.open(url, "newwin");
    w.focus();
</script>

Note: here I only use Facebook as an example. If you are into other online communities, simply change the URL in the script. 

The registry key:

[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Share on Facebook]
@="file://C:\\MyScripts\\IE_MenuExt\\goto_facebook.htm"
"Contexts"=dword:00000001

The "Contexts" value 0x00000001 means the default context. I.e. the "Share on Facebook" context menu item appears when you right click anywhere on the page (except links, images, etc.). No highlighting text is necessary.

More Complex Example - Hook up a Sudoku Solver

The above menu extensions are really simple ones. In fact, we can hook up any program logic we like with a menu extension. For example, I like to go to my favorite Sudoku wbsite to get my brain teased. However, at times I got frustrated with some harder games and hoped to see them solved right away. So I hooked up a Sodoku solver I found here with a menu extension. Now whenever I have the urge to see the solution of a game, I just right click and select the solver (see screenshots below).

  

  

Install and Uninstall Menu Extensions

You can download all the javascript and registry key files discussed in this post below.

IE_MenuExt.zip (6.33 kb)

To install all the menu extensions, just copy the IE_MenuExt folder to C:\MyScripts\ and double click the install.reg file. Note: if you prefer a different installation location, you need to edit the install.reg file before running it.

To uninstall all the menu extensions, double click the uninstall.reg file. The uninstall.reg file looks like this:

REGEDIT4
[-HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Dictionary Lookup]
[-HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Thesaurus Lookup]
[-HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Spanish Lookup]
[-HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Google It]
[-HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Google It quoted]
[-HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Go Wikipedia]
[-HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Map It]
[-HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Search Images]
[-HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Share on Facebook]
[-HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Digg It]
[-HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Sudoku Solver]

Stored Procedure for Spatial Search

by Gong Liu January 23, 2009 19:59

If you have some location based data, such as point of interests (POI) data in your database, one common query you want to have is to search POIs within an area, typically a circle defined by a center point and a radius. This is so called spatial search. To increase the speed of spatial search in a relational database, the first thing you do is to index the latitude and longitude columns. The next thing is to write an efficient stored procedure for the job - a topic of this post.

To determine if a point is within a circle, you need to calculate the distance between the point and the search center. An accurate distance formular can be quite complex considering the Earth's curvature and irregular shape, and thus takes a lot of time to compute, especially if you have millions of POIs in your database. However, if the search area is not very big, as in most real world cases, a simplified distance formular is good enough:

Based on the above formular, the spatial search stored procedure can be written as:

 CREATE PROCEDURE [dbo].[GetPOIs_Circle]
  @latitude float,
  @longitude float,
  @radius float,
  @catCode varchar(10), 
  @maxReturns int
 AS
 BEGIN
  SET NOCOUNT ON;
  Declare @R float -- Earth's radius in miles 
  Set @R = 3958.755866
  Set ROWCOUNT @maxReturns
  Select * From
  (
   Select POIName, Address, City, State, Country, Zip, Phone, Website, Latitude, Longitude,
     @R * radians(sqrt(square(Latitude - @latitude) +
     square((Longitude - @longitude) * cos(radians((Latitude + @latitude)/2))))) As Distance
   From POIs
   Where CatCode = @catCode
  ) As a
  Where a.Distance < @radius
  Order By a.Distance
 END
 GO 

The stored procedure will return no more than maxReturns POIs of specified category within the specified circular area, sorted by the distance from the search center. This stored procedure works but it is not optimal. It has to calculate many distances before it can narrow down to the POIs whose distances from the search center are smaller than the radius. A quick way to narrow down potential POIs is to search within a square that the search circle inscribes. Once these POIs are found, they can be further narrowed down to within the search circle using the distance formular. So the modified stored procedure is:

CREATE PROCEDURE [dbo].[GetPOIs_SquareCircle]
 @latitude float,
 @longitude float,
 @radius float,
 @catCode varchar(10), 
 @maxReturns int
AS
BEGIN
 SET NOCOUNT ON;
 Declare @R  float -- Earth's radius in miles 
 Set @R = 3958.755866
 -- Find bounding box
 declare @minLat float
 declare @minLon float
 declare @maxLat float
 declare @maxLon float
 declare @sita float
 declare @corr float
 set @sita = degrees(@radius/@R)
 set @corr = 1/cos(radians(@latitude))
 set @minLat = @latitude - @sita
 set @maxLat = @latitude + @sita
 set @minLon = @longitude - @sita * @corr
 set @maxLon = @longitude + @sita * @corr 
 Set ROWCOUNT @maxReturns
 Select * From
 (
  Select POIName, Address, City, State, Country, Zip, Phone, Website, Latitude, Longitude,
   @R * radians(sqrt(square(Latitude - @latitude) +
   square((Longitude - @longitude) * cos(radians((Latitude + @latitude)/2))))) As Distance
  From POIs
  Where CatCode = @catCode and 
   (Latitude > @minLat and Latitude < @maxLat) and
   (Longitude > @minLon and Longitude < @maxLon)
 ) As a
 Where a.Distance < @radius
 Order By a.Distance
END
GO

This version of the stored procedure first calculates the bounding box of the search circle and then applies it to the Where clause of the Select statement. Now let's compare the efficiency of the two stored procedures using the Actual Execution Plan tool in SQL Server 2005.

               

The above table showes the estimated CPU costs when both stored procedures are used for the same search - search the Golf Course category within 100 miles radius from downtown Los Angeles. The main POIs table contains some 14 million records. As you can see, the overall improvement of the second stored procedure over the first one is about 32% in terms of CPU usage. The estimated I/O costs are identical for both stored procedures, and thus are not included in the table. Percentage wise the biggest improvement is the Compute Scalar operator, which in our case is the evaluation of the distance formular. As expected, the second stored procedure avoids significant amount of distance calculations because of the square (bounding box) filtering. This also suggests that with the methodology of the second stored procedure we can afford more complex distance formular or shape, such as a polygon search area, without compromising the performance.  

RC4 Encryption in C#

by Gong Liu January 19, 2009 21:46

Recently I have involved in converting an ASP project to an ASP.NET project. A part of it is to translate the RC4 encryption algorithm from VB Script to C#. The VB Script implementation was taken from the article RC4 Encryption Using ASP & VBScript by Mike Shaffer published on 4guysfromrolla.com. I thought the work might be useful for someone else. So here you go...

The C# implementation of the RC4 algorithm consists of a single class RC4, which has the following public properties and methods:

  • Text property - the text to be encrypted or decrypted,
  • Password property - the password you use for the encryption or decryption,
  • EnDeCrypt method - this is the method you use to encrypt or decrypt the Text with the given Password,
  • StrToHexStr method - a utility method used to convert an encrypted string to a hex string so that it can be stored in a database or transmitted over a URL safely,
  • HexStrToStr method - a utility method used to convert a hex string to a string. It has the opposite effect as StrToHexStr method.

Now let's see some code. The following is the code snip for the EnDeCrypt method: 

        public string EnDeCrypt()
        {
            RC4Initialize();

            int i = 0, j = 0, k  = 0;
            StringBuilder cipher = new StringBuilder();
            for (int a = 0; a < text.Length; a++)
            {
                i = (i + 1) % N;
                j = (j + sbox[i]) % N;
                int tempSwap = sbox[i];
                sbox[i] = sbox[j];
                sbox[j] = tempSwap;

                k = sbox[(sbox[i] + sbox[j]) % N];
                int cipherBy = ((int)text[a]) ^ k;  //xor operation
                cipher.Append(Convert.ToChar(cipherBy));
            }
            return cipher.ToString();
        }

        private void RC4Initialize()
        {
            sbox = new int[N];
            int[] key = new int[N];
            int n = password.Length;
            for (int a = 0; a < N; a++)
            {
                key[a] = (int)password[a % n];
                sbox[a] = a;
            }

            int b = 0;
            for (int a = 0; a < N; a++)
            {
                b = (b + sbox[a] + key[a]) % N;
                int tempSwap = sbox[a];
                sbox[a] = sbox[b];
                sbox[b] = tempSwap;
            }
        }

The above code is a direct translation from the original VB Script. Note that the StringBuilder class is used here to improve the efficiency of string concatenation. The code snips for the StrToHexStr and HexStrToStr methods are as follows:

        public static string StrToHexStr(string str)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < str.Length; i++)
            {
                int v = Convert.ToInt32(str[i]);
                sb.Append(string.Format("{0:X2}", v));
            }
            return sb.ToString();
        }

        public static string HexStrToStr(string hexStr)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < hexStr.Length; i += 2)
            {
                int n = Convert.ToInt32(hexStr.Substring(i, 2), 16);
                sb.Append(Convert.ToChar(n));
            }
            return sb.ToString();        
        }

The StrToHexStr method converts each character in the input string to a 2-byte hex number without any prefix or suffix. The HexStrToStr method takes a hex string (generated by the StrToHexStr method) and converts it back to the original string.

Here is the code to show how to use the RC4 class in a simple ASP.Net project:

        protected void btnDoIt_Click(object sender, EventArgs e)
        {
            RC4 rc4 = new RC4(txtPassword.Text, txtText.Text);
            txtHexDump.Text = RC4.StrToHexStr(rc4.EnDeCrypt());
            rc4.Text = RC4.HexStrToStr(txtHexDump.Text);
            txtDecrypted.Text = rc4.EnDeCrypt();           
        }

In the "Do It!" button click event handler we first instantiate the RC4 class by passing the password and the text to be encrypted to the constructor. In the next line we call the EnDeCrypt method to encrypt the text, and then convert the encrypted string to a hex string by calling the StrToHexStr utility method. For decryption we simply do the opposite. Call the HexStrToStr method to convert the hex dump back to the original encrypted string and then call EnDeCrypt method again to decrypt it. The screenshot below shows what looks like after the "Do It!" button is clicked.

You can see a live demo here

You can download the RC4 class along with the sample ASP.Net project below.

TestRC4.zip (23.57 kb)

About

A seasoned computer professional. A tofu culture evangelist...
more >>

Tag Cloud

Calendar

<<  April 2017  >>
MoTuWeThFrSaSu
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

View posts in large calendar
Copyright © 2008-2011 Gong Liu. All rights reserved. | credits | contact me
The content on this site represents my own personal opinions, and does not reflect those of my employer in any way.