<commit_before>var Workspace = Backbone.View.extend({
    className: 'tab_container',

    events: {
        'click .sidebar_separator': 'toggle_sidebar',
        'change .cubes': 'new_query',
        'drop': 'remove_dimension'
    },

    template: function() {
        return Saiku.template.get('Workspace')({
            cube_navigation: Saiku.session.cube_navigation
        });
    },

    render: function() {
        // Load template
        $(this.el).html(this.template());

        // Generate toolbar
        this.toolbar.render();
        $(this.el).find('.workspace_toolbar').append($(this.toolbar.el));

        // Generate drop zones
        this.drop_zones.render();
        $(this.drop_zones.el)
            .insertAfter($(this.el).find('.workspace_toolbar'));

        // Activate sidebar for removing elements
        $(this.el).find('.sidebar')
            .droppable();

        return this;
    },

    clear: function() {
        // Prepare the workspace for a new query
        $(this.el).find('.workspace_results,.connectable')
            .html('');
    },

    initialize: function(args) {
        // Maintain `this` in jQuery event handlers
        _.bindAll(this, "adjust", "toggle_sidebar",
                "flash_cube_navigation", "new_query");

        // Generate toolbar and append to workspace
        this.toolbar = new WorkspaceToolbar({ workspace: this });
        this.tab = args.tab;

        // Create drop zones
        this.drop_zones = new WorkspaceDropZone({ workspace: this });

        // Adjust tab when selected
        this.tab.bind('tab:select', this.adjust);
        $(window).resize(this.adjust);

        // Flash cube navigation when rendered
        this.tab.bind('tab_rendered', this.flash_cube_navigation);
    },

    adjust: function() {
        // Adjust the height of the separator
        $separator = $(this.el).find('.sidebar_separator');
        $separator.height($("body").height() - 87);
    },

    toggle_sidebar: function() {
        // Toggle sidebar
        $(this.el).find('.sidebar').toggleClass('hide');
        var new_margin = $(this.el).find('.sidebar').hasClass('hide') ?
                5 : 265;
        $(this.el).find('.workspace_inner').css({ 'margin-left': new_margin });
    },

    flash_cube_navigation: function() {
        // Draw user's attention to cube navigation
        $(this.el).find('.cubes')
            .parent()
            .animate({ backgroundColor: '#AC1614' }, 'fast')
            .animate({ backgroundColor: '#fff' }, 'fast');
    },

    new_query: function() {
        // Delete the existing query
        if (this.query) {
            this.query.destroy();
        }

        // Initialize the new query
        var selected_cube = $(this.el).find('.cubes').val();
        this.query = new Query({ cube: selected_cube });
        this.query.workspace = this;

        // Create new DimensionList and MeasureList
        this.dimension_list = new DimensionList({
            workspace: this,
            dimension: Saiku.session.dimensions[selected_cube]
        });
        $(this.el).find('.dimension_tree').html('').append($(this.dimension_list.el));

        this.measure_list = new DimensionList({
            workspace: this,
            dimension: Saiku.session.measures[selected_cube]
        });
        $(this.el).find('.measure_tree').html('').append($(this.measure_list.el));

        // Clear workspace
        this.clear();

        // Make sure appropriate workspace buttons are enabled
        $(this.toolbar.el).find('.button').addClass('disabled_toolbar');
        $(this.toolbar.el).find('.auto,.non_empty,.toggle_fields')
            .removeClass('disabled_toolbar');
    },

    remove_dimension: function(event, ui) {
        this.drop_zones.remove_dimension(event, ui);
    }
});<commit_msg>Limit droppables<commit_after>var Workspace = Backbone.View.extend({
    className: 'tab_container',

    events: {
        'click .sidebar_separator': 'toggle_sidebar',
        'change .cubes': 'new_query',
        'drop': 'remove_dimension'
    },

    template: function() {
        return Saiku.template.get('Workspace')({
            cube_navigation: Saiku.session.cube_navigation
        });
    },

    render: function() {
        // Load template
        $(this.el).html(this.template());

        // Generate toolbar
        this.toolbar.render();
        $(this.el).find('.workspace_toolbar').append($(this.toolbar.el));

        // Generate drop zones
        this.drop_zones.render();
        $(this.drop_zones.el)
            .insertAfter($(this.el).find('.workspace_toolbar'));

        // Activate sidebar for removing elements
        $(this.el).find('.sidebar')
            .droppable({
                accept: '.d_measure, .d_dimension'
            });

        return this;
    },

    clear: function() {
        // Prepare the workspace for a new query
        $(this.el).find('.workspace_results,.connectable')
            .html('');
    },

    initialize: function(args) {
        // Maintain `this` in jQuery event handlers
        _.bindAll(this, "adjust", "toggle_sidebar",
                "flash_cube_navigation", "new_query");

        // Generate toolbar and append to workspace
        this.toolbar = new WorkspaceToolbar({ workspace: this });
        this.tab = args.tab;

        // Create drop zones
        this.drop_zones = new WorkspaceDropZone({ workspace: this });

        // Adjust tab when selected
        this.tab.bind('tab:select', this.adjust);
        $(window).resize(this.adjust);

        // Flash cube navigation when rendered
        this.tab.bind('tab_rendered', this.flash_cube_navigation);
    },

    adjust: function() {
        // Adjust the height of the separator
        $separator = $(this.el).find('.sidebar_separator');
        $separator.height($("body").height() - 87);
    },

    toggle_sidebar: function() {
        // Toggle sidebar
        $(this.el).find('.sidebar').toggleClass('hide');
        var new_margin = $(this.el).find('.sidebar').hasClass('hide') ?
                5 : 265;
        $(this.el).find('.workspace_inner').css({ 'margin-left': new_margin });
    },

    flash_cube_navigation: function() {
        // Draw user's attention to cube navigation
        $(this.el).find('.cubes')
            .parent()
            .animate({ backgroundColor: '#AC1614' }, 'fast')
            .animate({ backgroundColor: '#fff' }, 'fast');
    },

    new_query: function() {
        // Delete the existing query
        if (this.query) {
            this.query.destroy();
        }

        // Initialize the new query
        var selected_cube = $(this.el).find('.cubes').val();
        this.query = new Query({ cube: selected_cube });
        this.query.workspace = this;

        // Create new DimensionList and MeasureList
        this.dimension_list = new DimensionList({
            workspace: this,
            dimension: Saiku.session.dimensions[selected_cube]
        });
        $(this.el).find('.dimension_tree').html('').append($(this.dimension_list.el));

        this.measure_list = new DimensionList({
            workspace: this,
            dimension: Saiku.session.measures[selected_cube]
        });
        $(this.el).find('.measure_tree').html('').append($(this.measure_list.el));

        // Clear workspace
        this.clear();

        // Make sure appropriate workspace buttons are enabled
        $(this.toolbar.el).find('.button').addClass('disabled_toolbar');
        $(this.toolbar.el).find('.auto,.non_empty,.toggle_fields')
            .removeClass('disabled_toolbar');
    },

    remove_dimension: function(event, ui) {
        this.drop_zones.remove_dimension(event, ui);
    }
});<|endoftext|>


