<commit_before>/**
 *  ApiTree
 *
 *  A collection of tools to browserify server/shared/client trees.
 **/

/*global underscore, N*/

'use strict';

// stdlib
var fs = require('fs');

// 3rd-party
var _         = underscore;
var async     = require('async');
var fstools   = require('fs-tools');
var traverse  = require('traverse');

// internal
var apify         = require('./apify');
var safePropName  = require('./safe_prop_name');

////////////////////////////////////////////////////////////////////////////////

// internal
// collect(pathnames, callback(err, map)) -> Void
// - pathnames (Array): List of pathnames where to find JS files.
// - callback (Function): Executed once everything is done.
//
// Collect files from all `pathanmes` and build a map of `api -> str` pairs.
//
function collect(pathnames, callback) {
  var map = {};

  async.forEach(pathnames, function (pathname, nextPath) {
    fs.exists(pathname, function (exists) {
      if (!exists) {
        nextPath();
        return;
      }

      fstools.walk(pathname, /[.]js$/, function (file, lstats, nextFile) {
        fs.readFile(file, 'utf8', function (err, str) {
          if (err) {
            nextFile(err);
            return;
          }

          map[apify(file, pathname)] = str;
          nextFile();
        });
      }, nextPath);
    });
  }, function (err) {
    callback(err, map);
  });
}

/**
 *  ApiTree.browserifySources(pathnames, namespace, callback(err, str)) -> Void
 *  - pathnames (String|Array): Pathname(s) where to find JS files.
 *  - namespace (String): Global object used as container for the tree.
 *  - callback (Function): Executed once everything is done.
 *
 *  Generates a browserified function that will create api tree and will
 *  inject it into the `namespace`.
 *
 *
 *  ##### Example
 *
 *      var pathnames = ['/path/to/app1/client', '/path/to/app2/client'];
 *      browserifySources(pathnames, 'this.client', function (err, str) {
 *        // ...
 *        fs.writeFile('/path/to/my-app/public/api-client.js', str);
 *        // ...
 *      });
 **/
function browserifySources(pathnames, namespace, callback) {
  pathnames = _.isArray(pathnames) ? pathnames : [pathnames];

  collect(pathnames, function (err, sources) {
    var keys = _.keys(sources), head = [], body = [];

    if (err) {
      callback(err);
      return;
    }

    //
    // generate structure needed to build the tree
    //

    keys.forEach(function (api) {
      var parts = api.split('.').map(safePropName);

      // the last element is the function itself, so skip it
      parts.pop();

      while (parts.length) {
        head.push('this' + parts.join(''));
        parts.pop();
      }
    });

    head = _.uniq(head.sort(), true).map(function (ns) {
      return 'if (!' + ns + ') { ' + ns + ' = {}; }';
    });

    //
    // generate tree population with functions
    //

    keys.sort().forEach(function (api) {
      body.push('this' + api.split('.').map(safePropName).join('') +
                ' = (function () { "use strict";\n' +
                '(function (exports, module) {\n' +
                sources[api] +
                '}.call(this.exports, this.exports, this));\n' +
                'return this.exports;\n' +
                '}.call({exports: {}}));');
    });

    //
    // Return tree populator
    //

    callback(null,
             '(function () {\n' +
             head.join('\n') + '\n' + body.join('\n') +
             '\n}.call(' + namespace + ' || (' + namespace + ' = {})));');
  });
}

/**
 *  ApiTree.browserifyServerTree(obj[, namespace = 'this.server'[, options = {}]]) -> String
 *  - pathnames (Array): List of pathnames where to find JS files.
 *  - namespace (String): Global object used as container for the tree.
 *  - options (Object): Generator options (See below)
 *
 *  Generates a browserified function that will create api tree and will inject
 *  it into the `namespace`. Each generated method node will become a wrapper
 *  for corresponding server API tree method.
 *
 *
 *  ##### Options
 *
 *  - *prefix* (String): Prefix of server method wrappers. Useful when you
 *    generate a tree with offset, e.g. `'server.admin'`.
 *    Default: `''`.
 *
 *  - *method* (String): rpc function used to make an api tree call internally.
 *    This function must have signature with four arguments in an order:
 *    `apiPath`, `params`, `options`, `callback`.
 *
 *
 *  ##### Example
 *
 *      var output = browserifyServerTree(N.server);
 *      fs.writeFile('/path/to/my-app/public/api-server.js', output);
 **/
function browserifyServerTree(obj, namespace, options) {
  var keys = [], head = [], body = [], prefix, method;

  options = options || {};
  prefix  = !options.prefix ? [] : [options.prefix];
  method  = options.method || 'apiTreeRPC';

  //
  // collect only `leaf` paths.
  // each `this.path` is an array of strings representing api path:
  // `foo.bar` -> ['foo', 'bar']
  //

  traverse.forEach(obj, function () {
    if (_.isFunction(this.node)) {
      keys.push(this.path.map(function (p) {
        return p.split('.');
      }).reduce(function (a, el) {
        return a.concat(el);
      }, []));
    }
  });

  //
  // sort keys as if they were strings
  //

  keys.sort(function (a, b) {
    return a.join('.').localeCompare(b.join('.'));
  });

  //
  // generate structure needed to build the tree
  //

  keys.forEach(function (api) {
    var parts = api.map(safePropName);

    // the last element is the function itself, so skip it
    parts.pop();

    while (parts.length) {
      head.push('this' + parts.join(''));
      parts.pop();
    }
  });

  head = _.uniq(head.sort(), true).map(function (ns) {
    return 'if (!' + ns + ') { ' + ns + ' = {}; }';
  });

  //
  // generate tree population with functions
  //

  keys.forEach(function (api) {
    var path = JSON.stringify(prefix.concat(api).join('.'));
    body.push('this' + api.map(safePropName).join('') + ' = ' +
              'function (params, opts, cb) { ' + method + '(' +
              path + ', params, opts, cb); ' + '};');
  });

  //
  // Return tree populator
  //

  return  '(function () {\n' +
          head.join('\n') + '\n' + body.join('\n') +
          '\n}.call(' + namespace + ' || (' + namespace + ' = {})));';
}

////////////////////////////////////////////////////////////////////////////////

module.exports.browserifySources    = browserifySources;
module.exports.browserifyServerTree = browserifyServerTree;
<commit_msg>Remove traverse from api_tree<commit_after>/**
 *  ApiTree
 *
 *  A collection of tools to browserify server/shared/client trees.
 **/

/*global underscore, N*/

'use strict';

// stdlib
var fs = require('fs');

// 3rd-party
var _         = underscore;
var async     = require('async');
var fstools   = require('fs-tools');

// internal
var apify         = require('./apify');
var safePropName  = require('./safe_prop_name');

////////////////////////////////////////////////////////////////////////////////

// internal
// collect(pathnames, callback(err, map)) -> Void
// - pathnames (Array): List of pathnames where to find JS files.
// - callback (Function): Executed once everything is done.
//
// Collect files from all `pathanmes` and build a map of `api -> str` pairs.
//
function collect(pathnames, callback) {
  var map = {};

  async.forEach(pathnames, function (pathname, nextPath) {
    fs.exists(pathname, function (exists) {
      if (!exists) {
        nextPath();
        return;
      }

      fstools.walk(pathname, /[.]js$/, function (file, lstats, nextFile) {
        fs.readFile(file, 'utf8', function (err, str) {
          if (err) {
            nextFile(err);
            return;
          }

          map[apify(file, pathname)] = str;
          nextFile();
        });
      }, nextPath);
    });
  }, function (err) {
    callback(err, map);
  });
}

/**
 *  ApiTree.browserifySources(pathnames, namespace, callback(err, str)) -> Void
 *  - pathnames (String|Array): Pathname(s) where to find JS files.
 *  - namespace (String): Global object used as container for the tree.
 *  - callback (Function): Executed once everything is done.
 *
 *  Generates a browserified function that will create api tree and will
 *  inject it into the `namespace`.
 *
 *
 *  ##### Example
 *
 *      var pathnames = ['/path/to/app1/client', '/path/to/app2/client'];
 *      browserifySources(pathnames, 'this.client', function (err, str) {
 *        // ...
 *        fs.writeFile('/path/to/my-app/public/api-client.js', str);
 *        // ...
 *      });
 **/
function browserifySources(pathnames, namespace, callback) {
  pathnames = _.isArray(pathnames) ? pathnames : [pathnames];

  collect(pathnames, function (err, sources) {
    var keys = _.keys(sources), head = [], body = [];

    if (err) {
      callback(err);
      return;
    }

    //
    // generate structure needed to build the tree
    //

    keys.forEach(function (api) {
      var parts = api.split('.').map(safePropName);

      // the last element is the function itself, so skip it
      parts.pop();

      while (parts.length) {
        head.push('this' + parts.join(''));
        parts.pop();
      }
    });

    head = _.uniq(head.sort(), true).map(function (ns) {
      return 'if (!' + ns + ') { ' + ns + ' = {}; }';
    });

    //
    // generate tree population with functions
    //

    keys.sort().forEach(function (api) {
      body.push('this' + api.split('.').map(safePropName).join('') +
                ' = (function () { "use strict";\n' +
                '(function (exports, module) {\n' +
                sources[api] +
                '}.call(this.exports, this.exports, this));\n' +
                'return this.exports;\n' +
                '}.call({exports: {}}));');
    });

    //
    // Return tree populator
    //

    callback(null,
             '(function () {\n' +
             head.join('\n') + '\n' + body.join('\n') +
             '\n}.call(' + namespace + ' || (' + namespace + ' = {})));');
  });
}

/**
 *  ApiTree.browserifyServerTree(obj[, namespace = 'this.server'[, options = {}]]) -> String
 *  - pathnames (Array): List of pathnames where to find JS files.
 *  - namespace (String): Global object used as container for the tree.
 *  - options (Object): Generator options (See below)
 *
 *  Generates a browserified function that will create api tree and will inject
 *  it into the `namespace`. Each generated method node will become a wrapper
 *  for corresponding server API tree method.
 *
 *
 *  ##### Options
 *
 *  - *prefix* (String): Prefix of server method wrappers. Useful when you
 *    generate a tree with offset, e.g. `'server.admin'`.
 *    Default: `''`.
 *
 *  - *method* (String): rpc function used to make an api tree call internally.
 *    This function must have signature with four arguments in an order:
 *    `apiPath`, `params`, `options`, `callback`.
 *
 *
 *  ##### Example
 *
 *      var output = browserifyServerTree(N.server);
 *      fs.writeFile('/path/to/my-app/public/api-server.js', output);
 **/
