<commit_before>var ws = require('ws');
var WSServer = ws.Server;
var SCSocket = require('./scsocket');
var AuthEngine = require('../auth').AuthEngine;
var EventEmitter = require('events').EventEmitter;
var base64id = require('base64id');
var async = require('async');
var url = require('url');
var domain = require('domain');

var SCServer = function (options) {
  var self = this;

  var opts = {
    ackTimeout: 10000,
    allowClientPublish: true
  };

  for (var i in options) {
    if (options.hasOwnProperty(i)) {
      opts[i] = options[i];
    }
  }

  if (opts.path == null) {
    opts.path = '/socketcluster/';
  }

  this.MIDDLEWARE_HANDSHAKE = 'handshake';
  this.MIDDLEWARE_EMIT = 'emit';
  this.MIDDLEWARE_SUBSCRIBE = 'subscribe';
  this.MIDDLEWARE_PUBLISH_IN = 'publishIn';
  this.MIDDLEWARE_PUBLISH_OUT = 'publishOut';

  // Deprecated
  this.MIDDLEWARE_PUBLISH = this.MIDDLEWARE_PUBLISH_IN;

  this._subscribeEvent = '#subscribe';
  this._publishEvent = '#publish';

  this.ERROR_NO_PUBLISH = 'Error: Client publish feature is disabled';

  this._middleware = {};
  this._middleware[this.MIDDLEWARE_HANDSHAKE] = [];
  this._middleware[this.MIDDLEWARE_EMIT] = [];
  this._middleware[this.MIDDLEWARE_SUBSCRIBE] = [];
  this._middleware[this.MIDDLEWARE_PUBLISH_IN] = [];
  this._middleware[this.MIDDLEWARE_PUBLISH_OUT] = [];

  this.origins = opts.origins;
  this._allowAllOrigins = this.origins.indexOf('*:*') != -1;

  this.ackTimeout = opts.ackTimeout;
  this.pingInterval = opts.pingInterval;
  this.pingTimeout = opts.pingTimeout;
  this.allowClientPublish = opts.allowClientPublish;
  this.perMessageDeflate = opts.perMessageDeflate;
  this.httpServer = opts.httpServer;

  this._ioClusterClient = opts.ioClusterClient;
  this.appName = opts.appName || '';
  this.middlewareEmitNotices = opts.middlewareEmitNotices;
  this._path = opts.path;

  this.authKey = opts.authKey;
  this.defaultAuthTokenExpiryInMinutes = opts.defaultAuthTokenExpiryInMinutes;

  if (opts.authEngine) {
    this.auth = opts.authEngine;
  } else {
    // Default authentication engine
    this.auth = new AuthEngine();
  }

  this.clients = {};
  this.clientsCount = 0;

  this.global = this._ioClusterClient.global();

  this.wsServer = new WSServer({
    server: this.httpServer,
    clientTracking: false,
    perMessageDeflate: this.perMessageDeflate,
    handleProtocols: opts.handleProtocols,
    verifyClient: this.verifyHandshake.bind(this)
  });

  this.wsServer.on('error', this._handleServerError.bind(this));
  this.wsServer.on('connection', this._handleSocketConnection.bind(this));
};

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

SCServer.prototype.setAuthEngine = function (authEngine) {
  this.auth = authEngine;
};

SCServer.prototype._handleServerError = function (error) {
  this.emit('error', error);
};

SCServer.prototype._handleSocketError = function (error) {
  // We don't want to crash the entire worker on socket error
  // so we emit it as a notice instead.
  var errorPrefix = 'Socket Error: ';
  if (error.message) {
    error.message = errorPrefix + error.message;
  } else if (typeof error == 'string') {
    error = errorPrefix + error;
  }
  this.emit('notice', error);
};

SCServer.prototype._handleHandshakeTimeout = function (scSocket) {
  var errorMessage = new Error('Did not receive #handshake from client before timeout');
  scSocket.emit('error', errorMessage);
};

SCServer.prototype._processTokenError = function (socket, err, encryptedAuthToken) {
  // In case of an expired, malformed or invalid token, emit an event
  // and keep going without a token.
  var authError = null;

  if (err) {
    err.encryptedAuthToken = encryptedAuthToken;
    socket.emit('badAuthToken', err);
    this.emit('badSocketAuthToken', socket, err);

    authError = {
      name: err.name,
      message: err.message
    };
  }

  return authError;
};

SCServer.prototype._handleSocketConnection = function (wsSocket) {
  var self = this;

  var id = this.generateId();

  var socketDomain = domain.createDomain();
  var scSocket = new SCSocket(id, this, wsSocket);
  socketDomain.add(scSocket);

  // Emit event to signal that a socket handshake has been initiated.
  // The _handshake event is for internal use (including third-party plugins)
  this.emit('_handshake', scSocket);
  this.emit('handshake', scSocket);

  socketDomain.on('error', function (err) {
    self._handleSocketError(err);
  });

  scSocket.on('#authenticate', function (encryptedAuthToken, respond) {
    self.auth.verifyToken(encryptedAuthToken, self.authKey, function (err, authToken) {
      scSocket.authToken = authToken || null;

      var authError = self._processTokenError(scSocket, err, encryptedAuthToken);

      var authStatus = {
        isAuthenticated: !!authToken,
        authError: authError
      };

      // Treat authentication failure as a 'soft' error
      respond(null, authStatus);
    });
  });

  scSocket.once('_disconnect', function () {
    clearTimeout(scSocket._handshakeTimeout);
    scSocket.off('#handshake');
    scSocket.off('#authenticate');
  });

  scSocket._handshakeTimeout = setTimeout(this._handleHandshakeTimeout.bind(this, scSocket), this.ackTimeout);

  scSocket.once('#handshake', function (data, respond) {
    if (!data) {
      data = {};
    }
    var encryptedAuthToken = data.authToken;
    clearTimeout(scSocket._handshakeTimeout);

    self.auth.verifyToken(encryptedAuthToken, self.authKey, function (err, authToken) {
      scSocket.authToken = authToken || null;

      var authError;

      if (encryptedAuthToken != null) {
        authError = self._processTokenError(scSocket, err, encryptedAuthToken);
      }

      self.clients[id] = scSocket;
      self.clientsCount++;

      self._ioClusterClient.bind(scSocket, function (err, sock, isNotice) {
        if (err) {
          var errorMessage = 'Failed to bind socket to io cluster - ' + err;
          scSocket.emit('#fail', errorMessage);
          scSocket.disconnect();
          if (isNotice) {
            self.emit('notice', errorMessage);
          } else {
            self.emit('error', new Error(errorMessage));
          }
          respond(err);

        } else {
          scSocket.state = scSocket.OPEN;
          scSocket.global = self.global;

          self.emit('_connection', scSocket);
          self.emit('connection', scSocket);

          var status = {
            id: scSocket.id,
            isAuthenticated: !!authToken,
            pingTimeout: self.pingTimeout
          };

          if (authError) {
            status.authError = authError;
          }

          // Treat authentication failure as a 'soft' error
          respond(null, status);
        }
      });

      scSocket.once('_disconnect', function () {
        delete self.clients[id];
        self.clientsCount--;

        self._ioClusterClient.unbind(scSocket, function (err) {
          if (err) {
            self.emit('error', new Error('Failed to unbind socket from io cluster - ' + err));
          } else {
            self.emit('_disconnection', scSocket);
            self.emit('disconnection', scSocket);
          }
        });
      });
    });
  });
};

SCServer.prototype.close = function () {
  this.wsServer.close();
};

SCServer.prototype.getPath = function () {
  return this._path;
};

SCServer.prototype.generateId = function () {
  return base64id.generateId();
};

SCServer.prototype.on = function (event, listener) {
  if (event == 'ready') {
    this._ioClusterClient.once(event, listener);
  } else {
    EventEmitter.prototype.on.apply(this, arguments);
  }
};

SCServer.prototype.removeListener = function (event, listener) {
  if (event == 'ready') {
    this._ioClusterClient.removeListener(event, listener);
  } else {
    EventEmitter.prototype.removeListener.apply(this, arguments);
  }
};

SCServer.prototype.addMiddleware = function (type, middleware) {
  this._middleware[type].push(middleware);
};

SCServer.prototype.removeMiddleware = function (type, middleware) {
  var middlewareFunctions = this._middleware[type];

  this._middleware[type] = middlewareFunctions.filter(function (fn) {
    return fn != middleware;
  });
};

SCServer.prototype.verifyHandshake = function (info, cb) {
  var self = this;

  var req = info.req;
  var origin = info.origin;
  if (origin == 'null' || origin == null) {
    origin = '*';
  }
  var ok = false;

  if (this._allowAllOrigins) {
    ok = true;
  } else {
    try {
      var parts = url.parse(origin);
      parts.port = parts.port || 80;
      ok = ~this.origins.indexOf(parts.hostname + ':' + parts.port) ||
        ~this.origins.indexOf(parts.hostname + ':*') ||
        ~this.origins.indexOf('*:' + parts.port);
    } catch (e) {}
  }

  if (ok) {
    var handshakeMiddleware = this._middleware[this.MIDDLEWARE_HANDSHAKE];
    if (handshakeMiddleware.length) {
      var callbackInvoked = false;
      async.applyEachSeries(handshakeMiddleware, req, function (err) {
        if (callbackInvoked) {
          self.emit('notice', new Error('Callback for ' + self.MIDDLEWARE_HANDSHAKE + ' middleware was already invoked'));
        } else {
          callbackInvoked = true;
          if (err) {
            if (self.middlewareEmitNotices) {
              self.emit('notice', err);
            }
            cb(false, 401, err);
          } else {
            cb(true);
          }
        }
      });
    } else {
      cb(true);
    }
  } else {
    var err = 'Failed to authorize socket handshake - Invalid origin: ' + origin;
    this.emit('notice', err);
    cb(false, 403, err);
  }
};

SCServer.prototype._isPrivateTransmittedEvent = function (event) {
  return !!event && event.indexOf('#') == 0;
};

SCServer.prototype.verifyInboundEvent = function (socket, event, data, cb) {
  var self = this;

  var callbackInvoked = false;

  if (this._isPrivateTransmittedEvent(event)) {
    if (event == this._subscribeEvent) {
      async.applyEachSeries(this._middleware[this.MIDDLEWARE_SUBSCRIBE], socket, data,
        function (err) {
          if (callbackInvoked) {
            self.emit('notice', new Error('Callback for ' + self.MIDDLEWARE_SUBSCRIBE + ' middleware was already invoked'));
          } else {
            callbackInvoked = true;
            if (err && self.middlewareEmitNotices) {
              self.emit('notice', err);
            }
            cb(err);
          }
        }
      );
    } else if (event == this._publishEvent) {
      if (this.allowClientPublish) {
        async.applyEachSeries(this._middleware[this.MIDDLEWARE_PUBLISH_IN], socket, data.channel, data.data,
          function (err) {
            if (callbackInvoked) {
              self.emit('notice', new Error('Callback for ' + self.MIDDLEWARE_PUBLISH_IN + ' middleware was already invoked'));
            } else {
              callbackInvoked = true;
              if (err) {
                if (self.middlewareEmitNotices) {
                  self.emit('notice', err);
                }
                cb(err);
              } else {
                self.global.publish(data.channel, data.data, function (err) {
                  cb(err);
                });
              }
            }
          }
        );
      } else {
        cb(this.ERROR_NO_PUBLISH);
      }
    } else {
      // Do not allow blocking other reserved events or it could interfere with SC behaviour
      cb();
    }
  } else {
    async.applyEachSeries(this._middleware[this.MIDDLEWARE_EMIT], socket, event, data,
      function (err) {
        if (callbackInvoked) {
          self.emit('notice', new Error('Callback for ' + self.MIDDLEWARE_EMIT + ' middleware was already invoked'));
        } else {
          callbackInvoked = true;
          if (err && self.middlewareEmitNotices) {
            self.emit('notice', err);
          }
          cb(err);
        }
      }
    );
  }
};