<commit_before>/*    Copyright 2019 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename);

const Sensor = require('./Sensor.js').Sensor;

const sem = require('../sensor/SensorEventManager.js').getInstance();

const extensionManager = require('./ExtensionManager.js')

const f = require('../net2/Firewalla.js');

const userConfigFolder = f.getUserConfigFolder();
const dnsConfigFolder = `${userConfigFolder}/dns`;
const safeSearchConfigFile = `${dnsConfigFolder}/safeSearch.conf`;

const configKey = "ext.safeSearch.config";

const updateInterval = 3600 * 1000 // once per hour

const rclient = require('../util/redis_manager.js').getRedisClient();

const domainBlock = require('../control/DomainBlock.js')();

const fs = require('fs');
const Promise = require('bluebird');
Promise.promisifyAll(fs);

const DNSMASQ = require('../extension/dnsmasq/dnsmasq.js');
const dnsmasq = new DNSMASQ();

const domainMapping = {
  youtube_strict: {
    "restrict.youtube.com": [
      "www.youtube.com",
      "m.youtube.com",
      "youtubei.googleapis.com",
      "youtube.googleapis.com",
      "www.youtube-nocookie.com"
    ]
  },
  youtube_moderate: {
    "restrictmoderate.youtube.com": [
      "www.youtube.com",
      "m.youtube.com",
      "youtubei.googleapis.com",
      "youtube.googleapis.com",
      "www.youtube-nocookie.com"
    ]
  },
  google: {
    "forcesafesearch.google.com": [
      "www.google.com"
    ]
  },
  bing: {
    "strict.bing.com": [
      "www.bing.com"
    ]
  }
}

class SafeSearchPlugin extends Sensor {

  run() {
    this.cachedDomainResult = {};

    extensionManager.registerExtension("safeSearch", this, {
      applyPolicy: this.applyPolicy,
      start: this.start,
      stop: this.stop
    })
  }

  async applyPolicy(policy) {
    log.info("Applying policy:", policy)

    const state = policy && policy.state;

    if(state === true) {
      return this.start(policy.config)
    } else {
      return this.stop();
    }
  }

  // {
  //   google: "on", // "on", "off"
  //   youtube: "strict", // "strict", "moderate", "off"
  //   bing: "on" // "on", "off"
  // }

  getMappingKey(type, value) {
    switch(type) {
    case "google":
      return "google";
    case "bing":
      return "bing";
    case "youtube":
      switch(value) {
        case "strict":
          return "youtube_strict";
        case "moderate":
          return "youtube_moderate";
        default:
          return null;
      }
    default:
      return null;
    }
  }

  getMappingResult(key) {
    if(key) {
      return domainMapping[key];
    } else {
      return null;
    }
  }

  addToCache(domain ,result) {
    if(this.cachedDomainResult[domain]) {
      // do nothing
      return;
    }

    this.cachedDomainResult[domain] = result;

    setTimeout(() => {
      delete this.cachedDomainResult[domain];
    }, updateInterval);
  }

  async getDNSMasqEntry(domainToBeRedirect, ipAddress) {
    return `address=/${domainToBeRedirect}/${ipAddress}`;
  }

  async processMappingResult(mappingEntry) {
    const safeDomains = Object.keys(mappingEntry);

    const entries = await Promise.all(safeDomains.map(async (safeDomain) => {
      const ips = await domainBlock.resolveDomain(safeDomain);

      if(ips.length > 0) {
        const ip = ips[0];
        const domainsToBeRedirect = mappingEntry[safeDomain];
        return Promise.all(domainsToBeRedirect.map(async (domain) => {
          return this.getDNSMasqEntry(domain, ip);
        }));
      } else {
        return [];
      }
    }));

    const concat = (x,y) => x.concat(y)
    const flatMap = (f,xs) => xs.map(f).reduce(concat, [])

    const flattedEntries = flatMap(x => x, entries);

    return flattedEntries;
  }

  async generateConfigFile(config) {
    let entries = [];

    for(const type in config) {
      const value = config[type];
      if(value === 'off') {
        continue;
      }

      const key = this.getMappingKey(type, value);
      const result = this.getMappingResult(key);
      if(result) {
        const thisEntries = await this.processMappingResult(result);
        entries = entries.concat(thisEntries);
      }
    }

    entries.push("");

    await fs.writeFileAsync(safeSearchConfigFile, entries.join("\n"));
  }

  async deleteConfigFile() {
    return fs.unlinkAsync(safeSearchConfigFile).catch(() => undefined);
  }

  async start(config) {
    await this.generateConfigFile(config);
    await dnsmasq.start(true);
    return;
  }

  async stop() {
    await this.deleteConfigFile();
    await dnsmasq.start(true);
  }
}

module.exports = SafeSearchPlugin
<commit_msg>add duckduckgo<commit_after>/*    Copyright 2019 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename);

const Sensor = require('./Sensor.js').Sensor;

const sem = require('../sensor/SensorEventManager.js').getInstance();

const extensionManager = require('./ExtensionManager.js')

const f = require('../net2/Firewalla.js');

const userConfigFolder = f.getUserConfigFolder();
const dnsConfigFolder = `${userConfigFolder}/dns`;
const safeSearchConfigFile = `${dnsConfigFolder}/safeSearch.conf`;

const configKey = "ext.safeSearch.config";

const updateInterval = 3600 * 1000 // once per hour

const rclient = require('../util/redis_manager.js').getRedisClient();

const domainBlock = require('../control/DomainBlock.js')();

const fs = require('fs');
const Promise = require('bluebird');
Promise.promisifyAll(fs);

const DNSMASQ = require('../extension/dnsmasq/dnsmasq.js');
const dnsmasq = new DNSMASQ();

const domainMapping = {
  youtube_strict: {
    "restrict.youtube.com": [
      "www.youtube.com",
      "m.youtube.com",
      "youtubei.googleapis.com",
      "youtube.googleapis.com",
      "www.youtube-nocookie.com"
    ]
  },
  youtube_moderate: {
    "restrictmoderate.youtube.com": [
      "www.youtube.com",
      "m.youtube.com",
      "youtubei.googleapis.com",
      "youtube.googleapis.com",
      "www.youtube-nocookie.com"
    ]
  },
  google: {
    "forcesafesearch.google.com": [
      "www.google.com"
    ]
  },
  bing: {
    "strict.bing.com": [
      "www.bing.com"
    ]
  },
  duckduckgo: {
    "safe.duckduckgo.com": [
      "duckduckgo.com"
    ]
  }
}

class SafeSearchPlugin extends Sensor {

  run() {
    this.cachedDomainResult = {};

    extensionManager.registerExtension("safeSearch", this, {
      applyPolicy: this.applyPolicy,
      start: this.start,
      stop: this.stop
    })
  }

  async applyPolicy(policy) {
    log.info("Applying policy:", policy)

    const state = policy && policy.state;

    if(state === true) {
      return this.start(policy.config)
    } else {
      return this.stop();
    }
  }

  // {
  //   google: "on", // "on", "off"
  //   youtube: "strict", // "strict", "moderate", "off"
  //   bing: "on" // "on", "off"
  // }

  getMappingKey(type, value) {
    switch(type) {
    case "youtube":
      switch(value) {
        case "strict":
          return "youtube_strict";
        case "moderate":
          return "youtube_moderate";
        default:
          return null;
      }
    default:
      return type;
    }
  }

  getMappingResult(key) {
    if(key) {
      return domainMapping[key];
    } else {
      return null;
    }
  }

  addToCache(domain ,result) {
    if(this.cachedDomainResult[domain]) {
      // do nothing
      return;
    }

    this.cachedDomainResult[domain] = result;

    setTimeout(() => {
      delete this.cachedDomainResult[domain];
    }, updateInterval);
  }

  async getDNSMasqEntry(domainToBeRedirect, ipAddress) {
    return `address=/${domainToBeRedirect}/${ipAddress}`;
  }

  async processMappingResult(mappingEntry) {
    const safeDomains = Object.keys(mappingEntry);

    const entries = await Promise.all(safeDomains.map(async (safeDomain) => {
      const ips = await domainBlock.resolveDomain(safeDomain);

      if(ips.length > 0) {
        const ip = ips[0];
        const domainsToBeRedirect = mappingEntry[safeDomain];
        return Promise.all(domainsToBeRedirect.map(async (domain) => {
          return this.getDNSMasqEntry(domain, ip);
        }));
      } else {
        return [];
      }
    }));

    const concat = (x,y) => x.concat(y)
    const flatMap = (f,xs) => xs.map(f).reduce(concat, [])

    const flattedEntries = flatMap(x => x, entries);

    return flattedEntries;
  }

  async generateConfigFile(config) {
    let entries = [];

    for(const type in config) {
      const value = config[type];
      if(value === 'off') {
        continue;
      }

      const key = this.getMappingKey(type, value);
      const result = this.getMappingResult(key);
      if(result) {
        const thisEntries = await this.processMappingResult(result);
        entries = entries.concat(thisEntries);
      }
    }

    entries.push("");

    await fs.writeFileAsync(safeSearchConfigFile, entries.join("\n"));
  }

  async deleteConfigFile() {
    return fs.unlinkAsync(safeSearchConfigFile).catch(() => undefined);
  }

  async start(config) {
    await this.generateConfigFile(config);
    await dnsmasq.start(true);
    return;
  }

  async stop() {
    await this.deleteConfigFile();
    await dnsmasq.start(true);
  }
}

module.exports = SafeSearchPlugin
<|endoftext|>


<commit_before>#!/usr/bin/env node
var _ = require('lodash')
var config = require('config3')
var log = require('./log')
var emails = require('.')
var mustache = require('mustache')
var dailySummary = require('../../reports/dailySummary')

var BODY = [
  'Entries Created Today: {{entriesCreated}}',
  'Entries Updated Today: {{entriesUpdated}}',
  'Total Entries: {{totalEntries}}',
  'Total Users: {{totalUsers}}',
  'New Users Today: {{usersCreated}}'
].join('\n')
var SUBJECT = '{{appName}} daily summary {{for}}: {{usersCreated}} new users'

function build (callback) {
  dailySummary(function (error, result) {
    if (error) {
      callback(error)
      return
    }
    if (result.usersCreated < 1) {
      log.info('Daily summary report ran but no new users so no email')
      callback(new Error('Not enough activity to warrant email'))
      return
    }
    _.extend(result, _.pick(config, 'appName'))
    var email = {
      to: config.email.to,
      from: config.email.from,
      subject: mustache.render(SUBJECT, result),
      text: mustache.render(BODY, result)
    }
    callback(null, email)
  })
}

function send (callback) {
  build(function (error, email) {
    if (error) {
      callback(error)
      return
    }
    emails.send(email, callback)
  })
}

module.exports = send
if (require.main === module) {
  send(function () {
    setTimeout(process.exit, 200)
  })
}
<commit_msg>Fix app symlink path mistakes<commit_after>#!/usr/bin/env node
var _ = require('lodash')
var config = require('config3')
var log = require('../log')
var emails = require('./')
var mustache = require('mustache')
var dailySummary = require('../reports/dailySummary')

var BODY = [
  'Entries Created Today: {{entriesCreated}}',
  'Entries Updated Today: {{entriesUpdated}}',
  'Total Entries: {{totalEntries}}',
  'Total Users: {{totalUsers}}',
  'New Users Today: {{usersCreated}}'
].join('\n')
var SUBJECT = '{{appName}} daily summary {{for}}: {{usersCreated}} new users'

function build (callback) {
  dailySummary(function (error, result) {
    if (error) {
      callback(error)
      return
    }
    if (result.usersCreated < 1) {
      log.info('Daily summary report ran but no new users so no email')
      callback(new Error('Not enough activity to warrant email'))
      return
    }
    _.extend(result, _.pick(config, 'appName'))
    var email = {
      to: config.email.to,
      from: config.email.from,
      subject: mustache.render(SUBJECT, result),
      text: mustache.render(BODY, result)
    }
    callback(null, email)
  })
}

function send (callback) {
  build(function (error, email) {
    if (error) {
      callback(error)
      return
    }
    emails.send(email, callback)
  })
}

module.exports = send
if (require.main === module) {
  send(function () {
    setTimeout(process.exit, 200)
  })
}
<|endoftext|>


<commit_before>const containerdSplitRegexp = /^(.+[stdout|stderr] [F|P]) /

// Dictionary to store sources and log lines
/**
 * Key: sourceName
 * Value (Object): { streamFlag, previousStreamFlag, logLines }
 */
const sources = {}

/**
 * sourceName - origin of the log, e.g. file name
 * config - properties from the config section for this plugin
 * data - the log message as string
 * callback - callback function (err, data).
 */
