<commit_before>#!/usr/bin/env node

"use strict";

const os = require("os");
const fs = require("fs");
const path = require("path");
const meow = require("meow");
const chalk = require("chalk");
const execa = require("execa");
const indentString = require("indent-string");
const inquirer = require("inquirer");

// Global variables

const configDir = path.join(os.homedir(), "/.config/");
const configPath = path.join(configDir, "sshfs-box.json");

// CLI

const cli = meow(
  `
  Small CLI tool to mount/unmount directories on remote servers with sshfs.

  Usage
    $ sshfs-box

  Options
    --config, -c  Configure remote & local paths
                  Config stored in ~/.config/sshfs-box.json
`,
  {
    alias: {
      c: "config",
      configure: "config",
      settings: "config",
    },
  }
);

// Main

async function main() {
  let configString;
  try {
    configString = fs.readFileSync(configPath, "utf8");
  } catch (e) {
    process.stdout.write(
      `Can't open config on ${configPath}, creating new config`
    );
    const config = await promptEditConfig();
    await promptSshfs(config);
    return;
  }

  if (cli.flags.config) {
    const config = await promptEditConfig(configString);
    await promptSshfs(config);
  } else {
    const [configValid] = validateConfigString(configString);
    if (configValid) {
      await promptSshfs(JSON.parse(configString));
    } else {
      process.stdout.write(
        `${configPath} does not contain valid config, please fix it`
      );
      const config = await promptEditConfig(configString);
      await promptSshfs(config);
    }
  }
}

main(); // Start the app.

// Prompt functions.

async function promptEditConfig(defaultConfigOverride) {
  const defaultConfig = JSON.stringify(
    {
      urls: ["user@host1:", "user@host2:/home/user", "user@host2:/www"],
      folder: path.join(os.homedir(), "/remote"),
    },
    null,
    2
  );

  const promptSettings = {
    type: "editor",
    name: "config",
    message: "Configure sshfs-box",
    default: defaultConfigOverride || defaultConfig,
    validate: userInput => {
      const [valid, err] = validateConfigString(userInput);
      // Inquirer expects true if input is valid, otherwise string error message.
      return valid ? true : err;
    },
  };

  const response = await inquirer.prompt(promptSettings);

  const configString = response.config;
  const config = JSON.parse(configString);
  try {
    fs.mkdirSync(configDir);
  } catch (e) {
    // Pass.
  }

  fs.writeFileSync(configPath, configString, "utf8");

  return config;
}