SCServer.prototype.verifyOutboundEvent = function (socket, event, data, cb) {
  var self = this;

  var callbackInvoked = false;

  if (event == this._publishEvent) {
    async.applyEachSeries(this._middleware[this.MIDDLEWARE_PUBLISH_OUT], socket, data.channel, data.data,
      function (err) {
        if (callbackInvoked) {
          self.emit('notice', new Error('Callback for ' + self.MIDDLEWARE_PUBLISH_OUT + ' middleware was already invoked'));
        } else {
          callbackInvoked = true;
          if (err) {
            if (err !== true && self.middlewareEmitNotices) {
              self.emit('notice', err);
            }
            cb(err);
          } else {
            cb();
          }
        }
      }
    );
  } else {
    cb();
  }
};

module.exports = SCServer;
<commit_msg>If true is passed as the first error argument to middleware next() callback, the action will be blocked silently without emitting a notice.<commit_after>var ws = require('ws');
var WSServer = ws.Server;
var SCSocket = require('./scsocket');
var AuthEngine = require('../auth').AuthEngine;
var EventEmitter = require('events').EventEmitter;
var base64id = require('base64id');
var async = require('async');
var url = require('url');
var domain = require('domain');

var SCServer = function (options) {
  var self = this;

  var opts = {
    ackTimeout: 10000,
    allowClientPublish: true
  };

  for (var i in options) {
    if (options.hasOwnProperty(i)) {
      opts[i] = options[i];
    }
  }

  if (opts.path == null) {
    opts.path = '/socketcluster/';
  }

  this.MIDDLEWARE_HANDSHAKE = 'handshake';
  this.MIDDLEWARE_EMIT = 'emit';
  this.MIDDLEWARE_SUBSCRIBE = 'subscribe';
  this.MIDDLEWARE_PUBLISH_IN = 'publishIn';
  this.MIDDLEWARE_PUBLISH_OUT = 'publishOut';

  // Deprecated
  this.MIDDLEWARE_PUBLISH = this.MIDDLEWARE_PUBLISH_IN;

  this._subscribeEvent = '#subscribe';
  this._publishEvent = '#publish';

  this.ERROR_NO_PUBLISH = 'Error: Client publish feature is disabled';

  this._middleware = {};
  this._middleware[this.MIDDLEWARE_HANDSHAKE] = [];
  this._middleware[this.MIDDLEWARE_EMIT] = [];
  this._middleware[this.MIDDLEWARE_SUBSCRIBE] = [];
  this._middleware[this.MIDDLEWARE_PUBLISH_IN] = [];
  this._middleware[this.MIDDLEWARE_PUBLISH_OUT] = [];

  this.origins = opts.origins;
  this._allowAllOrigins = this.origins.indexOf('*:*') != -1;

  this.ackTimeout = opts.ackTimeout;
  this.pingInterval = opts.pingInterval;
  this.pingTimeout = opts.pingTimeout;
  this.allowClientPublish = opts.allowClientPublish;
  this.perMessageDeflate = opts.perMessageDeflate;
  this.httpServer = opts.httpServer;

  this._ioClusterClient = opts.ioClusterClient;
  this.appName = opts.appName || '';
  this.middlewareEmitNotices = opts.middlewareEmitNotices;
  this._path = opts.path;

  this.authKey = opts.authKey;
  this.defaultAuthTokenExpiryInMinutes = opts.defaultAuthTokenExpiryInMinutes;

  if (opts.authEngine) {
    this.auth = opts.authEngine;
  } else {
    // Default authentication engine
    this.auth = new AuthEngine();
  }

  this.clients = {};
  this.clientsCount = 0;

  this.global = this._ioClusterClient.global();

  this.wsServer = new WSServer({
    server: this.httpServer,
    clientTracking: false,
    perMessageDeflate: this.perMessageDeflate,
    handleProtocols: opts.handleProtocols,
    verifyClient: this.verifyHandshake.bind(this)
  });

  this.wsServer.on('error', this._handleServerError.bind(this));
  this.wsServer.on('connection', this._handleSocketConnection.bind(this));
};

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

SCServer.prototype.setAuthEngine = function (authEngine) {
  this.auth = authEngine;
};

SCServer.prototype._handleServerError = function (error) {
  this.emit('error', error);
};

SCServer.prototype._handleSocketError = function (error) {
  // We don't want to crash the entire worker on socket error
  // so we emit it as a notice instead.
  var errorPrefix = 'Socket Error: ';
  if (error.message) {
    error.message = errorPrefix + error.message;
  } else if (typeof error == 'string') {
    error = errorPrefix + error;
  }
  this.emit('notice', error);
};

SCServer.prototype._handleHandshakeTimeout = function (scSocket) {
  var errorMessage = new Error('Did not receive #handshake from client before timeout');
  scSocket.emit('error', errorMessage);
};

SCServer.prototype._processTokenError = function (socket, err, encryptedAuthToken) {
  // In case of an expired, malformed or invalid token, emit an event
  // and keep going without a token.
  var authError = null;

  if (err) {
    err.encryptedAuthToken = encryptedAuthToken;
    socket.emit('badAuthToken', err);
    this.emit('badSocketAuthToken', socket, err);

    authError = {
      name: err.name,
      message: err.message
    };
  }

  return authError;
};

SCServer.prototype._handleSocketConnection = function (wsSocket) {
  var self = this;

  var id = this.generateId();

  var socketDomain = domain.createDomain();
  var scSocket = new SCSocket(id, this, wsSocket);
  socketDomain.add(scSocket);

  // Emit event to signal that a socket handshake has been initiated.
  // The _handshake event is for internal use (including third-party plugins)
  this.emit('_handshake', scSocket);
  this.emit('handshake', scSocket);

  socketDomain.on('error', function (err) {
    self._handleSocketError(err);
  });

  scSocket.on('#authenticate', function (encryptedAuthToken, respond) {
    self.auth.verifyToken(encryptedAuthToken, self.authKey, function (err, authToken) {
      scSocket.authToken = authToken || null;

      var authError = self._processTokenError(scSocket, err, encryptedAuthToken);

      var authStatus = {
        isAuthenticated: !!authToken,
        authError: authError
      };

      // Treat authentication failure as a 'soft' error
      respond(null, authStatus);
    });
  });

  scSocket.once('_disconnect', function () {
    clearTimeout(scSocket._handshakeTimeout);
    scSocket.off('#handshake');
    scSocket.off('#authenticate');
  });

  scSocket._handshakeTimeout = setTimeout(this._handleHandshakeTimeout.bind(this, scSocket), this.ackTimeout);

  scSocket.once('#handshake', function (data, respond) {
    if (!data) {
      data = {};
    }
    var encryptedAuthToken = data.authToken;
    clearTimeout(scSocket._handshakeTimeout);

    self.auth.verifyToken(encryptedAuthToken, self.authKey, function (err, authToken) {
      scSocket.authToken = authToken || null;

      var authError;

      if (encryptedAuthToken != null) {
        authError = self._processTokenError(scSocket, err, encryptedAuthToken);
      }

      self.clients[id] = scSocket;
      self.clientsCount++;

      self._ioClusterClient.bind(scSocket, function (err, sock, isNotice) {
        if (err) {
          var errorMessage = 'Failed to bind socket to io cluster - ' + err;
          scSocket.emit('#fail', errorMessage);
          scSocket.disconnect();
          if (isNotice) {
            self.emit('notice', errorMessage);
          } else {
            self.emit('error', new Error(errorMessage));
          }
          respond(err);

        } else {
          scSocket.state = scSocket.OPEN;
          scSocket.global = self.global;

          self.emit('_connection', scSocket);
          self.emit('connection', scSocket);

          var status = {
            id: scSocket.id,
            isAuthenticated: !!authToken,
            pingTimeout: self.pingTimeout
          };

          if (authError) {
            status.authError = authError;
          }

          // Treat authentication failure as a 'soft' error
          respond(null, status);
        }
      });

      scSocket.once('_disconnect', function () {
        delete self.clients[id];
        self.clientsCount--;

        self._ioClusterClient.unbind(scSocket, function (err) {
          if (err) {
            self.emit('error', new Error('Failed to unbind socket from io cluster - ' + err));
          } else {
            self.emit('_disconnection', scSocket);
            self.emit('disconnection', scSocket);
          }
        });
      });
    });
  });
};

SCServer.prototype.close = function () {
  this.wsServer.close();
};

SCServer.prototype.getPath = function () {
  return this._path;
};

SCServer.prototype.generateId = function () {
  return base64id.generateId();
};

SCServer.prototype.on = function (event, listener) {
  if (event == 'ready') {
    this._ioClusterClient.once(event, listener);
  } else {
    EventEmitter.prototype.on.apply(this, arguments);
  }
};

SCServer.prototype.removeListener = function (event, listener) {
  if (event == 'ready') {
    this._ioClusterClient.removeListener(event, listener);
  } else {
    EventEmitter.prototype.removeListener.apply(this, arguments);
  }
};

SCServer.prototype.addMiddleware = function (type, middleware) {
  this._middleware[type].push(middleware);
};

SCServer.prototype.removeMiddleware = function (type, middleware) {
  var middlewareFunctions = this._middleware[type];

  this._middleware[type] = middlewareFunctions.filter(function (fn) {
    return fn != middleware;
  });
};

SCServer.prototype.verifyHandshake = function (info, cb) {
  var self = this;

  var req = info.req;
  var origin = info.origin;
  if (origin == 'null' || origin == null) {
    origin = '*';
  }
  var ok = false;

  if (this._allowAllOrigins) {
    ok = true;
  } else {
    try {
      var parts = url.parse(origin);
      parts.port = parts.port || 80;
      ok = ~this.origins.indexOf(parts.hostname + ':' + parts.port) ||
        ~this.origins.indexOf(parts.hostname + ':*') ||
        ~this.origins.indexOf('*:' + parts.port);
    } catch (e) {}
  }

  if (ok) {
    var handshakeMiddleware = this._middleware[this.MIDDLEWARE_HANDSHAKE];
    if (handshakeMiddleware.length) {
      var callbackInvoked = false;
      async.applyEachSeries(handshakeMiddleware, req, function (err) {
        if (callbackInvoked) {
          self.emit('notice', new Error('Callback for ' + self.MIDDLEWARE_HANDSHAKE + ' middleware was already invoked'));
        } else {
          callbackInvoked = true;
          if (err) {
            if (err === true) {
              err = 'Action was silently blocked by ' + self.MIDDLEWARE_HANDSHAKE + ' middleware';
            } else if (self.middlewareEmitNotices) {
              self.emit('notice', err);
            }
            cb(false, 401, err);
          } else {
            cb(true);
          }
        }
      });
    } else {
      cb(true);
    }
  } else {
    var err = 'Failed to authorize socket handshake - Invalid origin: ' + origin;
    this.emit('notice', err);
    cb(false, 403, err);
  }
};

SCServer.prototype._isPrivateTransmittedEvent = function (event) {
  return !!event && event.indexOf('#') == 0;
};

SCServer.prototype.verifyInboundEvent = function (socket, event, data, cb) {
  var self = this;

  var callbackInvoked = false;

  if (this._isPrivateTransmittedEvent(event)) {
    if (event == this._subscribeEvent) {
      async.applyEachSeries(this._middleware[this.MIDDLEWARE_SUBSCRIBE], socket, data,
        function (err) {
          if (callbackInvoked) {
            self.emit('notice', new Error('Callback for ' + self.MIDDLEWARE_SUBSCRIBE + ' middleware was already invoked'));
          } else {
            callbackInvoked = true;
            if (err) {
              if (err === true) {
                err = 'Action was silently blocked by ' + self.MIDDLEWARE_SUBSCRIBE + ' middleware';
              } else if (self.middlewareEmitNotices) {
                self.emit('notice', err);
              }
            }
            cb(err);
          }
        }
      );
    } else if (event == this._publishEvent) {
      if (this.allowClientPublish) {
        async.applyEachSeries(this._middleware[this.MIDDLEWARE_PUBLISH_IN], socket, data.channel, data.data,
          function (err) {
            if (callbackInvoked) {
              self.emit('notice', new Error('Callback for ' + self.MIDDLEWARE_PUBLISH_IN + ' middleware was already invoked'));
            } else {
              callbackInvoked = true;
              if (err) {
                if (err === true) {
                  err = 'Action was silently blocked by ' + self.MIDDLEWARE_PUBLISH_IN + ' middleware';
                } else if (self.middlewareEmitNotices) {
                  self.emit('notice', err);
                }
                cb(err);
              } else {
                self.global.publish(data.channel, data.data, function (err) {
                  cb(err);
                });
              }
            }
          }
        );
      } else {
        cb(this.ERROR_NO_PUBLISH);
      }
    } else {
      // Do not allow blocking other reserved events or it could interfere with SC behaviour
      cb();
    }
  } else {
    async.applyEachSeries(this._middleware[this.MIDDLEWARE_EMIT], socket, event, data,
      function (err) {
        if (callbackInvoked) {
          self.emit('notice', new Error('Callback for ' + self.MIDDLEWARE_EMIT + ' middleware was already invoked'));
        } else {
          callbackInvoked = true;
          if (err) {
            if (err === true) {
              err = 'Action was silently blocked by ' + self.MIDDLEWARE_EMIT + ' middleware';
            } else if (self.middlewareEmitNotices) {
              self.emit('notice', err);
            }
          }
          cb(err);
        }
      }
    );
  }
};

