<commit_before>'use strict';

const { exec } = require('child_process');
const path = require('path');
const ApiGateway = require('./ApiGateway');
const ApiGatewayWebSocket = require('./ApiGatewayWebSocket');
const debugLog = require('./debugLog');
const functionHelper = require('./functionHelper');
const { createDefaultApiKey, satisfiesVersionRange } = require('./utils');
const { peerDependencies } = require('../package.json');

module.exports = class ServerlessOffline {
  constructor(serverless, options) {
    this.serverless = serverless;
    this.service = serverless.service;
    this.serverlessLog = serverless.cli.log.bind(serverless.cli);
    this.options = options;
    this.exitCode = 0;
    this._experimentalWarningNotified = false;

    this.commands = {
      offline: {
        usage: 'Simulates API Gateway to call your lambda functions offline.',
        lifecycleEvents: ['start'],
        // add start nested options
        commands: {
          start: {
            usage:
              'Simulates API Gateway to call your lambda functions offline using backward compatible initialization.',
            lifecycleEvents: ['init', 'end'],
          },
        },
        options: {
          apiKey: {
            usage:
              'Defines the API key value to be used for endpoints marked as private. Defaults to a random hash.',
          },
          binPath: {
            usage: 'Path to the Serverless binary.',
            shortcut: 'b',
          },
          cacheInvalidationRegex: {
            usage:
              'Provide the plugin with a regexp to use for cache invalidation. Default: node_modules',
          },
          corsAllowHeaders: {
            usage:
              'Used to build the Access-Control-Allow-Headers header for CORS support.',
          },
          corsAllowOrigin: {
            usage:
              'Used to build the Access-Control-Allow-Origin header for CORS support.',
          },
          corsDisallowCredentials: {
            usage:
              'Used to override the Access-Control-Allow-Credentials default (which is true) to false.',
          },
          corsExposedHeaders: {
            usage:
              'USed to build the Access-Control-Exposed-Headers response header for CORS support',
          },
          disableCookieValidation: {
            usage: 'Used to disable cookie-validation on hapi.js-server',
          },
          enforceSecureCookies: {
            usage: 'Enforce secure cookies',
          },
          exec: {
            usage:
              'When provided, a shell script is executed when the server starts up, and the server will shut down after handling this command.',
          },
          hideStackTraces: {
            usage: 'Hide the stack trace on lambda failure. Default: false',
          },
          host: {
            usage: 'The host name to listen on. Default: localhost',
            shortcut: 'o',
          },
          httpsProtocol: {
            usage:
              'To enable HTTPS, specify directory (relative to your cwd, typically your project dir) for both cert.pem and key.pem files.',
            shortcut: 'H',
          },
          location: {
            usage: "The root location of the handlers' files.",
            shortcut: 'l',
          },
          noAuth: {
            usage: 'Turns off all authorizers',
          },
          noEnvironment: {
            usage:
              'Turns off loading of your environment variables from serverless.yml. Allows the usage of tools such as PM2 or docker-compose.',
          },
          port: {
            usage: 'Port to listen on. Default: 3000',
            shortcut: 'P',
          },
          prefix: {
            usage:
              'Adds a prefix to every path, to send your requests to http://localhost:3000/prefix/[your_path] instead.',
            shortcut: 'p',
          },
          preserveTrailingSlash: {
            usage: 'Used to keep trailing slashes on the request path',
          },
          printOutput: {
            usage: 'Outputs your lambda response to the terminal.',
          },
          providedRuntime: {
            usage: 'Sets the provided runtime for lambdas',
          },
          region: {
            usage: 'The region used to populate your templates.',
            shortcut: 'r',
          },
          resourceRoutes: {
            usage:
              'Turns on loading of your HTTP proxy settings from serverless.yml.',
          },
          showDuration: {
            usage: 'Show the execution time duration of the lambda function.',
          },
          skipCacheInvalidation: {
            usage:
              'Tells the plugin to skip require cache invalidation. A script reloading tool like Nodemon might then be needed',
            shortcut: 'c',
          },
          stage: {
            usage: 'The stage used to populate your templates.',
            shortcut: 's',
          },
          useSeparateProcesses: {
            usage: 'Uses separate node processes for handlers',
          },
          websocketPort: {
            usage: 'Websocket port to listen on. Default: 3001',
          },
        },
      },
    };

    this.hooks = {
      'offline:start:init': this.start.bind(this),
      'offline:start': this.startWithExplicitEnd.bind(this),
      'offline:start:end': this.end.bind(this),
    };
  }

  printBlankLine() {
    console.log();
  }

  logPluginIssue() {
    this.serverlessLog(
      'If you think this is an issue with the plugin please submit it, thanks!',
    );
    this.serverlessLog('https://github.com/dherault/serverless-offline/issues');
  }

  // Entry point for the plugin (sls offline) when running 'sls offline start'
  start() {
    this._verifyServerlessVersionCompatibility();

    // Some users would like to know their environment outside of the handler
    process.env.IS_OFFLINE = true;

    return Promise.resolve(this._buildServer())
      .then(() => this.apiGateway._listen())
      .then(() => this.hasWebsocketRoutes && this.apiGatewayWebSocket._listen())
      .then(() =>
        this.options.exec
          ? this._executeShellScript()
          : this._listenForTermination(),
      );
  }

  /**
   * Entry point for the plugin (sls offline) when running 'sls offline'
   * The call to this.end() would terminate the process before 'offline:start:end' could be consumed
   * by downstream plugins. When running sls offline that can be expected, but docs say that
   * 'sls offline start' will provide the init and end hooks for other plugins to consume
   * */
  startWithExplicitEnd() {
    return this.start().then(() => this.end());
  }

  _listenForTermination() {
    // SIGINT will be usually sent when user presses ctrl+c
    const waitForSigInt = new Promise((resolve) => {
      process.on('SIGINT', () => resolve('SIGINT'));
    });

    // SIGTERM is a default termination signal in many cases,
    // for example when "killing" a subprocess spawned in node
    // with child_process methods
    const waitForSigTerm = new Promise((resolve) => {
      process.on('SIGTERM', () => resolve('SIGTERM'));
    });

    return Promise.race([waitForSigInt, waitForSigTerm]).then((command) => {
      this.serverlessLog(`Got ${command} signal. Offline Halting...`);
    });
  }

  _executeShellScript() {
    const command = this.options.exec;
    const options = {
      env: Object.assign(
        { IS_OFFLINE: true },
        this.service.provider.environment,
        this.originalEnvironment,
      ),
    };

    this.serverlessLog(`Offline executing script [${command}]`);

    return new Promise((resolve) => {
      exec(command, options, (error, stdout, stderr) => {
        this.serverlessLog(`exec stdout: [${stdout}]`);
        this.serverlessLog(`exec stderr: [${stderr}]`);

        if (error) {
          // Use the failed command's exit code, proceed as normal so that shutdown can occur gracefully
          this.serverlessLog(`Offline error executing script [${error}]`);
          this.exitCode = error.code || 1;
        }
        resolve();
      });
    });
  }

  _buildServer() {
    // Methods
    this._setOptions(); // Will create meaningful options from cli options
    this._storeOriginalEnvironment(); // stores the original process.env for assigning upon invoking the handlers

    this.apiGateway = new ApiGateway(
      this.serverless,
      this.options,
      this.velocityContextOptions,
    );

    const server = this.apiGateway._createServer();

    this.hasWebsocketRoutes = false;
    this.apiGatewayWebSocket = new ApiGatewayWebSocket(
      this.serverless,
      this.options,
    );
    this.apiGatewayWebSocket._createWebSocket();

    this._setupEvents();
    this.apiGateway._createResourceRoutes(); // HTTP Proxy defined in Resource
    this.apiGateway._create404Route(); // Not found handling

    return server;
  }

  _storeOriginalEnvironment() {
    this.originalEnvironment = Object.assign({}, process.env);
  }

  _setOptions() {
    // Merge the different sources of values for this.options
    // Precedence is: command line options, YAML options, defaults.
    const defaultOptions = {
      apiKey: createDefaultApiKey(),
      cacheInvalidationRegex: 'node_modules',
      corsAllowOrigin: '*',
      corsAllowCredentials: true,
      corsAllowHeaders: 'accept,content-type,x-api-key,authorization',
      corsExposedHeaders: 'WWW-Authenticate,Server-Authorization',
      disableCookieValidation: false,
      enforceSecureCookies: false,
      exec: '',
      hideStackTraces: false,
      host: 'localhost',
      httpsProtocol: '',
      location: '.',
      noAuth: false,
      noEnvironment: false,
      noTimeout: false,
      port: 3000,
      prefix: '/',
      preserveTrailingSlash: false,
      printOutput: false,
      providedRuntime: '',
      showDuration: false,
      stage: this.service.provider.stage,
      region: this.service.provider.region,
      resourceRoutes: false,
      skipCacheInvalidation: false,
      useSeparateProcesses: false,
      websocketPort: 3001,
    };

    // In the constructor, stage and regions are set to undefined
    if (this.options.stage === undefined) delete this.options.stage;
    if (this.options.region === undefined) delete this.options.region;

    const yamlOptions = (this.service.custom || {})['serverless-offline'];
    this.options = Object.assign({}, defaultOptions, yamlOptions, this.options);

    // Prefix must start and end with '/'
    if (!this.options.prefix.startsWith('/'))
      this.options.prefix = `/${this.options.prefix}`;
    if (!this.options.prefix.endsWith('/')) this.options.prefix += '/';

    this.velocityContextOptions = {
      stageVariables: {}, // this.service.environment.stages[this.options.stage].vars,
      stage: this.options.stage,
    };

    // Parse CORS options
    this.options.corsAllowOrigin = this.options.corsAllowOrigin
      .replace(/\s/g, '')
      .split(',');
    this.options.corsAllowHeaders = this.options.corsAllowHeaders
      .replace(/\s/g, '')
      .split(',');
    this.options.corsExposedHeaders = this.options.corsExposedHeaders
      .replace(/\s/g, '')
      .split(',');

    if (this.options.corsDisallowCredentials)
      this.options.corsAllowCredentials = false;

    this.options.corsConfig = {
      credentials: this.options.corsAllowCredentials,
      exposedHeaders: this.options.corsExposedHeaders,
      headers: this.options.corsAllowHeaders,
      origin: this.options.corsAllowOrigin,
    };

    this.options.cacheInvalidationRegex = new RegExp(
      this.options.cacheInvalidationRegex,
    );

    this.serverlessLog(
      `Starting Offline: ${this.options.stage}/${this.options.region}.`,
    );
    debugLog('options:', this.options);
  }

  end() {
    this.serverlessLog('Halting offline server');
    functionHelper.cleanup();
    this.apiGateway.server
      .stop({ timeout: 5000 })
      .then(() => process.exit(this.exitCode));
  }

  _setupEvents() {
    let serviceRuntime = this.service.provider.runtime;
    const defaultContentType = 'application/json';
    const { apiKeys } = this.service.provider;
    const protectedRoutes = [];

    if (!serviceRuntime) {
      throw new Error('Missing required property "runtime" for provider.');
    }

    if (typeof serviceRuntime !== 'string') {
      throw new Error(
        'Provider configuration property "runtime" wasn\'t a string.',
      );
    }

    if (serviceRuntime === 'provided') {
      if (this.options.providedRuntime) {
        serviceRuntime = this.options.providedRuntime;
      } else {
        throw new Error(
          'Runtime "provided" is unsupported. Please add a --providedRuntime CLI option.',
        );
      }
    }

    if (
      !(
        serviceRuntime.startsWith('nodejs') ||
        serviceRuntime.startsWith('python') ||
        serviceRuntime.startsWith('ruby') ||
        serviceRuntime.startsWith('go')
      )
    ) {
      this.printBlankLine();
      this.serverlessLog(
        `Warning: found unsupported runtime '${serviceRuntime}'`,
      );

      return;
    }

    // for simple API Key authentication model
    if (apiKeys) {
      this.serverlessLog(`Key with token: ${this.options.apiKey}`);

      if (this.options.noAuth) {
        this.serverlessLog(
          'Authorizers are turned off. You do not need to use x-api-key header.',
        );
      } else {
        this.serverlessLog('Remember to use x-api-key on the request headers');
      }
    }

    Object.keys(this.service.functions).forEach((key) => {
      const fun = this.service.getFunction(key);
      const funName = key;
      const servicePath = path.join(
        this.serverless.config.servicePath,
        this.options.location,
      );
      const funOptions = functionHelper.getFunctionOptions(
        fun,
        key,
        servicePath,
        serviceRuntime,
      );

      debugLog(`funOptions ${JSON.stringify(funOptions, null, 2)} `);
      this.printBlankLine();
      debugLog(funName, 'runtime', serviceRuntime);
      this.serverlessLog(`Routes for ${funName}:`);

      if (!fun.events) {
        fun.events = [];
      }

      // Add proxy for lamda invoke
      fun.events.push({
        http: {
          integration: 'lambda',
          method: 'POST',
          path: `{apiVersion}/functions/${fun.name}/invocations`,
          request: {
            template: {
              // AWS SDK for NodeJS specifies as 'binary/octet-stream' not 'application/json'
              'binary/octet-stream': '$input.body',
            },
          },
          response: {
            headers: {
              'Content-Type': 'application/json',
            },
          },
        },
      });

      // Adds a route for each http endpoint
      fun.events.forEach((event) => {
        if (event.websocket) {
          this.hasWebsocketRoutes = true;

          this._experimentalWebSocketSupportWarning();

          this.apiGatewayWebSocket._createWsAction(
            fun,
            funName,
            servicePath,
            funOptions,
            event,
          );

          return;
        }

        if (!event.http) return;

        this.apiGateway._createRoutes(
          event,
          funOptions,
          protectedRoutes,
          funName,
          servicePath,
          serviceRuntime,
          defaultContentType,
          key,
          fun,
        );
      });
    });
  }

  // TODO: missing tests
  _verifyServerlessVersionCompatibility() {
    const { version: currentVersion } = this.serverless;
    const { serverless: requiredVersion } = peerDependencies;

    const versionIsSatisfied = satisfiesVersionRange(
      requiredVersion,
      currentVersion,
    );

    if (!versionIsSatisfied) {
      this.serverlessLog(
        `"Serverless-Offline" requires "Serverless" version ${requiredVersion} but found version ${currentVersion}.
          Be aware that functionality might be limited or has serious bugs.
          To avoid any issues update "Serverless" to a later version.
        `,
        'serverless-offline',
        { color: 'red' },
      );
    }
  }

  // TODO: eventually remove WARNING after release has been deemed stable
  _experimentalWebSocketSupportWarning() {
    // notify only once
    if (this._experimentalWarningNotified) {
      return;
    }

    this.serverlessLog(
      `WebSocket support in "Serverless-Offline is experimental.
       For any bugs, missing features or other feedback file an issue at https://github.com/dherault/serverless-offline/issues
      `,
      'serverless-offline',
      { color: 'magenta' },
    );

    this._experimentalWarningNotified = true;
  }
};
<commit_msg>Fix log formatting<commit_after>'use strict';