async function promptSshfs(config) {
  const mounted = execa.shellSync("mount").stdout.split("\n");

  function isMounted(remote, local) {
    return mounted.some(mount => mount.startsWith(remote + " on " + local));
  }

  const data = config.urls.map(remote => {
    // user@host:/dir/dir => user@host--dir-div
    const local = path.join(
      config.folder,
      remote.replace(/:/g, "-").replace(/\//g, "-")
    );

    const isChecked = isMounted(remote, local);

    return {
      name: `${remote} ↔ ${local}`,
      checked: isChecked,
      remote: remote,
      local: local,
    };
  });

  const answers = await inquirer.prompt({
    type: "checkbox",
    message: "SSHFS mount/unmount dirs",
    name: "urls",
    choices: data,
  });

  // mount selected items that are not already mounted
  answers.urls
    .map(url => data.find(item => item.name === url))
    .filter(item => !isMounted(item.remote, item.local))
    .forEach(data => {
      execa.sync("mkdir", ["-p", data.local]);

      try {
        execa.sync("sshfs", [data.remote, data.local]);
      } catch (err) {
        process.stdout.write(chalk.bgRed(`\n! ERROR:     ${data.remote}`));
        process.stdout.write(`\n`);
        process.stdout.write(indentString(err.toString(), 4));
        return;
      }

      process.stdout.write(chalk.green(`\n+ Mounted:   ${data.remote}`));
    });

  // unmount items that have been unselected
  data
    .filter(item => !answers.urls.includes(item.name))
    .filter(item => isMounted(item.remote, item.local))
    .forEach(data => {
      const res = execa.sync("fusermount", ["-u", data.local]);
      if (res.stderr) {
        process.stderr.write(res.stderr);
        process.exit(1);
      }
      execa.sync("rm", ["-r", data.local]);
      process.stdout.write(chalk.blue(`\n- Unmounted: ${data.remote}`));
    });
}

function validateConfigString(configString) {
  let config;
  try {
    config = JSON.parse(configString);
  } catch (e) {
    return [false, "Error parsing JSON"];
  }

  if (!config.urls) {
    return [false, `"urls" field is missing or empty`];
  } else if (!Array.isArray(config.urls)) {
    return [false, `"urls" filed is not an array`];
  } else if (config.urls.some(item => typeof item !== "string")) {
    return [false, `all fields in "urls" filed need to be string`];
  } else if (!config.folder) {
    return [false, `"folder" field is missing or empy`];
  } else if (typeof config.folder !== "string") {
    return [false, `"folder" field must be string`];
  }

  return [true, null];
}
<commit_msg>nicer errors when unmounting<commit_after>#!/usr/bin/env node

"use strict";

const os = require("os");
const fs = require("fs");
const path = require("path");
const meow = require("meow");
const chalk = require("chalk");
const execa = require("execa");
const indentString = require("indent-string");
const inquirer = require("inquirer");

// Global variables

const configDir = path.join(os.homedir(), "/.config/");
const configPath = path.join(configDir, "sshfs-box.json");

// CLI

const cli = meow(
  `
  Small CLI tool to mount/unmount directories on remote servers with sshfs.

  Usage
    $ sshfs-box

  Options
    --config, -c  Configure remote & local paths
                  Config stored in ~/.config/sshfs-box.json
`,
  {
    alias: {
      c: "config",
      configure: "config",
      settings: "config",
    },
  }
);

// Main

async function main() {
  let configString;
  try {
    configString = fs.readFileSync(configPath, "utf8");
  } catch (e) {
    process.stdout.write(
      `Can't open config on ${configPath}, creating new config`
    );
    const config = await promptEditConfig();
    await promptSshfs(config);
    return;
  }

  if (cli.flags.config) {
    const config = await promptEditConfig(configString);
    await promptSshfs(config);
  } else {
    const [configValid] = validateConfigString(configString);
    if (configValid) {
      await promptSshfs(JSON.parse(configString));
    } else {
      process.stdout.write(
        `${configPath} does not contain valid config, please fix it`
      );
      const config = await promptEditConfig(configString);
      await promptSshfs(config);
    }
  }
}

main(); // Start the app.

// Prompt functions.

async function promptEditConfig(defaultConfigOverride) {
  const defaultConfig = JSON.stringify(
    {
      urls: ["user@host1:", "user@host2:/home/user", "user@host2:/www"],
      folder: path.join(os.homedir(), "/remote"),
    },
    null,
    2
  );

  const promptSettings = {
    type: "editor",
    name: "config",
    message: "Configure sshfs-box",
    default: defaultConfigOverride || defaultConfig,
    validate: userInput => {
      const [valid, err] = validateConfigString(userInput);
      // Inquirer expects true if input is valid, otherwise string error message.
      return valid ? true : err;
    },
  };

  const response = await inquirer.prompt(promptSettings);

  const configString = response.config;
  const config = JSON.parse(configString);
  try {
    fs.mkdirSync(configDir);
  } catch (e) {
    // Pass.
  }

  fs.writeFileSync(configPath, configString, "utf8");

  return config;
}

async function promptSshfs(config) {
  const mounted = execa.shellSync("mount").stdout.split("\n");

  function isMounted(remote, local) {
    return mounted.some(mount => mount.startsWith(remote + " on " + local));
  }

  const data = config.urls.map(remote => {
    // user@host:/dir/dir => user@host--dir-div
    const local = path.join(
      config.folder,
      remote.replace(/:/g, "-").replace(/\//g, "-")
    );

    const isChecked = isMounted(remote, local);

    return {
      name: `${remote} ↔ ${local}`,
      checked: isChecked,
      remote: remote,
      local: local,
    };
  });

  const answers = await inquirer.prompt({
    type: "checkbox",
    message: "SSHFS mount/unmount dirs",
    name: "urls",
    choices: data,
  });

  // mount selected items that are not already mounted
  answers.urls
    .map(url => data.find(item => item.name === url))
    .filter(item => !isMounted(item.remote, item.local))
    .forEach(data => {
      execa.sync("mkdir", ["-p", data.local]);

      try {
        execa.sync("sshfs", [data.remote, data.local]);
      } catch (err) {
        process.stdout.write(chalk.bgRed(`\n! ERROR:     ${data.remote}`));
        process.stdout.write(`\n`);
        process.stdout.write(indentString(err.toString(), 4));
        return;
      }

      process.stdout.write(chalk.green(`\n+ Mounted:   ${data.remote}`));
    });

  // unmount items that have been unselected
  data
    .filter(item => !answers.urls.includes(item.name))
    .filter(item => isMounted(item.remote, item.local))
    .forEach(data => {
      try {
        execa.sync("fusermount", ["-u", data.local]);
      } catch (err) {
        process.stdout.write(chalk.bgRed(`\n! ERROR:     ${data.remote}`));
        process.stdout.write(`\n`);
        process.stdout.write(indentString(err.toString(), 4));
        return;
      }

      execa.sync("rm", ["-r", data.local]);
      process.stdout.write(chalk.blue(`\n- Unmounted: ${data.remote}`));
    });
}

function validateConfigString(configString) {
  let config;
  try {
    config = JSON.parse(configString);
  } catch (e) {
    return [false, "Error parsing JSON"];
  }

  if (!config.urls) {
    return [false, `"urls" field is missing or empty`];
  } else if (!Array.isArray(config.urls)) {
    return [false, `"urls" filed is not an array`];
  } else if (config.urls.some(item => typeof item !== "string")) {
    return [false, `all fields in "urls" filed need to be string`];
  } else if (!config.folder) {
    return [false, `"folder" field is missing or empy`];
  } else if (typeof config.folder !== "string") {
    return [false, `"folder" field must be string`];
  }

  return [true, null];
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

const _ = require('lodash')
const dateFormat = require('dateformat')
const faker = require('faker')
const fs = require('fs')
const glob = require('glob')
const meow = require('meow')
const path = require('path')

const cli = meow(`
  Usage
    $ congen <command>

  Commands
    generate [n]     Generates content files based on congen templates it finds in path
    delete           Deletes any congen generated content files it finds in path

  Options
    --help, -h       Show this help
    --version, -v    Current version of package

  Examples
    $ congen --version
    $ congen generate
    $ congen delete
`, {
  alias: {
    h: 'help',
    v: 'version'
  }
})

function main() {
  if (cli.input.length < 1) {
    displayHelp()
  } else {
    var command = cli.input[0]
    switch (command) {
      case 'generate':
        var numToGen = cli.input[1] || 1
        processFiles(numToGen)
        break
      case 'delete':
        deleteFiles()
        break
      default:
        displayHelp()
    }
  }
}

function displayHelp() {
  console.log(cli.help)
}

function deleteFiles() {
  glob('**/*-_CONGEN.*', function (err, filepaths) {
    if (err) {
      console.error('Error finding congen generated files: ', err)
    } else {
      if (filepaths.length > 0) {
        var plural = (filepaths.length > 1) ? 's' : ''
        console.log('Deleting ' + filepaths.length + ' congen generated file' + plural + '..')
        _.map(filepaths, function (filepath) {
          console.log('    ' + filepath)
          fs.unlinkSync(filepath)
        })
      }
    }
  })
}

function processFiles(numToGen) {
  glob('**/*.congen', function (err, filepaths) {
    if (err) {
      console.error('Error finding congen templates: ', err)
    } else {
      _.map(filepaths, function (filepath) {
        processFile(filepath, numToGen)
      })
    }
  })
}

function processFile(filepath, numToGen) {
  var plural = (numToGen > 1) ? 's' : ''
  console.log('Generating ' + numToGen + ' file' + plural + ' from ' + filepath + '..')

  var dirname = path.dirname(filepath)
  var ext = getCongenExt(filepath)

  fs.readFile(filepath, 'utf8', function(err, template) {
    if (err) {
      console.error('Error reading ' + filepath + ':', err)
    } else {
      var fib = fibSeq()

      for (var i=0; i<numToGen; i++) {
        var content = genContent(template, fib())
        var basename = formatBasename(content) + ext
        var pathname = path.join(dirname, basename)

        console.log('    ' + pathname)
        fs.writeFileSync(pathname, content.body);
      }
    }
  })
}

function getCongenExt(filepath) {
  var basename = path.basename(filepath, '.congen')
  return path.extname(basename)
}

function formatBasename(content) {
  var d = dateFormat(content.date, "yyyy-mm-dd")
  var title =
    _.kebabCase(_.join([
      d,
      content.title
    ], '-')) + '-_CONGEN'
  return title
}

function fibSeq() {
  return (function() {
    var a = 1, b = 1

    return function() {
      var a_old = a
      var b_old = b

      a = b_old
      b = a_old + b_old

      return a
    }
  }())
}

function genContent(template, dateOffset) {
  var title = _.capitalize(faker.company.catchPhrase())
  var date = new Date()
  if (dateOffset) {
    date.setDate(date.getDate() - dateOffset)
  }
  var parse = function(template) {
    // Adapted from https://github.com/Marak/faker.js/blob/master/lib/fake.js

    var open = '<{'
    var close = '}>'
    var res = ''

    if (typeof template !== 'string' || template.length === 0) {
      res = 'string parameter is required!'
      return res
    } else {
      // find first matching open and close
      var start = template.search(open)
      var end = template.search(close)

      // if no open and close are found, we are done with this template
      if (start === -1 && end === -1) {
        return template
      }

      // extract method name from between open and close
      var token = template.substr(start + open.length, end - start - close.length);
      var method = _.trim(token.replace(close, '').replace(open, ''))

      var methodNameMatch = method.match(/^[^\s]*/)
      var methodName = methodNameMatch ? methodNameMatch[0] : 'NO_METHOD_NAME'

      var methodArgsMatch = method.match(/^[^\s]+\s*(.*)/)
      var methodArgs = methodArgsMatch ? methodArgsMatch[1] : 'NO_METHOD_NAME'

      var result
      switch(methodName) {
        case 'title':
          result = faker.company.catchPhrase()
          title = result
          break
        case 'date':
          result = dateFormat(date, 'yyyy-mm-dd HH:MM:ss o')
          break;
        case 'lorem.paragraphs':
          result = faker.lorem.paragraphs().replace(/\n/g, '\n\n')
          break;
        case 'fake':
          result = faker.fake(methodArgs)
          break;
        default:
          result = ''
      }
      res = template.replace(open + token + close, result)

      return parse(res)
    }
  }
  var body = parse(template)

  return {
    title: title,
    date: date,
    body: body
  }
}

main()
<commit_msg>Validate input on generate numerical argument<commit_after>#!/usr/bin/env node

const _ = require('lodash')
const dateFormat = require('dateformat')
const faker = require('faker')
const fs = require('fs')
const glob = require('glob')
const meow = require('meow')
const path = require('path')

const cli = meow(`
  Usage
    $ congen <command>

  Commands
    generate [n]     Generates content files based on congen templates it finds in path
    delete           Deletes any congen generated content files it finds in path

  Options
    --help, -h       Show this help
    --version, -v    Current version of package

  Examples
    $ congen --version
    $ congen generate
    $ congen delete
`, {
  alias: {
    h: 'help',
    v: 'version'
  }
})

function main() {
  if (cli.input.length < 1) {
    displayHelp()
  } else {
    var command = cli.input[0]
    switch (command) {
      case 'generate':
        var numToGen = cli.input[1] || 1
        processFiles(numToGen)
        break
      case 'delete':
        deleteFiles()
        break
      default:
        displayHelp()
    }
  }
}

function displayHelp() {
  console.log(cli.help)
}

function deleteFiles() {
  glob('**/*-_CONGEN.*', function (err, filepaths) {
    if (err) {
      console.error('Error finding congen generated files: ', err)
    } else {
      if (filepaths.length > 0) {
        var plural = (filepaths.length > 1) ? 's' : ''
        console.log('Deleting ' + filepaths.length + ' congen generated file' + plural + '..')
        _.map(filepaths, function (filepath) {
          console.log('    ' + filepath)
          fs.unlinkSync(filepath)
        })
      }
    }
  })
}

function processFiles(numToGen) {
  glob('**/*.congen', function (err, filepaths) {
    if (err) {
      console.error('Error finding congen templates: ', err)
    } else {
      _.map(filepaths, function (filepath) {
        processFile(filepath, numToGen)
      })
    }
  })
}

function processFile(filepath, numToGen) {
  var numToGen = parseInt(numToGen)
  if (!numToGen || numToGen < 0) {
    console.error('Invalid input expecting n to be a natural number')
    process.exit(1)
  }
  var plural = (numToGen > 1) ? 's' : ''
  console.log('Generating ' + numToGen + ' file' + plural + ' from ' + filepath + '..')

  var dirname = path.dirname(filepath)
  var ext = getCongenExt(filepath)

  fs.readFile(filepath, 'utf8', function(err, template) {
    if (err) {
      console.error('Error reading ' + filepath + ':', err)
    } else {
      var fib = fibSeq()

      for (var i=0; i<numToGen; i++) {
        var content = genContent(template, fib())
        var basename = formatBasename(content) + ext
        var pathname = path.join(dirname, basename)

        console.log('    ' + pathname)
        fs.writeFileSync(pathname, content.body);
      }
    }
  })
}

function getCongenExt(filepath) {
  var basename = path.basename(filepath, '.congen')
  return path.extname(basename)
}

function formatBasename(content) {
  var d = dateFormat(content.date, "yyyy-mm-dd")
  var title =
    _.kebabCase(_.join([
      d,
      content.title
    ], '-')) + '-_CONGEN'
  return title
}

function fibSeq() {
  return (function() {
    var a = 1, b = 1

    return function() {
      var a_old = a
      var b_old = b

      a = b_old
      b = a_old + b_old

      return a
    }
  }())
}

function genContent(template, dateOffset) {
  var title = _.capitalize(faker.company.catchPhrase())
  var date = new Date()
  if (dateOffset) {
    date.setDate(date.getDate() - dateOffset)
  }
  var parse = function(template) {
    // Adapted from https://github.com/Marak/faker.js/blob/master/lib/fake.js

    var open = '<{'
    var close = '}>'
    var res = ''

    if (typeof template !== 'string' || template.length === 0) {
      res = 'string parameter is required!'
      return res
    } else {
      // find first matching open and close
      var start = template.search(open)
      var end = template.search(close)

      // if no open and close are found, we are done with this template
      if (start === -1 && end === -1) {
        return template
      }

      // extract method name from between open and close
      var token = template.substr(start + open.length, end - start - close.length);
      var method = _.trim(token.replace(close, '').replace(open, ''))

      var methodNameMatch = method.match(/^[^\s]*/)
      var methodName = methodNameMatch ? methodNameMatch[0] : 'NO_METHOD_NAME'

      var methodArgsMatch = method.match(/^[^\s]+\s*(.*)/)
      var methodArgs = methodArgsMatch ? methodArgsMatch[1] : 'NO_METHOD_NAME'

      var result
      switch(methodName) {
        case 'title':
          result = faker.company.catchPhrase()
          title = result
          break
        case 'date':
          result = dateFormat(date, 'yyyy-mm-dd HH:MM:ss o')
          break;
        case 'lorem.paragraphs':
          result = faker.lorem.paragraphs().replace(/\n/g, '\n\n')
          break;
        case 'fake':
          result = faker.fake(methodArgs)
          break;
        default:
          result = ''
      }
      res = template.replace(open + token + close, result)

      return parse(res)
    }
  }
  var body = parse(template)

  return {
    title: title,
    date: date,
    body: body
  }
}

main()
<|endoftext|>


<commit_before>/**
 * Author: Vova Zaytsev <zaytsev@usc.edu>
 */

app.controller("NlcdClientController", ["$scope", "$location", "$sce", "NcldApiFactory",
    function ($scope, $location, $sce, NcldApiFactory) {
        /**
        **/

        $scope.central              = null;
        $scope.related              = [];
        $scope.meta                 = null;
        $scope.selection            = [];
        $scope.textSelection        = null;
        $scope.display = {

            // Tab 'ALL'
            relatedTabAllReversed:        false,
            relatedTabAllPredicate:       "inSelection",

            // Tab 'SELECTION'
            relatedTabSelectionReversed:  false,
            relatedTabSelectionPredicate: "pubDate",

            // Tab 'SOURCES'
            relatedTabSourcesReversed:    false,
            relatedTabSourcesPredicate:   "referenceCount",

            // Tab 'AUTHORS'
            relatedTabAuthorsReversed:    false,
            relatedTabAuthorsPredicate:   "referenceCount"

        };

        // Init bootstrap javascript
        $("#relatedTabs a").click(function (e) {
            e.preventDefault();
            $(this).tab("show");
        });
        $("#relatedTabs a:first").tab("show");

        NcldApiFactory.getTestGraph().success(function(data){

            $scope.sg = new StoryGraph(data);
            $scope.central = $scope.sg.getCentralNode();
            $scope.related = $scope.sg.getNodes();
            $scope.meta = data.data.meta;

            var distrPlaceId = "storyDistribution";
            var nwPlaceId = "storyNetwork";

            var distrWidth = document.getElementById(distrPlaceId).offsetWidth;
            var nwWidth = document.getElementById(nwPlaceId).offsetWidth;

            console.log(nwWidth);

            var height = 300;

            $scope.sg.drawDistribution(distrPlaceId, distrWidth, height);
            $scope.sg.drawNetwork(nwPlaceId, nwWidth, height);
        });

        //
        $scope.RelatedAllTabOrderBy = function(predicate) {
            if ($scope.display.relatedTabAllPredicate == predicate) {
                $scope.display.relatedTabAllReversed = !$scope.display.relatedTabAllReversed;
            }
            $scope.display.relatedTabAllPredicate = predicate;
        }

        //
        $scope.RelatedSelectionTabOrderBy = function(predicate) {
            if ($scope.display.relatedTabSelectionPredicate == predicate) {
                $scope.display.relatedTabSelectionReversed = !$scope.display.relatedTabSelectionReversed;
            }
            $scope.display.relatedTabSelectionPredicate = predicate;
        }

        //
        $scope.SetSelection = function(referencesList) {
            for (var i in $scope.selection) {
                $scope.selection[i].inSelection = null;
            };
            $scope.selection = [];
            for (var i in referencesList) {
                var refId = referencesList[i];
                var item = $scope.sg.getNode(refId);
                item.inSelection = true;
                $scope.selection.push(item);
            };
            $scope.sg.gfx.SetDistributionSelection(referencesList);
            $scope.sg.gfx.SetNetworkSelection(referencesList);
        };

        //
        $scope.TextSelection = function(chunk, referencesList) {
            if ($scope.textSelection) {
                $scope.textSelection.isSelected = false;
                if ($scope.textSelection.tagId == chunk.tagId) {
                    $scope.textSelection = null;
                    $scope.SetSelection([]);
                    return;
                }
            }
            $scope.textSelection = chunk;
            $scope.textSelection.isSelected = true;
            $scope.SetSelection(referencesList);
        };

}]);<commit_msg>update webapp<commit_after>/**
 * Author: Vova Zaytsev <zaytsev@usc.edu>
 */

app.controller("NlcdClientController", ["$scope", "$location", "$sce", "NcldApiFactory",
    function ($scope, $location, $sce, NcldApiFactory) {
        /**
        **/

        $scope.central              = null;
        $scope.related              = [];
        $scope.meta                 = null;
        $scope.selection            = [];
        $scope.textSelection        = null;
        $scope.display = {

            // Tab 'ALL'
            relatedTabAllReversed:        false,
            relatedTabAllPredicate:       "inSelection",

            // Tab 'SELECTION'
            relatedTabSelectionReversed:  false,
            relatedTabSelectionPredicate: "pubDate",

            // Tab 'SOURCES'
            relatedTabSourcesReversed:    false,
            relatedTabSourcesPredicate:   "referenceCount",

            // Tab 'AUTHORS'
            relatedTabAuthorsReversed:    false,
            relatedTabAuthorsPredicate:   "referenceCount"

        };

        // Init bootstrap javascript
        $("#relatedTabs a").click(function (e) {
            e.preventDefault();
            $(this).tab("show");
        });
        $("#relatedTabs a:first").tab("show");

        NcldApiFactory.getTestGraph().success(function(data){

            $scope.sg = new StoryGraph(data);
            $scope.central = $scope.sg.getCentralNode();
            $scope.related = $scope.sg.getNodes();
            $scope.meta = data.meta;

            var distrPlaceId = "storyDistribution";
            var nwPlaceId = "storyNetwork";

            var distrWidth = document.getElementById(distrPlaceId).offsetWidth;
            var nwWidth = document.getElementById(nwPlaceId).offsetWidth;

            console.log(nwWidth);

            var height = 300;

            $scope.sg.drawDistribution(distrPlaceId, distrWidth, height);
            $scope.sg.drawNetwork(nwPlaceId, nwWidth, height);
        });

        //
        $scope.RelatedAllTabOrderBy = function(predicate) {
            if ($scope.display.relatedTabAllPredicate == predicate) {
                $scope.display.relatedTabAllReversed = !$scope.display.relatedTabAllReversed;
            }
            $scope.display.relatedTabAllPredicate = predicate;
        }

        //
        $scope.RelatedSelectionTabOrderBy = function(predicate) {
            if ($scope.display.relatedTabSelectionPredicate == predicate) {
                $scope.display.relatedTabSelectionReversed = !$scope.display.relatedTabSelectionReversed;
            }
            $scope.display.relatedTabSelectionPredicate = predicate;
        }

        //
        $scope.SetSelection = function(referencesList) {
            for (var i in $scope.selection) {
                $scope.selection[i].inSelection = null;
            };
            $scope.selection = [];
            for (var i in referencesList) {
                var refId = referencesList[i];
                var item = $scope.sg.getNode(refId);
                item.inSelection = true;
                $scope.selection.push(item);
            };
            $scope.sg.gfx.SetDistributionSelection(referencesList);
            $scope.sg.gfx.SetNetworkSelection(referencesList);
        };

        //
        $scope.TextSelection = function(chunk, referencesList) {
            if ($scope.textSelection) {
                $scope.textSelection.isSelected = false;
                if ($scope.textSelection.tagId == chunk.tagId) {
                    $scope.textSelection = null;
                    $scope.SetSelection([]);
                    return;
                }
            }
            $scope.textSelection = chunk;
            $scope.textSelection.isSelected = true;
            $scope.SetSelection(referencesList);
        };

}]);<|endoftext|>


<commit_before><commit_msg>SFV-1133 Reverted the changes originally made for this issue as it never really fixed the problem of not loading the CRS from cache.  SFV-1138 should correctly fix this.<commit_after><|endoftext|>


<commit_before>#! /usr/bin/env node

/**
 * Copyright 2015 Mozilla
 *
 * 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';

// Import this first so we can use it to wrap other modules we import.
var promisify = require("promisify-node");

var packageJson = require('./package.json');
var program = require('commander');
var rimraf = promisify(require('rimraf'));
var promptly = require('promisified-promptly');
var fs = require('fs');

// The scripts that implement the various commands/tasks we expose.
var configure = require('./lib/configure');
var deploy = require('./lib/deploy');
var offline = require('./lib/offline');
var bootstrap = require('./lib/bootstrap');
var integrate = require('./lib/integrate');

program
  .version(packageJson.version);

program
  .command('configure')
  .description('configure repository to auto-deploy to GitHub Pages using Travis CI')
  .action(function(env, options) {
    configure()
    .catch(function(err) {
      console.error(err);
    })
    .then(process.exit);
  });

program
  .command('deploy [dir]')
  .description('deploy directory to GitHub Pages')
  .option('-m, --message <message>', 'commit message')
  .action(function(dir, options) {
    deploy({
      rootDir: dir,
      cloneDir: '.gh-pages-cache',
      message: options.message,
    })
    .then(function() {
      // For perf, don't delete the repository.  This means users will have to
      // add .gh-pages-cache to their .gitignore file to hide its `git status`.
      // return rimraf('.gh-pages-cache');

      fs.access('.gitignore', function(err) {
        if (err) {
          console.log('.gh-pages-cache is a temporary repository that we use to push changes to your gh-pages branch. We suggest you add it to your .gitignore.');
          return;
        }

        var gitignore = fs.readFileSync('.gitignore', 'utf8');

        if (gitignore.indexOf('.gh-pages-cache') === -1) {
          return promptly.confirm('.gh-pages-cache is a temporary repository that we use to push changes to your gh-pages branch. Do you want to add it to .gitignore?').then(function(answer) {
            if (answer) {
              gitignore += '\n.gh-pages-cache\n';
              fs.writeFileSync('.gitignore', gitignore);
            }
          });
        }
      });
    })
    .catch(function(err) {
      console.error(err);
    });
  });

program
  .command('offline [dir]')
  .description('offline the files in the directory by generating offline-worker.js script')
  .option('--file-globs [fileGlobs]', 'a comma-separated list of file globs to offline (default: \'**/*\')', '**/*')
  .option('--import-scripts <importScripts>', 'a comma-separated list of additional scripts to import into offline-worker.js')
  .action(function(dir, options) {
    offline({
      rootDir: dir,
      fileGlobs: options.fileGlobs ? options.fileGlobs.split(',') : null,
      importScripts: options.importScripts ? options.importScripts.split(',') : null,
    })
    .catch(function(err) {
      console.error(err);
    });
  });

program
  .command('bootstrap [dir]')
  .description('bootstrap the directory with a template app')
  .action(function(dir) {
    bootstrap({
      rootDir: dir,
    })
    .catch(function(err) {
      console.error(err);
    });
  });

  program
    .command('integrate [dir]')
    .description('integrate the offline-manager.js script into your app')
    .action(function(dir) {
      integrate({
        dir: dir,
      })
      .catch(function(err) {
        console.error(err);
      });
    });

program.parse(process.argv);

if (!process.argv.slice(2).length) {
  program.outputHelp();
}
<commit_msg>Exit process when bootstrap is finished<commit_after>#! /usr/bin/env node

/**
 * Copyright 2015 Mozilla
 *
 * 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';

// Import this first so we can use it to wrap other modules we import.
var promisify = require("promisify-node");

var packageJson = require('./package.json');
var program = require('commander');
var rimraf = promisify(require('rimraf'));
var promptly = require('promisified-promptly');
var fs = require('fs');

// The scripts that implement the various commands/tasks we expose.
var configure = require('./lib/configure');
var deploy = require('./lib/deploy');
var offline = require('./lib/offline');
var bootstrap = require('./lib/bootstrap');
var integrate = require('./lib/integrate');

program
  .version(packageJson.version);

program
  .command('configure')
  .description('configure repository to auto-deploy to GitHub Pages using Travis CI')
  .action(function(env, options) {
    configure()
    .catch(function(err) {
      console.error(err);
    })
    .then(process.exit);
  });

program
  .command('deploy [dir]')
  .description('deploy directory to GitHub Pages')
  .option('-m, --message <message>', 'commit message')
  .action(function(dir, options) {
    deploy({
      rootDir: dir,
      cloneDir: '.gh-pages-cache',
      message: options.message,
    })
    .then(function() {
      // For perf, don't delete the repository.  This means users will have to
      // add .gh-pages-cache to their .gitignore file to hide its `git status`.
      // return rimraf('.gh-pages-cache');

      fs.access('.gitignore', function(err) {
        if (err) {
          console.log('.gh-pages-cache is a temporary repository that we use to push changes to your gh-pages branch. We suggest you add it to your .gitignore.');
          return;
        }

        var gitignore = fs.readFileSync('.gitignore', 'utf8');

        if (gitignore.indexOf('.gh-pages-cache') === -1) {
          return promptly.confirm('.gh-pages-cache is a temporary repository that we use to push changes to your gh-pages branch. Do you want to add it to .gitignore?').then(function(answer) {
            if (answer) {
              gitignore += '\n.gh-pages-cache\n';
              fs.writeFileSync('.gitignore', gitignore);
            }
          });
        }
      });
    })
    .catch(function(err) {
      console.error(err);
    });
  });

program
  .command('offline [dir]')
  .description('offline the files in the directory by generating offline-worker.js script')
  .option('--file-globs [fileGlobs]', 'a comma-separated list of file globs to offline (default: \'**/*\')', '**/*')
  .option('--import-scripts <importScripts>', 'a comma-separated list of additional scripts to import into offline-worker.js')
  .action(function(dir, options) {
    offline({
      rootDir: dir,
      fileGlobs: options.fileGlobs ? options.fileGlobs.split(',') : null,
      importScripts: options.importScripts ? options.importScripts.split(',') : null,
    })
    .catch(function(err) {
      console.error(err);
    });
  });

program
  .command('bootstrap [dir]')
  .description('bootstrap the directory with a template app')
  .action(function(dir) {
    bootstrap({
      rootDir: dir,
    })
    .catch(function(err) {
      console.error(err);
    })
    .then(process.exit);
  });

  program
    .command('integrate [dir]')
    .description('integrate the offline-manager.js script into your app')
    .action(function(dir) {
      integrate({
        dir: dir,
      })
      .catch(function(err) {
        console.error(err);
      });
    });

program.parse(process.argv);

if (!process.argv.slice(2).length) {
  program.outputHelp();
}
<|endoftext|>


<commit_before><commit_msg>Fix bad file copy<commit_after><|endoftext|>


<commit_before>var umd = require('./index.js');
umd('package.json');
<commit_msg>Fixup cli.js<commit_after>#!/usr/bin/env node
'use strict';

var umd = require('./');
umd('package.json');
<|endoftext|>


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

var input = process.argv.slice(2);
var pkg = require('./package.json');
var stdin = require('get-stdin');
var viewport = require('./');

/**
 * Help screen
 */

function help() {
    console.log(pkg.description);
    console.log('');
    console.log('Usage');
    console.log('  $ viewport [device]');
    console.log('  $ cat <file> | viewport [device]');
    console.log('');
    console.log('Example');
    console.log('  $ viewport');
    console.log('  $ viewport iphone4 iphone5');
    console.log('  $ cat devices.txt | viewport');
}

/**
 * Show help
 */

if (input.indexOf('-h') !== -1 || input.indexOf('--help') !== -1) {
    help();
    return;
}

/**
 * Show package version
 */

if (input.indexOf('-v') !== -1 || input.indexOf('--version') !== -1) {
    console.log(pkg.version);
    return;
}

/**
 * Run
 */

function run(input) {
    viewport(input, function (err, devices) {
        if (err) {
            throw err;
        }

        console.log(devices);
    });
}

/**
 * Apply arguments
 */

if (process.stdin.isTTY) {
    run(input);
} else {
    stdin(function (data) {
        [].push.apply(input, data.trim().split('\n'));
        run(input);
    });
}
<commit_msg>Convert CLI output to `.csv`<commit_after>#!/usr/bin/env node
'use strict';

var csv = require('to-csv');
var input = process.argv.slice(2);
var pkg = require('./package.json');
var stdin = require('get-stdin');
var viewport = require('./');

/**
 * Help screen
 */

function help() {
    console.log(pkg.description);
    console.log('');
    console.log('Usage');
    console.log('  $ viewport [device]');
    console.log('  $ cat <file> | viewport [device]');
    console.log('');
    console.log('Example');
    console.log('  $ viewport');
    console.log('  $ viewport iphone4 iphone5');
    console.log('  $ cat devices.txt | viewport');
}

/**
 * Show help
 */

if (input.indexOf('-h') !== -1 || input.indexOf('--help') !== -1) {
    help();
    return;
}

/**
 * Show package version
 */

if (input.indexOf('-v') !== -1 || input.indexOf('--version') !== -1) {
    console.log(pkg.version);
    return;
}

/**
 * Run
 */

function run(input) {
    viewport(input, function (err, devices) {
        if (err) {
            throw err;
        }

        console.log(csv(devices));
    });
}

/**
 * Apply arguments
 */

if (process.stdin.isTTY) {
    run(input);
} else {
    stdin(function (data) {
        [].push.apply(input, data.trim().split('\n'));
        run(input);
    });
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';
var meow = require('meow');
var npmProfileDownloadsCli = require('./');

var cli = meow([
	'Usage',
	'  $ npm-profile-downloads-cli [input]',
	'',
	'Options',
	'  --foo  Lorem ipsum. [Default: false]',
	'',
	'Examples',
	'  $ npm-profile-downloads-cli',
	'  unicorns & rainbows',
	'  $ npm-profile-downloads-cli ponies',
	'  ponies & rainbows'
]);

console.log(npmProfileDownloadsCli(cli.input[0] || 'unicorns'));
<commit_msg>implement<commit_after>#!/usr/bin/env node
'use strict';
var meow = require('meow');
var npmProfileDownloads = require('npm-profile-downloads').default;

var cli = meow([
	'Usage',
	'  $ npm-profile-downloads <day|week|month> <username>',
	'',
	'Examples',
	'  $ npm-profile-downloads month sindresorhus',
	'  323196379',
	'  $ npm-profile-downloads month tjholowaychuk',
	'  148049092'
], {
	alias: { h: 'help' }
});

var period = cli.input[0];
var username = cli.input[1];

if (!period || !username) {
	throw new Error('Period or Username had not been specified');
}

npmProfileDownloads('last-' + period, username, function(err, downloads) {
	if (err) throw err;
  console.log(downloads);
})
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import Select from 'react-select';
import 'react-select/dist/react-select.css';

import DashboardHelpers from '../DashboardHelpers';
import StudentsTable from '../StudentsTable';
import DashboardBarChart from '../DashboardBarChart';
import DashRangeButtons from '../DashRangeButtons';
import {latestNoteDateText} from '../../../helpers/latestNoteDateText';
import {sortByGrade} from '../../../helpers/SortHelpers';
import ExperimentalBanner from '../../../components/ExperimentalBanner';

class SchoolDisciplineDashboard extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      startDate: DashboardHelpers.schoolYearStart(),
      selectedChart: 'location',
      selectedCategory: null
    };
    this.setDate = this.setDate.bind(this);
    this.setStudentList = this.setStudentList.bind(this);
    this.resetStudentList = this.resetStudentList.bind(this);
    this.selectChart = this.selectChart.bind(this);
  }

  setDate(range) {
    this.setState({
      startDate: moment.unix(range[0]).format("YYYY-MM-DD")
    });
  }
  setStudentList(highchartsEvent) {
    this.setState({selectedCategory: highchartsEvent.point.category});
  }
  resetStudentList() {
    this.setState({selectedCategory: null});
  }
  selectChart(selection) {
    this.setState({selectedChart: selection.value, selectedCategory: null});
  }

  filterIncidentDates(incidents) {
    return incidents.filter((incident) => {
      return moment.utc(incident.occurred_at).isSameOrAfter(moment.utc(this.state.startDate));
    });
  }

  studentDisciplineIncidentCounts(incidentCategory) {
    let studentDisciplineIncidentCounts = {};
    const selectedChartData = this.getChartData(this.state.selectedChart);
    const incidents = incidentCategory ? selectedChartData.disciplineIncidents[incidentCategory] : this.props.schoolDisciplineEvents;
    this.filterIncidentDates(incidents).forEach((incident) => {
      studentDisciplineIncidentCounts[incident.student_id] = studentDisciplineIncidentCounts[incident.student_id] || 0;
      studentDisciplineIncidentCounts[incident.student_id]++;
    });
    return studentDisciplineIncidentCounts;
  }

  getChartData(selectedChart) {
    return {
      type: selectedChart,
      disciplineIncidents: _.groupBy(this.props.schoolDisciplineEvents, selectedChart),
      title: "Incidents by " + selectedChart};
  }

  sortChartKeys(chartKeys) {
    switch(this.state.selectedChart) {
    case 'time': return this.sortedTimes(chartKeys);
    case 'day': return this.sortedDays(chartKeys);
    case 'grade': return this.sortedGrades(chartKeys);
    case 'classroom': return this.sortedClassrooms(chartKeys);
    default: return chartKeys;
    }
  }

  sortedDays(chartKeys) {
    return ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
  }

  sortedTimes(chartKeys) {
    //chartKeys will either contain a time like "4:00 pm", "10:00 am", or "Not Logged"
    return chartKeys.sort((a, b) => {
      if (a == "Not Logged") return -1;
      if (b == "Not Logged") return 1;
      return new Date('1970/01/01 ' + a) - new Date('1970/01/01 ' + b);
    });
  }

  sortedGrades(chartKeys) {
    return chartKeys.sort((a,b) => sortByGrade(a,b));

  }

  sortedClassrooms(chartKeys) {
    const chart = this.getChartData(this.state.selectedChart);
    return chartKeys.sort((a,b) => {
      return chart.disciplineIncidents[b].length - chart.disciplineIncidents[a].length;
    });
  }

  render() {
    const selectedChart = this.getChartData(this.state.selectedChart);
    const chartOptions = [
      {value: 'location', label: 'Location'},
      {value: 'time', label: 'Time'},
      {value: 'classroom', label: 'Classroom'},
      {value: 'grade', label: 'Grade'},
      {value: 'day', label: 'Day'},
      {value: 'offense', label: 'Offense'},
    ];

    return(

      <ExperimentalBanner />

            {this.renderRangeSelector()}
            {this.renderStudentDisciplineTable()}

                Incidents by:

              <Select
                value={this.state.selectedChart}
                onChange={this.selectChart}
                options={chartOptions}
                style={styles.dropdown}
              />

           {this.renderDisciplineChart(selectedChart)}

    );
  }

  renderDisciplineChart(selectedChart) {
    const categories = this.sortChartKeys(Object.keys(selectedChart.disciplineIncidents));
    const seriesData = categories.map((type) => {
      if (!selectedChart.disciplineIncidents[type]) return [];
      const incidents = this.filterIncidentDates(selectedChart.disciplineIncidents[type]);
      return [type, incidents.length];
    });

    return (
        <DashboardBarChart
          id = "Discipline"
          categories = {{categories: categories}}
          seriesData = {seriesData}
          titleText = {null}
          measureText = {'Number of Incidents'}
          tooltip = {{
            pointFormat: 'Total incidents: {point.y}'}}
          onColumnClick = {this.setStudentList}
          onBackgroundClick = {this.resetStudentList}/>
    );
  }

  renderStudentDisciplineTable() {
    const students = this.props.dashboardStudents;
    const studentDisciplineIncidentCounts = this.studentDisciplineIncidentCounts(this.state.selectedCategory);
    let rows =[];
    students.forEach((student) => {
      rows.push({
        id: student.id,
        first_name: student.first_name,
        last_name: student.last_name,
        last_sst_date_text: latestNoteDateText(300, student.sst_notes),
        events: studentDisciplineIncidentCounts[student.id] || 0,
        grade: student.grade
      });
    });

    return (
      <StudentsTable
        rows = {rows}
        selectedCategory = {this.state.selectedCategory}
        incidentType={"Incidents"}
        resetFn={this.resetStudentList}/>
    );
  }

  renderRangeSelector() {
    const ninetyDaysAgo = moment.utc().subtract(90, 'days').format("YYYY-MM-DD");
    const fortyFiveDaysAgo = moment.utc().subtract(45, 'days').format("YYYY-MM-DD");
    const schoolYearStart = DashboardHelpers.schoolYearStart();
    return (

        <DashRangeButtons
          schoolYearFilter={() => this.setState({startDate: schoolYearStart, selectedRange: 'School Year'})}
          ninetyDayFilter={() => this.setState({startDate: ninetyDaysAgo, selectedRange: '90 Days'})}
          fortyFiveDayFilter={() => this.setState({startDate: fortyFiveDaysAgo, selectedRange: '45 Days'})}/>

    );
  }
}

