<commit_before>const caseless = require('caseless');
const qs = require('qs');
const fetch = require('cross-fetch');
const urlJoin = require('url-join');
require('abortcontroller-polyfill/dist/polyfill-patch-fetch');

const Interceptor = require('./interceptor');

const methods = ['get', 'head', 'post', 'put', 'del', 'options', 'patch'];

const respProperties = {
  readOnly: [
    'headers',
    'ok',
    'redirected',
    'status',
    'statusText',
    'type',
    'url',
    'bodyUsed'
  ],
  writable: ['useFinalURL'],
  callable: [
    'clone',
    'error',
    'redirect',
    'arrayBuffer',
    'blob',
    'formData',
    'json',
    'text'
  ]
};

function createFrisbeeResponse(origResp) {
  const resp = {
    originalResponse: origResp
  };

  respProperties.readOnly.forEach(prop =>
    Object.defineProperty(resp, prop, {
      value: origResp[prop]
    })
  );

  respProperties.writable.forEach(prop =>
    Object.defineProperty(resp, prop, {
      get() {
        return origResp[prop];
      },
      set(value) {
        origResp[prop] = value;
      }
    })
  );

  let callable = null;
  respProperties.callable.forEach(prop => {
    Object.defineProperty(resp, prop, {
      value: ((callable = origResp[prop]),
      typeof callable === 'function' && callable.bind(origResp))
    });
  });

  const headersObj = {};
  origResp.headers.forEach(pair => {
    headersObj[pair[0]] = pair[1];
  });
  Object.defineProperty(resp, 'headersObj', {
    value: headersObj
  });

  return resp;
}

class Frisbee {
  constructor(opts = {}) {
    this.opts = opts;

    let localAbortController;
    Object.defineProperty(this, 'abortController', {
      enumerable: false,
      get() {
        if (!localAbortController) {
          localAbortController = new AbortController();
          localAbortController.signal.addEventListener('abort', () => {
            // when this is aborted, null out the localAbortController
            // so we'll create a new one next time we need it
            localAbortController = null;
          });
        }
        return localAbortController;
      }
    });

    let localAbortTokenMap = new Map();
    Object.defineProperty(this, 'abortTokenMap', {
      enumerable: false,
      value: localAbortTokenMap
    });

    Object.defineProperty(this, 'parseErr', {
      enumerable: false,
      value:
        opts.parseErr ||
        new Error(
          `Invalid JSON received${opts.baseURI ? ` from ${opts.baseURI}` : ''}`
        )
    });

    this.headers = {
      ...opts.headers
    };

    this.arrayFormat = opts.arrayFormat || 'indices';

    this.raw = opts.raw === true;

    if (opts.auth) this.auth(opts.auth);

    methods.forEach(method => {
      this[method] = this._setup(method);
    });

    // interceptor should be initialized after methods setup
    this.interceptor = new Interceptor(this, methods);
  }

  abort(token) {
    const mapValue = this.abortTokenMap.get(token);
    if (mapValue && mapValue.abortController) {
      mapValue.abortController.abort();
    }
  }

  abortAll() {
    this.abortController.abort();
  }

  _setup(method) {
    return (originalPath = '/', originalOptions = {}) => {
      if (originalOptions && typeof originalOptions === 'object') {
        let abortController;
        if (originalOptions.abortToken) {
          // allow to use a single token to cancel multiple requests
          let mapValue = this.abortTokenMap.get(originalOptions.abortToken);
          if (!mapValue) {
            mapValue = {
              abortController: new AbortController(),
              count: 0
            };
          }
          mapValue.count++;

          this.abortTokenMap.set(originalOptions.abortToken, mapValue);
          abortController = mapValue.abortController;
        } else {
          abortController = new AbortController();
        }

        // the user has defined their own signal we won't use it directly, but we'll listen to it
        if (originalOptions.signal) {
          originalOptions.signal.addEventListener('abort',() => abortController.abort());
        }

        // abort this request whenever this.abortController.abort() gets called - a.k.a. - abortAll()
        this.abortController.signal.addEventListener('abort',() => abortController.abort());
        originalOptions.signal = abortController.signal;
      }

      // these can't change with interceptors, otherwise we're in weird behaviour land
      const { signal, abortToken } = originalOptions;

      return async (path = originalPath, options = originalOptions) => {
        // path must be string
        if (typeof path !== 'string')
          throw new TypeError('`path` must be a string');

        // otherwise check if its an object
        if (typeof options !== 'object' || Array.isArray(options))
          throw new TypeError('`options` must be an object');

        if (options.abortToken !== abortToken) {
          throw new Error('abortToken cannot be modified via an interceptor');
        }

        if (options.signal !== signal) {
          throw new Error('signal cannot be modified via an interceptor');
        }

        const { raw, ...noRaw } = options;

        const opts = {
          ...noRaw,
          headers: {
            ...this.headers,
            ...options.headers
          },
          method: method === 'del' ? 'DELETE' : method.toUpperCase()
        };

        // remove any nil or blank headers
        // (e.g. to automatically set Content-Type with `FormData` boundary)
        Object.keys(opts.headers).forEach(key => {
          if (
            typeof opts.headers[key] === 'undefined' ||
            opts.headers[key] === null ||
            opts.headers[key] === ''
          )
            delete opts.headers[key];
        });

        const c = caseless(opts.headers);

        // in order to support Android POST requests
        // we must allow an empty body to be sent
        // https://github.com/facebook/react-native/issues/4890
        if (typeof opts.body === 'undefined' && opts.method === 'POST') {
          opts.body = '';
        } else if (typeof opts.body === 'object' || Array.isArray(opts.body)) {
          if (opts.method === 'GET' || opts.method === 'DELETE') {
            const { arrayFormat } = this;
            path += `?${qs.stringify(opts.body, { arrayFormat })}`;
            delete opts.body;
          } else if (
            c.get('Content-Type') &&
            c.get('Content-Type').split(';')[0] === 'application/json'
          ) {
            try {
              opts.body = JSON.stringify(opts.body);
            } catch (error) {
              throw error;
            }
          }
        }

        // eslint-disable-next-line no-async-promise-executor
        const response = new Promise(async (resolve, reject) => {
          try {
            const fullUri = this.opts.baseURI
              ? urlJoin(this.opts.baseURI, path)
              : path;
            const originalRes = await fetch(fullUri, opts);
            const res = createFrisbeeResponse(originalRes);
            const contentType = res.headers.get('Content-Type');

            if (!res.ok) {
              res.err = new Error(res.statusText);

              // check if the response was JSON, and if so, better the error
              if (contentType && contentType.includes('application/json')) {
                try {
                  // attempt to parse json body to use as error message
                  if (typeof res.json === 'function') {
                    res.body = await res.json();
                  } else {
                    res.body = await res.text();
                    res.body = JSON.parse(res.body);
                  }

                  // attempt to use better and human-friendly error messages
                  if (
                    typeof res.body === 'object' &&
                    typeof res.body.message === 'string'
                  ) {
                    res.err = new Error(res.body.message);
                  } else if (
                    !Array.isArray(res.body) &&
                    // attempt to utilize Stripe-inspired error messages
                    typeof res.body.error === 'object'
                  ) {
                    if (res.body.error.message)
                      res.err = new Error(res.body.error.message);
                    if (res.body.error.stack)
                      res.err.stack = res.body.error.stack;
                    if (res.body.error.code) res.err.code = res.body.error.code;
                    if (res.body.error.param)
                      res.err.param = res.body.error.param;
                  }
                } catch (error) {
                  res.err = this.parseErr;
                }
              }

              resolve(res);
              return;
            }

            // if we just want a raw response then return early
            if (raw === true || (this.raw && raw !== false))
              return resolve(res.originalResponse);

            // determine whether we're returning text or json for body
            if (contentType && contentType.includes('application/json')) {
              try {
                if (typeof res.json === 'function') {
                  res.body = await res.json();
                } else {
                  res.body = await res.text();
                  res.body = JSON.parse(res.body);
                }
              } catch (error) {
                if (contentType === 'application/json') {
                  res.err = this.parseErr;
                  resolve(res);
                  return;
                }
              }
            } else {
              res.body = await res.text();
            }

            resolve(res);
          } catch (error) {
            reject(error);
          }
        });

        try {
          await response;
        } catch (e) {}

        // update the abortTokenMap
        let mapValue = this.abortTokenMap.get(options.abortToken);
        if (mapValue && !--mapValue.count) {
          this.abortTokenMap.delete(options.abortToken);
        }

        return response;
      };
    };
  }

  auth(creds) {
    if (typeof creds === 'string') {
      const index = creds.indexOf(':');
      if (index !== -1)
        creds = [creds.substr(0, index), creds.substr(index + 1)];
    }

    if (!Array.isArray(creds)) {
      // eslint-disable-next-line prefer-rest-params
      creds = [].slice.call(arguments);
    }

    switch (creds.length) {
      case 0:
        creds = ['', ''];
        break;
      case 1:
        creds.push('');
        break;
      case 2:
        break;
      default:
        throw new Error('auth option can only have two keys `[user, pass]`');
    }

    if (typeof creds[0] !== 'string')
      throw new TypeError('auth option `user` must be a string');

    if (typeof creds[1] !== 'string')
      throw new TypeError('auth option `pass` must be a string');

    if (!creds[0] && !creds[1]) {
      delete this.headers.Authorization;
    } else {
      this.headers.Authorization = `Basic ${Buffer.from(
        creds.join(':')
      ).toString('base64')}`;
    }

    return this;
  }

  jwt(token) {
    if (token === null || token === undefined)
      delete this.headers.Authorization;
    else if (typeof token === 'string')
      this.headers.Authorization = `Bearer ${token}`;
    else throw new TypeError('jwt token must be a string');

    return this;
  }
}

module.exports = Frisbee;
<commit_msg>Fixed up my own prettier errors<commit_after>const caseless = require('caseless');
const qs = require('qs');
const fetch = require('cross-fetch');
const urlJoin = require('url-join');
require('abortcontroller-polyfill/dist/polyfill-patch-fetch');

const Interceptor = require('./interceptor');

const methods = ['get', 'head', 'post', 'put', 'del', 'options', 'patch'];

const respProperties = {
  readOnly: [
    'headers',
    'ok',
    'redirected',
    'status',
    'statusText',
    'type',
    'url',
    'bodyUsed'
  ],
  writable: ['useFinalURL'],
  callable: [
    'clone',
    'error',
    'redirect',
    'arrayBuffer',
    'blob',
    'formData',
    'json',
    'text'
  ]
};

function createFrisbeeResponse(origResp) {
  const resp = {
    originalResponse: origResp
  };

  respProperties.readOnly.forEach(prop =>
    Object.defineProperty(resp, prop, {
      value: origResp[prop]
    })
  );

  respProperties.writable.forEach(prop =>
    Object.defineProperty(resp, prop, {
      get() {
        return origResp[prop];
      },
      set(value) {
        origResp[prop] = value;
      }
    })
  );

  let callable = null;
  respProperties.callable.forEach(prop => {
    Object.defineProperty(resp, prop, {
      value: ((callable = origResp[prop]),
      typeof callable === 'function' && callable.bind(origResp))
    });
  });

  const headersObj = {};
  origResp.headers.forEach(pair => {
    headersObj[pair[0]] = pair[1];
  });
  Object.defineProperty(resp, 'headersObj', {
    value: headersObj
  });

  return resp;
}

