<commit_before>'use strict';

const qs       = require('querystring');
const url      = require('url');
const Entities = require('html-entities').AllHtmlEntities;
const FORMATS  = require('./formats');

const VIDEO_URL = 'https://www.youtube.com/watch?v=';

/**
 * Parses a string representation of amount of milliseconds.
 *
 * @param {String} time
 * @return {Number}
 */
var timeRegexp = /(?:(\d+)h)?(?:(\d+)m(?!s))?(?:(\d+)s)?(?:(\d+)(?:ms)?)?/;
exports.parseTime = (time) => {
  var result = timeRegexp.exec(time.toString());
  var hours  = result[1] || 0;
  var mins   = result[2] || 0;
  var secs   = result[3] || 0;
  var ms     = result[4] || 0;

  return hours * 3600000 + mins * 60000 + secs * 1000 + parseInt(ms, 10);
};

// Use these to help sort formats, higher is better.
var audioEncodingRanks = {
  mp3: 1,
  vorbis: 2,
  aac: 3,
  opus: 4,
  flac: 5,
};
var videoEncodingRanks = {
  'Sorenson H.283': 1,
  'MPEG-4 Visual': 2,
  'VP8': 3,
  'VP9': 4,
  'H.264': 5,
};

/**
 * Sort formats from highest quality to lowest.
 * By resolution, then video bitrate, then audio bitrate.
 *
 * @param {Object} a
 * @param {Object} b
 */
exports.sortFormats = (a, b) => {
  var ares = a.resolution ? parseInt(a.resolution.slice(0, -1), 10) : 0;
  var bres = b.resolution ? parseInt(b.resolution.slice(0, -1), 10) : 0;
  var afeats = ~~!!ares * 2 + ~~!!a.audioBitrate;
  var bfeats = ~~!!bres * 2 + ~~!!b.audioBitrate;

  function getBitrate(c) {
    if (c.bitrate) {
      let s = c.bitrate.split('-');
      return parseFloat(s[s.length - 1], 10);
    } else {
      return 0;
    }
  }

  function audioScore(c) {
    var abitrate = c.audioBitrate || 0;
    var aenc = audioEncodingRanks[c.audioEncoding] || 0;
    return abitrate + aenc / 10;
  }

  if (afeats === bfeats) {
    if (ares === bres) {
      let avbitrate = getBitrate(a);
      let bvbitrate = getBitrate(b);
      if (avbitrate === bvbitrate) {
        let aascore = audioScore(a);
        let bascore = audioScore(b);
        if (aascore === bascore) {
          let avenc = videoEncodingRanks[a.encoding] || 0;
          let bvenc = videoEncodingRanks[b.encoding] || 0;
          return bvenc - avenc;
        } else {
          return bascore - aascore;
        }
      } else {
        return bvbitrate - avbitrate;
      }
    } else {
      return bres - ares;
    }
  } else {
    return bfeats - afeats;
  }
};

/**
 * Choose a format depending on the given options.
 *
 * @param {Array.<Object>} formats
 * @param {Object} options
 * @return {Object|Error}
 */
exports.chooseFormat = (formats, options) => {
  if (typeof options.format === 'object') {
    return options.format;
  }

  if (options.filter) {
    formats = exports.filterFormats(formats, options.filter);
    if (formats.length === 0) {
      return new Error('No formats found with custom filter');
    }
  }

  var format;
  var quality = options.quality || 'highest';
  switch (quality) {
    case 'highest':
      format = formats[0];
      break;

    case 'lowest':
      format = formats[formats.length - 1];
      break;

    case 'highestaudio':
      formats = exports.filterFormats(formats, 'audio');
      format = null;
      for (let f of formats) {
        if (!format
          || f.audioBitrate > format.audioBitrate
          || (f.audioBitrate === format.audioBitrate && format.encoding && !f.encoding))
            format = f;
      }
      break;

    case 'highestvideo':
      function getBitrate(f) {
        let s = f.bitrate.split('-');
        return parseFloat(s[s.length - 1], 10);
      }
      formats = exports.filterFormats(formats, 'video');
      format = null;
      for (let f of formats) {
        if (!format
          || getBitrate(f) > getBitrate(format)
          || (getBitrate(f) === getBitrate(format) && format.audioEncoding && !f.audioEncoding))
            format = f;
      }
      break;

    default: {
      let getFormat = (itag) => {
        return formats.find((format) => format.itag === '' + itag);
      };
      if (Array.isArray(quality)) {
        quality.find((q) => format = getFormat(q));
      } else {
        format = getFormat(quality);
      }
    }

  }

  if (!format) {
    return new Error('No such format found: ' + quality);
  }
  return format;
};

/**
 * @param {Array.<Object>} formats
 * @param {Function} filter
 * @return {Array.<Object>}
 */
exports.filterFormats = (formats, filter) => {
  var fn;
  switch (filter) {
    case 'audioandvideo':
      fn = (format) => format.bitrate && format.audioBitrate;
      break;

    case 'video':
      fn = (format) => format.bitrate;
      break;

    case 'videoonly':
      fn = (format) => format.bitrate && !format.audioBitrate;
      break;

    case 'audio':
      fn = (format) => format.audioBitrate;
      break;

    case 'audioonly':
      fn = (format) => !format.bitrate && format.audioBitrate;
      break;

    default:
      if (typeof filter === 'function') {
        fn = filter;
      } else {
        throw new TypeError(`Given filter (${filter}) is not supported`);
      }
  }
  return formats.filter(fn);
};

/**
 * Extract string inbetween another.
 *
 * @param {String} haystack
 * @param {String} left
 * @param {String} right
 * @return {String}
 */
exports.between = (haystack, left, right) => {
  var pos;
  pos = haystack.indexOf(left);
  if (pos === -1) { return ''; }
  haystack = haystack.slice(pos + left.length);
  pos = haystack.indexOf(right);
  if (pos === -1) { return ''; }
  haystack = haystack.slice(0, pos);
  return haystack;
};

/**
 * Get video ID.
 *
 * There are a few type of video URL formats.
 *  - http://www.youtube.com/watch?v=VIDEO_ID
 *  - http://m.youtube.com/watch?v=VIDEO_ID
 *  - http://youtu.be/VIDEO_ID
 *  - http://www.youtube.com/v/VIDEO_ID
 *  - http://www.youtube.com/embed/VIDEO_ID
 *
 * @param {String} link
 * @return {String|Error}
 */
exports.getURLVideoID = function(link) {
  var parsed = url.parse(link, true);
  var id = parsed.query.v;
  if (parsed.hostname === 'youtu.be' ||
     (parsed.hostname === 'youtube.com' ||
      parsed.hostname === 'www.youtube.com') && !id) {
    var s = parsed.pathname.split('/');
    id = s[s.length - 1];
  }
  if (!id) {
    return new Error('No video id found: ' + link);
  }
  id = id.substring(0, 11);
  if (!exports.validateID(id)) {
    return new TypeError(`Video id (${id}) does not match expected ` +
      `format (${idRegex.toString()})`);
  }
  return id;
};

/**
 * Gets video ID either from a url or by checking if the given string
 * matches the video ID format.
 *
 * @param {String} str
 * @return {String|Error}
 */
exports.getVideoID = (str) => {
  if (exports.validateID(str)) {
    return str;
  } else {
    return exports.getURLVideoID(str);
  }
};

/**
 * Returns true if given id satifies YouTube's id format.
 *
 * @param {String} id
 * @return {Boolean}
 */
var idRegex = /^[a-zA-Z0-9-_]{11}$/;
exports.validateID = (id) => {
  return idRegex.test(id);
};

/**
 * Checks wether the input string includes a valid id.
 *
 * @param {String} string
 * @return {Boolean}
 */
exports.validateURL = (string) => {
  return !(exports.getURLVideoID(string) instanceof Error);
};

/**
 * @param {Object} info
 * @return {Array.<Object>}
 */
exports.parseFormats = (info) => {
  var formats = [];
  if (info.url_encoded_fmt_stream_map) {
    formats = formats
      .concat(info.url_encoded_fmt_stream_map.split(','));
  }
  if (info.adaptive_fmts) {
    formats = formats.concat(info.adaptive_fmts.split(','));
  }

  formats = formats.map((format) => qs.parse(format));
  delete info.url_encoded_fmt_stream_map;
  delete info.adaptive_fmts;

  return formats;
};

/**
 * @param {Object} format
 */
exports.addFormatMeta = (format) => {
  var meta = FORMATS[format.itag];
  for (let key in meta) {
    format[key] = meta[key];
  }

  if (/\/live\/1\//.test(format.url)) {
    format.live = true;
  }
};

/**
 * Get only the string from an HTML string.
 *
 * @param {String} html
 * @return {String}
 */
exports.stripHTML = (html) => {
  return html
    .replace(/\n/g, ' ')
    .replace(/\s*<\s*br\s*\/?\s*>\s*/gi, '\n')
    .replace(/<\s*\/\s*p\s*>\s*<\s*p[^>]*>/gi, '\n')
    .replace(/<.*?>/gi, '')
    .trim();
};

/**
 * Get video description from html
 *
 * @param {String} html
 * @return {String}
 */
exports.getVideoDescription = (html) => {
  var regex = /
(.+?)<\/p>[\n\r\s]*?<\/div>/im;
  var description = html.match(regex);
  return description ?
    new Entities().decode(exports.stripHTML(description[1])) : '';
};

/**
 * Get video Owner from html.
 *
 * @param {String} body
 * @return {Object}
 */