SCServer.prototype.verifyOutboundEvent = function (socket, event, data, cb) {
  var self = this;

  var callbackInvoked = false;

  if (event == this._publishEvent) {
    async.applyEachSeries(this._middleware[this.MIDDLEWARE_PUBLISH_OUT], socket, data.channel, data.data,
      function (err) {
        if (callbackInvoked) {
          self.emit('notice', new Error('Callback for ' + self.MIDDLEWARE_PUBLISH_OUT + ' middleware was already invoked'));
        } else {
          callbackInvoked = true;
          if (err) {
            if (err === true) {
              err = 'Action was silently blocked by ' + self.MIDDLEWARE_PUBLISH_OUT + ' middleware';
            } else if (self.middlewareEmitNotices) {
              self.emit('notice', err);
            }
            cb(err);
          } else {
            cb();
          }
        }
      }
    );
  } else {
    cb();
  }
};

module.exports = SCServer;
<|endoftext|>


<commit_before>/* @flow */

export default class EnhancedError extends Error {
	code: string;
	data: ?any;

	constructor(message: string, code: string, data?: any) {
		super(message);

		this.name = this.constructor.name;
		this.message = message;
		this.code = code;
		this.data = data;

		if (typeof Error.captureStackTrace === 'function') {
			Error.captureStackTrace(this, this.constructor.name);
		} else {
			this.stack = (new Error(message)).stack;
		}
	}

	toJSON(): Object {
		const details = {};

		// $FlowIssue #1323
		Object.getOwnPropertyNames(this).forEach(key => (details[key] = this[key]));

		return details;
	}
}
<commit_msg>Adding error levels for the EnhancedError<commit_after>/* @flow */

export default class EnhancedError extends Error {
	code: string;
	level: ?number;
	data: ?any;

	constructor(message: string, code: string, level?: number = 0, data?: any) {
		super(message);

		this.name = this.constructor.name;
		this.message = message;
		this.code = code;
		this.level = level;
		this.data = data;

		if (typeof Error.captureStackTrace === 'function') {
			Error.captureStackTrace(this, this.constructor.name);
		} else {
			this.stack = (new Error(message)).stack;
		}
	}

	toJSON(): Object {
		const details = {};

		// $FlowIssue #1323
		Object.getOwnPropertyNames(this).forEach(key => (details[key] = this[key]));

		return details;
	}
}
<|endoftext|>


<commit_before>function loadTableBuilder(pageData, onSave, table) {

  var tableExists = table;
  var pageUrl = pageData.uri;
  var html = templates.tableBuilder(table);
  var previewTable;

  $('body').append(html);

  if (table) {
    renderTable(table.uri);
  }

  $('#upload-table-form').submit(function (event) {
    var formData = new FormData($(this)[0]);
    previewTable = buildJsonObjectFromForm(previewTable);
    var path = previewTable.uri;
    var xlsPath = path + ".xls";
    var htmlPath = path + ".html";

    // send xls file to zebedee
    $.ajax({
      url: "/zebedee/content/" + Florence.collection.id + "?uri=" + xlsPath,
      type: 'POST',
      data: formData,
      async: false,
      cache: false,
      contentType: false,
      processData: false,
      success: function () {
        createTableHtml();
      }
    });

    function createTableHtml() {
      $.ajax({
        url: "/zebedee/table/" + Florence.collection.id + "?uri=" + xlsPath,
        type: 'POST',
        success: function (html) {
          saveTableJson();
          saveTableHtml(html);
        }
      });
    }

    function saveTableHtml(data) {
      $.ajax({
        url: "/zebedee/content/" + Florence.collection.id + "?uri=" + htmlPath,
        type: 'POST',
        data: data,
        processData: false,
        success: function () {
          renderTable(path);
        }
      });
    }

    return false;
  });

  function renderTable(path) {
    var iframeMarkup = '';
    console.log(iframeMarkup);
    $('#preview-table').html(iframeMarkup);
    var iframe = $('#preview-frame');
    iframe.load(function(){
      var contents = iframe.contents();
      iframe.height(contents.find('html').height());
      iframe.css("opacity", "1");
      contents.find('body').css("background", "transparent");
    });

  }

  $('.btn-table-builder-cancel').on('click', function () {

    $('.table-builder').stop().fadeOut(200).remove();

    // delete the preview table
    if (previewTable) {
      previewTable.files.push({"type": "json", "filename": previewTable.filename + '.json'});
      $(previewTable.files).each(function (index, file) {
        var fileToDelete = pageUrl + '/' + file.filename;
        deleteContent(Florence.collection.id, fileToDelete,
          onSuccess = function () {
            console.log("deleted table file: " + fileToDelete);
          });
      });
    }
  });

  function saveTableJson() {

    previewTable = buildJsonObjectFromForm(previewTable);
    var tableJson = previewTable.uri + ".json";

    $.ajax({
      url: "/zebedee/content/" + Florence.collection.id + "?uri=" + tableJson,
      type: 'POST',
      data: JSON.stringify(previewTable),
      processData: false,
      contentType: 'application/json',
      success: function () {
      }
    });
  }

  function addTableToPageJson(table) {
    if (!pageData.tables) {
      pageData.tables = []
    } else {

      var existingTable = _.find(pageData.tables, function (existingTable) {
        return existingTable.filename === table.filename;
      });

      if (existingTable) {
        existingTable.title = table.title;
        return;
      }
    }

    pageData.tables.push({title: table.title, filename: table.filename, uri: table.uri});
  }

  $('.btn-table-builder-create').on('click', function () {

    // if uploaded = true rename the preview table

    saveTableJson(); // save the latest json

    // if a table exists, rename the preview to its name
    if (tableExists) {
      previewTable.files.push({"type": "json", "filename": previewTable.filename + '.json'});
      $(previewTable.files).each(function (index, file) {

        var fromFile = pageUrl + '/' + file.filename;
        var toFile = pageUrl + '/' + file.filename.replace(previewTable.filename, table.filename);
        console.log("moving... table file: " + fromFile + " to: " + toFile);
        moveContent(Florence.collection.id, fromFile, toFile,
          onSuccess = function () {
            console.log("Moved table file: " + fromFile + " to: " + toFile);
          });
      });
    } else { // just keep the preview files
      table = previewTable;
      addTableToPageJson(table);
    }

    if (onSave) {
      onSave(table.filename, '<ons-table path="' + table.uri + '" />');
    }
    $('.table-builder').stop().fadeOut(200).remove();

  });

  function buildJsonObjectFromForm(table) {
    if (!table) {
      table = {};
    }

    table.type = 'table';
    table.title = $('#table-title').val();
    table.filename = table.filename ? table.filename : StringUtils.randomId();

    table.uri = pageUrl + "/" + table.filename;

    if (table.title === '') {
      table.title = '[Title]';
    }

    table.files = [];
    table.files.push({type: 'download-xls', filename: table.filename + '.xls'});
    table.files.push({type: 'html', filename: table.filename + '.html'});

    return table;
  }
}

<commit_msg>Styling of table builder<commit_after>function loadTableBuilder(pageData, onSave, table) {

  var tableExists = table;
  var pageUrl = pageData.uri;
  var html = templates.tableBuilder(table);
  var previewTable;

  $('body').append(html);

  if (table) {
    renderTable(table.uri);
  }

  $('#upload-table-form').submit(function (event) {
    var formData = new FormData($(this)[0]);
    previewTable = buildJsonObjectFromForm(previewTable);
    var path = previewTable.uri;
    var xlsPath = path + ".xls";
    var htmlPath = path + ".html";

    // send xls file to zebedee
    $.ajax({
      url: "/zebedee/content/" + Florence.collection.id + "?uri=" + xlsPath,
      type: 'POST',
      data: formData,
      async: false,
      cache: false,
      contentType: false,
      processData: false,
      success: function () {
        createTableHtml();
      }
    });

    function createTableHtml() {
      $.ajax({
        url: "/zebedee/table/" + Florence.collection.id + "?uri=" + xlsPath,
        type: 'POST',
        success: function (html) {
          saveTableJson();
          saveTableHtml(html);
        }
      });
    }

    function saveTableHtml(data) {
      $.ajax({
        url: "/zebedee/content/" + Florence.collection.id + "?uri=" + htmlPath,
        type: 'POST',
        data: data,
        processData: false,
        success: function () {
          renderTable(path);
        }
      });
    }

    return false;
  });

  function renderTable(path) {
    var iframeMarkup = '';
    console.log(iframeMarkup);
    $('#preview-table').html(iframeMarkup);
    var iframe = $('#preview-frame');
    iframe.load(function(){
      var contents = iframe.contents();
      contents.find('body').css("background", "transparent");
      contents.find('body').css("width", "480px");
      iframe.height(contents.find('html').height());
      iframe.css("opacity", "1");
    });

  }

  $('.btn-table-builder-cancel').on('click', function () {

    $('.table-builder').stop().fadeOut(200).remove();

    // delete the preview table
    if (previewTable) {
      previewTable.files.push({"type": "json", "filename": previewTable.filename + '.json'});
      $(previewTable.files).each(function (index, file) {
        var fileToDelete = pageUrl + '/' + file.filename;
        deleteContent(Florence.collection.id, fileToDelete,
          onSuccess = function () {
            console.log("deleted table file: " + fileToDelete);
          });
      });
    }
  });

  function saveTableJson() {

    previewTable = buildJsonObjectFromForm(previewTable);
    var tableJson = previewTable.uri + ".json";

    $.ajax({
      url: "/zebedee/content/" + Florence.collection.id + "?uri=" + tableJson,
      type: 'POST',
      data: JSON.stringify(previewTable),
      processData: false,
      contentType: 'application/json',
      success: function () {
      }
    });
  }

  function addTableToPageJson(table) {
    if (!pageData.tables) {
      pageData.tables = []
    } else {

      var existingTable = _.find(pageData.tables, function (existingTable) {
        return existingTable.filename === table.filename;
      });

      if (existingTable) {
        existingTable.title = table.title;
        return;
      }
    }

    pageData.tables.push({title: table.title, filename: table.filename, uri: table.uri});
  }

  $('.btn-table-builder-create').on('click', function () {

    // if uploaded = true rename the preview table

    saveTableJson(); // save the latest json

    // if a table exists, rename the preview to its name
    if (tableExists) {
      previewTable.files.push({"type": "json", "filename": previewTable.filename + '.json'});
      $(previewTable.files).each(function (index, file) {

        var fromFile = pageUrl + '/' + file.filename;
        var toFile = pageUrl + '/' + file.filename.replace(previewTable.filename, table.filename);
        console.log("moving... table file: " + fromFile + " to: " + toFile);
        moveContent(Florence.collection.id, fromFile, toFile,
          onSuccess = function () {
            console.log("Moved table file: " + fromFile + " to: " + toFile);
          });
      });
    } else { // just keep the preview files
      table = previewTable;
      addTableToPageJson(table);
    }

    if (onSave) {
      onSave(table.filename, '<ons-table path="' + table.uri + '" />');
    }
    $('.table-builder').stop().fadeOut(200).remove();

  });

  function buildJsonObjectFromForm(table) {
    if (!table) {
      table = {};
    }

    table.type = 'table';
    table.title = $('#table-title').val();
    table.filename = table.filename ? table.filename : StringUtils.randomId();

    table.uri = pageUrl + "/" + table.filename;

    if (table.title === '') {
      table.title = '[Title]';
    }

    table.files = [];
    table.files.push({type: 'download-xls', filename: table.filename + '.xls'});
    table.files.push({type: 'html', filename: table.filename + '.html'});

    return table;
  }
}