class Frisbee {
  constructor(opts = {}) {
    this.opts = opts;

    let localAbortController;
    Object.defineProperty(this, 'abortController', {
      enumerable: false,
      get() {
        if (!localAbortController) {
          localAbortController = new AbortController();
          localAbortController.signal.addEventListener('abort', () => {
            // when this is aborted, null out the localAbortController
            // so we'll create a new one next time we need it
            localAbortController = null;
          });
        }

        return localAbortController;
      }
    });

    const localAbortTokenMap = new Map();
    Object.defineProperty(this, 'abortTokenMap', {
      enumerable: false,
      value: localAbortTokenMap
    });

    Object.defineProperty(this, 'parseErr', {
      enumerable: false,
      value:
        opts.parseErr ||
        new Error(
          `Invalid JSON received${opts.baseURI ? ` from ${opts.baseURI}` : ''}`
        )
    });

    this.headers = {
      ...opts.headers
    };

    this.arrayFormat = opts.arrayFormat || 'indices';

    this.raw = opts.raw === true;

    if (opts.auth) this.auth(opts.auth);

    methods.forEach(method => {
      this[method] = this._setup(method);
    });

    // interceptor should be initialized after methods setup
    this.interceptor = new Interceptor(this, methods);
  }

  abort(token) {
    const mapValue = this.abortTokenMap.get(token);
    if (mapValue && mapValue.abortController) {
      mapValue.abortController.abort();
    }
  }

  abortAll() {
    this.abortController.abort();
  }

  _setup(method) {
    return (originalPath = '/', originalOptions = {}) => {
      if (originalOptions && typeof originalOptions === 'object') {
        let abortController;
        if (originalOptions.abortToken) {
          // allow to use a single token to cancel multiple requests
          let mapValue = this.abortTokenMap.get(originalOptions.abortToken);
          if (!mapValue) {
            mapValue = {
              abortController: new AbortController(),
              count: 0
            };
          }

          mapValue.count++;

          this.abortTokenMap.set(originalOptions.abortToken, mapValue);
          abortController = mapValue.abortController;
        } else {
          abortController = new AbortController();
        }

        // the user has defined their own signal we won't use it directly, but we'll listen to it
        if (originalOptions.signal) {
          originalOptions.signal.addEventListener('abort', () =>
            abortController.abort()
          );
        }

        // abort this request whenever this.abortController.abort() gets called - a.k.a. - abortAll()
        this.abortController.signal.addEventListener('abort', () =>
          abortController.abort()
        );
        originalOptions.signal = abortController.signal;
      }

      // these can't change with interceptors, otherwise we're in weird behaviour land
      const { signal, abortToken } = originalOptions;

      return async (path = originalPath, options = originalOptions) => {
        // path must be string
        if (typeof path !== 'string')
          throw new TypeError('`path` must be a string');

        // otherwise check if its an object
        if (typeof options !== 'object' || Array.isArray(options))
          throw new TypeError('`options` must be an object');

        if (options.abortToken !== abortToken) {
          throw new Error('abortToken cannot be modified via an interceptor');
        }

        if (options.signal !== signal) {
          throw new Error('signal cannot be modified via an interceptor');
        }

        const { raw, ...noRaw } = options;

        const opts = {
          ...noRaw,
          headers: {
            ...this.headers,
            ...options.headers
          },
          method: method === 'del' ? 'DELETE' : method.toUpperCase()
        };

        // remove any nil or blank headers
        // (e.g. to automatically set Content-Type with `FormData` boundary)
        Object.keys(opts.headers).forEach(key => {
          if (
            typeof opts.headers[key] === 'undefined' ||
            opts.headers[key] === null ||
            opts.headers[key] === ''
          )
            delete opts.headers[key];
        });

        const c = caseless(opts.headers);

        // in order to support Android POST requests
        // we must allow an empty body to be sent
        // https://github.com/facebook/react-native/issues/4890
        if (typeof opts.body === 'undefined' && opts.method === 'POST') {
          opts.body = '';
        } else if (typeof opts.body === 'object' || Array.isArray(opts.body)) {
          if (opts.method === 'GET' || opts.method === 'DELETE') {
            const { arrayFormat } = this;
            path += `?${qs.stringify(opts.body, { arrayFormat })}`;
            delete opts.body;
          } else if (
            c.get('Content-Type') &&
            c.get('Content-Type').split(';')[0] === 'application/json'
          ) {
            try {
              opts.body = JSON.stringify(opts.body);
            } catch (error) {
              throw error;
            }
          }
        }

        // eslint-disable-next-line no-async-promise-executor
        const response = new Promise(async (resolve, reject) => {
          try {
            const fullUri = this.opts.baseURI
              ? urlJoin(this.opts.baseURI, path)
              : path;
            const originalRes = await fetch(fullUri, opts);
            const res = createFrisbeeResponse(originalRes);
            const contentType = res.headers.get('Content-Type');

            if (!res.ok) {
              res.err = new Error(res.statusText);

              // check if the response was JSON, and if so, better the error
              if (contentType && contentType.includes('application/json')) {
                try {
                  // attempt to parse json body to use as error message
                  if (typeof res.json === 'function') {
                    res.body = await res.json();
                  } else {
                    res.body = await res.text();
                    res.body = JSON.parse(res.body);
                  }

                  // attempt to use better and human-friendly error messages
                  if (
                    typeof res.body === 'object' &&
                    typeof res.body.message === 'string'
                  ) {
                    res.err = new Error(res.body.message);
                  } else if (
                    !Array.isArray(res.body) &&
                    // attempt to utilize Stripe-inspired error messages
                    typeof res.body.error === 'object'
                  ) {
                    if (res.body.error.message)
                      res.err = new Error(res.body.error.message);
                    if (res.body.error.stack)
                      res.err.stack = res.body.error.stack;
                    if (res.body.error.code) res.err.code = res.body.error.code;
                    if (res.body.error.param)
                      res.err.param = res.body.error.param;
                  }
                } catch (error) {
                  res.err = this.parseErr;
                }
              }

              resolve(res);
              return;
            }

            // if we just want a raw response then return early
            if (raw === true || (this.raw && raw !== false))
              return resolve(res.originalResponse);

            // determine whether we're returning text or json for body
            if (contentType && contentType.includes('application/json')) {
              try {
                if (typeof res.json === 'function') {
                  res.body = await res.json();
                } else {
                  res.body = await res.text();
                  res.body = JSON.parse(res.body);
                }
              } catch (error) {
                if (contentType === 'application/json') {
                  res.err = this.parseErr;
                  resolve(res);
                  return;
                }
              }
            } else {
              res.body = await res.text();
            }

            resolve(res);
          } catch (error) {
            reject(error);
          }
        });

        try {
          await response;
        } catch (err) {}

        // update the abortTokenMap
        let mapValue = this.abortTokenMap.get(options.abortToken);
        if (mapValue && !--mapValue.count) {
          this.abortTokenMap.delete(options.abortToken);
        }

        return response;
      };
    };
  }

  auth(creds) {
    if (typeof creds === 'string') {
      const index = creds.indexOf(':');
      if (index !== -1)
        creds = [creds.substr(0, index), creds.substr(index + 1)];
    }

    if (!Array.isArray(creds)) {
      // eslint-disable-next-line prefer-rest-params
      creds = [].slice.call(arguments);
    }

    switch (creds.length) {
      case 0:
        creds = ['', ''];
        break;
      case 1:
        creds.push('');
        break;
      case 2:
        break;
      default:
        throw new Error('auth option can only have two keys `[user, pass]`');
    }

    if (typeof creds[0] !== 'string')
      throw new TypeError('auth option `user` must be a string');

    if (typeof creds[1] !== 'string')
      throw new TypeError('auth option `pass` must be a string');

    if (!creds[0] && !creds[1]) {
      delete this.headers.Authorization;
    } else {
      this.headers.Authorization = `Basic ${Buffer.from(
        creds.join(':')
      ).toString('base64')}`;
    }

    return this;
  }

  jwt(token) {
    if (token === null || token === undefined)
      delete this.headers.Authorization;
    else if (typeof token === 'string')
      this.headers.Authorization = `Bearer ${token}`;
    else throw new TypeError('jwt token must be a string');

    return this;
  }
}

module.exports = Frisbee;
<|endoftext|>


<commit_before>'use strict'

/**
 * @module serverless-plugin-warmup
 *
 * @see {@link https://serverless.com/framework/docs/providers/aws/guide/plugins/}
 *
 * @requires 'bluebird'
 * @requires 'fs-extra'
 * @requires 'path'
 * */
const BbPromise = require('bluebird')
const fs = BbPromise.promisifyAll(require('fs-extra'))
const path = require('path')

/**
 * @classdesc Keep your lambdas warm during Winter
 * @class WarmUP
 * */
class WarmUP {
  /**
   * @description Serverless Warm Up
   * @constructor
   *
   * @param {!Object} serverless - Serverless object
   * @param {!Object} options - Serverless options
   * */
  constructor (serverless, options) {
    /** Serverless variables */
    this.serverless = serverless
    this.options = options
    this.custom = this.serverless.service.custom

    this.provider = this.serverless.getProvider('aws')

    this.hooks = {
      'after:package:initialize': this.afterPackageInitialize.bind(this),
      'after:package:createDeploymentArtifacts': this.afterCreateDeploymentArtifacts.bind(this),
      'after:deploy:deploy': this.afterDeployFunctions.bind(this)
    }
  }

  /**
   * @description After package initialize hook
   *
   * @fulfil {} — Warm up set
   * @reject {Error} Warm up error
   *
   * @return {(boolean|Promise)}
   * */
  afterPackageInitialize () {
    this.configPlugin()

    /** Create warmer function and add it to the service */
    return this.createWarmer()
  }

  /**
   * @description After create deployment artifacts
   *
   * @fulfil {} — Optimization finished
   * @reject {Error} Optimization error
   *
   * @return {Promise}
   * */
  afterCreateDeploymentArtifacts () {
    /** Clean prefix folder */
    return this.cleanFolder()
  }

  /**
   * @description After deploy functions hooks
   *
   * @fulfil {} — Functions warmed up sucessfuly
   * @reject {Error} Functions couldn't be warmed up
   *
   * @return {Promise}
   * */
  afterDeployFunctions () {
    if (this.warmup.prewarm) {
      return this.warmUpFunctions()
    }
  }

  /**
   * @description Configure the plugin based on the context of serverless.yml
   *
   * @return {}
   * */
  configPlugin () {
    /** Set warm up folder, file and handler paths */
    this.folderName = '_warmup'
    this.pathFolder = this.getPath(this.folderName)
    this.pathFile = this.pathFolder + '/index.js'
    this.pathHandler = this.folderName + '/index.warmUp'

    /** Default options */
    this.warmup = {
      memorySize: 128,
      name: 'warmup-plugin-' + this.serverless.service.service + '-' + this.options.stage,
      schedule: 'rate(5 minutes)',
      timeout: 10,
      prewarm: false
    }

    /** Set global custom options */
    if (!this.custom || !this.custom.warmup) {
      return
    }

    /** Memory size */
    if (typeof this.custom.warmup.memorySize === 'number') {
      this.warmup.memorySize = this.custom.warmup.memorySize
    }

    /** Function name */
    if (typeof this.custom.warmup.name === 'string') {
      this.warmup.name = this.custom.warmup.name
    }

    /** Schedule expression */
    if (typeof this.custom.warmup.schedule === 'string') {
      this.warmup.schedule = this.custom.warmup.schedule
    }

    /** Timeout */
    if (typeof this.custom.warmup.timeout === 'number') {
      this.warmup.timeout = this.custom.warmup.timeout
    }

    /** Pre-warm */
    if (typeof this.custom.warmup.prewarm === 'boolean') {
      this.warmup.prewarm = this.custom.warmup.prewarm
    }
  }

  /**
   * @description After create deployment artifacts
   *
   * @param {string} file — File path
   *
   * @return {String} Absolute file path
   * */
  getPath (file) {
    return path.join(this.serverless.config.servicePath, file)
  }

