Skip to content

Instantly share code, notes, and snippets.

@LouCypher
Last active Dec 31, 2015
Embed
What would you like to do?
Supports more add-on types in bootstrap.js for https://addons.mozilla.org/addon/addons-manager-hilite/
Components.utils.import("resource://gre/modules/AddonManager.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
var doAlphaSort = null; // for pref (alphabetical vs by-state sorting)
var updatedSort = false; // for pref (sort by last updated)
var dontSort = false; // ignore sort pref in category-userstyles (provides own sorting)
var restore = null; // for pref (restore hilite after closing AoM)
var showUI = null; // for pref (whether to create/display ac menu)
var UIwidth = null; // for pref (ac menu size)
var showVersionNumber = null; // for pref (whether to show version number in ac menu)
var spacer = " ";
var UIextra = null; // for restore-on-demand hidden pref
var extraRows = null;
var extArray = null;
var themeArray = null;
var pluginArray = null;
var userstyleArray = null;
var serviceArray = null;
var userscriptArray = null;
var scriptishArray = null;
var custombuttonsArray = null;
var dictionaryArray = null;
var localeArray = null;
var savedAddonName = null;
var displayDropdown = null; // used for Seamonkey & Tbird workaround
const LISTVIEW = 2;
const DETAILVIEW = 4;
var supportedCats = [ "category-extension", "category-theme", "category-plugin", "category-userstyle",
"category-service", "category-greasemonkey-user-script", "category-userscript",
"category-custombuttons", "category-dictionary", "category-locale" ];
// from http://erikvold.com/blog/index.cfm/2011/6/14/restartless-firefox-addons-part-6-better-includes
// Thanks, Erik!
(function(global) {
global.include = function include(src) {
var o = {};
Components.utils.import("resource://gre/modules/Services.jsm", o);
var uri = o.Services.io.newURI(src, null, o.Services.io.newURI(__SCRIPT_URI_SPEC__, null, null));
o.Services.scriptloader.loadSubScript(uri.spec, global);
};
})(this);
include("components/addonList_autocomplete.js");
function startup(data, reason) {
Services.obs.addObserver(ChromeDocObserver, "chrome-document-global-created", false);
var defPrefs = Services.prefs.getDefaultBranch("extensions.addonsmgrhilte@cfl.");
defPrefs.setBoolPref("restore", true);
defPrefs.setBoolPref("alphaSort", false);
defPrefs.setBoolPref("updatedSort", false);
defPrefs.setBoolPref("extraRows", false);
defPrefs.setBoolPref("showUI", true); // should we default this to true now??????
defPrefs.setIntPref("listboxSize", 18);
defPrefs.setBoolPref("UIextra", false);
defPrefs.setBoolPref("showVersion", false);
// register our custom autocomplete component
Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar).registerFactory(CLASS_ID,
CLASS_NAME, CONTRACT_ID, NSGetFactory(CLASS_ID));
if (reason == ADDON_ENABLE || reason == ADDON_INSTALL) {
var mgrs = findAddonsMgrs();
var i;
for (i = 0; i < mgrs.length; i++) {
mgrs[i].location.reload();
}
}
}
function shutdown(data, reason) {
Services.obs.removeObserver(ChromeDocObserver, "chrome-document-global-created");
// call flush due to the bundle service cache (for aomhilite.properties string bundle)
Components.classes["@mozilla.org/intl/stringbundle;1"].getService(Components.interfaces.nsIStringBundleService)
.flushBundles();
// unregister our custom autocomplete component
Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar).unregisterFactory(CLASS_ID,
NSGetFactory(CLASS_ID));
// remove stylesheets
removeStylesheet("chrome://aomhilite/content/aomhilite.css");
removeStylesheet("chrome://aomhilite/content/aomhiliteLG.css");
removeStylesheet("chrome://aomhilite/content/aomhiliteOSX.css");
if (reason == ADDON_DISABLE) {
// remove any UI from any open Add-ons Manager tabs and revert to default sorting
var mgrs = findAddonsMgrs();
var i;
for (i = 0; i < mgrs.length; i++) {
AddonsMgrOnUnload.call(mgrs[i]);
// reverts to default sort;
// also allows aomhilite.xml to be removed from memory w/o waiting for user to close AoM
mgrs[i].location.reload();
}
}
}
function install(data, reason) {
}
function uninstall(data, reason) {
// don't remove user's selection when updating ext (as opposed to actual uninstall)
if (reason != "ADDON_UNINSTALL") {
return;
}
var RDF = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService(Components.interfaces.nsIRDFService);
var localstore = RDF.GetDataSource('rdf:local-store');
var subj = RDF.GetResource('about:addons#addon-list');
var pred = RDF.GetResource('aomhilite');
var target = localstore.GetTarget(subj, pred, true);
if (target == null) {
return;
} // if it's an empty string we want to remove it's entry
target = target.QueryInterface(Components.interfaces.nsIRDFLiteral);
var obj = RDF.GetLiteral(target.Value);
localstore.Unassert(subj, pred, obj, true);
}
function ChromeDocObserver(aSubject, aTopic, aData) {
try {
if (aTopic != "chrome-document-global-created") {
return;
}
var win = aSubject;
var uri = win.document.documentURI;
// if Add-ons Manager Dialog Returns add-on is installed, uri will be about:blank until loaded
if (uri == "about:addons" || uri == "about:blank") {
win.addEventListener("load", AddonsMgrOnload, false);
}
} catch (e) {
Services.console.logStringMessage("Exception in ChromeDocObserver");
}
}
function AddonsMgrOnload() {
// removing this load listener is req'd to prevent mem leak
// if we're at about:blank we need to do that *now*
// might as well do them all here
// listener no longer needed since if the mgr is reloaded via F5,
// unload is called, then ChromeDocObserver re-adds the listener
this.removeEventListener("load", AddonsMgrOnload, false);
if (this.document.documentURI != "about:addons") {
return;
}
this.addEventListener("unload", AddonsMgrOnUnload, false);
var AMdoc = this.document; // document obj of Addons Mgr window
var addonList = AMdoc.getElementById("addon-list");
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService);
prefs = prefs.getBranch("extensions.addonsmgrhilte@cfl.");
doAlphaSort = prefs.getBoolPref("alphaSort");
updatedSort = prefs.getBoolPref("updatedSort");
restore = prefs.getBoolPref("restore");
UIextra = prefs.getBoolPref("UIextra");
showUI = prefs.getBoolPref("showUI");
extraRows = prefs.getBoolPref("extraRows");
// restore-on-demand useless if already auto-restored, or the UI not even visible
if (restore || !showUI) {
UIextra = false;
}
UIwidth = prefs.getIntPref("listboxSize");
UIwidth = (UIwidth > 50) ? 50 : UIwidth;
UIwidth = (UIwidth < 10) ? 10 : UIwidth;
if (showUI) {
initUI(AMdoc);
}
if (!restore) {
addonList.setAttribute('aomhilite', "");
}
this.addEventListener("ViewChanged", onViewChange, false);
var cat = AMdoc.getElementById("categories");
if (!cat || !addonList) {
return;
}
var attr = addonList.getAttribute("persist"); // 'persist' enables this info to survive a restart of fx
if (!attr) {
addonList.setAttribute("persist", "aomhilite");
} else if (attr.indexOf("aomhilite") < 0) {
addonList.setAttribute("persist", attr + " aomhilite");
}
addonList.addEventListener("select", onSelect, false);
}
var addonListener = {
onInstalled : function(addon) {
updateListboxContents(addon, "add");
updateAllListboxes();
},
/*
* due to the 'undo' option remaining available until aom closed/reopened, this won't get called until current
* instance (tab) is closed, because that's when the actual uninstall occurs (and when we should remove item from
* dropdown list)
*/
onUninstalled : function(addon) {
updateListboxContents(addon, "remove");
updateAllListboxes();
}
};
function initUI(AMdoc) {
var acListboxStyle = "margin-left: 1em; margin-right: 1em; width: " + UIwidth + "em;";
var acAddonList = AMdoc.createElement("textbox");
acAddonList.setAttribute("id", "aomhilite_ac_menu");
acAddonList.setAttribute("style", acListboxStyle);
acAddonList.setAttribute("type", "autocomplete");
// acAddonList.setAttribute("size", 24); // seems to have no effect; set via css width instead
acAddonList.setAttribute("autocompletesearch", "addonList_autocomplete");
acAddonList.setAttribute("enablehistory", true);
// acAddonList.setAttribute("completedefaultindex", true);
acAddonList.setAttribute("forcecomplete", true); // ensures selection is valid
// only applies to typing in box; see aomhilite.xml
acAddonList.setAttribute("maxrows", 10); // default is about 6
var notifyListbox = "var evt = document.createEvent('Events'); " + "evt.initEvent('textentered', true, true); " +
"this.dispatchEvent(evt);";
acAddonList.setAttribute("ontextentered", notifyListbox);
var strbundle = Components.classes["@mozilla.org/intl/stringbundle;1"].getService(
Components.interfaces.nsIStringBundleService).createBundle("chrome://aomhilite/locale/aomhilite.properties");
var selectaddon = strbundle.GetStringFromName("placeholderString");
acAddonList.setAttribute("placeholder", selectaddon);
/* set up stylesheets */
var osString = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULRuntime).OS;
addStylesheet("chrome://aomhilite/content/aomhilite.css");
if (osString == "Darwin") {
addStylesheet("chrome://aomhilite/content/aomhiliteOSX.css");
}
var updateBtn = AMdoc.getElementById("header-utils-btn");
var head = AMdoc.getElementById("header");
head.insertBefore(acAddonList, updateBtn);
// prevents "Extension cannot be installed because Nightly cannot modify the needed file"
// error when updating this ext (bug 719180)
// although 71980 is fixed in fx 19+, using (tested on fx 21.0a1):
// acAddonList.style.MozBinding = "url(chrome://aomhilite/content/aomhilite.xml#ac_menu_aomhilite)";
// leaves the aomhilite.xml file in memory even when AoM is closed and aomhilite is disabled!
var chromeReg = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
.getService(Components.interfaces.nsIChromeRegistry);
var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var bindingUri;
if (extraRows) {
bindingUri = ioService.newURI("chrome://aomhilite/content/aomhiliteLG.xml#ac_menu_aomhilite", null, null);
} else {
bindingUri = ioService.newURI("chrome://aomhilite/content/aomhilite.xml#ac_menu_aomhilite", null, null);
}
var convertedBindingUri = chromeReg.convertChromeURL(bindingUri);
acAddonList.style.MozBinding = 'url("' + convertedBindingUri.spec + '")';
const
FIREFOX_ID = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULAppInfo);
if (appInfo.ID != FIREFOX_ID) { // workaround - Seamonkey/Tbird (bug 708520) aren't opening popup by default
var arrow = AMdoc.getAnonymousElementByAttribute(acAddonList, "anonid", "historydropmarker");
displayDropdown = function() {
acAddonList.showHistoryPopup();
};
arrow.addEventListener("click", displayDropdown, false);
}
acAddonList.addEventListener("textentered", onACaddonListSelection, false);
AddonManager.addAddonListener(addonListener);
if (UIextra) {
var addonList = AMdoc.getElementById("addon-list");
addonList.setAttribute('restoreondemand', addonList.getAttribute('aomhilite'));
}
// (do here since addonListCallback will require data initialized above)
AddonManager.getAddonsByTypes([ "extension" ], function(addons) {
addonListCallback(addons, AMdoc, "extension");
});
AddonManager.getAddonsByTypes([ "theme" ], function(addons) {
addonListCallback(addons, AMdoc, "theme");
});
AddonManager.getAddonsByTypes([ "plugin" ], function(addons) {
addonListCallback(addons, AMdoc, "plugin");
});
AddonManager.getAddonsByTypes([ "userstyle" ], function(addons) {
addonListCallback(addons, AMdoc, "userstyle");
});
AddonManager.getAddonsByTypes([ "service" ], function(addons) {
addonListCallback(addons, AMdoc, "service");
});
// Greasemonkey
AddonManager.getAddonsByTypes([ "greasemonkey-user-script" ], function(addons) {
addonListCallback(addons, AMdoc, "greasemonkey-user-script");
});
// Scriptish
AddonManager.getAddonsByTypes([ "userscript" ], function(addons) {
addonListCallback(addons, AMdoc, "userscript");
});
// Custom Buttons
AddonManager.getAddonsByTypes([ "custombuttons" ], function(addons) {
addonListCallback(addons, AMdoc, "custombuttons");
});
AddonManager.getAddonsByTypes([ "dictionary" ], function(addons) {
addonListCallback(addons, AMdoc, "dictionary");
});
AddonManager.getAddonsByTypes([ "locale" ], function(addons) {
addonListCallback(addons, AMdoc, "locale");
});
}
function addStylesheet(sheet) {
var sss = Components.classes["@mozilla.org/content/style-sheet-service;1"]
.getService(Components.interfaces.nsIStyleSheetService);
var ios = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
var uri = ios.newURI(sheet, null, null);
if (!sss.sheetRegistered(uri, sss.USER_SHEET)) {
sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
}
}
function removeStylesheet(sheet) {
var sss = Components.classes["@mozilla.org/content/style-sheet-service;1"]
.getService(Components.interfaces.nsIStyleSheetService);
var ios = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
var uri = ios.newURI(sheet, null, null);
if (sss.sheetRegistered(uri, sss.USER_SHEET)) {
sss.unregisterSheet(uri, sss.USER_SHEET);
}
}
function findAddonsMgrs() {
// find all open mgrs
var mgrs = [];
var windows = Services.wm.getEnumerator("navigator:browser");
var window;
while (windows.hasMoreElements()) {
window = windows.getNext().QueryInterface(Components.interfaces.nsIDOMWindow);
window.gBrowser.browsers.forEach(findMgrTab);
}
function findMgrTab(browser) {
if (browser.currentURI.spec == "about:addons") {
mgrs.push(browser.contentWindow);
}
}
// Mgr in dialog window (for Add-ons Manager Dialog Returns compatibility)
var topWin = Services.wm.getMostRecentWindow(null);
if (topWin.document.documentURI == "about:addons") {
mgrs.push(topWin);
}
return mgrs;
}
function updateListboxContents(addon, action) {
if (action != "add" && action != "remove") {
return;
}
var currentArray = null;
switch (addon.type) {
case "extension":
currentArray = extArray;
break;
case "theme":
currentArray = themeArray;
break;
case "plugin":
currentArray = pluginArray;
break;
case "userstyle":
currentArray = userstyleArray;
break;
case "service":
currentArray = serviceArray;
break;
case "greasemonkey-user-script":
currentArray = userscriptArray;
break;
case "userscript":
currentArray = scriptishArray;
break;
case "custombuttons":
currentArray = custombuttonsArray;
break;
case "dictionary":
currentArray = dictionaryArray;
break;
case "locale":
currentArray = localeArray;
break;
default:
Services.console.logStringMessage("Addons Manager Hilite encountered an unsupported Add-on type: " +
addon.type + " " + addon.name);
}
var entryFound = false;
var element;
for (element = 0; element < currentArray.length; element++) {
let
entry = currentArray[element];
if (entry.indexOf(addon.name) != -1) { // found item
entryFound = true;
if (showVersionNumber && action == "add") { // version number needs updated
var nameVersion = entry.split(spacer, 2);
nameVersion[1] = (nameVersion[0].indexOf("<") == 0) ? addon.version + ">" : addon.version;
entry = nameVersion.join(spacer);
currentArray[element] = entry;
} else {
if (action == "remove") {
currentArray.splice(element, 1);
}
}
// process restoreondemand entry, if any + actual entry
if (entry.indexOf("<") != 0) {
break;
}
} // end if found
} // end for
if (!entryFound && action == "add") { // item not present; needs added
var entryToAdd = addon.name;
if (showVersionNumber) {
entryToAdd = entryToAdd + spacer + addon.version;
}
currentArray.push(entryToAdd);
currentArray.sort(function(addonItem1, addonItem2) {
var addon1Name = addonItem1.toLowerCase();
var addon2Name = addonItem2.toLowerCase();
return addon1Name.localeCompare(addon2Name);
});
}
}
function updateAllListboxes() {
var mgrs = findAddonsMgrs();
var i;
for (i = 0; i < mgrs.length; i++) {
var AMdoc = mgrs[i].document;
var acAddonList = AMdoc.getElementById("aomhilite_ac_menu");
var cat = AMdoc.getElementById('categories');
var selCat = cat.selectedItem.id;
switch (selCat) { // select the proper menulist for the current category
case "category-extension":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(extArray));
break;
case "category-theme":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(themeArray));
break;
case "category-plugin":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(pluginArray));
break;
case "category-userstyle":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(userstyleArray));
break;
case "category-service":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(serviceArray));
break;
case "category-greasemonkey-user-script":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(userscriptArray));
break;
case "category-userscript":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(scriptishArray));
break;
case "category-custombuttons":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(custombuttonsArray));
break;
case "category-dictionary":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(dictionaryArray));
break;
case "category-locale":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(localeArray));
break;
}
}
}
function restoreHilite(AMdoc) {
// purpose: retrieve saved selection and restore it
// content.document does not get document we want if AoM was opened in background via session restore
var selAddon;
var addonList = AMdoc.getElementById('addon-list');
var savedSelections = addonList.getAttribute('aomhilite');
var cat = AMdoc.getElementById('categories');
var selCat = cat.selectedItem.id; // category-extension, etc.
var selectionArray = savedSelections.split(",");
var lastSel = selectionArray[supportedCats.indexOf(selCat)];
if (lastSel) {
selAddon = addonList.getElementsByAttribute('value', lastSel);
}
if (!lastSel || !selAddon || !selAddon[0]) {
// no lastSel will occur on newly opened AoM if (always) restore pref is false
// otherwise typically occurs when selected category has no installed items or has never had a saved selection
var acAddonList = AMdoc.getElementById("aomhilite_ac_menu");
if (acAddonList) {
acAddonList.value = "";
} // onSelect not triggered (see below); need to clear this now
return;
}
addonList.selectItem(selAddon[0]); // will trigger onSelect
addonList.scrollBoxObject.scrollToElement(selAddon[0]);
addonList.focus();
}
function saveUpdatedSel(addonList, selCat, newSelId) {
var savedSelections = addonList.getAttribute('aomhilite');
var selectionArray = savedSelections.split(",");
selectionArray[supportedCats.indexOf(selCat)] = newSelId;
var updatedSel = selectionArray.toString();
addonList.setAttribute('aomhilite', updatedSel);
}
function onSelect() {
var currentSel = this.selectedItem.mAddon.id;
var AMdoc = this.ownerDocument;
var cat = AMdoc.getElementById('categories');
var selCat = cat.selectedItem.id;
if (!currentSel || (supportedCats.indexOf(selCat) == -1)) {
return;
}
saveUpdatedSel(this, selCat, currentSel);
var acAddonList = AMdoc.getElementById("aomhilite_ac_menu");
if (!acAddonList || !acAddonList.value) {
return;
}
if (acAddonList.value.indexOf(this.selectedItem.mAddon.name) < 0) {
acAddonList.value = ""; // clear ac list input field, rather than show name of a now unselected item
}
}
function onACaddonListSelection() {
// called when selection made from ac dropdown list or pressing ENTER in input field
var AMdoc = this.ownerDocument;
var cat = AMdoc.getElementById('categories');
var selCat = cat.selectedItem.id;
var acAddonList = AMdoc.getElementById("aomhilite_ac_menu");
// setting selectionStart & selectionEnd to same value sets the cursor position to that position
// set to beginning so text looks cropped on right instead of on left
acAddonList.inputField.selectionStart = 0;
acAddonList.inputField.selectionEnd = 0;
var sel = AMdoc.getElementById("aomhilite_ac_menu").value;
if (UIextra && sel.indexOf("<") == 0 && sel.lastIndexOf(">") == sel.length - 1) {
sel = sel.slice(1, sel.length - 1); // remove angle brackets
}
if (showVersionNumber) {
let
nameEnd = sel.lastIndexOf(spacer);
if (nameEnd > 0) { // remove spacer and version #, if found (should be there, but...)
sel = sel.slice(0, nameEnd);
}
}
var addonList = AMdoc.getElementById('addon-list');
var selAddon = addonList.getElementsByAttribute('name', sel);
if (selAddon && selAddon[0]) {
var newSelId = selAddon[0].mAddon.id;
if (!newSelId) {
return;
}
saveUpdatedSel(addonList, selCat, newSelId);
var view = AMdoc.getElementById('view-port');
if (view.getAttribute('selectedIndex') == LISTVIEW) {
restoreHilite(AMdoc);
} else {
selAddon[0].showInDetailView();
}
} else {
/*
* Workaround issue that occurs when using nav arrow takes us back into detail view of a non-current category,
* resulting in 'addon-list' element that is no longer populated with the list of add-ons, thus there are no
* elements with the name we are looking for
*/
savedAddonName = sel;
// repopulates the list, finish workaround in the resulting onViewChange
cat.selectedItem.click();
}
}
function onViewChange() {
var AMdoc = this.document;
var acAddonList = AMdoc.getElementById("aomhilite_ac_menu");
var view = AMdoc.getElementById('view-port');
var cat = AMdoc.getElementById('categories');
var selCat = cat.selectedItem.id;
if (selCat == "category-userstyle" || selCat == "category-greasemonkey-user-script" ||
selCat == "category-custombuttons") {
dontSort = true;
}
if (view && view.getAttribute('selectedIndex') == LISTVIEW) {
if (savedAddonName) {
// finish workaround from onACaddonListSelection - display add-on in detail view
var addonList = AMdoc.getElementById('addon-list');
// prevents annoying flash of list view before detail view appears
addonList.setAttribute('hidden', true);
var selAddon = addonList.getElementsByAttribute('name', savedAddonName);
if (selAddon && selAddon[0]) {
selAddon[0].showInDetailView();
var newSelId = selAddon[0].mAddon.id;
saveUpdatedSel(addonList, selCat, newSelId);
}
addonList.removeAttribute('hidden');
savedAddonName = null;
// workaround finished, we're now in detail view, which retriggers viewchange
// (if we unexpectedly fail to find add-on for detail view, just continue in list view)
if (selAddon && selAddon[0]) {
return;
}
}
// need to recheck pref, since we're not using pref observer
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService);
prefs = prefs.getBranch("extensions.addonsmgrhilte@cfl.");
doAlphaSort = prefs.getBoolPref("alphaSort");
updatedSort = prefs.getBoolPref("updatedSort");
if (doAlphaSort || updatedSort) {
resort(AMdoc);
}
restoreHilite(AMdoc);
} else if (view && view.getAttribute('selectedIndex') == DETAILVIEW) {
// if we are in this ext's detail view
if (AMdoc.getElementById("detail-name").textContent == "Addons Manager Hilite") {
// set input field of numeric setting to sensible size & disable if not applicable
var setting = AMdoc.getElementById("aomhilite_size");
var inputField = AMdoc.getAnonymousElementByAttribute(setting, "anonid", "input");
inputField.setAttribute("style", "max-width: 3em;");
}
}
// if our menu has not been created yet or the UI is pref'ed off, nothing else to do
if (!acAddonList) {
return;
}
switch (selCat) { // select the proper menulist for the current category
case "category-extension":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(extArray));
break;
case "category-theme":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(themeArray));
break;
case "category-plugin":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(pluginArray));
break;
case "category-userstyle":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(userstyleArray));
break;
case "category-service":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(serviceArray));
break;
case "category-greasemonkey-user-script":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(userscriptArray));
break;
case "category-userscript":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(scriptishArray));
break;
case "category-custombuttons":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(custombuttonsArray));
break;
case "category-dictionary":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(dictionaryArray));
break;
case "category-locale":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(localeArray));
break;
default:
acAddonList.setAttribute('hidden', 'true'); // hide UI in unsupported cats
return;
}
acAddonList.removeAttribute('hidden'); // show UI
}
function resort(AMdoc) {
// use alphabetical (or optionally updated) sort instead of by-state sort implemented by Bug 624808
if (dontSort) {
dontSort = false;
return;
}
var addonList = AMdoc.getElementById("addon-list");
if (!updatedSort) {
var sortby = [ "name" ];
AMdoc.defaultView.sortList(addonList, sortby, true); // true = ascending
return;
}
// most recently updated
var list = addonList.children;
list.sort(function(addonItem1, addonItem2) {
var addon1Time = Date.parse(addonItem1.mAddon.updateDate);
var addon2Time = Date.parse(addonItem2.mAddon.updateDate);
if (addon1Time > addon2Time) {
return -1;
}
if (addon1Time < addon2Time) {
return 1;
}
var addon1Name = addonItem1.mAddon.name.toLowerCase();
var addon2Name = addonItem2.mAddon.name.toLowerCase();
return addon1Name.localeCompare(addon2Name);
});
var i;
for (i = 0; i < list.length; i++) {
addonList.appendChild(list[i]);
}
}
function addonListCallback(addons, AMdoc, addonType) {
var addonList = AMdoc.getElementById("addon-list");
var prevHilite = addonList.getAttribute("restoreondemand");
var hiliteArray = prevHilite.split(",");
var currentArray = null;
var currentCat = null;
addons.sort(function(addonItem1, addonItem2) {
var addon1Name = addonItem1.name.toLowerCase();
var addon2Name = addonItem2.name.toLowerCase();
return addon1Name.localeCompare(addon2Name);
});
switch (addonType) {
case "extension":
extArray = [];
currentArray = extArray;
currentCat = "category-extension";
break;
case "theme":
themeArray = [];
currentArray = themeArray;
currentCat = "category-theme";
break;
case "plugin":
pluginArray = [];
currentArray = pluginArray;
currentCat = "category-plugin";
break;
case "userstyle":
userstyleArray = [];
currentArray = userstyleArray;
currentCat = "category-userstyle";
break;
case "service":
serviceArray = [];
currentArray = serviceArray;
currentCat = "category-service";
break;
case "greasemonkey-user-script":
userscriptArray = [];
currentArray = userscriptArray;
currentCat = "category-greasemonkey-user-script";
break;
case "userscript":
scriptishArray = [];
currentArray = scriptishArray;
currentCat = "category-userscript";
break;
case "custombuttons":
custombuttonsArray = [];
currentArray = custombuttonsArray;
currentCat = "category-custombuttons";
break;
case "dictionary":
dictionaryArray = [];
currentArray = dictionaryArray;
currentCat = "category-dictionary";
break;
case "locale":
localeArray = [];
currentArray = localeArray;
currentCat = "category-locale";
break;
}
var i;
// need to recheck pref, since we're not using pref observer
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService);
prefs = prefs.getBranch("extensions.addonsmgrhilte@cfl.");
showVersionNumber = prefs.getBoolPref("showVersion");
for (i = 0; i < addons.length; i++) {
if (showVersionNumber && addons[i].version) {
currentArray.push(addons[i].name + spacer + addons[i].version);
} else {
currentArray.push(addons[i].name);
} // for vers # opt off
if (UIextra && hiliteArray[supportedCats.indexOf(currentCat)] == addons[i].id) {
// if this is the previously hilited item, add it to the top of list for easy access
var entry = "<" + addons[i].name;
if (showVersionNumber) {
entry = entry + spacer + addons[i].version;
}
entry = entry + ">";
currentArray.unshift(entry);
}
}
// wait for all 5 requests to arrive
if (!(extArray || themeArray || pluginArray || userstyleArray || serviceArray ||
userscriptArray || scriptishArray || custombuttonsArray || dictionaryArray || localeArray)) {
return;
}
var acAddonList = AMdoc.getElementById("aomhilite_ac_menu");
var cat = AMdoc.getElementById("categories");
var selCat = cat.selectedItem.id;
if (acAddonList) {
switch (selCat) {
case "category-extension":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(extArray));
break;
case "category-theme":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(themeArray));
break;
case "category-plugin":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(pluginArray));
break;
case "category-userstyle":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(userstyleArray));
break;
case "category-service":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(serviceArray));
break;
case "category-greasemonkey-user-script":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(userscriptArray));
break;
case "category-userscript":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(scriptishArray));
break;
case "category-custombuttons":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(custombuttonsArray));
break;
case "category-dictionary":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(dictionaryArray));
break;
case "category-locale":
acAddonList.setAttribute("autocompletesearchparam", JSON.stringify(localeArray));
break;
}
}
var view = AMdoc.getElementById('view-port');
// check if we're in list view of a supported category; sort and hilite
if ((supportedCats.indexOf(selCat) != -1) && view.getAttribute('selectedIndex') == LISTVIEW) {
if (selCat == "category-userstyle" || selCat == "category-greasemonkey-user-script" ||
selCat == "category-custombuttons") {
dontSort = true;
}
if (doAlphaSort || updatedSort) {
resort(AMdoc);
}
restoreHilite(AMdoc);
}
if (acAddonList && supportedCats.indexOf(selCat) != -1) {
acAddonList.removeAttribute('hidden'); // hide ac menu in unsupported categories
} else if (acAddonList) {
acAddonList.setAttribute('hidden', true);
}
}
function AddonsMgrOnUnload() {
AddonManager.removeAddonListener(addonListener);
this.removeEventListener("unload", AddonsMgrOnUnload, false);
this.removeEventListener("ViewChanged", onViewChange, false);
var addonList = this.document.getElementById("addon-list");
addonList.removeEventListener("select", onSelect, false);
var acAddonList = this.document.getElementById("aomhilite_ac_menu");
if (acAddonList) {
acAddonList.removeEventListener("textentered", onACaddonListSelection, false);
acAddonList.removeEventListener("change", onACaddonListSelection, false);
acAddonList.popup.removeEventListener("click", onACaddonListSelection, false);
var arrow = this.document.getAnonymousElementByAttribute(acAddonList, "anonid", "historydropmarker");
if (arrow && displayDropdown) {
arrow.removeEventListener("click", displayDropdown, false);
displayDropdown = null;
}
var head = this.document.getElementById("header");
head.removeChild(acAddonList);
}
if (UIextra && addonList.hasAttribute("restoreondemand")) {
// save needed info to aomhite so it will survive fx restart
var savedSelections = addonList.getAttribute('aomhilite');
var selectionArray = savedSelections.split(",");
var prevHilites = addonList.getAttribute('restoreondemand');
var hiliteArray = prevHilites.split(",");
// save previous category selections until new ones have been made
var i;
for (i = 0; i < hiliteArray.length; i++) {
if (hiliteArray[i] && !selectionArray[i]) {
selectionArray[i] = hiliteArray[i];
}
}
var selectionsToSave = selectionArray.toString();
addonList.setAttribute('aomhilite', selectionsToSave);
}
extArray = null;
themeArray = null;
pluginArray = null;
userstyleArray = null;
serviceArray = null;
userscriptArray = null;
scriptishArray = null;
custombuttonsArray = null;
dictionaryArray = null;
localeArray = null;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment