<commit_before>//- creates and returns an error table conforming to
// JSON-RPC Invalid params.
var invalidParams = function(data) {
  var err = {
    code: -32602,
    message: 'Invalid params',
    data: data
  };
  return err;
};

// creates and returns an error table conforming to
// JSON-RPC Response Timeout.
var responseTimeout = function(data) {
  var err = {
    code: -32001,
    message: 'Response Timeout',
    data: data
  };
  return err;
};

//- creates and returns an error table conforming to
// JSON-RPC Internal Error.
var internalError = function(data) {
  var err = {
    code: -32603,
    message: 'Internal error',
    data: data,
  };
  return err;
};

//- creates and returns an error table conforming to
// JSON-RPC Parse Error.
var parseError = function(data) {
  var err = {
    code: -32700,
    message: 'Parse error',
    data: data
  };
  return err;
};

//- creates and returns an error table conforming to
// JSON-RPC Method not Found.
var methodNotFound = function(method) {
  var err = {
    code: -32601,
    message: 'Method not found',
    data: method
  };
  return err;
};

//- creates and returns an error table conforming to
// JSON-RPC Invalid request.
var invalidRequest = function(data) {
  var err = {
    code: -32600,
    message: 'Invalid Request',
    data: data
  };
  return err;
};

var errorObject = function(err) {
  var error;
  if (typeof(err) == 'object' && err.code && err.message) {
    error = err;
  } else {
    error = internal_error(err);
  }
  return error;
};

var noop = function() {};

var isDef = function(x) {
  return typeof(x) !== 'undefined';
};

var isArr = function(x) {
  return x instanceof Array;
};

var create = function(config) {
  config = config || {};
  var log = config.log || noop;
  var url = config.url || 'ws://127.0.0.1:11123';
  var encode = JSON.stringify;
  var decode = JSON.parse;
  var Websocket = Websocket || MozWebSocket;
    var wsock = new Websocket(url, 'jet');
    var messages = [];
    var queue = function(message) {
      messages.push(message);
    };
    var willFlush = true;
    var flush = function(reason) {
      if (messages.length === 1) {
        wsock.send(encode(messages[1]));
      } else if (message.length > 1) {
        wsock.send(encode(messages));
      }
      messages.length = 0;
      willFlush = false;
    };
    var requestDispatchers = {};
    var responseDispatchers = {};
    var dispatchResponse = function(message) {
      var mid = message.id;
      var callbacks = responseDispatchers[mid];
      delete responseDispatchers[mid];
      if (callbacks) {
        if (isDef(message.result)) {
          if (callbacks.success) {
            callbacks.success(message.result);
          }
        } else if (isDef(message.error)) {
          if (callbacks.error) {
            callbacks.error(message.error);
          }
        } else {
          log('invalid result:',cjson.encode(message));
        }
      } else {
        log('invalid result id:',mid,cjson.encode(message));
      }
    };
    var onNoDispatcher;
    // handles both method calls and fetchers (notifications)
    var dispatchRequest = function(message) {
      var dispatcher = requestDispatchers[message.method];
      var error;
      if (dispatcher) {
        try {
          dispatcher(message);
        } catch(e) {
          error = errorObject(err);
        }
      } else {
        error = methodNotFound(message.method);
        if (onNoDispatcher) {
          try {
            onNoDispatcher(message);
          } catch(e) {
            log(e);
          }
        }
      }
      var mid = message.id;
      if (error && isDef(mid)) {
        queue({
          id: mid,
          error: error
        });
      }
    };

    var dispatchSingleMessage = function(message) {
      if (message.method && message.params) {
        dispatchRequest(message);
      } else if (isDef(message.result) || isDef(message.error)) {
        dispatchResponse(self,message);
      } else {
        log('unhandled message',encode(message));
      }
    };

    var dispatchMessage = function(message) {
      try {
        message = decode(message);
        willFlush = true;
                if (typeof(message) === 'object' && message.length > 0) {
                    message.forEach(function(message) {
                      dispatchSingleMessage(message);
                    });
                  } else {
          dispatchSingleMessage(message);
        }
      } catch(e) {
        log('decoding message failed',e);
                queue({
          error: {
            code: -32700,
            messsage: 'Parse error'
          }
        });
      }
      flush('dispatchMessage');
    };
    wsock.onmessage = dispatchMessage;
    wsock.onerror = log;
    wsock.onclose = config.onclose;
    var j = {};

    j.onnodispatcher = function(f) {
      onNoDispatcher = f;
    };

    j.close = function() {
      flush('close');
      wsock.close();
    };

    var id = 0;
    var service = function(method,params,complete,callbacks) {
      var rpcId;
      // Only make a Request, if callbacks are specified.
      // Make complete call in case of success.
      // If no id is specified in the message, no Response
      // is expected, aka Notification.
      if (callbacks) {
        params.timeout = callbacks.timeout;
        id = id + 1;
        rpcId = id;
        if (complete) {
          if (callbacks.success) {
            var success = callbacks.success;
            callbacks.success = function(result) {
              complete(true);
              success();
            };
          } else {
            callbacks.success = function() {
              complete(true);
            };
          }

          if (callbacks.error) {
            var error = callbacks.error;
            callbacks.error = function(result) {
              complete(false);
              error();
            };
          } else {
            callbacks.error = function() {
              complete(false);
            };
          }
        }
        responseDispatchers[id] = callbacks;
      } else {
        // There will be no response, so call complete either way
        // and hope everything is ok
        if (complete) {
          complete(true);
        }
      }
      var message = {
        id: rpcId,
        method: method,
        params: params
      };
      if (willFlush) {
        queue(message);
    }   else {
        wsock.send(encode(message));
      }
    };

    j.batch = function(action) {
      willFlush = true;
      action();
      flush('batch');
    };

    j.add = function(desc,dispatch,callbacks) {
      var path = desc.path;
      var assignDispatcher = function(success) {
        if (success) {
          requestDispatchers[path] = dispatch;
        }
      };
      var params = {
        path: path,
        value: desc.value
      };
      service('add',params,assignDispatcher,callbacks);
      var ref = {
        remove: function(callbacks) {
          if (ref.isAdded()) {
          j.remove(path,callbacks);
        } else {
          callbacks.success();
        }
        },
        isAdded: function() {
          return isDef(requestDispatchers[path]);
        },
        add: function(value,callbacks) {
          if (ref.isAdded()) {
            callbacks.success();
          }
          if (isDef(value)) {
            desc.value = value;
          }
          j.add(desc,dispatch,callbacks);
        },
        path: function() {
          return path;
        }
      };
      return ref;
    };

    j.remove = function(path,callbacks) {
      var params = {
        path: path
      };
      var removeDispatcher = function(success) {
        delete request_dispatchers[path];
      };
      service('remove',params,remove_dispatcher,callbacks);
    };

    j.call = function(path,callparams,callbacks) {
      var params = {
        path: path,
        args: callparams || []
      };
      service('call',params,null,callbacks);
    };

    j.config = function(params,callbacks) {
      service('config',params,null,callbacks);
    };

    j.set = function(path,value,callbacks) {
      var params = {
        path: path,
        value: value
      };
      service('set',params,null,callbacks);
    };

    var fetchId = 0;

    j.fetch = function(params,f,callbacks) {
      var id = '__f__' + fetchId;
      var sorting = params.sort;
      fetchId = fetchId + 1;
      var ref;
      var addFetcher = function() {
        requestDispatchers[id] = function(message) {
          var params = message.params;
          if (!isDef(sorting)) {
            f(params.path,params.event,params.value,ref);
          } else {
            f(params.changes,params.n,ref);
          }
        };
      };
      if (typeof(params) === 'string') {
        params = {
          path: {
            contains: params
          }
        };
      }
      params.id = id;
      service('fetch',params,add_fetcher,callbacks);
      ref = {
        unfetch: function(callbacks) {
          var removeDispatcher = function() {
            delete requestDispatchers[id];
          };
          service('unfetch',{id:id},removeDispatcher,callbacks);
        },
        isFetching: function() {
          return isDef(requestDispatchers[id]);
        },
        fetch: function(callbacks) {
          service('fetch',params,add_fetcher,callbacks);
        }
      };
      return ref;
    };

    j.method = function(desc,addCallbacks) {
        var dispatch;
        if (desc.call) {
          dispatch = function(message) {
            var params = message.params;
            var result;
            var err;
          try {
            if (isArr(params) && params.length > 0) {
                  result = desc.call.apply(undefined,params);
                } else {
                  result = desc.call.call(undefined,params);
            }
          }
          catch(e) {
            err = e;
          }
            var mid = message.id;
            if (isDef(mid)) {
            if (ok)  {
              queue({
                id: mid,
                result:  result || {}
              });
            } else {
              queue({
                id: mid,
                error: errorObject(err)
              });
            }
          }
        };
      } else if (desc.callAsync) {
        dispatch = function(message) {
          var reply = function(resp,dontFlush) {
            var mid = message.id;
            if (isDef(mid)) {
              var response = {
                id: mid
              };
              if (isDef(resp.result) && !isDef(resp.error)) {
                response.result = resp.result;
              } else if (isDef(resp.error)) {
                response.error = resp.error;
              } else {
                response.error = 'jet.peer Invalid async method response ' + desc.path;
              }
              queue(response);
              if (!willFlush && !dontFlush) {
                flush('call_async');
              }
            }
          };

          var params = message.params;
            var err;

          try {
          if (isArr(params) && params.length > 0) {
            desc.callAsync.apply(undefined,reply,params);
          }
          else {
            desc.callAsync.call(undefined,reply,params);
          }
        } catch(e) {
          var mid = message.id;
          if (isDef(mid)) {
            queue(
            {
              id: mid,
              error: errorObject(result)
            });
          }
        }
      };

      } else {
        throw 'invalid method desc' + (desc.path || '?');
      }
      var ref = self.add(desc,dispatch,add_callbacks);
      return ref;
    };

    j.state = function(desc,add_callbacks) {
      var dispatch;
      if (desc.set) {
        dispatch = function(message) {
          var value = message.params.value;
            try {
              var result = desc.set(value) || {};
              desc.value = result.value || value;
            var mid = message.id;
            if (isDef(mid)) {
              queue({
                id: mid,
                result: true
              });
            }
            if (!result.dontNotify) {
              queue({
                method: 'change',
                params: {
                  path: desc.path,
                  value: desc.value
                }
              });
            }
          } catch(err) {
          if (isDef(message.id)) {
            queue({
              id: message.id,
              error: errorObject(err)
            });
          }
        }
      };
    } else if (isDef(desc.setAsync)) {
        dispatch = function(message) {
          var value = message.params.value;
          var reply = function(resp) {
            var mid = message.id;
            if (isDef(mid)) {
              var response = {
                id: mid
              };
              if (isDef(resp.result) && !isDef(resp.error)) {
                response.result = resp.result;
              } else if (isDef(resp.error)) {
                response.error = errorObject(resp.error);
              } else {
                response.error = 'jet.peer Invalid async state response ' + desc.path;
              }
              queue(response);
            }
            if (isDef(resp.result) && !isDef(resp.dontNotify)) {
              if (isDef(resp.value)) {
                desc.value = resp.value;
              } else {
                desc.value = value;
              }
              queue({
                method: 'change',
                params: {
                  path: desc.path,
                  value: desc.value
                }
              });
            }
            if (!willFlush && !resp.dontFlush) {
              flush('set_aync');
            }
          };
          try {
            desc.setAsync(reply,value);
          } catch(err) {
                      var mid = message.id;
            if (isDef(mid)) {
            queue({
              id: mid,
              error: errorObject(result)
            });

            }
          }
        };
      } else {
        dispatch = function(message) {
          var mid = message.id;
          if (isDef(mid)) {
            queue({
              id: mid,
              error: invalidParams()
            });
          }
        };
      }
      var ref = j.add(desc,dispatch,addCallbacks);
      ref.value = function(value) {
        if (isDef(value)) {
          desc.value = value;
          queue({
            method: 'change',
            params: {
              path: desc.path,
              value: value
            }
          });
          if (!willFlush) {
            flush();
          }
        } else {
          return desc.value;
        }
      };
      return ref;
    };

    wsock.onconnect = function() {
        if (isDef(config.name)) {
          j.config({
              name: config.name
              },{
              success: function() {
                flush('config');
                if (config.onconnect) {
                  config.onconnect(j);
                }
              },
              error: function(err) {
                j.close();
              }
          });
        } else if(config.onconnect) {
          config.onconnect(j);
        }
        flush('on_connect');
      };
    return j;
  };