  /**
   * @description Clean prefix folder
   *
   * @fulfil {} — Folder cleaned
   * @reject {Error} File system error
   *
   * @return {Promise}
   * */
  cleanFolder () {
    return fs.removeAsync(this.pathFolder)
  }

  /**
   * @description Warm up functions
   *
   * @fulfil {} — Warm up function created and added to service
   * @reject {Error} Warm up error
   *
   * @return {Promise}
   * */
  createWarmer () {
    /** Get functions */
    const allFunctions = this.serverless.service.getAllFunctions()

    /** Filter functions for warm up */
    return BbPromise.filter(allFunctions, (functionName) => {
      const functionObject = this.serverless.service.getFunction(functionName)

      /** Function needs to be warm */
      if (functionObject.warmup === true) {
        return functionObject
      }
    }).then((functionNames) => {
      /** Skip writing if no functions need to be warm */
      if (!functionNames.length) {
        /** Log no warmup */
        this.serverless.cli.log('WarmUP: no lambda to warm')
        return true
      }

      /** Write warm up function */
      return this.createWarmUpFunctionArtifact(functionNames)
    }).then((skip) => {
      /** Add warm up function to service */
      if (skip !== true) {
        return this.addWarmUpFunctionToService()
      }
    })
  }

  /**
   * @description Write warm up ES6 function
   *
   * @param {Array} functionNames - Function names
   *
   * @fulfil {} — Warm up function created
   * @reject {Error} Warm up error
   *
   * @return {Promise}
   * */
  createWarmUpFunctionArtifact (functionNames) {
    /** Log warmup start */
    this.serverless.cli.log('WarmUP: setting ' + functionNames.length + ' lambdas to be warm')

    /** Get function names */
    functionNames = functionNames.map((functionName) => {
      const functionObject = this.serverless.service.getFunction(functionName)
      this.serverless.cli.log('WarmUP: ' + functionObject.name)
      return functionObject.name
    })

    /** Write function invoke promises and push to array */
    const warmUpFunction = '"use strict";\n\n' +
      '/** Generated by Serverless WarmUP Plugin at ' + new Date().toISOString() + ' */\n' +
      'const aws = require("aws-sdk");\n' +
      'aws.config.region = "' + this.serverless.service.provider.region + '";\n' +
      'const lambda = new aws.Lambda();\n' +
      'const functionNames = "' + functionNames.join() + '".split(",");\n' +
      'module.exports.warmUp = (event, context, callback) => {\n' +
      '  let invokes = [];\n' +
      '  let errors = 0;\n' +
      '  console.log("Warm Up Start");\n' +
      '  functionNames.forEach((functionName) => {\n' +
      '    const params = {\n' +
      '      FunctionName: functionName,\n' +
      '      InvocationType: "RequestResponse",\n' +
      '      LogType: "None",\n' +
      '      Qualifier: process.env.SERVERLESS_ALIAS || "$LATEST",\n' +
      '      Payload: "{\\"source\\": \\"serverless-plugin-warmup\\"}"\n' +
      '    };\n' +
      '    invokes.push(lambda.invoke(params).promise().then((data) => {\n' +
      '      console.log("Warm Up Invoke Success: " + functionName + "", data);\n' +
      '    }, (error) => {\n' +
      '      errors++;\n' +
      '      console.log("Warm Up Invoke Error: " + functionName + "", error);\n' +
      '    }));\n' +
      '  });\n' +
      '  Promise.all(invokes).then(() => {\n' +
      '    console.log("Warm Up Finished with " + errors + " invoke errors");\n' +
      '    callback();\n' +
      '  });\n' +
      '}'

    /** Write warm up file */
    return fs.outputFileAsync(this.pathFile, warmUpFunction)
  }

  /**
   * @description Add warm up function to service
   *
   * @return {Object} Warm up service function object
   * */
  addWarmUpFunctionToService () {
    /** SLS warm up function */
    this.serverless.service.functions.warmUpPlugin = {
      description: 'Serverless WarmUP Plugin',
      events: [
        {
          schedule: this.warmup.schedule
        }
      ],
      handler: this.pathHandler,
      memorySize: this.warmup.memorySize,
      name: this.warmup.name,
      runtime: 'nodejs6.10',
      package: {
        exclude: ['**'],
        include: [this.folderName + '/**']
      },
      timeout: this.warmup.timeout
    }

    /** Return service function object */
    return this.serverless.service.functions.warmUpPlugin
  }

  /**
   * @description Warm up the functions immediately after deployment
   *
   * @fulfil {} — Functions warmed up sucessfuly
   * @reject {Error} Functions couldn't be warmed up
   *
   * @return {Promise}
   * */
  warmUpFunctions () {
    this.serverless.cli.log('WarmUP: Pre-warming up your functions')

    const params = {
      FunctionName: this.warmup.name,
      InvocationType: 'RequestResponse',
      LogType: 'None',
      Qualifier: process.env.SERVERLESS_ALIAS || '$LATEST',
      Payload: JSON.stringify({ source: 'serverless-plugin-warmup' })
    }

    return this.provider.request('Lambda', 'invoke', params)
      .then(data => this.serverless.cli.log('WarmUp: Functions sucessfuly pre-warmed'))
      .catch(error => this.serverless.cli.log('WarmUp: Error while pre-warming functions', error))
  }
}

/** Export WarmUP class */
module.exports = WarmUP
<commit_msg>refactor(Warmer lambda): Use JSON.stringify to generate the invoke payload<commit_after>'use strict'

/**
 * @module serverless-plugin-warmup
 *
 * @see {@link https://serverless.com/framework/docs/providers/aws/guide/plugins/}
 *
 * @requires 'bluebird'
 * @requires 'fs-extra'
 * @requires 'path'
 * */
const BbPromise = require('bluebird')
const fs = BbPromise.promisifyAll(require('fs-extra'))
const path = require('path')

/**
 * @classdesc Keep your lambdas warm during Winter
 * @class WarmUP
 * */
class WarmUP {
  /**
   * @description Serverless Warm Up
   * @constructor
   *
   * @param {!Object} serverless - Serverless object
   * @param {!Object} options - Serverless options
   * */
  constructor (serverless, options) {
    /** Serverless variables */
    this.serverless = serverless
    this.options = options
    this.custom = this.serverless.service.custom

    this.provider = this.serverless.getProvider('aws')

    this.hooks = {
      'after:package:initialize': this.afterPackageInitialize.bind(this),
      'after:package:createDeploymentArtifacts': this.afterCreateDeploymentArtifacts.bind(this),
      'after:deploy:deploy': this.afterDeployFunctions.bind(this)
    }
  }

  /**
   * @description After package initialize hook
   *
   * @fulfil {} — Warm up set
   * @reject {Error} Warm up error
   *
   * @return {(boolean|Promise)}
   * */
  afterPackageInitialize () {
    this.configPlugin()

    /** Create warmer function and add it to the service */
    return this.createWarmer()
  }

  /**
   * @description After create deployment artifacts
   *
   * @fulfil {} — Optimization finished
   * @reject {Error} Optimization error
   *
   * @return {Promise}
   * */
  afterCreateDeploymentArtifacts () {
    /** Clean prefix folder */
    return this.cleanFolder()
  }

  /**
   * @description After deploy functions hooks
   *
   * @fulfil {} — Functions warmed up sucessfuly
   * @reject {Error} Functions couldn't be warmed up
   *
   * @return {Promise}
   * */
  afterDeployFunctions () {
    if (this.warmup.prewarm) {
      return this.warmUpFunctions()
    }
  }

  /**
   * @description Configure the plugin based on the context of serverless.yml
   *
   * @return {}
   * */
  configPlugin () {
    /** Set warm up folder, file and handler paths */
    this.folderName = '_warmup'
    this.pathFolder = this.getPath(this.folderName)
    this.pathFile = this.pathFolder + '/index.js'
    this.pathHandler = this.folderName + '/index.warmUp'

    /** Default options */
    this.warmup = {
      memorySize: 128,
      name: 'warmup-plugin-' + this.serverless.service.service + '-' + this.options.stage,
      schedule: 'rate(5 minutes)',
      timeout: 10,
      prewarm: false
    }

    /** Set global custom options */
    if (!this.custom || !this.custom.warmup) {
      return
    }

    /** Memory size */
    if (typeof this.custom.warmup.memorySize === 'number') {
      this.warmup.memorySize = this.custom.warmup.memorySize
    }

    /** Function name */
    if (typeof this.custom.warmup.name === 'string') {
      this.warmup.name = this.custom.warmup.name
    }

    /** Schedule expression */
    if (typeof this.custom.warmup.schedule === 'string') {
      this.warmup.schedule = this.custom.warmup.schedule
    }

    /** Timeout */
    if (typeof this.custom.warmup.timeout === 'number') {
      this.warmup.timeout = this.custom.warmup.timeout
    }

    /** Pre-warm */
    if (typeof this.custom.warmup.prewarm === 'boolean') {
      this.warmup.prewarm = this.custom.warmup.prewarm
    }
  }

  /**
   * @description After create deployment artifacts
   *
   * @param {string} file — File path
   *
   * @return {String} Absolute file path
   * */
  getPath (file) {
    return path.join(this.serverless.config.servicePath, file)
  }

  /**
   * @description Clean prefix folder
   *
   * @fulfil {} — Folder cleaned
   * @reject {Error} File system error
   *
   * @return {Promise}
   * */
  cleanFolder () {
    return fs.removeAsync(this.pathFolder)
  }

  /**
   * @description Warm up functions
   *
   * @fulfil {} — Warm up function created and added to service
   * @reject {Error} Warm up error
   *
   * @return {Promise}
   * */
  createWarmer () {
    /** Get functions */
    const allFunctions = this.serverless.service.getAllFunctions()

    /** Filter functions for warm up */
    return BbPromise.filter(allFunctions, (functionName) => {
      const functionObject = this.serverless.service.getFunction(functionName)

      /** Function needs to be warm */
      if (functionObject.warmup === true) {
        return functionObject
      }
    }).then((functionNames) => {
      /** Skip writing if no functions need to be warm */
      if (!functionNames.length) {
        /** Log no warmup */
        this.serverless.cli.log('WarmUP: no lambda to warm')
        return true
      }

      /** Write warm up function */
      return this.createWarmUpFunctionArtifact(functionNames)
    }).then((skip) => {
      /** Add warm up function to service */
      if (skip !== true) {
        return this.addWarmUpFunctionToService()
      }
    })
  }

  /**
   * @description Write warm up ES6 function
   *
   * @param {Array} functionNames - Function names
   *
   * @fulfil {} — Warm up function created
   * @reject {Error} Warm up error
   *
   * @return {Promise}
   * */
  createWarmUpFunctionArtifact (functionNames) {
    /** Log warmup start */
    this.serverless.cli.log('WarmUP: setting ' + functionNames.length + ' lambdas to be warm')

    /** Get function names */
    functionNames = functionNames.map((functionName) => {
      const functionObject = this.serverless.service.getFunction(functionName)
      this.serverless.cli.log('WarmUP: ' + functionObject.name)
      return functionObject.name
    })

    /** Write function invoke promises and push to array */
    const warmUpFunction = '"use strict";\n\n' +
      '/** Generated by Serverless WarmUP Plugin at ' + new Date().toISOString() + ' */\n' +
      'const aws = require("aws-sdk");\n' +
      'aws.config.region = "' + this.serverless.service.provider.region + '";\n' +
      'const lambda = new aws.Lambda();\n' +
      'const functionNames = "' + functionNames.join() + '".split(",");\n' +
      'module.exports.warmUp = (event, context, callback) => {\n' +
      '  let invokes = [];\n' +
      '  let errors = 0;\n' +
      '  console.log("Warm Up Start");\n' +
      '  functionNames.forEach((functionName) => {\n' +
      '    const params = {\n' +
      '      FunctionName: functionName,\n' +
      '      InvocationType: "RequestResponse",\n' +
      '      LogType: "None",\n' +
      '      Qualifier: process.env.SERVERLESS_ALIAS || "$LATEST",\n' +
      '      Payload: JSON.stringify({ source: "serverless-plugin-warmup" })\n' +
      '    };\n' +
      '    invokes.push(lambda.invoke(params).promise().then((data) => {\n' +
      '      console.log("Warm Up Invoke Success: " + functionName + "", data);\n' +
      '    }, (error) => {\n' +
      '      errors++;\n' +
      '      console.log("Warm Up Invoke Error: " + functionName + "", error);\n' +
      '    }));\n' +
      '  });\n' +
      '  Promise.all(invokes).then(() => {\n' +
      '    console.log("Warm Up Finished with " + errors + " invoke errors");\n' +
      '    callback();\n' +
      '  });\n' +
      '}'

    /** Write warm up file */
    return fs.outputFileAsync(this.pathFile, warmUpFunction)
  }