function browserifyServerTree(obj, namespace, options) {
  var head = [], body = [], keys, prefix, method;

  options = options || {};
  prefix  = !options.prefix ? [] : [options.prefix];
  method  = options.method || 'apiTreeRPC';

  //
  // collect only `leaf` paths.
  // each `this.path` is an array of strings representing api path:
  // `foo.bar` -> ['foo', 'bar']
  //

  keys = Object.keys(obj).filter(function (key) {
    return _.isFunction(obj[key]);
  }).sort().map(function (key) {
    return key.split('.');
  });

  //
  // generate structure needed to build the tree
  //

  keys.forEach(function (api) {
    var parts = api.map(safePropName);

    // the last element is the function itself, so skip it
    parts.pop();

    while (parts.length) {
      head.push('this' + parts.join(''));
      parts.pop();
    }
  });

  head = _.uniq(head.sort(), true).map(function (ns) {
    return 'if (!' + ns + ') { ' + ns + ' = {}; }';
  });

  //
  // generate tree population with functions
  //

  keys.forEach(function (api) {
    var path = JSON.stringify(prefix.concat(api).join('.'));
    body.push('this' + api.map(safePropName).join('') + ' = ' +
              'function (params, opts, cb) { ' + method + '(' +
              path + ', params, opts, cb); ' + '};');
  });

  //
  // Return tree populator
  //

  return  '(function () {\n' +
          head.join('\n') + '\n' + body.join('\n') +
          '\n}.call(' + namespace + ' || (' + namespace + ' = {})));';
}

////////////////////////////////////////////////////////////////////////////////

module.exports.browserifySources    = browserifySources;
module.exports.browserifyServerTree = browserifyServerTree;
<|endoftext|>


<commit_before>$(document).ready(function() {var formatter = new CucumberHTML.DOMFormatter($('.cucumber-report'));formatter.uri("src/test/resources/features/admin/resources-associations/resourcesAssociations.feature");
formatter.feature({
  "id": "resources-associations",
  "description": "",
  "name": "Resources associations",
  "keyword": "Feature",
  "line": 1
});
formatter.before({
  "duration": 6660526100,
  "status": "passed"
});
formatter.before({
  "duration": 49365200,
  "status": "passed"
});
formatter.before({
  "duration": 47505700,
  "status": "passed"
});
formatter.scenario({
  "id": "resources-associations;the-quantity-of-the-resources-associated-to-one-room-is-edited-after-changing-the-value-of-this",
  "description": "",
  "name": "The quantity of the resources associated to one room is edited after changing the value of this",
  "keyword": "Scenario",
  "line": 4,
  "type": "scenario"
});
formatter.step({
  "name": "I am on the Conferences Rooms page",
  "keyword": "Given ",
  "line": 5
});
formatter.step({
  "name": "I associate a resource on resources association page",
  "keyword": "And ",
  "line": 6
});
formatter.step({
  "name": "I modify the quantity of the of the associated resource",
  "keyword": "When ",
  "line": 7
});
formatter.step({
  "name": "I can see that the quantity modified is displayed",
  "keyword": "Then ",
  "line": 8
});
formatter.match({
  "location": "TheQuantityOfTheResourceAssociatedsEdited.i_am_on_the_Conferences_Rooms_page()"
});
formatter.result({
  "duration": 4001048900,
  "status": "passed"
});
formatter.match({
  "location": "TheQuantityOfTheResourceAssociatedsEdited.i_associate_a_resource_on_resources_association_page()"
});
formatter.result({
  "duration": 4628168600,
  "status": "passed"
});
formatter.match({
  "location": "TheQuantityOfTheResourceAssociatedsEdited.i_modify_the_quantity_of_the_of_the_associated_resource()"
});
formatter.result({
  "duration": 2727054400,
  "status": "passed"
});
formatter.match({
  "location": "TheQuantityOfTheResourceAssociatedsEdited.i_can_see_that_the_quantity_modified_is_displayed()"
});
formatter.result({
  "duration": 2262198000,
  "status": "passed"
});
formatter.after({
  "duration": 2239000,
  "status": "passed"
});
formatter.after({
  "duration": 3462300,
  "status": "passed"
});
formatter.after({
  "duration": 1256500,
  "status": "passed"
});
formatter.after({
  "duration": 1050500,
  "status": "passed"
});
});<commit_msg>Delete report.js<commit_after><|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

const program = require('commander');
const chalk = require('chalk');
const miteApi = require('mite-api');

const pkg = require('./../package.json');
const config = require('./config');
const formater = require('./lib/formater');
const DataOutput = require('./lib/data-output');
const listCommand = require('./lib/commands/list');
const columnOptions = require('./lib/options/columns');
const commandOptions = require('./lib/options');
const { handleError } = require('./lib/errors');

// no other options or comamnd line arguments used, default "period" to "today"
if (process.argv.length === 2) {
  process.argv.splice(2, 0, 'today');
}

program
  .version(pkg.version)
  .description('list time entries', {
    period:
      `name of the period for which the time entries should be shown \
or a single date. F.e. "today" or "last_week or "2019-03-17". \
Defaults to "today" when no other options are used and to "all" \
when other options are used.`
  })
  .arguments('<period>')
  .option.apply(program, commandOptions.toArgs(commandOptions.billable),
    'show entries which are billable or not billable'
  )
  .option(
    '--columns <columns>',
    columnOptions.description(listCommand.columns.options),
    columnOptions.parse
  )
  .option(
    '--customer-id <id>',
    'customer id, can be either a single ID, or multiple comma-separated IDs.'
  )
  .option.apply(program, commandOptions.toArgs(commandOptions.format, null, config.get('outputFormat')))
  .option(
    '--from <period|YYYY-MM-DD>',
    'in combination with "to" used for selecting a specific time frame of ' +
    'time entries to return, same as "period" argument or a specific date in ' +
    'the format "YYYY-MM-DD". The period argument is ignored.'
  )
  .option(
    '--group-by <column>',
    'optional grouping parameter which should be used. Valid values are ' +
    listCommand.groupBy.options.join(', ')
  )
  .option(
    '-l, --limit <limit>',
    'optional number of items to show, default is infinite',
    undefined,
    ((val) => parseInt(val, 10))
  )
  .option(
    '--locked <true|false>',
    'filter entries by their locked status',
    (val => ['true', 'yes', 'ja', 'ok', '1'].indexOf(val.toLowerCase()) > -1),
    null
  )
  .option(
    '--min-duration <minDuration>',
    'filter out entries which have a duration below the given value (client side)',
  )
  .option(
    '--max-duration <maxDuration>',
    'filter out entries which have a duration above the given value (client side)',
  )
  .option(
    '--project-id <id>',
    'project id, can be either a single ID, or multiple comma-separated IDs.'
  )
  .option(
    '--to <period|YYYY-MM-DD>',
    'in combination with "from" used for selecting a specific time frame of ' +
    'time entries to return, same as "period" argument or a specific date in ' +
    'the format "YYYY-MM-DD". The period argument is ignored.'
  )
  .option.apply(program, commandOptions.toArgs(commandOptions.tracking))
  .option(
    '--reversed',
    'reverse the sorting direction',
  )
  .option(
    '-s --search <query>',
    'search within the notes, to filter by multiple criteria connected with OR use comma-separated query values',
    ((val) => {
      if (typeof val === 'string') {
        return val.split(/\s*,\s*/);
      }
      return val;
    })
  )
  .option(
    '--service-id <id>',
    'service id, can be either a single ID, or multiple comma-separated IDs.'
  )
  .option.apply(program, commandOptions.toArgs(
    commandOptions.sort,
    commandOptions.sort.description(listCommand.sort.options),
    listCommand.sort.default
  ))
  .option(
    '--user-id <id>',
    'optional single user id who’s time entries should be returned or ' +
    'multiple values comma-separated. Note that the current user may not ' +
    'have the permission ot read other user’s time entries which will result ' +
    'in an empty response',
    ((val) => {
      if (typeof val === 'string') {
        return val.split(/\s*,\s*/);
      }
      return val;
    })
  )
  .on('--help', function() {
    console.log(`
Examples:

  list all entries from the current month:
    mite list this_month

  list all entries which note contains the given search query:
    mite list this_year --search JIRA-123

  show all entries from two services 123 and 38171
    mite list last_month --service-id 123,38171

  show all users who tracked billable entries ordered by the amount of time
  they have tracked:
    mite list this_year --billable true --columns user,duration --group-by user --sort duration

  export all time-entries from the current month as csv:
    mite list last_week --format csv --columns user,id

  create a markdown report of all customer and their generated profits:
    mite list this_year --format md --group-by customer --sort revenue

  The output of mite list can be forwarded to other commands using xargs. The
  following example will delete all matchin entries:
    mite list this_month --search="query" --columns id --format text | xargs -n1 mite delete

  Get the notes for one specific service, project for the last month to put
  them on a bill or similar
    mite list last_month --project-id 2681601 --service-id 325329 --columns note --format text | sort -u

  Show a seperate report for all users showing the revenues and times per
  service for all users matching a query:
    mite users --search marc --columns id --format text | xargs mite list last_month --group-by service --user-id

`);
  })
  .parse(process.argv);

/**
 * Returns the request options for requesting the time entries or grouped
 * time entries for the given time entries and filtering options.
 *
 * @param {string} period
 * @param {object} program
 * @return {Object<String, any>} time entries or grouped time entries
 */
function getRequestOptions(period, program) {
  const data = {
    ...(typeof program.billable === 'boolean' && { billable: program.billable }),
    ...(program.customerId && { customer_id: program.customerId }),
    ...(program.reversed && { direction: 'asc' }),
    ...(program.groupBy && { group_by: program.groupBy }),
    ...(program.limit && { limit: program.limit }),
    ...(typeof program.locked === 'boolean' && { locked: program.locked }),
    ...(program.search && { note: program.search }),
    ...(program.projectId && { project_id: program.projectId }),
    ...(program.serviceId && { service_id: program.serviceId }),
    ...(program.sort && { sort: program.sort }),
    ...(typeof program.tracking === 'boolean' && { tracking: program.tracking }),
    ...(program.userId && { user_id: program.userId}),
  };

  // add optional time period using from & to
  if (program.from && program.to) {
    data.from = program.from;
    data.to = program.to;
  } else if (period) {
    data.at = period;
  }

  return data;
}