<|endoftext|>


<commit_before>/* global THREE CustomEvent */
import debounce from 'lodash.debounce';
import TransformControls from '../vendor/threejs/TransformControls.js';
import EditorControls from '../vendor/threejs/EditorControls.js';
import {getNumber} from '../utils';
var Events = require('../Events');

const gaTrackTransformEntity = debounce(transformMode => {
  ga('send', 'event', 'Viewport', 'transformEntity', transformMode);
}, 3000);

const gaTrackChangeEditorCamera = debounce(() => {
  ga('send', 'event', 'Viewport', 'changeEditorCamera');
}, 3000);

function Viewport (inspector) {
  var container = {
    dom: inspector.container
  };

  var prevActivedCameraEl = inspector.currentCameraEl;
  inspector.sceneEl.addEventListener('camera-set-active', event => {
    if (inspector.opened) {
      // If we're in edit mode, just save the current active camera for later and activate again the editorCamera
      if (event.detail.cameraEl !== inspector.inspectorCameraEl) {
        prevActivedCameraEl = event.detail.cameraEl;
      }
      inspector.inspectorCameraEl.setAttribute('camera', 'active', 'true');
    }
  });

  // helpers
  var sceneHelpers = inspector.sceneHelpers;
  var objects = [];

  var grid = new THREE.GridHelper(30, 1);
  sceneHelpers.add(grid);

  var camera = inspector.inspectorCameraEl.getObject3D('camera');

  var selectionBox = new THREE.BoxHelper();
  selectionBox.material.depthTest = false;
  selectionBox.material.transparent = true;
  selectionBox.material.color.set(0x1faaf2);
  selectionBox.visible = false;
  sceneHelpers.add(selectionBox);

  var objectPositionOnDown = null;
  var objectRotationOnDown = null;
  var objectScaleOnDown = null;

  /**
   * Update the helpers of the object and it childrens
   * @param  {object3D} object Object to update
   */
  function updateHelpers (object) {
    if (inspector.helpers[object.id] !== undefined) {
      for (var objectId in inspector.helpers[object.id]) {
        inspector.helpers[object.id][objectId].update();
      }
    }
  }

  const transformControls = new THREE.TransformControls(camera, inspector.container);
  transformControls.addEventListener('change', () => {
    const object = transformControls.object;
    if (object === undefined) { return; }

    selectionBox.update(object);

    updateHelpers(object);

    const transformMode = transformControls.getMode();
    switch (transformMode) {
      case 'translate':
        object.el.setAttribute('position', {
          x: getNumber(object.position.x),
          y: getNumber(object.position.y),
          z: getNumber(object.position.z)
        });
        break;
      case 'rotate':
        object.el.setAttribute('rotation', {
          x: THREE.Math.radToDeg(getNumber(object.rotation.x)),
          y: THREE.Math.radToDeg(getNumber(object.rotation.y)),
          z: THREE.Math.radToDeg(getNumber(object.rotation.z))
        });
        break;
      case 'scale':
        object.el.setAttribute('scale', {
          x: getNumber(object.scale.x),
          y: getNumber(object.scale.y),
          z: getNumber(object.scale.z)
        });
        break;
    }
    Events.emit('refreshsidebarobject3d', object);
    gaTrackTransformEntity(transformMode);
  });

  transformControls.addEventListener('mouseDown', () => {
    var object = transformControls.object;

    objectPositionOnDown = object.position.clone();
    objectRotationOnDown = object.rotation.clone();
    objectScaleOnDown = object.scale.clone();

    controls.enabled = false;
  });

  transformControls.addEventListener('mouseUp', () => {
    var object = transformControls.object;
    if (object !== null) {
      switch (transformControls.getMode()) {
        case 'translate':

          if (!objectPositionOnDown.equals(object.position)) {
            // @todo
          }
          break;

        case 'rotate':
          if (!objectRotationOnDown.equals(object.rotation)) {
            // @todo
          }
          break;

        case 'scale':
          if (!objectScaleOnDown.equals(object.scale)) {
            // @todo
          }
          break;
      }
    }
    controls.enabled = true;
  });

  sceneHelpers.add(transformControls);
/*
  signals.objectSelected.add(function (object) {
    selectionBox.visible = false;
    if (!inspector.selected) {
      // if (!inspector.selected || inspector.selected.el.helper) {
      return;
    }

    if (object !== null) {
      if (object.geometry !== undefined &&
        object instanceof THREE.Sprite === false) {
        selectionBox.update(object);
        selectionBox.visible = true;
      }

      transformControls.attach(object);
    }
  });
*/

  Events.on('objectchanged', () => {
    if (inspector.selectedEntity.object3DMap['mesh']) {
      selectionBox.update(inspector.selected);
    }
  });

  // object picking
  var raycaster = new THREE.Raycaster();
  var mouse = new THREE.Vector2();

  // events
  function getIntersects (point, objects) {
    mouse.set((point.x * 2) - 1, -(point.y * 2) + 1);
    raycaster.setFromCamera(mouse, camera);
    return raycaster.intersectObjects(objects);
  }

  var onDownPosition = new THREE.Vector2();
  var onUpPosition = new THREE.Vector2();
  var onDoubleClickPosition = new THREE.Vector2();

  function getMousePosition (dom, x, y) {
    var rect = dom.getBoundingClientRect();
    return [ (x - rect.left) / rect.width, (y - rect.top) / rect.height ];
  }

  function handleClick () {
    if (onDownPosition.distanceTo(onUpPosition) === 0) {
      var intersects = getIntersects(onUpPosition, objects);
      if (intersects.length > 0) {
        var object = intersects[ 0 ].object;
        if (object.userData.object !== undefined) {
          // helper
          inspector.selectEntity(object.userData.object.el);
        } else {
          inspector.selectEntity(object.el);
        }
      } else {
        inspector.selectEntity(null);
      }
    }
  }

  function onMouseDown (event) {
    if (event instanceof CustomEvent) {
      return;
    }

    event.preventDefault();

    var array = getMousePosition(inspector.container, event.clientX, event.clientY);
    onDownPosition.fromArray(array);

    document.addEventListener('mouseup', onMouseUp, false);
  }

  function onMouseUp (event) {
    if (event instanceof CustomEvent) {
      return;
    }

    var array = getMousePosition(inspector.container, event.clientX, event.clientY);
    onUpPosition.fromArray(array);
    handleClick();

    document.removeEventListener('mouseup', onMouseUp, false);
  }

  function onTouchStart (event) {
    var touch = event.changedTouches[ 0 ];
    var array = getMousePosition(inspector.container, touch.clientX, touch.clientY);
    onDownPosition.fromArray(array);

    document.addEventListener('touchend', onTouchEnd, false);
  }

  function onTouchEnd (event) {
    var touch = event.changedTouches[ 0 ];
    var array = getMousePosition(inspector.container, touch.clientX, touch.clientY);
    onUpPosition.fromArray(array);
    handleClick();
    document.removeEventListener('touchend', onTouchEnd, false);
  }

  function onDoubleClick (event) {
    var array = getMousePosition(inspector.container, event.clientX, event.clientY);
    onDoubleClickPosition.fromArray(array);

    var intersects = getIntersects(onDoubleClickPosition, objects);

    if (intersects.length > 0) {
      var intersect = intersects[ 0 ];
      Events.emit('objectfocused', intersect.object);
    }
  }

  // controls need to be added *after* main logic,
  // otherwise controls.enabled doesn't work.
  var controls = new THREE.EditorControls(camera, inspector.container);

  function disableControls () {
    inspector.container.removeEventListener('mousedown', onMouseDown);
    inspector.container.removeEventListener('touchstart', onTouchStart);
    inspector.container.removeEventListener('dblclick', onDoubleClick);
    controls.enabled = false;
  }

  function enableControls () {
    inspector.container.addEventListener('mousedown', onMouseDown, false);
    inspector.container.addEventListener('touchstart', onTouchStart, false);
    inspector.container.addEventListener('dblclick', onDoubleClick, false);
    controls.enabled = true;
  }
  enableControls();

  controls.addEventListener('change', () => {
    transformControls.update();
    gaTrackChangeEditorCamera();
    // inspector.signals.cameraChanged.dispatch(camera);
  });

  Events.on('inspectorcleared', () => {
    controls.center.set(0, 0, 0);
  });

  Events.on('transformmodechanged', mode => {
    transformControls.setMode(mode);
  });

  Events.on('snapchanged', dist => {
    transformControls.setTranslationSnap(dist);
  });

  Events.on('spacechanged', space => {
    transformControls.setSpace(space);
  });

  Events.on('objectselected', object => {
    selectionBox.visible = false;
    transformControls.detach();
    if (object && object.el) {
      if (object.el.getObject3D('mesh')) {
        selectionBox.update(object);
        selectionBox.visible = true;
      }

      transformControls.attach(object);
    }
  });

  Events.on('objectfocused', object => {
    controls.focus(object);
    ga('send', 'event', 'Viewport', 'selectEntity');
  });

  Events.on('geometrychanged', object => {
    if (object !== null) {
      selectionBox.update(object);
    }
  });

  Events.on('objectadded', object => {
    object.traverse(child => {
      if (objects.indexOf(child) === -1) {
        objects.push(child);
      }
    });
  });

  Events.on('objectchanged', object => {
    if (inspector.selected === object) {
      // Hack because object3D always has geometry :(
      if (object.geometry && object.geometry.vertices && object.geometry.vertices.length > 0) {
        selectionBox.update(object);
      }
      // transformControls.update();
    }

    transformControls.update();
    if (object instanceof THREE.PerspectiveCamera) {
      object.updateProjectionMatrix();
    }

    updateHelpers(object);
  });

  Events.on('selectedentitycomponentchanged', () => {
    Events.emit('objectchanged', inspector.selectedEntity.object3D);
  });

  Events.on('objectremoved', object => {
    object.traverse(child => {
      objects.splice(objects.indexOf(child), 1);
    });
  });
  Events.on('helperadded', helper => {
    objects.push(helper.getObjectByName('picker'));
    updateHelpers(helper.fromObject.parent);
  });
  Events.on('helperremoved', object => {
    objects.splice(objects.indexOf(object.getObjectByName('picker')), 1);
  });
  Events.on('windowresize', () => {
    camera.aspect = container.dom.offsetWidth / container.dom.offsetHeight;
    camera.updateProjectionMatrix();
    // renderer.setSize(container.dom.offsetWidth, container.dom.offsetHeight);
  });
  Events.on('gridvisibilitychanged', showGrid => {
    grid.visible = showGrid;
  });
  Events.on('togglegrid', () => {
    grid.visible = !grid.visible;
  });

  Events.on('inspectormodechanged', active => {
    if (active) {
      enableControls();
      inspector.inspectorCameraEl.setAttribute('camera', 'active', 'true');
      Array.prototype.slice.call(document.querySelectorAll('.a-enter-vr,.rs-base')).forEach(element => {
        element.style.display = 'none';
      });
    } else {
      disableControls();
      prevActivedCameraEl.setAttribute('camera', 'active', 'true');
      Array.prototype.slice.call(document.querySelectorAll('.a-enter-vr,.rs-base')).forEach(element => {
        element.style.display = 'block';
      });
    }
    ga('send', 'event', 'Viewport', 'toggleEditor', active);
  });
}

module.exports = Viewport;
<commit_msg>Fix gridHelper size<commit_after>/* global THREE CustomEvent */
import debounce from 'lodash.debounce';
import TransformControls from '../vendor/threejs/TransformControls.js';
import EditorControls from '../vendor/threejs/EditorControls.js';
import {getNumber} from '../utils';
var Events = require('../Events');

const gaTrackTransformEntity = debounce(transformMode => {
  ga('send', 'event', 'Viewport', 'transformEntity', transformMode);
}, 3000);

const gaTrackChangeEditorCamera = debounce(() => {
  ga('send', 'event', 'Viewport', 'changeEditorCamera');
}, 3000);

