<commit_before>#!/usr/bin/env node
/**
 * @license Copyright 2016 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
 */
'use strict';

/* eslint-disable no-console */

const fs = require('fs');
const spawnSync = require('child_process').spawnSync;
const yargs = require('yargs');
const log = require('lighthouse-logger');
const rimraf = require('rimraf');

const {collateResults, report} = require('./smokehouse-report.js');
const assetSaver = require('../../../lighthouse-core/lib/asset-saver.js');

const PROTOCOL_TIMEOUT_EXIT_CODE = 67;
const RETRIES = 3;

/**
 * Launch Chrome and do a full Lighthouse run.
 * @param {string} url
 * @param {string} configPath
 * @param {boolean=} isDebug
 * @return {Smokehouse.ExpectedRunnerResult}
 */
function runLighthouse(url, configPath, isDebug) {
  isDebug = isDebug || Boolean(process.env.LH_SMOKE_DEBUG);

  const command = 'node';
  const randInt = Math.round(Math.random() * 100000);
  const outputPath = `smokehouse-${randInt}.report.json`;
  const artifactsDirectory = `./.tmp/smokehouse-artifacts-${randInt}`;
  const args = [
    'lighthouse-cli/index.js',
    url,
    `--config-path=${configPath}`,
    `--output-path=${outputPath}`,
    '--output=json',
    `-G=${artifactsDirectory}`,
    `-A=${artifactsDirectory}`,
    '--quiet',
    '--port=0',
  ];

  if (process.env.APPVEYOR) {
    // Appveyor is hella slow already, disable CPU throttling so we're not 16x slowdown
    // see https://github.com/GoogleChrome/lighthouse/issues/4891
    args.push('--throttling.cpuSlowdownMultiplier=1');
  }

  // Lighthouse sometimes times out waiting to for a connection to Chrome in CI.
  // Watch for this error and retry relaunching Chrome and running Lighthouse up
  // to RETRIES times. See https://github.com/GoogleChrome/lighthouse/issues/833
  let runResults;
  let runCount = 0;
  do {
    if (runCount > 0) {
      console.log('  Lighthouse error: timed out waiting for debugger connection. Retrying...');
    }

    runCount++;
    console.log(`${log.dim}$ ${command} ${args.join(' ')} ${log.reset}`);
    runResults = spawnSync(command, args, {encoding: 'utf8', stdio: ['pipe', 'pipe', 'inherit']});
  } while (runResults.status === PROTOCOL_TIMEOUT_EXIT_CODE && runCount <= RETRIES);

  if (isDebug) {
    console.log(`STDOUT: ${runResults.stdout}`);
    console.error(`STDERR: ${runResults.stderr}`);
  }

  const exitCode = runResults.status;
  if (exitCode === PROTOCOL_TIMEOUT_EXIT_CODE) {
    console.error(`Lighthouse debugger connection timed out ${RETRIES} times. Giving up.`);
    process.exit(1);
  } else if (!fs.existsSync(outputPath)) {
    console.error(`Lighthouse run failed to produce a report and exited with ${exitCode}. stderr to follow:`); // eslint-disable-line max-len
    console.error(runResults.stderr);
    process.exit(exitCode);
  }

  /** @type {LH.Result} */
  const lhr = JSON.parse(fs.readFileSync(outputPath, 'utf8'));
  const artifacts = assetSaver.loadArtifacts(artifactsDirectory);

  if (isDebug) {
    console.log('LHR output available at: ', outputPath);
    console.log('Artifacts avaiable in: ', artifactsDirectory);
  } else {
    fs.unlinkSync(outputPath);
    rimraf.sync(artifactsDirectory);
  }

  // There should either be both an error exitCode and a lhr.runtimeError or neither.
  if (Boolean(exitCode) !== Boolean(lhr.runtimeError)) {
    const runtimeErrorCode = lhr.runtimeError && lhr.runtimeError.code;
    console.error(`Lighthouse did not exit with an error correctly, exiting with ${exitCode} but with runtimeError '${runtimeErrorCode}'`); // eslint-disable-line max-len
    process.exit(1);
  }

  return {
    lhr,
    artifacts,
  };
}

const cli = yargs
  .help('help')
  .describe({
    'smoke-id': 'The id of the smoke test to run',
    'debug': 'Save the artifacts along with the output',
  })
  .require('smoke-id', true)
  .argv;

const smokeId = cli['smoke-id'];
const smokeTest = require('./smoke-test-dfns.js').find(smoke => smoke.id === smokeId);

if (!smokeTest) {
  process.exit(1);
  throw new Error(`could not find smoke ${smokeId}`);
}

const configPath = `./.tmp/smoke-config-${smokeTest.id}.json`;
fs.writeFileSync(configPath, JSON.stringify(smokeTest.config));

// Loop sequentially over expectations, comparing against Lighthouse run, and
// reporting result.
let passingCount = 0;
let failingCount = 0;
smokeTest.expectations.forEach(expected => {
  console.log(`Doing a run of '${expected.lhr.requestedUrl}'...`);
  const results = runLighthouse(expected.lhr.requestedUrl, configPath, cli.debug);

  console.log(`Asserting expected results match those found. (${expected.lhr.requestedUrl})`);
  const collated = collateResults(results, expected);
  const counts = report(collated);
  passingCount += counts.passed;
  failingCount += counts.failed;
});

if (passingCount) {
  console.log(log.greenify(`${passingCount} passing`));
}
if (failingCount) {
  console.log(log.redify(`${failingCount} failing`));
  process.exit(1);
}
<commit_msg>tests(smokehouse): fix tmp directory creation (#9855)<commit_after>#!/usr/bin/env node
/**
 * @license Copyright 2016 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
 */
'use strict';

/* eslint-disable no-console */

const fs = require('fs');
const mkdirp = require('mkdirp');
const spawnSync = require('child_process').spawnSync;
const yargs = require('yargs');
const log = require('lighthouse-logger');
const rimraf = require('rimraf');

const {collateResults, report} = require('./smokehouse-report.js');
const assetSaver = require('../../../lighthouse-core/lib/asset-saver.js');

const PROTOCOL_TIMEOUT_EXIT_CODE = 67;
const RETRIES = 3;

/**
 * Launch Chrome and do a full Lighthouse run.
 * @param {string} url
 * @param {string} configPath
 * @param {boolean=} isDebug
 * @return {Smokehouse.ExpectedRunnerResult}
 */
function runLighthouse(url, configPath, isDebug) {
  isDebug = isDebug || Boolean(process.env.LH_SMOKE_DEBUG);

  const command = 'node';
  const randInt = Math.round(Math.random() * 100000);
  const outputPath = `smokehouse-${randInt}.report.json`;
  const artifactsDirectory = `./.tmp/smokehouse-artifacts-${randInt}`;
  const args = [
    'lighthouse-cli/index.js',
    url,
    `--config-path=${configPath}`,
    `--output-path=${outputPath}`,
    '--output=json',
    `-G=${artifactsDirectory}`,
    `-A=${artifactsDirectory}`,
    '--quiet',
    '--port=0',
  ];

  if (process.env.APPVEYOR) {
    // Appveyor is hella slow already, disable CPU throttling so we're not 16x slowdown
    // see https://github.com/GoogleChrome/lighthouse/issues/4891
    args.push('--throttling.cpuSlowdownMultiplier=1');
  }

  // Lighthouse sometimes times out waiting to for a connection to Chrome in CI.
  // Watch for this error and retry relaunching Chrome and running Lighthouse up
  // to RETRIES times. See https://github.com/GoogleChrome/lighthouse/issues/833
  let runResults;
  let runCount = 0;
  do {
    if (runCount > 0) {
      console.log('  Lighthouse error: timed out waiting for debugger connection. Retrying...');
    }

    runCount++;
    console.log(`${log.dim}$ ${command} ${args.join(' ')} ${log.reset}`);
    runResults = spawnSync(command, args, {encoding: 'utf8', stdio: ['pipe', 'pipe', 'inherit']});
  } while (runResults.status === PROTOCOL_TIMEOUT_EXIT_CODE && runCount <= RETRIES);

  if (isDebug) {
    console.log(`STDOUT: ${runResults.stdout}`);
    console.error(`STDERR: ${runResults.stderr}`);
  }

  const exitCode = runResults.status;
  if (exitCode === PROTOCOL_TIMEOUT_EXIT_CODE) {
    console.error(`Lighthouse debugger connection timed out ${RETRIES} times. Giving up.`);
    process.exit(1);
  } else if (!fs.existsSync(outputPath)) {
    console.error(`Lighthouse run failed to produce a report and exited with ${exitCode}. stderr to follow:`); // eslint-disable-line max-len
    console.error(runResults.stderr);
    process.exit(exitCode);
  }

  /** @type {LH.Result} */
  const lhr = JSON.parse(fs.readFileSync(outputPath, 'utf8'));
  const artifacts = assetSaver.loadArtifacts(artifactsDirectory);

  if (isDebug) {
    console.log('LHR output available at: ', outputPath);
    console.log('Artifacts avaiable in: ', artifactsDirectory);
  } else {
    fs.unlinkSync(outputPath);
    rimraf.sync(artifactsDirectory);
  }

  // There should either be both an error exitCode and a lhr.runtimeError or neither.
  if (Boolean(exitCode) !== Boolean(lhr.runtimeError)) {
    const runtimeErrorCode = lhr.runtimeError && lhr.runtimeError.code;
    console.error(`Lighthouse did not exit with an error correctly, exiting with ${exitCode} but with runtimeError '${runtimeErrorCode}'`); // eslint-disable-line max-len
    process.exit(1);
  }

  return {
    lhr,
    artifacts,
  };
}

const cli = yargs
  .help('help')
  .describe({
    'smoke-id': 'The id of the smoke test to run',
    'debug': 'Save the artifacts along with the output',
  })
  .require('smoke-id', true)
  .argv;

const smokeId = cli['smoke-id'];
const smokeTest = require('./smoke-test-dfns.js').find(smoke => smoke.id === smokeId);

if (!smokeTest) {
  process.exit(1);
  throw new Error(`could not find smoke ${smokeId}`);
}

const lhRootDir = `${__dirname}/../../..`;
const tmpDir = `${lhRootDir}/.tmp`;
mkdirp.sync(tmpDir);
const configPath = `${tmpDir}/smoke-config-${smokeTest.id}.json`;
fs.writeFileSync(configPath, JSON.stringify(smokeTest.config));

// Loop sequentially over expectations, comparing against Lighthouse run, and
// reporting result.
let passingCount = 0;
let failingCount = 0;
smokeTest.expectations.forEach(expected => {
  console.log(`Doing a run of '${expected.lhr.requestedUrl}'...`);
  const results = runLighthouse(expected.lhr.requestedUrl, configPath, cli.debug);

  console.log(`Asserting expected results match those found. (${expected.lhr.requestedUrl})`);
  const collated = collateResults(results, expected);
  const counts = report(collated);
  passingCount += counts.passed;
  failingCount += counts.failed;
});

if (passingCount) {
  console.log(log.greenify(`${passingCount} passing`));
}
if (failingCount) {
  console.log(log.redify(`${failingCount} failing`));
  process.exit(1);
}
<|endoftext|>


<commit_before>import { Duration } from "./constructor";

var proto = Duration.prototype;

import { abs } from "./abs";
import { add, subtract } from "./add-subtract";
import { as, asMilliseconds, asSeconds, asMinutes, asHours, asDays, asWeeks, asMonths, asYears, valueOf } from "./as";
import { bubble } from "./bubble";
import { get, milliseconds, seconds, minutes, hours, days, months, years, weeks } from "./get";
import { humanize } from "./humanize";
import { toISOString } from "./iso-string";
import { lang, locale, localeData } from "../moment/locale";

proto.abs            = abs;
proto.add            = add;
proto.subtract       = subtract;
proto.as             = as;
proto.asMilliseconds = asMilliseconds;
proto.asSeconds      = asSeconds;
proto.asMinutes      = asMinutes;
proto.asHours        = asHours;
proto.asDays         = asDays;
proto.asWeeks        = asWeeks;
proto.asMonths       = asMonths;
proto.asYears        = asYears;
proto.valueOf        = valueOf;
proto._bubble        = bubble;
proto.get            = get;
proto.milliseconds   = milliseconds;
proto.seconds        = seconds;
proto.minutes        = minutes;
proto.hours          = hours;
proto.days           = days;
proto.weeks          = weeks;
proto.months         = months;
proto.years          = years;
proto.humanize       = humanize;
proto.toISOString    = toISOString;
proto.toString       = toISOString;
proto.locale = locale;
proto.localeData = localeData;

// Deprecations
import { deprecate } from "../utils/deprecate";

proto.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString);
proto.lang = lang;
<commit_msg>Align =<commit_after>import { Duration } from "./constructor";