var authorRegexp = /]+>(.+?(?=<\/a>))/;
var aliasRegExp = / {
  var ownerinfo = exports.between(body,
    '
',
    '
');
  if (ownerinfo === '') {
    return {};
  }
  ownerinfo = new Entities().decode(ownerinfo);
  var channelMatch = ownerinfo.match(authorRegexp);
  var userMatch = ownerinfo.match(aliasRegExp);
  return {
    id: channelMatch[1],
    name: channelMatch[2],
    avatar: url.resolve(VIDEO_URL, exports.between(ownerinfo,
      'data-thumb="', '"')),
    user: userMatch ? userMatch[1] : null,
    channel_url: 'https://www.youtube.com/channel/' + channelMatch[1],
    user_url: userMatch ? 'https://www.youtube.com/user/' + userMatch[1] : null,
  };
};

/**
 * Get video published at from html.
 *
 * @param {String} body
 * @return {String}
 */
exports.getPublished = (body) => {
  return Date.parse(exports.between(body,
    '<meta itemprop="datePublished" content="', '">'));
};

/**
 * Get video published at from html.
 * Credits to https://github.com/paixaop.
 *
 * @param {String} body
 * @return {Array.<Object>}
 */
exports.getRelatedVideos = (body) => {
  var jsonStr = exports.between(body, '\'RELATED_PLAYER_ARGS\': {"rvs":', '},');
  try {
    jsonStr = JSON.parse(jsonStr);
  } catch (err) {
    return [];
  }
  return jsonStr.split(',').map((link) => qs.parse(link));
};

/**
 * @param {Array.<Function>} funcs
 * @param {Function(!Error, Array.<Object>)} callback
 */
exports.parallel = (funcs, callback) => {
  var funcsDone = 0;
  var len = funcs.length;
  var errGiven = false;
  var results = [];

  function checkDone(index, err, result) {
    if (errGiven) { return; }
    if (err) {
      errGiven = true;
      callback(err);
      return;
    }
    results[index] = result;
    if (++funcsDone === len) {
      callback(null, results);
    }
  }

  if (len > 0) {
    funcs.forEach((f, i) => { f(checkDone.bind(null, i)); });
  } else {
    callback(null, results);
  }
};

/**
 * Converts human friendly time to milliseconds. Supports the format
 * 00:00:00.000 for hours, minutes, seconds, and milliseconds respectively.
 * And 0ms, 0s, 0m, 0h, and together 1m1s.
 *
 * @param {String|Number} time
 * @return {Number}
 */
var numberFormat = /^\d+$/;
var timeFormat = /^(?:(?:(\d+):)?(\d{1,2}):)?(\d{1,2})(?:\.(\d{3}))?$/;
var timeUnits = {
  ms: 1,
  s: 1000,
  m: 60000,
  h: 3600000,
};
exports.fromHumanTime = (time) => {
  if (typeof time === 'number') { return time; }
  if (numberFormat.test(time)) { return +time; }
  var firstFormat = timeFormat.exec(time);
  if (firstFormat) {
    return +(firstFormat[1] || 0) * timeUnits.h +
      +(firstFormat[2] || 0) * timeUnits.m +
      +firstFormat[3] * timeUnits.s +
      +(firstFormat[4] || 0);
  } else {
    var total = 0;
    var r = /(\d+)(ms|s|m|h)/g;
    var rs;
    while ((rs = r.exec(time)) != null) {
      total += +rs[1] * timeUnits[rs[2]];
    }
    return total;
  }
};
<commit_msg>fix(react native): Move getBitrate func to fix crash in react native (#290)<commit_after>'use strict';

const qs       = require('querystring');
const url      = require('url');
const Entities = require('html-entities').AllHtmlEntities;
const FORMATS  = require('./formats');

const VIDEO_URL = 'https://www.youtube.com/watch?v=';

/**
 * Parses a string representation of amount of milliseconds.
 *
 * @param {String} time
 * @return {Number}
 */
var timeRegexp = /(?:(\d+)h)?(?:(\d+)m(?!s))?(?:(\d+)s)?(?:(\d+)(?:ms)?)?/;
exports.parseTime = (time) => {
  var result = timeRegexp.exec(time.toString());
  var hours  = result[1] || 0;
  var mins   = result[2] || 0;
  var secs   = result[3] || 0;
  var ms     = result[4] || 0;

  return hours * 3600000 + mins * 60000 + secs * 1000 + parseInt(ms, 10);
};

// Use these to help sort formats, higher is better.
var audioEncodingRanks = {
  mp3: 1,
  vorbis: 2,
  aac: 3,
  opus: 4,
  flac: 5,
};
var videoEncodingRanks = {
  'Sorenson H.283': 1,
  'MPEG-4 Visual': 2,
  'VP8': 3,
  'VP9': 4,
  'H.264': 5,
};

/**
 * Sort formats from highest quality to lowest.
 * By resolution, then video bitrate, then audio bitrate.
 *
 * @param {Object} a
 * @param {Object} b
 */
exports.sortFormats = (a, b) => {
  var ares = a.resolution ? parseInt(a.resolution.slice(0, -1), 10) : 0;
  var bres = b.resolution ? parseInt(b.resolution.slice(0, -1), 10) : 0;
  var afeats = ~~!!ares * 2 + ~~!!a.audioBitrate;
  var bfeats = ~~!!bres * 2 + ~~!!b.audioBitrate;

  function getBitrate(c) {
    if (c.bitrate) {
      let s = c.bitrate.split('-');
      return parseFloat(s[s.length - 1], 10);
    } else {
      return 0;
    }
  }

  function audioScore(c) {
    var abitrate = c.audioBitrate || 0;
    var aenc = audioEncodingRanks[c.audioEncoding] || 0;
    return abitrate + aenc / 10;
  }

  if (afeats === bfeats) {
    if (ares === bres) {
      let avbitrate = getBitrate(a);
      let bvbitrate = getBitrate(b);
      if (avbitrate === bvbitrate) {
        let aascore = audioScore(a);
        let bascore = audioScore(b);
        if (aascore === bascore) {
          let avenc = videoEncodingRanks[a.encoding] || 0;
          let bvenc = videoEncodingRanks[b.encoding] || 0;
          return bvenc - avenc;
        } else {
          return bascore - aascore;
        }
      } else {
        return bvbitrate - avbitrate;
      }
    } else {
      return bres - ares;
    }
  } else {
    return bfeats - afeats;
  }
};

/**
 * Choose a format depending on the given options.
 *
 * @param {Array.<Object>} formats
 * @param {Object} options
 * @return {Object|Error}
 */
exports.chooseFormat = (formats, options) => {
  if (typeof options.format === 'object') {
    return options.format;
  }

  if (options.filter) {
    formats = exports.filterFormats(formats, options.filter);
    if (formats.length === 0) {
      return new Error('No formats found with custom filter');
    }
  }

  var format;
  var quality = options.quality || 'highest';
  function getBitrate(f) {
    let s = f.bitrate.split('-');
    return parseFloat(s[s.length - 1], 10);
  }
  switch (quality) {
    case 'highest':
      format = formats[0];
      break;

    case 'lowest':
      format = formats[formats.length - 1];
      break;

    case 'highestaudio':
      formats = exports.filterFormats(formats, 'audio');
      format = null;
      for (let f of formats) {
        if (!format
          || f.audioBitrate > format.audioBitrate
          || (f.audioBitrate === format.audioBitrate && format.encoding && !f.encoding))
            format = f;
      }
      break;

    case 'highestvideo':
      formats = exports.filterFormats(formats, 'video');
      format = null;
      for (let f of formats) {
        if (!format
          || getBitrate(f) > getBitrate(format)
          || (getBitrate(f) === getBitrate(format) && format.audioEncoding && !f.audioEncoding))
            format = f;
      }
      break;

    default: {
      let getFormat = (itag) => {
        return formats.find((format) => format.itag === '' + itag);
      };
      if (Array.isArray(quality)) {
        quality.find((q) => format = getFormat(q));
      } else {
        format = getFormat(quality);
      }
    }

  }

  if (!format) {
    return new Error('No such format found: ' + quality);
  }
  return format;
};

/**
 * @param {Array.<Object>} formats
 * @param {Function} filter
 * @return {Array.<Object>}
 */
exports.filterFormats = (formats, filter) => {
  var fn;
  switch (filter) {
    case 'audioandvideo':
      fn = (format) => format.bitrate && format.audioBitrate;
      break;

    case 'video':
      fn = (format) => format.bitrate;
      break;

    case 'videoonly':
      fn = (format) => format.bitrate && !format.audioBitrate;
      break;

    case 'audio':
      fn = (format) => format.audioBitrate;
      break;

    case 'audioonly':
      fn = (format) => !format.bitrate && format.audioBitrate;
      break;

    default:
      if (typeof filter === 'function') {
        fn = filter;
      } else {
        throw new TypeError(`Given filter (${filter}) is not supported`);
      }
  }
  return formats.filter(fn);
};

/**
 * Extract string inbetween another.
 *
 * @param {String} haystack
 * @param {String} left
 * @param {String} right
 * @return {String}
 */
exports.between = (haystack, left, right) => {
  var pos;
  pos = haystack.indexOf(left);
  if (pos === -1) { return ''; }
  haystack = haystack.slice(pos + left.length);
  pos = haystack.indexOf(right);
  if (pos === -1) { return ''; }
  haystack = haystack.slice(0, pos);
  return haystack;
};

/**
 * Get video ID.
 *
 * There are a few type of video URL formats.
 *  - http://www.youtube.com/watch?v=VIDEO_ID
 *  - http://m.youtube.com/watch?v=VIDEO_ID
 *  - http://youtu.be/VIDEO_ID
 *  - http://www.youtube.com/v/VIDEO_ID
 *  - http://www.youtube.com/embed/VIDEO_ID
 *
 * @param {String} link
 * @return {String|Error}
 */
exports.getURLVideoID = function(link) {
  var parsed = url.parse(link, true);
  var id = parsed.query.v;
  if (parsed.hostname === 'youtu.be' ||
     (parsed.hostname === 'youtube.com' ||
      parsed.hostname === 'www.youtube.com') && !id) {
    var s = parsed.pathname.split('/');
    id = s[s.length - 1];
  }
  if (!id) {
    return new Error('No video id found: ' + link);
  }
  id = id.substring(0, 11);
  if (!exports.validateID(id)) {
    return new TypeError(`Video id (${id}) does not match expected ` +
      `format (${idRegex.toString()})`);
  }
  return id;
};

/**
 * Gets video ID either from a url or by checking if the given string
 * matches the video ID format.
 *
 * @param {String} str
 * @return {String|Error}
 */
exports.getVideoID = (str) => {
  if (exports.validateID(str)) {
    return str;
  } else {
    return exports.getURLVideoID(str);
  }
};

/**
 * Returns true if given id satifies YouTube's id format.
 *
 * @param {String} id
 * @return {Boolean}
 */
var idRegex = /^[a-zA-Z0-9-_]{11}$/;
exports.validateID = (id) => {
  return idRegex.test(id);
};

/**
 * Checks wether the input string includes a valid id.
 *
 * @param {String} string
 * @return {Boolean}
 */
exports.validateURL = (string) => {
  return !(exports.getURLVideoID(string) instanceof Error);
};

/**
 * @param {Object} info
 * @return {Array.<Object>}
 */
exports.parseFormats = (info) => {
  var formats = [];
  if (info.url_encoded_fmt_stream_map) {
    formats = formats
      .concat(info.url_encoded_fmt_stream_map.split(','));
  }
  if (info.adaptive_fmts) {
    formats = formats.concat(info.adaptive_fmts.split(','));
  }

  formats = formats.map((format) => qs.parse(format));
  delete info.url_encoded_fmt_stream_map;
  delete info.adaptive_fmts;

  return formats;
};

/**
 * @param {Object} format
 */
exports.addFormatMeta = (format) => {
  var meta = FORMATS[format.itag];
  for (let key in meta) {
    format[key] = meta[key];
  }

  if (/\/live\/1\//.test(format.url)) {
    format.live = true;
  }
};

/**
 * Get only the string from an HTML string.
 *
 * @param {String} html
 * @return {String}
 */
exports.stripHTML = (html) => {
  return html
    .replace(/\n/g, ' ')
    .replace(/\s*<\s*br\s*\/?\s*>\s*/gi, '\n')
    .replace(/<\s*\/\s*p\s*>\s*<\s*p[^>]*>/gi, '\n')
    .replace(/<.*?>/gi, '')
    .trim();
};

/**
 * Get video description from html
 *
 * @param {String} html
 * @return {String}
 */
exports.getVideoDescription = (html) => {
  var regex = /
(.+?)<\/p>[\n\r\s]*?<\/div>/im;
  var description = html.match(regex);
  return description ?
    new Entities().decode(exports.stripHTML(description[1])) : '';
};

/**
 * Get video Owner from html.
 *
 * @param {String} body
 * @return {Object}
 */
var authorRegexp = /]+>(.+?(?=<\/a>))/;
var aliasRegExp = / {
  var ownerinfo = exports.between(body,
    '
',
    '
');
  if (ownerinfo === '') {
    return {};
  }
  ownerinfo = new Entities().decode(ownerinfo);
  var channelMatch = ownerinfo.match(authorRegexp);
  var userMatch = ownerinfo.match(aliasRegExp);
  return {
    id: channelMatch[1],
    name: channelMatch[2],
    avatar: url.resolve(VIDEO_URL, exports.between(ownerinfo,
      'data-thumb="', '"')),
    user: userMatch ? userMatch[1] : null,
    channel_url: 'https://www.youtube.com/channel/' + channelMatch[1],
    user_url: userMatch ? 'https://www.youtube.com/user/' + userMatch[1] : null,
  };
};

/**
 * Get video published at from html.
 *
 * @param {String} body
 * @return {String}
 */
exports.getPublished = (body) => {
  return Date.parse(exports.between(body,
    '<meta itemprop="datePublished" content="', '">'));
};

/**
 * Get video published at from html.
 * Credits to https://github.com/paixaop.
 *
 * @param {String} body
 * @return {Array.<Object>}
 */
exports.getRelatedVideos = (body) => {
  var jsonStr = exports.between(body, '\'RELATED_PLAYER_ARGS\': {"rvs":', '},');
  try {
    jsonStr = JSON.parse(jsonStr);
  } catch (err) {
    return [];
  }
  return jsonStr.split(',').map((link) => qs.parse(link));
};

/**
 * @param {Array.<Function>} funcs
 * @param {Function(!Error, Array.<Object>)} callback
 */
exports.parallel = (funcs, callback) => {
  var funcsDone = 0;
  var len = funcs.length;
  var errGiven = false;
  var results = [];

  function checkDone(index, err, result) {
    if (errGiven) { return; }
    if (err) {
      errGiven = true;
      callback(err);
      return;
    }
    results[index] = result;
    if (++funcsDone === len) {
      callback(null, results);
    }
  }

  if (len > 0) {
    funcs.forEach((f, i) => { f(checkDone.bind(null, i)); });
  } else {
    callback(null, results);
  }
};

/**
 * Converts human friendly time to milliseconds. Supports the format
 * 00:00:00.000 for hours, minutes, seconds, and milliseconds respectively.
 * And 0ms, 0s, 0m, 0h, and together 1m1s.
 *
 * @param {String|Number} time
 * @return {Number}
 */
var numberFormat = /^\d+$/;
var timeFormat = /^(?:(?:(\d+):)?(\d{1,2}):)?(\d{1,2})(?:\.(\d{3}))?$/;
var timeUnits = {
  ms: 1,
  s: 1000,
  m: 60000,
  h: 3600000,
};
exports.fromHumanTime = (time) => {
  if (typeof time === 'number') { return time; }
  if (numberFormat.test(time)) { return +time; }
  var firstFormat = timeFormat.exec(time);
  if (firstFormat) {
    return +(firstFormat[1] || 0) * timeUnits.h +
      +(firstFormat[2] || 0) * timeUnits.m +
      +firstFormat[3] * timeUnits.s +
      +(firstFormat[4] || 0);
  } else {
    var total = 0;
    var r = /(\d+)(ms|s|m|h)/g;
    var rs;
    while ((rs = r.exec(time)) != null) {
      total += +rs[1] * timeUnits[rs[2]];
    }
    return total;
  }
};
<|endoftext|>


<commit_before>exports.config = {
  allScriptsTimeout: 11000,

  seleniumAddress: 'http://159.203.127.94:4444/wd/hub',

  specs: [
    '*.js'
  ],

  capabilities: {
    'browserName': process.env.SELENIUM_BROWSER
  },

  baseUrl: 'http://159.203.127.94:8000/app/',

  params: {
	  apiHostname: '45.55.142.115',
	  apiBasePath: '/recipe/api',
	  apiPort: 8080,
	  apiBaseUrl: 'http://45.55.142.115:8080/recipe/api'
  },

  framework: 'jasmine2',

  jasmineNodeOpts: {
    defaultTimeoutInterval: 5000
  }
};
<commit_msg>parameterizing the ci protractor run to use env vars for the ip of the apps being tested<commit_after>exports.config = {
  allScriptsTimeout: 11000,

  seleniumAddress: 'http://' + process.env.WEB_IP + ':4444/wd/hub',

  specs: [
    '*.js'
  ],

  capabilities: {
    'browserName': process.env.SELENIUM_BROWSER
  },

  baseUrl: 'http://' + process.env.WEB_IP + ':8000/app/',

  params: {
	  apiHostname: process.env.SERVICE_IP,
	  apiBasePath: '/recipe/api',
	  apiPort: 8080,
	  apiBaseUrl: 'http://' + process.env.SERVICE_IP + ':8080/recipe/api'
  },

  framework: 'jasmine2',

  jasmineNodeOpts: {
    defaultTimeoutInterval: 5000
  }
};
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import { Link } from 'react-router'
import classNames from 'classnames'
import { before, css, hover, media, select } from '../../styles/jss'
import * as s from '../../styles/jso'

const backgroundStyles = source => ({
  backgroundImage: `url("${source}")`,
})

// single tab style
const categoryTabStyle = css(
  s.relative,
  s.inlineBlock,
  s.fullWidth,
  s.fullHeight,
  s.pt0,
  s.pb0,
  s.pr10,
  s.pl10,
  s.overflowHidden,
  s.alignTop,
  s.bgcBlack,
  s.transitionColor,
  {
    whiteSpace: 'normal',
    backgroundSize: 'cover',
  },

  before(
    s.absolute,
    s.flood,
    s.zIndex1,
    s.transitionBgColor,
    {
      content: '""',
      backgroundColor: 'rgba(0, 0, 0, 0.5)',
    },
  ),

  select('& + &',
    {
      borderLeft: '1px solid #fff',
    },
  ),

  // text label
  select('& .text-label',
    s.relative,
    s.flex,
    s.justifyCenter,
    s.itemsCenter,
    s.fullWidth,
    s.fullHeight,
    s.center,
    s.zIndex2,
  ),

  // hover / active states
  select('&.isActive',
    select('& .text-label', s.sansBold, { textDecoration: 'underline' }),
    before({ backgroundColor: 'rgba(0, 0, 0, 0.8)', }),
  ),
  select('&:active',
    select('& .text-label', s.sansBold, { textDecoration: 'underline' }),
    before({ backgroundColor: 'rgba(0, 0, 0, 0.8)', }),
  ),
  select('.no-touch &:hover',
    before({ backgroundColor: 'rgba(0, 0, 0, 0.8)', }),
  ),

  // mini
  select('&.mini',
    {
      width: 120,
    },
  ),
  select('&.mini.all', {
    background: 'linear-gradient(50deg, #ff00ff, #1925ff)',
  }),
  select('&.mini.subscribed', {
    background: 'linear-gradient(45deg, #6E00FF, #0054FF, #007BF1, #00BEFF)',
  }),

  media(s.minBreak2,
    {
      width: 120,
    },
  ),
  media(s.minBreak4,
    s.fullWidth,
    {
      minWidth: 120,
    },
  ),
)

const CategoryTab = ({ isActive, label, source, to }) => (
  <Link
    className={classNames({ isActive }, `${categoryTabStyle}`)}
    to={to}
    style={source ? backgroundStyles(source) : null}
  >
    {label}
  </Link>
)
CategoryTab.propTypes = {
  isActive: PropTypes.bool.isRequired,
  label: PropTypes.string.isRequired,
  source: PropTypes.string.isRequired,
  to: PropTypes.string.isRequired,
}

const AllCategoryTab = ({ pathname }) => {
  const isActive = pathname === '/discover' ||
    pathname === '/discover/trending' ||
    pathname === '/discover/recent'
  return (
    <Link
      className={classNames('mini all', { isActive }, `${categoryTabStyle}`)}
      to="/discover"
    >
      All
    </Link>
  )
}
AllCategoryTab.propTypes = {
  pathname: PropTypes.string.isRequired,
}

const SubscribedCategoryTab = ({ pathname }) => {
  const isActive = pathname === '/discover/subscribed' ||
    pathname === '/discover/subscribed/trending' ||
    pathname === '/discover/subscribed/recent'
  return (
    <Link
      className={classNames('mini subscribed', { isActive }, `${categoryTabStyle}`)}
      to="/discover/subscribed"
    >
      Subscribed
    </Link>
  )
}
SubscribedCategoryTab.propTypes = {
  pathname: PropTypes.string.isRequired,
}

const SubscribedZeroStateTab = () => (
  <Link
    className={categoryTabStyle}
    to="/discover/all"
  >
    Find and subscribe to a community
  </Link>
)

// main tab bar style
const categoryTabBarStyle = css(
  s.absolute,
  s.zIndex2,
  s.fontSize12,
  s.colorWhite,
  s.nowrap,
  s.bgcBlack,
  s.borderBottom,
  {
    right: 0,
    bottom: -70,
    left: 0,
    height: 70,
    borderColor: '#fff',
  },
  media('(max-width: 23.375em)', // 374 / 16 = 23.375em
    {
      bottom: -50,
      height: 50,
    },
  ),
  media(s.maxBreak2,
    select('.isProfileMenuActive .Navbar ~ &',
      s.displayNone,
    ),
  ),
  media(s.minBreak2,
    s.fontSize14,
    {
      bottom: -80,
      height: 80,
    },
  ),
)

// nav tag that holds all of the tabs style
const categoryTabsHolderStyle = css(
  s.relative,
  s.inlineBlock,
  s.fullHeight,
  s.overflowScrollWebX,
  s.nowrap,
  s.alignTop,
  {
    width: 'calc(100% - 70px)',
  },
  media(s.minBreak2,
    s.fontSize14,
    {
      width: 'calc(100% - 90px)',
    },
  ),
  media(s.minBreak4,
    s.flex,
  ),
)

// tab bar tools style
const categoryTabBarToolsStyle = css(
  s.relative,
  s.inlineBlock,
  s.fullHeight,
  s.pr10,
  s.rightAlign,
  s.alignTop,
  s.bgcBlack,
  {
    width: 70,
  },

  before(
    s.absolute,
    s.zIndex2,
    s.wv20,
    s.fullHeight,
    {
      content: '""',
      top: 0,
      left: -20,
      background: 'linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 90%)'
    },
  ),

  media(s.minBreak2,
    s.pr20,
    {
      width: '90px',
    },
    before(
      s.wv30,
      {
        left: -30,
      },
    ),
  ),
  media(s.minBreak4,
    s.absolute,
    s.pr40,
    {
      top: 0,
      right: 0,
    },
  ),

  // style Edit link
  select('& .edit',
    s.flex,
    s.fullWidth,
    s.fullHeight,
    s.flexColumn,
    s.justifyCenter,
  ),
)

export const CategoryTabBar = ({ pathname, tabs, subscribed }) => (

      <AllCategoryTab pathname={pathname} />
      {subscribed && <SubscribedCategoryTab pathname={pathname} />}
      {subscribed && tabs.length < 1 && <SubscribedZeroStateTab />}

      {tabs.map(tab =>
        (<CategoryTab
          isActive={(tab.activePattern ? tab.activePattern.test(pathname) : tab.to === pathname)}
          key={`CategoryTab_${tab.to}`}
          label={tab.label}
          source={tab.source}
          to={tab.to}
        />),
      )}

      <Link
        activeClassName="isActive"
        className="edit"
        to="/discover/all"
      >
        Edit
      </Link>

)
CategoryTabBar.propTypes = {
  pathname: PropTypes.string.isRequired,
  subscribed: PropTypes.bool.isRequired,
  tabs: PropTypes.array.isRequired,
}

export default CategoryTabBar

<commit_msg>categories - tabs - zero-state + other tweaks<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import { Link } from 'react-router'
import classNames from 'classnames'
import { before, css, hover, media, select } from '../../styles/jss'
import * as s from '../../styles/jso'

const backgroundStyles = source => ({
  backgroundImage: `url("${source}")`,
})

// single tab style
const categoryTabStyle = css(
  s.relative,
  s.inlineBlock,
  s.fullWidth,
  s.fullHeight,
  s.pt0,
  s.pb0,
  s.pr10,
  s.pl10,
  s.overflowHidden,
  s.alignTop,
  s.bgcBlack,
  s.transitionColor,
  {
    minWidth: 100,
    whiteSpace: 'normal',
    backgroundSize: 'cover',
  },

  before(
    s.absolute,
    s.flood,
    s.zIndex1,
    s.transitionBgColor,
    {
      content: '""',
      backgroundColor: 'rgba(0, 0, 0, 0.5)',
    },
  ),

  select('& + &',
    {
      borderLeft: '1px solid #fff',
    },
  ),

  // text label
  select('& .text-label',
    s.relative,
    s.flex,
    s.justifyCenter,
    s.itemsCenter,
    s.fullWidth,
    s.fullHeight,
    s.center,
    s.zIndex2,
  ),

  // hover / active states
  select('&.isActive',
    select('& .text-label', s.sansBold, { textDecoration: 'underline' }),
    before({ backgroundColor: 'rgba(0, 0, 0, 0.8)', }),
  ),
  select('&:active',
    select('& .text-label', s.sansBold, { textDecoration: 'underline' }),
    before({ backgroundColor: 'rgba(0, 0, 0, 0.8)', }),
  ),
  select('.no-touch &:hover',
    before({ backgroundColor: 'rgba(0, 0, 0, 0.8)', }),
  ),

  // mini
  select('&.mini',
    {
      width: 100,
    },
    media(s.minBreak2,
      {
        width: 120,
      },
    ),
  ),
  select('&.mini.all', {
    background: 'linear-gradient(50deg, #ff00ff, #1925ff)',
  }),
  select('&.mini.subscribed', {
    background: 'linear-gradient(45deg, #6e00ff, #0054ff, #007bf1, #00beff)',
  }),

  // zero state
  select('&.zero-state',
    {
      width: 'calc(100% - 200px)',
      minWidth: '50%',
      background: 'linear-gradient(85deg, #fd00d2, #6200ff, #0038ff, #0083fc, #00d8c6, #00ff70, #00ff36)',
    },
    media(s.minBreak2,
      {
        width: 'calc(100% - 240px)',
      },
    ),
  ),

  media(s.minBreak2,
    {
      width: 120,
    },
  ),
  media(s.minBreak4,
    s.fullWidth,
    {
      minWidth: 120,
    },
  ),
)

const CategoryTab = ({ isActive, label, source, to }) => (
  <Link
    className={classNames({ isActive }, `${categoryTabStyle}`)}
    to={to}
    style={source ? backgroundStyles(source) : null}
  >
    {label}
  </Link>
)
CategoryTab.propTypes = {
  isActive: PropTypes.bool.isRequired,
  label: PropTypes.string.isRequired,
  source: PropTypes.string.isRequired,
  to: PropTypes.string.isRequired,
}

const AllCategoryTab = ({ pathname }) => {
  const isActive = pathname === '/discover' ||
    pathname === '/discover/trending' ||
    pathname === '/discover/recent'
  return (
    <Link
      className={classNames('mini all', { isActive }, `${categoryTabStyle}`)}
      to="/discover"
    >
      All
    </Link>
  )
}
AllCategoryTab.propTypes = {
  pathname: PropTypes.string.isRequired,
}

const SubscribedCategoryTab = ({ pathname }) => {
  const isActive = pathname === '/discover/subscribed' ||
    pathname === '/discover/subscribed/trending' ||
    pathname === '/discover/subscribed/recent'
  return (
    <Link
      className={classNames('mini subscribed', { isActive }, `${categoryTabStyle}`)}
      to="/discover/subscribed"
    >
      Subscribed
    </Link>
  )
}
SubscribedCategoryTab.propTypes = {
  pathname: PropTypes.string.isRequired,
}

const SubscribedZeroStateTab = () => (
  <Link
    className={`zero-state ${categoryTabStyle}`}
    to="/discover/all"
  >
    Find and subscribe to a community
  </Link>
)

// main tab bar style
const categoryTabBarStyle = css(
  s.absolute,
  s.zIndex2,
  s.fontSize12,
  s.colorWhite,
  s.nowrap,
  s.bgcBlack,
  s.borderBottom,
  {
    right: 0,
    bottom: -70,
    left: 0,
    height: 70,
    borderColor: '#fff',
  },
  media('(max-width: 23.375em)', // 374 / 16 = 23.375em
    {
      bottom: -50,
      height: 50,
    },
  ),
  media(s.maxBreak2,
    select('.isProfileMenuActive .Navbar ~ &',
      s.displayNone,
    ),
  ),
  media(s.minBreak2,
    s.fontSize14,
    {
      bottom: -80,
      height: 80,
    },
  ),
)

// nav tag that holds all of the tabs style
const categoryTabsHolderStyle = css(
  s.relative,
  s.inlineBlock,
  s.fullHeight,
  s.overflowScrollWebX,
  s.nowrap,
  s.alignTop,
  {
    width: 'calc(100% - 70px)',
  },
  media(s.minBreak2,
    s.fontSize14,
    {
      width: 'calc(100% - 90px)',
    },
  ),
  media(s.minBreak4,
    s.flex,
  ),
)

// tab bar tools style
const categoryTabBarToolsStyle = css(
  s.relative,
  s.inlineBlock,
  s.fullHeight,
  s.pr10,
  s.rightAlign,
  s.alignTop,
  s.bgcBlack,
  {
    width: 70,
  },

  before(
    s.absolute,
    s.zIndex2,
    s.wv20,
    s.fullHeight,
    {
      content: '""',
      top: 0,
      left: -20,
      background: 'linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 90%)'
    },
  ),

  media(s.minBreak2,
    s.pr20,
    {
      width: '90px',
    },
    before(
      s.wv30,
      {
        left: -30,
      },
    ),
  ),
  media(s.minBreak4,
    s.absolute,
    {
      top: 0,
      right: 0,
    },
  ),

  // style Edit link
  select('& .edit',
    s.flex,
    s.fullWidth,
    s.fullHeight,
    s.flexColumn,
    s.justifyCenter,
    s.center,
  ),
)

export const CategoryTabBar = ({ pathname, tabs, subscribed }) => (

      <AllCategoryTab pathname={pathname} />
      {subscribed && <SubscribedCategoryTab pathname={pathname} />}
      {subscribed && tabs.length < 1 && <SubscribedZeroStateTab />}

      {tabs.map(tab =>
        (<CategoryTab
          isActive={(tab.activePattern ? tab.activePattern.test(pathname) : tab.to === pathname)}
          key={`CategoryTab_${tab.to}`}
          label={tab.label}
          source={tab.source}
          to={tab.to}
        />),
      )}

      <Link
        activeClassName="isActive"
        className="edit"
        to="/discover/all"
      >
        Edit
      </Link>

)
CategoryTabBar.propTypes = {
  pathname: PropTypes.string.isRequired,
  subscribed: PropTypes.bool.isRequired,
  tabs: PropTypes.array.isRequired,
}

export default CategoryTabBar

<|endoftext|>


<commit_before>'use strict';

const debug                 = require('debug')('mongo-pubsub:cache'),
      toFactory             = require('tofactory'),
      MongoObserver         = require('mongo-observer'),
      getMongoConnection    = MongoObserver.getMongoConnection,
      idFromHexString       = MongoObserver.ObjectID.createFromHexString;

module.exports = toFactory(Cache);

function Cache(spec) {
    const cache = Object.create(null);

    let db;

    init();

    return {
        sync,
        'get': get,
        insert,
        remove
    };

    function init() {
        getMongoConnection(spec, function (err, dbConn) {
            if (err) throw err;
            db = dbConn;
        });
    }

    function get(collection, id) {
        return getCollection(collection)[id];
    }

    function insert(collection, id, data, next) {
        getCollection(collection)[id] = data;
    }

    function remove(collection, id) {
        delete getCollection(collection)[id];
    }

    function sync(collection, id, data, next) {
        const col = getCollection(collection),
              doc = col[id];

        if (doc) {
            col[id] = Object.assign(doc, data);
            next(undefined, data, doc);
        } else {
            findOneById(collection, id, function (err, doc) {
                if (err) return next(err);
                col[id] = doc;
                next(undefined, data, doc);
            });
        }
    }

    function findOneById(colName, id, next) {
        if (db) {
            db.collection(colName, function (err, collection) {
                if (err) return next(err);
                collection
                    .find({_id: idFromHexString(id)})
                    .limit(1)
                    .next(function (err, doc) {
                        if (err) return next(err);
                        if (doc) return next(undefined, doc);
                        debug(`doc not found ${id}`);
                    });
            });
        } else {
            next(new Error('no database connection'));
        }
    }

    function getCollection(collection) {
        return cache[collection] || (cache[collection] = {});
    }
}<commit_msg>removed cache file<commit_after><|endoftext|>


<commit_before>var fs = require('fs');
var cssParse = require('css-parse');

/**
 * Argument is file path or not
 * @param {String} file
 * @returns {Boolean}
 */
function isFile(file) {
  try {
    return fs.existsSync(file) && fs.statSync(file).isFile();
  } catch (error) {
    return false;
  }
}

/**
 * Argument is directory path or not
 * @param {String} dir
 * @returns {Boolean}
 */
function isDirectory(dir) {
  try {
    return fs.existsSync(dir) && fs.statSync(dir).isDirectory();
  } catch (error) {
    return false;
  }
}

/**
 * Argument is CSS or not
 * @param {String} str
 * @returns {Boolean}
 */
function isCSS(str) {
  try {
    return cssParse(str) instanceof Object;
  } catch (error) {
    return false;
  }
}

function isFunction(fn) {
  return typeof fn === 'function' || false;
}

function isObject(obj) {
  var type = typeof obj;
  return type === 'function' || type === 'object' && !!obj;
}

module.exports = {
  isFile: isFile,
  isDirectory: isDirectory,
  isCSS: isCSS,
  isFunction: isFunction,
  isObject: isObject
};
<commit_msg>ES6fy lib/util.js<commit_after>const fs = require('fs');
const cssParse = require('css-parse');

const utils = {
  /**
   * Argument is file path or not
   * @param {String} file
   * @returns {Boolean}
   */
  isFile: (file) => {
    try {
      return fs.existsSync(file) && fs.statSync(file).isFile();
    } catch (error) {
      return false;
    }
  },
  /**
   * Argument is directory path or not
   * @param {String} dir
   * @returns {Boolean}
   */
  isDirectory: (dir) => {
    try {
      return fs.existsSync(dir) && fs.statSync(dir).isDirectory();
    } catch (error) {
      return false;
    }
  },
  /**
   * Argument is CSS or not
   * @param {String} str
   * @returns {Boolean}
   */
  isCSS: (str) => {
    try {
      return cssParse(str) instanceof Object;
    } catch (error) {
      return false;
    }
  },
  isFunction: (fn) => {
    return typeof fn === 'function' || false;
  },
  isObject: (obj) => {
    var type = typeof obj;
    return type === 'function' || type === 'object' && !!obj;
  }
};

module.exports = utils;
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.script',
  name: 'Script',

  implements: [ 'foam.nanos.auth.EnabledAware' ],

  imports: [ 'scriptDAO' ],

  javaImports: [
    'bsh.EvalError',
    'bsh.Interpreter',
    'foam.nanos.pm.PM',
    'java.io.ByteArrayOutputStream',
    'java.io.PrintStream',
    'java.util.Date'
  ],

  tableColumns: [
    'id', 'enabled', 'server', /*'language',*/ 'description', 'run'
  ],

  searchColumns: [ ],

  properties: [
    {
      class: 'String',
      name: 'id'
    },
    {
      class: 'String',
      name: 'description'
    },
    {
      class: 'DateTime',
      name: 'lastRun',
      visibility: foam.u2.Visibility.RO
    },
    /*
    {
      class: 'Enum',
      of: 'foam.nanos.script.Language',
      name: 'language',
      value: foam.nanos.script.Language.BEANSHELL,
      transient: true
      // TODO: fix JS support
    },
    */
    {
      class: 'Boolean',
      name: 'server',
      value: true
    },
    {
      class: 'Boolean',
      name: 'scheduled',
      hidden: true
    },
    {
      class: 'String',
      name: 'code',
      view: { class: 'foam.u2.tag.TextArea', rows: 20, cols: 80 }
    },
    {
      class: 'String',
      name: 'output',
      visibility: foam.u2.Visibility.RO,
      view: { class: 'foam.u2.tag.TextArea', rows: 20, cols: 80 }
    },
    {
      class: 'String',
      name: 'notes',
      view: { class: 'foam.u2.tag.TextArea', rows: 10, cols: 80 }
    }
  ],

  methods: [
    {
      name: 'createInterpreter',
      args: [
        {
          name: 'x', javaType: 'foam.core.X'
        }
      ],
      javaReturns: 'Interpreter',
      javaCode: `
        Interpreter shell = new Interpreter();

        try {
          shell.set("currentScript", this);
          shell.set("x", getX());
          shell.eval("runScript(String name) { script = x.get(\\"scriptDAO\\").find(name); if ( script != null ) eval(script.code); }");
        } catch (EvalError e) {}

        return shell;
      `
    },
    {
      name: 'runScript',
      args: [
        {
          name: 'x', javaType: 'foam.core.X'
        }
      ],
      javaReturns: 'void',
      javaCode: `
        ByteArrayOutputStream baos  = new ByteArrayOutputStream();
        PrintStream           ps    = new PrintStream(baos);
        Interpreter           shell = createInterpreter(x);
        PM                    pm    = new PM(this.getClass(), getId());

        // TODO: import common packages like foam.core.*, foam.dao.*, etc.
        try {
          setOutput("");
          shell.setOut(ps);
          shell.eval(getCode());
        } catch (EvalError e) {
          e.printStackTrace();
        } finally {
          pm.log(x);
        }

        setLastRun(new Date());
        ps.flush();
      System.err.println("******************** Output: " + baos.toString());
        setOutput(baos.toString());
    `
    }
  ],

  actions: [
    {
      name: 'run',
      code: function() {
        var self = this;
        this.output = '';

//        if ( this.language === foam.nanos.script.Language.BEANSHELL ) {
        if ( this.server ) {
          this.scheduled = true;
          this.scriptDAO.put(this).then(function(script) {
            self.copyFrom(script);
          });
        } else {
          var log = function() { this.output = this.output + Array.prototype.join.call(arguments, ''); }.bind(this);

          with ( { log: log, print: log } ) {
            var ret = eval(this.code);
            console.log('ret: ', ret);
            // TODO: if Promise returned, then wait
          }

          this.scriptDAO.put(this);
        }
      }
    }
  ]
});
<commit_msg>Add Context as 'x' when running JS scripts.<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.script',
  name: 'Script',

  implements: [ 'foam.nanos.auth.EnabledAware' ],

  imports: [ 'scriptDAO' ],

  javaImports: [
    'bsh.EvalError',
    'bsh.Interpreter',
    'foam.nanos.pm.PM',
    'java.io.ByteArrayOutputStream',
    'java.io.PrintStream',
    'java.util.Date'
  ],

  tableColumns: [
    'id', 'enabled', 'server', /*'language',*/ 'description', 'run'
  ],

  searchColumns: [ ],

  properties: [
    {
      class: 'String',
      name: 'id'
    },
    {
      class: 'String',
      name: 'description'
    },
    {
      class: 'DateTime',
      name: 'lastRun',
      visibility: foam.u2.Visibility.RO
    },
    /*
    {
      class: 'Enum',
      of: 'foam.nanos.script.Language',
      name: 'language',
      value: foam.nanos.script.Language.BEANSHELL,
      transient: true
      // TODO: fix JS support
    },
    */
    {
      class: 'Boolean',
      name: 'server',
      value: true
    },
    {
      class: 'Boolean',
      name: 'scheduled',
      hidden: true
    },
    {
      class: 'String',
      name: 'code',
      view: { class: 'foam.u2.tag.TextArea', rows: 20, cols: 80 }
    },
    {
      class: 'String',
      name: 'output',
      visibility: foam.u2.Visibility.RO,
      view: { class: 'foam.u2.tag.TextArea', rows: 20, cols: 80 }
    },
    {
      class: 'String',
      name: 'notes',
      view: { class: 'foam.u2.tag.TextArea', rows: 10, cols: 80 }
    }
  ],

  methods: [
    {
      name: 'createInterpreter',
      args: [
        {
          name: 'x', javaType: 'foam.core.X'
        }
      ],
      javaReturns: 'Interpreter',
      javaCode: `
        Interpreter shell = new Interpreter();

        try {
          shell.set("currentScript", this);
          shell.set("x", getX());
          shell.eval("runScript(String name) { script = x.get(\\"scriptDAO\\").find(name); if ( script != null ) eval(script.code); }");
        } catch (EvalError e) {}

        return shell;
      `
    },
    {
      name: 'runScript',
      args: [
        {
          name: 'x', javaType: 'foam.core.X'
        }
      ],
      javaReturns: 'void',
      javaCode: `
        ByteArrayOutputStream baos  = new ByteArrayOutputStream();
        PrintStream           ps    = new PrintStream(baos);
        Interpreter           shell = createInterpreter(x);
        PM                    pm    = new PM(this.getClass(), getId());

        // TODO: import common packages like foam.core.*, foam.dao.*, etc.
        try {
          setOutput("");
          shell.setOut(ps);
          shell.eval(getCode());
        } catch (EvalError e) {
          e.printStackTrace();
        } finally {
          pm.log(x);
        }

        setLastRun(new Date());
        ps.flush();
      System.err.println("******************** Output: " + baos.toString());
        setOutput(baos.toString());
    `
    }
  ],

  actions: [
    {
      name: 'run',
      code: function() {
        var self = this;
        this.output = '';

//        if ( this.language === foam.nanos.script.Language.BEANSHELL ) {
        if ( this.server ) {
          this.scheduled = true;
          this.scriptDAO.put(this).then(function(script) {
            self.copyFrom(script);
          });
        } else {
          var log = function() { this.output = this.output + Array.prototype.join.call(arguments, ''); }.bind(this);

          with ( { log: log, print: log, x: self.__context__ } ) {
            var ret = eval(this.code);
            console.log('ret: ', ret);
            // TODO: if Promise returned, then wait
          }

          this.scriptDAO.put(this);
        }
      }
    }
  ]
});
<|endoftext|>