function parseK8sFileName (sourceName) {
  /**
   * SAMPLE sourceName *
   * ***************** *
   * sourceName: /var/log/containers/app-77b4d5595b-hmjxs_default_app-80209fd578c6be842b5b8a2d6389227ccab0196b7b658bd245eed4767c9b843c.log
   * fileName: app-77b4d5595b-hmjxs_default_app-80209fd578c6be842b5b8a2d6389227ccab0196b7b658bd245eed4767c9b843c.log
   * meta: {
   *   0: app-77b4d5595b-hmjxs, // pod
   *   1: default // namespace
   *   2: app-80209fd578c6be842b5b8a2d6389227ccab0196b7b658bd245eed4767c9b843c.log // container with .log suffix
   * }
   */

  // cut path from /var/log/containers/<pod_name>_<pod_namespace>_<container_name>-<container_id>.log
  // Reference: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/node/kubelet-cri-logging.md
  const index = sourceName.lastIndexOf('/')
  const fileName = sourceName.substr(index + 1, sourceName.length)
  const meta = fileName.split('_')
  const info = {}

  if (meta.length === 3) {
    const { 0: name, 1: namespace, 2: containerWithLogSuffix } = meta
    info.kubernetes = {
      pod: {
        name,
        container: {}
      },
      namespace
    }

    const positionOfNameAndIdSeparator = containerWithLogSuffix.lastIndexOf('-')
    const positionOfDotLog = containerWithLogSuffix.indexOf('.')
    const containerId = containerWithLogSuffix.substring(
      positionOfNameAndIdSeparator + 1,
      positionOfDotLog
    )
    const containerName = containerWithLogSuffix.substring(0, index)

    if (containerId) {
      info.kubernetes.pod.container.id = containerId
    }
    if (containerName) {
      info.kubernetes.pod.container.name = containerName
    }
  }
  return info
}

module.exports = function (context, config, data, callback) {
  try {
    const sections = data.split(containerdSplitRegexp)
    if (sections && sections.length === 3) {
      const k8sInfo = parseK8sFileName(context.sourceName)
      const meta = sections[1].split(' ')
      const logLine = sections[2]

      if (meta.length === 3 && meta[0]) {
        k8sInfo['@timestamp'] = new Date(meta[0])
        k8sInfo.streamName = meta[1]
        k8sInfo.streamFlag = meta[2]

        const sourceName = context.sourceName
        if (sources[sourceName] === undefined) {
          sources[sourceName] = {}
        }
        sources[sourceName].streamFlag = k8sInfo.streamFlag

        if (sources[sourceName].streamFlag === 'P') {
          if (sources[sourceName].logLines === undefined) {
            sources[sourceName].logLines = []
          }

          sources[sourceName].logLines.push(logLine)
          sources[sourceName].previousStreamFlag = 'P'

          return callback(null, null)
        }

        if (
          sources[sourceName].streamFlag === 'F' &&
          sources[sourceName].previousStreamFlag === 'P'
        ) {
          sources[sourceName].logLines.push(logLine)
          const joinedLogLine = sources[sourceName].logLines.join(' ')
          delete sources[sourceName]

          // a special property in context object to propagate fields to
          // the parsed object after parsing -> all logs will be enriched k8s metadata
          context.enrichEvent = k8sInfo
          return callback(null, joinedLogLine)
        }
      }

      return callback(null, logLine)
    }
    return callback(null, data)
  } catch (err) {
    console.error(err)
    return callback(null, data)
  }
}

// test function
// if (require.main === module) {
//   module.exports(
//     {
//       sourceName:
//         '/var/log/containers/busybox2_default_busybox-5f03725b871fe3f2cbfdde7864100a12aed2708d759bea14f8d41656accba8f6.log'
//     },
//     {},
//     '2019-03-28T23:13:41.945317977Z stdout F Mar 28 23:13:41 kube-mil01-pa1934121294964badacb5ddd3753d504c-w1 local1.notice haproxy[8]: Proxy masteretcdfrontend started',
//     console.log
//   )
// }
<commit_msg>bugfix: fixed regex matching and added debug log lines<commit_after>// const containerdSplitRegexp = /^(.+[stdout|stderr] [F|P]) / // old
const containerdSplitRegexp = /^(.+)\s(stdout|stderr)\s(F|P)\s(.*)/ // new

// Dictionary to store sources and log lines
/**
 * Key: sourceName
 * Value (Object): { streamFlag, previousStreamFlag, logLines }
 */
const sources = {}

/**
 * sourceName - origin of the log, e.g. file name
 * config - properties from the config section for this plugin
 * data - the log message as string
 * callback - callback function (err, data).
 */
function parseK8sFileName (sourceName) {
  /**
   * SAMPLE sourceName *
   * ***************** *
   * sourceName: /var/log/containers/app-77b4d5595b-hmjxs_default_app-80209fd578c6be842b5b8a2d6389227ccab0196b7b658bd245eed4767c9b843c.log
   * fileName: app-77b4d5595b-hmjxs_default_app-80209fd578c6be842b5b8a2d6389227ccab0196b7b658bd245eed4767c9b843c.log
   * meta: {
   *   0: app-77b4d5595b-hmjxs, // pod
   *   1: default // namespace
   *   2: app-80209fd578c6be842b5b8a2d6389227ccab0196b7b658bd245eed4767c9b843c.log // container with .log suffix
   * }
   */

  // cut path from /var/log/containers/<pod_name>_<pod_namespace>_<container_name>-<container_id>.log
  // Reference: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/node/kubelet-cri-logging.md
  const index = sourceName.lastIndexOf('/')
  const fileName = sourceName.substr(index + 1, sourceName.length)
  const meta = fileName.split('_')
  const info = {}

  if (meta.length === 3) {
    const { 0: name, 1: namespace, 2: containerWithLogSuffix } = meta
    info.kubernetes = {
      pod: {
        name,
        container: {}
      },
      namespace
    }

    const positionOfNameAndIdSeparator = containerWithLogSuffix.lastIndexOf('-')
    const positionOfDotLog = containerWithLogSuffix.indexOf('.')
    const containerId = containerWithLogSuffix.substring(
      positionOfNameAndIdSeparator + 1,
      positionOfDotLog
    )
    const containerName = containerWithLogSuffix.substring(0, index)

    if (containerId) {
      info.kubernetes.pod.container.id = containerId
    }
    if (containerName) {
      info.kubernetes.pod.container.name = containerName
    }
  }
  return info
}

module.exports = function (context, config, data, callback) {
  console.log('debug 1:', data)

  try {
    const sections = data.split(containerdSplitRegexp)
    console.log('debug 2:', sections)
    console.log('debug 3:', sections.length)

    if (sections && sections.length === 6) {
      const k8sInfo = parseK8sFileName(context.sourceName)
      const timestamp = sections[1]
      const streamName = sections[2]
      const streamFlag = sections[3]
      const logLine = sections[4]

      console.log('debug 4: k8sInfo', k8sInfo)
      console.log('debug 5: timestamp', timestamp)
      console.log('debug 6: streamName', streamName)
      console.log('debug 7: streamFlag', streamFlag)
      console.log('debug 8: logLine', logLine)

      if (timestamp && streamName && streamFlag) {
        k8sInfo['@timestamp'] = new Date(timestamp)
        k8sInfo.streamName = streamName
        k8sInfo.streamFlag = streamFlag

        const sourceName = context.sourceName
        if (sources[sourceName] === undefined) {
          sources[sourceName] = {}
        }
        sources[sourceName].streamFlag = k8sInfo.streamFlag

        if (sources[sourceName].streamFlag === 'P') {
          if (sources[sourceName].logLines === undefined) {
            sources[sourceName].logLines = []
          }

          sources[sourceName].logLines.push(logLine)
          sources[sourceName].previousStreamFlag = 'P'

          return callback(null, null)
        }

        if (
          sources[sourceName].streamFlag === 'F' &&
          sources[sourceName].previousStreamFlag === 'P'
        ) {
          sources[sourceName].logLines.push(logLine)
          const joinedLogLine = sources[sourceName].logLines.join(' ')
          delete sources[sourceName]

          // a special property in context object to propagate fields to
          // the parsed object after parsing -> all logs will be enriched k8s metadata
          context.enrichEvent = k8sInfo
          return callback(null, joinedLogLine)
        }
      }

      return callback(null, logLine)
    }
    return callback(null, data)
  } catch (err) {
    console.error(err)
    return callback(null, data)
  }
}

// test function
// if (require.main === module) {
//   module.exports(
//     {
//       sourceName:
//         '/var/log/containers/busybox2_default_busybox-5f03725b871fe3f2cbfdde7864100a12aed2708d759bea14f8d41656accba8f6.log'
//     },
//     {},
//     '2019-03-28T23:13:41.945317977Z stdout F Mar 28 23:13:41 kube-mil01-pa1934121294964badacb5ddd3753d504c-w1 local1.notice haproxy[8]: Proxy masteretcdfrontend started',
//     console.log
//   )
// }
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { FormattedMessage } from 'react-intl'
import styled from 'styled-components'

import {
  Button,
  Dialog,
  DialogActions,
  DialogContent,
  DialogContentText,
  DialogTitle,
  Typography,
  TextField
} from '@material-ui/core'

const Container = styled.div`
  display: inline-block;
  margin-right: 1rem;
`

class MessageAssignment extends Component {
  constructor (props) {
    super(props)
    this.state = { message: '' }
  }

  static defaultProps = { visible: false }

  static propTypes = {
    visible: PropTypes.bool,
    task: PropTypes.object,
    messageAction: PropTypes.func
  }

  onChangeMessage = event => this.setState({ message: event.target.value })

  messageAssignment = async () => {
    const { task, messageAction, assign } = this.props
    const { message } = this.state
    await messageAction(task.id, assign.id, message)
    this.props.onClose()
  }

  render () {
    const { visible, onClose } = this.props
    const { message } = this.state

    return (
      <Container>
        <Dialog
          open={ visible }
          onClose={ onClose }
          aria-labelledby='form-dialog-title'
        >
          <DialogTitle id='form-dialog-title'>
            <FormattedMessage id='task.assignment.message.user' defaultMessage='Send a message to this user' />
          </DialogTitle>
          <DialogContent>
            <DialogContentText>
              <Typography component='div'>
                <FormattedMessage id='task.assignment.message.reason' defaultMessage='Send a message to this user interested.' />
              </Typography>
            </DialogContentText>
            <TextField
              onChange={ this.onChangeMessage }
              autoFocus
              name='message'
              multiline
              rows='3'
              fullWidth
            />
          </DialogContent>
          <DialogActions>
            <Button onClick={ this.closeModal } color='primary'>
              <FormattedMessage id='task.assignment.actions.cancel' defaultMessage='Cancel' />
            </Button>
            <Button disabled={ message.length === 0 } onClick={ this.messageAssignment } variant='contained' color='secondary' >
              <FormattedMessage id='task.assignment.message.send' defaultMessage='Send Message' />
            </Button>
          </DialogActions>
        </Dialog>
      </Container>
    )
  }
}