var proto = Duration.prototype;

import { abs } from "./abs";
import { add, subtract } from "./add-subtract";
import { as, asMilliseconds, asSeconds, asMinutes, asHours, asDays, asWeeks, asMonths, asYears, valueOf } from "./as";
import { bubble } from "./bubble";
import { get, milliseconds, seconds, minutes, hours, days, months, years, weeks } from "./get";
import { humanize } from "./humanize";
import { toISOString } from "./iso-string";
import { lang, locale, localeData } from "../moment/locale";

proto.abs            = abs;
proto.add            = add;
proto.subtract       = subtract;
proto.as             = as;
proto.asMilliseconds = asMilliseconds;
proto.asSeconds      = asSeconds;
proto.asMinutes      = asMinutes;
proto.asHours        = asHours;
proto.asDays         = asDays;
proto.asWeeks        = asWeeks;
proto.asMonths       = asMonths;
proto.asYears        = asYears;
proto.valueOf        = valueOf;
proto._bubble        = bubble;
proto.get            = get;
proto.milliseconds   = milliseconds;
proto.seconds        = seconds;
proto.minutes        = minutes;
proto.hours          = hours;
proto.days           = days;
proto.weeks          = weeks;
proto.months         = months;
proto.years          = years;
proto.humanize       = humanize;
proto.toISOString    = toISOString;
proto.toString       = toISOString;
proto.locale         = locale;
proto.localeData     = localeData;

// Deprecations
import { deprecate } from "../utils/deprecate";

proto.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString);
proto.lang = lang;
<|endoftext|>


<commit_before>"use strict";

const fs = require("fs");
const log = require("matrix-appservice-bridge").Logging.get("substitutions");

// Ordered
var SUBSTITUTION_PAIRS = [];

function add(slack, matrix) {
    SUBSTITUTION_PAIRS.push({
        slack: slack,
        matrix: matrix
    });
}

// slack -> matrix substitutions are performed top -> bottom
// matrix -> slack substitutions are performed bottom -> top
//
// The ordering here matters because some characters are present in both the
// "old" and "new" patterns, and we may end up over- or under-escaping things,
// or in an escaping loop, if things aren't properly ordered.
add("<", "<");
add(">", ">");
add("&", "&"); // & must be after all replacements involving &s.

var emojiIndex = (function () {
    var emojiJson = fs.readFileSync("./external/emoji-data/emoji.json");
    var emoji = JSON.parse(emojiJson);
    var index = {};
    for (var i = 0; i < emoji.length; ++i) {
        var e = emoji[i];
        for (var j = 0; j < e.short_names.length; ++j) {
            if (e["unified"].indexOf("-") < 0) {
                var char = String.fromCodePoint(parseInt(e["unified"], 16));
                index[":" + e.short_names[j] + ":"] = char;
            }
        }
    }
    index[":simple_smile:"] = index[":smile:"];
    index[":+1:"] = index[":thumbsup:"];
    index[":squirrel:"] = index[":chipmunk:"];
    index[":shipit:"] = index[":chipmunk:"]; // https://www.quora.com/GitHub-What-is-the-significance-of-the-Ship-It-squirrel
    return index;
})();

function replaceAll(string, old, replacement) {
    return string.split(old).join(replacement);
}

/**
 * Performs any escaping, unescaping, or substituting required to make the text
 * of a Slack message appear like the text of a Matrix message.
 *
 * @param {string} string the text, in Slack's format.
 * @param {Object} file options slack file object
 */
var slackToMatrix = function(body, file) {
    log.info("running substitutions on: " + body);

    if (undefined != body) {
        for (var i = 0; i < SUBSTITUTION_PAIRS.length; ++i) {
            var pair = SUBSTITUTION_PAIRS[i];
            body = replaceAll(body, pair.slack, pair.matrix);
        }
    }

    // convert @channel to @room
    body = body.replace("<!channel>", "@room");

    // escape slack-links e.g:
    // "hello @oddvar:<http://oddvar.org|oddvar.org> how are ya"
    // becomes
    // hello @oddvar:http://oddvar.org how are ya
    body = body.replace(/<(https?:\/\/[^\|]+?)\|[^>]+?>/g, '$1');

    // remove <> from links. if you post http://oddvar.org in slack,
    // this becomes <http://oddvar.org> in the event sent to Matrix
    // so we need to strip <> from the link
    body = body.replace(/<(https?:\/\/[^>]+?)>/g, '$1');

    // if we have a file, attempt to get the direct link to the file
    if (file && file.public_url_shared) {
        var url = getSlackFileUrl(file);
        body = body.replace(file.permalink, url);
    }

    // attempt to match any text inside colons to emoji, e.g. :smiley:
    // we use replace to run a function on each match.
    body = body.replace(/:([+-z]|_)+:/g, (emojiKey) => {
        const emojiChar = emojiIndex[emojiKey];
        if (emojiChar !== undefined) {
            return emojiChar;
        }
        log.warn("Failed to recognise emoji sequence: " + emojiKey);
        return emojiKey;
    });
    return body;
};

var getSlackFileUrl = function(file) {
    var pub_secret = file.permalink_public.match(/https?:\/\/slack-files.com\/[^-]*-[^-]*-(.*)/);
    // try to get direct link to the file
    if (pub_secret !== undefined && pub_secret.length > 0) {
        return file.url_private + "?pub_secret=" + pub_secret[1];
    }
};

/**
 * Performs any escaping, unescaping, or substituting required to make the text
 * of a Matrix message appear like the text of a Slack message.
 *
 * @param {MatrixEvent} event the Matrix event.
 * @param {Main} main the toplevel main instance
 * @return An object which can be posted as JSON to the Slack API.
 */