SchoolDisciplineDashboard.propTypes = {
  dashboardStudents: PropTypes.array.isRequired,
  schoolDisciplineEvents: PropTypes.arrayOf(PropTypes.shape({
    student_id: PropTypes.number.isRequired, //ID of student involved in incident
    location: PropTypes.string, //Place where incident occurred
    time: PropTypes.string, //Time of day for incident - NULL if no specific time recorded
    classroom: PropTypes.string, //Name of student's homeroom teacher
    student_grade: PropTypes.string, //Grade of student
    day: PropTypes.string, //Day of week on which incident occurred
    offense: PropTypes.string, //Specific type of incident
    student_race: PropTypes.string, //Race of student
    occurred_at: PropTypes.string, //Date for incident, used in filtering specific date ranges
    last_sst_date_text: PropTypes.string //Date of last SST meeting
  })).isRequired
};

export default SchoolDisciplineDashboard;

const styles = {
  graphTitle: {
    display: 'flex',
    justifyContent: 'center',
    marginTop: '20px'
  },
  titleText: {
    fontSize: '18px',
    marginRight: '10px',
    alignSelf: 'center'
  },
  dropdown: {
    width: '200px'
  }
};
<commit_msg>dashboard dropdown does not need to be clearable<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import Select from 'react-select';
import 'react-select/dist/react-select.css';