return {
  new: create
};
<commit_msg>indent<commit_after>  function() {

      //- creates and returns an error table conforming to
      // JSON-RPC Invalid params.
      var invalidParams = function(data) {
              var err = {
                  code: -32602,
                  message: 'Invalid params',
                  data: data
              };
              return err;
          };

      // creates and returns an error table conforming to
      // JSON-RPC Response Timeout.
      var responseTimeout = function(data) {
              var err = {
                  code: -32001,
                  message: 'Response Timeout',
                  data: data
              };
              return err;
          };

      //- creates and returns an error table conforming to
      // JSON-RPC Internal Error.
      var internalError = function(data) {
              var err = {
                  code: -32603,
                  message: 'Internal error',
                  data: data,
              };
              return err;
          };

      //- creates and returns an error table conforming to
      // JSON-RPC Parse Error.
      var parseError = function(data) {
              var err = {
                  code: -32700,
                  message: 'Parse error',
                  data: data
              };
              return err;
          };

      //- creates and returns an error table conforming to
      // JSON-RPC Method not Found.
      var methodNotFound = function(method) {
              var err = {
                  code: -32601,
                  message: 'Method not found',
                  data: method
              };
              return err;
          };

      //- creates and returns an error table conforming to
      // JSON-RPC Invalid request.
      var invalidRequest = function(data) {
              var err = {
                  code: -32600,
                  message: 'Invalid Request',
                  data: data
              };
              return err;
          };

      var errorObject = function(err) {
              var error;
              if(typeof(err) == 'object' && err.code && err.message) {
                  error = err;
              } else {
                  error = internal_error(err);
              }
              return error;
          };

      var noop = function() {};

      var isDef = function(x) {
              return typeof(x) !== 'undefined';
          };

      var isArr = function(x) {
              return x instanceof Array;
          };

      var create = function(config) {
              config = config || {};
              var log = config.log || noop;
              var url = config.url || 'ws://127.0.0.1:11123';
              var encode = JSON.stringify;
              var decode = JSON.parse;
              var Websocket = Websocket || MozWebSocket;
              var wsock = new Websocket(url, 'jet');
              var messages = [];

              var queue = function(message) {
                      messages.push(message);
                  };

              var willFlush = true;
              var flush = function(reason) {
                      if(messages.length === 1) {
                          wsock.send(encode(messages[1]));
                      } else if(message.length > 1) {
                          wsock.send(encode(messages));
                      }
                      messages.length = 0;
                      willFlush = false;
                  };

              var requestDispatchers = {};
              var responseDispatchers = {};

              var dispatchResponse = function(message) {
                      var mid = message.id;
                      var callbacks = responseDispatchers[mid];
                      delete responseDispatchers[mid];
                      if(callbacks) {
                          if(isDef(message.result)) {
                              if(callbacks.success) {
                                  callbacks.success(message.result);
                              }
                          } else if(isDef(message.error)) {
                              if(callbacks.error) {
                                  callbacks.error(message.error);
                              }
                          } else {
                              log('invalid result:', cjson.encode(message));
                          }
                      } else {
                          log('invalid result id:', mid, cjson.encode(message));
                      }
                  };

              var onNoDispatcher;
              // handles both method calls and fetchers (notifications)
              var dispatchRequest = function(message) {
                      var dispatcher = requestDispatchers[message.method];
                      var error;
                      if(dispatcher) {
                          try {
                              dispatcher(message);
                          } catch(e) {
                              error = errorObject(err);
                          }
                      } else {
                          error = methodNotFound(message.method);
                          if(onNoDispatcher) {
                              try {
                                  onNoDispatcher(message);
                              } catch(e) {
                                  log(e);
                              }
                          }
                      }
                      var mid = message.id;
                      if(error && isDef(mid)) {
                          queue({
                              id: mid,
                              error: error
                          });
                      }
                  };

              var dispatchSingleMessage = function(message) {
                      if(message.method && message.params) {
                          dispatchRequest(message);
                      } else if(isDef(message.result) || isDef(message.error)) {
                          dispatchResponse(self, message);
                      } else {
                          log('unhandled message', encode(message));
                      }
                  };

              var dispatchMessage = function(message) {
                      try {
                          message = decode(message);
                          willFlush = true;
                          if(typeof(message) === 'object' && message.length > 0) {
                              message.forEach(function(message) {
                                  dispatchSingleMessage(message);
                              });
                          } else {
                              dispatchSingleMessage(message);
                          }
                      } catch(e) {
                          log('decoding message failed', e);
                          queue({
                              error: {
                                  code: -32700,
                                  messsage: 'Parse error'
                              }
                          });
                      }
                      flush('dispatchMessage');
                  };

              wsock.onmessage = dispatchMessage;
              wsock.onerror = log;
              wsock.onclose = config.onclose;

              var j = {};

              j.onnodispatcher = function(f) {
                  onNoDispatcher = f;
              };

              j.close = function() {
                  flush('close');
                  wsock.close();
              };

              var id = 0;
              var service = function(method, params, complete, callbacks) {
                      var rpcId;
                      // Only make a Request, if callbacks are specified.
                      // Make complete call in case of success.
                      // If no id is specified in the message, no Response
                      // is expected, aka Notification.
                      if(callbacks) {
                          params.timeout = callbacks.timeout;
                          id = id + 1;
                          rpcId = id;
                          if(complete) {
                              if(callbacks.success) {
                                  var success = callbacks.success;
                                  callbacks.success = function(result) {
                                      complete(true);
                                      success();
                                  };
                              } else {
                                  callbacks.success = function() {
                                      complete(true);
                                  };
                              }

                              if(callbacks.error) {
                                  var error = callbacks.error;
                                  callbacks.error = function(result) {
                                      complete(false);
                                      error();
                                  };
                              } else {
                                  callbacks.error = function() {
                                      complete(false);
                                  };
                              }
                          }
                          responseDispatchers[id] = callbacks;
                      } else {
                          // There will be no response, so call complete either way
                          // and hope everything is ok
                          if(complete) {
                              complete(true);
                          }
                      }
                      var message = {
                          id: rpcId,
                          method: method,
                          params: params
                      };
                      if(willFlush) {
                          queue(message);
                      } else {
                          wsock.send(encode(message));
                      }
                  };

              j.batch = function(action) {
                  willFlush = true;
                  action();
                  flush('batch');
              };

              j.add = function(desc, dispatch, callbacks) {
                  var path = desc.path;
                  var assignDispatcher = function(success) {
                          if(success) {
                              requestDispatchers[path] = dispatch;
                          }
                      };
                  var params = {
                      path: path,
                      value: desc.value
                  };
                  service('add', params, assignDispatcher, callbacks);
                  var ref = {
                      remove: function(callbacks) {
                          if(ref.isAdded()) {
                              j.remove(path, callbacks);
                          } else {
                              callbacks.success();
                          }
                      },
                      isAdded: function() {
                          return isDef(requestDispatchers[path]);
                      },
                      add: function(value, callbacks) {
                          if(ref.isAdded()) {
                              callbacks.success();
                          }
                          if(isDef(value)) {
                              desc.value = value;
                          }
                          j.add(desc, dispatch, callbacks);
                      },
                      path: function() {
                          return path;
                      }
                  };
                  return ref;
              };

              j.remove = function(path, callbacks) {
                  var params = {
                      path: path
                  };
                  var removeDispatcher = function(success) {
                          delete request_dispatchers[path];
                      };
                  service('remove', params, remove_dispatcher, callbacks);
              };

              j.call = function(path, callparams, callbacks) {
                  var params = {
                      path: path,
                      args: callparams || []
                  };
                  service('call', params, null, callbacks);
              };

              j.config = function(params, callbacks) {
                  service('config', params, null, callbacks);
              };

              j.set = function(path, value, callbacks) {
                  var params = {
                      path: path,
                      value: value
                  };
                  service('set', params, null, callbacks);
              };

              var fetchId = 0;

              j.fetch = function(params, f, callbacks) {
                  var id = '__f__' + fetchId;
                  var sorting = params.sort;
                  fetchId = fetchId + 1;
                  var ref;
                  var addFetcher = function() {
                          requestDispatchers[id] = function(message) {
                              var params = message.params;
                              if(!isDef(sorting)) {
                                  f(params.path, params.event, params.value, ref);
                              } else {
                                  f(params.changes, params.n, ref);
                              }
                          };
                      };
                  if(typeof(params) === 'string') {
                      params = {
                          path: {
                              contains: params
                          }
                      };
                  }
                  params.id = id;
                  service('fetch', params, add_fetcher, callbacks);
                  ref = {
                      unfetch: function(callbacks) {
                          var removeDispatcher = function() {
                                  delete requestDispatchers[id];
                              };
                          service('unfetch', {
                              id: id
                          }, removeDispatcher, callbacks);
                      },
                      isFetching: function() {
                          return isDef(requestDispatchers[id]);
                      },
                      fetch: function(callbacks) {
                          service('fetch', params, add_fetcher, callbacks);
                      }
                  };
                  return ref;
              };

              j.method = function(desc, addCallbacks) {
                  var dispatch;
                  if(desc.call) {
                      dispatch = function(message) {
                          var params = message.params;
                          var result;
                          var err;
                          try {
                              if(isArr(params) && params.length > 0) {
                                  result = desc.call.apply(undefined, params);
                              } else {
                                  result = desc.call.call(undefined, params);
                              }
                          } catch(e) {
                              err = e;
                          }
                          var mid = message.id;
                          if(isDef(mid)) {
                              if(ok) {
                                  queue({
                                      id: mid,
                                      result: result || {}
                                  });
                              } else {
                                  queue({
                                      id: mid,
                                      error: errorObject(err)
                                  });
                              }
                          }
                      };
                  } else if(desc.callAsync) {
                      dispatch = function(message) {
                          var reply = function(resp, dontFlush) {
                                  var mid = message.id;
                                  if(isDef(mid)) {
                                      var response = {
                                          id: mid
                                      };
                                      if(isDef(resp.result) && !isDef(resp.error)) {
                                          response.result = resp.result;
                                      } else if(isDef(resp.error)) {
                                          response.error = resp.error;
                                      } else {
                                          response.error = 'jet.peer Invalid async method response ' + desc.path;
                                      }
                                      queue(response);
                                      if(!willFlush && !dontFlush) {
                                          flush('call_async');
                                      }
                                  }
                              };

                          var params = message.params;
                          var err;

                          try {
                              if(isArr(params) && params.length > 0) {
                                  desc.callAsync.apply(undefined, reply, params);
                              } else {
                                  desc.callAsync.call(undefined, reply, params);
                              }
                          } catch(e) {
                              var mid = message.id;
                              if(isDef(mid)) {
                                  queue({
                                      id: mid,
                                      error: errorObject(result)
                                  });
                              }
                          }
                      };
                  } else {
                      throw 'invalid method desc' + (desc.path || '?');
                  }
                  var ref = self.add(desc, dispatch, add_callbacks);
                  return ref;
              };

              j.state = function(desc, add_callbacks) {
                  var dispatch;
                  if(desc.set) {
                      dispatch = function(message) {
                          var value = message.params.value;
                          try {
                              var result = desc.set(value) || {};
                              desc.value = result.value || value;
                              var mid = message.id;
                              if(isDef(mid)) {
                                  queue({
                                      id: mid,
                                      result: true
                                  });
                              }
                              if(!result.dontNotify) {
                                  queue({
                                      method: 'change',
                                      params: {
                                          path: desc.path,
                                          value: desc.value
                                      }
                                  });
                              }
                          } catch(err) {
                              if(isDef(message.id)) {
                                  queue({
                                      id: message.id,
                                      error: errorObject(err)
                                  });
                              }
                          }
                      };
                  } else if(isDef(desc.setAsync)) {
                      dispatch = function(message) {
                          var value = message.params.value;
                          var reply = function(resp) {
                                  var mid = message.id;
                                  if(isDef(mid)) {
                                      var response = {
                                          id: mid
                                      };
                                      if(isDef(resp.result) && !isDef(resp.error)) {
                                          response.result = resp.result;
                                      } else if(isDef(resp.error)) {
                                          response.error = errorObject(resp.error);
                                      } else {
                                          response.error = 'jet.peer Invalid async state response ' + desc.path;
                                      }
                                      queue(response);
                                  }
                                  if(isDef(resp.result) && !isDef(resp.dontNotify)) {
                                      if(isDef(resp.value)) {
                                          desc.value = resp.value;
                                      } else {
                                          desc.value = value;
                                      }
                                      queue({
                                          method: 'change',
                                          params: {
                                              path: desc.path,
                                              value: desc.value
                                          }
                                      });
                                  }
                                  if(!willFlush && !resp.dontFlush) {
                                      flush('set_aync');
                                  }
                              };
                          try {
                              desc.setAsync(reply, value);
                          } catch(err) {
                              var mid = message.id;
                              if(isDef(mid)) {
                                  queue({
                                      id: mid,
                                      error: errorObject(result)
                                  });

                              }
                          }
                      };
                  } else {
                      dispatch = function(message) {
                          var mid = message.id;
                          if(isDef(mid)) {
                              queue({
                                  id: mid,
                                  error: invalidParams()
                              });
                          }
                      };
                  }
                  var ref = j.add(desc, dispatch, addCallbacks);
                  ref.value = function(value) {
                      if(isDef(value)) {
                          desc.value = value;
                          queue({
                              method: 'change',
                              params: {
                                  path: desc.path,
                                  value: value
                              }
                          });
                          if(!willFlush) {
                              flush();
                          }
                      } else {
                          return desc.value;
                      }
                  };
                  return ref;
              };

              wsock.onconnect = function() {
                  if(isDef(config.name)) {
                      j.config({
                          name: config.name
                      }, {
                          success: function() {
                              flush('config');
                              if(config.onconnect) {
                                  config.onconnect(j);
                              }
                          },
                          error: function(err) {
                              j.close();
                          }
                      });
                  } else if(config.onconnect) {
                      config.onconnect(j);
                  }
                  flush('on_connect');
              };

              return j;
          };

      var jet = {
          Peer: create
      };

      if(typeof define === 'function' && define.amd) {
          define(jet);
      } else if(typeof module === 'object' && module.exports) {
          module.exports = jet;
      } else {
          this.jet = jet;
      }
  }();<|endoftext|>