  /**
   * @description Add warm up function to service
   *
   * @return {Object} Warm up service function object
   * */
  addWarmUpFunctionToService () {
    /** SLS warm up function */
    this.serverless.service.functions.warmUpPlugin = {
      description: 'Serverless WarmUP Plugin',
      events: [
        {
          schedule: this.warmup.schedule
        }
      ],
      handler: this.pathHandler,
      memorySize: this.warmup.memorySize,
      name: this.warmup.name,
      runtime: 'nodejs6.10',
      package: {
        exclude: ['**'],
        include: [this.folderName + '/**']
      },
      timeout: this.warmup.timeout
    }

    /** Return service function object */
    return this.serverless.service.functions.warmUpPlugin
  }

  /**
   * @description Warm up the functions immediately after deployment
   *
   * @fulfil {} — Functions warmed up sucessfuly
   * @reject {Error} Functions couldn't be warmed up
   *
   * @return {Promise}
   * */
  warmUpFunctions () {
    this.serverless.cli.log('WarmUP: Pre-warming up your functions')

    const params = {
      FunctionName: this.warmup.name,
      InvocationType: 'RequestResponse',
      LogType: 'None',
      Qualifier: process.env.SERVERLESS_ALIAS || '$LATEST',
      Payload: JSON.stringify({ source: 'serverless-plugin-warmup' })
    }

    return this.provider.request('Lambda', 'invoke', params)
      .then(data => this.serverless.cli.log('WarmUp: Functions sucessfuly pre-warmed'))
      .catch(error => this.serverless.cli.log('WarmUp: Error while pre-warming functions', error))
  }
}

/** Export WarmUP class */
module.exports = WarmUP
<|endoftext|>


<commit_before>#!/usr/bin/env node

var http = require('http');
var fs = require('fs');
var path = require('path');
var parseUrl = require('url').parse;

function getUsers() {
    return {
        'secret': 'pass',
        'root': 'root',
        'javi': 'javi',
        'presenter1': 'presenter1',
        'presenter2': 'presenter2',
    }
}

function getPollDefinitions() {
    return [
        {
            id: 0,
            title: 'Talk on something very techie.',
            questions: [
                {
                    type: 'update',
                    title: 'Do you like JS?',
                    id: 0,
                    responses: [
                        {
                            id: 0,
                            text: "Yes! JS is amazing. Best ever!",
                            count: 0
                        },
                        {
                            id: 1,
                            text: "Yes... but it has its pros and cons...",
                            count: 0
                        },
                        {
                            id: 2,
                            text: "Not a lot... coding everywhere with the same language helps.",
                            count: 0
                        },
                        {
                            id: 3,
                            text: "Nope! it is such an ugly language!",
                            count: 0
                        }
                    ]
                },
                {
                    type: 'update',
                    title: 'JS, Python or Java?',
                    id: 1,
                    responses: [
                        {
                            id: 0,
                            text: "JS always!",
                            count: 0
                        },
                        {
                            id: 1,
                            text: "Python, batteries included.",
                            count: 0
                        },
                        {
                            id: 2,
                            text: "Java & XML for the win.",
                            count: 0
                        },
                        {
                            id: 3,
                            text: "Those are toys... I prefer BrainFuck",
                            count: 0
                        }
                    ]
                }
            ]
        }
    ];
}

function authorize(user, pass) {
    console.log('>> user %s trying to login', user);
    presenterId = ""+Date.now();
    var authorized = getUsers()[user] === pass;
    console.log('<< user %s is authorized: %s', user, authorized);
    return authorized;
}

function initPoll(id){
    return JSON.parse(JSON.stringify(getPollDefinitions()[id]));
}

var currentPollId = 0;
var currentPoll = initPoll(currentPollId);
var currentQuestion = 0;
var clientResponses = [];
var presenterId = null;
var users = 0;

function getCurrentQuestion() {
    return currentPoll.questions[currentQuestion];
}

function updateClients(responses, object) {
    responses.forEach(function (response) {
        response.write('data: '+JSON.stringify(object)+'\n\n');
    })
}

function httpNotFound(req, res) {
    res.writeHead(404);
    res.end();
}

function decorateWithBody(func, req, res) {
    var body = '';
    req.on('data', function(chunk) {
        body += chunk;
    });

    req.on('end', function() {
        try {
            req.json = JSON.parse(body);
        } catch (err) {}
        req.body = body;
        func(req, res);
    });
    return function(){};

}

var router = {
    getRoutes: [],
    postRoutes: [],
    post: function(path, func) {
        this.postRoutes.push({path: path, func: func})
    },
    get: function(path, func) {
        this.getRoutes.push({path: path, func: func})
    },
    routeFor: function(req, res){
        var route;
        var routesArrName = req.method.toLowerCase()+'Routes';
        var routes = this[routesArrName];
        var funcToApply = this.findMatch(routes, req);
        console.log(' ++ funcToApply', funcToApply.name);
        route = decorateWithBody(funcToApply, req, res);
        return route;
    },
    findMatch: function(routes, req){
        var url = req.parsedUrl.pathname;
        console.log('>>>> req.parsedUrl', url, req.method);
        var route = httpNotFound;
        var currRoute;
        //return this.getRoutes[0].func; //TODO: IMPLEMENT.
        for(var i = 0; i < routes.length; i ++){
            currRoute = routes[i];
            console.log('checking:', url, 'against:', currRoute.path);
            if (currRoute.path === url) {
                console.log('Found route!');
                route = currRoute.func;
                break;
            }
        }
        return route;
    },
    route: function() {
        return function(req, res) {
            req.parsedUrl = parseUrl(req.url, true);
            var func = this.routeFor(req, res);
            func(req, res);
        }.bind(this);
    }
};

function getIndex(req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});
    var page = fs.readFileSync(path.join(__dirname, 'page.html'), {encoding: 'utf8'});
    var user=req.parsedUrl.query.user;
    var pass=req.parsedUrl.query.pass;

    if (user && pass) {
        if ( authorize(user, pass)) {
            page = page.replace('', presenterId);
            //TODO: render an html piece in a page.
            var presenterButtonsHtml = '<input type="button" id="page-prev" class="response-btn" name="prev" value="< prev" onclick="submitPagingRequest(\'prev\')">' +
                '<input type="button" id="page-next" class="response-btn" name="next" value="next >" onclick="submitPagingRequest(\'next\')">';
            page = page.replace('', presenterButtonsHtml);
        } else {
            res.writeHead(401, {'Content-Type': 'text/html'});
            res.end('Unauthorized');
        }
    } else {
        console.log('========>>> generating user page.');
        users += 1;
        page = page.replace('', 'happy-user-'+users);
    }

    return res.end(page);
}

function getPoll(req, res) {
    clientResponses.push(res);
    console.log('>>>>', req.url, req.headers);
    res.writeHead(200, {'Content-Type': 'text/event-stream'});
    var currQuestion = getCurrentQuestion();
    console.log('#############################################################currQuestion', currQuestion);
    updateClients(clientResponses, currQuestion);
    return;
}

function postResponse (req, res) {
    var question = getCurrentQuestion();
    var pollResponse = req.json;
    console.log('***** response: question:', pollResponse.question, 'response:', pollResponse.response);
    if(typeof pollResponse.question === 'number' && typeof pollResponse.response === 'number' ) {
        console.log('***** correct response: question:', pollResponse.question, 'response:', pollResponse.response);

        if(question.id === pollResponse.question && pollResponse.response <= question.responses.length - 1) {
            console.log('*** question and response found. counting.');
            question.responses[pollResponse.response].count += 1;
            updateClients(clientResponses, question);
            res.writeHead(200, "OK", {'Content-Type': 'text/html'});
        } else {
            var msg = "Not found: curr question: "+question.id+" max response: "+ question.responses.length -1;
            console.log('*** '+ msg);
            res.writeHead(404, msg, {'Content-Type': 'text/html'});
        }

    } else {
        var msg = ">>> could not parse question correctly :-(";
        console.log('====' + msg);
        res.writeHead(400, msg, {'Content-Type': 'text/html'});
    }
    res.end();

    return;

}

function postPagination(req, res){
    //curl -X POST --data '{"userId": "lksdjflsdkjf"}' http://localhost:8125/prev -v
    //curl -X POST --data '{"userId": "lksdjflsdkjf"}' http://localhost:8125/next -v
    var urlPath = req.parsedUrl.pathname;
    var isPageNextReq = urlPath.indexOf('next') !== -1;
    var isPagePrevReq = urlPath.indexOf('prev') !== -1;

    var pagingRequest = req.json;
    if (pagingRequest.userId !== presenterId) {//TODO: proper auth system.
        var msg = '%%%==> pagingRequest NOT authorized: userId: '+pagingRequest.userId+' presenterId: ' + presenterId;
        console.log(msg, pagingRequest);

        res.writeHead(401, msg, {'Content-Type': 'text/html'});
        return res.end();
    }
    console.log('%%%==> pagingRequest authorized', pagingRequest, urlPath);
    res.writeHead(204, "Done.", {'Content-Type': 'text/html'});
    res.end();
    if(isPageNextReq) {
        console.log('=========>>>>> NEXT');
        var numberOfPolls = currentPoll.questions.length;
        if(currentQuestion < numberOfPolls -1) {
            currentQuestion += 1;
        }
    } else {
        console.log('=========>>>>> PREV');
        if(currentQuestion > 0) {
            currentQuestion -= 1;
        }
    }
    updateClients(clientResponses, getCurrentQuestion());

    return;
}

router.get('/', getIndex);
router.get('/index.html', getIndex);
router.get('/poll', getPoll);
router.post('/response', postResponse);
router.post('/next', postPagination);
router.post('/prev', postPagination);

var server_port = process.env.OPENSHIFT_NODEJS_PORT || 8126;
var server_ip_address = process.env.OPENSHIFT_NODEJS_IP || "0.0.0.0";

http.createServer(router.route()).listen(server_port, server_ip_address);
console.log('Server running at http://'+server_ip_address+':'+server_port+'/');
<commit_msg>preparing for presenter sessions using cookies.<commit_after>#!/usr/bin/env node

var http = require('http');
var fs = require('fs');
var path = require('path');
var parseUrl = require('url').parse;

var presenterSessions = [];

function getUsers() {
    return {
        'secret': 'pass',
        'root': 'root',
        'javi': 'javi',
        'presenter1': 'presenter1',
        'presenter2': 'presenter2',
    }
}

