summaryrefslogtreecommitdiffstats
path: root/comm/suite/chatzilla/xul/content/config.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/suite/chatzilla/xul/content/config.js')
-rw-r--r--comm/suite/chatzilla/xul/content/config.js1775
1 files changed, 1775 insertions, 0 deletions
diff --git a/comm/suite/chatzilla/xul/content/config.js b/comm/suite/chatzilla/xul/content/config.js
new file mode 100644
index 0000000000..12eefa9da3
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/config.js
@@ -0,0 +1,1775 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const MEDIATOR_CONTRACTID = "@mozilla.org/appshell/window-mediator;1";
+
+const nsIWindowMediator = Components.interfaces.nsIWindowMediator;
+
+const CONFIG_WINDOWTYPE = "irc:chatzilla:config";
+
+/* Now we create and set up some required items from other Chatzilla JS files
+ * that we really have no reason to load, but the ones we do need won't work
+ * without these...
+ */
+var ASSERT = function(cond, msg) { if (!cond) { alert(msg); } return cond; }
+var client;
+
+function CIRCNetwork() {}
+function CIRCServer() {}
+function CIRCChannel() {}
+function CIRCChanUser() {}
+function CIRCUser() {}
+function CIRCDCC() {}
+function CIRCDCCUser() {}
+function CIRCDCCChat() {}
+function CIRCDCCFileTransfer() {}
+function CIRCSTS() {}
+
+function getObjectDetails(obj)
+{
+ var rv = new Object();
+ rv.sourceObject = obj;
+ rv.TYPE = obj.TYPE;
+ rv.parent = ("parent" in obj) ? obj.parent : null;
+ rv.user = null;
+ rv.channel = null;
+ rv.server = null;
+ rv.network = null;
+
+ switch (obj.TYPE)
+ {
+ case "PrefNetwork":
+ rv.network = obj;
+ if ("primServ" in rv.network)
+ rv.server = rv.network.primServ;
+ else
+ rv.server = null;
+ break;
+
+ case "PrefChannel":
+ rv.channel = obj;
+ rv.server = rv.channel.parent;
+ rv.network = rv.server.parent;
+ break;
+
+ case "PrefUser":
+ rv.user = obj;
+ rv.server = rv.user.parent;
+ rv.network = rv.server.parent;
+ break;
+ }
+
+ return rv;
+}
+
+/* Global object for the prefs. The 'root' of all the objects to do with the
+ * prefs.
+ */
+function PrefGlobal()
+{
+ this.networks = new Object();
+ this.commandManager = new Object();
+ this.commandManager.defineCommand = function() {};
+ this.commandManager.removeCommand = function() {};
+ this.entities = new Object();
+ this.hostCompat = new Object();
+}
+PrefGlobal.prototype.TYPE = "PrefGlobal";
+
+/* Represents a single network in the hierarchy.
+ *
+ * |force| - If true, sets a pref on this object. This makes sure the object
+ * is "known" next time we load up (since we look for any prefs).
+ *
+ * |show| - If true, the object still exists even if the magic pref is not set.
+ * Thus, allows an object to exist without any prefs set.
+ */
+function PrefNetwork(parent, name, force, show)
+{
+ if (":" + name in parent.networks)
+ return parent.networks[":" + name];
+
+ this.parent = parent;
+ this.unicodeName = name;
+ this.viewName = name;
+ this.canonicalName = name;
+ this.collectionKey = ":" + name;
+ this.encodedName = name;
+ this.prettyName = getMsg(MSG_PREFS_FMT_DISPLAY_NETWORK, this.unicodeName);
+ this.servers = new Object();
+ this.primServ = new PrefServer(this, "dummy server");
+ this.channels = this.primServ.channels;
+ this.users = this.primServ.users;
+ this.prefManager = getNetworkPrefManager(this);
+ this.prefs = this.prefManager.prefs;
+ this.prefManager.onPrefChanged = function(){};
+
+ if (force)
+ this.prefs["hasPrefs"] = true;
+
+ if (this.prefs["hasPrefs"] || show)
+ this.parent.networks[this.collectionKey] = this;
+
+ return this;
+};
+PrefNetwork.prototype.TYPE = "PrefNetwork";
+
+/* Cleans up the mess. */
+PrefNetwork.prototype.clear =
+function pnet_clear()
+{
+ this.prefs["hasPrefs"] = false;
+ delete this.parent.networks[this.collectionKey];
+}
+
+/* A middle-management object.
+ *
+ * Exists only to satisfy the IRC library pref functions that expect this
+ * particular hierarchy.
+ */
+function PrefServer(parent, name)
+{
+ this.parent = parent;
+ this.unicodeName = name;
+ this.viewName = name;
+ this.canonicalName = name;
+ this.collectionKey = ":" + name;
+ this.encodedName = name;
+ this.prettyName = this.unicodeName; // Not used, thus not localised.
+ this.channels = new Object();
+ this.users = new Object();
+ this.parent.servers[this.collectionKey] = this;
+ return this;
+};
+PrefServer.prototype.TYPE = "PrefServer";
+
+/* Represents a single channel in the hierarchy.
+ *
+ * |force| and |show| the same as PrefNetwork.
+ */
+function PrefChannel(parent, name, force, show)
+{
+ if (":" + name in parent.channels)
+ return parent.channels[":" + name];
+
+ this.parent = parent;
+ this.unicodeName = name;
+ this.viewName = name;
+ this.canonicalName = name;
+ this.collectionKey = ":" + name;
+ this.encodedName = name;
+ this.prettyName = getMsg(MSG_PREFS_FMT_DISPLAY_CHANNEL,
+ [this.parent.parent.unicodeName, this.unicodeName]);
+ this.prefManager = getChannelPrefManager(this);
+ this.prefs = this.prefManager.prefs;
+ this.prefManager.onPrefChanged = function(){};
+
+ if (force)
+ this.prefs["hasPrefs"] = true;
+
+ if (this.prefs["hasPrefs"] || show)
+ this.parent.channels[this.collectionKey] = this;
+
+ return this;
+};
+PrefChannel.prototype.TYPE = "PrefChannel";
+
+/* Cleans up the mess. */
+PrefChannel.prototype.clear =
+function pchan_clear()
+{
+ this.prefs["hasPrefs"] = false;
+ delete this.parent.channels[this.collectionKey];
+}
+
+/* Represents a single user in the hierarchy.
+ *
+ * |force| and |show| the same as PrefNetwork.
+ */
+function PrefUser(parent, name, force, show)
+{
+ if (":" + name in parent.users)
+ return parent.users[":" + name];
+
+ this.parent = parent;
+ this.unicodeName = name;
+ this.viewName = name;
+ this.canonicalName = name;
+ this.collectionKey = ":" + name;
+ this.encodedName = name;
+ this.prettyName = getMsg(MSG_PREFS_FMT_DISPLAY_USER,
+ [this.parent.parent.unicodeName, this.unicodeName]);
+ this.prefManager = getUserPrefManager(this);
+ this.prefs = this.prefManager.prefs;
+ this.prefManager.onPrefChanged = function(){};
+
+ if (force)
+ this.prefs["hasPrefs"] = true;
+
+ if (this.prefs["hasPrefs"] || show)
+ this.parent.users[this.collectionKey] = this;
+
+ return this;
+};
+PrefUser.prototype.TYPE = "PrefUser";
+
+/* Cleans up the mess. */
+PrefUser.prototype.clear =
+function puser_clear()
+{
+ this.prefs["hasPrefs"] = false;
+ delete this.parent.users[this.collectionKey];
+}
+
+// Stores a list of |PrefObject|s.
+function PrefObjectList()
+{
+ this.objects = new Array();
+
+ return this;
+}
+
+// Add an object, and init it's private data.
+PrefObjectList.prototype.addObject =
+function polist_addObject(pObject)
+{
+ this.objects.push(pObject);
+ return pObject.privateData = new ObjectPrivateData(pObject, this.objects.length - 1);
+}
+
+/* Removes an object, without changing the index. */
+PrefObjectList.prototype.deleteObject =
+function polist_addObject(index)
+{
+ this.objects[index].privateData.clear();
+ this.objects[index].clear();
+ this.objects[index] = { privateData: null };
+}
+
+// Get a specific object.
+PrefObjectList.prototype.getObject =
+function polist_getObject(index)
+{
+ return this.objects[index].privateData;
+}
+
+// Gets the private data for an object.
+PrefObjectList.getPrivateData =
+function polist_getPrivateData(object)
+{
+ return object.privateData;
+}
+
+// Stores the pref object's private data.
+function ObjectPrivateData(parent, index)
+{
+ this.parent = parent; // Real pref object.
+ this.prefs = new Object();
+ this.groups = new Object();
+
+ this.arrayIndex = index;
+ this.deckIndex = -1;
+ this.dataLoaded = false;
+
+ var treeObj = document.getElementById("pref-tree-object");
+ this.tree = document.getElementById("pref-tree");
+ this.treeContainer = document.createElement("treeitem");
+ this.treeNode = document.createElement("treerow");
+ this.treeCell = document.createElement("treecell");
+
+ this.treeContainer.setAttribute("prefobjectindex", this.arrayIndex);
+ this.treeCell.setAttribute("label", this.parent.unicodeName);
+
+ switch (this.parent.TYPE)
+ {
+ case "PrefChannel":
+ case "PrefUser":
+ var p = this.parent.parent.parent; // Network object.
+ var pData = PrefObjectList.getPrivateData(p);
+
+ if (!("treeChildren" in pData) || !pData.treeChildren)
+ {
+ pData.treeChildren = document.createElement("treechildren");
+ pData.treeContainer.appendChild(pData.treeChildren);
+ treeObj.view.toggleOpenState(treeObj.view.rowCount - 1);
+ }
+ pData.treeContainer.setAttribute("container", "true");
+ pData.treeChildren.appendChild(this.treeContainer);
+ break;
+
+ default:
+ this.tree.appendChild(this.treeContainer);
+ break;
+ }
+
+ this.treeContainer.appendChild(this.treeNode);
+ this.treeNode.appendChild(this.treeCell);
+
+ return this;
+}
+
+// Creates all the XUL elements needed to show this pref object.
+ObjectPrivateData.prototype.loadXUL =
+function opdata_loadXUL(tabOrder)
+{
+ var t = this;
+
+ /* Function that sorts the preferences by their label, else they look
+ * fairly random in order.
+ *
+ * Sort keys: not grouped, sub-group name, boolean, pref label.
+ */
+ function sortByLabel(a, b) {
+ if (t.prefs[a].subGroup || t.prefs[b].subGroup)
+ {
+ // Non-grouped go first.
+ if (!t.prefs[a].subGroup)
+ return -1;
+ if (!t.prefs[b].subGroup)
+ return 1;
+
+ // Sub-group names.
+ if (t.prefs[a].subGroup < t.prefs[b].subGroup)
+ return -1;
+ if (t.prefs[a].subGroup > t.prefs[b].subGroup)
+ return 1;
+ }
+
+ // Booleans go first.
+ if ((t.prefs[a].type == "boolean") && (t.prefs[b].type != "boolean"))
+ return -1;
+ if ((t.prefs[a].type != "boolean") && (t.prefs[b].type == "boolean"))
+ return 1;
+
+ // ...then label.
+ if (t.prefs[a].label < t.prefs[b].label)
+ return -1;
+ if (t.prefs[a].label > t.prefs[b].label)
+ return 1;
+ return 0;
+ };
+
+ if (this.deckIndex >= 0)
+ return;
+
+ this.deck = document.getElementById("pref-object-deck");
+ this.tabbox = document.createElement("tabbox");
+ this.tabs = document.createElement("tabs");
+ this.tabPanels = document.createElement("tabpanels");
+
+ this.tabbox.setAttribute("flex", 1);
+ this.tabPanels.setAttribute("flex", 1);
+
+ this.tabbox.appendChild(this.tabs);
+ this.tabbox.appendChild(this.tabPanels);
+ this.deck.appendChild(this.tabbox);
+
+ this.deckIndex = this.deck.childNodes.length - 1;
+
+ this.loadData();
+
+ var prefList = keys(this.prefs);
+ prefList.sort(sortByLabel);
+
+ for (var i = 0; i < tabOrder.length; i++)
+ {
+ var pto = tabOrder[i];
+ var needTab = pto.fixed;
+ if (!needTab)
+ {
+ // Not a "always visible" tab, check we need it.
+ for (var j = 0; j < prefList.length; j++)
+ {
+ if (this.prefs[prefList[j]].mainGroup == pto.name)
+ {
+ needTab = true;
+ break;
+ }
+ }
+ }
+ if (needTab)
+ this.addGroup(pto.name);
+ }
+
+ for (i = 0; i < prefList.length; i++)
+ this.prefs[prefList[i]].loadXUL();
+
+ if (this.tabs.childNodes.length > 0)
+ this.tabbox.selectedIndex = 0;
+}
+
+// Loads all the prefs.
+ObjectPrivateData.prototype.loadData =
+function opdata_loadData()
+{
+ if (this.dataLoaded)
+ return;
+
+ this.dataLoaded = true;
+
+ // Now get the list of pref names, and add them...
+ var prefList = this.parent.prefManager.prefNames;
+
+ for (var i in prefList)
+ this.addPref(prefList[i]);
+}
+
+// Clears up all the XUL objects and data.
+ObjectPrivateData.prototype.clear =
+function opdata_clear()
+{
+ //dd("Removing prefs for " + this.parent.displayName + " {");
+ if (!this.dataLoaded)
+ this.loadData();
+ for (var i in this.prefs)
+ this.prefs[i].clear();
+ //dd("}");
+
+ if (this.deckIndex >= 0)
+ {
+ this.deck.removeChild(this.tabbox);
+ this.treeContainer.removeAttribute("container");
+ this.treeContainer.parentNode.removeChild(this.treeContainer);
+ }
+}
+
+// Resets all the prefs to their original values.
+ObjectPrivateData.prototype.reset =
+function opdata_reset()
+{
+ for (var i in this.prefs)
+ if (this.prefs[i].type != "hidden")
+ this.prefs[i].reset();
+}
+
+// Adds a pref to the internal data structures.
+ObjectPrivateData.prototype.addPref =
+function opdata_addPref(name)
+{
+ return this.prefs[name] = new PrefData(this, name);
+}
+
+// Adds a group to a pref object's data.
+ObjectPrivateData.prototype.addGroup =
+function opdata_addPref(name)
+{
+ // Special group for prefs we don't want shown (nothing sinister here).
+ if (name == "hidden")
+ return null;
+
+ if (!(name in this.groups))
+ this.groups[name] = new PrefMainGroup(this, name);
+
+ return this.groups[name];
+}
+
+// Represents a single pref on a single object within the pref window.
+function PrefData(parent, name)
+{
+ // We want to keep all this "worked out" info, so make a hash of all
+ // the prefs on the pwData [Pref Window Data] property of the object.
+
+ // First, lets find out what kind of pref we've got:
+ this.parent = parent; // Private data for pref object.
+ this.name = name;
+ this.manager = this.parent.parent.prefManager; // PrefManager.
+ this.record = this.manager.prefRecords[name]; // PrefRecord.
+ this.def = this.record.defaultValue; // Default value.
+ this.type = typeof this.def; // Pref type.
+ this.val = this.manager.prefs[name]; // Current value.
+ this.startVal = this.val; // Start value.
+ this.label = this.record.label; // Display name.
+ this.help = this.record.help; // Help text.
+ this.group = this.record.group; // Group identifier.
+ this.labelFor = "none"; // Auto-grouped label.
+
+ // Handle defered prefs (call defer function, and use resulting
+ // value/type instead).
+ if (this.type == "function")
+ this.def = this.def(this.name);
+ this.type = typeof this.def;
+
+ // And those arrays... this just makes our life easier later by having
+ // a particular name for array prefs.
+ if (isinstance(this.def, Array))
+ this.type = "array";
+
+ if (this.group == "hidden")
+ this.type = "hidden";
+
+ // Convert "a.b" into sub-properties...
+ var m = this.group.match(/^([^.]*)(\.(.*))?$/)
+ ASSERT(m, "Failed group match!");
+ this.mainGroup = m[1];
+ this.subGroup = m[3];
+
+ return this;
+}
+
+/* Creates all the XUL elements to display this one pref. */
+PrefData.prototype.loadXUL =
+function pdata_loadXUL()
+{
+ if (this.type == "hidden")
+ return;
+
+ // Create the base box for the pref.
+ this.box = document.createElement("box");
+ this.box.orient = "horizontal";
+ this.box.setAttribute("align", "center");
+
+ switch (this.type)
+ {
+ case "string":
+ label = document.createElement("label");
+ label.setAttribute("value", this.label);
+ label.width = 100;
+ label.flex = 1;
+ this.box.appendChild(label);
+
+ this.edit = document.createElement("textbox");
+ // We choose the size based on the length of the default.
+ if (this.def.length < 8)
+ this.edit.setAttribute("size", "10");
+ else if (this.def.length < 20)
+ this.edit.setAttribute("size", "25");
+ else
+ this.edit.flex = 1;
+
+ var editCont = document.createElement("hbox");
+ editCont.flex = 1000;
+ editCont.appendChild(this.edit);
+ this.box.appendChild(editCont);
+
+ // But if it's a file/URL...
+ if (this.def.match(/^([a-z]+:\/|[a-z]:\\)/i))
+ {
+ // ...we make it as big as possible.
+ this.edit.removeAttribute("size");
+ this.edit.flex = 1;
+
+ if (!this.name.match(/path$/i) &&
+ (this.def.match(/^(file|chrome):\//i) ||
+ this.name.match(/filename$/i)))
+ {
+ // So long as the pref name doesn't end in "path", and
+ // it's chrome:, file: or a local file, we add the button.
+ var ext = "";
+ var m = this.def.match(/\.([a-z0-9]+)$/);
+ if (m)
+ ext = "*." + m[1];
+
+ // We're cheating again here, if it ends "filename" it's
+ // a local file path.
+ var type = (this.name.match(/filename$/i) ? "file" : "fileurl");
+ type = (this.name.match(/folder$/i) ? "folder" : type);
+ appendButton(this.box, "onPrefBrowse",
+ { label: MSG_PREFS_BROWSE, spec: ext,
+ kind: type });
+ }
+ }
+ break;
+
+ case "number":
+ label = document.createElement("label");
+ label.setAttribute("value", this.label);
+ label.width = 100;
+ label.flex = 1;
+ this.box.appendChild(label);
+
+ this.edit = document.createElement("textbox");
+ this.edit.setAttribute("size", "5");
+ this.edit.setAttribute("type", "number");
+ this.edit.setAttribute("min", "-1");
+
+ editCont = document.createElement("hbox");
+ editCont.flex = 1000;
+ editCont.appendChild(this.edit);
+ this.box.appendChild(editCont);
+ break;
+
+ case "boolean":
+ this.edit = document.createElement("checkbox");
+ this.edit.setAttribute("label", this.label);
+ this.box.appendChild(this.edit);
+ break;
+
+ case "array":
+ this.box.removeAttribute("align");
+
+ var oBox = document.createElement("box");
+ oBox.orient = "vertical";
+ oBox.flex = 1;
+ this.box.appendChild(oBox);
+
+ if (this.help)
+ {
+ label = document.createElement("label");
+ label.appendChild(document.createTextNode(this.help));
+ oBox.appendChild(label);
+ }
+
+ this.edit = document.createElement("listbox");
+ this.edit.flex = 1;
+ this.edit.setAttribute("style", "height: 1em;");
+ this.edit.setAttribute("kind", "url");
+ if (this.def.length > 0 && this.def[0].match(/^file:\//))
+ this.edit.setAttribute("kind", "fileurl");
+ this.edit.setAttribute("onselect", "gPrefWindow.onPrefListSelect(this);");
+ this.edit.setAttribute("ondblclick", "gPrefWindow.onPrefListEdit(this);");
+ oBox.appendChild(this.edit);
+
+ var box = document.createElement("box");
+ box.orient = "vertical";
+ this.box.appendChild(box);
+
+ // NOTE: This order is important - getRelatedItem needs to be
+ // kept in sync with this order. Perhaps a better way is needed...
+ appendButton(box, "onPrefListUp", { label: MSG_PREFS_MOVE_UP,
+ "class": "up" });
+ appendButton(box, "onPrefListDown", { label: MSG_PREFS_MOVE_DOWN,
+ "class": "down" });
+ appendSeparator(box);
+ appendButton(box, "onPrefListAdd", { label: MSG_PREFS_ADD });
+ appendButton(box, "onPrefListEdit", { label: MSG_PREFS_EDIT });
+ appendButton(box, "onPrefListDelete", { label: MSG_PREFS_DELETE });
+ break;
+
+ default:
+ // This is really more of an error case, since we really should
+ // know about all the valid pref types.
+ var label = document.createElement("label");
+ label.setAttribute("value", "[not editable] " + this.type);
+ this.box.appendChild(label);
+ }
+
+ this.loadData();
+
+ if (this.edit)
+ {
+ this.edit.setAttribute("prefobjectindex", this.parent.arrayIndex);
+ this.edit.setAttribute("prefname", this.name);
+ // Associate textbox with label for accessibility.
+ if (label)
+ {
+ this.edit.id = this.manager.branchName + this.name;
+ label.setAttribute("control", this.edit.id);
+ }
+ }
+
+ if (!ASSERT("groups" in this.parent, "Must have called " +
+ "[ObjectPrivateData].loadXUL before trying to display prefs."))
+ return;
+
+ this.parent.addGroup(this.mainGroup);
+ if (this.subGroup)
+ this.parent.groups[this.mainGroup].addGroup(this.subGroup);
+
+ if (!this.subGroup)
+ this.parent.groups[this.mainGroup].box.appendChild(this.box);
+ else
+ this.parent.groups[this.mainGroup].groups[this.subGroup].box.appendChild(this.box);
+
+ // Setup tooltip stuff...
+ if (this.help && (this.type != "array"))
+ {
+ this.box.setAttribute("tooltiptitle", this.label);
+ this.box.setAttribute("tooltipcontent", this.help);
+ this.box.setAttribute("onmouseover", "gPrefWindow.onPrefMouseOver(this);");
+ this.box.setAttribute("onmousemove", "gPrefWindow.onPrefMouseMove(this);");
+ this.box.setAttribute("onmouseout", "gPrefWindow.onPrefMouseOut(this);");
+ }
+}
+
+/* Loads the pref's data into the edit component. */
+PrefData.prototype.loadData =
+function pdata_loadData()
+{
+ /* Note about .value and .setAttribute as used here:
+ *
+ * XBL doesn't kick in until CSS is calculated on a node, so the code makes
+ * a compromise and uses these two methods as appropriate. Initally this
+ * is called is before the node has been placed in the document DOM tree,
+ * and thus hasn't been "magiced" by XBL and so .value is meaningless to
+ * it. After initally being set as an attribute, it's added to the DOM,
+ * XBL kicks in, and after that .value is the only way to change the value.
+ */
+ switch (this.type)
+ {
+ case "string":
+ if (this.edit.hasAttribute("value"))
+ this.edit.value = this.val;
+ else
+ this.edit.setAttribute("value", this.val);
+ break;
+
+ case "number":
+ if (this.edit.hasAttribute("value"))
+ this.edit.value = this.val;
+ else
+ this.edit.setAttribute("value", this.val);
+ break;
+
+ case "boolean":
+ if (this.edit.hasAttribute("checked"))
+ this.edit.checked = this.val;
+ else
+ this.edit.setAttribute("checked", this.val);
+ break;
+
+ case "array":
+ // Remove old entires.
+ while (this.edit.firstChild)
+ this.edit.removeChild(this.edit.firstChild);
+
+ // Add new ones.
+ for (var i = 0; i < this.val.length; i++)
+ {
+ var item = document.createElement("listitem");
+ item.value = this.val[i];
+ item.crop = "center";
+ item.setAttribute("label", this.val[i]);
+ this.edit.appendChild(item);
+ }
+
+ // Make sure buttons are up-to-date.
+ gPrefWindow.onPrefListSelect(this.edit);
+ break;
+
+ default:
+ }
+}
+
+/* Cleans up the mess. */
+PrefData.prototype.clear =
+function pdata_clear()
+{
+ //dd("Clearing pref " + this.name);
+ if (("box" in this) && this.box)
+ {
+ this.box.parentNode.removeChild(this.box);
+ delete this.box;
+ }
+ try {
+ this.manager.clearPref(this.name);
+ } catch(ex) {}
+}
+
+/* Resets the pref to it's default. */
+PrefData.prototype.reset =
+function pdata_reset()
+{
+ //try {
+ // this.manager.clearPref(this.name);
+ //} catch(ex) {}
+ this.val = this.def;
+ this.loadData();
+}
+
+/* Saves the pref... or would do. */
+PrefData.prototype.save =
+function pdata_save()
+{
+ //FIXME//
+}
+
+// Represents a "main group", i.e. a tab for a single pref object.
+function PrefMainGroup(parent, name)
+{
+ // Init this group's object.
+ this.parent = parent; // Private data for pref object.
+ this.name = name;
+ this.groups = new Object();
+ this.label = getMsg("pref.group." + this.name + ".label", null, this.name);
+ this.tab = document.createElement("tab");
+ this.tabPanel = document.createElement("tabpanel");
+ this.box = this.sb = document.createElement("scroller");
+
+ this.tab.setAttribute("label", this.label);
+ this.tabPanel.setAttribute("orient", "vertical");
+ this.sb.setAttribute("orient", "vertical");
+ this.sb.setAttribute("flex", 1);
+
+ this.parent.tabs.appendChild(this.tab);
+ this.parent.tabPanels.appendChild(this.tabPanel);
+ this.tabPanel.appendChild(this.sb);
+
+ return this;
+}
+// Adds a sub group to this main group.
+PrefMainGroup.prototype.addGroup =
+function pmgroup_addGroup(name)
+{
+ // If the sub group doesn't exist, make it exist.
+ if (!(name in this.groups))
+ this.groups[name] = new PrefSubGroup(this, name);
+
+ return this.groups[name];
+}
+
+// Represents a "sub group", i.e. a groupbox on a tab, for a single main group.
+function PrefSubGroup(parent, name)
+{
+ this.parent = parent; // Main group.
+ this.name = name;
+ this.fullGroup = this.parent.name + "." + this.name;
+ this.label = getMsg("pref.group." + this.fullGroup + ".label", null, this.name);
+ this.help = getMsg("pref.group." + this.fullGroup + ".help", null, "");
+ this.gb = document.createElement("groupbox");
+ this.cap = document.createElement("caption");
+ this.box = document.createElement("box");
+
+ this.cap.setAttribute("label", this.label);
+ this.gb.appendChild(this.cap);
+ this.box.orient = "vertical";
+
+ // If there's some help text, we place it as the first thing inside
+ // the <groupbox>, as an explanation for the entire subGroup.
+ if (this.help)
+ {
+ this.helpLabel = document.createElement("label");
+ this.helpLabel.appendChild(document.createTextNode(this.help));
+ this.gb.appendChild(this.helpLabel);
+ }
+ this.gb.appendChild(this.box);
+ this.parent.box.appendChild(this.gb);
+
+ return this;
+}
+
+// Actual pref window itself.
+function PrefWindow()
+{
+ // Not loaded until the pref list and objects have been created in |onLoad|.
+ this.loaded = false;
+
+ /* PREF TAB ORDER: Determins the order, and fixed tabs, found on the UI.
+ *
+ * It is an array of mainGroup names, and a flag indicating if the tab
+ * should be created even when there's no prefs for it.
+ *
+ * This is for consistency, although none of the ChatZilla built-in pref
+ * objects leave fixed tabs empty currently.
+ */
+ this.prefTabOrder = [{ fixed: true, name: "general"},
+ { fixed: true, name: "appearance"},
+ { fixed: false, name: "lists"},
+ { fixed: false, name: "dcc"},
+ { fixed: false, name: "startup"},
+ { fixed: false, name: "global"},
+ { fixed: false, name: "munger"}
+ ];
+
+ /* PREF OBJECTS: Stores all the objects we've using that have prefs.
+ *
+ * Each object gets a "privateData" object, which is then used by the pref
+ * window code for storing all of it's data on the object.
+ */
+ this.prefObjects = null;
+
+ /* TOOLTIPS: Special tooltips for preference items.
+ *
+ * Timer: return value from |setTimeout| whenever used. There is only
+ * ever one timer going for the tooltips.
+ * Showing: stores visibility of the tooltip.
+ * ShowDelay: ms delay which them mouse must be still to show tooltips.
+ * HideDelay: ms delay before the tooltips hide themselves.
+ */
+ this.tooltipTimer = 0;
+ this.tooltipShowing = false;
+ this.tooltipShowDelay = 1000;
+ this.tooltipHideDelay = 20000;
+ this.tooltipBug418798 = false;
+}
+PrefWindow.prototype.TYPE = "PrefWindow";
+
+/* Updates the tooltip state, either showing or hiding it. */
+PrefWindow.prototype.setTooltipState =
+function pwin_setTooltipState(visible)
+{
+ // Shortcut: if we're already in the right state, don't bother.
+ if (this.tooltipShowing == visible)
+ return;
+
+ var tt = document.getElementById("czPrefTip");
+
+ // If we're trying to show it, and we have a reference object
+ // (this.tooltipObject), we are ok to go.
+ if (visible && this.tooltipObject)
+ {
+ /* Get the boxObject for the reference object, as we're going to
+ * place to tooltip explicitly based on this.
+ */
+ var tipobjBox = this.tooltipObject.boxObject;
+
+ // Adjust the width to that of the reference box.
+ tt.sizeTo(tipobjBox.width, tt.boxObject.height);
+ /* show tooltip using the reference object, and it's screen
+ * position. NOTE: Most of these parameters are supposed to affect
+ * things, and they do seem to matter, but don't work anything like
+ * the documentation. Be careful changing them.
+ */
+ tt.showPopup(this.tooltipObject, -1, -1, "tooltip", "bottomleft", "topleft");
+
+ // Set the timer to hide the tooltip some time later...
+ // (note the fun inline function)
+ this.tooltipTimer = setTimeout(setTooltipState, this.tooltipHideDelay,
+ this, false);
+ this.tooltipShowing = true;
+ }
+ else
+ {
+ /* We're here because either we are meant to be hiding the tooltip,
+ * or we lacked the information needed to show one. So hide it.
+ */
+ tt.hidePopup();
+ this.tooltipShowing = false;
+ }
+}
+
+/** Window event handlers **/
+
+/* Initalises, and loads all the data/utilities and prefs. */
+PrefWindow.prototype.onLoad =
+function pwin_onLoad()
+{
+ // Get ourselves a base object for the object hierarchy.
+ client = new PrefGlobal();
+
+ // Kick off the localisation load.
+ initMessages();
+
+ // Use localised name.
+ client.viewName = MSG_PREFS_GLOBAL;
+ client.unicodeName = client.viewName;
+ client.prettyName = client.viewName;
+
+ // Use the window mediator service to prevent mutliple instances.
+ var windowMediator = Components.classes[MEDIATOR_CONTRACTID];
+ var windowManager = windowMediator.getService(nsIWindowMediator);
+ var enumerator = windowManager.getEnumerator(CONFIG_WINDOWTYPE);
+
+ // We only want one open at a time because don't (currently) cope with
+ // pref-change notifications. In fact, it's not easy to cope with.
+ // Save it for some time later. :)
+
+ enumerator.getNext();
+ if (enumerator.hasMoreElements())
+ {
+ alert(MSG_PREFS_ALREADYOPEN);
+ window.close();
+ return;
+ }
+
+ // Make sure we know what host we're on.
+ initApplicationCompatibility();
+
+ // Kick off the core pref initalisation code.
+ initPrefs();
+
+ // Turn off all notifications, or it'll get confused when any pref
+ // does change.
+ client.prefManager.onPrefChanged = function(){};
+
+ // The list of objects we're tacking the prefs of.
+ this.prefObjects = new PrefObjectList();
+
+ /* Oh, this is such an odd way to do this. But hey, it works. :)
+ * What we do is ask the pref branch for the client object to give us
+ * a list of all the preferences under it. This just gets us all the
+ * Chatzilla prefs. Then, we filter them so that we only deal with
+ * ones for networks, channels and users. This means, even if only
+ * one pref is set for a channel, we get it's network and channel
+ * object created here.
+ */
+ var prefRE = new RegExp("^networks.([^.]+)" +
+ "(?:\\.(users|channels)?\\.([^.]+))?\\.");
+ var rv = new Object();
+ var netList = client.prefManager.prefBranch.getChildList("networks.", rv);
+ for (var i in netList)
+ {
+ var m = netList[i].match(prefRE);
+ if (!m)
+ continue;
+
+ var netName = unMungeNetworkName(m[1]);
+ /* We're forcing the network into existance (3rd param) if the
+ * pref is actually set, as opposed to just being known about (the
+ * pref branch will list all *known* prefs, not just those set).
+ *
+ * NOTE: |force| will, if |true|, set a property on the object so it
+ * will still exist next time we're here. If |false|, the
+ * the object will only come into existance if this magical
+ * [hidden] pref is set.
+ */
+ var force = client.prefManager.prefBranch.prefHasUserValue(netList[i]);
+
+ // Don't bother with the new if it's already there (time saving).
+ if (!(":" + netName in client.networks))
+ new PrefNetwork(client, netName, force);
+
+ if ((2 in m) && (3 in m) && (":" + netName in client.networks))
+ {
+ let net = client.networks[":" + netName];
+
+ // Create a channel object if appropriate.
+ if (m[2] == "channels")
+ new PrefChannel(net.primServ, unMungeNetworkName(m[3]), force);
+
+ // Create a user object if appropriate.
+ if (m[2] == "users")
+ new PrefUser(net.primServ, unMungeNetworkName(m[3]), force);
+ }
+ }
+
+ // Store out object that represents the current view.
+ var currentView = null;
+
+ // Enumerate source window's tab list...
+ if (("arguments" in window) && (0 in window.arguments) && ("client" in window.arguments[0]))
+ {
+ /* Make sure we survive this, external data could be bad. :) */
+ try
+ {
+ var czWin = window.arguments[0];
+ var s;
+ var n, c, u;
+ this.ownerClient = czWin.client;
+ this.ownerClient.configWindow = window;
+
+ /* Go nick the source window's view list. We can then show items in
+ * the tree for the currently connected/shown networks, channels
+ * and users even if they don't have any known prefs yet.
+ *
+ * NOTE: the |false, true| params tell the objects to not force
+ * any prefs into existance, but still show in the tree.
+ */
+ for (i = 0; i < czWin.client.viewsArray.length; i++)
+ {
+ var view = czWin.client.viewsArray[i].source;
+
+ // Network view...
+ if (view.TYPE == "IRCNetwork")
+ {
+ n = new PrefNetwork(client, view.unicodeName, false, true);
+ if (view == czWin.client.currentObject)
+ currentView = n;
+ }
+
+ if (view.TYPE.match(/^IRC(Channel|User)$/))
+ {
+ n = new PrefNetwork(client, view.parent.parent.unicodeName,
+ false, true);
+ s = n.primServ;
+ }
+
+ // Channel view...
+ if (view.TYPE == "IRCChannel")
+ {
+ c = new PrefChannel(s, view.unicodeName, false, true);
+ if (view == czWin.client.currentObject)
+ currentView = c;
+ }
+
+ // User view...
+ if (view.TYPE == "IRCUser")
+ {
+ u = new PrefUser(s, view.unicodeName, false, true);
+ if (view == czWin.client.currentObject)
+ currentView = u;
+ }
+ }
+ }
+ catch(ex)
+ {}
+ }
+
+ // Add the client object...
+ this.prefObjects.addObject(client);
+ // ...and everyone else.
+ var i, j;
+ /* We sort the keys (property names, i.e. network names). This means the UI
+ * will show them in lexographical order, which is good.
+ */
+ var sortedNets = keys(client.networks).sort();
+ for (i = 0; i < sortedNets.length; i++) {
+ net = client.networks[sortedNets[i]];
+ this.prefObjects.addObject(net);
+
+ var sortedChans = keys(net.channels).sort();
+ for (j = 0; j < sortedChans.length; j++)
+ this.prefObjects.addObject(net.channels[sortedChans[j]]);
+
+ var sortedUsers = keys(net.users).sort();
+ for (j = 0; j < sortedUsers.length; j++)
+ this.prefObjects.addObject(net.users[sortedUsers[j]]);
+ }
+
+ // Select the first item in the list.
+ var prefTree = document.getElementById("pref-tree-object");
+ if ("selection" in prefTree.treeBoxObject)
+ prefTree.treeBoxObject.selection.select(0);
+ else
+ prefTree.view.selection.select(0);
+
+ // Find and select the current view.
+ for (i = 0; i < this.prefObjects.objects.length; i++)
+ {
+ if (this.prefObjects.objects[i] == currentView)
+ {
+ if ("selection" in prefTree.treeBoxObject)
+ prefTree.treeBoxObject.selection.select(i);
+ else
+ prefTree.view.selection.select(i);
+ }
+ }
+
+ this.onSelectObject();
+
+ // We're done, without error, so it's safe to show the stuff.
+ document.getElementById("loadDeck").selectedIndex = 1;
+ // This allows [OK] to actually save, without this it'll just close.
+ this.loaded = true;
+
+ // Force the window to be the right size now, not later.
+ window.sizeToContent();
+ // XXX: If we're on mac, make it wider because the default theme's
+ // tabs are huge:
+ if (client.platform == "Mac")
+ window.resizeBy(140, 0);
+
+ // Center window.
+ if (("arguments" in window) && (0 in window.arguments))
+ {
+ var ow = window.arguments[0];
+
+ window.moveTo(ow.screenX + Math.max((ow.outerWidth - window.outerWidth ) / 2, 0),
+ ow.screenY + Math.max((ow.outerHeight - window.outerHeight) / 2, 0));
+ }
+}
+
+/* Closing the window. Clean up. */
+PrefWindow.prototype.onClose =
+function pwin_onClose()
+{
+ if (this.ownerClient)
+ delete this.ownerClient.configWindow;
+ if (this.loaded)
+ destroyPrefs();
+}
+
+/* OK button. */
+PrefWindow.prototype.onOK =
+function pwin_onOK()
+{
+ if (this.onApply())
+ window.close();
+ return true;
+}
+
+/* Apply button. */
+PrefWindow.prototype.onApply =
+function pwin_onApply()
+{
+ // If the load failed, better not to try to save.
+ if (!this.loaded)
+ return false;
+
+ try {
+ // Get an array of all the (XUL) items we have to save.
+ var list = getPrefTags();
+
+ //if (!confirm("There are " + list.length + " pref tags to save. OK?")) return false;
+
+ for (var i = 0; i < list.length; i++)
+ {
+ // Save this one pref...
+ var index = list[i].getAttribute("prefobjectindex");
+ var name = list[i].getAttribute("prefname");
+ // Get the private data for the object out, since everything is
+ // based on this.
+ var po = this.prefObjects.getObject(index);
+ var pref = po.prefs[name];
+
+ var value;
+ // We just need to force the value from the DOMString form to
+ // the right form, so we don't get anything silly happening.
+ switch (pref.type)
+ {
+ case "string":
+ value = list[i].value;
+ break;
+ case "number":
+ value = 1 * list[i].value;
+ break;
+ case "boolean":
+ value = list[i].checked;
+ break;
+ case "array":
+ var l = new Array();
+ for (var j = 0; j < list[i].childNodes.length; j++)
+ l.push(list[i].childNodes[j].value);
+ value = l;
+ break;
+ default:
+ throw "Unknown pref type: " + pref.type + "!";
+ }
+ /* Fun stuff. We don't want to save if the user hasn't changed
+ * it. This is so that if the default is defered, and changed,
+ * but this hasn't, we keep the defered default. Which is a
+ * Good Thing. :)
+ */
+ if (((pref.type != "array") && (value != pref.startVal)) ||
+ ((pref.type == "array") &&
+ (value.join(";") != pref.startVal.join(";"))))
+ {
+ po.parent.prefs[pref.name] = value;
+ }
+ // Update our saved value, so the above check works the 2nd
+ // time the user clicks Apply.
+ pref.val = value;
+ pref.startVal = pref.val;
+ }
+
+ return true;
+ }
+ catch (e)
+ {
+ alert(getMsg(MSG_PREFS_ERR_SAVE, e));
+ return false;
+ }
+}
+
+/* Cancel button. */
+PrefWindow.prototype.onCancel =
+function pwin_onCancel()
+{
+ window.close();
+ return true;
+}
+
+/** Tooltips' event handlers **/
+
+PrefWindow.prototype.onPrefMouseOver =
+function pwin_onPrefMouseOver(object)
+{
+ this.tooltipObject = object;
+ this.tooltipTitle = object.getAttribute("tooltiptitle");
+ this.tooltipText = object.getAttribute("tooltipcontent");
+ // Reset the timer now we're over a new pref.
+ clearTimeout(this.tooltipTimer);
+ this.tooltipTimer = setTimeout(setTooltipState, this.tooltipShowDelay,
+ this, true);
+}
+
+PrefWindow.prototype.onPrefMouseMove =
+function pwin_onPrefMouseMove(object)
+{
+ // If the tooltip isn't showing, we need to reset the timer.
+ if (!this.tooltipShowing)
+ {
+ clearTimeout(this.tooltipTimer);
+ this.tooltipTimer = setTimeout(setTooltipState, this.tooltipShowDelay,
+ this, true);
+ }
+}
+
+PrefWindow.prototype.onPrefMouseOut =
+function pwin_onPrefMouseOut(object)
+{
+ // Left the pref! Hide tooltip, and clear timer.
+ this.setTooltipState(false);
+ clearTimeout(this.tooltipTimer);
+}
+
+PrefWindow.prototype.onTooltipPopupShowing =
+function pwin_onTooltipPopupShowing(popup)
+{
+ if (!this.tooltipText)
+ return false;
+
+ var fChild = popup.firstChild;
+ var diff = popup.boxObject.height - fChild.boxObject.height;
+
+ // Setup the labels...
+ var ttt = document.getElementById("czPrefTipTitle");
+ ttt.firstChild.nodeValue = this.tooltipTitle;
+ var ttl = document.getElementById("czPrefTipLabel");
+ ttl.firstChild.nodeValue = this.tooltipText;
+
+ /* In Gecko 1.9, the popup has done no layout at this point, unlike in
+ * earlier versions. As a result, the box object of all the elements
+ * within it are 0x0. It also means the height of the labels isn't
+ * updated. To deal with this, we avoid calling sizeTo with the box
+ * object (as it's 0) and instead just force the popup height to 0 -
+ * otherwise it will only ever get bigger each time, never smaller.
+ */
+ if (popup.boxObject.width == 0)
+ this.tooltipBug418798 = true;
+
+ if (this.tooltipBug418798)
+ popup.height = 0;
+ else
+ popup.sizeTo(popup.boxObject.width, fChild.boxObject.height + diff);
+
+ return true;
+}
+
+/** Components' event handlers **/
+
+// Selected an item in the tree.
+PrefWindow.prototype.onSelectObject =
+function pwin_onSelectObject()
+{
+ var prefTree = document.getElementById("pref-tree-object");
+ var rv = new Object();
+ if ("selection" in prefTree.treeBoxObject)
+ prefTree.treeBoxObject.selection.getRangeAt(0, rv, {});
+ else
+ prefTree.view.selection.getRangeAt(0, rv, {});
+ var prefTreeIndex = rv.value;
+
+ var delButton = document.getElementById("object-delete");
+ if (prefTreeIndex > 0)
+ delButton.removeAttribute("disabled");
+ else
+ delButton.setAttribute("disabled", "true");
+
+ var prefItem = prefTree.contentView.getItemAtIndex(prefTreeIndex);
+
+ var pObjectIndex = prefItem.getAttribute("prefobjectindex");
+ var pObject = this.prefObjects.getObject(pObjectIndex);
+
+ if (!ASSERT(pObject, "Object not found for index! " +
+ prefItem.getAttribute("prefobjectindex")))
+ return;
+
+ pObject.loadXUL(this.prefTabOrder);
+
+ var header = document.getElementById("pref-header");
+ header.setAttribute("title", getMsg(MSG_PREFS_FMT_HEADER,
+ pObject.parent.prettyName));
+
+ var deck = document.getElementById("pref-object-deck");
+ deck.selectedIndex = pObject.deckIndex;
+
+ this.currentObject = pObject;
+}
+
+// Browse button for file prefs.
+PrefWindow.prototype.onPrefBrowse =
+function pwin_onPrefBrowse(button)
+{
+ var spec = "$all";
+ if (button.hasAttribute("spec"))
+ spec = button.getAttribute("spec") + " " + spec;
+
+ var type = button.getAttribute("kind");
+ var edit = button.previousSibling.lastChild;
+
+ var rv;
+ if (type == "folder")
+ {
+ try
+ {
+ // if the user set the pref to an invalid folder, this will throw:
+ var current = getFileFromURLSpec(edit.value);
+ }
+ catch (ex)
+ {
+ // Just setting it to null will work:
+ current = null;
+ }
+ rv = pickGetFolder(MSG_PREFS_BROWSE_TITLE, current);
+ }
+ else
+ {
+ rv = pickOpen(MSG_PREFS_BROWSE_TITLE, spec);
+ }
+
+ if (!rv.ok)
+ return;
+
+ edit.value = (type == "file") ? rv.file.path : rv.picker.fileURL.spec;
+},
+
+// Selection changed on listbox.
+PrefWindow.prototype.onPrefListSelect =
+function pwin_onPrefListSelect(object)
+{
+ var list = getRelatedItem(object, "list");
+ var buttons = new Object();
+ buttons.up = getRelatedItem(object, "button-up");
+ buttons.down = getRelatedItem(object, "button-down");
+ buttons.add = getRelatedItem(object, "button-add");
+ buttons.edit = getRelatedItem(object, "button-edit");
+ buttons.del = getRelatedItem(object, "button-delete");
+
+ if (("selectedItems" in list) && list.selectedItems &&
+ list.selectedItems.length)
+ {
+ buttons.edit.removeAttribute("disabled");
+ buttons.del.removeAttribute("disabled");
+ }
+ else
+ {
+ buttons.edit.setAttribute("disabled", "true");
+ buttons.del.setAttribute("disabled", "true");
+ }
+
+ if (!("selectedItems" in list) || !list.selectedItems ||
+ list.selectedItems.length == 0 || list.selectedIndex == 0)
+ {
+ buttons.up.setAttribute("disabled", "true");
+ }
+ else
+ {
+ buttons.up.removeAttribute("disabled");
+ }
+
+ if (!("selectedItems" in list) || !list.selectedItems ||
+ list.selectedItems.length == 0 ||
+ list.selectedIndex == list.childNodes.length - 1)
+ {
+ buttons.down.setAttribute("disabled", "true");
+ }
+ else
+ {
+ buttons.down.removeAttribute("disabled");
+ }
+}
+
+// Up button for lists.
+PrefWindow.prototype.onPrefListUp =
+function pwin_onPrefListUp(object)
+{
+ var list = getRelatedItem(object, "list");
+
+ var selected = list.selectedItems[0];
+ var before = selected.previousSibling;
+ if (before)
+ {
+ before.parentNode.insertBefore(selected, before);
+ list.selectItem(selected);
+ list.ensureElementIsVisible(selected);
+ }
+}
+
+// Down button for lists.
+PrefWindow.prototype.onPrefListDown =
+function pwin_onPrefListDown(object)
+{
+ var list = getRelatedItem(object, "list");
+
+ var selected = list.selectedItems[0];
+ if (selected.nextSibling)
+ {
+ if (selected.nextSibling.nextSibling)
+ list.insertBefore(selected, selected.nextSibling.nextSibling);
+ else
+ list.appendChild(selected);
+
+ list.selectItem(selected);
+ }
+}
+
+// Add button for lists.
+PrefWindow.prototype.onPrefListAdd =
+function pwin_onPrefListAdd(object)
+{
+ var list = getRelatedItem(object, "list");
+ var newItem;
+
+ switch (list.getAttribute("kind"))
+ {
+ case "url":
+ var item = prompt(MSG_PREFS_LIST_ADD);
+ if (item)
+ {
+ newItem = document.createElement("listitem");
+ newItem.setAttribute("label", item);
+ newItem.value = item;
+ list.appendChild(newItem);
+ this.onPrefListSelect(object);
+ }
+ break;
+ case "file":
+ case "fileurl":
+ var spec = "$all";
+
+ var rv = pickOpen(MSG_PREFS_BROWSE_TITLE, spec);
+ if (rv.ok)
+ {
+ var data = { file: rv.file.path, fileurl: rv.picker.fileURL.spec };
+ var kind = list.getAttribute("kind");
+
+ newItem = document.createElement("listitem");
+ newItem.setAttribute("label", data[kind]);
+ newItem.value = data[kind];
+ list.appendChild(newItem);
+ this.onPrefListSelect(object);
+ }
+
+ break;
+ }
+}
+
+// Edit button for lists.
+PrefWindow.prototype.onPrefListEdit =
+function pwin_onPrefListEdit(object)
+{
+ var list = getRelatedItem(object, "list");
+
+ switch (list.getAttribute("kind"))
+ {
+ case "url":
+ case "file":
+ case "fileurl":
+ // We're letting the user edit file types here, since it saves us
+ // a lot of work, and we can't let them pick a file OR a directory,
+ // so they pick a file and can edit it off to use a directory.
+ var listItem = list.selectedItems[0];
+ var newValue = prompt(MSG_PREFS_LIST_EDIT, listItem.value);
+ if (newValue)
+ {
+ listItem.setAttribute("label", newValue);
+ listItem.value = newValue;
+ }
+ break;
+ }
+}
+
+// Delete button for lists.
+PrefWindow.prototype.onPrefListDelete =
+function pwin_onPrefListDelete(object)
+{
+ var list = getRelatedItem(object, "list");
+
+ var listItem = list.selectedItems[0];
+ if (confirm(getMsg(MSG_PREFS_LIST_DELETE, listItem.value)))
+ list.removeChild(listItem);
+}
+
+/* Add... button. */
+PrefWindow.prototype.onAddObject =
+function pwin_onAddObject()
+{
+ var rv = new Object();
+
+ /* Try to nobble the current selection and pre-fill as needed. */
+ switch (this.currentObject.parent.TYPE)
+ {
+ case "PrefNetwork":
+ rv.type = "net";
+ rv.net = this.currentObject.parent.unicodeName;
+ break;
+ case "PrefChannel":
+ rv.type = "chan";
+ rv.net = this.currentObject.parent.parent.parent.unicodeName;
+ rv.chan = this.currentObject.parent.unicodeName;
+ break;
+ case "PrefUser":
+ rv.type = "user";
+ rv.net = this.currentObject.parent.parent.parent.unicodeName;
+ rv.chan = this.currentObject.parent.unicodeName;
+ break;
+ }
+
+ // Show add dialog, passing the data object along.
+ window.openDialog("config-add.xul", "cz-config-add", "chrome,dialog,modal", rv);
+
+ if (!rv.ok)
+ return;
+
+ /* Ok, so what type did they want again?
+ *
+ * NOTE: The param |true| in the object creation calls is for |force|. It
+ * causes the hidden pref to be set for the objects so they are shown
+ * every time this window opens, until the user deletes them.
+ */
+ switch (rv.type)
+ {
+ case "net":
+ this.prefObjects.addObject(new PrefNetwork(client, rv.net, true));
+ break;
+ case "chan":
+ if (!(":" + rv.net in client.networks))
+ this.prefObjects.addObject(new PrefNetwork(client, rv.net, true));
+ this.prefObjects.addObject(new PrefChannel(client.networks[":" + rv.net].primServ, rv.chan, true));
+ break;
+ case "user":
+ if (!(":" + rv.net in client.networks))
+ this.prefObjects.addObject(new PrefNetwork(client, rv.net, true));
+ this.prefObjects.addObject(new PrefUser(client.networks[":" + rv.net].primServ, rv.chan, true));
+ break;
+ default:
+ // Oops. Not good, if we got here.
+ alert("Unknown pref type: " + rv.type);
+ }
+}
+
+/* Delete button. */
+PrefWindow.prototype.onDeleteObject =
+function pwin_onDeleteObject()
+{
+ // Save current node before we re-select.
+ var sel = this.currentObject;
+
+ // Check they want to go ahead.
+ if (!confirm(getMsg(MSG_PREFS_OBJECT_DELETE, sel.parent.unicodeName)))
+ return;
+
+ // Select a new item BEFORE removing the current item, so the <tree>
+ // doesn't freak out on us.
+ var prefTree = document.getElementById("pref-tree-object");
+ if ("selection" in prefTree.treeBoxObject)
+ prefTree.treeBoxObject.selection.select(0);
+ else
+ prefTree.view.selection.select(0);
+
+ // If it's a network, nuke all the channels and users too.
+ if (sel.parent.TYPE == "PrefNetwork")
+ {
+ var chans = sel.parent.channels;
+ for (k in chans)
+ PrefObjectList.getPrivateData(chans[k]).clear();
+
+ var users = sel.parent.users;
+ for (k in users)
+ PrefObjectList.getPrivateData(users[k]).clear();
+ }
+ sel.clear();
+
+ this.onSelectObject();
+}
+
+/* Reset button. */
+PrefWindow.prototype.onResetObject =
+function pwin_onResetObject()
+{
+ // Save current node before we re-select.
+ var sel = this.currentObject;
+
+ // Check they want to go ahead.
+ if (!confirm(getMsg(MSG_PREFS_OBJECT_RESET, sel.parent.unicodeName)))
+ return;
+
+ // Reset the prefs.
+ sel.reset();
+}
+
+// End of PrefWindow. //
+
+/*** Base functions... ***/
+
+/* Gets a "related" items, such as the buttons associated with a list. */
+function getRelatedItem(object, thing)
+{
+ switch (object.nodeName)
+ {
+ case "listbox":
+ switch (thing) {
+ case "list":
+ return object;
+ case "button-up":
+ return object.parentNode.nextSibling.childNodes[0];
+ case "button-down":
+ return object.parentNode.nextSibling.childNodes[1];
+ case "button-add":
+ return object.parentNode.nextSibling.childNodes[3];
+ case "button-edit":
+ return object.parentNode.nextSibling.childNodes[4];
+ case "button-delete":
+ return object.parentNode.nextSibling.childNodes[5];
+ }
+ break;
+ case "button":
+ var n = object.parentNode.previousSibling.lastChild;
+ if (n)
+ return getRelatedItem(n, thing);
+ break;
+ }
+ return null;
+}
+
+// Wrap this call so we have the right |this|.
+function setTooltipState(w, s)
+{
+ w.setTooltipState(s);
+}
+
+// Reverses the Pref Manager's munging of network names.
+function unMungeNetworkName(name)
+{
+ name = ecmaUnescape(name);
+ return name.replace(/_/g, ":").replace(/-/g, ".");
+}
+
+// Adds a button to a container, setting up the command in a simple way.
+function appendButton(cont, oncommand, attr)
+{
+ var btn = document.createElement("button");
+ if (attr)
+ for (var a in attr)
+ btn.setAttribute(a, attr[a]);
+ if (oncommand)
+ btn.setAttribute("oncommand", "gPrefWindow." + oncommand + "(this);");
+ else
+ btn.setAttribute("disabled", "true");
+ cont.appendChild(btn);
+}
+
+// Like appendButton, but just drops in a separator.
+function appendSeparator(cont, attr)
+{
+ var spacer = document.createElement("separator");
+ if (attr)
+ for (var a in attr)
+ spacer.setAttribute(a, attr[a]);
+ cont.appendChild(spacer);
+}
+
+/* This simply collects together all the <textbox>, <checkbox> and <listbox>
+ * elements that have the attribute "prefname". Thus, we generate a list of
+ * all elements that are for prefs.
+ */
+function getPrefTags()
+{
+ var rv = new Array();
+ var i, list;
+
+ list = document.getElementsByTagName("textbox");
+ for (i = 0; i < list.length; i++)
+ {
+ if (list[i].hasAttribute("prefname"))
+ rv.push(list[i]);
+ }
+ list = document.getElementsByTagName("checkbox");
+ for (i = 0; i < list.length; i++)
+ {
+ if (list[i].hasAttribute("prefname"))
+ rv.push(list[i]);
+ }
+ list = document.getElementsByTagName("listbox");
+ for (i = 0; i < list.length; i++)
+ {
+ if (list[i].hasAttribute("prefname"))
+ rv.push(list[i]);
+ }
+
+ return rv;
+}
+
+// Sets up the "extra1" button (Apply).
+function setupButtons()
+{
+ // Hacky-hacky-hack. Looks like the new toolkit does provide a solution,
+ // but we need to support SeaMonkey too. :)
+
+ var dialog = document.documentElement;
+ dialog.getButton("extra1").label = dialog.getAttribute("extra1Label");
+}
+
+// And finally, we want one of these.
+var gPrefWindow = new PrefWindow();