<commit_before>import Underscore from 'underscore';
import Widget from '../Widget';
import myTemplate from './template.html';
import candela from '../../../../../src/candela';
import './style.css';

let STATUS = {
  OK: 0,
  LOADING: 1,
  NO_VIS_SELECTED: 2,
  NOT_ENOUGH_MAPPINGS: 3,
  FAILED_RENDER: 4
};

let VisualizationView = Widget.extend({
  initialize: function () {
    Widget.prototype.initialize.apply(this, arguments);

    this.friendlyName = 'Visualization';

    this.statusText.onclick = () => {
      window.mainPage.overlay.render('VisualizationLibrary');
    };
    this.statusText.title = 'Click to select a different visualization.';

    this.icons.splice(0, 0, {
      src: () => {
        return window.mainPage.currentUser.preferences
          .hasSeenAllTips(this.getDefaultTips()) ? Widget.infoIcon : Widget.newInfoIcon;
      },
      title: () => {
        return 'About this panel';
      },
      onclick: () => {
        this.renderInfoScreen();
      }
    });

    this.status = STATUS.LOADING;
    this.icons.splice(0, 0, {
      src: () => {
        if (this.status === STATUS.LOADING) {
          return Widget.spinnerIcon;
        } else if (this.status === STATUS.OK) {
          return Widget.okayIcon;
        } else {
          return Widget.warningIcon;
        }
      },
      title: () => {
        if (this.status === STATUS.LOADING) {
          return 'The visualization hasn\'t finished loading yet';
        } else if (this.status === STATUS.OK) {
          return 'The visualization appears to be working';
        } else {
          return 'Something isn\'t quite right; click for details';
        }
      },
      onclick: () => {
        this.renderHelpScreen();
      }
    });

    this.listenTo(window.mainPage, 'rl:changeProject',
      this.handleNewProject);
    this.handleNewProject();
  },
  handleNewProject: function () {
    this.$el.html('');
    this.status = STATUS.LOADING;
    this.vis = null;

    this.listenTo(window.mainPage.project, 'rl:changeVisualizations',
      this.render);
    this.listenTo(window.mainPage.project, 'rl:changeMatchings',
      this.render);
  },
  renderInfoScreen: function () {
    window.mainPage.helpLayer.showTips(this.getDefaultTips());
  },
  renderHelpScreen: function () {
    if (this.status === STATUS.LOADING) {
      window.mainPage.overlay.renderLoadingScreen('Still attempting to render the visualization...');
    } else if (this.status === STATUS.OK) {
      window.mainPage.overlay.renderSuccessScreen('The visualization appears to be functioning correctly.');
    } else if (this.status === STATUS.NO_VIS_SELECTED) {
      window.mainPage.overlay.renderUserErrorScreen('You have not chosen a visualization yet. Click  here to choose one.');
    } else if (this.status === STATUS.NOT_ENOUGH_MAPPINGS) {
      window.mainPage.overlay.renderUserErrorScreen('This visualization needs more data matchings. Make sure all the circles in the Matching panel are green.');
    } else if (this.status === STATUS.FAILED_RENDER) {
      window.mainPage.overlay.renderReallyBadErrorScreen('There was a failure in attempting to render the visualization. There may be some hints in the developer console.');
    }
  },
  render: Underscore.debounce(function () {
    let widgetIsShowing = Widget.prototype.render.apply(this, arguments);
    if (!widgetIsShowing) {
      this.vis = undefined;
    }

    // Get the visualization in the project (if there is one)
    let spec = window.mainPage.project ? window.mainPage.project.getMeta('visualizations') : null;
    if (spec) {
      // Use the first spec (TODO: support multiple visualizations)
      spec = spec[0];

      // Get the options for the vis
      let options = window.mainPage.project.getVisOptions();

      // Start with an initial empty dataset (gets populated
      // asynchronously)
      options.data = [];

      if (widgetIsShowing) {
        // How is this render pass different from the last?
        if (!this.vis || this.vis.spec.name !== spec.name) {
          // We've changed visualizations; nuke the DOM element
          // and create a new candela component
          this.$el.html(myTemplate);

          this.vis = {
            spec: spec,
            options: options,
            component: new candela.components[spec.name](
              '#' + this.spec.hashName + 'Container .visualization', options)
          };
        } else {
          // The visualization hasn't changed, but the options may have.
          Object.keys(this.vis.options).forEach(key => {
            if (!options.hasOwnProperty(key)) {
              // This option is no longer specified;
              // remove it so that it's removed from the visualization
              delete options[key];
            }
          });

          this.vis.options = options;
        }
      }

      // Okay, now ask the project if it has any new data for
      // us (changing the matchings, editing the data, or grabbing
      // a new dataset will invalidate the parsed cache).
      if (widgetIsShowing) {
        this.vis.component.render();
      }
      this.status = STATUS.LOADING;
      this.statusText.text = 'Loading...';
      this.renderIndicators();
      window.mainPage.project.shapeDataForVis().then(data => {
        widgetIsShowing = this.isTargeted();
        if (widgetIsShowing) {
          this.vis.options.data = data;

          // TODO: how do we update the data for a component in general?
          let successfullyUpdated = false;
          if (this.vis.component.chart &&
            this.vis.component.chart.update) {
            try {
              this.vis.component.chart.update(this.vis.options);
              successfullyUpdated = true;
            } catch (errorObj) {
              // TODO: warn the user that something is up?
              // window.mainPage.trigger('rlab:error', errorObj);
              console.warn('Could not update the visualization in place:',
                errorObj);
            }
          }
          if (!successfullyUpdated) {
            // Nuke the vis and start fresh
            this.$el.html(myTemplate);
            try {
              this.vis.component = new candela.components[this.vis.spec.name](
                '#' + this.spec.hashName + 'Container .visualization', options);
            } catch (errorObj) {
              // Problem rendering the vis...
              this.$el.html(myTemplate);
              this.status = STATUS.FAILED_RENDER;
              this.statusText.text = spec.name;
              window.mainPage.trigger('rlab:error', errorObj);
              return;
            }
          }
          this.vis.component.render();
        }
        // Okay, finally change the status if there aren't enough mappings
        // (for now, the empty state is the partially-rendered visualization...
        // should probably add something in its place in the future)
        if (!window.mainPage.widgetPanels.widgets.MatchingView.widget.isSatisfied()) {
          this.status = STATUS.NOT_ENOUGH_MAPPINGS;
        } else {
          this.status = STATUS.OK;
        }
        this.statusText.text = spec.name;
        this.renderIndicators();
      });
    } else {
      this.$el.html(myTemplate);
      this.vis = null;
      this.status = STATUS.NO_VIS_SELECTED;
      this.statusText.text = 'None selected';
      this.renderIndicators();
    }
  }, 200)
});

export default VisualizationView;
<commit_msg>Fix one more minor error when opening an empty project<commit_after>import Underscore from 'underscore';
import Widget from '../Widget';
import myTemplate from './template.html';
import candela from '../../../../../src/candela';
import './style.css';

let STATUS = {
  OK: 0,
  LOADING: 1,
  NO_VIS_SELECTED: 2,
  NOT_ENOUGH_MAPPINGS: 3,
  FAILED_RENDER: 4
};

let VisualizationView = Widget.extend({
  initialize: function () {
    Widget.prototype.initialize.apply(this, arguments);

    this.friendlyName = 'Visualization';

    this.statusText.onclick = () => {
      window.mainPage.overlay.render('VisualizationLibrary');
    };
    this.statusText.title = 'Click to select a different visualization.';

    this.icons.splice(0, 0, {
      src: () => {
        return window.mainPage.currentUser.preferences
          .hasSeenAllTips(this.getDefaultTips()) ? Widget.infoIcon : Widget.newInfoIcon;
      },
      title: () => {
        return 'About this panel';
      },
      onclick: () => {
        this.renderInfoScreen();
      }
    });

    this.status = STATUS.LOADING;
    this.icons.splice(0, 0, {
      src: () => {
        if (this.status === STATUS.LOADING) {
          return Widget.spinnerIcon;
        } else if (this.status === STATUS.OK) {
          return Widget.okayIcon;
        } else {
          return Widget.warningIcon;
        }
      },
      title: () => {
        if (this.status === STATUS.LOADING) {
          return 'The visualization hasn\'t finished loading yet';
        } else if (this.status === STATUS.OK) {
          return 'The visualization appears to be working';
        } else {
          return 'Something isn\'t quite right; click for details';
        }
      },
      onclick: () => {
        this.renderHelpScreen();
      }
    });

    this.listenTo(window.mainPage, 'rl:changeProject',
      this.handleNewProject);
    this.handleNewProject();
  },
  handleNewProject: function () {
    this.$el.html('');
    this.status = STATUS.LOADING;
    this.vis = null;

    this.listenTo(window.mainPage.project, 'rl:changeVisualizations',
      this.render);
    this.listenTo(window.mainPage.project, 'rl:changeMatchings',
      this.render);
  },
  renderInfoScreen: function () {
    window.mainPage.helpLayer.showTips(this.getDefaultTips());
  },
  renderHelpScreen: function () {
    if (this.status === STATUS.LOADING) {
      window.mainPage.overlay.renderLoadingScreen('Still attempting to render the visualization...');
    } else if (this.status === STATUS.OK) {
      window.mainPage.overlay.renderSuccessScreen('The visualization appears to be functioning correctly.');
    } else if (this.status === STATUS.NO_VIS_SELECTED) {
      window.mainPage.overlay.renderUserErrorScreen('You have not chosen a visualization yet. Click  here to choose one.');
    } else if (this.status === STATUS.NOT_ENOUGH_MAPPINGS) {
      window.mainPage.overlay.renderUserErrorScreen('This visualization needs more data matchings. Make sure all the circles in the Matching panel are green.');
    } else if (this.status === STATUS.FAILED_RENDER) {
      window.mainPage.overlay.renderReallyBadErrorScreen('There was a failure in attempting to render the visualization. There may be some hints in the developer console.');
    }
  },
  render: Underscore.debounce(function () {
    let widgetIsShowing = Widget.prototype.render.apply(this, arguments);
    if (!widgetIsShowing) {
      this.vis = undefined;
    }

    // Get the visualization in the project (if there is one)
    let spec = window.mainPage.project ? window.mainPage.project.getMeta('visualizations') : null;
    if (spec && spec.length > 0) {
      // Use the first spec (TODO: support multiple visualizations)
      spec = spec[0];

      // Get the options for the vis
      let options = window.mainPage.project.getVisOptions();

      // Start with an initial empty dataset (gets populated
      // asynchronously)
      options.data = [];

      if (widgetIsShowing) {
        // How is this render pass different from the last?
        if (!this.vis || this.vis.spec.name !== spec.name) {
          // We've changed visualizations; nuke the DOM element
          // and create a new candela component
          this.$el.html(myTemplate);

          this.vis = {
            spec: spec,
            options: options,
            component: new candela.components[spec.name](
              '#' + this.spec.hashName + 'Container .visualization', options)
          };
        } else {
          // The visualization hasn't changed, but the options may have.
          Object.keys(this.vis.options).forEach(key => {
            if (!options.hasOwnProperty(key)) {
              // This option is no longer specified;
              // remove it so that it's removed from the visualization
              delete options[key];
            }
          });

          this.vis.options = options;
        }
      }

      // Okay, now ask the project if it has any new data for
      // us (changing the matchings, editing the data, or grabbing
      // a new dataset will invalidate the parsed cache).
      if (widgetIsShowing) {
        this.vis.component.render();
      }
      this.status = STATUS.LOADING;
      this.statusText.text = 'Loading...';
      this.renderIndicators();
      window.mainPage.project.shapeDataForVis().then(data => {
        widgetIsShowing = this.isTargeted();
        if (widgetIsShowing) {
          this.vis.options.data = data;

          // TODO: how do we update the data for a component in general?
          let successfullyUpdated = false;
          if (this.vis.component.chart &&
            this.vis.component.chart.update) {
            try {
              this.vis.component.chart.update(this.vis.options);
              successfullyUpdated = true;
            } catch (errorObj) {
              // TODO: warn the user that something is up?
              // window.mainPage.trigger('rlab:error', errorObj);
              console.warn('Could not update the visualization in place:',
                errorObj);
            }
          }
          if (!successfullyUpdated) {
            // Nuke the vis and start fresh
            this.$el.html(myTemplate);
            try {
              this.vis.component = new candela.components[this.vis.spec.name](
                '#' + this.spec.hashName + 'Container .visualization', options);
            } catch (errorObj) {
              // Problem rendering the vis...
              this.$el.html(myTemplate);
              this.status = STATUS.FAILED_RENDER;
              this.statusText.text = spec.name;
              window.mainPage.trigger('rlab:error', errorObj);
              return;
            }
          }
          this.vis.component.render();
        }
        // Okay, finally change the status if there aren't enough mappings
        // (for now, the empty state is the partially-rendered visualization...
        // should probably add something in its place in the future)
        if (!window.mainPage.widgetPanels.widgets.MatchingView.widget.isSatisfied()) {
          this.status = STATUS.NOT_ENOUGH_MAPPINGS;
        } else {
          this.status = STATUS.OK;
        }
        this.statusText.text = spec.name;
        this.renderIndicators();
      });
    } else {
      this.$el.html(myTemplate);
      this.vis = null;
      this.status = STATUS.NO_VIS_SELECTED;
      this.statusText.text = 'None selected';
      this.renderIndicators();
    }
  }, 200)
});

