<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>richwklein.com &#187; Add-On</title>
	<atom:link href="http://richwklein.com/tag/add-on/feed/" rel="self" type="application/rss+xml" />
	<link>http://richwklein.com</link>
	<description>A blog about nothing</description>
	<lastBuildDate>Tue, 05 Apr 2011 02:07:01 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1</generator>
	<atom:link rel='hub' href='http://richwklein.com/?pushpress=hub'/>
		<item>
		<title>JavaScript Singletons in Firefox Add-ons</title>
		<link>http://richwklein.com/2010/01/19/javascript-singletons-in-firefox-add-ons/</link>
		<comments>http://richwklein.com/2010/01/19/javascript-singletons-in-firefox-add-ons/#comments</comments>
		<pubDate>Tue, 19 Jan 2010 21:20:08 +0000</pubDate>
		<dc:creator>Rich</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[Add-On]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Extension]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[Hack]]></category>
		<category><![CDATA[Iframe]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Service]]></category>
		<category><![CDATA[Singleton]]></category>
		<category><![CDATA[Tip]]></category>
		<category><![CDATA[Trick]]></category>
		<category><![CDATA[XPCOM]]></category>

		<guid isPermaLink="false">http://richwklein.com/?p=984</guid>
		<description><![CDATA[Occasionally when developing add-ons for Firefox, you want your JavaScript to only run once for the lifetime of the application. Normally, you would place your code in a browser overlay, but this causes the code to run every time a new window is opened. There are several ways to get around this. You could write [...]]]></description>
			<content:encoded><![CDATA[<p>Occasionally when developing <a href="http://addons.mozilla.org">add-ons</a> for <a href="http://getfirefox.com">Firefox</a>, you want your JavaScript to only run once for the lifetime of the application.  Normally, you would place your code in a browser overlay, but this causes the code to run every time a new window is opened.  There are several ways to get around this.  You could write an <a href="https://developer.mozilla.org/en/XPCOM">XPCOM service</a> or use a <a href="https://developer.mozilla.org/en/JavaScript_code_modules">JavaScript module</a>.  These are both valid methods and work very well if your code is Firefox specific.  However, if you are also writing your add-on for <a href="http://www.google.com/chrome">Chrome</a>, you might want the code to be able to run in both places.  Here is a nice little hack that uses the hidden window so that you can run your <a href="http://code.google.com/chrome/extensions/background_pages.html">Chrome background page</a> within an iframe in Firefox. </p>
<p><span id="more-984"></span></p>
<pre>

  function getBackgroundPage(id, src) {

    // get the firefox hidden window
    var win = Components.classes["@mozilla.org/appshell/appShellService;1"].
                   getService(Components.interfaces.nsIAppShellService).
                   hiddenDOMWindow;

    // if the iframe was previously loaded store it and callback
    var iframe = win.document.getElementsById(id);
    if (iframe)
      return iframe.contentWindow;

    // create the iframe
    iframe = win.document.createElement("iframe");
    iframe.setAttribute("id", id);
    iframe.setAttribute("style", "display:none;");

    // load the source
    iframe.setAttribute("src", src);
    win.document.documentElement.appendChild(iframe);

    // return the content window
    return iframe.contentWindow;
</pre>
<p>Now you can access your background page by including the script in your overlay and doing:</p>
<pre>
  var bgp = getBackgroundPage("id for background page", "url of the background page");
</pre>
<p>** UPDATE **<br />
instead of setting the style to display:none you should instead set the attribute &#8220;collapsed&#8221; to true.  This causes docshell errors in some cases.</p>
<p><strike>iframe.setAttribute(&#8220;style&#8221;, &#8220;display:none;&#8221;);</strike><br />
iframe.setAttribute(&#8220;collapsed&#8221;, &#8220;true&#8221;);</p>
]]></content:encoded>
			<wfw:commentRss>http://richwklein.com/2010/01/19/javascript-singletons-in-firefox-add-ons/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Diamond in the HTML &#8220;Rough&#8221;</title>
		<link>http://richwklein.com/2009/12/08/diamond-in-the-html-rough/</link>
		<comments>http://richwklein.com/2009/12/08/diamond-in-the-html-rough/#comments</comments>
		<pubDate>Tue, 08 Dec 2009 17:12:35 +0000</pubDate>
		<dc:creator>Rich</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[Add-On]]></category>
		<category><![CDATA[Clover]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Diamond]]></category>
		<category><![CDATA[Extension]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[Hack]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[KwiClick]]></category>
		<category><![CDATA[Search]]></category>
		<category><![CDATA[Search Clover]]></category>
		<category><![CDATA[Shadow]]></category>
		<category><![CDATA[Transform]]></category>

		<guid isPermaLink="false">http://richwklein.com/?p=937</guid>
		<description><![CDATA[For the latest version of KwiClick we introduced a feature called search clovers. When you highlight a word on a page, a single diamond shaped &#8220;clover&#8221; appears. When you hover over the diamond, 3 more diamonds appear creating a 4 leaf clover configuration. To create these diamonds we use two html tags, an img surrounded [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://richwklein.com/wp-content/uploads/2009/12/clovers.png"><img src="http://richwklein.com/wp-content/uploads/2009/12/clovers.png" alt="KwiClick Search Clovers" title="KwiClick Search Clovers" width="106" height="125" class="alignleft pad" /></a>For the latest <a href="https://addons.mozilla.org/en-US/firefox/addon/5655">version</a> of <a href="http://www.kwiclick.com/">KwiClick</a> we introduced a feature called search clovers.  When you highlight a word on a page, a single diamond shaped &#8220;clover&#8221; appears.  When you hover over the diamond, 3 more diamonds appear creating a 4 leaf clover configuration.  To create these diamonds we use two html tags, an img surrounded by a div.  <a href="http://richwklein.com/wp-content/uploads/2009/12/cloveroverlap.png"><img src="http://richwklein.com/wp-content/uploads/2009/12/cloveroverlap.png" alt="Clover Overlap" title="Clover Overlap" width="91" height="89" class="alignright pad" /></a>In Firefox versions prior to 3.5 we used a background image on the div to create the look of a the diamond.  The corners of the images overlapped each other causing problems when a user tried to hover or click on one of them.</p>
<p><span id="more-937"></span><br />
With Firefox 3.5 we were able to fix this by creating the diamond using only html and some specialized css tags.  Using the tags: -moz-transform, -moz-border-radius, and -moz-box-shadow we rotate the div 45 degrees and add a shadow to them.</p>
<pre>
  display: inline-block !important;
  border: 2px solid #494949 !important;
  background: none !important;
  background-color: white !important;
  -moz-transform: rotate(45deg) !important;
  -moz-border-radius: 4px !important;
  -moz-box-shadow: grey 2px 2px 2px !important;
</pre>
<p> This also rotates the content within the div.  To fix that we rotate the image back 45 degrees. </p>
<pre>
  -moz-transform: rotate(-45deg) !important;
</pre>
<p>And viola, a diamond with all the html event goodness, made with only html and css.  Safari and Chrome support these transforms with -webkit-transform so this same effect can be used there.  </p>
]]></content:encoded>
			<wfw:commentRss>http://richwklein.com/2009/12/08/diamond-in-the-html-rough/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>KwiClick 2.4.1</title>
		<link>http://richwklein.com/2009/11/29/kwiclick-2-4-1/</link>
		<comments>http://richwklein.com/2009/11/29/kwiclick-2-4-1/#comments</comments>
		<pubDate>Sun, 29 Nov 2009 13:58:28 +0000</pubDate>
		<dc:creator>Rich</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[Add-On]]></category>
		<category><![CDATA[Clover]]></category>
		<category><![CDATA[Extension]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[KwiClick]]></category>
		<category><![CDATA[Search]]></category>
		<category><![CDATA[Search Clover]]></category>
		<category><![CDATA[Update]]></category>
		<category><![CDATA[Version]]></category>

		<guid isPermaLink="false">http://richwklein.com/?p=917</guid>
		<description><![CDATA[KwiClick 2.4.1 was released on AMO this week. This is a pretty large update from the last AMO release so make sure to check it out. Here is a list of what is new in this release: New Search Clovers for selection based searching functionality Updated Bing provider Updated compatibility for ff 3.6betas Improve sqlite [...]]]></description>
			<content:encoded><![CDATA[<p><a href="https://addons.mozilla.org/en-US/firefox/addon/5655">KwiClick 2.4.1</a> was released on AMO this week.  This is a pretty large update from the last AMO release so make sure to check it out.  Here is a list of what is new in this release:</p>
<ul>
<li>New Search Clovers for selection based searching functionality</li>
<li>Updated Bing provider</li>
<li>Updated compatibility for ff 3.6betas</li>
<li>Improve sqlite performance</li>
<li>Trim leading and trailing spaces from searches to improve accuracy</li>
<li>Updated kwiclick skin</li>
<li>Replace getBoxObjectFor with getBoundingClientRect</li>
<li>Clover preferences to fine tune functionality</li>
<li>Turn on/off clover from statusbar button</li>
<li>Updated wikipedia provider increases accuracy of searching and results</li>
</ul>
<p>Here is the video for the 2.4 release which was submitted to the extend firefox contest.  This is a pretty good explanation of the things you can do with <a href="http://kwiclick.com">KwiClick</a>.<br />
<object class="pad video"><param name="movie" value="http://www.youtube.com/v/tsP7LuuRWPQ&#038;hl=en_US&#038;fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/tsP7LuuRWPQ&#038;hl=en_US&#038;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
]]></content:encoded>
			<wfw:commentRss>http://richwklein.com/2009/11/29/kwiclick-2-4-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>FAIL:  libIDL on Mac OS X 10.6 (Snow Leopard)</title>
		<link>http://richwklein.com/2009/10/27/fail-libidl-on-mac-os-x-10-6-snow-leopard/</link>
		<comments>http://richwklein.com/2009/10/27/fail-libidl-on-mac-os-x-10-6-snow-leopard/#comments</comments>
		<pubDate>Tue, 27 Oct 2009 19:11:08 +0000</pubDate>
		<dc:creator>Rich</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[10.6]]></category>
		<category><![CDATA[32 bit]]></category>
		<category><![CDATA[64]]></category>
		<category><![CDATA[Add-On]]></category>
		<category><![CDATA[bit]]></category>
		<category><![CDATA[Compile]]></category>
		<category><![CDATA[Extension]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[Gecko]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[LibIDL]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[OS]]></category>
		<category><![CDATA[Question]]></category>
		<category><![CDATA[SDK]]></category>
		<category><![CDATA[Snow Leopard]]></category>
		<category><![CDATA[XPCOM]]></category>

		<guid isPermaLink="false">http://richwklein.com/?p=904</guid>
		<description><![CDATA[Since I upgraded my laptop to Snow Leopard, I&#8217;ve been trying to setup my development environment so that I can compile idl files. I need to do this for one of the Firefox extension I work on. I&#8217;ve been able to put this off for a while since my interfaces haven&#8217;t changed in a while. [...]]]></description>
			<content:encoded><![CDATA[<p>Since I upgraded my laptop to Snow Leopard, I&#8217;ve been trying to setup my development environment so that I can compile idl files.  I need to do this for one of the Firefox extension I work on.  I&#8217;ve been able to put this off for a while since my interfaces haven&#8217;t changed in a while.  I&#8217;ve recently changed an interface so now I need to get the idl compiling setup.  When I&#8217;ve set this up in the past, I haven&#8217;t had any problems.  I install the gecko sdk, install libidl, and I&#8217;m ready to roll.  This time I can&#8217;t make it work so I&#8217;m turning to you, the interweb, to help me figure this out.  Here is what I&#8217;ve go so far.</p>
<p>I install the <a href="https://developer.mozilla.org/en/Gecko_SDK">Gecko 1.9.1 SDK</a> from developer.mozilla.org.  I then install libidl using the following commands in MacPorts:</p>
<pre>
sudo port selfupdate
sudo port sync
sudo port install libidl
</pre>
<p>Now, when I run my build script I get the error:</p>
<pre>
     [exec] dyld: Library not loaded: /opt/local/lib/libIDL-2.0.dylib
     [exec]   Referenced from: /Developer/xulrunner-sdk-1.9.1/bin/xpidl
     [exec]   Reason: no suitable image found.  Did find:
     [exec] 	/opt/local/lib/libIDL-2.0.dylib: mach-o, but wrong architecture
</pre>
<p>Figuring that libidl is compiled as 64 bit and the gecko sdk is 32 bit, I go searching the web.  This leads me to a forum post on <a href="http://forums.mozillazine.org/viewtopic.php?f=12&#038;t=1518455">compiling Camino on Snow Leopard</a>.</p>
<blockquote><p>
Mac OS 10.6 is 64-bit as native, while Camino is far from 64-bit ready. So cross-compiling to 32-bit is required.<br />
First hurdle was to cross-compile and install the required libIDL library, in 32-bit. Luckily MacPorts contain a universal libIDL, so after I got the hint about &#8220;versions&#8221; I got that going using the command &#8211; &#8220;sudo port install libIDL +universal&#8221;.
</p></blockquote>
<p>Alright, so now I&#8217;m trying to install the universal libIDL file and I hit another error.</p>
<pre>
Command output:       _IDL_inhibit_get in libIDL_2_la-parser.o
      _IDL_queue_new_ident_comment in libIDL_2_la-parser.o
      _IDL_file_set in libIDL_2_la-parser.o
      ___IDL_do_pragma in libIDL_2_la-parser.o
      _IDL_ns_scope_levels_from_here in libIDL_2_la-ns.o
      _IDL_tree_properties_copy in libIDL_2_la-util.o
      _IDL_tree_walk2 in libIDL_2_la-util.o
      _IDL_tree_to_IDL in libIDL_2_la-util.o
      _IDL_tree_get_scope in libIDL_2_la-util.o
      _IDL_tree_remove_inhibits in libIDL_2_la-util.o
      _IDL_tree_property_remove in libIDL_2_la-util.o
      _IDL_tree_property_get in libIDL_2_la-util.o
      _IDL_tree_property_set in libIDL_2_la-util.o
  "_g_log", referenced from:
      _IDL_check_type_cast in libIDL_2_la-util.o
      _IDL_tree_walk_real in libIDL_2_la-util.o
      _IDL_tree_walk_real in libIDL_2_la-util.o
      _IDL_emit_IDL_literal in libIDL_2_la-util.o
      _IDL_tree_get_node_info in libIDL_2_la-util.o
      _IDL_tree_get_scope in libIDL_2_la-util.o
      _IDL_tree_remove_empty_modules in libIDL_2_la-util.o
      _IDL_tree_remove_inhibits in libIDL_2_la-util.o
      _IDL_tree_process_forward_dcls in libIDL_2_la-util.o
      _load_inhibits in libIDL_2_la-util.o
      _IDL_tree_free in libIDL_2_la-util.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
make[2]: *** [libIDL-2.la] Error 1
make[1]: *** [all-recursive] Error 1
make: *** [all] Error 2
</pre>
<p>Here is where I&#8217;m stuck.  I have no idea what symbols are missing.  Please, leave me a comment if you have any ideas on how to make this work.</p>
<p>** Update **<br />
This is what I did to get this to work.</p>
<pre>
sudo port -f uninstall installed
sudo port install libIDL universal
</pre>
]]></content:encoded>
			<wfw:commentRss>http://richwklein.com/2009/10/27/fail-libidl-on-mac-os-x-10-6-snow-leopard/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Tab Listener</title>
		<link>http://richwklein.com/2009/07/21/tab-listener/</link>
		<comments>http://richwklein.com/2009/07/21/tab-listener/#comments</comments>
		<pubDate>Tue, 21 Jul 2009 14:00:17 +0000</pubDate>
		<dc:creator>Rich</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[Add-On]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Controller]]></category>
		<category><![CDATA[Extension]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Listener]]></category>
		<category><![CDATA[Private]]></category>
		<category><![CDATA[Private Browsing]]></category>
		<category><![CDATA[Session]]></category>
		<category><![CDATA[Session Store]]></category>
		<category><![CDATA[Store]]></category>
		<category><![CDATA[Tab]]></category>
		<category><![CDATA[Tip]]></category>
		<category><![CDATA[Trick]]></category>

		<guid isPermaLink="false">http://richwklein.com/?p=769</guid>
		<description><![CDATA[Sometimes when developing Firefox extensions, you need to store information on a per tab basis. I’ve developed some reusable code for doing this. As an added bonus the information is stored using the session store api so it will be restored when a tab is restored. It also listens to the private browsing notifications and [...]]]></description>
			<content:encoded><![CDATA[<p>Sometimes when developing Firefox <a href="https://addons.mozilla.org/">extensions</a>, you need to store information on a per tab basis.  I’ve developed some reusable code for doing this.  As an added bonus the information is stored using the <a href=”https://developer.mozilla.org/en/Session_store_API”>session store api</a> so it will be restored when a tab is restored.  It also listens to the <a href=”https://developer.mozilla.org/En/Supporting_private_browsing_mode”>private browsing notifications</a> and clears its state when entering or exiting private browsing mode.  This code only works in Firefox 3.5 or greater.  It is based on 2 JavaScript objects.  The first is a tab controller that will store the actual information.  This is the object you will need to modify to suit your own purpose.  The other is the tab listener that listens to different DOM events to keep track of the first object.</p>
<pre>
/*******************************************************************************
 *  Object used to store information on a per tab basis.  This should be passed
 *  into the TabListener as the func argument.
 *
 *  @version 1.0
 ******************************************************************************/
function TabController() {}
TabController.prototype = {

  /**
   * IMPORTANT!!! Add the properties here that you need to keep track of.
   */

  /**
   * Called when a tab is being restored so that we restore the controllers state.
   *
   * @param   data           A string of json data stored in the session store.
   */
  fromString: function TabController_fromString(data) {
    var json = window.JSON.parse(data);
    for (var property in json)
        this[property] = json[property];
  },

  /**
   * Called when a tab is being closed so that we can store the controllers state.
   *
   * @returns                A string of json data stored in the session store.
   */
  toString: function TabController_toString() {
    var json = {};
    for (var property in this)
      json[property] = this[property]
    return window.JSON.stringify(json);
  }
};
</pre>
<p><span id="more-769"></span></p>
<pre>
/*******************************************************************************
 *  Object used to listen to tab events and keep track of controllers
 *
 *  @param    func           The controller function to instantiate.
 *
 *  @version 1.0
 ******************************************************************************/
function TabListener(func) {
  this.func = func;

  // get the session store
  this.store = Components.classes["@mozilla.org/browser/sessionstore;1"].
               getService(Components.interfaces.nsISessionStore);

  // prefill the controller array
  this.prefill();

  // add session event listeners
  document.addEventListener("SSTabRestoring", this, false);
  document.addEventListener("SSTabClosing", this, false);

  // add tab event listeners
  var container = getBrowser().tabContainer;
  container.addEventListener("TabOpen", this, false);
  container.addEventListener("TabMove", this, false);
  container.addEventListener("TabClose", this, false);
  container.addEventListener("TabSelect", this, false);

  // add the private browsing observer
  var os = Components.classes["@mozilla.org/observer-service;1"].
           getService(Components.interfaces.nsIObserverService);
  os.addObserver(this, "private-browsing", true);
}
TabListener.prototype = {
  func: null,
  store: null,

  /**
   * Array of controllers stored in the same order as tabs.
   */
  _controllers: null,
  get controllers() { return this._controllers; },
  set controllers(val) { this._controllers = val; },

  /**
   * Index of the selected tab.
   */
  _index: null,
  get index() { return this._index; },
  set index(val) { this._index = val; },

  /**
   * Controller for the selected tab.
   */
  get selected() { return this.controllers[this.index]; },

  /**
   * Prefill the array of controllers.
   */
  prefill: function TabListener_prefill() {

    // set the index to the selected tab index
    this.index = getBrowser().selectedTab._tPos;

    // loop through the open tabs and instantiate a controller for each tab.
    controllers = []
    var container = getBrowser().tabContainer;
    for (var i=0; i&lt;container.childNodes.length; i++)
      controllers.append(new this.func());
    this.controllers = controllers;
  },

  /* ::::: nsIDOMEventListener ::::: */
  handleEvent: function urlnavOverlay_handleEvent(event) {
    var index = event.originalTarget._tPos;
    var controller;

    switch (event.type) {

    // tab open - store a new controller in the array
    case "TabOpen":
      this.controllers.splice(index, 0, new this.func());
      break;

    // tab is being restored - restore controller from the session store
    case "SSTabRestoring":
      controller = this.controllers[index];
      var data = this.store.getTabValue(event.originalTarget, "tab-controller");
      if (data != "undefined")
        controller.fromString(data);
      break;

    // user moved the tab - move the controller in the array
    case "TabMove":
      var oldIndex = event.detail;
      this.controllers.splice(index, 0, this.controllers.splice(oldIndex, 1)[0]);
      break;

    // tab is closing - save state to the session store
    case "SSTabClosing":
      controller = this.controllers[index];
      this.store.setTabValue(event.originalTarget, "tab-controller", controller.toString());
      break;

    // tab is closed - remove the controller from the array
    case "TabClose":
      this.controllers.splice(index, 1);
      break;

    // tab is selected - change the stored tab index
    case "TabSelect":
      this.index = index;
      break;

    default:
      return;
    }
  },

  /* ::::: nsIObserver ::::: */
  observe: function urlnavOverlay_observe(subject, topic, data) {

    // make sure it's the private browsing topic
    if (topic != "private-browsing")
      return;

    // reset the controllers
    this.prefill();
  },

  /* ::::: nsISupports ::::: */
  QueryInterface: function TabListener_QI(iid) {
    if (iid.equals(Components.interfaces.nsIDOMEventListener) ||
        iid.equals(Components.interfaces.nsIObserver) ||
        iid.equals(Components.interfaces.nsISupportsWeakReference) ||
        iid.equals(Components.interfaces.nsISupports))
      return this;
    throw Components.results.NS_ERROR_NO_INTERFACE;
  }
}
</pre>
<p><a href='http://richwklein.com/wp-content/uploads/2009/07/tablistener1.js'>Here</a> is a version you can download and use for yourself.</p>
]]></content:encoded>
			<wfw:commentRss>http://richwklein.com/2009/07/21/tab-listener/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JavaScript Expiring Cache</title>
		<link>http://richwklein.com/2009/07/15/javascript-expiring-cache/</link>
		<comments>http://richwklein.com/2009/07/15/javascript-expiring-cache/#comments</comments>
		<pubDate>Wed, 15 Jul 2009 19:12:13 +0000</pubDate>
		<dc:creator>Rich</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[Add-On]]></category>
		<category><![CDATA[Cache]]></category>
		<category><![CDATA[Expiring]]></category>
		<category><![CDATA[Extension]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Tip]]></category>
		<category><![CDATA[Trick]]></category>
		<category><![CDATA[XPCOM]]></category>

		<guid isPermaLink="false">http://richwklein.com/?p=745</guid>
		<description><![CDATA[I&#8217;ve been working with JavaScript for a long time on web pages and Firefox extensions. From time to time I run into instance where I want to keep an object around for a while in case I need it later. At the same time I don’t want to bloat memory usage by holding reference that [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been working with JavaScript for a long time on web pages and Firefox extensions.  From time to time I run into instance where I want to keep an object around for a while in case I need it later.  At the same time I don’t want to bloat memory usage by holding reference that I may not ever need again.  The solution I came up with is the JavaScript Expiring Cache.  I originally developed this as an <a href="https://developer.mozilla.org/en/XPCOM">XPCOM object</a> for use in one of my Firefox <a href="https://addons.mozilla.org/en-US/firefox/">extensions</a>.  The concept is to store an object by key and then expire items every so often if they have not been accessed.  I’ve retooled it down below so that it could be used in a web page.  I can see this being useful for AJAX heavy applications like a picture viewer.  In the future I can see converting this into a javascript module to use in extensions as well.</p>
<pre>
/*******************************************************************************
 * Object used to store values in the expiring cache.
 *
 * @param     key            The key used to store the object in the cache.
 * @param     value          Value to store in the cache.
 ******************************************************************************/
function CacheItem(key, value) {
  this.key = key;
  this.value = value;
  this.updateAccessed();
}
CacheItem.prototype = {

  _key: null,
  get key() { return this._key; },
  set key(val) { this._key = val; },

  _value: null,
  get value() { return this._value; },
  set value(val) { this._value = val; },

  _lastAccessed: null,
  get lastAccessed() { return this._lastAccessed; },
  set lastAccessed(val) { this._lastAccessed = val; },

  updateAccessed: function ci_updateAccessed() {
    this.lastAccessed = (new Date()).getTime();
  }
};

/*******************************************************************************
 * Interface used to store an item in memory for a finite amount of time.  A
 * maximum size and percentage of fill can be specified to limit the amount of
 * data stored.
 *
 * @param     maxSize        The maximum number of objects to store in the cache.
 * @param     expireRate     How often to expire items out of the cache (milliseconds)
 * @param     updateAccessed Update the lastAccessed time on an object when
 *                           retrieving an object or checking if it's in the cache.
 * @version   1.0
 ******************************************************************************/
function Cache(maxSize, expireRate, updateAccessed) {
  this.maxSize = maxSize;
  this.expireRate = expireRate;
  this.updateAccessed = updateAccessed;

  var self = this;
  this._items = {}
  this._timer = window.setInterval(self.purge(), this.expireRate)
}
Cache.prototype = {
  _timer: null,
  _items: null,

  _maxSize: null,
  get maxSize() { return this._maxSize; },
  set maxSize(val) { this._maxSize = val; },

  _expireRate: null,
  get expireRate() { return this._expireRate; },
  set expireRate(val) { this._expireRate = val; },

  _updateAccessed: null,
  get updateAccessed() { return this._updateAccessed; },
  set updateAccessed(val) { this._updateAccessed = val; },

  /**
   * Destroys the cache when you are done using it.  This stops the expire
   * timer and removes any items in the cache.  It also clears all the internal
   * state variables.
   */
  destroy: function Cache_destroy() {

    // stop the timer
    if (this._timer)
      window.clearInterval(this._timer);

    // remove the items
    this.clear();

    // cleanup the variables
    this.maxSize = null;
    this.expireRate = null;
    this._timer = null;
    this._items = null;
  },

  /**
   * Check to see if a key/value pair is stored in the cache.  If updateAccessed
   * has been specified then the items lastAccessed time will be updated.
   *
   * @param   key            The key to check for.
   * @returns boolean        True if found, otherwise false.
   */
  hasKey: function Cache_hasKey(key) {
    if (this._items.hasOwnProperty(key)) {
      if (this.updateAccessed)
        this._items[key].updateAccessed();
      return true;
    }
    return false;
  },

  /**
   * Get an array of all the keys stored in the cache.
   *
   * @returns array          An array of cache keys sorted alphanumerically.
   */
  getKeys: function Cache_getKeys(count) {
    var keys = [];
    for (var key in this._items)
      keys.push(key);

    keys.sort();
    return keys;
  },

  /**
   * Get the value from a key/value pair if the key is present in the cache.
   * If updateAccessed has been specified then the items lastAccessed value
   * will be updated.
   *
   * @param   key            Key for the value to return.
   * @returns object         Value stored in the cache.  If the key is not found,
   *                         then null is returned.
   */
  getValue: function Cache_getValue(key) {

    // return null if we do not have the item
    if (!this.hasKey(key))
      return null;

    // return the item
    return this._items[key].value;
  },

  /**
   * Set a key/value pair.
   *
   * @param   key            The key used to access an object.
   * @param   value          The value to be stored.
   */
  setValue: function Cache_setValue(key, value) {
    var item = new CacheItem(key, value);
    this._items[key] = item;
  },

  /**
   * Delete a key/value pair from the cache.
   *
   * @param   key            The key of the pair to remove.
   */
  deleteValue: function Cache_deleteValue(key) {

    // return early if key not stored
    if (!this.hasKey(key))
      return;

    // remove the item
    delete this._items[key];
  },

  /**
   * Clear all items out of the cache.
   */
  clear: function Cache_clear() {
    var keys = this.getKeys({});
    for (var i=0; i<keys.length; i++)
      this.deleteValue(keys[i]);
  },

  /**
   * Internal function that is used to expire items out of the cache.
   * The should never have to be called externally.
   */
  purge: function Cache_purge() {
    var purgeSize = this.maxSize * 0.80;
    var temp = [];

    // loop through the cache, expire items that should be expired
    // otherwise, add the item to an array
    for (var key in this._items) {
        var item = this._items[key];
        if (this._isExpired(item))
          this.deleteValue(key);
        else
          temp.push(item);
    }

    // no max
    if (this.maxSize < 0)
      return;

    // if we have more items than the max size of the cache remove the oldest
    if (temp.length > this.maxSize) {

      // sort this array last accessed date
      temp = temp.sort(function(a, b) { return b.lastAccessed - a.lastAccessed; });       

      // remove items from the end of the array
      while (temp.length > purgeSize) {
        var ritem = temp.pop();
        this.deleteValue(ritem.key);
      }
    }
  },

  /**
   * Internal function called from purge used to determine if an item has passed
   * it's expiration.
   *
   * @param   item           The CacheItem to check.
   */
  _isExpired: function Cache__isExpired(item) {
    var now = (new Date()).getTime();
    var expires = item.lastAccessed + this.expireRate;
    if (expires < now)
      return true;
    return false;
  }
};
</pre>
<p> To use the cache you can either include a copy in your code or download the attached file.  Then you simply call:</p>
<pre>
jsec = new Cache(maxSize, expireRate, updateAccessed);
jsec.setValue(key, value);
value = jsec.getValue(key);
jsec.destroy();
</pre>
<p><a href='http://richwklein.com/wp-content/uploads/2009/07/jsec.js'>Here</a> is a version you can download and use for yourself.</p>
]]></content:encoded>
			<wfw:commentRss>http://richwklein.com/2009/07/15/javascript-expiring-cache/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Real Life</title>
		<link>http://richwklein.com/2006/12/07/real-life/</link>
		<comments>http://richwklein.com/2006/12/07/real-life/#comments</comments>
		<pubDate>Thu, 07 Dec 2006 18:21:00 +0000</pubDate>
		<dc:creator>Rich</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[Add-On]]></category>
		<category><![CDATA[Extension]]></category>
		<category><![CDATA[Firefox]]></category>

		<guid isPermaLink="false">http://richwklein.com/?p=245</guid>
		<description><![CDATA[Real life has interfered with my extension development lately. I just haven&#8217;t had a chance to work on much of it. We do have a beta version of Forecastfox 0.9.5 that we are still working on, and I have started new versions of Location Navigator and Flickr Sidebar. Hopefully, I can find some time soon [...]]]></description>
			<content:encoded><![CDATA[<p>Real life has interfered with my extension development lately.  I just haven&#8217;t had a chance to work on much of it.  We do have a beta version of <a href="https://addons.mozilla.org/firefox/398/">Forecastfox</a> 0.9.5 that we are still working on, and I have started new versions of <a href="https://addons.mozilla.org/firefox/163/">Location Navigator</a> and <a href="https://addons.mozilla.org/firefox/1717/">Flickr Sidebar</a>.  Hopefully, I can find some time soon to get back to it.</p>
]]></content:encoded>
			<wfw:commentRss>http://richwklein.com/2006/12/07/real-life/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Location Navigator</title>
		<link>http://richwklein.com/2006/10/02/location-navigator/</link>
		<comments>http://richwklein.com/2006/10/02/location-navigator/#comments</comments>
		<pubDate>Mon, 02 Oct 2006 19:33:00 +0000</pubDate>
		<dc:creator>Rich</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[Add-On]]></category>
		<category><![CDATA[Extension]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[Location]]></category>
		<category><![CDATA[Location Navigator]]></category>
		<category><![CDATA[Update]]></category>
		<category><![CDATA[Urlnav]]></category>
		<category><![CDATA[Version]]></category>

		<guid isPermaLink="false">http://richwklein.com/?p=234</guid>
		<description><![CDATA[I&#8217;ve started work on my next version of Location Navigator. Some things to look for in this next release: I&#8217;ve added prefetching for up/down navigation. Improved the way navigation operations are stored in the back-end. Made prefetch, history, and valid checking asynchronous operations. Integrated into the new Session Store API. Integrated into the tab events [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve started work on my next version of <a href="https://addons.mozilla.org/firefox/163/">Location Navigator</a>.  Some things to look for in this next release:</p>
<ul>
<li>I&#8217;ve added prefetching for up/down navigation.</li>
<li>Improved the way navigation operations are stored in the back-end.</li>
<li>Made prefetch, history, and valid checking asynchronous operations.</li>
<li>Integrated into the new Session Store API.</li>
<li>Integrated into the tab events for faster capturing.</li>
<li>Added min/max guessing to the capture dialog.</li>
<li>The numeric builder now ignores character encoding.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://richwklein.com/2006/10/02/location-navigator/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Location Navigator 0.6.2</title>
		<link>http://richwklein.com/2006/05/08/location-navigator-062/</link>
		<comments>http://richwklein.com/2006/05/08/location-navigator-062/#comments</comments>
		<pubDate>Mon, 08 May 2006 13:46:00 +0000</pubDate>
		<dc:creator>Rich</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[Add-On]]></category>
		<category><![CDATA[Extension]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[Location]]></category>
		<category><![CDATA[Location Navigator]]></category>
		<category><![CDATA[Navigator]]></category>
		<category><![CDATA[Update]]></category>
		<category><![CDATA[Urlnav]]></category>
		<category><![CDATA[Version]]></category>

		<guid isPermaLink="false">http://richwklein.com/?p=177</guid>
		<description><![CDATA[Location Navigator 0.6.2 has been released. Changes for this version are: Added locales: es-AR and pt-Br. Added support for Bon Echo, and nightly trunk builds.]]></description>
			<content:encoded><![CDATA[<p><a href="https://addons.mozilla.org/firefox/163">Location Navigator 0.6.2</a> has been released. Changes for this version are:</p>
<ul>
<li>Added locales: es-AR and pt-Br.</li>
<li>Added support for Bon Echo, and nightly trunk builds.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://richwklein.com/2006/05/08/location-navigator-062/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Flickr Sidebar 0.1.2</title>
		<link>http://richwklein.com/2006/05/04/flickr-sidebar-012/</link>
		<comments>http://richwklein.com/2006/05/04/flickr-sidebar-012/#comments</comments>
		<pubDate>Fri, 05 May 2006 00:19:00 +0000</pubDate>
		<dc:creator>Rich</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[Add-On]]></category>
		<category><![CDATA[Extension]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[Flickr]]></category>
		<category><![CDATA[Flickr Sidebar]]></category>
		<category><![CDATA[Sidebar]]></category>
		<category><![CDATA[Update]]></category>
		<category><![CDATA[Version]]></category>

		<guid isPermaLink="false">http://richwklein.com/?p=175</guid>
		<description><![CDATA[Flickr Sidebar 0.1.2 has been released. Changes for this version are: Added locales: cs-CZ, fr-FR, it-IT, pt-Br, and zh-CN Added support for Bon Echo, and nightly trunk builds. Removed anonymous functions for better debugging. Fixed empty menu item by falling back to the username if the realname doesn&#8217;t exist for a contact.]]></description>
			<content:encoded><![CDATA[<p><a href="https://addons.mozilla.org/firefox/1717/">Flickr Sidebar 0.1.2</a> has been released.  Changes for this version are:</p>
<ul>
<li>Added locales: cs-CZ, fr-FR, it-IT, pt-Br, and zh-CN</li>
<li>Added support for Bon Echo, and nightly trunk builds.</li>
<li>Removed anonymous functions for better debugging.</li>
<li>Fixed empty menu item by falling back to the username if the realname doesn&#8217;t exist for a contact.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://richwklein.com/2006/05/04/flickr-sidebar-012/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic Page Served (once) in 0.268 seconds -->

