<commit_before>import { map, prop, groupBy, sum, mapObjIndexed, values, pipe, uniq, pick, sortBy, reverse, find, filter, contains, isNil, concat, indexOf, remove, reduce, where, equals, omit, merge, not } from 'ramda'
import * as taskRecipes from '../../tasks/data/recipes'

import getCurrentOrderApplicableOrderIntentsFlattened from '../../ordering/getters/getCurrentOrderApplicableOrderIntentsFlattened'

const feathersKnex = require('feathers-knex')
const { iff } = require('feathers-hooks-common')

module.exports = function () {
  const app = this
  const db = app.get('db')

  const name = 'taskWorks'
  const options = { Model: db, name }

  app.use(name, feathersKnex(options))
  app.service(name).hooks(hooks)
}

const hooks = {
  before: {
  },
  after: {
    create: [
      iff(taskRecipeIsCompleteOrderSetup, createCastOrderIntentTaskPlan),
      iff(taskRecipeIsCompleteOrderSetup, createCommitOrderTaskPlan),
      iff(taskRecipeIsStartOrder, sendStartOrderEmails),
      iff(taskRecipeIsCloseOrder, createCastOrderIntentTaskWorks),
      iff(taskRecipeIsCloseOrder, createOrderPlans)
    ]
  },
  error: {}
}

function taskRecipeIsCompleteOrderSetup (hook) {
  return hook.data.taskRecipeId === 'completeOrderSetupWithPrereqs'
    || hook.data.taskRecipeId === 'completeOrderSetup'
}

function taskRecipeIsStartOrder (hook) {
  return hook.data.taskRecipeId === 'startOrder'
}

function taskRecipeIsCloseOrder (hook) {
  return hook.data.taskRecipeId === 'closeOrder'
}

function createOrderPlans (hook) {
  const taskPlans = hook.app.service('taskPlans')
  const orders = hook.app.service('orders')
  const orderIntents = hook.app.service('orderIntents')
  const priceSpecs = hook.app.service('priceSpecs')
  const orderPlans = hook.app.service('orderPlans')

  const getUniqProductIds = pipe(map(prop('productId')), uniq)

  return taskPlans.get(hook.data.taskPlanId)
    .then((taskPlan) => {
      return orders.get(taskPlan.params.orderId)
      .then((order) => {
        return orderIntents.find({
          query: {
            orderId: order.id
          }
        })
      })
      .then((queriedOrderIntents) => {
        return priceSpecs.find({
          query: {
            productId: {
              $in: getUniqProductIds(queriedOrderIntents)
            }
          }
        })
        .then((queriedPriceSpecs) => {
          // need 'state' and 'props' objects for the getters
          const state = {
            orderIntents: queriedOrderIntents,
            priceSpecs: queriedPriceSpecs
          }
          const props = { taskPlan }

          const flattenedOrderIntents = getCurrentOrderApplicableOrderIntentsFlattened(state, props)
          return Promise.all(
            flattenedOrderIntents.map((orderIntent) => {
              return orderPlans.create(
                omit(['id', 'desiredQuantity'], merge(orderIntent, { quantity: orderIntent.desiredQuantity }))
              )
            })
          )
        })
      })
    })
    .then(() => hook)
}

function sendOrderStartEmail (credential, appConfig, mailer, order) {
  const orderName = order.name ? order.name : `Order ${order.id}`
  return mailer.create({
    from: `${appConfig.email}`,
    to: credential.email || 'no@email.com',
    subject: `An order has been started on ${appConfig.name}!`,
    html: `
      Hi. You're invited to join an order, ${orderName}, on ${appConfig.name}!

      ${appConfig.bodyText}

      Click here to join the order!
    `
  })
}

function sendWelcomeEmail (credential, appConfig, mailer, order, token) {
  return mailer.create({
    from: `${appConfig.email}`,
    to: credential.email || 'no@email.com',
    subject: `You're invited to join ${appConfig.name}!`,
    html: `
      Hi. You're invited to join a group on ${appConfig.name}!

      ${appConfig.bodyText}

      Click here to set your password and start buying together!
    `
  })
}

function sendEmailBasedOnPasswordStatus (hook, order) {
  const tokens = hook.app.service('tokens')
  const appConfig = hook.app.get('app')
  const mailer = hook.app.service('mailer')

  const hasPassword = (credential) => {
    return not(isNil(credential.password))
  }

  return (credential) => {
    if (hasPassword(credential)) {
      return sendOrderStartEmail(credential, appConfig, mailer, order)
    } else {
      return tokens.create({
        agentId: credential.agentId,
        service: 'credentials',
        method: 'patch',
        params: { serviceId: credential.id }
      })
      .then((token) => {
        return sendWelcomeEmail(credential, appConfig, mailer, order, token)
      })
    }
  }
}

function sendStartOrderEmails (hook) {
  const taskPlans = hook.app.service('taskPlans')
  const orders = hook.app.service('orders')
  const relationships = hook.app.service('relationships')
  // const agents = hook.app.service('agents')
  const credentials = hook.app.service('credentials')

  return taskPlans.get(hook.data.taskPlanId)
  .then((taskPlanResult) => {
    return orders.get(taskPlanResult.params.orderId)
    .then((orderResult) => {
      return relationships.find({
        query: {
          sourceId: orderResult.consumerAgentId,
          relationshipType: 'member'
        }
      })
      .then((relationshipResults) => {
        const agentIds = map((r) => r.targetId, relationshipResults)
        return credentials.find({
          query: {
            agentId: {
              $in: agentIds
            }
          }
        })
        .then((credentialResults) => {
          const sendEmail = sendEmailBasedOnPasswordStatus(hook, orderResult)
          return Promise.all(map(sendEmail, credentialResults))
        })
      })
    })
  })
  .then(() => {
    return hook
  })
}

function createCastOrderIntentTaskPlan (hook) {
  const taskPlans = hook.app.service('taskPlans')
  const orders = hook.app.service('orders')
  const relationships = hook.app.service('relationships')
  const taskRecipeId = taskRecipes.castIntent.id
  const completeOrderTaskPlanId = hook.data.taskPlanId

  return taskPlans.get(completeOrderTaskPlanId)
  .then((completeOrderTaskPlan) => {
    const orderId = completeOrderTaskPlan.params.orderId
    return orders.get(orderId)
  })
  .then((order) => {
    const { id, consumerAgentId, supplierAgentId, adminAgentId } = order
    const params = { orderId: id, consumerAgentId, supplierAgentId, adminAgentId }
    return Promise.all([
      Promise.resolve(params),
      relationships.find({
        query: {
          sourceId: consumerAgentId,
          relationshipType: 'member'
        }
      })
    ])
  })
  .then(([params, relationships]) => {
    return Promise.all(
      map((relationship) => {
        const assigneeId = prop('targetId', relationship)
        return taskPlans.create({ taskRecipeId, params, assigneeId })
      }, relationships)
    )
  })
  .then(() => hook)
}

function createCommitOrderTaskPlan (hook) {
  const taskPlans = hook.app.service('taskPlans')
  const orders = hook.app.service('orders')
  const taskRecipeId = taskRecipes.commitOrder.id
  const completedTaskPlanId = hook.result.taskPlanId

  return taskPlans.get(completedTaskPlanId)
    .then((completedTaskPlan) => {
      const { orderId } = completedTaskPlan.params
      return orders.get(orderId)
    })
    .then((order) => {
      const { id, adminAgentId } = order
      const params = {
        orderId: id,
      }
      return taskPlans.create({ taskRecipeId, params, assigneeId: adminAgentId })
    })
    .then(() => {
      return hook
    })
}

function createCastOrderIntentTaskWorks (hook) {
  // order has closed, get all relevant taskPlans, create a taskWork for em
  const taskPlans = hook.app.service('taskPlans')
  const taskWorks = hook.app.service('taskWorks')
  const closeOrderTaskPlanId = hook.data.taskPlanId

  return taskPlans.get(closeOrderTaskPlanId)
    .then((closeOrderTaskPlan) => {
      const orderId = closeOrderTaskPlan.params.orderId
      // const params = { orderId, consumerAgentId: groupId, supplierAgentId }

      // TODO: IK: feels like a pretty sub-optimal way of querying, probably makes sense to have orderId as it's own column
      return taskPlans.find({
        query: {
          taskRecipeId: 'castIntent'
          // TODO: IK: can't compare JSON values in postgres, need another way to do it
          // params: {
          //   $like: `%"orderId":${orderId}%`
          // }
        }
      })
        .then((taskPlans) => {
          // TODO: IK: can't compare JSON values in postgres, need another way to do it
          const currentOrderTaskPlans = filter((plan) => { return plan.params.orderId === orderId }, taskPlans)
          return Promise.all(currentOrderTaskPlans.map((taskPlan) => {
            const { id, taskRecipeId, assigneeId, params } = taskPlan
            return taskWorks.create({
              taskPlanId: id,
              taskRecipeId,
              workerAgentId: assigneeId,
              params
            })
          }))
        })
    })
    .then(() => hook)
}
<commit_msg>chore(refactor): remove unused code<commit_after>import { map, prop, groupBy, sum, mapObjIndexed, values, pipe, uniq, pick, sortBy, reverse, find, filter, contains, isNil, concat, indexOf, remove, reduce, where, equals, omit, merge, not } from 'ramda'
import * as taskRecipes from '../../tasks/data/recipes'

import getCurrentOrderApplicableOrderIntentsFlattened from '../../ordering/getters/getCurrentOrderApplicableOrderIntentsFlattened'

const feathersKnex = require('feathers-knex')
const { iff } = require('feathers-hooks-common')

module.exports = function () {
  const app = this
  const db = app.get('db')

  const name = 'taskWorks'
  const options = { Model: db, name }

  app.use(name, feathersKnex(options))
  app.service(name).hooks(hooks)
}

const hooks = {
  before: {
  },
  after: {
    create: [
      iff(taskRecipeIsCompleteOrderSetup, createCastOrderIntentTaskPlan),
      iff(taskRecipeIsCompleteOrderSetup, createCommitOrderTaskPlan),
      iff(taskRecipeIsStartOrder, sendStartOrderEmails),
      iff(taskRecipeIsCloseOrder, createCastOrderIntentTaskWorks),
      iff(taskRecipeIsCloseOrder, createOrderPlans)
    ]
  },
  error: {}
}