export default VisualizationView;
<|endoftext|>


<commit_before>const path = require('path');
const fs = require('fs-extra');

const exec = require('./exec').exec;
const execLater = require('./exec').execLater;
const configureExec = require('./exec').configure;
const log = require('./log').log;
const configureLog = require('./log').configure;

var options = {};

var gitInformation = {};

function getGitInformation() {
  log('getGitInformation');

  return exec('git log -n 1 --format="%H" HEAD')
    // Get current branch commit hash.
    .then(hash => gitInformation.hash = hash)
    // Get current branch commit title.
    .then(execLater('git log -n 1 --format="%s" HEAD'))
    .then(title => gitInformation.title = title)
    // Get current branch name.
    .then(execLater('git rev-parse --abbrev-ref HEAD'))
    .then(branch => gitInformation.branch = branch);
}

function checkoutOriginalBranch() {
  log('checkoutOriginalBranch');

  return exec(`git checkout ${gitInformation.branch}`);
}

function checkoutDeployBranch() {
  log('checkoutDeployBranch');

  return exec(`git checkout ${options.branch}`).then(
    () => {

    },
    (stderr) => {
      var message = stderr.message;
      if (message.indexOf('error: pathspec') > -1) {
        console.log('Did not find branch locally, creating.')
        return createGitBranch();
      } else {
        console.log(message);
        throw stderr;
      }
    }
  )
}

function createGitBranch() {
  log('createGitBranch');

  return exec(`git checkout --orphan ${options.branch}`)
    .then(execLater(`git rm --cached -r . `))
    .then(execLater(`git add .gitignore`))
    .then(execLater(`git clean -f -d`))
    .then(execLater(`git commit -m 'initial ${options.branch} commit'`))
    // Return to original branch.
    .then(checkoutOriginalBranch)
    // Resume normal code path.
    .then(checkoutDeployBranch);
}

function makeDeployBranchClean() {
  log('makeDeployBranchClean');

  return exec(`cp .gitignore ${options.src}/.gitignore`)
    .then(execLater('git rm -rf .'));
}

function copyFiles() {
  log('copyFiles');

  function filePath(fileName) {
    return path.resolve(process.cwd(), fileName);
  }

  console.log('Copying files...');
  return new Promise(function(resolve, reject) {
    fs.copy(
      filePath(options.src),
      filePath(options.target),
      (err) => {
        if (err) {
          reject(err);
        } else {
          resolve();
        }
      }
    );
  })
}

function addAndCommitToGit() {
  log('addAndCommitToGit');

  var commitMessage = `deploy: ${gitInformation.title}

  generated from commit ${gitInformation.hash}`;

  return exec(`git add .`)
    .then(stdout => {
      if (stdout.indexOf('nothing to commit') > -1) {
        console.log('Nothing to commit, no changes detected.');
      }
    })
    .then(execLater(`git commit -m '${commitMessage}'`))
    .catch(() => {
      // Catch error if there's nothing to commit.
    });
}

function pushBranchToRemote() {
  log('pushBranchToRemote');
  return exec(`git push ${options.remote} ${options.branch}`);
}

function main(opts) {
  options = opts;

  configureLog(opts);
  configureExec(opts);

  return getGitInformation()
    .then(checkoutDeployBranch)
    .then(makeDeployBranchClean)
    .then(copyFiles)
    .then(addAndCommitToGit)
    .then(checkoutOriginalBranch)
    .then(pushBranchToRemote)
    .then(() => {
      console.log('Deployed!');
    });
}

module.exports = main;<commit_msg>change commit message to chore<commit_after>const path = require('path');
const fs = require('fs-extra');

const exec = require('./exec').exec;
const execLater = require('./exec').execLater;
const configureExec = require('./exec').configure;
const log = require('./log').log;
const configureLog = require('./log').configure;

var options = {};

var gitInformation = {};

function getGitInformation() {
  log('getGitInformation');

  return exec('git log -n 1 --format="%H" HEAD')
    // Get current branch commit hash.
    .then(hash => gitInformation.hash = hash)
    // Get current branch commit title.
    .then(execLater('git log -n 1 --format="%s" HEAD'))
    .then(title => gitInformation.title = title)
    // Get current branch name.
    .then(execLater('git rev-parse --abbrev-ref HEAD'))
    .then(branch => gitInformation.branch = branch);
}

function checkoutOriginalBranch() {
  log('checkoutOriginalBranch');

  return exec(`git checkout ${gitInformation.branch}`);
}

function checkoutDeployBranch() {
  log('checkoutDeployBranch');

  return exec(`git checkout ${options.branch}`).then(
    () => {

    },
    (stderr) => {
      var message = stderr.message;
      if (message.indexOf('error: pathspec') > -1) {
        console.log('Did not find branch locally, creating.')
        return createGitBranch();
      } else {
        console.log(message);
        throw stderr;
      }
    }
  )
}

function createGitBranch() {
  log('createGitBranch');

  return exec(`git checkout --orphan ${options.branch}`)
    .then(execLater(`git rm --cached -r . `))
    .then(execLater(`git add .gitignore`))
    .then(execLater(`git clean -f -d`))
    .then(execLater(`git commit -m 'initial ${options.branch} commit'`))
    // Return to original branch.
    .then(checkoutOriginalBranch)
    // Resume normal code path.
    .then(checkoutDeployBranch);
}

function makeDeployBranchClean() {
  log('makeDeployBranchClean');

  return exec(`cp .gitignore ${options.src}/.gitignore`)
    .then(execLater('git rm -rf .'));
}

function copyFiles() {
  log('copyFiles');

  function filePath(fileName) {
    return path.resolve(process.cwd(), fileName);
  }

  console.log('Copying files...');
  return new Promise(function(resolve, reject) {
    fs.copy(
      filePath(options.src),
      filePath(options.target),
      (err) => {
        if (err) {
          reject(err);
        } else {
          resolve();
        }
      }
    );
  })
}

function addAndCommitToGit() {
  log('addAndCommitToGit');

  var commitMessage = `chore: ${gitInformation.title}

  generated from commit ${gitInformation.hash}`;

  return exec(`git add .`)
    .then(stdout => {
      if (stdout.indexOf('nothing to commit') > -1) {
        console.log('Nothing to commit, no changes detected.');
      }
    })
    .then(execLater(`git commit -m '${commitMessage}'`))
    .catch(() => {
      // Catch error if there's nothing to commit.
    });
}

function pushBranchToRemote() {
  log('pushBranchToRemote');
  return exec(`git push ${options.remote} ${options.branch}`);
}

function main(opts) {
  options = opts;

  configureLog(opts);
  configureExec(opts);

  return getGitInformation()
    .then(checkoutDeployBranch)
    .then(makeDeployBranchClean)
    .then(copyFiles)
    .then(addAndCommitToGit)
    .then(checkoutOriginalBranch)
    .then(pushBranchToRemote)
    .then(() => {
      console.log('Deployed!');
    });
}

module.exports = main;
<|endoftext|>


<commit_before>

// IMPORT
// =============================================================================

require('./Globals')
import Koa from 'koa'
import session from 'koa-session'
import conditional from 'koa-conditional-get'
import etag from 'koa-etag'
const app = new Koa()
import querystring from 'koa-qs'
import koaStatic from 'koa-static'
import router from './classes/Router'
querystring(app)
import koaBody from 'koa-body'
import TrafficControl from './classes/TrafficControl'
import render from 'koa-ejs'
import path from 'path'
import http from 'http'
import logger from './loggly/logger'
import { promisify } from 'util'
const {
  APIError,
  InternalServerError,
  TooManyRequestsAPIError
} = require('./classes/APIError')

import npid from 'npid'

// Import config
import config from '../config'

// Import controllers
import Authentication from './classes/Authentication'
import oauth2 from './routes/OAuth2'

import WebSocket from './classes/WebSocket'
import { db } from './db/index'

try {
  npid.remove('api.pid')
  let pid = npid.create('api.pid')
  pid.removeOnExit()
} catch (err) {
  process.exit(1)
}

app.keys = [config.cookie.secret]

let sessionConfiguration = {
  key: 'fuelrats:session',
  overwrite: true,
  signed: true
}

app.use(conditional())
app.use(etag())
app.use(session(sessionConfiguration, app))
app.use(koaStatic('static', {
  hidden: false,
  gzip: true
}))
app.use(koaBody())

let port = config.port || process.env.PORT

app.use(async function (ctx, next) {
  ctx.data = ctx.request.body
  ctx.client = {}

  let { query } = ctx
  ctx.query = parseQuery(query)

  if (ctx.request.headers['x-forwarded-for']) {
    [ctx.inet] = ctx.request.headers['x-forwarded-for'].split(', ')
  } else {
    ctx.inet =  ctx.request.ip
  }

  await next()
})

app.use(async function (ctx, next) {
  if (Array.isArray(ctx.data) || typeof ctx.data === 'object') {
    ['createdAt', 'updatedAt', 'deletedAt', 'revision'].map((cleanField) => {
      delete ctx.data[cleanField]
    })
  }
  await next()
})