<commit_before>var Youtube = require("youtube-api");
var Soundcloud = require('node-soundcloud');

var GLOBAL_CONFIG = require("../../global-config.js");

module.exports = function(Search) {
  Search.youtube = function(keyword, pthis, next) {
    Youtube.authenticate({
      type: "key",
      key: GLOBAL_CONFIG.youtubeApiKey
    });

    Youtube.search.list({q:keyword, part:"snippet", maxResults:30, type:"video"}, function(err, data) {
      var youtubeUrl, orderNumber=0;

      data.items.forEach(function(item) {
        youtubeUrl = "http://www.youtube.com/watch?v=" + item.id.videoId;
        pthis.musics.create({title:item.snippet.title, type:"youtube", url:youtubeUrl, order:orderNumber}, function(err, obj){
        });
        orderNumber++;
      });

      next();
    });
  };

  Search.soundcloud = function(keyword, pthis, next) {
    Soundcloud.init(GLOBAL_CONFIG.soundcloud);

    Soundcloud.get('/tracks?q=' + keyword, function(err, tracks) {
      var youtubeUrl, orderNumber=0;

      tracks.forEach(function(track) {
        pthis.musics.create({title:track.title, type:"soundcloud", url:track.permalink_url, order:orderNumber}, function(err, obj){
        });

        orderNumber++;
      });

      next();
    });
  };

  Search.afterCreate = function(next) {
    var pthis = this;

    switch(this.source) {
      case "soundcloud":
        Search.soundcloud(this.keyword, pthis, next);
        break;

      case "youtube":
      default:
        Search.youtube(this.keyword, pthis, next);
        break;
    }
  };
};
<commit_msg>通らなくなったテストの対応 searchモデルにsource要素が無い場合は追加<commit_after>var Youtube = require("youtube-api");
var Soundcloud = require('node-soundcloud');

var GLOBAL_CONFIG = require("../../global-config.js");