<commit_before>
    var   cmds = require('../')
        , fs = require('graceful-fs')
        , path = require('path')
        , cmd = require('../../util/process')
        , haxelib = require('../../util/haxelib')
        , util = require('../../util/util')
        , bars = require('handlebars')
        , fse = require('fs-extra')

var internal = {};

exports.post_build = function(flow, done) {

    flow.log(3,'build - running cpp post process');

    if(flow.target_desktop) {
        flow.log(3,'build - cpp - target desktop post build');
        internal.post_build_desktop(flow, done);
    } else if(flow.target_mobile) {
        flow.log(3,'build - cpp - target mobile post build');
        internal.post_build_mobile(flow, done);
    } else {
        flow.log(3,'build - cpp - non desktop/mobile post build');
        if(done) done();
    }

} //post_build

exports.ios_combine_archs = function(flow, done) {

    if(flow.flags.archs) {

        var archs = flow.flags.archs.split(',');
            archs = archs.map(function(a){ return flow.project.adjust_arch(flow, a.trim()) });

        flow.log(2, "build - ios - combining multiple archs into one", archs);

        var dest = flow.project.get_path_binary_dest_full(flow, flow.project.prepared, 'fat');
            fse.ensureFileSync(dest);

        var args = ['-sdk','iphoneos', 'lipo',
                    '-output', dest,
                    '-create'];

        var input_list = archs.map(function(a){
            return flow.project.get_path_binary_dest_full(flow, flow.project.prepared, a);
        });

        args = args.concat(input_list);

        cmd.exec(flow, 'xcrun', args, { quiet:false }, function(code,out,err){
            if(done) {
                done(code);
            }
        });

        return;

    }  //if multiple archs

} //ios_combine_archs