function getPollDefinitions() {
    return [
        {
            id: 0,
            title: 'Talk on something very techie.',
            questions: [
                {
                    type: 'update',
                    title: 'Do you like JS?',
                    id: 0,
                    responses: [
                        {
                            id: 0,
                            text: "Yes! JS is amazing. Best ever!",
                            count: 0
                        },
                        {
                            id: 1,
                            text: "Yes... but it has its pros and cons...",
                            count: 0
                        },
                        {
                            id: 2,
                            text: "Not a lot... coding everywhere with the same language helps.",
                            count: 0
                        },
                        {
                            id: 3,
                            text: "Nope! it is such an ugly language!",
                            count: 0
                        }
                    ]
                },
                {
                    type: 'update',
                    title: 'JS, Python or Java?',
                    id: 1,
                    responses: [
                        {
                            id: 0,
                            text: "JS always!",
                            count: 0
                        },
                        {
                            id: 1,
                            text: "Python, batteries included.",
                            count: 0
                        },
                        {
                            id: 2,
                            text: "Java & XML for the win.",
                            count: 0
                        },
                        {
                            id: 3,
                            text: "Those are toys... I prefer BrainFuck",
                            count: 0
                        }
                    ]
                }
            ]
        }
    ];
}

function authorize(user, pass) {
    console.log('>> user %s trying to login', user);
    presenterId = ""+Date.now();
    var authorized = getUsers()[user] === pass;
    console.log('<< user %s is authorized: %s', user, authorized);
    return authorized;
}

function initPoll(id){
    return JSON.parse(JSON.stringify(getPollDefinitions()[id]));
}

var currentPollId = 0;
var currentPoll = initPoll(currentPollId);
var currentQuestion = 0;
var clientResponses = [];
var presenterId = null;
var users = 0;

function getCurrentQuestion() {
    return currentPoll.questions[currentQuestion];
}

function updateClients(responses, object) {
    responses.forEach(function (response) {
        response.write('data: '+JSON.stringify(object)+'\n\n');
    })
}

function httpNotFound(req, res) {
    res.writeHead(404);
    res.end();
}

function decorateWithBody(func, req, res) {
    var body = '';
    req.on('data', function(chunk) {
        body += chunk;
    });

    req.on('end', function() {
        try {
            req.json = JSON.parse(body);
        } catch (err) {}
        req.body = body;
        func(req, res);
    });
    return function(){};

}

var router = {
    getRoutes: [],
    postRoutes: [],
    post: function(path, func) {
        this.postRoutes.push({path: path, func: func})
    },
    get: function(path, func) {
        this.getRoutes.push({path: path, func: func})
    },
    routeFor: function(req, res){
        var route;
        var routesArrName = req.method.toLowerCase()+'Routes';
        var routes = this[routesArrName];
        var funcToApply = this.findMatch(routes, req);
        console.log(' ++ funcToApply', funcToApply.name);
        route = decorateWithBody(funcToApply, req, res);
        return route;
    },
    findMatch: function(routes, req){
        var url = req.parsedUrl.pathname;
        console.log('>>>> req.parsedUrl', url, req.method);
        var route = httpNotFound;
        var currRoute;
        //return this.getRoutes[0].func; //TODO: IMPLEMENT.
        for(var i = 0; i < routes.length; i ++){
            currRoute = routes[i];
            console.log('checking:', url, 'against:', currRoute.path);
            if (currRoute.path === url) {
                console.log('Found route!');
                route = currRoute.func;
                break;
            }
        }
        return route;
    },
    route: function() {
        return function(req, res) {
            req.parsedUrl = parseUrl(req.url, true);
            var func = this.routeFor(req, res);
            func(req, res);
        }.bind(this);
    }
};

function getIndex(req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});
    var page = fs.readFileSync(path.join(__dirname, 'page.html'), {encoding: 'utf8'});
    var user=req.parsedUrl.query.user;
    var pass=req.parsedUrl.query.pass;

    if (user && pass) {
        if ( authorize(user, pass)) {
            var newSession = Date.now();
            page = page.replace('', presenterId);
            //TODO: render an html piece in a page.
            var presenterButtonsHtml = 'Your session is: '+newSession+'
<input type="button" id="page-prev" class="response-btn" name="prev" value="< prev" onclick="submitPagingRequest(\'prev\')">' +
                '<input type="button" id="page-next" class="response-btn" name="next" value="next >" onclick="submitPagingRequest(\'next\')">';
            page = page.replace('', presenterButtonsHtml);

            presenterSessions.push(newSession);
            res.writeHead(200, {'Set-Cookie': 'feedbacker-session='+newSession});
        } else {
            res.writeHead(401, {'Content-Type': 'text/html'});
            res.end('Unauthorized');
        }
    } else {
        console.log('========>>> generating user page.');
        users += 1;
        page = page.replace('', 'happy-user-'+users);
    }

    return res.end(page);
}

function getPoll(req, res) {
    clientResponses.push(res);
    console.log('>>>>', req.url, req.headers);
    res.writeHead(200, {'Content-Type': 'text/event-stream'});
    var currQuestion = getCurrentQuestion();
    console.log('#############################################################currQuestion', currQuestion);
    updateClients(clientResponses, currQuestion);
    return;
}

function postResponse (req, res) {
    var question = getCurrentQuestion();
    var pollResponse = req.json;
    console.log('***** response: question:', pollResponse.question, 'response:', pollResponse.response);
    console.log('***** req.cookies:', req.headers.cookie);
    if(typeof pollResponse.question === 'number' && typeof pollResponse.response === 'number' ) {
        console.log('***** correct response: question:', pollResponse.question, 'response:', pollResponse.response);

        if(question.id === pollResponse.question && pollResponse.response <= question.responses.length - 1) {
            console.log('*** question and response found. counting.');
            question.responses[pollResponse.response].count += 1;
            updateClients(clientResponses, question);
            res.writeHead(200, "OK", {'Content-Type': 'text/html'});
        } else {
            var msg = "Not found: curr question: "+question.id+" max response: "+ question.responses.length -1;
            console.log('*** '+ msg);
            res.writeHead(404, msg, {'Content-Type': 'text/html'});
        }

    } else {
        var msg = ">>> could not parse question correctly :-(";
        console.log('====' + msg);
        res.writeHead(400, msg, {'Content-Type': 'text/html'});
    }
    res.end();

    return;

}

function postPagination(req, res){
    //curl -X POST --data '{"userId": "lksdjflsdkjf"}' http://localhost:8125/prev -v
    //curl -X POST --data '{"userId": "lksdjflsdkjf"}' http://localhost:8125/next -v
    var urlPath = req.parsedUrl.pathname;
    var isPageNextReq = urlPath.indexOf('next') !== -1;
    var isPagePrevReq = urlPath.indexOf('prev') !== -1;

    var pagingRequest = req.json;
    if (pagingRequest.userId !== presenterId) {//TODO: proper auth system.
        var msg = '%%%==> pagingRequest NOT authorized: userId: '+pagingRequest.userId+' presenterId: ' + presenterId;
        console.log(msg, pagingRequest);

        res.writeHead(401, msg, {'Content-Type': 'text/html'});
        return res.end();
    }
    console.log('%%%==> pagingRequest authorized', pagingRequest, urlPath);
    res.writeHead(204, "Done.", {'Content-Type': 'text/html'});
    res.end();
    if(isPageNextReq) {
        console.log('=========>>>>> NEXT');
        var numberOfPolls = currentPoll.questions.length;
        if(currentQuestion < numberOfPolls -1) {
            currentQuestion += 1;
        }
    } else {
        console.log('=========>>>>> PREV');
        if(currentQuestion > 0) {
            currentQuestion -= 1;
        }
    }
    updateClients(clientResponses, getCurrentQuestion());

    return;
}

router.get('/', getIndex);
router.get('/index.html', getIndex);
router.get('/poll', getPoll);
router.post('/response', postResponse);
router.post('/next', postPagination);
router.post('/prev', postPagination);

var server_port = process.env.OPENSHIFT_NODEJS_PORT || 8126;
var server_ip_address = process.env.OPENSHIFT_NODEJS_IP || "0.0.0.0";

http.createServer(router.route()).listen(server_port, server_ip_address);
console.log('Server running at http://'+server_ip_address+':'+server_port+'/');
<|endoftext|>


<commit_before>var Tone = require('tone');
var Soundfont = require('soundfont-player');

function AudioEngine (sounds) {

    // tone setup

    this.tone = new Tone();

	// effects setup
    // each effect has a single parameter controlled by the effects block

    this.delay = new Tone.FeedbackDelay(0.25, 0.5);
    this.panner = new Tone.Panner();
    this.reverb = new Tone.Freeverb();
    this.pitchShiftRatio;

    // reset effects to their default parameters
    this.clearEffects();

    // the effects are chained to an effects node for this clone, then to the master output
    // so audio is sent from each sampler or instrument, through the effects in order, then out
    // note that the pitch effect works differently - it sets the playback rate for each sampler
    this.effectsNode = new Tone.Gain();
    this.effectsNode.chain(this.delay, this.panner, this.reverb, Tone.Master);

    // drum sounds

    // var drumFileNames = ['high_conga', 'small_cowbell',
    // 'snare_drum', 'splash cymbal'];
    // this.drumSamplers = this._loadSoundFiles(drumFileNames);

    // sound urls - map each url to its tone.sampler
    this.soundSamplers = [];
    this.loadSounds(sounds);

   // soundfont setup

    // instrument names used by Musyng Kite soundfont, in order to
    // match scratch instruments
    this.instrumentNames = ['acoustic_grand_piano', 'electric_piano_1',
        'drawbar_organ', 'acoustic_guitar_nylon', 'electric_guitar_clean',
         'acoustic_bass', 'pizzicato_strings', 'cello', 'trombone', 'clarinet'];

    Soundfont.instrument(Tone.context, this.instrumentNames[0]).then(
        function (inst) {
            this.instrument = inst;
            this.instrument.connect(this.effectsNode);
        }.bind(this)
    );
}

AudioEngine.prototype.loadSounds = function (sounds) {
    for (var i=0; i<sounds.length; i++) {
        var url = sounds[i].fileUrl;
        var sampler = new Tone.Sampler(url);
        sampler.connect(this.effectsNode);
        this.soundSamplers.push(sampler);
    }
};

AudioEngine.prototype.playSound = function (index) {
    this.soundSamplers[index].triggerAttack();
};

AudioEngine.prototype.getSoundDuration = function (index) {
    return this.soundSamplers[index].player.buffer.duration;
};

AudioEngine.prototype.playNoteForBeats = function (note, beats) {
    this.instrument.play(
        note, Tone.context.currentTime, {duration : Number(beats)}
    );
};

AudioEngine.prototype.playDrumForBeats = function (drumNum) {
    this.drumSamplers[drumNum].triggerAttack();
};

AudioEngine.prototype.stopAllSounds = function () {
    // stop drum notes
    // for (var i = 0; i<this.drumSamplers.length; i++) {
    //     this.drumSamplers[i].triggerRelease();
    // }
    // stop sounds triggered with playSound (indexed by their urls)
    for (var key in this.soundSamplers) {
        this.soundSamplers[key].triggerRelease();
    }
    // stop soundfont notes
    this.instrument.stop();
};

AudioEngine.prototype.setEffect = function (effect, value) {
    switch (effect) {
    case 'ECHO':
        this.delay.wet.value = (value / 100) / 2; // max 50% wet
        break;
    case 'PAN':
        this.panner.pan.value = value / 100;
        break;
    case 'REVERB':
        this.reverb.wet.value = value / 100;
        break;
    case 'PITCH':
        this._setPitchShift(value / 20);
        break;
    }
};

AudioEngine.prototype.changeEffect = function (effect, value) {
    switch (effect) {
    case 'ECHO':
        this.delay.wet.value += (value / 100) / 2; // max 50% wet
        this.delay.wet.value = this._clamp(this.delay.wet.value, 0, 0.5);
        break;
    case 'PAN':
        this.panner.pan.value += value / 100;
        this.panner.pan.value = this._clamp(this.panner.pan.value, -1, 1);
        break;
    case 'REVERB':
        this.reverb.wet.value += value / 100;
        this.reverb.wet.value = this._clamp(this.reverb.wet.value, 0, 1);
        break;
    case 'PITCH':
        this._setPitchShift(this.pitchShiftRatio + (value / 20));
        break;
    }
};