function Viewport (inspector) {
  var container = {
    dom: inspector.container
  };

  var prevActivedCameraEl = inspector.currentCameraEl;
  inspector.sceneEl.addEventListener('camera-set-active', event => {
    if (inspector.opened) {
      // If we're in edit mode, just save the current active camera for later and activate again the editorCamera
      if (event.detail.cameraEl !== inspector.inspectorCameraEl) {
        prevActivedCameraEl = event.detail.cameraEl;
      }
      inspector.inspectorCameraEl.setAttribute('camera', 'active', 'true');
    }
  });

  // helpers
  var sceneHelpers = inspector.sceneHelpers;
  var objects = [];

  var grid = new THREE.GridHelper(30, 60, 0xbbbbbb, 0x888888);

  sceneHelpers.add(grid);

  var camera = inspector.inspectorCameraEl.getObject3D('camera');

  var selectionBox = new THREE.BoxHelper();
  selectionBox.material.depthTest = false;
  selectionBox.material.transparent = true;
  selectionBox.material.color.set(0x1faaf2);
  selectionBox.visible = false;
  sceneHelpers.add(selectionBox);

  var objectPositionOnDown = null;
  var objectRotationOnDown = null;
  var objectScaleOnDown = null;

  /**
   * Update the helpers of the object and it childrens
   * @param  {object3D} object Object to update
   */
  function updateHelpers (object) {
    if (inspector.helpers[object.id] !== undefined) {
      for (var objectId in inspector.helpers[object.id]) {
        inspector.helpers[object.id][objectId].update();
      }
    }
  }

  const transformControls = new THREE.TransformControls(camera, inspector.container);
  transformControls.addEventListener('change', () => {
    const object = transformControls.object;
    if (object === undefined) { return; }

    selectionBox.update(object);

    updateHelpers(object);

    const transformMode = transformControls.getMode();
    switch (transformMode) {
      case 'translate':
        object.el.setAttribute('position', {
          x: getNumber(object.position.x),
          y: getNumber(object.position.y),
          z: getNumber(object.position.z)
        });
        break;
      case 'rotate':
        object.el.setAttribute('rotation', {
          x: THREE.Math.radToDeg(getNumber(object.rotation.x)),
          y: THREE.Math.radToDeg(getNumber(object.rotation.y)),
          z: THREE.Math.radToDeg(getNumber(object.rotation.z))
        });
        break;
      case 'scale':
        object.el.setAttribute('scale', {
          x: getNumber(object.scale.x),
          y: getNumber(object.scale.y),
          z: getNumber(object.scale.z)
        });
        break;
    }
    Events.emit('refreshsidebarobject3d', object);
    gaTrackTransformEntity(transformMode);
  });

  transformControls.addEventListener('mouseDown', () => {
    var object = transformControls.object;

    objectPositionOnDown = object.position.clone();
    objectRotationOnDown = object.rotation.clone();
    objectScaleOnDown = object.scale.clone();

    controls.enabled = false;
  });

  transformControls.addEventListener('mouseUp', () => {
    var object = transformControls.object;
    if (object !== null) {
      switch (transformControls.getMode()) {
        case 'translate':

          if (!objectPositionOnDown.equals(object.position)) {
            // @todo
          }
          break;

        case 'rotate':
          if (!objectRotationOnDown.equals(object.rotation)) {
            // @todo
          }
          break;

        case 'scale':
          if (!objectScaleOnDown.equals(object.scale)) {
            // @todo
          }
          break;
      }
    }
    controls.enabled = true;
  });

  sceneHelpers.add(transformControls);
/*
  signals.objectSelected.add(function (object) {
    selectionBox.visible = false;
    if (!inspector.selected) {
      // if (!inspector.selected || inspector.selected.el.helper) {
      return;
    }

    if (object !== null) {
      if (object.geometry !== undefined &&
        object instanceof THREE.Sprite === false) {
        selectionBox.update(object);
        selectionBox.visible = true;
      }

      transformControls.attach(object);
    }
  });
*/

  Events.on('objectchanged', () => {
    if (inspector.selectedEntity.object3DMap['mesh']) {
      selectionBox.update(inspector.selected);
    }
  });

  // object picking
  var raycaster = new THREE.Raycaster();
  var mouse = new THREE.Vector2();

  // events
  function getIntersects (point, objects) {
    mouse.set((point.x * 2) - 1, -(point.y * 2) + 1);
    raycaster.setFromCamera(mouse, camera);
    return raycaster.intersectObjects(objects);
  }

  var onDownPosition = new THREE.Vector2();
  var onUpPosition = new THREE.Vector2();
  var onDoubleClickPosition = new THREE.Vector2();

  function getMousePosition (dom, x, y) {
    var rect = dom.getBoundingClientRect();
    return [ (x - rect.left) / rect.width, (y - rect.top) / rect.height ];
  }

  function handleClick () {
    if (onDownPosition.distanceTo(onUpPosition) === 0) {
      var intersects = getIntersects(onUpPosition, objects);
      if (intersects.length > 0) {
        var object = intersects[ 0 ].object;
        if (object.userData.object !== undefined) {
          // helper
          inspector.selectEntity(object.userData.object.el);
        } else {
          inspector.selectEntity(object.el);
        }
      } else {
        inspector.selectEntity(null);
      }
    }
  }

  function onMouseDown (event) {
    if (event instanceof CustomEvent) {
      return;
    }

    event.preventDefault();

    var array = getMousePosition(inspector.container, event.clientX, event.clientY);
    onDownPosition.fromArray(array);

    document.addEventListener('mouseup', onMouseUp, false);
  }

  function onMouseUp (event) {
    if (event instanceof CustomEvent) {
      return;
    }

    var array = getMousePosition(inspector.container, event.clientX, event.clientY);
    onUpPosition.fromArray(array);
    handleClick();

    document.removeEventListener('mouseup', onMouseUp, false);
  }

  function onTouchStart (event) {
    var touch = event.changedTouches[ 0 ];
    var array = getMousePosition(inspector.container, touch.clientX, touch.clientY);
    onDownPosition.fromArray(array);

    document.addEventListener('touchend', onTouchEnd, false);
  }

  function onTouchEnd (event) {
    var touch = event.changedTouches[ 0 ];
    var array = getMousePosition(inspector.container, touch.clientX, touch.clientY);
    onUpPosition.fromArray(array);
    handleClick();
    document.removeEventListener('touchend', onTouchEnd, false);
  }

  function onDoubleClick (event) {
    var array = getMousePosition(inspector.container, event.clientX, event.clientY);
    onDoubleClickPosition.fromArray(array);

    var intersects = getIntersects(onDoubleClickPosition, objects);

    if (intersects.length > 0) {
      var intersect = intersects[ 0 ];
      Events.emit('objectfocused', intersect.object);
    }
  }

  // controls need to be added *after* main logic,
  // otherwise controls.enabled doesn't work.
  var controls = new THREE.EditorControls(camera, inspector.container);

  function disableControls () {
    inspector.container.removeEventListener('mousedown', onMouseDown);
    inspector.container.removeEventListener('touchstart', onTouchStart);
    inspector.container.removeEventListener('dblclick', onDoubleClick);
    controls.enabled = false;
  }

  function enableControls () {
    inspector.container.addEventListener('mousedown', onMouseDown, false);
    inspector.container.addEventListener('touchstart', onTouchStart, false);
    inspector.container.addEventListener('dblclick', onDoubleClick, false);
    controls.enabled = true;
  }
  enableControls();

  controls.addEventListener('change', () => {
    transformControls.update();
    gaTrackChangeEditorCamera();
    // inspector.signals.cameraChanged.dispatch(camera);
  });

  Events.on('inspectorcleared', () => {
    controls.center.set(0, 0, 0);
  });

  Events.on('transformmodechanged', mode => {
    transformControls.setMode(mode);
  });

  Events.on('snapchanged', dist => {
    transformControls.setTranslationSnap(dist);
  });

  Events.on('spacechanged', space => {
    transformControls.setSpace(space);
  });

  Events.on('objectselected', object => {
    selectionBox.visible = false;
    transformControls.detach();
    if (object && object.el) {
      if (object.el.getObject3D('mesh')) {
        selectionBox.update(object);
        selectionBox.visible = true;
      }

      transformControls.attach(object);
    }
  });

  Events.on('objectfocused', object => {
    controls.focus(object);
    ga('send', 'event', 'Viewport', 'selectEntity');
  });

  Events.on('geometrychanged', object => {
    if (object !== null) {
      selectionBox.update(object);
    }
  });

  Events.on('objectadded', object => {
    object.traverse(child => {
      if (objects.indexOf(child) === -1) {
        objects.push(child);
      }
    });
  });

  Events.on('objectchanged', object => {
    if (inspector.selected === object) {
      // Hack because object3D always has geometry :(
      if (object.geometry && object.geometry.vertices && object.geometry.vertices.length > 0) {
        selectionBox.update(object);
      }
      // transformControls.update();
    }

    transformControls.update();
    if (object instanceof THREE.PerspectiveCamera) {
      object.updateProjectionMatrix();
    }

    updateHelpers(object);
  });

  Events.on('selectedentitycomponentchanged', () => {
    Events.emit('objectchanged', inspector.selectedEntity.object3D);
  });

  Events.on('objectremoved', object => {
    object.traverse(child => {
      objects.splice(objects.indexOf(child), 1);
    });
  });
  Events.on('helperadded', helper => {
    objects.push(helper.getObjectByName('picker'));
    updateHelpers(helper.fromObject.parent);
  });
  Events.on('helperremoved', object => {
    objects.splice(objects.indexOf(object.getObjectByName('picker')), 1);
  });
  Events.on('windowresize', () => {
    camera.aspect = container.dom.offsetWidth / container.dom.offsetHeight;
    camera.updateProjectionMatrix();
    // renderer.setSize(container.dom.offsetWidth, container.dom.offsetHeight);
  });
  Events.on('gridvisibilitychanged', showGrid => {
    grid.visible = showGrid;
  });
  Events.on('togglegrid', () => {
    grid.visible = !grid.visible;
  });

  Events.on('inspectormodechanged', active => {
    if (active) {
      enableControls();
      inspector.inspectorCameraEl.setAttribute('camera', 'active', 'true');
      Array.prototype.slice.call(document.querySelectorAll('.a-enter-vr,.rs-base')).forEach(element => {
        element.style.display = 'none';
      });
    } else {
      disableControls();
      prevActivedCameraEl.setAttribute('camera', 'active', 'true');
      Array.prototype.slice.call(document.querySelectorAll('.a-enter-vr,.rs-base')).forEach(element => {
        element.style.display = 'block';
      });
    }
    ga('send', 'event', 'Viewport', 'toggleEditor', active);
  });
}

module.exports = Viewport;
<|endoftext|>


<commit_before><commit_msg>test that `PullRequestCommentsView` does not render comments if collapsed<commit_after><|endoftext|>


<commit_before>import algoliasearch from 'algoliasearch';
import algoliasearchHelper from 'algoliasearch-helper';
import forEach from 'lodash/collection/forEach';
import merge from 'lodash/object/merge';
import union from 'lodash/array/union';
import clone from 'lodash/lang/clone';
import {EventEmitter} from 'events';
import urlSyncWidget from './url-sync.js';
import version from './version.js';
import createHelpers from './createHelpers.js';

function defaultCreateURL() { return '#'; }