internal.post_build_mobile = function(flow, done) {

    //run platform specific build chains
    if(flow.target == 'android') {
        return internal.build_android(flow, done);
    }

    if(flow.target == 'ios') {
        return exports.ios_combine_archs(flow, done);
    } //ios

        //other platforms end here
    if(done) {
        done();
    }

} //post_build_mobile

    //runs ant, and such
internal.build_android = function(flow, done) {

    flow.log(2, 'android specifics', flow.project.prepared.source.project.app.mobile.android);

    //handle ability to compile store build, vs debug test build
    var build_type = flow.project.prepared.source.project.app.mobile.android.build_type;

        //where to build from
    var project_root = path.join(flow.project.paths.build, flow.config.build.android.project);
        //a build file to remove
    var build_meta_file = path.join(project_root,'bin/build.prop');

        //remove build meta data to ensure build happens
    if(fs.existsSync(build_meta_file)) {
        fs.unlinkSync(build_meta_file);
    }

    var args = [build_type];
    var opt = {
        cwd: project_root,
        quiet:false
    }

            if(flow.timing) console.time('build - android - ant');

    var ant_path = util.normalize(flow.config.build.android.ant_path);

    flow.log(2, 'build - android - using ant at %s', ant_path);

    if(!fs.existsSync(ant_path)) {
        flow.log(1, 'build - android - cannot find ant at specified path %s', ant_path );
        flow.project.failed = true;
        return flow.finished();
    }

    try {

        cmd.exec(flow, ant_path, args, opt, function(code,out,err){

                if(flow.timing) console.timeEnd('build - android - ant');

            if(code != 0) {
                flow.log(1,'build - android - stopping because ant failed to build, exit code %d', code);
                flow.project.failed = true;
                return flow.finished();
            }

                //now move the apk out into the user bin folder
            var apk_name = flow.project.prepared.source.project.app.name + '-' + build_type + '.apk';
            var apk_path = path.join(project_root, 'bin/' + apk_name);
            var apk_dest = path.join(flow.project.paths.output, apk_name);

            if(!fs.existsSync(apk_path)) {

                flow.log(1,'build - android - cannot copy build to output path!');
                flow.log(1,'build - android - missing file because %s build failed to generate an apk:', build_type, apk_path);
                flow.project.failed = true;
                return flow.finished();

            } else {

                    if(flow.timing) console.time('build - apk copy');
                util.copy_path(flow, apk_path, apk_dest);
                    if(flow.timing) console.timeEnd('build - apk copy');

                if(done) {
                    done(code,out,err);
                }

            }

        });

    } catch(e) {
        flow.log(1, 'build - android - cannot execute ant', e );
        return done('cannot execute ant...', null);
    }

} //build_android