AudioEngine.prototype._setPitchShift = function (value) {
    this.pitchShiftRatio = value;
    for (var i in this.soundSamplers) {
        this.soundSamplers[i].player.playbackRate = 1 + this.pitchShiftRatio;
    }
};

AudioEngine.prototype.clearEffects = function () {
    this.delay.wet.value = 0;
    this._setPitchShift(0);
    this.panner.pan.value = 0;
    this.reverb.wet.value = 0;
};

AudioEngine.prototype._clamp = function (input, min, max) {
    return Math.min(Math.max(input, min), max);
};

module.exports = AudioEngine;

<commit_msg>play spooky note (theremin) and set instrument blocks<commit_after>var Tone = require('tone');
var Soundfont = require('soundfont-player');

function AudioEngine (sounds) {

    // tone setup

    this.tone = new Tone();

	// effects setup
    // each effect has a single parameter controlled by the effects block

    this.delay = new Tone.FeedbackDelay(0.25, 0.5);
    this.panner = new Tone.Panner();
    this.reverb = new Tone.Freeverb();
    this.pitchShiftRatio;

    // reset effects to their default parameters
    this.clearEffects();

    // the effects are chained to an effects node for this clone, then to the master output
    // so audio is sent from each sampler or instrument, through the effects in order, then out
    // note that the pitch effect works differently - it sets the playback rate for each sampler
    this.effectsNode = new Tone.Gain();
    this.effectsNode.chain(this.delay, this.panner, this.reverb, Tone.Master);

    // drum sounds

    // var drumFileNames = ['high_conga', 'small_cowbell',
    // 'snare_drum', 'splash cymbal'];
    // this.drumSamplers = this._loadSoundFiles(drumFileNames);

    // sound urls - map each url to its tone.sampler
    this.soundSamplers = [];
    this.loadSounds(sounds);

   // soundfont setup

    // instrument names used by Musyng Kite soundfont, in order to
    // match scratch instruments
    this.instrumentNames = ['acoustic_grand_piano', 'electric_piano_1',
        'drawbar_organ', 'acoustic_guitar_nylon', 'electric_guitar_clean',
         'acoustic_bass', 'pizzicato_strings', 'cello', 'trombone', 'clarinet'];

    this.setInstrument(0);

    // theremin setup

    this.theremin = new Tone.Synth();
    this.portamentoTime = 0.25;
    this.theremin.portamento = this.portamentoTime;
    this.thereminVibrato = new Tone.Vibrato(4, 0.5);
    this.theremin.chain(this.thereminVibrato, this.effectsNode);
    this.thereminTimeout;
    this.thereminIsPlaying = false;
}

AudioEngine.prototype.loadSounds = function (sounds) {
    for (var i=0; i<sounds.length; i++) {
        var url = sounds[i].fileUrl;
        var sampler = new Tone.Sampler(url);
        sampler.connect(this.effectsNode);
        this.soundSamplers.push(sampler);
    }
};

AudioEngine.prototype.playSound = function (index) {
    this.soundSamplers[index].triggerAttack();
    this.soundSamplers[index].player.playbackRate = 1 + this.pitchShiftRatio;

};

AudioEngine.prototype.getSoundDuration = function (index) {
    return this.soundSamplers[index].player.buffer.duration;
};

AudioEngine.prototype.playNoteForBeats = function (note, beats) {
    this.instrument.play(
        note, Tone.context.currentTime, {duration : Number(beats)}
    );
};

AudioEngine.prototype.playThereminForBeats = function (note, beats) {
    // if the theremin is playing
    //      set frequency
    // else
    //      trigger attack
    // create a timeout for slightly longer than the duration of the block
    // that releases the theremin - so we can slide continuously between
    // successive notes without releasing and attacking

    var freq = this._midiToFreq(note);

    if (this.thereminIsPlaying) {
        this.theremin.frequency.rampTo(freq, this.portamentoTime);
    } else {
        this.theremin.triggerAttack(freq);
        this.thereminIsPlaying = true;
    }
    clearTimeout(this.thereminTimeout);
    this.thereminTimeout = setTimeout(function () {
        this.theremin.triggerRelease();
        this.thereminIsPlaying = false;
    }.bind(this), (1000 * beats) + 100);
};

AudioEngine.prototype._midiToFreq = function (midiNote) {
    var freq = this.tone.intervalToFrequencyRatio(midiNote - 60) * 261.63; // 60 is C4
    return freq;
};

AudioEngine.prototype.playDrumForBeats = function (drumNum) {
    this.drumSamplers[drumNum].triggerAttack();
};

AudioEngine.prototype.stopAllSounds = function () {
    // stop drum notes
    // for (var i = 0; i<this.drumSamplers.length; i++) {
    //     this.drumSamplers[i].triggerRelease();
    // }
    // stop sounds triggered with playSound (indexed by their urls)
    for (var key in this.soundSamplers) {
        this.soundSamplers[key].triggerRelease();
    }
    // stop soundfont notes
    this.instrument.stop();
};

AudioEngine.prototype.setEffect = function (effect, value) {
    switch (effect) {
    case 'ECHO':
        this.delay.wet.value = (value / 100) / 2; // max 50% wet
        break;
    case 'PAN':
        this.panner.pan.value = value / 100;
        break;
    case 'REVERB':
        this.reverb.wet.value = value / 100;
        break;
    case 'PITCH':
        this._setPitchShift(value / 20);
        break;
    }
};

AudioEngine.prototype.changeEffect = function (effect, value) {
    switch (effect) {
    case 'ECHO':
        this.delay.wet.value += (value / 100) / 2; // max 50% wet
        this.delay.wet.value = this._clamp(this.delay.wet.value, 0, 0.5);
        break;
    case 'PAN':
        this.panner.pan.value += value / 100;
        this.panner.pan.value = this._clamp(this.panner.pan.value, -1, 1);
        break;
    case 'REVERB':
        this.reverb.wet.value += value / 100;
        this.reverb.wet.value = this._clamp(this.reverb.wet.value, 0, 1);
        break;
    case 'PITCH':
        this._setPitchShift(this.pitchShiftRatio + (value / 20));
        break;
    }
};

AudioEngine.prototype._setPitchShift = function (value) {
    this.pitchShiftRatio = value;
    for (var i in this.soundSamplers) {
        this.soundSamplers[i].player.playbackRate = 1 + this.pitchShiftRatio;
    }
};

AudioEngine.prototype.setInstrument = function (instrumentNum) {
    return Soundfont.instrument(Tone.context, this.instrumentNames[instrumentNum]).then(
        function (inst) {
            this.instrument = inst;
            this.instrument.connect(this.effectsNode);
        }.bind(this)
    );
};

AudioEngine.prototype.clearEffects = function () {
    this.delay.wet.value = 0;
    this._setPitchShift(0);
    this.panner.pan.value = 0;
    this.reverb.wet.value = 0;
};

AudioEngine.prototype._clamp = function (input, min, max) {
    return Math.min(Math.max(input, min), max);
};

module.exports = AudioEngine;

<|endoftext|>


<commit_before>import React, { PropTypes, Component } from 'react';

const propTypes = {
  className: PropTypes.string,
  onloadCallbackName: PropTypes.string,
  elementID: PropTypes.string,
  onloadCallback: PropTypes.func,
  verifyCallback: PropTypes.func,
  expiredCallback: PropTypes.func,
  render: PropTypes.string,
  sitekey: PropTypes.string,
  theme: PropTypes.string,
  type: PropTypes.string,
  verifyCallbackName: PropTypes.string,
  expiredCallbackName: PropTypes.string,
  size: PropTypes.string,
  tabindex: PropTypes.string,
};

const defaultProps = {
  elementID: 'g-recaptcha',
  onloadCallback: undefined,
  onloadCallbackName: 'onloadCallback',
  verifyCallback: undefined,
  verifyCallbackName: 'verifyCallback',
  expiredCallback: undefined,
  expiredCallbackName: 'expiredCallback',
  render: 'onload',
  theme: 'light',
  type: 'image',
  size: 'normal',
  tabindex: '0',
};

const isReady = () => typeof window !== 'undefined' && typeof window.grecaptcha !== 'undefined';

let readyCheck;

export default class Recaptcha extends Component {

  constructor(props) {
    super(props);
    this.renderGrecaptcha = this.renderGrecaptcha.bind(this);
    this.state = {
      ready: isReady(),
    };

    if (!this.state.ready) {
      readyCheck = setInterval(this.updateReadyState.bind(this), 1000);
    }
  }

  componentDidMount() {
    if (this.state.ready) {
      this.renderGrecaptcha();
    }
  }

  componentDidUpdate(prevProps, prevState) {
    const { render, onloadCallback } = this.props;

    if (render === 'explicit' && onloadCallback && this.state.ready && !prevState.ready) {
      this.renderGrecaptcha();
    }
  }

  reset() {
    if (this.state.ready) {
      grecaptcha.reset();
    }
  }

  updateReadyState() {
    if (isReady()) {
      this.setState({
        ready: true,
      });

      clearInterval(readyCheck);
    }
  }

  renderGrecaptcha() {
    grecaptcha.render(this.props.elementID, {
      sitekey: this.props.sitekey,
      callback: (this.props.verifyCallback) ? this.props.verifyCallback : undefined,
      theme: this.props.theme,
      type: this.props.type,
      tabindex: this.props.tabindex,
      'expired-callback': (this.props.expiredCallback) ? this.props.expiredCallback : undefined,
    });

    this.props.onloadCallback();
  }

  render() {
    if (this.props.render === 'explicit' && this.props.onloadCallback) {
      return (

      );
    }

    return (

    );
  }
}

Recaptcha.propTypes = propTypes;
Recaptcha.defaultProps = defaultProps;
<commit_msg>Prefixed all custom methods that are intended for private use only with an underscore<commit_after>import React, { PropTypes, Component } from 'react';

const propTypes = {
  className: PropTypes.string,
  onloadCallbackName: PropTypes.string,
  elementID: PropTypes.string,
  onloadCallback: PropTypes.func,
  verifyCallback: PropTypes.func,
  expiredCallback: PropTypes.func,
  render: PropTypes.string,
  sitekey: PropTypes.string,
  theme: PropTypes.string,
  type: PropTypes.string,
  verifyCallbackName: PropTypes.string,
  expiredCallbackName: PropTypes.string,
  size: PropTypes.string,
  tabindex: PropTypes.string,
};

const defaultProps = {
  elementID: 'g-recaptcha',
  onloadCallback: undefined,
  onloadCallbackName: 'onloadCallback',
  verifyCallback: undefined,
  verifyCallbackName: 'verifyCallback',
  expiredCallback: undefined,
  expiredCallbackName: 'expiredCallback',
  render: 'onload',
  theme: 'light',
  type: 'image',
  size: 'normal',
  tabindex: '0',
};

const isReady = () => typeof window !== 'undefined' && typeof window.grecaptcha !== 'undefined';

let readyCheck;

export default class Recaptcha extends Component {

  constructor(props) {
    super(props);
    this._renderGrecaptcha = this._renderGrecaptcha.bind(this);
    this.state = {
      ready: isReady(),
    };

    if (!this.state.ready) {
      readyCheck = setInterval(this._updateReadyState.bind(this), 1000);
    }
  }

  componentDidMount() {
    if (this.state.ready) {
      this._renderGrecaptcha();
    }
  }

  componentDidUpdate(prevProps, prevState) {
    const { render, onloadCallback } = this.props;

    if (render === 'explicit' && onloadCallback && this.state.ready && !prevState.ready) {
      this._renderGrecaptcha();
    }
  }