/**
 * @function instantsearch
 * @param  {string} options.appId The Algolia application ID
 * @param  {string} options.apiKey The Algolia search-only API key
 * @param  {string} options.indexName The name of the main index
 * @param  {string} [options.numberLocale] The locale used to display numbers. This will be passed
 * to Number.prototype.toLocaleString()
 * @param  {function} [options.searchFunction] A hook that will be called each time a search needs to be done, with the
 * helper as a parameter. It's your responsibility to call helper.search(). This option allows you to avoid doing
 * searches at page load for example.
 * @param  {Object} [options.searchParameters] Additional parameters to pass to
 * the Algolia API.
 * [Full documentation](https://community.algolia.com/algoliasearch-helper-js/docs/SearchParameters.html)
 * @param  {Object|boolean} [options.urlSync] Url synchronization configuration.
 * Setting to `true` will synchronize the needed search parameters with the browser url.
 * @param  {Object} [options.urlSync.mapping] Object used to define replacement query
 * parameter to use in place of another. Keys are current query parameters
 * and value the new value, e.g. `{ q: 'query' }`.
 * @param  {number} [options.urlSync.threshold] Time in ms after which a new
 * state is created in the browser history. The default value is 700.
 * @param  {string[]} [options.urlSync.trackedParameters] Parameters that will
 * be synchronized in the URL. By default, it will track the query, all the
 * refinable attribute (facets and numeric filters), the index and the page.
 * [Full documentation](https://community.algolia.com/algoliasearch-helper-js/docs/SearchParameters.html)
 * @param  {boolean} [options.urlSync.useHash] If set to true, the url will be
 * hash based. Otherwise, it'll use the query parameters using the modern
 * history API.
 * @param  {function} [options.urlSync.getHistoryState] Pass this function to override the
 * default history API state we set to `null`. For example this could be used to force passing
 * {turbolinks: true} to the history API every time we update it.
 * @return {Object} the instantsearch instance
 */
class InstantSearch extends EventEmitter {
  constructor({
    appId = null,
    apiKey = null,
    indexName = null,
    numberLocale,
    searchParameters = {},
    urlSync = null,
    searchFunction
  }) {
    super();
    if (appId === null || apiKey === null || indexName === null) {
      let usage = `
Usage: instantsearch({
  appId: 'my_application_id',
  apiKey: 'my_search_api_key',
  indexName: 'my_index_name'
});`;
      throw new Error(usage);
    }

    let client = algoliasearch(appId, apiKey);
    client.addAlgoliaAgent('instantsearch.js ' + version);

    this.client = client;
    this.helper = null;
    this.indexName = indexName;
    this.searchParameters = {...searchParameters, index: indexName};
    this.widgets = [];
    this.templatesConfig = {
      helpers: createHelpers({numberLocale}),
      compileOptions: {}
    };

    if (searchFunction) {
      this._searchFunction = searchFunction;
    }

    this.urlSync = urlSync === true ? {} : urlSync;
  }

  /**
   * Add a widget
   * @param  {Object} [widget] The widget to add
   * @param  {function} [widget.render] Called after each search response has been received
   * @param  {function} [widget.getConfiguration] Let the widget update the configuration
   * of the search with new parameters
   * @param  {function} [widget.init] Called once before the first search
   * @return {Object} the added widget
   */
  addWidget(widget) {
    // Add the widget to the list of widget
    if (widget.render === undefined && widget.init === undefined) {
      throw new Error('Widget definition missing render or init method');
    }

    this.widgets.push(widget);
  }

  start() {
    if (!this.widgets) throw new Error('No widgets were added to instantsearch.js');

    if (this.urlSync) {
      let syncWidget = urlSyncWidget(this.urlSync);
      this._createURL = syncWidget.createURL.bind(syncWidget);
      this._createAbsoluteURL = relative => this._createURL(relative, {absolute: true});
      this._onHistoryChange = syncWidget.onHistoryChange.bind(syncWidget);
      this.widgets.push(syncWidget);
    } else {
      this._createURL = defaultCreateURL;
      this._createAbsoluteURL = defaultCreateURL;
      this._onHistoryChange = function() {};
    }

    this.searchParameters = this.widgets.reduce(enhanceConfiguration, this.searchParameters);

    let helper = algoliasearchHelper(
      this.client,
      this.searchParameters.index || this.indexName,
      this.searchParameters
    );

    if (this._searchFunction) {
      this._originalHelperSearch = helper.search.bind(helper);
      helper.search = this._wrappedSearch.bind(this);
    }

    this.helper = helper;

    this._init(helper.state, helper);

    helper.on('result', this._render.bind(this, helper));
    helper.search();
  }

  _wrappedSearch() {
    let helper = clone(this.helper);
    helper.search = this._originalHelperSearch;
    this._searchFunction(helper);
    return;
  }

  createURL(params) {
    if (!this._createURL) {
      throw new Error('You need to call start() before calling createURL()');
    }
    return this._createURL(this.helper.state.setQueryParameters(params));
  }

  _render(helper, results, state) {
    forEach(this.widgets, function(widget) {
      if (!widget.render) {
        return;
      }
      widget.render({
        templatesConfig: this.templatesConfig,
        results,
        state,
        helper,
        createURL: this._createAbsoluteURL
      });
    }, this);
    this.emit('render');
  }

  _init(state, helper) {
    const {_onHistoryChange, templatesConfig} = this;
    forEach(this.widgets, function(widget) {
      if (widget.init) {
        widget.init({
          state,
          helper,
          templatesConfig,
          createURL: this._createAbsoluteURL,
          onHistoryChange: _onHistoryChange
        });
      }
    }, this);
  }
}

function enhanceConfiguration(configuration, widgetDefinition) {
  if (!widgetDefinition.getConfiguration) return configuration;

  // Update searchParameters with the configuration from the widgets
  let partialConfiguration = widgetDefinition.getConfiguration(configuration);
  return merge(
    {},
    configuration,
    partialConfiguration,
    (a, b) => {
      if (Array.isArray(a)) {
        return union(a, b);
      }
    }
  );
}

export default InstantSearch;
<commit_msg>docs(urlSync): better thresold documentation<commit_after>import algoliasearch from 'algoliasearch';
import algoliasearchHelper from 'algoliasearch-helper';
import forEach from 'lodash/collection/forEach';
import merge from 'lodash/object/merge';
import union from 'lodash/array/union';
import clone from 'lodash/lang/clone';
import {EventEmitter} from 'events';
import urlSyncWidget from './url-sync.js';
import version from './version.js';
import createHelpers from './createHelpers.js';

function defaultCreateURL() { return '#'; }

/**
 * @function instantsearch
 * @param  {string} options.appId The Algolia application ID
 * @param  {string} options.apiKey The Algolia search-only API key
 * @param  {string} options.indexName The name of the main index
 * @param  {string} [options.numberLocale] The locale used to display numbers. This will be passed
 * to Number.prototype.toLocaleString()
 * @param  {function} [options.searchFunction] A hook that will be called each time a search needs to be done, with the
 * helper as a parameter. It's your responsibility to call helper.search(). This option allows you to avoid doing
 * searches at page load for example.
 * @param  {Object} [options.searchParameters] Additional parameters to pass to
 * the Algolia API.
 * [Full documentation](https://community.algolia.com/algoliasearch-helper-js/docs/SearchParameters.html)
 * @param  {Object|boolean} [options.urlSync] Url synchronization configuration.
 * Setting to `true` will synchronize the needed search parameters with the browser url.
 * @param  {Object} [options.urlSync.mapping] Object used to define replacement query
 * parameter to use in place of another. Keys are current query parameters
 * and value the new value, e.g. `{ q: 'query' }`.
 * @param  {number} [options.urlSync.threshold] Idle time in ms after which a new
 * state is created in the browser history. The default value is 700. The url is always updated at each keystroke
 * but we only create a "previous search state" (activated when click on back button) every 700ms of idle time.
 * @param  {string[]} [options.urlSync.trackedParameters] Parameters that will
 * be synchronized in the URL. By default, it will track the query, all the
 * refinable attribute (facets and numeric filters), the index and the page.
 * [Full documentation](https://community.algolia.com/algoliasearch-helper-js/docs/SearchParameters.html)
 * @param  {boolean} [options.urlSync.useHash] If set to true, the url will be
 * hash based. Otherwise, it'll use the query parameters using the modern
 * history API.
 * @param  {function} [options.urlSync.getHistoryState] Pass this function to override the
 * default history API state we set to `null`. For example this could be used to force passing
 * {turbolinks: true} to the history API every time we update it.
 * @return {Object} the instantsearch instance
 */
class InstantSearch extends EventEmitter {
  constructor({
    appId = null,
    apiKey = null,
    indexName = null,
    numberLocale,
    searchParameters = {},
    urlSync = null,
    searchFunction
  }) {
    super();
    if (appId === null || apiKey === null || indexName === null) {
      let usage = `
Usage: instantsearch({
  appId: 'my_application_id',
  apiKey: 'my_search_api_key',
  indexName: 'my_index_name'
});`;
      throw new Error(usage);
    }

    let client = algoliasearch(appId, apiKey);
    client.addAlgoliaAgent('instantsearch.js ' + version);

    this.client = client;
    this.helper = null;
    this.indexName = indexName;
    this.searchParameters = {...searchParameters, index: indexName};
    this.widgets = [];
    this.templatesConfig = {
      helpers: createHelpers({numberLocale}),
      compileOptions: {}
    };

    if (searchFunction) {
      this._searchFunction = searchFunction;
    }

    this.urlSync = urlSync === true ? {} : urlSync;
  }

  /**
   * Add a widget
   * @param  {Object} [widget] The widget to add
   * @param  {function} [widget.render] Called after each search response has been received
   * @param  {function} [widget.getConfiguration] Let the widget update the configuration
   * of the search with new parameters
   * @param  {function} [widget.init] Called once before the first search
   * @return {Object} the added widget
   */
  addWidget(widget) {
    // Add the widget to the list of widget
    if (widget.render === undefined && widget.init === undefined) {
      throw new Error('Widget definition missing render or init method');
    }

    this.widgets.push(widget);
  }

  start() {
    if (!this.widgets) throw new Error('No widgets were added to instantsearch.js');

    if (this.urlSync) {
      let syncWidget = urlSyncWidget(this.urlSync);
      this._createURL = syncWidget.createURL.bind(syncWidget);
      this._createAbsoluteURL = relative => this._createURL(relative, {absolute: true});
      this._onHistoryChange = syncWidget.onHistoryChange.bind(syncWidget);
      this.widgets.push(syncWidget);
    } else {
      this._createURL = defaultCreateURL;
      this._createAbsoluteURL = defaultCreateURL;
      this._onHistoryChange = function() {};
    }

    this.searchParameters = this.widgets.reduce(enhanceConfiguration, this.searchParameters);

    let helper = algoliasearchHelper(
      this.client,
      this.searchParameters.index || this.indexName,
      this.searchParameters
    );

    if (this._searchFunction) {
      this._originalHelperSearch = helper.search.bind(helper);
      helper.search = this._wrappedSearch.bind(this);
    }

    this.helper = helper;

    this._init(helper.state, helper);

    helper.on('result', this._render.bind(this, helper));
    helper.search();
  }

  _wrappedSearch() {
    let helper = clone(this.helper);
    helper.search = this._originalHelperSearch;
    this._searchFunction(helper);
    return;
  }

  createURL(params) {
    if (!this._createURL) {
      throw new Error('You need to call start() before calling createURL()');
    }
    return this._createURL(this.helper.state.setQueryParameters(params));
  }

  _render(helper, results, state) {
    forEach(this.widgets, function(widget) {
      if (!widget.render) {
        return;
      }
      widget.render({
        templatesConfig: this.templatesConfig,
        results,
        state,
        helper,
        createURL: this._createAbsoluteURL
      });
    }, this);
    this.emit('render');
  }

  _init(state, helper) {
    const {_onHistoryChange, templatesConfig} = this;
    forEach(this.widgets, function(widget) {
      if (widget.init) {
        widget.init({
          state,
          helper,
          templatesConfig,
          createURL: this._createAbsoluteURL,
          onHistoryChange: _onHistoryChange
        });
      }
    }, this);
  }
}

function enhanceConfiguration(configuration, widgetDefinition) {
  if (!widgetDefinition.getConfiguration) return configuration;

  // Update searchParameters with the configuration from the widgets
  let partialConfiguration = widgetDefinition.getConfiguration(configuration);
  return merge(
    {},
    configuration,
    partialConfiguration,
    (a, b) => {
      if (Array.isArray(a)) {
        return union(a, b);
      }
    }
  );
}

export default InstantSearch;
<|endoftext|>


<commit_before>// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
//= require jquery
//= require jquery_ujs
//= require_tree .
<commit_msg>Fix: Dependancy on jquery/jquery ui<commit_after>// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
//= javascript_include_tag 'ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js', :defer => true, :async => true
//= require_tree .
<|endoftext|>


<commit_before><commit_msg>Created util for lokijs<commit_after>var loki = require('lokijs');

var db = new loki('db.json');

module.exports = db;<|endoftext|>