internal.post_build_desktop = function(flow, done) {

    if(flow.system == 'mac' || flow.system == 'linux') {

                if(flow.timing) console.time('build - binary chmod');

        cmd.exec(flow, 'chmod', ['+x',flow.project.paths.binary.full], {quiet:true}, function(code,out,err){

                if(flow.timing) console.timeEnd('build - binary chmod');

            if(done) {
                done(code,out,err);
            }

        }); //exec

    } else {

        if(done) {
            done();
        }

    }

} //post_build_desktop

internal.move_binary = function(flow, target_arch) {

    target_arch = target_arch || flow.target_arch;

    var binary_source = flow.project.get_path_binary_name_source(flow, flow.project.prepared, target_arch);
    var binary_dest_full = flow.project.get_path_binary_dest_full(flow, flow.project.prepared, target_arch);
    var source_path = util.normalize(path.join(flow.project.paths.build, 'cpp/' + binary_source));

    flow.log(3,'build - moving binary for %s from %s to %s', target_arch, source_path, binary_dest_full);

        if(flow.timing) console.time('build - binary copy');

    util.copy_path(flow, source_path, binary_dest_full);

        if(flow.timing) console.timeEnd('build - binary copy');

} //move_binary

internal.build_hxcpp_arch_list = function(flow, arch_list, run_path, hxcpp_file, done ) {

    //no list?
    if(!arch_list) {
        if(done) done();
        return;
    }

        //no more left in list?
    if(arch_list.length <= 0) {
        if(done) done();
        return;
    }

        //remove current arch from list
    var curr_arch = arch_list.shift();

    exports.build_hxcpp(flow, curr_arch, run_path, hxcpp_file, function(err) {

        if(err) {
            flow.log(1, '\n build - stopping because of errors in hxcpp compile, while building arch %s \n', curr_arch);
            flow.project.failed = true;
            return flow.finished();
        }

            //for now lib based projects don't do this step
        if(!flow.project.parsed.project.lib) {
            internal.move_binary(flow, curr_arch);
        }

        internal.build_hxcpp_arch_list(flow, arch_list, run_path, hxcpp_file, done);

    }); //run hxcpp

} //build_hxcpp_arch_list

