<?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>Ashita.org &#187; XUL</title>
	<atom:link href="http://www.ashita.org/category/xul/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.ashita.org</link>
	<description></description>
	<lastBuildDate>Thu, 22 Apr 2010 17:21:34 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>The loadOverlay Dilemma</title>
		<link>http://www.ashita.org/the-loadoverlay-dilemma/</link>
		<comments>http://www.ashita.org/the-loadoverlay-dilemma/#comments</comments>
		<pubDate>Wed, 25 Mar 2009 04:51:41 +0000</pubDate>
		<dc:creator>Jonathan Fingland</dc:creator>
				<category><![CDATA[Firefox]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Pirate Questing]]></category>
		<category><![CDATA[XUL]]></category>

		<guid isPermaLink="false">http://ashita.org/?p=256</guid>
		<description><![CDATA[When making Firefox extensions with modular components, it&#8217;s nice to be able to include overlays based on preferences or some other criteria at loadtime. One of the problems that quickly comes up is that sequential loadOverlay calls will fail. This bug is documented here. The solution described there, and elsewhere, is to use chained observers [...]]]></description>
			<content:encoded><![CDATA[<p>When making Firefox extensions with modular components, it&#8217;s nice to be able to include overlays based on preferences or some other criteria at loadtime. One of the problems that quickly comes up is that sequential loadOverlay calls will fail. This bug is documented <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=330458">here</a>. The solution described there, and elsewhere, is to use chained observers and a custom queue. Strangely, I never came across an implementation example of such an observer/queue combination. So&#8230; with that stunning introduction, I give you the system used in PirateQuesting 2.</p>
<p>The first part here is an overlay registry. Essentially, the queue portion of the solution.</p>
<pre>
piratequesting.overlayRegistry = function() {
	var overlays = [];
	var index = 0;

	function conflicts(tabid, tabpanelid, overlayFile) {
		for (var i = 0, len = overlays.length; i&lt;len;++i)
			if ((tabid == overlays[i].getTabId() &#038;&#038; tabid != null )  || (tabpanelid == overlays[i].getTabPanelId() &#038;&#038; tabpanelid != null) || overlayFile == overlays[i].getOverlayFile())
				return true;
		return false;
	}

	function overlay(tabid, tabpanelid, overlayFile) {
		var tabid = tabid;
		var tabpanelid = tabpanelid;
		var overlayFile = overlayFile;
		var added = false;

		return {
			toString : function() {
				return "tabid: " + tabid + "\ntabpanelid: " + tabpanelid
						+ "\noverlayFile: " + overlayFile;
			},
			getTabId : function() {
				return tabid;
			},
			getTabPanelId : function() {
				return tabpanelid;
			},
			getOverlayFile : function() {
				return overlayFile;
			},
			getAdded : function() {
				return added;
			},
			setAdded : function(val) {
				added = !!val; // ensure boolean
			}
		}

	}

	return {
		addOverlay : function(tabid, tabpanelid, stringFile) {
			if (!conflicts(tabid, tabpanelid, stringFile))
				overlays.push(new overlay(tabid, tabpanelid, stringFile));
			else
				dump("\nOverlay conflict occurred on: " + tabid + ", " + tabpanelid + ", " + stringFile);
		},
		getOverlayByIndex : function(index) {
			return overlays[index];
		},
		getOverlayByTabId : function(tabid) {
			var ol = overlays.length;
			for (var i = 0; i &lt; ol; i++) {
				if (overlays[i].getTabId() == tabid)
					return overlays[i];
			}
			return false;
		},
		count : function() {
			return overlays.length;
		},
		reset : function() {
			index = 0;
		},
		next : function() {
			if (index &lt; overlays.length) {
				return overlays[index++];
			} else
				return null;
		},
		progress : function() {
			return Math.ceil(index * 100 / overlays.length);
		},
		resetAll : function() {
			this.reset();
			var nex;
			while (nex = this.next()) {
				nex.setAdded(false);
			}
			this.reset();
		}

	}
}();
</pre>
<p>Now, that is a somewhat specialized case. The tabid and tabpanelid are arguably unnecessary but have been included to prevent two modules having the same tab ids, and, more importantly, to be able to refer to the overlay by a known value, in this case the tabid.</p>
<p>A somewhat stripped down version might look like:</p>
<pre>
var overlayRegistry = function() {
	var overlays = [];
	var index = 0;

	function conflicts(overlayFile) {
		for (var i = 0, len = overlays.length; i&lt;len;++i)
			if (overlayFile == overlays[i].getOverlayFile())
				return true;
		return false;
	}

	function overlay(overlayFile) {
		var overlayFile = overlayFile;
		var added = false;

		return {
			toString : function() {
				return "\noverlayFile: " + overlayFile;
			},
			getOverlayFile : function() {
				return overlayFile;
			},
			getAdded : function() {
				return added;
			},
			setAdded : function(val) {
				added = !!val; // ensure boolean
			}
		}

	}

	return {
		addOverlay : function(stringFile) {
			if (!conflicts(stringFile))
				overlays.push(new overlay(stringFile));
			else
				dump("\nOverlay conflict occurred on: " + stringFile);
		},
		getOverlayByIndex : function(index) {
			return overlays[index];
		},
		count : function() {
			return overlays.length;
		},
		reset : function() {
			index = 0;
		},
		next : function() {
			if (index &lt; overlays.length) {
				return overlays[index++];
			} else
				return null;
		},
		progress : function() {
			return Math.ceil(index * 100 / overlays.length);
		},
		resetAll : function() {
			this.reset();
			var nex;
			while (nex = this.next()) {
				nex.setAdded(false);
			}
			this.reset();
		}

	}
}();
</pre>
<p>The overlay registry is simply an iterator-style queue. This makes walking through the items very easy for the observer (shown next) which doesn&#8217;t (and shouldn&#8217;t) really have any idea of the state of the queue. The queue, overlayRegistry, makes a number of methods available for getting basic info about the queue (size, progress, etc) for use in progress bars or the like. It also provides ways of resetting the queue. Obviously, since it&#8217;s designed to be an iterator, there are ways of getting the next item and advancing the queue. </p>
<p>The second part of the solution is using a chained observer. Again, the piratequesting implementation is:</p>
<pre>
function overlayObserver()
{
  this.register();
}
overlayObserver.prototype = {
  observe: function(subject, topic, data) {

  	function cleanUp() {
		sidebar.contentDocument.getElementById("pqmain_deck").selectedIndex="1";

		var mod_boxes = sidebar.contentDocument.getElementsByTagName("tabbox");
		for (var i=0,len=mod_boxes.length;i&lt;len;++i) {
			if (hasClassName(mod_boxes[i],"moduleBox")) {
				mod_boxes[i].selectedIndex = 0;
			}
		}
  	}

  	if (topic == "xul-overlay-merged") {
		try {
			var nex = piratequesting.overlayRegistry.next();
			if (nex) {
  				sidebar.contentDocument.getElementById("pqloadprogress").value = piratequesting.overlayRegistry.progress();
  				try {
  					sidebar.contentDocument.loadOverlay(nex.getOverlayFile(),this);
	  			} catch (error) {
  					cleanUp();
  					dump("Failed to load: " + nex.getOverlayFile() + "\nReported Error " + getErrorString(error));
	  			}
  			} else {
  				cleanUp();
	  		}
  		} catch (error) { alert(getErrorString(error)); }
  	}
  },
  register: function() {
    var observerService = Components.classes["@mozilla.org/observer-service;1"]
                          .getService(Components.interfaces.nsIObserverService);
    observerService.addObserver(this, "xul-overlay-merged", false);
  },
  unregister: function() {
    var observerService = Components.classes["@mozilla.org/observer-service;1"]
                            .getService(Components.interfaces.nsIObserverService);
    observerService.removeObserver(this, "xul-overlay-merged");
  }
}
</pre>
<p>And a stripped down version would look something like this: </p>
<pre>
function overlayObserver()
{
  this.register();
}
overlayObserver.prototype = {
  observe: function(subject, topic, data) {

  	if (topic == "xul-overlay-merged") {
		try {
			var nex = overlayRegistry.next();
			if (nex) {
  				try {
  					document.loadOverlay(nex.getOverlayFile(),this);
	  			} catch (error) {
  					dump("Failed to load: " + nex.getOverlayFile() + "\nReported Error " + getErrorString(error));
	  			}
  			} else {
  				cleanUp();
	  		}
  		} catch (error) { alert(getErrorString(error)); }
  	}
  },
  register: function() {
    var observerService = Components.classes["@mozilla.org/observer-service;1"]
                          .getService(Components.interfaces.nsIObserverService);
    observerService.addObserver(this, "xul-overlay-merged", false);
  },
  unregister: function() {
    var observerService = Components.classes["@mozilla.org/observer-service;1"]
                            .getService(Components.interfaces.nsIObserverService);
    observerService.removeObserver(this, "xul-overlay-merged");
  }
}
</pre>
<p>This observer chain is started with:</p>
<pre>
overlayObserver.observe(null,"xul-overlay-merged", null);
</pre>
<p>The observer code is also fairly simple but relies on some things that were not terribly well documented. The key thing to know about loadOverlay is that it raises an <code>xul-overlay-merged</code> observer notification. <a href="http://developer.mozilla.org>MDC</a> has a pretty good list of the observer notifications <a href="http://developer.mozilla.org/en/Observer_Notifications">here</a>, but you&#8217;ll notice what&#8217;s missing. <a href="https://developer.mozilla.org/en/Document.loadOverlay">This entry</a>, however, has that piece of info. If you&#8217;ve never used observers before, <a href="https://developer.mozilla.org/en/nsIObserver">this</a> gives a good rundown.</p>
<p>Sooo&#8230;. What happens? how does it work? Basically, at some point during the initialization of the sidebar (not important in this discussion), the observer chain is started and will loop as follows:</p>
<ol>
<li>receive notification of the last loadOverlay finishing</li>
<li>get the next item in the iterator</li>
<li>if the &#8220;next item&#8221; is null, stop</li>
<li>load the overlay file specified by said item</li>
</ol>
<p>And you&#8217;re done. One Note I would make is that if there are multiple extensions making use of this at the same time, you&#8217;re very likely to have problems when both extensions receive the notifications and both start loading their next items. As firefox still lacks a built-in queue for overlay loading, we&#8217;re stuck hoping that nobody else will use this at the same time.</p>
<p>Note: You may notice that a lot of the piratequesting code makes use of a function <code>getErrorString()</code>. This is a very siple function that puts all of the error info I want into a string. <code>dump()</code> is a function available in firefox for dumping text to the console. I am also currently working on an error logging system and will cover all of these issues in more detail when that is finished. For the time being, ignore how I handle the errors but for obvious reasons, you will want to have some kind of error handling in place.</p>
<p>Edit 2009/04/04:<br />
It has since occurred to me that by using additional information, that is, <code>subject</code>, from the observer notification, I can reduce the chances of conflict by ensuring it only fires on the completion of it&#8217;s own loadOverlay calls.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ashita.org/the-loadoverlay-dilemma/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Passing parameters into a created dialog, and retrieving them on exit</title>
		<link>http://www.ashita.org/passing-parameters-into-a-created-dialog-and-retrieving-them-on-exit/</link>
		<comments>http://www.ashita.org/passing-parameters-into-a-created-dialog-and-retrieving-them-on-exit/#comments</comments>
		<pubDate>Sun, 26 Oct 2008 07:50:05 +0000</pubDate>
		<dc:creator>Jonathan Fingland</dc:creator>
				<category><![CDATA[DOM]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Pirate Questing]]></category>
		<category><![CDATA[XUL]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Firefox Extension]]></category>

		<guid isPermaLink="false">http://ashita.org/?p=251</guid>
		<description><![CDATA[This is going to be a pretty short tutorial today to help explain how to pass information to an window when opening it. The following is taken from PirateQuesting.
First, here is the function for entering the code when PirateQuest asks the user for verification.
function enterCode(url,imgsrc, func) {
	var params = {in:imgsrc, out:null};
	window.openDialog("chrome://piratequesting/content/codeDialog.xul", "",
		"chrome, dialog, modal, resizable=no, [...]]]></description>
			<content:encoded><![CDATA[<p>This is going to be a pretty short tutorial today to help explain how to pass information to an window when opening it. The following is taken from PirateQuesting.</p>
<p>First, here is the function for entering the code when PirateQuest asks the user for verification.</p>
<pre>function enterCode(url,imgsrc, func) {
	var params = {in:imgsrc, out:null};
	window.openDialog("chrome://piratequesting/content/codeDialog.xul", "",
		"chrome, dialog, modal, resizable=no, status=no,
		height=250, width=400", params).focus();
	if (params.out) {
		piratequesting.Code.submit(url,params.out,imgsrc,func);
	}
	else {
	    // User clicked cancel. stop here
	}
}</pre>
<p>So, as we can see in the first line the params variable stores a hash. A has is useful here as it allows us to easily pass more than one variable in without goign to the work of creating an object. There are actually much more significant differences between a hash and an object but, for this tutorial, know that it stores values in name:value pairs separated by commas and all of it enclosed by curly braces. The last element must not be followed by a comma.</p>
<p>Next, when we use openDialog we pass params into the dialog.</p>
<p>After the user has clicked OK, the value of params.out is checked. The condition will be true unless the value is still null or by some strange miracle taken on a value like &#8216;false&#8217;.</p>
<p>Now, let&#8217;s look at the code behind the dialog itself</p>
<pre>function codeDialogOnLoad() {

    // Use the arguments passed to us by the caller
    document.getElementById("codeImage").setAttribute('src',
            window.arguments[0].in);
}

// Called once if and only if the user clicks OK
function onOK() {
    window.arguments[0].out = document.getElementById("codeValue").value;
    return true;
}</pre>
<p>Ok, so what do we have here? well, when the dialog first loads we call codeDialogOnLoad which then sets the image source on the dialog based on the value passed in params.in. Note that it is now referred to as window.arguments[0].in.</p>
<p>When the user presses OK, the value of an input box, codeValue, is assigned to params.out (a.k.a. window.arguments[0].out).</p>
<p>Last thing to look at is the codeDialog.xul</p>
<pre>&lt;?xml version="1.0"?&gt;
&lt;?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?&gt;
&lt;dialog
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
  id="codeDialog"
  title="Enter the Code"
  ondialogaccept="return onOK();"
  buttonlabelaccept="Submit"
  onload="codeDialogOnLoad();"
  persist="screenX screenY width height"&gt;

	&lt;script type="application/x-javascript" src="chrome://piratequesting/content/codeDialog.js"/&gt;
	&lt;vbox&gt;
		&lt;label value="Enter the code shown below" /&gt;
		&lt;image id="codeImage" /&gt;
		&lt;textbox width="50" id="codeValue" /&gt;
	&lt;/vbox&gt;
&lt;/dialog&gt;</pre>
<p>As you can see, codeDialog.xul is very simple and contains only three elements inside a vbox. This is really one of the simplest examples you could use and was chosen to illustrate how to simply and easily pass information into and out of a dialog</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ashita.org/passing-parameters-into-a-created-dialog-and-retrieving-them-on-exit/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Pirate Questing</title>
		<link>http://www.ashita.org/pirate-questing/</link>
		<comments>http://www.ashita.org/pirate-questing/#comments</comments>
		<pubDate>Wed, 13 Aug 2008 05:24:58 +0000</pubDate>
		<dc:creator>Jonathan Fingland</dc:creator>
				<category><![CDATA[DOM]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Pirate Questing]]></category>
		<category><![CDATA[XUL]]></category>

		<guid isPermaLink="false">http://ashita.org/?p=3</guid>
		<description><![CDATA[Pirate Questing is a firefox addon thatI&#8217;ve been devloping for a while. It&#8217;s available at AMO here
I&#8217;ll mostly be using this space to document interesting solutions I come across.
]]></description>
			<content:encoded><![CDATA[<p>Pirate Questing is a firefox addon thatI&#8217;ve been devloping for a while. It&#8217;s available at AMO <a href="http://addons.mozilla.org/en-US/firefox/addon/6801">here</a></p>
<p>I&#8217;ll mostly be using this space to document interesting solutions I come across.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ashita.org/pirate-questing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
