<commit_before>const _ = require('lodash')
const Factory = require('../factory')

module.exports = function(context) {
  //
  // Select all of the rooms
  //
  return context.jabber.fetch(
    'select * from dbo.tc_users'
  )
    //
    // The convert them to channels and
    // write them to the datafile
    //
    .then(function(results) {
      console.log(`users: processing ${results.recordset.length} users`)
      //
      // Convenience references
      //
      var values = context.values
      var define = context.config.define

      //
      // Map of userss
      //
      var users = {}

      //
      // Iterate over the record set and
      // create a channel for each room
      //
      results.recordset.forEach(function(record) {
        //
        // Return a reference to the user in the user map
        // or add one if it doesn't yet exist
        //
        var user = users[record.real_jid] = _.get(users, record.real_jid, {
          username: toUsername(record.real_jid),
          email: toEmail(record.real_jid),
          auth_service: define.user.auth_service,
          auth_data: define.user.auth_data,
          teams: [{
            name: values.team.name,
            channels: []
          }]
        })
        //
        // Look up the channel and add it to the
        // user object if it exists
        //
        var channel = values.channels[record.room_jid]
        if(channel) {
          user.teams[0].channels.push({
            name: channel.name
          })
        }
        //
        // Write the channel data to the
        // output
        //
        console.log(`users: ... writing ${user.username}`)
        context.output.write(
          Factory.user(user)
        )
      })
      //
      // Add the users map to the context
      //
      context.values.users = users
      //
      // Return the context
      //
      return context
    })
}

//
// Parse the email
//
const toEmail = function (jid='') {
  return jid.split('/')[0]
}
//
// Parse the username
//
const toUsername = function (jid='') {
  return jid.split('@')[0] // jid.substr(0, jid.indexOf('@'))
}
// //
// // Parse the first name
// //
// const toFirstName = function (username='') {
//   return _.capitalize(username.substr(0, username.indexOf('.')))
// }
// //
// // Parse the last name
// //
// const toLastName = function (username='') {
//   return _.capitalize(username.substr(username.indexOf('.') + 1)).replace(/[0-9]/g, '')
// }
<commit_msg>Fix comment<commit_after>const _ = require('lodash')
const Factory = require('../factory')

module.exports = function(context) {
  //
  // Select all of the rooms
  //
  return context.jabber.fetch(
    'select * from dbo.tc_users'
  )
    //
    // The convert them to channels and
    // write them to the datafile
    //
    .then(function(results) {
      console.log(`users: processing ${results.recordset.length} users`)
      //
      // Convenience references
      //
      var values = context.values
      var define = context.config.define

      //
      // Map of userss
      //
      var users = {}

      //
      // Iterate over the record set and
      // create a channel for each room
      //
      results.recordset.forEach(function(record) {
        //
        // Return a reference to the user in the user map
        // or add one if it doesn't yet exist
        //
        var user = users[record.real_jid] = _.get(users, record.real_jid, {
          username: toUsername(record.real_jid),
          email: toEmail(record.real_jid),
          auth_service: define.user.auth_service,
          auth_data: define.user.auth_data,
          teams: [{
            name: values.team.name,
            channels: []
          }]
        })
        //
        // Look up the channel and add it to the
        // user object if it exists
        //
        var channel = values.channels[record.room_jid]
        if(channel) {
          user.teams[0].channels.push({
            name: channel.name
          })
        }
        //
        // Write the user data to the
        // output
        //
        console.log(`users: ... writing ${user.username}`)
        context.output.write(
          Factory.user(user)
        )
      })
      //
      // Add the users map to the context
      //
      context.values.users = users
      //
      // Return the context
      //
      return context
    })
}

//
// Parse the email
//
const toEmail = function (jid='') {
  return jid.split('/')[0]
}
//
// Parse the username
//
const toUsername = function (jid='') {
  return jid.split('@')[0] // jid.substr(0, jid.indexOf('@'))
}
// //
// // Parse the first name
// //
// const toFirstName = function (username='') {
//   return _.capitalize(username.substr(0, username.indexOf('.')))
// }
// //
// // Parse the last name
// //
// const toLastName = function (username='') {
//   return _.capitalize(username.substr(username.indexOf('.') + 1)).replace(/[0-9]/g, '')
// }
<|endoftext|>


<commit_before>/**
 * class Ruler
 *
 * Helper class, used by [[MarkdownIt#core]], [[MarkdownIt#block]] and
 * [[MarkdownIt#inline]] to manage sequences of functions (rules):
 *
 * - keep rules in defined order
 * - assign the name to each rule
 * - enable/disable rules
 * - add/replace rules
 * - allow assign rules to additional named chains (in the same)
 * - cacheing lists of active rules
 *
 * You will not need use this class directly until write plugins. For simple
 * rules control use [[MarkdownIt.disable]], [[MarkdownIt.enable]] and
 * [[MarkdownIt.use]].
 **/
'use strict';

/**
 * new Ruler()
 **/
function Ruler() {
  // List of added rules. Each element is:
  //
  // {
  //   name: XXX,
  //   enabled: Boolean,
  //   fn: Function(),
  //   alt: [ name2, name3 ]
  // }
  //
  this.__rules__ = [];

  // Cached rule chains.
  //
  // First level - chain name, '' for default.
  // Second level - diginal anchor for fast filtering by charcodes.
  //
  this.__cache__ = null;
}

////////////////////////////////////////////////////////////////////////////////
// Helper methods, should not be used directly

// Find rule index by name
//
Ruler.prototype.__find__ = function (name) {
  for (var i = 0; i < this.__rules__.length; i++) {
    if (this.__rules__[i].name === name) {
      return i;
    }
  }
  return -1;
};

// Build rules lookup cache
//
Ruler.prototype.__compile__ = function () {
  var self = this;
  var chains = [ '' ];

  // collect unique names
  self.__rules__.forEach(function (rule) {
    if (!rule.enabled) { return; }

    rule.alt.forEach(function (altName) {
      if (chains.indexOf(altName) < 0) {
        chains.push(altName);
      }
    });
  });

  self.__cache__ = {};

  chains.forEach(function (chain) {
    self.__cache__[chain] = [];
    self.__rules__.forEach(function (rule) {
      if (!rule.enabled) { return; }

      if (chain && rule.alt.indexOf(chain) < 0) { return; }

      self.__cache__[chain].push(rule.fn);
    });
  });
};

/**
 * Ruler.at(name, fn [, options])
 * - name (String): rule name to replace.
 * - fn (Function): new rule function.
 * - options (Object): new rule options (not mandatory).
 *
 * Replace rule by name with new function & options. Throws error if name not
 * found.
 *
 * ##### Options:
 *
 * - __alt__ - array with names of "alternate" chains.
 *
 * ##### Example
 *
 * Replace existing typorgapher replacement rule with new one:
 *
 * ```javascript
 * var md = require('markdown-it')();
 *
 * md.core.ruler.at('replacements', function replace(state) {
 *   //...
 * });
 * ```
 **/
Ruler.prototype.at = function (name, fn, options) {
  var index = this.__find__(name);
  var opt = options || {};

  if (index === -1) { throw new Error('Parser rule not found: ' + name); }

  this.__rules__[index].fn = fn;
  this.__rules__[index].alt = opt.alt || [];
  this.__cache__ = null;
};

/**
 * Ruler.before(beforeName, ruleName, fn [, options])
 * - beforeName (String): new rule will be added before this one.
 * - ruleName (String): name of added rule.
 * - fn (Function): rule function.
 * - options (Object): rule options (not mandatory).
 *
 * Add new rule to chain before one with given name. See also
 * [[Ruler.after]], [[Ruler.push]].
 *
 * ##### Options:
 *
 * - __alt__ - array with names of "alternate" chains.
 *
 * ##### Example
 *
 * ```javascript
 * var md = require('markdown-it')();
 *
 * md.block.ruler.before('paragraph', 'my_rule', function replace(state) {
 *   //...
 * });
 * ```
 **/
Ruler.prototype.before = function (beforeName, ruleName, fn, options) {
  var index = this.__find__(beforeName);
  var opt = options || {};

  if (index === -1) { throw new Error('Parser rule not found: ' + beforeName); }

  this.__rules__.splice(index, 0, {
    name: ruleName,
    enabled: true,
    fn: fn,
    alt: opt.alt || []
  });

  this.__cache__ = null;
};

/**
 * Ruler.after(afterName, ruleName, fn [, options])
 * - afterName (String): new rule will be added after this one.
 * - ruleName (String): name of added rule.
 * - fn (Function): rule function.
 * - options (Object): rule options (not mandatory).
 *
 * Add new rule to chain after one with given name. See also
 * [[Ruler.before]], [[Ruler.push]].
 *
 * ##### Options:
 *
 * - __alt__ - array with names of "alternate" chains.
 *
 * ##### Example
 *
 * ```javascript
 * var md = require('markdown-it')();
 *
 * md.inline.ruler.after('text', 'my_rule', function replace(state) {
 *   //...
 * });
 * ```
 **/
Ruler.prototype.after = function (afterName, ruleName, fn, options) {
  var index = this.__find__(afterName);
  var opt = options || {};

  if (index === -1) { throw new Error('Parser rule not found: ' + afterName); }

  this.__rules__.splice(index + 1, 0, {
    name: ruleName,
    enabled: true,
    fn: fn,
    alt: opt.alt || []
  });

  this.__cache__ = null;
};

/**
 * Ruler.push(ruleName, fn [, options])
 * - ruleName (String): name of added rule.
 * - fn (Function): rule function.
 * - options (Object): rule options (not mandatory).
 *
 * Push new rule to the end of chain. See also
 * [[Ruler.before]], [[Ruler.after]].
 *
 * ##### Options:
 *
 * - __alt__ - array with names of "alternate" chains.
 *
 * ##### Example
 *
 * ```javascript
 * var md = require('markdown-it')();
 *
 * md.core.ruler.push('emphasis', 'my_rule', function replace(state) {
 *   //...
 * });
 * ```
 **/
Ruler.prototype.push = function (ruleName, fn, options) {
  var opt = options || {};

  this.__rules__.push({
    name: ruleName,
    enabled: true,
    fn: fn,
    alt: opt.alt || []
  });

  this.__cache__ = null;
};

/**
 * Ruler.enable(list [, ignoreInvalid]) -> Array
 * - list (String|Array): list of rule names to enable.
 * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found.
 *
 * Enable rules with given names. If any rule name not found - throw Error.
 * Errors can be disabled by second param.
 *
 * Returns list of found rule names (if no exception happened).
 *
 * See also [[Ruler.disable]], [[Ruler.enableOnly]].
 **/
Ruler.prototype.enable = function (list, ignoreInvalid) {
  if (!Array.isArray(list)) { list = [ list ]; }

  var result = [];

  // Search by name and enable
  list.forEach(function (name) {
    var idx = this.__find__(name);

    if (idx < 0) {
      if (ignoreInvalid) { return; }
      throw new Error('Rules manager: invalid rule name ' + name);
    }
    this.__rules__[idx].enabled = true;
    result.push(name);
  }, this);

  this.__cache__ = null;
  return result;
};

/**
 * Ruler.enableOnly(list [, ignoreInvalid])
 * - list (String|Array): list of rule names to enable (whitelist).
 * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found.
 *
 * Enable rules with given names, and disable everything else. If any rule name
 * not found - throw Error. Errors can be disabled by second param.
 *
 * See also [[Ruler.disable]], [[Ruler.enable]].
 **/
Ruler.prototype.enableOnly = function (list, ignoreInvalid) {
  if (!Array.isArray(list)) { list = [ list ]; }

  this.__rules__.forEach(function (rule) { rule.enabled = false; });

  this.enable(list, ignoreInvalid);
};

/**
 * Ruler.disable(list [, ignoreInvalid]) -> Array
 * - list (String|Array): list of rule names to disable.
 * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found.
 *
 * Disable rules with given names. If any rule name not found - throw Error.
 * Errors can be disabled by second param.
 *
 * Returns list of found rule names (if no exception happened).
 *
 * See also [[Ruler.enable]], [[Ruler.enableOnly]].
 **/
