diff --git a/client.js b/client.js index 6d72d03..3f66353 100644 --- a/client.js +++ b/client.js @@ -6,7 +6,10 @@ function() { if (self.collections[name]) { return self.collections[name]; } else { - self.collections[name] = new Meteor.Collection(name); + if (name === "users") + self.collections[name] = Meteor.users; + else + self.collections[name] = new Meteor.Collection(name); return self.collections[name]; } } @@ -42,63 +45,184 @@ function($rootScope, MeteorCollections, $meteorObject) { setTimeout($rootScope.apply, 0); } }, 100); - function CollectionFactory(collection) { - var collection = MeteorCollections.getCollection(collection); - var value = []; + function CollectionFactory(collection, populates) { + var collection = MeteorCollections.getCollection(collection), + values = [], + value = {}, + handle, + populateHandles = {}, + populatedDocuments = {}, + populateCollections = {}; + + + populates = populates || []; + populates = [].concat(populates); + + populates = _.map(populates, function(populate){ + if (_.isObject(populate)){ + var property = _.chain(populate).keys().first().value(); + populateCollections[property] = populate[property]; + return property; + } else { + populateCollections[populate] = + populate[0].toUpperCase() + populate.slice(1) + 's'; + return populate; + } + }); function Collection(value) { value = {}; } + function stopPopulates(document, populate) { + var id = document[populate]; + + if (populatedDocuments[populate] && populatedDocuments[populate][id]) { + delete populatedDocuments[populate][id][document._id]; + + if (_.isEmpty(populatedDocuments[populate][id]) && + populateHandles[populate][id]) { + populateHandles[populate][id].stop(); + populateHandles[populate][id] = undefined; + } + } + } + + function stopAllPopulates(document) { + _.each(populates, function(populate){ + stopPopulates(document, populate); + }); + } + + function updatePopulatedDocuments(join, id, jdocument){ + _.each(populatedDocuments[join][id], function(document){ + document[join] = jdocument; + if (value) + angular.extend(value, document); + }); + $rootScope.apply(); + } + + function populate(document){ + _.each(_.intersection(populates, _.keys(document)), function(join){ + var id = document[join]; + var jColl = populateCollections[join]; + + jColl = MeteorCollections.getCollection(jColl); + + if (!populateHandles[join]) + populateHandles[join] = {}; + + if (!populatedDocuments[join]) + populatedDocuments[join] = {}; + + if (!populatedDocuments[join][id]) + populatedDocuments[join][id] = {}; + + populatedDocuments[join][id][document._id] = document; + + if (!populateHandles[join][id]) { + populateHandles[join][id] = jColl.find({'_id' : id}).observe({ + "added" : function(jdocument) { + updatePopulatedDocuments(join, id, jdocument); + }, + "changed" : function(jdocument) { + updatePopulatedDocuments(join, id, jdocument); + }, + "removed" : function() { + updatePopulatedDocuments(join, id, undefined); + }, + }); + } else { + document[join] = jColl.findOne(id); + } + + }); + } + + function depopulate(document) { + _.each(_.intersection(populates, _.keys(document)), function(populate){ + if (document[populate] && document[populate]._id) + document[populate] = document[populate]._id; + }); + } + + function getDeletedKeys(obj){ + var keys = {}; + _.each(obj, function(value, key){ + if (value === undefined) + keys[key] = 1; + }); + return _.isEmpty(keys)?undefined:keys; + } Collection.observe = function(cursor, array) { - cursor.observe({ + if (handle) { + handle.stop(); + handle = undefined; + } + + handle = cursor.observe({ "addedAt" : function(document, atIndex, before) { - //console.log(document); + stopAllPopulates(document); + populate(document); if (!array) { - value = new $meteorObject(collection, document); + angular.extend(value, document); } if (array) { - value[atIndex] = new $meteorObject(collection, document); + values[atIndex] = new $meteorObject(collection, document); } $rootScope.apply(); }, "changedAt" : function(newDocument, oldDocument, atIndex) { - value[atIndex] = new $meteorObject(collection, newDocument); + stopAllPopulates(oldDocument); + populate(newDocument); + if (!array) { + angular.extend(value, newDocument); + } else { + values[atIndex] = new $meteorObject(collection, newDocument); + } $rootScope.apply(); }, "removedAt" : function(oldDocument, atIndex) { - - value.splice(atIndex, 1); + stopAllPopulates(oldDocument); + values.splice(atIndex, 1); $rootScope.apply(); } - }) + }); } Collection.find = function(selector, options, callback) { - value = this instanceof Collection ? this : []; + value = undefined; this.observe(collection.find(selector, options), true); - return value; + return values; } Collection.findOne = function(selector, options, callback) { - value = this instanceof Collection ? this : {}; - value = new $meteorObject(collection,collection.find(selector,options).fetch()[0]); + value = new $meteorObject(collection, collection.find(selector,options).fetch()[0]); this.observe(collection.find(selector, options), false); return value; } Collection.insert = function(values) { values = angular.copy(values); - cleanupAngularObject(values); + cleanupAngularObject(values); + depopulate(values); return collection.insert(values); } Collection.update = function(selector, updateValues) { updateValues = angular.copy(updateValues); - cleanupAngularObject(updateValues); - delete updateValues._id; - return collection.update(selector, { + cleanupAngularObject(updateValues); + delete updateValues._id; + + depopulate(updateValues); + var modifier = { $set : updateValues - }); + }; + var deletedKeys = getDeletedKeys(updateValues); + + if (deletedKeys) + modifier['$unset'] = deletedKeys; + return collection.update(selector, modifier); } Collection.remove = function(selector) { return collection.remove(selector); @@ -107,7 +231,7 @@ function($rootScope, MeteorCollections, $meteorObject) { } return CollectionFactory; -}]); +}]); /** Removes AngularJS transient properties from Object tree */ cleanupAngularObject = function(value) {