export default MessageAssignment
<commit_msg>fixing dialog cancel when send message<commit_after>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { FormattedMessage } from 'react-intl'
import styled from 'styled-components'

import {
  Button,
  Dialog,
  DialogActions,
  DialogContent,
  DialogContentText,
  DialogTitle,
  Typography,
  TextField
} from '@material-ui/core'

const Container = styled.div`
  display: inline-block;
  margin-right: 1rem;
`

class MessageAssignment extends Component {
  constructor (props) {
    super(props)
    this.state = { message: '' }
  }

  static defaultProps = { visible: false }

  static propTypes = {
    visible: PropTypes.bool,
    task: PropTypes.object,
    messageAction: PropTypes.func
  }

  onChangeMessage = event => this.setState({ message: event.target.value })

  messageAssignment = async () => {
    const { task, messageAction, assign } = this.props
    const { message } = this.state
    await messageAction(task.id, assign.id, message)
    this.props.onClose()
  }

  render () {
    const { visible, onClose } = this.props
    const { message } = this.state

    return (
      <Container>
        <Dialog
          open={ visible }
          onClose={ onClose }
          aria-labelledby='form-dialog-title'
        >
          <DialogTitle id='form-dialog-title'>
            <FormattedMessage id='task.assignment.message.user' defaultMessage='Send a message to this user' />
          </DialogTitle>
          <DialogContent>
            <DialogContentText>
              <Typography component='div'>
                <FormattedMessage id='task.assignment.message.reason' defaultMessage='Send a message to this user interested.' />
              </Typography>
            </DialogContentText>
            <TextField
              onChange={ this.onChangeMessage }
              autoFocus
              name='message'
              multiline
              rows='3'
              fullWidth
            />
          </DialogContent>
          <DialogActions>
            <Button onClick={ onClose } color='primary'>
              <FormattedMessage id='task.assignment.actions.cancel' defaultMessage='Cancel' />
            </Button>
            <Button disabled={ message.length === 0 } onClick={ this.messageAssignment } variant='contained' color='secondary' >
              <FormattedMessage id='task.assignment.message.send' defaultMessage='Send Message' />
            </Button>
          </DialogActions>
        </Dialog>
      </Container>
    )
  }
}

export default MessageAssignment
<|endoftext|>


<commit_before>/*!
 * Copyright 2012-2014 Mattias Fliesberg
 *
 * This file is part of opmuse.
 *
 * opmuse is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * opmuse is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with opmuse.  If not, see <http://www.gnu.org/licenses/>.
 */

define([
        'jquery',
        'inheritance',
        'ajaxify',
        'typeahead',
        'sprintf',
        'domReady!'
    ], function ($, inheritance, ajaxify) {

    'use strict';

    var instance = null;

    var Edit = Class.extend({
        init: function () {
            if (instance !== null) {
                throw Error('Only one instance of Edit allowed!');
            }

            var that = this;

            $('#main').on('ajaxifyInit', function (event) {
                that.internalInit();
            });

            that.internalInit();
        },
        initTypeahead: function () {
            this.createTypeahead('artist');
            this.createTypeahead('album');
            this.createTypeahead('track');
        },
        createTypeahead: function (type) {
            $(sprintf('#edit input[name=%ss]', type)).typeahead('destroy');
            $(sprintf('#edit input[name=%ss]:not(.locked)', type)).each(function () {
                var input = this;

                $(input).typeahead({
                    remote: {
                        url: sprintf('/library/search/api/%s?query=%%QUERY', type),
                        filter: function (parsedResponse) {
                            var originalValue = $(input).data('originalValue');
                            var index = parsedResponse.indexOf(originalValue);

                            if (index != -1) {
                                parsedResponse.splice(index, 1);
                            }

                            return parsedResponse;
                        }
                    }
                });
            });
        },
        internalInit: function () {
            var that = this;

            that.initTypeahead();

            $('#edit form button').click(function (event) {
                var form = $(this).closest('form');
                var data = $(form).serialize();

                // serialize the button too..
                var name = $(this).attr('name');

                if (typeof name != 'undefined' && name !== null) {
                    data += '&' + name + '=' + name;
                }

                $.ajax($(form).attr('action'), {
                    type: 'post',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded'
                    },
                    data: data,
                    success: function (data) {
                        ajaxify.setPageInDom(data);
                    },
                    error: function (xhr) {
                        ajaxify.setPageInDom(xhr.responseText);
                    }
                });

                return false;
            });

            $('#edit .lock')
                .off('click.edit')
                .on('click.edit',
                    function (event) {
                        var type = null;

                        var th = $(this).closest('th');

                        if (th.is('.album')) {
                            type = 'album';
                        } else if (th.is('.artist')) {
                            type = 'artist';
                        } else if (th.is('.date')) {
                            type = 'date';
                        } else if (th.is('.disc')) {
                            type = 'disc';
                        }

                        var selector = '#edit input.' + type;

                        if ($(this).hasClass('locked')) {
                            $(selector)
                                .removeAttr('readonly')
                                .off('keyup.lock, blur.lock')
                                .removeClass('locked');

                            $(this).removeClass('locked');
                        } else {
                            $(selector).addClass('locked');
                            $(this).addClass('locked');

                            $($(selector).attr('readonly', 'readonly').get(0))
                                .removeAttr('readonly').removeClass('locked')
                                .on('keyup.lock, blur.lock',
                                    function (event) {
                                        $(selector).val($(this).val());
                                    }
                                ).blur();
                        }

                        that.initTypeahead();

                        return false;
                    }
                );

                $('#edit input').off('focus.marker').on('focus.marker', function (event) {
                    $('#edit tr').removeClass('active');
                    $(this).closest('tr').addClass('active');
                });
        }
    });

    return (function () {
        if (instance === null) {
            instance = new Edit();
        }

        return instance;
    })();

});
<commit_msg>corrected /search url for edit autocomplete<commit_after>/*!
 * Copyright 2012-2014 Mattias Fliesberg
 *
 * This file is part of opmuse.
 *
 * opmuse is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * opmuse is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with opmuse.  If not, see <http://www.gnu.org/licenses/>.
 */

define([
        'jquery',
        'inheritance',
        'ajaxify',
        'typeahead',
        'sprintf',
        'domReady!'
    ], function ($, inheritance, ajaxify) {

    'use strict';

    var instance = null;

    var Edit = Class.extend({
        init: function () {
            if (instance !== null) {
                throw Error('Only one instance of Edit allowed!');
            }

            var that = this;

            $('#main').on('ajaxifyInit', function (event) {
                that.internalInit();
            });

            that.internalInit();
        },
        initTypeahead: function () {
            this.createTypeahead('artist');
            this.createTypeahead('album');
            this.createTypeahead('track');
        },
        createTypeahead: function (type) {
            $(sprintf('#edit input[name=%ss]', type)).typeahead('destroy');
            $(sprintf('#edit input[name=%ss]:not(.locked)', type)).each(function () {
                var input = this;

                $(input).typeahead({
                    remote: {
                        url: sprintf('/search/api/%s?query=%%QUERY', type),
                        filter: function (parsedResponse) {
                            var originalValue = $(input).data('originalValue');
                            var index = parsedResponse.indexOf(originalValue);

                            if (index != -1) {
                                parsedResponse.splice(index, 1);
                            }

                            return parsedResponse;
                        }
                    }
                });
            });
        },
        internalInit: function () {
            var that = this;

            that.initTypeahead();

            $('#edit form button').click(function (event) {
                var form = $(this).closest('form');
                var data = $(form).serialize();

                // serialize the button too..
                var name = $(this).attr('name');

                if (typeof name != 'undefined' && name !== null) {
                    data += '&' + name + '=' + name;
                }

                $.ajax($(form).attr('action'), {
                    type: 'post',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded'
                    },
                    data: data,
                    success: function (data) {
                        ajaxify.setPageInDom(data);
                    },
                    error: function (xhr) {
                        ajaxify.setPageInDom(xhr.responseText);
                    }
                });

                return false;
            });

            $('#edit .lock')
                .off('click.edit')
                .on('click.edit',
                    function (event) {
                        var type = null;

                        var th = $(this).closest('th');

                        if (th.is('.album')) {
                            type = 'album';
                        } else if (th.is('.artist')) {
                            type = 'artist';
                        } else if (th.is('.date')) {
                            type = 'date';
                        } else if (th.is('.disc')) {
                            type = 'disc';
                        }

                        var selector = '#edit input.' + type;

                        if ($(this).hasClass('locked')) {
                            $(selector)
                                .removeAttr('readonly')
                                .off('keyup.lock, blur.lock')
                                .removeClass('locked');

                            $(this).removeClass('locked');
                        } else {
                            $(selector).addClass('locked');
                            $(this).addClass('locked');

                            $($(selector).attr('readonly', 'readonly').get(0))
                                .removeAttr('readonly').removeClass('locked')
                                .on('keyup.lock, blur.lock',
                                    function (event) {
                                        $(selector).val($(this).val());
                                    }
                                ).blur();
                        }

                        that.initTypeahead();

                        return false;
                    }
                );

                $('#edit input').off('focus.marker').on('focus.marker', function (event) {
                    $('#edit tr').removeClass('active');
                    $(this).closest('tr').addClass('active');
                });
        }
    });

    return (function () {
        if (instance === null) {
            instance = new Edit();
        }

        return instance;
    })();

});
<|endoftext|>