Ruler.prototype.disable = function (list, ignoreInvalid) {
  if (!Array.isArray(list)) { list = [ list ]; }

  var result = [];

  // Search by name and disable
  list.forEach(function (name) {
    var idx = this.__find__(name);

    if (idx < 0) {
      if (ignoreInvalid) { return; }
      throw new Error('Rules manager: invalid rule name ' + name);
    }
    this.__rules__[idx].enabled = false;
    result.push(name);
  }, this);

  this.__cache__ = null;
  return result;
};

/**
 * Ruler.getRules(chainName) -> Array
 *
 * Return array of active functions (rules) for given chain name. It analyzes
 * rules configuration, compiles caches if not exists and returns result.
 *
 * Default chain name is `''` (empty string). It can't be skipped. That's
 * done intentionally, to keep signature monomorphic for high speed.
 **/
Ruler.prototype.getRules = function (chainName) {
  if (this.__cache__ === null) {
    this.__compile__();
  }

  // Chain can be empty, if rules disabled. But we still have to return Array.
  return this.__cache__[chainName] || [];
};

module.exports = Ruler;
<commit_msg>Small fix in ruler documentation example<commit_after>/**
 * class Ruler
 *
 * Helper class, used by [[MarkdownIt#core]], [[MarkdownIt#block]] and
 * [[MarkdownIt#inline]] to manage sequences of functions (rules):
 *
 * - keep rules in defined order
 * - assign the name to each rule
 * - enable/disable rules
 * - add/replace rules
 * - allow assign rules to additional named chains (in the same)
 * - cacheing lists of active rules
 *
 * You will not need use this class directly until write plugins. For simple
 * rules control use [[MarkdownIt.disable]], [[MarkdownIt.enable]] and
 * [[MarkdownIt.use]].
 **/
'use strict';

/**
 * new Ruler()
 **/
function Ruler() {
  // List of added rules. Each element is:
  //
  // {
  //   name: XXX,
  //   enabled: Boolean,
  //   fn: Function(),
  //   alt: [ name2, name3 ]
  // }
  //
  this.__rules__ = [];

  // Cached rule chains.
  //
  // First level - chain name, '' for default.
  // Second level - diginal anchor for fast filtering by charcodes.
  //
  this.__cache__ = null;
}

////////////////////////////////////////////////////////////////////////////////
// Helper methods, should not be used directly

// Find rule index by name
//
Ruler.prototype.__find__ = function (name) {
  for (var i = 0; i < this.__rules__.length; i++) {
    if (this.__rules__[i].name === name) {
      return i;
    }
  }
  return -1;
};

// Build rules lookup cache
//
Ruler.prototype.__compile__ = function () {
  var self = this;
  var chains = [ '' ];

  // collect unique names
  self.__rules__.forEach(function (rule) {
    if (!rule.enabled) { return; }

    rule.alt.forEach(function (altName) {
      if (chains.indexOf(altName) < 0) {
        chains.push(altName);
      }
    });
  });

  self.__cache__ = {};

  chains.forEach(function (chain) {
    self.__cache__[chain] = [];
    self.__rules__.forEach(function (rule) {
      if (!rule.enabled) { return; }

      if (chain && rule.alt.indexOf(chain) < 0) { return; }

      self.__cache__[chain].push(rule.fn);
    });
  });
};

/**
 * Ruler.at(name, fn [, options])
 * - name (String): rule name to replace.
 * - fn (Function): new rule function.
 * - options (Object): new rule options (not mandatory).
 *
 * Replace rule by name with new function & options. Throws error if name not
 * found.
 *
 * ##### Options:
 *
 * - __alt__ - array with names of "alternate" chains.
 *
 * ##### Example
 *
 * Replace existing typorgapher replacement rule with new one:
 *
 * ```javascript
 * var md = require('markdown-it')();
 *
 * md.core.ruler.at('replacements', function replace(state) {
 *   //...
 * });
 * ```
 **/
Ruler.prototype.at = function (name, fn, options) {
  var index = this.__find__(name);
  var opt = options || {};

  if (index === -1) { throw new Error('Parser rule not found: ' + name); }

  this.__rules__[index].fn = fn;
  this.__rules__[index].alt = opt.alt || [];
  this.__cache__ = null;
};

/**
 * Ruler.before(beforeName, ruleName, fn [, options])
 * - beforeName (String): new rule will be added before this one.
 * - ruleName (String): name of added rule.
 * - fn (Function): rule function.
 * - options (Object): rule options (not mandatory).
 *
 * Add new rule to chain before one with given name. See also
 * [[Ruler.after]], [[Ruler.push]].
 *
 * ##### Options:
 *
 * - __alt__ - array with names of "alternate" chains.
 *
 * ##### Example
 *
 * ```javascript
 * var md = require('markdown-it')();
 *
 * md.block.ruler.before('paragraph', 'my_rule', function replace(state) {
 *   //...
 * });
 * ```
 **/
Ruler.prototype.before = function (beforeName, ruleName, fn, options) {
  var index = this.__find__(beforeName);
  var opt = options || {};

  if (index === -1) { throw new Error('Parser rule not found: ' + beforeName); }

  this.__rules__.splice(index, 0, {
    name: ruleName,
    enabled: true,
    fn: fn,
    alt: opt.alt || []
  });

  this.__cache__ = null;
};

/**
 * Ruler.after(afterName, ruleName, fn [, options])
 * - afterName (String): new rule will be added after this one.
 * - ruleName (String): name of added rule.
 * - fn (Function): rule function.
 * - options (Object): rule options (not mandatory).
 *
 * Add new rule to chain after one with given name. See also
 * [[Ruler.before]], [[Ruler.push]].
 *
 * ##### Options:
 *
 * - __alt__ - array with names of "alternate" chains.
 *
 * ##### Example
 *
 * ```javascript
 * var md = require('markdown-it')();
 *
 * md.inline.ruler.after('text', 'my_rule', function replace(state) {
 *   //...
 * });
 * ```
 **/
Ruler.prototype.after = function (afterName, ruleName, fn, options) {
  var index = this.__find__(afterName);
  var opt = options || {};

  if (index === -1) { throw new Error('Parser rule not found: ' + afterName); }

  this.__rules__.splice(index + 1, 0, {
    name: ruleName,
    enabled: true,
    fn: fn,
    alt: opt.alt || []
  });

  this.__cache__ = null;
};

/**
 * Ruler.push(ruleName, fn [, options])
 * - ruleName (String): name of added rule.
 * - fn (Function): rule function.
 * - options (Object): rule options (not mandatory).
 *
 * Push new rule to the end of chain. See also
 * [[Ruler.before]], [[Ruler.after]].
 *
 * ##### Options:
 *
 * - __alt__ - array with names of "alternate" chains.
 *
 * ##### Example
 *
 * ```javascript
 * var md = require('markdown-it')();
 *
 * md.core.ruler.push('my_rule', function replace(state) {
 *   //...
 * });
 * ```
 **/
Ruler.prototype.push = function (ruleName, fn, options) {
  var opt = options || {};

  this.__rules__.push({
    name: ruleName,
    enabled: true,
    fn: fn,
    alt: opt.alt || []
  });

  this.__cache__ = null;
};

/**
 * Ruler.enable(list [, ignoreInvalid]) -> Array
 * - list (String|Array): list of rule names to enable.
 * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found.
 *
 * Enable rules with given names. If any rule name not found - throw Error.
 * Errors can be disabled by second param.
 *
 * Returns list of found rule names (if no exception happened).
 *
 * See also [[Ruler.disable]], [[Ruler.enableOnly]].
 **/
Ruler.prototype.enable = function (list, ignoreInvalid) {
  if (!Array.isArray(list)) { list = [ list ]; }

  var result = [];

  // Search by name and enable
  list.forEach(function (name) {
    var idx = this.__find__(name);

    if (idx < 0) {
      if (ignoreInvalid) { return; }
      throw new Error('Rules manager: invalid rule name ' + name);
    }
    this.__rules__[idx].enabled = true;
    result.push(name);
  }, this);

  this.__cache__ = null;
  return result;
};

/**
 * Ruler.enableOnly(list [, ignoreInvalid])
 * - list (String|Array): list of rule names to enable (whitelist).
 * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found.
 *
 * Enable rules with given names, and disable everything else. If any rule name
 * not found - throw Error. Errors can be disabled by second param.
 *
 * See also [[Ruler.disable]], [[Ruler.enable]].
 **/
Ruler.prototype.enableOnly = function (list, ignoreInvalid) {
  if (!Array.isArray(list)) { list = [ list ]; }

  this.__rules__.forEach(function (rule) { rule.enabled = false; });

  this.enable(list, ignoreInvalid);
};

/**
 * Ruler.disable(list [, ignoreInvalid]) -> Array
 * - list (String|Array): list of rule names to disable.
 * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found.
 *
 * Disable rules with given names. If any rule name not found - throw Error.
 * Errors can be disabled by second param.
 *
 * Returns list of found rule names (if no exception happened).
 *
 * See also [[Ruler.enable]], [[Ruler.enableOnly]].
 **/
Ruler.prototype.disable = function (list, ignoreInvalid) {
  if (!Array.isArray(list)) { list = [ list ]; }

  var result = [];

  // Search by name and disable
  list.forEach(function (name) {
    var idx = this.__find__(name);

    if (idx < 0) {
      if (ignoreInvalid) { return; }
      throw new Error('Rules manager: invalid rule name ' + name);
    }
    this.__rules__[idx].enabled = false;
    result.push(name);
  }, this);

  this.__cache__ = null;
  return result;
};

/**
 * Ruler.getRules(chainName) -> Array
 *
 * Return array of active functions (rules) for given chain name. It analyzes
 * rules configuration, compiles caches if not exists and returns result.
 *
 * Default chain name is `''` (empty string). It can't be skipped. That's
 * done intentionally, to keep signature monomorphic for high speed.
 **/
Ruler.prototype.getRules = function (chainName) {
  if (this.__cache__ === null) {
    this.__compile__();
  }

  // Chain can be empty, if rules disabled. But we still have to return Array.
  return this.__cache__[chainName] || [];
};

module.exports = Ruler;
<|endoftext|>


<commit_before>// -------------------------------------------------------------------------- \\
// File: DateParser.js                                                        \\
// Module: Parser                                                             \\
// Requires: Localisation, Parse.js                                           \\
// Author: Neil Jenkins                                                       \\
// License: © 2010–2013 FastMail Pty Ltd. All rights reserved.                \\
// -------------------------------------------------------------------------- \\

"use strict";