const traffic = new TrafficControl()

app.use(async (ctx, next) => {
  try {
    await Authentication.authenticate(ctx)

    let rateLimit = traffic.validateRateLimit(ctx)

    ctx.set('X-API-Version', '2.0')
    ctx.set('X-Rate-Limit-Limit', rateLimit.total)
    ctx.set('X-Rate-Limit-Remaining', rateLimit.remaining)
    ctx.set('X-Rate-Limit-Reset', rateLimit.nextResetDate)

    logger.info({ tags: ['request'] }, `Request by ${ctx.inet} to ${ctx.request.path}`, {
      'ip': ctx.inet,
      'path': ctx.request.path,
      'rate-limit-limit': rateLimit.total,
      'rate-limit-remaining': rateLimit.remaining,
      'query': ctx.query,
      'body': censor(ctx.data),
      'method': ctx.request.req.method
    })

    if (rateLimit.exceeded) {
      next(new TooManyRequestsAPIError({}))
      return
    }

    if (ctx.state.client) {
      ctx.state.user = ctx.state.client
    }

    let result = await next()
    if (result === true) {
      ctx.status = 204
    } else if (result) {
      ctx.body = result
    }
  } catch (ex) {
    let errors = ex

    if (errors.hasOwnProperty('name')) {
      errors = APIError.fromValidationError(errors)
    }

    if (Array.isArray(errors) === false) {
      errors = [errors]
    }

    errors = errors.map(error => {
      if ((error instanceof APIError) === false) {
        return new InternalServerError({})
      }
      return error
    })

    ctx.status = errors[0].status
    ctx.body = {
      errors
    }
  }
})

render(app, {
  root: path.join(__dirname, 'views'),
  layout: false,
  viewExt: 'html',
  cache: false,
  debug: true
})

// ROUTES
// =============================================================================

import Rescue from './routes/Rescues'
import User from './routes/Users'
import Rats from './routes/Rats'
import Clients from './routes/Clients'
import Nicknames from './routes/Nicknames'
import Ships from './routes/Ships'
import Login from './routes/Login'
import Register from './routes/Register'
import Profile from './routes/Profiles'
import Reset from './routes/Resets'
import AnopeWebhook from './routes/AnopeWebhook'
import Statistics from './routes/Statistics'
import Version from './routes/Version'
import Decals from './routes/Decals'
import Stream from './routes/Stream'
import JiraDrillWebhook from './routes/JiraDrillWebhook'
import NPO from './routes/NPO'

export let routes = [
  new Rescue(),
  new User(),
  new Rats(),
  new Clients(),
  new Nicknames(),
  new Ships(),
  new Login(),
  new Register(),
  new Profile(),
  new Reset(),
  new AnopeWebhook(),
  new Statistics(),
  new Version(),
  new Decals(),
  new Stream(),
  new JiraDrillWebhook(),
  new NPO()
]

// OAUTH2

router.post('/oauth2/authorize',
  ...oauth2.server.decision())

router.post('/oauth2/token',
  Authentication.isClientAuthenticated,
  oauth2.server.token(),
  oauth2.server.errorHandler())

app.use(router.routes())
app.use(router.allowedMethods())

/**
 * Parses an object of URL query parameters and builds a nested object by delimiting periods into sub objects.
 * @param query an array of URL query parameters
 * @returns {{}} a nested object
 */
function parseQuery (query) {
  let queryObj = {}

  // Iterate over each individual query item
  for (let key of Object.keys(query)) {
    // Split them into period delimited arrays
    let keys = key.split('.')
    let target = queryObj

    // Iterate over the period delimited arrays to construct a nested hierarchy
    for (let keyPair of keys.entries()) {
      let [, subkey ] = keyPair
      if (keyPair[0] === keys.length - 1) {
        // We have reached the end of the delimited array which means we can insert the value

        target[subkey] = query[key]
      } else if (!target[subkey]) {
        /* We have not reached the end of the delimited array so we need to create a nested object unless
        it already exists */
        target[subkey] = {}
        target = target[subkey]
      }
    }
  }
  return queryObj
}

let server = http.createServer(app.callback())
new WebSocket(server, traffic)

/**
 * Goes through an object and sets properties commonly usde to hold sensitive information to a static value.
 * @param obj The object to censor
 * @returns {{}} A censored object
 */
function censor (obj) {
  let censoredObj = {}
  Object.assign(censoredObj, obj)

  if (censoredObj.password) {
    censoredObj.password = '[CENSORED]'
  }
  if (censoredObj.secret) {
    censoredObj.secret = '[CENSORED]'
  }

  return censoredObj
}

(async function startServer () {
  try {
    await db.sync()
    const listen = promisify(server.listen.bind(server))
    await listen(port, config.hostname)
    logger.info(`HTTP Server listening on ${config.hostname} port ${port}`)
  } catch (error) {
    logger.error(error)
  }
})()

// allow launch of app from unit tests
module.exports = server
<commit_msg>Fix unable to retrieve html template for password reset<commit_after>

// IMPORT
// =============================================================================

require('./Globals')
import Koa from 'koa'
import session from 'koa-session'
import conditional from 'koa-conditional-get'
import etag from 'koa-etag'
const app = new Koa()
import querystring from 'koa-qs'
import koaStatic from 'koa-static'
import router from './classes/Router'
querystring(app)
import koaBody from 'koa-body'
import TrafficControl from './classes/TrafficControl'
import render from 'koa-ejs'
import path from 'path'
import http from 'http'
import logger from './loggly/logger'
import { promisify } from 'util'
const {
  APIError,
  InternalServerError,
  TooManyRequestsAPIError
} = require('./classes/APIError')

import npid from 'npid'

// Import config
import config from '../config'

// Import controllers
import Authentication from './classes/Authentication'
import oauth2 from './routes/OAuth2'

import WebSocket from './classes/WebSocket'
import { db } from './db/index'

try {
  npid.remove('api.pid')
  let pid = npid.create('api.pid')
  pid.removeOnExit()
} catch (err) {
  process.exit(1)
}

app.keys = [config.cookie.secret]

let sessionConfiguration = {
  key: 'fuelrats:session',
  overwrite: true,
  signed: true
}

app.use(conditional())
app.use(etag())
app.use(session(sessionConfiguration, app))
app.use(koaStatic('../static', {
  hidden: false,
  gzip: true
}))
app.use(koaBody())

let port = config.port || process.env.PORT

app.use(async function (ctx, next) {
  ctx.data = ctx.request.body
  ctx.client = {}

  let { query } = ctx
  ctx.query = parseQuery(query)

  if (ctx.request.headers['x-forwarded-for']) {
    [ctx.inet] = ctx.request.headers['x-forwarded-for'].split(', ')
  } else {
    ctx.inet =  ctx.request.ip
  }

  await next()
})

app.use(async function (ctx, next) {
  if (Array.isArray(ctx.data) || typeof ctx.data === 'object') {
    ['createdAt', 'updatedAt', 'deletedAt', 'revision'].map((cleanField) => {
      delete ctx.data[cleanField]
    })
  }
  await next()
})

const traffic = new TrafficControl()

app.use(async (ctx, next) => {
  try {
    await Authentication.authenticate(ctx)

    let rateLimit = traffic.validateRateLimit(ctx)

    ctx.set('X-API-Version', '2.0')
    ctx.set('X-Rate-Limit-Limit', rateLimit.total)
    ctx.set('X-Rate-Limit-Remaining', rateLimit.remaining)
    ctx.set('X-Rate-Limit-Reset', rateLimit.nextResetDate)

    logger.info({ tags: ['request'] }, `Request by ${ctx.inet} to ${ctx.request.path}`, {
      'ip': ctx.inet,
      'path': ctx.request.path,
      'rate-limit-limit': rateLimit.total,
      'rate-limit-remaining': rateLimit.remaining,
      'query': ctx.query,
      'body': censor(ctx.data),
      'method': ctx.request.req.method
    })

    if (rateLimit.exceeded) {
      next(new TooManyRequestsAPIError({}))
      return
    }

    if (ctx.state.client) {
      ctx.state.user = ctx.state.client
    }

    let result = await next()
    if (result === true) {
      ctx.status = 204
    } else if (result) {
      ctx.body = result
    }
  } catch (ex) {
    let errors = ex

    if (errors.hasOwnProperty('name')) {
      errors = APIError.fromValidationError(errors)
    }

    if (Array.isArray(errors) === false) {
      errors = [errors]
    }

    errors = errors.map(error => {
      if ((error instanceof APIError) === false) {
        return new InternalServerError({})
      }
      return error
    })

    ctx.status = errors[0].status
    ctx.body = {
      errors
    }
  }
})

render(app, {
  root: path.join(__dirname, 'views'),
  layout: false,
  viewExt: 'html',
  cache: false,
  debug: true
})

// ROUTES
// =============================================================================

import Rescue from './routes/Rescues'
import User from './routes/Users'
import Rats from './routes/Rats'
import Clients from './routes/Clients'
import Nicknames from './routes/Nicknames'
import Ships from './routes/Ships'
import Login from './routes/Login'
import Register from './routes/Register'
import Profile from './routes/Profiles'
import Reset from './routes/Resets'
import AnopeWebhook from './routes/AnopeWebhook'
import Statistics from './routes/Statistics'
import Version from './routes/Version'
import Decals from './routes/Decals'
import Stream from './routes/Stream'
import JiraDrillWebhook from './routes/JiraDrillWebhook'
import NPO from './routes/NPO'

export let routes = [
  new Rescue(),
  new User(),
  new Rats(),
  new Clients(),
  new Nicknames(),
  new Ships(),
  new Login(),
  new Register(),
  new Profile(),
  new Reset(),
  new AnopeWebhook(),
  new Statistics(),
  new Version(),
  new Decals(),
  new Stream(),
  new JiraDrillWebhook(),
  new NPO()
]

// OAUTH2

router.post('/oauth2/authorize',
  ...oauth2.server.decision())

router.post('/oauth2/token',
  Authentication.isClientAuthenticated,
  oauth2.server.token(),
  oauth2.server.errorHandler())

app.use(router.routes())
app.use(router.allowedMethods())

/**
 * Parses an object of URL query parameters and builds a nested object by delimiting periods into sub objects.
 * @param query an array of URL query parameters
 * @returns {{}} a nested object
 */
function parseQuery (query) {
  let queryObj = {}

  // Iterate over each individual query item
  for (let key of Object.keys(query)) {
    // Split them into period delimited arrays
    let keys = key.split('.')
    let target = queryObj

    // Iterate over the period delimited arrays to construct a nested hierarchy
    for (let keyPair of keys.entries()) {
      let [, subkey ] = keyPair
      if (keyPair[0] === keys.length - 1) {
        // We have reached the end of the delimited array which means we can insert the value

        target[subkey] = query[key]
      } else if (!target[subkey]) {
        /* We have not reached the end of the delimited array so we need to create a nested object unless
        it already exists */
        target[subkey] = {}
        target = target[subkey]
      }
    }
  }
  return queryObj
}

let server = http.createServer(app.callback())
new WebSocket(server, traffic)

/**
 * Goes through an object and sets properties commonly usde to hold sensitive information to a static value.
 * @param obj The object to censor
 * @returns {{}} A censored object
 */