const { exec } = require('child_process');
const path = require('path');
const ApiGateway = require('./ApiGateway');
const ApiGatewayWebSocket = require('./ApiGatewayWebSocket');
const debugLog = require('./debugLog');
const functionHelper = require('./functionHelper');
const { createDefaultApiKey, satisfiesVersionRange } = require('./utils');
const { peerDependencies } = require('../package.json');

module.exports = class ServerlessOffline {
  constructor(serverless, options) {
    this.serverless = serverless;
    this.service = serverless.service;
    this.serverlessLog = serverless.cli.log.bind(serverless.cli);
    this.options = options;
    this.exitCode = 0;
    this._experimentalWarningNotified = false;

    this.commands = {
      offline: {
        usage: 'Simulates API Gateway to call your lambda functions offline.',
        lifecycleEvents: ['start'],
        // add start nested options
        commands: {
          start: {
            usage:
              'Simulates API Gateway to call your lambda functions offline using backward compatible initialization.',
            lifecycleEvents: ['init', 'end'],
          },
        },
        options: {
          apiKey: {
            usage:
              'Defines the API key value to be used for endpoints marked as private. Defaults to a random hash.',
          },
          binPath: {
            usage: 'Path to the Serverless binary.',
            shortcut: 'b',
          },
          cacheInvalidationRegex: {
            usage:
              'Provide the plugin with a regexp to use for cache invalidation. Default: node_modules',
          },
          corsAllowHeaders: {
            usage:
              'Used to build the Access-Control-Allow-Headers header for CORS support.',
          },
          corsAllowOrigin: {
            usage:
              'Used to build the Access-Control-Allow-Origin header for CORS support.',
          },
          corsDisallowCredentials: {
            usage:
              'Used to override the Access-Control-Allow-Credentials default (which is true) to false.',
          },
          corsExposedHeaders: {
            usage:
              'USed to build the Access-Control-Exposed-Headers response header for CORS support',
          },
          disableCookieValidation: {
            usage: 'Used to disable cookie-validation on hapi.js-server',
          },
          enforceSecureCookies: {
            usage: 'Enforce secure cookies',
          },
          exec: {
            usage:
              'When provided, a shell script is executed when the server starts up, and the server will shut down after handling this command.',
          },
          hideStackTraces: {
            usage: 'Hide the stack trace on lambda failure. Default: false',
          },
          host: {
            usage: 'The host name to listen on. Default: localhost',
            shortcut: 'o',
          },
          httpsProtocol: {
            usage:
              'To enable HTTPS, specify directory (relative to your cwd, typically your project dir) for both cert.pem and key.pem files.',
            shortcut: 'H',
          },
          location: {
            usage: "The root location of the handlers' files.",
            shortcut: 'l',
          },
          noAuth: {
            usage: 'Turns off all authorizers',
          },
          noEnvironment: {
            usage:
              'Turns off loading of your environment variables from serverless.yml. Allows the usage of tools such as PM2 or docker-compose.',
          },
          port: {
            usage: 'Port to listen on. Default: 3000',
            shortcut: 'P',
          },
          prefix: {
            usage:
              'Adds a prefix to every path, to send your requests to http://localhost:3000/prefix/[your_path] instead.',
            shortcut: 'p',
          },
          preserveTrailingSlash: {
            usage: 'Used to keep trailing slashes on the request path',
          },
          printOutput: {
            usage: 'Outputs your lambda response to the terminal.',
          },
          providedRuntime: {
            usage: 'Sets the provided runtime for lambdas',
          },
          region: {
            usage: 'The region used to populate your templates.',
            shortcut: 'r',
          },
          resourceRoutes: {
            usage:
              'Turns on loading of your HTTP proxy settings from serverless.yml.',
          },
          showDuration: {
            usage: 'Show the execution time duration of the lambda function.',
          },
          skipCacheInvalidation: {
            usage:
              'Tells the plugin to skip require cache invalidation. A script reloading tool like Nodemon might then be needed',
            shortcut: 'c',
          },
          stage: {
            usage: 'The stage used to populate your templates.',
            shortcut: 's',
          },
          useSeparateProcesses: {
            usage: 'Uses separate node processes for handlers',
          },
          websocketPort: {
            usage: 'Websocket port to listen on. Default: 3001',
          },
        },
      },
    };

    this.hooks = {
      'offline:start:init': this.start.bind(this),
      'offline:start': this.startWithExplicitEnd.bind(this),
      'offline:start:end': this.end.bind(this),
    };
  }

  printBlankLine() {
    console.log();
  }

  logPluginIssue() {
    this.serverlessLog(
      'If you think this is an issue with the plugin please submit it, thanks!',
    );
    this.serverlessLog('https://github.com/dherault/serverless-offline/issues');
  }

  // Entry point for the plugin (sls offline) when running 'sls offline start'
  start() {
    this._verifyServerlessVersionCompatibility();

    // Some users would like to know their environment outside of the handler
    process.env.IS_OFFLINE = true;

    return Promise.resolve(this._buildServer())
      .then(() => this.apiGateway._listen())
      .then(() => this.hasWebsocketRoutes && this.apiGatewayWebSocket._listen())
      .then(() =>
        this.options.exec
          ? this._executeShellScript()
          : this._listenForTermination(),
      );
  }

  /**
   * Entry point for the plugin (sls offline) when running 'sls offline'
   * The call to this.end() would terminate the process before 'offline:start:end' could be consumed
   * by downstream plugins. When running sls offline that can be expected, but docs say that
   * 'sls offline start' will provide the init and end hooks for other plugins to consume
   * */
  startWithExplicitEnd() {
    return this.start().then(() => this.end());
  }

  _listenForTermination() {
    // SIGINT will be usually sent when user presses ctrl+c
    const waitForSigInt = new Promise((resolve) => {
      process.on('SIGINT', () => resolve('SIGINT'));
    });

    // SIGTERM is a default termination signal in many cases,
    // for example when "killing" a subprocess spawned in node
    // with child_process methods
    const waitForSigTerm = new Promise((resolve) => {
      process.on('SIGTERM', () => resolve('SIGTERM'));
    });

    return Promise.race([waitForSigInt, waitForSigTerm]).then((command) => {
      this.serverlessLog(`Got ${command} signal. Offline Halting...`);
    });
  }

  _executeShellScript() {
    const command = this.options.exec;
    const options = {
      env: Object.assign(
        { IS_OFFLINE: true },
        this.service.provider.environment,
        this.originalEnvironment,
      ),
    };

    this.serverlessLog(`Offline executing script [${command}]`);

    return new Promise((resolve) => {
      exec(command, options, (error, stdout, stderr) => {
        this.serverlessLog(`exec stdout: [${stdout}]`);
        this.serverlessLog(`exec stderr: [${stderr}]`);

        if (error) {
          // Use the failed command's exit code, proceed as normal so that shutdown can occur gracefully
          this.serverlessLog(`Offline error executing script [${error}]`);
          this.exitCode = error.code || 1;
        }
        resolve();
      });
    });
  }

  _buildServer() {
    // Methods
    this._setOptions(); // Will create meaningful options from cli options
    this._storeOriginalEnvironment(); // stores the original process.env for assigning upon invoking the handlers

    this.apiGateway = new ApiGateway(
      this.serverless,
      this.options,
      this.velocityContextOptions,
    );

    const server = this.apiGateway._createServer();

    this.hasWebsocketRoutes = false;
    this.apiGatewayWebSocket = new ApiGatewayWebSocket(
      this.serverless,
      this.options,
    );
    this.apiGatewayWebSocket._createWebSocket();

    this._setupEvents();
    this.apiGateway._createResourceRoutes(); // HTTP Proxy defined in Resource
    this.apiGateway._create404Route(); // Not found handling

    return server;
  }

  _storeOriginalEnvironment() {
    this.originalEnvironment = Object.assign({}, process.env);
  }

  _setOptions() {
    // Merge the different sources of values for this.options
    // Precedence is: command line options, YAML options, defaults.
    const defaultOptions = {
      apiKey: createDefaultApiKey(),
      cacheInvalidationRegex: 'node_modules',
      corsAllowOrigin: '*',
      corsAllowCredentials: true,
      corsAllowHeaders: 'accept,content-type,x-api-key,authorization',
      corsExposedHeaders: 'WWW-Authenticate,Server-Authorization',
      disableCookieValidation: false,
      enforceSecureCookies: false,
      exec: '',
      hideStackTraces: false,
      host: 'localhost',
      httpsProtocol: '',
      location: '.',
      noAuth: false,
      noEnvironment: false,
      noTimeout: false,
      port: 3000,
      prefix: '/',
      preserveTrailingSlash: false,
      printOutput: false,
      providedRuntime: '',
      showDuration: false,
      stage: this.service.provider.stage,
      region: this.service.provider.region,
      resourceRoutes: false,
      skipCacheInvalidation: false,
      useSeparateProcesses: false,
      websocketPort: 3001,
    };

    // In the constructor, stage and regions are set to undefined
    if (this.options.stage === undefined) delete this.options.stage;
    if (this.options.region === undefined) delete this.options.region;

    const yamlOptions = (this.service.custom || {})['serverless-offline'];
    this.options = Object.assign({}, defaultOptions, yamlOptions, this.options);

    // Prefix must start and end with '/'
    if (!this.options.prefix.startsWith('/'))
      this.options.prefix = `/${this.options.prefix}`;
    if (!this.options.prefix.endsWith('/')) this.options.prefix += '/';

    this.velocityContextOptions = {
      stageVariables: {}, // this.service.environment.stages[this.options.stage].vars,
      stage: this.options.stage,
    };

    // Parse CORS options
    this.options.corsAllowOrigin = this.options.corsAllowOrigin
      .replace(/\s/g, '')
      .split(',');
    this.options.corsAllowHeaders = this.options.corsAllowHeaders
      .replace(/\s/g, '')
      .split(',');
    this.options.corsExposedHeaders = this.options.corsExposedHeaders
      .replace(/\s/g, '')
      .split(',');

    if (this.options.corsDisallowCredentials)
      this.options.corsAllowCredentials = false;

    this.options.corsConfig = {
      credentials: this.options.corsAllowCredentials,
      exposedHeaders: this.options.corsExposedHeaders,
      headers: this.options.corsAllowHeaders,
      origin: this.options.corsAllowOrigin,
    };

    this.options.cacheInvalidationRegex = new RegExp(
      this.options.cacheInvalidationRegex,
    );

    this.serverlessLog(
      `Starting Offline: ${this.options.stage}/${this.options.region}.`,
    );
    debugLog('options:', this.options);
  }

  end() {
    this.serverlessLog('Halting offline server');
    functionHelper.cleanup();
    this.apiGateway.server
      .stop({ timeout: 5000 })
      .then(() => process.exit(this.exitCode));
  }

  _setupEvents() {
    let serviceRuntime = this.service.provider.runtime;
    const defaultContentType = 'application/json';
    const { apiKeys } = this.service.provider;
    const protectedRoutes = [];

    if (!serviceRuntime) {
      throw new Error('Missing required property "runtime" for provider.');
    }

    if (typeof serviceRuntime !== 'string') {
      throw new Error(
        'Provider configuration property "runtime" wasn\'t a string.',
      );
    }

    if (serviceRuntime === 'provided') {
      if (this.options.providedRuntime) {
        serviceRuntime = this.options.providedRuntime;
      } else {
        throw new Error(
          'Runtime "provided" is unsupported. Please add a --providedRuntime CLI option.',
        );
      }
    }

    if (
      !(
        serviceRuntime.startsWith('nodejs') ||
        serviceRuntime.startsWith('python') ||
        serviceRuntime.startsWith('ruby') ||
        serviceRuntime.startsWith('go')
      )
    ) {
      this.printBlankLine();
      this.serverlessLog(
        `Warning: found unsupported runtime '${serviceRuntime}'`,
      );

      return;
    }

    // for simple API Key authentication model
    if (apiKeys) {
      this.serverlessLog(`Key with token: ${this.options.apiKey}`);

      if (this.options.noAuth) {
        this.serverlessLog(
          'Authorizers are turned off. You do not need to use x-api-key header.',
        );
      } else {
        this.serverlessLog('Remember to use x-api-key on the request headers');
      }
    }

    Object.keys(this.service.functions).forEach((key) => {
      const fun = this.service.getFunction(key);
      const funName = key;
      const servicePath = path.join(
        this.serverless.config.servicePath,
        this.options.location,
      );
      const funOptions = functionHelper.getFunctionOptions(
        fun,
        key,
        servicePath,
        serviceRuntime,
      );

      debugLog(`funOptions ${JSON.stringify(funOptions, null, 2)} `);
      this.printBlankLine();
      debugLog(funName, 'runtime', serviceRuntime);
      this.serverlessLog(`Routes for ${funName}:`);

      if (!fun.events) {
        fun.events = [];
      }

      // Add proxy for lamda invoke
      fun.events.push({
        http: {
          integration: 'lambda',
          method: 'POST',
          path: `{apiVersion}/functions/${fun.name}/invocations`,
          request: {
            template: {
              // AWS SDK for NodeJS specifies as 'binary/octet-stream' not 'application/json'
              'binary/octet-stream': '$input.body',
            },
          },
          response: {
            headers: {
              'Content-Type': 'application/json',
            },
          },
        },
      });

      // Adds a route for each http endpoint
      fun.events.forEach((event) => {
        if (event.websocket) {
          this.hasWebsocketRoutes = true;

          this._experimentalWebSocketSupportWarning();

          this.apiGatewayWebSocket._createWsAction(
            fun,
            funName,
            servicePath,
            funOptions,
            event,
          );

          return;
        }

        if (!event.http) return;

        this.apiGateway._createRoutes(
          event,
          funOptions,
          protectedRoutes,
          funName,
          servicePath,
          serviceRuntime,
          defaultContentType,
          key,
          fun,
        );
      });
    });
  }

  // TODO: missing tests
  _verifyServerlessVersionCompatibility() {
    const { version: currentVersion } = this.serverless;
    const { serverless: requiredVersion } = peerDependencies;

    const versionIsSatisfied = satisfiesVersionRange(
      requiredVersion,
      currentVersion,
    );

    if (!versionIsSatisfied) {
      this.serverlessLog(
        `"Serverless-Offline" requires "Serverless" version ${requiredVersion} but found version ${currentVersion}.
         Be aware that functionality might be limited or has serious bugs.
         To avoid any issues update "Serverless" to a later version.
        `,
        'serverless-offline',
        { color: 'red' },
      );
    }
  }

  // TODO: eventually remove WARNING after release has been deemed stable
  _experimentalWebSocketSupportWarning() {
    // notify only once
    if (this._experimentalWarningNotified) {
      return;
    }

    this.serverlessLog(
      `WebSocket support in "Serverless-Offline is experimental.
       For any bugs, missing features or other feedback file an issue at https://github.com/dherault/serverless-offline/issues
      `,
      'serverless-offline',
      { color: 'magenta' },
    );

    this._experimentalWarningNotified = true;
  }
};
<|endoftext|>