module.exports = function(Search) {
  Search.youtube = function(keyword, pthis, next) {
    Youtube.authenticate({
      type: "key",
      key: GLOBAL_CONFIG.youtubeApiKey
    });

    Youtube.search.list({q:keyword, part:"snippet", maxResults:30, type:"video"}, function(err, data) {
      var youtubeUrl, orderNumber=0;

      data.items.forEach(function(item) {
        youtubeUrl = "http://www.youtube.com/watch?v=" + item.id.videoId;
        pthis.musics.create({title:item.snippet.title, type:"youtube", url:youtubeUrl, order:orderNumber}, function(err, obj){
        });
        orderNumber++;
      });

      next();
    });
  };

  Search.soundcloud = function(keyword, pthis, next) {
    Soundcloud.init(GLOBAL_CONFIG.soundcloud);

    Soundcloud.get('/tracks?q=' + keyword, function(err, tracks) {
      var youtubeUrl, orderNumber=0;

      tracks.forEach(function(track) {
        pthis.musics.create({title:track.title, type:"soundcloud", url:track.permalink_url, order:orderNumber}, function(err, obj){
        });

        orderNumber++;
      });

      next();
    });
  };

  Search.beforeSave = function(next, modelInstance) {
    if (typeof this.source === "undefined") {
      this.source = "youtube";
    }

    next();
  };

  Search.afterCreate = function(next) {
    var pthis = this;

    switch(this.source) {
      case "soundcloud":
        Search.soundcloud(this.keyword, pthis, next);
        break;

      case "youtube":
      default:
        Search.youtube(this.keyword, pthis, next);
        break;
    }
  };
};
<|endoftext|>


<commit_before>
var _ = require('lodash');
var Args = require('args-js');
var request = require('request');
var async = require('async');

var Peer = function(client, data) {
    _.assign(this, data);
    this.clientId = function() {
        return client.id;
    };
    this.check = function(cb) {
        if(typeof cb != 'function')
            cb = _.noop;
        client.check(this, cb);
    };
};

Peer.prototype.send = function(type, json, cb) {
    var self = this;
    var args = Args([
        {type: Args.STRING | Args.Optional, _default: 'message'},
        {json: Args.OBJECT | Args.Optional, _default: {}},
        {cb: Args.FUNCTION | Args.Optional, _default: _.noop}
    ], arguments);
    type = args.type; json = args.json; cb = args.cb;

    request({
        method: 'POST',
        json: true,
        timeout: 1000,
        headers: {
            'x-peercloud-id': self.clientId()
        },
        url: require('url').format({
            protocol: 'http',
            hostname: self.ip,
            port: self.port,
            pathname: type
        }),
        body: json
    }, function(err, res, body) {
        if(err)
            return cb(err);
        if(_.toString(res.statusCode)[0] != '2')
            return cb({
                statusCode: res.statusCode,
                body: body
            });
        cb(null, body);
    });
};

// calls send on a list of peers (eg broadcast same message)
// cb(err, values) is called with results in:
// * err if there's at least one error
// * values if all calls succeeded
Peer.sendAll = function(peers, type, json, cb) {
    var args = Args([
        {peers: Args.ARRAY | Args.Required},
        {type: Args.STRING | Args.Optional, _default: 'message'},
        {json: Args.OBJECT | Args.Optional, _default: {}},
        {cb: Args.FUNCTION | Args.Optional, _default: _.noop}
    ], arguments);
    peers = args.peers; type = args.type; json = args.json; cb = args.cb;

    async.map(peers, function(peer, cb) {
        peer.send(type, json, function(err, body) {
            var result = {peer: peer};
            if(err)
                result.error = err;
            else
                result.value = body;
            cb(null, result);
        });
    }, function(err, results) {
        if(_.find(results, function(res) {
            return res.error;
        }))
            return cb(results);
        cb(null, results);
    });
};

module.exports = Peer;
<commit_msg>peer.sendAll: replies should have the peer data only, not the full peer object<commit_after>
var _ = require('lodash');
var Args = require('args-js');
var request = require('request');
var async = require('async');

var Peer = function(client, data) {
    _.assign(this, data);
    this.clientId = function() {
        return client.id;
    };
    this.check = function(cb) {
        if(typeof cb != 'function')
            cb = _.noop;
        client.check(this, cb);
    };
};

Peer.prototype.send = function(type, json, cb) {
    var self = this;
    var args = Args([
        {type: Args.STRING | Args.Optional, _default: 'message'},
        {json: Args.OBJECT | Args.Optional, _default: {}},
        {cb: Args.FUNCTION | Args.Optional, _default: _.noop}
    ], arguments);
    type = args.type; json = args.json; cb = args.cb;

    request({
        method: 'POST',
        json: true,
        timeout: 1000,
        headers: {
            'x-peercloud-id': self.clientId()
        },
        url: require('url').format({
            protocol: 'http',
            hostname: self.ip,
            port: self.port,
            pathname: type
        }),
        body: json
    }, function(err, res, body) {
        if(err)
            return cb(err);
        if(_.toString(res.statusCode)[0] != '2')
            return cb({
                statusCode: res.statusCode,
                body: body
            });
        cb(null, body);
    });
};

// calls send on a list of peers (eg broadcast same message)
// cb(err, values) is called with results in:
// * err if there's at least one error
// * values if all calls succeeded
Peer.sendAll = function(peers, type, json, cb) {
    var args = Args([
        {peers: Args.ARRAY | Args.Required},
        {type: Args.STRING | Args.Optional, _default: 'message'},
        {json: Args.OBJECT | Args.Optional, _default: {}},
        {cb: Args.FUNCTION | Args.Optional, _default: _.noop}
    ], arguments);
    peers = args.peers; type = args.type; json = args.json; cb = args.cb;

    async.map(peers, function(peer, cb) {
        peer.send(type, json, function(err, body) {
            var result = {peer: peer.data};
            if(err)
                result.error = err;
            else
                result.value = body;
            cb(null, result);
        });
    }, function(err, results) {
        if(_.find(results, function(res) {
            return res.error;
        }))
            return cb(results);
        cb(null, results);
    });
};

module.exports = Peer;
<|endoftext|>


<commit_before>"use strict";

export default {
  pipe(value, ...funcs) {
    return funcs.reduce((v, func) => func(v), value);
  }
};<commit_msg>Using compose<commit_after>"use strict";

import { compose, id } from './bundle';

export default {
  pipe(value, ...funcs) {
    return compose(...funcs, id)(value);
  }
};<|endoftext|>


<commit_before>
'use strict';

var EventEmitter = require( 'events' ).EventEmitter;
var util = require( './util' );

/**
 * Single user on the server.
 *
 * @param {Object} data - User data.
 * @param {MumbleClient} client - Mumble client that owns this user.
 */
var User = function( data, client ) {
    this.client = client;
    this._applyProperties( data );
};

User.prototype = Object.create( EventEmitter.prototype );

/**
 * @summary Moves the user to a channel
 *
 * @param {Channel|string} channel - Channel name or a channel object
 */
User.prototype.moveToChannel = function( channel ) {

    // Fetch the channel ID based on the parameter.
    var id;
    if( typeof channel === 'string' ) {
        id = this.client.channelByName( channel ).id;
    } else if( typeof channel === 'object' ) {
        id = channel.id;
    } else {
        return;
    }

    // Send the move message.
    this.client.connection.sendMessage( 'UserState', {
        session: this.session, actor: this.client.user.session, channel_id: id } );
};

/**
 * @summary Change a user's self deafened state. (Obviously) only works on yourself.
 *
 * @param {Boolean} isSelfDeaf - The new self deafened state
 */
User.prototype.setSelfDeaf = function( isSelfDeaf ) {
    this.client.connection.sendMessage( 'UserState', {
        session: this.session, actor: this.client.user.session, self_deaf: isSelfDeaf } );
};

/**
 * @summary Change a user's self muted state. (Obviously) only works on yourself.
 *
 * @param {Boolean} isSelfMute - The new self muted state
 */
User.prototype.setSelfMute = function( isSelfMute ) {
    this.client.connection.sendMessage( 'UserState', {
        session: this.session, actor: this.client.user.session, self_mute: isSelfMute } );
};

/**
 * @summary Attempts to kick the user.
 *
 * @param {string} [reason] - The reason to kick the user for.
 */
User.prototype.kick = function( reason ) {
    this._sendRemoveUser( reason || 'You have been kicked', false );
};

/**
 * @summary Attempts to ban the user.
 *
 * @param {string} [reason] - The reason to ban the user for.
 */
User.prototype.ban = function( reason ) {
    this._sendRemoveUser( reason || 'You have been banned', true );
};

/**
 * @summary Sends a message to the user.
 *
 * @param {string} message - The message to send.
 */
User.prototype.sendMessage = function( message ) {
    this.client.sendMessage( message, { session: [ this.session ] } );
};

/**
 * @summary Returns an output stream for listening to the user audio.
 *
 * @param {Boolean} [noEmptyFrames]
 *      True to cut the output stream during silence. If the output stream
 *      isn't cut it will keep emitting zero-values when the user isn't
 *      talking.
 *
 * @returns {MumbleOutputStream} Output stream.
 */
User.prototype.outputStream = function( noEmptyFrames ) {
    return this.client.connection.outputStream( this.session, noEmptyFrames );
};

/**
 * @summary Returns an input stream for sending audio to the user.
 *
 * @returns {MumbleInputStream} Input stream.
 */
User.prototype.inputStream = function() {
    return this.client.inputStreamForUser( this.session );
};

/**
 * @summary Checks whether the user can talk or not.
 *
 * @returns {Boolean} True if the user can talk.
 */
User.prototype.canTalk = function() {
    return !this.mute && !this.selfMute && !this.suppress;
};

/**
 * @summary Checks whether the user can hear other people.
 *
 * @returns {Boolean} True if the user can hear.
 */