function censor (obj) {
  let censoredObj = {}
  Object.assign(censoredObj, obj)

  if (censoredObj.password) {
    censoredObj.password = '[CENSORED]'
  }
  if (censoredObj.secret) {
    censoredObj.secret = '[CENSORED]'
  }

  return censoredObj
}

(async function startServer () {
  try {
    await db.sync()
    const listen = promisify(server.listen.bind(server))
    await listen(port, config.hostname)
    logger.info(`HTTP Server listening on ${config.hostname} port ${port}`)
  } catch (error) {
    logger.error(error)
  }
})()

// allow launch of app from unit tests
module.exports = server
<|endoftext|>


<commit_before>import Radium from 'radium';

import _EditableDiv from './components/EditableDiv.react';
import Header from './components/Header.react';
import InputControl from './components/InputControl.react';

const EditableDiv = Radium(_EditableDiv);

export default {
    EditableDiv,
    Header,
    InputControl
};
<commit_msg>Don't use `export default` in index.js<commit_after>import Radium from 'radium';

import _EditableDiv from './components/EditableDiv.react';
import Header from './components/Header.react';
import InputControl from './components/InputControl.react';

const EditableDiv = Radium(_EditableDiv);

export {
    EditableDiv,
    Header,
    InputControl
};
<|endoftext|>


<commit_before>import Dexie from 'dexie'
import SchemaParser from './schema-parser'
import {isIndexableType} from './utils'

const Relationships = (db) => {
  // Use Dexie.Promise to ensure transaction safety.
  const Promise = Dexie.Promise

  /**
   * Iterate through all items and collect related records
   *
   * @param relationships
   *
   * @returns {Dexie.Promise}
   */
  db.Table.prototype.with = function (relationships) {
    return this.toCollection().with(relationships)
  }

  /**
   * Iterate through all items and collect related records
   *
   * @param relationships
   *
   * @returns {Dexie.Promise}
   */
  db.Collection.prototype.with = function (relationships) {
    const baseTable = this._ctx.table.name
    const databaseTables = db._allTables

    // this holds tables that have foreign keys pointing at the current table
    let usableForeignTables = []

    // validate target tables and add them into our usable tables object
    Object.keys(relationships).forEach((column) => {
      let tableOrIndex = relationships[column]
      let matchingIndex = this._ctx.table.schema.idxByName[tableOrIndex]

      if (matchingIndex && matchingIndex.hasOwnProperty('foreignKey')) {
        let index = matchingIndex
        usableForeignTables.push({
          column: column,
          index: index.foreignKey.targetIndex,
          tableName: index.foreignKey.targetTable,
          targetIndex: index.foreignKey.index,
          oneToOne: true
        })
      } else {
        let table = tableOrIndex

        if (!databaseTables.hasOwnProperty(table)) {
          throw new Error('Relationship table ' + table + ' doesn\'t exist.')
        }

        if (!databaseTables[table].schema.hasOwnProperty('foreignKeys')) {
          throw new Error('Relationship table ' + table + ' doesn\'t have foreign keys set.')
        }

        // remove the foreign keys that don't link to the base table
        let columns = databaseTables[table].schema.foreignKeys.filter(column => column.targetTable === baseTable)

        if (columns.length > 0) {
          usableForeignTables[table] = {
            column: column,
            index: columns[0].index,
            tableName: table,
            targetIndex: columns[0].targetIndex
          }
        }
      }
    })

    return this.toArray().then(rows => {
      //
      // Extract the mix of all related keys in all rows
      //
      let queries = usableForeignTables
        .map(foreignTable => {
          // For each foreign table, query all items that any row refers to
          let tableName = foreignTable.tableName
          let allRelatedKeys = rows
            .map(row => row[foreignTable.targetIndex])
            .filter(isIndexableType)

          // Build the Collection to retrieve all related items
          return databaseTables[tableName]
              .where(foreignTable.index)
              .anyOf(allRelatedKeys)
        })

      // Execute queries in parallell
      let queryPromises = queries.map(query => query.toArray())

      //
      // Await all results and then try mapping each response
      // with its corresponding row and attach related items onto each row
      //
      return Promise.all(queryPromises).then(queryResults => {
        usableForeignTables.forEach((foreignTable, idx) => {
          let tableName = foreignTable.tableName
          let result = queryResults[idx]
          let targetIndex = foreignTable.targetIndex
          let foreignIndex = foreignTable.index
          let column = foreignTable.column

          // Create a lookup by targetIndex (normally 'id')
          // and set the column onto the target
          let lookup = {}
          result.forEach(record => {
            let foreignKey = record[foreignIndex]
            if (foreignTable.oneToOne) {
              lookup[foreignKey] = record
            } else {
              (lookup[foreignKey] = lookup[foreignKey] || [])
                .push(record)
            }
          })

          // Populate column on each row
          rows.forEach(row => {
            let foreignKey = row[targetIndex]
            let record = lookup[foreignKey]
            if (!record) {
              throw new Error(
                `Could not lookup foreign key where ` +
                `${tableName}.${foreignIndex} == ${baseTable}.${column}. ` +
                `The content of the failing key was: ${JSON.stringify(foreignKey)}.`)
            }

            // Set it as a non-enumerable property so that the object can be safely put back
            // to indexeddb without storing relations redundantly (IndexedDB will only store "own non-
            // enumerable properties")
            Object.defineProperty(row, column, {
              value: record,
              enumerable: false,
              configurable: true,
              writable: true
            })
          })
        })
      }).then(() => rows)
    })
  }

  db.Version.prototype._parseStoresSpec = Dexie.override(
    db.Version.prototype._parseStoresSpec,
    parseStoresSpec => function (storesSpec, outDbSchema) {
      const parser = new SchemaParser(storesSpec)

      let foreignKeys = parser.getForeignKeys()
      // call the original method
      let rv = parseStoresSpec.call(this, parser.getCleanedSchema(), outDbSchema)

      // set foreign keys into database table objects
      // to use later in 'with' method
      Object.keys(outDbSchema).forEach(table => {
        if (foreignKeys.hasOwnProperty(table)) {
          outDbSchema[table].foreignKeys = foreignKeys[table]
          foreignKeys[table].forEach(fk => {
            outDbSchema[table].idxByName[fk.index].foreignKey = fk
          })
        }
      })

      return rv
    })
}

export default Relationships
<commit_msg>fix broken unit test<commit_after>import Dexie from 'dexie'
import SchemaParser from './schema-parser'
import {isIndexableType} from './utils'

const Relationships = (db) => {
  // Use Dexie.Promise to ensure transaction safety.
  const Promise = Dexie.Promise

  /**
   * Iterate through all items and collect related records
   *
   * @param relationships
   *
   * @returns {Dexie.Promise}
   */
  db.Table.prototype.with = function (relationships) {
    return this.toCollection().with(relationships)
  }

  /**
   * Iterate through all items and collect related records
   *
   * @param relationships
   *
   * @returns {Dexie.Promise}
   */
  db.Collection.prototype.with = function (relationships) {
    const baseTable = this._ctx.table.name
    const databaseTables = db._allTables

    // this holds tables that have foreign keys pointing at the current table
    let usableForeignTables = []

    // validate target tables and add them into our usable tables object
    Object.keys(relationships).forEach((column) => {
      let tableOrIndex = relationships[column]
      let matchingIndex = this._ctx.table.schema.idxByName[tableOrIndex]

      if (matchingIndex && matchingIndex.hasOwnProperty('foreignKey')) {
        let index = matchingIndex
        usableForeignTables.push({
          column: column,
          index: index.foreignKey.targetIndex,
          tableName: index.foreignKey.targetTable,
          targetIndex: index.foreignKey.index,
          oneToOne: true
        })
      } else {
        let table = tableOrIndex

        if (!databaseTables.hasOwnProperty(table)) {
          throw new Error('Relationship table ' + table + ' doesn\'t exist.')
        }

        if (!databaseTables[table].schema.hasOwnProperty('foreignKeys')) {
          throw new Error('Relationship table ' + table + ' doesn\'t have foreign keys set.')
        }

        // remove the foreign keys that don't link to the base table
        let columns = databaseTables[table].schema.foreignKeys.filter(column => column.targetTable === baseTable)

        if (columns.length > 0) {
          usableForeignTables.push({
            column: column,
            index: columns[0].index,
            tableName: table,
            targetIndex: columns[0].targetIndex
          })
        }
      }
    })

    return this.toArray().then(rows => {
      //
      // Extract the mix of all related keys in all rows
      //
      let queries = usableForeignTables
        .map(foreignTable => {
          // For each foreign table, query all items that any row refers to
          let tableName = foreignTable.tableName
          let allRelatedKeys = rows
            .map(row => row[foreignTable.targetIndex])
            .filter(isIndexableType)

          // Build the Collection to retrieve all related items
          return databaseTables[tableName]
              .where(foreignTable.index)
              .anyOf(allRelatedKeys)
        })

      // Execute queries in parallell
      let queryPromises = queries.map(query => query.toArray())

      //
      // Await all results and then try mapping each response
      // with its corresponding row and attach related items onto each row
      //
      return Promise.all(queryPromises).then(queryResults => {
        usableForeignTables.forEach((foreignTable, idx) => {
          let tableName = foreignTable.tableName
          let result = queryResults[idx]
          let targetIndex = foreignTable.targetIndex
          let foreignIndex = foreignTable.index
          let column = foreignTable.column

          // Create a lookup by targetIndex (normally 'id')
          // and set the column onto the target
          let lookup = {}
          result.forEach(record => {
            let foreignKey = record[foreignIndex]
            if (foreignTable.oneToOne) {
              lookup[foreignKey] = record
            } else {
              (lookup[foreignKey] = lookup[foreignKey] || [])
                .push(record)
            }
          })

          // Populate column on each row
          rows.forEach(row => {
            let foreignKey = row[targetIndex]
            let record = lookup[foreignKey]
            if (!record) {
              throw new Error(
                `Could not lookup foreign key where ` +
                `${tableName}.${foreignIndex} == ${baseTable}.${column}. ` +
                `The content of the failing key was: ${JSON.stringify(foreignKey)}.`)
            }

            // Set it as a non-enumerable property so that the object can be safely put back
            // to indexeddb without storing relations redundantly (IndexedDB will only store "own non-
            // enumerable properties")
            Object.defineProperty(row, column, {
              value: record,
              enumerable: false,
              configurable: true,
              writable: true
            })
          })
        })
      }).then(() => rows)
    })
  }

  db.Version.prototype._parseStoresSpec = Dexie.override(
    db.Version.prototype._parseStoresSpec,
    parseStoresSpec => function (storesSpec, outDbSchema) {
      const parser = new SchemaParser(storesSpec)

      let foreignKeys = parser.getForeignKeys()
      // call the original method
      let rv = parseStoresSpec.call(this, parser.getCleanedSchema(), outDbSchema)

      // set foreign keys into database table objects
      // to use later in 'with' method
      Object.keys(outDbSchema).forEach(table => {
        if (foreignKeys.hasOwnProperty(table)) {
          outDbSchema[table].foreignKeys = foreignKeys[table]
          foreignKeys[table].forEach(fk => {
            outDbSchema[table].idxByName[fk.index].foreignKey = fk
          })
        }
      })

      return rv
    })
}

