ddp.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. app.factory("DDP", ['$q', function ($q) {
  2. function DDP(wsUri) {
  3. this.VERSIONS = ["pre1"];
  4. this.wsUri = wsUri;
  5. this.sock;
  6. this.defs = {}; // { deferred_id => deferred_object }
  7. this.subs = {}; // { pub_name => deferred_id }
  8. this.watchers = {}; // { coll_name => [cb1, cb2, ...] }
  9. this.collections = {}; // { coll_name => {docId => {doc}, docId => {doc}, ...} }
  10. }
  11. DDP.prototype._Ids = function() {
  12. var count = 0;
  13. return {
  14. next: function() {
  15. return ++count + '';
  16. }
  17. }
  18. }();
  19. DDP.prototype.connect = function() {
  20. var self = this;
  21. var conn = new $q.defer();
  22. self.sock = new WebSocket(self.wsUri);
  23. self.sock.onopen = function() {
  24. self.send({
  25. msg: 'connect',
  26. version: self.VERSIONS[0],
  27. support: self.VERSIONS
  28. });
  29. };
  30. self.sock.onerror = function(err) {
  31. conn.reject(err);
  32. };
  33. self.sock.onmessage = function(msg) {
  34. var data = JSON.parse(msg.data);
  35. console.log(msg);
  36. switch (data.msg) {
  37. case 'connected':
  38. conn.resolve(data);
  39. break;
  40. case 'result':
  41. self._resolveCall(data);
  42. break;
  43. case 'updated':
  44. console.log('%s:%o','updated',data);
  45. break;
  46. case 'changed':
  47. self._changeDoc(data);
  48. break;
  49. case 'added':
  50. self._addDoc(data);
  51. break;
  52. case 'removed':
  53. self._removeDoc(data);
  54. break;
  55. case 'ready':
  56. self._resolveSubs(data);
  57. break;
  58. case 'nosub':
  59. self._resolveNoSub(data);
  60. break;
  61. case 'addedBefore':
  62. self._addDoc(data);
  63. break;
  64. case 'movedBefore':
  65. console.log('%s:%o','movedBefore',data);
  66. break;
  67. }
  68. };
  69. return conn.promise;
  70. };
  71. DDP.prototype._resolveNoSub = function(data) {
  72. if (data.error) {
  73. var error = data.error;
  74. this.defs[data.id].reject(error.reason || 'Subscription not found');
  75. } else {
  76. this.defs[data.id].resolve();
  77. }
  78. };
  79. DDP.prototype._resolveCall = function(data) {
  80. if (data.error) {
  81. this.defs[data.id].reject(data.error.reason);
  82. } else if (typeof data.result !== 'undefined') {
  83. this.defs[data.id].resolve(data.result);
  84. }
  85. };
  86. DDP.prototype._resolveSubs = function(data) {
  87. var subIds = data.subs;
  88. for (var i = 0; i < subIds.length; i++) {
  89. this.defs[subIds[i]].resolve();
  90. }
  91. };
  92. DDP.prototype._changeDoc = function(msg) {
  93. var collName = msg.collection;
  94. console.log('%s %s', 'changeDoc change', collName);
  95. var id = msg.id;
  96. var fields = msg.fields;
  97. var cleared = msg.cleared;
  98. var coll = this.collections[collName];
  99. if (fields) {
  100. for (var k in fields) {
  101. coll[id][k] = fields[k];
  102. }
  103. } else if (cleared) {
  104. for (var i = 0; i < cleared.length; i++) {
  105. var fieldName = cleared[i];
  106. delete coll[id][fieldName];
  107. }
  108. }
  109. var changedDoc = coll[id];
  110. this._notifyWatchers(collName, changedDoc, id, msg.msg);
  111. };
  112. DDP.prototype._addDoc = function(msg) {
  113. var collName = msg.collection;
  114. console.log('%s %s', '_addDoc add', collName);
  115. var id = msg.id;
  116. if (!this.collections[collName]) {
  117. this.collections[collName] = {};
  118. }
  119. /* NOTE: Ordered docs will have a 'before' field containing the id of
  120. * the doc after it. If it is the last doc, it will be null.
  121. */
  122. this.collections[collName][id] = msg.fields;
  123. var changedDoc = this.collections[collName][id];
  124. this._notifyWatchers(collName, changedDoc, id, msg.msg);
  125. };
  126. DDP.prototype._removeDoc = function(msg) {
  127. var collName = msg.collection;
  128. var id = msg.id;
  129. var doc = this.collections[collName][id];
  130. var docCopy = JSON.parse(JSON.stringify(doc));
  131. delete this.collections[collName][id];
  132. this._notifyWatchers(collName, docCopy, id, msg.msg);
  133. };
  134. DDP.prototype._notifyWatchers = function(collName, changedDoc, docId, message) {
  135. changedDoc = JSON.parse(JSON.stringify(changedDoc)); // make a copy
  136. changedDoc._id = docId; // id might be useful to watchers, attach it.
  137. if (!this.watchers[collName]) {
  138. this.watchers[collName] = [];
  139. } else {
  140. for (var i = 0; i < this.watchers[collName].length; i++) {
  141. this.watchers[collName][i](changedDoc, message);
  142. }
  143. }
  144. };
  145. DDP.prototype._deferredSend = function(actionType, name, params) {
  146. var id = this._Ids.next();
  147. this.defs[id] = new $q.defer();
  148. var args = params || [];
  149. var o = {
  150. msg: actionType,
  151. params: args,
  152. id: id
  153. };
  154. if (actionType === 'method') {
  155. o.method = name;
  156. } else if (actionType === 'sub') {
  157. o.name = name;
  158. this.subs[name] = id;
  159. }
  160. this.send(o);
  161. return this.defs[id].promise;
  162. };
  163. DDP.prototype.call = function(methodName, params) {
  164. return this._deferredSend('method', methodName, params);
  165. };
  166. DDP.prototype.subscribe = function(pubName, params) {
  167. return this._deferredSend('sub', pubName, params);
  168. };
  169. DDP.prototype.unsubscribe = function(pubName) {
  170. this.defs[id] = new $.Deferred();
  171. if (!this.subs[pubName]) {
  172. this.defs[id].reject(pubName + " was never subscribed");
  173. } else {
  174. var id = this.subs[pubName];
  175. var o = {
  176. msg: 'unsub',
  177. id: id
  178. };
  179. this.send(o);
  180. }
  181. return this.defs[id].promise;
  182. };
  183. DDP.prototype.watch = function(collectionName, cb) {
  184. if (!this.watchers[collectionName]) {
  185. this.watchers[collectionName] = [];
  186. }
  187. this.watchers[collectionName].push(cb);
  188. };
  189. DDP.prototype.getCollection = function(collectionName) {
  190. return this.collections[collectionName] || null;
  191. }
  192. DDP.prototype.getDocument = function(collectionName, docId) {
  193. return this.collections[collectionName][docId] || null;
  194. }
  195. DDP.prototype.send = function(msg) {
  196. this.sock.send(JSON.stringify(msg));
  197. };
  198. DDP.prototype.close = function() {
  199. this.sock.close();
  200. };
  201. return DDP;
  202. }]);