<commit_before>var pgSchedule = angular.module('pgSchedule', ['ui.bootstrap', 'ngCookies']);

pgSchedule.controller('mainController', ['$scope', '$http', '$log', '$interval', '$location', '$cookies', '$modal', '$sce', function($scope, $http, $log, $interval, $location, $cookies, $modal, $sce) {
  $scope.timeUntil = '';
  $scope.currentDay = '';
  $scope.currentBlock = '';
  $scope.dateString = '';
  $scope.cookies = []; // A=BLOCK,B=BLOCK
  $scope.weekend = false;
  $scope.lunch = [];
  $scope.day = new Date();
  $scope.week = '';
  $scope.nightMode = false;
  $scope.announcement;
  $scope.specialMiddle = false;
  $scope.specialUpper = false;

  $scope.getTimeUntil = function() {
    $http.get(getApi('timeUntil')).success(function(data) {
      if (data === -1) {
        $scope.dateString = 'No Class';
      } else {
        if (data) {
          $scope.timeUntil = (parseInt(data) + 1) + '';
        } else {
          $scope.dateString = '';
          return;
        }
        $scope.dateString = ($scope.timeUntil) + ' minutes until next class.';
        if ($scope.timeUntil === 1) {
          $scope.dateString = ($scope.timeUntil) + ' minute until next class';
        }
      }
    });
  };

  $scope.updateDate = function() {
    if ($scope.dt === null) {
      return;
    }
    $scope.day = new Date($scope.dt);
    var apiString = 'getFutureDate' +
      '/' + ($scope.day.getMonth() + 1) +
      '/' + ($scope.day.getDate()) +
      '/' + ($scope.day.getFullYear());
    $http.get(getApi(apiString)).success(function(data) {
      $scope.currentDay = data[Object.keys(data)[0]];
      if (Object.keys(data)[0] === 'WEEKEND') {
        $scope.week = 'Weekend';
      } else {
        $scope.week = Object.keys(data)[0] + ' Week';
      }
      $scope.weekend = (Object.keys(data)[0] === 'WEEKEND');
    });
  };

  $scope.changeDay = function(offset) {
    $scope.specialUpper = false;
    $scope.specialMiddle = false;
    var dateOffset = (24 * 60 * 60 * 1000) * offset;
    $scope.day.setTime($scope.day.getTime() + dateOffset);
    var apiString = 'getFutureDate' +
      '/' + ($scope.day.getMonth() + 1) +
      '/' + ($scope.day.getDate()) +
      '/' + ($scope.day.getFullYear());
    $http.get(getApi(apiString)).success(function(data) {
      $scope.currentDay = data[Object.keys(data)[0]];
      if (Object.keys(data)[0] === '') {
        $scope.week = 'Weekend';
      } else {
        $scope.week = Object.keys(data)[0] + ' Week';
      }
      if (data[Object.keys(data)[0]].specialUpper && !$scope.specialUpper) {
        $scope.specialUpper = true;
        loadSpecialUpper(data[Object.keys(data)[0]].date);
      }
      if (data[Object.keys(data)[0]].specialMiddle && !$scope.specialMiddle) {
        $scope.specialMiddle = true;
        loadSpecialMiddle(data[Object.keys(data)[0]].date);
      }
      $scope.weekend = (Object.keys(data)[0] === '');
    });
  };

  $scope.getCurrentBlock = function() {
    $http.get(getApi('currentBlock')).success(function(data) {
      $scope.currentBlock = data;
      if ($cookies.get('schedule')) {
        var classCookie = $cookies.get('schedule').split(',');
        for (var i = 0; i < classCookie.length; i++) {
          var theClass = classCookie[i].split('=');
          $scope.cookies[theClass[0]] = theClass[1];
        }
      }
    });
  };

  $scope.getCurrentDay = function() {
    if ($scope.day.getDate() !== new Date().getDate()) {
      return;
    }
    $http.get(getApi('currentDay')).success(function(data) {
      $scope.currentDay = data[Object.keys(data)[0]];
      if (data[Object.keys(data)[0]].specialUpper && !$scope.specialUpper) {
        console.log(data[Object.keys(data)[0]].specialUpper);
        $scope.specialUpper = true;
        loadSpecialUpper(data[Object.keys(data)[0]].date);
      }
      if (data[Object.keys(data)[0]].specialMiddle && !$scope.specialMiddle) {
        console.log(data[Object.keys(data)[0]].specialMiddle);
        $scope.specialMiddle = true;
        loadSpecialMiddle(data[Object.keys(data)[0]].date);
      }
      if (Object.keys(data)[0] === 'WEEKEND') {
        $scope.week = 'Weekend';
      } else {
        $scope.week = Object.keys(data)[0] + ' Week';
      }
      $scope.weekend = (Object.keys(data)[0] === 'WEEKEND');
    });
  };

  function loadSpecialUpper(day) {
    if (parseInt(day)) {
      var extra = '.pdf#toolbar=0&navpanes=0&scrollbar=0&view=FitH';
      $scope.specialUpper = true;
      $scope.week = 'Special Schedule';
      $scope.specialUpperUrl = $sce.trustAsResourceUrl('/special/upper/' + day + extra);
    }
  }

  function loadSpecialMiddle(day) {
    if (parseInt(day)) {
      var extra = '.pdf#toolbar=0&navpanes=0&scrollbar=0&view=FitH';
      $scope.specialMiddle = true;
      $scope.week = 'Special Schedule';
      $scope.specialMiddleUrl = $sce.trustAsResourceUrl('/special/middle/' + day + extra);
    }
  }

  $scope.addClasses = function() {
    var cookieString = '';
    angular.forEach(angular.element.find('.classInput'), function(node) {
      cookieString += (node.id.substring(0, 1)) + '=' + '' + node.value + ',';
    });
    $cookies.put('schedule', cookieString, {expires: new Date('5/29/2020')});
    location.reload(); // Until we get angular-bootstrap working correctly.
  };

  $scope.getLunchMenu = function() {
    $http.get('/api/getLunchMenu').success(function(data) {
      $scope.lunch = data;
      $scope.lunch.pop();
    });
  };

  $scope.getAnnouncement = function() {
    var views = 0;
    if ($cookies.get('announcement')) {
      var cookie = $cookies.get('announcement');
      views = parseInt(cookie.split(':')[0]);
      if (views > parseInt(cookie.split(':')[1])) {
        return;
      }
    }
    $http.get('/api/getAnnouncement').success(function(data) {
      $scope.announcement = data;
      $cookies.put('announcement', views + 1 + ':' + data.loads, {expires: new Date(data.expires)});
    });
  };

  $scope.beautifyTime = function(time) {
    var hours = parseInt(time.substring(0, 2)) % 12;
    hours = (hours === 0 ? 12 : hours);
    return hours + time.substring(2, 5);
  };

  function getApi(endpoint) {
    return '/api/' + endpoint + (($location.absUrl().indexOf('middle') > -1) ? '?middle=1' : '');
  }

  $scope.getCurrentDay();
  $scope.getCurrentBlock();
  $scope.getTimeUntil();
  $scope.getAnnouncement();

  $interval($scope.getCurrentBlock, 1000 * 10);
  $interval($scope.getCurrentDay, 1000 * 60 * 60);
  $interval($scope.getTimeUntil, 1000 * 10);

  $scope.clear = function() {
    $scope.dt = null;
  };

  $scope.open = function($event) {
    $scope.showWeeks = false;
    $scope.status.opened = true;
  };

  $scope.status = {
    opened: false,
  };

  $scope.getDayClass = function(date, mode) {
    if (mode === 'day') {
      var dayToCheck = new Date(date).setHours(0, 0, 0, 0);
      for (var i = 0; i < $scope.events.length; i++) {
        var currentDay = new Date($scope.events[i].date).setHours(0, 0, 0, 0);
        if (dayToCheck === currentDay) {
          return $scope.events[i].status;
        }
      }
    }
    return '';
  };

}]);
<commit_msg>Update angularCore.js<commit_after>var pgSchedule = angular.module('pgSchedule', ['ui.bootstrap', 'ngCookies']);

