1 var io = require('socket.io');
  2 var fileServer = require('./fileServer');
  3 var EventEmitter = require('events').EventEmitter;
  4 var nowUtil = require('./nowUtil').nowUtil;
  5 
  6 /**
  7  * @name nowjs
  8  * @constructor
  9  * @description The object returned by require('now').
 10  */
 11 var Now = function () {
 12   this.closures = {};
 13   this.groups = {};
 14   this.users = {};
 15   this.options = {
 16     clientWrite: true,
 17     autoHost: true,
 18     socketio: {"log level" : 1},
 19     client: {},
 20     scope: 'window',
 21     closureTimeout: 30000
 22   };
 23 };
 24 
 25 Now.prototype.__proto__ = EventEmitter.prototype;
 26 
 27 Now.prototype._readConfigFile = function (path) {
 28   path = path || process.cwd() + "/nowjs.json";
 29   try {
 30     var conf = require('fs').readFileSync(path), parsedConf;
 31     try {
 32       parsedConf = JSON.parse(conf);
 33     } catch (e) {
 34       //throw new logging.NowError("Malformed configuration file.");
 35       //throw e;
 36     }
 37     return parsedConf;
 38   } catch (err) {
 39     //throw e;
 40     //logging.log("No configuration file found");
 41     return undefined;
 42   }
 43 };
 44 
 45 /**
 46  * @memberOf nowjs#
 47  * @function
 48  * @name getClient
 49 
 50  * @description Retrieves a user from the given client ID and executes
 51  * of several actions in the context of that user.
 52 
 53  * @param {String} id The client ID associated with the target user.
 54  * @param {Function} callback Takes no arguments. Called in the
 55  * context of the user corresponding to the given id.
 56  * @example nowjs.getClient('1234567890' function () {
 57  *   this.now.receiveMessage('SERVER', 'Anything is possible with NowJS.');
 58  * });
 59  */
 60 Now.prototype.getClient = function (id, callback) {
 61   callback.apply(this.users[id]);
 62 };
 63 
 64 /**
 65  * @memberOf nowjs#
 66  * @function
 67  * @name getGroups
 68 
 69  * @description Retrieves a list (represented in Javascript as an
 70  * array) of all groups that have been created and passes it in to the
 71  * supplied callback.
 72 
 73  * @param {Function} callback Takes one argument, an array of all
 74  * groups that have been created.
 75 
 76  * @example nowjs.on('connect', function () {
 77  *   var self = this;
 78  *   getGroups(function (groups) {
 79  *     nowjs.getGroup(groups[Math.floor(groups.length * Math.random())]).addUser(self);
 80  *   });
 81  * });
 82  */
 83 Now.prototype.getGroups = function (callback) {
 84   callback(Object.keys(this.groups));
 85 };
 86 
 87 /**
 88  * @memberOf nowjs#
 89  * @function
 90  * @name getGroup
 91  * @description Retrieves and returns a group from its name, creating it if
 92  * necessary.
 93  * @param {String} name The name of the group to be retrieved.
 94  * @type Group
 95  * @example var new_group = nowjs.getGroup('a new group');
 96  */
 97 Now.prototype.getGroup = function (name) {
 98   if (!nowUtil.hasProperty(this.groups, name)) {
 99     this.groups[name] = new this.Group(name);
100     /**
101      * @name nowjs#newgroup
102      * @event
103      * @param {Group} group The group created by {@link nowjs#getGroup}.
104      * @description Called when a new group is created.
105      * @example nowjs.on('newgroup', function (group) {
106      *   console.log('You have successfully created the group `' + group.groupName + '`');
107      * });
108      */
109 
110     this.emit('newgroup', this.groups[name]);
111   }
112   return this.groups[name];
113 };
114 
115 /**
116  * @memberOf nowjs#
117  * @function
118  * @name removeGroup
119  * @description Removes all traces of a group.
120  * @param {String} name The name of the group to be retrieved.
121  * @type Group
122  * @example var new_group = nowjs.getGroup('a new group');
123  */
124 Now.prototype.removeGroup = function (name) {
125   /**
126    * @name nowjs#removegroup
127    * @event
128    * @param {Group} group The group removed by {@link nowjs#getGroup}.
129    * @description Called when a group is removed.
130    * @example nowjs.on('removegroup', function (group) {
131    *   console.log('Group `' + group.groupName + '` eliminated from existence.');
132    * });
133    */
134   this.emit('removegroup', name);
135 };
136 
137 
138 /**
139  * @static
140  * @memberOf nowjs
141  * @function
142  * @name initialize
143 
144  * @description Returns a reference to the `everyone` object. The
145  * options object, if supplied, will be automatically merged with the
146  * default values.
147 
148  * @param {httpServer} server A Node.js http server (such as the one
149  * available in the http module or a module like Express) on which to
150  * run Now.
151 
152  * @param {Object} [options={"clientWrite" : true, "autoHost" : true,
153   "host" : undefined, "port" : undefined, "socketio" : {},
154   "closureTimeout : 30000, "client : {},  "scope" : "window"}]
155 
156  * @type Group
157 
158  * @example nowjs.initialize(server, {clientWrite: false, socketio: {'log level': 2});
159  */
160 Now.prototype.initialize = function (server, options) {
161   // Merge options
162   if (typeof options === 'object') {
163     nowUtil.extend(this.options, options);
164   } else {
165     options = this._readConfigFile(options);
166     if (options) {
167       nowUtil.extend(this.options, options);
168     }
169   }
170 
171   this.Group = require('./group').initialize(this);
172   this.User = require('./user').initialize(this);
173   this.Handlers = require('./handlers').initialize(this);
174   this.Support = require('./support').initialize(this);
175 
176   var self = this;
177 
178   fileServer.wrapServer(server, this.options);
179   this.server = io.listen(server, this.options.socketio);
180 
181   // Need this to be separate from clientsMap.
182   this.server.sockets.on('connection', function (socket) {
183     var user = new self.User(socket);
184     socket.user = self.users[socket.id] = user;
185     self.getGroup('everyone').addUser(socket.id);
186     socket.emit('rd');
187   });
188 
189   var everyone = this.getGroup('everyone');
190   everyone.isSuperGroup = true;
191 
192   // Shim for backwards compatibility.
193   this.on('connect', function () {
194     var user = nowUtil.clone(this);
195     user._events = everyone._events;
196     everyone.emit.apply(user, ['connect']);
197   });
198 
199   this.on('disconnect', function () {
200     var user = nowUtil.clone(this);
201     user._events = everyone._events;
202     everyone.emit.apply(user, ['disconnect']);
203   });
204 
205   return everyone;
206 };
207 
208 Now.prototype.addSupportServer = function(host, port){
209   var server = new this.Support(host, port);
210   return server;
211 }
212 
213 exports.Now = Now;
214 
215 /**
216  * @name nowjs#connect
217  * @event
218  * @version 0.7.0
219 
220  * @description Called in the context of a user when the server first
221  * receives a message from the given user.
222 
223  * @example nowjs.on('connect', function () {
224  *   this.now.receiveMessage('SERVER', 'Welcome to NowJS.');
225  * });
226  */
227 
228 /**
229  * @name nowjs#disconnect
230  * @event
231  * @version 0.7.0
232 
233  * @description Called in the context of a user who has just
234  * disconnected from the server.
235 
236  * @example nowjs.on('disconnect, function () {
237  *   delete myArray[this.user.clientId];
238  * });
239  */
240 
241 /**
242  * @name nowjs#groupdel
243  * @event
244  * @version 0.7.0
245 
246  * @param {Group} group Actually not quite a group; this parameter
247  * refers to a clone of the group in question that also carries the
248  * fully qualified name (fqn) of the variable to delete. Access the
249  * fqn via `group.fqn`.
250 
251  * @description Called when deleting a variable from all members of
252  * the group specified by this function's argument.
253 
254  * @example nowjs.on('groupdel', function (group) {
255  *   if (group.groupName === 'everyone') {
256  *     console.log('Everyone now no longer possesses ' + group.fqn);
257  *   }
258  * });
259  */
260 
261 /**
262  * @name nowjs#grouprv
263  * @event
264  * @version 0.7.0
265 
266  * @param {Group} group Similar to {@link nowjs#groupdel}, this is also
267  * a clone of the actual group. In addition to the fully qualified
268  * name, this clone also possesses a serialized form of its target
269  * value, accessible via `group.val`.
270 
271  * @description Called when replacing the value of a variable for all
272  * members of the group specified by this function's argument.
273 
274  * @example nowjs.on('grouprv', function (group) {
275  *   if (group.groupName === 'everyone') {
276  *     console.log('Everyone now sees ' + group.fqn + ' as ' + group.val);
277  *   }
278  * });
279  */
280 
281