diff --git a/chrome.manifest b/chrome.manifest
index 75423120..f1d7e455 100644
--- a/chrome.manifest
+++ b/chrome.manifest
@@ -1,5 +1,6 @@
# Firefox
-content vimperator content/
-locale vimperator en-US locale/en-US/
+content vimperator content/
+resource vimperator content/
+locale vimperator en-US locale/en-US/
skin vimperator classic/1.0 skin/
overlay chrome://browser/content/browser.xul chrome://vimperator/content/vimperator.xul
diff --git a/content/bookmarks.js b/content/bookmarks.js
index 969a41df..742bbc86 100644
--- a/content/bookmarks.js
+++ b/content/bookmarks.js
@@ -790,15 +790,8 @@ liberator.QuickMarks = function () //{{{
////////////////////// PRIVATE SECTION /////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
- var qmarks = {};
- // TODO: move to a storage module
- var savedMarks = liberator.options.getPref("extensions.vimperator.quickmarks", "").split("\n");
-
- // load the saved quickmarks
- for (var i = 0; i < savedMarks.length - 1; i += 2)
- {
- qmarks[savedMarks[i]] = savedMarks[i + 1];
- }
+ liberator.storage.newObject("quickmarks", true);
+ var qmarks = liberator.storage.quickmarks;
/////////////////////////////////////////////////////////////////////////////}}}
////////////////////// MAPPINGS ////////////////////////////////////////////////
@@ -899,28 +892,28 @@ liberator.QuickMarks = function () //{{{
add: function (qmark, location)
{
- qmarks[qmark] = location;
+ qmarks.set(qmark, location);
},
remove: function (filter)
{
var pattern = new RegExp("[" + filter.replace(/\s+/g, "") + "]");
- for (var qmark in qmarks)
+ for (var [qmark,] in qmarks)
{
if (pattern.test(qmark))
- delete qmarks[qmark];
+ qmarks.remove(qmark);
}
},
removeAll: function ()
{
- qmarks = {};
+ qmarks.clear();
},
jumpTo: function (qmark, where)
{
- var url = qmarks[qmark];
+ var url = qmarks.get(qmark);
if (url)
liberator.open(url, where);
@@ -934,7 +927,7 @@ liberator.QuickMarks = function () //{{{
var uppercaseMarks = [];
var numberMarks = [];
- for (var qmark in qmarks)
+ for (var [qmark,] in qmarks)
{
if (/[a-z]/.test(qmark))
lowercaseMarks.push(qmark);
@@ -972,28 +965,13 @@ liberator.QuickMarks = function () //{{{
for (var i = 0; i < marks.length; i++)
{
list += "
| " + marks[i] +
- " | " + liberator.util.escapeHTML(qmarks[marks[i]]) + " |
";
+ "" + liberator.util.escapeHTML(qmarks.get(marks[i])) + " | ";
}
list += "";
liberator.commandline.echo(list, liberator.commandline.HL_NORMAL, liberator.commandline.FORCE_MULTILINE);
},
-
- destroy: function ()
- {
- // save the quickmarks
- var savedQuickMarks = "";
-
- for (var i in qmarks)
- {
- savedQuickMarks += i + "\n";
- savedQuickMarks += qmarks[i] + "\n";
- }
-
- liberator.options.setPref("extensions.vimperator.quickmarks", savedQuickMarks);
- }
-
};
//}}}
}; //}}}
diff --git a/content/liberator.js b/content/liberator.js
index 4d9f28f1..a6bb08df 100644
--- a/content/liberator.js
+++ b/content/liberator.js
@@ -1095,12 +1095,11 @@ const liberator = (function () //{{{
{
liberator.autocommands.trigger(liberator.config.name + "LeavePre", "");
+ liberator.storage.saveAll();
+
// save our preferences
- liberator.commandline.destroy();
liberator.options.destroy();
liberator.events.destroy();
- if (liberator.has("quickmarks"))
- liberator.quickmarks.destroy();
if (liberator.has("bookmarks"))
liberator.bookmarks.destroy();
@@ -1167,6 +1166,8 @@ const liberator = (function () //{{{
//}}}
})(); //}}}
+Components.utils.import("resource://vimperator/storage.jsi", liberator);
+
// called when the chrome is fully loaded and before the main window is shown
window.addEventListener("load", liberator.startup, false);
window.addEventListener("unload", liberator.shutdown, false);
diff --git a/content/options.js b/content/options.js
index cc447f20..af9d07ba 100644
--- a/content/options.js
+++ b/content/options.js
@@ -36,17 +36,17 @@ liberator.Option = function (names, description, type, defaultValue, extraInfo)
if (!extraInfo)
extraInfo = {};
- var value = null;
-
- this.name = names[0];
+ let cannonName = names[0];
+ this.name = cannonName;
this.names = names;
this.type = type;
- this.scope = (extraInfo.scope & liberator.options.OPTION_SCOPE_BOTH) || liberator.options.OPTION_SCOPE_GLOBAL; // XXX set to BOTH by default someday? - kstep
+ this.scope = (extraInfo.scope & liberator.options.OPTION_SCOPE_BOTH) ||
+ liberator.options.OPTION_SCOPE_GLOBAL;
+ // XXX set to BOTH by default someday? - kstep
this.description = description || "";
// "", 0 are valid default values
this.defaultValue = (defaultValue === undefined) ? null : defaultValue;
- value = this.defaultValue;
this.setter = extraInfo.setter || null;
this.getter = extraInfo.getter || null;
@@ -68,6 +68,10 @@ liberator.Option = function (names, description, type, defaultValue, extraInfo)
}
}
+ this.__defineGetter__("globalvalue", function() liberator.options.store.get(cannonName));
+ this.__defineSetter__("globalvalue", function(val) liberator.options.store.set(cannonName, val));
+ this.globalvalue = this.defaultValue;
+
this.get = function (scope)
{
if (scope)
@@ -85,7 +89,7 @@ liberator.Option = function (names, description, type, defaultValue, extraInfo)
if (liberator.has("tabs") && (scope & liberator.options.OPTION_SCOPE_LOCAL))
aValue = liberator.tabs.options[this.name];
if ((scope & liberator.options.OPTION_SCOPE_GLOBAL) && (aValue == undefined))
- aValue = value;
+ aValue = this.globalvalue;
if (this.getter)
this.getter.call(this, aValue);
@@ -120,7 +124,7 @@ liberator.Option = function (names, description, type, defaultValue, extraInfo)
if (liberator.has("tabs") && (scope & liberator.options.OPTION_SCOPE_LOCAL))
liberator.tabs.options[this.name] = newValue;
if (scope & liberator.options.OPTION_SCOPE_GLOBAL)
- value = newValue;
+ this.globalvalue = newValue;
this.hasChanged = true;
};
@@ -131,7 +135,7 @@ liberator.Option = function (names, description, type, defaultValue, extraInfo)
this.hasName = function (name)
{
- return this.names.some(function (e) { return e == name; });
+ return this.names.indexOf(name) >= 0;
};
this.isValidValue = function (value)
@@ -155,6 +159,8 @@ liberator.Options = function () //{{{
////////////////////// PRIVATE SECTION /////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
+ liberator.storage.newObject("options", false);
+
var prefService = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
var options = [];
@@ -768,8 +774,6 @@ liberator.Options = function () //{{{
{
for (var i = 0; i < options.length; i++)
yield options[i];
-
- throw StopIteration;
},
add: function (names, description, type, defaultValue, extraInfo)
@@ -782,9 +786,9 @@ liberator.Options = function () //{{{
if (!option)
return false;
- for (var i = 0; i < options.length; i++)
+ for (var opt in Iterator(this))
{
- if (options[i].name == option.name)
+ if (opt.name == option.name)
{
// never replace for now
liberator.log("Warning: '" + names[0] + "' already exists, NOT replacing existing option.", 1);
@@ -832,19 +836,19 @@ liberator.Options = function () //{{{
if (!scope)
scope = liberator.options.OPTION_SCOPE_BOTH;
- for (var i = 0; i < options.length; i++)
+ for (var opt in Iterator(this))
{
- name = options[i].name;
- value = options[i].value;
- def = options[i].defaultValue;
+ name = opt.name;
+ value = opt.value;
+ def = opt.defaultValue;
if (onlyNonDefault && value == def)
continue;
- if (!(options[i].scope & scope))
+ if (!(opt.scope & scope))
continue;
- if (options[i].type == "boolean")
+ if (opt.type == "boolean")
{
name = value ? " " + name : "no" + name;
if (value != def)
@@ -882,12 +886,12 @@ liberator.Options = function () //{{{
" Options ---";
var name, value, defaultValue;
- for (var i = 0; i < prefArray.length; i++)
+ prefArray.forEach(function (pref)
{
- var userValue = prefService.prefHasUserValue(prefArray[i]);
- if ((!onlyNonDefault || userValue) && prefArray[i].indexOf(filter) >= 0)
+ var userValue = prefService.prefHasUserValue(pref);
+ if ((!onlyNonDefault || userValue) && pref.indexOf(filter) >= 0)
{
- name = prefArray[i];
+ name = pref;
value = this.getPref(name);
if (typeof value == "string")
value = value.substr(0, 100).replace(/\n/g, " ");
@@ -907,11 +911,13 @@ liberator.Options = function () //{{{
else
list += "| " + name + "=" + value + " |
";
}
- }
+ });
list += "";
liberator.commandline.echo(list, liberator.commandline.HL_NORMAL, liberator.commandline.FORCE_MULTILINE);
},
+ get store() liberator.storage.options,
+
getPref: function (name, forcedDefault)
{
return loadPreference(name, forcedDefault);
diff --git a/content/storage.jsi b/content/storage.jsi
new file mode 100644
index 00000000..aa7c6795
--- /dev/null
+++ b/content/storage.jsi
@@ -0,0 +1,236 @@
+/***** 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.
+
+(c) 2006-2008: Martin Stubenschrott
+
+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 *****/
+
+var EXPORTED_SYMBOLS = ["storage"];
+
+var prefService = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefBranch);
+var json = Components.classes["@mozilla.org/dom/json;1"]
+ .createInstance(Components.interfaces.nsIJSON);
+
+function getCharPref(name)
+{
+ try
+ {
+ var value = prefService.getComplexValue(name, Components.interfaces.nsISupportsString).data;
+ // try in case it's a localized string (will throw an exception if not)
+ if (!prefService.prefIsLocked(name) && !prefService.prefHasUserValue(name) &&
+ /^chrome:\/\/.+\/locale\/.+\.properties/.test(value))
+ value = branch.getComplexValue(name, Components.interfaces.nsIPrefLocalizedString).data;
+ return value;
+ }
+ catch (e)
+ {
+ return undefined;
+ }
+}
+
+function prefName(key) {
+ return "extensions.liberator.datastore." + key;
+}
+
+function loadPref(name, store, type)
+{
+ if (store)
+ var pref = getCharPref(prefName(name));
+ if (pref)
+ var obj = json.decode(pref);
+ if (obj instanceof type)
+ return obj;
+}
+
+var prototype = {
+ fireEvent: function(event, arg) { storage.fireEvent(this.name, event, arg) },
+ save: function()
+ {
+ if(this.store)
+ prefService.setCharPref(prefName(this.name), this.serial)
+ },
+};
+
+function ObjectStore(name, store)
+{
+ var object = loadPref(name, store, Object) || {};
+
+ this.__defineGetter__("store", function() store);
+ this.__defineGetter__("name", function() name);
+ this.__defineGetter__("serial", function() json.encode(object));
+
+ this.set = function set(key, val)
+ {
+ var orig = object[key];
+ object[key] = val;
+ if (orig == val)
+ this.fireEvent("change", key);
+ else
+ this.fireEvent("add", key);
+ };
+
+ this.remove = function remove(key)
+ {
+ var ret = object[key];
+ delete object[key];
+ this.fireEvent("remove", key);
+ return ret;
+ };
+
+ this.get = function get(val) object[val];
+
+ this.clear = function()
+ {
+ object = {};
+ };
+
+ this.__iterator__ = function() {
+ for (let key in object)
+ yield [key, object[key]];
+ }
+}
+ObjectStore.prototype = prototype;
+
+function ArrayStore(name, store)
+{
+ var array = loadPref(name, store, Array) || {};
+
+ this.__defineGetter__("store", function() store);
+ this.__defineGetter__("name", function() name);
+ this.__defineGetter__("serial", function() json.encode(array));
+ this.__defineGetter__("length", function() array.length);
+
+ this.set = function set(index, value)
+ {
+ var orig = array[index];
+ array[index] = value;
+ this.fireEvent("change", index);
+ };
+
+ this.push = function push(value)
+ {
+ array.push(value);
+ this.fireEvent("push", array.length);
+ };
+
+ this.pop = function pop(value)
+ {
+ var ret = array.pop();
+ this.fireEvent("pop", array.length);
+ return ret;
+ };
+
+ this.truncate = function truncate(length, fromEnd)
+ {
+ var ret = array.length;
+ if (array.length > length)
+ {
+ if(fromEnd)
+ array.splice(0, array.length - length);
+ array.length = length;
+ }
+ this.fireEvent("truncate", length);
+ return ret;
+ };
+
+ // XXX: Awkward.
+ this.mutate = function mutate(aFuncName)
+ {
+ var funcName = aFuncName;
+ arguments[0] = array;
+ array = Array[funcName].apply(Array, arguments);
+ },
+
+ this.get = function get(index)
+ {
+ return index >= 0 ? array[index] : array[array.length + index];
+ };
+
+ this.__iterator__ = function() {
+ for(let i = 0; i < array.length; i++)
+ yield [i, array[i]];
+ }
+}
+ArrayStore.prototype = prototype;
+
+var keys = {};
+var observers = {};
+
+var storage = {
+ _addKey: function addKey(key, val)
+ {
+ if(key in this)
+ throw Error; // TODO: Find a better error.
+ keys[key] = val;
+ this.__defineGetter__(key, function() keys[key]);
+ },
+
+ newObject: function newObject(key, store)
+ {
+ // TODO: Add type checking.
+ if(key in keys)
+ return;
+ this._addKey(key, new ObjectStore(key, store));
+ },
+
+ newArray: function newArray(key, store)
+ {
+ // TODO: Add type checking.
+ if(key in keys)
+ return;
+ this._addKey(key, new ArrayStore(key, store));
+ },
+
+ addObserver: function addObserver(key, callback)
+ {
+ if(!(key in observers))
+ observers[key] = [];
+ if(observers[key].indexOf(key) >= 0)
+ return;
+ observers[key].push(callback);
+ },
+
+ removeObserver: function(key, callback)
+ {
+ if(!(key in observers))
+ return;
+ observers[key] = observers[key].filter(function(elem) elem != callback);
+ if(obsevers[key].length == 0)
+ delete obsevers[key];
+ },
+
+ fireEvent: function fireEvent(key, event, arg)
+ {
+ for each(callback in observers[key])
+ callback(key, event, arg);
+ },
+
+ saveAll: function storeAll() {
+ for each(key in keys)
+ key.store();
+ }
+};
+
+// vim: set fdm=marker sw=4 sts=4 et ft=javascript:
diff --git a/content/ui.js b/content/ui.js
index 7f1099ca..1255f45d 100644
--- a/content/ui.js
+++ b/content/ui.js
@@ -43,53 +43,29 @@ liberator.CommandLine = function () //{{{
var completionlist = new liberator.InformationList("liberator-completion", { minItems: 1, maxItems: 10 });
var completions = [];
+ liberator.storage.newArray("history-search", true);
+ liberator.storage.newArray("history-command", true);
+
// TODO: clean this up when it's not 3am...
var history = {
- get size() { return liberator.options["history"]; },
+ get mode() (liberator.modes.extended == liberator.modes.EX) ? "command" : "search",
- get mode() { return (liberator.modes.extended == liberator.modes.EX) ? "cmd" : "search"; },
+ get store() liberator.storage["history-" + this.mode],
- cmd: null, // ex command history
+ get length() this.store.length,
- search: null, // text search history
-
- get: function () { return this[this.mode]; },
-
- set: function (lines) { this[this.mode] = lines; },
-
- load: function ()
- {
- // TODO: move to storage module
- this.cmd = liberator.options.getPref("extensions.vimperator.commandline_cmd_history", "").split("\n");
- this.search = liberator.options.getPref("extensions.vimperator.commandline_search_history", "").split("\n");
- },
-
- save: function ()
- {
- liberator.options.setPref("extensions.vimperator.commandline_cmd_history", this.cmd.join("\n"));
- liberator.options.setPref("extensions.vimperator.commandline_search_history", this.search.join("\n"));
- },
+ get: function(index) this.store.get(index),
add: function (str)
{
if (!str)
return;
- var lines = this.get();
-
- // remove all old history lines which have this string
- lines = lines.filter(function (line) {
- return line != str;
- });
-
- // add string to the command line history
- if (lines.push(str) > this.size) // remove the first 10% of the history
- lines = lines.slice(this.size / 10);
-
- this.set(lines);
+ this.store.mutate('filter', function(line) line != str);
+ this.store.push(str);
+ this.store.truncate(liberator.options["history"], true);
}
};
- history.load();
var historyIndex = UNINITIALIZED;
var historyStart = "";
@@ -643,8 +619,6 @@ liberator.CommandLine = function () //{{{
// user pressed UP or DOWN arrow to cycle history completion
else if (key == "" || key == "")
{
- var lines = history.get();
-
event.preventDefault();
event.stopPropagation();
@@ -654,18 +628,18 @@ liberator.CommandLine = function () //{{{
// save 'start' position for iterating through the history
if (historyIndex == UNINITIALIZED)
{
- historyIndex = lines.length;
+ historyIndex = history.length;
historyStart = command;
}
// search the history for the first item matching the current
// commandline string
- while (historyIndex >= -1 && historyIndex <= lines.length)
+ while (historyIndex >= -1 && historyIndex <= history.length)
{
key == "" ? historyIndex-- : historyIndex++;
// user pressed DOWN when there is no newer history item
- if (historyIndex == lines.length)
+ if (historyIndex == history.length)
{
setCommand(historyStart);
liberator.triggerCallback("change", currentExtendedMode, this.getCommand());
@@ -679,16 +653,16 @@ liberator.CommandLine = function () //{{{
liberator.beep();
break;
}
- else if (historyIndex >= lines.length + 1)
+ else if (historyIndex >= history.length + 1)
{
- historyIndex = lines.length;
+ historyIndex = history.length;
liberator.beep();
break;
}
- if (lines[historyIndex].indexOf(historyStart) == 0)
+ if (history.get(historyIndex).indexOf(historyStart) == 0)
{
- setCommand(lines[historyIndex]);
+ setCommand(history.get(historyIndex));
liberator.triggerCallback("change", currentExtendedMode, this.getCommand());
break;
}
@@ -1094,13 +1068,6 @@ liberator.CommandLine = function () //{{{
{
completionIndex = historyIndex = UNINITIALIZED;
},
-
- // it would be better if we had a destructor in javascript ...
- destroy: function ()
- {
- history.save();
- }
-
};
//}}}
}; //}}}