ordered dictionary, for eventual use in observeChanges->observe

This commit is contained in:
Naomi Seyfer
2013-01-25 16:07:14 -08:00
parent c10c7f7c91
commit f65b5c738d
4 changed files with 177 additions and 4 deletions

View File

@@ -8,7 +8,7 @@ Package.on_use(function (api, where) {
// It would be sort of nice if minimongo didn't depend on
// underscore, so we could ship it separately.
api.use(['underscore', 'json', 'ejson'], where);
api.use(['underscore', 'json', 'ejson', 'ordereddict'], where);
api.add_files([
'minimongo.js',

View File

@@ -0,0 +1,158 @@
(function () {
var element = function (key, value, next, prev) {
return {
key: key,
value: value,
next: next,
prev: prev
};
};
OrderedDict = function (/* ... */) {
var self = this;
self._dict = {};
self._first = null;
self._last = null;
_.each(arguments, function (kv) {
self.putBefore(kv[0], kv[1], null);
});
};
_.extend(OrderedDict.prototype, {
putBefore: function (key, item, before) {
var self = this;
var elt = before ?
element(key, item, self._dict[before]) :
element(key, item, null);
if (elt.next === undefined)
throw new Error("could not find item to put this one before");
if (!elt.next) {
elt.prev = self._last;
self._last.next = elt;
self._last = elt;
} else {
elt.prev = elt.next.prev;
elt.next.prev = elt;
elt.prev.next = elt;
}
if (self._first === null || self._first === elt.next)
self._first = elt;
},
remove: function (key) {
var self = this;
var elt = self._dict[key];
if (elt !== undefined) {
elt.next.prev = elt.prev;
elt.prev.next = elt.next;
if (elt === self._last)
self._last = elt.prev;
if (elt === self._first)
self._first = elt.next;
delete self._dict[key];
return elt.value;
} else {
return undefined;
}
},
get: function (key) {
var self = this;
if (_.has(self._dict, key))
return self._dict[key].value;
return undefined;
},
has: function (key) {
var self = this;
return _.has(self._dict[key]);
},
each: function (iter) {
var self = this;
var i = 0;
var elt = self._first;
while (elt !== null) {
var b = iter(elt.value, elt.key, i);
if (b === OrderedDict.BREAK)
return;
elt = elt.next;
}
},
first: function () {
var self = this;
return self._first.key;
},
firstValue: function () {
var self = this;
return self._first.value;
},
last: function () {
var self = this;
return self._last.key;
},
lastValue: function () {
var self = this;
return self._last.value;
},
prev: function (key) {
var self = this;
if (_.has(self._dict, key)) {
var elt = self._dict[key];
if (elt.prev)
return elt.prev.key;
}
return null;
},
next: function (key) {
var self = this;
if (_.has(self._dict, key)) {
var elt = self._dict[key];
if (elt.next)
return elt.next.key;
}
return null;
},
moveBefore: function (key, before) {
var self = this;
var elt = self._dict[key];
var eltBefore = before ? self._dict[before] : null;
if (elt === undefined)
throw new Error("Item to move is not present");
if (eltBefore === undefined)
throw new Error("Could not find element to move this one before");
if (eltBefore === elt.next) // no moving necessary.
return;
// remove from its old place
elt.next.prev = elt.prev;
elt.prev.next = elt.next;
if (elt === self._last)
self._last = elt.prev;
if (elt === self._first)
self._first = elt.next;
// now patch it in to its new place
if (eltBefore === null) {
elt.next = null;
elt.prev = self._last;
self._last.next = elt.prev;
self._last = elt;
} else {
elt.next = eltBefore;
elt.prev = eltBefore.prev;
eltBefore.prev = elt;
elt.prev.next = elt;
}
},
getIndex: function (key) {
var self = this;
var ret = null;
self.each(function (v, k, i) {
if (k === key) {
ret = i;
return OrderedDict.BREAK;
}
return undefined;
});
return ret;
}
});
OrderedDict.BREAK = {break: true};
})();

View File

@@ -0,0 +1,9 @@
Package.describe({
summary: "Ordered traversable dictionary with a mutable ordering",
internal: true
});
Package.on_use(function (api) {
api.use('underscore');
api.add_files('ordereddict.js', ['client', 'server']);
});

View File

@@ -892,15 +892,18 @@ Spark.list = function (cursor, itemFunc, elseFunc) {
});
// Get the current contents of the cursor.
// XXX currently we count on observe() using only added() to deliver
// the initial contents. are we allow to do that, or do we need to
// implement removed/moved/changed here as well?
// TODO: unify initialContents and itemRanges into one
// OrderedDict from id -> (doc, LiveRange), called
// `itemDict`.
var initialContents = [];
_.extend(callbacks, {
added: function (item, beforeIndex) {
// TODO: itemDict.addBefore ...
initialContents.splice(beforeIndex, 0, item);
}
});
// TODO: observeChanges
var handle = cursor.observe(observerCallbacks);
// Get the renderer, if any
@@ -914,15 +917,18 @@ Spark.list = function (cursor, itemFunc, elseFunc) {
// off for later.
var html = '';
var outerRange;
// TODO: itemRanges is a map from id to LiveRange
var itemRanges = [];
if (! initialContents.length)
html = elseFunc();
else {
// TODO: iterate over itemDict
for (var i = 0; i < initialContents.length; i++) {
(function (i) {
html += maybeAnnotate(itemFunc(initialContents[i]),
Spark._ANNOTATION_LIST_ITEM,
function (range) {
// TOOD: mutate value in itemDict
itemRanges[i] = range;
});
})(i); // scope i to closure