/**
 * Callback function to filter time entries which are shown depending on the
 * command line options
 *
 * @param {object} program
 * @param {MiteTimeEntry} entry
 * @return {boolean}
 */
function filterData(program, row) {
  let valid = true;
  if (program.minDuration) {
    const minDuration = formater.durationToMinutes(program.minDuration, 10);
    valid &= row.minutes >= minDuration;
  }
  if (program.maxDuration) {
    const maxDuration = formater.durationToMinutes(program.maxDuration, 10);
    valid &= row.minutes <= maxDuration;
  }
  return valid;
}

function getReport(items, columns, format) {
  const tableData = DataOutput.compileTableData(items, columns);

  // Table footer
  // add table footer if any of the table columns has a reducer
  if (columnOptions.hasReducer(columns)) {
    let footerColumns = DataOutput.getTableFooterColumns(items, columns);
    footerColumns = footerColumns.map(v => v ? chalk.bold(v) : v); // make footer bold
    tableData.push(footerColumns);
  }

  return DataOutput.formatData(tableData, format, columns);
}

function main(period) {
  const mite = miteApi(config.get());

  const requestOpts = getRequestOptions(period, program);
  // "columns" default option is different
  if (!program.columns) {
    // when groupBy is used, make sure that revenue and duration are used
    if (program.groupBy) {
      program.columns = program.groupBy + ',revenue,duration';
    } else {
      program.columns = config.get().listColumns;
    }
  }
  const columns = columnOptions.resolve(program.columns, listCommand.columns.options);

  mite.getTimeEntries(requestOpts, (err, results) => {
    if (err) return handleError(err);

    // decide wheter to output grouped report or single entry report
    let items = results
      .map(data => data[program.groupBy ? 'time_entry_group' : 'time_entry'])
      .filter(v => v)
      .filter(filterData.bind(this, program))
      ;
    const report = getReport(items, columns, program.format);
    console.log(report);
  });
} // main

const period = program.args[0];
main(period);
<commit_msg>fix(mite-list): accept also underscored time period’s like "this_month"<commit_after>#!/usr/bin/env node
'use strict';

const program = require('commander');
const chalk = require('chalk');
const miteApi = require('mite-api');

const pkg = require('./../package.json');
const config = require('./config');
const formater = require('./lib/formater');
const DataOutput = require('./lib/data-output');
const listCommand = require('./lib/commands/list');
const columnOptions = require('./lib/options/columns');
const commandOptions = require('./lib/options');
const { handleError } = require('./lib/errors');

// no other options or comamnd line arguments used, default "period" to "today"
if (process.argv.length === 2) {
  process.argv.splice(2, 0, 'today');
}

program
  .version(pkg.version)
  .description('list time entries', {
    period:
      `name of the period for which the time entries should be shown \
or a single date. F.e. "today" or "last_week or "2019-03-17". \
Defaults to "today" when no other options are used and to "all" \
when other options are used.`
  })
  .arguments('<period>')
  .option.apply(program, commandOptions.toArgs(commandOptions.billable),
    'show entries which are billable or not billable'
  )
  .option(
    '--columns <columns>',
    columnOptions.description(listCommand.columns.options),
    columnOptions.parse
  )
  .option(
    '--customer-id <id>',
    'customer id, can be either a single ID, or multiple comma-separated IDs.'
  )
  .option.apply(program, commandOptions.toArgs(commandOptions.format, null, config.get('outputFormat')))
  .option(
    '--from <period|YYYY-MM-DD>',
    'in combination with "to" used for selecting a specific time frame of ' +
    'time entries to return, same as "period" argument or a specific date in ' +
    'the format "YYYY-MM-DD". The period argument is ignored.'
  )
  .option(
    '--group-by <column>',
    'optional grouping parameter which should be used. Valid values are ' +
    listCommand.groupBy.options.join(', ')
  )
  .option(
    '-l, --limit <limit>',
    'optional number of items to show, default is infinite',
    undefined,
    ((val) => parseInt(val, 10))
  )
  .option(
    '--locked <true|false>',
    'filter entries by their locked status',
    (val => ['true', 'yes', 'ja', 'ok', '1'].indexOf(val.toLowerCase()) > -1),
    null
  )
  .option(
    '--min-duration <minDuration>',
    'filter out entries which have a duration below the given value (client side)',
  )
  .option(
    '--max-duration <maxDuration>',
    'filter out entries which have a duration above the given value (client side)',
  )
  .option(
    '--project-id <id>',
    'project id, can be either a single ID, or multiple comma-separated IDs.'
  )
  .option(
    '--to <period|YYYY-MM-DD>',
    'in combination with "from" used for selecting a specific time frame of ' +
    'time entries to return, same as "period" argument or a specific date in ' +
    'the format "YYYY-MM-DD". The period argument is ignored.'
  )
  .option.apply(program, commandOptions.toArgs(commandOptions.tracking))
  .option(
    '--reversed',
    'reverse the sorting direction',
  )
  .option(
    '-s --search <query>',
    'search within the notes, to filter by multiple criteria connected with OR use comma-separated query values',
    ((val) => {
      if (typeof val === 'string') {
        return val.split(/\s*,\s*/);
      }
      return val;
    })
  )
  .option(
    '--service-id <id>',
    'service id, can be either a single ID, or multiple comma-separated IDs.'
  )
  .option.apply(program, commandOptions.toArgs(
    commandOptions.sort,
    commandOptions.sort.description(listCommand.sort.options),
    listCommand.sort.default
  ))
  .option(
    '--user-id <id>',
    'optional single user id who’s time entries should be returned or ' +
    'multiple values comma-separated. Note that the current user may not ' +
    'have the permission ot read other user’s time entries which will result ' +
    'in an empty response',
    ((val) => {
      if (typeof val === 'string') {
        return val.split(/\s*,\s*/);
      }
      return val;
    })
  )
  .on('--help', function() {
    console.log(`
Examples:

  list all entries from the current month:
    mite list this_month

  list all entries which note contains the given search query:
    mite list this_year --search JIRA-123

  show all entries from two services 123 and 38171
    mite list last_month --service-id 123,38171

  show all users who tracked billable entries ordered by the amount of time
  they have tracked:
    mite list this_year --billable true --columns user,duration --group-by user --sort duration

  export all time-entries from the current month as csv:
    mite list last_week --format csv --columns user,id

  create a markdown report of all customer and their generated profits:
    mite list this_year --format md --group-by customer --sort revenue

  The output of mite list can be forwarded to other commands using xargs. The
  following example will delete all matchin entries:
    mite list this_month --search="query" --columns id --format text | xargs -n1 mite delete

  Get the notes for one specific service, project for the last month to put
  them on a bill or similar
    mite list last_month --project-id 2681601 --service-id 325329 --columns note --format text | sort -u

  Show a seperate report for all users showing the revenues and times per
  service for all users matching a query:
    mite users --search marc --columns id --format text | xargs mite list last_month --group-by service --user-id

`);
  })
  .parse(process.argv);

/**
 * Returns the request options for requesting the time entries or grouped
 * time entries for the given time entries and filtering options.
 *
 * @param {string} period
 * @param {object} program
 * @return {Object<String, any>} time entries or grouped time entries
 */
function getRequestOptions(period, program) {

  // mite’s "periods" strings using underscore like in "this_month", "this_week"
  // but sometimes the user enters a minus instead of underscore, then replace
  // it
  if (typeof period === 'string') {
    period = period.replace(/-/g, '_');
  }

  const data = {
    ...(typeof program.billable === 'boolean' && { billable: program.billable }),
    ...(program.customerId && { customer_id: program.customerId }),
    ...(program.reversed && { direction: 'asc' }),
    ...(program.groupBy && { group_by: program.groupBy }),
    ...(program.limit && { limit: program.limit }),
    ...(typeof program.locked === 'boolean' && { locked: program.locked }),
    ...(program.search && { note: program.search }),
    ...(program.projectId && { project_id: program.projectId }),
    ...(program.serviceId && { service_id: program.serviceId }),
    ...(program.sort && { sort: program.sort }),
    ...(typeof program.tracking === 'boolean' && { tracking: program.tracking }),
    ...(program.userId && { user_id: program.userId}),
  };

  // add optional time period using from & to
  if (program.from && program.to) {
    data.from = program.from;
    data.to = program.to;
  } else if (period) {
    data.at = period;
  }

  return data;
}

/**
 * Callback function to filter time entries which are shown depending on the
 * command line options
 *
 * @param {object} program
 * @param {MiteTimeEntry} entry
 * @return {boolean}
 */
function filterData(program, row) {
  let valid = true;
  if (program.minDuration) {
    const minDuration = formater.durationToMinutes(program.minDuration, 10);
    valid &= row.minutes >= minDuration;
  }
  if (program.maxDuration) {
    const maxDuration = formater.durationToMinutes(program.maxDuration, 10);
    valid &= row.minutes <= maxDuration;
  }
  return valid;
}

function getReport(items, columns, format) {
  const tableData = DataOutput.compileTableData(items, columns);

  // Table footer
  // add table footer if any of the table columns has a reducer
  if (columnOptions.hasReducer(columns)) {
    let footerColumns = DataOutput.getTableFooterColumns(items, columns);
    footerColumns = footerColumns.map(v => v ? chalk.bold(v) : v); // make footer bold
    tableData.push(footerColumns);
  }

  return DataOutput.formatData(tableData, format, columns);
}

function main(period) {
  const mite = miteApi(config.get());

  const requestOpts = getRequestOptions(period, program);
  // "columns" default option is different
  if (!program.columns) {
    // when groupBy is used, make sure that revenue and duration are used
    if (program.groupBy) {
      program.columns = program.groupBy + ',revenue,duration';
    } else {
      program.columns = config.get().listColumns;
    }
  }
  const columns = columnOptions.resolve(program.columns, listCommand.columns.options);

  mite.getTimeEntries(requestOpts, (err, results) => {
    if (err) return handleError(err);

    // decide wheter to output grouped report or single entry report
    let items = results
      .map(data => data[program.groupBy ? 'time_entry_group' : 'time_entry'])
      .filter(v => v)
      .filter(filterData.bind(this, program))
      ;
    const report = getReport(items, columns, program.format);
    console.log(report);
  });
} // main

const period = program.args[0];
main(period);
<|endoftext|>