<commit_before>/* global define:true */
define([
    'pixi',
    'gs',
    'signals',
    'experience/issue',
    'experience/responsive'
], function (
    PIXI,
    gs,
    signals,
    Issue,
    Responsive
) {
    'use strict';

    var Topic = function (data, index, issues, fakes) {
        this._radius = 70;
        this._linearDist = 40;
        this._linearWidth = 100;
        this.isOver = false;

        this._data = data;
        this._index = index;
        this._issues = issues;
        this._fakes = fakes;

        this.elm = new PIXI.DisplayObjectContainer();

        this.mouseOverS = new signals.Signal();
        this.mouseOutS = new signals.Signal();

        // this._topicArea;
        // this._linearArea;
        // this._topicTitle;
        // this._topicDesc;

        Topic.TOPICS.push(this);
    };

    Topic.TOPICS = [];
    Topic.SELECTED_TOPIC = null;

    Topic.prototype.setup = function () {
        // topic mouse over
        this._topicArea = new PIXI.Graphics();
        this._topicArea.i = this._index;
        this._topicArea.hitArea = new PIXI.Rectangle(
            -this._radius,
            -this._radius,
            this._radius * 2,
            this._radius * 2);
        this._topicArea.interactive = true;
        this._topicArea.buttonMode = true;
        this.elm.addChild(this._topicArea);
        this._topicArea.mouseover = this._mouseOver.bind(this);
        this._topicArea.tap = this._mouseOver.bind(this);

        // topic mouse out area
        var aMargin = 20;
        this._linearArea = new PIXI.Graphics();
        this._linearArea.i = this._index;
        this._linearArea.x = - 100 - aMargin / 2;
        this._linearArea.interactive = true;
        this._linearArea.buttonMode = true;
        this._linearArea.hitArea = new PIXI.Rectangle(
            -this._linearWidth / 2,
            -(this._linearDist * this._issues.length / 2) - aMargin,
            this._linearWidth + 100 + aMargin,
            (this._linearDist * this._issues.length) + aMargin);

        // title
        this._topicTitle = new PIXI.Text(this._data.getName().toUpperCase(),
            {font: '300 22px "Fira Sans", sans-serif', fill: '#222222'});
        this.elm.addChild(this._topicTitle);
        this._topicTitle.resolution = Responsive.RATIO;
        this._topicTitle.x = Math.round(-(this._topicTitle.width / Responsive.RATIO) / 2);
        this._topicTitle.y = Math.round(-(this._topicTitle.height / Responsive.RATIO) / 2);

        // description
        this._topicDesc = new PIXI.Text(this._data.getTagline(),
            {
                font: '300 14px "Fira Sans", sans-serif',
                fill: '#666666',
                wordWrap: true,
                wordWrapWidth: 200,
                align: 'center'
        });
        this.elm.addChild(this._topicDesc);
        this._topicDesc.resolution = Responsive.RATIO;
        this._topicDesc.x = Math.round(-(this._topicDesc.width / Responsive.RATIO) / 2);
        this._topicDesc.y = Math.round(this._radius + 50);

        // topic issue elements
        var issue;
        var i;
        var tH = ((this._topicTitle.height / Responsive.RATIO) / 2) + 5; // half title height
        for(i = 0; i < this._issues.length; i ++) {
            issue = this._issues[i];
            issue.setTextAlwaysVisible(false);
            issue.setIsInteractive(false);

            issue.topicX = (Math.random() * this._radius * 2) - this._radius;
             // from -(radius - tH / 2) to (radius - tH / 2)
            issue.topicY = (Math.random() * (this._radius - tH) * 2) - (this._radius - tH);
            // dont go between -tH/2 and tH/2 (the title area)
            issue.topicY += tH * (issue.topicY > 0 ? 1 : -1);
        }

        for(i = 0; i < this._fakes.length; i ++) {
            issue = this._fakes[i];
            issue.topicX = (Math.random() * this._radius * 2) - this._radius;
            issue.topicY = (Math.random() * this._radius * 2) - this._radius;
        }

        return this;
    };

    Topic.prototype.show = function () {
        var issue;
        var i;
        for(i = 0; i < this._issues.length; i ++) {
            issue = this._issues[i];
            issue.setMode(Issue.MODE_TOPICS);
            issue.moveTo(
                this.elm.x + issue.topicX,
                this.elm.y + issue.topicY,
                issue._resumeStaticAnimation.bind(issue),
                {alpha: 1});
            issue.topicMouseOver = this._mouseOverIssue.bind(this);
            issue.topicMouseOut = this._mouseOutIssue.bind(this);
            issue.topicTap = this._tapIssue.bind(this);
            issue.mouseOverS.add(issue.topicMouseOver);
            issue.mouseOutS.add(issue.topicMouseOut);
            issue.tapS.add(issue.topicTap);
        }

        for(i = 0; i < this._fakes.length; i ++) {
            issue = this._fakes[i];
            gs.TweenMax.to(
                issue.elm,
                0.3,
                {
                    alpha: issue.implodeAlpha,
                    x: this.elm.x + issue.topicX,
                    y: this.elm.y + issue.topicY,
                    overwrite: true, roundProps: 'x,y'
                }
            );
        }
    };

    Topic.prototype.hide = function () {
        var issue;
        var i;

        for(i = 0; i < this._issues.length; i ++) {
            issue = this._issues[i];
            issue.mouseOverS.remove(issue.topicMouseOver);
            issue.mouseOutS.remove(issue.topicMouseOut);
            issue.tapS.remove(issue.topicTap);
        }

        for(i = 0; i < this._fakes.length; i ++) {
            this._fakes[i].explode();
        }
    };

    Topic.prototype._mouseOverIssue = function (issue) {
        if (this.isOver) {
            this.selectedIssue = issue;
            issue.mouseOver(this.mousePosition);
        }
    };

    Topic.prototype._mouseOutIssue = function (issue) {
        if (this.isOver) {
            this.selectedIssue = null;
            issue.mouseOut();
        }
    };

    Topic.prototype._tapIssue = function (issue) {
        issue._onPress();
    };

    /**
    * When hovering a topic
    */
    Topic.prototype._mouseOver = function () {
        // dont mouse in if theres another one still selected
        if (Topic.SELECTED_TOPIC) {
            return;
        }

        Topic.SELECTED_TOPIC = this;

        var i;
        var issue;

        // move issues to a linear position
        var posX;
        var posY;
        for(i = 0; i < this._issues.length; i ++) {
            issue = this._issues[i];
            issue.setTextAlwaysVisible(true);
            issue.stopMoving();
            posX = this.elm.x + this._linearArea.x;
            posY = this.elm.y + this._linearArea.y;
            posY += (((this._linearDist * i) - this._linearDist * this._issues.length / 2));
            issue.moveTo(Math.round(posX), Math.round(posY), null, {alpha: 1});
        }

        // hide fakes
        for(i = 0; i < this._fakes.length; i ++) {
            issue = this._fakes[i];
            issue.implodeAlpha = issue.elm.alpha;
            gs.TweenMax.to(issue.elm, 0.3, {alpha: 0, overwrite: true});
        }

        // move selected title and desc
        posY = -this._linearDist * this._issues.length / 2;
        posY -= (this._topicTitle.height / Responsive.RATIO) + 50;
        gs.TweenMax.to(this._topicTitle, 0.3, {y: posY, alpha: 0, overwrite: true});
        gs.TweenMax.to(this._topicDesc, 0.3, {alpha: 0, overwrite: true});
        // this._linearArea.mouseout = this._linearArea.touchend = this._mouseOut.bind(this);

        setTimeout(function () {
            this.elm.removeChild(this._topicArea);
            this.isOver = true;
        }.bind(this), 600);

        this.mouseOverS.dispatch(this);
    };

    /**
    * When the mouse leaves a topic
    */
    Topic.prototype._mouseOut = function () {
        // dont mouse out if it's animating in
        if (Topic.SELECTED_TOPIC && !this.isOver) {
            return;
        }

        var i;
        var issue;

        // move selected title and desc
        gs.TweenMax.to(
            this._topicTitle,
            0.3,
            {
                y: Math.round(-(this._topicTitle.height / Responsive.RATIO) / 2),
                tint: 0xFFFFFF,
                alpha: 1,
                overwrite: true
            }
        );
        gs.TweenMax.to(this._topicDesc, 0.3, {alpha: 1, overwrite: true});

        // show fakes
        for(i = 0; i < this._fakes.length; i ++) {
            issue = this._fakes[i];
            gs.TweenMax.to(issue.elm, 0.3, {alpha: issue.implodeAlpha, overwrite: true});
        }

        for(i = 0; i < this._issues.length; i ++) {
            issue = this._issues[i];
            issue.mouseOut();
            issue.setTextAlwaysVisible(false);
            issue.moveTo(
                this.elm.x + issue.topicX,
                this.elm.y + issue.topicY,
                issue._resumeStaticAnimation.bind(issue),
                {alpha: 1});
        }

        setTimeout(function () {
            this.elm.addChild(this._topicArea);
            this.isOver = false;
            Topic.SELECTED_TOPIC = null;
        }.bind(this), 600);

        this.mouseOutS.dispatch(this);
    };

    Topic.prototype.toneDown = function () {
        gs.TweenMax.to(this._topicTitle, 0.3, {alpha: 0.5});
        gs.TweenMax.to(this._topicDesc, 0.3, {alpha: 0.5});

        for(var i = 0; i < this._issues.length; i ++) {
            gs.TweenMax.to(this._issues[i].elm, 0.3, {alpha: 0.5});
        }
    };

    Topic.prototype.endToneDown = function () {
        gs.TweenMax.to(this._topicTitle, 0.3, {alpha: 1});
        gs.TweenMax.to(this._topicDesc, 0.3, {alpha: 1});

        for(var i = 0; i < this._issues.length; i ++) {
            gs.TweenMax.to(this._issues[i].elm, 1, {alpha: 1});
        }
    };

    Topic.prototype.moveTo = function (position) {
        var i;
        var issue;

        this._topicArea.mouseover = this._topicArea.touchstart = null;

        gs.TweenMax.to(
            this.elm,
            0.3,
            {x:position.x, y:position.y, overwrite: true, roundProps: 'x,y', ease: gs.Elastic.easeOut.config(2, 0.7)}
        );

        for(i = 0; i < this._issues.length; i ++) {
            issue = this._issues[i];
            issue.moveTo(position.x + issue.topicX, position.y + issue.topicY, issue._resumeStaticAnimation.bind(issue));
        }

        for(i = 0; i < this._fakes.length; i ++) {
            issue = this._fakes[i];
            gs.TweenMax.to(
                issue.elm,
                0.3,
                {
                    alpha: issue.implodeAlpha,
                    x: position.x + issue.topicX,
                    y: position.y + issue.topicY,
                    overwrite: true, roundProps: 'x,y'
                }
            );
        }

        setTimeout(function resumeEvents () {
            this._topicArea.mouseover = this._topicArea.touchstart = this._mouseOver.bind(this);
        }.bind(this), 5000);
    };

    Topic.prototype.update = function (mousePosition) {
        this.mousePosition = mousePosition;
        if (this.isOver) {
            // check for mouse out
            var x0 = this.elm.x + this._linearArea.x + this._linearArea.hitArea.x;
            var x1 = x0 + this._linearArea.hitArea.width;
            var y0 = this.elm.y + this._linearArea.y + this._linearArea.hitArea.y;
            var y1 = y0 + this._linearArea.hitArea.height;

            if (mousePosition.x < x0 || mousePosition.x > x1 ||
                mousePosition.y < y0 || mousePosition.y > y1) {
                this._mouseOut();
            }
        }
    };

    return Topic;
});
<commit_msg>fixed bug when going from detail to topic<commit_after>/* global define:true */
define([
    'pixi',
    'gs',
    'signals',
    'experience/issue',
    'experience/responsive'
], function (
    PIXI,
    gs,
    signals,
    Issue,
    Responsive
) {
    'use strict';

    var Topic = function (data, index, issues, fakes) {
        this._radius = 70;
        this._linearDist = 40;
        this._linearWidth = 100;
        this.isOver = false;

        this._data = data;
        this._index = index;
        this._issues = issues;
        this._fakes = fakes;

        this.elm = new PIXI.DisplayObjectContainer();

        this.mouseOverS = new signals.Signal();
        this.mouseOutS = new signals.Signal();

        // this._topicArea;
        // this._linearArea;
        // this._topicTitle;
        // this._topicDesc;

        Topic.TOPICS.push(this);
    };

    Topic.TOPICS = [];
    Topic.SELECTED_TOPIC = null;

    Topic.prototype.setup = function () {
        // topic mouse over
        this._topicArea = new PIXI.Graphics();
        this._topicArea.i = this._index;
        this._topicArea.hitArea = new PIXI.Rectangle(
            -this._radius,
            -this._radius,
            this._radius * 2,
            this._radius * 2);
        this._topicArea.interactive = true;
        this._topicArea.buttonMode = true;
        this.elm.addChild(this._topicArea);
        this._topicArea.mouseover = this._mouseOver.bind(this);
        this._topicArea.tap = this._mouseOver.bind(this);

        // topic mouse out area
        var aMargin = 20;
        this._linearArea = new PIXI.Graphics();
        this._linearArea.i = this._index;
        this._linearArea.x = - 100 - aMargin / 2;
        this._linearArea.interactive = true;
        this._linearArea.buttonMode = true;
        this._linearArea.hitArea = new PIXI.Rectangle(
            -this._linearWidth / 2,
            -(this._linearDist * this._issues.length / 2) - aMargin,
            this._linearWidth + 100 + aMargin,
            (this._linearDist * this._issues.length) + aMargin);

        // title
        this._topicTitle = new PIXI.Text(this._data.getName().toUpperCase(),
            {font: '300 22px "Fira Sans", sans-serif', fill: '#222222'});
        this.elm.addChild(this._topicTitle);
        this._topicTitle.resolution = Responsive.RATIO;
        this._topicTitle.x = Math.round(-(this._topicTitle.width / Responsive.RATIO) / 2);
        this._topicTitle.y = Math.round(-(this._topicTitle.height / Responsive.RATIO) / 2);

        // description
        this._topicDesc = new PIXI.Text(this._data.getTagline(),
            {
                font: '300 14px "Fira Sans", sans-serif',
                fill: '#666666',
                wordWrap: true,
                wordWrapWidth: 200,
                align: 'center'
        });
        this.elm.addChild(this._topicDesc);
        this._topicDesc.resolution = Responsive.RATIO;
        this._topicDesc.x = Math.round(-(this._topicDesc.width / Responsive.RATIO) / 2);
        this._topicDesc.y = Math.round(this._radius + 50);

        // topic issue elements
        var issue;
        var i;
        var tH = ((this._topicTitle.height / Responsive.RATIO) / 2) + 5; // half title height
        for(i = 0; i < this._issues.length; i ++) {
            issue = this._issues[i];
            issue.setTextAlwaysVisible(false);
            issue.setIsInteractive(false);

            issue.topicX = (Math.random() * this._radius * 2) - this._radius;
             // from -(radius - tH / 2) to (radius - tH / 2)
            issue.topicY = (Math.random() * (this._radius - tH) * 2) - (this._radius - tH);
            // dont go between -tH/2 and tH/2 (the title area)
            issue.topicY += tH * (issue.topicY > 0 ? 1 : -1);
        }

        for(i = 0; i < this._fakes.length; i ++) {
            issue = this._fakes[i];
            issue.topicX = (Math.random() * this._radius * 2) - this._radius;
            issue.topicY = (Math.random() * this._radius * 2) - this._radius;
        }

        return this;
    };

    Topic.prototype.show = function () {
        var issue;
        var i;
        for(i = 0; i < this._issues.length; i ++) {
            issue = this._issues[i];
            issue.setMode(Issue.MODE_TOPICS);
            issue.moveTo(
                this.elm.x + issue.topicX,
                this.elm.y + issue.topicY,
                issue._resumeStaticAnimation.bind(issue),
                {alpha: 1});
            issue.topicMouseOver = this._mouseOverIssue.bind(this);
            issue.topicMouseOut = this._mouseOutIssue.bind(this);
            issue.topicTap = this._tapIssue.bind(this);
            issue.mouseOverS.add(issue.topicMouseOver);
            issue.mouseOutS.add(issue.topicMouseOut);
            issue.pressS.add(issue.topicTap);
            issue.tapS.add(issue.topicTap);
        }

        for(i = 0; i < this._fakes.length; i ++) {
            issue = this._fakes[i];
            gs.TweenMax.to(
                issue.elm,
                0.3,
                {
                    alpha: issue.implodeAlpha,
                    x: this.elm.x + issue.topicX,
                    y: this.elm.y + issue.topicY,
                    overwrite: true, roundProps: 'x,y'
                }
            );
        }
    };

    Topic.prototype.hide = function () {
        var issue;
        var i;

        for(i = 0; i < this._issues.length; i ++) {
            issue = this._issues[i];
            issue.mouseOverS.remove(issue.topicMouseOver);
            issue.mouseOutS.remove(issue.topicMouseOut);
            issue.pressS.remove(issue.topicTap);
            issue.tapS.remove(issue.topicTap);
        }

        for(i = 0; i < this._fakes.length; i ++) {
            this._fakes[i].explode();
        }
    };

    Topic.prototype._mouseOverIssue = function (issue) {
        if (this.isOver) {
            this.selectedIssue = issue;
            issue.mouseOver(this.mousePosition);
        }
    };

    Topic.prototype._mouseOutIssue = function (issue) {
        if (this.isOver) {
            this.selectedIssue = null;
            issue.mouseOut();
        }
    };

    Topic.prototype._tapIssue = function (issue) {
        this._mouseOut();
        issue._onPress();
    };

    /**
    * When hovering a topic
    */
    Topic.prototype._mouseOver = function () {
        // dont mouse in if theres another one still selected
        if (Topic.SELECTED_TOPIC) {
            return;
        }

        Topic.SELECTED_TOPIC = this;

        var i;
        var issue;

        // move issues to a linear position
        var posX;
        var posY;
        for(i = 0; i < this._issues.length; i ++) {
            issue = this._issues[i];
            issue.setTextAlwaysVisible(true);
            issue.stopMoving();
            posX = this.elm.x + this._linearArea.x;
            posY = this.elm.y + this._linearArea.y;
            posY += (((this._linearDist * i) - this._linearDist * this._issues.length / 2));
            issue.moveTo(Math.round(posX), Math.round(posY), null, {alpha: 1});
        }

        // hide fakes
        for(i = 0; i < this._fakes.length; i ++) {
            issue = this._fakes[i];
            issue.implodeAlpha = issue.elm.alpha;
            gs.TweenMax.to(issue.elm, 0.3, {alpha: 0, overwrite: true});
        }

        // move selected title and desc
        posY = -this._linearDist * this._issues.length / 2;
        posY -= (this._topicTitle.height / Responsive.RATIO) + 50;
        gs.TweenMax.to(this._topicTitle, 0.3, {y: posY, alpha: 0, overwrite: true});
        gs.TweenMax.to(this._topicDesc, 0.3, {alpha: 0, overwrite: true});
        // this._linearArea.mouseout = this._linearArea.touchend = this._mouseOut.bind(this);

        setTimeout(function () {
            this.elm.removeChild(this._topicArea);
            this.isOver = true;
        }.bind(this), 600);

        this.mouseOverS.dispatch(this);
    };

    /**
    * When the mouse leaves a topic
    */
    Topic.prototype._mouseOut = function () {
        // dont mouse out if it's animating in
        if (Topic.SELECTED_TOPIC && !this.isOver) {
            return;
        }

        var i;
        var issue;

        // move selected title and desc
        gs.TweenMax.to(
            this._topicTitle,
            0.3,
            {
                y: Math.round(-(this._topicTitle.height / Responsive.RATIO) / 2),
                tint: 0xFFFFFF,
                alpha: 1,
                overwrite: true
            }
        );
        gs.TweenMax.to(this._topicDesc, 0.3, {alpha: 1, overwrite: true});

        // show fakes
        for(i = 0; i < this._fakes.length; i ++) {
            issue = this._fakes[i];
            gs.TweenMax.to(issue.elm, 0.3, {alpha: issue.implodeAlpha, overwrite: true});
        }

        for(i = 0; i < this._issues.length; i ++) {
            issue = this._issues[i];
            issue.mouseOut();
            issue.setTextAlwaysVisible(false);
            issue.moveTo(
                this.elm.x + issue.topicX,
                this.elm.y + issue.topicY,
                issue._resumeStaticAnimation.bind(issue),
                {alpha: 1});
        }

        setTimeout(function () {
            this.elm.addChild(this._topicArea);
            this.isOver = false;
            Topic.SELECTED_TOPIC = null;
        }.bind(this), 600);

        this.mouseOutS.dispatch(this);
    };

    Topic.prototype.toneDown = function () {
        gs.TweenMax.to(this._topicTitle, 0.3, {alpha: 0.5});
        gs.TweenMax.to(this._topicDesc, 0.3, {alpha: 0.5});

        for(var i = 0; i < this._issues.length; i ++) {
            gs.TweenMax.to(this._issues[i].elm, 0.3, {alpha: 0.5});
        }
    };

    Topic.prototype.endToneDown = function () {
        gs.TweenMax.to(this._topicTitle, 0.3, {alpha: 1});
        gs.TweenMax.to(this._topicDesc, 0.3, {alpha: 1});

        for(var i = 0; i < this._issues.length; i ++) {
            gs.TweenMax.to(this._issues[i].elm, 1, {alpha: 1});
        }
    };

    Topic.prototype.moveTo = function (position) {
        var i;
        var issue;

        this._topicArea.mouseover = this._topicArea.touchstart = null;

        gs.TweenMax.to(
            this.elm,
            0.3,
            {x:position.x, y:position.y, overwrite: true, roundProps: 'x,y', ease: gs.Elastic.easeOut.config(2, 0.7)}
        );

        for(i = 0; i < this._issues.length; i ++) {
            issue = this._issues[i];
            issue.moveTo(position.x + issue.topicX, position.y + issue.topicY, issue._resumeStaticAnimation.bind(issue));
        }

        for(i = 0; i < this._fakes.length; i ++) {
            issue = this._fakes[i];
            gs.TweenMax.to(
                issue.elm,
                0.3,
                {
                    alpha: issue.implodeAlpha,
                    x: position.x + issue.topicX,
                    y: position.y + issue.topicY,
                    overwrite: true, roundProps: 'x,y'
                }
            );
        }

        setTimeout(function resumeEvents () {
            this._topicArea.mouseover = this._topicArea.touchstart = this._mouseOver.bind(this);
        }.bind(this), 5000);
    };

    Topic.prototype.update = function (mousePosition) {
        this.mousePosition = mousePosition;
        if (this.isOver) {
            // check for mouse out
            var x0 = this.elm.x + this._linearArea.x + this._linearArea.hitArea.x;
            var x1 = x0 + this._linearArea.hitArea.width;
            var y0 = this.elm.y + this._linearArea.y + this._linearArea.hitArea.y;
            var y1 = y0 + this._linearArea.hitArea.height;

            if (mousePosition.x < x0 || mousePosition.x > x1 ||
                mousePosition.y < y0 || mousePosition.y > y1) {
                this._mouseOut();
            }
        }
    };

    return Topic;
});
<|endoftext|>