( function ( NS ) {

// --- Date Grammar ---

var JUST_TIME = 1,
    JUST_DATE = 2,
    DATE_AND_TIME = 3;

var generateLocalisedDateParser = function ( locale, mode ) {
    var Parse = NS.Parse,
        define = Parse.define,
        optional = Parse.optional,
        not = Parse.not,
        sequence = Parse.sequence,
        firstMatch = Parse.firstMatch,
        longestMatch = Parse.longestMatch;

    var datePatterns = locale.datePatterns;

    var anyInLocale = function ( type, names ) {
        return firstMatch(
            names.split( ' ' ).map( function ( name ) {
                return define( type, datePatterns[ name ], name );
            })
        );
    };

    var whitespace = define( 'whitespace', (/^[\s"']+/) );

    var hours = define( 'hour', /^(?:2[0-3]|[0-1]?\d)/ ),
        minutes = define( 'minute', /^[0-5][0-9]/ ),
        seconds = define( 'second', /^[0-5][0-9]/ ),
        meridian = firstMatch([
            define( 'am', datePatterns.am ),
            define( 'pm', datePatterns.pm )
        ]),
        timeSuffix = sequence([
            optional( whitespace ),
            optional( meridian )
        ]),
        timeDelimiter = define( 'timeDelimiter', ( /^[:.]/ ) ),
        timeContext = define( 'timeContext', datePatterns.timeContext ),
        time = sequence([
            hours,
            optional( sequence([
                timeDelimiter,
                minutes,
                optional( sequence([
                    timeDelimiter,
                    seconds
                ]))
            ])),
            optional(
                timeSuffix
            )
        ]);

    if ( mode === JUST_TIME ) {
        return firstMatch([
            time,
            whitespace
        ]);
    }

    var ordinalSuffix = define( 'ordinalSuffix', datePatterns.ordinalSuffix ),

        weekday = anyInLocale( 'weekday', 'sun mon tue wed thu fri sat' ),
        day = sequence([
            define( 'day', /^(?:[0-2]\d|3[0-1]|\d)/ ),
            optional( ordinalSuffix ),
            not( timeContext )
        ]),
        monthnumber = sequence([
            define( 'month', /^(?:1[0-2]|0\d|\d)/ ),
            not( firstMatch([
                timeContext,
                ordinalSuffix
            ]))
        ]),
        monthname = anyInLocale( 'monthname',
            'jan feb mar apr may jun jul aug sep oct nov dec' ),
        month = firstMatch([
            monthnumber,
            monthname
        ]),
        year = sequence([
            define( 'year', /^\d\d(?:\d\d)?/ ),
            not( firstMatch([
                timeContext,
                ordinalSuffix
            ]))
        ]),
        searchMethod = anyInLocale( 'searchMethod', 'past future' ),

        dateDelimiter = define( 'dateDelimiter',
            ( /^(?:[\s\-\.\,\'\/]|of)+/ ) ),

        relativeDate = anyInLocale( 'relativeDate',
            'yesterday tomorrow today now' ),

        dayMonthYear = sequence([
            day,
            dateDelimiter,
            month,
            dateDelimiter,
            year
        ]),
        dayMonth = sequence([
            day,
            dateDelimiter,
            month
        ]),
        monthYear = sequence([
            month,
            dateDelimiter,
            year,
            not( timeContext )
        ]),
        monthDayYear = sequence([
            month,
            dateDelimiter,
            day,
            dateDelimiter,
            year
        ]),
        monthDay = sequence([
            month,
            dateDelimiter,
            day
        ]),
        yearMonthDay = sequence([
            year,
            dateDelimiter,
            month,
            dateDelimiter,
            day
        ]),
        yearMonth = sequence([
            year,
            dateDelimiter,
            month
        ]),

        date = sequence([
            longestMatch(
                locale.dateElementOrder === 'dmy' ? [
                    dayMonthYear,
                    dayMonth,
                    monthYear,
                    monthDayYear,
                    monthDay,
                    yearMonthDay,
                    yearMonth
                ] : locale.dateElementOrder === 'mdy' ?     [
                    monthDayYear,
                    monthDay,
                    monthYear,
                    dayMonthYear,
                    dayMonth,
                    yearMonthDay,
                    yearMonth
                ] : [
                    yearMonthDay,
                    yearMonth,
                    dayMonthYear,
                    dayMonth,
                    monthYear,
                    monthDayYear,
                    monthDay
                ]
            ),
            not( define( '', /^\d/ ) )
        ]);

    if ( mode === JUST_DATE ) {
        return firstMatch([
            date,
            weekday,
            monthname,
            year,
            relativeDate,
            searchMethod,
            whitespace
        ]);
    }

    return firstMatch([
        date,
        time,
        weekday,
        monthname,
        year,
        relativeDate,
        searchMethod,
        whitespace
    ]);
};

// --- Interpreter ---

var monthNameToIndex = 'jan feb mar apr may jun jul aug sep oct nov dec'
    .split( ' ' )
    .reduce( function ( monthNameToIndex, name, i ) {
        monthNameToIndex[ name ] = i;
        return monthNameToIndex;
    }, {} );

var dayNameToIndex = 'sun mon tue wed thu fri sat'
    .split( ' ' )
    .reduce( function ( dayNameToIndex, name, i ) {
        dayNameToIndex[ name ] = i;
        return dayNameToIndex;
    }, {} );

var isLeapYear = Date.isLeapYear;
var getDaysInMonth = Date.getDaysInMonth;

var NOW = 0;
var PAST = -1;
var FUTURE = 1;

var interpreter = {
    interpret: function ( tokens, implicitSearchMethod ) {
        var date = {},
            i, l, token, name;
        for ( i = 0, l = tokens.length; i < l; i += 1 ) {
            token = tokens[i];
            name = token[0];
            if ( this[ name ] ) {
                this[ name ]( date, token[1], token[2], tokens );
            }
        }
        return this.findDate( date, date.searchMethod || implicitSearchMethod );
    },
    findDate: function ( constraints, searchMethod ) {
        var keys = Object.keys( constraints );
        if ( !keys.length ) {
            return null;
        }
        var date = new Date();

        // If we don't do this, setting month lower down could go wrong,
        // because if the date is 30th and we set month as Feb, we'll end up
        // in March!
        date.setDate( 1 );

        // Time:
        date.setHours( constraints.hour || 0 );
        date.setMinutes( constraints.minute || 0 );
        date.setSeconds( constraints.second || 0 );
        date.setMilliseconds( 0 );

        // Date:
        var day = constraints.day,
            month = constraints.month,
            year = constraints.year,
            weekday = constraints.weekday,

            hasMonth = !!( month || month === 0 ),
            hasWeekday = !!( weekday || weekday === 0 ),

            dayInMs = 86400000,
            currentMonth, isFeb29, delta;

        if ( day && hasMonth && year ) {
            if ( day > getDaysInMonth( month, year ) ) {
                date = null;
            } else {
                date.setFullYear( year );
                date.setMonth( month );
                date.setDate( day );
            }
        } else if ( hasMonth && year ) {
            date.setFullYear( year );
            date.setMonth( month );
            if ( hasWeekday ) {
                if ( searchMethod !== PAST ) {
                    // Date is currently 1.
                    day = ( weekday - date.getDay() ).mod( 7 ) + 1;
                } else {
                    date.setDate( day = getDaysInMonth( month, year ) );
                    day = day - ( date.getDay() - weekday ).mod( 7 );
                }
            } else {
                day = 1;
            }
            date.setDate( day );
        } else if ( day && hasMonth ) {
            currentMonth = date.getMonth();
            year = date.getFullYear();
            // We just use the current year if searchMethod === NOW
            // If it's FUTURE or PAST though, make sure the date conforms to
            // that.
            if ( searchMethod === FUTURE ) {
                if ( month < currentMonth ||
                        ( month === currentMonth && day <= date.getDate() ) ) {
                    year += 1;
                }
            }
            if ( searchMethod === PAST ) {
                if ( month > currentMonth ||
                        ( month === currentMonth && day >= date.getDate() ) ) {
                    year -= 1;
                }
            }
            date.setFullYear( year );
            date.setMonth( month );
            date.setDate( day );
            // If we have a weekday constraint, iterate in the past or future
            // direction until we find a year where that matches.
            if ( hasWeekday ) {
                isFeb29 = ( day === 29 && month === 1 );
                if ( isFeb29 ) {
                    while ( !isLeapYear( year ) ) {
                        year += ( searchMethod || 1 );
                    }
                    date.setFullYear( year );
                }
                delta = ( isFeb29 ? 4 : 1 ) * ( searchMethod || 1 ) ;
                while ( date.getDay() !== weekday ) {
                    do {
                        year += delta;
                    } while ( isFeb29 && !isLeapYear( year ) );
                    date.setFullYear( year );
                }
            }
        } else if ( day ) {
            year = date.getFullYear();
            month = date.getMonth();
            date.setDate( day );
            if ( hasWeekday ) {
                // Find month which satisfies this.
                while ( date.getDay() !== weekday || date.getDate() !== day ) {
                    if ( searchMethod === PAST ) {
                        if ( month ) {
                            month -= 1;
                        } else {
                            year -= 1;
                            month = 11;
                        }
                    } else {
                        if ( month === 11 ) {
                            month += 1;
                        } else {
                            year += 1;
                            month = 0;
                        }
                    }
                    date.setFullYear( year );
                    date.setMonth( month );
                    date.setDate( day );
                }
            }
        } else if ( hasMonth ) {
            year = date.getFullYear();
            currentMonth = date.getMonth();
            // We just use the current year if searchMethod === NOW
            // If it's FUTURE or PAST though, make sure the date conforms to
            // that.
            if ( searchMethod === FUTURE && month <= currentMonth ) {
                year += 1;
            }
            if ( searchMethod === PAST && month > currentMonth ) {
                year -= 1;
            }
            date.setFullYear( year );
            date.setMonth( month );

            if ( hasWeekday ) {
                if ( searchMethod !== PAST ) {
                    day = ( weekday - date.getDay() ).mod( 7 ) + 1;
                } else {
                    date.setDate( day = getDaysInMonth( month, year ) );
                    day = day - ( date.getDay() - weekday ).mod( 7 );
                }
                date.setDate( day );
            }
        } else if ( year ) {
            date.setFullYear( year );
            date.setMonth( 0 );
            if ( hasWeekday ) {
                if ( searchMethod !== PAST ) {
                    day = ( weekday - date.getDay() ).mod( 7 ) + 1;
                } else {
                    date.setDate( day = getDaysInMonth( month, year ) );
                    day = day - ( date.getDay() - weekday ).mod( 7 );
                }
                date.setDate( day );
            }
        } else if ( hasWeekday ) {
            if ( searchMethod === PAST ) {
                date.setTime( date.getTime() - dayInMs );
                date.setTime( date.getTime() -
                    ( dayInMs * ( date.getDay() - weekday ).mod( 7 ) ) );
            } else {
                date.setTime( date.getTime() + dayInMs );
                date.setTime( date.getTime() +
                    ( dayInMs * ( weekday - date.getDay() ).mod( 7 ) ) );
            }
            return date;
        }

        return date;
    },

    weekday: function ( date, string, weekday ) {
        date.weekday = dayNameToIndex[ weekday ];
    },
    day: function ( date, string ) {
        date.day = +string;
    },
    month: function ( date, string ) {
        date.month = +string - 1;
    },
    monthname: function ( date, string, name ) {
        date.month = monthNameToIndex[ name ];
    },
    year: function ( date, string ) {
        var year = +string;
        if ( string.length === 2 ) {
            year += 2000;
            if ( year > new Date().getFullYear() + 30 ) {
                year -= 100;
            }
        }
        date.year = year;
    },
    hour: function ( date, string ) {
        date.hour = +string;
        var meridian = date.meridian;
        if ( meridian ) {
            this[ meridian ]( date );
        }
    },
    minute: function ( date, string ) {
        date.minute = +string;
    },
    second: function ( date, string ) {
        date.second = +string;
    },
    am: function ( date ) {
        date.meridian = 'am';
        var hour = date.hour;
        if ( hour && hour === 12 ) {
            date.hour = 0;
        }
    },
    pm: function ( date ) {
        date.meridian = 'pm';
        var hour = date.hour;
        if ( hour && hour < 12 ) {
            date.hour = hour + 12;
        }
    },
    searchMethod: function ( date, string, pastOrFuture ) {
        date.searchMethod = ( pastOrFuture === 'past' ) ? PAST : FUTURE;
    },
    relativeDate: function ( date, string, context ) {
        var now = new Date(),
            dayInMs = 86400000;
        switch ( context ) {
            case 'yesterday':
                now.setTime( now.getTime() - dayInMs );
                break;
            case 'tomorrow':
                now.setTime( now.getTime() + dayInMs );
                break;
        }
        date.day = now.getDate();
        date.month = now.getMonth();
        date.year = now.getFullYear();
    }
};

// ---

var unknown = NS.Parse.define( 'unknown', /^[^\s]+/ );

var dateParsers = {};
var parseDateTime = function ( string, locale, implicitPast, mode ) {
    if ( !locale ) {
        locale = NS.i18n.getLocale();
    }
    var code = locale.code + mode;
    var dateParser = dateParsers[ code ] ||
        ( dateParsers[ code ] = generateLocalisedDateParser( locale, mode ) );
    var parse = new NS.Parse( string.trim() );
    while ( parse.string.length ) {
        if ( !dateParser( parse ) ) {
            // We've hit something unexpected. Skip it.
            unknown( parse );
        }
    }
    return parse.tokens;
};

NS.parse.tokeniseDateTime = parseDateTime;
NS.parse.interpretDateTime = function ( tokens, implicitSearchMethod ) {
    return interpreter.interpret( tokens, implicitSearchMethod || NOW );
};

NS.parse.time = function ( string, locale ) {
    var tokens = parseDateTime( string, locale, false, JUST_TIME );
    return interpreter.interpret( tokens );
};

NS.parse.date = function ( string, locale, implicitPast ) {
    var tokens = parseDateTime( string, locale, implicitPast, JUST_DATE );
    return interpreter.interpret( tokens, implicitPast ? PAST : NOW );
};

NS.parse.dateTime = function ( string, locale, implicitPast ) {
    var tokens = parseDateTime( string, locale, implicitPast, DATE_AND_TIME );
    return interpreter.interpret( tokens, implicitPast ? PAST : NOW );
};

}( this.O ) );
<commit_msg>Accept 24h time without delimiter when parsing just time<commit_after>// -------------------------------------------------------------------------- \\
// File: DateParser.js                                                        \\
// Module: Parser                                                             \\
// Requires: Localisation, Parse.js                                           \\
// Author: Neil Jenkins                                                       \\
// License: © 2010–2013 FastMail Pty Ltd. All rights reserved.                \\
// -------------------------------------------------------------------------- \\

"use strict";

( function ( NS ) {

// --- Date Grammar ---

var JUST_TIME = 1,
    JUST_DATE = 2,
    DATE_AND_TIME = 3;

var generateLocalisedDateParser = function ( locale, mode ) {
    var Parse = NS.Parse,
        define = Parse.define,
        optional = Parse.optional,
        not = Parse.not,
        sequence = Parse.sequence,
        firstMatch = Parse.firstMatch,
        longestMatch = Parse.longestMatch;

    var datePatterns = locale.datePatterns;

    var anyInLocale = function ( type, names ) {
        return firstMatch(
            names.split( ' ' ).map( function ( name ) {
                return define( type, datePatterns[ name ], name );
            })
        );
    };

    var whitespace = define( 'whitespace', (/^(?:[\s"']+|$)/) );

    var hours = define( 'hour', /^(?:2[0-3]|[01]?\d)/ ),
        minutes = define( 'minute', /^[0-5][0-9]/ ),
        seconds = define( 'second', /^[0-5][0-9]/ ),
        meridian = firstMatch([
            define( 'am', datePatterns.am ),
            define( 'pm', datePatterns.pm )
        ]),
        timeSuffix = sequence([
            optional( whitespace ),
            meridian
        ]),
        timeDelimiter = define( 'timeDelimiter', ( /^[:.]/ ) ),
        timeContext = define( 'timeContext', datePatterns.timeContext ),
        time = sequence([
            hours,
            optional( sequence([
                timeDelimiter,
                minutes,
                optional( sequence([
                    timeDelimiter,
                    seconds
                ]))
            ])),
            optional(
                timeSuffix
            ),
            whitespace
        ]);

    if ( mode === JUST_TIME ) {
        return firstMatch([
            time,
            sequence([
                hours,
                minutes
            ]),
            whitespace
        ]);
    }

    var ordinalSuffix = define( 'ordinalSuffix', datePatterns.ordinalSuffix ),

        weekday = anyInLocale( 'weekday', 'sun mon tue wed thu fri sat' ),
        day = sequence([
            define( 'day', /^(?:[0-2]\d|3[0-1]|\d)/ ),
            optional( ordinalSuffix ),
            not( timeContext )
        ]),
        monthnumber = sequence([
            define( 'month', /^(?:1[0-2]|0\d|\d)/ ),
            not( firstMatch([
                timeContext,
                ordinalSuffix
            ]))
        ]),
        monthname = anyInLocale( 'monthname',
            'jan feb mar apr may jun jul aug sep oct nov dec' ),
        month = firstMatch([
            monthnumber,
            monthname
        ]),
        year = sequence([
            define( 'year', /^\d\d(?:\d\d)?/ ),
            not( firstMatch([
                timeContext,
                ordinalSuffix
            ]))
        ]),
        searchMethod = anyInLocale( 'searchMethod', 'past future' ),

        dateDelimiter = define( 'dateDelimiter',
            ( /^(?:[\s\-\.\,\'\/]|of)+/ ) ),

        relativeDate = anyInLocale( 'relativeDate',
            'yesterday tomorrow today now' ),

        dayMonthYear = sequence([
            day,
            dateDelimiter,
            month,
            dateDelimiter,
            year
        ]),
        dayMonth = sequence([
            day,
            dateDelimiter,
            month
        ]),
        monthYear = sequence([
            month,
            dateDelimiter,
            year,
            not( timeContext )
        ]),
        monthDayYear = sequence([
            month,
            dateDelimiter,
            day,
            dateDelimiter,
            year
        ]),
        monthDay = sequence([
            month,
            dateDelimiter,
            day
        ]),
        yearMonthDay = sequence([
            year,
            dateDelimiter,
            month,
            dateDelimiter,
            day
        ]),
        yearMonth = sequence([
            year,
            dateDelimiter,
            month
        ]),

        date = sequence([
            longestMatch(
                locale.dateElementOrder === 'dmy' ? [
                    dayMonthYear,
                    dayMonth,
                    monthYear,
                    monthDayYear,
                    monthDay,
                    yearMonthDay,
                    yearMonth
                ] : locale.dateElementOrder === 'mdy' ?     [
                    monthDayYear,
                    monthDay,
                    monthYear,
                    dayMonthYear,
                    dayMonth,
                    yearMonthDay,
                    yearMonth
                ] : [
                    yearMonthDay,
                    yearMonth,
                    dayMonthYear,
                    dayMonth,
                    monthYear,
                    monthDayYear,
                    monthDay
                ]
            ),
            not( define( '', /^\d/ ) )
        ]);

    if ( mode === JUST_DATE ) {
        return firstMatch([
            date,
            weekday,
            monthname,
            year,
            relativeDate,
            searchMethod,
            whitespace
        ]);
    }

    return firstMatch([
        date,
        time,
        weekday,
        monthname,
        year,
        relativeDate,
        searchMethod,
        whitespace
    ]);
};

// --- Interpreter ---

var monthNameToIndex = 'jan feb mar apr may jun jul aug sep oct nov dec'
    .split( ' ' )
    .reduce( function ( monthNameToIndex, name, i ) {
        monthNameToIndex[ name ] = i;
        return monthNameToIndex;
    }, {} );

var dayNameToIndex = 'sun mon tue wed thu fri sat'
    .split( ' ' )
    .reduce( function ( dayNameToIndex, name, i ) {
        dayNameToIndex[ name ] = i;
        return dayNameToIndex;
    }, {} );

var isLeapYear = Date.isLeapYear;
var getDaysInMonth = Date.getDaysInMonth;

var NOW = 0;
var PAST = -1;
var FUTURE = 1;

var interpreter = {
    interpret: function ( tokens, implicitSearchMethod ) {
        var date = {},
            i, l, token, name;
        for ( i = 0, l = tokens.length; i < l; i += 1 ) {
            token = tokens[i];
            name = token[0];
            if ( this[ name ] ) {
                this[ name ]( date, token[1], token[2], tokens );
            }
        }
        return this.findDate( date, date.searchMethod || implicitSearchMethod );
    },
    findDate: function ( constraints, searchMethod ) {
        var keys = Object.keys( constraints );
        if ( !keys.length ) {
            return null;
        }
        var date = new Date();

        // If we don't do this, setting month lower down could go wrong,
        // because if the date is 30th and we set month as Feb, we'll end up
        // in March!
        date.setDate( 1 );

        // Time:
        date.setHours( constraints.hour || 0 );
        date.setMinutes( constraints.minute || 0 );
        date.setSeconds( constraints.second || 0 );
        date.setMilliseconds( 0 );

        // Date:
        var day = constraints.day,
            month = constraints.month,
            year = constraints.year,
            weekday = constraints.weekday,

            hasMonth = !!( month || month === 0 ),
            hasWeekday = !!( weekday || weekday === 0 ),

            dayInMs = 86400000,
            currentMonth, isFeb29, delta;

        if ( day && hasMonth && year ) {
            if ( day > getDaysInMonth( month, year ) ) {
                date = null;
            } else {
                date.setFullYear( year );
                date.setMonth( month );
                date.setDate( day );
            }
        } else if ( hasMonth && year ) {
            date.setFullYear( year );
            date.setMonth( month );
            if ( hasWeekday ) {
                if ( searchMethod !== PAST ) {
                    // Date is currently 1.
                    day = ( weekday - date.getDay() ).mod( 7 ) + 1;
                } else {
                    date.setDate( day = getDaysInMonth( month, year ) );
                    day = day - ( date.getDay() - weekday ).mod( 7 );
                }
            } else {
                day = 1;
            }
            date.setDate( day );
        } else if ( day && hasMonth ) {
            currentMonth = date.getMonth();
            year = date.getFullYear();
            // We just use the current year if searchMethod === NOW
            // If it's FUTURE or PAST though, make sure the date conforms to
            // that.
            if ( searchMethod === FUTURE ) {
                if ( month < currentMonth ||
                        ( month === currentMonth && day <= date.getDate() ) ) {
                    year += 1;
                }
            }
            if ( searchMethod === PAST ) {
                if ( month > currentMonth ||
                        ( month === currentMonth && day >= date.getDate() ) ) {
                    year -= 1;
                }
            }
            date.setFullYear( year );
            date.setMonth( month );
            date.setDate( day );
            // If we have a weekday constraint, iterate in the past or future
            // direction until we find a year where that matches.
            if ( hasWeekday ) {
                isFeb29 = ( day === 29 && month === 1 );
                if ( isFeb29 ) {
                    while ( !isLeapYear( year ) ) {
                        year += ( searchMethod || 1 );
                    }
                    date.setFullYear( year );
                }
                delta = ( isFeb29 ? 4 : 1 ) * ( searchMethod || 1 ) ;
                while ( date.getDay() !== weekday ) {
                    do {
                        year += delta;
                    } while ( isFeb29 && !isLeapYear( year ) );
                    date.setFullYear( year );
                }
            }
        } else if ( day ) {
            year = date.getFullYear();
            month = date.getMonth();
            date.setDate( day );
            if ( hasWeekday ) {
                // Find month which satisfies this.
                while ( date.getDay() !== weekday || date.getDate() !== day ) {
                    if ( searchMethod === PAST ) {
                        if ( month ) {
                            month -= 1;
                        } else {
                            year -= 1;
                            month = 11;
                        }
                    } else {
                        if ( month === 11 ) {
                            month += 1;
                        } else {
                            year += 1;
                            month = 0;
                        }
                    }
                    date.setFullYear( year );
                    date.setMonth( month );
                    date.setDate( day );
                }
            }
        } else if ( hasMonth ) {
            year = date.getFullYear();
            currentMonth = date.getMonth();
            // We just use the current year if searchMethod === NOW
            // If it's FUTURE or PAST though, make sure the date conforms to
            // that.
            if ( searchMethod === FUTURE && month <= currentMonth ) {
                year += 1;
            }
            if ( searchMethod === PAST && month > currentMonth ) {
                year -= 1;
            }
            date.setFullYear( year );
            date.setMonth( month );

            if ( hasWeekday ) {
                if ( searchMethod !== PAST ) {
                    day = ( weekday - date.getDay() ).mod( 7 ) + 1;
                } else {
                    date.setDate( day = getDaysInMonth( month, year ) );
                    day = day - ( date.getDay() - weekday ).mod( 7 );
                }
                date.setDate( day );
            }
        } else if ( year ) {
            date.setFullYear( year );
            date.setMonth( 0 );
            if ( hasWeekday ) {
                if ( searchMethod !== PAST ) {
                    day = ( weekday - date.getDay() ).mod( 7 ) + 1;
                } else {
                    date.setDate( day = getDaysInMonth( month, year ) );
                    day = day - ( date.getDay() - weekday ).mod( 7 );
                }
                date.setDate( day );
            }
        } else if ( hasWeekday ) {
            if ( searchMethod === PAST ) {
                date.setTime( date.getTime() - dayInMs );
                date.setTime( date.getTime() -
                    ( dayInMs * ( date.getDay() - weekday ).mod( 7 ) ) );
            } else {
                date.setTime( date.getTime() + dayInMs );
                date.setTime( date.getTime() +
                    ( dayInMs * ( weekday - date.getDay() ).mod( 7 ) ) );
            }
            return date;
        }

        return date;
    },

    weekday: function ( date, string, weekday ) {
        date.weekday = dayNameToIndex[ weekday ];
    },
    day: function ( date, string ) {
        date.day = +string;
    },
    month: function ( date, string ) {
        date.month = +string - 1;
    },
    monthname: function ( date, string, name ) {
        date.month = monthNameToIndex[ name ];
    },
    year: function ( date, string ) {
        var year = +string;
        if ( string.length === 2 ) {
            year += 2000;
            if ( year > new Date().getFullYear() + 30 ) {
                year -= 100;
            }
        }
        date.year = year;
    },
    hour: function ( date, string ) {
        date.hour = +string;
        var meridian = date.meridian;
        if ( meridian ) {
            this[ meridian ]( date );
        }
    },
    minute: function ( date, string ) {
        date.minute = +string;
    },
    second: function ( date, string ) {
        date.second = +string;
    },
    am: function ( date ) {
        date.meridian = 'am';
        var hour = date.hour;
        if ( hour && hour === 12 ) {
            date.hour = 0;
        }
    },
    pm: function ( date ) {
        date.meridian = 'pm';
        var hour = date.hour;
        if ( hour && hour < 12 ) {
            date.hour = hour + 12;
        }
    },
    searchMethod: function ( date, string, pastOrFuture ) {
        date.searchMethod = ( pastOrFuture === 'past' ) ? PAST : FUTURE;
    },
    relativeDate: function ( date, string, context ) {
        var now = new Date(),
            dayInMs = 86400000;
        switch ( context ) {
            case 'yesterday':
                now.setTime( now.getTime() - dayInMs );
                break;
            case 'tomorrow':
                now.setTime( now.getTime() + dayInMs );
                break;
        }
        date.day = now.getDate();
        date.month = now.getMonth();
        date.year = now.getFullYear();
    }
};

// ---

var unknown = NS.Parse.define( 'unknown', /^[^\s]+/ );

var dateParsers = {};
var parseDateTime = function ( string, locale, implicitPast, mode ) {
    if ( !locale ) {
        locale = NS.i18n.getLocale();
    }
    var code = locale.code + mode;
    var dateParser = dateParsers[ code ] ||
        ( dateParsers[ code ] = generateLocalisedDateParser( locale, mode ) );
    var parse = new NS.Parse( string.trim() );
    while ( parse.string.length ) {
        if ( !dateParser( parse ) ) {
            // We've hit something unexpected. Skip it.
            unknown( parse );
        }
    }
    return parse.tokens;
};

NS.parse.tokeniseDateTime = parseDateTime;
NS.parse.interpretDateTime = function ( tokens, implicitSearchMethod ) {
    return interpreter.interpret( tokens, implicitSearchMethod || NOW );
};

NS.parse.time = function ( string, locale ) {
    var tokens = parseDateTime( string, locale, false, JUST_TIME );
    return interpreter.interpret( tokens );
};

NS.parse.date = function ( string, locale, implicitPast ) {
    var tokens = parseDateTime( string, locale, implicitPast, JUST_DATE );
    return interpreter.interpret( tokens, implicitPast ? PAST : NOW );
};

NS.parse.dateTime = function ( string, locale, implicitPast ) {
    var tokens = parseDateTime( string, locale, implicitPast, DATE_AND_TIME );
    return interpreter.interpret( tokens, implicitPast ? PAST : NOW );
};

}( this.O ) );
<|endoftext|>


<commit_before>var assign = require('object-assign');
var fs = require('fs-extra');
var path = require('path');
var mime = require('mime-types');
var nameFunctions = require('./nameFunctions');

var debug = require('debug')('keystone:storage:adapter:fs');

/**
	The Adapter Compatibility Level is used to ensure that provided adapters
	have been designed to work with the version of Storage implemented in the
	version of Keystone being used.

	The Adapter Compatibility Level must be an exact match or an error will be
	thrown when the Storage instance is initialised.
*/
var ADAPTER_COMPATIBILITY_LEVEL = 1;

/**
	This is the default storage schema for file fields. These paths will be
	persisted to the database and represent the value of the file field.

	Storage Adapters can specify additional schema properties.
*/
var SCHEMA_TYPES = {
//	filename: String,       // the filename, without the full path
	size: Number,           // the size of the file
	mimetype: String,       // the mime type of the file
	path: String,           // the path (e.g directory) the file is stored in; not the full path to the file
	originalname: String,   // the original (uploaded) name of the file; useful when filename generated
	url: String,            // publicly accessible URL of the stored file
};

/**
	This is the default set of schema fields that are enabled. The storage
	options can override these (must be supported by the adapter, or the
	adapter must use the default schema implementation)
*/
var SCHEMA_FIELD_DEFAULTS = {
//	filename: true,
	size: true,
	mimetype: true,
	path: false,
	originalname: false,
	url: false,
};

// TODO: We could support custom schema mappings for backwards compatibilty
// with Keystone 0.3 or imported schamas... not sure if it's worth it though.

/**
	# Storage Prototype

	Creates a new Keystone Storage instance, and validates and initialises the
	specified adapter. Storage schema is configured from the adapter or default.
*/
function Storage (options) {
	// we're going to mutate options so get a clean copy of it
	options = assign({}, options);

	var AdapterType = options.adapter;
	delete options.adapter;

	debug('Initialising Storage with adapter ' + AdapterType.name);

	// ensure the adapter compatibilityLevel of the adapter matches
	if (AdapterType.compatibilityLevel !== ADAPTER_COMPATIBILITY_LEVEL) {
		throw new Error('Incompatible Storage Adapter\n\n'
			+ 'The storage adapter specified (' + AdapterType.name + ') '
			+ 'does not match the compatibility level required for this '
			+ 'version of Keystone.');
	}

	// assign ensures the default schema constant isn't exposed as a property
	var schemaFields = assign({}, SCHEMA_FIELD_DEFAULTS, AdapterType.SCHEMA_FIELD_DEFAULTS, options.schema);
	delete options.schema;

	// Copy requested fields into local schema.
	var schema = this.schema = {};
	for (var path in schemaFields) {
		if (!schemaFields[path]) continue;

		var type = AdapterType.SCHEMA_TYPES[path] || SCHEMA_TYPES[path];
		if (!type) throw Error('Unknown type for requested schema field ' + path);
		schema[path] = type;
	}

	// ensure Storage schema features are supported by the Adapter
	if (schema.url && typeof AdapterType.prototype.getFileURL !== 'function') {
		throw Error('URL schema field is not supported by the ' + AdapterType.name + ' adapter');
	}

	// create the adapter
	this.adapter = new AdapterType(options, schema);
}

// Helper function for figuring out the size of a file before uploading it.
function getSize (file, callback) {
	if (file.size) return callback(null, file.size);

	fs.stat(file.path, function (err, stats) {
		if (!stats.isFile()) {
			return callback(Error(file.path + ' is not a file'));
		}

		callback(err, stats ? stats.size : null);
	});
}

// Helper to fill out missing fields in the file object
function normalizeFile (file, schema, callback) {
	// Detect required information if it wasn't provided by inspecting the
	// file stored at file.path
	if (schema.mimetype && !file.mimetype) file.mimetype = mime(file.path);

	if (!file.originalname) {
		file.originalname = file.name
			|| (file.path) ? path.parse(file.path).base : 'unnamedfile';
	}

	if (schema.size && !file.size) {
		getSize(file, function (err, size) {
			if (err) return callback(err);
			file.size = size;
			callback(null, file);
		});
	} else callback(null, file);
}

/**
	Uploads a new file via the adapter, then decorates the result with the
	public url for the file. The result is what is saved back to the field.
*/
Storage.prototype.uploadFile = function (file, callback) {
	var self = this;

	// The path to the file is required
	if (!file.path) return callback(Error('Cannot upload file - No source path'));

	normalizeFile(file, this.schema, function (err, file) {
		if (err) return callback(err);
		self.adapter.uploadFile(file, function (err, result) {
			if (err) return callback(err);
			if (this.schema.url && self.adapter.getFileURL) {
				result.url = self.adapter.getFileURL(result);
			}
			callback(null, result);
		});
	});
};

/**
	Removes a stored file by passing the field value to the adapter.
*/
Storage.prototype.removeFile = function (value, callback) {
	this.adapter.removeFile(value, callback);
};

/*
	Built-in Adapters
*/
Storage.Adapters = {};
Storage.Adapters.FS = require('./adapters/fs');
Object.assign(Storage, nameFunctions);

module.exports = Storage;
<commit_msg>Fixing self reference in Storage<commit_after>var assign = require('object-assign');
var fs = require('fs-extra');
var path = require('path');
var mime = require('mime-types');
var nameFunctions = require('./nameFunctions');

var debug = require('debug')('keystone:storage:adapter:fs');

/**
	The Adapter Compatibility Level is used to ensure that provided adapters
	have been designed to work with the version of Storage implemented in the
	version of Keystone being used.

	The Adapter Compatibility Level must be an exact match or an error will be
	thrown when the Storage instance is initialised.
*/
var ADAPTER_COMPATIBILITY_LEVEL = 1;

/**
	This is the default storage schema for file fields. These paths will be
	persisted to the database and represent the value of the file field.

	Storage Adapters can specify additional schema properties.
*/
var SCHEMA_TYPES = {
//	filename: String,       // the filename, without the full path
	size: Number,           // the size of the file
	mimetype: String,       // the mime type of the file
	path: String,           // the path (e.g directory) the file is stored in; not the full path to the file
	originalname: String,   // the original (uploaded) name of the file; useful when filename generated
	url: String,            // publicly accessible URL of the stored file
};

/**
	This is the default set of schema fields that are enabled. The storage
	options can override these (must be supported by the adapter, or the
	adapter must use the default schema implementation)
*/
var SCHEMA_FIELD_DEFAULTS = {
//	filename: true,
	size: true,
	mimetype: true,
	path: false,
	originalname: false,
	url: false,
};

// TODO: We could support custom schema mappings for backwards compatibilty
// with Keystone 0.3 or imported schamas... not sure if it's worth it though.

/**
	# Storage Prototype

	Creates a new Keystone Storage instance, and validates and initialises the
	specified adapter. Storage schema is configured from the adapter or default.
*/
function Storage (options) {
	// we're going to mutate options so get a clean copy of it
	options = assign({}, options);

	var AdapterType = options.adapter;
	delete options.adapter;

	debug('Initialising Storage with adapter ' + AdapterType.name);

	// ensure the adapter compatibilityLevel of the adapter matches
	if (AdapterType.compatibilityLevel !== ADAPTER_COMPATIBILITY_LEVEL) {
		throw new Error('Incompatible Storage Adapter\n\n'
			+ 'The storage adapter specified (' + AdapterType.name + ') '
			+ 'does not match the compatibility level required for this '
			+ 'version of Keystone.');
	}

	// assign ensures the default schema constant isn't exposed as a property
	var schemaFields = assign({}, SCHEMA_FIELD_DEFAULTS, AdapterType.SCHEMA_FIELD_DEFAULTS, options.schema);
	delete options.schema;

	// Copy requested fields into local schema.
	var schema = this.schema = {};
	for (var path in schemaFields) {
		if (!schemaFields[path]) continue;

		var type = AdapterType.SCHEMA_TYPES[path] || SCHEMA_TYPES[path];
		if (!type) throw Error('Unknown type for requested schema field ' + path);
		schema[path] = type;
	}

	// ensure Storage schema features are supported by the Adapter
	if (schema.url && typeof AdapterType.prototype.getFileURL !== 'function') {
		throw Error('URL schema field is not supported by the ' + AdapterType.name + ' adapter');
	}

	// create the adapter
	this.adapter = new AdapterType(options, schema);
}

// Helper function for figuring out the size of a file before uploading it.
function getSize (file, callback) {
	if (file.size) return callback(null, file.size);

	fs.stat(file.path, function (err, stats) {
		if (!stats.isFile()) {
			return callback(Error(file.path + ' is not a file'));
		}

		callback(err, stats ? stats.size : null);
	});
}

// Helper to fill out missing fields in the file object
function normalizeFile (file, schema, callback) {
	// Detect required information if it wasn't provided by inspecting the
	// file stored at file.path
	if (schema.mimetype && !file.mimetype) file.mimetype = mime(file.path);

	if (!file.originalname) {
		file.originalname = file.name
			|| (file.path) ? path.parse(file.path).base : 'unnamedfile';
	}

	if (schema.size && !file.size) {
		getSize(file, function (err, size) {
			if (err) return callback(err);
			file.size = size;
			callback(null, file);
		});
	} else callback(null, file);
}

/**
	Uploads a new file via the adapter, then decorates the result with the
	public url for the file. The result is what is saved back to the field.
*/
Storage.prototype.uploadFile = function (file, callback) {
	var self = this;

	// The path to the file is required
	if (!file.path) return callback(Error('Cannot upload file - No source path'));

	normalizeFile(file, this.schema, function (err, file) {
		if (err) return callback(err);
		self.adapter.uploadFile(file, function (err, result) {
			if (err) return callback(err);
			if (self.schema.url && self.adapter.getFileURL) {
				result.url = self.adapter.getFileURL(result);
			}
			callback(null, result);
		});
	});
};

/**
	Removes a stored file by passing the field value to the adapter.
*/
Storage.prototype.removeFile = function (value, callback) {
	this.adapter.removeFile(value, callback);
};

/*
	Built-in Adapters
*/
Storage.Adapters = {};
Storage.Adapters.FS = require('./adapters/fs');
Object.assign(Storage, nameFunctions);

module.exports = Storage;
<|endoftext|>


<commit_before>var knowledge = require('./knowledge');
var Interval = require('./interval');

var scales = {
  aeolian: ['P1', 'M2', 'm3', 'P4', 'P5', 'm6', 'm7'],
  blues: ['P1', 'm3', 'P4', 'A4', 'P5', 'm7'],
  chromatic: ['P1', 'm2', 'M2', 'm3', 'M3', 'P4', 'A4', 'P5', 'm6', 'M6', 'm7', 'M7'],
  dorian: ['P1', 'M2', 'm3', 'P4', 'P5', 'M6', 'm7'],
  doubleharmonic: ['P1', 'm2', 'M3', 'P4', 'P5', 'm6', 'M7'],
  harmonicminor: ['P1', 'M2', 'm3', 'P4', 'P5', 'm6', 'M7'],
  ionian: ['P1', 'M2', 'M3', 'P4', 'P5', 'M6', 'M7'],
  locrian: ['P1', 'm2', 'm3', 'P4', 'd5', 'm6', 'm7'],
  lydian: ['P1', 'M2', 'M3', 'A4', 'P5', 'M6', 'M7'],
  majorpentatonic: ['P1', 'M2', 'M3', 'P5', 'M6'],
  melodicminor: ['P1', 'M2', 'm3', 'P4', 'P5', 'M6', 'M7'],
  minorpentatonic: ['P1', 'm3', 'P4', 'P5', 'm7'],
  mixolydian: ['P1', 'M2', 'M3', 'P4', 'P5', 'M6', 'm7'],
  phrygian: ['P1', 'm2', 'm3', 'P4', 'P5', 'm6', 'm7']
}

// synonyms
scales.harmonicchromatic = scales.chromatic;
scales.minor = scales.aeolian;
scales.major = scales.ionian;
scales.flamenco = scales.doubleharmonic;

function Scale(tonic, scale) {
  if (!(this instanceof Scale)) return new Scale(tonic, scale);
  var scaleName, i;
  if (!('coord' in tonic)) {
    throw new Error('Invalid Tonic');
  }

  if (typeof scale === 'string') {
    scaleName = scale;
    scale = scales[scale];
    if (!scale)
      throw new Error('Invalid Scale');
  } else {
    for (i in scales) {
      if (scales.hasOwnProperty(i)) {
        if (scales[i].toString() === scale.toString()) {
          scaleName = i;
          break;
        }
      }
    }
  }

  this.name = scaleName;
  this.tonic = tonic;
  this.scale = scale;
}

Scale.prototype = {
  notes: function() {
    var notes = [];

    for (var i = 0, length = this.scale.length; i < length; i++) {
      notes.push(this.tonic.interval(this.scale[i]));
    }

    return notes;
  },

  simple: function() {
    return this.notes().map(function(n) { return n.toString(true); });
  },

  type: function() {
    var length = this.scale.length - 2;
    if (length < 8) {
      return ['di', 'tri', 'tetra', 'penta', 'hexa', 'hepta', 'octa'][length] +
        'tonic';
    }
  },

  get: function(i) {
    i = (typeof i === 'string' && i in knowledge.stepNumber) ? knowledge.stepNumber[i] : i;

    return this.tonic.interval(this.scale[i - 1]);
  },

  solfege: function(index, showOctaves) {
    if (index)
      return this.get(index).solfege(this, showOctaves);

    return this.notes().map(function(n) {
      return n.solfege(this, showOctaves);
    });
  },

  interval: function(interval) {
    interval = (typeof interval === 'string') ?
      Interval.toCoord(interval) : interval;
    return new Scale(this.tonic.interval(interval), this.scale);
  },

  transpose: function(interval) {
    var scale = this.interval(interval);
    this.scale = scale.scale;
    this.tonic = scale.tonic;

    return this;
  }
};

module.exports = Scale;
<commit_msg>fix blues scale<commit_after>var knowledge = require('./knowledge');
var Interval = require('./interval');

var scales = {
  aeolian: ['P1', 'M2', 'm3', 'P4', 'P5', 'm6', 'm7'],
  blues: ['P1', 'm3', 'P4', 'd5', 'P5', 'm7'],
  chromatic: ['P1', 'm2', 'M2', 'm3', 'M3', 'P4', 'A4', 'P5', 'm6', 'M6', 'm7', 'M7'],
  dorian: ['P1', 'M2', 'm3', 'P4', 'P5', 'M6', 'm7'],
  doubleharmonic: ['P1', 'm2', 'M3', 'P4', 'P5', 'm6', 'M7'],
  harmonicminor: ['P1', 'M2', 'm3', 'P4', 'P5', 'm6', 'M7'],
  ionian: ['P1', 'M2', 'M3', 'P4', 'P5', 'M6', 'M7'],
  locrian: ['P1', 'm2', 'm3', 'P4', 'd5', 'm6', 'm7'],
  lydian: ['P1', 'M2', 'M3', 'A4', 'P5', 'M6', 'M7'],
  majorpentatonic: ['P1', 'M2', 'M3', 'P5', 'M6'],
  melodicminor: ['P1', 'M2', 'm3', 'P4', 'P5', 'M6', 'M7'],
  minorpentatonic: ['P1', 'm3', 'P4', 'P5', 'm7'],
  mixolydian: ['P1', 'M2', 'M3', 'P4', 'P5', 'M6', 'm7'],
  phrygian: ['P1', 'm2', 'm3', 'P4', 'P5', 'm6', 'm7']
}

// synonyms
scales.harmonicchromatic = scales.chromatic;
scales.minor = scales.aeolian;
scales.major = scales.ionian;
scales.flamenco = scales.doubleharmonic;

function Scale(tonic, scale) {
  if (!(this instanceof Scale)) return new Scale(tonic, scale);
  var scaleName, i;
  if (!('coord' in tonic)) {
    throw new Error('Invalid Tonic');
  }

  if (typeof scale === 'string') {
    scaleName = scale;
    scale = scales[scale];
    if (!scale)
      throw new Error('Invalid Scale');
  } else {
    for (i in scales) {
      if (scales.hasOwnProperty(i)) {
        if (scales[i].toString() === scale.toString()) {
          scaleName = i;
          break;
        }
      }
    }
  }

  this.name = scaleName;
  this.tonic = tonic;
  this.scale = scale;
}

Scale.prototype = {
  notes: function() {
    var notes = [];

    for (var i = 0, length = this.scale.length; i < length; i++) {
      notes.push(this.tonic.interval(this.scale[i]));
    }

    return notes;
  },

  simple: function() {
    return this.notes().map(function(n) { return n.toString(true); });
  },

  type: function() {
    var length = this.scale.length - 2;
    if (length < 8) {
      return ['di', 'tri', 'tetra', 'penta', 'hexa', 'hepta', 'octa'][length] +
        'tonic';
    }
  },

  get: function(i) {
    i = (typeof i === 'string' && i in knowledge.stepNumber) ? knowledge.stepNumber[i] : i;

    return this.tonic.interval(this.scale[i - 1]);
  },

  solfege: function(index, showOctaves) {
    if (index)
      return this.get(index).solfege(this, showOctaves);

    return this.notes().map(function(n) {
      return n.solfege(this, showOctaves);
    });
  },

  interval: function(interval) {
    interval = (typeof interval === 'string') ?
      Interval.toCoord(interval) : interval;
    return new Scale(this.tonic.interval(interval), this.scale);
  },

  transpose: function(interval) {
    var scale = this.interval(interval);
    this.scale = scale.scale;
    this.tonic = scale.tonic;

    return this;
  }
};

module.exports = Scale;
<|endoftext|>


<commit_before>import {createReadStream} from 'fs';
import {resolve, basename, extname, dirname} from 'path';

import browserify from 'browserify';
import qio from 'q-io/fs';

function scriptRouteFactory (application) {
	const browserifyConfig = application.configuration.assets.browserify || {};

	return async function scriptRoute () {
		let suffix = application.runtime.env === 'development' ? '' : 'bundle';
		let ext = extname(this.params.path).slice(1);

		let filename = [basename(this.params.path, `.${ext}`), suffix, ext]
			.filter((fragment) => fragment)
			.join('.');

		let relative = dirname(this.params.path);
		let path = resolve(application.runtime.cwd, 'assets', 'script', relative, filename);

		if (!await qio.exists(path)) {
			return;
		}

		this.type = 'js';

		try {
			if (application.runtime.env === 'development') {
				let bundler = browserify(path, browserifyConfig);
				this.body = bundler.bundle();
			} else {
				this.body = createReadStream(path);
			}
		} catch(err) {
			application.log.error(err);
			this.throw(err, 500);
		}
	};
}

export default scriptRouteFactory;
<commit_msg>fix: avoid browserify dependency for production<commit_after>import {createReadStream} from 'fs';
import {resolve, basename, extname, dirname} from 'path';

import exists from 'path-exists';

function scriptRouteFactory(application) {
	const browserifyConfig = application.configuration.assets.browserify || {};

	return async function scriptRoute() {
		const suffix = application.runtime.env === 'development' ? '' : 'bundle';
		const ext = extname(this.params.path).slice(1);

		const filename = [basename(this.params.path, `.${ext}`), suffix, ext]
			.filter(Boolean)
			.join('.');

		const relative = dirname(this.params.path);
		const path = resolve(application.runtime.cwd, 'assets', 'script', relative, filename);

		if (!await exists(path)) {
			return;
		}

		this.type = 'js';

		try {
			if (application.runtime.env === 'development') {
				const browserify = require('browserify');
				const bundler = browserify(path, browserifyConfig);
				this.body = bundler.bundle();
			} else {
				this.body = createReadStream(path);
			}
		} catch (err) {
			application.log.error(err);
			this.throw(err, 500);
		}
	};
}

export default scriptRouteFactory;
<|endoftext|>


<commit_before>var common = require('./common');
var strtok = require('strtok');

var MusicMetadata = module.exports = function(stream) {
  this.stream = stream;
  this.parse();
};

MusicMetadata.prototype = new process.EventEmitter();

MusicMetadata.prototype.parse = function() {
  var self = this;

  this.stream.once('data', function(result) {

    //default to id3v1.1 if we cannot detect any other tags
    var tag = 'id3v1';
    if ('ID3' === result.toString('binary', 0, 3)) {
      tag = 'id3v2';
    } else if ('ftypM4A' === result.toString('binary', 4, 11)) {
      tag = 'id4';
    } else if ('OggS' === result.toString('binary', 0, 4)) {
      tag = 'ogg';
    } else if ('fLaC' === result.toString('binary', 0, 4)) {
      tag = 'flac';
    } else if ('MAC' === result.toString('binary', 0, 3)) {
      tag = 'monkeysaudio';
    }

    var module = require('./' + tag);
    var parser = new module(self.stream);

    //hijack the processors emit function so we can see what the event is
    parser.emit = emitClosure(self);

    //re-emitting the same event so the parser
    //picks the stream up from the start
    self.stream.emit('data', result);
  });
};

var emitClosure = function(self) {
  var metadata = { title : '', artist : [], albumartist : [], album : '',
                   year : 0, track : { no : 0, of : 0 }, genre : [],
                   disk : { no : 0, of : 0 }, picture : {} };

  var aliased = {};

  function emit() {
    var event = arguments[0];
    var value = arguments[1];

    //we only emit aliased events once the 'done' event has been raised,
    //this is because an alias like 'artist' could have values split
    //over many data chunks
    if (event === 'done') {
      for (var alias in aliased) {
        if (aliased.hasOwnProperty(alias)) {
          var val;
          if (alias === 'title' || alias === 'album' || alias === 'year') {
            val = aliased[alias][0];
          } else {
            val = aliased[alias];
          }

          self.emit(alias, val);

          if (metadata.hasOwnProperty(alias)) {
            metadata[alias] = val;
          }
        }
      }

      self.emit('metadata', metadata);
      self.emit('done', value);
      return;
    }

    //lookup alias
    var alias;
    for (var i=0; i < MAPPINGS.length; i++) {
      for (var j=0; j < MAPPINGS[i].length; j++) {
        var cur = MAPPINGS[i][j];
        if (cur.toUpperCase() === event.toUpperCase()) {
          alias = MAPPINGS[i][0];
          break;
        }
      }
    }

    //emit original event & value
    if (event !== alias) {
      self.emit(event, value);
    }

    //we need to do something special for these events
    //TODO: parseInt will return NaN for strings
    if (event === 'TRACKTOTAL' || event === 'DISCTOTAL') {
      var evt;
      if (event === 'TRACKTOTAL') evt = 'track';
      if (event === 'DISCTOTAL') evt = 'disk';

      var cleaned = parseInt(value)
      if (!aliased.hasOwnProperty(evt)) {
        aliased[evt] = { no : 0, of : cleaned };
      } else {
        aliased[evt]['of'] = cleaned;
      }
    }

    //if the event has been aliased then we need to clean it before
    //it is emitted to the user. e.g. genre (20) -> Electronic
    if (alias) {
      var cleaned = value;
      if (alias === 'genre') cleaned = common.parseGenre(value);
      if (alias === 'picture') cleaned = cleanupPicture(value);

      if (alias === 'track' || alias === 'disk') {
        cleaned = cleanupTrack(value);

        if (aliased[alias]) {
          aliased[alias].no = cleaned.no;
          return;
        } else {
          aliased[alias] = cleaned;
          return;
        }
      }

      //many tagging libraries use forward slashes to separate artists etc
      //within a string, this code separates those strings into an array
      if (cleaned.constructor === String) {
        //limit to these three aliases, we don't
        //want to be splitting anything else
        if (alias === 'artist' || alias === 'albumartist' || alias === 'genre') {
          cleaned = cleaned.split('/');
          if (cleaned.length === 1) cleaned = cleaned[0];
        }
      }

      //if we haven't previously aliased this tag then
      //initialize it to an array, ready for values to be entered
      if (!aliased.hasOwnProperty(alias)) {
        aliased[alias] = [];
      }

      if (cleaned.constructor === Array) {
        aliased[alias] = cleaned;
      } else {
        aliased[alias].push(cleaned);
      }
    }
  };

  return emit;
};

function cleanupArtist(origVal) {
  return origVal.split('/');
}

//TODO: a string of 1of1 would fail to be converted
//converts 1/10 to no : 1, of : 10
//or 1 to no : 1, of : 0
function cleanupTrack(origVal) {
  var split = origVal.toString().split('/');
  var number = parseInt(split[0], 10) || 0;
  var total = parseInt(split[1], 10) || 0;
  return { no : number, of : total };
}

function cleanupPicture(picture) {
  var newFormat;

  if (picture.format) {
    var split = picture.format.toLowerCase().split('/');
    newFormat = (split.length > 1) ? split[1] : split[0];
    if (newFormat === 'jpeg') newFormat = 'jpg';
  } else {
    newFormat = 'jpg';
  }

  return { format : newFormat, data : picture.data };
}

//mappings for common metadata types(id3v2.3, id3v2.2, id4, vorbis, APEv2)
var MAPPINGS = [
  [ 'title',       'TIT2', 'TT2', '©nam', 'TITLE' ],
  [ 'artist',      'TPE1', 'TP1', '©ART', 'ARTIST' ],
  [ 'albumartist', 'TPE2', 'TP2', 'aART', 'ALBUMARTIST', 'ENSEMBLE' ],
  [ 'album',       'TALB', 'TAL', '©alb', 'ALBUM' ],
  [ 'year',        'TDRC', 'TYER', 'TYE', '©day', 'DATE', 'Year' ],
  [ 'comment',     'COMM', 'COM', '©cmt', 'COMMENT' ],
  [ 'track',       'TRCK', 'TRK', 'trkn', 'TRACKNUMBER', 'Track' ],
  [ 'disk',        'TPOS', 'TPA', 'disk', 'DISCNUMBER', 'Disk' ],
  [ 'genre',       'TCON', 'TCO', '©gen', 'gnre', 'GENRE' ],
  [ 'picture',     'APIC', 'PIC', 'covr', 'METADATA_BLOCK_PICTURE',
                   'Cover Art (Front)', 'Cover Art (Back)' ],
  [ 'composer',    'TCOM', 'TCM', '©wrt', 'COMPOSER' ]
];

strtok.UINT24_BE = {
  len : 3,
  get : function(buf, off) {
    return (((buf[off] << 8) + buf[off + 1]) << 8) + buf[off + 2];
  }
};

strtok.BITSET = {
  len : 1,
  get : function(buf, off, bit) {
    return (buf[off] & (1 << bit)) !== 0;
  }
};

strtok.INT32SYNCSAFE = {
  len : 4,
  get : function(buf, off) {
    return buf[off + 3]   & 0x7f |
         ((buf[off + 2]) << 7)   |
         ((buf[off + 1]) << 14)  |
         ((buf[off])     << 21);
  }
};
<commit_msg>correctly detect mp4 header, fixes #14<commit_after>var common = require('./common');
var strtok = require('strtok');

var MusicMetadata = module.exports = function(stream) {
  this.stream = stream;
  this.parse();
};

MusicMetadata.prototype = new process.EventEmitter();

MusicMetadata.prototype.parse = function() {
  var self = this;

  this.stream.once('data', function(result) {

    //default to id3v1.1 if we cannot detect any other tags
    var tag = 'id3v1';
    if ('ID3' === result.toString('binary', 0, 3)) {
      tag = 'id3v2';
    } else if ('ftypM4A' === result.toString('binary', 4, 11)) {
      tag = 'id4';
    } else if ('ftypmp42' === result.toString('binary', 4, 12)) {
        tag = 'id4'
    } else if ('OggS' === result.toString('binary', 0, 4)) {
      tag = 'ogg';
    } else if ('fLaC' === result.toString('binary', 0, 4)) {
      tag = 'flac';
    } else if ('MAC' === result.toString('binary', 0, 3)) {
      tag = 'monkeysaudio';
    }

    var module = require('./' + tag);
    var parser = new module(self.stream);

    //hijack the processors emit function so we can see what the event is
    parser.emit = emitClosure(self);

    //re-emitting the same event so the parser
    //picks the stream up from the start
    self.stream.emit('data', result);
  });
};

var emitClosure = function(self) {
  var metadata = { title : '', artist : [], albumartist : [], album : '',
                   year : 0, track : { no : 0, of : 0 }, genre : [],
                   disk : { no : 0, of : 0 }, picture : {} };

  var aliased = {};

  function emit() {
    var event = arguments[0];
    var value = arguments[1];

    //we only emit aliased events once the 'done' event has been raised,
    //this is because an alias like 'artist' could have values split
    //over many data chunks
    if (event === 'done') {
      for (var alias in aliased) {
        if (aliased.hasOwnProperty(alias)) {
          var val;
          if (alias === 'title' || alias === 'album' || alias === 'year') {
            val = aliased[alias][0];
          } else {
            val = aliased[alias];
          }

          self.emit(alias, val);

          if (metadata.hasOwnProperty(alias)) {
            metadata[alias] = val;
          }
        }
      }

      self.emit('metadata', metadata);
      self.emit('done', value);
      return;
    }

    //lookup alias
    var alias;
    for (var i=0; i < MAPPINGS.length; i++) {
      for (var j=0; j < MAPPINGS[i].length; j++) {
        var cur = MAPPINGS[i][j];
        if (cur.toUpperCase() === event.toUpperCase()) {
          alias = MAPPINGS[i][0];
          break;
        }
      }
    }

    //emit original event & value
    if (event !== alias) {
      self.emit(event, value);
    }

    //we need to do something special for these events
    //TODO: parseInt will return NaN for strings
    if (event === 'TRACKTOTAL' || event === 'DISCTOTAL') {
      var evt;
      if (event === 'TRACKTOTAL') evt = 'track';
      if (event === 'DISCTOTAL') evt = 'disk';

      var cleaned = parseInt(value)
      if (!aliased.hasOwnProperty(evt)) {
        aliased[evt] = { no : 0, of : cleaned };
      } else {
        aliased[evt]['of'] = cleaned;
      }
    }

    //if the event has been aliased then we need to clean it before
    //it is emitted to the user. e.g. genre (20) -> Electronic
    if (alias) {
      var cleaned = value;
      if (alias === 'genre') cleaned = common.parseGenre(value);
      if (alias === 'picture') cleaned = cleanupPicture(value);

      if (alias === 'track' || alias === 'disk') {
        cleaned = cleanupTrack(value);

        if (aliased[alias]) {
          aliased[alias].no = cleaned.no;
          return;
        } else {
          aliased[alias] = cleaned;
          return;
        }
      }

      //many tagging libraries use forward slashes to separate artists etc
      //within a string, this code separates those strings into an array
      if (cleaned.constructor === String) {
        //limit to these three aliases, we don't
        //want to be splitting anything else
        if (alias === 'artist' || alias === 'albumartist' || alias === 'genre') {
          cleaned = cleaned.split('/');
          if (cleaned.length === 1) cleaned = cleaned[0];
        }
      }

      //if we haven't previously aliased this tag then
      //initialize it to an array, ready for values to be entered
      if (!aliased.hasOwnProperty(alias)) {
        aliased[alias] = [];
      }

      if (cleaned.constructor === Array) {
        aliased[alias] = cleaned;
      } else {
        aliased[alias].push(cleaned);
      }
    }
  };

  return emit;
};

function cleanupArtist(origVal) {
  return origVal.split('/');
}

//TODO: a string of 1of1 would fail to be converted
//converts 1/10 to no : 1, of : 10
//or 1 to no : 1, of : 0
function cleanupTrack(origVal) {
  var split = origVal.toString().split('/');
  var number = parseInt(split[0], 10) || 0;
  var total = parseInt(split[1], 10) || 0;
  return { no : number, of : total };
}

function cleanupPicture(picture) {
  var newFormat;

  if (picture.format) {
    var split = picture.format.toLowerCase().split('/');
    newFormat = (split.length > 1) ? split[1] : split[0];
    if (newFormat === 'jpeg') newFormat = 'jpg';
  } else {
    newFormat = 'jpg';
  }

  return { format : newFormat, data : picture.data };
}

//mappings for common metadata types(id3v2.3, id3v2.2, id4, vorbis, APEv2)
var MAPPINGS = [
  [ 'title',       'TIT2', 'TT2', '©nam', 'TITLE' ],
  [ 'artist',      'TPE1', 'TP1', '©ART', 'ARTIST' ],
  [ 'albumartist', 'TPE2', 'TP2', 'aART', 'ALBUMARTIST', 'ENSEMBLE' ],
  [ 'album',       'TALB', 'TAL', '©alb', 'ALBUM' ],
  [ 'year',        'TDRC', 'TYER', 'TYE', '©day', 'DATE', 'Year' ],
  [ 'comment',     'COMM', 'COM', '©cmt', 'COMMENT' ],
  [ 'track',       'TRCK', 'TRK', 'trkn', 'TRACKNUMBER', 'Track' ],
  [ 'disk',        'TPOS', 'TPA', 'disk', 'DISCNUMBER', 'Disk' ],
  [ 'genre',       'TCON', 'TCO', '©gen', 'gnre', 'GENRE' ],
  [ 'picture',     'APIC', 'PIC', 'covr', 'METADATA_BLOCK_PICTURE',
                   'Cover Art (Front)', 'Cover Art (Back)' ],
  [ 'composer',    'TCOM', 'TCM', '©wrt', 'COMPOSER' ]
];

strtok.UINT24_BE = {
  len : 3,
  get : function(buf, off) {
    return (((buf[off] << 8) + buf[off + 1]) << 8) + buf[off + 2];
  }
};

strtok.BITSET = {
  len : 1,
  get : function(buf, off, bit) {
    return (buf[off] & (1 << bit)) !== 0;
  }
};

strtok.INT32SYNCSAFE = {
  len : 4,
  get : function(buf, off) {
    return buf[off + 3]   & 0x7f |
         ((buf[off + 2]) << 7)   |
         ((buf[off + 1]) << 14)  |
         ((buf[off])     << 21);
  }
};
<|endoftext|>


<commit_before>var _ = require('lodash');

function Shape () {
}

Shape.prototype.canMoveDown = function () {
  return _.every(this.pieces, function(piece) {
    return piece.canMoveDown();
  });
};

Shape.prototype.moveDown = function () {
  if (this.canMoveDown()) {
    _.each(this.pieces, function(piece) { // note: this and
      piece.moveDown();                   // this work without returns
    });
  }
};

Shape.prototype.canMoveLeft = function () {
  return _.every(this.pieces, function(piece) {
    return piece.canMoveLeft();
  });
};

Shape.prototype.moveLeft = function () {
  if (this.canMoveLeft()) {
    _.each(this.pieces, function(piece) {
      piece.moveLeft();
    });
  }
};

Shape.prototype.canMoveRight = function () {
  return _.every(this.pieces, function(piece) {
    return piece.canMoveRight();
  });
};

Shape.prototype.moveRight = function () {
  if (this.canMoveRight()) {
    _.each(this.pieces, function(piece) {
      piece.moveRight();
    });
  }
};

Shape.prototype.canRotate = function () {
  var offset = this.rotationOffsets[this.currentOffset];

  return _.every(this.pieces, function(piece, index) {
    var newX = offset[index][0];
    var newY = offset[index][1];

    return piece.canRotateTo(newX, newY);
  });
};

Shape.prototype.rotate = function () {
  if (this.canRotate()) {
    var offset = this.rotationOffsets[this.currentOffset];

    // Index here represents the current piece it's modifying in the rotationOffsets group
    _.each(this.pieces, function(piece, index) {
      piece.xPos += offset[index][0];
      piece.yPos += offset[index][1];
    });

    // Reset the offset
    this.currentOffset += 1;
    if (this.currentOffset > 4) {
      this.currentOffset = 1;
    }
  }

};

module.exports = Shape;

// Shape.prototype.rotate = function () {
//   var offset = this.rotationOffsets[this.currentOffset];
//   this.pieces[1].xPos += offset[1][0];
//   this.pieces[1].yPos += offset[1][1];

//   this.pieces[2].xPos += offset[2][0];
//   this.pieces[2].yPos += offset[2][1];

//   this.pieces[3].xPos += offset[3][0];
//   this.pieces[3].yPos += offset[3][1];

//   this.pieces[4].xPos += offset[4][0];
//   this.pieces[4].yPos += offset[4][1];
// };<commit_msg>Tests all green<commit_after>var _ = require('lodash');

function Shape () {
}

Shape.prototype.canMoveDown = function () {
  return _.every(this.pieces, function(piece) {
    return piece.canMoveDown();
  });
};

Shape.prototype.moveDown = function () {
  if (this.canMoveDown()) {
    _.each(this.pieces, function(piece) { // note: this and
      piece.moveDown();                   // this work without returns
    });
  }
};

Shape.prototype.canMoveLeft = function () {
  return _.every(this.pieces, function(piece) {
    return piece.canMoveLeft();
  });
};

Shape.prototype.moveLeft = function () {
  if (this.canMoveLeft()) {
    _.each(this.pieces, function(piece) {
      piece.moveLeft();
    });
  }
};

Shape.prototype.canMoveRight = function () {
  return _.every(this.pieces, function(piece) {
    return piece.canMoveRight();
  });
};

Shape.prototype.moveRight = function () {
  if (this.canMoveRight()) {
    _.each(this.pieces, function(piece) {
      piece.moveRight();
    });
  }
};

Shape.prototype.canRotate = function () {
  var offset = this.rotationOffsets[this.currentOffset];

  return _.every(this.pieces, function(piece, index) {
    var newX = offset[index][0];
    var newY = offset[index][1];

    return piece.canRotateTo(newX, newY);
  });
};

Shape.prototype.rotate = function () {
  if (this.canRotate()) {
    var offset = this.rotationOffsets[this.currentOffset];

    // Index here represents the current piece it's modifying in the rotationOffsets group
    _.each(this.pieces, function(piece, index) {
      piece.xPos += offset[index][0];
      piece.yPos += offset[index][1];
    });

    // Reset the offset
    this.currentOffset += 1;
    if (this.currentOffset > 4) {
      this.currentOffset = 1;
    }
  }
};

module.exports = Shape;

// Shape.prototype.rotate = function () {
//   var offset = this.rotationOffsets[this.currentOffset];
//   this.pieces[1].xPos += offset[1][0];
//   this.pieces[1].yPos += offset[1][1];

//   this.pieces[2].xPos += offset[2][0];
//   this.pieces[2].yPos += offset[2][1];

//   this.pieces[3].xPos += offset[3][0];
//   this.pieces[3].yPos += offset[3][1];

//   this.pieces[4].xPos += offset[4][0];
//   this.pieces[4].yPos += offset[4][1];
// };<|endoftext|>


<commit_before>var Scanner = require('./url-scanner'),
    url = require('./url'),
    stylus = require('stylus'),
      nodes = stylus.nodes;

function mergeUrls() {
  new Scanner(this.root, this.imagesSeen).combine();
  return new nodes.Expression(false);
}

function plugin(options) {
  return function(stylus) {
    stylus.define('url', url(options));
    stylus.define('merge-urls', mergeUrls);
  };
}

function init(source, options) {
  var imageOptions = (options || {}).images || {};

  var self = stylus(source, options)
        .use(plugin(imageOptions));
  var _render = self.render;

  self.render = function(fn) {
    _render.call(this, function(err, data) {
      if (!err) {
        var ret = {},
            imagesSeen = self.evaluator.imagesSeen;
        (imageOptions.resolutions || [1]).forEach(function(resolution) {
          ret[resolution] = data.replace(/url\("stylusImages:\/\/(\d+)"\)/g, function(value, id) {
            // Replace image content
            id = parseInt(id, 10);

            var image;
            for (var name in imagesSeen) {
              if (imagesSeen[name].id == id) {
                image = imagesSeen[name];
              }
            }
            if (!image) {
              return value;
            }

            image = url.selectResolution(resolution, image.resolutions);
            return url.output(image, '', imageOptions).val;
          });
        });
        data = ret;
      }
      fn(err, data);
    });
  };
  return self;
}

init.plugin = plugin;

module.exports = exports = init;
<commit_msg>Use res for default image resolution<commit_after>var Scanner = require('./url-scanner'),
    url = require('./url'),
    stylus = require('stylus'),
      nodes = stylus.nodes;

function mergeUrls() {
  new Scanner(this.root, this.imagesSeen).combine();
  return new nodes.Expression(false);
}

function plugin(options) {
  return function(stylus) {
    stylus.define('url', url(options));
    stylus.define('merge-urls', mergeUrls);
  };
}

function init(source, options) {
  var imageOptions = (options || {}).images || {};

  var self = stylus(source, options)
        .use(plugin(imageOptions));
  var _render = self.render;

  self.render = function(fn) {
    _render.call(this, function(err, data) {
      if (!err) {
        var ret = {},
            imagesSeen = self.evaluator.imagesSeen;
        (imageOptions.resolutions || [imageOptions.res || 1]).forEach(function(resolution) {
          ret[resolution] = data.replace(/url\("stylusImages:\/\/(\d+)"\)/g, function(value, id) {
            // Replace image content
            id = parseInt(id, 10);

            var image;
            for (var name in imagesSeen) {
              if (imagesSeen[name].id == id) {
                image = imagesSeen[name];
              }
            }
            if (!image) {
              return value;
            }

            image = url.selectResolution(resolution, image.resolutions);
            return url.output(image, '', imageOptions).val;
          });
        });
        data = ret;
      }
      fn(err, data);
    });
  };
  return self;
}

init.plugin = plugin;

module.exports = exports = init;
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';

import UserStore from '../stores/UserStore';
import MatrixCheck from '../common/MatrixCheck';

const ComputedPropLabel = ({ cprops }) => {
  const currentUser = (UserStore.getState() && UserStore.getState().currentUser) || {};
  const enableComputedProps = MatrixCheck(currentUser.matrix, 'computedProp');
  if (!enableComputedProps) return <span />;

  if (!cprops || cprops.length === 0) return <span />;
  cprops.sort((a, b) => a.updated_at - b.updated_at);
  const { status } = cprops[cprops.length - 1];

  let statusIcon = '';
  const style = {};
  if (status === 'completed') {
    statusIcon = 'fa-calculator';
    style.color = 'green';
  } else if (status === 'in_progress') {
    statusIcon = 'fa-spinner fa-spin';
  }

  return (
    <i className={`fa ${statusIcon}`} style={style} />
  );
};

ComputedPropLabel.propTypes = {
  cprops: PropTypes.arrayOf(PropTypes.object),
};

export default ComputedPropLabel;
<commit_msg>update compute task label styling<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';

import UserStore from '../stores/UserStore';
import MatrixCheck from '../common/MatrixCheck';

const ComputedPropLabel = ({ cprops }) => {
  const currentUser = (UserStore.getState() && UserStore.getState().currentUser) || {};
  const enableComputedProps = MatrixCheck(currentUser.matrix, 'computedProp');
  if (!enableComputedProps) return <span />;

  if (!cprops || cprops.length === 0) return <span />;
  cprops.sort((a, b) => a.updated_at - b.updated_at);
  const { status } = cprops[cprops.length - 1];

  let statusIcon = '';
  const style = {};
  if (status === 'success') {
    statusIcon = 'fa-calculator';
    style.color = 'green';
  } else if (status === 'pending') {
    statusIcon = 'fa-ellipsis-h';
    style.color = '#f0ad4e';
  } else if (status === 'started') {
    statusIcon = 'fa-spinner fa-spin';
    style.color = '#5bc0de';
  }

  return (
    <i className={`fa ${statusIcon}`} style={style} />
  );
};

ComputedPropLabel.propTypes = {
  cprops: PropTypes.arrayOf(PropTypes.object),
};

export default ComputedPropLabel;
<|endoftext|>