function taskRecipeIsCompleteOrderSetup (hook) {
  return hook.data.taskRecipeId === 'completeOrderSetupWithPrereqs'
    || hook.data.taskRecipeId === 'completeOrderSetup'
}

function taskRecipeIsStartOrder (hook) {
  return hook.data.taskRecipeId === 'startOrder'
}

function taskRecipeIsCloseOrder (hook) {
  return hook.data.taskRecipeId === 'closeOrder'
}

function createOrderPlans (hook) {
  const taskPlans = hook.app.service('taskPlans')
  const orders = hook.app.service('orders')
  const orderIntents = hook.app.service('orderIntents')
  const priceSpecs = hook.app.service('priceSpecs')
  const orderPlans = hook.app.service('orderPlans')

  const getUniqProductIds = pipe(map(prop('productId')), uniq)

  return taskPlans.get(hook.data.taskPlanId)
    .then((taskPlan) => {
      return orders.get(taskPlan.params.orderId)
      .then((order) => {
        return orderIntents.find({
          query: {
            orderId: order.id
          }
        })
      })
      .then((queriedOrderIntents) => {
        return priceSpecs.find({
          query: {
            productId: {
              $in: getUniqProductIds(queriedOrderIntents)
            }
          }
        })
        .then((queriedPriceSpecs) => {
          // need 'state' and 'props' objects for the getters
          const state = {
            orderIntents: queriedOrderIntents,
            priceSpecs: queriedPriceSpecs
          }
          const props = { taskPlan }

          const flattenedOrderIntents = getCurrentOrderApplicableOrderIntentsFlattened(state, props)
          return Promise.all(
            flattenedOrderIntents.map((orderIntent) => {
              return orderPlans.create(
                omit(['id', 'desiredQuantity'], merge(orderIntent, { quantity: orderIntent.desiredQuantity }))
              )
            })
          )
        })
      })
    })
    .then(() => hook)
}

function sendOrderStartEmail (credential, appConfig, mailer, order) {
  const orderName = order.name ? order.name : `Order ${order.id}`
  return mailer.create({
    from: `${appConfig.email}`,
    to: credential.email || 'no@email.com',
    subject: `An order has been started on ${appConfig.name}!`,
    html: `
      Hi. You're invited to join an order, ${orderName}, on ${appConfig.name}!

      ${appConfig.bodyText}

      Click here to join the order!
    `
  })
}

function sendWelcomeEmail (credential, appConfig, mailer, order, token) {
  return mailer.create({
    from: `${appConfig.email}`,
    to: credential.email || 'no@email.com',
    subject: `You're invited to join ${appConfig.name}!`,
    html: `
      Hi. You're invited to join a group on ${appConfig.name}!

      ${appConfig.bodyText}

      Click here to set your password and start buying together!
    `
  })
}

function sendEmailBasedOnPasswordStatus (hook, order) {
  const tokens = hook.app.service('tokens')
  const appConfig = hook.app.get('app')
  const mailer = hook.app.service('mailer')

  const hasPassword = (credential) => {
    return not(isNil(credential.password))
  }

  return (credential) => {
    if (hasPassword(credential)) {
      return sendOrderStartEmail(credential, appConfig, mailer, order)
    } else {
      return tokens.create({
        agentId: credential.agentId,
        service: 'credentials',
        method: 'patch',
        params: { serviceId: credential.id }
      })
      .then((token) => {
        return sendWelcomeEmail(credential, appConfig, mailer, order, token)
      })
    }
  }
}

function sendStartOrderEmails (hook) {
  const taskPlans = hook.app.service('taskPlans')
  const orders = hook.app.service('orders')
  const relationships = hook.app.service('relationships')
  const credentials = hook.app.service('credentials')

  return taskPlans.get(hook.data.taskPlanId)
  .then((taskPlanResult) => {
    return orders.get(taskPlanResult.params.orderId)
    .then((orderResult) => {
      return relationships.find({
        query: {
          sourceId: orderResult.consumerAgentId,
          relationshipType: 'member'
        }
      })
      .then((relationshipResults) => {
        const agentIds = map((r) => r.targetId, relationshipResults)
        return credentials.find({
          query: {
            agentId: {
              $in: agentIds
            }
          }
        })
        .then((credentialResults) => {
          const sendEmail = sendEmailBasedOnPasswordStatus(hook, orderResult)
          return Promise.all(map(sendEmail, credentialResults))
        })
      })
    })
  })
  .then(() => {
    return hook
  })
}

function createCastOrderIntentTaskPlan (hook) {
  const taskPlans = hook.app.service('taskPlans')
  const orders = hook.app.service('orders')
  const relationships = hook.app.service('relationships')
  const taskRecipeId = taskRecipes.castIntent.id
  const completeOrderTaskPlanId = hook.data.taskPlanId

  return taskPlans.get(completeOrderTaskPlanId)
  .then((completeOrderTaskPlan) => {
    const orderId = completeOrderTaskPlan.params.orderId
    return orders.get(orderId)
  })
  .then((order) => {
    const { id, consumerAgentId, supplierAgentId, adminAgentId } = order
    const params = { orderId: id, consumerAgentId, supplierAgentId, adminAgentId }
    return Promise.all([
      Promise.resolve(params),
      relationships.find({
        query: {
          sourceId: consumerAgentId,
          relationshipType: 'member'
        }
      })
    ])
  })
  .then(([params, relationships]) => {
    return Promise.all(
      map((relationship) => {
        const assigneeId = prop('targetId', relationship)
        return taskPlans.create({ taskRecipeId, params, assigneeId })
      }, relationships)
    )
  })
  .then(() => hook)
}

function createCommitOrderTaskPlan (hook) {
  const taskPlans = hook.app.service('taskPlans')
  const orders = hook.app.service('orders')
  const taskRecipeId = taskRecipes.commitOrder.id
  const completedTaskPlanId = hook.result.taskPlanId

  return taskPlans.get(completedTaskPlanId)
    .then((completedTaskPlan) => {
      const { orderId } = completedTaskPlan.params
      return orders.get(orderId)
    })
    .then((order) => {
      const { id, adminAgentId } = order
      const params = {
        orderId: id,
      }
      return taskPlans.create({ taskRecipeId, params, assigneeId: adminAgentId })
    })
    .then(() => {
      return hook
    })
}

function createCastOrderIntentTaskWorks (hook) {
  // order has closed, get all relevant taskPlans, create a taskWork for em
  const taskPlans = hook.app.service('taskPlans')
  const taskWorks = hook.app.service('taskWorks')
  const closeOrderTaskPlanId = hook.data.taskPlanId

  return taskPlans.get(closeOrderTaskPlanId)
    .then((closeOrderTaskPlan) => {
      const orderId = closeOrderTaskPlan.params.orderId
      // const params = { orderId, consumerAgentId: groupId, supplierAgentId }

      // TODO: IK: feels like a pretty sub-optimal way of querying, probably makes sense to have orderId as it's own column
      return taskPlans.find({
        query: {
          taskRecipeId: 'castIntent'
          // TODO: IK: can't compare JSON values in postgres, need another way to do it
          // params: {
          //   $like: `%"orderId":${orderId}%`
          // }
        }
      })
        .then((taskPlans) => {
          // TODO: IK: can't compare JSON values in postgres, need another way to do it
          const currentOrderTaskPlans = filter((plan) => { return plan.params.orderId === orderId }, taskPlans)
          return Promise.all(currentOrderTaskPlans.map((taskPlan) => {
            const { id, taskRecipeId, assigneeId, params } = taskPlan
            return taskWorks.create({
              taskPlanId: id,
              taskRecipeId,
              workerAgentId: assigneeId,
              params
            })
          }))
        })
    })
    .then(() => hook)
}
<|endoftext|>


<commit_before>import Morph from './Morph.js';

import { Graph } from 'src/client/triples/triples.js';

export default class KnotInput extends Morph {
  get inputSelector() { return '#input'}
  get listSelector() { return '#list'}
  get input() { return this.get(this.inputSelector); }
  get list() { return this.get(this.listSelector); }
  get label() { return this.get('#label'); }

  async initialize() {
    this.windowTitle = "Knot Input";

    this.input.addEventListener('keyup',  event => {
      if (event.keyCode == 13) { // ENTER
        this.onEnter();
      }
    });

    await this.prepareDatalist();
    this.setLabel('Foo');
    this.setPlaceholder('Foo');
  }

  async prepareDatalist() {
    let graph = Graph.getInstance();
    await graph.loadFromDir('https://lively4/dropbox/');

    graph.getKnots().forEach(knot => {
      let option = document.createElement('option');

      option.innerHTML = knot.label();
      option.value = knot.url;
      option.dataset.url = knot.url;

      this.list.appendChild(option);
    });
  }

  // https://derickbailey.com/2016/03/23/get-a-data-attribute-value-from-the-selected-datalist-option/
  getURLStringFor(inputSelector, listSelector) {
    let input = this.get(inputSelector);
    let list = this.get(listSelector);

    var value = input.value;
    lively.notify('input value: ' + value);
    // TODO: check for empty value ('')
    // TODO: value could be a literal or a url
    var option = this.get(`${listSelector} [value='${value}']`);
    // value could also be an external url
    if(!option) return;
    lively.notify(option.innerHTML)
    let url = option.dataset.url;
    lively.notify('url: ' + url);
    return url;
  }
  setLabel(text) { this.label.innerHTML = text; }
  setPlaceholder(text) { this.input.setAttribute('placeholder', text); }
  onEnter() {
    lively.notify(123 + this.getURLStringFor(this.inputSelector, this.listSelector))
  }
}<commit_msg>AUTO-COMMIT templates/knot-input.js<commit_after>import Morph from './Morph.js';

import { Graph } from 'src/client/triples/triples.js';

export default class KnotInput extends Morph {
  get inputSelector() { return '#input'}
  get listSelector() { return '#list'}
  get input() { return this.get(this.inputSelector); }
  get list() { return this.get(this.listSelector); }
  get label() { return this.get('#label'); }