<commit_before>define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/signal/Pow"], function(Tone){

	"use strict";

	/**
	 *  @class  ADSR envelope generator attaches to an AudioParam or Signal.
	 *
	 *  @constructor
	 *  @extends {Tone}
	 *  @param {Tone.Time|Object} [attack=0.01]	the attack time in seconds
	 *  @param {Tone.Time} [decay=0.1]	the decay time in seconds
	 *  @param {number} [sustain=0.5] 	a percentage (0-1) of the full amplitude
	 *  @param {Tone.Time} [release=1]	the release time in seconds
	 *  @example
	 *  var gainNode = Tone.context.createGain();
	 *  var env = new Tone.Envelope({
	 *  	"attack" : 0.1,
	 *  	"decay" : 0.2,
	 *  	"sustain" : 1,
	 *  	"release" : 0.8,
	 *  });
	 *  env.connect(gainNode.gain);
	 */
	Tone.Envelope = function(){

		//get all of the defaults
		var options = this.optionsObject(arguments, ["attack", "decay", "sustain", "release"], Tone.Envelope.defaults);

		/**
		 *  The attack time
		 *  @type {Tone.Time}
		 */
		this.attack = options.attack;

		/**
		 *  The decay time
		 *  @type {Tone.Time}
		 */
		this.decay = options.decay;

		/**
		 *  the sustain is a value between 0-1
		 *  @type {number}
		 */
		this.sustain = options.sustain;

		/**
		 *  The release time
		 *  @type {Tone.Time}
		 */
		this.release = options.release;

		/**
		 *  the signal
		 *  @type {Tone.Signal}
		 *  @private
		 */
		this._sig = this.output = new Tone.Signal(0);
	};

	Tone.extend(Tone.Envelope);

	/**
	 *  the default parameters
	 *  @static
	 *  @const
	 */
	Tone.Envelope.defaults = {
		"attack" : 0.01,
		"decay" : 0.1,
		"sustain" : 0.5,
		"release" : 1,
	};

	/**
	 *  the envelope time multipler
	 *  @type {number}
	 *  @private
	 */
	Tone.Envelope.prototype._timeMult = 0.25;

	/**
	 *  Trigger the attack/decay portion of the ADSR envelope.
	 *  @param  {Tone.Time} [time=now]
	 *  @param {number} [velocity=1] the velocity of the envelope scales the vales.
	 *                               number between 0-1
	 *  @returns {Tone.Envelope} `this`
	 *  @example
	 *  //trigger the attack 0.5 seconds from now with a velocity of 0.2
	 *  env.triggerAttack("+0.5", 0.2);
	 */
	Tone.Envelope.prototype.triggerAttack = function(time, velocity){
		velocity = this.defaultArg(velocity, 1);
		var attack = this.toSeconds(this.attack);
		var decay = this.toSeconds(this.decay);
		var scaledMax = velocity;
		var sustainVal = this.sustain * scaledMax;
		time = this.toSeconds(time);
		this._sig.cancelScheduledValues(time);
		this._sig.setTargetAtTime(scaledMax, time, attack * this._timeMult);
		this._sig.setTargetAtTime(sustainVal, time + attack, decay * this._timeMult);
		return this;
	};

	/**
	 *  Triggers the release of the envelope.
	 *  @param  {Tone.Time} [time=now]
	 *  @returns {Tone.Envelope} `this`
	 *  @example
	 *  //trigger release immediately
	 *  env.triggerRelease();
	 */
	Tone.Envelope.prototype.triggerRelease = function(time){
		time = this.toSeconds(time);
		this._sig.cancelScheduledValues(time);
		var release = this.toSeconds(this.release);
		this._sig.setTargetAtTime(0, time, release * this._timeMult);
		return this;
	};

	/**
	 *  Trigger the attack and release after a sustain time
	 *  @param {Tone.Time} duration the duration of the note
	 *  @param {Tone.Time} [time=now] the time of the attack
	 *  @param {number} [velocity=1] the velocity of the note
	 *  @returns {Tone.Envelope} `this`
	 *  @example
	 *  //trigger the attack and then the release after 0.6 seconds.
	 *  env.triggerAttackRelease(0.6);
	 */
	Tone.Envelope.prototype.triggerAttackRelease = function(duration, time, velocity) {
		time = this.toSeconds(time);
		this.triggerAttack(time, velocity);
		this.triggerRelease(time + this.toSeconds(duration));
		return this;
	};

	/**
	 *  Borrows the connect method from {@link Tone.Signal}
	 *  @function
	 */
	Tone.Envelope.prototype.connect = Tone.Signal.prototype.connect;

	/**
	 *  disconnect and dispose
	 *  @returns {Tone.Envelope} `this`
	 */
	Tone.Envelope.prototype.dispose = function(){
		Tone.prototype.dispose.call(this);
		this._sig.dispose();
		this._sig = null;
		return this;
	};

	return Tone.Envelope;
});
<commit_msg>Envelope has exponential or linear attack<commit_after>define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/signal/Pow"], function(Tone){

	"use strict";

	/**
	 *  @class  ADSR envelope generator attaches to an AudioParam or Signal.
	 *
	 *  @constructor
	 *  @extends {Tone}
	 *  @param {Tone.Time|Object} [attack=0.01]	the attack time in seconds
	 *  @param {Tone.Time} [decay=0.1]	the decay time in seconds
	 *  @param {number} [sustain=0.5] 	a percentage (0-1) of the full amplitude
	 *  @param {Tone.Time} [release=1]	the release time in seconds
	 *  @example
	 *  var gainNode = Tone.context.createGain();
	 *  var env = new Tone.Envelope({
	 *  	"attack" : 0.1,
	 *  	"decay" : 0.2,
	 *  	"sustain" : 1,
	 *  	"release" : 0.8,
	 *  });
	 *  env.connect(gainNode.gain);
	 */
	Tone.Envelope = function(){

		//get all of the defaults
		var options = this.optionsObject(arguments, ["attack", "decay", "sustain", "release"], Tone.Envelope.defaults);

		/**
		 *  The attack time
		 *  @type {Tone.Time}
		 */
		this.attack = options.attack;

		/**
		 *  The decay time
		 *  @type {Tone.Time}
		 */
		this.decay = options.decay;

		/**
		 *  the sustain is a value between 0-1
		 *  @type {number}
		 */
		this.sustain = options.sustain;

		/**
		 *  The release time
		 *  @type {Tone.Time}
		 */
		this.release = options.release;

		/**
		 *  the next time the envelope is attacked
		 *  @type {number}
		 *  @private
		 */
		this._nextAttack = Infinity;

		/**
		 *  the next time the envelope is decayed
		 *  @type {number}
		 *  @private
		 */
		this._nextDecay = Infinity;

		/**
		 *  the next time the envelope is sustain
		 *  @type {number}
		 *  @private
		 */
		this._nextSustain = Infinity;

		/**
		 *  the next time the envelope is released
		 *  @type {number}
		 *  @private
		 */
		this._nextRelease = Infinity;

		/**
		 *  the next time the envelope is at standby
		 *  @type {number}
		 *  @private
		 */
		this._nextStandby = Infinity;

		/**
		 *  the next time the envelope is at standby
		 *  @type {number}
		 *  @private
		 */
		this._attackType = Tone.Envelope.Type.LINEAR;

		/**
		 *  the last recorded velocity value
		 *  @type {number}
		 *  @private
		 */
		this._peakValue = 1;

		/**
		 *  the minimum output value
		 *  @type {number}
		 *  @private
		 */
		this._minOutput = 0;

		/**
		 *  the signal
		 *  @type {Tone.Signal}
		 *  @private
		 */
		this._sig = this.output = new Tone.Signal(0);

		//set the attackType initially
		this.attackType = options.attackType;
	};

	Tone.extend(Tone.Envelope);

	/**
	 *  the default parameters
	 *  @static
	 *  @const
	 */
	Tone.Envelope.defaults = {
		"attack" : 0.01,
		"decay" : 0.1,
		"sustain" : 0.5,
		"release" : 1,
		"attackType" : "linear"
	};

	/**
	 *  the envelope time multipler
	 *  @type {number}
	 *  @private
	 */
	Tone.Envelope.prototype._timeMult = 0.25;

	/**
	 * The slope of the attack. Either "linear" or "exponential"
	 * @memberOf Tone.Envelope#
	 * @type {number}
	 * @name attackType
	 * @example
	 * env.attackType = "linear";
	 */
	Object.defineProperty(Tone.Envelope.prototype, "attackType", {
		get : function(){
			return this._attackType;
		},
		set : function(type){
			if (type === Tone.Envelope.Type.LINEAR ||
				type === Tone.Envelope.Type.EXPONENTIAL){
				this._attackType = type;
				this._minOutput = this._attackType === Tone.Envelope.Type.LINEAR ? 0 : 0.0001;
			} else {
				throw Error("attackType must be either \"linear\" or \"exponential\". Invalid type: ", type);
			}
		}
	});

	/**
	 *  Get the phase of the envelope at the specified time.
	 *  @param  {number}  time
	 *  @return  {Tone.Envelope.Phase}
	 *  @private
	 */
	Tone.Envelope.prototype._phaseAtTime = function(time){
		if (this._nextRelease > time){
			if (this._nextAttack <= time && this._nextDecay > time){
				return Tone.Envelope.Phase.ATTACK;
			} else if (this._nextDecay <= time && this._nextSustain > time){
				return Tone.Envelope.Phase.DECAY;
			} else if (this._nextSustain <= time && this._nextRelease > time){
				return Tone.Envelope.Phase.SUSTAIN;
			} else {
				return Tone.Envelope.Phase.STANDBY;
			}
		} else if (this._nextRelease < time && this._nextStandby > time){
			return Tone.Envelope.Phase.RELEASE;
		} else {
			return Tone.Envelope.Phase.STANDBY;
		}
	};

	// https://github.com/jsantell/web-audio-automation-timeline
	// MIT License, copyright (c) 2014 Jordan Santell
	Tone.Envelope.prototype._exponentialApproach = function (t0, v0, v1, timeConstant, t) {
		return v1 + (v0 - v1) * Math.exp(-(t - t0) / timeConstant);
	};

	Tone.Envelope.prototype._linearInterpolate = function (t0, v0, t1, v1, t) {
		return v0 + (v1 - v0) * ((t - t0) / (t1 - t0));
	};

	Tone.Envelope.prototype._exponentialInterpolate = function (t0, v0, t1, v1, t) {
		return v0 * Math.pow(v1 / v0, (t - t0) / (t1 - t0));
	};

	/**
	 *  Get the envelopes value at the given time
	 *  @param  {number}  time
	 *  @param  {number}  velocity
	 *  @return  {number}
	 *  @private
	 */
	Tone.Envelope.prototype._valueAtTime = function(time){
		var attack = this.toSeconds(this.attack);
		var decay = this.toSeconds(this.decay);
		var release = this.toSeconds(this.release);
		switch(this._phaseAtTime(time)){
			case Tone.Envelope.Phase.ATTACK:
				if (this._attackType === Tone.Envelope.Type.LINEAR){
					return this._linearInterpolate(this._nextAttack, this._minOutput, this._nextAttack + attack, this._peakValue, time);
				} else {
					return this._exponentialInterpolate(this._nextAttack, this._minOutput, this._nextAttack + attack, this._peakValue, time);
				}
				break;
			case Tone.Envelope.Phase.DECAY:
				return this._exponentialApproach(this._nextDecay, this._peakValue, this.sustain * this._peakValue, decay * this._timeMult, time);
			case Tone.Envelope.Phase.RELEASE:
				return this._exponentialApproach(this._nextRelease, this._peakValue, this._minOutput, release * this._timeMult, time);
			case Tone.Envelope.Phase.SUSTAIN:
				return this.sustain * this._peakValue;
			case Tone.Envelope.Phase.STANDBY:
				return this._minOutput;
		}
	};

	/**
	 *  Trigger the attack/decay portion of the ADSR envelope.
	 *  @param  {Tone.Time} [time=now]
	 *  @param {number} [velocity=1] the velocity of the envelope scales the vales.
	 *                               number between 0-1
	 *  @returns {Tone.Envelope} `this`
	 *  @example
	 *  //trigger the attack 0.5 seconds from now with a velocity of 0.2
	 *  env.triggerAttack("+0.5", 0.2);
	 */
	Tone.Envelope.prototype.triggerAttack = function(time, velocity){
		//to seconds
		time = this.toSeconds(time);
		var attack = this.toSeconds(this.attack);
		var decay = this.toSeconds(this.decay);

		//get the phase and position
		var valueAtTime = this._valueAtTime(time);
		var attackPast = valueAtTime * attack;

		//compute the timing
		this._nextAttack = time - attackPast;
		this._nextDecay = this._nextAttack + attack;
		this._nextSustain = this._nextDecay + decay;
		this._nextRelease = Infinity;

		//get the values
		this._peakValue = this.defaultArg(velocity, 1);
		var scaledMax = this._peakValue;
		var sustainVal = this.sustain * scaledMax;

		//set the curve
		this._sig.cancelScheduledValues(time);
		this._sig.setValueAtTime(valueAtTime, time);
		if (this._attackType === Tone.Envelope.Type.LINEAR){
			this._sig.linearRampToValueAtTime(scaledMax, this._nextDecay);
			// this._sig.setTargetAtTime(scaledMax, this._nextAttack, attack * this._timeMult);
		} else {
			this._sig.exponentialRampToValueAtTime(scaledMax, this._nextDecay);
		}
		this._sig.setTargetAtTime(sustainVal, this._nextDecay, decay * this._timeMult);
		return this;
	};

	/**
	 *  Triggers the release of the envelope.
	 *  @param  {Tone.Time} [time=now]
	 *  @returns {Tone.Envelope} `this`
	 *  @example
	 *  //trigger release immediately
	 *  env.triggerRelease();
	 */
	Tone.Envelope.prototype.triggerRelease = function(time){
		time = this.toSeconds(time);
		var phase = this._phaseAtTime(time);
		var release = this.toSeconds(this.release);

		//computer the value at the start of the next release
		var valueAtTime = this._valueAtTime(time);
		this._peakValue = valueAtTime;

		this._nextRelease = time;
		this._nextStandby = this._nextRelease + release;

		//set the values
		this._sig.cancelScheduledValues(this._nextRelease);

		//if the phase is in the attack still, must reschedule the rest of the attack
		if (phase === Tone.Envelope.Phase.ATTACK){
			this._sig.setCurrentValueNow();
			if (this.attackType === Tone.Envelope.Type.LINEAR){
				this._sig.linearRampToValueAtTime(this._peakValue, this._nextRelease);
			} else {
				this._sig.exponentialRampToValueAtTime(this._peakValue, this._nextRelease);
			}
		} else {
			this._sig.setValueAtTime(this._peakValue, this._nextRelease);
		}
		this._sig.setTargetAtTime(this._minOutput, this._nextRelease, release * this._timeMult);
		return this;
	};

	/**
	 *  Trigger the attack and release after a sustain time
	 *  @param {Tone.Time} duration the duration of the note
	 *  @param {Tone.Time} [time=now] the time of the attack
	 *  @param {number} [velocity=1] the velocity of the note
	 *  @returns {Tone.Envelope} `this`
	 *  @example
	 *  //trigger the attack and then the release after 0.6 seconds.
	 *  env.triggerAttackRelease(0.6);
	 */
	Tone.Envelope.prototype.triggerAttackRelease = function(duration, time, velocity) {
		time = this.toSeconds(time);
		this.triggerAttack(time, velocity);
		this.triggerRelease(time + this.toSeconds(duration));
		return this;
	};

	/**
	 *  Borrows the connect method from {@link Tone.Signal}
	 *  @function
	 */
	Tone.Envelope.prototype.connect = Tone.Signal.prototype.connect;

	/**
	 *  disconnect and dispose
	 *  @returns {Tone.Envelope} `this`
	 */
	Tone.Envelope.prototype.dispose = function(){
		Tone.prototype.dispose.call(this);
		this._sig.dispose();
		this._sig = null;
		return this;
	};

	/**
	 *  The phase of the envelope.
	 *  @enum {string}
	 */
	Tone.Envelope.Phase = {
		ATTACK : "attack",
		DECAY : "decay",
		SUSTAIN : "sustain",
		RELEASE : "release",
		STANDBY : "standby",
 	};

 	/**
	 *  The phase of the envelope.
	 *  @enum {string}
	 */
	Tone.Envelope.Type = {
		LINEAR : "linear",
		EXPONENTIAL : "exponential",
 	};

	return Tone.Envelope;
});
<|endoftext|>