import DashboardHelpers from '../DashboardHelpers';
import StudentsTable from '../StudentsTable';
import DashboardBarChart from '../DashboardBarChart';
import DashRangeButtons from '../DashRangeButtons';
import {latestNoteDateText} from '../../../helpers/latestNoteDateText';
import {sortByGrade} from '../../../helpers/SortHelpers';
import ExperimentalBanner from '../../../components/ExperimentalBanner';

class SchoolDisciplineDashboard extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      startDate: DashboardHelpers.schoolYearStart(),
      selectedChart: 'location',
      selectedCategory: null
    };
    this.setDate = this.setDate.bind(this);
    this.setStudentList = this.setStudentList.bind(this);
    this.resetStudentList = this.resetStudentList.bind(this);
    this.selectChart = this.selectChart.bind(this);
  }

  setDate(range) {
    this.setState({
      startDate: moment.unix(range[0]).format("YYYY-MM-DD")
    });
  }
  setStudentList(highchartsEvent) {
    this.setState({selectedCategory: highchartsEvent.point.category});
  }
  resetStudentList() {
    this.setState({selectedCategory: null});
  }
  selectChart(selection) {
    this.setState({selectedChart: selection.value, selectedCategory: null});
  }

  filterIncidentDates(incidents) {
    return incidents.filter((incident) => {
      return moment.utc(incident.occurred_at).isSameOrAfter(moment.utc(this.state.startDate));
    });
  }

  studentDisciplineIncidentCounts(incidentCategory) {
    let studentDisciplineIncidentCounts = {};
    const selectedChartData = this.getChartData(this.state.selectedChart);
    const incidents = incidentCategory ? selectedChartData.disciplineIncidents[incidentCategory] : this.props.schoolDisciplineEvents;
    this.filterIncidentDates(incidents).forEach((incident) => {
      studentDisciplineIncidentCounts[incident.student_id] = studentDisciplineIncidentCounts[incident.student_id] || 0;
      studentDisciplineIncidentCounts[incident.student_id]++;
    });
    return studentDisciplineIncidentCounts;
  }

  getChartData(selectedChart) {
    return {
      type: selectedChart,
      disciplineIncidents: _.groupBy(this.props.schoolDisciplineEvents, selectedChart),
      title: "Incidents by " + selectedChart};
  }

  sortChartKeys(chartKeys) {
    switch(this.state.selectedChart) {
    case 'time': return this.sortedTimes(chartKeys);
    case 'day': return this.sortedDays(chartKeys);
    case 'grade': return this.sortedGrades(chartKeys);
    case 'classroom': return this.sortedClassrooms(chartKeys);
    default: return chartKeys;
    }
  }

  sortedDays(chartKeys) {
    return ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
  }

  sortedTimes(chartKeys) {
    //chartKeys will either contain a time like "4:00 pm", "10:00 am", or "Not Logged"
    return chartKeys.sort((a, b) => {
      if (a == "Not Logged") return -1;
      if (b == "Not Logged") return 1;
      return new Date('1970/01/01 ' + a) - new Date('1970/01/01 ' + b);
    });
  }

  sortedGrades(chartKeys) {
    return chartKeys.sort((a,b) => sortByGrade(a,b));

  }

  sortedClassrooms(chartKeys) {
    const chart = this.getChartData(this.state.selectedChart);
    return chartKeys.sort((a,b) => {
      return chart.disciplineIncidents[b].length - chart.disciplineIncidents[a].length;
    });
  }

  render() {
    const selectedChart = this.getChartData(this.state.selectedChart);
    const chartOptions = [
      {value: 'location', label: 'Location'},
      {value: 'time', label: 'Time'},
      {value: 'classroom', label: 'Classroom'},
      {value: 'grade', label: 'Grade'},
      {value: 'day', label: 'Day'},
      {value: 'offense', label: 'Offense'},
    ];

    return(

      <ExperimentalBanner />

            {this.renderRangeSelector()}
            {this.renderStudentDisciplineTable()}

                Incidents by:

              <Select
                value={this.state.selectedChart}
                onChange={this.selectChart}
                options={chartOptions}
                style={styles.dropdown}
                clearable={false}
              />

           {this.renderDisciplineChart(selectedChart)}

    );
  }

  renderDisciplineChart(selectedChart) {
    const categories = this.sortChartKeys(Object.keys(selectedChart.disciplineIncidents));
    const seriesData = categories.map((type) => {
      if (!selectedChart.disciplineIncidents[type]) return [];
      const incidents = this.filterIncidentDates(selectedChart.disciplineIncidents[type]);
      return [type, incidents.length];
    });

    return (
        <DashboardBarChart
          id = "Discipline"
          categories = {{categories: categories}}
          seriesData = {seriesData}
          titleText = {null}
          measureText = {'Number of Incidents'}
          tooltip = {{
            pointFormat: 'Total incidents: {point.y}'}}
          onColumnClick = {this.setStudentList}
          onBackgroundClick = {this.resetStudentList}/>
    );
  }

  renderStudentDisciplineTable() {
    const students = this.props.dashboardStudents;
    const studentDisciplineIncidentCounts = this.studentDisciplineIncidentCounts(this.state.selectedCategory);
    let rows =[];
    students.forEach((student) => {
      rows.push({
        id: student.id,
        first_name: student.first_name,
        last_name: student.last_name,
        last_sst_date_text: latestNoteDateText(300, student.sst_notes),
        events: studentDisciplineIncidentCounts[student.id] || 0,
        grade: student.grade
      });
    });

    return (
      <StudentsTable
        rows = {rows}
        selectedCategory = {this.state.selectedCategory}
        incidentType={"Incidents"}
        resetFn={this.resetStudentList}/>
    );
  }

  renderRangeSelector() {
    const ninetyDaysAgo = moment.utc().subtract(90, 'days').format("YYYY-MM-DD");
    const fortyFiveDaysAgo = moment.utc().subtract(45, 'days').format("YYYY-MM-DD");
    const schoolYearStart = DashboardHelpers.schoolYearStart();
    return (

        <DashRangeButtons
          schoolYearFilter={() => this.setState({startDate: schoolYearStart, selectedRange: 'School Year'})}
          ninetyDayFilter={() => this.setState({startDate: ninetyDaysAgo, selectedRange: '90 Days'})}
          fortyFiveDayFilter={() => this.setState({startDate: fortyFiveDaysAgo, selectedRange: '45 Days'})}/>

    );
  }
}