User.prototype.canHear = function() {
    return !this.selfDeaf;
};

User.prototype._applyProperties = function( data ) {

    /**
     * @summary Session ID
     *
     * @description
     * Session ID is present for all users. The ID specifies the current user
     * session and will change when the user reconnects.
     *
     * @see User#id
     *
     * @name User#session
     * @type Number
     */
    this.session = data.session;

    /**
     * @summary User name
     *
     * @name User#name
     * @type String
     */
    this.name = data.name;

    /**
     * @summary User ID
     *
     * @description
     * User ID is specified only for users who are registered on the server.
     * The user ID won't change when the user reconnects.
     *
     * @see User#session
     *
     * @name User#id
     * @type Number
     */
    this.id = data.user_id;

    /**
     * @summary _true_ when the user is muted by an admin.
     *
     * @name User#mute
     * @type Boolean
     */
    this.mute = data.mute;

    /**
     * @summary _true_ when the user is deafened by an admin.
     *
     * @name User#deaf
     * @type Boolean
     */
    this.deaf = data.deaf;

    /**
     * @summary _true_ when the user is suppressed due to lack of
     * permissions.
     *
     * @description
     * The user will be suppressed by the server if they don't have permissions
     * to speak on the current channel.
     *
     * @name User#suppress
     * @type Boolean
     */
    this.suppress = data.suppress;

    /**
     * @summary _true_ when the user has muted themselves.
     *
     * @name User#selfMute
     * @type Boolean
     */
    this.selfMute = data.self_mute;

    /**
     * @summary _true_ when the user has deafened themselves.
     *
     * @name User#selfDeaf
     * @type Boolean
     */
    this.selfDeaf = data.self_deaf;

    /**
     * @summary The hash of the user certificate
     *
     * @name User#hash
     * @type String
     */
    this.hash = data.hash;

    /**
     * @summary _true_ when the user is recording the conversation.
     *
     * @name User#recording
     * @type Boolean
     */
    this.recording = data.recording;

    /**
     * @summary _true_ when the user is a priority speaker.
     *
     * @name User#prioritySpeaker
     * @type Boolean
     */
    this.prioritySpeaker = data.priority_speaker;

    /**
     * @summary User's current channel.
     *
     * @name User#channel
     * @type Channel
     */
    if( data.channel_id !== null ) {
        this.channel = this.client.channelById( data.channel_id );
    } else { // New users always enter root
        this.channel = this.client.rootChannel;
    }
    this.channel._addUser( this );

    // TODO: Comments, textures
};

/**
 * @summary Emitted when the user disconnects
 *
 * @description
 * Also available through the client `user-disconnect` event.
 *
 * @event User#disconnect
 */

User.prototype._detach = function() {
    this.emit( 'disconnect' );
    this.channel._removeUser( this );
};

/**
 * @summary Emitted when the user moves between channels.
 *
 * @event User#move
 * @param {Channel} oldChannel - The channel where the user was moved from.
 * @param {Channel} newChannel - The channel where the user was moved to.
 * @param {User} actor - The user who moved the channel or undefined for server.
 */

/**
 * @summary Checks whether the user changed channel.
 *
 * @private
 *
 * @param {Object} data - New user data.
 */
User.prototype._checkChangeChannel = function( data ) {

    // Get the two channel instances.
    var newChannel = this.client.channelById( data.channel_id );
    var oldChannel = this.channel;

    // Make sure there is a change in the channel.
    if( newChannel === oldChannel )
        return;

    // Make the channel change and notify listeners.
    this.channel = newChannel;
    oldChannel._removeUser( this );
    newChannel._addUser( this );

    var actor = this.client.userBySession( data.actor );
    this.emit( 'move', oldChannel, newChannel, actor );
};

/**
 * @summary Emitted when the user is muted or unmuted by the server.
 *
 * @description
 * Also available through the client `user-mute` event.
 *
 * @event User#mute
 * @param {Boolean} status
 *      True when the user is muted, false when unmuted.
 */

/**
 * @summary Emitted when the user mutes or unmutes themselves.
 *
 * @description
 * Also available through the client `user-self-mute` event.
 *
 * @event User#self-mute
 * @param {Boolean} status
 *      True when the user mutes themselves. False when unmuting.
 */

/**
 * @summary Emitted when the user deafens or undeafens themselves.
 *
 * @description
 * Also available through the client `user-self-deaf` event.
 *
 * @event User#self-deaf
 * @param {Boolean} status
 *      True when the user deafens themselves. False when undeafening.
 */

/**
 * @summary Emitted when the user is suppressed or unsuppressed.
 *
 * @description
 * Also available through the client `user-suppress` event.
 *
 * @event User#suppress
 * @param {Boolean} status
 *      True when the user is suppressed. False when unsuppressed.
 */

/**
 * @summary Emitted when the user starts or stops recording.
 *
 * @description
 * Also available through the client `user-recording` event.
 *
 * @event User#recording
 * @param {Boolean} status
 *      True when the user starts recording. False when they stop.
 */

/**
 * @summary Emitted when the user gains or loses priority speaker status.
 *
 * @description
 * Also available through the client `user-priority-speaker` event.
 *
 * @event User#priority-speaker
 * @param {Boolean} status
 *      True when the user gains priority speaker status. False when they lose
 *      it.
 */

User.prototype.update = function( data ) {
    var self = this;

    // Check the simple fields.
    [
        'mute', 'selfMute', 'suppress',
        'selfDeaf',
        'recording', 'prioritySpeaker',
    ].forEach( function( f ) {
        self._checkField( data, f );
    } );

    // Channel check
    if( data.channel_id !== null ) {
        this._checkChangeChannel( data );
    }

};

User.prototype._sendRemoveUser = function( reason, ban ) {
    this.client.connection.sendMessage( 'UserRemove', {
        session: this.session,
        actor: this.client.user.session,
        reason: reason,
        ban: ban
    } );
};

User.prototype._checkField = function( data, field ) {

    // Make sure the field has a value.
    var newValue = data[ util.toFieldName( field ) ];
    if( newValue === undefined )
        return;

    // Make sure the new value differs.
    var oldValue = this[ field ];
    if( newValue === oldValue )
        return;

    // All checks succeeded. Store the new value and emit change event.
    this[ field ] = newValue;
    var actor = this.client.userBySession( data.actor );
    this.emit( util.toEventName( field ), newValue, actor );
};

module.exports = User;
<commit_msg>Add `isRegistered()` method to the User<commit_after>
'use strict';

var EventEmitter = require( 'events' ).EventEmitter;
var util = require( './util' );

/**
 * Single user on the server.
 *
 * @param {Object} data - User data.
 * @param {MumbleClient} client - Mumble client that owns this user.
 */
var User = function( data, client ) {
    this.client = client;
    this._applyProperties( data );
};

User.prototype = Object.create( EventEmitter.prototype );

/**
 * @summary Moves the user to a channel
 *
 * @param {Channel|string} channel - Channel name or a channel object
 */
User.prototype.moveToChannel = function( channel ) {

    // Fetch the channel ID based on the parameter.
    var id;
    if( typeof channel === 'string' ) {
        id = this.client.channelByName( channel ).id;
    } else if( typeof channel === 'object' ) {
        id = channel.id;
    } else {
        return;
    }

    // Send the move message.
    this.client.connection.sendMessage( 'UserState', {
        session: this.session, actor: this.client.user.session, channel_id: id } );
};

/**
 * @summary Change a user's self deafened state. (Obviously) only works on yourself.
 *
 * @param {Boolean} isSelfDeaf - The new self deafened state
 */
User.prototype.setSelfDeaf = function( isSelfDeaf ) {
    this.client.connection.sendMessage( 'UserState', {
        session: this.session, actor: this.client.user.session, self_deaf: isSelfDeaf } );
};

/**
 * @summary Change a user's self muted state. (Obviously) only works on yourself.
 *
 * @param {Boolean} isSelfMute - The new self muted state
 */
User.prototype.setSelfMute = function( isSelfMute ) {
    this.client.connection.sendMessage( 'UserState', {
        session: this.session, actor: this.client.user.session, self_mute: isSelfMute } );
};

/**
 * @summary Attempts to kick the user.
 *
 * @param {string} [reason] - The reason to kick the user for.
 */
User.prototype.kick = function( reason ) {
    this._sendRemoveUser( reason || 'You have been kicked', false );
};

/**
 * @summary Attempts to ban the user.
 *
 * @param {string} [reason] - The reason to ban the user for.
 */
User.prototype.ban = function( reason ) {
    this._sendRemoveUser( reason || 'You have been banned', true );
};

/**
 * @summary Sends a message to the user.
 *
 * @param {string} message - The message to send.
 */
User.prototype.sendMessage = function( message ) {
    this.client.sendMessage( message, { session: [ this.session ] } );
};

/**
 * @summary Returns an output stream for listening to the user audio.
 *
 * @param {Boolean} [noEmptyFrames]
 *      True to cut the output stream during silence. If the output stream
 *      isn't cut it will keep emitting zero-values when the user isn't
 *      talking.
 *
 * @returns {MumbleOutputStream} Output stream.
 */
User.prototype.outputStream = function( noEmptyFrames ) {
    return this.client.connection.outputStream( this.session, noEmptyFrames );
};

/**
 * @summary Returns an input stream for sending audio to the user.
 *
 * @returns {MumbleInputStream} Input stream.
 */
User.prototype.inputStream = function() {
    return this.client.inputStreamForUser( this.session );
};

/**
 * @summary Checks whether the user can talk or not.
 *
 * @returns {Boolean} True if the user can talk.
 */
User.prototype.canTalk = function() {
    return !this.mute && !this.selfMute && !this.suppress;
};