  async initialize() {
    this.windowTitle = "Knot Input";

    this.input.addEventListener('keyup',  event => {
      if (event.keyCode == 13) { // ENTER
        this.onEnter();
      }
    });

    await this.prepareDatalist();
    this.setLabel('Foo');
    this.setPlaceholder('Foo');
  }

  async prepareDatalist() {
    let graph = Graph.getInstance();
    await graph.loadFromDir('https://lively4/dropbox/');

    graph.getKnots().forEach(knot => {
      let option = document.createElement('option');

      option.innerHTML = knot.label();
      option.value = knot.url;
      option.dataset.url = knot.url;

      this.list.appendChild(option);
    });
  }

  // https://derickbailey.com/2016/03/23/get-a-data-attribute-value-from-the-selected-datalist-option/
  getURLStringFor(inputSelector, listSelector) {
    let input = this.get(inputSelector);
    let list = this.get(listSelector);

    var value = input.value;
    lively.notify('input value: ' + value);
    // TODO: check for empty value ('')
    // TODO: value could be a literal or a url
    var option = this.get(`${listSelector} [value='${value}']`);
    // value could also be an external url
    if(!option) return;
    lively.notify(option.innerHTML)
    let url = option.dataset.url;
    lively.notify('url: ' + url);
    return url;
  }

  setLabel(text) { this.label.innerHTML = text; }
  setPlaceholder(text) { this.input.setAttribute('placeholder', text); }
  onEnter() {
    lively.notify(123 + this.getURLStringFor(this.inputSelector, this.listSelector))
  }
}<|endoftext|>


<commit_before>// Manage a connection to the Rackspace cloud. Retain and export handles to created resources.

var url = require('url');
var async = require('async');
var pkgcloud = require('pkgcloud');
var mongo = require('mongodb');
var elasticsearch = require('elasticsearch');
var config = require('../config');
var logger = require('../logging').getLogger();

/**
 * @description Create a function that asynchronously creates a Rackspace container if it
 *   doesn't already exist.
 */
function makeContainerCreator (client, containerName, logicalName, cdn) {
  return function (callback) {
    var reportBack = function (err, container) {
      if (err) return callback(err);

      exports[logicalName] = container;

      logger.debug('Container [' + container.name + '] now exists.');

      callback(null, container);
    };

    var cdnEnable = function (err, container) {
      if (err) return callback(err);

      logger.debug('Enabling CDN on container [' + container.name + '].');

      client.setCdnEnabled(container, {
        ttl: 31536000,
        enabled: true
      }, reportBack);
    };

    var handleCreation = cdn ? cdnEnable : reportBack;

    logger.info('Ensuring that container [' + containerName + '] exists.');

    // Instead of checking if the container exists first, we try to create it, and
    // if it already exists, we get a no-op (202) and move on.
    client.createContainer({
      name: containerName
    }, handleCreation);
  };
}

/**
 * @description Authenticate to MongoDB, export the active MongoDB connection as "db", and
 *   perform any necessary one-time initialization.
 */
function mongoInit (callback) {
  mongo.MongoClient.connect(config.mongodbURL(), function (err, db) {
    if (err) return callback(err);

    logger.debug('Connected to MongoDB database at [' + config.mongodbURL() + '].');

    exports.db = db;

    var envelopes = db.collection('envelopes');

    // Create indices on collections as necessary.
    async.parallel([
      function (callback) {
        envelopes.createIndex('tags', {
          sparse: true
        }, callback);
      },
      function (callback) {
        envelopes.createIndex('categories', {
          sparse: true
        }, callback);
      },
      function (callback) {
        envelopes.createIndex('contentID', {
          unique: true
        }, callback);
      }
    ], function (err, db) {
      if (err) return callback(err);

      logger.debug('All indices created.');

      callback(null);
    });
  });
}

/**
 * @description Shunt Elasticsearch log messages to the existing logger.
 */
function ElasticLogs (config) {
  var makeLogHandler = function (level) {
    return function (message) {
      logger[level]({
        action: 'elasticsearch',
        message: message
      });
    };
  };

  this.error = makeLogHandler('error');
  this.warning = makeLogHandler('warning');
  this.info = makeLogHandler('info');
  this.debug = makeLogHandler('debug');

  this.trace = function (httpMethod, requestUrl, requestBody, responseBody, responseStatus) {
    logger.trace({
      message: 'Elasticsearch HTTP request',
      httpMethod: httpMethod,
      requestUrl: url.format(requestUrl),
      requestBody: requestBody,
      responseBody: responseBody,
      responseStatus: responseStatus
    });

    this.close = function () {};
  };
}

/**
 * @description Authenticate to Elasticsearch and perform one-time initialization. Export the active
 * Elasticsearch client as "elastic".
 */
function elasticInit (callback) {
  var client = new elasticsearch.Client({
    host: config.elasticsearchHost(),
    ssl: { rejectUnauthorized: true }, // Plz no trivial MITM attacks
    log: ElasticLogs
  });

  exports.elastic = client;

  callback(null);
}

exports.setup = function (callback) {
  var client = pkgcloud.providers.rackspace.storage.createClient({
    username: config.rackspaceUsername(),
    apiKey: config.rackspaceAPIKey(),
    region: config.rackspaceRegion(),
    useInternal: config.rackspaceServiceNet()
  });

  client.on('log::*', function (message, object) {
    if (object) {
      logger.log(this.event.split('::')[1], message, object);
    } else {
      logger.log(this.event.split('::')[1], message);
    }
  });

  client.auth(function (err) {
    if (err) return callback(err);

    exports.client = client;

    async.parallel([
      makeContainerCreator(client, config.contentContainer(), 'contentContainer', false),
      makeContainerCreator(client, config.assetContainer(), 'assetContainer', true),
      mongoInit,
      elasticInit
    ], callback);
  });
};
<commit_msg>Explicit mapping for envelopes.<commit_after>// Manage a connection to the Rackspace cloud. Retain and export handles to created resources.

var url = require('url');
var async = require('async');
var pkgcloud = require('pkgcloud');
var mongo = require('mongodb');
var elasticsearch = require('elasticsearch');
var config = require('../config');
var logger = require('../logging').getLogger();

/**
 * @description Create a function that asynchronously creates a Rackspace container if it
 *   doesn't already exist.
 */
function makeContainerCreator (client, containerName, logicalName, cdn) {
  return function (callback) {
    var reportBack = function (err, container) {
      if (err) return callback(err);

      exports[logicalName] = container;

      logger.debug('Container [' + container.name + '] now exists.');

      callback(null, container);
    };

    var cdnEnable = function (err, container) {
      if (err) return callback(err);

      logger.debug('Enabling CDN on container [' + container.name + '].');

      client.setCdnEnabled(container, {
        ttl: 31536000,
        enabled: true
      }, reportBack);
    };

    var handleCreation = cdn ? cdnEnable : reportBack;

    logger.info('Ensuring that container [' + containerName + '] exists.');

    // Instead of checking if the container exists first, we try to create it, and
    // if it already exists, we get a no-op (202) and move on.
    client.createContainer({
      name: containerName
    }, handleCreation);
  };
}

/**
 * @description Authenticate to MongoDB, export the active MongoDB connection as "db", and
 *   perform any necessary one-time initialization.
 */
function mongoInit (callback) {
  mongo.MongoClient.connect(config.mongodbURL(), function (err, db) {
    if (err) return callback(err);

    logger.debug('Connected to MongoDB database at [' + config.mongodbURL() + '].');

    exports.db = db;

    var envelopes = db.collection('envelopes');

    // Create indices on collections as necessary.
    async.parallel([
      function (callback) {
        envelopes.createIndex('tags', {
          sparse: true
        }, callback);
      },
      function (callback) {
        envelopes.createIndex('categories', {
          sparse: true
        }, callback);
      },
      function (callback) {
        envelopes.createIndex('contentID', {
          unique: true
        }, callback);
      }
    ], function (err, db) {
      if (err) return callback(err);

      logger.debug('All indices created.');

      callback(null);
    });
  });
}

/**
 * @description Shunt Elasticsearch log messages to the existing logger.
 */
function ElasticLogs (config) {
  var makeLogHandler = function (level) {
    return function (message) {
      logger[level]({
        action: 'elasticsearch',
        message: message
      });
    };
  };

  this.error = makeLogHandler('error');
  this.warning = makeLogHandler('warning');
  this.info = makeLogHandler('info');
  this.debug = makeLogHandler('debug');

  this.trace = function (httpMethod, requestUrl, requestBody, responseBody, responseStatus) {
    logger.trace({
      message: 'Elasticsearch HTTP request',
      httpMethod: httpMethod,
      requestUrl: url.format(requestUrl),
      requestBody: requestBody,
      responseBody: responseBody,
      responseStatus: responseStatus
    });

    this.close = function () {};
  };
}

/**
 * @description Authenticate to Elasticsearch and perform one-time initialization. Export the active
 * Elasticsearch client as "elastic".
 */
function elasticInit (callback) {
  var client = new elasticsearch.Client({
    host: config.elasticsearchHost(),
    ssl: { rejectUnauthorized: true }, // Plz no trivial MITM attacks
    log: ElasticLogs
  });

  exports.elastic = client;

  var envelopeMapping = {
    properties: {
      title: {
        type: 'string',
        index: 'analyzed'
      },
      body: {
        type: 'string',
        index: 'analyzed'
      }
    }
  };

  client.indices.create({
    index: 'envelopes',
    body: {
      mappings: { envelope: envelopeMapping }
    }
  }, callback);
}

exports.setup = function (callback) {
  var client = pkgcloud.providers.rackspace.storage.createClient({
    username: config.rackspaceUsername(),
    apiKey: config.rackspaceAPIKey(),
    region: config.rackspaceRegion(),
    useInternal: config.rackspaceServiceNet()
  });

  client.on('log::*', function (message, object) {
    if (object) {
      logger.log(this.event.split('::')[1], message, object);
    } else {
      logger.log(this.event.split('::')[1], message);
    }
  });

  client.auth(function (err) {
    if (err) return callback(err);

    exports.client = client;

    async.parallel([
      makeContainerCreator(client, config.contentContainer(), 'contentContainer', false),
      makeContainerCreator(client, config.assetContainer(), 'assetContainer', true),
      mongoInit,
      elasticInit
    ], callback);
  });
};
<|endoftext|>


<commit_before>import axios from 'axios'

