gecko-dev/mobile/chrome/content/config.js
Geoff Lankow 7a41cf6cbc Bug 653637 - Provide a simple way for addons to have preferences UI - Part 1; r=mark.finkle,dtownsend
--HG--
rename : mobile/chrome/content/bindings/setting.xml => toolkit/mozapps/extensions/content/setting.xml
2011-06-03 11:33:55 +12:00

422 lines
13 KiB
JavaScript

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Mobile Browser.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
let Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/Services.jsm");
var ViewConfig = {
get _container() {
delete this._container;
return this._container = document.getElementById("prefs-container");
},
get _editor() {
delete this._editor;
return this._editor = document.getElementById("editor");
},
init: function init() {
window.addEventListener("resize", this, false);
window.addEventListener("prefchange", this, false);
window.addEventListener("prefnew", this, false);
this._handleWindowResize();
this.filter("");
document.getElementById("textbox").focus();
},
uninit: function uninit() {
window.removeEventListener("resize", this, false);
window.removeEventListener("prefchange", this, false);
window.removeEventListener("prefnew", this, false);
},
filter: function filter(aValue) {
let row = document.getElementById("editor-row");
row.setAttribute("hidden", aValue != "");
let container = this._container;
container.scrollBoxObject.scrollTo(0, 0);
// Clear the list by replacing with a shallow copy
let empty = container.cloneNode(false);
empty.appendChild(row);
container.parentNode.replaceChild(empty, container);
this._container = empty;
let result = Utils.getPrefs(aValue);
this._container.setItems(result.map(this._createItem, this));
},
open: function open(aType) {
let buttons = document.getElementById("editor-buttons-add");
buttons.setAttribute("hidden", "true");
let shouldFocus = false;
let setting = document.getElementById("editor-setting");
switch (aType) {
case Ci.nsIPrefBranch.PREF_INT:
setting.setAttribute("type", "integer");
break;
case Ci.nsIPrefBranch.PREF_BOOL:
setting.setAttribute("type", "bool");
break;
case Ci.nsIPrefBranch.PREF_STRING:
setting.setAttribute("type", "string");
break;
}
setting.removeAttribute("title");
setting.removeAttribute("pref");
if (setting.input)
setting.input.value = "";
document.getElementById("editor-container").appendChild(this._editor);
let nameField = document.getElementById("editor-name");
nameField.value = "";
this._editor.setAttribute("hidden", "false");
this._currentItem = null;
nameField.focus();
},
close: function close(aValid) {
this._editor.setAttribute("hidden", "true");
let buttons = document.getElementById("editor-buttons-add");
buttons.setAttribute("hidden", "false");
if (aValid) {
let name = document.getElementById("editor-name").inputField.value;
if (name != "") {
let setting = document.getElementById("editor-setting");
setting.setAttribute("pref", name);
setting.valueToPreference();
}
}
document.getElementById("editor-container").appendChild(this._editor);
},
_currentItem: null,
delayEdit: function(aItem) {
setTimeout(this.edit.bind(this), 0, aItem);
},
edit: function(aItem) {
if (!aItem)
return;
let pref = Utils.getPref(aItem.getAttribute("name"));
if (pref.lock || !pref.name || aItem == this._currentItem)
return;
this.close(false);
this._currentItem = aItem;
let setting = document.getElementById("editor-setting");
let shouldFocus = false;
switch (pref.type) {
case Ci.nsIPrefBranch.PREF_BOOL:
setting.setAttribute("type", "bool");
break;
case Ci.nsIPrefBranch.PREF_INT:
setting.setAttribute("type", "integer");
setting.setAttribute("increment", this.getIncrementForValue(pref.value));
shouldFocus = true;
break;
case Ci.nsIPrefBranch.PREF_STRING:
setting.setAttribute("type", "string");
shouldFocus = true;
break;
}
setting.setAttribute("title", pref.name);
setting.setAttribute("pref", pref.name);
this._container.insertBefore(this._editor, aItem);
let resetButton = document.getElementById("editor-reset");
resetButton.setAttribute("disabled", pref.default);
this._editor.setAttribute("default", pref.default);
this._editor.setAttribute("hidden", "false");
if (shouldFocus && setting.input)
setting.input.focus();
},
reset: function reset(aItem) {
let setting = document.getElementById("editor-setting");
let pref = Utils.getPref(setting.getAttribute("pref"));
if (!pref.default)
Utils.resetPref(pref.name);
},
handleEvent: function handleEvent(aEvent) {
switch (aEvent.type) {
case "resize":
this._handleWindowResize();
break;
case "prefchange":
case "prefnew":
this._handlePrefChange(aEvent.detail, aEvent.type == "prefnew");
break;
}
},
_handleWindowResize: function _handleWindowResize() {
let mainBox = document.getElementById("main-container");
let textbox = document.getElementById("textbox");
let height = window.innerHeight - textbox.getBoundingClientRect().height;
mainBox.setAttribute("height", height);
},
_handlePrefChange: function _handlePrefChange(aIndex, aNew) {
let isEditing = !this._editor.hidden;
let shouldUpdateEditor = false;
if (isEditing) {
let setting = document.getElementById("editor-setting");
let editorIndex = Utils.getPrefIndex(setting.getAttribute("pref"));
shouldUpdateEditor = (aIndex == editorIndex);
if(shouldUpdateEditor || aIndex > editorIndex)
aIndex += 1;
}
// XXX An item display value will probably fail if a pref is changed in the
// background while there is a filter on the pref
let item = shouldUpdateEditor ? this._editor.nextSibling
: this._container.childNodes[aIndex + 1];// add 1 because of the new pref row
if (!item) // the pref is not viewable
return;
if (aNew) {
let pref = Utils.getPrefByIndex(aIndex);
let row = this._createItem(pref);
this._container.insertBefore(row, item);
return;
}
let pref = Utils.getPref(item.getAttribute("name"));
if (shouldUpdateEditor) {
this._editor.setAttribute("default", pref.default);
let resetButton = document.getElementById("editor-reset");
resetButton.disabled = pref.default;
}
item.setAttribute("default", pref.default);
item.lastChild.setAttribute("value", pref.value);
},
_createItem: function _createItem(aPref) {
let row = document.createElement("richlistitem");
row.setAttribute("name", aPref.name);
row.setAttribute("type", aPref.type);
row.setAttribute("role", "button");
row.setAttribute("default", aPref.default);
let label = document.createElement("label");
label.setAttribute("class", "preferences-title");
label.setAttribute("value", aPref.name);
label.setAttribute("crop", "end");
row.appendChild(label);
label = document.createElement("label");
label.setAttribute("class", "preferences-value");
label.setAttribute("value", aPref.value);
label.setAttribute("crop", "end");
row.appendChild(label);
return row;
},
getIncrementForValue: function getIncrementForValue(aValue) {
let count = 1;
while (aValue >= 100) {
aValue /= 10;
count *= 10;
}
return count;
}
};
var Utils = {
QueryInterface: function(aIID) {
if (!aIID.equals(Ci.nsIObserver) && !aIID.equals(Ci.nsISupportsWeakReference))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
},
get _branch() {
delete this._branch;
this._branch = Services.prefs.getBranch(null).QueryInterface(Ci.nsIPrefBranch2);
this._branch.addObserver("", this, true);
return this._branch;
},
get _preferences() {
delete this._preferences;
let list = this._branch.getChildList("", {}).filter(function(element) {
return !(/^capability\./.test(element));
});
return this._preferences = list.sort().map(this.getPref, this);
},
getPrefs: function getPrefs(aValue) {
let result = this._preferences.slice();;
if (aValue != "") {
let reg = this._generateRegexp(aValue);
if (!reg)
return [];
result = this._preferences.filter(function(element, index, array) {
return reg.test(element.name + ";" + element.value);
});
}
return result;
},
getPref: function getPref(aPrefName) {
let branch = this._branch;
let pref = {
name: aPrefName,
value: "",
default: !branch.prefHasUserValue(aPrefName),
lock: branch.prefIsLocked(aPrefName),
type: branch.getPrefType(aPrefName)
};
try {
switch (pref.type) {
case Ci.nsIPrefBranch.PREF_BOOL:
pref.value = branch.getBoolPref(aPrefName).toString();
break;
case Ci.nsIPrefBranch.PREF_INT:
pref.value = branch.getIntPref(aPrefName).toString();
break;
default:
case Ci.nsIPrefBranch.PREF_STRING:
pref.value = branch.getComplexValue(aPrefName, Ci.nsISupportsString).data;
// Try in case it's a localized string (will throw an exception if not)
if (pref.default && /^chrome:\/\/.+\/locale\/.+\.properties/.test(pref.value))
pref.value = branch.getComplexValue(aPrefName, Ci.nsIPrefLocalizedString).data;
break;
}
} catch (e) {}
return pref;
},
getPrefByIndex: function getPrefByIndex(aIndex) {
return this._preferences[aIndex];
},
getPrefIndex: function getPrefIndex(aPrefName) {
let prefs = this._preferences;
let high = prefs.length - 1;
let low = 0, middle, element;
while (low <= high) {
middle = parseInt((low + high) / 2);
element = prefs[middle];
if (element.name > aPrefName)
high = middle - 1;
else if (element.name < aPrefName)
low = middle + 1;
else
return middle;
}
return -1;
},
resetPref: function resetPref(aPrefName) {
this._branch.clearUserPref(aPrefName);
},
observe: function observe(aSubject, aTopic, aPrefName) {
if (aTopic != "nsPref:changed" || /^capability\./.test(aPrefName)) // avoid displaying "private" preferences
return;
let type = "prefchange";
let index = this.getPrefIndex(aPrefName);
if (index != - 1) {
// update the inner array
let pref = this.getPref(aPrefName);
this._preferences[index].value = pref.value;
}
else {
// XXX we could do better here
let list = this._branch.getChildList("", {}).filter(function(element, index, array) {
return !(/^capability\./.test(element));
});
this._preferences = list.sort().map(this.getPref, this);
type = "prefnew";
index = this.getPrefIndex(aPrefName);
}
let evt = document.createEvent("UIEvents");
evt.initUIEvent(type, true, true, window, index);
window.dispatchEvent(evt);
},
_generateRegexp: function _generateRegexp(aValue) {
if (aValue.charAt(0) == "/") {
try {
let rv = aValue.match(/^\/(.*)\/(i?)$/);
return RegExp(rv[1], rv[2]);
}
catch (e) {
return null; // Do nothing on incomplete or bad RegExp
}
}
return RegExp(aValue.replace(/([^* \w])/g, "\\$1").replace(/^\*+/, "")
.replace(/\*+/g, ".*"), "i");
}
};