exports.run_hxcpp = function(flow, run_path, hxcpp_file, done) {

    //building multiple archs?
    var archs = [flow.target_arch];

    if(flow.flags.archs) {
        archs = flow.flags.archs.split(',');
        archs = archs.map(function(a){ return flow.project.adjust_arch(flow, a.trim()); });
        flow.log(2, 'build - building multiple archs', archs);
    } else {
            //just build the normal one
        archs = [flow.target_arch];
    }

    internal.build_hxcpp_arch_list(flow, archs, run_path, hxcpp_file, done );

} //run_hxcpp

exports.post_haxe = function(flow, done) {

    var cpp_path = path.join(flow.project.paths.build, 'cpp/');
        cpp_path = path.resolve(flow.project.root, cpp_path);

        //write custom xml
    exports.write_hxcpp(flow, cpp_path);

        //build against it
    exports.run_hxcpp(flow, cpp_path, 'flow.Build.xml', done);

} //exports

    //write out the hxcpp file from the flow project into the build folder
exports.write_hxcpp = function(flow, run_path) {

    var dest_file = path.join(run_path,'flow.Build.xml');
    var src_template = path.join(__dirname, 'cpp/flow.Build.xml');

    var context = {
        includes : {
            __haxe : {
                name:'__haxe', file:'Build.xml', path:'Build.xml',
                source:'flow internal', internal:true
            }
        }
    };

    if(flow.project.prepared.hxcpp.includes) {
        for(name in flow.project.prepared.hxcpp.includes) {
            context.includes[name] = flow.project.prepared.hxcpp.includes[name];
        }
    }

    var template_content = fs.readFileSync(src_template, 'utf8');
    var template = bars.compile(template_content);
    var result = template(context);

    fse.ensureFileSync(dest_file);
    fs.writeFileSync(dest_file, result, 'utf8');

        //now, for each of the includes
        //copy them over with a special context
        //that will allow them to know their source

    for(include in context.includes) {

            var node = context.includes[include];

        if(!node.internal) {
            if(fs.existsSync(node.path)) {

                var inc_context = { FLOW_SOURCE:path.dirname(node.path) };
                var inc_content = fs.readFileSync(node.path,'utf8');
                var inc_template = bars.compile(inc_content);
                var inc_result = inc_template(inc_context);

                var inc_dest = path.join(run_path, node.file);

                fse.ensureFileSync(inc_dest);
                fs.writeFileSync(inc_dest, inc_result, 'utf8');

            } else { //if exists
                flow.log(1, 'build - hxcpp - missing external include file?? this will cause errors for sure ', node);
            }

        } //if not internal
    }

} //write_hxcpp

exports.build_hxcpp = function(flow, target_arch, run_path, hxcpp_file, done) {

    var hxcpp_file = hxcpp_file || 'flow.Build.xml';
    var args = [hxcpp_file];

    if(run_path) {
        run_path = util.normalize(run_path, true);
    }

    if(flow.flags.debug) {
        args.push("-Ddebug");
    }

        //with --log 3+ hxcpp can also be verbose
    if(flow.flags.log > 2) {
        args.push('-verbose');
    }

        //default to no windows console, but allow it through
        //--d show_console or user define in project tree
    if(flow.target == 'windows' &&
       flow.project.prepared.defines_list.indexOf('show_console') == -1) {
        args.push('-Dno_console');
    }

    args.push('-D' + flow.target);

    switch(target_arch) {
        case '64':
            args.push('-DHXCPP_M64');

            if(flow.target_mobile) {
                flow.log(1, 'build / WARNING / you are using a --arch 64 setting on mobile, you probably meant --arch arm64?')
            }

            break;
        case 'arm64':
            args.push('-DHXCPP_ARM64');
            args.push('-DHXCPP_M64');
            break;
        case 'armv6':
            args.push('-DHXCPP_ARMV6');
            break;
        case 'armv7':
            args.push('-DHXCPP_ARMV7');
            break;
        case 'armv7s':
            args.push('-DHXCPP_ARMV7S');
            break;
        case 'x86':
            args.push('-DHXCPP_X86');
            break;
        case 'sim64':
            args.push('-Dsimulator');
            args.push('-DHXCPP_M64');
            break;
        case 'sim':
            args.push('-Dsimulator');
            break;
    }

    if(flow.target == 'ios') {
        args.push('-Diphone');
        args.push('-DHXCPP_CPP11');
        args.push('-DHXCPP_CLANG');
    }

    if(flow.target == 'android') {
        args.push('-Dandroid-' + flow.project.parsed.project.app.mobile.android.sdk_target);
    }

        //append command line + project based flags
    args = util.array_union(args, flow.project.prepared.hxcpp.flags);

        //and finally include the Options.txt flags
    try {

        var options_file = path.join(run_path,'Options.txt');
        var options_str = fs.readFileSync(options_file,'utf8');
        var opt_empty_err = 'build - cpp - Options.txt was blank/empty?!';

        flow.log(3, 'build - cpp - using Options.txt from %s', options_file);

        if(options_str && options_str.length) {
            var opt_lines = options_str.split(/\r?\n/g);
            if(opt_lines && opt_lines.length) {
                var opt_list = opt_lines[0].split(/ /);
                if(opt_list && opt_list.length) {
                    args = util.array_union(args, opt_list);
                } else {
                    flow.log(1,opt_empty_err,options_file);
                }
            } else {
                flow.log(1,opt_empty_err,options_file);
            }
        } else {
            flow.log(1, opt_empty_err, options_file);
        }

    } catch(e) {
        flow.log(3, 'build - cpp - no Options.txt for this build? ', e);
    }

    flow.log(2, 'build - running hxcpp for arch %s ...', target_arch);
    flow.log(3, 'haxelib run hxcpp %s', args.join(' ') );
    flow.log(3, 'running hxcpp from %s', run_path );

    var opt = {
        quiet : false,
        cwd: run_path
    }

    cmd.exec(flow, 'haxelib', ['run','hxcpp'].concat(args), opt, done);

} //build_hxcpp
<commit_msg>build; cpp; parse options.txt based on haxe version 3.2.0-rc.1<commit_after>
    var   cmds = require('../')
        , fs = require('graceful-fs')
        , path = require('path')
        , cmd = require('../../util/process')
        , haxelib = require('../../util/haxelib')
        , util = require('../../util/util')
        , bars = require('handlebars')
        , fse = require('fs-extra')