export default Relationships
<|endoftext|>


<commit_before><commit_msg>py_utils.js : move list_comp and ternary from py2js.js<commit_after><|endoftext|>


<commit_before>'use strict';
import backbone from 'backbone';
import $ from 'jquery';
console.log(backbone);
export class Hello extends backbone.View {
    render() {
        this.$el.html('Hello, world.');
    }
}

var myView = new Hello({el: document.getElementById('root')});
myView.render();<commit_msg>Remove superfluous jQuery import, and remove console.log<commit_after>'use strict';
import backbone from 'backbone';

class Hello extends backbone.View {
    render() {
        this.$el.html('Hello, world.');
    }
}

var myView = new Hello({el: document.getElementById('root')});
myView.render();<|endoftext|>


<commit_before>/**
 * Created by Mauriel on 5/16/2018.
 */
var c20 = d3.scale.category20();

var margin = {top: 40, right: 20, bottom: 60, left: 40},
    width = $(".svg-container").width() - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

var formatPercent = d3.format(".0%");

var x = d3.scale.ordinal()
    .rangeRoundBands([0, width], .1);

var y = d3.scale.linear()
    .domain([0, 1])
    .range([height, 0]);

var xAxis = d3.svg.axis()
    .scale(x)
    .tickValues([])
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .tickFormat(formatPercent);

var tip = d3.tip()
    .attr('class', 'd3-tip')
    .offset([-10, 0])
    .html(function (d) {
        return d.taxon + "

% of Total Individuals: " + (d.frequency * 100) + "%";
    });

var svg = d3.select(".svg-container").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

svg.call(tip);

// Get the data
var data = [];
var total = 0;
$(".taxon").each(function () {
    var name = $(this).find("[data-name]").text().trim();
    var count = parseFloat($(this).find("[data-count]").text().trim());
    data.push({taxon: name, frequency: count});
    total += count;
});

var legendContainer = $("#legend-container ul");

// Compute percentages
for (var i = 0; i < data.length; i++) {
    data[i].frequency = data[i].frequency / total;

    // Populate legend container
    legendContainer.append(
        '
' +
            '' +
                '' +
                data[i].taxon +
            '' +
        '
'
    );
}

plot(data);

function plot(data) {
    x.domain(data.map(function (d) {
        return d.taxon;
    }));

    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis)
        .append("text")
        .style("text-anchor", "middle")
        .attr("x", width / 2 )
        .attr("y", 30 )
        .text("Taxonomic Group");

    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis)
        .append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 6)
        .attr("dy", ".71em")
        .style("text-anchor", "end")
        .text("% of Total Individuals");

    svg.selectAll(".bar")
        .data(data)
        .enter().append("rect")
        .attr("class", "bar")
        .attr("x", function (d) {
            return x(d.taxon);
        })
        .attr("width", x.rangeBand())
        .attr("y", function (d) {
            return y(d.frequency);
        })
        .attr("fill", function (d, i) {
            return c20(i);
        })
        .attr("height", function (d) {
            return height - y(d.frequency);
        })
        .on('mouseover', tip.show)
        .on('mouseout', tip.hide)
}

function type(d) {
    d.frequency = +d.frequency;
    return d;
}
<commit_msg>Plot responsive behavior<commit_after>/**
 * Created by Mauriel on 5/16/2018.
 */
var c20 = d3.scale.category20();

var margin = {top: 40, right: 20, bottom: 60, left: 40},
    width = $(".svg-container").width() - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

var formatPercent = d3.format(".0%");

var x = d3.scale.ordinal()
    .rangeRoundBands([0, width], .1);

var y = d3.scale.linear()
    .domain([0, 1])
    .range([height, 0]);

var xAxis = d3.svg.axis()
    .scale(x)
    .tickValues([])
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .tickFormat(formatPercent);

var tip = d3.tip()
    .attr('class', 'd3-tip')
    .offset([-10, 0])
    .html(function (d) {
        return d.taxon + "

% of Total Individuals: " + (d.frequency * 100) + "%";
    });

var svg = d3.select(".svg-container").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

svg.call(tip);

// Get the data
var data = [];
var total = 0;
$(".taxon").each(function () {
    var name = $(this).find("[data-name]").text().trim();
    var count = parseFloat($(this).find("[data-count]").text().trim());
    data.push({taxon: name, frequency: count});
    total += count;
});

var legendContainer = $("#legend-container ul");

// Compute percentages
for (var i = 0; i < data.length; i++) {
    data[i].frequency = data[i].frequency / total;

    // Populate legend container
    legendContainer.append(
        '
' +
            '' +
                '' +
                data[i].taxon +
            '' +
        '
'
    );
}

// Plot the data
x.domain(data.map(function (d) {
    return d.taxon;
}));

svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis)
    .append("text")
    .attr("class", "label")
    .style("text-anchor", "middle")
    .attr("x", width / 2)
    .attr("y", margin.bottom / 1.5)
    .text("Taxonomic Group");

svg.append("g")
    .attr("class", "y axis")
    .call(yAxis)
    .append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", ".71em")
    .style("text-anchor", "end")
    .text("% of Total Individuals");

svg.selectAll(".bar")
    .data(data)
    .enter().append("rect")
    .attr("class", "bar")
    .attr("x", function (d) {
        return x(d.taxon);
    })
    .attr("width", x.rangeBand())
    .attr("y", function (d) {
        return y(d.frequency);
    })
    .attr("fill", function (d, i) {
        return c20(i);
    })
    .attr("height", function (d) {
        return height - y(d.frequency);
    })
    .on('mouseover', tip.show)
    .on('mouseout', tip.hide);

// Define responsive behavior
function resize() {
    width = $(".svg-container").width() - margin.left - margin.right;

    // Update the range of the scale with new width/height
    x.rangeRoundBands([0, width], .1);

    // Update the axis and text with the new scale
    svg.select(".x.axis")
        .call(xAxis)
        .attr("transform", "translate(0," + height + ")")
        .select(".label")
        .attr("transform", "translate(" + width / 2 + "," + margin.bottom / 1.5 + ")");

    svg.select(".y.axis")
        .call(yAxis);

    // Force D3 to recalculate and update the line
    svg.selectAll(".bar")
        .attr("width", x.rangeBand())
        .attr("y", function (d) {
            return y(d.frequency);
        })
        .attr("x", function (d) {
            return x(d.taxon);
        })
        .attr("height", function (d) {
            return height - y(d.frequency);
        })
};

// Call the resize function whenever a resize event occurs
d3.select(window).on('resize', resize);

// Call the resize function
resize();

function type(d) {
    d.frequency = +d.frequency;
    return d;
}
<|endoftext|>


<commit_before>var util = require('../util');
var sql = require('mssql');

var querySelectId = 'SELECT us_id FROM Users WHERE us_sid=@sid;';
var queryInsertSid = 'INSERT INTO Users (us_sid, us_alias, us_email, us_firstName, us_lastName, us_picture) values (@sid, @alias, @email, @firstName, @lastName, @picture);';

var tranDone = false;

var api = {
	post: function (req, res, next) {
		var conn = new sql.Connection(util.sqlConfiguration);
		conn.connect(function(err) {
			if (err) { completeError(err, res); return; }
			var tran = new sql.Transaction(conn);
			tran.begin(function(err) {
				if (err) { rollback500(err, res, tran); return; }
				var sqlRequest = new sql.Request(tran);
				var psSelectId = new sql.PreparedStatement(conn);
				psSelectId.input('sid', sql.NVarChar);
				psSelectId.prepare(querySelectId, function(err) {
					if (err) { rollback500(err, res, tran); return; }
					psSelectId.execute({ sid: req.azureMobile.user.id },
						function(err, recordset, affected) {
							if (err) { rollback500(err, res, tran); return; }
							psSelectId.unprepare();
							if (typeof recordset != 'undefined' && recordset[0] != null) {
								console.log('User registered; Returning.');
								rollback200(res, tran);
								next();
							} else {
								console.log('User not registered; Registering.');
								userName(req.azureMobile.user, function(userInfo) {
									var psInsertSid = new sql.PreparedStatement(conn);
									psInsertSid.input('sid', sql.NVarChar);
									psInsertSid.input('alias', sql.NVarChar);
									psInsertSid.input('email', sql.NVarChar);
									psInsertSid.input('firstName', sql.NVarChar);
									psInsertSid.input('lastName', sql.NVarChar);
									psInsertSid.input('picture', sql.NVarChar);
									psInsertSid.prepare(queryInsertSid, function(err) {
										if (err) { rollback500(err, res, tran); return; }
										psInsertSid.execute({
												sid: req.azureMobile.user.id,
												alias: userInfo.alias ? userInfo.alias : userInfo.email,
												email: userInfo.email,
												firstName: userInfo.firstName,
												lastName: userInfo.lastName,
												picture: userInfo.picture
											}, function(err, recordset, affected) {
												if (err) { rollback500(err, res, tran); return; }
												commit200(res, tran);
												psInsertSid.unprepare();
												next();
											}
										);
									});
								});
							}
						}
					);
				});
			});
		});
    }
};

api.access = 'authenticated';

var userName = function(user, next) {
    user.getIdentity().then(function(identity){
    	var claims = identity.google.claims;
    	next({
    		alias: "",
    		email: claims.email_verified ? claims.emailaddress : "",
    		firstName: claims.givenname,
    		lastName: claims.surname,
    		picture: claims.picture
    	});
    });
};

var completeError = function(err, res) {
	if (err) {
		console.log(err);
		if (res) res.sendStatus(500);
	}
}

var completetran = function(err, data) {
	if (err) {
		console.log(err);
		return;
	}
	if (!data) {
		console.log('no data');
		return;
	}
	if (!data.tranDone) {
		data.tranDone = true;
		if (data.action && util.isFunction(data.action))
			data.action();
		if (data.res && data.sendStatus && util.isFunction(data.res.sendStatus)) {
			console.log('sending status ' + data.sendStatus);
			data.res.sendStatus(data.sendStatus);
		}
	}
}

var rollback500 = function(err, res, tran) {
	completetran(err, {
		tranDone: tranDone,
		action: tran.rollback,
		res: res,
		sendStatus: 500
	});
}

var rollback200 = function(res, tran) {
	completetran(undefined, {
		tranDone: tranDone,
		action: tran.rollback,
		res: res,
		sendStatus: 200
	});
}

var commit200 = function(res, tran) {
	completetran(undefined, {
		tranDone: tranDone,
		action: tran.commit,
		res: res,
		sendStatus: 200
	});
}

module.exports = api;
<commit_msg>wip save<commit_after>var util = require('../util');
var sql = require('mssql');

var querySelectId = 'SELECT us_id FROM Users WHERE us_sid=@sid;';
var queryInsertSid = 'INSERT INTO Users (us_sid, us_alias, us_email, us_firstName, us_lastName, us_picture) values (@sid, @alias, @email, @firstName, @lastName, @picture);';

var tranDone = false;