<commit_before>import { Component, ContainerEditor } from 'substance'

class BodyComponent extends Component {

  render($$) {
    let node = this.props.node
    let configurator = this.props.configurator
    let el = $$('div')
      .addClass('sc-body')
      .attr('data-id', this.props.node.id)

     let editor = $$(ContainerEditor, {
        disabled: this.props.disabled,
        node: node,
        commands: configurator.getSurfaceCommandNames(),
        textTypes: configurator.getTextTypes()
      }).ref('body')
	editor.setAttribute('contenteditable', false)
    el.append(editor)
    return el
  }
}

export default BodyComponent
<commit_msg>made body component read-only<commit_after>import { Component, ContainerEditor } from 'substance'

class BodyComponent extends Component {

  render($$) {
    let node = this.props.node
    let configurator = this.props.configurator
    let el = $$('div')
      .addClass('sc-body')
      .attr('data-id', this.props.node.id)

     let editor = $$(ContainerEditor, {
        disabled: this.props.disabled,
        node: node,
        commands: configurator.getSurfaceCommandNames(),
        textTypes: configurator.getTextTypes(),
		editing: "no"
      }).ref('body')
	editor.setAttribute('contenteditable', false)
	el.append(editor)
    return el
  }
}

export default BodyComponent
<|endoftext|>


<commit_before>import $ from 'jquery';