<commit_before>/* eslint new-cap: ["error", { "capIsNewExceptions": ["DataTable"] }]*/
const $ = require('jquery');
const moment = require('moment');

/**
 * Download table in specified format.
 * @param {string} format format of downloaded doc.
 */
function downloadItem({format = 'xlsx'}) {
  const url = `${window.PAGE.urls.export}&dtf=${format}`;
  const anchor = document.createElement('a');
  anchor.style.display = 'none';
  anchor.href = url;
  anchor.click();
}

if (typeof window.PAGE === 'object') {
  $('#projects').DataTable({
    dom: `
    <".row"
      <".col-md-8.buttons"B><".col-md-4"f>>
    rt
    <".row"<".col-md-3"l><".col-md-6"p><".col-md-3"i>>`,
    buttons: [
      {
        extend: 'collection',
        text: `

     ${window.PAGE.i18n.exportBtn}

`,
        buttons: window.buttons.map(button => ({
          text: button.name,
          action() {
            downloadItem({format: button.format});
          }
        }))
      }
    ],
    processing: true,
    serverSide: true,
    ajax: window.PAGE.urls.projects,
    order: [[5, "desc"]],
    columnDefs: [
      {
        targets: [1],
        render: function(data, type, full) {
          return `
${data}
`;
        }
      },
      {
        targets: [4, 5],
        render: function(data) {
          return `

`;
        }
      }
    ],
    createdRow: function(row) {
      $(row).tooltip({selector: '[data-toggle="tooltip"]'});
    }
  });
}
<commit_msg>Fixed tooltip date.<commit_after>/* eslint new-cap: ["error", { "capIsNewExceptions": ["DataTable"] }]*/
const $ = require('jquery');
const moment = require('moment');

/**
 * Download table in specified format.
 * @param {string} format format of downloaded doc.
 */
function downloadItem({format = 'xlsx'}) {
  const url = `${window.PAGE.urls.export}&dtf=${format}`;
  const anchor = document.createElement('a');
  anchor.style.display = 'none';
  anchor.href = url;
  anchor.click();
}

if (typeof window.PAGE === 'object') {
  $('#projects').DataTable({
    dom: `
    <".row"
      <".col-md-8.buttons"B><".col-md-4"f>>
    rt
    <".row"<".col-md-3"l><".col-md-6"p><".col-md-3"i>>`,
    buttons: [
      {
        extend: 'collection',
        text: `

     ${window.PAGE.i18n.exportBtn}

`,
        buttons: window.buttons.map(button => ({
          text: button.name,
          action() {
            downloadItem({format: button.format});
          }
        }))
      }
    ],
    processing: true,
    serverSide: true,
    ajax: window.PAGE.urls.projects,
    order: [[5, "desc"]],
    columnDefs: [
      {
        targets: [1],
        render: function(data, type, full) {
          return `
${data}
`;
        }
      },
      {
        targets: [4, 5],
        render: function(data) {
          const date = moment(data);
          return `

`;
        }
      }
    ],
    createdRow: function(row) {
      $(row).tooltip({selector: '[data-toggle="tooltip"]'});
    }
  });
}
<|endoftext|>


<commit_before>/*!
 * TemplateLayout Wef plugin
 * Copyright (c) 2011 Pablo Escalada
 * MIT Licensed
 */

//requires: cssParser
//exports: templateLayout
(function () {
    var templateLayout = {
        name:"templateLayout",
        version:"0.0.1",
        description:"W3C CSS Template Layout Module",
        authors:["Pablo Escalada <uo1398@uniovi.es>"],
        licenses:["MIT"], //TODO: Licenses
        templateLayout:function () {
            document.addEventListener('selectorFound', function (e) {
                // e.target matches the elem from above
                lastEvent = e;
                //console.log(lastEvent.selectorText | lastEvent.property);
            }, false);

            document.addEventListener('propertyFound', function (e) {
                // e.target matches the elem from above
                lastEvent = e;
                //console.log(lastEvent.selectorText | lastEvent.property);
            }, false);
            return templateLayout;
        },
        getLastEvent:function () {
            return lastEvent;
        }
    };
    var lastEvent = 0;

    wef.plugins.register("templateLayout", templateLayout);
})();<commit_msg>Remove selectorFound event listener Rename templateLayout() to init()<commit_after>/*!
 * TemplateLayout Wef plugin
 * Copyright (c) 2011 Pablo Escalada
 * MIT Licensed
 */

//requires: cssParser
//exports: templateLayout
(function () {
    var templateLayout = {
        name:"templateLayout",
        version:"0.0.1",
        description:"W3C CSS Template Layout Module",
        authors:["Pablo Escalada <uo1398@uniovi.es>"],
        licenses:["MIT"], //TODO: Licenses
        init:function () {

            document.addEventListener('propertyFound', function (e) {
                // e.target matches the elem from above
                lastEvent = e;
                //console.log(lastEvent.property);
            }, false);
            return templateLayout;
        },
        getLastEvent:function () {
            return lastEvent;
        }
    };
    var lastEvent = 0;

    wef.plugins.register("templateLayout", templateLayout);
})();<|endoftext|>