var api = {
	post: function (req, res, next) {
		var conn = new sql.Connection(util.sqlConfiguration);
		conn.connect(function(err) {
			if (err) { completeError(err, res); return; }
			var tran = new sql.Transaction(conn);
			tran.begin(function(err) {
				if (err) { rollback500(err, res, tran); return; }
				var sqlRequest = new sql.Request(tran);
				var psSelectId = new sql.PreparedStatement(conn);
				psSelectId.input('sid', sql.NVarChar);
				psSelectId.prepare(querySelectId, function(err) {
					if (err) { rollback500(err, res, tran); return; }
					psSelectId.execute({ sid: req.azureMobile.user.id },
						function(err, recordset, affected) {
							if (err) { rollback500(err, res, tran); return; }
							psSelectId.unprepare();
							if (typeof recordset != 'undefined' && recordset[0] != null) {
								console.log('User registered; Returning.');
								rollback200(res, tran);
								next();
							} else {
								console.log('User not registered; Registering.');
								userName(req.azureMobile.user, function(userInfo) {
									var psInsertSid = new sql.PreparedStatement(conn);
									psInsertSid.input('sid', sql.NVarChar);
									psInsertSid.input('alias', sql.NVarChar);
									psInsertSid.input('email', sql.NVarChar);
									psInsertSid.input('firstName', sql.NVarChar);
									psInsertSid.input('lastName', sql.NVarChar);
									psInsertSid.input('picture', sql.NVarChar);
									psInsertSid.prepare(queryInsertSid, function(err) {
										if (err) { rollback500(err, res, tran); return; }
										psInsertSid.execute({
												sid: req.azureMobile.user.id,
												alias: userInfo.alias ? userInfo.alias : left(userInfo.email, 20),
												email: userInfo.email,
												firstName: userInfo.firstName,
												lastName: userInfo.lastName,
												picture: userInfo.picture
											}, function(err, recordset, affected) {
												if (err) { rollback500(err, res, tran); return; }
												commit200(res, tran);
												psInsertSid.unprepare();
												next();
											}
										);
									});
								});
							}
						}
					);
				});
			});
		});
    }
};

api.access = 'authenticated';

var userName = function(user, next) {
    user.getIdentity().then(function(identity){
    	var claims = identity.google.claims;
    	next({
    		alias: "",
    		email: claims.email_verified ? claims.emailaddress : "",
    		firstName: claims.givenname,
    		lastName: claims.surname,
    		picture: claims.picture
    	});
    });
};

var completeError = function(err, res) {
	if (err) {
		console.log(err);
		if (res) res.sendStatus(500);
	}
}

var completetran = function(err, data) {
	if (err) {
		console.log(err);
		return;
	}
	if (!data) {
		console.log('no data');
		return;
	}
	if (!data.tranDone) {
		data.tranDone = true;
		if (data.action && util.isFunction(data.action))
			data.action();
		if (data.res && data.sendStatus && util.isFunction(data.res.sendStatus)) {
			console.log('sending status ' + data.sendStatus);
			data.res.sendStatus(data.sendStatus);
		}
	}
}

var rollback500 = function(err, res, tran) {
	completetran(err, {
		tranDone: tranDone,
		action: tran.rollback,
		res: res,
		sendStatus: 500
	});
}

var rollback200 = function(res, tran) {
	completetran(undefined, {
		tranDone: tranDone,
		action: tran.rollback,
		res: res,
		sendStatus: 200
	});
}

var commit200 = function(res, tran) {
	completetran(undefined, {
		tranDone: tranDone,
		action: tran.commit,
		res: res,
		sendStatus: 200
	});
}

module.exports = api;
<|endoftext|>


<commit_before>// Find all indexes of given value

var indexOf = Array.prototype.indexOf

module.exports = function (value) {
	var r = [], start = 0, i;
	while ((i = indexOf.call(this, value, start)) !== -1) {
		r.push(i);
		start = i + 1;
	}
	return r;
};
<commit_msg>Added fromIndex argument to indexesOf method<commit_after>// Find all indexes of given value

var indexOf = Array.prototype.indexOf

module.exports = function (value, fromIndex) {
	var r = [], i;
	while ((i = indexOf.call(this, value, fromIndex)) !== -1) {
		r.push(i);
		fromIndex = i + 1;
	}
	return r;
};
<|endoftext|>


<commit_before>/**
 * Copyright (c) UNA, Inc - https://una.io
 * MIT License - https://opensource.org/licenses/MIT
 *
 * @defgroup    Analytics Analytics
 * @ingroup     UnaModules
 *
 * @{
 */

function BxAnalytics(oOptions) {
    this._sContainerId = 'bx_analytics_container';
    this._sModuleSelector = '#' + this._sContainerId + ' .bx_analytics_module_selector';
    this._sReportSelector = '#' + this._sContainerId + " .bx_analytics_report_selector";
    this._sContainerDataSelector = '#' + this._sContainerId + " .bx_analytics_data";
    this._sCanvasSelector = '#' + this._sContainerId + " .bx_analytics_grath";
    this._sDatePickerSelector = '#' + this._sContainerId + " .bx_analytics_date_picker";
    this._sActionsUri = oOptions.sActionUri;
    this._aStrings = oOptions.aStrings;
    var $this = this;
    $(document).ready(function () {
        $this.init();
    });
}

BxAnalytics.prototype.init = function () {
    var $this = this;
    $($this._sModuleSelector).change(function () {
        $this.reloadReports();
    });
    $($this._sReportSelector).change(function () {
        $this.reloadData();
    });
    $this.reloadReports();

    $($this._sDatePickerSelector).daterangepicker({
        opens: 'left',
        locale: {
            "format": "DD/MM/YYYY"
        },
    }, function (start, end, label) {
        $this.reloadData();
    });

}

BxAnalytics.prototype.reloadReports = function () {
    var $this = this;
    $.getJSON($this._sActionsUri + 'GetReports/' + $($this._sModuleSelector).val() + '/', function (aReports) {
        $($this._sReportSelector).empty();
        $.each(aReports, function (key, value) {
            $($this._sReportSelector).append('<option value="' + key + '">' + value + '</option>');
        });
        $this.reloadData();
    });
};

BxAnalytics.prototype.reloadData = function () {
    var $this = this;
    bx_loading($($this._sContainerDataSelector), true);
    $.getJSON($this._sActionsUri + 'GetReportsData/' + $($this._sModuleSelector).val() + '/' + $($this._sReportSelector).val() + '/' + $($this._sDatePickerSelector).data('daterangepicker').startDate.format('YYYY-MM-DD') + '/' + $($this._sDatePickerSelector).data('daterangepicker').endDate.format('YYYY-MM-DD') + '/', function (oData) {

        Chart.defaults.global.layout = {
            padding: {
                left: 40,
                right: 40,
                top: 40,
                bottom: 20
            }
        }

        Chart.defaults.scale.ticks.callback = function (label, index, labels) {
            if (label.length > 10)
                return label.slice(0, 10) + '...';
            else
                return label;
        };

        bx_loading($($this._sContainerDataSelector), false);
        var oDataForChart = oData.data || false;
        var oChartOptions = oData.options || oOptionsDefault;
        if ($this._oChart)
            $this._oChart.destroy();
        $this._oChart = new Chart($($this._sCanvasSelector), {
            type: oData.type != undefined ? oData.type : 'line',
            data: oDataForChart,
            options: $.extend({}, $this._oChartOptionsDefault, oChartOptions)
        });
        $this.dataToTable(oData);
    });
};