/**
 * @summary Checks whether the user can hear other people.
 *
 * @returns {Boolean} True if the user can hear.
 */
User.prototype.canHear = function() {
    return !this.selfDeaf;
};

/**
 * @summary _true_ when the user is registered on the server.
 *
 * @returns {Boolena} True if the user is registered.
 */
User.prototype.isRegistered = function() {
    return !!this.id;
};

User.prototype._applyProperties = function( data ) {

    /**
     * @summary Session ID
     *
     * @description
     * Session ID is present for all users. The ID specifies the current user
     * session and will change when the user reconnects.
     *
     * @see User#id
     *
     * @name User#session
     * @type Number
     */
    this.session = data.session;

    /**
     * @summary User name
     *
     * @name User#name
     * @type String
     */
    this.name = data.name;

    /**
     * @summary User ID
     *
     * @description
     * User ID is specified only for users who are registered on the server.
     * The user ID won't change when the user reconnects.
     *
     * @see User#session
     *
     * @name User#id
     * @type Number
     */
    this.id = data.user_id;

    /**
     * @summary _true_ when the user is muted by an admin.
     *
     * @name User#mute
     * @type Boolean
     */
    this.mute = data.mute;

    /**
     * @summary _true_ when the user is deafened by an admin.
     *
     * @name User#deaf
     * @type Boolean
     */
    this.deaf = data.deaf;

    /**
     * @summary _true_ when the user is suppressed due to lack of
     * permissions.
     *
     * @description
     * The user will be suppressed by the server if they don't have permissions
     * to speak on the current channel.
     *
     * @name User#suppress
     * @type Boolean
     */
    this.suppress = data.suppress;

    /**
     * @summary _true_ when the user has muted themselves.
     *
     * @name User#selfMute
     * @type Boolean
     */
    this.selfMute = data.self_mute;

    /**
     * @summary _true_ when the user has deafened themselves.
     *
     * @name User#selfDeaf
     * @type Boolean
     */
    this.selfDeaf = data.self_deaf;

    /**
     * @summary The hash of the user certificate
     *
     * @name User#hash
     * @type String
     */
    this.hash = data.hash;

    /**
     * @summary _true_ when the user is recording the conversation.
     *
     * @name User#recording
     * @type Boolean
     */
    this.recording = data.recording;

    /**
     * @summary _true_ when the user is a priority speaker.
     *
     * @name User#prioritySpeaker
     * @type Boolean
     */
    this.prioritySpeaker = data.priority_speaker;

    /**
     * @summary User's current channel.
     *
     * @name User#channel
     * @type Channel
     */
    if( data.channel_id !== null ) {
        this.channel = this.client.channelById( data.channel_id );
    } else { // New users always enter root
        this.channel = this.client.rootChannel;
    }
    this.channel._addUser( this );

    // TODO: Comments, textures
};

/**
 * @summary Emitted when the user disconnects
 *
 * @description
 * Also available through the client `user-disconnect` event.
 *
 * @event User#disconnect
 */

User.prototype._detach = function() {
    this.emit( 'disconnect' );
    this.channel._removeUser( this );
};

/**
 * @summary Emitted when the user moves between channels.
 *
 * @event User#move
 * @param {Channel} oldChannel - The channel where the user was moved from.
 * @param {Channel} newChannel - The channel where the user was moved to.
 * @param {User} actor - The user who moved the channel or undefined for server.
 */

/**
 * @summary Checks whether the user changed channel.
 *
 * @private
 *
 * @param {Object} data - New user data.
 */
User.prototype._checkChangeChannel = function( data ) {

    // Get the two channel instances.
    var newChannel = this.client.channelById( data.channel_id );
    var oldChannel = this.channel;

    // Make sure there is a change in the channel.
    if( newChannel === oldChannel )
        return;

    // Make the channel change and notify listeners.
    this.channel = newChannel;
    oldChannel._removeUser( this );
    newChannel._addUser( this );

    var actor = this.client.userBySession( data.actor );
    this.emit( 'move', oldChannel, newChannel, actor );
};

/**
 * @summary Emitted when the user is muted or unmuted by the server.
 *
 * @description
 * Also available through the client `user-mute` event.
 *
 * @event User#mute
 * @param {Boolean} status
 *      True when the user is muted, false when unmuted.
 */

/**
 * @summary Emitted when the user mutes or unmutes themselves.
 *
 * @description
 * Also available through the client `user-self-mute` event.
 *
 * @event User#self-mute
 * @param {Boolean} status
 *      True when the user mutes themselves. False when unmuting.
 */

/**
 * @summary Emitted when the user deafens or undeafens themselves.
 *
 * @description
 * Also available through the client `user-self-deaf` event.
 *
 * @event User#self-deaf
 * @param {Boolean} status
 *      True when the user deafens themselves. False when undeafening.
 */

/**
 * @summary Emitted when the user is suppressed or unsuppressed.
 *
 * @description
 * Also available through the client `user-suppress` event.
 *
 * @event User#suppress
 * @param {Boolean} status
 *      True when the user is suppressed. False when unsuppressed.
 */

/**
 * @summary Emitted when the user starts or stops recording.
 *
 * @description
 * Also available through the client `user-recording` event.
 *
 * @event User#recording
 * @param {Boolean} status
 *      True when the user starts recording. False when they stop.
 */

/**
 * @summary Emitted when the user gains or loses priority speaker status.
 *
 * @description
 * Also available through the client `user-priority-speaker` event.
 *
 * @event User#priority-speaker
 * @param {Boolean} status
 *      True when the user gains priority speaker status. False when they lose
 *      it.
 */

User.prototype.update = function( data ) {
    var self = this;

    // Check the simple fields.
    [
        'mute', 'selfMute', 'suppress',
        'selfDeaf',
        'recording', 'prioritySpeaker',
    ].forEach( function( f ) {
        self._checkField( data, f );
    } );

    // Channel check
    if( data.channel_id !== null ) {
        this._checkChangeChannel( data );
    }

};

User.prototype._sendRemoveUser = function( reason, ban ) {
    this.client.connection.sendMessage( 'UserRemove', {
        session: this.session,
        actor: this.client.user.session,
        reason: reason,
        ban: ban
    } );
};

User.prototype._checkField = function( data, field ) {

    // Make sure the field has a value.
    var newValue = data[ util.toFieldName( field ) ];
    if( newValue === undefined )
        return;

    // Make sure the new value differs.
    var oldValue = this[ field ];
    if( newValue === oldValue )
        return;

    // All checks succeeded. Store the new value and emit change event.
    this[ field ] = newValue;
    var actor = this.client.userBySession( data.actor );
    this.emit( util.toEventName( field ), newValue, actor );
};

module.exports = User;
<|endoftext|>


<commit_before>
"use strict";

var EventEmitter = require('events').EventEmitter;
var util = require( './util' );

/**
 * Single user on the server.
 */
var User = function(data, client) {
    this.client = client;
    this._applyProperties(data);
};

User.prototype = Object.create(EventEmitter.prototype);

/**
 * @summary Moves the user to a channel
 *
 * @param {Channel|String} channel - Channel name or a channel object
 */
User.prototype.moveToChannel = function(channel) {
    var id;
    if(typeof channel === "string") {
        id = this.client.channelByName(channel).id;
    }
    else if(typeof channel === "object") {
        id = channel.id;
    }
    else {
        return;
    }
    this.client.connection.sendMessage( 'UserState', { session: this.session, actor: this.client.user.session, channel_id: id });
};

/**
 * @summary Sends a message to the user.
 *
 * @param {String} message - The message to send.
 */
User.prototype.sendMessage = function(message) {
    this.client.sendMessage( message, { session: [ this.session ] } );
};

/**
 * @summary Returns an output stream for listening to the user audio.
 *
 * @param {Boolean} [noEmptyFrames]
 *      True to cut the output stream during silence. If the output stream
 *      isn't cut it will keep emitting zero-values when the user isn't
 *      talking.
 *
 * @returns {MumbleOutputStream} Output stream.
 */
User.prototype.outputStream = function(noEmptyFrames) {
    return this.client.connection.outputStream(this.session, noEmptyFrames);
};

/**
 * @summary Returns an input stream for sending audio to the user.
 *
 * @returns {MumbleInputStream} Input stream.
 */
User.prototype.inputStream = function() {
    return this.client.inputStreamForUser( this.session );
};

/**
 * @summary Checks whether the user can talk or not.
 *
 * @returns {Boolean} True if the user can talk.
 */
User.prototype.canTalk = function() {
    return !this.mute && !this.selfMute && !this.suppress;
};

/**
 * @summary Checks whether the user can hear other people.
 *
 * @returns {Boolean} True if the user can hear.
 */
User.prototype.canHear = function() {
    return !this.selfDeaf;
};