SchoolDisciplineDashboard.propTypes = {
  dashboardStudents: PropTypes.array.isRequired,
  schoolDisciplineEvents: PropTypes.arrayOf(PropTypes.shape({
    student_id: PropTypes.number.isRequired, //ID of student involved in incident
    location: PropTypes.string, //Place where incident occurred
    time: PropTypes.string, //Time of day for incident - NULL if no specific time recorded
    classroom: PropTypes.string, //Name of student's homeroom teacher
    student_grade: PropTypes.string, //Grade of student
    day: PropTypes.string, //Day of week on which incident occurred
    offense: PropTypes.string, //Specific type of incident
    student_race: PropTypes.string, //Race of student
    occurred_at: PropTypes.string, //Date for incident, used in filtering specific date ranges
    last_sst_date_text: PropTypes.string //Date of last SST meeting
  })).isRequired
};

export default SchoolDisciplineDashboard;

const styles = {
  graphTitle: {
    display: 'flex',
    justifyContent: 'center',
    marginTop: '20px'
  },
  titleText: {
    fontSize: '18px',
    marginRight: '10px',
    alignSelf: 'center'
  },
  dropdown: {
    width: '200px'
  }
};
<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

const fs = require('fs');
const url = require('url');
const http = require('follow-redirects').http;

const colors = require('colors');

colors.setTheme({
    error: ['red', 'bold']
});

colors.setTheme({
    info: ['cyan', 'bold']
});

colors.setTheme({
    normal: ['green', 'bold']
});

const argv = require('yargs')
    .usage('\nUsage: $0 -u [/user.id] -n [file name]'.info)
    .demand(['u', 'n'])
    .describe('u', 'ID of facebook user')
    .describe('n', 'File name')
    .argv;

var file = fs.createWriteStream(argv.n+".jpg");

var start_downlaod = http.get('http://graph.facebook.com/' + argv.u + '/picture?width=800', function(res) {
    res.pipe(file);
}).on('error', function(err) {
    console.error(err);
    console.log(image_url + ' downloaded to ' + image_in);
});<commit_msg>directory sec<commit_after>#!/usr/bin/env node

'use strict';

const fs = require('fs');
const url = require('url');
const http = require('follow-redirects').http;
const https = require('follow-redirects').https;
const colors = require('colors');
const mkdirp = require('mkdirp');

colors.setTheme({
    error: ['red', 'bold']
});

colors.setTheme({
    info: ['cyan', 'bold']
});

colors.setTheme({
    normal: ['green', 'bold']
});

const argv = require('yargs')
    .usage('\nUsage: $0 -u [/user.id] -n [file name]'.info)
    .demand(['u', 'n'])
    .describe('u', 'ID of facebook user')
    .describe('n', 'File name')
    .argv;

var image_in = mkdirp('/home/rishi/Downloads/Facebook_Images', function (err) {
    if (err) console.error(err)
    else console.log('pow!')
});

var file = fs.createWriteStream(argv.n+".jpg");

var start_downlaod = http.get('http://graph.facebook.com/' + argv.u + '/picture?width=800', function(res) {
    res.pipe(file);
}).on('error', function(err) {
    console.error(err);
});

fs.mkdirSync(path, mode);<|endoftext|>