<commit_before>( function( mw, $ ) { "use strict";

/* reference  players look like this:
 *
 * <video id="player" width="400" height="300" preload="none"
					 src="http://demo.tremormedia.com/~agrant/html5test.mp4">
		</video>

 */
// Set the Tremor config:
mw.setDefaultConfig({
	// The url for the ad Manager
	// for debugging we use the following AdManager url: 'http://localhost/html5.kaltura/mwEmbed/modules/Tremor/AdManager.js'
	'Tremor.acudeoUrl': 'http://objects.tremormedia.com/embed/sjs/acudeo.html5.js'
});

mw.Tremor = function( embedPlayer, callback ){
	return this.init( embedPlayer, callback );
};

mw.Tremor.prototype = {
	bindPostfix: '.tremor',

	// The current ad slot type:
	currentAdSlotType: null,

	init: function(  embedPlayer, callback){
		var _this = this;
		// Inherit BaseAdPlugin
		mw.inherit( this, new mw.BaseAdPlugin( embedPlayer, callback ) );

		// unbind any existing bindings:
		_this.embedPlayer.unbindHelper( _this.bindPostfix );
		// Load the Tremor ad manager then setup the ads
		if( !window['ACUDEO'] ){
			kWidget.appendScriptUrl( _this.getAdManagerUrl(), function(){
				_this.setupAds();
				callback();
			}, document );
		} else{
			_this.setupAds();
			callback();
		}
	},
	getAdManagerUrl: function(){
		return mw.getConfig( 'Tremor.acudeoUrl' );
	},
	setupAds: function(){
		var _this = this;
		var embedPlayer = this.embedPlayer;

		// add a hidden banner:
		if( this.getConfig( 'banner' ) ){
			$('
')
				.attr('id', this.getConfig( 'banner' ) )
				.css({
					'position':'absolute',
					'top': '0px',
					'left': '-4048px',
					'width': '300px',
					'height': '250px'
				})
				.appendTo( 'body' )
		}
		this.setupACUDEO();
		embedPlayer.bindHelper('onpause', function(){
			$( embedPlayer.getPlayerElement() ).removeAttr( "controls" );
		});

		// bind player preSequence to trigger ACUDEO
		embedPlayer.bindHelper( 'AdSupport_preroll' + _this.bindPostfix, function( event, sequenceProxy ){
			// Add Tremor to the sequence proxy:
			sequenceProxy[ _this.getSequenceIndex( 'preroll' ) ] = function( callback ){
				_this.currentAdCallback = callback;
				// no API for what ad we are playing? assume preroll
				_this.currentAdSlotType = 'preroll';
				_this.startAd()
			};
		});
		// bind player postSequence to trigger ACUDEO
		embedPlayer.bindHelper( 'AdSupport_postroll' + _this.bindPostfix, function( event, sequenceProxy ){
			// Add Tremor to the sequence proxy:
			sequenceProxy[ _this.getSequenceIndex( 'postroll' ) ] = function( callback ){
				_this.currentAdCallback = callback;
				// no API for what ad we are playing? assume postroll
				_this.currentAdSlotType = 'postroll';
				_this.startAd()
			};
		})
	},
	setupACUDEO: function(){
		var _this = this,
			embedPlayer = this.embedPlayer;

		// setup ACUDEO options:
		var acudeoOptions = {
			policy: this.getConfig( 'progId' ), //ad policy from acudeo onsole
			banner: this.getConfig( 'banner' ),
			playerAttributes: {
				height: embedPlayer.getHeight(),
				width: embedPlayer.getWidth()
			},
			contentAttributes: {
				duration: embedPlayer.getDuration()
			},
			contentData: {
				id: embedPlayer.evaluate("{mediaProxy.entry.id}"),
				url: embedPlayer.getSrc(), // content source url
				title: embedPlayer.evaluate("{mediaProxy.entry.name}"),
				descriptionUrl: document.URL, // page url?
				description: embedPlayer.evaluate("{mediaProxy.entry.description}")
			}
		}
		mw.log("Tremor: Setup acudeoOptions ", acudeoOptions);

		ACUDEO.init(acudeoOptions, function() {
			mw.log("Tremor: ACUDEO.init executed");
			// initializing using ad options. list ad options. post-init methods you want to run. list calls like content, contentdata
		});
		/**
		 * Policy and video content are loaded
		 */
		ACUDEO.addEventListener("AdStarted", function( info ) {
			var vid = embedPlayer.getPlayerElement();
			ACUDEO.mode("ad");
			mw.log("Tremor: ACUDEO.mode(\"ad\") - Acudeo Player in Ad mode.");
			mw.log("Tremor: AdStarted events recieved");
			/* listener for ad - after ad started, listen for these events... */
			var origSrc = vid.getAttribute("src");
			/*vid.load();
			vid.src =  info.video.url;
			vid.play();
			setTimeout(function(){
				vid.play();
			},1000)*/
			var source = new mw.MediaSource( $('<source>').attr('src', info.video.url )[0] );
			embedPlayer.playerSwitchSource( source , function(){
				mw.log(" Tremor: durationchange recieved" );
				ACUDEO.setAttribute( "ad.duration", vid.duration );
				// Tremor switched source
				mw.log( "Tremor: Play ad content:" + info.video.url );
				vid.play();
			}, function(){
				mw.log("Tremor: ended event recieved");
				ACUDEO.mode("content"); //from one common player

				vid.setAttribute("src", origSrc); //set content source
				vid.load(); //load content from source attribute
				vid.play(); //initiate playback

				// issue the stop ad call:
				_this.stopAd();

				mw.log("Tremor: ACUDEO.mode(\"content\") - Acudeo Player in content mode.");
			});
		});
	},
	startAd: function(){
		var _this = this;
		mw.log("Tremor:: startAd: " +  _this.currentAdSlotType );
		// Send ACUDEO startAd event
		ACUDEO.startAd( _this.currentAdSlotType );
		// Started add playback
		_this.embedPlayer.adTimeline.updateUiForAdPlayback( _this.currentAdSlotType );
		// start monitoring the ad:
		_this.monitorAd();
		// show the loading spinner until we start ad playback
		_this.embedPlayer.addPlayerSpinner();
		// listen for playing
		$( _this.getAcudeoVid() ).bind( 'playing' + _this.bindPostfix, function(){
			// hide spinner:
			_this.embedPlayer.hideSpinnerAndPlayBtn();
		});

		var adPlay = false;
		$( _this.getAcudeoVid() ).bind( 'play' + _this.bindPostfix, function(){
			adPlay = true;
			// started ad playback check for banner and send content across the iframe
			if( $('#' + _this.getConfig( 'banner' ) ).html() != '' ){
				parent.document.getElementById( _this.getConfig( 'banner' ) ).innerHTML =
					 $('#' + _this.getConfig( 'banner' ) ).html();
			}
		});
		var timeout = this.getConfig( 'timeout' ) || 10;
		setTimeout(function(){
			if( !adPlay ){
				// ad skip
				_this.stopAd( true );
			}
		}, timeout * 1000 )
	},
	stopAd: function( adSkip ){
		var _this = this;
		mw.log("Tremor:: stopAd, was skiped? " + adSkip );
		// remove true player:
		// stop ad monitoring:
		_this.stopAdMonitor();
		// restore player:
		_this.embedPlayer.adTimeline.restorePlayer( _this.currentAdSlotType, !adSkip );
		// ACUDEO adds controls to player :(
		$( _this.embedPlayer.getPlayerElement() ).removeAttr( 'controls' );
		// Restore the player via adSequence callback:
		if( $.isFunction( _this.currentAdCallback ) ){
			_this.currentAdCallback();
			_this.currentAdCallback = null;
		}
	},
	getAcudeoVid: function(){
		//return $( this.embedPlayer ).parent().find( 'video' ).not( '#' + this.embedPlayer.pid )[0];
		// tremor now works with shared video element:
		return this.embedPlayer.getPlayerElement();
	},
	monitorAd: function(){
		var _this = this;
		var embedPlayer = _this.embedPlayer
		// find the ad ( ACUDEO preAppends )
		var vidACUDEO = _this.getAcudeoVid();
		// Give ACUDEO the video play time:
		ACUDEO.setProgress( vidACUDEO.currentTime );

		// add some bindings to vidACUDEO
		if( vidACUDEO.currentTime && vidACUDEO.duration ){
			embedPlayer.adTimeline.updateSequenceProxy( 'timeRemaining',  vidACUDEO.currentTime -vidACUDEO.duration  );
			embedPlayer.adTimeline.updateSequenceProxy( 'duration', vidACUDEO.duration );

			embedPlayer.triggerHelper( 'AdSupport_AdUpdatePlayhead', vidACUDEO.currentTime );

			// TODO player interface updates should be configurable see Mantis 14076 and 14019
			embedPlayer.controlBuilder.setStatus(
				mw.seconds2npt( vidACUDEO.currentTime ) + '/' + mw.seconds2npt( vidACUDEO.duration )
			);
			// update ad playhead
			_this.embedPlayer.updatePlayHead( vidACUDEO.currentTime / vidACUDEO.duration );
		}
		_this.embedPlayer.sequenceProxy.isInSequence = true;

		// Keep monitoring ad progress at MonitorRate as long as ad is playing:
		if( !this.adMonitorInterval ){
			this.adMonitorInterval = setInterval( function(){
				_this.monitorAd();
			}, mw.getConfig( 'EmbedPlayer.MonitorRate' ) );
		}
	},
	stopAdMonitor: function(){
		window.clearInterval( this.adMonitorInterval );
		this.adMonitorInterval = 0;
	},
	getConfig: function( propId ){
		// return the attribute value
		return this.embedPlayer.getKalturaConfig( 'tremor', propId );
	},
}

} )( window.mw, window.jQuery );<commit_msg>Tremor: remove trailling comma <commit_after>( function( mw, $ ) { "use strict";

/* reference  players look like this:
 *
 * <video id="player" width="400" height="300" preload="none"
					 src="http://demo.tremormedia.com/~agrant/html5test.mp4">
		</video>

 */
// Set the Tremor config:
mw.setDefaultConfig({
	// The url for the ad Manager
	// for debugging we use the following AdManager url: 'http://localhost/html5.kaltura/mwEmbed/modules/Tremor/AdManager.js'
	'Tremor.acudeoUrl': 'http://objects.tremormedia.com/embed/sjs/acudeo.html5.js'
});

mw.Tremor = function( embedPlayer, callback ){
	return this.init( embedPlayer, callback );
};

mw.Tremor.prototype = {
	bindPostfix: '.tremor',

	// The current ad slot type:
	currentAdSlotType: null,

	init: function(  embedPlayer, callback){
		var _this = this;
		// Inherit BaseAdPlugin
		mw.inherit( this, new mw.BaseAdPlugin( embedPlayer, callback ) );

		// unbind any existing bindings:
		_this.embedPlayer.unbindHelper( _this.bindPostfix );
		// Load the Tremor ad manager then setup the ads
		if( !window['ACUDEO'] ){
			kWidget.appendScriptUrl( _this.getAdManagerUrl(), function(){
				_this.setupAds();
				callback();
			}, document );
		} else{
			_this.setupAds();
			callback();
		}
	},
	getAdManagerUrl: function(){
		return mw.getConfig( 'Tremor.acudeoUrl' );
	},
	setupAds: function(){
		var _this = this;
		var embedPlayer = this.embedPlayer;

		// add a hidden banner:
		if( this.getConfig( 'banner' ) ){
			$('
')
				.attr('id', this.getConfig( 'banner' ) )
				.css({
					'position':'absolute',
					'top': '0px',
					'left': '-4048px',
					'width': '300px',
					'height': '250px'
				})
				.appendTo( 'body' )
		}
		this.setupACUDEO();
		embedPlayer.bindHelper('onpause', function(){
			$( embedPlayer.getPlayerElement() ).removeAttr( "controls" );
		});

		// bind player preSequence to trigger ACUDEO
		embedPlayer.bindHelper( 'AdSupport_preroll' + _this.bindPostfix, function( event, sequenceProxy ){
			// Add Tremor to the sequence proxy:
			sequenceProxy[ _this.getSequenceIndex( 'preroll' ) ] = function( callback ){
				_this.currentAdCallback = callback;
				// no API for what ad we are playing? assume preroll
				_this.currentAdSlotType = 'preroll';
				_this.startAd()
			};
		});
		// bind player postSequence to trigger ACUDEO
		embedPlayer.bindHelper( 'AdSupport_postroll' + _this.bindPostfix, function( event, sequenceProxy ){
			// Add Tremor to the sequence proxy:
			sequenceProxy[ _this.getSequenceIndex( 'postroll' ) ] = function( callback ){
				_this.currentAdCallback = callback;
				// no API for what ad we are playing? assume postroll
				_this.currentAdSlotType = 'postroll';
				_this.startAd()
			};
		})
	},
	setupACUDEO: function(){
		var _this = this,
			embedPlayer = this.embedPlayer;

		// setup ACUDEO options:
		var acudeoOptions = {
			policy: this.getConfig( 'progId' ), //ad policy from acudeo onsole
			banner: this.getConfig( 'banner' ),
			playerAttributes: {
				height: embedPlayer.getHeight(),
				width: embedPlayer.getWidth()
			},
			contentAttributes: {
				duration: embedPlayer.getDuration()
			},
			contentData: {
				id: embedPlayer.evaluate("{mediaProxy.entry.id}"),
				url: embedPlayer.getSrc(), // content source url
				title: embedPlayer.evaluate("{mediaProxy.entry.name}"),
				descriptionUrl: document.URL, // page url?
				description: embedPlayer.evaluate("{mediaProxy.entry.description}")
			}
		}
		mw.log("Tremor: Setup acudeoOptions ", acudeoOptions);

		ACUDEO.init(acudeoOptions, function() {
			mw.log("Tremor: ACUDEO.init executed");
			// initializing using ad options. list ad options. post-init methods you want to run. list calls like content, contentdata
		});
		/**
		 * Policy and video content are loaded
		 */
		ACUDEO.addEventListener("AdStarted", function( info ) {
			var vid = embedPlayer.getPlayerElement();
			ACUDEO.mode("ad");
			mw.log("Tremor: ACUDEO.mode(\"ad\") - Acudeo Player in Ad mode.");
			mw.log("Tremor: AdStarted events recieved");
			/* listener for ad - after ad started, listen for these events... */
			var origSrc = vid.getAttribute("src");
			/*vid.load();
			vid.src =  info.video.url;
			vid.play();
			setTimeout(function(){
				vid.play();
			},1000)*/
			var source = new mw.MediaSource( $('<source>').attr('src', info.video.url )[0] );
			embedPlayer.playerSwitchSource( source , function(){
				mw.log(" Tremor: durationchange recieved" );
				ACUDEO.setAttribute( "ad.duration", vid.duration );
				// Tremor switched source
				mw.log( "Tremor: Play ad content:" + info.video.url );
				vid.play();
			}, function(){
				mw.log("Tremor: ended event recieved");
				ACUDEO.mode("content"); //from one common player

				vid.setAttribute("src", origSrc); //set content source
				vid.load(); //load content from source attribute
				vid.play(); //initiate playback

				// issue the stop ad call:
				_this.stopAd();

				mw.log("Tremor: ACUDEO.mode(\"content\") - Acudeo Player in content mode.");
			});
		});
	},
	startAd: function(){
		var _this = this;
		mw.log("Tremor:: startAd: " +  _this.currentAdSlotType );
		// Send ACUDEO startAd event
		ACUDEO.startAd( _this.currentAdSlotType );
		// Started add playback
		_this.embedPlayer.adTimeline.updateUiForAdPlayback( _this.currentAdSlotType );
		// start monitoring the ad:
		_this.monitorAd();
		// show the loading spinner until we start ad playback
		_this.embedPlayer.addPlayerSpinner();
		// listen for playing
		$( _this.getAcudeoVid() ).bind( 'playing' + _this.bindPostfix, function(){
			// hide spinner:
			_this.embedPlayer.hideSpinnerAndPlayBtn();
		});

		var adPlay = false;
		$( _this.getAcudeoVid() ).bind( 'play' + _this.bindPostfix, function(){
			adPlay = true;
			// started ad playback check for banner and send content across the iframe
			if( $('#' + _this.getConfig( 'banner' ) ).html() != '' ){
				parent.document.getElementById( _this.getConfig( 'banner' ) ).innerHTML =
					 $('#' + _this.getConfig( 'banner' ) ).html();
			}
		});
		var timeout = this.getConfig( 'timeout' ) || 10;
		setTimeout(function(){
			if( !adPlay ){
				// ad skip
				_this.stopAd( true );
			}
		}, timeout * 1000 )
	},
	stopAd: function( adSkip ){
		var _this = this;
		mw.log("Tremor:: stopAd, was skiped? " + adSkip );
		// remove true player:
		// stop ad monitoring:
		_this.stopAdMonitor();
		// restore player:
		_this.embedPlayer.adTimeline.restorePlayer( _this.currentAdSlotType, !adSkip );
		// ACUDEO adds controls to player :(
		$( _this.embedPlayer.getPlayerElement() ).removeAttr( 'controls' );
		// Restore the player via adSequence callback:
		if( $.isFunction( _this.currentAdCallback ) ){
			_this.currentAdCallback();
			_this.currentAdCallback = null;
		}
	},
	getAcudeoVid: function(){
		//return $( this.embedPlayer ).parent().find( 'video' ).not( '#' + this.embedPlayer.pid )[0];
		// tremor now works with shared video element:
		return this.embedPlayer.getPlayerElement();
	},
	monitorAd: function(){
		var _this = this;
		var embedPlayer = _this.embedPlayer
		// find the ad ( ACUDEO preAppends )
		var vidACUDEO = _this.getAcudeoVid();
		// Give ACUDEO the video play time:
		ACUDEO.setProgress( vidACUDEO.currentTime );

		// add some bindings to vidACUDEO
		if( vidACUDEO.currentTime && vidACUDEO.duration ){
			embedPlayer.adTimeline.updateSequenceProxy( 'timeRemaining',  vidACUDEO.currentTime -vidACUDEO.duration  );
			embedPlayer.adTimeline.updateSequenceProxy( 'duration', vidACUDEO.duration );

			embedPlayer.triggerHelper( 'AdSupport_AdUpdatePlayhead', vidACUDEO.currentTime );

			// TODO player interface updates should be configurable see Mantis 14076 and 14019
			embedPlayer.controlBuilder.setStatus(
				mw.seconds2npt( vidACUDEO.currentTime ) + '/' + mw.seconds2npt( vidACUDEO.duration )
			);
			// update ad playhead
			_this.embedPlayer.updatePlayHead( vidACUDEO.currentTime / vidACUDEO.duration );
		}
		_this.embedPlayer.sequenceProxy.isInSequence = true;

		// Keep monitoring ad progress at MonitorRate as long as ad is playing:
		if( !this.adMonitorInterval ){
			this.adMonitorInterval = setInterval( function(){
				_this.monitorAd();
			}, mw.getConfig( 'EmbedPlayer.MonitorRate' ) );
		}
	},
	stopAdMonitor: function(){
		window.clearInterval( this.adMonitorInterval );
		this.adMonitorInterval = 0;
	},
	getConfig: function( propId ){
		// return the attribute value
		return this.embedPlayer.getKalturaConfig( 'tremor', propId );
	}
}

} )( window.mw, window.jQuery );<|endoftext|>