User.prototype._applyProperties = function(data) {

    /**
     * @summary Session ID
     *
     * @description
     * Session ID is present for all users. The ID specifies the current user
     * session and will change when the user reconnects.
     *
     * @see User#id
     *
     * @name User#session
     * @type Number
     */
    this.session = data.session;

    /**
     * @summary User name
     *
     * @name User#name
     * @type String
     */
    this.name = data.name;

    /**
     * @summary User ID
     *
     * @description
     * User ID is specified only for users who are registered on the server.
     * The user ID won't change when the user reconnects.
     *
     * @see User#session
     *
     * @name User#id
     * @type Number
     */
    this.id = data.user_id;

    /**
     * @summary _true_ when the user is muted by an admin.
     *
     * @name User#mute
     * @type Boolean
     */
    this.mute = data.mute;

    /**
     * @summary _true_ when the user is deafened by an admin.
     *
     * @name User#deaf
     * @type Boolean
     */
    this.deaf = data.deaf;

    /**
     * @summary _true_ when the user is suppressed due to lack of
     * permissions.
     *
     * @description
     * The user will be suppressed by the server if they don't have permissions
     * to speak on the current channel.
     *
     * @name User#suppress
     * @type Boolean
     */
    this.suppress = data.suppress;

    /**
     * @summary _true_ when the user has muted themselves.
     *
     * @name User#selfMute
     * @type Boolean
     */
    this.selfMute = data.self_mute;

    /**
     * @summary _true_ when the user has deafened themselves.
     *
     * @name User#selfDeaf
     * @type Boolean
     */
    this.selfDeaf = data.self_deaf;

    /**
     * @summary The hash of the user certificate
     *
     * @name User#hash
     * @type String
     */
    this.hash = data.hash;

    /**
     * @summary _true_ when the user is recording the conversation.
     *
     * @name User#recording
     * @type Boolean
     */
    this.recording = data.recording;

    /**
     * @summary _true_ when the user is a priority speaker.
     *
     * @name User#prioritySpeaker
     * @type Boolean
     */
    this.prioritySpeaker = data.priority_speaker;

    /**
     * @summary User's current channel.
     *
     * @name User#channel
     * @type Channel
     */
    if(data.channel_id !== null) {
        this.channel = this.client.channelById(data.channel_id);
    }
    else { // New users always enter root
        this.channel = this.client.rootChannel;
    }
    this.channel._addUser(this);
    //TODO: Comments, textures
};

/**
 * @summary Emitted when the user disconnects
 *
 * @description
 * Also available through the client `user-disconnect` event.
 *
 * @event User#disconnect
 */

User.prototype._detach = function() {
    this.emit('disconnect');
    this.channel._removeUser(this);
};

/**
 * @summary Emitted when the user moves between channels.
 *
 * @event User#move
 * @param {Channel} oldChannel - The channel where the user was moved from.
 * @param {Channel} newChannel - The channel where the user was moved to.
 * @param {User} actor - The user who moved the channel or undefined for server.
 */
User.prototype._checkChangeChannel = function( data ) {

    // Get the two channel instances.
    var newChannel = this.client.channelById( data.channel_id );
    var oldChannel = this.channel;

    // Make sure there is a change in the channel.
    if( newChannel === oldChannel )
        return;

    // Make the channel change and notify listeners.
    this.channel = newChannel;
    oldChannel._removeUser( this );
    newChannel._addUser( this );

    var actor = this.client.userBySession( data.actor );
    this.emit( 'move', oldChannel, newChannel, actor );
};

/**
 * @summary Emitted when the user is muted or unmuted by the server.
 *
 * @description
 * Also available through the client `user-mute` event.
 *
 * @event User#mute
 * @param {Boolean} status
 *      True when the user is muted, false when unmuted.
 */

/**
 * @summary Emitted when the user mutes or unmutes themselves.
 *
 * @description
 * Also available through the client `user-self-mute` event.
 *
 * @event User#self-mute
 * @param {Boolean} status
 *      True when the user mutes themselves. False when unmuting.
 */

/**
 * @summary Emitted when the user deafens or undeafens themselves.
 *
 * @description
 * Also available through the client `user-self-deaf` event.
 *
 * @event User#self-deaf
 * @param {Boolean} status
 *      True when the user deafens themselves. False when undeafening.
 */

/**
 * @summary Emitted when the user is suppressed or unsuppressed.
 *
 * @description
 * Also available through the client `user-suppress` event.
 *
 * @event User#suppress
 * @param {Boolean} status
 *      True when the user is suppressed. False when unsuppressed.
 */

/**
 * @summary Emitted when the user starts or stops recording.
 *
 * @description
 * Also available through the client `user-recording` event.
 *
 * @event User#recording
 * @param {Boolean} status
 *      True when the user starts recording. False when they stop.
 */

/**
 * @summary Emitted when the user gains or loses priority speaker status.
 *
 * @description
 * Also available through the client `user-priority-speaker` event.
 *
 * @event User#priority-speaker
 * @param {Boolean} status
 *      True when the user gains priority speaker status. False when they lose
 *      it.
 */

User.prototype.update = function(data) {
    var self = this;

    // Check the simple fields.
    [
        'mute', 'selfMute', 'suppress',
        'selfDeaf',
        'recording', 'prioritySpeaker',
    ].forEach( function(f) {
        self._checkField( data, f );
    });

    // Channel check
    if( data.channel_id !== null ) {
        this._checkChangeChannel( data );
    }

};

User.prototype._checkField = function( data, field ) {

    // Make sure the field has a value.
    var newValue = data[ field ];
    if( newValue === undefined )
        return;

    // Make sure the new value differs.
    var oldValue = this[ field ];
    if( newValue === oldValue )
        return;

    // All checks succeeded. Store the new value and emit change event.
    this[ field ] = newValue;
    var actor = this.client.userBySession( data.actor );
    this.emit( util.toEventName( field ), newValue, actor );
};

module.exports = User;
<commit_msg>Add User.kick<commit_after>
"use strict";

var EventEmitter = require('events').EventEmitter;
var util = require( './util' );

/**
 * Single user on the server.
 */
var User = function(data, client) {
    this.client = client;
    this._applyProperties(data);
};

User.prototype = Object.create(EventEmitter.prototype);

/**
 * @summary Moves the user to a channel
 *
 * @param {Channel|String} channel - Channel name or a channel object
 */
User.prototype.moveToChannel = function(channel) {
    var id;
    if(typeof channel === "string") {
        id = this.client.channelByName(channel).id;
    }
    else if(typeof channel === "object") {
        id = channel.id;
    }
    else {
        return;
    }
    this.client.connection.sendMessage( 'UserState', { session: this.session, actor: this.client.user.session, channel_id: id });
};

/**
 * @summary Attempts to kick the user.
 *
 * @param {String} [reason] - The reason to kick the user for.
 * @param {Boolean} [ban] - If true, this will attempt to ban the user as well.
 */
User.prototype.kick = function(reason, ban) {
    this.client.connection.sendMessage( 'UserRemove', { session.this.session, actor: this.client.user.session, reason: reason, ban: ban } );
};

/**
 * @summary Sends a message to the user.
 *
 * @param {String} message - The message to send.
 */
User.prototype.sendMessage = function(message) {
    this.client.sendMessage( message, { session: [ this.session ] } );
};

/**
 * @summary Returns an output stream for listening to the user audio.
 *
 * @param {Boolean} [noEmptyFrames]
 *      True to cut the output stream during silence. If the output stream
 *      isn't cut it will keep emitting zero-values when the user isn't
 *      talking.
 *
 * @returns {MumbleOutputStream} Output stream.
 */
User.prototype.outputStream = function(noEmptyFrames) {
    return this.client.connection.outputStream(this.session, noEmptyFrames);
};

/**
 * @summary Returns an input stream for sending audio to the user.
 *
 * @returns {MumbleInputStream} Input stream.
 */
User.prototype.inputStream = function() {
    return this.client.inputStreamForUser( this.session );
};

/**
 * @summary Checks whether the user can talk or not.
 *
 * @returns {Boolean} True if the user can talk.
 */
User.prototype.canTalk = function() {
    return !this.mute && !this.selfMute && !this.suppress;
};

/**
 * @summary Checks whether the user can hear other people.
 *
 * @returns {Boolean} True if the user can hear.
 */
User.prototype.canHear = function() {
    return !this.selfDeaf;
};

User.prototype._applyProperties = function(data) {

    /**
     * @summary Session ID
     *
     * @description
     * Session ID is present for all users. The ID specifies the current user
     * session and will change when the user reconnects.
     *
     * @see User#id
     *
     * @name User#session
     * @type Number
     */
    this.session = data.session;

    /**
     * @summary User name
     *
     * @name User#name
     * @type String
     */
    this.name = data.name;

    /**
     * @summary User ID
     *
     * @description
     * User ID is specified only for users who are registered on the server.
     * The user ID won't change when the user reconnects.
     *
     * @see User#session
     *
     * @name User#id
     * @type Number
     */
    this.id = data.user_id;

    /**
     * @summary _true_ when the user is muted by an admin.
     *
     * @name User#mute
     * @type Boolean
     */
    this.mute = data.mute;

    /**
     * @summary _true_ when the user is deafened by an admin.
     *
     * @name User#deaf
     * @type Boolean
     */
    this.deaf = data.deaf;

    /**
     * @summary _true_ when the user is suppressed due to lack of
     * permissions.
     *
     * @description
     * The user will be suppressed by the server if they don't have permissions
     * to speak on the current channel.
     *
     * @name User#suppress
     * @type Boolean
     */
    this.suppress = data.suppress;

    /**
     * @summary _true_ when the user has muted themselves.
     *
     * @name User#selfMute
     * @type Boolean
     */
    this.selfMute = data.self_mute;

    /**
     * @summary _true_ when the user has deafened themselves.
     *
     * @name User#selfDeaf
     * @type Boolean
     */
    this.selfDeaf = data.self_deaf;

    /**
     * @summary The hash of the user certificate
     *
     * @name User#hash
     * @type String
     */
    this.hash = data.hash;

    /**
     * @summary _true_ when the user is recording the conversation.
     *
     * @name User#recording
     * @type Boolean
     */
    this.recording = data.recording;

    /**
     * @summary _true_ when the user is a priority speaker.
     *
     * @name User#prioritySpeaker
     * @type Boolean
     */
    this.prioritySpeaker = data.priority_speaker;

    /**
     * @summary User's current channel.
     *
     * @name User#channel
     * @type Channel
     */
    if(data.channel_id !== null) {
        this.channel = this.client.channelById(data.channel_id);
    }
    else { // New users always enter root
        this.channel = this.client.rootChannel;
    }
    this.channel._addUser(this);
    //TODO: Comments, textures
};

