mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge branch 'deps-utils-private' into devel
This adds three deps utilities, which are currently undocumented and internal: Meteor._ContextSet, Meteor._autorun, and Meteor._atFlush.
This commit is contained in:
@@ -9,6 +9,7 @@ PACKAGES_DIR=`dirname $0`/../packages
|
||||
echo 'Meteor = {};'
|
||||
cat $PACKAGES_DIR/uuid/uuid.js
|
||||
cat $PACKAGES_DIR/deps/deps.js
|
||||
cat $PACKAGES_DIR/deps/deps-utils.js
|
||||
cat $PACKAGES_DIR/liverange/liverange.js
|
||||
cat $PACKAGES_DIR/universal-events/listener.js
|
||||
cat $PACKAGES_DIR/universal-events/events-ie.js
|
||||
|
||||
@@ -137,32 +137,6 @@ Template.timer.destroyed = function () {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Run f(). Record its dependencies. Rerun it whenever the
|
||||
// dependencies change.
|
||||
//
|
||||
// Returns an object with a stop() method. Call stop() to stop the
|
||||
// rerunning.
|
||||
//
|
||||
// XXX this should go into Meteor core as Meteor.autorun
|
||||
var autorun = function (f) {
|
||||
var ctx;
|
||||
var slain = false;
|
||||
var rerun = function () {
|
||||
if (slain)
|
||||
return;
|
||||
ctx = new Meteor.deps.Context;
|
||||
ctx.run(f);
|
||||
ctx.onInvalidate(rerun);
|
||||
};
|
||||
rerun();
|
||||
return {
|
||||
stop: function () {
|
||||
slain = true;
|
||||
ctx.invalidate();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Template.d3Demo.left = function () {
|
||||
return { group: "left" };
|
||||
};
|
||||
@@ -233,7 +207,7 @@ Template.circles.rendered = function () {
|
||||
|
||||
if (! self.handle) {
|
||||
d3.select(self.node).append("rect");
|
||||
self.handle = autorun(function () {
|
||||
self.handle = Meteor._autorun(function () {
|
||||
var circle = d3.select(self.node).selectAll("circle")
|
||||
.data(Circles.find({group: data.group}).fetch(),
|
||||
function (d) { return d._id; });
|
||||
|
||||
118
packages/deps/deps-utils.js
Normal file
118
packages/deps/deps-utils.js
Normal file
@@ -0,0 +1,118 @@
|
||||
(function () {
|
||||
// XXX Document, test, and remove the leading underscore from everything.
|
||||
|
||||
////////// Meteor.deps._ContextSet
|
||||
|
||||
// Constructor for an empty _ContextSet.
|
||||
//
|
||||
// A _ContextSet is used to hold a set of Meteor.deps.Contexts that
|
||||
// are to be invalidated at some future time. If a Context in the
|
||||
// set becomes invalidated for any reason, it's immediately removed
|
||||
// from the set.
|
||||
var _ContextSet = function () {
|
||||
this._contextsById = {};
|
||||
};
|
||||
|
||||
// Adds the Context `ctx` to this set if it is not already
|
||||
// present. Returns true if the context is new to this set.
|
||||
_ContextSet.prototype.add = function (ctx) {
|
||||
var self = this;
|
||||
if (ctx && ! (ctx.id in self._contextsById)) {
|
||||
self._contextsById[ctx.id] = ctx;
|
||||
ctx.onInvalidate(function () {
|
||||
delete self._contextsById[ctx.id];
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Adds the current Context to this set if there is one. Returns
|
||||
// true if there is a current Context and it's new to the set.
|
||||
_ContextSet.prototype.addCurrentContext = function () {
|
||||
var self = this;
|
||||
var context = Meteor.deps.Context.current;
|
||||
if (! context)
|
||||
return false;
|
||||
return self.add(context);
|
||||
};
|
||||
|
||||
// Invalidate all Contexts in this set. They will be removed
|
||||
// from the set as a consequence.
|
||||
_ContextSet.prototype.invalidateAll = function () {
|
||||
var self = this;
|
||||
for (var id in self._contextsById)
|
||||
self._contextsById[id].invalidate();
|
||||
};
|
||||
|
||||
// Returns true if there are no Contexts in this set.
|
||||
_ContextSet.prototype.isEmpty = function () {
|
||||
var self = this;
|
||||
for(var id in self._contextsById)
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
Meteor.deps._ContextSet = _ContextSet;
|
||||
|
||||
////////// Meteor._autorun
|
||||
|
||||
// Run f(). Record its dependencies. Rerun it whenever the
|
||||
// dependencies change.
|
||||
//
|
||||
// Returns an object with a stop() method. Call stop() to stop the
|
||||
// rerunning. Also passes this object as an argument to f.
|
||||
Meteor._autorun = function (f) {
|
||||
var ctx;
|
||||
var slain = false;
|
||||
var handle = {
|
||||
stop: function () {
|
||||
slain = true;
|
||||
ctx.invalidate();
|
||||
}
|
||||
};
|
||||
var rerun = function () {
|
||||
if (slain)
|
||||
return;
|
||||
ctx = new Meteor.deps.Context;
|
||||
ctx.run(function () { f.call(this, handle); });
|
||||
ctx.onInvalidate(rerun);
|
||||
};
|
||||
rerun();
|
||||
return handle;
|
||||
};
|
||||
|
||||
////////// Meteor._atFlush
|
||||
|
||||
// Run 'f' at Meteor.flush()-time. If atFlush is called multiple times,
|
||||
// we guarantee that the 'f's will run in the same order that
|
||||
// atFlush was called on them. If we are inside a Meteor.flush() already,
|
||||
// f will be scheduled as part of the current flush().
|
||||
|
||||
var atFlushQueue = [];
|
||||
var atFlushContext = null;
|
||||
Meteor._atFlush = function (f) {
|
||||
atFlushQueue.push(f);
|
||||
|
||||
if (! atFlushContext) {
|
||||
atFlushContext = new Meteor.deps.Context;
|
||||
atFlushContext.onInvalidate(function () {
|
||||
var f;
|
||||
while ((f = atFlushQueue.shift())) {
|
||||
// Since atFlushContext is truthy, if f() calls atFlush
|
||||
// reentrantly, it's guaranteed to append to atFlushQueue and
|
||||
// not contruct a new atFlushContext.
|
||||
try {
|
||||
f();
|
||||
} catch (e) {
|
||||
Meteor._debug("Exception from Meteor._atFlush:", e);
|
||||
}
|
||||
}
|
||||
atFlushContext = null;
|
||||
});
|
||||
|
||||
atFlushContext.invalidate();
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -59,7 +59,11 @@
|
||||
|
||||
_.each(pending, function (ctx) {
|
||||
_.each(ctx._callbacks, function (f) {
|
||||
f(ctx); // XXX wrap in try?
|
||||
try {
|
||||
f(ctx);
|
||||
} catch (e) {
|
||||
Meteor._debug("Exception from Meteor.flush:", e);
|
||||
}
|
||||
});
|
||||
delete ctx._callbacks; // maybe help the GC
|
||||
});
|
||||
|
||||
@@ -9,5 +9,5 @@ Package.on_use(function (api, where) {
|
||||
where = where || ['client', 'server'];
|
||||
|
||||
api.use('underscore', where);
|
||||
api.add_files('deps.js', where);
|
||||
api.add_files(['deps.js', 'deps-utils.js'], where);
|
||||
});
|
||||
|
||||
@@ -1,31 +1,9 @@
|
||||
// XXX could use some tests
|
||||
|
||||
Session = _.extend({}, {
|
||||
keys: {},
|
||||
key_deps: {}, // key -> context id -> context
|
||||
key_value_deps: {}, // key -> value -> context id -> context
|
||||
|
||||
// XXX remove debugging method (or improve it, but anyway, don't
|
||||
// ship it in production)
|
||||
dump_state: function () {
|
||||
var self = this;
|
||||
console.log("=== Session state ===");
|
||||
for (var key in self.key_deps) {
|
||||
var ids = _.keys(self.key_deps[key]);
|
||||
if (!ids.length)
|
||||
continue;
|
||||
console.log(key + ": " + _.reject(ids, function (x) {return x === "_once"}).join(' '));
|
||||
}
|
||||
|
||||
for (var key in self.key_value_deps) {
|
||||
for (var value in self.key_value_deps[key]) {
|
||||
var ids = _.keys(self.key_value_deps[key][value]);
|
||||
if (!ids.length)
|
||||
continue;
|
||||
console.log(key + "(" + value + "): " + _.reject(ids, function (x) {return x === "_once";}).join(' '));
|
||||
}
|
||||
}
|
||||
},
|
||||
keys: {}, // key -> value
|
||||
keyDeps: {}, // key -> _ContextSet
|
||||
keyValueDeps: {}, // key -> value -> _ContextSet
|
||||
|
||||
set: function (key, value) {
|
||||
var self = this;
|
||||
@@ -36,35 +14,26 @@ Session = _.extend({}, {
|
||||
value !== null && value !== undefined)
|
||||
throw new Error("Session.set: value can't be an object");
|
||||
|
||||
var old_value = self.keys[key];
|
||||
if (value === old_value)
|
||||
var oldValue = self.keys[key];
|
||||
if (value === oldValue)
|
||||
return;
|
||||
self.keys[key] = value;
|
||||
|
||||
var invalidate = function (map) {
|
||||
if (map)
|
||||
for (var id in map)
|
||||
map[id].invalidate();
|
||||
var invalidateAll = function (cset) {
|
||||
cset && cset.invalidateAll();
|
||||
};
|
||||
|
||||
self._ensureKey(key);
|
||||
invalidate(self.key_deps[key]);
|
||||
invalidate(self.key_value_deps[key][old_value]);
|
||||
invalidate(self.key_value_deps[key][value]);
|
||||
invalidateAll(self.keyDeps[key]);
|
||||
if (self.keyValueDeps[key]) {
|
||||
invalidateAll(self.keyValueDeps[key][oldValue]);
|
||||
invalidateAll(self.keyValueDeps[key][value]);
|
||||
}
|
||||
},
|
||||
|
||||
get: function (key) {
|
||||
var self = this;
|
||||
var context = Meteor.deps.Context.current;
|
||||
self._ensureKey(key);
|
||||
|
||||
if (context && !(context.id in self.key_deps[key])) {
|
||||
self.key_deps[key][context.id] = context;
|
||||
context.onInvalidate(function () {
|
||||
delete self.key_deps[key][context.id];
|
||||
});
|
||||
}
|
||||
|
||||
self.keyDeps[key].addCurrentContext();
|
||||
return self.keys[key];
|
||||
},
|
||||
|
||||
@@ -75,24 +44,23 @@ Session = _.extend({}, {
|
||||
if (typeof value !== 'string' &&
|
||||
typeof value !== 'number' &&
|
||||
typeof value !== 'boolean' &&
|
||||
value !== null && value !== undefined)
|
||||
typeof value !== 'undefined' &&
|
||||
value !== null)
|
||||
throw new Error("Session.equals: value can't be an object");
|
||||
|
||||
if (context) {
|
||||
self._ensureKey(key);
|
||||
if (!(value in self.key_value_deps[key]))
|
||||
self.key_value_deps[key][value] = {};
|
||||
|
||||
if (!(context.id in self.key_value_deps[key][value])) {
|
||||
self.key_value_deps[key][value][context.id] = context;
|
||||
if (!(value in self.keyValueDeps[key]))
|
||||
self.keyValueDeps[key][value] = new Meteor.deps._ContextSet;
|
||||
|
||||
var isNew = self.keyValueDeps[key][value].add(context);
|
||||
if (isNew) {
|
||||
context.onInvalidate(function () {
|
||||
delete self.key_value_deps[key][value][context.id];
|
||||
|
||||
// clean up [key][value] if it's now empty, so we don't use
|
||||
// O(n) memory for n = values seen ever
|
||||
for (var x in self.key_value_deps[key][value])
|
||||
return;
|
||||
delete self.key_value_deps[key][value];
|
||||
if (self.keyValueDeps[key][value].isEmpty())
|
||||
delete self.keyValueDeps[key][value];
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -102,9 +70,9 @@ Session = _.extend({}, {
|
||||
|
||||
_ensureKey: function (key) {
|
||||
var self = this;
|
||||
if (!(key in self.key_deps)) {
|
||||
self.key_deps[key] = {};
|
||||
self.key_value_deps[key] = {};
|
||||
if (!(key in self.keyDeps)) {
|
||||
self.keyDeps[key] = new Meteor.deps._ContextSet;
|
||||
self.keyValueDeps[key] = {};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
// XXX in landmark-demo, if Template.timer.created throws an exception,
|
||||
// then it is never called again, even if you push the 'create a
|
||||
// timer' button again. the problem is almost certainly in atFlushTime
|
||||
// timer' button again. the problem is almost certainly in atFlush
|
||||
// (not hard to see what it is.)
|
||||
|
||||
(function() {
|
||||
@@ -144,9 +144,19 @@ Spark._Renderer = function () {
|
||||
|
||||
_.extend(Spark._Renderer.prototype, {
|
||||
// `what` can be a function that takes a LiveRange, or just a set of
|
||||
// attributes to add to the liverange. tag and what are optional.
|
||||
// if no tag is passed, no liverange will be created.
|
||||
annotate: function (html, type, what, unusedFunc) {
|
||||
// attributes to add to the liverange. type and what are optional.
|
||||
// if no type is passed, no liverange will be created.
|
||||
// If what is a function, it will be called no matter what, even
|
||||
// if the annotated HTML was not used and no LiveRange was created,
|
||||
// in which case it gets null as an argument.
|
||||
annotate: function (html, type, what) {
|
||||
if (typeof what !== 'function') {
|
||||
var attribs = what;
|
||||
what = function (range) {
|
||||
if (range)
|
||||
_.extend(range, attribs);
|
||||
};
|
||||
}
|
||||
// The annotation tags that we insert into HTML strings must be
|
||||
// unguessable in order to not create potential cross-site scripting
|
||||
// attack vectors, so we use random strings. Even a well-written app
|
||||
@@ -156,20 +166,15 @@ _.extend(Spark._Renderer.prototype, {
|
||||
// and not arbitrary user-entered data.
|
||||
var id = (type || '') + ":" + Spark._createId();
|
||||
this.annotations[id] = function (start, end) {
|
||||
if (! start) {
|
||||
// materialize called us with no args because this annotation
|
||||
// wasn't used
|
||||
unusedFunc && unusedFunc();
|
||||
if ((! start) || (! type)) {
|
||||
// ! start: materialize called us with no args because this
|
||||
// annotation wasn't used
|
||||
// ! type: no type given, don't generate a LiveRange
|
||||
what(null);
|
||||
return;
|
||||
}
|
||||
if (! type)
|
||||
// no type given; don't generate a LiveRange
|
||||
return;
|
||||
var range = makeRange(type, start, end);
|
||||
if (what instanceof Function)
|
||||
what(range);
|
||||
else
|
||||
_.extend(range, what);
|
||||
what(range);
|
||||
};
|
||||
|
||||
return "<$" + id + ">" + html + "</$" + id + ">";
|
||||
@@ -340,8 +345,7 @@ var scheduleOnscreenSetup = function (frag, landmarkRanges) {
|
||||
finalized = true;
|
||||
};
|
||||
|
||||
var ctx = new Meteor.deps.Context;
|
||||
ctx.onInvalidate(function () {
|
||||
Meteor._atFlush(function () {
|
||||
if (finalized)
|
||||
return;
|
||||
|
||||
@@ -395,8 +399,6 @@ var scheduleOnscreenSetup = function (frag, landmarkRanges) {
|
||||
notifyWatchers(renderedRange.firstNode(), renderedRange.lastNode());
|
||||
renderedRange.destroy();
|
||||
});
|
||||
|
||||
ctx.invalidate();
|
||||
};
|
||||
|
||||
Spark.render = function (htmlFunc) {
|
||||
@@ -721,6 +723,9 @@ Spark.attachEvents = withRenderer(function (eventMap, html, _renderer) {
|
||||
|
||||
html = _renderer.annotate(
|
||||
html, Spark._ANNOTATION_EVENTS, function (range) {
|
||||
if (! range)
|
||||
return;
|
||||
|
||||
_.each(eventTypes, function (t) {
|
||||
listener.addType(t);
|
||||
});
|
||||
@@ -787,66 +792,40 @@ Spark.isolate = function (htmlFunc) {
|
||||
if (!renderer)
|
||||
return htmlFunc();
|
||||
|
||||
var ctx = new Meteor.deps.Context;
|
||||
|
||||
return renderer.annotate(
|
||||
ctx.run(htmlFunc), Spark._ANNOTATION_ISOLATE, function (range) {
|
||||
range.finalize = function () {
|
||||
// Spark.finalize() was called on us (presumably because we were
|
||||
// removed from the document.) Tear down our structures without
|
||||
// doing any more updates. note that range is about to be
|
||||
// destroyed by finalize.
|
||||
range = null;
|
||||
ctx.invalidate();
|
||||
};
|
||||
|
||||
var refresh = function () {
|
||||
if (! range)
|
||||
return; // killed by finalize. range has already been destroyed.
|
||||
|
||||
ctx = new Meteor.deps.Context;
|
||||
Spark.renderToRange(range, function () {
|
||||
return ctx.run(htmlFunc);
|
||||
var range;
|
||||
var firstRun = true;
|
||||
var retHtml;
|
||||
Meteor._autorun(function (handle) {
|
||||
if (firstRun) {
|
||||
retHtml = renderer.annotate(
|
||||
htmlFunc(), Spark._ANNOTATION_ISOLATE,
|
||||
function (r) {
|
||||
if (! r) {
|
||||
// annotation not used; kill our context
|
||||
handle.stop();
|
||||
} else {
|
||||
range = r;
|
||||
range.finalize = function () {
|
||||
// Spark.finalize() was called on our range (presumably
|
||||
// because it was removed from the document.) Kill
|
||||
// this context and stop rerunning.
|
||||
handle.stop();
|
||||
};
|
||||
}
|
||||
});
|
||||
ctx.onInvalidate(refresh);
|
||||
};
|
||||
firstRun = false;
|
||||
} else {
|
||||
Spark.renderToRange(range, htmlFunc);
|
||||
}
|
||||
});
|
||||
|
||||
ctx.onInvalidate(refresh);
|
||||
});
|
||||
return retHtml;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
/* Lists */
|
||||
/******************************************************************************/
|
||||
|
||||
// Run 'f' at flush()-time. If atFlushTime is called multiple times,
|
||||
// we guarantee that the 'f's will run in the order of their
|
||||
// respective atFlushTime calls.
|
||||
//
|
||||
// XXX either break this out into a separate package or fold it into
|
||||
// deps
|
||||
var atFlushQueue = [];
|
||||
var atFlushContext = null;
|
||||
var atFlushTime = function (f) {
|
||||
atFlushQueue.push(f);
|
||||
|
||||
if (! atFlushContext) {
|
||||
atFlushContext = new Meteor.deps.Context;
|
||||
atFlushContext.onInvalidate(function () {
|
||||
var f;
|
||||
while ((f = atFlushQueue.shift())) {
|
||||
// Since atFlushContext is truthy, if f() calls atFlushTime
|
||||
// reentrantly, it's guaranteed to append to atFlushQueue and
|
||||
// not contruct a new atFlushContext.
|
||||
f();
|
||||
}
|
||||
atFlushContext = null;
|
||||
});
|
||||
|
||||
atFlushContext.invalidate();
|
||||
}
|
||||
};
|
||||
|
||||
Spark.list = function (cursor, itemFunc, elseFunc) {
|
||||
elseFunc = elseFunc || function () { return ''; };
|
||||
|
||||
@@ -874,8 +853,8 @@ Spark.list = function (cursor, itemFunc, elseFunc) {
|
||||
|
||||
// Get the renderer, if any
|
||||
var renderer = Spark._currentRenderer.get();
|
||||
var annotate = renderer ?
|
||||
_.bind(renderer.annotate, renderer) :
|
||||
var maybeAnnotate = renderer ?
|
||||
_.bind(renderer.annotate, renderer) :
|
||||
function (html) { return html; };
|
||||
|
||||
// Render the initial contents. If we have a renderer, create a
|
||||
@@ -889,11 +868,11 @@ Spark.list = function (cursor, itemFunc, elseFunc) {
|
||||
else {
|
||||
for (var i = 0; i < initialContents.length; i++) {
|
||||
(function (i) {
|
||||
html += annotate(itemFunc(initialContents[i]),
|
||||
Spark._ANNOTATION_LIST_ITEM,
|
||||
function (range) {
|
||||
itemRanges[i] = range;
|
||||
});
|
||||
html += maybeAnnotate(itemFunc(initialContents[i]),
|
||||
Spark._ANNOTATION_LIST_ITEM,
|
||||
function (range) {
|
||||
itemRanges[i] = range;
|
||||
});
|
||||
})(i); // scope i to closure
|
||||
}
|
||||
}
|
||||
@@ -903,13 +882,15 @@ Spark.list = function (cursor, itemFunc, elseFunc) {
|
||||
handle.stop();
|
||||
stopped = true;
|
||||
};
|
||||
html = annotate(html, Spark._ANNOTATION_LIST, function (range) {
|
||||
outerRange = range;
|
||||
outerRange.finalize = cleanup;
|
||||
}, function () {
|
||||
// We never ended up on the screen (caller discarded our return
|
||||
// value)
|
||||
cleanup();
|
||||
html = maybeAnnotate(html, Spark._ANNOTATION_LIST, function (range) {
|
||||
if (! range) {
|
||||
// We never ended up on the screen (caller discarded our return
|
||||
// value)
|
||||
cleanup();
|
||||
} else {
|
||||
outerRange = range;
|
||||
outerRange.finalize = cleanup;
|
||||
}
|
||||
});
|
||||
|
||||
// No renderer? Then we have no way to update the returned html and
|
||||
@@ -930,7 +911,7 @@ Spark.list = function (cursor, itemFunc, elseFunc) {
|
||||
};
|
||||
|
||||
var later = function (f) {
|
||||
atFlushTime(function () {
|
||||
Meteor._atFlush(function () {
|
||||
if (! stopped)
|
||||
withEventGuard(f);
|
||||
});
|
||||
@@ -1124,6 +1105,12 @@ Spark.createLandmark = function (options, htmlFunc) {
|
||||
var html = htmlFunc(landmark);
|
||||
return renderer.annotate(
|
||||
html, Spark._ANNOTATION_LANDMARK, function (range) {
|
||||
if (! range) {
|
||||
// annotation not used
|
||||
options.destroyed && options.destroyed.call(landmark);
|
||||
return;
|
||||
}
|
||||
|
||||
_.extend(range, {
|
||||
preserve: preserve,
|
||||
constant: !! options.constant,
|
||||
@@ -1140,9 +1127,6 @@ Spark.createLandmark = function (options, htmlFunc) {
|
||||
|
||||
landmark._range = range;
|
||||
renderer.landmarkRanges.push(range);
|
||||
}, function () {
|
||||
// "annotation not used" callback
|
||||
options.destroyed && options.destroyed.call(landmark);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -47,11 +47,10 @@ Meteor._Stream = function (url) {
|
||||
retry_count: 0
|
||||
};
|
||||
|
||||
self.status_listeners = {}; // context.id -> context
|
||||
self.status_listeners = (Meteor.deps && new Meteor.deps._ContextSet);
|
||||
self.status_changed = function () {
|
||||
_.each(self.status_listeners, function (context) {
|
||||
context.invalidate();
|
||||
});
|
||||
if (self.status_listeners)
|
||||
self.status_listeners.invalidateAll();
|
||||
};
|
||||
|
||||
//// Retry logic
|
||||
@@ -120,13 +119,8 @@ _.extend(Meteor._Stream.prototype, {
|
||||
// Get current status. Reactive.
|
||||
status: function () {
|
||||
var self = this;
|
||||
var context = Meteor.deps && Meteor.deps.Context.current;
|
||||
if (context && !(context.id in self.status_listeners)) {
|
||||
self.status_listeners[context.id] = context;
|
||||
context.onInvalidate(function () {
|
||||
delete self.status_listeners[context.id];
|
||||
});
|
||||
}
|
||||
if (self.status_listeners)
|
||||
self.status_listeners.addCurrentContext();
|
||||
return self.current_status;
|
||||
},
|
||||
|
||||
|
||||
@@ -50,11 +50,9 @@ OnscreenDiv.prototype.kill = function() {
|
||||
if (self.div.parentNode)
|
||||
self.div.parentNode.removeChild(self.div);
|
||||
|
||||
var cx = new Meteor.deps.Context;
|
||||
cx.onInvalidate(function() {
|
||||
Meteor._atFlush(function () {
|
||||
Spark.finalize(self.div);
|
||||
});
|
||||
cx.invalidate();
|
||||
};
|
||||
|
||||
// remove the DIV from the document
|
||||
|
||||
@@ -19,19 +19,11 @@ var ReactiveVar = function(initialValue) {
|
||||
|
||||
this._value = (typeof initialValue === "undefined" ? null :
|
||||
initialValue);
|
||||
this._deps = {};
|
||||
this._deps = new Meteor.deps._ContextSet;
|
||||
};
|
||||
|
||||
ReactiveVar.prototype.get = function() {
|
||||
var context = Meteor.deps.Context.current;
|
||||
if (context && !(context.id in this._deps)) {
|
||||
this._deps[context.id] = context;
|
||||
var self = this;
|
||||
context.onInvalidate(function() {
|
||||
delete self._deps[context.id];
|
||||
});
|
||||
}
|
||||
|
||||
this._deps.addCurrentContext();
|
||||
return this._value;
|
||||
};
|
||||
|
||||
@@ -43,11 +35,9 @@ ReactiveVar.prototype.set = function(newValue) {
|
||||
|
||||
this._value = newValue;
|
||||
|
||||
for(var id in this._deps)
|
||||
this._deps[id].invalidate();
|
||||
|
||||
this._deps.invalidateAll();
|
||||
};
|
||||
|
||||
ReactiveVar.prototype.numListeners = function() {
|
||||
return _.keys(this._deps).length;
|
||||
return _.keys(this._deps._contextsById).length;
|
||||
};
|
||||
|
||||
@@ -31,13 +31,11 @@ WrappedFrag.prototype.release = function() {
|
||||
// decrement frag's GC protection reference count
|
||||
// Clean up on flush, if hits 0. Wait to decrement
|
||||
// so no one else cleans it up first.
|
||||
var cx = new Meteor.deps.Context;
|
||||
cx.onInvalidate(function() {
|
||||
Meteor._atFlush(function () {
|
||||
if (! --frag["_protect"]) {
|
||||
Spark.finalize(frag);
|
||||
}
|
||||
});
|
||||
cx.invalidate();
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,19 +12,12 @@ Meteor.startup(function () {
|
||||
});
|
||||
|
||||
Template.test_table.running = function() {
|
||||
var cx = Meteor.deps.Context.current;
|
||||
if (cx) {
|
||||
resultDeps.push(cx);
|
||||
}
|
||||
|
||||
resultDeps.addCurrentContext();
|
||||
return running;
|
||||
};
|
||||
|
||||
Template.test_table.passed = function() {
|
||||
var cx = Meteor.deps.Context.current;
|
||||
if (cx) {
|
||||
resultDeps.push(cx);
|
||||
}
|
||||
resultDeps.addCurrentContext();
|
||||
|
||||
// walk whole tree to look for failed tests
|
||||
var walk = function (groups) {
|
||||
@@ -53,10 +46,7 @@ Template.test_table.passed = function() {
|
||||
|
||||
|
||||
Template.test_table.total_test_time = function() {
|
||||
var cx = Meteor.deps.Context.current;
|
||||
if (cx) {
|
||||
resultDeps.push(cx);
|
||||
}
|
||||
resultDeps.addCurrentContext();
|
||||
|
||||
// walk whole tree to get all tests
|
||||
var walk = function (groups) {
|
||||
@@ -79,11 +69,7 @@ Template.test_table.total_test_time = function() {
|
||||
|
||||
|
||||
Template.test_table.data = function() {
|
||||
var cx = Meteor.deps.Context.current;
|
||||
if (cx) {
|
||||
resultDeps.push(cx);
|
||||
}
|
||||
|
||||
resultDeps.addCurrentContext();
|
||||
return resultTree;
|
||||
};
|
||||
|
||||
@@ -185,13 +171,10 @@ Template.event.is_debuggable = function() {
|
||||
|
||||
|
||||
var resultTree = [];
|
||||
var resultDeps = [];
|
||||
var resultDeps = new Meteor.deps._ContextSet;
|
||||
|
||||
var _resultsChanged = function() {
|
||||
_.each(resultDeps, function(cx) {
|
||||
cx.invalidate();
|
||||
});
|
||||
resultDeps.length = 0;
|
||||
resultDeps.invalidateAll();
|
||||
};
|
||||
|
||||
var _testTime = function(t) {
|
||||
|
||||
Reference in New Issue
Block a user