<commit_before>Markup = {
	// Parses the passed markup text to a DOM tree contained in a RootTag
	// object, which can be rendered through RootTag#render. This object can be
	// used for caching. But that is not a necessity since parsing is very fast.
	parse: function(text, param) {
		if (!text)
			return null;
		// Create the root tag as a container for all the other bits
		var rootTag = MarkupTag.create('root');
		var start = 0, end = 0;
		// Current tag:
		var tag = rootTag;
		while (start != -1) {
			start = text.indexOf('<', end);
			if (start > end)
				tag.parts.push(text.substring(end, start));
			if (start >= 0) {
				end = text.indexOf('>', start) + 1;
				if (end <= start) {
					// Non-closed tag:
					tag.parts.push(text.substring(start));
					break;
				}
				var closing = text.charAt(start + 1) == '/';
				// empty = contentless tag: <tag/>
				var empty = !closing && text.charAt(end - 2) == '/';
				var definition = text.substring(start + (closing ? 2 : 1),
						end - (empty ? 2 : 1));
				// There is a special convention in place for empty tags:
				// These are interpretated as empty tags:
				// <tag/>, <tag />, <tag param />
				// Thes are not an empty tags. The / is regarded as part of the
				// parameter instead:
				// <tag param/ >, <tag param/>
				// This is to make unnamed url parameters easy to handle, among
				// other things. Detect this here:
				// If the tag definition contains white space, we have
				// parameters. If such a tag ended with / and the last char is
				// not white, it's not actually an empty tag but the / is  part
				// of the parameter:
				if (empty && /\s.*[^\s]$/.test(definition)) {
					empty = false;
					definition += '/';
				}
				var closeTag = null;
				if (!closing || empty) {
					// Opening tag, pass current tag as parent
					tag = MarkupTag.create(definition, tag, param);
					// If this tag does not allow nesting, search for its end
					// immediately now, all what's inbetween to its parts and
					// close it straight away.
					if (tag._nesting === false) {
						// Search for closing tag
						var close = '</' + tag.name + '>';
						start = text.indexOf(close, end);
						if (start >= 0) {
							// Found it, add the part
							tag.parts.push(text.substring(end, start));
							end = start + close.length;
							// Close this tag now (see below):
							closeTag = tag;
						}
					}
				}
				if (closing || empty) {
					// Closing tag
					closeTag = tag;
					// Walk up hierarchy until we find opening tag:
					while(!empty && closeTag && closeTag.name != definition)
						closeTag = closeTag.parent;
				}
				if (closeTag && closeTag != rootTag) {
					// Activate parent tag
				 	tag = closeTag.parent;
					// Add the closed tag to its parent's parts
					tag.parts.push(closeTag);
				}
			}
		}
		if (end > start)
			rootTag.parts.push(text.substring(end));
		return rootTag;
	},

	// Parses the passed text into a DOM tree and renders it directly.
	render: function(text, param) {
		var markup = Markup.parse(text, param);
		return markup && markup.render(param) || '';
	}
};
<commit_msg>Fix newly introduced bug which breaks contentless tags that don't allow nesting.<commit_after>Markup = {
	// Parses the passed markup text to a DOM tree contained in a RootTag
	// object, which can be rendered through RootTag#render. This object can be
	// used for caching. But that is not a necessity since parsing is very fast.
	parse: function(text, param) {
		if (!text)
			return null;
		// Create the root tag as a container for all the other bits
		var rootTag = MarkupTag.create('root');
		var start = 0, end = 0;
		// Current tag:
		var tag = rootTag;
		while (start != -1) {
			start = text.indexOf('<', end);
			if (start > end)
				tag.parts.push(text.substring(end, start));
			if (start >= 0) {
				end = text.indexOf('>', start) + 1;
				if (end <= start) {
					// Non-closed tag:
					tag.parts.push(text.substring(start));
					break;
				}
				var closing = text.charAt(start + 1) == '/';
				// empty = contentless tag: <tag/>
				var empty = !closing && text.charAt(end - 2) == '/';
				var definition = text.substring(start + (closing ? 2 : 1),
						end - (empty ? 2 : 1));
				// There is a special convention in place for empty tags:
				// These are interpretated as empty tags:
				// <tag/>, <tag />, <tag param />
				// Thes are not an empty tags. The / is regarded as part of the
				// parameter instead:
				// <tag param/ >, <tag param/>
				// This is to make unnamed url parameters easy to handle, among
				// other things. Detect this here:
				// If the tag definition contains white space, we have
				// parameters. If such a tag ended with / and the last char is
				// not white, it's not actually an empty tag but the / is  part
				// of the parameter:
				if (empty && /\s.*[^\s]$/.test(definition)) {
					empty = false;
					definition += '/';
				}
				var closeTag = null;
				if (!closing || empty) {
					// Opening tag, pass current tag as parent
					tag = MarkupTag.create(definition, tag, param);
					// If this tag does not allow nesting, search for its end
					// immediately now, all what's inbetween to its parts and
					// close it straight away.
					if (!empty && tag._nesting === false) {
						// Search for closing tag
						var close = '</' + tag.name + '>';
						start = text.indexOf(close, end);
						if (start >= 0) {
							// Found it, add the part
							tag.parts.push(text.substring(end, start));
							end = start + close.length;
							// Close this tag now (see below):
							closeTag = tag;
						}
					}
				}
				if (closing || empty) {
					// Closing tag
					closeTag = tag;
					// Walk up hierarchy until we find opening tag:
					while(!empty && closeTag && closeTag.name != definition)
						closeTag = closeTag.parent;
				}
				if (closeTag && closeTag != rootTag) {
					// Activate parent tag
				 	tag = closeTag.parent;
					// Add the closed tag to its parent's parts
					tag.parts.push(closeTag);
				}
			}
		}
		if (end > start)
			rootTag.parts.push(text.substring(end));
		return rootTag;
	},

	// Parses the passed text into a DOM tree and renders it directly.
	render: function(text, param) {
		var markup = Markup.parse(text, param);
		return markup && markup.render(param) || '';
	}
};
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';
const childProcess = require('child_process');
const chalk = require('chalk');
const meow = require('meow');
const ora = require('ora');
const inquirer = require('inquirer');
const isBin = require('isbin');
const dateFormat = require('dateformat');
const trim = require('trim');
const termTitle = require('term-title');
const cliTruncate = require('cli-truncate');
const copy = require('copy-paste').copy;
const logUpdate = require('log-update');
const cliCursor = require('cli-cursor');
const notifier = require('node-notifier');
const editor = require('editor');
const figures = require('./figures');
const utils = require('./utils');
const somafm = require('.');

const cli = meow(`
  Usage
    $ somafm [<command> <args>] [<options>]

  Example
    $ somafm
    $ somafm list
    $ somafm list ambient
    $ somafm info groovesalad
    $ somafm play fluid

  Commands
    list [<keywords>]   list channels, optionally filter by keywords
    info <channel>      show channel information
    play <channel>      play channel
    record <channel>    start recording channel
    list-favourites     list your favourite songs
    edit-favourites     edit your favourite songs

  Options
    -n   Don't show desktop notifications
`, {
  alias: {
    h: 'help',
    v: 'version'
  }
});

const players = [
  {
    cmd: 'mplayer',
    args: ['-quiet'],
    titleRegex: /StreamTitle='(.*)';StreamUrl=/,
    keyPress: true
  },
  {
    cmd: 'mpv',
    args: ['--msg-level=all=info'],
    titleRegex: /.*icy-title: (.*)$/
  }
];
const streamripperBin = 'streamripper';

const spinner = ora({color: 'yellow'});

let notify = (data, favourite) => {
  if (favourite) {
    data.title = `${figures.heart} ${data.title}`;
  }

  notifier.notify(data);
};

function showChannelList(channels) {
  for (const channel of channels) {
    const str = `${chalk.bold(channel.title)} [${chalk.green(channel.id)}] (${chalk.blue(channel.genre)}) - ${(channel.description)}`;

    if (process.stdout.columns) {
      console.log(cliTruncate(str, process.stdout.columns - 1));
    } else {
      console.log(str);
    }
  }
}

function showChannel(channel) {
  console.log(
`  ${chalk.bold(channel.fullTitle)} [${chalk.green(channel.id)}]

  ${chalk.blue(channel.description)}

    ${chalk.yellow('Now playing')}   ${channel.lastPlaying}

             ${chalk.yellow('DJ')}   ${channel.dj}
          ${chalk.yellow('Genre')}   ${channel.genre}
      ${chalk.yellow('Listeners')}   ${channel.listeners}

     ${chalk.yellow('Stream URL')}   ${channel.stream.urls[0]}`
  );
}

function list(search) {
  spinner.start('Loading channels');

  somafm.getChannels({search, sortChannels: true})
    .then(channels => {
      spinner.stop();
      return Promise.resolve(channels);
    })
    .then(showChannelList)
    .catch(err => {
      spinner.fail(err.toString());
      process.exit(20);
    });
}

function info(channelId) {
  spinner.start('Loading channel information');

  somafm.getChannel(channelId)
    .then(channel => {
      spinner.stop();
      showChannel(channel);
    })
    .catch(err => {
      spinner.fail(err.toString());
      process.exit(10);
    });
}

function play(channelId) {
  spinner.start('Loading channel information');

  somafm.getChannel(channelId)
    .then(channel => {
      spinner.stop();
      return channel;
    })
    .then(playChannel)
    .catch(err => {
      spinner.fail(err.toString());
      process.exit(10);
    });
}

function getPlayer() {
  return new Promise((resolve, reject) => {
    for (const player of players) {
      if (isBin(player.cmd)) {
        resolve(player);
      }
    }

    reject(new Error('No player executable found. Please ensure MPlayer or mpv is installed on your system and runnable within your shell.'));
  });
}

function playChannel(channel) {
  return getPlayer()
    .then(player => {
      let currentTitle = '';
      let currentTitleOut;
      let currentTime;
      let currentFavourite;

      cliCursor.hide();
      console.log(`  Playing   ${chalk.bold(channel.fullTitle)}\n`);

      const args = player.args.concat(channel.stream.urls[0]);
      const playerProc = childProcess.spawn(player.cmd, args);

      const stdin = process.stdin;
      stdin.setRawMode(true);
      stdin.resume();
      stdin.setEncoding('utf-8');

      stdin.on('data', key => {
        if (player.keyPress && ['m', '9', '0', '/', '*'].indexOf(key) > -1) {
          playerProc.stdin.write(key);
        }

        if (key === 'c') {
          copy(currentTitle);
        }

        if (['f', '+'].indexOf(key) > -1) {
          utils.addToFavourites(currentTitle);
          currentFavourite = true;

          logTitle(currentTime, currentTitleOut, true, true);
          windowTitle(currentTitleOut, true);
        }

        if (['u', '-'].indexOf(key) > -1) {
          utils.removeFromFavourites(currentTitle);
          currentFavourite = false;

          logTitle(currentTime, currentTitleOut, false, true);
          windowTitle(currentTitleOut);
        }

        // `ctrl`+`c`, `esc`, `q`
        if (['\u0003', '\u001b', 'q'].indexOf(key) > -1) {
          if (currentTitleOut) {
            logTitle(currentTime, currentTitleOut, currentFavourite);
          }
          logUpdate.done();

          playerProc.kill();
        }
      });

      playerProc.stdout.on('data', data => {
        const line = trim(data.toString());

        const res = line.match(player.titleRegex);
        let title;

        if (res && (title = res[1])) {
          const time = dateFormat(new Date(), 'HH:MM:ss');
          const titleOut = title.match(new RegExp(`SomaFM|Big Url|${channel.title}`, 'i')) ? chalk.gray(title) : title;

          // Overwrite last line
          if (currentTime) {
            logTitle(currentTime, currentTitleOut, currentFavourite);
          }

          currentTitle = title;
          currentTime = chalk.yellow(time);
          currentTitleOut = titleOut;

          currentFavourite = utils.isFavourite(currentTitle);

          logUpdate.done();
          logTitle(currentTime, currentTitleOut, currentFavourite, true);
          windowTitle(currentTitle, currentFavourite);
          notify({
            title: currentTitle,
            message: channel.fullTitle,
            icon: channel.imageFile
          }, currentFavourite);
        }
      });

      playerProc.on('error', Promise.reject);

      playerProc.on('exit', () => {
        termTitle();
        process.exit(0);
      });

      return Promise.resolve();
    });
}

function interactive() {
  spinner.start('Loading channels');

  somafm.getChannels({sortChannels: true})
    .then(channels => {
      spinner.stop();
      return channels;
    })
    .then(showPrompt)
    .then(answers => {
      console.log();
      play(answers.channel);
    })
    .catch(err => {
      console.error(err.toString());
      process.exit(20);
    });
}

function showPrompt(channels) {
  inquirer.registerPrompt('autocomplete', require('inquirer-autocomplete-prompt'));

  const lines = channels.map(channel => ({
    name: `${channel.title} (${chalk.blue(channel.genre)})`,
    value: channel.id,
    short: channel.title
  }));

  return inquirer.prompt([
    {
      type: 'autocomplete',
      name: 'channel',
      message: 'Channel  ',
      source: (answers, input) => Promise.resolve().then(() => filerLines(input, lines))
    }
  ]);
}

function filerLines(input, lines) {
  if (input !== null) {
    return lines.filter(channel => channel.name.toLowerCase().includes(input.toLowerCase()));
  }

  return lines;
}

function logTitle(time, title, favourite, playing) {
  let state = 0;
  let prefix = '';

  if (favourite) {
    state++;
  }
  if (playing) {
    state += 2;
  }

  switch (state) {
    case 1:
      prefix = `${chalk.red(figures.heart)} `;
      break;
    case 2:
      prefix = `${chalk.green(figures.play)} `;
      break;
    case 3:
      prefix = `${chalk.green(figures.heart)} `;
      break;
    default:
  }

  logUpdate(`  ${time}  ${prefix}${title}`);
}