/**
 * @summary Emitted when the user disconnects
 *
 * @description
 * Also available through the client `user-disconnect` event.
 *
 * @event User#disconnect
 */

User.prototype._detach = function() {
    this.emit('disconnect');
    this.channel._removeUser(this);
};

/**
 * @summary Emitted when the user moves between channels.
 *
 * @event User#move
 * @param {Channel} oldChannel - The channel where the user was moved from.
 * @param {Channel} newChannel - The channel where the user was moved to.
 * @param {User} actor - The user who moved the channel or undefined for server.
 */
User.prototype._checkChangeChannel = function( data ) {

    // Get the two channel instances.
    var newChannel = this.client.channelById( data.channel_id );
    var oldChannel = this.channel;

    // Make sure there is a change in the channel.
    if( newChannel === oldChannel )
        return;

    // Make the channel change and notify listeners.
    this.channel = newChannel;
    oldChannel._removeUser( this );
    newChannel._addUser( this );

    var actor = this.client.userBySession( data.actor );
    this.emit( 'move', oldChannel, newChannel, actor );
};

/**
 * @summary Emitted when the user is muted or unmuted by the server.
 *
 * @description
 * Also available through the client `user-mute` event.
 *
 * @event User#mute
 * @param {Boolean} status
 *      True when the user is muted, false when unmuted.
 */

/**
 * @summary Emitted when the user mutes or unmutes themselves.
 *
 * @description
 * Also available through the client `user-self-mute` event.
 *
 * @event User#self-mute
 * @param {Boolean} status
 *      True when the user mutes themselves. False when unmuting.
 */

/**
 * @summary Emitted when the user deafens or undeafens themselves.
 *
 * @description
 * Also available through the client `user-self-deaf` event.
 *
 * @event User#self-deaf
 * @param {Boolean} status
 *      True when the user deafens themselves. False when undeafening.
 */

/**
 * @summary Emitted when the user is suppressed or unsuppressed.
 *
 * @description
 * Also available through the client `user-suppress` event.
 *
 * @event User#suppress
 * @param {Boolean} status
 *      True when the user is suppressed. False when unsuppressed.
 */

/**
 * @summary Emitted when the user starts or stops recording.
 *
 * @description
 * Also available through the client `user-recording` event.
 *
 * @event User#recording
 * @param {Boolean} status
 *      True when the user starts recording. False when they stop.
 */

/**
 * @summary Emitted when the user gains or loses priority speaker status.
 *
 * @description
 * Also available through the client `user-priority-speaker` event.
 *
 * @event User#priority-speaker
 * @param {Boolean} status
 *      True when the user gains priority speaker status. False when they lose
 *      it.
 */

User.prototype.update = function(data) {
    var self = this;

    // Check the simple fields.
    [
        'mute', 'selfMute', 'suppress',
        'selfDeaf',
        'recording', 'prioritySpeaker',
    ].forEach( function(f) {
        self._checkField( data, f );
    });

    // Channel check
    if( data.channel_id !== null ) {
        this._checkChangeChannel( data );
    }

};

User.prototype._checkField = function( data, field ) {

    // Make sure the field has a value.
    var newValue = data[ field ];
    if( newValue === undefined )
        return;

    // Make sure the new value differs.
    var oldValue = this[ field ];
    if( newValue === oldValue )
        return;

    // All checks succeeded. Store the new value and emit change event.
    this[ field ] = newValue;
    var actor = this.client.userBySession( data.actor );
    this.emit( util.toEventName( field ), newValue, actor );
};

module.exports = User;
<|endoftext|>


<commit_before>import async from 'async';
import cpr from 'cpr';
import dateformat from 'dateformat';
import Engine from './engine.js';
import minifier from 'minifier';
import p from 'path';
// import watchtree from 'watch-tree-maintained';
import wrench from 'wrench';
// console.dir(p.join(process.cwd(), 'params.js'))
// import userlandParams from p.join(process.cwd(), 'test/fixtures/params.js');

/**
 * class AE86
 */
function AE86(opts) {
  opts = opts || {};
  this.outDir = opts.outDir || 'out';
}

/**
 * Create example AE86 project files in current directory.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
AE86.prototype.init = function (cb) {
  cpr.cpr(p.join(__dirname, '../examples'), '.', cb);
};

/**
 * Generate website based on the templates and params.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
AE86.prototype.generate = function (cb) {

  const self = this;

  function _static(cb) {
    // copy static files as-is
    cpr.cpr('static', self.outDir, function (err) {
      if (err) {
        cb(err);
      } else {
        minifier.on('error', cb);
        minifier.minify(self.outDir, { clean: true });
        cb();
      }
    });
  }

  function _pages(cb) {

    function _params() {
      // initialise userland params
      const params = {}; //userlandParams.params;
      // add website info
      params.sitemap = params.sitemap || {};
      params.__genId = dateformat('yyyymmddHHMMss');
      return params;
    }

    const engine = new Engine(),
      tasks = {};

    ['partials', 'layouts', 'pages'].forEach(function (dir) {
      tasks[dir] = function (cb) {
        engine.compile(dir, cb);
      };
    });

    async.parallel(tasks, function (err, results) {
      engine.merge(self.outDir, results, _params(), cb);
    });
  }

  async.parallel([_static, _pages], cb);
};

// /**
//  * Watch for any file changes in AE86 project files.
//  * A change means the project website will automatically be regenerated.
//  */
// AE86.prototype.watch = function () {
//   const SAMPLE_RATE = 5;

//   const self = this;

//   function _listener() {
//     // remove cached params.js module so the regeneration will pick up a fresh (possibly modified) params.js
//     delete require.cache[p.join(process.cwd(), 'params.js')];
//     // no callback because the process shouldn't exit
//     self.generate();
//   }

//   function _watch(file) {
//     const watcher = watchtree.watchTree(file, {
//       ignore: '\\.swp',
//       'sample-rate': SAMPLE_RATE
//     });
//     watcher.on('fileCreated', function(path, stats) {
//       console.log('%s was created', file);
//       _listener();
//     });
//     watcher.on('fileModified', function(path, stats) {
//       console.log('%s was modified', file);
//       _listener();
//     });
//     watcher.on('fileDeleted', function(path) {
//       console.log('%s was deleted', file);
//       self.clean();
//       _listener();
//     });
//   }

//   ['static', 'partials', 'layouts', 'pages', 'params.js'].forEach(_watch);
// };

/**
 * Remove the generated website.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
AE86.prototype.clean = function (cb) {
  wrench.rmdirRecursive(this.outDir, cb);
};

export {
  AE86 as default
};<commit_msg>Re-enable watch function.<commit_after>import async from 'async';
import cpr from 'cpr';
import dateformat from 'dateformat';
import Engine from './engine.js';
import minifier from 'minifier';
import p from 'path';
import watchtree from 'watch-tree-maintained';
import wrench from 'wrench';
// console.dir(p.join(process.cwd(), 'params.js'))
// import userlandParams from p.join(process.cwd(), 'test/fixtures/params.js');

/**
 * class AE86
 */
function AE86(opts) {
  opts = opts || {};
  this.outDir = opts.outDir || 'out';
}

/**
 * Create example AE86 project files in current directory.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
AE86.prototype.init = function (cb) {
  cpr.cpr(p.join(__dirname, '../examples'), '.', cb);
};

/**
 * Generate website based on the templates and params.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
AE86.prototype.generate = function (cb) {

  const self = this;

  function _static(cb) {
    // copy static files as-is
    cpr.cpr('static', self.outDir, function (err) {
      if (err) {
        cb(err);
      } else {
        minifier.on('error', cb);
        minifier.minify(self.outDir, { clean: true });
        cb();
      }
    });
  }

  function _pages(cb) {

    function _params() {
      // initialise userland params
      const params = {}; //userlandParams.params;
      // add website info
      params.sitemap = params.sitemap || {};
      params.__genId = dateformat('yyyymmddHHMMss');
      return params;
    }

    const engine = new Engine(),
      tasks = {};

    ['partials', 'layouts', 'pages'].forEach(function (dir) {
      tasks[dir] = function (cb) {
        engine.compile(dir, cb);
      };
    });

    async.parallel(tasks, function (err, results) {
      engine.merge(self.outDir, results, _params(), cb);
    });
  }

  async.parallel([_static, _pages], cb);
};

/**
 * Watch for any file changes in AE86 project files.
 * A change means the project website will automatically be regenerated.
 */
AE86.prototype.watch = function () {
  const SAMPLE_RATE = 5;

  const self = this;

  function _listener() {
    // remove cached params.js module so the regeneration will pick up a fresh (possibly modified) params.js
    delete require.cache[p.join(process.cwd(), 'params.js')];
    // no callback because the process shouldn't exit
    self.generate();
  }

  function _watch(file) {
    const watcher = watchtree.watchTree(file, {
      ignore: '\\.swp',
      'sample-rate': SAMPLE_RATE
    });
    /* eslint-disable */
    watcher.on('fileCreated', function(path, stats) {
      console.log('%s was created', file);
      _listener();
    });
    watcher.on('fileModified', function(path, stats) {
      console.log('%s was modified', file);
      _listener();
    });
    watcher.on('fileDeleted', function(path) {
      console.log('%s was deleted', file);
      self.clean();
      _listener();
    });
    /* eslint-enable */
  }

  ['static', 'partials', 'layouts', 'pages', 'params.js'].forEach(_watch);
};

/**
 * Remove the generated website.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
AE86.prototype.clean = function (cb) {
  wrench.rmdirRecursive(this.outDir, cb);
};

export {
  AE86 as default
};<|endoftext|>