  reset() {
    if (this.state.ready) {
      grecaptcha.reset();
    }
  }

  _updateReadyState() {
    if (isReady()) {
      this.setState({
        ready: true,
      });

      clearInterval(readyCheck);
    }
  }

  _renderGrecaptcha() {
    grecaptcha.render(this.props.elementID, {
      sitekey: this.props.sitekey,
      callback: (this.props.verifyCallback) ? this.props.verifyCallback : undefined,
      theme: this.props.theme,
      type: this.props.type,
      tabindex: this.props.tabindex,
      'expired-callback': (this.props.expiredCallback) ? this.props.expiredCallback : undefined,
    });

    this.props.onloadCallback();
  }

  render() {
    if (this.props.render === 'explicit' && this.props.onloadCallback) {
      return (

      );
    }

    return (

    );
  }
}

Recaptcha.propTypes = propTypes;
Recaptcha.defaultProps = defaultProps;
<|endoftext|>


<commit_before>//let Cycle = require('cycle-react')
//import App from './App2'
//Cycle.applyToDOM('#root', App)

require("./app.css")
/** @jsx hJSX */
import Cycle from '@cycle/core'
//import {Rx} from '@cycle/core'
import {makeDOMDriver, hJSX} from '@cycle/dom'

import Rx from 'rx'
import combineTemplate from 'rx.observable.combinetemplate'

function intent(){
  return Rx.Observable.just({foo:"bar"})
}
function model(){
  return Rx.Observable.just("doo")
}

import Class from "classnames"
let fromEvent = Rx.Observable.fromEvent

function combineLatestObj(obj) {
  var sources = [];
  var keys = [];
  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      keys.push(key.replace(/\$$/, ''));
      sources.push(obj[key]);
    }
  }
  return Rx.Observable.combineLatest(sources, function () {
    var argsLength = arguments.length;
    var combination = {};
    for (var i = argsLength - 1; i >= 0; i--) {
      combination[keys[i]] = arguments[i];
    }
    return combination;
  })
}

function SettingsView({DOM, props$}, name = '') {

  let settings$ = props$.pluck('settings').startWith({showGrid:false})
  let toggled$  = DOM.select(".toggler").events("click")
    .map(true)
    .scan((acc,val)=>!acc)
    .startWith(false)

  //any outside taps close the settings
  let outsideTaps$     = fromEvent(window,"click")
    .map(e=>e.target)
    .map(function(target){
      return document.querySelector(".settings").contains(target)
    })
    .filter(x=>(x===false))//we only care if it was clicked outside, not in

  toggled$ = toggled$.merge( outsideTaps$ )

  let vtree$ = combineLatestObj({settings$,toggled$})
    .map( function({settings , toggled}){
      let fields = undefined
      let showGrid = settings.grid.show
      let showAnnot = settings.annotations.show
      let autoRotate = settings.camera.autoRotate

      if(toggled)
      {
        fields = (

              <input className="showGrid" type="checkbox" id="showGrid" checked={showGrid}> </input>
               Show grid

              <input className="autoRotate" type="checkbox" id="autoRotate" checked={autoRotate}> </input>
               Auto rotate camera

        )
      }

      return(

        <button id="toggler" className={Class("toggler", {toggled: toggled})} >
          <svg version="1.1" id="Cog" xmlns="http://www.w3.org/2000/svg"  x="0px" y="0px"
               viewBox="0 0 20 20" enable-background="new 0 0 20 20" className="icon">
            <path fill="#FFFFFF" d="M16.783,10c0-1.049,0.646-1.875,1.617-2.443c-0.176-0.584-0.407-1.145-0.692-1.672
              c-1.089,0.285-1.97-0.141-2.711-0.883c-0.741-0.74-0.968-1.621-0.683-2.711c-0.527-0.285-1.088-0.518-1.672-0.691
              C12.074,2.57,11.047,3.215,10,3.215c-1.048,0-2.074-0.645-2.643-1.615C6.772,1.773,6.213,2.006,5.686,2.291
              c0.285,1.09,0.059,1.971-0.684,2.711C4.262,5.744,3.381,6.17,2.291,5.885C2.006,6.412,1.774,6.973,1.6,7.557
              C2.57,8.125,3.215,8.951,3.215,10c0,1.047-0.645,2.074-1.615,2.643c0.175,0.584,0.406,1.144,0.691,1.672
              c1.09-0.285,1.971-0.059,2.711,0.682c0.741,0.742,0.969,1.623,0.684,2.711c0.527,0.285,1.087,0.518,1.672,0.693
              c0.568-0.973,1.595-1.617,2.643-1.617c1.047,0,2.074,0.645,2.643,1.617c0.584-0.176,1.144-0.408,1.672-0.693
              c-0.285-1.088-0.059-1.969,0.683-2.711c0.741-0.74,1.622-1.166,2.711-0.883c0.285-0.527,0.517-1.086,0.692-1.672
              C17.429,11.873,16.783,11.047,16.783,10z M10,13.652c-2.018,0-3.653-1.635-3.653-3.652c0-2.018,1.636-3.654,3.653-3.654
              c2.018,0,3.652,1.637,3.652,3.654C13.652,12.018,12.018,13.652,10,13.652z"/>
            </svg>
        </button>

        {fields}

)
      }
    )

  return {
    DOM: vtree$,
  }
}

function view({DOM,props$}){
  let _props$ = Rx.Observable.just({
    settings:{
      grid:{show:true}
      ,annotations:{show:true}
      ,camera:{autoRotate:true}
    }
  })
  let settingsUi = SettingsView({DOM, props$:_props$}, "settingsView")
  let settingsUIDom$ = settingsUi.DOM

  return Rx.Observable.combineLatest(settingsUIDom$,function(settingsView){

    return

       sttuff
      Foo, bar, baz
      {settingsView}

  })

}

function main(drivers) {
  let DOM      = drivers.DOM

  let model$ = model(intent(DOM))

  let v = view({DOM})

  return {
      DOM: v
  }
}

//////////setup drivers
let domDriver      = makeDOMDriver('#root')

let drivers = {
   DOM: domDriver
}

Cycle.run(main, drivers)

//for isomorphic/server side rendering
/*
let context = {}
let componentHtml = React.renderToString(
  React.createElement(App, {context: context})
)*/<commit_msg>- solved issue with not rendering svg icons - various cleanups<commit_after>//let Cycle = require('cycle-react')
//import App from './App2'
//Cycle.applyToDOM('#root', App)

require("./app.css")
/** @jsx hJSX */
import Cycle from '@cycle/core'
//import {Rx} from '@cycle/core'
import {makeDOMDriver, hJSX} from '@cycle/dom'

import Rx from 'rx'
import combineTemplate from 'rx.observable.combinetemplate'

let just = Rx.Observable.just

function intent(){
  return Rx.Observable.just({foo:"bar"})
}
function model(){
  return Rx.Observable.just("doo")
}

import Class from "classnames"
let fromEvent = Rx.Observable.fromEvent

function combineLatestObj(obj) {
  var sources = [];
  var keys = [];
  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      keys.push(key.replace(/\$$/, ''));
      sources.push(obj[key]);
    }
  }
  return Rx.Observable.combineLatest(sources, function () {
    var argsLength = arguments.length;
    var combination = {};
    for (var i = argsLength - 1; i >= 0; i--) {
      combination[keys[i]] = arguments[i];
    }
    return combination;
  })
}

function SettingsView({DOM, props$}, name = '') {

  let settings$ = props$.pluck('settings').startWith({})
  let schema$   = props$.pluck('schema').startWith({})

  let toggled$  = DOM.select(".toggler").events("click")
    .map(true)
    .scan((acc,val)=>!acc)
    .startWith(false)

  //any outside taps close the settings
  let outsideTaps$     = fromEvent(window,"click")
    .map(e=>e.target)
    .map(function(target){
      return document.querySelector(".settings").contains(target)
    })
    .filter(x=>(x===false))//we only care if it was clicked outside, not in

  toggled$ = toggled$.merge( outsideTaps$ )

  let vtree$ = combineLatestObj({settings$,toggled$})
    .map( function({settings , toggled}){
      let fields = undefined
      let showGrid = settings.grid.show
      let showAnnot = settings.annotations.show
      let autoRotate = settings.camera.autoRotate

      if(toggled)
      {
        fields = (

              <input className="showGrid" type="checkbox" id="showGrid" checked={showGrid}> </input>
               Show grid

              <input className="autoRotate" type="checkbox" id="autoRotate" checked={autoRotate}> </input>
               Auto rotate camera

        )
      }

      const iconSvg = `<svg version="1.1" id="Cog" xmlns="http://www.w3.org/2000/svg"  x="0px" y="0px"
           viewBox="0 0 20 20" enable-background="new 0 0 20 20" class="icon">
        <path fill="#FFFFFF" d="M16.783,10c0-1.049,0.646-1.875,1.617-2.443c-0.176-0.584-0.407-1.145-0.692-1.672
          c-1.089,0.285-1.97-0.141-2.711-0.883c-0.741-0.74-0.968-1.621-0.683-2.711c-0.527-0.285-1.088-0.518-1.672-0.691
          C12.074,2.57,11.047,3.215,10,3.215c-1.048,0-2.074-0.645-2.643-1.615C6.772,1.773,6.213,2.006,5.686,2.291
          c0.285,1.09,0.059,1.971-0.684,2.711C4.262,5.744,3.381,6.17,2.291,5.885C2.006,6.412,1.774,6.973,1.6,7.557
          C2.57,8.125,3.215,8.951,3.215,10c0,1.047-0.645,2.074-1.615,2.643c0.175,0.584,0.406,1.144,0.691,1.672
          c1.09-0.285,1.971-0.059,2.711,0.682c0.741,0.742,0.969,1.623,0.684,2.711c0.527,0.285,1.087,0.518,1.672,0.693
          c0.568-0.973,1.595-1.617,2.643-1.617c1.047,0,2.074,0.645,2.643,1.617c0.584-0.176,1.144-0.408,1.672-0.693
          c-0.285-1.088-0.059-1.969,0.683-2.711c0.741-0.74,1.622-1.166,2.711-0.883c0.285-0.527,0.517-1.086,0.692-1.672
          C17.429,11.873,16.783,11.047,16.783,10z M10,13.652c-2.018,0-3.653-1.635-3.653-3.652c0-2.018,1.636-3.654,3.653-3.654
          c2.018,0,3.652,1.637,3.652,3.654C13.652,12.018,12.018,13.652,10,13.652z"/>
      </svg>`

      return(

        <button id="toggler" className={Class("toggler", {toggled: toggled})}
          innerHTML={iconSvg}>
        </button>

        {fields}

)
      }
    )

  return {
    DOM: vtree$,
  }
}

function view({DOM,props$}){
  const settingProps$ = just({
    settings:{
      grid:{show:true}
      ,annotations:{show:true}
      ,camera:{autoRotate:true}
    }
    ,schema : {
      showGrid:{type:"checkbox",path:"grid.show"}
      ,autoRotate:{type:"checkbox",path:"camera.autoRotate"}
      //,annotations:{type:"checkbox",path:"grid.show"}
    }
  })

  let settingsUi = SettingsView({DOM, props$:settingProps$}, "settingsView")
  let settingsView$ = settingsUi.DOM

  return  Rx.Observable.combineLatest(settingsView$,function(settingsView){
    return

       sttuff
      {settingsView}

  })

}

function main(drivers) {
  let DOM      = drivers.DOM

  let model$ = model(intent(DOM))

  let v = view({DOM})

  return {
      DOM: v
  }
}

//////////setup drivers
let domDriver      = makeDOMDriver('#root')

let drivers = {
   DOM: domDriver
}

Cycle.run(main, drivers)