export default axios.create({
  apiURL: process.env.apiUrl
})
<commit_msg>Fix incorrect axios url again<commit_after>import axios from 'axios'

export default axios.create({
  apiUrl: process.env.apiUrl
})
<|endoftext|>


<commit_before>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2010 elements.at New Media Solutions GmbH (http://www.elements.at)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.object.edit");
pimcore.object.edit = Class.create({

    initialize: function(object) {
        this.object = object;
        this.dataFields = {};

        this.fieldsToMask = [];
    },

    getLayout: function (conf) {

        if (this.layout == null) {
            this.layout = new Ext.Panel({
                title: t('edit'),
                bodyStyle:'background-color: #fff;',
                padding: 10,
                border: false,
                layout: "fit",
                forceLayout: true,
                defaults: {
                    forceLayout: true
                },
                iconCls: "pimcore_icon_tab_edit",
                items: [this.getRecursiveLayout(conf).items],
                listeners: {
                    afterrender: function () {
                        pimcore.layout.refresh();
                    }
                }
            });
        }

        return this.layout;
    },

    getDataForField: function (name) {
        return this.object.data.data[name];
    },

    getMetaDataForField: function (name) {
        return this.object.data.metaData[name];
    },

    addToDataFields: function (field, name) {
        this.dataFields[name] = field;
    },

    addFieldsToMask: function (field) {
        this.fieldsToMask.push(field);
    },

    unmaskFrames: function () {
        // remove the masks from iframes
        var masks = Ext.query(".pimcore_iframe_mask");
        for (var i = 0; i < masks.length; i++) {
            masks[i].remove();
        }

        // unmask wysiwyg editors
        for (var i = 0; i < this.fieldsToMask.length; i++) {
            this.fieldsToMask[i].unmask();
        }
    },

    maskFrames: function () {
        // this is for dnd over iframes, with this method it's not nessercery to register the dnd manager in each iframe (wysiwyg)

        // mask wysiwyg editors
        for (var i = 0; i < this.fieldsToMask.length; i++) {
            this.fieldsToMask[i].mask();
        }
    },

    getValues: function () {

        if (!this.layout.rendered) {
            throw "edit not available";
        }

        var dataKeys = Object.keys(this.dataFields);
        var values = {};
        var currentField;
        var mendatoryError = false;

        for (var i = 0; i < dataKeys.length; i++) {

            try {
                if (this.dataFields[dataKeys[i]] && typeof this.dataFields[dataKeys[i]] == "object") {
                    currentField = this.dataFields[dataKeys[i]];
                    if (currentField.isMandatory() == true && this.object.ignoreMandatoryFields != true) {
                        if (currentField.isInvalidMandatory()) {
                            Ext.MessageBox.alert(t("error"), t("mandatory_field_empty"));
                            currentField.markMandatory();
                            mendatoryError = true;
                        }
                        else {
                            currentField.unmarkMandatory();
                        }
                    }

                    var currentValue =  currentField.getValue();

                    //only include changed values in save response.
                    if(currentField.isDirty()) {
                        values[currentField.getName()] =  currentValue;
                    }
                }
            }
            catch (e) {
                console.log(e);
                values[currentField.getName()] = "";
            }
        }

        if (mendatoryError) {
            return false;
        }

        return values;
    }

});

pimcore.object.edit.addMethods(pimcore.object.helpers.edit);<commit_msg>mandatory field error message<commit_after>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2010 elements.at New Media Solutions GmbH (http://www.elements.at)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.object.edit");
pimcore.object.edit = Class.create({

    initialize: function(object) {
        this.object = object;
        this.dataFields = {};

        this.fieldsToMask = [];
    },

    getLayout: function (conf) {

        if (this.layout == null) {
            this.layout = new Ext.Panel({
                title: t('edit'),
                bodyStyle:'background-color: #fff;',
                padding: 10,
                border: false,
                layout: "fit",
                forceLayout: true,
                defaults: {
                    forceLayout: true
                },
                iconCls: "pimcore_icon_tab_edit",
                items: [this.getRecursiveLayout(conf).items],
                listeners: {
                    afterrender: function () {
                        pimcore.layout.refresh();
                    }
                }
            });
        }

        return this.layout;
    },

    getDataForField: function (name) {
        return this.object.data.data[name];
    },

    getMetaDataForField: function (name) {
        return this.object.data.metaData[name];
    },

    addToDataFields: function (field, name) {
        this.dataFields[name] = field;
    },

    addFieldsToMask: function (field) {
        this.fieldsToMask.push(field);
    },

    unmaskFrames: function () {
        // remove the masks from iframes
        var masks = Ext.query(".pimcore_iframe_mask");
        for (var i = 0; i < masks.length; i++) {
            masks[i].remove();
        }

        // unmask wysiwyg editors
        for (var i = 0; i < this.fieldsToMask.length; i++) {
            this.fieldsToMask[i].unmask();
        }
    },

    maskFrames: function () {
        // this is for dnd over iframes, with this method it's not nessercery to register the dnd manager in each iframe (wysiwyg)

        // mask wysiwyg editors
        for (var i = 0; i < this.fieldsToMask.length; i++) {
            this.fieldsToMask[i].mask();
        }
    },

    getValues: function () {

        if (!this.layout.rendered) {
            throw "edit not available";
        }

        var dataKeys = Object.keys(this.dataFields);
        var values = {};
        var currentField;
        var mendatoryError = false;

        for (var i = 0; i < dataKeys.length; i++) {

            try {
                if (this.dataFields[dataKeys[i]] && typeof this.dataFields[dataKeys[i]] == "object") {
                    currentField = this.dataFields[dataKeys[i]];
                    if (currentField.isMandatory() == true && this.object.ignoreMandatoryFields != true) {
                        if (currentField.isInvalidMandatory()) {
                            Ext.MessageBox.alert(t("error"), t("mandatory_field_empty") + "<br />- " + currentField.getName());
                            currentField.markMandatory();
                            mendatoryError = true;
                        }
                        else {
                            currentField.unmarkMandatory();
                        }
                    }

                    var currentValue =  currentField.getValue();

                    //only include changed values in save response.
                    if(currentField.isDirty()) {
                        values[currentField.getName()] =  currentValue;
                    }
                }
            }
            catch (e) {
                console.log(e);
                values[currentField.getName()] = "";
            }
        }

        if (mendatoryError) {
            return false;
        }

        return values;
    }

});

pimcore.object.edit.addMethods(pimcore.object.helpers.edit);<|endoftext|>


<commit_before>OV.SettingsSidebarPanel = class extends OV.SidebarPanel
{
    constructor (parentDiv)
    {
        super (parentDiv);
        this.backgroundColorInput = null;
        this.defaultColorInput = null;
        this.defaultColorWarning = null;
    }

    GetTitle ()
    {
        return 'Settings';
    }

    HidePopups ()
    {
        this.backgroundColorInput.pickr.hide ();
        this.defaultColorInput.pickr.hide ();
    }

    InitSettings (settings, defaultSettings, callbacks)
    {
        this.backgroundColorInput = this.AddColorParameter (
            'Background Color',
            'Background color affects only the visualization of the model.',
            null,
            ['#ffffff', '#e3e3e3', '#c9c9c9', '#898989', '#5f5f5f', '#494949', '#383838', '#0f0f0f'],
            settings.backgroundColor,
            callbacks.onBackgroundColorChange
        );
        this.defaultColorInput = this.AddColorParameter (
            'Default Color',
            'Default color appears when the model doesn\'t contain materials.',
            'This setting has no effect on the currently loaded file.',
            ['#ffffff', '#e3e3e3', '#cc3333', '#fac832', '#4caf50', '#3393bd', '#9b27b0', '#fda4b8'],
            settings.defaultColor,
            callbacks.onDefaultColorChange
        );
        this.AddThemeParameter (settings.themeId, callbacks.onThemeChange);
        this.AddResetToDefaultsButton (defaultSettings, callbacks);
    }

    UpdateSettings (settings)
    {
        this.backgroundColorInput.pickr.setColor ('#' + OV.ColorToHexString (settings.backgroundColor));
        this.defaultColorInput.pickr.setColor ('#' + OV.ColorToHexString (settings.defaultColor));
    }

    Update (model)
    {
        let hasDefaultMaterial = OV.HasDefaultMaterial (model);
        if (!hasDefaultMaterial) {
            this.defaultColorInput.warning.show ();
        } else {
            this.defaultColorInput.warning.hide ();
        }
        this.Resize ();
    }

    AddColorParameter (title, description, warningText, predefinedColors, defaultValue, onChange)
    {
        let contentDiv = $('<div>').addClass ('ov_sidebar_settings_content').appendTo (this.contentDiv);
        let titleDiv = $('<div>').addClass ('ov_sidebar_subtitle').appendTo (contentDiv);
        let colorInput = $('<div>').addClass ('color-picker').appendTo (titleDiv);
        $('<span>').html (title).appendTo (titleDiv);
        const pickr = Pickr.create ({
            el : colorInput.get (0),
            theme : 'monolith',
            position : 'left-start',
            swatches : predefinedColors,
            comparison : false,
            default : '#' + OV.ColorToHexString (defaultValue),
            components : {
                preview : false,
                opacity : false,
                hue : true,
                interaction: {
                    hex : false,
                    rgba : false,
                    hsla : false,
                    hsva : false,
                    cmyk : false,
                    input : true,
                    clear : false,
                    save : false
                }
            }
        });
        pickr.on ('change', (color, source, instance) => {
            let rgbaColor = color.toRGBA ();
            let ovColor = new OV.Color (
                parseInt (rgbaColor[0], 10),
                parseInt (rgbaColor[1], 10),
                parseInt (rgbaColor[2], 10)
            );
            onChange (ovColor);
        });
        $('<div>').addClass ('ov_sidebar_settings_padded').html (description).appendTo (contentDiv);
        let warningDiv = null;
        if (warningText !== null) {
            warningDiv = $('<div>').addClass ('ov_sidebar_settings_padded').appendTo (contentDiv);
            OV.CreateSvgIcon (warningDiv, 'warning', 'left_inline light');
            $('<div>').addClass ('ov_sidebar_settings_warning').html (warningText).appendTo (warningDiv);
        }
        return {
            pickr : pickr,
            warning : warningDiv
        };
    }

    AddThemeParameter (defaultValue, onChange)
    {
        function AddRadioButton (contentDiv, defaultValue, themeId, themeName, onChange)
        {
            let row = $('<div>').addClass ('ov_sidebar_settings_row').appendTo (contentDiv);
            let radio = $('<input>').addClass ('ov_radio_button').attr ('type', 'radio').attr ('id', themeId).attr ('name', 'theme').appendTo (row);
            $('<label>').attr ('for', themeId).html (themeName).appendTo (row);
            radio.prop ('checked', themeId === defaultValue);
            radio.change (() => {
                onChange (themeId);
            });
        }

        let contentDiv = $('<div>').addClass ('ov_sidebar_settings_content').appendTo (this.contentDiv);
        // TODO: icon
        let titleDiv = $('<div>').addClass ('ov_sidebar_subtitle').html ('Appearance').appendTo (contentDiv);
        let buttonsDiv = $('<div>').addClass ('ov_sidebar_settings_padded').appendTo (contentDiv);
        AddRadioButton (buttonsDiv, defaultValue, OV.Theme.Light, 'Light', onChange);
        AddRadioButton (buttonsDiv, defaultValue, OV.Theme.Dark, 'Dark', onChange);
        AddRadioButton (buttonsDiv, defaultValue, OV.Theme.System, 'System', onChange);
        return;

    }

    AddResetToDefaultsButton (defaultSettings, callbacks)
    {
        // TODO: reset appearance
        let resetToDefaultsButton = $('<div>').addClass ('ov_button').addClass ('outline').addClass ('ov_sidebar_button').html ('Reset to Default').appendTo (this.contentDiv);
        resetToDefaultsButton.click (() => {
            this.backgroundColorInput.pickr.setColor ('#' + OV.ColorToHexString (defaultSettings.backgroundColor));
            this.defaultColorInput.pickr.setColor ('#' + OV.ColorToHexString (defaultSettings.defaultColor));
            callbacks.onBackgroundColorChange (defaultSettings.backgroundColor);
            callbacks.onDefaultColorChange (defaultSettings.defaultColor);
        });
    }
};
<commit_msg>Reset theme when needed.<commit_after>OV.SettingsSidebarPanel = class extends OV.SidebarPanel
{
    constructor (parentDiv)
    {
        super (parentDiv);
        this.backgroundColorInput = null;
        this.defaultColorInput = null;
        this.defaultColorWarning = null;
        this.themeInput = null;
    }

    GetTitle ()
    {
        return 'Settings';
    }

    HidePopups ()
    {
        this.backgroundColorInput.pickr.hide ();
        this.defaultColorInput.pickr.hide ();
    }

    InitSettings (settings, defaultSettings, callbacks)
    {
        this.backgroundColorInput = this.AddColorParameter (
            'Background Color',
            'Background color affects only the visualization of the model.',
            null,
            ['#ffffff', '#e3e3e3', '#c9c9c9', '#898989', '#5f5f5f', '#494949', '#383838', '#0f0f0f'],
            settings.backgroundColor,
            callbacks.onBackgroundColorChange
        );
        this.defaultColorInput = this.AddColorParameter (
            'Default Color',
            'Default color appears when the model doesn\'t contain materials.',
            'This setting has no effect on the currently loaded file.',
            ['#ffffff', '#e3e3e3', '#cc3333', '#fac832', '#4caf50', '#3393bd', '#9b27b0', '#fda4b8'],
            settings.defaultColor,
            callbacks.onDefaultColorChange
        );
        this.themeInput = this.AddThemeParameter (settings.themeId, callbacks.onThemeChange);
        this.AddResetToDefaultsButton (defaultSettings, callbacks);
    }

    UpdateSettings (settings)
    {
        this.backgroundColorInput.pickr.setColor ('#' + OV.ColorToHexString (settings.backgroundColor));
        this.defaultColorInput.pickr.setColor ('#' + OV.ColorToHexString (settings.defaultColor));
    }

    Update (model)
    {
        let hasDefaultMaterial = OV.HasDefaultMaterial (model);
        if (!hasDefaultMaterial) {
            this.defaultColorInput.warning.show ();
        } else {
            this.defaultColorInput.warning.hide ();
        }
        this.Resize ();
    }

    AddColorParameter (title, description, warningText, predefinedColors, defaultValue, onChange)
    {
        let contentDiv = $('<div>').addClass ('ov_sidebar_settings_content').appendTo (this.contentDiv);
        let titleDiv = $('<div>').addClass ('ov_sidebar_subtitle').appendTo (contentDiv);
        let colorInput = $('<div>').addClass ('color-picker').appendTo (titleDiv);
        $('<span>').html (title).appendTo (titleDiv);
        const pickr = Pickr.create ({
            el : colorInput.get (0),
            theme : 'monolith',
            position : 'left-start',
            swatches : predefinedColors,
            comparison : false,
            default : '#' + OV.ColorToHexString (defaultValue),
            components : {
                preview : false,
                opacity : false,
                hue : true,
                interaction: {
                    hex : false,
                    rgba : false,
                    hsla : false,
                    hsva : false,
                    cmyk : false,
                    input : true,
                    clear : false,
                    save : false
                }
            }
        });
        pickr.on ('change', (color, source, instance) => {
            let rgbaColor = color.toRGBA ();
            let ovColor = new OV.Color (
                parseInt (rgbaColor[0], 10),
                parseInt (rgbaColor[1], 10),
                parseInt (rgbaColor[2], 10)
            );
            onChange (ovColor);
        });
        $('<div>').addClass ('ov_sidebar_settings_padded').html (description).appendTo (contentDiv);
        let warningDiv = null;
        if (warningText !== null) {
            warningDiv = $('<div>').addClass ('ov_sidebar_settings_padded').appendTo (contentDiv);
            OV.CreateSvgIcon (warningDiv, 'warning', 'left_inline light');
            $('<div>').addClass ('ov_sidebar_settings_warning').html (warningText).appendTo (warningDiv);
        }
        return {
            pickr : pickr,
            warning : warningDiv
        };
    }

    AddThemeParameter (defaultValue, onChange)
    {
        function AddRadioButton (contentDiv, themeId, themeName, onChange)
        {
            let row = $('<div>').addClass ('ov_sidebar_settings_row').appendTo (contentDiv);
            let radio = $('<input>').addClass ('ov_radio_button').attr ('type', 'radio').attr ('id', themeId.toString ()).attr ('name', 'theme').appendTo (row);
            $('<label>').attr ('for', themeId.toString ()).html (themeName).appendTo (row);
            radio.change (() => {
                onChange (themeId);
            });
            return radio;
        }

        function Select (radioButtons, defaultValue)
        {
            for (let i = 0; i < radioButtons.length; i++) {
                let radioButton = radioButtons[i];
                radioButton.prop ('checked', radioButton.attr ('id') === defaultValue.toString ());
            }
        }

        let contentDiv = $('<div>').addClass ('ov_sidebar_settings_content').appendTo (this.contentDiv);
        // TODO: icon
        let titleDiv = $('<div>').addClass ('ov_sidebar_subtitle').html ('Appearance').appendTo (contentDiv);
        let buttonsDiv = $('<div>').addClass ('ov_sidebar_settings_padded').appendTo (contentDiv);
        let result = {
            buttons : [],
            select: (value) => {
                Select (result.buttons, value);
            }
        };
        result.buttons.push (AddRadioButton (buttonsDiv, OV.Theme.Light, 'Light', onChange));
        result.buttons.push (AddRadioButton (buttonsDiv, OV.Theme.Dark, 'Dark', onChange));
        result.buttons.push (AddRadioButton (buttonsDiv, OV.Theme.System, 'System', onChange));
        Select (result.buttons, defaultValue);
        return result;

    }

    AddResetToDefaultsButton (defaultSettings, callbacks)
    {
        let resetToDefaultsButton = $('<div>').addClass ('ov_button').addClass ('outline').addClass ('ov_sidebar_button').html ('Reset to Default').appendTo (this.contentDiv);
        resetToDefaultsButton.click (() => {
            this.backgroundColorInput.pickr.setColor ('#' + OV.ColorToHexString (defaultSettings.backgroundColor));
            this.defaultColorInput.pickr.setColor ('#' + OV.ColorToHexString (defaultSettings.defaultColor));
            this.themeInput.select (defaultSettings.themeId);
            callbacks.onBackgroundColorChange (defaultSettings.backgroundColor);
            callbacks.onDefaultColorChange (defaultSettings.defaultColor);
            callbacks.onThemeChange (defaultSettings.themeId);
        });
    }
};
<|endoftext|>


<commit_before>import alt from '../alt'
import _ from 'underscore'
import Immutable from 'immutable'
import immutable from 'alt/utils/ImmutableUtil'
import regex from '../utils/regex'
import browseActions from '../actions/browseActions'

@immutable
class BrowseStore {

  constructor () {
    // Bind actions.
    this.bindActions(browseActions)

    // Initialize state.
    this.keys = Immutable.OrderedMap()
    this.keysIndex = Immutable.List()
    this.selectedKey = null
    this.selectedIndex = null
    this.loading = false
    this.loaded = false
    this.finished = false
    this.error = null
    this.offset = 0
    this.match = null
    this.matchRegExp = null
  }

  /* General
   ****************************************************************************/
  onResetKeys () {
    this.keys = this.keys.clear()
    this.keysIndex = this.keysIndex.clear()
    this.selectedKey = null
    this.selectedIndex = null
    this.loading = false
    this.loaded = false
    this.finished = false
    this.error = null
    this.offset = 0
    this.match = null
    this.matchRegExp = null
  }

  onSetOffset (offset) {
    this.offset = offset
  }

  onSetMatch (match) {
    this.matchRegExp = null
    if (match && match.length) {
      this.match = match
    } else {
      this.match = null
    }
  }

  onToggleSelectedKey (key) {
    if (key) {
      let index = this.keysIndex.indexOf(key)
      this.onToggleSelected({key, index})
    } else {
      this.onToggleSelected()
    }
  }

  onToggleSelectedIndex (index) {
    let key = this.keysIndex.get(index)
    this.onToggleSelected({key, index})
  }

  onToggleSelected (item = {}) {
    let {key, index} = item
    key = (typeof key !== 'undefined') ? key : this.selectedKey.get('key')
    index = (typeof index !== 'undefined') ? index : this.selectedIndex
    this.keys = this.keys.withMutations((keys) => {
      if (keys.get(key).get('selected')) {
        keys.update(key, (item) => item.set('selected', false))
        this.selectedKey = null
        this.selectedIndex = null
      } else {
        if (this.selectedKey) {
          keys.update(this.selectedKey.get('key'), (item) => item.set('selected', false))
        }
        keys.update(key, (item) => item.set('selected', true))
        this.selectedKey = keys.get(key)
        this.selectedIndex = index
      }
    })
  }

  /* Fetch Keys Lifecycle
   ****************************************************************************/
  onFetchKeys () {
    this.keys = this.keys.clear()
    this.keysIndex = this.keysIndex.clear()
    this.selectedKey = null
    this.selectedIndex = null
    this.loading = true
    this.loaded = false
    this.finished = false
    this.error = null
    this.offset = 0

    // Compute match regular expression.
    if (this.match && !this.matchRegExp) {
      this.matchRegExp = regex.fromGlob(this.match)
    }
  }

  onFetchKeysAdd (newKeys) {
    this.loading = false
    this.loaded = true
    this.keys = this.keys.withMutations((keys) => {
      this.keysIndex = this.keysIndex.withMutations((index) => {
        newKeys.forEach((key) => {
          keys.set(key.key, Immutable.Map(key))
          index.push(key.key)
        })
      })
    })
  }

  onFetchKeysFailed (err) {
    this.loading = false
    this.loaded = true
    this.error = err
  }

  onFetchKeysFinished () {
    this.loading = false
    this.loaded = true
    this.finished = true
  }

  /* Fetch Keys Lifecycle
   ****************************************************************************/
  onFetchValues (keys) {
    // @todo Maybe set a loading flag on each key?
  }

  onFetchValuesAdd (values) {
    this.keys = this.keys.withMutations((keys) => {
      _.each(values, (value, key) => {
        keys.update(key, (item) => item.set('value', value))
      })
    })
  }

  onFetchValuesFailed (err) {
    this.error = err
  }
}

export default alt.createStore(BrowseStore)
<commit_msg>Fixes code comment typo.<commit_after>import alt from '../alt'
import _ from 'underscore'
import Immutable from 'immutable'
import immutable from 'alt/utils/ImmutableUtil'
import regex from '../utils/regex'
import browseActions from '../actions/browseActions'

@immutable
class BrowseStore {

  constructor () {
    // Bind actions.
    this.bindActions(browseActions)

    // Initialize state.
    this.keys = Immutable.OrderedMap()
    this.keysIndex = Immutable.List()
    this.selectedKey = null
    this.selectedIndex = null
    this.loading = false
    this.loaded = false
    this.finished = false
    this.error = null
    this.offset = 0
    this.match = null
    this.matchRegExp = null
  }

  /* General
   ****************************************************************************/
  onResetKeys () {
    this.keys = this.keys.clear()
    this.keysIndex = this.keysIndex.clear()
    this.selectedKey = null
    this.selectedIndex = null
    this.loading = false
    this.loaded = false
    this.finished = false
    this.error = null
    this.offset = 0
    this.match = null
    this.matchRegExp = null
  }

  onSetOffset (offset) {
    this.offset = offset
  }

  onSetMatch (match) {
    this.matchRegExp = null
    if (match && match.length) {
      this.match = match
    } else {
      this.match = null
    }
  }

  onToggleSelectedKey (key) {
    if (key) {
      let index = this.keysIndex.indexOf(key)
      this.onToggleSelected({key, index})
    } else {
      this.onToggleSelected()
    }
  }

  onToggleSelectedIndex (index) {
    let key = this.keysIndex.get(index)
    this.onToggleSelected({key, index})
  }

  onToggleSelected (item = {}) {
    let {key, index} = item
    key = (typeof key !== 'undefined') ? key : this.selectedKey.get('key')
    index = (typeof index !== 'undefined') ? index : this.selectedIndex
    this.keys = this.keys.withMutations((keys) => {
      if (keys.get(key).get('selected')) {
        keys.update(key, (item) => item.set('selected', false))
        this.selectedKey = null
        this.selectedIndex = null
      } else {
        if (this.selectedKey) {
          keys.update(this.selectedKey.get('key'), (item) => item.set('selected', false))
        }
        keys.update(key, (item) => item.set('selected', true))
        this.selectedKey = keys.get(key)
        this.selectedIndex = index
      }
    })
  }

  /* Fetch Keys Lifecycle
   ****************************************************************************/
  onFetchKeys () {
    this.keys = this.keys.clear()
    this.keysIndex = this.keysIndex.clear()
    this.selectedKey = null
    this.selectedIndex = null
    this.loading = true
    this.loaded = false
    this.finished = false
    this.error = null
    this.offset = 0

    // Compute match regular expression.
    if (this.match && !this.matchRegExp) {
      this.matchRegExp = regex.fromGlob(this.match)
    }
  }

  onFetchKeysAdd (newKeys) {
    this.loading = false
    this.loaded = true
    this.keys = this.keys.withMutations((keys) => {
      this.keysIndex = this.keysIndex.withMutations((index) => {
        newKeys.forEach((key) => {
          keys.set(key.key, Immutable.Map(key))
          index.push(key.key)
        })
      })
    })
  }

  onFetchKeysFailed (err) {
    this.loading = false
    this.loaded = true
    this.error = err
  }

  onFetchKeysFinished () {
    this.loading = false
    this.loaded = true
    this.finished = true
  }

  /* Fetch Values Lifecycle
   ****************************************************************************/
  onFetchValues (keys) {
    // @todo Maybe set a loading flag on each key?
  }

  onFetchValuesAdd (values) {
    this.keys = this.keys.withMutations((keys) => {
      _.each(values, (value, key) => {
        keys.update(key, (item) => item.set('value', value))
      })
    })
  }

  onFetchValuesFailed (err) {
    this.error = err
  }
}

export default alt.createStore(BrowseStore)
<|endoftext|>


<commit_before>'use strict';

var env, s3, stream, util, client, bucket;

env    = require('../../config/environment_vars');
s3     = require('aws-sdk').S3;
stream = require('stream');
util   = require('util');

// create an AWS S3 client with the config data
client = new s3({
  accessKeyId: env.AWS_ACCESS_KEY_ID,
  secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
  region: env.AWS_REGION
});
bucket = env.S3_BUCKET;

function s3Stream(image){
  /* jshint validthis:true */
  if (!(this instanceof s3Stream)){
    return new s3Stream(image);
  }
  stream.Readable.call(this, { objectMode : true });
  this.image = image;
  this.ended = false;
}

util.inherits(s3Stream, stream.Readable);

s3Stream.prototype._read = function(){
  var _this = this;

  if ( this.ended ){ return; }

  // pass through if there is an error on the image object
  if (this.image.isError()){
    this.ended = true;
    this.push(this.image);
    return this.push(null);
  }

  // Set the AWS options
  var awsOptions = {
    Bucket: bucket,
    Key: this.image.path.replace(/^\//,'')
  };

  this.image.log.time('s3');

  client.getObject(awsOptions, function(err, data){
    _this.image.log.timeEnd('s3');

    // if there is an error store it on the image object and pass it along
    if (err) {
      _this.image.error = new Error(err);
    }

    // if not store the image buffer
    else {
      _this.image.contents = data.Body;
      _this.image.originalContentLength = data.Body.length;
    }

    _this.ended = true;
    _this.push(_this.image);
    _this.push(null);
  });
};

module.exports = s3Stream;
<commit_msg>Stopped lack of AWS credentials from throwing an error<commit_after>'use strict';

var env, s3, stream, util, client, bucket;

env    = require('../../config/environment_vars');
s3     = require('aws-sdk').S3;
stream = require('stream');
util   = require('util');

try {
  // create an AWS S3 client with the config data
  client = new s3({
    accessKeyId: env.AWS_ACCESS_KEY_ID,
    secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
    region: env.AWS_REGION
  });
  bucket = env.S3_BUCKET;
} catch(e) {

}

function s3Stream(image){
  /* jshint validthis:true */
  if (!(this instanceof s3Stream)){
    return new s3Stream(image);
  }
  stream.Readable.call(this, { objectMode : true });
  this.image = image;
  this.ended = false;
}

util.inherits(s3Stream, stream.Readable);

s3Stream.prototype._read = function(){
  var _this = this;

  if ( this.ended ){ return; }

  // pass through if there is an error on the image object
  if (this.image.isError()){
    this.ended = true;
    this.push(this.image);
    return this.push(null);
  }

  // Set the AWS options
  var awsOptions = {
    Bucket: bucket,
    Key: this.image.path.replace(/^\//,'')
  };

  this.image.log.time('s3');

  client.getObject(awsOptions, function(err, data){
    _this.image.log.timeEnd('s3');

    // if there is an error store it on the image object and pass it along
    if (err) {
      _this.image.error = new Error(err);
    }

    // if not store the image buffer
    else {
      _this.image.contents = data.Body;
      _this.image.originalContentLength = data.Body.length;
    }

    _this.ended = true;
    _this.push(_this.image);
    _this.push(null);
  });
};

module.exports = s3Stream;
<|endoftext|>


<commit_before>/*
May it be usefull - download scenario just from browser

https://github.com/eligrey/FileSaver.js
var blob = new Blob(["Hello, world!"], {type: "text/plain;charset=utf-8"});
saveAs(blob, "hello world.txt");
 */

/*===================================================================================================================================*/
/** Global variables * */
// event id
var eventId = 0;

// browser window/tab uuid
var tabUuid = guid();

// is tracking mouse enabled
var trackMove = false;
/** Global variables * */

// base url to make links right
var baseUrl = '';
/*===================================================================================================================================*/

/**
 * Based on firebug method of getting xpath of dom element
 */
function getElementXPath(element) {
	if (element && element.id)
		return '// *[@id="' + element.id + '"]';
	else
		return getElementTreeXPath(element);
}

function getElementTreeXPath(element) {
	var paths = [];

	// Use nodeName (instead of localName) so namespace prefix is included (if
	// any).
	for (; element && element.nodeType == 1; element = element.parentNode) {
		var index = 0;
		for (var sibling = element.previousSibling; sibling; sibling = sibling.previousSibling) {
			// Ignore document type declaration.
			if (sibling.nodeType == Node.DOCUMENT_TYPE_NODE)
				continue;

			if (sibling.nodeName == element.nodeName)
				++index;
		}

		var tagName = element.nodeName.toLowerCase();
		var pathIndex = (index ? "[" + (index + 1) + "]" : "");
		paths.splice(0, 0, tagName + pathIndex);
	}

	return paths.length ? "/" + paths.join("/") : null;
}

/**
 * Make json of mouse event.
 *
 * @param mouseEvent
 * @returns {___anonymous1625_1630}
 */
function getEventInfo(mouseEvent) {
	if (mouseEvent === undefined)
		mouseEvent = window.event;

	var target = 'target' in mouseEvent ? mouseEvent.target
			: mouseEvent.srcElement;

	mouseEvent.target = target;

	var result = new Object;
	// To be really sure what target element was, it may be useful to hold the
	// whole path to target element
	// var path = mouseEvent.path;
	// var xpaths = [];
	// for(var i=0;i<path.length;i++){
	// if(path[i] && path[i]!=document && path[i]!=window){
	// xpaths.push(getElementXPath(path[i]))
	// }
	// }
	// result['path'] = xpaths;

	result['tabuuid'] = tabUuid;
	result['type'] = mouseEvent.type;
	result['url'] = window.location.href;
	result['charCode'] = mouseEvent.charCode;
	result['button'] = mouseEvent.button;

	result['target'] = getElementXPath(mouseEvent.target);
	result['timestamp'] = mouseEvent.timeStamp;

	result['screenX'] = mouseEvent.screenX;
	result['screenY'] = mouseEvent.screenY;

	result['pageX'] = mouseEvent.pageX;
	result['pageY'] = mouseEvent.pageY;

	result['screen.width'] = screen.width;
	result['screen.height'] = screen.height;

	result['window.width'] = window.outerWidth;
	result['window.height'] = window.outerHeight;

	result['page.width'] = window.innerWidth;
	result['page.height'] = window.innerHeight;

	return result;
}

/**
 * Store event to session storage
 *
 * @param eventid
 * @param eventdata
 */
function saveToStorage(eventid, eventdata) {
	if (typeof (window.sessionStorage) == "undefined") {
		console.log('No support of window.sessionStorage');
		return;
	}

	window.sessionStorage.setItem("recorder.max", eventid);
	window.sessionStorage.setItem('recorder.eventId.' + eventid, eventdata);
}

/**
 * Clear session storage
 */
function clearStorage() {
	eventId = 0;
	sessionStorage.clear();
}

/**
 * Generate browser window/tab uuid
 *
 * @returns {String}
 */
function guid() {
	function s4() {
		return Math.floor((1 + Math.random()) * 0x10000).toString(16)
				.substring(1);
	}
	return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4()
			+ s4() + s4();
}

/**
 * Process mouse event
 */
var TrackMouse = function(mouseEvent) {
	if (mouseEvent.type == 'mousemove' && trackMove == false) {
		return;
	}
	if (mouseEvent.target && mouseEvent.target.id) {
		// ignoring self buttons
		if (mouseEvent.target.id.indexOf("no-track-flight-cp") === 0) {
			return;
		}
	}
	var data = JSON.stringify(getEventInfo(mouseEvent));
	saveToStorage(eventId, data);
	console.log("Event: " + data + "\n");
	eventId++;
};

/**
 * Process keyboard event
 */
var TrackKeyboard = function(keyboardEvent) {
	if (keyboardEvent.target && keyboardEvent.target.id) {
		// ignoring self buttons
		if (keyboardEvent.target.id.indexOf("no-track-flight-cp") === 0) {
			return;
		}
	}

	var data = JSON.stringify(getEventInfo(keyboardEvent));
	saveToStorage(eventId, data);
	console.log("Event: " + data + "\n");
	eventId++;
};

/**
 * Start recorder
 *
 * @returns {Boolean}
 */
function startRecorder() {
	if (document.addEventListener) {
		document.addEventListener('mousedown', TrackMouse);
		document.addEventListener('mousemove', TrackMouse);
		document.addEventListener('keypress', TrackKeyboard);
	} else {
		document.attachEvent('mousedown', TrackMouse);
		document.attachEvent('mousemove', TrackMouse);
		document.attachEvent('keypress', TrackKeyboard);
	}
	if (typeof (window.sessionStorage) == "undefined") {
		console.log('No support of window.sessionStorage');
		return false;
	}

	window.sessionStorage.setItem('recorder.active', true);
	eventId = +window.sessionStorage.getItem('recorder.max') + 1;
	if (!eventId) {
		eventId = 0;
	}
}

/**
 * Stop recorder
 *
 * @returns {Boolean}
 */
function stopRecorder() {
	if (document.removeEventListener) {
		document.removeEventListener('mousedown', TrackMouse);
		document.removeEventListener('mousemove', TrackMouse);
		document.removeEventListener('keypress', TrackKeyboard);
	} else {
		document.detachEvent('mousedown', TrackMouse);
		document.detachEvent('mousemove', TrackMouse);
		document.detachEvent('keypress', TrackKeyboard);
	}

	if (typeof (window.sessionStorage) == "undefined") {
		console.log('No support of window.sessionStorage');
		return false;
	}

	window.sessionStorage.removeItem('recorder.active');
}

/**
 * Convert all event to json
 *
 * @returns
 */
function getEventsAsString() {
	if (typeof (window.sessionStorage) == "undefined") {
		console.log('No support of window.sessionStorage');
		return;
	}
	var storage = window.sessionStorage;

	var events = [];
	for ( var key in storage) {
		if (key.indexOf('recorder.eventId.') === 0) {
			events.push(storage.getItem(key));
		}
	}

	return JSON.stringify(events)
}

/**
 * method to show/hide control panel
 *
 * @param event
 */
function controlHook(event) {
	if (event.ctrlKey && event.altKey && (event.which || event.keyCode) == 38) {
		var panel = document.getElementById("flight-cp");
		panel.style.display = 'block';
	}
	if (event.ctrlKey && event.altKey && (event.which || event.keyCode) == 40) {
		var panel = document.getElementById("flight-cp");
		panel.style.display = 'none';
	}
}

/**
 * Will recording be continued in case of page reload
 *
 * @returns {Boolean}
 */
function shouldStartOnLoad() {
	if (typeof (window.sessionStorage) == "undefined") {
		console.log('No support of window.sessionStorage');
		return false;
	}

	if (window.sessionStorage.getItem('recorder.active')) {
		return true;
	}

	return false
}

/**
 * Inject control panel specific js-code and markup
 */
function addControlHook() {
	tabUuid = guid();

	var script = document.createElement('script')
	script.type = 'text/javascript'
	script.charset = 'utf-8'
	script.text = ' \
		function flight_hide(){ \
	        var panel = document.getElementById("flight-cp"); \
	        panel.style.display="none"; \
		}\
		function flight_getEvents(){ \
        	document.getElementById("data").value = getEventsAsString();\
        	return true; \
	    } \
	    function flight_start(){ \
			flight_hide(); \
	    	startRecorder(); \
	    } \
	    function flight_stop(){ \
	        stopRecorder(); \
	    } \
	    function flight_clear(){ \
			stopRecorder(); \
	        clearStorage(); \
	    }';
	document.body.appendChild(script);

	var div = document.createElement("div");
	div.id = "flight-cp";
	div.style.display = 'none';

	div.innerHTML = '
JSFlightRecorder

https://github.com/d0k1/JSFlightRecorder

Control panel
 \

 \
	   <form action="'+baseUrl+'/jsflight/recorder/download" method="post" target="_blank"> \
	       <input id="data" type="hidden" value="secret" name="data"/> \
	       <input type="submit" value="Download recording" onclick="flight_getEvents()"/> \
	   </form>\

\

 \
	        <button id="no-track-flight-cp1" onclick="flight_start()">Start</button> \
	        <button id="no-track-flight-cp2" onclick="flight_stop()">Stop</button> \
	        <button id="no-track-flight-cp3" onclick="flight_clear()">Clear</button> \
	        <button id="no-track-flight-cp4" onclick="flight_hide()">Hide</button> \
	        <button id="no-track-flight-cp5" onclick="window.open(\''+baseUrl+'/jsflight/recorder/status\', \'_blank\')">Status</button> \

 \

 \
			<button id="no-track-flight-cp6" onclick="trackMove=true;">Track Move</button> \
			<button id="no-track-flight-cp7" onclick="trackMove=false">Dont Track Move</button> \

 \
		';

	document.body.appendChild(div);

	if (document.addEventListener) {
		document.addEventListener('keyup', controlHook);
	} else {
		document.attachEvent('keyup', controlHook);
	}

	if (shouldStartOnLoad()) {
		startRecorder();
	}
}

/**
 * Clean up when tab is closing
 */
function removeControlHook() {
	if (document.removeEventListener) {
		document.removeEventListener('keyup', controlHook);
	} else {
		document.detachEvent('keyup', controlHook);
	}
}

/**
 * Init method. should be called on host page. uses onload and onbeforeunload
 * mechanics to add/remove event listeners
 *
 * @param trackMouseMove
 */
function addJSFlightHooksOnDocumentLoad(url, trackMouseMove) {
	window.onload = function() {
		addControlHook();
	}
	window.onbeforeunload = function() {
		removeControlHook();
	};

	baseUrl = url;

	if (trackMouseMove === true) {
		trackMove = true;
	}
}<commit_msg>making namespace and options<commit_after>/*
May it be usefull - download scenario just from browser

https://github.com/eligrey/FileSaver.js
var blob = new Blob(["Hello, world!"], {type: "text/plain;charset=utf-8"});
saveAs(blob, "hello world.txt");
 */

/*===================================================================================================================================*/
/** Global variables * */
// event id
var eventId = 0;

// browser window/tab uuid
var tabUuid = guid();

// is tracking mouse enabled
var trackMove = false;
/** Global variables * */

// base url to make links right
var baseUrl = '';

// recorder options
var options = {
		baseUrl:'',
		downloadPath:'/jsflight/recorder/download',
		statusPath:'/jsflight/recorder/status',
		trackMouse:false,
		trackHash:false,
		trackXhr:false,
		propertyProvider: function(prop){}
}
/*===================================================================================================================================*/

/**
 * Based on firebug method of getting xpath of dom element
 */
function getElementXPath(element) {
	if (element && element.id)
		return '// *[@id="' + element.id + '"]';
	else
		return getElementTreeXPath(element);
}

function getElementTreeXPath(element) {
	var paths = [];

	// Use nodeName (instead of localName) so namespace prefix is included (if
	// any).
	for (; element && element.nodeType == 1; element = element.parentNode) {
		var index = 0;
		for (var sibling = element.previousSibling; sibling; sibling = sibling.previousSibling) {
			// Ignore document type declaration.
			if (sibling.nodeType == Node.DOCUMENT_TYPE_NODE)
				continue;

			if (sibling.nodeName == element.nodeName)
				++index;
		}

		var tagName = element.nodeName.toLowerCase();
		var pathIndex = (index ? "[" + (index + 1) + "]" : "");
		paths.splice(0, 0, tagName + pathIndex);
	}

	return paths.length ? "/" + paths.join("/") : null;
}

/**
 * Make json of mouse event.
 *
 * @param mouseEvent
 * @returns {___anonymous1625_1630}
 */
function getEventInfo(mouseEvent) {
	if (mouseEvent === undefined)
		mouseEvent = window.event;

	var target = 'target' in mouseEvent ? mouseEvent.target
			: mouseEvent.srcElement;

	mouseEvent.target = target;

	var result = new Object;
	// To be really sure what target element was, it may be useful to hold the
	// whole path to target element
	// var path = mouseEvent.path;
	// var xpaths = [];
	// for(var i=0;i<path.length;i++){
	// if(path[i] && path[i]!=document && path[i]!=window){
	// xpaths.push(getElementXPath(path[i]))
	// }
	// }
	// result['path'] = xpaths;

	result['tabuuid'] = tabUuid;
	result['type'] = mouseEvent.type;
	result['url'] = window.location.href;
	result['charCode'] = mouseEvent.charCode;
	result['button'] = mouseEvent.button;

	result['target'] = getElementXPath(mouseEvent.target);
	result['timestamp'] = mouseEvent.timeStamp;

	result['screenX'] = mouseEvent.screenX;
	result['screenY'] = mouseEvent.screenY;

	result['pageX'] = mouseEvent.pageX;
	result['pageY'] = mouseEvent.pageY;

	result['screen.width'] = screen.width;
	result['screen.height'] = screen.height;

	result['window.width'] = window.outerWidth;
	result['window.height'] = window.outerHeight;

	result['page.width'] = window.innerWidth;
	result['page.height'] = window.innerHeight;

	return result;
}

/**
 * Store event to session storage
 *
 * @param eventid
 * @param eventdata
 */
function saveToStorage(eventid, eventdata) {
	if (typeof (window.sessionStorage) == "undefined") {
		console.log('No support of window.sessionStorage');
		return;
	}

	window.sessionStorage.setItem("recorder.max", eventid);
	window.sessionStorage.setItem('recorder.eventId.' + eventid, eventdata);
}

/**
 * Clear session storage
 */
function clearStorage() {
	eventId = 0;
	sessionStorage.clear();
}

/**
 * Generate browser window/tab uuid
 *
 * @returns {String}
 */
function guid() {
	function s4() {
		return Math.floor((1 + Math.random()) * 0x10000).toString(16)
				.substring(1);
	}
	return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4()
			+ s4() + s4();
}

/**
 * Process mouse event
 */
var TrackMouse = function(mouseEvent) {
	if (mouseEvent.type == 'mousemove' && trackMove == false) {
		return;
	}
	if (mouseEvent.target && mouseEvent.target.id) {
		// ignoring self buttons
		if (mouseEvent.target.id.indexOf("no-track-flight-cp") === 0) {
			return;
		}
	}
	var data = JSON.stringify(getEventInfo(mouseEvent));
	saveToStorage(eventId, data);
	console.log("Event: " + data + "\n");
	eventId++;
};

/**
 * Process keyboard event
 */
var TrackKeyboard = function(keyboardEvent) {
	if (keyboardEvent.target && keyboardEvent.target.id) {
		// ignoring self buttons
		if (keyboardEvent.target.id.indexOf("no-track-flight-cp") === 0) {
			return;
		}
	}

	var data = JSON.stringify(getEventInfo(keyboardEvent));
	saveToStorage(eventId, data);
	console.log("Event: " + data + "\n");
	eventId++;
};

/**
 * Start recorder
 *
 * @returns {Boolean}
 */
function startRecorder() {
	if (document.addEventListener) {
		document.addEventListener('mousedown', TrackMouse);
		document.addEventListener('mousemove', TrackMouse);
		document.addEventListener('keypress', TrackKeyboard);
	} else {
		document.attachEvent('mousedown', TrackMouse);
		document.attachEvent('mousemove', TrackMouse);
		document.attachEvent('keypress', TrackKeyboard);
	}
	if (typeof (window.sessionStorage) == "undefined") {
		console.log('No support of window.sessionStorage');
		return false;
	}

	window.sessionStorage.setItem('recorder.active', true);
	eventId = +window.sessionStorage.getItem('recorder.max') + 1;
	if (!eventId) {
		eventId = 0;
	}
}

/**
 * Stop recorder
 *
 * @returns {Boolean}
 */
function stopRecorder() {
	if (document.removeEventListener) {
		document.removeEventListener('mousedown', TrackMouse);
		document.removeEventListener('mousemove', TrackMouse);
		document.removeEventListener('keypress', TrackKeyboard);
	} else {
		document.detachEvent('mousedown', TrackMouse);
		document.detachEvent('mousemove', TrackMouse);
		document.detachEvent('keypress', TrackKeyboard);
	}

	if (typeof (window.sessionStorage) == "undefined") {
		console.log('No support of window.sessionStorage');
		return false;
	}

	window.sessionStorage.removeItem('recorder.active');
}

/**
 * Convert all event to json
 *
 * @returns
 */
function getEventsAsString() {
	if (typeof (window.sessionStorage) == "undefined") {
		console.log('No support of window.sessionStorage');
		return;
	}
	var storage = window.sessionStorage;

	var events = [];
	for ( var key in storage) {
		if (key.indexOf('recorder.eventId.') === 0) {
			events.push(storage.getItem(key));
		}
	}

	return JSON.stringify(events)
}

/**
 * method to show/hide control panel
 *
 * @param event
 */
function controlHook(event) {
	if (event.ctrlKey && event.altKey && (event.which || event.keyCode) == 38) {
		var panel = document.getElementById("flight-cp");
		panel.style.display = 'block';
	}
	if (event.ctrlKey && event.altKey && (event.which || event.keyCode) == 40) {
		var panel = document.getElementById("flight-cp");
		panel.style.display = 'none';
	}
}

/**
 * Will recording be continued in case of page reload
 *
 * @returns {Boolean}
 */
function shouldStartOnLoad() {
	if (typeof (window.sessionStorage) == "undefined") {
		console.log('No support of window.sessionStorage');
		return false;
	}

	if (window.sessionStorage.getItem('recorder.active')) {
		return true;
	}

	return false
}

/**
 * Inject control panel specific js-code and markup
 */
function addControlHook() {
	tabUuid = guid();

	var script = document.createElement('script')
	script.type = 'text/javascript'
	script.charset = 'utf-8'
	script.text = ' \
		function flight_hide(){ \
	        var panel = document.getElementById("flight-cp"); \
	        panel.style.display="none"; \
		}\
		function flight_getEvents(){ \
        	document.getElementById("data").value = getEventsAsString();\
        	return true; \
	    } \
	    function flight_start(){ \
			flight_hide(); \
	    	startRecorder(); \
	    } \
	    function flight_stop(){ \
	        stopRecorder(); \
	    } \
	    function flight_clear(){ \
			stopRecorder(); \
	        clearStorage(); \
	    }';
	document.body.appendChild(script);

	var div = document.createElement("div");
	div.id = "flight-cp";
	div.style.display = 'none';

	div.innerHTML = '
JSFlightRecorder

https://github.com/d0k1/JSFlightRecorder

Control panel
 \

 \
	   <form action="'+baseUrl+'/jsflight/recorder/download" method="post" target="_blank"> \
	       <input id="data" type="hidden" value="secret" name="data"/> \
	       <input type="submit" value="Download recording" onclick="flight_getEvents()"/> \
	   </form>\

\

 \
	        <button id="no-track-flight-cp1" onclick="flight_start()">Start</button> \
	        <button id="no-track-flight-cp2" onclick="flight_stop()">Stop</button> \
	        <button id="no-track-flight-cp3" onclick="flight_clear()">Clear</button> \
	        <button id="no-track-flight-cp4" onclick="flight_hide()">Hide</button> \
	        <button id="no-track-flight-cp5" onclick="window.open(\''+baseUrl+'/jsflight/recorder/status\', \'_blank\')">Status</button> \

 \

 \
			<button id="no-track-flight-cp6" onclick="trackMove=true;">Track Move</button> \
			<button id="no-track-flight-cp7" onclick="trackMove=false">Dont Track Move</button> \

 \
		';

	document.body.appendChild(div);

	if (document.addEventListener) {
		document.addEventListener('keyup', controlHook);
	} else {
		document.attachEvent('keyup', controlHook);
	}

	if (shouldStartOnLoad()) {
		startRecorder();
	}
}

/**
 * Clean up when tab is closing
 */
function removeControlHook() {
	if (document.removeEventListener) {
		document.removeEventListener('keyup', controlHook);
	} else {
		document.detachEvent('keyup', controlHook);
	}
}

/**
 * Init method. should be called on host page. uses onload and onbeforeunload
 * mechanics to add/remove event listeners
 *
 * @param trackMouseMove
 */
function addJSFlightHooksOnDocumentLoad(url, trackMouseMove) {
	window.onload = function() {
		addControlHook();
	}
	window.onbeforeunload = function() {
		removeControlHook();
	};

	baseUrl = url;

	if (trackMouseMove === true) {
		trackMove = true;
	}
}<|endoftext|>