<commit_before>l4.isIncluded = 42;
l4.importScripts('./../../../node_modules/chai/chai.js');

function describe(name, callback) {
  l4.describeName = name;
  l4.testResults[l4.describeName] = {};

  callback();
}

function it(name, callback) {
  var fullName = l4.describeName + '---' + name;

  // TODO: async tests
  if(callback.length > 0) {
    throw new Error('Async testcases not yet supported');
  }

  try{
    callback();

    l4.testResults.push({
      fullName: fullName,
      result: 'pass'
    });
  } catch(e) {
    l4.testResults.push({
      fullName: fullName,
      result: 'fail',
      error: packError(e)
    });
  }
}

function packError(error) {
  return {
    name: error.name,
    message: error.message,
    stack: error.stack
  };
}

function setupTestEnvironment(testFiles) {
  importScripts('./src/external/es6-module-loader-dev.js');
  System.baseURL = '/base/';
  System.paths['babel'] = 'src/external/babel-browser.js';
  System.transpiler = 'babel';

  System.babelOptions = { experimental: true };

  return System.import(testFiles[0]);
}

l4.messageTask('run test', function match(event) {
  return hasPort(event) &&
    event.data &&
    event.data.meta &&
    event.data.meta.type === 'run sw tests';
}, function react(event) {
  function sendTestResults(error, results) {
    getSource(event).postMessage({
      meta: {
        type: 'test results',
        receivedMessage: event.data
      },
      message: {
        error: error ? packError(error) : undefined,
        results: results ? results : undefined
      }
    });
  }

  l4.testResults = [];
  var testFiles = eval(event.data.message);

  setupTestEnvironment(testFiles)
    .then(() => {
      sendTestResults(undefined, l4.testResults);
    })
    .catch(sendTestResults);

  return true;
});
<commit_msg>support multiple test files<commit_after>l4.isIncluded = 42;
l4.importScripts('./../../../src/external/bluebird.js');
l4.importScripts('./../../../node_modules/chai/chai.js');