pgSchedule.controller('mainController', ['$scope', '$http', '$log', '$interval', '$location', '$cookies', '$modal', '$sce', function($scope, $http, $log, $interval, $location, $cookies, $modal, $sce) {
  $scope.timeUntil = '';
  $scope.currentDay = '';
  $scope.currentBlock = '';
  $scope.dateString = '';
  $scope.cookies = []; // A=BLOCK,B=BLOCK
  $scope.weekend = false;
  $scope.lunch = [];
  $scope.day = new Date();
  $scope.week = '';
  $scope.nightMode = false;
  $scope.announcement;
  $scope.specialMiddle = false;
  $scope.specialUpper = false;

  $scope.getTimeUntil = function() {
    $http.get(getApi('timeUntil')).success(function(data) {
      if (data === -1) {
        $scope.dateString = 'No Class';
      } else {
        if (data) {
          $scope.timeUntil = (parseInt(data) + 1) + '';
        } else {
          $scope.dateString = '';
          return;
        }
        $scope.dateString = ($scope.timeUntil) + ' minutes until next class.';
        if ($scope.timeUntil === 1) {
          $scope.dateString = ($scope.timeUntil) + ' minute until next class';
        }
      }
    });
  };

  $scope.updateDate = function() {
    if ($scope.dt === null) {
      return;
    }
    $scope.day = new Date($scope.dt);
    var apiString = 'getFutureDate' +
      '/' + ($scope.day.getMonth() + 1) +
      '/' + ($scope.day.getDate()) +
      '/' + ($scope.day.getFullYear());
    $http.get(getApi(apiString)).success(function(data) {
      $scope.currentDay = data[Object.keys(data)[0]];
      if (Object.keys(data)[0] === 'WEEKEND') {
        $scope.week = 'Weekend';
      } else {
        $scope.week = Object.keys(data)[0] + ' Week';
      }
      $scope.weekend = (Object.keys(data)[0] === 'WEEKEND');
    });
  };

  $scope.changeDay = function(offset) {
    $scope.specialUpper = false;
    $scope.specialMiddle = false;
    var dateOffset = (24 * 60 * 60 * 1000) * offset;
    $scope.day.setTime($scope.day.getTime() + dateOffset);
    var apiString = 'getFutureDate' +
      '/' + ($scope.day.getMonth() + 1) +
      '/' + ($scope.day.getDate()) +
      '/' + ($scope.day.getFullYear());
    $http.get(getApi(apiString)).success(function(data) {
      $scope.currentDay = data[Object.keys(data)[0]];
      if (Object.keys(data)[0] === '') {
        $scope.week = 'Weekend';
      } else {
        $scope.week = Object.keys(data)[0] + ' Week';
      }
      if (data[Object.keys(data)[0]].specialUpper && !$scope.specialUpper) {
        $scope.specialUpper = true;
        loadSpecialUpper(data[Object.keys(data)[0]].date);
      }
      if (data[Object.keys(data)[0]].specialMiddle && !$scope.specialMiddle) {
        $scope.specialMiddle = true;
        loadSpecialMiddle(data[Object.keys(data)[0]].date);
      }
      $scope.weekend = (Object.keys(data)[0] === '');
    });
  };

  $scope.getCurrentBlock = function() {
    $http.get(getApi('currentBlock')).success(function(data) {
      $scope.currentBlock = data;
      if ($cookies.get('schedule')) {
        var classCookie = $cookies.get('schedule').split(',');
        for (var i = 0; i < classCookie.length; i++) {
          var theClass = classCookie[i].split('=');
          $scope.cookies[theClass[0]] = theClass[1];
        }
      }
    });
  };

  $scope.getCurrentDay = function() {
    if ($scope.day.getDate() !== new Date().getDate()) {
      return;
    }
    $http.get(getApi('currentDay')).success(function(data) {
      $scope.currentDay = data[Object.keys(data)[0]];
      if (data[Object.keys(data)[0]].specialUpper && !$scope.specialUpper) {
        console.log(data[Object.keys(data)[0]].specialUpper);
        $scope.specialUpper = true;
        loadSpecialUpper(data[Object.keys(data)[0]].date);
      }
      if (data[Object.keys(data)[0]].specialMiddle && !$scope.specialMiddle) {
        console.log(data[Object.keys(data)[0]].specialMiddle);
        $scope.specialMiddle = true;
        loadSpecialMiddle(data[Object.keys(data)[0]].date);
      }
      if (Object.keys(data)[0] === 'WEEKEND') {
        $scope.week = 'Weekend';
      } else {
        $scope.week = Object.keys(data)[0] + ' Week';
      }
      $scope.weekend = (Object.keys(data)[0] === 'WEEKEND');
    });
  };

  function loadSpecialUpper(day) {
    if (parseInt(day)) {
      var extra = '.pdf#toolbar=0&navpanes=0&scrollbar=0&view=FitH';
      $scope.specialUpper = true;
      $scope.week = 'Special Schedule';
      $scope.specialUpperUrl = $sce.trustAsResourceUrl('/special/upper/' + day + extra);
    }
  }

  function loadSpecialMiddle(day) {
    if (parseInt(day)) {
      var extra = '.pdf#toolbar=0&navpanes=0&scrollbar=0&view=FitH';
      $scope.specialMiddle = true;
      $scope.week = 'Special Schedule';
      $scope.specialMiddleUrl = $sce.trustAsResourceUrl('/special/middle/' + day + extra);
    }
  }

  $scope.addClasses = function() {
    var cookieString = '';
    angular.forEach(angular.element.find('.classInput'), function(node) {
      cookieString += (node.id.substring(0, 1)) + '=' + '' + node.value + ',';
    });
    $cookies.put('schedule', cookieString, {expires: new Date('12/12/18')});
    location.reload(); // Until we get angular-bootstrap working correctly.
  };

  $scope.getLunchMenu = function() {
    $http.get('/api/getLunchMenu').success(function(data) {
      $scope.lunch = data;
      $scope.lunch.pop();
    });
  };

  $scope.getAnnouncement = function() {
    var views = 0;
    if ($cookies.get('announcement')) {
      var cookie = $cookies.get('announcement');
      views = parseInt(cookie.split(':')[0]);
      if (views > parseInt(cookie.split(':')[1])) {
        return;
      }
    }
    $http.get('/api/getAnnouncement').success(function(data) {
      $scope.announcement = data;
      $cookies.put('announcement', views + 1 + ':' + data.loads, {expires: new Date(data.expires)});
    });
  };

  $scope.beautifyTime = function(time) {
    var hours = parseInt(time.substring(0, 2)) % 12;
    hours = (hours === 0 ? 12 : hours);
    return hours + time.substring(2, 5);
  };

  function getApi(endpoint) {
    return '/api/' + endpoint + (($location.absUrl().indexOf('middle') > -1) ? '?middle=1' : '');
  }

  $scope.getCurrentDay();
  $scope.getCurrentBlock();
  $scope.getTimeUntil();
  $scope.getAnnouncement();

  $interval($scope.getCurrentBlock, 1000 * 10);
  $interval($scope.getCurrentDay, 1000 * 60 * 60);
  $interval($scope.getTimeUntil, 1000 * 10);

  $scope.clear = function() {
    $scope.dt = null;
  };

  $scope.open = function($event) {
    $scope.showWeeks = false;
    $scope.status.opened = true;
  };

  $scope.status = {
    opened: false,
  };

  $scope.getDayClass = function(date, mode) {
    if (mode === 'day') {
      var dayToCheck = new Date(date).setHours(0, 0, 0, 0);
      for (var i = 0; i < $scope.events.length; i++) {
        var currentDay = new Date($scope.events[i].date).setHours(0, 0, 0, 0);
        if (dayToCheck === currentDay) {
          return $scope.events[i].status;
        }
      }
    }
    return '';
  };

}]);
<|endoftext|>


