//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
-"use strict";
+/* use strict */
Components.utils.import("resource://dactyl/bootstrap.jsm");
defineModule("storage", {
require: ["services", "util"]
}, this);
+this.lazyRequire("config", ["config"]);
+this.lazyRequire("io", ["IO"]);
+
var win32 = /^win(32|nt)$/i.test(services.runtime.OS);
var myObject = JSON.parse("{}").constructor;
function loadData(name, store, type) {
try {
- let data = storage.infoPath.child(name).read();
- let result = JSON.parse(data);
- if (result instanceof type)
- return result;
+ let file = storage.infoPath.child(name);
+ if (file.exists()) {
+ let data = file.read();
+ let result = JSON.parse(data);
+ if (result instanceof type)
+ return result;
+ }
+ }
+ catch (e) {
+ util.reportError(e);
}
- catch (e) {}
}
function saveData(obj) {
get length() this._object.length,
- set: function set(index, value) {
+ set: function set(index, value, quiet) {
var orig = this._object[index];
this._object[index] = value;
- this.fireEvent("change", index);
+ if (!quiet)
+ this.fireEvent("change", index);
+
+ return orig;
},
push: function push(value) {
this.fireEvent("push", this._object.length);
},
- pop: function pop(value) {
- var res = this._object.pop();
- this.fireEvent("pop", this._object.length);
+ pop: function pop(value, ord) {
+ if (ord == null)
+ var res = this._object.pop();
+ else
+ res = this._object.splice(ord, 1)[0];
+
+ this.fireEvent("pop", this._object.length, ord);
+ return res;
+ },
+
+ shift: function shift(value) {
+ var res = this._object.shift();
+ this.fireEvent("shift", this._object.length);
return res;
},
+ insert: function insert(value, ord) {
+ if (ord == 0)
+ this._object.unshift(value);
+ else
+ this._object = this._object.slice(0, ord)
+ .concat([value])
+ .concat(this._object.slice(ord));
+ this.fireEvent("insert", this._object.length, ord);
+ },
+
truncate: function truncate(length, fromEnd) {
var res = this._object.length;
if (this._object.length > length) {
this[key].timer.flush();
delete this[key];
}
- for (let ary in values(this.observers))
- for (let obj in values(ary))
- if (obj.ref && obj.ref.get())
- delete obj.ref.get().dactylStorageRefs;
this.keys = {};
this.observers = {};
},
- exists: function exists(name) this.infoPath.child(name).exists(),
+ infoPath: Class.Memoize(function ()
+ File(IO.runtimePath.replace(/,.*/, ""))
+ .child("info").child(config.profileName)),
+
+ exists: function exists(key) this.infoPath.child(key).exists(),
+
+ remove: function remove(key) {
+ if (this.exists(key)) {
+ if (this[key] && this[key].timer)
+ this[key].timer.flush();
+ delete this[key];
+ delete this.keys[key];
+ this.infoPath.child(key).remove(false);
+ }
+ },
newObject: function newObject(key, constructor, params) {
if (params == null || !isObject(params))
addObserver: function addObserver(key, callback, ref) {
if (ref) {
- if (!ref.dactylStorageRefs)
- ref.dactylStorageRefs = [];
- ref.dactylStorageRefs.push(callback);
- var callbackRef = Cu.getWeakReference(callback);
+ let refs = overlay.getData(ref, "storage-refs");
+ refs.push(callback);
+ var callbackRef = util.weakReference(callback);
}
else {
callbackRef = { get: function () callback };
removeDeadObservers: function () {
for (let [key, ary] in Iterator(this.observers)) {
- this.observers[key] = ary = ary.filter(function (o) o.callback.get() && (!o.ref || o.ref.get() && o.ref.get().dactylStorageRefs));
+ this.observers[key] = ary = ary.filter(function (o) o.callback.get()
+ && (!o.ref || o.ref.get()
+ && overlay.getData(o.ref.get(), "storage-refs", null)));
if (!ary.length)
delete this.observers[key];
}
skipXpcom: function skipXpcom(key, val) val instanceof Ci.nsISupports ? null : val
}
}, {
- init: function init(dactyl, modules) {
- init.superapply(this, arguments);
- storage.infoPath = File(modules.IO.runtimePath.replace(/,.*/, ""))
- .child("info").child(dactyl.profileName);
- },
-
cleanup: function (dactyl, modules, window) {
- delete window.dactylStorageRefs;
+ overlay.setData(window, "storage-refs", null);
this.removeDeadObservers();
}
});
* @param {nsIFile|string} path Expanded according to {@link IO#expandPath}
* @param {boolean} checkPWD Whether to allow expansion relative to the
* current directory. @default true
+ * @param {string} charset The charset of the file. @default File.defaultEncoding
*/
var File = Class("File", {
- init: function (path, checkPWD) {
+ init: function (path, checkPWD, charset) {
let file = services.File();
+ if (charset)
+ this.charset = charset;
+
+ if (path instanceof Ci.nsIFileURL)
+ path = path.file;
+
if (path instanceof Ci.nsIFile)
file = path.clone();
else if (/file:\/\//.test(path))
return self;
},
+ charset: Class.Memoize(function () File.defaultEncoding),
+
/**
* @property {nsIFileURL} Returns the nsIFileURL object for this file.
*/
- get URI() services.io.newFileURI(this),
+ URI: Class.Memoize(function () {
+ let uri = services.io.newFileURI(this).QueryInterface(Ci.nsIFileURL);
+ uri.QueryInterface(Ci.nsIMutable).mutable = false;
+ return uri;
+ }),
/**
* Iterates over the objects in this directory.
return f;
},
+ /**
+ * Returns an iterator for all lines in a file.
+ */
+ get lines() File.readLines(services.FileInStream(this, -1, 0, 0),
+ this.charset),
+
/**
* Reads this file's entire contents in "text" mode and returns the
* content as a string.
*
* @param {string} encoding The encoding from which to decode the file.
- * @default options["fileencoding"]
+ * @default #charset
* @returns {string}
*/
read: function (encoding) {
- let ifstream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
- ifstream.init(this, -1, 0, 0);
+ let ifstream = services.FileInStream(this, -1, 0, 0);
- return File.readStream(ifstream, encoding);
+ return File.readStream(ifstream, encoding || this.charset);
},
/**
* permissions if the file exists.
* @default 0644
* @param {string} encoding The encoding to used to write the file.
- * @default options["fileencoding"]
+ * @default #charset
*/
write: function (buf, mode, perms, encoding) {
- let ofstream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
function getStream(defaultChar) {
- let stream = Cc["@mozilla.org/intl/converter-output-stream;1"].createInstance(Ci.nsIConverterOutputStream);
- stream.init(ofstream, encoding, 0, defaultChar);
- return stream;
+ return services.ConvOutStream(ofstream, encoding, 0, defaultChar);
}
if (buf instanceof File)
buf = buf.read();
if (!encoding)
- encoding = File.defaultEncoding;
+ encoding = this.charset;
if (mode == ">>")
mode = File.MODE_WRONLY | File.MODE_CREATE | File.MODE_APPEND;
if (!this.exists()) // OCREAT won't create the directory
this.create(this.NORMAL_FILE_TYPE, perms);
- ofstream.init(this, mode, perms, 0);
+ let ofstream = services.FileOutStream(this, mode, perms, 0);
try {
var ocstream = getStream(0);
ocstream.writeString(buf);
/**
* @property {string} The current platform's path separator.
*/
- PATH_SEP: Class.memoize(function () {
+ PATH_SEP: Class.Memoize(function () {
let f = services.directory.get("CurProcD", Ci.nsIFile);
f.append("foo");
return f.path.substr(f.parent.path.length, 1);
}),
- pathSplit: Class.memoize(function () util.regexp("(?:/|" + util.regexp.escape(this.PATH_SEP) + ")", "g")),
+ pathSplit: Class.Memoize(function () util.regexp("(?:/|" + util.regexp.escape(this.PATH_SEP) + ")", "g")),
- DoesNotExist: function (path, error) ({
+ DoesNotExist: function DoesNotExist(path, error) ({
path: path,
exists: function () false,
__noSuchMethod__: function () { throw error || Error("Does not exist"); }
* @param {boolean} relative Whether the path is relative or absolute.
* @returns {string}
*/
- expandPath: function (path, relative) {
+ expandPath: function expandPath(path, relative) {
function getenv(name) services.environment.get(name);
// expand any $ENV vars - this is naive but so is Vim and we like to be compatible
expandPathList: function (list) list.map(this.expandPath),
- readStream: function (ifstream, encoding) {
+ readURL: function readURL(url, encoding) {
+ let channel = services.io.newChannel(url, null, null);
+ channel.contentType = "text/plain";
+ return this.readStream(channel.open(), encoding);
+ },
+
+ readStream: function readStream(ifstream, encoding) {
try {
- var icstream = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream);
- icstream.init(ifstream, encoding || File.defaultEncoding, 4096, // 4096 bytes buffering
- Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
+ var icstream = services.CharsetStream(
+ ifstream, encoding || File.defaultEncoding, 4096, // buffer size
+ services.CharsetStream.DEFAULT_REPLACEMENT_CHARACTER);
+
let buffer = [];
let str = {};
while (icstream.readString(4096, str) != 0)
}
},
- isAbsolutePath: function (path) {
+ readLines: function readLines(ifstream, encoding) {
+ try {
+ var icstream = services.CharsetStream(
+ ifstream, encoding || File.defaultEncoding, 4096, // buffer size
+ services.CharsetStream.DEFAULT_REPLACEMENT_CHARACTER);
+
+ var value = {};
+ while (icstream.readLine(value))
+ yield value.value;
+ }
+ finally {
+ icstream.close();
+ ifstream.close();
+ }
+ },
+
+
+ isAbsolutePath: function isAbsolutePath(path) {
try {
services.File().initWithPath(path);
return true;
}
},
- joinPaths: function (head, tail, cwd) {
+ joinPaths: function joinPaths(head, tail, cwd) {
let path = this(head, cwd);
try {
// FIXME: should only expand environment vars and normalize path separators
endModule();
-// catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
+// catch(e){ dump(e + "\n" + (e.stack || Error().stack)); Components.utils.reportError(e) }
// vim: set fdm=marker sw=4 sts=4 et ft=javascript: