Allow server uses of LocalCollection to prefer _UnyieldingQueue. This
will mean that, as long as their observeChanges callbacks do not yield,
calls to insert/update/remove will not yield either.
- move _makeNonreactive into wrapTransform
- Use EJSON.equals for _id comparison
- Return null for nonexistent transforms
- Use isPlainObject for objectness check
- Add unit tests; remove some end-to-end tests
Also, add an EJSON.clone to the recently backported observeHandle._fetch
So, in HTML, the following are equivalent, and both mean that a checkbox is checked, because the `checked` attribute is present:
- `<input type="checkbox" checked>`
- `<input type="checkbox" checked="">`
We can't mess with that. On the other hand, in Spacebars before this commit, the following would *also* result in the checkbox being checked, regardless of whether `foo` evaluates to null, undefined, false, or the empty string:
- `<input type="checkbox" checked={{foo}}>`
- `<input type="checkbox" checked="{{foo}}">`
With this commit, the checkbox will NOT be checked if `foo` evaluates to null, undefined, or false.
To achieve this:
- In HTMLjs, an attribute is considered absent if its value is "nully" after being fully evaluated (i.e. after expanding functions and components via HTML.evaluateDynamicAttributes / HTML.evaluate). A nully value is one consisting of null, undefined, an empty array, or an array of those things. `false` is not nully and renders as "false". An empty string is not nully, and will "prop open" an attribute that would otherwise collapse into absence.
- Spacebars.mustache converts null, undefined, and false to null. So if you use {{foo}} anywhere in a template and foo evaluates to "false", that gets to converted to a null in HTMLjs (which is ignored). (true is rendered as "true".)
- When parsing HTML, an attribute that consists of *no tokens* becomes an empty string in the HTMLjs, which props open the attribute (unlike null or an empty array). (Since comment tokens are stripped during tokenization, if there are only comments in an attribute value that counts as no tokens.)
We were weirdly ignoring helpers that were falsy constants, so in `Template.main.foo = 0` / `{{foo}}` you'd get nothing, while if you did `Template.main.foo = function () { return 0; }` you would get "0".
A helper can return an object with a set of element attributes via
`<p {{attrs}}>`. When it re-runs due to a dependency changing the
value for a given attribute might stay the same. Test that the
attribute is not set on the DOM element.
- When helpers return SafeStrings, compare their underlying
strings.
- When helpers return objects (such as for dynamic attributes),
never isolate. The objects may have been mutated directly.
AttributeHandler ends up only affecting real changes.
Do the appropriate equality check for three cases:
(1) helpers in text nodes
(2) helpers in element attributes
(3) helpers in dynamic template inclusion
Also, assert that helpers passed to template inclusion indeed
return components or null.
More specifically, a helper re-runs and its return value hash't
changed, nothing should change in the DOM. Test this for:
(1) helpers in text nodes
(2) helpers in element attributes
(3) helpers in dynamic template inclusion
All existing listener callbacks were already calling complete()
synchronously, so this should not be a functional change.
This allows us to also eliminate the callback from crossbar.fire().
This in turn allows us to eliminate the `proxy_write` in
Meteor.refresh. The only purpose of that write was to keep the current
write fence open until fire's async callback got called; now that fire
works synchronously, it's not necessary! (This relies on the fact that
write fences never get armed while they are the current write fence,
which now has an assertion.)