<commit_before>/* eslint default-case: 0 */
/* eslint guard-for-in: 0 */
import React, { Component, PropTypes } from 'react';
import classSet from 'classnames';
import Const from './Const';
import Util from './util';
import DateFilter from './filters/Date';
import TextFilter from './filters/Text';
import RegexFilter from './filters/Regex';
import SelectFilter from './filters/Select';
import NumberFilter from './filters/Number';

class TableHeaderColumn extends Component {

  constructor(props) {
    super(props);
    this.handleFilter = this.handleFilter.bind(this);
  }

  handleColumnClick = () => {
    if (!this.props.dataSort) return;
    const order = this.props.sort === Const.SORT_DESC ? Const.SORT_ASC : Const.SORT_DESC;
    this.props.onSort(order, this.props.dataField);
  }

  handleFilter(value, type) {
    this.props.filter.emitter.handleFilter(this.props.dataField, value, type);
  }

  getFilters() {
    switch (this.props.filter.type) {
    case Const.FILTER_TYPE.TEXT: {
      return (
        <TextFilter ref='textFilter' { ...this.props.filter }
          columnName={ this.props.children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.REGEX: {
      return (
        <RegexFilter ref='regexFilter' { ...this.props.filter }
          columnName={ this.props.children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.SELECT: {
      return (
        <SelectFilter ref='selectFilter' { ...this.props.filter }
          columnName={ this.props.children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.NUMBER: {
      return (
        <NumberFilter ref='numberFilter' { ...this.props.filter }
          columnName={ this.props.children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.DATE: {
      return (
        <DateFilter ref='dateFilter' { ...this.props.filter }
          columnName={ this.props.children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.CUSTOM: {
      return this.props.filter.getElement(this.handleFilter,
          this.props.filter.customFilterParameters);
    }
    }
  }

  componentDidMount() {
    this.refs['header-col'].setAttribute('data-field', this.props.dataField);
  }

  render() {
    let defaultCaret;
    const {
      dataAlign,
      dataField,
      headerAlign,
      hidden,
      sort,
      dataSort,
      sortIndicator,
      children,
      caretRender,
      className
    } = this.props;
    const thStyle = {
      textAlign: headerAlign || dataAlign,
      display: hidden ? 'none' : null
    };
    if (sortIndicator) {
      defaultCaret = (!dataSort) ? null : (

      );
    }
    let sortCaret = sort ? Util.renderReactSortCaret(sort) : defaultCaret;
    if (caretRender) {
      sortCaret = caretRender(sort, dataField);
    }
    const classes = classSet(
      typeof className === 'function' ? className() : className,
      dataSort ? 'sort-column' : '');

    const title = typeof children === 'string' ? { title: children } : null;
    return (

        { children }{ sortCaret }

 e.stopPropagation() }>
          { this.props.filter ? this.getFilters() : null }

    );
  }

  cleanFiltered() {
    if (this.props.filter === undefined) {
      return;
    }

    switch (this.props.filter.type) {
    case Const.FILTER_TYPE.TEXT: {
      this.refs.textFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.REGEX: {
      this.refs.regexFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.SELECT: {
      this.refs.selectFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.NUMBER: {
      this.refs.numberFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.DATE: {
      this.refs.dateFilter.cleanFiltered();
      break;
    }
    }
  }

  applyFilter(val) {
    if (this.props.filter === undefined) return;
    switch (this.props.filter.type) {
    case Const.FILTER_TYPE.TEXT: {
      this.refs.textFilter.applyFilter(val);
      break;
    }
    case Const.FILTER_TYPE.REGEX: {
      this.refs.regexFilter.applyFilter(val);
      break;
    }
    case Const.FILTER_TYPE.SELECT: {
      this.refs.selectFilter.applyFilter(val);
      break;
    }
    case Const.FILTER_TYPE.NUMBER: {
      this.refs.numberFilter.applyFilter(val);
      break;
    }
    case Const.FILTER_TYPE.DATE: {
      this.refs.dateFilter.applyFilter(val);
      break;
    }
    }
  }
}

const filterTypeArray = [];
for (const key in Const.FILTER_TYPE) {
  filterTypeArray.push(Const.FILTER_TYPE[key]);
}

TableHeaderColumn.propTypes = {
  dataField: PropTypes.string,
  dataAlign: PropTypes.string,
  headerAlign: PropTypes.string,
  dataSort: PropTypes.bool,
  onSort: PropTypes.func,
  dataFormat: PropTypes.func,
  csvFormat: PropTypes.func,
  csvHeader: PropTypes.string,
  isKey: PropTypes.bool,
  editable: PropTypes.any,
  hidden: PropTypes.bool,
  hiddenOnInsert: PropTypes.bool,
  searchable: PropTypes.bool,
  className: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.func
  ]),
  width: PropTypes.string,
  sortFunc: PropTypes.func,
  sortFuncExtraData: PropTypes.any,
  columnClassName: PropTypes.any,
  columnTitle: PropTypes.bool,
  filterFormatted: PropTypes.bool,
  filterValue: PropTypes.func,
  sort: PropTypes.string,
  caretRender: PropTypes.func,
  formatExtraData: PropTypes.any,
  filter: PropTypes.shape({
    type: PropTypes.oneOf(filterTypeArray),
    delay: PropTypes.number,
    options: PropTypes.oneOfType([
      PropTypes.object, // for SelectFilter
      PropTypes.arrayOf(PropTypes.number) // for NumberFilter
    ]),
    numberComparators: PropTypes.arrayOf(PropTypes.string),
    emitter: PropTypes.object,
    placeholder: PropTypes.string,
    getElement: PropTypes.func,
    customFilterParameters: PropTypes.object
  }),
  sortIndicator: PropTypes.bool,
  export: PropTypes.bool
};

TableHeaderColumn.defaultProps = {
  dataAlign: 'left',
  headerAlign: undefined,
  dataSort: false,
  dataFormat: undefined,
  csvFormat: undefined,
  csvHeader: undefined,
  isKey: false,
  editable: true,
  onSort: undefined,
  hidden: false,
  hiddenOnInsert: false,
  searchable: true,
  className: '',
  columnTitle: false,
  width: null,
  sortFunc: undefined,
  columnClassName: '',
  filterFormatted: false,
  filterValue: undefined,
  sort: undefined,
  formatExtraData: undefined,
  sortFuncExtraData: undefined,
  filter: undefined,
  sortIndicator: true
};

export default TableHeaderColumn;
<commit_msg>fix #793<commit_after>/* eslint default-case: 0 */
/* eslint guard-for-in: 0 */
import React, { Component, PropTypes } from 'react';
import classSet from 'classnames';
import Const from './Const';
import Util from './util';
import DateFilter from './filters/Date';
import TextFilter from './filters/Text';
import RegexFilter from './filters/Regex';
import SelectFilter from './filters/Select';
import NumberFilter from './filters/Number';

class TableHeaderColumn extends Component {

  constructor(props) {
    super(props);
    this.handleFilter = this.handleFilter.bind(this);
  }

  handleColumnClick = () => {
    if (!this.props.dataSort) return;
    const order = this.props.sort === Const.SORT_DESC ? Const.SORT_ASC : Const.SORT_DESC;
    this.props.onSort(order, this.props.dataField);
  }

  handleFilter(value, type) {
    this.props.filter.emitter.handleFilter(this.props.dataField, value, type);
  }

  getFilters() {
    switch (this.props.filter.type) {
    case Const.FILTER_TYPE.TEXT: {
      return (
        <TextFilter ref='textFilter' { ...this.props.filter }
          columnName={ this.props.children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.REGEX: {
      return (
        <RegexFilter ref='regexFilter' { ...this.props.filter }
          columnName={ this.props.children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.SELECT: {
      return (
        <SelectFilter ref='selectFilter' { ...this.props.filter }
          columnName={ this.props.children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.NUMBER: {
      return (
        <NumberFilter ref='numberFilter' { ...this.props.filter }
          columnName={ this.props.children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.DATE: {
      return (
        <DateFilter ref='dateFilter' { ...this.props.filter }
          columnName={ this.props.children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.CUSTOM: {
      return this.props.filter.getElement(this.handleFilter,
          this.props.filter.customFilterParameters);
    }
    }
  }

  componentDidMount() {
    this.refs['header-col'].setAttribute('data-field', this.props.dataField);
  }

  render() {
    let defaultCaret;
    const {
      dataAlign,
      dataField,
      headerAlign,
      headerTitle,
      hidden,
      sort,
      dataSort,
      sortIndicator,
      children,
      caretRender,
      className
    } = this.props;
    const thStyle = {
      textAlign: headerAlign || dataAlign,
      display: hidden ? 'none' : null
    };
    if (sortIndicator) {
      defaultCaret = (!dataSort) ? null : (

      );
    }
    let sortCaret = sort ? Util.renderReactSortCaret(sort) : defaultCaret;
    if (caretRender) {
      sortCaret = caretRender(sort, dataField);
    }
    const classes = classSet(
      typeof className === 'function' ? className() : className,
      dataSort ? 'sort-column' : '');

    const title = headerTitle && typeof children === 'string' ? { title: children } : null;
    return (

        { children }{ sortCaret }

 e.stopPropagation() }>
          { this.props.filter ? this.getFilters() : null }

    );
  }

  cleanFiltered() {
    if (this.props.filter === undefined) {
      return;
    }

    switch (this.props.filter.type) {
    case Const.FILTER_TYPE.TEXT: {
      this.refs.textFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.REGEX: {
      this.refs.regexFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.SELECT: {
      this.refs.selectFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.NUMBER: {
      this.refs.numberFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.DATE: {
      this.refs.dateFilter.cleanFiltered();
      break;
    }
    }
  }

  applyFilter(val) {
    if (this.props.filter === undefined) return;
    switch (this.props.filter.type) {
    case Const.FILTER_TYPE.TEXT: {
      this.refs.textFilter.applyFilter(val);
      break;
    }
    case Const.FILTER_TYPE.REGEX: {
      this.refs.regexFilter.applyFilter(val);
      break;
    }
    case Const.FILTER_TYPE.SELECT: {
      this.refs.selectFilter.applyFilter(val);
      break;
    }
    case Const.FILTER_TYPE.NUMBER: {
      this.refs.numberFilter.applyFilter(val);
      break;
    }
    case Const.FILTER_TYPE.DATE: {
      this.refs.dateFilter.applyFilter(val);
      break;
    }
    }
  }
}

const filterTypeArray = [];
for (const key in Const.FILTER_TYPE) {
  filterTypeArray.push(Const.FILTER_TYPE[key]);
}

TableHeaderColumn.propTypes = {
  dataField: PropTypes.string,
  dataAlign: PropTypes.string,
  headerAlign: PropTypes.string,
  headerTitle: PropTypes.bool,
  dataSort: PropTypes.bool,
  onSort: PropTypes.func,
  dataFormat: PropTypes.func,
  csvFormat: PropTypes.func,
  csvHeader: PropTypes.string,
  isKey: PropTypes.bool,
  editable: PropTypes.any,
  hidden: PropTypes.bool,
  hiddenOnInsert: PropTypes.bool,
  searchable: PropTypes.bool,
  className: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.func
  ]),
  width: PropTypes.string,
  sortFunc: PropTypes.func,
  sortFuncExtraData: PropTypes.any,
  columnClassName: PropTypes.any,
  columnTitle: PropTypes.bool,
  filterFormatted: PropTypes.bool,
  filterValue: PropTypes.func,
  sort: PropTypes.string,
  caretRender: PropTypes.func,
  formatExtraData: PropTypes.any,
  filter: PropTypes.shape({
    type: PropTypes.oneOf(filterTypeArray),
    delay: PropTypes.number,
    options: PropTypes.oneOfType([
      PropTypes.object, // for SelectFilter
      PropTypes.arrayOf(PropTypes.number) // for NumberFilter
    ]),
    numberComparators: PropTypes.arrayOf(PropTypes.string),
    emitter: PropTypes.object,
    placeholder: PropTypes.string,
    getElement: PropTypes.func,
    customFilterParameters: PropTypes.object
  }),
  sortIndicator: PropTypes.bool,
  export: PropTypes.bool
};

TableHeaderColumn.defaultProps = {
  dataAlign: 'left',
  headerAlign: undefined,
  headerTitle: true,
  dataSort: false,
  dataFormat: undefined,
  csvFormat: undefined,
  csvHeader: undefined,
  isKey: false,
  editable: true,
  onSort: undefined,
  hidden: false,
  hiddenOnInsert: false,
  searchable: true,
  className: '',
  columnTitle: false,
  width: null,
  sortFunc: undefined,
  columnClassName: '',
  filterFormatted: false,
  filterValue: undefined,
  sort: undefined,
  formatExtraData: undefined,
  sortFuncExtraData: undefined,
  filter: undefined,
  sortIndicator: true
};

export default TableHeaderColumn;
<|endoftext|>


<commit_before>var test = require('tape')
    , streamify = require('..')
    , concat = require('concat-stream')
;

test('immutable', function(t) {
    var s, a = [1, 2, 3, 4, 5];

    if (Object && 'function' === typeof(Object.observe)) {
        // will produce ugly error, but also doesn't
        // cause `covert` to detect an untested line
        Object.observe(a, t.fail);
    }

    s = streamify(a);

    s.pipe(concat({encoding: 'object'}, function(res) {
        t.equal(1, arguments.length, 'concat returns 1 arg');
        t.deepEqual(a, res, 'result array matches input');
        t.end();
    }));
});
<commit_msg>test/4.js :: covert no longer needs the hack<commit_after>var test = require('tape')
    , streamify = require('..')
    , concat = require('concat-stream')
;

test('immutable', function(t) {
    var s, a = [1, 2, 3, 4, 5];

    s = streamify(a);
    s.pipe(concat({encoding: 'object'}, function(res) {
        t.equal(1, arguments.length, 'concat returns 1 arg');
        t.deepEqual(a, res, 'result array matches input');
        t.end();
    }));
});
<|endoftext|>


<commit_before>$(document).ready(function() {
  var prelingerData;

  $("#welcome").on("submit", function(event) {
    event.preventDefault();
    // because the preventDefault is preventing the submission
    // of the data, we had to create formData to catch
    // what the search query is supposed to be.
    var formData = $(event.target).serialize()
    console.log("Default prevented")
    $("#video").empty()

    var request = $.ajax({
      url: "/new_search",
      type: 'POST',
      data: formData,
      dataType: 'json'
    });
    console.log('test')
    request.done(function(response){
      if (response.match === true){
        console.log("we're done!")
        $("#video").append(addVideo(response))
      } else {
        for (i = 0;i < response.film.length; i++){
        // $(response).each(function(index, element) {
        //   console.log(response.film[index].title)
        //   // $(response.film[index].title).each(function(index, element){
          $("#video").append(listSearchResults(response.film[i].title))
        //   $("#video").append(listSearchResults(response.film[index].title))
        //     // $("#video").append(listSearchResults(element))
        //   // })
        // })
        // )
        }
      }
    });

  });

});

var addVideo = function(data){
  var video = '<video  style="width:100%;height:100%;" controls>'
    + '<source src="https://archive.org/download/'+ data.identifier +'/'+ data.identifier +'_512kb.mp4">'
    + '<source src="https://archive.org/download/'+ data.identifier +'/'+ data.identifier +'.ogv">'
    + '</video>'
  return video
}

var listSearchResults = function(data){
  var searchResults = '
' + data + '
'
  return searchResults
}

<commit_msg>work on click video list<commit_after>$(document).ready(function() {
  var prelingerData;

  $("#welcome").on("submit", function(event) {
    event.preventDefault();
    // because the preventDefault is preventing the submission
    // of the data, we had to create formData to catch
    // what the search query is supposed to be.
    var formData = $(event.target).serialize()
    console.log("Default prevented")
    $("#video").empty()

    var request = $.ajax({
      url: "/new_search",
      type: 'POST',
      data: formData,
      dataType: 'json'
    });
    console.log('test')
    request.done(function(response){
      if (response.match === true){
        console.log("we're done!")
        $("#video").append(addVideo(response))
      } else {
        for (i = 0;i < response.film.length; i++){
        // $(response).each(function(index, element) {
        //   console.log(response.film[index].title)
        //   // $(response.film[index].title).each(function(index, element){
          $("#video").append(listSearchResults(response.film[i]))
        //   $("#video").append(listSearchResults(response.film[index].title))
        //     // $("#video").append(listSearchResults(element))
        //   // })
        // })
        // )
        }
      }
    });

  });

});

var addVideo = function(data){
  var video = '<video  style="width:100%;height:100%;" controls>'
    + '<source src="https://archive.org/download/'+ data.identifier +'/'+ data.identifier +'_512kb.mp4">'
    + '<source src="https://archive.org/download/'+ data.identifier +'/'+ data.identifier +'.ogv">'
    + '</video>'
  return video
}

var listSearchResults = function(data){
  var searchResults = '
' + data.title + '
'
  return searchResults
  playSearchListVideo()
}

var playSearchListVideo = function(filmData){
  $("#video").empty()
  $("h3#video").select(function(){
    $("#video").append(addVideo(filmData))
  })

}
<|endoftext|>


<commit_before>// to allow for download button to be introduced via beta feature
$('a.save').click(function (event) {
  event.preventDefault();

  analytics.milestone();
  // if save is disabled, hitting save will trigger a reload
  saveCode('save', jsbin.saveDisabled === true ? false : true);

  return false;
});

var saveChecksum = sessionStorage.getItem('checksum') || false;

// only start live saving it they're allowed to (whereas save is disabled if they're following)
if (!jsbin.saveDisabled) {
  $document.bind('jsbinReady', function () {
    $('.code.panel .label .name').append('Saved');

    var savingLabels = {
      html: $('.panel.html .name span'),
      javascript: $('.panel.javascript .name span'),
      css: $('.panel.css .name span')
    };

    $document.bind('codeChange', function (event, data) {
      // savingLabels[data.panelId].text('Saving');
      if (savingLabels[data.panelId]) {
        savingLabels[data.panelId].css({ 'opacity': 0 }).stop(true, true);
      }
    });

    $document.bind('saveComplete', throttle(function (event, data) {
      // show saved, then revert out animation
      savingLabels[data.panelId].stop(true, true).animate({ 'opacity': 1 }, 100).delay(1200).animate({
        'opacity': '0'
      }, 500);
    }, 500));

    var stream = false;

    if (jsbin.state.stream && window.WebSocket) {
      stream = new WebSocket('ws://' + window.location.origin + '/update');
    }

    $document.bind('codeChange', throttle(function (event, data) {
      if (!data.panelId) return;

      var panelId = data.panelId;

      if (!saveChecksum) {
        // create the bin and when the response comes back update the url
        saveCode('save', true);
      } else {
        $.ajax({
          url: jsbin.getURL() + '/save',
          data: {
            code: jsbin.state.code,
            revision: jsbin.state.revision,
            method: 'update',
            panel: data.panelId,
            content: editors[data.panelId].getCode(),
            checksum: saveChecksum
          },
          type: 'post',
          dataType: 'json',
          success: function (data) {
            $document.trigger('saveComplete', { panelId: panelId });
            if (data.error) {
              saveCode('save', true, function (data) {
                // savedAlready = data.checksum;
              });
            }
          },
          error: function () {
            console.log('update error');
          }
        });
      }
    }, 250));
  });
}

$('a.clone').click(function (event) {
  event.preventDefault();

  // save our panel layout - assumes our user is happy with this layout
  jsbin.panels.save();
  analytics.clone();

  var $form = setupform('save,new');
  $form.submit();

  return false;
});

function setupform(method) {
var $form = $('form#saveform').empty()
    .append('<input type="hidden" name="javascript" />')
    .append('<input type="hidden" name="html" />')
    .append('<input type="hidden" name="css" />')
    .append('<input type="hidden" name="method" />')
    .append('<input type="hidden" name="_csrf" value="' + jsbin.state.token + '" />');

  $form.find('input[name=javascript]').val(editors.javascript.getCode());
  $form.find('input[name=css]').val(editors.css.getCode());
  $form.find('input[name=html]').val(editors.html.getCode());
  $form.find('input[name=method]').val(method);

  return $form;
}

function pad(n){
  return n<10 ? '0'+n : n
}

function ISODateString(d){
  return d.getFullYear()+'-'
    + pad(d.getMonth()+1)+'-'
    + pad(d.getDate())+'T'
    + pad(d.getHours())+':'
    + pad(d.getMinutes())+':'
    + pad(d.getSeconds())+'Z'
}

function saveCode(method, ajax, ajaxCallback) {
  // create form and post to it
  var $form = setupform(method);
  // save our panel layout - assumes our user is happy with this layout
  jsbin.panels.save();
  jsbin.panels.saveOnExit = true;

  if (ajax) {
    $.ajax({
      url: $form.attr('action'),
      data: $form.serialize(),
      dataType: 'json',
      type: 'post',
      success: function (data) {
        var $binGroup,
            edit;

        $form.attr('action', data.url + '/save');
        ajaxCallback && ajaxCallback(data);

        sessionStorage.setItem('checksum', data.checksum);
        saveChecksum = data.checksum;

        jsbin.state.code = data.code;
        jsbin.state.revision = data.revision;

        $binGroup = $('#history tr[data-url="' + jsbin.getURL() + '"]');
        edit = data.edit.replace(location.protocol + '//' + window.location.hostname, '') + window.location.search;
        $binGroup.find('td.url a span.first').removeClass('first');
        $binGroup.before('
' + data.code + '/' + data.revision + '/	Just now	' + data.title + '
');

        $('#share').find('input').val(function () {
          return jsbin.getURL() + this.getAttribute('data-path');
        }).closest('.menu').removeClass('hidden');

        $('#jsbinurl').attr('href', jsbin.getURL()).removeClass('hidden');

        if (window.history && window.history.pushState) {
          window.history.pushState(null, edit, edit);
          sessionStorage.setItem('url', jsbin.getURL());
        } else {
          // window.location = data.edit;
        }
      },
      error: function () {

      }
    });
  } else {
    $form.submit();
  }
}
<commit_msg>Force Accept header in all save requests<commit_after>// to allow for download button to be introduced via beta feature
$('a.save').click(function (event) {
  event.preventDefault();

  analytics.milestone();
  // if save is disabled, hitting save will trigger a reload
  saveCode('save', jsbin.saveDisabled === true ? false : true);

  return false;
});

var saveChecksum = sessionStorage.getItem('checksum') || false;

// only start live saving it they're allowed to (whereas save is disabled if they're following)
if (!jsbin.saveDisabled) {
  $document.bind('jsbinReady', function () {
    $('.code.panel .label .name').append('Saved');

    var savingLabels = {
      html: $('.panel.html .name span'),
      javascript: $('.panel.javascript .name span'),
      css: $('.panel.css .name span')
    };

    $document.bind('codeChange', function (event, data) {
      // savingLabels[data.panelId].text('Saving');
      if (savingLabels[data.panelId]) {
        savingLabels[data.panelId].css({ 'opacity': 0 }).stop(true, true);
      }
    });

    $document.bind('saveComplete', throttle(function (event, data) {
      // show saved, then revert out animation
      savingLabels[data.panelId].stop(true, true).animate({ 'opacity': 1 }, 100).delay(1200).animate({
        'opacity': '0'
      }, 500);
    }, 500));

    var stream = false;

    if (jsbin.state.stream && window.WebSocket) {
      stream = new WebSocket('ws://' + window.location.origin + '/update');
    }

    $document.bind('codeChange', throttle(function (event, data) {
      if (!data.panelId) return;

      var panelId = data.panelId;

      if (!saveChecksum) {
        // create the bin and when the response comes back update the url
        saveCode('save', true);
      } else {
        $.ajax({
          url: jsbin.getURL() + '/save',
          data: {
            code: jsbin.state.code,
            revision: jsbin.state.revision,
            method: 'update',
            panel: data.panelId,
            content: editors[data.panelId].getCode(),
            checksum: saveChecksum
          },
          type: 'post',
          dataType: 'json',
          headers: {'Accept': 'application/json'},
          success: function (data) {
            $document.trigger('saveComplete', { panelId: panelId });
            if (data.error) {
              saveCode('save', true, function (data) {
                // savedAlready = data.checksum;
              });
            }
          },
          error: function () {
            console.log('update error');
          }
        });
      }
    }, 250));
  });
}

$('a.clone').click(function (event) {
  event.preventDefault();

  // save our panel layout - assumes our user is happy with this layout
  jsbin.panels.save();
  analytics.clone();

  var $form = setupform('save,new');
  $form.submit();

  return false;
});

function setupform(method) {
var $form = $('form#saveform').empty()
    .append('<input type="hidden" name="javascript" />')
    .append('<input type="hidden" name="html" />')
    .append('<input type="hidden" name="css" />')
    .append('<input type="hidden" name="method" />')
    .append('<input type="hidden" name="_csrf" value="' + jsbin.state.token + '" />');

  $form.find('input[name=javascript]').val(editors.javascript.getCode());
  $form.find('input[name=css]').val(editors.css.getCode());
  $form.find('input[name=html]').val(editors.html.getCode());
  $form.find('input[name=method]').val(method);

  return $form;
}

function pad(n){
  return n<10 ? '0'+n : n
}

function ISODateString(d){
  return d.getFullYear()+'-'
    + pad(d.getMonth()+1)+'-'
    + pad(d.getDate())+'T'
    + pad(d.getHours())+':'
    + pad(d.getMinutes())+':'
    + pad(d.getSeconds())+'Z'
}

function saveCode(method, ajax, ajaxCallback) {
  // create form and post to it
  var $form = setupform(method);
  // save our panel layout - assumes our user is happy with this layout
  jsbin.panels.save();
  jsbin.panels.saveOnExit = true;

  if (ajax) {
    $.ajax({
      url: $form.attr('action'),
      data: $form.serialize(),
      dataType: 'json',
      type: 'post',
      headers: {'Accept': 'application/json'},
      success: function (data) {
        var $binGroup,
            edit;

        $form.attr('action', data.url + '/save');
        ajaxCallback && ajaxCallback(data);

        sessionStorage.setItem('checksum', data.checksum);
        saveChecksum = data.checksum;

        jsbin.state.code = data.code;
        jsbin.state.revision = data.revision;

        $binGroup = $('#history tr[data-url="' + jsbin.getURL() + '"]');
        edit = data.edit.replace(location.protocol + '//' + window.location.hostname, '') + window.location.search;
        $binGroup.find('td.url a span.first').removeClass('first');
        $binGroup.before('
' + data.code + '/' + data.revision + '/	Just now	' + data.title + '
');

        $('#share').find('input').val(function () {
          return jsbin.getURL() + this.getAttribute('data-path');
        }).closest('.menu').removeClass('hidden');

        $('#jsbinurl').attr('href', jsbin.getURL()).removeClass('hidden');

        if (window.history && window.history.pushState) {
          window.history.pushState(null, edit, edit);
          sessionStorage.setItem('url', jsbin.getURL());
        } else {
          // window.location = data.edit;
        }
      },
      error: function () {

      }
    });
  } else {
    $form.submit();
  }
}
<|endoftext|>


<commit_before>var todoControllers = angular.module('todoControllers', []);

todoControllers.controller('TaskCtrl', ['$scope', function($scope) {
    $scope.todos = [];
}]);
<commit_msg>fixed bug: app name wasn't correct, so angular threw an exception<commit_after>var todoControllers = angular.module('todoApp', []);

todoControllers.controller('TaskCtrl', ['$scope', function($scope) {
    $scope.todos = [];
}]);
<|endoftext|>


<commit_before>angular.module('elke')

.controller('AppCtrl', [
  '$scope',
  '$feathers',
  'App',
  'Elke',
  function($scope, $feathers, App, Elke) {
    $scope.user = false;
    $scope.$watch(function() {
      return $feathers.get('user');
    }, function(user) {
      $scope.user = user;
    });
    $scope.logout = $feathers.logout;
    // Store app data in global service
    for(var key in App.data) {
      Elke.set(key, App.data[key]);
    }
    // Redirect to registration if app not active (no users found in db)
    if(!Elke.get('active')) {
      $state.go('main.auth', {register: true});
    }
  }
])

.controller('HomeCtrl', [
  '$scope',
  '$state',
  '$feathers',
  'Elke',
  'Streamings',
  function($scope, $state, $feathers, Elke, Streamings) {
    var streamingService = $feathers.service('streamings');
    $scope.streamings = Streamings.data;
    streamingService.on('created', function(data) {
      $scope.$apply(function() {
        $scope.streamings.push(data);
      });
    });
    streamingService.on('removed', function(data) {
      $scope.$apply(function() {
        $scope.streamings = _.filter($scope.streamings, function(streaming) {
          return streaming.id !== data.id;
        });
      });
    });
    streamingService.on('updated', function(data) {
      $scope.$apply(function() {
        $scope.streamings.forEach(function(streaming, i) {
          if(streaming.id == data.id) {
            $scope.streamings[i] = data;
          }
        });
      });
    });
    streamingService.on('patched', function(data) {
      $scope.$apply(function() {
        $scope.streamings.forEach(function(streaming, i) {
          if(streaming.id == data.id) {
            $scope.streamings[i] = data;
          }
        });
      });
    });
  }
])

.controller('AuthCtrl', [
  '$scope',
  '$feathers',
  '$state',
  '$stateParams',
  'Elke',
  function($scope, $feathers, $state, $stateParams, Elke) {
    var userService = $feathers.service('users');
    $scope.active = Elke.get('active');
    if($stateParams.register) {
      $scope.registration = true;
    }
    $scope.credentials = {};
    var auth = function() {
      $feathers.authenticate({
        type: 'local',
        email: $scope.credentials.email,
        password: $scope.credentials.password
      }).then(function(res) {
        $state.go('main.home', {}, {reload:true});
      }).catch(function(err) {
        console.error('Error authenticating', err);
      });
    };
    $scope.auth = function() {
      if($scope.registration) {
        userService.create($scope.credentials)
          .then(auth)
          .catch(function(err) {
            console.error('Error creating user', err);
          });
      } else {
        auth();
      }
    }
  }
])

.controller('StreamCtrl', [
  '$scope',
  '$state',
  '$feathers',
  'Streaming',
  function($scope, $state, $feathers, Streaming) {
    var service = $feathers.service('streamings');
    $scope.streaming = angular.copy(Streaming);
    $scope.save = function() {
      if(Streaming.id) {
        service.patch(Streaming.id, $scope.streaming).then(function(streaming) {
          $scope.streaming = streaming;
        });
      } else {
        service.create($scope.streaming).then(function(res) {
          $state.go('main.home');
        }).catch(function(err) {
          console.error('Error creating streaming', err);
        });
      }
    }
    $scope.createStream = function() {
    };
  }
]);
<commit_msg>remove garbage<commit_after>angular.module('elke')

.controller('AppCtrl', [
  '$scope',
  '$feathers',
  'App',
  'Elke',
  function($scope, $feathers, App, Elke) {
    $scope.user = false;
    $scope.$watch(function() {
      return $feathers.get('user');
    }, function(user) {
      $scope.user = user;
    });
    $scope.logout = $feathers.logout;
    // Store app data in global service
    for(var key in App.data) {
      Elke.set(key, App.data[key]);
    }
    // Redirect to registration if app not active (no users found in db)
    if(!Elke.get('active')) {
      $state.go('main.auth', {register: true});
    }
  }
])

.controller('HomeCtrl', [
  '$scope',
  '$state',
  '$feathers',
  'Elke',
  'Streamings',
  function($scope, $state, $feathers, Elke, Streamings) {
    var streamingService = $feathers.service('streamings');
    $scope.streamings = Streamings.data;
    streamingService.on('created', function(data) {
      $scope.$apply(function() {
        $scope.streamings.push(data);
      });
    });
    streamingService.on('removed', function(data) {
      $scope.$apply(function() {
        $scope.streamings = _.filter($scope.streamings, function(streaming) {
          return streaming.id !== data.id;
        });
      });
    });
    streamingService.on('updated', function(data) {
      $scope.$apply(function() {
        $scope.streamings.forEach(function(streaming, i) {
          if(streaming.id == data.id) {
            $scope.streamings[i] = data;
          }
        });
      });
    });
    streamingService.on('patched', function(data) {
      $scope.$apply(function() {
        $scope.streamings.forEach(function(streaming, i) {
          if(streaming.id == data.id) {
            $scope.streamings[i] = data;
          }
        });
      });
    });
  }
])

.controller('AuthCtrl', [
  '$scope',
  '$feathers',
  '$state',
  '$stateParams',
  'Elke',
  function($scope, $feathers, $state, $stateParams, Elke) {
    var userService = $feathers.service('users');
    $scope.active = Elke.get('active');
    if($stateParams.register) {
      $scope.registration = true;
    }
    $scope.credentials = {};
    var auth = function() {
      $feathers.authenticate({
        type: 'local',
        email: $scope.credentials.email,
        password: $scope.credentials.password
      }).then(function(res) {
        $state.go('main.home', {}, {reload:true});
      }).catch(function(err) {
        console.error('Error authenticating', err);
      });
    };
    $scope.auth = function() {
      if($scope.registration) {
        userService.create($scope.credentials)
          .then(auth)
          .catch(function(err) {
            console.error('Error creating user', err);
          });
      } else {
        auth();
      }
    }
  }
])

.controller('StreamCtrl', [
  '$scope',
  '$state',
  '$feathers',
  'Streaming',
  function($scope, $state, $feathers, Streaming) {
    var service = $feathers.service('streamings');
    $scope.streaming = angular.copy(Streaming);
    $scope.save = function() {
      if(Streaming.id) {
        service.patch(Streaming.id, $scope.streaming).then(function(streaming) {
          $scope.streaming = streaming;
        });
      } else {
        service.create($scope.streaming).then(function(res) {
          $state.go('main.home');
        }).catch(function(err) {
          console.error('Error creating streaming', err);
        });
      }
    };
  }
]);
<|endoftext|>


<commit_before>'use strict';

/* Controllers */

function HangDownListCntr($scope) {
  $scope.apiLive = false;
  var _apiRequiredFunction = function(innerFunction){ return function(){
    if ($scope.apiLive) innerFunction.apply(this, arguments);
  }; };

  $scope.topics = [];

  var activeTopicId = null;
  $scope.activeTopicIndex = 0;
  $scope.currentUser = null;

  var resetActiveTopicIndex = function(){
    var activeIndex = 0;

    for (var i = $scope.topics.length - 1; i >= 0; i--) {
      if ($scope.topics[i].id == activeTopicId) {
        activeIndex = i;
        break;
      }
    };

    $scope.activeTopicIndex = activeIndex;
  };

  $scope.topicSortConfig = {
    stop: function(e, ui){
      // First, make sure that the active index is correct.
      resetActiveTopicIndex();

      // Push state.
      pushSharedState();
    }
  };

  $scope.goToTopic = function(topicId){
    // TODO: If we keep this function around, make sure to check for non-existent IDs.
    activeTopicId = topicId;
    resetActiveTopicIndex();
  };

  $scope.regressTopic = _apiRequiredFunction(function(){
    // Only regress if the topic is not already the first.
    if ($scope.activeTopicIndex > 0) {
      activeTopicId = $scope.topics[$scope.activeTopicIndex-1].id;
      resetActiveTopicIndex();

      // Submit changes to Google.
      pushSharedState();
    }
  });

  $scope.advanceTopic = _apiRequiredFunction(function(){
    // Only advance if the topic is not already the last.
    if ($scope.activeTopicIndex < $scope.topics.length - 1) {
      activeTopicId = $scope.topics[$scope.activeTopicIndex+1].id;
      resetActiveTopicIndex();

      // Submit changes to Google.
      pushSharedState();
    }
  });

  var _topicIndex = 0;
  var createTopic = function(newLabel){
    return {
      id: new Date().getTime() + '-' + _topicIndex++,         // Still don't like this way of doing it.
      label: newLabel,
      creator: $scope.currentUser.person.displayName
    };
  };

  $scope.newTopicBuffer = '';
  $scope.addNewTopic = _apiRequiredFunction(function(){
    // If there is no topic set, bail.
    if (!$scope.newTopicBuffer.length) return;

    // If there are any ';;', break the topic into multiple topics.
    var newTopics = $scope.newTopicBuffer.split(';;');
    $scope.newTopicBuffer = '';

    // Push add all topics.
    for (var i = 0; i < newTopics.length; i++) {
      $scope.topics.push(createTopic(newTopics[i].trim()));
    };

    // If there is no active topic yet, set the active topic.
    if (activeTopicId == null) {
      activeTopicId = $scope.topics[0].id;
      $scope.activeTopicIndex = 0;
    }

    // Submit changes to Google.
    pushSharedState();
  });

  $scope.deleteTopic = _apiRequiredFunction(function(deleteTopicId){
    // If we're about to delete the current topic,
    // determine the next topic to select.
    if (deleteTopicId == activeTopicId) {
      // If the next topic exists, use it.
      if ($scope.topics[$scope.activeTopicIndex+1]) activeTopicId = $scope.topics[$scope.activeTopicIndex+1].id;
      else if ($scope.topics[$scope.activeTopicIndex-1]) activeTopicId = $scope.topics[$scope.activeTopicIndex-1].id;
      else activeTopicId = null;
    }

    // Filter the array.
    var initialLength = $scope.topics.length;
    $scope.topics = $scope.topics.filter(function(topic){ return topic.id != deleteTopicId; });

    // If the length hasn't changed, we're done.
    if (initialLength == $scope.topics.length) return;

    // Reset the active index.
    resetActiveTopicIndex();

    // Push state.
    pushSharedState();
  });

  var initGapiModel = function(){
    // Create expected values in the shared model.
    gapi.hangout.data.submitDelta({
      activeTopicId: JSON.stringify(null),
      topics: JSON.stringify([])
    });
  };

  var pushSharedState = function(stateDelta){
    // If there was no state provided, just push the whole system.
    if (!stateDelta) {
      stateDelta = {
        activeTopicId: JSON.stringify(activeTopicId),
        topics: JSON.stringify($scope.topics)
      };
    }

    // Next, set the current updater.
    stateDelta.modifier = $scope.currentUser.id;

    // Then submit the delta to GAPI.
    gapi.hangout.data.submitDelta( stateDelta );
  };

  var processStateUpdate = function(stateChangedEvent){
    // If the current shared state update was self-originated, skip.
    if (stateChangedEvent.state.modifier == $scope.currentUser.id) return;

    // Otherwise, apply the shared state.
    applySharedState(stateChangedEvent.state);
  };

  var applySharedState = function(newState){
    // Update the internal model.
    $scope.topics = JSON.parse(newState.topics);
    activeTopicId = JSON.parse(newState.activeTopicId);

    // Make sure that the activeTopicIndex is updated.
    resetActiveTopicIndex();

    $scope.$apply();  // Have to do this to force the view to update.
  };

  // Add a callback to initialize gAPI elements.
  gapi.hangout.onApiReady.add(function(eventObj){
    var initialState = gapi.hangout.data.getState();

    // If the state has not been initialized, do that now.
    if (initialState.activeTopicId == undefined) initGapiModel();
    // Otherwise, update internal state with shared state.
    else applySharedState(initialState);

    // Set up internal model to work with gapi.
    $scope.apiLive = true;
    $scope.currentUser = gapi.hangout.getLocalParticipant();

    // Install the event handler for a change in model state.
    gapi.hangout.data.onStateChanged.add(processStateUpdate);
  });
}

//HangDownListCntr.$inject = ['$scope'];
<commit_msg>Make the topic IDs a little more unique.<commit_after>'use strict';

/* Controllers */

function HangDownListCntr($scope) {
  $scope.apiLive = false;
  var _apiRequiredFunction = function(innerFunction){ return function(){
    if ($scope.apiLive) innerFunction.apply(this, arguments);
  }; };

  $scope.topics = [];

  var activeTopicId = null;
  $scope.activeTopicIndex = 0;
  $scope.currentUser = null;

  var resetActiveTopicIndex = function(){
    var activeIndex = 0;

    for (var i = $scope.topics.length - 1; i >= 0; i--) {
      if ($scope.topics[i].id == activeTopicId) {
        activeIndex = i;
        break;
      }
    };

    $scope.activeTopicIndex = activeIndex;
  };

  $scope.topicSortConfig = {
    stop: function(e, ui){
      // First, make sure that the active index is correct.
      resetActiveTopicIndex();

      // Push state.
      pushSharedState();
    }
  };

  $scope.goToTopic = function(topicId){
    // TODO: If we keep this function around, make sure to check for non-existent IDs.
    activeTopicId = topicId;
    resetActiveTopicIndex();
  };

  $scope.regressTopic = _apiRequiredFunction(function(){
    // Only regress if the topic is not already the first.
    if ($scope.activeTopicIndex > 0) {
      activeTopicId = $scope.topics[$scope.activeTopicIndex-1].id;
      resetActiveTopicIndex();

      // Submit changes to Google.
      pushSharedState();
    }
  });

  $scope.advanceTopic = _apiRequiredFunction(function(){
    // Only advance if the topic is not already the last.
    if ($scope.activeTopicIndex < $scope.topics.length - 1) {
      activeTopicId = $scope.topics[$scope.activeTopicIndex+1].id;
      resetActiveTopicIndex();

      // Submit changes to Google.
      pushSharedState();
    }
  });

  var _topicIndex = 0;
  var createTopic = function(newLabel){
    return {
      id: new Date().getTime() + '-' + $scope.currentUser.id + '-' + _topicIndex++,
      label: newLabel,
      creator: $scope.currentUser.person.displayName
    };
  };

  $scope.newTopicBuffer = '';
  $scope.addNewTopic = _apiRequiredFunction(function(){
    // If there is no topic set, bail.
    if (!$scope.newTopicBuffer.length) return;

    // If there are any ';;', break the topic into multiple topics.
    var newTopics = $scope.newTopicBuffer.split(';;');
    $scope.newTopicBuffer = '';

    // Push add all topics.
    for (var i = 0; i < newTopics.length; i++) {
      $scope.topics.push(createTopic(newTopics[i].trim()));
    };

    // If there is no active topic yet, set the active topic.
    if (activeTopicId == null) {
      activeTopicId = $scope.topics[0].id;
      $scope.activeTopicIndex = 0;
    }

    // Submit changes to Google.
    pushSharedState();
  });

  $scope.deleteTopic = _apiRequiredFunction(function(deleteTopicId){
    // If we're about to delete the current topic,
    // determine the next topic to select.
    if (deleteTopicId == activeTopicId) {
      // If the next topic exists, use it.
      if ($scope.topics[$scope.activeTopicIndex+1]) activeTopicId = $scope.topics[$scope.activeTopicIndex+1].id;
      else if ($scope.topics[$scope.activeTopicIndex-1]) activeTopicId = $scope.topics[$scope.activeTopicIndex-1].id;
      else activeTopicId = null;
    }

    // Filter the array.
    var initialLength = $scope.topics.length;
    $scope.topics = $scope.topics.filter(function(topic){ return topic.id != deleteTopicId; });

    // If the length hasn't changed, we're done.
    if (initialLength == $scope.topics.length) return;

    // Reset the active index.
    resetActiveTopicIndex();

    // Push state.
    pushSharedState();
  });

  var initGapiModel = function(){
    // Create expected values in the shared model.
    gapi.hangout.data.submitDelta({
      activeTopicId: JSON.stringify(null),
      topics: JSON.stringify([])
    });
  };

  var pushSharedState = function(stateDelta){
    // If there was no state provided, just push the whole system.
    if (!stateDelta) {
      stateDelta = {
        activeTopicId: JSON.stringify(activeTopicId),
        topics: JSON.stringify($scope.topics)
      };
    }

    // Next, set the current updater.
    stateDelta.modifier = $scope.currentUser.id;

    // Then submit the delta to GAPI.
    gapi.hangout.data.submitDelta( stateDelta );
  };

  var processStateUpdate = function(stateChangedEvent){
    // If the current shared state update was self-originated, skip.
    if (stateChangedEvent.state.modifier == $scope.currentUser.id) return;

    // Otherwise, apply the shared state.
    applySharedState(stateChangedEvent.state);
  };

  var applySharedState = function(newState){
    // Update the internal model.
    $scope.topics = JSON.parse(newState.topics);
    activeTopicId = JSON.parse(newState.activeTopicId);

    // Make sure that the activeTopicIndex is updated.
    resetActiveTopicIndex();

    $scope.$apply();  // Have to do this to force the view to update.
  };

  // Add a callback to initialize gAPI elements.
  gapi.hangout.onApiReady.add(function(eventObj){
    var initialState = gapi.hangout.data.getState();

    // If the state has not been initialized, do that now.
    if (initialState.activeTopicId == undefined) initGapiModel();
    // Otherwise, update internal state with shared state.
    else applySharedState(initialState);

    // Set up internal model to work with gapi.
    $scope.apiLive = true;
    $scope.currentUser = gapi.hangout.getLocalParticipant();

    // Install the event handler for a change in model state.
    gapi.hangout.data.onStateChanged.add(processStateUpdate);
  });
}

//HangDownListCntr.$inject = ['$scope'];
<|endoftext|>