var internal = {};

exports.post_build = function(flow, done) {

    flow.log(3,'build - running cpp post process');

    if(flow.target_desktop) {
        flow.log(3,'build - cpp - target desktop post build');
        internal.post_build_desktop(flow, done);
    } else if(flow.target_mobile) {
        flow.log(3,'build - cpp - target mobile post build');
        internal.post_build_mobile(flow, done);
    } else {
        flow.log(3,'build - cpp - non desktop/mobile post build');
        if(done) done();
    }

} //post_build

exports.ios_combine_archs = function(flow, done) {

    if(flow.flags.archs) {

        var archs = flow.flags.archs.split(',');
            archs = archs.map(function(a){ return flow.project.adjust_arch(flow, a.trim()) });

        flow.log(2, "build - ios - combining multiple archs into one", archs);

        var dest = flow.project.get_path_binary_dest_full(flow, flow.project.prepared, 'fat');
            fse.ensureFileSync(dest);

        var args = ['-sdk','iphoneos', 'lipo',
                    '-output', dest,
                    '-create'];

        var input_list = archs.map(function(a){
            return flow.project.get_path_binary_dest_full(flow, flow.project.prepared, a);
        });

        args = args.concat(input_list);

        cmd.exec(flow, 'xcrun', args, { quiet:false }, function(code,out,err){
            if(done) {
                done(code);
            }
        });

        return;

    }  //if multiple archs

} //ios_combine_archs

internal.post_build_mobile = function(flow, done) {

    //run platform specific build chains
    if(flow.target == 'android') {
        return internal.build_android(flow, done);
    }

    if(flow.target == 'ios') {
        return exports.ios_combine_archs(flow, done);
    } //ios

        //other platforms end here
    if(done) {
        done();
    }

} //post_build_mobile

    //runs ant, and such
internal.build_android = function(flow, done) {

    flow.log(2, 'android specifics', flow.project.prepared.source.project.app.mobile.android);

    //handle ability to compile store build, vs debug test build
    var build_type = flow.project.prepared.source.project.app.mobile.android.build_type;

        //where to build from
    var project_root = path.join(flow.project.paths.build, flow.config.build.android.project);
        //a build file to remove
    var build_meta_file = path.join(project_root,'bin/build.prop');

        //remove build meta data to ensure build happens
    if(fs.existsSync(build_meta_file)) {
        fs.unlinkSync(build_meta_file);
    }

    var args = [build_type];
    var opt = {
        cwd: project_root,
        quiet:false
    }

            if(flow.timing) console.time('build - android - ant');

    var ant_path = util.normalize(flow.config.build.android.ant_path);

    flow.log(2, 'build - android - using ant at %s', ant_path);

    if(!fs.existsSync(ant_path)) {
        flow.log(1, 'build - android - cannot find ant at specified path %s', ant_path );
        flow.project.failed = true;
        return flow.finished();
    }

    try {

        cmd.exec(flow, ant_path, args, opt, function(code,out,err){

                if(flow.timing) console.timeEnd('build - android - ant');

            if(code != 0) {
                flow.log(1,'build - android - stopping because ant failed to build, exit code %d', code);
                flow.project.failed = true;
                return flow.finished();
            }

                //now move the apk out into the user bin folder
            var apk_name = flow.project.prepared.source.project.app.name + '-' + build_type + '.apk';
            var apk_path = path.join(project_root, 'bin/' + apk_name);
            var apk_dest = path.join(flow.project.paths.output, apk_name);

            if(!fs.existsSync(apk_path)) {

                flow.log(1,'build - android - cannot copy build to output path!');
                flow.log(1,'build - android - missing file because %s build failed to generate an apk:', build_type, apk_path);
                flow.project.failed = true;
                return flow.finished();

            } else {

                    if(flow.timing) console.time('build - apk copy');
                util.copy_path(flow, apk_path, apk_dest);
                    if(flow.timing) console.timeEnd('build - apk copy');

                if(done) {
                    done(code,out,err);
                }

            }

        });

    } catch(e) {
        flow.log(1, 'build - android - cannot execute ant', e );
        return done('cannot execute ant...', null);
    }

} //build_android

internal.post_build_desktop = function(flow, done) {

    if(flow.system == 'mac' || flow.system == 'linux') {

                if(flow.timing) console.time('build - binary chmod');

        cmd.exec(flow, 'chmod', ['+x',flow.project.paths.binary.full], {quiet:true}, function(code,out,err){

                if(flow.timing) console.timeEnd('build - binary chmod');

            if(done) {
                done(code,out,err);
            }

        }); //exec

    } else {

        if(done) {
            done();
        }

    }

} //post_build_desktop

internal.move_binary = function(flow, target_arch) {

    target_arch = target_arch || flow.target_arch;

    var binary_source = flow.project.get_path_binary_name_source(flow, flow.project.prepared, target_arch);
    var binary_dest_full = flow.project.get_path_binary_dest_full(flow, flow.project.prepared, target_arch);
    var source_path = util.normalize(path.join(flow.project.paths.build, 'cpp/' + binary_source));

    flow.log(3,'build - moving binary for %s from %s to %s', target_arch, source_path, binary_dest_full);

        if(flow.timing) console.time('build - binary copy');

    util.copy_path(flow, source_path, binary_dest_full);

        if(flow.timing) console.timeEnd('build - binary copy');

} //move_binary

internal.build_hxcpp_arch_list = function(flow, arch_list, run_path, hxcpp_file, done ) {

    //no list?
    if(!arch_list) {
        if(done) done();
        return;
    }

        //no more left in list?
    if(arch_list.length <= 0) {
        if(done) done();
        return;
    }

        //remove current arch from list
    var curr_arch = arch_list.shift();

    exports.build_hxcpp(flow, curr_arch, run_path, hxcpp_file, function(err) {

        if(err) {
            flow.log(1, '\n build - stopping because of errors in hxcpp compile, while building arch %s \n', curr_arch);
            flow.project.failed = true;
            return flow.finished();
        }

            //for now lib based projects don't do this step
        if(!flow.project.parsed.project.lib) {
            internal.move_binary(flow, curr_arch);
        }

        internal.build_hxcpp_arch_list(flow, arch_list, run_path, hxcpp_file, done);

    }); //run hxcpp

} //build_hxcpp_arch_list

exports.run_hxcpp = function(flow, run_path, hxcpp_file, done) {

    //building multiple archs?
    var archs = [flow.target_arch];

    if(flow.flags.archs) {
        archs = flow.flags.archs.split(',');
        archs = archs.map(function(a){ return flow.project.adjust_arch(flow, a.trim()); });
        flow.log(2, 'build - building multiple archs', archs);
    } else {
            //just build the normal one
        archs = [flow.target_arch];
    }

    internal.build_hxcpp_arch_list(flow, archs, run_path, hxcpp_file, done );

} //run_hxcpp

exports.post_haxe = function(flow, done) {

    var cpp_path = path.join(flow.project.paths.build, 'cpp/');
        cpp_path = path.resolve(flow.project.root, cpp_path);

        //write custom xml
    exports.write_hxcpp(flow, cpp_path);

        //build against it
    exports.run_hxcpp(flow, cpp_path, 'flow.Build.xml', done);

} //exports

    //write out the hxcpp file from the flow project into the build folder
exports.write_hxcpp = function(flow, run_path) {

    var dest_file = path.join(run_path,'flow.Build.xml');
    var src_template = path.join(__dirname, 'cpp/flow.Build.xml');

    var context = {
        includes : {
            __haxe : {
                name:'__haxe', file:'Build.xml', path:'Build.xml',
                source:'flow internal', internal:true
            }
        }
    };

    if(flow.project.prepared.hxcpp.includes) {
        for(name in flow.project.prepared.hxcpp.includes) {
            context.includes[name] = flow.project.prepared.hxcpp.includes[name];
        }
    }

    var template_content = fs.readFileSync(src_template, 'utf8');
    var template = bars.compile(template_content);
    var result = template(context);

    fse.ensureFileSync(dest_file);
    fs.writeFileSync(dest_file, result, 'utf8');

        //now, for each of the includes
        //copy them over with a special context
        //that will allow them to know their source

    for(include in context.includes) {

            var node = context.includes[include];

        if(!node.internal) {
            if(fs.existsSync(node.path)) {

                var inc_context = { FLOW_SOURCE:path.dirname(node.path) };
                var inc_content = fs.readFileSync(node.path,'utf8');
                var inc_template = bars.compile(inc_content);
                var inc_result = inc_template(inc_context);

                var inc_dest = path.join(run_path, node.file);

                fse.ensureFileSync(inc_dest);
                fs.writeFileSync(inc_dest, inc_result, 'utf8');

            } else { //if exists
                flow.log(1, 'build - hxcpp - missing external include file?? this will cause errors for sure ', node);
            }

        } //if not internal
    }

} //write_hxcpp

internal.parse_optionstxt = function(flow, options_file, text) {

    var result = [];
    var opt_empty_err = 'build - cpp - Options.txt was blank/empty?!';

    if(text && text.length) {
        flow.log(4, options_file + ' / ' + text);
        var opt_lines = text.split(/\r?\n/g);
        if(opt_lines && opt_lines.length) {

            if(flow.haxe.major == 3
            && flow.haxe.minor == 1) {
                opt_lines = opt_lines[0].split(/ /);
                opt_lines = opt_lines.filter(function(l) { if(l) return true; })
            } else
            if(flow.haxe.major == 3
            && flow.haxe.minor == 2) {
                opt_lines = opt_lines.filter(function(l) { if(l) return true; })
                opt_lines = opt_lines.map(function(l) { return '-D'+l; })
            }

            result = opt_lines;

        } else {
            flow.log(1,opt_empty_err,options_file);
        }
    } else {
        flow.log(1, opt_empty_err, options_file);
    }

    flow.log(4, 'hxcpp flags: ');
    flow.log(4, result);

    return result;

}

