mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
remove old code
This commit is contained in:
@@ -198,867 +198,6 @@ _extend(UI.Component, {
|
||||
//};
|
||||
|
||||
|
||||
/*
|
||||
_extend(UI.Component, {
|
||||
// Parent Component in the composition hierarchy.
|
||||
// An inited
|
||||
parent: null,
|
||||
// Child Components in the composition hierarchy,
|
||||
// in a dictionary keyed on their `guid` property.
|
||||
//
|
||||
// For memory efficiency, childless Components share
|
||||
// the same dictionary.
|
||||
children: SEALED_EMPTY_OBJECT,
|
||||
|
||||
// # component.add(child)
|
||||
//
|
||||
// Adds `child` to this component in the parent/child
|
||||
// hierarchy.
|
||||
//
|
||||
// Components must be assembled from "top to bottom." Each
|
||||
// component must either be added as a child of another,
|
||||
// or made a root using `component.makeRoot()`, before
|
||||
// it can receive its own children. This ensures that
|
||||
// every component already knows its parent when it is
|
||||
// initialized. A component's parent is permanent; the
|
||||
// component cannot be removed or reparented without
|
||||
// destroying it.
|
||||
//
|
||||
// The child is not built or put into the DOM.
|
||||
// The `append`, `prepend`, and `insertBefore`
|
||||
// methods all add their argument as a child in addition
|
||||
// to building it if necessary and putting it into the
|
||||
// component's DOM.
|
||||
//
|
||||
// Requires `component` is not destroyed.
|
||||
add: function (child) {
|
||||
var self = this;
|
||||
|
||||
if (self.isDestroyed)
|
||||
throw new Error("Can't add child to a destroyed component");
|
||||
if (! self.isInited)
|
||||
throw new Error("Component must be inited already to add a child");
|
||||
|
||||
var guid = child.guid;
|
||||
|
||||
if (self.children[guid])
|
||||
throw new Error("Child already added to this component!");
|
||||
|
||||
if (child.isInited)
|
||||
throw new Error("Child already inited, can't add to a different parent");
|
||||
|
||||
// allocate a new dictionary to hold children if necessary
|
||||
if (self.children === SEALED_EMPTY_OBJECT)
|
||||
self.children = {};
|
||||
|
||||
self.children[guid] = child;
|
||||
|
||||
child.parent = self;
|
||||
|
||||
// Note on ordering of these two lines: You see `isInited`
|
||||
// as `true` from `init` callbacks, even though
|
||||
// linguistically it seems odd that you are marked
|
||||
// inited before `init` is called. What's really going
|
||||
// on is `init` is a callback which would normally be
|
||||
// named in the past tense; for example, we'd set
|
||||
// `isAdded` to true and then call the `added` callback.
|
||||
//
|
||||
// `isInited` in fact means essentially "has been added/
|
||||
// instantiated", and `init` is the callback you get
|
||||
// when that happens.
|
||||
child.isInited = true;
|
||||
Deps.nonreactive(function () {
|
||||
callChainedCallback(child, 'init');
|
||||
});
|
||||
|
||||
// useful in: `this.foo = this.add(Foo.extend())`
|
||||
return child;
|
||||
},
|
||||
|
||||
hasChild: function (comp) {
|
||||
this._requireNotDestroyed();
|
||||
this._requireInited();
|
||||
|
||||
return this.children[comp.guid] === comp;
|
||||
},
|
||||
|
||||
// Init this Component without giving it a parent; it will
|
||||
// never have a parent and always be the root of its own
|
||||
// parent/child hierarchy.
|
||||
//
|
||||
// This is primarily intended for unit testing, or embedding
|
||||
// Meteor UI.
|
||||
makeRoot: function (comp) {
|
||||
if (this.isInited) {
|
||||
if (this.parent)
|
||||
throw new Error("Component already parented");
|
||||
throw new Error("Component already inited as a root");
|
||||
}
|
||||
|
||||
this.isInited = true;
|
||||
callChainedCallback(this, 'init');
|
||||
},
|
||||
|
||||
remove: function (child) {
|
||||
var self = this;
|
||||
|
||||
self._requireNotDestroyed();
|
||||
|
||||
if (! child) {
|
||||
// Support `()` form of args; remove self.
|
||||
// Can't `remove()` if we are a root or haven't been
|
||||
// inited.
|
||||
if (! self.isInited || ! self.parent)
|
||||
throw new Error("Component to remove must have a parent");
|
||||
self.parent.remove(self);
|
||||
return;
|
||||
}
|
||||
|
||||
// Child is inited but may or may not be built.
|
||||
// Child may be destroyed.
|
||||
|
||||
// Note that child is not removed from the DOM if it is already
|
||||
// destroyed. This is used when a Component is rebuilt -- the
|
||||
// children are first destroyed, then removed as children, then
|
||||
// removed from the DOM wholesale in one operation.
|
||||
if (child.isBuilt && ! child.isDestroyed &&
|
||||
child.isAttached) {
|
||||
|
||||
child.detach(true); // _forDestruction = true
|
||||
}
|
||||
|
||||
var guid = child.guid;
|
||||
if (! self.children[guid])
|
||||
throw new Error("Child not found (id " + guid + ")");
|
||||
|
||||
delete self.children[guid];
|
||||
// (don't delete child.parent pointer, could be useful
|
||||
// in destroyed callback?)
|
||||
|
||||
child.destroy();
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
_extend(UI.Component, {
|
||||
// If the Component is built into DOM, `start` and `end`
|
||||
// are the first and last *nodes or Components* in this
|
||||
// Component's subtree of DOM nodes and Components.
|
||||
start: null,
|
||||
end: null,
|
||||
|
||||
firstNode: function () {
|
||||
this._requireBuilt();
|
||||
this._requireNotDestroyed();
|
||||
|
||||
return UI.isComponent(this.start) ?
|
||||
this.start.firstNode() : this.start;
|
||||
},
|
||||
|
||||
lastNode: function () {
|
||||
this._requireBuilt();
|
||||
this._requireNotDestroyed();
|
||||
|
||||
return UI.isComponent(this.end) ?
|
||||
this.end.lastNode() : this.end;
|
||||
},
|
||||
|
||||
parentNode: function () {
|
||||
return this.firstNode().parentNode;
|
||||
},
|
||||
|
||||
// Built Components are either attached or detached.
|
||||
// An attached Component is assumed to be part of
|
||||
// its parent's DOM tree (or for a root Component, the
|
||||
// document). A detached Component lives in its own private
|
||||
// offscreen DIV. Components start detached (offscreen) but
|
||||
// are typically attached immediately. They may then be
|
||||
// attached and detached at will, which inserts and removes
|
||||
// them from the parent's DOM. A detached Component has
|
||||
// a functional DOM and can have attached and detached
|
||||
// children.
|
||||
//
|
||||
// Components should only be inserted into the DOM by calling
|
||||
// `append`, `insertBefore`, et al. on their parent, or
|
||||
// for a root using `attachRoot`.
|
||||
isAttached: false,
|
||||
|
||||
// DIV holding offscreen content (when component is built and not attached).
|
||||
// It's a DIV rather than a fragment so that jQuery can run against it.
|
||||
_offscreen: null,
|
||||
|
||||
// `content` and `elseContent` must be Components or functions
|
||||
// that return components.
|
||||
content: Empty,
|
||||
elseContent: Empty,
|
||||
|
||||
// The `render` method is overridden by compiled templates
|
||||
// and other components to declare the component's
|
||||
// constituent HTML/DOM and children. It's called during
|
||||
// building on the client, and it can also be used on the
|
||||
// client or server to generate initial HTML.
|
||||
render: function (buf) {
|
||||
buf.write(this.content);
|
||||
},
|
||||
|
||||
_populate: function (div) {
|
||||
var self = this;
|
||||
|
||||
var buf = makeRenderBuffer(self);
|
||||
self.render(buf);
|
||||
|
||||
var html = buf.getHtml();
|
||||
|
||||
$(div).append(html);
|
||||
|
||||
// returns info object with {start, end}
|
||||
return buf.wireUpDOM(div);
|
||||
},
|
||||
|
||||
build: function () {
|
||||
var self = this;
|
||||
|
||||
self._requireNotDestroyed();
|
||||
if (self.isBuilt)
|
||||
throw new Error("Component already built");
|
||||
|
||||
if (! self.isInited)
|
||||
self.makeRoot();
|
||||
|
||||
self._rebuilder = self.autorun(function (c) {
|
||||
// record set of children that existed before,
|
||||
// or null (for efficiency)
|
||||
var oldChildren = null;
|
||||
for (var k in self.children)
|
||||
(oldChildren || (oldChildren = {}))[k] = true;
|
||||
|
||||
if (c.firstRun) {
|
||||
var div = makeSafeDiv();
|
||||
// capture reactivity:
|
||||
var info = self._populate(div);
|
||||
|
||||
if (! div.firstChild)
|
||||
div.appendChild(createEmptyComment());
|
||||
|
||||
self._offscreen = div;
|
||||
self.start = info.start || div.firstChild;
|
||||
self.end = info.end || div.lastChild;
|
||||
} else {
|
||||
// capture reactivity:
|
||||
self._rebuild(c.builtChildren);
|
||||
}
|
||||
|
||||
var newChildren = null;
|
||||
for (var k in self.children)
|
||||
if (! (oldChildren && oldChildren[k]))
|
||||
(newChildren || (newChildren = {}))[k] = self.children[k];
|
||||
|
||||
// `builtChildren` is actually children *added* during build
|
||||
c.builtChildren = newChildren;
|
||||
|
||||
// don't capture dependencies, but provide a
|
||||
// parent autorun (so that any autoruns created
|
||||
// from a built callback are stopped on rebuild)
|
||||
var x = Deps.autorun(function (c) {
|
||||
if (c.firstRun) {
|
||||
self.isBuilt = true;
|
||||
self._callOnNextBuiltCallbacks();
|
||||
|
||||
// FAKE-ISH (NON-DELEGATED) EVENT MAP STUFF
|
||||
if (self._events && self._events.length) {
|
||||
_.each(self._events, function (info) {
|
||||
$(self.firstNode().parentNode).find(info.selector).on(
|
||||
info.type, function (evt) {
|
||||
if (self.containsElement(evt.currentTarget))
|
||||
info.handler(evt);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
callChainedCallback(self, 'rendered');
|
||||
}
|
||||
});
|
||||
Deps.onInvalidate(function () {
|
||||
x.stop();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Don't call this directly. It implements the re-run of the
|
||||
// build autorun, so it assumes it's already inside the appropriate
|
||||
// reactive computation.
|
||||
//
|
||||
// `builtChildren` is a map of children that were added during
|
||||
// the previous build (as opposed to at some other time, such as
|
||||
// earlier from an `init` callback).
|
||||
_rebuild: function (builtChildren) {
|
||||
var self = this;
|
||||
|
||||
if (! (self.isBuilt && ! self.isDestroyed))
|
||||
throw new Error("Assertion failed in _rebuild");
|
||||
|
||||
// Should work whether this component is detached or attached!
|
||||
// In other words, it may reside in an offscreen element.
|
||||
|
||||
var firstNode = self.firstNode();
|
||||
var lastNode = self.lastNode();
|
||||
var parentNode = lastNode.parentNode;
|
||||
var nextNode = lastNode.nextSibling || null;
|
||||
var prevNode = firstNode.previousSibling || null;
|
||||
|
||||
// for efficiency, do a quick check to see if we've *ever*
|
||||
// had children or if we are still using the prototype's
|
||||
// empty object.
|
||||
if (self.children !== UI.Component.children) {
|
||||
Deps.nonreactive(function () {
|
||||
// kill children from last render, and also any
|
||||
// attached children
|
||||
var children = self.children;
|
||||
for (var k in children) {
|
||||
var child = children[k];
|
||||
if (builtChildren && builtChildren[k]) {
|
||||
// destroy first, then remove
|
||||
// (which doesn't affect DOM, which we will
|
||||
// remove all at once)
|
||||
child.destroy();
|
||||
self.remove(child);
|
||||
} else if (child.isAttached) {
|
||||
// detach the child; we don't have a good way
|
||||
// of keeping this from affecting the DOM
|
||||
child.detach();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var oldNodes = [];
|
||||
// must be careful as call to `detach` above may have
|
||||
// must with firstNode or lastNode
|
||||
for (var n = prevNode ? prevNode.nextSibling :
|
||||
parentNode.firstChild;
|
||||
n && n !== nextNode;
|
||||
n = n.nextSibling)
|
||||
oldNodes.push(n);
|
||||
|
||||
$(oldNodes).remove();
|
||||
|
||||
var div = makeSafeDiv();
|
||||
// set `self.start` to null so that calls to `attach` from
|
||||
// `_populate` don't try to do start/end pointer logic.
|
||||
self.start = self.end = null;
|
||||
var info = self._populate(div);
|
||||
if (! div.firstChild)
|
||||
div.appendChild(createEmptyComment());
|
||||
|
||||
self.start = info.start || div.firstChild;
|
||||
self.end = info.end || div.lastChild;
|
||||
insertNodesBefore(div.childNodes, parentNode, nextNode);
|
||||
},
|
||||
|
||||
// Internal method used by insertBefore, render buffer,
|
||||
// and attachRoot.
|
||||
_attach: function (parentNode, beforeNode) {
|
||||
var self = this;
|
||||
|
||||
self._requireNotDestroyed();
|
||||
|
||||
if (! self.isInited)
|
||||
throw new Error("Component to attach must be inited");
|
||||
|
||||
if (! self.isBuilt)
|
||||
self.build();
|
||||
|
||||
if (self.isAttached)
|
||||
throw new Error("Component already attached; must be detached first");
|
||||
|
||||
if ((! parentNode) || ! parentNode.nodeType)
|
||||
throw new Error("first argument to attach must be a Node");
|
||||
if (beforeNode && ! beforeNode.nodeType)
|
||||
throw new Error("second argument to attach must be a Node" +
|
||||
" if given");
|
||||
|
||||
insertNodesBefore(self._offscreen.childNodes,
|
||||
parentNode, beforeNode);
|
||||
|
||||
self._offscreen = null;
|
||||
self.isAttached = true;
|
||||
|
||||
var parent = self.parent;
|
||||
// We could be a root (and have no parent). Parent could
|
||||
// theoretically be destroyed, or not yet built (if we
|
||||
// are currently building).
|
||||
//
|
||||
// We use a falsy `parent.start` as a cue that this is a
|
||||
// rebuild, another case where we skip the start/end adjustment
|
||||
// logic.
|
||||
//
|
||||
// `attach` is special in that it is used during building
|
||||
// and rebuilding; it is not required that the parent is
|
||||
// completely built.
|
||||
if (parent && parent.isBuilt && ! parent.isDestroyed &&
|
||||
parent.start) {
|
||||
if (parent.isEmpty()) {
|
||||
var comment = parent.start;
|
||||
parent.start = parent.end = self;
|
||||
comment.parentNode.removeChild(comment);
|
||||
} else {
|
||||
if (parent.firstNode() === self.lastNode().nextSibling)
|
||||
parent.start = self;
|
||||
if (parent.lastNode() === self.firstNode().previousSibling)
|
||||
parent.end = self;
|
||||
}
|
||||
}
|
||||
|
||||
callChainedCallback(self, 'attached');
|
||||
},
|
||||
|
||||
isEmpty: function () {
|
||||
this._requireBuilt();
|
||||
this._requireNotDestroyed();
|
||||
|
||||
var start = this.start;
|
||||
return start === this.end &&
|
||||
! UI.isComponent(start) && isEmptyComment(start);
|
||||
},
|
||||
|
||||
// # component.detach()
|
||||
//
|
||||
// Component must be built and attached. Removes this component's
|
||||
// DOM and puts it into an offscreen storage. Updates the parent's
|
||||
// `start` and `end` and populates it with a comment if it becomes
|
||||
// empty.
|
||||
detach: function (_forDestruction) {
|
||||
var self = this;
|
||||
|
||||
self._requireBuilt();
|
||||
self._requireNotDestroyed();
|
||||
if (! self.isAttached)
|
||||
throw new Error("Component not attached");
|
||||
|
||||
var parent = self.parent;
|
||||
var A = self.firstNode();
|
||||
var B = self.lastNode();
|
||||
|
||||
// We could be a root (and have no parent). Parent could
|
||||
// theoretically be destroyed, or not yet built.
|
||||
if (parent && parent.isBuilt && ! parent.isDestroyed) {
|
||||
// Do some magic to update the
|
||||
// firstNode and lastNode. The main issue is we need to
|
||||
// know if the new firstNode or lastNode is part of a
|
||||
// child component or not, because if it is, we need to
|
||||
// set `start` or `end` to the component rather than the
|
||||
// node. Since we don't have any pointers from the DOM
|
||||
// and can't make any assumptions about the structure of
|
||||
// the component, we have to do a search over our children.
|
||||
// Repeatedly detaching the first or last of O(N) top-level
|
||||
// components is asymptotically bad -- O(n^2).
|
||||
//
|
||||
// Components that manage large numbers of top-level components
|
||||
// should override _findStartComponent and _findEndComponent.
|
||||
if (parent.start === self) {
|
||||
if (parent.end === self) {
|
||||
// we're emptying the parent; populate it with a
|
||||
// comment in an appropriate place (adjacent to
|
||||
// the not-yet-extracted DOM) and set pointers.
|
||||
var comment = createEmptyComment();
|
||||
A.parentNode.insertBefore(comment, A);
|
||||
parent.start = parent.end = comment;
|
||||
} else {
|
||||
// Removing component at the beginning of parent.
|
||||
//
|
||||
// Figure out if the following top-level node is the
|
||||
// first node of a Component.
|
||||
var newFirstNode = B.nextSibling;
|
||||
parent.start = parent._findStartComponent(newFirstNode);
|
||||
if (! (parent.start && parent.start.firstNode() === newFirstNode))
|
||||
parent.start = newFirstNode;
|
||||
}
|
||||
} else if (parent.end === self) {
|
||||
// Removing component at the end of parent.
|
||||
//
|
||||
// Figure out if the previous top-level node is the
|
||||
// last node of a Component.
|
||||
var newLastNode = A.previousSibling;
|
||||
parent.end = parent._findEndComponent(newLastNode);
|
||||
if (! (parent.end && parent.end.lastNode() === newLastNode))
|
||||
parent.end = newLastNode;
|
||||
}
|
||||
}
|
||||
|
||||
var nodes = [];
|
||||
for (var n = A; n !== B; n = n.nextSibling)
|
||||
nodes.push(n);
|
||||
nodes.push(B);
|
||||
|
||||
if (_forDestruction === true) {
|
||||
$(nodes).remove();
|
||||
} else {
|
||||
// Move nodes into an offscreen div, preserving
|
||||
// any event handlers and data associated with the nodes.
|
||||
var div = makeSafeDiv();
|
||||
$(div).append(nodes);
|
||||
|
||||
self._offscreen = div;
|
||||
self.isAttached = false;
|
||||
|
||||
callChainedCallback(self, 'detached');
|
||||
}
|
||||
},
|
||||
|
||||
// # component.append(childOrDom)
|
||||
//
|
||||
// childOrDom is a Component, or node, or HTML string,
|
||||
// or array of elements (various things a la jQuery).
|
||||
//
|
||||
// Given `child`: It must be a child of this component or addable
|
||||
// as one. Builds it if necessary. Attaches it at the end of
|
||||
// this component. Updates `start` and `end` of this component.
|
||||
|
||||
append: function (childOrDom) {
|
||||
this.insertAfter(childOrDom, this.lastNode());
|
||||
},
|
||||
|
||||
prepend: function (childOrDom) {
|
||||
this.insertBefore(childOrDom, this.firstNode());
|
||||
},
|
||||
|
||||
// # component.insertBefore(childOrDom, before, parentNode)
|
||||
//
|
||||
// `before` is a Component or node. parentNode is only used
|
||||
// if `before` is null. It defaults to the Component's
|
||||
// parentNode.
|
||||
//
|
||||
// See append.
|
||||
|
||||
insertBefore: function (childOrDom, before, parentNode) {
|
||||
var self = this;
|
||||
|
||||
self._requireBuilt();
|
||||
self._requireNotDestroyed();
|
||||
|
||||
if (UI.isComponent(before)) {
|
||||
before = before.firstNode();
|
||||
} else if (! before) {
|
||||
if ((! parentNode) || (parentNode === self.parentNode())) {
|
||||
before = self.lastNode().nextSibling;
|
||||
parentNode = parentNode || self.parentNode();
|
||||
}
|
||||
}
|
||||
parentNode = parentNode || before.parentNode;
|
||||
|
||||
if (UI.isComponent(childOrDom)) {
|
||||
var child = childOrDom;
|
||||
|
||||
child._requireNotDestroyed();
|
||||
|
||||
if (! child.isInited) {
|
||||
self.add(child);
|
||||
} else if (child.parent !== self) {
|
||||
throw new Error("Can only append/prepend/insert" +
|
||||
" a child (or a component addable as one)");
|
||||
}
|
||||
|
||||
child._attach(parentNode, before);
|
||||
} else {
|
||||
var nodes;
|
||||
if (typeof childOrDom === 'string') {
|
||||
nodes = $.parseHTML(childOrDom) || [];
|
||||
} else if (childOrDom.nodeType) {
|
||||
nodes = [childOrDom];
|
||||
} else if (typeof childOrDom.length === 'number' &&
|
||||
typeof childOrDom === 'object') {
|
||||
nodes = Array.prototype.slice.call(childOrDom);
|
||||
} else {
|
||||
throw new Error(
|
||||
"Expected HTML, DOM node, array, or Component, found " +
|
||||
childOrDom);
|
||||
}
|
||||
|
||||
if (nodes.length) {
|
||||
insertNodesBefore(nodes, parentNode, before);
|
||||
|
||||
if (self.isEmpty()) {
|
||||
var comment = self.start;
|
||||
comment.parentNode.removeChild(comment);
|
||||
self.start = nodes[0];
|
||||
self.end = nodes[nodes.length - 1];
|
||||
} else if (before === self.firstNode()) {
|
||||
self.start = nodes[0];
|
||||
} else if (nodes[0].previousSibling === self.lastNode()) {
|
||||
self.end = nodes[nodes.length - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
insertAfter: function (childOrDom, after, parentNode) {
|
||||
var self = this;
|
||||
|
||||
if (UI.isComponent(after)) {
|
||||
after = after.lastNode();
|
||||
} else if (! after) {
|
||||
if ((! parentNode) || (parentNode === self.parentNode())) {
|
||||
after = self.firstNode().previousSibling;
|
||||
parentNode = parentNode || self.parentNode();
|
||||
}
|
||||
}
|
||||
parentNode = parentNode || after.parentNode;
|
||||
|
||||
this.insertBefore(childOrDom, after.nextSibling, parentNode);
|
||||
},
|
||||
|
||||
// Is `elem` between `this.firstNode()` and `this.lastNode()`?
|
||||
containsElement: function (elem) {
|
||||
if (elem.nodeType !== 1)
|
||||
throw new Error("containsElement requires an Element node");
|
||||
|
||||
var self = this;
|
||||
self._requireBuilt();
|
||||
self._requireNotDestroyed();
|
||||
|
||||
var firstNode = self.firstNode();
|
||||
var lastNode = self.lastNode();
|
||||
if (! elementContains(firstNode.parentNode, elem))
|
||||
return false;
|
||||
|
||||
// because compareElementIndex only works on elements, we find
|
||||
// previous and next element siblings. (previousSiblingElement and
|
||||
// nextSiblingElement do the same thing but they neither work on
|
||||
// IE8 nor are they available on text nodes)
|
||||
var prevElem = firstNode.previousSibling;
|
||||
while (prevElem && prevElem.nodeType !== 1)
|
||||
prevElem = prevElem.previousSibling;
|
||||
var nextElem = lastNode.nextSibling;
|
||||
while (nextElem && nextElem.nodeType !== 1)
|
||||
nextElem = nextElem.nextSibling;
|
||||
|
||||
// element must not be "at or before" prevElem
|
||||
if (prevElem && compareElementIndex(prevElem, elem) >= 0)
|
||||
return false;
|
||||
// element must not be "at or after" nextElem
|
||||
if (nextElem && compareElementIndex(elem, nextElem) >= 0)
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
|
||||
// Take element `elem` and find the innermost component containing
|
||||
// it which is either this component or a descendent of this component.
|
||||
findByElement: function (elem) {
|
||||
if (elem.nodeType !== 1)
|
||||
throw new Error("findByElement requires an Element node");
|
||||
|
||||
var self = this;
|
||||
self._requireBuilt();
|
||||
|
||||
if (! self.containsElement(elem))
|
||||
return null;
|
||||
|
||||
var children = self.children;
|
||||
// XXX linear-time scan through all child components,
|
||||
// running DOM comparison methods that may themselves
|
||||
// be O(N). Not sure what the constants are like.
|
||||
for (var k in children) {
|
||||
var child = children[k];
|
||||
if (child.isBuilt && (! child.isDestroyed) &&
|
||||
child.isAttached) {
|
||||
var found = child.findByElement(elem);
|
||||
if (found)
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
},
|
||||
*/
|
||||
/*
|
||||
_extend(UI.Component, {
|
||||
$: function (selector) {
|
||||
var self = this;
|
||||
|
||||
UI._requireDom(self);
|
||||
UI._requireNotDestroyed(self);
|
||||
|
||||
var firstNode = self.dom.getFirstNode();
|
||||
var parentNode = firstNode.parentNode;
|
||||
var prevNode = firstNode.previousSibling;
|
||||
var nextNode = self.dom.getLastNode().nextSibling;
|
||||
|
||||
// Don't assume `results` has jQuery API; a plain array
|
||||
// should do just as well. However, if we do have a jQuery
|
||||
// array, we want to end up with one also.
|
||||
var results = $(selector, self.dom.parentNode());
|
||||
|
||||
// Function that selects only elements that are actually in this
|
||||
// Component, out of elements that are descendents of the Component's
|
||||
// parentNode in the DOM (but may be, or descend from, siblings of
|
||||
// this Component's top-level nodes that aren't between `start` and
|
||||
// `end` inclusive).
|
||||
var filterFunc = function (elem) {
|
||||
// handle jQuery's arguments to filter, where the node
|
||||
// is in `this` and the index is the first argument.
|
||||
if (typeof elem === 'number')
|
||||
elem = this;
|
||||
|
||||
if (prevNode && compareElementIndex(prevNode, elem) >= 0)
|
||||
return false;
|
||||
if (nextNode && compareElementIndex(elem, nextNode) >= 0)
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
if (! results.filter) {
|
||||
// not a jQuery array, and not a browser with
|
||||
// Array.prototype.filter (e.g. IE <9)
|
||||
var newResults = [];
|
||||
for (var i = 0; i < results.length; i++) {
|
||||
var x = results[i];
|
||||
if (filterFunc(x))
|
||||
newResults.push(x);
|
||||
}
|
||||
results = newResults;
|
||||
} else {
|
||||
// `results.filter` is either jQuery's or ECMAScript's `filter`
|
||||
results = results.filter(filterFunc);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
});
|
||||
*/
|
||||
/*
|
||||
autorun: function (compFunc) {
|
||||
var self = this;
|
||||
|
||||
self._requireNotDestroyed();
|
||||
|
||||
// XXX so many nested functions... Deps.nonreactive here
|
||||
// feels heavyweight, but we don't want building a child
|
||||
// while building a parent to mean that when the parent
|
||||
// rebuilds, the child automatically does.
|
||||
var c = Deps.nonreactive(function () {
|
||||
return Deps.autorun(compFunc);
|
||||
});
|
||||
|
||||
self._computations = self._computations || [];
|
||||
self._computations.push(c);
|
||||
|
||||
return c;
|
||||
},
|
||||
|
||||
replaceChild: function (oldChild, newChild) {
|
||||
var self = this;
|
||||
|
||||
self._requireBuilt();
|
||||
self._requireNotDestroyed();
|
||||
oldChild._requireBuilt();
|
||||
oldChild._requireNotDestroyed();
|
||||
if (! oldChild.isAttached)
|
||||
throw new Error("Child to replace must be attached");
|
||||
|
||||
var lastNode = oldChild.lastNode();
|
||||
var parentNode = lastNode.parentNode;
|
||||
var nextNode = lastNode.nextSibling;
|
||||
|
||||
oldChild.remove();
|
||||
self.insertBefore(newChild, nextNode, parentNode);
|
||||
},
|
||||
|
||||
swapChild: function (oldChild, newChild) {
|
||||
var self = this;
|
||||
|
||||
self._requireBuilt();
|
||||
self._requireNotDestroyed();
|
||||
oldChild._requireBuilt();
|
||||
oldChild._requireNotDestroyed();
|
||||
if (! oldChild.isAttached)
|
||||
throw new Error("Child to swap out must be attached");
|
||||
|
||||
var lastNode = oldChild.lastNode();
|
||||
var parentNode = lastNode.parentNode;
|
||||
var nextNode = lastNode.nextSibling;
|
||||
|
||||
oldChild.detach();
|
||||
self.insertBefore(newChild, nextNode, parentNode);
|
||||
},
|
||||
|
||||
_onNextBuilt: function (cb) {
|
||||
var self = this;
|
||||
var cbs = self._builtCallbacks;
|
||||
if (! cbs)
|
||||
cbs = self._builtCallbacks = [];
|
||||
cbs.push(cb);
|
||||
},
|
||||
|
||||
_callOnNextBuiltCallbacks: function () {
|
||||
var self = this;
|
||||
var cbs = self._builtCallbacks;
|
||||
if (cbs) {
|
||||
for (var i = 0, N = cbs.length; i < N; i++)
|
||||
cbs[i](self);
|
||||
self._builtCallbacks.length = 0;
|
||||
}
|
||||
},
|
||||
|
||||
// Return a child whose firstNode() may be `firstNode`.
|
||||
// If such a child exists, it must be found by this function.
|
||||
// If no such child exists, this function may return null
|
||||
// or a wrong guess at a child. Subclasses that know,
|
||||
// for example, the earliest child component in the DOM
|
||||
// at all times can supply that as a guess.
|
||||
_findStartComponent: function (firstNode) {
|
||||
var children = this.children;
|
||||
// linear-time scan until found
|
||||
for (var k in children) {
|
||||
var c = children[k];
|
||||
if (c.isBuilt && c.isAttached &&
|
||||
c.firstNode() === firstNode)
|
||||
return c;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_findEndComponent: function (lastNode) {
|
||||
var children = this.children;
|
||||
// linear-time scan until found
|
||||
for (var k in children) {
|
||||
var c = children[k];
|
||||
if (c.isBuilt && c.isAttached &&
|
||||
c.lastNode() === lastNode)
|
||||
return c;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// If Component is ever emptied, it gets an empty comment node.
|
||||
// This case is treated specially and the comment is removed
|
||||
// if you then, say, append a node or component. However,
|
||||
// the developer doing advanced things needs to be aware of
|
||||
// this case or they may be surprised there is a node there
|
||||
// that they didn't put there, e.g. if they call remove() on
|
||||
// the last component and then start inserting DOM nodes
|
||||
// manually.
|
||||
|
||||
// You are free to manipulate the DOM of your component, excluding
|
||||
// the regions that belong to child components, though if you do it
|
||||
// using jQuery or any other means besides the methods here
|
||||
// (attach, detach, append, prepend, insert), you are responsible
|
||||
// for ensuring that `start` and `end` point to the first and last
|
||||
// *node or Component* at the top level of the component's DOM,
|
||||
// and that the component does not become empty
|
||||
|
||||
});
|
||||
|
||||
|
||||
var emptyCommentProp = 'meteor-ui-empty';
|
||||
var createEmptyComment = function (beforeNode) {
|
||||
var x = document.createComment("empty");
|
||||
x[emptyCommentProp] = true;
|
||||
return x;
|
||||
};
|
||||
var isEmptyComment = function (node) {
|
||||
return node.nodeType === 8 && node[emptyCommentProp] === true;
|
||||
};
|
||||
*/
|
||||
// Returns 0 if the nodes are the same or either one contains the other;
|
||||
// otherwise, -1 if a comes before b, or else 1 if b comes before a in
|
||||
// document order.
|
||||
@@ -1079,48 +218,6 @@ var compareElementIndex = function (a, b) {
|
||||
return (a.sourceIndex < b.sourceIndex ? -1 : 1);
|
||||
}
|
||||
};
|
||||
/*
|
||||
// Returns true if element a contains node b and is not node b.
|
||||
var elementContains = function (a, b) {
|
||||
if (a.nodeType !== 1) // ELEMENT
|
||||
return false;
|
||||
if (a === b)
|
||||
return false;
|
||||
|
||||
if (a.compareDocumentPosition) {
|
||||
return a.compareDocumentPosition(b) & 0x10;
|
||||
} else {
|
||||
// Should be only old IE and maybe other old browsers here.
|
||||
// Modern Safari has both functions but seems to get contains() wrong.
|
||||
// IE can't handle b being a text node. We work around this
|
||||
// by doing a direct parent test now.
|
||||
b = b.parentNode;
|
||||
if (! (b && b.nodeType === 1)) // ELEMENT
|
||||
return false;
|
||||
if (a === b)
|
||||
return true;
|
||||
|
||||
return a.contains(b);
|
||||
}
|
||||
};
|
||||
|
||||
var insertNodesBefore = function (nodes, parentNode, beforeNode) {
|
||||
if (beforeNode) {
|
||||
$(nodes).insertBefore(beforeNode);
|
||||
} else {
|
||||
$(nodes).appendTo(parentNode);
|
||||
}
|
||||
};
|
||||
|
||||
var makeSafeDiv = function () {
|
||||
// create a DIV in a DocumentFragment, where the DocumentFragment
|
||||
// is created by jQuery, which uses tricks to create a "safe"
|
||||
// fragment for HTML5 tags in IE <9.
|
||||
var div = document.createElement("DIV");
|
||||
var frag = $.buildFragment([div], document);
|
||||
return div;
|
||||
};
|
||||
*/
|
||||
|
||||
UI.body = UI.Component.extend({
|
||||
kind: 'body',
|
||||
|
||||
@@ -88,87 +88,6 @@ UI.render = function (kind, props, parentComp) {
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
// Render an instance of a component kind directly into the DOM,
|
||||
// optionally with a parentComp (for e.g. name resolution).
|
||||
// `parentNode` must be an ELEMENT, not a fragment.
|
||||
UI.renderTo = function (kind, props,
|
||||
parentNode, beforeNode, parentComp) {
|
||||
// XXX too bad we don't validate arguments before mutating
|
||||
// the DOM
|
||||
var range = new DomRange;
|
||||
// Insert new DomRange's start/end markers
|
||||
DomRange.insert(range, parentNode, beforeNode);
|
||||
|
||||
// var nodes = range.getNodes();
|
||||
// for (var i = 0, N = nodes.length; i < N; i++)
|
||||
// parentNode.insertBefore(nodes[i],
|
||||
// IE needs null
|
||||
// beforeNode || null);
|
||||
|
||||
|
||||
return UI.renderToRange(kind, props, range, parentComp, true);
|
||||
};
|
||||
|
||||
UI.renderToRange = function (kind, props, range, parentComp, _XXXrenderTo) {
|
||||
|
||||
// XXX Handle case where kind is function reactively.
|
||||
// Reuse the same DomRange.
|
||||
if ((typeof kind) === 'function') {
|
||||
// XXX scope this autorun
|
||||
Deps.autorun(function (c) {
|
||||
if (c.firstRun) {
|
||||
kind = kind();
|
||||
} else {
|
||||
debugger; // XXX
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (kind === null)
|
||||
return null;
|
||||
if (! UI.isComponent(kind))
|
||||
throw new Error("Expected Component, function, or null");
|
||||
if (kind.isInited)
|
||||
throw new Error("Expected uninited Component");
|
||||
|
||||
var comp = kind.extend(props);
|
||||
|
||||
comp.dom = range;
|
||||
// XXXXXX
|
||||
if (_XXXrenderTo) {
|
||||
range.component = comp;
|
||||
comp.parented();
|
||||
}
|
||||
// XXXXXX
|
||||
comp.isInited = true;
|
||||
if (parentComp)
|
||||
comp.parent = parentComp;
|
||||
|
||||
if (comp.init)
|
||||
comp.init();
|
||||
|
||||
if (comp.render) {
|
||||
// XXX scope this autorun
|
||||
Deps.autorun(function (c) {
|
||||
if (! c.firstRun) {
|
||||
range.removeAll();
|
||||
}
|
||||
var buf = makeRenderBuffer();
|
||||
comp.render(buf);
|
||||
buf.build(comp);
|
||||
});
|
||||
}
|
||||
|
||||
// XXX think about this callback's semantics
|
||||
if (comp.rendered)
|
||||
comp.rendered();
|
||||
|
||||
return comp;
|
||||
|
||||
};
|
||||
*/
|
||||
|
||||
var ESCAPED_CHARS_UNQUOTED_REGEX = /[&<>]/g;
|
||||
var ESCAPED_CHARS_QUOTED_REGEX = /[&<>"]/g;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user