<?xml version="1.0" encoding="UTF-8"?>
<xbl:xbl
xmlns="http://www.w3.org/1999/xhtml"
xmlns:xbl="http://www.w3.org/ns/xbl">
<xbl:binding id="autocomplete">
<xbl:template>
<div>
<input id="entry" type="text" name="entryText" value="" />
<span id="subject"></span><br/>
<pre id="body"></pre>
</div>
</xbl:template>
<xbl:resources>
<xbl:style><![CDATA[
]]></xbl:style>
</xbl:resources>
<xbl:implementation><![CDATA[
({
SELECTED_CLASS: "auco_selected",
_autocompleters: [],
addCompleter: function (aCompleter) {
this._autocompleters.push(aCompleter);
},
_serial: 0,
resultsByType: null,
goFish: function (aText) {
var serial = this._serial++;
this.resultsByType = {};
this._autocompleters.forEach(function (completer) {
completer.complete(this, aText);
}, this);
},
haveSomeResults: function(aText, aNodes, aCompleter, aPriority) {
// bail if this is not the reslt for what is currently typed
if (aText != this._entry.value)
return;
this.resultsByType[aCompleter.type] = [aPriority, aNodes];
var nodes = [];
var prioritizedResults = [];
for each (var tupe in this.resultsByType)
prioritizedResults.push(tupe);
prioritizedResults.sort(function (a,b) { return b[0] - a[0]; });
for (var i = 0; i < prioritizedResults.length; i++)
nodes = nodes.concat(prioritizedResults[i][1]);
this.clearResultsList();
$(this._resultDiv).append(nodes);
this.showResultsList();
},
/* ===== Results List Stuff ===== */
_resultDiv: null,
resultsVisible: false,
showResultsList: function() {
var entryOffset = $(this._entry).offset();
$(this._resultDiv).css({
left: entryOffset.let,
top: entryOffset.top + this._entry.offsetHeight,
width: $(this._entry).width()
}).show();
this.resultsVisible = true;
},
hideResultsList: function() {
$(this._resultDiv).hide();
this.resultsVisible = false;
},
clearResultsList: function() {
$(this._resultDiv).empty();
this.selectedIndex = null;
},
selectedIndex: null,
setSelectedIndex: function(aDesiredIndex) {
var children = this._resultDiv.childNodes;
if (children.length == 0)
aDesiredIndex = null;
else if (aDesiredIndex === undefined)
aDesiredIndex = 0;
else if (aDesiredIndex < 0)
aDesiredIndex = 0;
else if (aDesiredIndex >= children.length)
aDesiredIndex = children.length - 1;
if (aDesiredIndex == this.selectedIndex)
return;
if (this.selectedIndex != null)
$(children[this.selectedIndex]).removeClass(this.SELECTED_CLASS);
this.selectedIndex = aDesiredIndex;
if (this.selectedIndex != null)
$(children[this.selectedIndex]).addClass(this.SELECTED_CLASS);
},
/* ===== Action ===== */
actionListener: null,
addActionListener: function(aActionListener) {
this.actionListener = aActionListener;
},
actOnSelected: function() {
console.log("Taking action on index", this.selectedIndex);
if (this.selectedIndex != null) {
var selected = this._resultDiv.childNodes[this.selectedIndex];
if (this.actionListener != null)
this.actionListener(selected);
else
console.log("Warning! There was no action listener!", this, this.actionListener);
}
console.log("Actiong taking done!");
},
/* ===== House-keeping ==== */
xblBindingAttached: function () {
console.log("autocompleter binding and what not", this);
this._entry = this.shadowTree.getElementById("entry");
this._resultDiv = $("<div/>").hide().css("position", "absolute")
.addClass("auco_results")
.appendTo(document.body)[0];
console.log("done binding");
},
})
]]></xbl:implementation>
<xbl:handlers>
<xbl:handler event="textInput"><![CDATA[
var text = this._entry.value + event.data;
this.goFish(text);
]]></xbl:handler>
<xbl:handler event="keydown"><![CDATA[
if (event.keyIdentifier == "Enter") {
this.actOnSelected();
this.hideResultsList();
}
else if (event.keyIdentifier == "Up") {
if (this.selectedIndex == 0)
this.setSelectedIndex(null);
else
this.setSelectedIndex(this.selectedIndex - 1);
}
else if (event.keyIdentifier == "Down") {
// if the results aren't currently visible, show them and go to the
// first index
if (!this.resultsVisible) {
this.showResultsList();
this.setSelectedIndex(0);
return;
}
if (this.selectedIndex == null)
this.setSelectedIndex(0);
else
this.setSelectedIndex(this.selectedIndex + 1);
}
else if (event.keyIdentifier == "U+001B") { // escape
this.hideResultsList();
}
]]></xbl:handler>
<xbl:handler event="blur"><![CDATA[
]]></xbl:handler>
</xbl:handlers>
</xbl:binding>
<!-- Contact -->
<xbl:binding id="contact-completion">
<xbl:template>
<img id="picture" class="contactpic"/>
<span id="name"></span><br />
<span id="emails"></span>
</xbl:template>
<xbl:resources>
<xbl:style><![CDATA[
.contactpic {
width: 32;
height: 32;
float: left;
}
]]></xbl:style>
</xbl:resources>
<xbl:implementation><![CDATA[
({
type: "contact",
getType: function() {
return this.type;
},
contact: null,
getContact: function() {
return this.contact;
},
setContact: function(aContact) {
this.contact = aContact;
console.log("setContact", this);
this._nName.textContent = this.contact.name;
var bestEmail = null;
var emailText = this.contact.identities.map(function (identity) {
if (identity.kind == "email")
bestEmail = identity.value;
return identity.value;
}).join(", ");
this._nEmails.textContent = emailText;
if (bestEmail) {
this._nPicture.setAttribute("src",
"http://www.gravatar.com/avatar/" + hex_md5(bestEmail) +
".jpg?r=pg&d=identicon&s=32");
// XXX soon, let's point at the gravatar
// XXX later, let's use a pic if it exists, or gravatar push if it
// doesn't. (or maybe have a daemon that takes care of it?)
}
},
xblBindingAttached: function () {
console.log("attaching contact result");
this._nPicture = this.shadowTree.getElementById("picture");
this._nName = this.shadowTree.getElementById("name");
this._nEmails = this.shadowTree.getElementById("emails");
console.log("done attaching contact result");
},
})
]]></xbl:implementation>
<xbl:handlers>
</xbl:handlers>
</xbl:binding>
<!-- Tag -->
<xbl:binding id="tag-completion">
<xbl:template>
messages tagged <span id="tagname"/>
</xbl:template>
<xbl:implementation><![CDATA[
({
type: "tag",
getType: function() {
return this.type;
},
tagname: null,
getTagName: function() {
return this.tagname;
},
setTagName: function(aTagName) {
this.tagname = aTagName;
this.shadowTree.getElementById("tagname").textContent = aTagName;
}
})
]]></xbl:implementation>
<xbl:handlers>
</xbl:handlers>
</xbl:binding>
</xbl:xbl>