BxAnalytics.prototype.dataToTable = function (oDataIn) {
    var $this = this;
    oData = oDataIn.data
    var sHtml = '
';
    var sCol1 = oDataIn.strings[0];

    var iColumnCount = oData.datasets.length;
    sHtml += '

 ' + sCol1 + '	';
    $.each(oData.datasets, function (iDx, oItem) {
        var sText = oItem.label;
        sHtml += '' + (oItem.label != '' ? oItem.label : oDataIn.strings[1]) + '	';
    });

    sHtml += '

';
    var iK = 0;

    if (oData.labels.length == 0) {
        oData.datasets[0].data.reverse();
    }

    $.each(oData.datasets[0].data, function (iDx, oItemData) {
        var sTxt = "";
        if (oData.labels.length > 0)
            sTxt = oData.labels[iDx];
        else{
            sTxt = oData.datasets[0].data[iDx].x;
        }
        if (oDataIn.links && count(oDataIn.links) > 0) {
            sTxt = "" + sTxt + ""
        }
        sHtml += '
 '+  sTxt + '	';
        for (i = 0; i < iColumnCount; i++) {
            var sText = '';
            if (typeof oData.datasets[i].data[iDx] === 'object') {
                sText = oData.datasets[i].data[iDx].y;
            }
            else {
                sText = oData.datasets[i].data[iDx];
            }
            sHtml += '' + sText + '	';
        }
        sHtml += '
';
        iK++;
    });

    sHtml += '

';
    if ($this._oTable)
        $this._oTable.destroy();
    $('.bx_analytics_table').html(sHtml);
    $this._oTable = $('.bx_analytics_table').DataTable({ dom: '<"top"i>rt<"bottom"flp><"clear">', paging: true, searching: false, ordering: true, order: [] });
};
/** @} */
<commit_msg>#1526<commit_after>/**
 * Copyright (c) UNA, Inc - https://una.io
 * MIT License - https://opensource.org/licenses/MIT
 *
 * @defgroup    Analytics Analytics
 * @ingroup     UnaModules
 *
 * @{
 */

function BxAnalytics(oOptions) {
    this._sContainerId = 'bx_analytics_container';
    this._sModuleSelector = '#' + this._sContainerId + ' .bx_analytics_module_selector';
    this._sReportSelector = '#' + this._sContainerId + " .bx_analytics_report_selector";
    this._sContainerDataSelector = '#' + this._sContainerId + " .bx_analytics_data";
    this._sCanvasSelector = '#' + this._sContainerId + " .bx_analytics_grath";
    this._sDatePickerSelector = '#' + this._sContainerId + " .bx_analytics_date_picker";
    this._sActionsUri = oOptions.sActionUri;
    this._aStrings = oOptions.aStrings;
    var $this = this;
    $(document).ready(function () {
        $this.init();
    });
}

BxAnalytics.prototype.init = function () {
    var $this = this;
    $($this._sModuleSelector).change(function () {
        $this.reloadReports();
    });
    $($this._sReportSelector).change(function () {
        $this.reloadData();
    });
    $this.reloadReports();

    $($this._sDatePickerSelector).daterangepicker({
        opens: 'left',
        locale: {
            "format": "DD/MM/YYYY"
        },
    }, function (start, end, label) {
        $this.reloadData();
    });

}

BxAnalytics.prototype.reloadReports = function () {
    var $this = this;
    $.getJSON($this._sActionsUri + 'GetReports/' + $($this._sModuleSelector).val() + '/', function (aReports) {
        $($this._sReportSelector).empty();
        $.each(aReports, function (key, value) {
            $($this._sReportSelector).append('<option value="' + key + '">' + value + '</option>');
        });
        $this.reloadData();
    });
};

BxAnalytics.prototype.reloadData = function () {
    var $this = this;
    bx_loading($($this._sContainerDataSelector), true);
    $.getJSON($this._sActionsUri + 'GetReportsData/' + $($this._sModuleSelector).val() + '/' + $($this._sReportSelector).val() + '/' + $($this._sDatePickerSelector).data('daterangepicker').startDate.format('YYYY-MM-DD') + '/' + $($this._sDatePickerSelector).data('daterangepicker').endDate.format('YYYY-MM-DD') + '/', function (oData) {

        Chart.defaults.global.layout = {
            padding: {
                left: 40,
                right: 40,
                top: 40,
                bottom: 20
            }
        }

        Chart.defaults.scale.ticks.callback = function (label, index, labels) {
            if (label.length > 10)
                return label.slice(0, 10) + '...';
            else
                return label;
        };

        bx_loading($($this._sContainerDataSelector), false);
        var oDataForChart = oData.data || false;
        var oChartOptions = oData.options || oOptionsDefault;
        if ($this._oChart)
            $this._oChart.destroy();
        $this._oChart = new Chart($($this._sCanvasSelector), {
            type: oData.type != undefined ? oData.type : 'line',
            data: oDataForChart,
            options: $.extend({}, $this._oChartOptionsDefault, oChartOptions)
        });
        $this.dataToTable(oData);
    });
};

BxAnalytics.prototype.dataToTable = function (oDataIn) {
    var $this = this;
    oData = oDataIn.data
    var sHtml = '
';
    var sCol1 = oDataIn.strings[0];

    var iColumnCount = oData.datasets.length;
    sHtml += '

 ' + sCol1 + '	';
    $.each(oData.datasets, function (iDx, oItem) {
        var sText = oItem.label;
        sHtml += '' + (oItem.label != '' ? oItem.label : oDataIn.strings[1]) + '	';
    });

    sHtml += '

';
    var iK = 0;

    if (oData.labels.length == 0) {
        oData.datasets[0].data.reverse();
    }

    $.each(oData.datasets[0].data, function (iDx, oItemData) {
        var sTxt = "";
        if (oData.labels.length > 0)
            sTxt = oData.labels[iDx];
        else{
            sTxt = oData.datasets[0].data[iDx].x;
        }
        if (oDataIn.links && oDataIn.links.length > 0) {
            sTxt = "" + sTxt + ""
        }
        sHtml += '
 '+  sTxt + '	';
        for (i = 0; i < iColumnCount; i++) {
            var sText = '';
            if (typeof oData.datasets[i].data[iDx] === 'object') {
                sText = oData.datasets[i].data[iDx].y;
            }
            else {
                sText = oData.datasets[i].data[iDx];
            }
            sHtml += '' + sText + '	';
        }
        sHtml += '
';
        iK++;
    });

    sHtml += '

';
    if ($this._oTable)
        $this._oTable.destroy();
    $('.bx_analytics_table').html(sHtml);
    $this._oTable = $('.bx_analytics_table').DataTable({ dom: '<"top"i>rt<"bottom"flp><"clear">', paging: true, searching: false, ordering: true, order: [] });
};
/** @} */
<|endoftext|>


<commit_before>import React from 'react'
import ReactDOM from 'react-dom'
import VoicePlayer from './voice-player'

class Demo extends React.Component {
  state = {
    play: true,
    pause: false
  }

  render () {
    return (

        <button onClick={() => this.setState({ play: true })}>Play</button>
        <button onClick={() => this.setState({ pause: true })}>Pause</button>
        <button onClick={() => this.setState({ pause: false })}>Resume</button>
        <button onClick={() => this.setState({ cancel: true })}>Resume</button>

        <VoicePlayer
          text="Welcome to React Voice Components! Please turn off the lights before leaving"
          lang="en-us"
          play={this.state.play}
          pause={this.state.pause}
        />

    )
  }
}

ReactDOM.render(
  <Demo />,
  document.getElementById('root')
)
<commit_msg>Add pause/resume examples on demo<commit_after>import React from 'react'
import ReactDOM from 'react-dom'
import VoicePlayer from './voice-player'

class Demo extends React.Component {
  state = {
    play: true,
    pause: false
  }

  render () {
    return (

        <button onClick={() => this.setState({ play: true })}>Play</button>
        <button onClick={() => this.setState({ pause: true })}>Pause</button>
        <button onClick={() => this.setState({ pause: false })}>Resume</button>

        <VoicePlayer
          play={this.state.play}
          pause={this.state.pause}
          text="Welcome to the jungle! We got fun and games"
          onPause={() => console.info('pause')}
          onResume={() => console.info('resume')}
          onStart={() => console.info('start')}
          onEnd={() => console.info('end')}
        />

    )
  }
}

ReactDOM.render(
  <Demo />,
  document.getElementById('root')
)

<|endoftext|>


<commit_before>/**
 * Created by elad.benedict on 8/12/2015.
 */

var m3u8Parser = require('./promise-m3u8');
var loggerDecorator = require('./utils/log-decorator');
var qio = require('q-io/fs');
var Q = require('q');
var path = require('path');
var _ = require('underscore');

module.exports = function(outputFolderPath, filename, timeWindowSize, maxChunkCount, logger){

    timeWindowSize = timeWindowSize || Number.MAX_VALUE;
    maxChunkCount = maxChunkCount || Number.MAX_VALUE;

    var messageDecoration = function(msg) {
        return "Chunklist manifest generator: " + msg;
    };
    var decoratedLogger = loggerDecorator(logger, messageDecoration);

    var currentManifest;
    var shouldAddDiscontinuityTag = false;

    var manifestFullPath = path.join(outputFolderPath, filename);

    var readCurrentManifest = function readCurrentManifest(){
        return m3u8Parser.parseM3U8(manifestFullPath);
    };

    var writeCurrentManifest = function writeCurrentManifest(){
        return qio.write(manifestFullPath, currentManifest.toString());
    };

    // Generate an empty M3U8 or read the existing manifest from disk
    var init = function init()
    {
        var manifestPath = path.join(outputFolderPath, filename);
        return qio.exists(manifestPath).then(function(result)
        {
            var currentManifest;
            if (!result) {
                currentManifest = createEmptyM3U8();
                return qio.write(manifestPath, currentManifest.toString()).then(function()
                {
                    return currentManifest;
                });
            }
            else
            {
                // A manifest already exists - add a discontinuity tag upon next chunk addition
                // *** Temporarily disabled since our player currently doesn't support this tag **
                shouldAddDiscontinuityTag = false; // should have been true
                return readCurrentManifest();
            }
        }).then(function(manifest)
        {
            // Update in-memory manifest
            currentManifest = manifest;
            return currentManifest;
        });
    };

    var createEmptyM3U8 = function createEmptyM3U8()
    {
        var manifest = new m3u8Parser.M3U();
        manifest.set("allowCache", "NO");
        manifest.set("version", 3);
        manifest.set("mediaSequence", 0);
        manifest.set("targetDuration", 0);
        return manifest;
    };

    var update = function updateChunklist(newItems) {

        // 1. Read or create chunklist
        // 2. Add discontinuity if relevant
        // 3. Append chunks
        // 4. Update media sequence number
        // 5. Add EXT-X-ENDLIST if relevant
        // 6. Write updated chunklist to disk
        var result = {
            removedChunks : []
        };

        return Q.fcall(function()
        {
            var itemToRemove, i;

            logger.debug('new items to update: ' + _.map(newItems, function(item) { return item.get('uri') + " ";}));

            // Ignore any items that are already available in the manifest
            if (currentManifest.items.PlaylistItem.length > 0) {
                var lastItemInCurrentManifest = _.last(currentManifest.items.PlaylistItem);
                var indexToStartFrom = _.indexOf(_.map(newItems, function (item) {
                    return item.get('uri');
                }), lastItemInCurrentManifest.get('uri'));
                if (indexToStartFrom >= 0) {
                    for (i = 0; i <= indexToStartFrom; i++) {
                        itemToRemove = newItems.shift();
                        logger.info('item at index ' + i + ' - ' + itemToRemove.get('uri') + ' already appears in manifest - ignoring it');
                    }
                }
            }

            var originalDuration = currentManifest.get('targetDuration');
            var maxChunkDuration = originalDuration;

            if (shouldAddDiscontinuityTag && newItems.length > 0)
            {
                // Update first item with discontinuity tag
                newItems[0].set('discontinuity', true);
                shouldAddDiscontinuityTag = false;
            }

            // Add new items
            _.forEach(newItems, function(item)
            {
                var itemDuration = item.get('duration');
                if (itemDuration > maxChunkDuration){
                    maxChunkDuration = itemDuration;
                }
                currentManifest.addItem(item);
            });

            // Remove items exceeding the DVR window size
            var totalDuration = currentManifest.totalDuration();
            var exceedingAmount = totalDuration - timeWindowSize;
            if (exceedingAmount > 0)
            {
                decoratedLogger.info("Manifest overall duration passed the DVR window size - removing chunks");
                while (exceedingAmount > 0)
                {
                    itemToRemove = currentManifest.items.PlaylistItem.shift();
                    decoratedLogger.debug('Removed ' + itemToRemove.get('uri') + ' with duration ' + itemToRemove.get('duration'));
                    exceedingAmount = exceedingAmount - itemToRemove.get('duration');
                    result.removedChunks.push(itemToRemove);
                }
            }

            // Remove items exceeding the chunk count
            var manifestLength = currentManifest.items.PlaylistItem.length;
            if (manifestLength > maxChunkCount)
            {
                decoratedLogger.info("Manifest chunk count exceeded the allowed chunk limit - removing chunks");
                var chunksToRemove = manifestLength - maxChunkCount;
                for (i=0 ; i<chunksToRemove ; i++)
                {
                    itemToRemove = currentManifest.items.PlaylistItem.shift();
                    result.removedChunks.push(itemToRemove);
                    decoratedLogger.debug('Removed ' + itemToRemove.get('uri') + ' with duration ' + itemToRemove.get('duration'));
                }
            }

            decoratedLogger.info("Removed " + result.removedChunks.length + " items from the manifest");

            // Update manifest sequence number
            var currentSequenceNumber = currentManifest.get('mediaSequence');
            currentManifest.set('mediaSequence', currentSequenceNumber + result.removedChunks.length);

            var playlistLength = currentManifest.items.PlaylistItem.length;
            decoratedLogger.debug('Length of currentManifest.items.PlaylistItem: ' + playlistLength);
            if (playlistLength > 0) { // The manifest is not empty
                // Update manifest media sequence duration with maximal #EXTINF (rounded up)
                var chunkWithLongestDuration = _.max(currentManifest.items.PlaylistItem, function(item) {
                    return item.get('duration');
                });

                decoratedLogger.debug("chunkWithLongestDuration: " + JSON.stringify(chunkWithLongestDuration));
                if (chunkWithLongestDuration) { // Some chunk exists
                    currentManifest.set('targetDuration', Math.ceil(chunkWithLongestDuration.get('duration')));
                }
            }
        }).then(function(){
            return writeCurrentManifest();
        }).then(function(){
            return result;
        });
    };

    var getCurrentManifest = function getCurrentManifest()
    {
        return currentManifest;
    };

    return {
        init : init,
        update : update,
        getCurrentManifest : getCurrentManifest
    };
};<commit_msg>Optimization - write manifest only if it changed<commit_after>/**
 * Created by elad.benedict on 8/12/2015.
 */

var m3u8Parser = require('./promise-m3u8');
var loggerDecorator = require('./utils/log-decorator');
var qio = require('q-io/fs');
var Q = require('q');
var path = require('path');
var _ = require('underscore');

module.exports = function(outputFolderPath, filename, timeWindowSize, maxChunkCount, logger){

    timeWindowSize = timeWindowSize || Number.MAX_VALUE;
    maxChunkCount = maxChunkCount || Number.MAX_VALUE;

    var messageDecoration = function(msg) {
        return "Chunklist manifest generator: " + msg;
    };
    var decoratedLogger = loggerDecorator(logger, messageDecoration);

    var currentManifest;
    var shouldAddDiscontinuityTag = false;

    var manifestFullPath = path.join(outputFolderPath, filename);

    var readCurrentManifest = function readCurrentManifest(){
        return m3u8Parser.parseM3U8(manifestFullPath);
    };

    var writeCurrentManifest = function writeCurrentManifest(){
        return qio.write(manifestFullPath, currentManifest.toString());
    };

    // Generate an empty M3U8 or read the existing manifest from disk
    var init = function init()
    {
        var manifestPath = path.join(outputFolderPath, filename);
        return qio.exists(manifestPath).then(function(result)
        {
            var currentManifest;
            if (!result) {
                currentManifest = createEmptyM3U8();
                return qio.write(manifestPath, currentManifest.toString()).then(function()
                {
                    return currentManifest;
                });
            }
            else
            {
                // A manifest already exists - add a discontinuity tag upon next chunk addition
                // *** Temporarily disabled since our player currently doesn't support this tag **
                shouldAddDiscontinuityTag = false; // should have been true
                return readCurrentManifest();
            }
        }).then(function(manifest)
        {
            // Update in-memory manifest
            currentManifest = manifest;
            return currentManifest;
        });
    };

    var createEmptyM3U8 = function createEmptyM3U8()
    {
        var manifest = new m3u8Parser.M3U();
        manifest.set("allowCache", "NO");
        manifest.set("version", 3);
        manifest.set("mediaSequence", 0);
        manifest.set("targetDuration", 0);
        return manifest;
    };

    var update = function updateChunklist(newItems) {

        // 1. Read or create chunklist
        // 2. Add discontinuity if relevant
        // 3. Append chunks
        // 4. Update media sequence number
        // 5. Add EXT-X-ENDLIST if relevant
        // 6. Write updated chunklist to disk
        var result = {
            removedChunks : []
        };

        return Q.fcall(function()
        {
            var itemToRemove, i;

            logger.debug('new items to update: ' + _.map(newItems, function(item) { return item.get('uri') + " ";}));

            // Ignore any items that are already available in the manifest
            if (currentManifest.items.PlaylistItem.length > 0) {
                var lastItemInCurrentManifest = _.last(currentManifest.items.PlaylistItem);
                var indexToStartFrom = _.indexOf(_.map(newItems, function (item) {
                    return item.get('uri');
                }), lastItemInCurrentManifest.get('uri'));
                if (indexToStartFrom >= 0) {
                    for (i = 0; i <= indexToStartFrom; i++) {
                        itemToRemove = newItems.shift();
                        logger.info('item at index ' + i + ' - ' + itemToRemove.get('uri') + ' already appears in manifest - ignoring it');
                    }
                }
            }

            if (newItems.length === 0)
            {
                // No new items
                logger.debug("No new chunks to update");
                return false;
            }

            var originalDuration = currentManifest.get('targetDuration');
            var maxChunkDuration = originalDuration;

            if (shouldAddDiscontinuityTag && newItems.length > 0)
            {
                // Update first item with discontinuity tag
                newItems[0].set('discontinuity', true);
                shouldAddDiscontinuityTag = false;
            }

            // Add new items
            _.forEach(newItems, function(item)
            {
                var itemDuration = item.get('duration');
                if (itemDuration > maxChunkDuration){
                    maxChunkDuration = itemDuration;
                }
                currentManifest.addItem(item);
            });

            // Remove items exceeding the DVR window size
            var totalDuration = currentManifest.totalDuration();
            var exceedingAmount = totalDuration - timeWindowSize;
            if (exceedingAmount > 0)
            {
                decoratedLogger.info("Manifest overall duration passed the DVR window size - removing chunks");
                while (exceedingAmount > 0)
                {
                    itemToRemove = currentManifest.items.PlaylistItem.shift();
                    decoratedLogger.debug('Removed ' + itemToRemove.get('uri') + ' with duration ' + itemToRemove.get('duration'));
                    exceedingAmount = exceedingAmount - itemToRemove.get('duration');
                    result.removedChunks.push(itemToRemove);
                }
            }

            // Remove items exceeding the chunk count
            var manifestLength = currentManifest.items.PlaylistItem.length;
            if (manifestLength > maxChunkCount)
            {
                decoratedLogger.info("Manifest chunk count exceeded the allowed chunk limit - removing chunks");
                var chunksToRemove = manifestLength - maxChunkCount;
                for (i=0 ; i<chunksToRemove ; i++)
                {
                    itemToRemove = currentManifest.items.PlaylistItem.shift();
                    result.removedChunks.push(itemToRemove);
                    decoratedLogger.debug('Removed ' + itemToRemove.get('uri') + ' with duration ' + itemToRemove.get('duration'));
                }
            }

            decoratedLogger.info("Removed " + result.removedChunks.length + " items from the manifest");

            // Update manifest sequence number
            var currentSequenceNumber = currentManifest.get('mediaSequence');
            currentManifest.set('mediaSequence', currentSequenceNumber + result.removedChunks.length);

            var playlistLength = currentManifest.items.PlaylistItem.length;
            decoratedLogger.debug('Length of currentManifest.items.PlaylistItem: ' + playlistLength);
            if (playlistLength > 0) { // The manifest is not empty
                // Update manifest media sequence duration with maximal #EXTINF (rounded up)
                var chunkWithLongestDuration = _.max(currentManifest.items.PlaylistItem, function(item) {
                    return item.get('duration');
                });

                decoratedLogger.debug("chunkWithLongestDuration: " + JSON.stringify(chunkWithLongestDuration));
                if (chunkWithLongestDuration) { // Some chunk exists
                    currentManifest.set('targetDuration', Math.ceil(chunkWithLongestDuration.get('duration')));
                }
            }
            return true;
        }).then(function(manifestChanged){
            if (manifestChanged)
            {
                return writeCurrentManifest();
            }
        }).then(function(){
            return result;
        });
    };

    var getCurrentManifest = function getCurrentManifest()
    {
        return currentManifest;
    };

    return {
        init : init,
        update : update,
        getCurrentManifest : getCurrentManifest
    };
};<|endoftext|>


<commit_before>/*globals define, _*/
/*jshint browser: true, camelcase: false*/

define([
    'decorators/EllipseDecorator/EasyDAG/EllipseDecorator.EasyDAGWidget',
    './PointerField.RO',
    './AttributeField.RO',
    'css!./JobDecorator.EasyDAGWidget.css'
], function (
    EllipseDecorator,
    PointerField,
    AttributeField
) {

    'use strict';

    var JobDecorator,
        DECORATOR_ID = 'JobDecorator',
        COLORS = {
            pending: '#9e9e9e',
            queued: '#cfd8dc',
            running: '#fff59d',
            canceled: '#ffcc80',
            success: '#66bb6a',
            fail: '#e57373'
        };

    // Job nodes need to be able to...
    //     - show their ports
    //     - highlight ports
    //     - unhighlight ports
    //     - report the location of specific ports
    JobDecorator = function (options) {
        options.skipAttributes = {
            name: true,
            status: true,
            execFiles: true,
            stdout: true,
            secret: true,
            jobId: true,
            debug: true
        };
        EllipseDecorator.call(this, options);
    };

    _.extend(JobDecorator.prototype, EllipseDecorator.prototype);

    JobDecorator.prototype.DECORATOR_ID = DECORATOR_ID;
    JobDecorator.prototype.AttributeField = AttributeField;
    JobDecorator.prototype.PointerField = PointerField;

    JobDecorator.prototype.getDisplayName = function() {
        return this._node.name;
    };

    JobDecorator.prototype.setAttributes = function() {
        EllipseDecorator.prototype.setAttributes.call(this);
        var attrs = this._node.attributes,
            status = attrs.status && attrs.status.value,
            opAttrs = Object.keys(this._node.opAttributes);

        // Update the color based on the 'status' attr
        this.color = COLORS[status] || COLORS.fail;

        // Set _attributes from opAttributes
        for (var i = opAttrs.length; i--;) {
            this._attributes[opAttrs[i]] = this._node.opAttributes[opAttrs[i]];
        }
    };

    return JobDecorator;
});
<commit_msg>Set the ArtifactLoader name to target in execution view. Fixes #574 (#706)<commit_after>/*globals define, _*/
/*jshint browser: true, camelcase: false*/

define([
    'decorators/EllipseDecorator/EasyDAG/EllipseDecorator.EasyDAGWidget',
    './PointerField.RO',
    './AttributeField.RO',
    'css!./JobDecorator.EasyDAGWidget.css'
], function (
    EllipseDecorator,
    PointerField,
    AttributeField
) {

    'use strict';

    var JobDecorator,
        DECORATOR_ID = 'JobDecorator',
        COLORS = {
            pending: '#9e9e9e',
            queued: '#cfd8dc',
            running: '#fff59d',
            canceled: '#ffcc80',
            success: '#66bb6a',
            fail: '#e57373'
        };

    // Job nodes need to be able to...
    //     - show their ports
    //     - highlight ports
    //     - unhighlight ports
    //     - report the location of specific ports
    JobDecorator = function (options) {
        options.skipAttributes = {
            name: true,
            status: true,
            execFiles: true,
            stdout: true,
            secret: true,
            jobId: true,
            debug: true
        };
        EllipseDecorator.call(this, options);
    };

    _.extend(JobDecorator.prototype, EllipseDecorator.prototype);

    JobDecorator.prototype.DECORATOR_ID = DECORATOR_ID;
    JobDecorator.prototype.AttributeField = AttributeField;
    JobDecorator.prototype.PointerField = PointerField;

    JobDecorator.prototype.isArtifactLoader = function() {
        return this._node.name === 'ArtifactLoader';
    };

    JobDecorator.prototype.getDisplayName = function() {
        if (this.isArtifactLoader()) {
            var id = this._node.pointers.artifact;

            // Try to look up the pointer name
            return this.nameFor[id] || this._node.name;
        }
        return this._node.name;
    };

    JobDecorator.prototype.setAttributes = function() {
        EllipseDecorator.prototype.setAttributes.call(this);
        var attrs = this._node.attributes,
            status = attrs.status && attrs.status.value,
            opAttrs = Object.keys(this._node.opAttributes);

        // Update the color based on the 'status' attr
        this.color = COLORS[status] || COLORS.fail;

        // Set _attributes from opAttributes
        for (var i = opAttrs.length; i--;) {
            this._attributes[opAttrs[i]] = this._node.opAttributes[opAttrs[i]];
        }
    };

    JobDecorator.prototype.updateTargetName = function() {
        EllipseDecorator.prototype.updateTargetName.apply(this, arguments);
        var name = this.getDisplayName();

        if (name !== this.name) {
            this.name = name;
            this.onResize();
        }
    };

    return JobDecorator;
});
<|endoftext|>