var matrixToSlack = function(event, main) {
    log.debug("Event from matrix: " + JSON.stringify(event));

    var string = event.content.body;

    // remove <> from links, e.g:
    string = string.replace(/<((https?:\/\/)?[^>]+?)>/g, '$1');

    if (undefined != string) {
        for (var i = SUBSTITUTION_PAIRS.length - 1; i >= 0; --i) {
            var pair = SUBSTITUTION_PAIRS[i];
            string = replaceAll(string, pair.matrix, pair.slack);
        }
        // emotes in slack are just italicised
        if (event.content.msgtype == "m.emote") {
            string = "_" + string + "_";
        }

        // replace riot "pill" behavior to "@" mention for slack users
        const html_string = event.content.formatted_body;
        if (undefined != html_string) {
            const regex = new RegExp('([^<]+)', "g");

            let match = regex.exec(html_string);
            while (match != null) {
                // Extract the slack ID from the matrix id
                const userid = match[1].split("_")[1].split(":")[0];
                // Construct the new slack mention
                string = string.replace(match[2], "<@" + userid + ">");
                match = regex.exec(html_string);
            }
        }
    }

    // convert @room to @channel
    string = string.replace("@room", "@channel");

    // Strip out any language specifier on the code tags, as they are not supported by slack.
    string = string.replace(/```[\w*]+\n/g, "```\n");

    // Note: This slower plainTextSlackMentions call is only used if there is not a pill in the message,
    // meaning if we can we use the much simpler pill subs rather than this.
    return plainTextSlackMentions(main, string, event.room_id).then(string => {
        const ret = {
            username: event.user_id,
            text: string,
            link_names: 1  //This no longer works for nicks but is needed to make @channel work.
        };
        if (event.content.msgtype == "m.image" && event.content.url.indexOf("mxc://") === 0) {
            const url = main.getUrlForMxc(event.content.url);
            delete ret.text;
            ret.attachments = [{
                fallback: string,
                image_url: url
            }];
        } else if (event.content.msgtype == "m.file" && event.content.url.indexOf("mxc://") === 0) {
            const url = main.getUrlForMxc(event.content.url);
            ret.text = "<" + url + "|" + string + ">";
        }

        return ret;
    });
}

var htmlEscape = function(s) {
    return s.replace(/&/g, "&")
        .replace(/</g, "<")
        .replace(/>/g, ">");
};

/**
 * Get a mapping of the nicknames of all Slack Ghosts in the room to their slack user ids.
 *
 * @param {String} room_id The room id to make the maps for.
 * @return {Promise} A mapping of display name to slack user id.
 */
function getDisplayMap(main, room_id) {
    const store = main.getUserStore();
    return main.listGhostUsers(room_id)
        .then(users => Promise.all(users.map(user => store.select({id: user}))))
        .then(users => {
            const displaymap = {};
            users.forEach(user => {
                if (user && user[0]) {
                    displaymap[user[0].display_name] = user[0].id.split("_")[2].split(":")[0];
                }
            });
            return displaymap;
        });
}

/**
 * Construct a mapping of the first words of all the display names to
 * an array of objects which map full display names to their slack
 * user ids.
 * i.e. {"bob": [{"bob": "U123123"}, {"bob smith": "U2891283"}]}
 *
 * @param {Object} displaymap A mapping of display names to slack user ids.
 * @return {Object} A mapping of first words of display names.
 */
function makeFirstWordMap(displaymap) {
    const displaynames = Object.keys(displaymap);
    const firstwords = {};

    for (const dispname of displaynames) {
        const firstword = dispname.split(" ")[0];
        const amap = {};
        amap[dispname] = displaymap[dispname];
        if (firstwords.hasOwnProperty(firstword)) {
            firstwords[firstword].push(amap);
        }
        else {
            firstwords[firstword] = [amap];
        }
    }
    return firstwords;
}

/**
 * Do string replacement on a message given the display map.
 *
 * @param {String} string The string to perform replacements on.
 * @param {Object} displaymap A mapping of display names to slack user ids.
 * @return {String} The string with replacements performed.
 */
function replacementFromDisplayMap(string, displaymap) {
    const firstwords = makeFirstWordMap(displaymap);

    // Now parse the message to find the intersection of every word in the
    // message with every first word of all nicks.
    const match_words = new Set(Object.keys(firstwords));
    const string_words = new Set(string.split(" "));
    const matches = [...string_words].filter(x => match_words.has(x));

    if (matches && matches.length) {
        for (const firstword of matches) {
            if (firstwords.hasOwnProperty(firstword)) {
                const nicks = firstwords[firstword];
                if (nicks.length === 1) {
                    string = string.replace(firstword, "<@" + nicks[0][firstword] + ">");
                }
                else {
                    // Sort the displaynames by longest string first, and then match them from longest to shortest.
                    // This can match multiple times if there is more than one mention in the message
                    const displaynames = nicks.map(x => Object.keys(x)[0]);
                    displaynames.sort((x, y) => y.length - x.length);
                    for (const displayname of displaynames) {
                        if (string.includes(displayname)) {
                            string = string.replace(displayname, "<@" + displaymap[displayname] + ">");
                        }
                    }
                }
            }
        }
    }
    return string;
}

/**
 * Replace plain text form of @displayname mentions with the slack mention syntax.
 *
 * @param {Main} main the toplevel main instance
 * @param {String} string The string to perform replacements on.
 * @param {String} room_id The room the message was sent in.
 * @return {String} The string with replacements performed.
 */
function plainTextSlackMentions(main, string, room_id) {
    return getDisplayMap(main, room_id).then(displaymap => replacementFromDisplayMap(string, displaymap));
}

// These functions are copied and modified from the Gitter AS
// idx counts backwards from the end of the string; 0 is final character
function rcharAt(s,idx) {
    return s.charAt(s.length-1 - idx);
}

function firstWord(s) {
    var groups = s.match(/^\s*\S+/);
    return groups ? groups[0] : "";
}

function finalWord(s) {
    var groups = s.match(/\S+\s*$/);
    return groups ? groups[0] : "";
}

function makeDiff(prev, curr) {
    var i;
    for (i = 0; i < curr.length && i < prev.length; i++) {
        if (curr.charAt(i) != prev.charAt(i)) break;
    }
    // retreat to the start of a word
    while(i > 0 && /\S/.test(curr.charAt(i-1))) i--;

    var prefixLen = i;

    for(i = 0; i < curr.length && i < prev.length; i++) {
        if (rcharAt(curr, i) != rcharAt(prev, i)) break;
    }
    // advance to the end of a word
    while(i > 0 && /\S/.test(rcharAt(curr, i-1))) i--;

    var suffixLen = i;

    // Extract the common prefix and suffix strings themselves and
    //   mutate the prev/curr strings to only contain the differing
    //   middle region
    var prefix = curr.slice(0, prefixLen);
    curr = curr.slice(prefixLen);
    prev = prev.slice(prefixLen);

    var suffix = "";
    if (suffixLen > 0) {
        suffix = curr.slice(-suffixLen);
        curr = curr.slice(0, -suffixLen);
        prev = prev.slice(0, -suffixLen);
    }

    // At this point, we have four strings; the common prefix and
    //   suffix, and the edited middle part. To display it nicely as a
    //   matrix message we'll use the final word of the prefix and the
    //   first word of the suffix as "context" for a customly-formatted
    //   message.

    var before = finalWord(prefix);
    if (before != prefix) { before = "... " + before; }

    var after = firstWord(suffix);
    if (after != suffix) { after = after + " ..."; }

    // return {prev: prev,
    //         curr: curr,
    //         before: before,
    //         after: after};
    return {prev, curr, before, after};
}

module.exports = {
    matrixToSlack: matrixToSlack,
    slackToMatrix: slackToMatrix,
    getSlackFileUrl: getSlackFileUrl,
    htmlEscape: htmlEscape,
    replacementFromDisplayMap: replacementFromDisplayMap,
    makeFirstWordMap: makeFirstWordMap,
    makeDiff: makeDiff
};
<commit_msg>Remove slack link escpaing because slackdown does it better<commit_after>"use strict";

const fs = require("fs");
const log = require("matrix-appservice-bridge").Logging.get("substitutions");

// Ordered
var SUBSTITUTION_PAIRS = [];

function add(slack, matrix) {
    SUBSTITUTION_PAIRS.push({
        slack: slack,
        matrix: matrix
    });
}

// slack -> matrix substitutions are performed top -> bottom
// matrix -> slack substitutions are performed bottom -> top
//
// The ordering here matters because some characters are present in both the
// "old" and "new" patterns, and we may end up over- or under-escaping things,
// or in an escaping loop, if things aren't properly ordered.
add("<", "<");
add(">", ">");
add("&", "&"); // & must be after all replacements involving &s.

var emojiIndex = (function () {
    var emojiJson = fs.readFileSync("./external/emoji-data/emoji.json");
    var emoji = JSON.parse(emojiJson);
    var index = {};
    for (var i = 0; i < emoji.length; ++i) {
        var e = emoji[i];
        for (var j = 0; j < e.short_names.length; ++j) {
            if (e["unified"].indexOf("-") < 0) {
                var char = String.fromCodePoint(parseInt(e["unified"], 16));
                index[":" + e.short_names[j] + ":"] = char;
            }
        }
    }
    index[":simple_smile:"] = index[":smile:"];
    index[":+1:"] = index[":thumbsup:"];
    index[":squirrel:"] = index[":chipmunk:"];
    index[":shipit:"] = index[":chipmunk:"]; // https://www.quora.com/GitHub-What-is-the-significance-of-the-Ship-It-squirrel
    return index;
})();

function replaceAll(string, old, replacement) {
    return string.split(old).join(replacement);
}

/**
 * Performs any escaping, unescaping, or substituting required to make the text
 * of a Slack message appear like the text of a Matrix message.
 *
 * @param {string} string the text, in Slack's format.
 * @param {Object} file options slack file object
 */
var slackToMatrix = function(body, file) {
    log.info("running substitutions on: " + body);

    if (undefined != body) {
        for (var i = 0; i < SUBSTITUTION_PAIRS.length; ++i) {
            var pair = SUBSTITUTION_PAIRS[i];
            body = replaceAll(body, pair.slack, pair.matrix);
        }
    }

    // convert @channel to @room
    body = body.replace("<!channel>", "@room");

    // if we have a file, attempt to get the direct link to the file
    if (file && file.public_url_shared) {
        var url = getSlackFileUrl(file);
        body = body.replace(file.permalink, url);
    }

    // attempt to match any text inside colons to emoji, e.g. :smiley:
    // we use replace to run a function on each match.
    body = body.replace(/:([+-z]|_)+:/g, (emojiKey) => {
        const emojiChar = emojiIndex[emojiKey];
        if (emojiChar !== undefined) {
            return emojiChar;
        }
        log.warn("Failed to recognise emoji sequence: " + emojiKey);
        return emojiKey;
    });
    return body;
};

var getSlackFileUrl = function(file) {
    var pub_secret = file.permalink_public.match(/https?:\/\/slack-files.com\/[^-]*-[^-]*-(.*)/);
    // try to get direct link to the file
    if (pub_secret !== undefined && pub_secret.length > 0) {
        return file.url_private + "?pub_secret=" + pub_secret[1];
    }
};

/**
 * Performs any escaping, unescaping, or substituting required to make the text
 * of a Matrix message appear like the text of a Slack message.
 *
 * @param {MatrixEvent} event the Matrix event.
 * @param {Main} main the toplevel main instance
 * @return An object which can be posted as JSON to the Slack API.
 */
var matrixToSlack = function(event, main) {
    log.debug("Event from matrix: " + JSON.stringify(event));

    var string = event.content.body;

    // remove <> from links, e.g:
    string = string.replace(/<((https?:\/\/)?[^>]+?)>/g, '$1');

    if (undefined != string) {
        for (var i = SUBSTITUTION_PAIRS.length - 1; i >= 0; --i) {
            var pair = SUBSTITUTION_PAIRS[i];
            string = replaceAll(string, pair.matrix, pair.slack);
        }
        // emotes in slack are just italicised
        if (event.content.msgtype == "m.emote") {
            string = "_" + string + "_";
        }

        // replace riot "pill" behavior to "@" mention for slack users
        const html_string = event.content.formatted_body;
        if (undefined != html_string) {
            const regex = new RegExp('([^<]+)', "g");

            let match = regex.exec(html_string);
            while (match != null) {
                // Extract the slack ID from the matrix id
                const userid = match[1].split("_")[1].split(":")[0];
                // Construct the new slack mention
                string = string.replace(match[2], "<@" + userid + ">");
                match = regex.exec(html_string);
            }
        }
    }

    // convert @room to @channel
    string = string.replace("@room", "@channel");

    // Strip out any language specifier on the code tags, as they are not supported by slack.
    string = string.replace(/```[\w*]+\n/g, "```\n");

    // Note: This slower plainTextSlackMentions call is only used if there is not a pill in the message,
    // meaning if we can we use the much simpler pill subs rather than this.
    return plainTextSlackMentions(main, string, event.room_id).then(string => {
        const ret = {
            username: event.user_id,
            text: string,
            link_names: 1  //This no longer works for nicks but is needed to make @channel work.
        };
        if (event.content.msgtype == "m.image" && event.content.url.indexOf("mxc://") === 0) {
            const url = main.getUrlForMxc(event.content.url);
            delete ret.text;
            ret.attachments = [{
                fallback: string,
                image_url: url
            }];
        } else if (event.content.msgtype == "m.file" && event.content.url.indexOf("mxc://") === 0) {
            const url = main.getUrlForMxc(event.content.url);
            ret.text = "<" + url + "|" + string + ">";
        }

        return ret;
    });
}

var htmlEscape = function(s) {
    return s.replace(/&/g, "&")
        .replace(/</g, "<")
        .replace(/>/g, ">");
};

/**
 * Get a mapping of the nicknames of all Slack Ghosts in the room to their slack user ids.
 *
 * @param {String} room_id The room id to make the maps for.
 * @return {Promise} A mapping of display name to slack user id.
 */
function getDisplayMap(main, room_id) {
    const store = main.getUserStore();
    return main.listGhostUsers(room_id)
        .then(users => Promise.all(users.map(user => store.select({id: user}))))
        .then(users => {
            const displaymap = {};
            users.forEach(user => {
                if (user && user[0]) {
                    displaymap[user[0].display_name] = user[0].id.split("_")[2].split(":")[0];
                }
            });
            return displaymap;
        });
}

/**
 * Construct a mapping of the first words of all the display names to
 * an array of objects which map full display names to their slack
 * user ids.
 * i.e. {"bob": [{"bob": "U123123"}, {"bob smith": "U2891283"}]}
 *
 * @param {Object} displaymap A mapping of display names to slack user ids.
 * @return {Object} A mapping of first words of display names.
 */
function makeFirstWordMap(displaymap) {
    const displaynames = Object.keys(displaymap);
    const firstwords = {};

    for (const dispname of displaynames) {
        const firstword = dispname.split(" ")[0];
        const amap = {};
        amap[dispname] = displaymap[dispname];
        if (firstwords.hasOwnProperty(firstword)) {
            firstwords[firstword].push(amap);
        }
        else {
            firstwords[firstword] = [amap];
        }
    }
    return firstwords;
}

/**
 * Do string replacement on a message given the display map.
 *
 * @param {String} string The string to perform replacements on.
 * @param {Object} displaymap A mapping of display names to slack user ids.
 * @return {String} The string with replacements performed.
 */
function replacementFromDisplayMap(string, displaymap) {
    const firstwords = makeFirstWordMap(displaymap);

    // Now parse the message to find the intersection of every word in the
    // message with every first word of all nicks.
    const match_words = new Set(Object.keys(firstwords));
    const string_words = new Set(string.split(" "));
    const matches = [...string_words].filter(x => match_words.has(x));

    if (matches && matches.length) {
        for (const firstword of matches) {
            if (firstwords.hasOwnProperty(firstword)) {
                const nicks = firstwords[firstword];
                if (nicks.length === 1) {
                    string = string.replace(firstword, "<@" + nicks[0][firstword] + ">");
                }
                else {
                    // Sort the displaynames by longest string first, and then match them from longest to shortest.
                    // This can match multiple times if there is more than one mention in the message
                    const displaynames = nicks.map(x => Object.keys(x)[0]);
                    displaynames.sort((x, y) => y.length - x.length);
                    for (const displayname of displaynames) {
                        if (string.includes(displayname)) {
                            string = string.replace(displayname, "<@" + displaymap[displayname] + ">");
                        }
                    }
                }
            }
        }
    }
    return string;
}

/**
 * Replace plain text form of @displayname mentions with the slack mention syntax.
 *
 * @param {Main} main the toplevel main instance
 * @param {String} string The string to perform replacements on.
 * @param {String} room_id The room the message was sent in.
 * @return {String} The string with replacements performed.
 */
function plainTextSlackMentions(main, string, room_id) {
    return getDisplayMap(main, room_id).then(displaymap => replacementFromDisplayMap(string, displaymap));
}

// These functions are copied and modified from the Gitter AS
// idx counts backwards from the end of the string; 0 is final character
function rcharAt(s,idx) {
    return s.charAt(s.length-1 - idx);
}

function firstWord(s) {
    var groups = s.match(/^\s*\S+/);
    return groups ? groups[0] : "";
}

function finalWord(s) {
    var groups = s.match(/\S+\s*$/);
    return groups ? groups[0] : "";
}

function makeDiff(prev, curr) {
    var i;
    for (i = 0; i < curr.length && i < prev.length; i++) {
        if (curr.charAt(i) != prev.charAt(i)) break;
    }
    // retreat to the start of a word
    while(i > 0 && /\S/.test(curr.charAt(i-1))) i--;

    var prefixLen = i;

    for(i = 0; i < curr.length && i < prev.length; i++) {
        if (rcharAt(curr, i) != rcharAt(prev, i)) break;
    }
    // advance to the end of a word
    while(i > 0 && /\S/.test(rcharAt(curr, i-1))) i--;

    var suffixLen = i;

    // Extract the common prefix and suffix strings themselves and
    //   mutate the prev/curr strings to only contain the differing
    //   middle region
    var prefix = curr.slice(0, prefixLen);
    curr = curr.slice(prefixLen);
    prev = prev.slice(prefixLen);

    var suffix = "";
    if (suffixLen > 0) {
        suffix = curr.slice(-suffixLen);
        curr = curr.slice(0, -suffixLen);
        prev = prev.slice(0, -suffixLen);
    }

    // At this point, we have four strings; the common prefix and
    //   suffix, and the edited middle part. To display it nicely as a
    //   matrix message we'll use the final word of the prefix and the
    //   first word of the suffix as "context" for a customly-formatted
    //   message.

    var before = finalWord(prefix);
    if (before != prefix) { before = "... " + before; }

    var after = firstWord(suffix);
    if (after != suffix) { after = after + " ..."; }

    // return {prev: prev,
    //         curr: curr,
    //         before: before,
    //         after: after};
    return {prev, curr, before, after};
}

module.exports = {
    matrixToSlack: matrixToSlack,
    slackToMatrix: slackToMatrix,
    getSlackFileUrl: getSlackFileUrl,
    htmlEscape: htmlEscape,
    replacementFromDisplayMap: replacementFromDisplayMap,
    makeFirstWordMap: makeFirstWordMap,
    makeDiff: makeDiff
};
<|endoftext|>


<commit_before>// @flow
import React from 'react';
import { take, xor, omit } from 'lodash';
import UndoIcon from 'react-icons/lib/md/undo';
import LeftArrow from 'react-icons/lib/fa/long-arrow-left';
import Cogs from 'react-icons/lib/fa/cogs';
import Color from 'color';
import {
  compose,
  withState,
  pure,
  withHandlers,
  withPropsOnChange,
  withProps,
} from 'recompose';
import JSURL from 'jsurl';

import { humanify } from '@ncigdc/utils/string';
import withRouter from '@ncigdc/utils/withRouter';

import Button, { buttonBaseStyles } from '@ncigdc/uikit/Button';
import { Row } from '@ncigdc/uikit/Flex';
import Info from '@ncigdc/uikit/Info';

import styled from '@ncigdc/theme/styled';
import { buttonLike } from '@ncigdc/theme/mixins';
import UnstyledButton from '@ncigdc/uikit/UnstyledButton';
import Link from '@ncigdc/components/Links/Link';

import { facetFieldDisplayMapper } from '@ncigdc/components/Aggregations';
import GeneSymbol from '@ncigdc/containers/GeneSymbol';

/*----------------------------------------------------------------------------*/

const Field = styled(Button, {
  backgroundColor: ({ theme }) => theme.greyScale2,
  ':hover': {
    backgroundColor: ({ theme }) =>
      Color(theme.greyScale2).lighten(0.7).rgbString(),
  },
});

const Value = styled(Button, {
  backgroundColor: ({ theme }) => theme.success,
  ':hover': {
    backgroundColor: ({ theme }) =>
      Color(theme.success).lighten(0.7).rgbString(),
  },
});

const Op = styled.span({
  ...buttonLike,
  backgroundColor: ({ theme }) => theme.primary,
  color: 'white',
});

const NotUnderlinedLink = styled(Link, {
  ':link': {
    textDecoration: 'none',
  },
});

const LinkButton = styled(Link, {
  ...buttonBaseStyles,
  flex: 'none',
  ':link': {
    textDecoration: 'none',
    color: buttonBaseStyles.color,
  },
});

type TProps = {
  query: Object,
  currentFilters: Array<Object>,
  onLessClicked: Function,
  isFilterExpanded: Function,
  style: Object,
  linkPathname?: string,
  linkText?: string,
  linkFieldMap?: Function,
  hideLinkOnEmpty: boolean,
};

const enhance = compose(
  withRouter,
  withPropsOnChange(['query'], ({ query: { filters } }) => ({
    filters: JSURL.parse(filters),
  })),
  withPropsOnChange(['filters'], ({ filters }) => ({
    currentFilters: (filters && filters.content) || [],
  })),
  withState('expandedFilters', 'setExpandedFilters', []),
  withProps(({ expandedFilters }) => ({
    isFilterExpanded: filter => expandedFilters.includes(filter),
  })),
  withProps(({ geneSymbolFragment }) => ({
    getDisplayValue: (field, value) => {
      switch (typeof value) {
        case 'string':
          if (field === 'genes.gene_id') {
            return <GeneSymbol explore={geneSymbolFragment} geneId={value} />;
          }
          return value;
        case 'boolean':
          return value ? 'true' : 'false';
        case 'number':
          return value;
        default:
          return value;
      }
    },
  })),
  withHandlers({
    onLessClicked: ({ expandedFilters, setExpandedFilters }) => filter => {
      setExpandedFilters(xor(expandedFilters, [filter]));
    },
  }),
  pure,
);

const styles = {
  leftParen: {
    fontSize: '2rem',
    marginRight: '0.3rem',
    display: 'flex',
    alignItems: 'center',
  },
  rightParen: {
    fontSize: '2rem',
    marginRight: '0.3rem',
    display: 'flex',
    alignItems: 'center',
  },
  groupPadding: {
    padding: '0.5rem 0',
  },
};

const CurrentFilters = (
  {
    query,
    currentFilters,
    onLessClicked,
    isFilterExpanded,
    style,
    linkPathname,
    linkText,
    linkFieldMap = f => f,
    hideLinkOnEmpty = true,
    geneSymbolFragment,
    getDisplayValue,
  }: TProps = {},
) =>
  <Info style={style}>
    {!currentFilters.length &&

        <LeftArrow />

          Start searching by selecting a facet

      }
    {!!currentFilters.length &&
      <Row
        style={{
          width: '100%',
          justifyContent: 'space-between',
        }}
      >
        <Row wrap spacing="0.3rem">
          <NotUnderlinedLink
            style={styles.groupPadding}
            query={omit(query, 'filters')}
          >
            <Button leftIcon={<UndoIcon />}>Clear</Button>
          </NotUnderlinedLink>

          {currentFilters.map((filter, i) =>
            <Row
              key={`${filter.content
                .field}.${filter.op}.${filter.content.value.map(v => v)}`}
              spacing="0.3rem"
              style={styles.groupPadding}
            >
              <NotUnderlinedLink
                merge="toggle"
                query={{
                  offset: 0,
                  filters: {
                    op: 'and',
                    content: [filter],
                  },
                }}
              >
                <Field>
                  {humanify({
                    term: facetFieldDisplayMapper(filter.content.field),
                  })}
                </Field>
              </NotUnderlinedLink>
              {filter.op === 'in' &&
                filter.content.value.length === 1 &&
                <Op>IS</Op>}
              {filter.op === 'in' &&
                filter.content.value.length > 1 &&
                <Op>IN</Op>}
              {filter.op !== 'in' && <Button>{filter.op}</Button>}
              {filter.content.value.length > 1 &&
                (}
              {(isFilterExpanded(filter)
                ? filter.content.value
                : take(filter.content.value, 2)).map(value =>
                <NotUnderlinedLink
                  key={value}
                  merge="toggle"
                  query={{
                    offset: 0,
                    filters: {
                      op: 'and',
                      content: [filter],
                    },
                  }}
                >
                  <Value>
                    {getDisplayValue(filter.content.field, value)}
                  </Value>
                </NotUnderlinedLink>,
              )}
              {filter.content.value.length > 2 &&
                <UnstyledButton
                  style={styles.rightParen}
                  onClick={() => onLessClicked(filter)}
                >
                  …
                </UnstyledButton>}
              {isFilterExpanded(filter) &&
                <UnstyledButton
                  style={{ display: 'flex', alignItems: 'center' }}
                  onClick={() => onLessClicked(filter)}
                >
                  Less
                </UnstyledButton>}
              {filter.content.value.length > 1 &&
                )}
              {i < currentFilters.length - 1 && <Op>AND</Op>}
            </Row>,
          )}
        </Row>
      </Row>}
    {linkPathname &&
      (!hideLinkOnEmpty || !!currentFilters.length) &&
      <LinkButton
        pathname={linkPathname}
        query={
          currentFilters.length && {
            filters: {
              op: 'AND',
              content: currentFilters.map(
                ({ content: { field, value }, op }) => ({
                  op,
                  content: { field: linkFieldMap(field), value },
                }),
              ),
            },
          }
        }
      >
        <Cogs style={{ marginRight: 5 }} />{linkText}
      </LinkButton>}
  </Info>;

/*----------------------------------------------------------------------------*/

export default enhance(CurrentFilters);
<commit_msg>🐛 [PRTL-1330] clear individual values from current filters<commit_after>// @flow
import React from 'react';
import { take, xor, omit } from 'lodash';
import UndoIcon from 'react-icons/lib/md/undo';
import LeftArrow from 'react-icons/lib/fa/long-arrow-left';
import Cogs from 'react-icons/lib/fa/cogs';
import Color from 'color';
import {
  compose,
  withState,
  pure,
  withHandlers,
  withPropsOnChange,
  withProps,
} from 'recompose';
import JSURL from 'jsurl';

import { humanify } from '@ncigdc/utils/string';
import withRouter from '@ncigdc/utils/withRouter';

import Button, { buttonBaseStyles } from '@ncigdc/uikit/Button';
import { Row } from '@ncigdc/uikit/Flex';
import Info from '@ncigdc/uikit/Info';

import styled from '@ncigdc/theme/styled';
import { buttonLike } from '@ncigdc/theme/mixins';
import UnstyledButton from '@ncigdc/uikit/UnstyledButton';
import Link from '@ncigdc/components/Links/Link';

import { facetFieldDisplayMapper } from '@ncigdc/components/Aggregations';
import GeneSymbol from '@ncigdc/containers/GeneSymbol';

/*----------------------------------------------------------------------------*/

const Field = styled(Button, {
  backgroundColor: ({ theme }) => theme.greyScale2,
  ':hover': {
    backgroundColor: ({ theme }) =>
      Color(theme.greyScale2).lighten(0.7).rgbString(),
  },
});

const Value = styled(Button, {
  backgroundColor: ({ theme }) => theme.success,
  ':hover': {
    backgroundColor: ({ theme }) =>
      Color(theme.success).lighten(0.7).rgbString(),
  },
});

const Op = styled.span({
  ...buttonLike,
  backgroundColor: ({ theme }) => theme.primary,
  color: 'white',
});

const NotUnderlinedLink = styled(Link, {
  ':link': {
    textDecoration: 'none',
  },
});

const LinkButton = styled(Link, {
  ...buttonBaseStyles,
  flex: 'none',
  ':link': {
    textDecoration: 'none',
    color: buttonBaseStyles.color,
  },
});

type TProps = {
  query: Object,
  currentFilters: Array<Object>,
  onLessClicked: Function,
  isFilterExpanded: Function,
  style: Object,
  linkPathname?: string,
  linkText?: string,
  linkFieldMap?: Function,
  hideLinkOnEmpty: boolean,
};

const enhance = compose(
  withRouter,
  withPropsOnChange(['query'], ({ query: { filters } }) => ({
    filters: JSURL.parse(filters),
  })),
  withPropsOnChange(['filters'], ({ filters }) => ({
    currentFilters: (filters && filters.content) || [],
  })),
  withState('expandedFilters', 'setExpandedFilters', []),
  withProps(({ expandedFilters }) => ({
    isFilterExpanded: filter => expandedFilters.includes(filter),
  })),
  withProps(({ geneSymbolFragment }) => ({
    getDisplayValue: (field, value) => {
      switch (typeof value) {
        case 'string':
          if (field === 'genes.gene_id') {
            return <GeneSymbol explore={geneSymbolFragment} geneId={value} />;
          }
          return value;
        case 'boolean':
          return value ? 'true' : 'false';
        case 'number':
          return value;
        default:
          return value;
      }
    },
  })),
  withHandlers({
    onLessClicked: ({ expandedFilters, setExpandedFilters }) => filter => {
      setExpandedFilters(xor(expandedFilters, [filter]));
    },
  }),
  pure,
);

const styles = {
  leftParen: {
    fontSize: '2rem',
    marginRight: '0.3rem',
    display: 'flex',
    alignItems: 'center',
  },
  rightParen: {
    fontSize: '2rem',
    marginRight: '0.3rem',
    display: 'flex',
    alignItems: 'center',
  },
  groupPadding: {
    padding: '0.5rem 0',
  },
};

const CurrentFilters = (
  {
    query,
    currentFilters,
    onLessClicked,
    isFilterExpanded,
    style,
    linkPathname,
    linkText,
    linkFieldMap = f => f,
    hideLinkOnEmpty = true,
    geneSymbolFragment,
    getDisplayValue,
  }: TProps = {},
) =>
  <Info style={style}>
    {!currentFilters.length &&

        <LeftArrow />

          Start searching by selecting a facet

      }
    {!!currentFilters.length &&
      <Row
        style={{
          width: '100%',
          justifyContent: 'space-between',
        }}
      >
        <Row wrap spacing="0.3rem">
          <NotUnderlinedLink
            style={styles.groupPadding}
            query={omit(query, 'filters')}
          >
            <Button leftIcon={<UndoIcon />}>Clear</Button>
          </NotUnderlinedLink>

          {currentFilters.map((filter, i) =>
            <Row
              key={`${filter.content
                .field}.${filter.op}.${filter.content.value.map(v => v)}`}
              spacing="0.3rem"
              style={styles.groupPadding}
            >
              <NotUnderlinedLink
                merge="toggle"
                query={{
                  offset: 0,
                  filters: {
                    op: 'and',
                    content: [filter],
                  },
                }}
              >
                <Field>
                  {humanify({
                    term: facetFieldDisplayMapper(filter.content.field),
                  })}
                </Field>
              </NotUnderlinedLink>
              {filter.op === 'in' &&
                filter.content.value.length === 1 &&
                <Op>IS</Op>}
              {filter.op === 'in' &&
                filter.content.value.length > 1 &&
                <Op>IN</Op>}
              {filter.op !== 'in' && <Button>{filter.op}</Button>}
              {filter.content.value.length > 1 &&
                (}
              {(isFilterExpanded(filter)
                ? filter.content.value
                : take(filter.content.value, 2)).map(value =>
                <NotUnderlinedLink
                  key={value}
                  merge="toggle"
                  query={{
                    offset: 0,
                    filters: {
                      op: 'and',
                      content: [
                        {
                          op: filter.op,
                          content: {
                            field: filter.content.field,
                            value: [value],
                          },
                        },
                      ],
                    },
                  }}
                >
                  <Value>
                    {getDisplayValue(filter.content.field, value)}
                  </Value>
                </NotUnderlinedLink>,
              )}
              {filter.content.value.length > 2 &&
                <UnstyledButton
                  style={styles.rightParen}
                  onClick={() => onLessClicked(filter)}
                >
                  …
                </UnstyledButton>}
              {isFilterExpanded(filter) &&
                <UnstyledButton
                  style={{ display: 'flex', alignItems: 'center' }}
                  onClick={() => onLessClicked(filter)}
                >
                  Less
                </UnstyledButton>}
              {filter.content.value.length > 1 &&
                )}
              {i < currentFilters.length - 1 && <Op>AND</Op>}
            </Row>,
          )}
        </Row>
      </Row>}
    {linkPathname &&
      (!hideLinkOnEmpty || !!currentFilters.length) &&
      <LinkButton
        pathname={linkPathname}
        query={
          currentFilters.length && {
            filters: {
              op: 'AND',
              content: currentFilters.map(
                ({ content: { field, value }, op }) => ({
                  op,
                  content: { field: linkFieldMap(field), value },
                }),
              ),
            },
          }
        }
      >
        <Cogs style={{ marginRight: 5 }} />{linkText}
      </LinkButton>}
  </Info>;

/*----------------------------------------------------------------------------*/

export default enhance(CurrentFilters);
<|endoftext|>


<commit_before>"use strict";

var tools = require("./tools.js");
var helper = require("./helper.js");
var assert = require("assert");
var EventEmitter = require("events").EventEmitter;

function noop() {}
function emptyTask(done) { done && done(); }

// ----------------------------------------
// tools

tools.task = function task(taskName, depends, taskFunc) {
    // arguments.length === 1
    if(depends === undefined && taskFunc === undefined) {
        depends = [];
        taskFunc = emptyTask;

    // arguments.length === 2
    } else if(taskFunc === undefined) {
        if(typeof depends === "function") {
            taskFunc = depends;
            depends = [];
        } else {
            taskFunc = emptyTask;
        }
    }

    var _task = new Task(taskName, depends, taskFunc);
    Task.all[taskName] = _task;
    return _task;
};

tools.asyncTask = function asyncTask(taskName, depends, taskFunc) {
    var task = tools.task(taskName, depends, taskFunc);
    task.setAsync();
    return task;
};

tools.before = function before(taskName) {
    Task.beforeTaskList.push(taskName);
};

tools.after = function after(taskName) {
    Task.afterTaskList.push(taskName);
};

tools.run = function run(taskName, callback) {
    var task = Task.get(taskName);
    task.first = true;
    tools.runOnce(taskName, callback);
};

tools.runOnce = function runOnce(taskName, callback) {
    var task = Task.get(taskName);
    task.start(callback || function(err) {
        if(err) { throw err; }
    });
};

tools.catchError = function(taskName, listener) {
    Task.errorEvents.on(taskName, listener);
};

// ----------------------------------------
// exports

exports.nextMode = function nextMode() {
    assert(Task.modeList.length !== 0);
    Task.mode = Task.modeList.pop();
};

exports.runMain = function runMain(mainTasks, errorHandler) {
    tools.asyncTask("__before", Task.beforeTaskList, emptyTask);
    var main = tools.asyncTask("__main", ["__before"].concat(mainTasks), emptyTask);
    var after = tools.asyncTask("__after", Task.afterTaskList, emptyTask);

    main.start(function(mainTaskError) {
        after.start(function(afterTaskError) {
            errorHandler(mainTaskError, afterTaskError);
        });
    });
};

exports.reset = function () {
    Task.all = Object.create(null);
    Task.beforeTaskList = [];
    Task.afterTaskList = [];
    Task.errorEvents = new EventEmitter();
};

/**
 * Task class
 */

function Task(taskName, depends, func) {
    if(!(this instanceof Task)) { throw new TypeError("Constructor Task requires 'new'"); }
    assert(typeof taskName === "string", "TaskName must be a string");
    assert(depends && helper.isArrayLike(depends), "Dependency tasks must be a array");
    assert(typeof func === "function", "Task func must be a function");
    if(Task.mode !== Task.BUILTIN && taskName.slice(2) === "--") {
        throw new Error("Invalid task name '" + taskName + "'");
    }

    this.name = taskName;
    this.depends = depends;
    this.func = func;
    this.async = false;
    this.first = true;
    this.mode = Task.mode;
    this.description = "";
    this.timeout = Task.NO_TIMEOUT;
    this.readTaskOptions();
}

exports.Task = Task;

Task.NO_TIMEOUT = -1;

Task.USER = "user";

Task.PLUGIN = "plugin";

Task.BUILTIN = "builtin";

Task.modeList = [Task.USER, Task.PLUGIN, Task.BUILTIN];

Task.mode = Task.modeList.pop();

exports.reset();

Task.get = function(taskName) {
    var task = Task.all[taskName];
    if(task === undefined) {
        throw new Error("Unkown task '" + taskName + "'");
    }
    return task;
};

Task.getAll = function() {
    var tasks = [];
    for(var name in Task.all) {
        tasks.push(Task.all[name]);
    }
    return tasks;
};

Task.prototype.start = function(done) {
    assert(typeof done === "function");
    var runner = new TaskRunner(this);
    var depends = this.depends;
    var dependsTaskIndex = 0;

    startDependsTask();

    function startDependsTask() {
        if(dependsTaskIndex === depends.length) {
            runner.run(done);
        } else {
            Task.get(depends[dependsTaskIndex]).start(callback);
        }
    }

    function callback(error) {
        dependsTaskIndex += 1;
        if(error) {
            done(error);
        } else {
            startDependsTask();
        }
    }
};

Task.prototype.handleError = function(error) {
    if(error === undefined || error === null) {
        return null;
    }

    var listeners = Task.errorEvents.listeners(this.name);
    for(var i = 0; i < listeners.length; i++) {
        try {
            listeners[i](error);
            error = null;
            break;
        } catch(e) {
            error = e;
        }
    }
    return error;
};

Task.prototype.setAsync = function() {
    this.async = true;
};

Task.prototype.readTaskOptions = function() {
    var regex = /\/\/\s*(\w+)\s*:\s*(.*)/g;
    var source = this.func.toString();
    var match = regex.exec(source);

    while(match) {
        var optKey = match[1];
        var optValue = match[2];
        if(optKey === "description" || optKey === "desc") {
            this.description = optValue;
        } else if(optKey === "timeout") {
            var timeout = parseInt(optValue);
            if(optValue.match(/^\dmin/)) {
                // timeout: minutes
                this.timeout = timeout * 1000 * 60;
            } else if(optValue.match(/^\d+sec/)) {
                // timeout: seconds
                this.timeout = timeout * 1000;
            } else {
                // timeout: milliseconds
                this.timeout = timeout;
            }
        } else {
            throw new Error("Unkown task option '" + optKey + "'");
        }
        match = regex.exec(source);
    }
};

/**
 * TaskRunner class
 */

function TaskRunner(task) {
    this.task = task;
}

TaskRunner.prototype.run = function(done) {
    if(this.task.first === false) {
        done(null);
        return;
    }
    this.task.first = false;
    if(this.task.async) {
        this.runAsync(done);
    } else {
        this.runSync(done);
    }
};

TaskRunner.prototype.runSync = function(done) {
    var error = null;
    try {
        this.task.func();
    } catch(e) {
        error = this.task.handleError(e);
    }
    done(error);
};

TaskRunner.prototype.runAsync = function (done) {
    var task = this.task;
    var timeoutId;

    try {
        task.func(function(error) {
            clearTimeout(timeoutId);
            error = task.handleError(error);
            setImmediate(done, error);
        });
    } catch(error) {
        setImmediate(done, task.handleError(error));
    }

    if(task.timeout !== Task.NO_TIMEOUT) {
        timeoutId = setTimeout(function() {
            done(new Error("timeout"));
            done = noop;
        }, task.timeout);
    }
};
<commit_msg>Typo fix<commit_after>"use strict";

var tools = require("./tools.js");
var helper = require("./helper.js");
var assert = require("assert");
var EventEmitter = require("events").EventEmitter;

function noop() {}
function emptyTask(done) { done && done(); }

// ----------------------------------------
// tools

tools.task = function task(taskName, depends, taskFunc) {
    // arguments.length === 1
    if(depends === undefined && taskFunc === undefined) {
        depends = [];
        taskFunc = emptyTask;

    // arguments.length === 2
    } else if(taskFunc === undefined) {
        if(typeof depends === "function") {
            taskFunc = depends;
            depends = [];
        } else {
            taskFunc = emptyTask;
        }
    }

    var _task = new Task(taskName, depends, taskFunc);
    Task.all[taskName] = _task;
    return _task;
};

tools.asyncTask = function asyncTask(taskName, depends, taskFunc) {
    var task = tools.task(taskName, depends, taskFunc);
    task.setAsync();
    return task;
};

tools.before = function before(taskName) {
    Task.beforeTaskList.push(taskName);
};

tools.after = function after(taskName) {
    Task.afterTaskList.push(taskName);
};

tools.run = function run(taskName, callback) {
    var task = Task.get(taskName);
    task.first = true;
    tools.runOnce(taskName, callback);
};

tools.runOnce = function runOnce(taskName, callback) {
    var task = Task.get(taskName);
    task.start(callback || function(err) {
        if(err) { throw err; }
    });
};

tools.catchError = function(taskName, listener) {
    Task.errorEvents.on(taskName, listener);
};

// ----------------------------------------
// exports

exports.nextMode = function nextMode() {
    assert(Task.modeList.length !== 0);
    Task.mode = Task.modeList.pop();
};

exports.runMain = function runMain(mainTasks, errorHandler) {
    tools.asyncTask("__before", Task.beforeTaskList, emptyTask);
    var main = tools.asyncTask("__main", ["__before"].concat(mainTasks), emptyTask);
    var after = tools.asyncTask("__after", Task.afterTaskList, emptyTask);

    main.start(function(mainTaskError) {
        after.start(function(afterTaskError) {
            errorHandler(mainTaskError, afterTaskError);
        });
    });
};

exports.reset = function () {
    Task.all = Object.create(null);
    Task.beforeTaskList = [];
    Task.afterTaskList = [];
    Task.errorEvents = new EventEmitter();
};

/**
 * Task class
 */

function Task(taskName, depends, func) {
    if(!(this instanceof Task)) { throw new TypeError("Constructor Task requires 'new'"); }
    assert(typeof taskName === "string", "TaskName must be a string");
    assert(depends && helper.isArrayLike(depends), "Dependency tasks must be a array");
    assert(typeof func === "function", "Task func must be a function");
    if(Task.mode !== Task.BUILTIN && taskName.slice(2) === "--") {
        throw new Error("Invalid task name '" + taskName + "'");
    }

    this.name = taskName;
    this.depends = depends;
    this.func = func;
    this.async = false;
    this.first = true;
    this.mode = Task.mode;
    this.description = "";
    this.timeout = Task.NO_TIMEOUT;
    this.readTaskOptions();
}

exports.Task = Task;

Task.NO_TIMEOUT = -1;

Task.USER = "user";

Task.PLUGIN = "plugin";

Task.BUILTIN = "builtin";

Task.modeList = [Task.USER, Task.PLUGIN, Task.BUILTIN];

Task.mode = Task.modeList.pop();

exports.reset();

Task.get = function(taskName) {
    var task = Task.all[taskName];
    if(task === undefined) {
        throw new Error("Unknown task '" + taskName + "'");
    }
    return task;
};

Task.getAll = function() {
    var tasks = [];
    for(var name in Task.all) {
        tasks.push(Task.all[name]);
    }
    return tasks;
};

Task.prototype.start = function(done) {
    assert(typeof done === "function");
    var runner = new TaskRunner(this);
    var depends = this.depends;
    var dependsTaskIndex = 0;

    startDependsTask();

    function startDependsTask() {
        if(dependsTaskIndex === depends.length) {
            runner.run(done);
        } else {
            Task.get(depends[dependsTaskIndex]).start(callback);
        }
    }

    function callback(error) {
        dependsTaskIndex += 1;
        if(error) {
            done(error);
        } else {
            startDependsTask();
        }
    }
};

Task.prototype.handleError = function(error) {
    if(error === undefined || error === null) {
        return null;
    }

    var listeners = Task.errorEvents.listeners(this.name);
    for(var i = 0; i < listeners.length; i++) {
        try {
            listeners[i](error);
            error = null;
            break;
        } catch(e) {
            error = e;
        }
    }
    return error;
};

Task.prototype.setAsync = function() {
    this.async = true;
};

Task.prototype.readTaskOptions = function() {
    var regex = /\/\/\s*(\w+)\s*:\s*(.*)/g;
    var source = this.func.toString();
    var match = regex.exec(source);

    while(match) {
        var optKey = match[1];
        var optValue = match[2];
        if(optKey === "description" || optKey === "desc") {
            this.description = optValue;
        } else if(optKey === "timeout") {
            var timeout = parseInt(optValue);
            if(optValue.match(/^\dmin/)) {
                // timeout: minutes
                this.timeout = timeout * 1000 * 60;
            } else if(optValue.match(/^\d+sec/)) {
                // timeout: seconds
                this.timeout = timeout * 1000;
            } else {
                // timeout: milliseconds
                this.timeout = timeout;
            }
        } else {
            throw new Error("Unkown task option '" + optKey + "'");
        }
        match = regex.exec(source);
    }
};

/**
 * TaskRunner class
 */

function TaskRunner(task) {
    this.task = task;
}

TaskRunner.prototype.run = function(done) {
    if(this.task.first === false) {
        done(null);
        return;
    }
    this.task.first = false;
    if(this.task.async) {
        this.runAsync(done);
    } else {
        this.runSync(done);
    }
};

TaskRunner.prototype.runSync = function(done) {
    var error = null;
    try {
        this.task.func();
    } catch(e) {
        error = this.task.handleError(e);
    }
    done(error);
};

TaskRunner.prototype.runAsync = function (done) {
    var task = this.task;
    var timeoutId;

    try {
        task.func(function(error) {
            clearTimeout(timeoutId);
            error = task.handleError(error);
            setImmediate(done, error);
        });
    } catch(error) {
        setImmediate(done, task.handleError(error));
    }

    if(task.timeout !== Task.NO_TIMEOUT) {
        timeoutId = setTimeout(function() {
            done(new Error("timeout"));
            done = noop;
        }, task.timeout);
    }
};
<|endoftext|>


<commit_before>var ini = require ("node-ini");
var radius = require ("node-radius");
var fs = require("fs");
var zpad = require ("zpad");
var strftime = require ("strftime");
var Tail = require ("node-tail").Tail;
var dgram = require ("dgram");

// Read the config file
var nsini = ini.parseSync('./config/config.ini');

// FUNCTIONS

// Find latest file. Used at startup and when nacspeed determines the log has been rotated
// Sync function. Returns path of latest end-system event logfile.
function findLatestFile (dirpath) {
	var n = 0;
	while (n < 20) {
		n++;
		try {
			fs.statSync(dirpath + "NetSight/appdata/logs/" + strftime("nacESE.%Y_%m_%d_") + zpad(n) + ".log").isFile();
		}
		catch (err) {
			n--;
			break;
		}
	}
	return dirpath + "NetSight/appdata/logs/" + strftime("nacESE.%Y_%m_%d_") + zpad(n) + ".log";
}

// Process nacESE log file line
function nacese (line) {
	// This is tab-delimited, we need to split this string
	var fields = line.split("\t");
	if (fields[5] == "AUTH_MAC_PAP") {
		if (fields[2] == "<null/>" || fields[8] == "<empty/>") {
			console.log ("discarded empty nacESE entry");
		} else {
			console.log ("processed nacESE entry from " + fields[2] + " " + fields[8] + " (" + fields[6] + ")");
			sendRadiusLogin (fields[8], fields[2]);
		}
	}
}

// Construct and send RADIUS accounting packet
function sendRadiusLogin (username, framedip) {
	var packet = radius.encode({
	  code: "Accounting-Request",
	  secret: nsini.nacspeed.radius_ss,
	  attributes: [
		['User-Name', username],
		['Framed-IP-Address', framedip]
	  ]
	});

	var client = dgram.createSocket("udp4");
	client.bind(49001);

	client.send(packet, 0, packet.length, 1813, nsini.nacspeed.lsip);
	client.close();

}

var logfile = findLatestFile (nsini.nacspeed.nsroot);

tail = new Tail(logfile);

tail.on("line", function(data) {
  nacese (data);
});

<commit_msg>RADIUS debugging<commit_after>var ini = require ("node-ini");
var radius = require ("node-radius");
var fs = require("fs");
var zpad = require ("zpad");
var strftime = require ("strftime");
var Tail = require ("node-tail").Tail;
var dgram = require ("dgram");

// Create the UDP client
var client = dgram.createSocket("udp4");

// Read the config file
var nsini = ini.parseSync('./config/config.ini');

// FUNCTIONS

// Find latest file. Used at startup and when nacspeed determines the log has been rotated
// Sync function. Returns path of latest end-system event logfile.
function findLatestFile (dirpath) {
	var n = 0;
	while (n < 20) {
		n++;
		try {
			fs.statSync(dirpath + "NetSight/appdata/logs/" + strftime("nacESE.%Y_%m_%d_") + zpad(n) + ".log").isFile();
		}
		catch (err) {
			n--;
			break;
		}
	}
	return dirpath + "NetSight/appdata/logs/" + strftime("nacESE.%Y_%m_%d_") + zpad(n) + ".log";
}

// Process nacESE log file line
function nacese (line) {
	// This is tab-delimited, we need to split this string
	var fields = line.split("\t");
	if (fields[5] == "AUTH_MAC_PAP") {
		if (fields[2] == "<null/>" || fields[8] == "<empty/>") {
			console.log ("discarded empty nacESE entry");
		} else {
			console.log ("processed nacESE entry from " + fields[2] + " " + fields[8] + " (" + fields[6] + ")");
			sendRadiusLogin (fields[8], fields[2]);
		}
	}
}

// Construct and send RADIUS accounting packet
function sendRadiusLogin (username, framedip) {
	var packet = radius.encode({
	  code: "Accounting-Request",
	  secret: nsini.nacspeed.radius_ss,
	  attributes: [
		['User-Name', username],
		['Framed-IP-Address', framedip],
		['NAS-IP-Address', '0.0.0.0'],
		['NAS-Port', '0'],
		['NAS-Port-Type', 'Wireless-Other'],
		['Acct-Status-Type', 'Start']
	  ]
	});

	client.send(packet, 0, packet.length, 1813, nsini.nacspeed.lsip);

}

var logfile = findLatestFile (nsini.nacspeed.nsroot);

tail = new Tail(logfile);

tail.on("line", function(data) {
  nacese (data);
});

<|endoftext|>


<commit_before>$(function() {
  $("#cy").height('500');
  var cy = cytoscape({
    container: document.getElementById('cy'),
    style: [
      {
        selector: 'node',
        style: {
          content: "data(name)"
        }
      }
    ],
    ready: function(){
      cy.add([
        { group: "nodes", data: { id: "n0", name: "Pathway Commons @Malaga" }, position: { x: 300, y: 200 } },
        { group: "nodes", data: { id: "n1", name: "Linked Life Data" }, position: { x: 600, y: 200 } },
        { group: "nodes", data: { id: "n2", name: "National Center for Biomedical Ontologies" }, position: { x: 300, y: 100 } },
        { group: "nodes", data: { id: "n3", name: "Allie" }, position: { x: 100, y: 300 } },
        { group: "nodes", data: { id: "n4", name: "HCLS DERI" }, position: { x: 300, y: 300 } },
        { group: "nodes", data: { id: "n5", name: "DrugBank @FU Berlin" }, position: { x: 600, y: 100 } },
        { group: "edges", data: { id: "e0", source: "n0", target: "n1" } },
        { group: "edges", data: { id: "e1", source: "n0", target: "n2" } },
        { group: "edges", data: { id: "e2", source: "n3", target: "n4" } },
        { group: "edges", data: { id: "e3", source: "n1", target: "n5" } },
        { group: "edges", data: { id: "e4", source: "n4", target: "n0" } },
      ]);
    }
  });
});
<commit_msg>Set layout and style appropriately to draw graphs<commit_after>$(function() {
  $("#cy").height('500');
  var url = "/api/endpoints/graph/"
  $.getJSON(url, function(data) {
    var cy = window.cy = cytoscape({
      container: document.getElementById('cy'),

      layout: {
        name: 'cose',
        idealEdgeLength: 100,
        nodeOverlap: 20
      },

      style: [
        {
          selector: 'node',
          style: {
            content: "data(name)"
          }
        },
        {
          selector: 'edge',
          style: {
            'label': 'data(label)',
            'width': 3,
            'line-color': '#ccc'
          }
        },
        {
          selector: 'edge',
          css: {
            'target-arrow-shape': 'triangle',
            'curve-style': 'bezier'
          }
        }
      ],

      elements: data

    });
  });

});
<|endoftext|>


<commit_before>"use strict"

const p = require("path")
const Promise = require("bluebird")
const wrapp = require("./wrapp")
const util = require("./utils")
const boot = require("./boot")
const $ = require("./fn")

const RGX = /[\\|\/]/g
const co = Promise.coroutine
const normalize = p.normalize
const format = p.format
const parse = p.parse
const sep = p.sep

function Task(fly) {
	// construct shape
	this.$ = util
	this.root = fly.root
	this._ = {files: [], globs: [], prevs: []}
	// attach parent fns to Task
	this.parallel = fly.parallel.bind(fly)
	this.serial = fly.serial.bind(fly)
	this.start = fly.start.bind(fly)
	this.emit = fly.emit.bind(fly)
	// attach `fly.plugins` to prototype
	for (const k in fly.plugins) {
		this[k] = fly.plugins[k].bind(this)
	}
	// return chained methods + shared
	return boot(this)
}

Task.prototype.exec = function(fn, opts, data) {
	// cache ref to `fly.tasks[].data` values
	this._ = data
	return fn.call(this, this, opts)
}

Task.prototype.run = co(function * (opts, func) {
	return yield wrapp(opts, func).call(this)
})

Task.prototype.source = co(function * (globs, opts) {
	globs = $.flatten($.toArray(globs))
	const files = yield this.$.expand(globs, opts)

	if (globs.length && !files.length) {
		this.emit("globs_no_match", globs, opts)
	}

	// pre-fetch each file"s content
	const datas = yield Promise.all(files.map(f => this.$.read(f)))

	// update known globs
	this._.globs = globs
	// update known files, as (mod"d) `pathObject`s
	this._.files = files.map((el, idx) => {
		const obj = parse(el)
		return {
			dir: normalize(obj.dir),
			data: datas[idx],
			base: obj.base
		}
	})
})

Task.prototype.target = co(function * (dirs, opts) {
	dirs = $.flatten($.toArray(dirs))
	opts = opts || {}

	const files = this._.files
	// using `watcher`? original globs passed as `prevs`
	// non-wildcard glob segments that should be trimmed!
	const globs = (this._.prevs && this._.prevs.length > 0) ? this._.prevs : this._.globs

	const trims = globs.map(g => {
		let seg = g.split(RGX)
		const idx = seg.findIndex(str => str.includes("*"))

		if (idx === -1) {
			seg.pop()
		} else {
			seg = seg.slice(0, idx)
		}

		return normalize(seg.join(sep))
	}).sort((a, b) => b.length - a.length)

	const tLength = trims.length

	return yield Promise.all(
		$.flatten(
			files.map(obj => dirs.map(d => {
				let i = 0
				// clone `pathObject` per target dir
				const o = { dir: obj.dir, base: obj.base }
				// replace `source` segments with `target` dir
				for (; i < tLength; i++) {
					o.dir = o.dir.replace(trims[i], d)
				}
				// create final filepath & write to it!
				return this.$.write(format(o), obj.data, opts)
			}))
		)
	)
})

module.exports = Task
<commit_msg>update Task class<commit_after>"use strict"

const p = require("path")
const Promise = require("bluebird")
const wrapp = require("./wrapp")
const util = require("./utils")
const boot = require("./boot")
const $ = require("./fn")

const RGX = /[\\|\/]/g
const co = Promise.coroutine
const normalize = p.normalize
const format = p.format
const parse = p.parse
const sep = p.sep

function Task(ctx) {
	// construct shape
	this.$ = util
	this.root = ctx.root
	this._ = {files: [], globs: [], prevs: []}
	// attach parent fns to Task
	this.parallel = ctx.parallel.bind(ctx)
	this.serial = ctx.serial.bind(ctx)
	this.start = ctx.start.bind(ctx)
	this.emit = ctx.emit.bind(ctx)
	// attach `ctx.plugins` to prototype
	for (const k in ctx.plugins) {
		this[k] = ctx.plugins[k].bind(this)
	}
	// return chained methods + shared
	return boot(this)
}

Task.prototype.exec = function(fn, opts, data) {
	// cache ref to `ctx.tasks[].data` values
	this._ = data
	return fn.call(this, this, opts)
}

Task.prototype.run = co(function * (opts, func) {
	return yield wrapp(opts, func).call(this)
})

Task.prototype.source = co(function * (globs, opts) {
	globs = $.flatten($.toArray(globs))
	const files = yield this.$.expand(globs, opts)

	if (globs.length && !files.length) {
		this.emit("globs_no_match", globs, opts)
	}

	// pre-fetch each file"s content
	const datas = yield Promise.all(files.map(f => this.$.read(f)))

	// update known globs
	this._.globs = globs
	// update known files, as (mod"d) `pathObject`s
	this._.files = files.map((el, idx) => {
		const obj = parse(el)
		return {
			dir: normalize(obj.dir),
			data: datas[idx],
			base: obj.base
		}
	})
})

Task.prototype.target = co(function * (dirs, opts) {
	dirs = $.flatten($.toArray(dirs))
	opts = opts || {}

	const files = this._.files
	// using `watcher`? original globs passed as `prevs`
	// non-wildcard glob segments that should be trimmed!
	const globs = (this._.prevs && this._.prevs.length > 0) ? this._.prevs : this._.globs

	const trims = globs.map(g => {
		let seg = g.split(RGX)
		const idx = seg.findIndex(str => str.includes("*"))

		if (idx === -1) {
			seg.pop()
		} else {
			seg = seg.slice(0, idx)
		}

		return normalize(seg.join(sep))
	}).sort((a, b) => b.length - a.length)

	const tLength = trims.length

	return yield Promise.all(
		$.flatten(
			files.map(obj => dirs.map(d => {
				let i = 0
				// clone `pathObject` per target dir
				const o = { dir: obj.dir, base: obj.base }
				// replace `source` segments with `target` dir
				for (; i < tLength; i++) {
					o.dir = o.dir.replace(trims[i], d)
				}
				// create final filepath & write to it!
				return this.$.write(format(o), obj.data, opts)
			}))
		)
	)
})

module.exports = Task
<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

export const ACTIONS = {
  // tableAction constants
  SORT_DATA: 'sortData',
  FILTER_DATA: 'filterData',
  REFRESH_DATA: 'refreshData',
  ADD_ITEM: 'addItem',
  ADD_RECORD: 'addRecord',
  HIDE_OVER_STOCKED: 'hideOverStocked',
  TOGGLE_STOCK_OUT: 'toggleStockOut',
  TOGGLE_SHOW_FINALISED: 'toggleShowFinalised',
  REFRESH_DATA_WITH_FINALISED_TOGGLE: 'refreshDataWithFinalisedToggle',
  FILTER_DATA_WITH_FINALISED_TOGGLE: 'filterDataWithFinalisedToggle',
  FILTER_DATA_WITH_OVER_STOCK_TOGGLE: 'filterDataWithOverStockToggle',

  // pageAction constants
  OPEN_MODAL: 'openModal',
  CLOSE_MODAL: 'closeModal',
  EDIT_NAME: 'editName',

  // rowAction constants
  REFRESH_ROW: 'refreshRow',
  EDIT_TOTAL_QUANTITY: 'editTotalQuantity',
  EDIT_COUNTED_QUANTITY: 'editCountedQuantity',
  EDIT_REQUIRED_QUANTITY: 'editRequiredQuantity',
  EDIT_EXPIRY_DATE: 'editExpiryDate',
  ENFORCE_REASON: 'enforceReasonChoice',

  // cellAction constants
  FOCUS_CELL: 'focusCell',
  FOCUS_NEXT: 'focusNext',
  SELECT_ROW: 'selectRow',
  TOGGLE_SELECT_ALL: 'toggleSelectAll',
  DESELECT_ALL: 'deselectAll',
  SELECT_ONE_ROW: 'selectOneRow',
  DESELECT_ONE_ROW: 'deselectOneRow',
  SELECT_ROWS: 'selectRows',
  DESELECT_ROW: 'deselectRow',
  DELETE_RECORDS: 'deleteRecords',
};
<commit_msg>Add DATA_SET constants<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

export const ACTIONS = {
  // tableAction constants
  SORT_DATA: 'sortData',
  FILTER_DATA: 'filterData',
  REFRESH_DATA: 'refreshData',
  ADD_ITEM: 'addItem',
  ADD_RECORD: 'addRecord',
  HIDE_OVER_STOCKED: 'hideOverStocked',
  TOGGLE_STOCK_OUT: 'toggleStockOut',
  TOGGLE_SHOW_FINALISED: 'toggleShowFinalised',
  REFRESH_DATA_WITH_FINALISED_TOGGLE: 'refreshDataWithFinalisedToggle',
  FILTER_DATA_WITH_FINALISED_TOGGLE: 'filterDataWithFinalisedToggle',
  FILTER_DATA_WITH_OVER_STOCK_TOGGLE: 'filterDataWithOverStockToggle',

  // pageAction constants
  OPEN_MODAL: 'openModal',
  CLOSE_MODAL: 'closeModal',
  EDIT_NAME: 'editName',

  // rowAction constants
  REFRESH_ROW: 'refreshRow',
  EDIT_TOTAL_QUANTITY: 'editTotalQuantity',
  EDIT_COUNTED_QUANTITY: 'editCountedQuantity',
  EDIT_REQUIRED_QUANTITY: 'editRequiredQuantity',
  EDIT_EXPIRY_DATE: 'editExpiryDate',
  ENFORCE_REASON: 'enforceReasonChoice',

  // cellAction constants
  FOCUS_CELL: 'focusCell',
  FOCUS_NEXT: 'focusNext',
  SELECT_ROW: 'selectRow',
  TOGGLE_SELECT_ALL: 'toggleSelectAll',
  DESELECT_ALL: 'deselectAll',
  SELECT_ONE_ROW: 'selectOneRow',
  DESELECT_ONE_ROW: 'deselectOneRow',
  SELECT_ROWS: 'selectRows',
  DESELECT_ROW: 'deselectRow',
  DELETE_RECORDS: 'deleteRecords',
};

export const DATA_SET = {
  PATIENTS: 'patients',
  PRESCRIBERS: 'prescribers',
};
<|endoftext|>


<commit_before>var disqus_identifier;
var disqus_title;
var disqus_url;
var disqus_developer = 1;

Mizuho.initializeCommenting = $.proxy(function() {
	var self = this;
	this.commentBalloons = $('.comments');
	this.commentBalloons.click(function() {
		self.showCommentsPopup(this);
	});
	this.reloadCommentCount();
}, Mizuho);

Mizuho.showLightbox = $.proxy(function(creationCallback, closeCallback) {
	var lightbox = $(
		'
' +
		'

' +
		'

' +
		'
');
	var shadow = $('#comments_lightbox_shadow', lightbox);
	var contents = $('#comments_lightbox_contents > .shell', lightbox);
	shadow.click(function() {
		lightbox.remove();
		if (closeCallback) {
			closeCallback();
		}
	});
	lightbox.appendTo(document.body);
	creationCallback(contents);
}, Mizuho);

Mizuho.resetDisqus = $.proxy(function(identifier, title) {
	disqus_identifier = identifier;
	disqus_title = title;
	disqus_url = location.href;
	if (window.DISQUS) {
		window.DISQUS.reset({
			reload: true,
			config: function () {
				this.page.identifier = identifier;
				this.page.url = location.href;
				this.page.title = title;
			}
		});
	} else {
		var dsq = document.createElement('script');
		dsq.type = 'text/javascript';
		dsq.async = true;
		dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';
		(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
	}
}, Mizuho);

Mizuho.getCommentThreadInfo = $.proxy(function(balloon) {
	var info = {};
	if ($(balloon).closest('#toc').length > 0) {
		info.id = 'toctitle';
		info.topic = 'toctitle';
		info.title = $('#header h1').text() + " - " + $('#toctitle').text();
	} else {
		var header = $(balloon).next('h2, h3, h4');
		if (header.length > 0) {
			info.id = header.attr('id');
			info.topic = header.data('comment-topic');
			info.title = $('#header h1').text() + " - " + header.text();
		} else {
			info = undefined;
		}
	}
	return info;
}, Mizuho);

Mizuho.showCommentsPopup = $.proxy(function(balloon) {
	var info = this.getCommentThreadInfo(balloon);
	if (!info) {
		return;
	}

	var self = this;
	this.showLightbox(function(element) {
		element.html(
			'
Please use Gist if you want to post code snippets.
' +
			'

' +
			'comments powered by Disqus'
		);

		console.log(info);
		return;

		var thread = $('#disqus_thread', element);
		var timerID;

		function monitorDisqusLoaded() {
			if ($('#dsq-account-dropdown', thread).length > 0) {
				clearInterval(timerID);
				element.addClass('disqus-loaded');
				var likes = $('#dsq-global-toolbar .dsq-global-toolbar-likes', element);
				if ($.trim(likes.html()) != "") {
					element.addClass('align-center');
				}
			}
		}

		timerID = setInterval(monitorDisqusLoaded, 50);

		self.changingHash = true;
		location.hash = '#!/' + info.id;
		self.resetDisqus(info.topic, info.title);
	}, function() {
		self.reloadCommentCount();
	});
}, Mizuho);

Mizuho.reloadCommentCount = $.proxy(function() {
	var self = this;
	var hiddenContainer = $('

').appendTo(document.body);
	var locationWithoutHash = window.location.href.replace(/#.*/, '');
	var checks = [];

	this.commentBalloons.each(function() {
		var info = self.getCommentThreadInfo(this);
		if (info) {
			var link = $('').appendTo(hiddenContainer);
			link.attr('href', locationWithoutHash + '#!/' + info.id + '#disqus_thread');
			link.attr('data-disqus-identifier', info.topic);

			var balloon = $(this);
			function callback(count) {
				if (count > 0) {
					$('.count', balloon).text(count);
					balloon.removeClass('empty');
					balloon.addClass('nonempty');
					balloon.attr('title', null);
				}
			}

			checks.push({ element: link[0], callback: callback });
		}
	});

	var s = document.createElement('script');
	s.async = true;
	s.type = 'text/javascript';
	s.src = 'http://' + disqus_shortname + '.disqus.com/count.js';
	(document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s);

	var timerID;
	timerID = setInterval(function() {
		for (var i = checks.length - 1; i >= 0; i--) {
			if (checks[i].element.childNodes.length > 0) {
				checks[i].callback(parseInt($(checks[i].element).text()));
				checks.splice(i, 1);
			}
		}
		if (checks.length == 0) {
			clearInterval(timerID);
			hiddenContainer.remove();
		}
	}, 100);
}, Mizuho);

$(document).ready(Mizuho.initializeCommenting);
<commit_msg>Disqus code already works.<commit_after>var disqus_identifier;
var disqus_title;
var disqus_url;
var disqus_developer = 1;

Mizuho.initializeCommenting = $.proxy(function() {
	var self = this;
	this.commentBalloons = $('.comments');
	this.commentBalloons.click(function() {
		self.showCommentsPopup(this);
	});
	this.reloadCommentCount();
}, Mizuho);

Mizuho.showLightbox = $.proxy(function(creationCallback, closeCallback) {
	var lightbox = $(
		'
' +
		'

' +
		'

' +
		'
');
	var shadow = $('#comments_lightbox_shadow', lightbox);
	var contents = $('#comments_lightbox_contents > .shell', lightbox);
	shadow.click(function() {
		lightbox.remove();
		if (closeCallback) {
			closeCallback();
		}
	});
	lightbox.appendTo(document.body);
	creationCallback(contents);
}, Mizuho);

Mizuho.resetDisqus = $.proxy(function(identifier, title) {
	disqus_identifier = identifier;
	disqus_title = title;
	disqus_url = location.href;
	if (window.DISQUS) {
		window.DISQUS.reset({
			reload: true,
			config: function () {
				this.page.identifier = identifier;
				this.page.url = location.href;
				this.page.title = title;
			}
		});
	} else {
		var dsq = document.createElement('script');
		dsq.type = 'text/javascript';
		dsq.async = true;
		dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';
		(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
	}
}, Mizuho);

Mizuho.getCommentThreadInfo = $.proxy(function(balloon) {
	var info = {};
	if ($(balloon).closest('#toc').length > 0) {
		info.id = 'toctitle';
		info.topic = 'toctitle';
		info.title = $('#header h1').text() + " - " + $('#toctitle').text();
	} else {
		var header = $(balloon).next('h2, h3, h4');
		if (header.length > 0) {
			info.id = header.attr('id');
			info.topic = header.data('comment-topic');
			info.title = $('#header h1').text() + " - " + header.text();
		} else {
			info = undefined;
		}
	}
	return info;
}, Mizuho);

Mizuho.showCommentsPopup = $.proxy(function(balloon) {
	var info = this.getCommentThreadInfo(balloon);
	if (!info) {
		return;
	}

	var self = this;
	this.showLightbox(function(element) {
		element.html(
			'
Please use Gist if you want to post code snippets.
' +
			'

' +
			'comments powered by Disqus'
		);

		var thread = $('#disqus_thread', element);
		var timerID;

		function monitorDisqusLoaded() {
			if ($('#dsq-account-dropdown', thread).length > 0) {
				clearInterval(timerID);
				element.addClass('disqus-loaded');
				var likes = $('#dsq-global-toolbar .dsq-global-toolbar-likes', element);
				if ($.trim(likes.html()) != "") {
					element.addClass('align-center');
				}
			}
		}

		timerID = setInterval(monitorDisqusLoaded, 50);

		self.changingHash = true;
		location.hash = '#!/' + info.id;
		self.resetDisqus(info.topic, info.title);
	}, function() {
		self.reloadCommentCount();
	});
}, Mizuho);

Mizuho.reloadCommentCount = $.proxy(function() {
	var self = this;
	var hiddenContainer = $('

').appendTo(document.body);
	var locationWithoutHash = window.location.href.replace(/#.*/, '');
	var checks = [];

	this.commentBalloons.each(function() {
		var info = self.getCommentThreadInfo(this);
		if (info) {
			var link = $('').appendTo(hiddenContainer);
			link.attr('href', locationWithoutHash + '#!/' + info.id + '#disqus_thread');
			link.attr('data-disqus-identifier', info.topic);

			var balloon = $(this);
			function callback(count) {
				if (count > 0) {
					$('.count', balloon).text(count);
					balloon.removeClass('empty');
					balloon.addClass('nonempty');
					balloon.attr('title', null);
				}
			}

			checks.push({ element: link[0], callback: callback });
		}
	});

	var s = document.createElement('script');
	s.async = true;
	s.type = 'text/javascript';
	s.src = 'http://' + disqus_shortname + '.disqus.com/count.js';
	(document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s);

	var timerID;
	timerID = setInterval(function() {
		for (var i = checks.length - 1; i >= 0; i--) {
			if (checks[i].element.childNodes.length > 0) {
				checks[i].callback(parseInt($(checks[i].element).text()));
				checks.splice(i, 1);
			}
		}
		if (checks.length == 0) {
			clearInterval(timerID);
			hiddenContainer.remove();
		}
	}, 100);
}, Mizuho);

$(document).ready(Mizuho.initializeCommenting);
<|endoftext|>


<commit_before>let foo = "Hello, world!";
let bar = foo.replace("world", "foo$'bar");
console.log(bar);
<commit_msg>Removed temp file I accidentally committed because I'm suuuuper smart<commit_after><|endoftext|>


<commit_before>$(document).ready(function() {
    $("#design ul").hide();
    $("#design").on('click', function(event) {
        event.preventDefault();
        /* Act on the event */
        $(this).children('ul').slideToggle('slow', function() {

        });
    });
});
<commit_msg>Remove click on nav event 1.move the event to hover in css<commit_after>$(document).ready(function() {

});
<|endoftext|>