//for isomorphic/server side rendering
/*
let context = {}
let componentHtml = React.renderToString(
  React.createElement(App, {context: context})
)*/<|endoftext|>


<commit_before>import {Observable as O} from "rx"
import React from "react"
import DOM from "react-dom"
import h from "react-hyperscript"
import selmatch from "matches-selector"

const zipObj = pairs => {
  const o = {}
  pairs.forEach(([k, v]) => o[k] = v)
  return o
}

const keys = x => x ? Object.keys(x) : []

const values = obj => keys(obj).map(k => obj[k])

const extend = Object.assign

const matches = (ev, sel) => !sel || (ev.target && selmatch(ev.target, sel))

const shallowEq = (a, b) => {
  const aKeys = keys(a), bKeys = keys(b)
  if (aKeys.length !== bKeys.length) return false
  for (let i = 0; i < aKeys.length; i++) {
    if (a[aKeys[i]] !== b[aKeys[i]]) return false
  }
  return true
}

const rm = (arr, obj) => {
  const idx = arr.indexOf(obj)
  if (idx !== -1) arr.splice(idx, 1)
  return arr.length
}

const reactEvents =
  "onCopy onCut onPaste onKeyDown onKeyPress onKeyUp onFocus onBlur onChange onInput onSubmit " +
  "onClick onContextMenu onDoubleClick onDrag onDragEnd onDragEnter onDragExit " +
  "onDragLeave onDragOver onDragStart onDrop onMouseDown onMouseEnter onMouseLeave " +
  "onMouseMove onMouseOut onMouseOver onMouseUp onTouchCancel onTouchEnd onTouchMove onTouchStart " +
  "onAbort onCanPlay onCanPlayThrough onDurationChange onEmptied onEncrypted onEnded " +
  "onError onLoadedData onLoadedMetadata onLoadStart onPause onPlay onPlaying onProgress " +
  "onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting " +
  "onSelect onScroll onWheel onLoad onError"

const eventsByName =
  zipObj(reactEvents.split(" ").map(e => [e.replace(/^on/, "").toLowerCase(), e]))

/*
 * Mutable multicasting proxy object that allows (de)registering
 * observers and broadcasting 'next' and 'completed' events to them
 */
function MulticastProxy() {
  this.obs = []
  this.next = this.next.bind(this)
  this.dispose = this.dispose.bind(this)
}
extend(MulticastProxy.prototype, {
  add(obs) {
    this.obs.push(obs)
  },
  rm(obs) {
    return rm(this.obs, obs) === 0
  },
  next(ev) {
    this.obs.forEach(o => o.next(ev))
  },
  dispose() {
    this.obs.forEach(o => o.completed())
    this.obs = []
  }
})

/*
 * Event source that listens to React element lifecycle methods and
 * allows subscribing to element's events, managing the event listener
 * attachment/detachment under the hood.
 */
function EventSource() {
  this.elems = []
  this.proxies = {}
}
extend(EventSource.prototype, {
  mount(elem) {
    if (this.elems.indexOf(elem) === -1) {
      this.elems.push(elem)
      if (keys(this.proxies).length > 0) {
        this.refreshListeners([elem])
      }
    }
  },
  unmount(elem) {
    if (rm(this.elems, elem) === 0) {
      const p = this.proxies
      this.proxies = []
      values(p).forEach(proxy => proxy.dispose())
    }
  },
  refreshListeners(elems) {
    elems = elems || this.elems
    const p = this.proxies
    const listeners = zipObj(keys(p).map(k => [eventsByName[k] || k, p[k].next]))
    elems.forEach(elem => {
      if (!elem.state || !shallowEq(elem.state.listeners, listeners)) {
        elem.setState({listeners})
      }
    })
  },
  subscribe(selector, eventName) {
    const observer = o => ({
      next: ev => matches(ev, selector) && o ? o.onNext(ev) : null,
      completed: () => o && o.onCompleted()
    })
    const isNew = !(eventName in this.proxies)
    const proxy = isNew ? (this.proxies[eventName] = new MulticastProxy()) : this.proxies[eventName]
    return O.create(o => {
      const obs = observer(o)
      proxy.add(obs)
      if (isNew) {
        this.refreshListeners()
      }
      return () => proxy.rm(obs) && delete this.proxies[eventName]
    })
  }
})

const Prepared = React.createClass({
  componentDidMount() {
    this.props.events.mount(this)
  },
  componentWillReceiveProps(nextProps) {
    if (nextProps.events !== this.props.events) {
      this.props.events.unmount(this)
      nextProps.events.mount(this)
    }
  },
  componentWillUnmount() {
    this.props.events.unmount(this)
  },
  render() {
    return !this.state ? this.props.children
      : React.cloneElement(this.props.children, this.state.listeners)
  }
})

function makeReactDOMDriver(rootElem) {
  return function ReactDOMDriver() {
    function withEvents(vdom$) {
      return vdom$
        .map(vdom => vdom.type === Prepared ? vdom
          : React.createElement(Prepared, {events: new EventSource()}, vdom))
        .shareReplay(1)
    }

    function events(vdom$, selector, eventName) {
      return vdom$.merge(O.never()).flatMap(vdom => {
        if (vdom.type !== Prepared) {
          console.warn(                                 // eslint-disable-line
            "DOM.events :: incompatible VDOM type.",
            "Perhaps you forgot to call DOM.withEvents() first?"
          )
          return O.empty()
        } else {
          return vdom.props.events.subscribe(selector, eventName)
        }
      })
    }

    const Transducers = {
      React,
      h,
      events,
      withEvents
    }

    const signal$ = O.empty()

    function executor(vdom$) {
      const $root = typeof rootElem === "string" ? document.querySelector(rootElem) : rootElem
      return vdom$.subscribe(vdom => {
        DOM.render(vdom, $root)
      })
    }

    return [Transducers, signal$, executor]
  }
}

export default makeReactDOMDriver

// for testing package
export const __resources = {
  MulticastProxy,
  EventSource,
  Prepared,
  eventsByName,
  matches
}
<commit_msg>Rename “withEvents” -> “prepare” <commit_after>import {Observable as O} from "rx"
import React from "react"
import DOM from "react-dom"
import h from "react-hyperscript"
import selmatch from "matches-selector"

const zipObj = pairs => {
  const o = {}
  pairs.forEach(([k, v]) => o[k] = v)
  return o
}

const keys = x => x ? Object.keys(x) : []

const values = obj => keys(obj).map(k => obj[k])

const extend = Object.assign

const matches = (ev, sel) => !sel || (ev.target && selmatch(ev.target, sel))

const shallowEq = (a, b) => {
  const aKeys = keys(a), bKeys = keys(b)
  if (aKeys.length !== bKeys.length) return false
  for (let i = 0; i < aKeys.length; i++) {
    if (a[aKeys[i]] !== b[aKeys[i]]) return false
  }
  return true
}

const rm = (arr, obj) => {
  const idx = arr.indexOf(obj)
  if (idx !== -1) arr.splice(idx, 1)
  return arr.length
}

const reactEvents =
  "onCopy onCut onPaste onKeyDown onKeyPress onKeyUp onFocus onBlur onChange onInput onSubmit " +
  "onClick onContextMenu onDoubleClick onDrag onDragEnd onDragEnter onDragExit " +
  "onDragLeave onDragOver onDragStart onDrop onMouseDown onMouseEnter onMouseLeave " +
  "onMouseMove onMouseOut onMouseOver onMouseUp onTouchCancel onTouchEnd onTouchMove onTouchStart " +
  "onAbort onCanPlay onCanPlayThrough onDurationChange onEmptied onEncrypted onEnded " +
  "onError onLoadedData onLoadedMetadata onLoadStart onPause onPlay onPlaying onProgress " +
  "onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting " +
  "onSelect onScroll onWheel onLoad onError"

const eventsByName =
  zipObj(reactEvents.split(" ").map(e => [e.replace(/^on/, "").toLowerCase(), e]))

/*
 * Mutable multicasting proxy object that allows (de)registering
 * observers and broadcasting 'next' and 'completed' events to them
 */
function MulticastProxy() {
  this.obs = []
  this.next = this.next.bind(this)
  this.dispose = this.dispose.bind(this)
}
extend(MulticastProxy.prototype, {
  add(obs) {
    this.obs.push(obs)
  },
  rm(obs) {
    return rm(this.obs, obs) === 0
  },
  next(ev) {
    this.obs.forEach(o => o.next(ev))
  },
  dispose() {
    this.obs.forEach(o => o.completed())
    this.obs = []
  }
})

/*
 * Event source that listens to React element lifecycle methods and
 * allows subscribing to element's events, managing the event listener
 * attachment/detachment under the hood.
 */
function EventSource() {
  this.elems = []
  this.proxies = {}
}
extend(EventSource.prototype, {
  mount(elem) {
    if (this.elems.indexOf(elem) === -1) {
      this.elems.push(elem)
      if (keys(this.proxies).length > 0) {
        this.refreshListeners([elem])
      }
    }
  },
  unmount(elem) {
    if (rm(this.elems, elem) === 0) {
      const p = this.proxies
      this.proxies = []
      values(p).forEach(proxy => proxy.dispose())
    }
  },
  refreshListeners(elems) {
    elems = elems || this.elems
    const p = this.proxies
    const listeners = zipObj(keys(p).map(k => [eventsByName[k] || k, p[k].next]))
    elems.forEach(elem => {
      if (!elem.state || !shallowEq(elem.state.listeners, listeners)) {
        elem.setState({listeners})
      }
    })
  },
  subscribe(selector, eventName) {
    const observer = o => ({
      next: ev => matches(ev, selector) && o ? o.onNext(ev) : null,
      completed: () => o && o.onCompleted()
    })
    const isNew = !(eventName in this.proxies)
    const proxy = isNew ? (this.proxies[eventName] = new MulticastProxy()) : this.proxies[eventName]
    return O.create(o => {
      const obs = observer(o)
      proxy.add(obs)
      if (isNew) {
        this.refreshListeners()
      }
      return () => proxy.rm(obs) && delete this.proxies[eventName]
    })
  }
})

const Prepared = React.createClass({
  componentDidMount() {
    this.props.events.mount(this)
  },
  componentWillReceiveProps(nextProps) {
    if (nextProps.events !== this.props.events) {
      this.props.events.unmount(this)
      nextProps.events.mount(this)
    }
  },
  componentWillUnmount() {
    this.props.events.unmount(this)
  },
  render() {
    return !this.state ? this.props.children
      : React.cloneElement(this.props.children, this.state.listeners)
  }
})

function makeReactDOMDriver(rootElem) {
  return function ReactDOMDriver() {
    function prepare(vdom$) {
      return vdom$
        .map(vdom => vdom.type === Prepared ? vdom
          : React.createElement(Prepared, {events: new EventSource()}, vdom))
        .shareReplay(1)
    }

    function events(vdom$, selector, eventName) {
      return vdom$.merge(O.never()).flatMap(vdom => {
        if (vdom.type !== Prepared) {
          console.warn(                                 // eslint-disable-line
            "DOM.events :: VDOM is not prepared for event listening.",
            "Perhaps you forgot to call DOM.prepare(vdom$)?"
          )
          return O.empty()
        } else {
          return vdom.props.events.subscribe(selector, eventName)
        }
      })
    }

    const Transducers = {
      React,
      h,
      events,
      prepare
    }

    const signal$ = O.empty()

    function executor(vdom$) {
      const $root = typeof rootElem === "string" ? document.querySelector(rootElem) : rootElem
      return vdom$.subscribe(vdom => {
        DOM.render(vdom, $root)
      })
    }

    return [Transducers, signal$, executor]
  }
}

export default makeReactDOMDriver

// for testing package
export const __resources = {
  MulticastProxy,
  EventSource,
  Prepared,
  eventsByName,
  matches
}
<|endoftext|>