exports.build_hxcpp = function(flow, target_arch, run_path, hxcpp_file, done) {

    var hxcpp_file = hxcpp_file || 'flow.Build.xml';
    var args = [hxcpp_file];

    if(run_path) {
        run_path = util.normalize(run_path, true);
    }

    if(flow.flags.debug) {
        args.push("-Ddebug");
    }

        //with --log 3+ hxcpp can also be verbose
    if(flow.flags.log > 2) {
        args.push('-verbose');
    }

        //default to no windows console, but allow it through
        //--d show_console or user define in project tree
    if(flow.target == 'windows' &&
       flow.project.prepared.defines_list.indexOf('show_console') == -1) {
        args.push('-Dno_console');
    }

    args.push('-D' + flow.target);

    switch(target_arch) {
        case '64':
            args.push('-DHXCPP_M64');

            if(flow.target_mobile) {
                flow.log(1, 'build / WARNING / you are using a --arch 64 setting on mobile, you probably meant --arch arm64?')
            }

            break;
        case 'arm64':
            args.push('-DHXCPP_ARM64');
            args.push('-DHXCPP_M64');
            break;
        case 'armv6':
            args.push('-DHXCPP_ARMV6');
            break;
        case 'armv7':
            args.push('-DHXCPP_ARMV7');
            break;
        case 'armv7s':
            args.push('-DHXCPP_ARMV7S');
            break;
        case 'x86':
            args.push('-DHXCPP_X86');
            break;
        case 'sim64':
            args.push('-Dsimulator');
            args.push('-DHXCPP_M64');
            break;
        case 'sim':
            args.push('-Dsimulator');
            break;
    }

    if(flow.target == 'ios') {
        args.push('-Diphone');
        args.push('-DHXCPP_CPP11');
        args.push('-DHXCPP_CLANG');
    }

    if(flow.target == 'android') {
        args.push('-Dandroid-' + flow.project.parsed.project.app.mobile.android.sdk_target);
    }

        //append command line + project based flags
    args = util.array_union(args, flow.project.prepared.hxcpp.flags);

        //and finally include the Options.txt flags
    try {

        var options_file = path.join(run_path,'Options.txt');
        var options_str = fs.readFileSync(options_file,'utf8');

        flow.log(3, 'build - cpp - using Options.txt from %s', options_file);

        var list = internal.parse_optionstxt(flow, options_file, options_str);
        args = util.array_union(args, list);

    } catch(e) {
        flow.log(3, 'build - cpp - no Options.txt for this build? ', e);
    }

    flow.log(2, 'build - running hxcpp for arch %s ...', target_arch);
    flow.log(3, 'haxelib run hxcpp %s', args.join(' ') );
    flow.log(3, 'running hxcpp from %s', run_path );

    var opt = {
        quiet : false,
        cwd: run_path
    }

    cmd.exec(flow, 'haxelib', ['run','hxcpp'].concat(args), opt, done);

} //build_hxcpp
<|endoftext|>


<commit_before> define(['app',
    './forms/view/record-loader.directive.html.js',
    './directives/document-list.partial.html.js',
    './forms/view/view-abs-checkpoint.directive.js',
    './forms/view/view-abs-checkpoint-communique.directive.js',
    './forms/view/view-abs-permit.directive.js',
    './forms/view/view-authority.directive.js',
    './forms/view/view-authority-reference.directive.js',
    './forms/view/view-contact.directive.js',
    './forms/view/view-contact-reference.directive.js',
    './forms/view/view-database.directive.js',
    './forms/view/view-measure.directive.js',
    './forms/view/view-organization.directive.js',
    './forms/view/view-organization-reference.directive.js',
    './forms/view/view-resource.directive.js',
    'directives/angucomplete-extended'], function (app) {
//require("app", "linqjs")
app.controller("ProfileController", ["$scope", "authHttp", "$routeParams","linqjs", "$filter", "realm", function ($scope, $http, $routeParams, linqjs, $filter, realm) {

    $scope.code = $routeParams.code;
    $scope.documentCount       = 0;
    $scope.currentPage     = 0;
    $scope.searchText = '';
    $scope.autocompleteData = [];

	//*******************************************************
	$http.get('/api/v2013/countries/'+ $scope.code, { cache: true }).then(function (response) {
        $scope.country = response.data;
        $scope.searchText = '';
        $scope.autocompleteData = [];
    });

	//*******************************************************
	$http.get('/api/v2013/index/select?cb=1394816088237&fl=id,identifier_s,title_t,description_t,url_ss,schema_EN_t,date_dt,government_EN_t,schema_s,number_d,aichiTarget_ss,reference_s,sender_s,meeting_ss,recipient_ss,symbol_s,eventCity_EN_t,eventCountry_EN_t,startDate_s,endDate_s,body_s,code_s,meeting_s,group_s,function_t,department_t,organization_t,summary_EN_t,reportType_EN_t,completion_EN_t,jurisdiction_EN_t,development_EN_t&q=(realm_ss:' + realm.value.toLowerCase() + ' or realm_ss:absch)+AND+schema_s:*+AND+((+schema_s:*+))+AND+((+government_s:'+ $scope.code.toLowerCase() +'+))+AND+(*:*)&rows=25&sort=createdDate_dt+desc,+title_t+asc&start=0&wt=json', { cache: true }).then(function (response) {
        $scope.absch_nfp = response.data;
        // console.log($scope.absch_nfp);

        $scope.absch_nfp.response.docs.forEach(function(document){
                $scope.autocompleteData.push({ title: document.title_t ? document.title_t: ''});
                $scope.autocompleteData.push({ title:document.description_t ? document.description_t: ''});
                $scope.autocompleteData.push({ title:document.department_t ? document.department_t: ''});
                $scope.autocompleteData.push({ title:document.organization_t ? document.organization_t: ''});
        });
    });

    $scope.$watch('absch_nfp', function(value){

        if(undefined == value) return;

        $scope.getFacets(value.response.docs);

    });

    $scope.getFacets = function(data){

        var linqObj = linqjs.from(data);
        $scope.nationalAuthority = linqObj.count(function(schema){

                        return schema.schema_s.toLowerCase() == 'authority';
                });
            //console.log(response.data.response.docs);
            $scope.nfpCount = linqObj.count(function(schema){
                // console.log(schema.schema_EN_t.toLowerCase() + ' ' + 'national focal point'.toLowerCase());
                        return schema.schema_s.toLowerCase() == 'focalpoint'.toLowerCase();
                });

            $scope.nationalMeasure =linqObj.count(function(schema){
                        return schema.schema_s.toLowerCase() =='measure';
                });

            $scope.Permit = linqObj.count(function(schema){
                        return schema.schema_s.toLowerCase() =='abspermit';
                });

            $scope.absCheckpoint = linqObj.count(function(schema){
                        return schema.schema_s.toLowerCase() =='abscheckpoint';
                });

            $scope.absCheckpointCommunique = linqObj.count(function(schema){
                        return schema.schema_s.toLowerCase() =='abscheckpointcommunique';
                });

            $scope.database = linqObj.count(function(schema){
                        return schema.schema_s.toLowerCase() =='database';
                });
            $scope.resource = linqObj.count(function(schema){
                        return schema.schema_s.toLowerCase() =='resource';
                });
    }

    $scope.$watch('searchText.$', function(value){

        if(undefined == value || value ==null || $scope.absch_nfp ==null) return;
        $scope.getFacets($filter('filter')($scope.absch_nfp.response.docs,value));

    });

}]);

})
<commit_msg> country profile color fix<commit_after> define(['app',
    './forms/view/record-loader.directive.html.js',
    './directives/document-list.partial.html.js',
    './forms/view/view-abs-checkpoint.directive.js',
    './forms/view/view-abs-checkpoint-communique.directive.js',
    './forms/view/view-abs-permit.directive.js',
    './forms/view/view-authority.directive.js',
    './forms/view/view-authority-reference.directive.js',
    './forms/view/view-contact.directive.js',
    './forms/view/view-contact-reference.directive.js',
    './forms/view/view-database.directive.js',
    './forms/view/view-measure.directive.js',
    './forms/view/view-organization.directive.js',
    './forms/view/view-organization-reference.directive.js',
    './forms/view/view-resource.directive.js',
    'directives/angucomplete-extended'], function (app) {
//require("app", "linqjs")
app.controller("ProfileController", ["$scope", "authHttp", "$routeParams","linqjs", "$filter", "realm", function ($scope, $http, $routeParams, linqjs, $filter, realm) {

    $scope.code = $routeParams.code;
    $scope.documentCount       = 0;
    $scope.currentPage     = 0;
    $scope.searchText = '';
    $scope.autocompleteData = [];

	//*******************************************************
	$http.get('/api/v2013/countries/'+ $scope.code, { cache: true }).then(function (response) {
        $scope.country = response.data;
        $scope.searchText = '';
        $scope.autocompleteData = [];
    });

	//*******************************************************
	$http.get('/api/v2013/index/select?cb=1394816088237&fl=id,identifier_s,title_t,description_t,url_ss,schema_EN_t,date_dt,government_s,government_EN_t,schema_s,number_d,aichiTarget_ss,reference_s,sender_s,meeting_ss,recipient_ss,symbol_s,eventCity_EN_t,eventCountry_EN_t,startDate_s,endDate_s,body_s,code_s,meeting_s,group_s,function_t,department_t,organization_t,summary_EN_t,reportType_EN_t,completion_EN_t,jurisdiction_EN_t,development_EN_t&q=(realm_ss:' + realm.value.toLowerCase() + ' or realm_ss:absch)+AND+schema_s:*+AND+((+schema_s:*+))+AND+((+government_s:'+ $scope.code.toLowerCase() +'+))+AND+(*:*)&rows=25&sort=createdDate_dt+desc,+title_t+asc&start=0&wt=json', { cache: true }).then(function (response) {
        $scope.absch_nfp = response.data;
        // console.log($scope.absch_nfp);

        $scope.absch_nfp.response.docs.forEach(function(document){
                $scope.autocompleteData.push({ title: document.title_t ? document.title_t: ''});
                $scope.autocompleteData.push({ title:document.description_t ? document.description_t: ''});
                $scope.autocompleteData.push({ title:document.department_t ? document.department_t: ''});
                $scope.autocompleteData.push({ title:document.organization_t ? document.organization_t: ''});
        });
    });

    $scope.$watch('absch_nfp', function(value){

        if(undefined == value) return;

        $scope.getFacets(value.response.docs);

    });

    $scope.getFacets = function(data){

        var linqObj = linqjs.from(data);
        $scope.nationalAuthority = linqObj.count(function(schema){

                        return schema.schema_s.toLowerCase() == 'authority';
                });
            //console.log(response.data.response.docs);
            $scope.nfpCount = linqObj.count(function(schema){
                // console.log(schema.schema_EN_t.toLowerCase() + ' ' + 'national focal point'.toLowerCase());
                        return schema.schema_s.toLowerCase() == 'focalpoint'.toLowerCase();
                });

            $scope.nationalMeasure =linqObj.count(function(schema){
                        return schema.schema_s.toLowerCase() =='measure';
                });

            $scope.Permit = linqObj.count(function(schema){
                        return schema.schema_s.toLowerCase() =='abspermit';
                });

            $scope.absCheckpoint = linqObj.count(function(schema){
                        return schema.schema_s.toLowerCase() =='abscheckpoint';
                });

            $scope.absCheckpointCommunique = linqObj.count(function(schema){
                        return schema.schema_s.toLowerCase() =='abscheckpointcommunique';
                });

            $scope.database = linqObj.count(function(schema){
                        return schema.schema_s.toLowerCase() =='database';
                });
            $scope.resource = linqObj.count(function(schema){
                        return schema.schema_s.toLowerCase() =='resource';
                });
    }

    $scope.$watch('searchText.$', function(value){

        if(undefined == value || value ==null || $scope.absch_nfp ==null) return;
        $scope.getFacets($filter('filter')($scope.absch_nfp.response.docs,value));

    });

}]);

})
<|endoftext|>