function describe(name, callback) {
  l4.describeName = name;
  l4.testResults[l4.describeName] = {};

  callback();
}

function it(name, callback) {
  var fullName = l4.describeName + '---' + name;

  // TODO: async tests
  if(callback.length > 0) {
    throw new Error('Async testcases not yet supported');
  }

  Promise.resolve().spread(()=>{'works'});

  try{
    callback();

    l4.testResults.push({
      fullName: fullName,
      result: 'pass'
    });
  } catch(e) {
    l4.testResults.push({
      fullName: fullName,
      result: 'fail',
      error: packError(e)
    });
  }
}

function packError(error) {
  return {
    name: error.name,
    message: error.message,
    stack: error.stack
  };
}

function setupTestEnvironment(testFiles) {
  importScripts('./src/external/es6-module-loader-dev.js');
  System.baseURL = '/base/';
  System.paths['babel'] = 'src/external/babel-browser.js';
  System.transpiler = 'babel';

  System.babelOptions = { experimental: true };

  return Promise.all(testFiles.map(testFile => System.import(testFile)));
}

l4.messageTask('run test', function match(event) {
  return hasPort(event) &&
    event.data &&
    event.data.meta &&
    event.data.meta.type === 'run sw tests';
}, function react(event) {
  function sendTestResults(error, results) {
    getSource(event).postMessage({
      meta: {
        type: 'test results',
        receivedMessage: event.data
      },
      message: {
        error: error ? packError(error) : undefined,
        results: results ? results : undefined
      }
    });
  }

  l4.testResults = [];
  var testFiles = eval(event.data.message);

  setupTestEnvironment(testFiles)
    .then(() => {
      sendTestResults(undefined, l4.testResults);
    })
    .catch(sendTestResults);

  return true;
});
<|endoftext|>


<commit_before>
$(function(){

  var vfrHudModel       = new VfrHudModel;
  var gpsRawIntModel    = new GpsRawIntModel;
  var heartbeatModel    = new HeartbeatModel;
  var attitudeModel     = new AttitudeModel;
  var metaWaypointModel = new MetaWaypointModel;
  var statusTextModel   = new StatusTextModel;

  var commStatusModel   = new CommStatusModel;
  var guideModel        = new GuideModel;
  guideModel.withMetaWaypointModel( metaWaypointModel );

  var mmapModel         = new MMapModel;
  mmapModel.withGpsModel( gpsRawIntModel );

  var mmapProviderModel = new MMapProviderModel;

  var mavlinkAPI = new MavlinkAPI(
        { 'HEARTBEAT':     sendNewMavlinkMessageToModel( heartbeatModel )
        , 'GPS_RAW_INT':   sendNewMavlinkMessageToModel( gpsRawIntModel )
        , 'VFR_HUD':       sendNewMavlinkMessageToModel( vfrHudModel )
        , 'ATTITUDE':      sendNewMavlinkMessageToModel( attitudeModel )
        , 'META_WAYPOINT': sendNewMavlinkMessageToModel( metaWaypointModel )
        , 'STATUSTEXT':    sendNewMavlinkMessageToModel( statusTextModel )
        }
      , commStatusModel);

  var adidrawing = new ADI('adi');

  var vfrtextview    = new VfrHudTextView({ model: vfrHudModel });
  var gpstextview    = new GpsTextView({ model: gpsRawIntModel });
  var modetextview   = new ModeTextView({ model: heartbeatModel });
  var commstatusview = new CommStatusView({ model: commStatusModel });
  var guidealtview   = new GuideAltitudeView({ model: guideModel });
  var statustextview = new StatusTextView({ model: statusTextModel });
  var adiview        = new ADIView({ model: attitudeModel, adi: adidrawing });

  var droneview      = new DroneView({ model: vfrHudModel });

  var mapsettings    = new MMapSettingsView({
                              providerModel: mmapProviderModel,
                              mapModel: mmapModel
                       });
  var mapview        = new MMapView({
                              providerModel: mmapProviderModel,
                              mapModel: mmapModel
                       });

  var providerdebug  = new MMapProviderView({
                              providerModel: mmapProviderModel,
                              mapModel: mmapModel
                        });
  console.log('appview initialized');

  setInterval(function() {
    mavlinkAPI.update();
  }, 1000);

});
<commit_msg>app: update refresh speed to 4hz (250ms interval on mavlinkAPI.update)<commit_after>
$(function(){

  var vfrHudModel       = new VfrHudModel;
  var gpsRawIntModel    = new GpsRawIntModel;
  var heartbeatModel    = new HeartbeatModel;
  var attitudeModel     = new AttitudeModel;
  var metaWaypointModel = new MetaWaypointModel;
  var statusTextModel   = new StatusTextModel;

  var commStatusModel   = new CommStatusModel;
  var guideModel        = new GuideModel;
  guideModel.withMetaWaypointModel( metaWaypointModel );

  var mmapModel         = new MMapModel;
  mmapModel.withGpsModel( gpsRawIntModel );

  var mmapProviderModel = new MMapProviderModel;

  var mavlinkAPI = new MavlinkAPI(
        { 'HEARTBEAT':     sendNewMavlinkMessageToModel( heartbeatModel )
        , 'GPS_RAW_INT':   sendNewMavlinkMessageToModel( gpsRawIntModel )
        , 'VFR_HUD':       sendNewMavlinkMessageToModel( vfrHudModel )
        , 'ATTITUDE':      sendNewMavlinkMessageToModel( attitudeModel )
        , 'META_WAYPOINT': sendNewMavlinkMessageToModel( metaWaypointModel )
        , 'STATUSTEXT':    sendNewMavlinkMessageToModel( statusTextModel )
        }
      , commStatusModel);

  var adidrawing = new ADI('adi');

  var vfrtextview    = new VfrHudTextView({ model: vfrHudModel });
  var gpstextview    = new GpsTextView({ model: gpsRawIntModel });
  var modetextview   = new ModeTextView({ model: heartbeatModel });
  var commstatusview = new CommStatusView({ model: commStatusModel });
  var guidealtview   = new GuideAltitudeView({ model: guideModel });
  var statustextview = new StatusTextView({ model: statusTextModel });
  var adiview        = new ADIView({ model: attitudeModel, adi: adidrawing });

  var droneview      = new DroneView({ model: vfrHudModel });

  var mapsettings    = new MMapSettingsView({
                              providerModel: mmapProviderModel,
                              mapModel: mmapModel
                       });
  var mapview        = new MMapView({
                              providerModel: mmapProviderModel,
                              mapModel: mmapModel
                       });

  var providerdebug  = new MMapProviderView({
                              providerModel: mmapProviderModel,
                              mapModel: mmapModel
                        });
  console.log('appview initialized');

  setInterval(function() {
    mavlinkAPI.update();
  }, 250);

});
<|endoftext|>