class ModalUI {
  constructor($node, options) {
    this.options = $.extend({}, {
      target: options.container || 'body',
    }, options);

    this.$modal = $node;
    this.$backdrop = $('
');
  }

  show() {
    if (this.options.target === 'body') {
      this.$backdrop.css('position', 'fixed');
      this.$modal.css('position', 'fixed');
    } else {
      this.$backdrop.css('position', 'absolute');
      this.$modal.css('position', 'absolute');
    }

    this.$backdrop.appendTo(this.options.target).show();
    this.$modal.appendTo(this.options.target).addClass('open').show();

    this.$modal.trigger('note.modal.show');
    this.$modal.off('click', '.close').on('click', '.close', this.hide.bind(this));
  }

  hide() {
    this.$modal.removeClass('open').hide();
    this.$backdrop.hide();
    this.$modal.trigger('note.modal.hide');
  }
}

export default ModalUI;
<commit_msg>Add closing lite dialog by pressing esc key<commit_after>import $ from 'jquery';

class ModalUI {
  constructor($node, options) {
    this.options = $.extend({}, {
      target: options.container || 'body',
    }, options);

    this.$modal = $node;
    this.$backdrop = $('
');
  }

  show() {
    if (this.options.target === 'body') {
      this.$backdrop.css('position', 'fixed');
      this.$modal.css('position', 'fixed');
    } else {
      this.$backdrop.css('position', 'absolute');
      this.$modal.css('position', 'absolute');
    }

    this.$backdrop.appendTo(this.options.target).show();
    this.$modal.appendTo(this.options.target).addClass('open').show();

    this.$modal.trigger('note.modal.show');
    this.$modal.off('click', '.close').on('click', '.close', this.hide.bind(this));
    this.$modal.on('keydown', (event) => {
      if (event.which === 27) {
        event.preventDefault();
        this.hide();
      }
    });
  }

  hide() {
    this.$modal.removeClass('open').hide();
    this.$backdrop.hide();
    this.$modal.trigger('note.modal.hide');
    this.$modal.off('keydown');
  }
}

export default ModalUI;
<|endoftext|>


<commit_before>/*
* SoundTouch JS audio processing library
* Copyright (c) Olli Parviainen
* Copyright (c) Ryan Berdeen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

function RateTransposer(createBuffers) {
    AbstractFifoSamplePipe.call(this, createBuffers);
    this._reset();
    this.rate = 1;
}

extend(RateTransposer.prototype, AbstractFifoSamplePipe.prototype);
extend(RateTransposer.prototype, {
    set rate(rate) {
        this._rate = rate;
        // TODO aa filter
    },

    _reset: function () {
        this.slopeCount = 0;
        this.prevSampleL = 0;
        this.prevSampleR = 0;
    },

    clone: function () {
        var result = new RateTransposer();
        result.rate = this._rate;
        return result;
    },

    process: function () {
        // TODO aa filter
        var numFrames = this._inputBuffer.frameCount;
        console.log('ensureAdditionalCapacity', numFrames / this._rate + 1)
        this._outputBuffer.ensureAdditionalCapacity(numFrames / this._rate + 1);
        var numFramesOutput = this._transpose(numFrames);
        this._inputBuffer.receive();
        this._outputBuffer.put(numFramesOutput);
    },

    _transpose: function (numFrames) {
        if (numFrames == 0) {
            // no work
            return 0;
        }

        var src = this._inputBuffer.vector;
        var srcOffset = this._inputBuffer.startIndex;

        var dest = this._outputBuffer.vector;
        var destOffset = this._outputBuffer.endIndex;

        var used = 0;
        var i = 0;

        while(this.slopeCount < 1.0) {
            dest[destOffset + 2 * i] = (1.0 - this.slopeCount) * this.prevSampleL + this.slopeCount * src[srcOffset];
            dest[destOffset + 2 * i + 1] = (1.0 - this.slopeCount) * this.prevSampleR + this.slopeCount * src[srcOffset + 1];
            i++;
            this.slopeCount += this._rate;
        }

        this.slopeCount -= 1.0;

        if (numFrames != 1) {
            out: while (true) {
                while (this.slopeCount > 1.0) {
                    this.slopeCount -= 1.0;
                    used++;
                    if (used >= numFrames - 1) {
                        break out;
                    }
                }

                var srcIndex = srcOffset + 2 * used;
                dest[destOffset + 2 * i] = (1.0 - this.slopeCount) * src[srcIndex] + this.slopeCount * src[srcIndex + 2];
                dest[destOffset + 2 * i + 1] = (1.0 - this.slopeCount) * src[srcIndex + 1] + this.slopeCount * src[srcIndex + 3];

                i++;
                this.slopeCount += this._rate;
            }
        }

        this.prevSampleL = src[srcOffset + 2 * numFrames - 2];
        this.prevSampleR = src[srcOffset + 2 * numFrames - 1];

        return i;
    }
});<commit_msg>remove logging<commit_after>/*
* SoundTouch JS audio processing library
* Copyright (c) Olli Parviainen
* Copyright (c) Ryan Berdeen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

function RateTransposer(createBuffers) {
    AbstractFifoSamplePipe.call(this, createBuffers);
    this._reset();
    this.rate = 1;
}

extend(RateTransposer.prototype, AbstractFifoSamplePipe.prototype);
extend(RateTransposer.prototype, {
    set rate(rate) {
        this._rate = rate;
        // TODO aa filter
    },

    _reset: function () {
        this.slopeCount = 0;
        this.prevSampleL = 0;
        this.prevSampleR = 0;
    },

    clone: function () {
        var result = new RateTransposer();
        result.rate = this._rate;
        return result;
    },

    process: function () {
        // TODO aa filter
        var numFrames = this._inputBuffer.frameCount;
        this._outputBuffer.ensureAdditionalCapacity(numFrames / this._rate + 1);
        var numFramesOutput = this._transpose(numFrames);
        this._inputBuffer.receive();
        this._outputBuffer.put(numFramesOutput);
    },

    _transpose: function (numFrames) {
        if (numFrames == 0) {
            // no work
            return 0;
        }

        var src = this._inputBuffer.vector;
        var srcOffset = this._inputBuffer.startIndex;

        var dest = this._outputBuffer.vector;
        var destOffset = this._outputBuffer.endIndex;

        var used = 0;
        var i = 0;

        while(this.slopeCount < 1.0) {
            dest[destOffset + 2 * i] = (1.0 - this.slopeCount) * this.prevSampleL + this.slopeCount * src[srcOffset];
            dest[destOffset + 2 * i + 1] = (1.0 - this.slopeCount) * this.prevSampleR + this.slopeCount * src[srcOffset + 1];
            i++;
            this.slopeCount += this._rate;
        }

        this.slopeCount -= 1.0;

        if (numFrames != 1) {
            out: while (true) {
                while (this.slopeCount > 1.0) {
                    this.slopeCount -= 1.0;
                    used++;
                    if (used >= numFrames - 1) {
                        break out;
                    }
                }

                var srcIndex = srcOffset + 2 * used;
                dest[destOffset + 2 * i] = (1.0 - this.slopeCount) * src[srcIndex] + this.slopeCount * src[srcIndex + 2];
                dest[destOffset + 2 * i + 1] = (1.0 - this.slopeCount) * src[srcIndex + 1] + this.slopeCount * src[srcIndex + 3];

                i++;
                this.slopeCount += this._rate;
            }
        }

        this.prevSampleL = src[srcOffset + 2 * numFrames - 2];
        this.prevSampleR = src[srcOffset + 2 * numFrames - 1];

        return i;
    }
});<|endoftext|>


<commit_before>define([
  'jquery',
  './utils'
], function ($, Utils) {
  function Results ($element, options, dataAdapter) {
    this.$element = $element;
    this.data = dataAdapter;
    this.options = options;

    Results.__super__.constructor.call(this);
  }

  Utils.Extend(Results, Utils.Observable);

  Results.prototype.render = function () {
    var $results = $(
      '

'
    );

    if (this.options.get('multiple')) {
      $results.attr('aria-multiselectable', 'true');
    }

    this.$results = $results;

    return $results;
  };

  Results.prototype.clear = function () {
    this.$results.empty();
  };

  Results.prototype.displayMessage = function (params) {
    var escapeMarkup = this.options.get('escapeMarkup');

    this.clear();
    this.hideLoading();

    var $message = $(
      '

'
    );

    var message = this.options.get('translations').get(params.message);

    $message.append(
      escapeMarkup(
        message(params.args)
      )
    );

    $message[0].className += ' select2-results__message';

    this.$results.append($message);
  };

  Results.prototype.hideMessages = function () {
    this.$results.find('.select2-results__message').remove();
  };

  Results.prototype.append = function (data) {
    this.hideLoading();

    var $options = [];

    if (data.results == null || data.results.length === 0) {
      if (this.$results.children().length === 0) {
        this.trigger('results:message', {
          message: 'noResults'
        });
      }

      return;
    }

    data.results = this.sort(data.results);

    for (var d = 0; d < data.results.length; d++) {
      var item = data.results[d];

      var $option = this.option(item);

      $options.push($option);
    }

    this.$results.append($options);
  };

  Results.prototype.position = function ($results, $dropdown) {
    var $resultsContainer = $dropdown.find('.select2-results');
    $resultsContainer.append($results);
  };

  Results.prototype.sort = function (data) {
    var sorter = this.options.get('sorter');

    return sorter(data);
  };

  Results.prototype.setClasses = function () {
    var self = this;

    this.data.current(function (selected) {
      var selectedIds = $.map(selected, function (s) {
        return s.id.toString();
      });

      var $options = self.$results
        .find('.select2-results__option[aria-selected]');

      $options.each(function () {
        var $option = $(this);

        var item = $.data(this, 'data');

        // id needs to be converted to a string when comparing
        var id = '' + item.id;

        if ((item.element != null && item.element.selected) ||
            (item.element == null && $.inArray(id, selectedIds) > -1)) {
          $option.attr('aria-selected', 'true');
        } else {
          $option.attr('aria-selected', 'false');
        }
      });

      var $selected = $options.filter('[aria-selected=true]');

      // Check if there are any selected options
      if ($selected.length > 0) {
        // If there are selected options, highlight the first
        $selected.first().trigger('mouseenter');
      } else {
        // If there are no selected options, highlight the first option
        // in the dropdown
        $options.first().trigger('mouseenter');
      }
    });
  };

  Results.prototype.showLoading = function (params) {
    this.hideLoading();

    var loadingMore = this.options.get('translations').get('searching');

    var loading = {
      disabled: true,
      loading: true,
      text: loadingMore(params)
    };
    var $loading = this.option(loading);
    $loading.className += ' loading-results';

    this.$results.prepend($loading);
  };

  Results.prototype.hideLoading = function () {
    this.$results.find('.loading-results').remove();
  };

  Results.prototype.option = function (data) {
    var option = document.createElement('li');
    option.className = 'select2-results__option';

    var attrs = {
      'role': 'treeitem',
      'aria-selected': 'false'
    };

    if (data.disabled) {
      delete attrs['aria-selected'];
      attrs['aria-disabled'] = 'true';
    }

    if (data.id == null) {
      delete attrs['aria-selected'];
    }

    if (data._resultId != null) {
      option.id = data._resultId;
    }

    if (data.title) {
      option.title = data.title;
    }

    if (data.children) {
      attrs.role = 'group';
      attrs['aria-label'] = data.text;
      delete attrs['aria-selected'];
    }

    for (var attr in attrs) {
      var val = attrs[attr];

      option.setAttribute(attr, val);
    }

    if (data.children) {
      var $option = $(option);

      var label = document.createElement('strong');
      label.className = 'select2-results__group';

      var $label = $(label);
      this.template(data, label);

      var $children = [];

      for (var c = 0; c < data.children.length; c++) {
        var child = data.children[c];

        var $child = this.option(child);

        $children.push($child);
      }

      var $childrenContainer = $('

', {
        'class': 'select2-results__options select2-results__options--nested'
      });

      $childrenContainer.append($children);

      $option.append(label);
      $option.append($childrenContainer);
    } else {
      this.template(data, option);
    }

    $.data(option, 'data', data);

    return option;
  };

  Results.prototype.bind = function (container, $container) {
    var self = this;

    var id = container.id + '-results';

    this.$results.attr('id', id);

    container.on('results:all', function (params) {
      self.clear();
      self.append(params.data);

      if (container.isOpen()) {
        self.setClasses();
      }
    });

    container.on('results:append', function (params) {
      self.append(params.data);

      if (container.isOpen()) {
        self.setClasses();
      }
    });

    container.on('query', function (params) {
      self.hideMessages();
      self.showLoading(params);
    });

    container.on('select', function () {
      if (!container.isOpen()) {
        return;
      }

      self.setClasses();
    });

    container.on('unselect', function () {
      if (!container.isOpen()) {
        return;
      }

      self.setClasses();
    });

    container.on('open', function () {
      // When the dropdown is open, aria-expended="true"
      self.$results.attr('aria-expanded', 'true');
      self.$results.attr('aria-hidden', 'false');

      self.setClasses();
      self.ensureHighlightVisible();
    });

    container.on('close', function () {
      // When the dropdown is closed, aria-expended="false"
      self.$results.attr('aria-expanded', 'false');
      self.$results.attr('aria-hidden', 'true');
      self.$results.removeAttr('aria-activedescendant');
    });

    container.on('results:toggle', function () {
      var $highlighted = self.getHighlightedResults();

      if ($highlighted.length === 0) {
        return;
      }

      $highlighted.trigger('mouseup');
    });

    container.on('results:select', function () {
      var $highlighted = self.getHighlightedResults();

      if ($highlighted.length === 0) {
        return;
      }

      var data = $highlighted.data('data');

      if ($highlighted.attr('aria-selected') == 'true') {
        self.trigger('close', {});
      } else {
        self.trigger('select', {
          data: data
        });
      }
    });

    container.on('results:previous', function () {
      var $highlighted = self.getHighlightedResults();

      var $options = self.$results.find('[aria-selected]');

      var currentIndex = $options.index($highlighted);

      // If we are already at te top, don't move further
      if (currentIndex === 0) {
        return;
      }

      var nextIndex = currentIndex - 1;

      // If none are highlighted, highlight the first
      if ($highlighted.length === 0) {
        nextIndex = 0;
      }

      var $next = $options.eq(nextIndex);

      $next.trigger('mouseenter');

      var currentOffset = self.$results.offset().top;
      var nextTop = $next.offset().top;
      var nextOffset = self.$results.scrollTop() + (nextTop - currentOffset);

      if (nextIndex === 0) {
        self.$results.scrollTop(0);
      } else if (nextTop - currentOffset < 0) {
        self.$results.scrollTop(nextOffset);
      }
    });

    container.on('results:next', function () {
      var $highlighted = self.getHighlightedResults();

      var $options = self.$results.find('[aria-selected]');

      var currentIndex = $options.index($highlighted);

      var nextIndex = currentIndex + 1;

      // If we are at the last option, stay there
      if (nextIndex >= $options.length) {
        return;
      }

      var $next = $options.eq(nextIndex);

      $next.trigger('mouseenter');

      var currentOffset = self.$results.offset().top +
        self.$results.outerHeight(false);
      var nextBottom = $next.offset().top + $next.outerHeight(false);
      var nextOffset = self.$results.scrollTop() + nextBottom - currentOffset;

      if (nextIndex === 0) {
        self.$results.scrollTop(0);
      } else if (nextBottom > currentOffset) {
        self.$results.scrollTop(nextOffset);
      }
    });

    container.on('results:focus', function (params) {
      params.element.addClass('select2-results__option--highlighted');
    });

    container.on('results:message', function (params) {
      self.displayMessage(params);
    });

    if ($.fn.mousewheel) {
      this.$results.on('mousewheel', function (e) {
        var top = self.$results.scrollTop();

        var bottom = (
          self.$results.get(0).scrollHeight -
          self.$results.scrollTop() +
          e.deltaY
        );

        var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0;
        var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height();

        if (isAtTop) {
          self.$results.scrollTop(0);

          e.preventDefault();
          e.stopPropagation();
        } else if (isAtBottom) {
          self.$results.scrollTop(
            self.$results.get(0).scrollHeight - self.$results.height()
          );

          e.preventDefault();
          e.stopPropagation();
        }
      });
    }

    this.$results.on('mouseup', '.select2-results__option[aria-selected]',
      function (evt) {
      var $this = $(this);

      var data = $this.data('data');

      if ($this.attr('aria-selected') === 'true') {
        if (self.options.get('multiple')) {
          self.trigger('unselect', {
            originalEvent: evt,
            data: data
          });
        } else {
          self.trigger('close', {});
        }

        return;
      }

      self.trigger('select', {
        originalEvent: evt,
        data: data
      });
    });

    this.$results.on('mouseenter', '.select2-results__option[aria-selected]',
      function (evt) {
      var data = $(this).data('data');

      self.getHighlightedResults()
          .removeClass('select2-results__option--highlighted');

      self.trigger('results:focus', {
        data: data,
        element: $(this)
      });
    });
  };

  Results.prototype.getHighlightedResults = function () {
    var $highlighted = this.$results
    .find('.select2-results__option--highlighted');

    return $highlighted;
  };

  Results.prototype.destroy = function () {
    this.$results.remove();
  };

  Results.prototype.ensureHighlightVisible = function () {
    var $highlighted = this.getHighlightedResults();

    if ($highlighted.length === 0) {
      return;
    }

    var $options = this.$results.find('[aria-selected]');

    var currentIndex = $options.index($highlighted);

    var currentOffset = this.$results.offset().top;
    var nextTop = $highlighted.offset().top;
    var nextOffset = this.$results.scrollTop() + (nextTop - currentOffset);

    var offsetDelta = nextTop - currentOffset;
    nextOffset -= $highlighted.outerHeight(false) * 2;

    if (currentIndex <= 2) {
      this.$results.scrollTop(0);
    } else if (offsetDelta > this.$results.outerHeight() || offsetDelta < 0) {
      this.$results.scrollTop(nextOffset);
    }
  };

  Results.prototype.template = function (result, container) {
    var template = this.options.get('templateResult');
    var escapeMarkup = this.options.get('escapeMarkup');

    var content = template(result);

    if (content == null) {
      container.style.display = 'none';
    } else if (typeof content === 'string') {
      container.innerHTML = escapeMarkup(content);
    } else {
      $(container).append(content);
    }
  };

  return Results;
});
<commit_msg>Add container to templateResult<commit_after>define([
  'jquery',
  './utils'
], function ($, Utils) {
  function Results ($element, options, dataAdapter) {
    this.$element = $element;
    this.data = dataAdapter;
    this.options = options;

    Results.__super__.constructor.call(this);
  }

  Utils.Extend(Results, Utils.Observable);

  Results.prototype.render = function () {
    var $results = $(
      '

'
    );

    if (this.options.get('multiple')) {
      $results.attr('aria-multiselectable', 'true');
    }

    this.$results = $results;

    return $results;
  };

  Results.prototype.clear = function () {
    this.$results.empty();
  };

  Results.prototype.displayMessage = function (params) {
    var escapeMarkup = this.options.get('escapeMarkup');

    this.clear();
    this.hideLoading();

    var $message = $(
      '

'
    );

    var message = this.options.get('translations').get(params.message);

    $message.append(
      escapeMarkup(
        message(params.args)
      )
    );

    $message[0].className += ' select2-results__message';

    this.$results.append($message);
  };

  Results.prototype.hideMessages = function () {
    this.$results.find('.select2-results__message').remove();
  };

  Results.prototype.append = function (data) {
    this.hideLoading();

    var $options = [];

    if (data.results == null || data.results.length === 0) {
      if (this.$results.children().length === 0) {
        this.trigger('results:message', {
          message: 'noResults'
        });
      }

      return;
    }

    data.results = this.sort(data.results);

    for (var d = 0; d < data.results.length; d++) {
      var item = data.results[d];

      var $option = this.option(item);

      $options.push($option);
    }

    this.$results.append($options);
  };

  Results.prototype.position = function ($results, $dropdown) {
    var $resultsContainer = $dropdown.find('.select2-results');
    $resultsContainer.append($results);
  };

  Results.prototype.sort = function (data) {
    var sorter = this.options.get('sorter');

    return sorter(data);
  };

  Results.prototype.setClasses = function () {
    var self = this;

    this.data.current(function (selected) {
      var selectedIds = $.map(selected, function (s) {
        return s.id.toString();
      });

      var $options = self.$results
        .find('.select2-results__option[aria-selected]');

      $options.each(function () {
        var $option = $(this);

        var item = $.data(this, 'data');

        // id needs to be converted to a string when comparing
        var id = '' + item.id;

        if ((item.element != null && item.element.selected) ||
            (item.element == null && $.inArray(id, selectedIds) > -1)) {
          $option.attr('aria-selected', 'true');
        } else {
          $option.attr('aria-selected', 'false');
        }
      });

      var $selected = $options.filter('[aria-selected=true]');

      // Check if there are any selected options
      if ($selected.length > 0) {
        // If there are selected options, highlight the first
        $selected.first().trigger('mouseenter');
      } else {
        // If there are no selected options, highlight the first option
        // in the dropdown
        $options.first().trigger('mouseenter');
      }
    });
  };

  Results.prototype.showLoading = function (params) {
    this.hideLoading();

    var loadingMore = this.options.get('translations').get('searching');

    var loading = {
      disabled: true,
      loading: true,
      text: loadingMore(params)
    };
    var $loading = this.option(loading);
    $loading.className += ' loading-results';

    this.$results.prepend($loading);
  };

  Results.prototype.hideLoading = function () {
    this.$results.find('.loading-results').remove();
  };

  Results.prototype.option = function (data) {
    var option = document.createElement('li');
    option.className = 'select2-results__option';

    var attrs = {
      'role': 'treeitem',
      'aria-selected': 'false'
    };

    if (data.disabled) {
      delete attrs['aria-selected'];
      attrs['aria-disabled'] = 'true';
    }

    if (data.id == null) {
      delete attrs['aria-selected'];
    }

    if (data._resultId != null) {
      option.id = data._resultId;
    }

    if (data.title) {
      option.title = data.title;
    }

    if (data.children) {
      attrs.role = 'group';
      attrs['aria-label'] = data.text;
      delete attrs['aria-selected'];
    }

    for (var attr in attrs) {
      var val = attrs[attr];

      option.setAttribute(attr, val);
    }

    if (data.children) {
      var $option = $(option);

      var label = document.createElement('strong');
      label.className = 'select2-results__group';

      var $label = $(label);
      this.template(data, label);

      var $children = [];

      for (var c = 0; c < data.children.length; c++) {
        var child = data.children[c];

        var $child = this.option(child);

        $children.push($child);
      }

      var $childrenContainer = $('

', {
        'class': 'select2-results__options select2-results__options--nested'
      });

      $childrenContainer.append($children);

      $option.append(label);
      $option.append($childrenContainer);
    } else {
      this.template(data, option);
    }

    $.data(option, 'data', data);

    return option;
  };

  Results.prototype.bind = function (container, $container) {
    var self = this;

    var id = container.id + '-results';

    this.$results.attr('id', id);

    container.on('results:all', function (params) {
      self.clear();
      self.append(params.data);

      if (container.isOpen()) {
        self.setClasses();
      }
    });

    container.on('results:append', function (params) {
      self.append(params.data);

      if (container.isOpen()) {
        self.setClasses();
      }
    });

    container.on('query', function (params) {
      self.hideMessages();
      self.showLoading(params);
    });

    container.on('select', function () {
      if (!container.isOpen()) {
        return;
      }

      self.setClasses();
    });

    container.on('unselect', function () {
      if (!container.isOpen()) {
        return;
      }

      self.setClasses();
    });

    container.on('open', function () {
      // When the dropdown is open, aria-expended="true"
      self.$results.attr('aria-expanded', 'true');
      self.$results.attr('aria-hidden', 'false');

      self.setClasses();
      self.ensureHighlightVisible();
    });

    container.on('close', function () {
      // When the dropdown is closed, aria-expended="false"
      self.$results.attr('aria-expanded', 'false');
      self.$results.attr('aria-hidden', 'true');
      self.$results.removeAttr('aria-activedescendant');
    });

    container.on('results:toggle', function () {
      var $highlighted = self.getHighlightedResults();

      if ($highlighted.length === 0) {
        return;
      }

      $highlighted.trigger('mouseup');
    });

    container.on('results:select', function () {
      var $highlighted = self.getHighlightedResults();

      if ($highlighted.length === 0) {
        return;
      }

      var data = $highlighted.data('data');

      if ($highlighted.attr('aria-selected') == 'true') {
        self.trigger('close', {});
      } else {
        self.trigger('select', {
          data: data
        });
      }
    });

    container.on('results:previous', function () {
      var $highlighted = self.getHighlightedResults();

      var $options = self.$results.find('[aria-selected]');

      var currentIndex = $options.index($highlighted);

      // If we are already at te top, don't move further
      if (currentIndex === 0) {
        return;
      }

      var nextIndex = currentIndex - 1;

      // If none are highlighted, highlight the first
      if ($highlighted.length === 0) {
        nextIndex = 0;
      }

      var $next = $options.eq(nextIndex);

      $next.trigger('mouseenter');

      var currentOffset = self.$results.offset().top;
      var nextTop = $next.offset().top;
      var nextOffset = self.$results.scrollTop() + (nextTop - currentOffset);

      if (nextIndex === 0) {
        self.$results.scrollTop(0);
      } else if (nextTop - currentOffset < 0) {
        self.$results.scrollTop(nextOffset);
      }
    });

    container.on('results:next', function () {
      var $highlighted = self.getHighlightedResults();

      var $options = self.$results.find('[aria-selected]');

      var currentIndex = $options.index($highlighted);

      var nextIndex = currentIndex + 1;

      // If we are at the last option, stay there
      if (nextIndex >= $options.length) {
        return;
      }

      var $next = $options.eq(nextIndex);

      $next.trigger('mouseenter');

      var currentOffset = self.$results.offset().top +
        self.$results.outerHeight(false);
      var nextBottom = $next.offset().top + $next.outerHeight(false);
      var nextOffset = self.$results.scrollTop() + nextBottom - currentOffset;

      if (nextIndex === 0) {
        self.$results.scrollTop(0);
      } else if (nextBottom > currentOffset) {
        self.$results.scrollTop(nextOffset);
      }
    });

    container.on('results:focus', function (params) {
      params.element.addClass('select2-results__option--highlighted');
    });

    container.on('results:message', function (params) {
      self.displayMessage(params);
    });

    if ($.fn.mousewheel) {
      this.$results.on('mousewheel', function (e) {
        var top = self.$results.scrollTop();

        var bottom = (
          self.$results.get(0).scrollHeight -
          self.$results.scrollTop() +
          e.deltaY
        );

        var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0;
        var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height();

        if (isAtTop) {
          self.$results.scrollTop(0);

          e.preventDefault();
          e.stopPropagation();
        } else if (isAtBottom) {
          self.$results.scrollTop(
            self.$results.get(0).scrollHeight - self.$results.height()
          );

          e.preventDefault();
          e.stopPropagation();
        }
      });
    }

    this.$results.on('mouseup', '.select2-results__option[aria-selected]',
      function (evt) {
      var $this = $(this);

      var data = $this.data('data');

      if ($this.attr('aria-selected') === 'true') {
        if (self.options.get('multiple')) {
          self.trigger('unselect', {
            originalEvent: evt,
            data: data
          });
        } else {
          self.trigger('close', {});
        }

        return;
      }

      self.trigger('select', {
        originalEvent: evt,
        data: data
      });
    });

    this.$results.on('mouseenter', '.select2-results__option[aria-selected]',
      function (evt) {
      var data = $(this).data('data');

      self.getHighlightedResults()
          .removeClass('select2-results__option--highlighted');

      self.trigger('results:focus', {
        data: data,
        element: $(this)
      });
    });
  };

  Results.prototype.getHighlightedResults = function () {
    var $highlighted = this.$results
    .find('.select2-results__option--highlighted');

    return $highlighted;
  };

  Results.prototype.destroy = function () {
    this.$results.remove();
  };

  Results.prototype.ensureHighlightVisible = function () {
    var $highlighted = this.getHighlightedResults();

    if ($highlighted.length === 0) {
      return;
    }

    var $options = this.$results.find('[aria-selected]');

    var currentIndex = $options.index($highlighted);

    var currentOffset = this.$results.offset().top;
    var nextTop = $highlighted.offset().top;
    var nextOffset = this.$results.scrollTop() + (nextTop - currentOffset);

    var offsetDelta = nextTop - currentOffset;
    nextOffset -= $highlighted.outerHeight(false) * 2;

    if (currentIndex <= 2) {
      this.$results.scrollTop(0);
    } else if (offsetDelta > this.$results.outerHeight() || offsetDelta < 0) {
      this.$results.scrollTop(nextOffset);
    }
  };

  Results.prototype.template = function (result, container) {
    var template = this.options.get('templateResult');
    var escapeMarkup = this.options.get('escapeMarkup');

    var content = template(result, container);

    if (content == null) {
      container.style.display = 'none';
    } else if (typeof content === 'string') {
      container.innerHTML = escapeMarkup(content);
    } else {
      $(container).append(content);
    }
  };

  return Results;
});
<|endoftext|>


<commit_before>(function() {
    window.onload = function() {
        // clear the description
        document.querySelector('.description').innerHTML = '';

        var words = 'Building libraries should be the main priority. Once you do that, it should be about sharing them with the world. 🌎 🎉';
        var letters = words.split('');
        for(var i = 0; i < letters.length; i++) {
            setTimeout(function(c) {
                return function() { document.querySelector('.description').innerHTML = document.querySelector('.description').innerHTML + c; }
            }(letters[i]), 50 * i);
        };

        var buttons = document.querySelectorAll('.run');
        [].forEach.call(buttons, function(button) {
          button.click();
        });
    }
}())
<commit_msg>fixes linting error<commit_after>(function() {
    window.onload = function() {
        // clear the description
        document.querySelector('.description').innerHTML = '';

        var words = 'Building libraries should be the main priority. Once you do that, it should be about sharing them with the world. 🌎 🎉';
        var letters = words.split('');
        for(var i = 0; i < letters.length; i++) {
            setTimeout(function(c) {
                return function() { document.querySelector('.description').innerHTML = document.querySelector('.description').innerHTML + c; }
            }(letters[i]), 50 * i);
        }

        var buttons = document.querySelectorAll('.run');
        [].forEach.call(buttons, function(button) {
          button.click();
        });
    }
}())
<|endoftext|>


<commit_before><commit_msg>Update project underscore.string to v1.1.6<commit_after><|endoftext|>


<commit_before>import Easings from './easing';

let SCROLLABLE_CONTAINER = getScrollableContainer();

/**
 * Returns the correct DOM element to be used for scrolling the
 * page, due to Firefox not scrolling on document.body.
 * @return {Element} Scrollable Element.
 */
function getScrollableContainer() {
  if (SCROLLABLE_CONTAINER) {
    return SCROLLABLE_CONTAINER;
  }

  const documentElement = window.document.documentElement;
  let scrollableContainer;

  documentElement.scrollTop = 1;

  if (documentElement.scrollTop === 1) {
    documentElement.scrollTop = 0;
    scrollableContainer = documentElement;
  } else {
    scrollableContainer = document.body;
  }

  SCROLLABLE_CONTAINER = scrollableContainer;

  return scrollableContainer;
}

/**
 * Smoothly scrolls to a given Y position using Easing.Swing. It'll run a
 * callback upon finishing.
 * @param {number} y Offset of the page to scroll to.
 * @param {number} duration Duration of the animation. 500ms by default.
 * @param {function} cb Callback function to call upon completion.
 */
function scrollTo(y, duration = 500, cb = () => {}) {
  const scrollableContainer = getScrollableContainer();
  const delta = y - scrollableContainer.scrollTop;
  const startLocation = scrollableContainer.scrollTop;
  const increment = 16;

  if (!duration) {
    scrollableContainer.scrollTop = y;
    cb();
    return;
  }

  const animateScroll = elapsedTime => {
    elapsedTime += increment;
    const percent = Math.min(1, elapsedTime / duration);
    const easingP = Easings.swing(
      percent,
      elapsedTime * percent,
      y,
      delta,
      duration);

    scrollableContainer.scrollTop = Math.floor(startLocation +
        (easingP * delta));

    if (elapsedTime < duration) {
      setTimeout(() => animateScroll(elapsedTime), increment);
    } else {
      cb();
    }
  };

  animateScroll(0);
}

export default { getScrollableContainer, scrollTo };
<commit_msg>Scrollable container is now defined<commit_after>import Easings from './easing';

let SCROLLABLE_CONTAINER = document.getElementById('webslides');

/**
 * Smoothly scrolls to a given Y position using Easing.Swing. It'll run a
 * callback upon finishing.
 * @param {number} y Offset of the page to scroll to.
 * @param {number} duration Duration of the animation. 500ms by default.
 * @param {function} cb Callback function to call upon completion.
 */
export default function scrollTo(y, duration = 500, cb = () => {}) {
  const delta = y - SCROLLABLE_CONTAINER.scrollTop;
  const startLocation = SCROLLABLE_CONTAINER.scrollTop;
  const increment = 16;

  if (!duration) {
    SCROLLABLE_CONTAINER.scrollTop = y;
    cb();
    return;
  }

  const animateScroll = elapsedTime => {
    elapsedTime += increment;
    const percent = Math.min(1, elapsedTime / duration);
    const easingP = Easings.swing(
      percent,
      elapsedTime * percent,
      y,
      delta,
      duration);

    SCROLLABLE_CONTAINER.scrollTop = Math.floor(startLocation +
        (easingP * delta));

    if (elapsedTime < duration) {
      setTimeout(() => animateScroll(elapsedTime), increment);
    } else {
      cb();
    }
  };

  animateScroll(0);
}
<|endoftext|>