function windowTitle(title, favourite) {
  termTitle(`${favourite ? figures.heart : figures.play} ${title}`);
}

function record(channel) {
  return new Promise((resolve, reject) => {
    if (!isBin(streamripperBin)) {
      reject(new Error('Streamripper executable not found. Please ensure Streamripper is installed on your system and runnable with the "streamripper" command.'));
    }

    const date = dateFormat(new Date(), 'yyyymmdd_HHMMss');
    const args = [
      channel.stream.url,
      '-D', `${channel.fullTitle}/${date}/%1q %A - %T`
    ];
    const streamripperProc = childProcess.spawn(streamripperBin, args, {stdio: [process.stdin, 'pipe', 'pipe']});
    let currentStatus;
    let currentTitle;

    cliCursor.hide();
    console.log(
`  Recording ${chalk.bold(channel.fullTitle)}
  to directory ${chalk.yellow(`${channel.fullTitle}/${date}`)}\n`
    );

    streamripperProc.stdout.on('data', data => {
      const line = data.toString();

      const regex = /^\[(r|sk)ipping.*] (.*) \[(.{7})]$/m;
      const res = line.match(regex);

      if (res && res[1] && res[2]) {
        if ((currentStatus !== res[1] || currentTitle !== res[2]) && res[2].length > 1) {
          if (res[3] && trim(res[3]) === '0b') {
            return;
          }

          currentStatus = res[1];
          currentTitle = res[2];

          const time = dateFormat(new Date(), 'HH:MM:ss');
          const status = res[1] === 'r' ? 'Recording' : 'Skipping ';
          console.log(`  ${chalk.yellow(time)}  ${chalk.bold(status)}  ${currentTitle}`);
        }
      }
    });

    streamripperProc.on('error', reject);

    streamripperProc.on('exit', resolve);
  });
}

function init() {
  if (cli.flags.n) {
    // Disable desktop notifications
    notify = () => {};
  }

  console.log();

  if (cli.input.length === 0) {
    interactive();
    return;
  }

  if (['list', 'ls', 'l'].indexOf(cli.input[0]) > -1) {
    list(cli.input.slice(1));
    return;
  }

  if (['info', 'i'].indexOf(cli.input[0]) > -1 && cli.input[1]) {
    info(cli.input[1]);
    return;
  }

  if (['play', 'p'].indexOf(cli.input[0]) > -1 && cli.input[1]) {
    play(cli.input[1]);
    return;
  }

  if (['record', 'r'].indexOf(cli.input[0]) > -1 && cli.input[1]) {
    somafm.getChannel(cli.input[1])
      .then(channel => {
        record(channel);
      })
      .catch(err => {
        console.error(err.toString());
        process.exit(10);
      });
    return;
  }

  if (['list-favourites', 'list-favorites', 'lf'].indexOf(cli.input[0]) > -1) {
    utils.getFavourites().then(favourites => {
      for (const title of favourites) {
        console.log(`  ${chalk.red(figures.heart)} ${title}`);
      }
    });
    return;
  }

  if (['edit-favourites', 'edit-favorites', 'ef'].indexOf(cli.input[0]) > -1) {
    editor(utils.getFavouritesFile());
    return;
  }

  cli.showHelp();
  process.exit(1);
}

init();
<commit_msg>Show channel description when playing<commit_after>#!/usr/bin/env node
'use strict';
const childProcess = require('child_process');
const chalk = require('chalk');
const meow = require('meow');
const ora = require('ora');
const inquirer = require('inquirer');
const isBin = require('isbin');
const dateFormat = require('dateformat');
const trim = require('trim');
const termTitle = require('term-title');
const cliTruncate = require('cli-truncate');
const copy = require('copy-paste').copy;
const logUpdate = require('log-update');
const cliCursor = require('cli-cursor');
const notifier = require('node-notifier');
const editor = require('editor');
const figures = require('./figures');
const utils = require('./utils');
const somafm = require('.');

const cli = meow(`
  Usage
    $ somafm [<command> <args>] [<options>]

  Example
    $ somafm
    $ somafm list
    $ somafm list ambient
    $ somafm info groovesalad
    $ somafm play fluid

  Commands
    list [<keywords>]   list channels, optionally filter by keywords
    info <channel>      show channel information
    play <channel>      play channel
    record <channel>    start recording channel
    list-favourites     list your favourite songs
    edit-favourites     edit your favourite songs

  Options
    -n   Don't show desktop notifications
`, {
  alias: {
    h: 'help',
    v: 'version'
  }
});

const players = [
  {
    cmd: 'mplayer',
    args: ['-quiet'],
    titleRegex: /StreamTitle='(.*)';StreamUrl=/,
    keyPress: true
  },
  {
    cmd: 'mpv',
    args: ['--msg-level=all=info'],
    titleRegex: /.*icy-title: (.*)$/
  }
];
const streamripperBin = 'streamripper';

const spinner = ora({color: 'yellow'});

let notify = (data, favourite) => {
  if (favourite) {
    data.title = `${figures.heart} ${data.title}`;
  }

  notifier.notify(data);
};

function showChannelList(channels) {
  for (const channel of channels) {
    const str = `${chalk.bold(channel.title)} [${chalk.green(channel.id)}] (${chalk.blue(channel.genre)}) - ${(channel.description)}`;

    if (process.stdout.columns) {
      console.log(cliTruncate(str, process.stdout.columns - 1));
    } else {
      console.log(str);
    }
  }
}

function showChannel(channel) {
  console.log(
`  ${chalk.bold(channel.fullTitle)} [${chalk.green(channel.id)}]

  ${chalk.blue(channel.description)}

    ${chalk.yellow('Now playing')}   ${channel.lastPlaying}

             ${chalk.yellow('DJ')}   ${channel.dj}
          ${chalk.yellow('Genre')}   ${channel.genre}
      ${chalk.yellow('Listeners')}   ${channel.listeners}

     ${chalk.yellow('Stream URL')}   ${channel.stream.urls[0]}`
  );
}

function list(search) {
  spinner.start('Loading channels');

  somafm.getChannels({search, sortChannels: true})
    .then(channels => {
      spinner.stop();
      return Promise.resolve(channels);
    })
    .then(showChannelList)
    .catch(err => {
      spinner.fail(err.toString());
      process.exit(20);
    });
}

function info(channelId) {
  spinner.start('Loading channel information');

  somafm.getChannel(channelId)
    .then(channel => {
      spinner.stop();
      showChannel(channel);
    })
    .catch(err => {
      spinner.fail(err.toString());
      process.exit(10);
    });
}

function play(channelId) {
  spinner.start('Loading channel information');

  somafm.getChannel(channelId)
    .then(channel => {
      spinner.stop();
      return channel;
    })
    .then(playChannel)
    .catch(err => {
      spinner.fail(err.toString());
      process.exit(10);
    });
}

function getPlayer() {
  return new Promise((resolve, reject) => {
    for (const player of players) {
      if (isBin(player.cmd)) {
        resolve(player);
      }
    }

    reject(new Error('No player executable found. Please ensure MPlayer or mpv is installed on your system and runnable within your shell.'));
  });
}

function playChannel(channel) {
  return getPlayer()
    .then(player => {
      let currentTitle = '';
      let currentTitleOut;
      let currentTime;
      let currentFavourite;

      cliCursor.hide();
      console.log(`  Playing   ${chalk.bold(channel.fullTitle)}\n\n  ${chalk.blue(channel.description)}\n`);

      const args = player.args.concat(channel.stream.urls[0]);
      const playerProc = childProcess.spawn(player.cmd, args);

      const stdin = process.stdin;
      stdin.setRawMode(true);
      stdin.resume();
      stdin.setEncoding('utf-8');

      stdin.on('data', key => {
        if (player.keyPress && ['m', '9', '0', '/', '*'].indexOf(key) > -1) {
          playerProc.stdin.write(key);
        }

        if (key === 'c') {
          copy(currentTitle);
        }

        if (['f', '+'].indexOf(key) > -1) {
          utils.addToFavourites(currentTitle);
          currentFavourite = true;

          logTitle(currentTime, currentTitleOut, true, true);
          windowTitle(currentTitleOut, true);
        }

        if (['u', '-'].indexOf(key) > -1) {
          utils.removeFromFavourites(currentTitle);
          currentFavourite = false;

          logTitle(currentTime, currentTitleOut, false, true);
          windowTitle(currentTitleOut);
        }

        // `ctrl`+`c`, `esc`, `q`
        if (['\u0003', '\u001b', 'q'].indexOf(key) > -1) {
          if (currentTitleOut) {
            logTitle(currentTime, currentTitleOut, currentFavourite);
          }
          logUpdate.done();

          playerProc.kill();
        }
      });

      playerProc.stdout.on('data', data => {
        const line = trim(data.toString());

        const res = line.match(player.titleRegex);
        let title;

        if (res && (title = res[1])) {
          const time = dateFormat(new Date(), 'HH:MM:ss');
          const titleOut = title.match(new RegExp(`SomaFM|Big Url|${channel.title}`, 'i')) ? chalk.gray(title) : title;

          // Overwrite last line
          if (currentTime) {
            logTitle(currentTime, currentTitleOut, currentFavourite);
          }

          currentTitle = title;
          currentTime = chalk.yellow(time);
          currentTitleOut = titleOut;

          currentFavourite = utils.isFavourite(currentTitle);

          logUpdate.done();
          logTitle(currentTime, currentTitleOut, currentFavourite, true);
          windowTitle(currentTitle, currentFavourite);
          notify({
            title: currentTitle,
            message: channel.fullTitle,
            icon: channel.imageFile
          }, currentFavourite);
        }
      });

      playerProc.on('error', Promise.reject);

      playerProc.on('exit', () => {
        termTitle();
        process.exit(0);
      });

      return Promise.resolve();
    });
}

function interactive() {
  spinner.start('Loading channels');

  somafm.getChannels({sortChannels: true})
    .then(channels => {
      spinner.stop();
      return channels;
    })
    .then(showPrompt)
    .then(answers => {
      console.log();
      play(answers.channel);
    })
    .catch(err => {
      console.error(err.toString());
      process.exit(20);
    });
}

function showPrompt(channels) {
  inquirer.registerPrompt('autocomplete', require('inquirer-autocomplete-prompt'));

  const lines = channels.map(channel => ({
    name: `${channel.title} (${chalk.blue(channel.genre)})`,
    value: channel.id,
    short: channel.title
  }));

  return inquirer.prompt([
    {
      type: 'autocomplete',
      name: 'channel',
      message: 'Channel  ',
      source: (answers, input) => Promise.resolve().then(() => filerLines(input, lines))
    }
  ]);
}

function filerLines(input, lines) {
  if (input !== null) {
    return lines.filter(channel => channel.name.toLowerCase().includes(input.toLowerCase()));
  }

  return lines;
}

function logTitle(time, title, favourite, playing) {
  let state = 0;
  let prefix = '';

  if (favourite) {
    state++;
  }
  if (playing) {
    state += 2;
  }

  switch (state) {
    case 1:
      prefix = `${chalk.red(figures.heart)} `;
      break;
    case 2:
      prefix = `${chalk.green(figures.play)} `;
      break;
    case 3:
      prefix = `${chalk.green(figures.heart)} `;
      break;
    default:
  }

  logUpdate(`  ${time}  ${prefix}${title}`);
}