<commit_before>const fs = require('fs');
const path = require('path');
const compose = require('lodash.flowright');

const BUILD_GRADLE_PATH = path.join(
  process.cwd(), 'android', 'app', 'build.gradle'
);

const SETTINGS_GRADLE_PATH = path.join(
  process.cwd(), 'android', 'settings.gradle'
);

const MAIN_ACTIVITY_PATH = path.join(
  process.cwd(), 'android', 'app', 'src', 'main',
  'java', 'com', 'basic', 'MainActivity.java'
);

const SETTINGS_PATCH_PATTERN = `include ':app'`;
const BUILD_PATCH_PATTERN = `dependencies {`;

/**
 * Get module dir
 * @param  {String} name Name of the module
 * @return {String}      Path to the module inside node_modules folder
 */
const getModuleDir = (name) =>
  path.join(process.cwd(), 'node_modules', name);

/**
 * Get module's package.json content
 * @param  {String} name Name of the module
 * @return {Object}      Content of module's package.json
 */
const getModulePackage = (name) =>
  fs.readFileSync(path.join(getModuleDir(name), 'package.json'), 'utf8');

/**
 * Get config from package.json's content
 * @param  {String} content Content of package.json
 * @return {Object}         Module's rnpm config
 */
const getConfig = (content) => JSON.parse(content).rnpm;

/**
 * @param  {Object} packageJSON Content of the package.json
 * @return {Object}             Android config for processing module
 */
const getModuleAndroidConfig = compose(
  (c) => c.android, getConfig, getModulePackage
);

const readFile = (file) => fs.readFileSync(file, 'utf8');
const writeFile = (file, content) => {
  if (content) {
    return fs.writeFileSync(file, content, 'utf8');
  }

  return (c) => fs.writeFileSync(file, c, 'utf8');
};

const writeSettingsGradle = (content) =>
  fs.writeFileSync(SETTINGS_GRADLE_PATH, content, 'utf8');
const writeBuildGradle = (content) =>
  fs.writeFileSync(BUILD_GRADLE_PATH, content, 'utf8');

const replace = (scope, pattern, patch) =>
  scope.replace(pattern, `${pattern}\n${patch}`);

const getSettingsPatch = (name) =>
  `\nproject(':${name}').projectDir = ` +
    `new File(rootProject.projectDir, '../node_modules/${name}/android')\n`;

const getBuildPatch = (name) =>
  `\n\tcompile project(':${name}')\n`;

/**
 * Make a project settings patcher
 * @param  {String}   name Name of the project we're going to include
 * @return {Function}      Patcher function
 */
const makeProjectSettingsPatcher = (name) =>
  /**
   * Replace SETTINGS_PATCH_PATTERN by patch in the passed content
   * @param  {String} content Content of the Settings.gradle file
   * @return {String}         Patched content of Settings.gradle
   */
  (content) => replace(content, SETTINGS_PATCH_PATTERN, getSettingsPatch(name));

/**
 * Make a project settings patcher
 * @param  {String}   name Name of the project we're going to include
 * @return {Function}      Patcher function
 */
const makeProjectBuildPatcher = (name) =>
  /**
   * Replace BUILD_PATCH_PATTERN by patch in the passed content
   * @param  {String} content Content of the Build.gradle file
   * @return {String}         Patched content of Build.gradle
   */
  (content) => replace(content, BUILD_PATCH_PATTERN, getBuildPatch(name));

const patchSettingsGradle = (name) =>
  compose(
    writeFile(SETTINGS_GRADLE_PATH),
    makeProjectSettingsPatcher(name),
    readFile(SETTINGS_GRADLE_PATH),
  );

const patchBuildGradle = (name) =>
  compose(
    writeFile(BUILD_GRADLE_PATH),
    makeProjectBuildPatcher(name),
    readFile(BUILD_GRADLE_PATH),
  );

module.exports = function registerNativeAndroidModule(name) {
  patchSettingsGradle(name);
  patchBuildGradle(name);
};
<commit_msg>Android MainActivity.java patcher<commit_after>const fs = require('fs');
const path = require('path');
const compose = require('lodash.flowright');

const BUILD_GRADLE_PATH = path.join(
  process.cwd(), 'android', 'app', 'build.gradle'
);

const SETTINGS_GRADLE_PATH = path.join(
  process.cwd(), 'android', 'settings.gradle'
);

const MAIN_ACTIVITY_PATH = path.join(
  process.cwd(), 'android', 'app', 'src', 'main',
  'java', 'com', 'basic', 'MainActivity.java'
);

const SETTINGS_PATCH_PATTERN = `include ':app'`;
const BUILD_PATCH_PATTERN = `dependencies {`;
const MAIN_ACTIVITY_IMPORT_PATTERN = `import android.app.Activity;`;
const MAIN_ACTIVITY_PACKAGE_PATTERN = `.addPackage(new MainReactPackage())`;

/**
 * Get module dir
 * @param  {String} name Name of the module
 * @return {String}      Path to the module inside node_modules folder
 */
const getModuleDir = (name) =>
  path.join(process.cwd(), 'node_modules', name);

/**
 * Get module's package.json content
 * @param  {String} name Name of the module
 * @return {Object}      Content of module's package.json
 */
const getModulePackage = (name) =>
  fs.readFileSync(path.join(getModuleDir(name), 'package.json'), 'utf8');

/**
 * Get config from package.json's content
 * @param  {String} content Content of package.json
 * @return {Object}         Module's rnpm config
 */
const getConfig = (content) => JSON.parse(content).rnpm;

/**
 * @param  {String} Name of the module
 * @return {Object} Android config for processing module
 */
const getModuleAndroidConfig = compose(
  (c) => c.android, getConfig, getModulePackage
);

const readFile = (file) => fs.readFileSync(file, 'utf8');
const writeFile = (file, content) => {
  if (content) {
    return fs.writeFileSync(file, content, 'utf8');
  }

  return (c) => fs.writeFileSync(file, c, 'utf8');
};

const writeSettingsGradle = (content) =>
  fs.writeFileSync(SETTINGS_GRADLE_PATH, content, 'utf8');
const writeBuildGradle = (content) =>
  fs.writeFileSync(BUILD_GRADLE_PATH, content, 'utf8');

const replace = (scope, pattern, patch) =>
  scope.replace(pattern, `${pattern}\n${patch}`);

const getSettingsPatch = (name) =>
  `\nproject(':${name}').projectDir = ` +
    `new File(rootProject.projectDir, '../node_modules/${name}/android')\n`;

const getBuildPatch = (name) =>
  `\n\tcompile project(':${name}')\n`;

const getImportPatch = (importPath) =>
  `\nimport ${importPath}\n`;

const getPackagePatch = (instance) =>
  `\n\t.addPackage(${instance})\n`;

/**
 * Make a project settings patcher
 * @param  {String}   name Name of the project we're going to include
 * @return {Function}      Patcher function
 */
const makeProjectSettingsPatcher = (name) =>
  /**
   * Replace SETTINGS_PATCH_PATTERN by patch in the passed content
   * @param  {String} content Content of the Settings.gradle file
   * @return {String}         Patched content of Settings.gradle
   */
  (content) => replace(content, SETTINGS_PATCH_PATTERN, getSettingsPatch(name));

/**
 * Make a project settings patcher
 * @param  {String}   name Name of the project we're going to include
 * @return {Function}      Patcher function
 */
const makeProjectBuildPatcher = (name) =>
  /**
   * Replace BUILD_PATCH_PATTERN by patch in the passed content
   * @param  {String} content Content of the Build.gradle file
   * @return {String}         Patched content of Build.gradle
   */
  (content) => replace(content, BUILD_PATCH_PATTERN, getBuildPatch(name));

const makeMainActivityPatcher = (importPath, instance) =>
  (content) =>
    replace(content, MAIN_ACTIVITY_IMPORT_PATTERN, getImportPatch(importPath)) &&
    replace(content, MAIN_ACTIVITY_PACKAGE_PATTERN, getPackagePatch(instance));

const patchSettingsGradle = (name) =>
  compose(
    writeFile(SETTINGS_GRADLE_PATH),
    makeProjectSettingsPatcher(name),
    readFile(SETTINGS_GRADLE_PATH)
  );

const patchBuildGradle = (name) =>
  compose(
    writeFile(BUILD_GRADLE_PATH),
    makeProjectBuildPatcher(name),
    readFile(BUILD_GRADLE_PATH)
  );

const patchMainActivity = (importPath, instance) =>
  compose(
    writeFile(MAIN_ACTIVITY_PATH),
    makeMainActivityPatcher(importPath, instance),
    readFile(MAIN_ACTIVITY_PATH)
  );

module.exports = function registerNativeAndroidModule(name) {
  const config = getModuleAndroidConfig(name);

  patchSettingsGradle(name);
  patchBuildGradle(name);
  patchMainActivity(config.importPath, config.instance);
};
<|endoftext|>