function windowTitle(title, favourite) {
  termTitle(`${favourite ? figures.heart : figures.play} ${title}`);
}

function record(channel) {
  return new Promise((resolve, reject) => {
    if (!isBin(streamripperBin)) {
      reject(new Error('Streamripper executable not found. Please ensure Streamripper is installed on your system and runnable with the "streamripper" command.'));
    }

    const date = dateFormat(new Date(), 'yyyymmdd_HHMMss');
    const args = [
      channel.stream.url,
      '-D', `${channel.fullTitle}/${date}/%1q %A - %T`
    ];
    const streamripperProc = childProcess.spawn(streamripperBin, args, {stdio: [process.stdin, 'pipe', 'pipe']});
    let currentStatus;
    let currentTitle;

    cliCursor.hide();
    console.log(
`  Recording ${chalk.bold(channel.fullTitle)}
  to directory ${chalk.yellow(`${channel.fullTitle}/${date}`)}\n`
    );

    streamripperProc.stdout.on('data', data => {
      const line = data.toString();

      const regex = /^\[(r|sk)ipping.*] (.*) \[(.{7})]$/m;
      const res = line.match(regex);

      if (res && res[1] && res[2]) {
        if ((currentStatus !== res[1] || currentTitle !== res[2]) && res[2].length > 1) {
          if (res[3] && trim(res[3]) === '0b') {
            return;
          }

          currentStatus = res[1];
          currentTitle = res[2];

          const time = dateFormat(new Date(), 'HH:MM:ss');
          const status = res[1] === 'r' ? 'Recording' : 'Skipping ';
          console.log(`  ${chalk.yellow(time)}  ${chalk.bold(status)}  ${currentTitle}`);
        }
      }
    });

    streamripperProc.on('error', reject);

    streamripperProc.on('exit', resolve);
  });
}

function init() {
  if (cli.flags.n) {
    // Disable desktop notifications
    notify = () => {};
  }

  console.log();

  if (cli.input.length === 0) {
    interactive();
    return;
  }

  if (['list', 'ls', 'l'].indexOf(cli.input[0]) > -1) {
    list(cli.input.slice(1));
    return;
  }

  if (['info', 'i'].indexOf(cli.input[0]) > -1 && cli.input[1]) {
    info(cli.input[1]);
    return;
  }

  if (['play', 'p'].indexOf(cli.input[0]) > -1 && cli.input[1]) {
    play(cli.input[1]);
    return;
  }

  if (['record', 'r'].indexOf(cli.input[0]) > -1 && cli.input[1]) {
    somafm.getChannel(cli.input[1])
      .then(channel => {
        record(channel);
      })
      .catch(err => {
        console.error(err.toString());
        process.exit(10);
      });
    return;
  }

  if (['list-favourites', 'list-favorites', 'lf'].indexOf(cli.input[0]) > -1) {
    utils.getFavourites().then(favourites => {
      for (const title of favourites) {
        console.log(`  ${chalk.red(figures.heart)} ${title}`);
      }
    });
    return;
  }

  if (['edit-favourites', 'edit-favorites', 'ef'].indexOf(cli.input[0]) > -1) {
    editor(utils.getFavouritesFile());
    return;
  }

  cli.showHelp();
  process.exit(1);
}

init();
<|endoftext|>


<commit_before>(function ($) {

    $(document).on("cmck", function (e, params) {

        switch (params.type) {

            case 'editForm.init':
            case 'sequenceForm.init':
            case 'sequenceForm.refresh':

                $('.formelement-file-modal-button').click(function () {

                    // get the input field
                    var input = $($(this).attr('data-input'));

                    parent.cmck_set_var('fe_file_property', input);

                    parent.cmck_modal($(this).attr('href'));

                    return false;
                });

                $('.formelement-file-modal-button-view').click(function () {

                    var id = $(this).attr('data-input');
                    var value = $(id).val();

                    $(parent.document).find('#modal_files_file_zoom_title').html(value);
                    $(parent.document).find('#modal_files_file_zoom_iframe').attr('src', $(this).attr('href') + value);

                    $.ajax({
                        url: $(this).attr('href') + value,
                        type: 'HEAD',
                        error: function () {
                            alert('File not found. Please check file path.');
                        },
                        success: function () {
                            parent.cmck_modal_id('modal_files_file_zoom');
                        }
                    });

                    return false;
                });

                $('.formelement-file-modal-button-download').click(function () {

                    var id = $(this).attr('data-input');
                    var value = $(id).val();

                    if (value.trim() != '') {

                        value = $(this).attr('href') + value;

                        $.ajax({
                            url: value,
                            type: 'HEAD',
                            success: function () {
                                window.location.href = value;
                                return false;
                            }
                        });
                    }
                    alert('File not found. Please check file path.');

                    return false;
                });

                $('.formelement-file input, .formelement-image input').on('change', function () {

                    var id = '#' + $(this).attr('id') + '_preview';
                    var value = $(this).val();

                    $(id).hide();
                    $(id + ' a').attr('href', '');
                    $(id + ' img').attr('src', '');

                    if (value) {

                        if (value.toLowerCase().match(/\.(jpeg|jpg|gif|png)$/) != null) {
                            var url_view = $(this).attr('data-url-view') + value;

                            $.ajax({
                                url: url_view,
                                type: 'HEAD',
                                error: function () {
                                    $(id).hide();
                                },
                                success: function () {
                                    $(id).show();
                                    $(id + ' a').attr('href', url_view);
                                    $(id + ' img').attr('src', url_view);
                                }
                            });
                        }
                    }

                });
                break;

        }

    });

    /* ---------------------------------------- */
})(jQuery);

<commit_msg>adjusted JavaScript for file existence check on file form elements. Works now with slower connections and with files not on the same domain.<commit_after>(function ($) {

    $(document).on("cmck", function (e, params) {

        switch (params.type) {

            case 'editForm.init':
            case 'sequenceForm.init':
            case 'sequenceForm.refresh':

                $('.formelement-file-modal-button').click(function () {

                    // get the input field
                    var input = $($(this).attr('data-input'));

                    parent.cmck_set_var('fe_file_property', input);

                    parent.cmck_modal($(this).attr('href'));

                    return false;
                });

                $('.formelement-file-modal-button-view').click(function () {

                    var id = $(this).attr('data-input');
                    var value = $(id).val();

                    $(parent.document).find('#modal_files_file_zoom_title').html(value);
                    $(parent.document).find('#modal_files_file_zoom_iframe').attr('src', $(this).attr('href') + value);

                    $.ajax({
                        url: $(this).attr('href') + value,
                        type: 'HEAD'
                    }).always(function (o) {
                        if (o.status == 404) { // ignore all other errors, especially missing Access-Control-Allow-Origin header
                            alert('File not found. Please check file path.');
                        }
                        else {
                            parent.cmck_modal_id('modal_files_file_zoom');
                        }
                    });

                    return false;
                });

                $('.formelement-file-modal-button-download').click(function () {

                    var id = $(this).attr('data-input');
                    var value = $(id).val();

                    if (value.trim() != '') {

                        value = $(this).attr('href') + value;

                        $.ajax({
                            url: value,
                            type: 'HEAD',
                            success: function () {
                                window.location.href = value;
                                return false;
                            },
                            error: function () {
                                alert('File not found. Please check file path.');
                            }
                        });
                    }

                    return false;
                });

                $('.formelement-file input, .formelement-image input').on('change', function () {

                    var id = '#' + $(this).attr('id') + '_preview';
                    var value = $(this).val();

                    $(id).hide();
                    $(id + ' a').attr('href', '');
                    $(id + ' img').attr('src', '');

                    if (value) {

                        if (value.toLowerCase().match(/\.(jpeg|jpg|gif|png)$/) != null) {
                            var url_view = $(this).attr('data-url-view') + value;

                            $.ajax({
                                url: url_view,
                                type: 'HEAD',
                                error: function () {
                                    $(id).hide();
                                },
                                success: function () {
                                    $(id).show();
                                    $(id + ' a').attr('href', url_view);
                                    $(id + ' img').attr('src', url_view);
                                }
                            });
                        }
                    }

                });
                break;

        }

    });

    /* ---------------------------------------- */
})(jQuery);

<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';
var progressControl = require('progress-control');
var indentString = require('indent-string');
var cliCursor = require('cli-cursor');
var toPercent = require('to-percent');
var firstRun = require('first-run');
var chalk = require('chalk');
var meow = require('meow');
var vol = require('./');

var cli = meow({
	help: [
		'Usage',
		'  $ vol (Interactive CLI)',
		'  $ vol <level>',
		'  $ vol mute'
	]
});

if (!cli.input.length) {
	vol.get(function (err, level) {
		if (err) {
			console.error(err.message);
			process.exit(1);
		}

		if (!process.stdin.isTTY) {
			console.log(val);
			return;
		}

		function updateBar(level) {
			vol.set(level, function (err) {
				if (err) {
					console.error(err.message);
					process.exit(1);
				}
			});

			var str = toPercent(level) + '%';
			var maxLength = 4;
			bar.update(level, {level: indentString(str, ' ', maxLength - str.length)});
		}

		cliCursor.hide();

		var text = '[:bar] :level';

		if (firstRun()) {
			text += '   ' + chalk.dim('Use up/down arrows');
		}

		var bar = progressControl(text, {total: 10}, {
			up: function () {
				level = Math.min(Math.round((level + 0.1) * 10) / 10, 1);
				updateBar(level);
			},
			down: function () {
				level = Math.max(Math.round((level - 0.1) * 10) / 10, 0);
				updateBar(level);
			}
		});

		updateBar(level);
	});

	return;
}

vol.set(cli.input[0] === 'mute' ? 0 : cli.input[0], function (err) {
	if (err) {
		console.error(err.message);
		process.exit(1);
	}
});
<commit_msg>Tweaks<commit_after>#!/usr/bin/env node
'use strict';
var progressControl = require('progress-control');
var indentString = require('indent-string');
var cliCursor = require('cli-cursor');
var toPercent = require('to-percent');
var firstRun = require('first-run');
var chalk = require('chalk');
var meow = require('meow');
var vol = require('./');

var cli = meow({
	help: [
		'Usage',
		'  $ vol (Interactive CLI)',
		'  $ vol <level>',
		'  $ vol mute'
	]
});

if (!cli.input.length) {
	vol.get(function (err, level) {
		if (err) {
			console.error(err.message);
			process.exit(1);
		}

		if (!process.stdin.isTTY) {
			console.log(level);
			return;
		}

		var text = '[:bar] :level';
		var bar = progressControl(text, {total: 10}, {
			up: function () {
				level = Math.min(Math.round((level + 0.1) * 10) / 10, 1);
				updateBar(level);
			},
			down: function () {
				level = Math.max(Math.round((level - 0.1) * 10) / 10, 0);
				updateBar(level);
			}
		});

		function updateBar(level) {
			vol.set(level, function (err) {
				if (err) {
					console.error(err.message);
					process.exit(1);
				}
			});

			var str = toPercent(level) + '%';
			var maxLength = 4;
			bar.update(level, {level: indentString(str, ' ', maxLength - str.length)});
		}

		cliCursor.hide();

		if (firstRun()) {
			text += '   ' + chalk.dim('Use up/down arrows');
		}

		updateBar(level);
	});

	return;
}

vol.set(cli.input[0] === 'mute' ? 0 : cli.input[0], function (err) {
	if (err) {
		console.error(err.message);
		process.exit(1);
	}
});
<|endoftext|>